Show More
@@ -0,0 +1,76 b'' | |||||
|
1 | Test rebase --continue with rebasestate written by legacy client | |||
|
2 | ||||
|
3 | $ cat >> $HGRCPATH <<EOF | |||
|
4 | > [extensions] | |||
|
5 | > rebase= | |||
|
6 | > drawdag=$TESTDIR/drawdag.py | |||
|
7 | > EOF | |||
|
8 | ||||
|
9 | $ hg init | |||
|
10 | $ hg debugdrawdag <<'EOF' | |||
|
11 | > D H | |||
|
12 | > | | | |||
|
13 | > C G | |||
|
14 | > | | | |||
|
15 | > B F | |||
|
16 | > | | | |||
|
17 | > Z A E | |||
|
18 | > \|/ | |||
|
19 | > R | |||
|
20 | > EOF | |||
|
21 | ||||
|
22 | rebasestate generated by a legacy client running "hg rebase -r B+D+E+G+H -d Z" | |||
|
23 | ||||
|
24 | $ touch .hg/last-message.txt | |||
|
25 | $ cat > .hg/rebasestate <<EOF | |||
|
26 | > 0000000000000000000000000000000000000000 | |||
|
27 | > f424eb6a8c01c4a0c0fba9f863f79b3eb5b4b69f | |||
|
28 | > 0000000000000000000000000000000000000000 | |||
|
29 | > 0 | |||
|
30 | > 0 | |||
|
31 | > 0 | |||
|
32 | > | |||
|
33 | > 21a6c45028857f500f56ae84fbf40689c429305b:-2 | |||
|
34 | > de008c61a447fcfd93f808ef527d933a84048ce7:0000000000000000000000000000000000000000 | |||
|
35 | > c1e6b162678d07d0b204e5c8267d51b4e03b633c:0000000000000000000000000000000000000000 | |||
|
36 | > aeba276fcb7df8e10153a07ee728d5540693f5aa:-3 | |||
|
37 | > bd5548558fcf354d37613005737a143871bf3723:-3 | |||
|
38 | > d2fa1c02b2401b0e32867f26cce50818a4bd796a:0000000000000000000000000000000000000000 | |||
|
39 | > 6f7a236de6852570cd54649ab62b1012bb78abc8:0000000000000000000000000000000000000000 | |||
|
40 | > 6582e6951a9c48c236f746f186378e36f59f4928:0000000000000000000000000000000000000000 | |||
|
41 | > EOF | |||
|
42 | ||||
|
43 | $ hg rebase --continue | |||
|
44 | rebasing 4:c1e6b162678d "B" (B) | |||
|
45 | rebasing 8:6f7a236de685 "D" (D) | |||
|
46 | rebasing 2:de008c61a447 "E" (E) | |||
|
47 | rebasing 7:d2fa1c02b240 "G" (G) | |||
|
48 | rebasing 9:6582e6951a9c "H" (H tip) | |||
|
49 | warning: orphaned descendants detected, not stripping c1e6b162678d, de008c61a447 | |||
|
50 | saved backup bundle to $TESTTMP/.hg/strip-backup/6f7a236de685-9880a3dc-rebase.hg (glob) | |||
|
51 | ||||
|
52 | $ hg log -G -T '{rev}:{node|short} {desc}\n' | |||
|
53 | o 11:721b8da0a708 H | |||
|
54 | | | |||
|
55 | o 10:9d65695ec3c2 G | |||
|
56 | | | |||
|
57 | o 9:21c8397a5d68 E | |||
|
58 | | | |||
|
59 | | o 8:fc52970345e8 D | |||
|
60 | | | | |||
|
61 | | o 7:eac96551b107 B | |||
|
62 | |/ | |||
|
63 | | o 6:bd5548558fcf C | |||
|
64 | | | | |||
|
65 | | | o 5:aeba276fcb7d F | |||
|
66 | | | | | |||
|
67 | | o | 4:c1e6b162678d B | |||
|
68 | | | | | |||
|
69 | o | | 3:f424eb6a8c01 Z | |||
|
70 | | | | | |||
|
71 | +---o 2:de008c61a447 E | |||
|
72 | | | | |||
|
73 | | o 1:21a6c4502885 A | |||
|
74 | |/ | |||
|
75 | o 0:b41ce7760717 R | |||
|
76 |
@@ -21,7 +21,6 b' import os' | |||||
21 |
|
21 | |||
22 | from mercurial.i18n import _ |
|
22 | from mercurial.i18n import _ | |
23 | from mercurial.node import ( |
|
23 | from mercurial.node import ( | |
24 | hex, |
|
|||
25 | nullid, |
|
24 | nullid, | |
26 | nullrev, |
|
25 | nullrev, | |
27 | short, |
|
26 | short, | |
@@ -60,6 +59,7 b' templateopts = cmdutil.templateopts' | |||||
60 |
|
59 | |||
61 | # Indicates that a revision needs to be rebased |
|
60 | # Indicates that a revision needs to be rebased | |
62 | revtodo = -1 |
|
61 | revtodo = -1 | |
|
62 | revtodostr = '-1' | |||
63 |
|
63 | |||
64 | # legacy revstates no longer needed in current code |
|
64 | # legacy revstates no longer needed in current code | |
65 | # -2: nullmerge, -3: revignored, -4: revprecursor, -5: revpruned |
|
65 | # -2: nullmerge, -3: revignored, -4: revprecursor, -5: revpruned | |
@@ -146,7 +146,7 b' class rebaseruntime(object):' | |||||
146 | # dict will be what contains most of the rebase progress state. |
|
146 | # dict will be what contains most of the rebase progress state. | |
147 | self.state = {} |
|
147 | self.state = {} | |
148 | self.activebookmark = None |
|
148 | self.activebookmark = None | |
149 |
self.dest = |
|
149 | self.destmap = {} | |
150 | self.skipped = set() |
|
150 | self.skipped = set() | |
151 |
|
151 | |||
152 | self.collapsef = opts.get('collapse', False) |
|
152 | self.collapsef = opts.get('collapse', False) | |
@@ -177,34 +177,34 b' class rebaseruntime(object):' | |||||
177 | def _writestatus(self, f): |
|
177 | def _writestatus(self, f): | |
178 | repo = self.repo.unfiltered() |
|
178 | repo = self.repo.unfiltered() | |
179 | f.write(repo[self.originalwd].hex() + '\n') |
|
179 | f.write(repo[self.originalwd].hex() + '\n') | |
180 | f.write(repo[self.dest].hex() + '\n') |
|
180 | # was "dest". we now write dest per src root below. | |
|
181 | f.write('\n') | |||
181 | f.write(repo[self.external].hex() + '\n') |
|
182 | f.write(repo[self.external].hex() + '\n') | |
182 | f.write('%d\n' % int(self.collapsef)) |
|
183 | f.write('%d\n' % int(self.collapsef)) | |
183 | f.write('%d\n' % int(self.keepf)) |
|
184 | f.write('%d\n' % int(self.keepf)) | |
184 | f.write('%d\n' % int(self.keepbranchesf)) |
|
185 | f.write('%d\n' % int(self.keepbranchesf)) | |
185 | f.write('%s\n' % (self.activebookmark or '')) |
|
186 | f.write('%s\n' % (self.activebookmark or '')) | |
|
187 | destmap = self.destmap | |||
186 | for d, v in self.state.iteritems(): |
|
188 | for d, v in self.state.iteritems(): | |
187 | oldrev = repo[d].hex() |
|
189 | oldrev = repo[d].hex() | |
188 | if v >= 0: |
|
190 | if v >= 0: | |
189 | newrev = repo[v].hex() |
|
191 | newrev = repo[v].hex() | |
190 | elif v == revtodo: |
|
|||
191 | # To maintain format compatibility, we have to use nullid. |
|
|||
192 | # Please do remove this special case when upgrading the format. |
|
|||
193 | newrev = hex(nullid) |
|
|||
194 | else: |
|
192 | else: | |
195 | newrev = v |
|
193 | newrev = v | |
196 | f.write("%s:%s\n" % (oldrev, newrev)) |
|
194 | destnode = repo[destmap[d]].hex() | |
|
195 | f.write("%s:%s:%s\n" % (oldrev, newrev, destnode)) | |||
197 | repo.ui.debug('rebase status stored\n') |
|
196 | repo.ui.debug('rebase status stored\n') | |
198 |
|
197 | |||
199 | def restorestatus(self): |
|
198 | def restorestatus(self): | |
200 | """Restore a previously stored status""" |
|
199 | """Restore a previously stored status""" | |
201 | repo = self.repo |
|
200 | repo = self.repo | |
202 | keepbranches = None |
|
201 | keepbranches = None | |
203 | dest = None |
|
202 | legacydest = None | |
204 | collapse = False |
|
203 | collapse = False | |
205 | external = nullrev |
|
204 | external = nullrev | |
206 | activebookmark = None |
|
205 | activebookmark = None | |
207 | state = {} |
|
206 | state = {} | |
|
207 | destmap = {} | |||
208 |
|
208 | |||
209 | try: |
|
209 | try: | |
210 | f = repo.vfs("rebasestate") |
|
210 | f = repo.vfs("rebasestate") | |
@@ -212,7 +212,10 b' class rebaseruntime(object):' | |||||
212 | if i == 0: |
|
212 | if i == 0: | |
213 | originalwd = repo[l].rev() |
|
213 | originalwd = repo[l].rev() | |
214 | elif i == 1: |
|
214 | elif i == 1: | |
215 | dest = repo[l].rev() |
|
215 | # this line should be empty in newer version. but legacy | |
|
216 | # clients may still use it | |||
|
217 | if l: | |||
|
218 | legacydest = repo[l].rev() | |||
216 | elif i == 2: |
|
219 | elif i == 2: | |
217 | external = repo[l].rev() |
|
220 | external = repo[l].rev() | |
218 | elif i == 3: |
|
221 | elif i == 3: | |
@@ -227,10 +230,17 b' class rebaseruntime(object):' | |||||
227 | # oldrev:newrev lines |
|
230 | # oldrev:newrev lines | |
228 | activebookmark = l |
|
231 | activebookmark = l | |
229 | else: |
|
232 | else: | |
230 |
|
|
233 | args = l.split(':') | |
|
234 | oldrev = args[0] | |||
|
235 | newrev = args[1] | |||
231 | if newrev in legacystates: |
|
236 | if newrev in legacystates: | |
232 | continue |
|
237 | continue | |
233 |
|
|
238 | if len(args) > 2: | |
|
239 | destnode = args[2] | |||
|
240 | else: | |||
|
241 | destnode = legacydest | |||
|
242 | destmap[repo[oldrev].rev()] = repo[destnode].rev() | |||
|
243 | if newrev in (nullid, revtodostr): | |||
234 | state[repo[oldrev].rev()] = revtodo |
|
244 | state[repo[oldrev].rev()] = revtodo | |
235 | # Legacy compat special case |
|
245 | # Legacy compat special case | |
236 | else: |
|
246 | else: | |
@@ -247,7 +257,7 b' class rebaseruntime(object):' | |||||
247 | skipped = set() |
|
257 | skipped = set() | |
248 | # recompute the set of skipped revs |
|
258 | # recompute the set of skipped revs | |
249 | if not collapse: |
|
259 | if not collapse: | |
250 |
seen = |
|
260 | seen = set(destmap.values()) | |
251 | for old, new in sorted(state.items()): |
|
261 | for old, new in sorted(state.items()): | |
252 | if new != revtodo and new in seen: |
|
262 | if new != revtodo and new in seen: | |
253 | skipped.add(old) |
|
263 | skipped.add(old) | |
@@ -258,7 +268,7 b' class rebaseruntime(object):' | |||||
258 | _setrebasesetvisibility(repo, set(state.keys()) | {originalwd}) |
|
268 | _setrebasesetvisibility(repo, set(state.keys()) | {originalwd}) | |
259 |
|
269 | |||
260 | self.originalwd = originalwd |
|
270 | self.originalwd = originalwd | |
261 | self.dest = dest |
|
271 | self.destmap = destmap | |
262 | self.state = state |
|
272 | self.state = state | |
263 | self.skipped = skipped |
|
273 | self.skipped = skipped | |
264 | self.collapsef = collapse |
|
274 | self.collapsef = collapse | |
@@ -267,12 +277,11 b' class rebaseruntime(object):' | |||||
267 | self.external = external |
|
277 | self.external = external | |
268 | self.activebookmark = activebookmark |
|
278 | self.activebookmark = activebookmark | |
269 |
|
279 | |||
270 |
def _handleskippingobsolete(self, |
|
280 | def _handleskippingobsolete(self, obsoleterevs, destmap): | |
271 | """Compute structures necessary for skipping obsolete revisions |
|
281 | """Compute structures necessary for skipping obsolete revisions | |
272 |
|
282 | |||
273 | rebaserevs: iterable of all revisions that are to be rebased |
|
|||
274 | obsoleterevs: iterable of all obsolete revisions in rebaseset |
|
283 | obsoleterevs: iterable of all obsolete revisions in rebaseset | |
275 |
dest: |
|
284 | destmap: {srcrev: destrev} destination revisions | |
276 | """ |
|
285 | """ | |
277 | self.obsoletenotrebased = {} |
|
286 | self.obsoletenotrebased = {} | |
278 | if not self.ui.configbool('experimental', 'rebaseskipobsolete', |
|
287 | if not self.ui.configbool('experimental', 'rebaseskipobsolete', | |
@@ -280,7 +289,7 b' class rebaseruntime(object):' | |||||
280 | return |
|
289 | return | |
281 | obsoleteset = set(obsoleterevs) |
|
290 | obsoleteset = set(obsoleterevs) | |
282 | self.obsoletenotrebased = _computeobsoletenotrebased(self.repo, |
|
291 | self.obsoletenotrebased = _computeobsoletenotrebased(self.repo, | |
283 | obsoleteset, dest) |
|
292 | obsoleteset, destmap) | |
284 | skippedset = set(self.obsoletenotrebased) |
|
293 | skippedset = set(self.obsoletenotrebased) | |
285 | _checkobsrebase(self.repo, self.ui, obsoleteset, skippedset) |
|
294 | _checkobsrebase(self.repo, self.ui, obsoleteset, skippedset) | |
286 |
|
295 | |||
@@ -300,13 +309,14 b' class rebaseruntime(object):' | |||||
300 | hint = _('use "hg rebase --abort" to clear broken state') |
|
309 | hint = _('use "hg rebase --abort" to clear broken state') | |
301 | raise error.Abort(msg, hint=hint) |
|
310 | raise error.Abort(msg, hint=hint) | |
302 | if isabort: |
|
311 | if isabort: | |
303 | return abort(self.repo, self.originalwd, self.dest, |
|
312 | return abort(self.repo, self.originalwd, self.destmap, | |
304 | self.state, activebookmark=self.activebookmark) |
|
313 | self.state, activebookmark=self.activebookmark) | |
305 |
|
314 | |||
306 |
def _preparenewrebase(self, dest |
|
315 | def _preparenewrebase(self, destmap): | |
307 |
if |
|
316 | if not destmap: | |
308 | return _nothingtorebase() |
|
317 | return _nothingtorebase() | |
309 |
|
318 | |||
|
319 | rebaseset = destmap.keys() | |||
310 | allowunstable = obsolete.isenabled(self.repo, obsolete.allowunstableopt) |
|
320 | allowunstable = obsolete.isenabled(self.repo, obsolete.allowunstableopt) | |
311 | if (not (self.keepf or allowunstable) |
|
321 | if (not (self.keepf or allowunstable) | |
312 | and self.repo.revs('first(children(%ld) - %ld)', |
|
322 | and self.repo.revs('first(children(%ld) - %ld)', | |
@@ -316,10 +326,10 b' class rebaseruntime(object):' | |||||
316 | " unrebased descendants"), |
|
326 | " unrebased descendants"), | |
317 | hint=_('use --keep to keep original changesets')) |
|
327 | hint=_('use --keep to keep original changesets')) | |
318 |
|
328 | |||
319 |
obsrevs = _filterobsoleterevs(self.repo, |
|
329 | obsrevs = _filterobsoleterevs(self.repo, rebaseset) | |
320 |
self._handleskippingobsolete( |
|
330 | self._handleskippingobsolete(obsrevs, destmap) | |
321 |
|
331 | |||
322 |
result = buildstate(self.repo, dest |
|
332 | result = buildstate(self.repo, destmap, self.collapsef, | |
323 | self.obsoletenotrebased) |
|
333 | self.obsoletenotrebased) | |
324 |
|
334 | |||
325 | if not result: |
|
335 | if not result: | |
@@ -333,12 +343,19 b' class rebaseruntime(object):' | |||||
333 | % root, |
|
343 | % root, | |
334 | hint=_("see 'hg help phases' for details")) |
|
344 | hint=_("see 'hg help phases' for details")) | |
335 |
|
345 | |||
336 | (self.originalwd, self.dest, self.state) = result |
|
346 | (self.originalwd, self.destmap, self.state) = result | |
337 | if self.collapsef: |
|
347 | if self.collapsef: | |
338 | destancestors = self.repo.changelog.ancestors([self.dest], |
|
348 | dests = set(self.destmap.values()) | |
|
349 | if len(dests) != 1: | |||
|
350 | raise error.Abort( | |||
|
351 | _('--collapse does not work with multiple destinations')) | |||
|
352 | destrev = next(iter(dests)) | |||
|
353 | destancestors = self.repo.changelog.ancestors([destrev], | |||
339 | inclusive=True) |
|
354 | inclusive=True) | |
340 | self.external = externalparent(self.repo, self.state, destancestors) |
|
355 | self.external = externalparent(self.repo, self.state, destancestors) | |
341 |
|
356 | |||
|
357 | for destrev in sorted(set(destmap.values())): | |||
|
358 | dest = self.repo[destrev] | |||
342 | if dest.closesbranch() and not self.keepbranchesf: |
|
359 | if dest.closesbranch() and not self.keepbranchesf: | |
343 | self.ui.status(_('reopening closed branch head %s\n') % dest) |
|
360 | self.ui.status(_('reopening closed branch head %s\n') % dest) | |
344 |
|
361 | |||
@@ -371,6 +388,7 b' class rebaseruntime(object):' | |||||
371 | total = len(cands) |
|
388 | total = len(cands) | |
372 | pos = 0 |
|
389 | pos = 0 | |
373 | for rev in sortedrevs: |
|
390 | for rev in sortedrevs: | |
|
391 | dest = self.destmap[rev] | |||
374 | ctx = repo[rev] |
|
392 | ctx = repo[rev] | |
375 | desc = _ctxdesc(ctx) |
|
393 | desc = _ctxdesc(ctx) | |
376 | if self.state[rev] == rev: |
|
394 | if self.state[rev] == rev: | |
@@ -380,7 +398,8 b' class rebaseruntime(object):' | |||||
380 | ui.status(_('rebasing %s\n') % desc) |
|
398 | ui.status(_('rebasing %s\n') % desc) | |
381 | ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, ctx)), |
|
399 | ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, ctx)), | |
382 | _('changesets'), total) |
|
400 | _('changesets'), total) | |
383 |
p1, p2, base = defineparents(repo, rev, self.dest, |
|
401 | p1, p2, base = defineparents(repo, rev, self.destmap, | |
|
402 | self.state) | |||
384 | self.storestatus(tr=tr) |
|
403 | self.storestatus(tr=tr) | |
385 | storecollapsemsg(repo, self.collapsemsg) |
|
404 | storecollapsemsg(repo, self.collapsemsg) | |
386 | if len(repo[None].parents()) == 2: |
|
405 | if len(repo[None].parents()) == 2: | |
@@ -390,7 +409,7 b' class rebaseruntime(object):' | |||||
390 | ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), |
|
409 | ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), | |
391 | 'rebase') |
|
410 | 'rebase') | |
392 | stats = rebasenode(repo, rev, p1, base, self.state, |
|
411 | stats = rebasenode(repo, rev, p1, base, self.state, | |
393 |
self.collapsef, |
|
412 | self.collapsef, dest) | |
394 | if stats and stats[3] > 0: |
|
413 | if stats and stats[3] > 0: | |
395 | raise error.InterventionRequired( |
|
414 | raise error.InterventionRequired( | |
396 | _('unresolved conflicts (see hg ' |
|
415 | _('unresolved conflicts (see hg ' | |
@@ -436,8 +455,8 b' class rebaseruntime(object):' | |||||
436 | def _finishrebase(self): |
|
455 | def _finishrebase(self): | |
437 | repo, ui, opts = self.repo, self.ui, self.opts |
|
456 | repo, ui, opts = self.repo, self.ui, self.opts | |
438 | if self.collapsef and not self.keepopen: |
|
457 | if self.collapsef and not self.keepopen: | |
439 | p1, p2, _base = defineparents(repo, min(self.state), |
|
458 | p1, p2, _base = defineparents(repo, min(self.state), self.destmap, | |
440 |
|
|
459 | self.state) | |
441 | editopt = opts.get('edit') |
|
460 | editopt = opts.get('edit') | |
442 | editform = 'rebase.collapse' |
|
461 | editform = 'rebase.collapse' | |
443 | if self.collapsemsg: |
|
462 | if self.collapsemsg: | |
@@ -483,7 +502,7 b' class rebaseruntime(object):' | |||||
483 | collapsedas = None |
|
502 | collapsedas = None | |
484 | if self.collapsef: |
|
503 | if self.collapsef: | |
485 | collapsedas = newnode |
|
504 | collapsedas = newnode | |
486 | clearrebased(ui, repo, self.dest, self.state, self.skipped, |
|
505 | clearrebased(ui, repo, self.destmap, self.state, self.skipped, | |
487 | collapsedas) |
|
506 | collapsedas) | |
488 |
|
507 | |||
489 | clearstatus(repo) |
|
508 | clearstatus(repo) | |
@@ -675,9 +694,9 b' def rebase(ui, repo, **opts):' | |||||
675 | if retcode is not None: |
|
694 | if retcode is not None: | |
676 | return retcode |
|
695 | return retcode | |
677 | else: |
|
696 | else: | |
678 |
dest |
|
697 | destmap = _definedestmap(ui, repo, destf, srcf, basef, revf, | |
679 |
|
|
698 | destspace=destspace) | |
680 |
retcode = rbsrt._preparenewrebase(dest |
|
699 | retcode = rbsrt._preparenewrebase(destmap) | |
681 | if retcode is not None: |
|
700 | if retcode is not None: | |
682 | return retcode |
|
701 | return retcode | |
683 |
|
702 | |||
@@ -695,10 +714,9 b' def rebase(ui, repo, **opts):' | |||||
695 |
|
714 | |||
696 | rbsrt._finishrebase() |
|
715 | rbsrt._finishrebase() | |
697 |
|
716 | |||
698 |
def _define |
|
717 | def _definedestmap(ui, repo, destf=None, srcf=None, basef=None, revf=None, | |
699 | destspace=None): |
|
718 | destspace=None): | |
700 |
"""use revisions argument to define dest |
|
719 | """use revisions argument to define destmap {srcrev: destrev}""" | |
701 | """ |
|
|||
702 | if revf is None: |
|
720 | if revf is None: | |
703 | revf = [] |
|
721 | revf = [] | |
704 |
|
722 | |||
@@ -725,12 +743,12 b' def _definesets(ui, repo, destf=None, sr' | |||||
725 | rebaseset = scmutil.revrange(repo, revf) |
|
743 | rebaseset = scmutil.revrange(repo, revf) | |
726 | if not rebaseset: |
|
744 | if not rebaseset: | |
727 | ui.status(_('empty "rev" revision set - nothing to rebase\n')) |
|
745 | ui.status(_('empty "rev" revision set - nothing to rebase\n')) | |
728 |
return None |
|
746 | return None | |
729 | elif srcf: |
|
747 | elif srcf: | |
730 | src = scmutil.revrange(repo, [srcf]) |
|
748 | src = scmutil.revrange(repo, [srcf]) | |
731 | if not src: |
|
749 | if not src: | |
732 | ui.status(_('empty "source" revision set - nothing to rebase\n')) |
|
750 | ui.status(_('empty "source" revision set - nothing to rebase\n')) | |
733 |
return None |
|
751 | return None | |
734 | rebaseset = repo.revs('(%ld)::', src) |
|
752 | rebaseset = repo.revs('(%ld)::', src) | |
735 | assert rebaseset |
|
753 | assert rebaseset | |
736 | else: |
|
754 | else: | |
@@ -738,7 +756,7 b' def _definesets(ui, repo, destf=None, sr' | |||||
738 | if not base: |
|
756 | if not base: | |
739 | ui.status(_('empty "base" revision set - ' |
|
757 | ui.status(_('empty "base" revision set - ' | |
740 | "can't compute rebase set\n")) |
|
758 | "can't compute rebase set\n")) | |
741 |
return None |
|
759 | return None | |
742 | if not destf: |
|
760 | if not destf: | |
743 | dest = repo[_destrebase(repo, base, destspace=destspace)] |
|
761 | dest = repo[_destrebase(repo, base, destspace=destspace)] | |
744 | destf = str(dest) |
|
762 | destf = str(dest) | |
@@ -782,13 +800,17 b' def _definesets(ui, repo, destf=None, sr' | |||||
782 | else: # can it happen? |
|
800 | else: # can it happen? | |
783 | ui.status(_('nothing to rebase from %s to %s\n') % |
|
801 | ui.status(_('nothing to rebase from %s to %s\n') % | |
784 | ('+'.join(str(repo[r]) for r in base), dest)) |
|
802 | ('+'.join(str(repo[r]) for r in base), dest)) | |
785 |
return None |
|
803 | return None | |
786 |
|
804 | |||
787 | if not destf: |
|
805 | if not destf: | |
788 | dest = repo[_destrebase(repo, rebaseset, destspace=destspace)] |
|
806 | dest = repo[_destrebase(repo, rebaseset, destspace=destspace)] | |
789 | destf = str(dest) |
|
807 | destf = str(dest) | |
790 |
|
808 | |||
791 | return dest, rebaseset |
|
809 | # assign dest to each rev in rebaseset | |
|
810 | destrev = dest.rev() | |||
|
811 | destmap = {r: destrev for r in rebaseset} # {srcrev: destrev} | |||
|
812 | ||||
|
813 | return destmap | |||
792 |
|
814 | |||
793 | def externalparent(repo, state, destancestors): |
|
815 | def externalparent(repo, state, destancestors): | |
794 | """Return the revision that should be used as the second parent |
|
816 | """Return the revision that should be used as the second parent | |
@@ -874,7 +896,7 b' def rebasenode(repo, rev, p1, base, stat' | |||||
874 | copies.duplicatecopies(repo, rev, p1rev, skiprev=dest) |
|
896 | copies.duplicatecopies(repo, rev, p1rev, skiprev=dest) | |
875 | return stats |
|
897 | return stats | |
876 |
|
898 | |||
877 | def adjustdest(repo, rev, dest, state): |
|
899 | def adjustdest(repo, rev, destmap, state): | |
878 | """adjust rebase destination given the current rebase state |
|
900 | """adjust rebase destination given the current rebase state | |
879 |
|
901 | |||
880 | rev is what is being rebased. Return a list of two revs, which are the |
|
902 | rev is what is being rebased. Return a list of two revs, which are the | |
@@ -914,8 +936,9 b' def adjustdest(repo, rev, dest, state):' | |||||
914 | |/ |/ |
|
936 | |/ |/ | |
915 | A A |
|
937 | A A | |
916 | """ |
|
938 | """ | |
917 | # pick already rebased revs from state |
|
939 | # pick already rebased revs with same dest from state as interesting source | |
918 | source = [s for s, d in state.items() if d > 0] |
|
940 | dest = destmap[rev] | |
|
941 | source = [s for s, d in state.items() if d > 0 and destmap[s] == dest] | |||
919 |
|
942 | |||
920 | result = [] |
|
943 | result = [] | |
921 | for prev in repo.changelog.parentrevs(rev): |
|
944 | for prev in repo.changelog.parentrevs(rev): | |
@@ -957,7 +980,7 b' def successorrevs(repo, rev):' | |||||
957 | if s in nodemap: |
|
980 | if s in nodemap: | |
958 | yield nodemap[s] |
|
981 | yield nodemap[s] | |
959 |
|
982 | |||
960 | def defineparents(repo, rev, dest, state): |
|
983 | def defineparents(repo, rev, destmap, state): | |
961 | """Return new parents and optionally a merge base for rev being rebased |
|
984 | """Return new parents and optionally a merge base for rev being rebased | |
962 |
|
985 | |||
963 | The destination specified by "dest" cannot always be used directly because |
|
986 | The destination specified by "dest" cannot always be used directly because | |
@@ -981,9 +1004,10 b' def defineparents(repo, rev, dest, state' | |||||
981 | return False |
|
1004 | return False | |
982 | return cl.isancestor(cl.node(a), cl.node(b)) |
|
1005 | return cl.isancestor(cl.node(a), cl.node(b)) | |
983 |
|
1006 | |||
|
1007 | dest = destmap[rev] | |||
984 | oldps = repo.changelog.parentrevs(rev) # old parents |
|
1008 | oldps = repo.changelog.parentrevs(rev) # old parents | |
985 | newps = [nullrev, nullrev] # new parents |
|
1009 | newps = [nullrev, nullrev] # new parents | |
986 | dests = adjustdest(repo, rev, dest, state) # adjusted destinations |
|
1010 | dests = adjustdest(repo, rev, destmap, state) # adjusted destinations | |
987 | bases = list(oldps) # merge base candidates, initially just old parents |
|
1011 | bases = list(oldps) # merge base candidates, initially just old parents | |
988 |
|
1012 | |||
989 | if all(r == nullrev for r in oldps[1:]): |
|
1013 | if all(r == nullrev for r in oldps[1:]): | |
@@ -1238,7 +1262,7 b' def needupdate(repo, state):' | |||||
1238 |
|
1262 | |||
1239 | return False |
|
1263 | return False | |
1240 |
|
1264 | |||
1241 | def abort(repo, originalwd, dest, state, activebookmark=None): |
|
1265 | def abort(repo, originalwd, destmap, state, activebookmark=None): | |
1242 | '''Restore the repository to its original state. Additional args: |
|
1266 | '''Restore the repository to its original state. Additional args: | |
1243 |
|
1267 | |||
1244 | activebookmark: the name of the bookmark that should be active after the |
|
1268 | activebookmark: the name of the bookmark that should be active after the | |
@@ -1248,7 +1272,7 b' def abort(repo, originalwd, dest, state,' | |||||
1248 | # If the first commits in the rebased set get skipped during the rebase, |
|
1272 | # If the first commits in the rebased set get skipped during the rebase, | |
1249 | # their values within the state mapping will be the dest rev id. The |
|
1273 | # their values within the state mapping will be the dest rev id. The | |
1250 | # dstates list must must not contain the dest rev (issue4896) |
|
1274 | # dstates list must must not contain the dest rev (issue4896) | |
1251 |
dstates = [s for s in state. |
|
1275 | dstates = [s for r, s in state.items() if s >= 0 and s != destmap[r]] | |
1252 | immutable = [d for d in dstates if not repo[d].mutable()] |
|
1276 | immutable = [d for d in dstates if not repo[d].mutable()] | |
1253 | cleanup = True |
|
1277 | cleanup = True | |
1254 | if immutable: |
|
1278 | if immutable: | |
@@ -1267,13 +1291,14 b' def abort(repo, originalwd, dest, state,' | |||||
1267 |
|
1291 | |||
1268 | if cleanup: |
|
1292 | if cleanup: | |
1269 | shouldupdate = False |
|
1293 | shouldupdate = False | |
1270 | rebased = filter(lambda x: x >= 0 and x != dest, state.values()) |
|
1294 | rebased = [s for r, s in state.items() | |
|
1295 | if s >= 0 and s != destmap[r]] | |||
1271 | if rebased: |
|
1296 | if rebased: | |
1272 | strippoints = [ |
|
1297 | strippoints = [ | |
1273 | c.node() for c in repo.set('roots(%ld)', rebased)] |
|
1298 | c.node() for c in repo.set('roots(%ld)', rebased)] | |
1274 |
|
1299 | |||
1275 | updateifonnodes = set(rebased) |
|
1300 | updateifonnodes = set(rebased) | |
1276 |
updateifonnodes. |
|
1301 | updateifonnodes.update(destmap.values()) | |
1277 | updateifonnodes.add(originalwd) |
|
1302 | updateifonnodes.add(originalwd) | |
1278 | shouldupdate = repo['.'].rev() in updateifonnodes |
|
1303 | shouldupdate = repo['.'].rev() in updateifonnodes | |
1279 |
|
1304 | |||
@@ -1295,21 +1320,22 b' def abort(repo, originalwd, dest, state,' | |||||
1295 | repo.ui.warn(_('rebase aborted\n')) |
|
1320 | repo.ui.warn(_('rebase aborted\n')) | |
1296 | return 0 |
|
1321 | return 0 | |
1297 |
|
1322 | |||
1298 |
def buildstate(repo, dest |
|
1323 | def buildstate(repo, destmap, collapse, obsoletenotrebased): | |
1299 | '''Define which revisions are going to be rebased and where |
|
1324 | '''Define which revisions are going to be rebased and where | |
1300 |
|
1325 | |||
1301 | repo: repo |
|
1326 | repo: repo | |
1302 | dest: context |
|
1327 | destmap: {srcrev: destrev} | |
1303 | rebaseset: set of rev |
|
|||
1304 | ''' |
|
1328 | ''' | |
|
1329 | rebaseset = destmap.keys() | |||
1305 | originalwd = repo['.'].rev() |
|
1330 | originalwd = repo['.'].rev() | |
1306 | _setrebasesetvisibility(repo, set(rebaseset) | {originalwd}) |
|
1331 | _setrebasesetvisibility(repo, set(rebaseset) | {originalwd}) | |
1307 |
|
1332 | |||
1308 | # This check isn't strictly necessary, since mq detects commits over an |
|
1333 | # This check isn't strictly necessary, since mq detects commits over an | |
1309 | # applied patch. But it prevents messing up the working directory when |
|
1334 | # applied patch. But it prevents messing up the working directory when | |
1310 | # a partially completed rebase is blocked by mq. |
|
1335 | # a partially completed rebase is blocked by mq. | |
1311 |
if 'qtip' in repo.tags() |
|
1336 | if 'qtip' in repo.tags(): | |
1312 |
|
|
1337 | mqapplied = set(repo[s.node].rev() for s in repo.mq.applied) | |
|
1338 | if set(destmap.values()) & mqapplied: | |||
1313 | raise error.Abort(_('cannot rebase onto an applied mq patch')) |
|
1339 | raise error.Abort(_('cannot rebase onto an applied mq patch')) | |
1314 |
|
1340 | |||
1315 | roots = list(repo.set('roots(%ld)', rebaseset)) |
|
1341 | roots = list(repo.set('roots(%ld)', rebaseset)) | |
@@ -1319,6 +1345,7 b' def buildstate(repo, dest, rebaseset, co' | |||||
1319 | state = dict.fromkeys(rebaseset, revtodo) |
|
1345 | state = dict.fromkeys(rebaseset, revtodo) | |
1320 | emptyrebase = True |
|
1346 | emptyrebase = True | |
1321 | for root in roots: |
|
1347 | for root in roots: | |
|
1348 | dest = repo[destmap[root.rev()]] | |||
1322 | commonbase = root.ancestor(dest) |
|
1349 | commonbase = root.ancestor(dest) | |
1323 | if commonbase == root: |
|
1350 | if commonbase == root: | |
1324 | raise error.Abort(_('source is ancestor of destination')) |
|
1351 | raise error.Abort(_('source is ancestor of destination')) | |
@@ -1352,6 +1379,7 b' def buildstate(repo, dest, rebaseset, co' | |||||
1352 | if succ is None: |
|
1379 | if succ is None: | |
1353 | msg = _('note: not rebasing %s, it has no successor\n') % desc |
|
1380 | msg = _('note: not rebasing %s, it has no successor\n') % desc | |
1354 | del state[r] |
|
1381 | del state[r] | |
|
1382 | del destmap[r] | |||
1355 | else: |
|
1383 | else: | |
1356 | destctx = unfi[succ] |
|
1384 | destctx = unfi[succ] | |
1357 | destdesc = '%d:%s "%s"' % (destctx.rev(), destctx, |
|
1385 | destdesc = '%d:%s "%s"' % (destctx.rev(), destctx, | |
@@ -1359,10 +1387,11 b' def buildstate(repo, dest, rebaseset, co' | |||||
1359 | msg = (_('note: not rebasing %s, already in destination as %s\n') |
|
1387 | msg = (_('note: not rebasing %s, already in destination as %s\n') | |
1360 | % (desc, destdesc)) |
|
1388 | % (desc, destdesc)) | |
1361 | del state[r] |
|
1389 | del state[r] | |
|
1390 | del destmap[r] | |||
1362 | repo.ui.status(msg) |
|
1391 | repo.ui.status(msg) | |
1363 |
return originalwd, dest |
|
1392 | return originalwd, destmap, state | |
1364 |
|
1393 | |||
1365 | def clearrebased(ui, repo, dest, state, skipped, collapsedas=None): |
|
1394 | def clearrebased(ui, repo, destmap, state, skipped, collapsedas=None): | |
1366 | """dispose of rebased revision at the end of the rebase |
|
1395 | """dispose of rebased revision at the end of the rebase | |
1367 |
|
1396 | |||
1368 | If `collapsedas` is not None, the rebase was a collapse whose result if the |
|
1397 | If `collapsedas` is not None, the rebase was a collapse whose result if the | |
@@ -1371,7 +1400,7 b' def clearrebased(ui, repo, dest, state, ' | |||||
1371 | # Move bookmark of skipped nodes to destination. This cannot be handled |
|
1400 | # Move bookmark of skipped nodes to destination. This cannot be handled | |
1372 | # by scmutil.cleanupnodes since it will treat rev as removed (no successor) |
|
1401 | # by scmutil.cleanupnodes since it will treat rev as removed (no successor) | |
1373 | # and move bookmark backwards. |
|
1402 | # and move bookmark backwards. | |
1374 | bmchanges = [(name, tonode(max(adjustdest(repo, rev, dest, state)))) |
|
1403 | bmchanges = [(name, tonode(max(adjustdest(repo, rev, destmap, state)))) | |
1375 | for rev in skipped |
|
1404 | for rev in skipped | |
1376 | for name in repo.nodebookmarks(tonode(rev))] |
|
1405 | for name in repo.nodebookmarks(tonode(rev))] | |
1377 | if bmchanges: |
|
1406 | if bmchanges: | |
@@ -1477,7 +1506,7 b' def _filterobsoleterevs(repo, revs):' | |||||
1477 | """returns a set of the obsolete revisions in revs""" |
|
1506 | """returns a set of the obsolete revisions in revs""" | |
1478 | return set(r for r in revs if repo[r].obsolete()) |
|
1507 | return set(r for r in revs if repo[r].obsolete()) | |
1479 |
|
1508 | |||
1480 | def _computeobsoletenotrebased(repo, rebaseobsrevs, dest): |
|
1509 | def _computeobsoletenotrebased(repo, rebaseobsrevs, destmap): | |
1481 | """return a mapping obsolete => successor for all obsolete nodes to be |
|
1510 | """return a mapping obsolete => successor for all obsolete nodes to be | |
1482 | rebased that have a successors in the destination |
|
1511 | rebased that have a successors in the destination | |
1483 |
|
1512 | |||
@@ -1486,9 +1515,9 b' def _computeobsoletenotrebased(repo, reb' | |||||
1486 |
|
1515 | |||
1487 | cl = repo.unfiltered().changelog |
|
1516 | cl = repo.unfiltered().changelog | |
1488 | nodemap = cl.nodemap |
|
1517 | nodemap = cl.nodemap | |
1489 | destnode = cl.node(dest) |
|
|||
1490 | for srcrev in rebaseobsrevs: |
|
1518 | for srcrev in rebaseobsrevs: | |
1491 | srcnode = cl.node(srcrev) |
|
1519 | srcnode = cl.node(srcrev) | |
|
1520 | destnode = cl.node(destmap[srcrev]) | |||
1492 | # XXX: more advanced APIs are required to handle split correctly |
|
1521 | # XXX: more advanced APIs are required to handle split correctly | |
1493 | successors = list(obsutil.allsuccessors(repo.obsstore, [srcnode])) |
|
1522 | successors = list(obsutil.allsuccessors(repo.obsstore, [srcnode])) | |
1494 | if len(successors) == 1: |
|
1523 | if len(successors) == 1: |
General Comments 0
You need to be logged in to leave comments.
Login now