##// END OF EJS Templates
rebase: skip extinct revisions even if it has no successor in rebase set...
Martin von Zweigbergk -
r39364:94a49806 default
parent child Browse files
Show More
@@ -1,1934 +1,1934 b''
1 1 # rebase.py - rebasing feature for mercurial
2 2 #
3 3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 '''command to move sets of revisions to a different ancestor
9 9
10 10 This extension lets you rebase changesets in an existing Mercurial
11 11 repository.
12 12
13 13 For more information:
14 14 https://mercurial-scm.org/wiki/RebaseExtension
15 15 '''
16 16
17 17 from __future__ import absolute_import
18 18
19 19 import errno
20 20 import os
21 21
22 22 from mercurial.i18n import _
23 23 from mercurial.node import (
24 24 nullrev,
25 25 short,
26 26 )
27 27 from mercurial import (
28 28 bookmarks,
29 29 cmdutil,
30 30 commands,
31 31 copies,
32 32 destutil,
33 33 dirstateguard,
34 34 error,
35 35 extensions,
36 36 hg,
37 37 merge as mergemod,
38 38 mergeutil,
39 39 obsolete,
40 40 obsutil,
41 41 patch,
42 42 phases,
43 43 pycompat,
44 44 registrar,
45 45 repair,
46 46 revset,
47 47 revsetlang,
48 48 scmutil,
49 49 smartset,
50 50 state as statemod,
51 51 util,
52 52 )
53 53
54 54 # The following constants are used throughout the rebase module. The ordering of
55 55 # their values must be maintained.
56 56
57 57 # Indicates that a revision needs to be rebased
58 58 revtodo = -1
59 59 revtodostr = '-1'
60 60
61 61 # legacy revstates no longer needed in current code
62 62 # -2: nullmerge, -3: revignored, -4: revprecursor, -5: revpruned
63 63 legacystates = {'-2', '-3', '-4', '-5'}
64 64
65 65 cmdtable = {}
66 66 command = registrar.command(cmdtable)
67 67 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
68 68 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
69 69 # be specifying the version(s) of Mercurial they are tested with, or
70 70 # leave the attribute unspecified.
71 71 testedwith = 'ships-with-hg-core'
72 72
73 73 def _nothingtorebase():
74 74 return 1
75 75
76 76 def _savegraft(ctx, extra):
77 77 s = ctx.extra().get('source', None)
78 78 if s is not None:
79 79 extra['source'] = s
80 80 s = ctx.extra().get('intermediate-source', None)
81 81 if s is not None:
82 82 extra['intermediate-source'] = s
83 83
84 84 def _savebranch(ctx, extra):
85 85 extra['branch'] = ctx.branch()
86 86
87 87 def _destrebase(repo, sourceset, destspace=None):
88 88 """small wrapper around destmerge to pass the right extra args
89 89
90 90 Please wrap destutil.destmerge instead."""
91 91 return destutil.destmerge(repo, action='rebase', sourceset=sourceset,
92 92 onheadcheck=False, destspace=destspace)
93 93
94 94 revsetpredicate = registrar.revsetpredicate()
95 95
96 96 @revsetpredicate('_destrebase')
97 97 def _revsetdestrebase(repo, subset, x):
98 98 # ``_rebasedefaultdest()``
99 99
100 100 # default destination for rebase.
101 101 # # XXX: Currently private because I expect the signature to change.
102 102 # # XXX: - bailing out in case of ambiguity vs returning all data.
103 103 # i18n: "_rebasedefaultdest" is a keyword
104 104 sourceset = None
105 105 if x is not None:
106 106 sourceset = revset.getset(repo, smartset.fullreposet(repo), x)
107 107 return subset & smartset.baseset([_destrebase(repo, sourceset)])
108 108
109 109 @revsetpredicate('_destautoorphanrebase')
110 110 def _revsetdestautoorphanrebase(repo, subset, x):
111 111 """automatic rebase destination for a single orphan revision"""
112 112 unfi = repo.unfiltered()
113 113 obsoleted = unfi.revs('obsolete()')
114 114
115 115 src = revset.getset(repo, subset, x).first()
116 116
117 117 # Empty src or already obsoleted - Do not return a destination
118 118 if not src or src in obsoleted:
119 119 return smartset.baseset()
120 120 dests = destutil.orphanpossibledestination(repo, src)
121 121 if len(dests) > 1:
122 122 raise error.Abort(
123 123 _("ambiguous automatic rebase: %r could end up on any of %r") % (
124 124 src, dests))
125 125 # We have zero or one destination, so we can just return here.
126 126 return smartset.baseset(dests)
127 127
128 128 def _ctxdesc(ctx):
129 129 """short description for a context"""
130 130 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
131 131 ctx.description().split('\n', 1)[0])
132 132 repo = ctx.repo()
133 133 names = []
134 134 for nsname, ns in repo.names.iteritems():
135 135 if nsname == 'branches':
136 136 continue
137 137 names.extend(ns.names(repo, ctx.node()))
138 138 if names:
139 139 desc += ' (%s)' % ' '.join(names)
140 140 return desc
141 141
142 142 class rebaseruntime(object):
143 143 """This class is a container for rebase runtime state"""
144 144 def __init__(self, repo, ui, inmemory=False, opts=None):
145 145 if opts is None:
146 146 opts = {}
147 147
148 148 # prepared: whether we have rebasestate prepared or not. Currently it
149 149 # decides whether "self.repo" is unfiltered or not.
150 150 # The rebasestate has explicit hash to hash instructions not depending
151 151 # on visibility. If rebasestate exists (in-memory or on-disk), use
152 152 # unfiltered repo to avoid visibility issues.
153 153 # Before knowing rebasestate (i.e. when starting a new rebase (not
154 154 # --continue or --abort)), the original repo should be used so
155 155 # visibility-dependent revsets are correct.
156 156 self.prepared = False
157 157 self._repo = repo
158 158
159 159 self.ui = ui
160 160 self.opts = opts
161 161 self.originalwd = None
162 162 self.external = nullrev
163 163 # Mapping between the old revision id and either what is the new rebased
164 164 # revision or what needs to be done with the old revision. The state
165 165 # dict will be what contains most of the rebase progress state.
166 166 self.state = {}
167 167 self.activebookmark = None
168 168 self.destmap = {}
169 169 self.skipped = set()
170 170
171 171 self.collapsef = opts.get('collapse', False)
172 172 self.collapsemsg = cmdutil.logmessage(ui, opts)
173 173 self.date = opts.get('date', None)
174 174
175 175 e = opts.get('extrafn') # internal, used by e.g. hgsubversion
176 176 self.extrafns = [_savegraft]
177 177 if e:
178 178 self.extrafns = [e]
179 179
180 180 self.backupf = ui.configbool('ui', 'history-editing-backup')
181 181 self.keepf = opts.get('keep', False)
182 182 self.keepbranchesf = opts.get('keepbranches', False)
183 183 self.obsoletenotrebased = {}
184 184 self.obsoletewithoutsuccessorindestination = set()
185 185 self.inmemory = inmemory
186 186 self.stateobj = statemod.cmdstate(repo, 'rebasestate')
187 187
188 188 @property
189 189 def repo(self):
190 190 if self.prepared:
191 191 return self._repo.unfiltered()
192 192 else:
193 193 return self._repo
194 194
195 195 def storestatus(self, tr=None):
196 196 """Store the current status to allow recovery"""
197 197 if tr:
198 198 tr.addfilegenerator('rebasestate', ('rebasestate',),
199 199 self._writestatus, location='plain')
200 200 else:
201 201 with self.repo.vfs("rebasestate", "w") as f:
202 202 self._writestatus(f)
203 203
204 204 def _writestatus(self, f):
205 205 repo = self.repo
206 206 assert repo.filtername is None
207 207 f.write(repo[self.originalwd].hex() + '\n')
208 208 # was "dest". we now write dest per src root below.
209 209 f.write('\n')
210 210 f.write(repo[self.external].hex() + '\n')
211 211 f.write('%d\n' % int(self.collapsef))
212 212 f.write('%d\n' % int(self.keepf))
213 213 f.write('%d\n' % int(self.keepbranchesf))
214 214 f.write('%s\n' % (self.activebookmark or ''))
215 215 destmap = self.destmap
216 216 for d, v in self.state.iteritems():
217 217 oldrev = repo[d].hex()
218 218 if v >= 0:
219 219 newrev = repo[v].hex()
220 220 else:
221 221 newrev = "%d" % v
222 222 destnode = repo[destmap[d]].hex()
223 223 f.write("%s:%s:%s\n" % (oldrev, newrev, destnode))
224 224 repo.ui.debug('rebase status stored\n')
225 225
226 226 def restorestatus(self):
227 227 """Restore a previously stored status"""
228 228 if not self.stateobj.exists():
229 229 cmdutil.wrongtooltocontinue(self.repo, _('rebase'))
230 230
231 231 data = self._read()
232 232 self.repo.ui.debug('rebase status resumed\n')
233 233
234 234 self.originalwd = data['originalwd']
235 235 self.destmap = data['destmap']
236 236 self.state = data['state']
237 237 self.skipped = data['skipped']
238 238 self.collapsef = data['collapse']
239 239 self.keepf = data['keep']
240 240 self.keepbranchesf = data['keepbranches']
241 241 self.external = data['external']
242 242 self.activebookmark = data['activebookmark']
243 243
244 244 def _read(self):
245 245 self.prepared = True
246 246 repo = self.repo
247 247 assert repo.filtername is None
248 248 data = {'keepbranches': None, 'collapse': None, 'activebookmark': None,
249 249 'external': nullrev, 'keep': None, 'originalwd': None}
250 250 legacydest = None
251 251 state = {}
252 252 destmap = {}
253 253
254 254 if True:
255 255 f = repo.vfs("rebasestate")
256 256 for i, l in enumerate(f.read().splitlines()):
257 257 if i == 0:
258 258 data['originalwd'] = repo[l].rev()
259 259 elif i == 1:
260 260 # this line should be empty in newer version. but legacy
261 261 # clients may still use it
262 262 if l:
263 263 legacydest = repo[l].rev()
264 264 elif i == 2:
265 265 data['external'] = repo[l].rev()
266 266 elif i == 3:
267 267 data['collapse'] = bool(int(l))
268 268 elif i == 4:
269 269 data['keep'] = bool(int(l))
270 270 elif i == 5:
271 271 data['keepbranches'] = bool(int(l))
272 272 elif i == 6 and not (len(l) == 81 and ':' in l):
273 273 # line 6 is a recent addition, so for backwards
274 274 # compatibility check that the line doesn't look like the
275 275 # oldrev:newrev lines
276 276 data['activebookmark'] = l
277 277 else:
278 278 args = l.split(':')
279 279 oldrev = repo[args[0]].rev()
280 280 newrev = args[1]
281 281 if newrev in legacystates:
282 282 continue
283 283 if len(args) > 2:
284 284 destrev = repo[args[2]].rev()
285 285 else:
286 286 destrev = legacydest
287 287 destmap[oldrev] = destrev
288 288 if newrev == revtodostr:
289 289 state[oldrev] = revtodo
290 290 # Legacy compat special case
291 291 else:
292 292 state[oldrev] = repo[newrev].rev()
293 293
294 294 if data['keepbranches'] is None:
295 295 raise error.Abort(_('.hg/rebasestate is incomplete'))
296 296
297 297 data['destmap'] = destmap
298 298 data['state'] = state
299 299 skipped = set()
300 300 # recompute the set of skipped revs
301 301 if not data['collapse']:
302 302 seen = set(destmap.values())
303 303 for old, new in sorted(state.items()):
304 304 if new != revtodo and new in seen:
305 305 skipped.add(old)
306 306 seen.add(new)
307 307 data['skipped'] = skipped
308 308 repo.ui.debug('computed skipped revs: %s\n' %
309 309 (' '.join('%d' % r for r in sorted(skipped)) or ''))
310 310
311 311 return data
312 312
313 313 def _handleskippingobsolete(self, obsoleterevs, destmap):
314 314 """Compute structures necessary for skipping obsolete revisions
315 315
316 316 obsoleterevs: iterable of all obsolete revisions in rebaseset
317 317 destmap: {srcrev: destrev} destination revisions
318 318 """
319 319 self.obsoletenotrebased = {}
320 320 if not self.ui.configbool('experimental', 'rebaseskipobsolete'):
321 321 return
322 322 obsoleteset = set(obsoleterevs)
323 323 (self.obsoletenotrebased,
324 324 self.obsoletewithoutsuccessorindestination,
325 325 obsoleteextinctsuccessors) = _computeobsoletenotrebased(
326 326 self.repo, obsoleteset, destmap)
327 327 skippedset = set(self.obsoletenotrebased)
328 328 skippedset.update(self.obsoletewithoutsuccessorindestination)
329 329 skippedset.update(obsoleteextinctsuccessors)
330 330 _checkobsrebase(self.repo, self.ui, obsoleteset, skippedset)
331 331
332 332 def _prepareabortorcontinue(self, isabort, backup=True, suppwarns=False):
333 333 try:
334 334 self.restorestatus()
335 335 self.collapsemsg = restorecollapsemsg(self.repo, isabort)
336 336 except error.RepoLookupError:
337 337 if isabort:
338 338 clearstatus(self.repo)
339 339 clearcollapsemsg(self.repo)
340 340 self.repo.ui.warn(_('rebase aborted (no revision is removed,'
341 341 ' only broken state is cleared)\n'))
342 342 return 0
343 343 else:
344 344 msg = _('cannot continue inconsistent rebase')
345 345 hint = _('use "hg rebase --abort" to clear broken state')
346 346 raise error.Abort(msg, hint=hint)
347 347
348 348 if isabort:
349 349 backup = backup and self.backupf
350 350 return abort(self.repo, self.originalwd, self.destmap, self.state,
351 351 activebookmark=self.activebookmark, backup=backup,
352 352 suppwarns=suppwarns)
353 353
354 354 def _preparenewrebase(self, destmap):
355 355 if not destmap:
356 356 return _nothingtorebase()
357 357
358 358 rebaseset = destmap.keys()
359 359 allowunstable = obsolete.isenabled(self.repo, obsolete.allowunstableopt)
360 360 if (not (self.keepf or allowunstable)
361 361 and self.repo.revs('first(children(%ld) - %ld)',
362 362 rebaseset, rebaseset)):
363 363 raise error.Abort(
364 364 _("can't remove original changesets with"
365 365 " unrebased descendants"),
366 366 hint=_('use --keep to keep original changesets'))
367 367
368 368 result = buildstate(self.repo, destmap, self.collapsef)
369 369
370 370 if not result:
371 371 # Empty state built, nothing to rebase
372 372 self.ui.status(_('nothing to rebase\n'))
373 373 return _nothingtorebase()
374 374
375 375 for root in self.repo.set('roots(%ld)', rebaseset):
376 376 if not self.keepf and not root.mutable():
377 377 raise error.Abort(_("can't rebase public changeset %s")
378 378 % root,
379 379 hint=_("see 'hg help phases' for details"))
380 380
381 381 (self.originalwd, self.destmap, self.state) = result
382 382 if self.collapsef:
383 383 dests = set(self.destmap.values())
384 384 if len(dests) != 1:
385 385 raise error.Abort(
386 386 _('--collapse does not work with multiple destinations'))
387 387 destrev = next(iter(dests))
388 388 destancestors = self.repo.changelog.ancestors([destrev],
389 389 inclusive=True)
390 390 self.external = externalparent(self.repo, self.state, destancestors)
391 391
392 392 for destrev in sorted(set(destmap.values())):
393 393 dest = self.repo[destrev]
394 394 if dest.closesbranch() and not self.keepbranchesf:
395 395 self.ui.status(_('reopening closed branch head %s\n') % dest)
396 396
397 397 self.prepared = True
398 398
399 399 def _assignworkingcopy(self):
400 400 if self.inmemory:
401 401 from mercurial.context import overlayworkingctx
402 402 self.wctx = overlayworkingctx(self.repo)
403 403 self.repo.ui.debug("rebasing in-memory\n")
404 404 else:
405 405 self.wctx = self.repo[None]
406 406 self.repo.ui.debug("rebasing on disk\n")
407 407 self.repo.ui.log("rebase", "", rebase_imm_used=self.inmemory)
408 408
409 409 def _performrebase(self, tr):
410 410 self._assignworkingcopy()
411 411 repo, ui = self.repo, self.ui
412 412 if self.keepbranchesf:
413 413 # insert _savebranch at the start of extrafns so if
414 414 # there's a user-provided extrafn it can clobber branch if
415 415 # desired
416 416 self.extrafns.insert(0, _savebranch)
417 417 if self.collapsef:
418 418 branches = set()
419 419 for rev in self.state:
420 420 branches.add(repo[rev].branch())
421 421 if len(branches) > 1:
422 422 raise error.Abort(_('cannot collapse multiple named '
423 423 'branches'))
424 424
425 425 # Calculate self.obsoletenotrebased
426 426 obsrevs = _filterobsoleterevs(self.repo, self.state)
427 427 self._handleskippingobsolete(obsrevs, self.destmap)
428 428
429 429 # Keep track of the active bookmarks in order to reset them later
430 430 self.activebookmark = self.activebookmark or repo._activebookmark
431 431 if self.activebookmark:
432 432 bookmarks.deactivate(repo)
433 433
434 434 # Store the state before we begin so users can run 'hg rebase --abort'
435 435 # if we fail before the transaction closes.
436 436 self.storestatus()
437 437 if tr:
438 438 # When using single transaction, store state when transaction
439 439 # commits.
440 440 self.storestatus(tr)
441 441
442 442 cands = [k for k, v in self.state.iteritems() if v == revtodo]
443 443 p = repo.ui.makeprogress(_("rebasing"), unit=_('changesets'),
444 444 total=len(cands))
445 445 def progress(ctx):
446 446 p.increment(item=("%d:%s" % (ctx.rev(), ctx)))
447 447 allowdivergence = self.ui.configbool(
448 448 'experimental', 'evolution.allowdivergence')
449 449 for subset in sortsource(self.destmap):
450 450 sortedrevs = self.repo.revs('sort(%ld, -topo)', subset)
451 451 if not allowdivergence:
452 452 sortedrevs -= self.repo.revs(
453 453 'descendants(%ld) and not %ld',
454 454 self.obsoletewithoutsuccessorindestination,
455 455 self.obsoletewithoutsuccessorindestination,
456 456 )
457 457 for rev in sortedrevs:
458 458 self._rebasenode(tr, rev, allowdivergence, progress)
459 459 p.complete()
460 460 ui.note(_('rebase merging completed\n'))
461 461
462 462 def _concludenode(self, rev, p1, p2, editor, commitmsg=None):
463 463 '''Commit the wd changes with parents p1 and p2.
464 464
465 465 Reuse commit info from rev but also store useful information in extra.
466 466 Return node of committed revision.'''
467 467 repo = self.repo
468 468 ctx = repo[rev]
469 469 if commitmsg is None:
470 470 commitmsg = ctx.description()
471 471 date = self.date
472 472 if date is None:
473 473 date = ctx.date()
474 474 extra = {'rebase_source': ctx.hex()}
475 475 for c in self.extrafns:
476 476 c(ctx, extra)
477 477 keepbranch = self.keepbranchesf and repo[p1].branch() != ctx.branch()
478 478 destphase = max(ctx.phase(), phases.draft)
479 479 overrides = {('phases', 'new-commit'): destphase}
480 480 if keepbranch:
481 481 overrides[('ui', 'allowemptycommit')] = True
482 482 with repo.ui.configoverride(overrides, 'rebase'):
483 483 if self.inmemory:
484 484 newnode = commitmemorynode(repo, p1, p2,
485 485 wctx=self.wctx,
486 486 extra=extra,
487 487 commitmsg=commitmsg,
488 488 editor=editor,
489 489 user=ctx.user(),
490 490 date=date)
491 491 mergemod.mergestate.clean(repo)
492 492 else:
493 493 newnode = commitnode(repo, p1, p2,
494 494 extra=extra,
495 495 commitmsg=commitmsg,
496 496 editor=editor,
497 497 user=ctx.user(),
498 498 date=date)
499 499
500 500 if newnode is None:
501 501 # If it ended up being a no-op commit, then the normal
502 502 # merge state clean-up path doesn't happen, so do it
503 503 # here. Fix issue5494
504 504 mergemod.mergestate.clean(repo)
505 505 return newnode
506 506
507 507 def _rebasenode(self, tr, rev, allowdivergence, progressfn):
508 508 repo, ui, opts = self.repo, self.ui, self.opts
509 509 dest = self.destmap[rev]
510 510 ctx = repo[rev]
511 511 desc = _ctxdesc(ctx)
512 512 if self.state[rev] == rev:
513 513 ui.status(_('already rebased %s\n') % desc)
514 514 elif (not allowdivergence
515 515 and rev in self.obsoletewithoutsuccessorindestination):
516 516 msg = _('note: not rebasing %s and its descendants as '
517 517 'this would cause divergence\n') % desc
518 518 repo.ui.status(msg)
519 519 self.skipped.add(rev)
520 520 elif rev in self.obsoletenotrebased:
521 521 succ = self.obsoletenotrebased[rev]
522 522 if succ is None:
523 523 msg = _('note: not rebasing %s, it has no '
524 524 'successor\n') % desc
525 525 else:
526 526 succdesc = _ctxdesc(repo[succ])
527 527 msg = (_('note: not rebasing %s, already in '
528 528 'destination as %s\n') % (desc, succdesc))
529 529 repo.ui.status(msg)
530 530 # Make clearrebased aware state[rev] is not a true successor
531 531 self.skipped.add(rev)
532 532 # Record rev as moved to its desired destination in self.state.
533 533 # This helps bookmark and working parent movement.
534 534 dest = max(adjustdest(repo, rev, self.destmap, self.state,
535 535 self.skipped))
536 536 self.state[rev] = dest
537 537 elif self.state[rev] == revtodo:
538 538 ui.status(_('rebasing %s\n') % desc)
539 539 progressfn(ctx)
540 540 p1, p2, base = defineparents(repo, rev, self.destmap,
541 541 self.state, self.skipped,
542 542 self.obsoletenotrebased)
543 543 if len(repo[None].parents()) == 2:
544 544 repo.ui.debug('resuming interrupted rebase\n')
545 545 else:
546 546 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
547 547 with ui.configoverride(overrides, 'rebase'):
548 548 stats = rebasenode(repo, rev, p1, base, self.collapsef,
549 549 dest, wctx=self.wctx)
550 550 if stats.unresolvedcount > 0:
551 551 if self.inmemory:
552 552 raise error.InMemoryMergeConflictsError()
553 553 else:
554 554 raise error.InterventionRequired(
555 555 _('unresolved conflicts (see hg '
556 556 'resolve, then hg rebase --continue)'))
557 557 if not self.collapsef:
558 558 merging = p2 != nullrev
559 559 editform = cmdutil.mergeeditform(merging, 'rebase')
560 560 editor = cmdutil.getcommiteditor(editform=editform,
561 561 **pycompat.strkwargs(opts))
562 562 newnode = self._concludenode(rev, p1, p2, editor)
563 563 else:
564 564 # Skip commit if we are collapsing
565 565 if self.inmemory:
566 566 self.wctx.setbase(repo[p1])
567 567 else:
568 568 repo.setparents(repo[p1].node())
569 569 newnode = None
570 570 # Update the state
571 571 if newnode is not None:
572 572 self.state[rev] = repo[newnode].rev()
573 573 ui.debug('rebased as %s\n' % short(newnode))
574 574 else:
575 575 if not self.collapsef:
576 576 ui.warn(_('note: rebase of %d:%s created no changes '
577 577 'to commit\n') % (rev, ctx))
578 578 self.skipped.add(rev)
579 579 self.state[rev] = p1
580 580 ui.debug('next revision set to %d\n' % p1)
581 581 else:
582 582 ui.status(_('already rebased %s as %s\n') %
583 583 (desc, repo[self.state[rev]]))
584 584 if not tr:
585 585 # When not using single transaction, store state after each
586 586 # commit is completely done. On InterventionRequired, we thus
587 587 # won't store the status. Instead, we'll hit the "len(parents) == 2"
588 588 # case and realize that the commit was in progress.
589 589 self.storestatus()
590 590
591 591 def _finishrebase(self):
592 592 repo, ui, opts = self.repo, self.ui, self.opts
593 593 fm = ui.formatter('rebase', opts)
594 594 fm.startitem()
595 595 if self.collapsef:
596 596 p1, p2, _base = defineparents(repo, min(self.state), self.destmap,
597 597 self.state, self.skipped,
598 598 self.obsoletenotrebased)
599 599 editopt = opts.get('edit')
600 600 editform = 'rebase.collapse'
601 601 if self.collapsemsg:
602 602 commitmsg = self.collapsemsg
603 603 else:
604 604 commitmsg = 'Collapsed revision'
605 605 for rebased in sorted(self.state):
606 606 if rebased not in self.skipped:
607 607 commitmsg += '\n* %s' % repo[rebased].description()
608 608 editopt = True
609 609 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
610 610 revtoreuse = max(self.state)
611 611
612 612 newnode = self._concludenode(revtoreuse, p1, self.external,
613 613 editor, commitmsg=commitmsg)
614 614
615 615 if newnode is not None:
616 616 newrev = repo[newnode].rev()
617 617 for oldrev in self.state:
618 618 self.state[oldrev] = newrev
619 619
620 620 if 'qtip' in repo.tags():
621 621 updatemq(repo, self.state, self.skipped,
622 622 **pycompat.strkwargs(opts))
623 623
624 624 # restore original working directory
625 625 # (we do this before stripping)
626 626 newwd = self.state.get(self.originalwd, self.originalwd)
627 627 if newwd < 0:
628 628 # original directory is a parent of rebase set root or ignored
629 629 newwd = self.originalwd
630 630 if newwd not in [c.rev() for c in repo[None].parents()]:
631 631 ui.note(_("update back to initial working directory parent\n"))
632 632 hg.updaterepo(repo, newwd, overwrite=False)
633 633
634 634 collapsedas = None
635 635 if self.collapsef and not self.keepf:
636 636 collapsedas = newnode
637 637 clearrebased(ui, repo, self.destmap, self.state, self.skipped,
638 638 collapsedas, self.keepf, fm=fm, backup=self.backupf)
639 639
640 640 clearstatus(repo)
641 641 clearcollapsemsg(repo)
642 642
643 643 ui.note(_("rebase completed\n"))
644 644 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
645 645 if self.skipped:
646 646 skippedlen = len(self.skipped)
647 647 ui.note(_("%d revisions have been skipped\n") % skippedlen)
648 648 fm.end()
649 649
650 650 if (self.activebookmark and self.activebookmark in repo._bookmarks and
651 651 repo['.'].node() == repo._bookmarks[self.activebookmark]):
652 652 bookmarks.activate(repo, self.activebookmark)
653 653
654 654 @command('rebase',
655 655 [('s', 'source', '',
656 656 _('rebase the specified changeset and descendants'), _('REV')),
657 657 ('b', 'base', '',
658 658 _('rebase everything from branching point of specified changeset'),
659 659 _('REV')),
660 660 ('r', 'rev', [],
661 661 _('rebase these revisions'),
662 662 _('REV')),
663 663 ('d', 'dest', '',
664 664 _('rebase onto the specified changeset'), _('REV')),
665 665 ('', 'collapse', False, _('collapse the rebased changesets')),
666 666 ('m', 'message', '',
667 667 _('use text as collapse commit message'), _('TEXT')),
668 668 ('e', 'edit', False, _('invoke editor on commit messages')),
669 669 ('l', 'logfile', '',
670 670 _('read collapse commit message from file'), _('FILE')),
671 671 ('k', 'keep', False, _('keep original changesets')),
672 672 ('', 'keepbranches', False, _('keep original branch names')),
673 673 ('D', 'detach', False, _('(DEPRECATED)')),
674 674 ('i', 'interactive', False, _('(DEPRECATED)')),
675 675 ('t', 'tool', '', _('specify merge tool')),
676 676 ('', 'stop', False, _('stop interrupted rebase')),
677 677 ('c', 'continue', False, _('continue an interrupted rebase')),
678 678 ('a', 'abort', False, _('abort an interrupted rebase')),
679 679 ('', 'auto-orphans', '', _('automatically rebase orphan revisions '
680 680 'in the specified revset (EXPERIMENTAL)')),
681 681 ] + cmdutil.dryrunopts + cmdutil.formatteropts + cmdutil.confirmopts,
682 682 _('[-s REV | -b REV] [-d REV] [OPTION]'))
683 683 def rebase(ui, repo, **opts):
684 684 """move changeset (and descendants) to a different branch
685 685
686 686 Rebase uses repeated merging to graft changesets from one part of
687 687 history (the source) onto another (the destination). This can be
688 688 useful for linearizing *local* changes relative to a master
689 689 development tree.
690 690
691 691 Published commits cannot be rebased (see :hg:`help phases`).
692 692 To copy commits, see :hg:`help graft`.
693 693
694 694 If you don't specify a destination changeset (``-d/--dest``), rebase
695 695 will use the same logic as :hg:`merge` to pick a destination. if
696 696 the current branch contains exactly one other head, the other head
697 697 is merged with by default. Otherwise, an explicit revision with
698 698 which to merge with must be provided. (destination changeset is not
699 699 modified by rebasing, but new changesets are added as its
700 700 descendants.)
701 701
702 702 Here are the ways to select changesets:
703 703
704 704 1. Explicitly select them using ``--rev``.
705 705
706 706 2. Use ``--source`` to select a root changeset and include all of its
707 707 descendants.
708 708
709 709 3. Use ``--base`` to select a changeset; rebase will find ancestors
710 710 and their descendants which are not also ancestors of the destination.
711 711
712 712 4. If you do not specify any of ``--rev``, ``source``, or ``--base``,
713 713 rebase will use ``--base .`` as above.
714 714
715 715 If ``--source`` or ``--rev`` is used, special names ``SRC`` and ``ALLSRC``
716 716 can be used in ``--dest``. Destination would be calculated per source
717 717 revision with ``SRC`` substituted by that single source revision and
718 718 ``ALLSRC`` substituted by all source revisions.
719 719
720 720 Rebase will destroy original changesets unless you use ``--keep``.
721 721 It will also move your bookmarks (even if you do).
722 722
723 723 Some changesets may be dropped if they do not contribute changes
724 724 (e.g. merges from the destination branch).
725 725
726 726 Unlike ``merge``, rebase will do nothing if you are at the branch tip of
727 727 a named branch with two heads. You will need to explicitly specify source
728 728 and/or destination.
729 729
730 730 If you need to use a tool to automate merge/conflict decisions, you
731 731 can specify one with ``--tool``, see :hg:`help merge-tools`.
732 732 As a caveat: the tool will not be used to mediate when a file was
733 733 deleted, there is no hook presently available for this.
734 734
735 735 If a rebase is interrupted to manually resolve a conflict, it can be
736 736 continued with --continue/-c, aborted with --abort/-a, or stopped with
737 737 --stop.
738 738
739 739 .. container:: verbose
740 740
741 741 Examples:
742 742
743 743 - move "local changes" (current commit back to branching point)
744 744 to the current branch tip after a pull::
745 745
746 746 hg rebase
747 747
748 748 - move a single changeset to the stable branch::
749 749
750 750 hg rebase -r 5f493448 -d stable
751 751
752 752 - splice a commit and all its descendants onto another part of history::
753 753
754 754 hg rebase --source c0c3 --dest 4cf9
755 755
756 756 - rebase everything on a branch marked by a bookmark onto the
757 757 default branch::
758 758
759 759 hg rebase --base myfeature --dest default
760 760
761 761 - collapse a sequence of changes into a single commit::
762 762
763 763 hg rebase --collapse -r 1520:1525 -d .
764 764
765 765 - move a named branch while preserving its name::
766 766
767 767 hg rebase -r "branch(featureX)" -d 1.3 --keepbranches
768 768
769 769 - stabilize orphaned changesets so history looks linear::
770 770
771 771 hg rebase -r 'orphan()-obsolete()'\
772 772 -d 'first(max((successors(max(roots(ALLSRC) & ::SRC)^)-obsolete())::) +\
773 773 max(::((roots(ALLSRC) & ::SRC)^)-obsolete()))'
774 774
775 775 Configuration Options:
776 776
777 777 You can make rebase require a destination if you set the following config
778 778 option::
779 779
780 780 [commands]
781 781 rebase.requiredest = True
782 782
783 783 By default, rebase will close the transaction after each commit. For
784 784 performance purposes, you can configure rebase to use a single transaction
785 785 across the entire rebase. WARNING: This setting introduces a significant
786 786 risk of losing the work you've done in a rebase if the rebase aborts
787 787 unexpectedly::
788 788
789 789 [rebase]
790 790 singletransaction = True
791 791
792 792 By default, rebase writes to the working copy, but you can configure it to
793 793 run in-memory for for better performance, and to allow it to run if the
794 794 working copy is dirty::
795 795
796 796 [rebase]
797 797 experimental.inmemory = True
798 798
799 799 Return Values:
800 800
801 801 Returns 0 on success, 1 if nothing to rebase or there are
802 802 unresolved conflicts.
803 803
804 804 """
805 805 opts = pycompat.byteskwargs(opts)
806 806 inmemory = ui.configbool('rebase', 'experimental.inmemory')
807 807 dryrun = opts.get('dry_run')
808 808 confirm = opts.get('confirm')
809 809 selactions = [k for k in ['abort', 'stop', 'continue'] if opts.get(k)]
810 810 if len(selactions) > 1:
811 811 raise error.Abort(_('cannot use --%s with --%s')
812 812 % tuple(selactions[:2]))
813 813 action = selactions[0] if selactions else None
814 814 if dryrun and action:
815 815 raise error.Abort(_('cannot specify both --dry-run and --%s') % action)
816 816 if confirm and action:
817 817 raise error.Abort(_('cannot specify both --confirm and --%s') % action)
818 818 if dryrun and confirm:
819 819 raise error.Abort(_('cannot specify both --confirm and --dry-run'))
820 820
821 821 if action or repo.currenttransaction() is not None:
822 822 # in-memory rebase is not compatible with resuming rebases.
823 823 # (Or if it is run within a transaction, since the restart logic can
824 824 # fail the entire transaction.)
825 825 inmemory = False
826 826
827 827 if opts.get('auto_orphans'):
828 828 for key in opts:
829 829 if key != 'auto_orphans' and opts.get(key):
830 830 raise error.Abort(_('--auto-orphans is incompatible with %s') %
831 831 ('--' + key))
832 832 userrevs = list(repo.revs(opts.get('auto_orphans')))
833 833 opts['rev'] = [revsetlang.formatspec('%ld and orphan()', userrevs)]
834 834 opts['dest'] = '_destautoorphanrebase(SRC)'
835 835
836 836 if dryrun or confirm:
837 837 return _dryrunrebase(ui, repo, action, opts)
838 838 elif action == 'stop':
839 839 rbsrt = rebaseruntime(repo, ui)
840 840 with repo.wlock(), repo.lock():
841 841 rbsrt.restorestatus()
842 842 if rbsrt.collapsef:
843 843 raise error.Abort(_("cannot stop in --collapse session"))
844 844 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
845 845 if not (rbsrt.keepf or allowunstable):
846 846 raise error.Abort(_("cannot remove original changesets with"
847 847 " unrebased descendants"),
848 848 hint=_('either enable obsmarkers to allow unstable '
849 849 'revisions or use --keep to keep original '
850 850 'changesets'))
851 851 if needupdate(repo, rbsrt.state):
852 852 # update to the current working revision
853 853 # to clear interrupted merge
854 854 hg.updaterepo(repo, rbsrt.originalwd, overwrite=True)
855 855 rbsrt._finishrebase()
856 856 return 0
857 857 elif inmemory:
858 858 try:
859 859 # in-memory merge doesn't support conflicts, so if we hit any, abort
860 860 # and re-run as an on-disk merge.
861 861 overrides = {('rebase', 'singletransaction'): True}
862 862 with ui.configoverride(overrides, 'rebase'):
863 863 return _dorebase(ui, repo, action, opts, inmemory=inmemory)
864 864 except error.InMemoryMergeConflictsError:
865 865 ui.warn(_('hit merge conflicts; re-running rebase without in-memory'
866 866 ' merge\n'))
867 867 _dorebase(ui, repo, action='abort', opts={})
868 868 return _dorebase(ui, repo, action, opts, inmemory=False)
869 869 else:
870 870 return _dorebase(ui, repo, action, opts)
871 871
872 872 def _dryrunrebase(ui, repo, action, opts):
873 873 rbsrt = rebaseruntime(repo, ui, inmemory=True, opts=opts)
874 874 confirm = opts.get('confirm')
875 875 if confirm:
876 876 ui.status(_('starting in-memory rebase\n'))
877 877 else:
878 878 ui.status(_('starting dry-run rebase; repository will not be '
879 879 'changed\n'))
880 880 with repo.wlock(), repo.lock():
881 881 needsabort = True
882 882 try:
883 883 overrides = {('rebase', 'singletransaction'): True}
884 884 with ui.configoverride(overrides, 'rebase'):
885 885 _origrebase(ui, repo, action, opts, rbsrt, inmemory=True,
886 886 leaveunfinished=True)
887 887 except error.InMemoryMergeConflictsError:
888 888 ui.status(_('hit a merge conflict\n'))
889 889 return 1
890 890 else:
891 891 if confirm:
892 892 ui.status(_('rebase completed successfully\n'))
893 893 if not ui.promptchoice(_(b'apply changes (yn)?'
894 894 b'$$ &Yes $$ &No')):
895 895 # finish unfinished rebase
896 896 rbsrt._finishrebase()
897 897 else:
898 898 rbsrt._prepareabortorcontinue(isabort=True, backup=False,
899 899 suppwarns=True)
900 900 needsabort = False
901 901 else:
902 902 ui.status(_('dry-run rebase completed successfully; run without'
903 903 ' -n/--dry-run to perform this rebase\n'))
904 904 return 0
905 905 finally:
906 906 if needsabort:
907 907 # no need to store backup in case of dryrun
908 908 rbsrt._prepareabortorcontinue(isabort=True, backup=False,
909 909 suppwarns=True)
910 910
911 911 def _dorebase(ui, repo, action, opts, inmemory=False):
912 912 rbsrt = rebaseruntime(repo, ui, inmemory, opts)
913 913 return _origrebase(ui, repo, action, opts, rbsrt, inmemory=inmemory)
914 914
915 915 def _origrebase(ui, repo, action, opts, rbsrt, inmemory=False,
916 916 leaveunfinished=False):
917 917 assert action != 'stop'
918 918 with repo.wlock(), repo.lock():
919 919 # Validate input and define rebasing points
920 920 destf = opts.get('dest', None)
921 921 srcf = opts.get('source', None)
922 922 basef = opts.get('base', None)
923 923 revf = opts.get('rev', [])
924 924 # search default destination in this space
925 925 # used in the 'hg pull --rebase' case, see issue 5214.
926 926 destspace = opts.get('_destspace')
927 927 if opts.get('interactive'):
928 928 try:
929 929 if extensions.find('histedit'):
930 930 enablehistedit = ''
931 931 except KeyError:
932 932 enablehistedit = " --config extensions.histedit="
933 933 help = "hg%s help -e histedit" % enablehistedit
934 934 msg = _("interactive history editing is supported by the "
935 935 "'histedit' extension (see \"%s\")") % help
936 936 raise error.Abort(msg)
937 937
938 938 if rbsrt.collapsemsg and not rbsrt.collapsef:
939 939 raise error.Abort(
940 940 _('message can only be specified with collapse'))
941 941
942 942 if action:
943 943 if rbsrt.collapsef:
944 944 raise error.Abort(
945 945 _('cannot use collapse with continue or abort'))
946 946 if srcf or basef or destf:
947 947 raise error.Abort(
948 948 _('abort and continue do not allow specifying revisions'))
949 949 if action == 'abort' and opts.get('tool', False):
950 950 ui.warn(_('tool option will be ignored\n'))
951 951 if action == 'continue':
952 952 ms = mergemod.mergestate.read(repo)
953 953 mergeutil.checkunresolved(ms)
954 954
955 955 retcode = rbsrt._prepareabortorcontinue(isabort=(action == 'abort'))
956 956 if retcode is not None:
957 957 return retcode
958 958 else:
959 959 destmap = _definedestmap(ui, repo, inmemory, destf, srcf, basef,
960 960 revf, destspace=destspace)
961 961 retcode = rbsrt._preparenewrebase(destmap)
962 962 if retcode is not None:
963 963 return retcode
964 964 storecollapsemsg(repo, rbsrt.collapsemsg)
965 965
966 966 tr = None
967 967
968 968 singletr = ui.configbool('rebase', 'singletransaction')
969 969 if singletr:
970 970 tr = repo.transaction('rebase')
971 971
972 972 # If `rebase.singletransaction` is enabled, wrap the entire operation in
973 973 # one transaction here. Otherwise, transactions are obtained when
974 974 # committing each node, which is slower but allows partial success.
975 975 with util.acceptintervention(tr):
976 976 # Same logic for the dirstate guard, except we don't create one when
977 977 # rebasing in-memory (it's not needed).
978 978 dsguard = None
979 979 if singletr and not inmemory:
980 980 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
981 981 with util.acceptintervention(dsguard):
982 982 rbsrt._performrebase(tr)
983 983 if not leaveunfinished:
984 984 rbsrt._finishrebase()
985 985
986 986 def _definedestmap(ui, repo, inmemory, destf=None, srcf=None, basef=None,
987 987 revf=None, destspace=None):
988 988 """use revisions argument to define destmap {srcrev: destrev}"""
989 989 if revf is None:
990 990 revf = []
991 991
992 992 # destspace is here to work around issues with `hg pull --rebase` see
993 993 # issue5214 for details
994 994 if srcf and basef:
995 995 raise error.Abort(_('cannot specify both a source and a base'))
996 996 if revf and basef:
997 997 raise error.Abort(_('cannot specify both a revision and a base'))
998 998 if revf and srcf:
999 999 raise error.Abort(_('cannot specify both a revision and a source'))
1000 1000
1001 1001 if not inmemory:
1002 1002 cmdutil.checkunfinished(repo)
1003 1003 cmdutil.bailifchanged(repo)
1004 1004
1005 1005 if ui.configbool('commands', 'rebase.requiredest') and not destf:
1006 1006 raise error.Abort(_('you must specify a destination'),
1007 1007 hint=_('use: hg rebase -d REV'))
1008 1008
1009 1009 dest = None
1010 1010
1011 1011 if revf:
1012 1012 rebaseset = scmutil.revrange(repo, revf)
1013 1013 if not rebaseset:
1014 1014 ui.status(_('empty "rev" revision set - nothing to rebase\n'))
1015 1015 return None
1016 1016 elif srcf:
1017 1017 src = scmutil.revrange(repo, [srcf])
1018 1018 if not src:
1019 1019 ui.status(_('empty "source" revision set - nothing to rebase\n'))
1020 1020 return None
1021 1021 rebaseset = repo.revs('(%ld)::', src)
1022 1022 assert rebaseset
1023 1023 else:
1024 1024 base = scmutil.revrange(repo, [basef or '.'])
1025 1025 if not base:
1026 1026 ui.status(_('empty "base" revision set - '
1027 1027 "can't compute rebase set\n"))
1028 1028 return None
1029 1029 if destf:
1030 1030 # --base does not support multiple destinations
1031 1031 dest = scmutil.revsingle(repo, destf)
1032 1032 else:
1033 1033 dest = repo[_destrebase(repo, base, destspace=destspace)]
1034 1034 destf = bytes(dest)
1035 1035
1036 1036 roots = [] # selected children of branching points
1037 1037 bpbase = {} # {branchingpoint: [origbase]}
1038 1038 for b in base: # group bases by branching points
1039 1039 bp = repo.revs('ancestor(%d, %d)', b, dest.rev()).first()
1040 1040 bpbase[bp] = bpbase.get(bp, []) + [b]
1041 1041 if None in bpbase:
1042 1042 # emulate the old behavior, showing "nothing to rebase" (a better
1043 1043 # behavior may be abort with "cannot find branching point" error)
1044 1044 bpbase.clear()
1045 1045 for bp, bs in bpbase.iteritems(): # calculate roots
1046 1046 roots += list(repo.revs('children(%d) & ancestors(%ld)', bp, bs))
1047 1047
1048 1048 rebaseset = repo.revs('%ld::', roots)
1049 1049
1050 1050 if not rebaseset:
1051 1051 # transform to list because smartsets are not comparable to
1052 1052 # lists. This should be improved to honor laziness of
1053 1053 # smartset.
1054 1054 if list(base) == [dest.rev()]:
1055 1055 if basef:
1056 1056 ui.status(_('nothing to rebase - %s is both "base"'
1057 1057 ' and destination\n') % dest)
1058 1058 else:
1059 1059 ui.status(_('nothing to rebase - working directory '
1060 1060 'parent is also destination\n'))
1061 1061 elif not repo.revs('%ld - ::%d', base, dest.rev()):
1062 1062 if basef:
1063 1063 ui.status(_('nothing to rebase - "base" %s is '
1064 1064 'already an ancestor of destination '
1065 1065 '%s\n') %
1066 1066 ('+'.join(bytes(repo[r]) for r in base),
1067 1067 dest))
1068 1068 else:
1069 1069 ui.status(_('nothing to rebase - working '
1070 1070 'directory parent is already an '
1071 1071 'ancestor of destination %s\n') % dest)
1072 1072 else: # can it happen?
1073 1073 ui.status(_('nothing to rebase from %s to %s\n') %
1074 1074 ('+'.join(bytes(repo[r]) for r in base), dest))
1075 1075 return None
1076 1076
1077 1077 rebasingwcp = repo['.'].rev() in rebaseset
1078 1078 ui.log("rebase", "", rebase_rebasing_wcp=rebasingwcp)
1079 1079 if inmemory and rebasingwcp:
1080 1080 # Check these since we did not before.
1081 1081 cmdutil.checkunfinished(repo)
1082 1082 cmdutil.bailifchanged(repo)
1083 1083
1084 1084 if not destf:
1085 1085 dest = repo[_destrebase(repo, rebaseset, destspace=destspace)]
1086 1086 destf = bytes(dest)
1087 1087
1088 1088 allsrc = revsetlang.formatspec('%ld', rebaseset)
1089 1089 alias = {'ALLSRC': allsrc}
1090 1090
1091 1091 if dest is None:
1092 1092 try:
1093 1093 # fast path: try to resolve dest without SRC alias
1094 1094 dest = scmutil.revsingle(repo, destf, localalias=alias)
1095 1095 except error.RepoLookupError:
1096 1096 # multi-dest path: resolve dest for each SRC separately
1097 1097 destmap = {}
1098 1098 for r in rebaseset:
1099 1099 alias['SRC'] = revsetlang.formatspec('%d', r)
1100 1100 # use repo.anyrevs instead of scmutil.revsingle because we
1101 1101 # don't want to abort if destset is empty.
1102 1102 destset = repo.anyrevs([destf], user=True, localalias=alias)
1103 1103 size = len(destset)
1104 1104 if size == 1:
1105 1105 destmap[r] = destset.first()
1106 1106 elif size == 0:
1107 1107 ui.note(_('skipping %s - empty destination\n') % repo[r])
1108 1108 else:
1109 1109 raise error.Abort(_('rebase destination for %s is not '
1110 1110 'unique') % repo[r])
1111 1111
1112 1112 if dest is not None:
1113 1113 # single-dest case: assign dest to each rev in rebaseset
1114 1114 destrev = dest.rev()
1115 1115 destmap = {r: destrev for r in rebaseset} # {srcrev: destrev}
1116 1116
1117 1117 if not destmap:
1118 1118 ui.status(_('nothing to rebase - empty destination\n'))
1119 1119 return None
1120 1120
1121 1121 return destmap
1122 1122
1123 1123 def externalparent(repo, state, destancestors):
1124 1124 """Return the revision that should be used as the second parent
1125 1125 when the revisions in state is collapsed on top of destancestors.
1126 1126 Abort if there is more than one parent.
1127 1127 """
1128 1128 parents = set()
1129 1129 source = min(state)
1130 1130 for rev in state:
1131 1131 if rev == source:
1132 1132 continue
1133 1133 for p in repo[rev].parents():
1134 1134 if (p.rev() not in state
1135 1135 and p.rev() not in destancestors):
1136 1136 parents.add(p.rev())
1137 1137 if not parents:
1138 1138 return nullrev
1139 1139 if len(parents) == 1:
1140 1140 return parents.pop()
1141 1141 raise error.Abort(_('unable to collapse on top of %d, there is more '
1142 1142 'than one external parent: %s') %
1143 1143 (max(destancestors),
1144 1144 ', '.join("%d" % p for p in sorted(parents))))
1145 1145
1146 1146 def commitmemorynode(repo, p1, p2, wctx, editor, extra, user, date, commitmsg):
1147 1147 '''Commit the memory changes with parents p1 and p2.
1148 1148 Return node of committed revision.'''
1149 1149 # Replicates the empty check in ``repo.commit``.
1150 1150 if wctx.isempty() and not repo.ui.configbool('ui', 'allowemptycommit'):
1151 1151 return None
1152 1152
1153 1153 # By convention, ``extra['branch']`` (set by extrafn) clobbers
1154 1154 # ``branch`` (used when passing ``--keepbranches``).
1155 1155 branch = repo[p1].branch()
1156 1156 if 'branch' in extra:
1157 1157 branch = extra['branch']
1158 1158
1159 1159 memctx = wctx.tomemctx(commitmsg, parents=(p1, p2), date=date,
1160 1160 extra=extra, user=user, branch=branch, editor=editor)
1161 1161 commitres = repo.commitctx(memctx)
1162 1162 wctx.clean() # Might be reused
1163 1163 return commitres
1164 1164
1165 1165 def commitnode(repo, p1, p2, editor, extra, user, date, commitmsg):
1166 1166 '''Commit the wd changes with parents p1 and p2.
1167 1167 Return node of committed revision.'''
1168 1168 dsguard = util.nullcontextmanager()
1169 1169 if not repo.ui.configbool('rebase', 'singletransaction'):
1170 1170 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
1171 1171 with dsguard:
1172 1172 repo.setparents(repo[p1].node(), repo[p2].node())
1173 1173
1174 1174 # Commit might fail if unresolved files exist
1175 1175 newnode = repo.commit(text=commitmsg, user=user, date=date,
1176 1176 extra=extra, editor=editor)
1177 1177
1178 1178 repo.dirstate.setbranch(repo[newnode].branch())
1179 1179 return newnode
1180 1180
1181 1181 def rebasenode(repo, rev, p1, base, collapse, dest, wctx):
1182 1182 'Rebase a single revision rev on top of p1 using base as merge ancestor'
1183 1183 # Merge phase
1184 1184 # Update to destination and merge it with local
1185 1185 if wctx.isinmemory():
1186 1186 wctx.setbase(repo[p1])
1187 1187 else:
1188 1188 if repo['.'].rev() != p1:
1189 1189 repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
1190 1190 mergemod.update(repo, p1, False, True)
1191 1191 else:
1192 1192 repo.ui.debug(" already in destination\n")
1193 1193 # This is, alas, necessary to invalidate workingctx's manifest cache,
1194 1194 # as well as other data we litter on it in other places.
1195 1195 wctx = repo[None]
1196 1196 repo.dirstate.write(repo.currenttransaction())
1197 1197 repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev]))
1198 1198 if base is not None:
1199 1199 repo.ui.debug(" detach base %d:%s\n" % (base, repo[base]))
1200 1200 # When collapsing in-place, the parent is the common ancestor, we
1201 1201 # have to allow merging with it.
1202 1202 stats = mergemod.update(repo, rev, True, True, base, collapse,
1203 1203 labels=['dest', 'source'], wc=wctx)
1204 1204 if collapse:
1205 1205 copies.duplicatecopies(repo, wctx, rev, dest)
1206 1206 else:
1207 1207 # If we're not using --collapse, we need to
1208 1208 # duplicate copies between the revision we're
1209 1209 # rebasing and its first parent, but *not*
1210 1210 # duplicate any copies that have already been
1211 1211 # performed in the destination.
1212 1212 p1rev = repo[rev].p1().rev()
1213 1213 copies.duplicatecopies(repo, wctx, rev, p1rev, skiprev=dest)
1214 1214 return stats
1215 1215
1216 1216 def adjustdest(repo, rev, destmap, state, skipped):
1217 1217 """adjust rebase destination given the current rebase state
1218 1218
1219 1219 rev is what is being rebased. Return a list of two revs, which are the
1220 1220 adjusted destinations for rev's p1 and p2, respectively. If a parent is
1221 1221 nullrev, return dest without adjustment for it.
1222 1222
1223 1223 For example, when doing rebasing B+E to F, C to G, rebase will first move B
1224 1224 to B1, and E's destination will be adjusted from F to B1.
1225 1225
1226 1226 B1 <- written during rebasing B
1227 1227 |
1228 1228 F <- original destination of B, E
1229 1229 |
1230 1230 | E <- rev, which is being rebased
1231 1231 | |
1232 1232 | D <- prev, one parent of rev being checked
1233 1233 | |
1234 1234 | x <- skipped, ex. no successor or successor in (::dest)
1235 1235 | |
1236 1236 | C <- rebased as C', different destination
1237 1237 | |
1238 1238 | B <- rebased as B1 C'
1239 1239 |/ |
1240 1240 A G <- destination of C, different
1241 1241
1242 1242 Another example about merge changeset, rebase -r C+G+H -d K, rebase will
1243 1243 first move C to C1, G to G1, and when it's checking H, the adjusted
1244 1244 destinations will be [C1, G1].
1245 1245
1246 1246 H C1 G1
1247 1247 /| | /
1248 1248 F G |/
1249 1249 K | | -> K
1250 1250 | C D |
1251 1251 | |/ |
1252 1252 | B | ...
1253 1253 |/ |/
1254 1254 A A
1255 1255
1256 1256 Besides, adjust dest according to existing rebase information. For example,
1257 1257
1258 1258 B C D B needs to be rebased on top of C, C needs to be rebased on top
1259 1259 \|/ of D. We will rebase C first.
1260 1260 A
1261 1261
1262 1262 C' After rebasing C, when considering B's destination, use C'
1263 1263 | instead of the original C.
1264 1264 B D
1265 1265 \ /
1266 1266 A
1267 1267 """
1268 1268 # pick already rebased revs with same dest from state as interesting source
1269 1269 dest = destmap[rev]
1270 1270 source = [s for s, d in state.items()
1271 1271 if d > 0 and destmap[s] == dest and s not in skipped]
1272 1272
1273 1273 result = []
1274 1274 for prev in repo.changelog.parentrevs(rev):
1275 1275 adjusted = dest
1276 1276 if prev != nullrev:
1277 1277 candidate = repo.revs('max(%ld and (::%d))', source, prev).first()
1278 1278 if candidate is not None:
1279 1279 adjusted = state[candidate]
1280 1280 if adjusted == dest and dest in state:
1281 1281 adjusted = state[dest]
1282 1282 if adjusted == revtodo:
1283 1283 # sortsource should produce an order that makes this impossible
1284 1284 raise error.ProgrammingError(
1285 1285 'rev %d should be rebased already at this time' % dest)
1286 1286 result.append(adjusted)
1287 1287 return result
1288 1288
1289 1289 def _checkobsrebase(repo, ui, rebaseobsrevs, rebaseobsskipped):
1290 1290 """
1291 1291 Abort if rebase will create divergence or rebase is noop because of markers
1292 1292
1293 1293 `rebaseobsrevs`: set of obsolete revision in source
1294 1294 `rebaseobsskipped`: set of revisions from source skipped because they have
1295 1295 successors in destination or no non-obsolete successor.
1296 1296 """
1297 1297 # Obsolete node with successors not in dest leads to divergence
1298 1298 divergenceok = ui.configbool('experimental',
1299 1299 'evolution.allowdivergence')
1300 1300 divergencebasecandidates = rebaseobsrevs - rebaseobsskipped
1301 1301
1302 1302 if divergencebasecandidates and not divergenceok:
1303 1303 divhashes = (bytes(repo[r])
1304 1304 for r in divergencebasecandidates)
1305 1305 msg = _("this rebase will cause "
1306 1306 "divergences from: %s")
1307 1307 h = _("to force the rebase please set "
1308 1308 "experimental.evolution.allowdivergence=True")
1309 1309 raise error.Abort(msg % (",".join(divhashes),), hint=h)
1310 1310
1311 1311 def successorrevs(unfi, rev):
1312 1312 """yield revision numbers for successors of rev"""
1313 1313 assert unfi.filtername is None
1314 1314 nodemap = unfi.changelog.nodemap
1315 1315 for s in obsutil.allsuccessors(unfi.obsstore, [unfi[rev].node()]):
1316 1316 if s in nodemap:
1317 1317 yield nodemap[s]
1318 1318
1319 1319 def defineparents(repo, rev, destmap, state, skipped, obsskipped):
1320 1320 """Return new parents and optionally a merge base for rev being rebased
1321 1321
1322 1322 The destination specified by "dest" cannot always be used directly because
1323 1323 previously rebase result could affect destination. For example,
1324 1324
1325 1325 D E rebase -r C+D+E -d B
1326 1326 |/ C will be rebased to C'
1327 1327 B C D's new destination will be C' instead of B
1328 1328 |/ E's new destination will be C' instead of B
1329 1329 A
1330 1330
1331 1331 The new parents of a merge is slightly more complicated. See the comment
1332 1332 block below.
1333 1333 """
1334 1334 # use unfiltered changelog since successorrevs may return filtered nodes
1335 1335 assert repo.filtername is None
1336 1336 cl = repo.changelog
1337 1337 isancestor = cl.isancestorrev
1338 1338
1339 1339 dest = destmap[rev]
1340 1340 oldps = repo.changelog.parentrevs(rev) # old parents
1341 1341 newps = [nullrev, nullrev] # new parents
1342 1342 dests = adjustdest(repo, rev, destmap, state, skipped)
1343 1343 bases = list(oldps) # merge base candidates, initially just old parents
1344 1344
1345 1345 if all(r == nullrev for r in oldps[1:]):
1346 1346 # For non-merge changeset, just move p to adjusted dest as requested.
1347 1347 newps[0] = dests[0]
1348 1348 else:
1349 1349 # For merge changeset, if we move p to dests[i] unconditionally, both
1350 1350 # parents may change and the end result looks like "the merge loses a
1351 1351 # parent", which is a surprise. This is a limit because "--dest" only
1352 1352 # accepts one dest per src.
1353 1353 #
1354 1354 # Therefore, only move p with reasonable conditions (in this order):
1355 1355 # 1. use dest, if dest is a descendent of (p or one of p's successors)
1356 1356 # 2. use p's rebased result, if p is rebased (state[p] > 0)
1357 1357 #
1358 1358 # Comparing with adjustdest, the logic here does some additional work:
1359 1359 # 1. decide which parents will not be moved towards dest
1360 1360 # 2. if the above decision is "no", should a parent still be moved
1361 1361 # because it was rebased?
1362 1362 #
1363 1363 # For example:
1364 1364 #
1365 1365 # C # "rebase -r C -d D" is an error since none of the parents
1366 1366 # /| # can be moved. "rebase -r B+C -d D" will move C's parent
1367 1367 # A B D # B (using rule "2."), since B will be rebased.
1368 1368 #
1369 1369 # The loop tries to be not rely on the fact that a Mercurial node has
1370 1370 # at most 2 parents.
1371 1371 for i, p in enumerate(oldps):
1372 1372 np = p # new parent
1373 1373 if any(isancestor(x, dests[i]) for x in successorrevs(repo, p)):
1374 1374 np = dests[i]
1375 1375 elif p in state and state[p] > 0:
1376 1376 np = state[p]
1377 1377
1378 1378 # "bases" only record "special" merge bases that cannot be
1379 1379 # calculated from changelog DAG (i.e. isancestor(p, np) is False).
1380 1380 # For example:
1381 1381 #
1382 1382 # B' # rebase -s B -d D, when B was rebased to B'. dest for C
1383 1383 # | C # is B', but merge base for C is B, instead of
1384 1384 # D | # changelog.ancestor(C, B') == A. If changelog DAG and
1385 1385 # | B # "state" edges are merged (so there will be an edge from
1386 1386 # |/ # B to B'), the merge base is still ancestor(C, B') in
1387 1387 # A # the merged graph.
1388 1388 #
1389 1389 # Also see https://bz.mercurial-scm.org/show_bug.cgi?id=1950#c8
1390 1390 # which uses "virtual null merge" to explain this situation.
1391 1391 if isancestor(p, np):
1392 1392 bases[i] = nullrev
1393 1393
1394 1394 # If one parent becomes an ancestor of the other, drop the ancestor
1395 1395 for j, x in enumerate(newps[:i]):
1396 1396 if x == nullrev:
1397 1397 continue
1398 1398 if isancestor(np, x): # CASE-1
1399 1399 np = nullrev
1400 1400 elif isancestor(x, np): # CASE-2
1401 1401 newps[j] = np
1402 1402 np = nullrev
1403 1403 # New parents forming an ancestor relationship does not
1404 1404 # mean the old parents have a similar relationship. Do not
1405 1405 # set bases[x] to nullrev.
1406 1406 bases[j], bases[i] = bases[i], bases[j]
1407 1407
1408 1408 newps[i] = np
1409 1409
1410 1410 # "rebasenode" updates to new p1, and the old p1 will be used as merge
1411 1411 # base. If only p2 changes, merging using unchanged p1 as merge base is
1412 1412 # suboptimal. Therefore swap parents to make the merge sane.
1413 1413 if newps[1] != nullrev and oldps[0] == newps[0]:
1414 1414 assert len(newps) == 2 and len(oldps) == 2
1415 1415 newps.reverse()
1416 1416 bases.reverse()
1417 1417
1418 1418 # No parent change might be an error because we fail to make rev a
1419 1419 # descendent of requested dest. This can happen, for example:
1420 1420 #
1421 1421 # C # rebase -r C -d D
1422 1422 # /| # None of A and B will be changed to D and rebase fails.
1423 1423 # A B D
1424 1424 if set(newps) == set(oldps) and dest not in newps:
1425 1425 raise error.Abort(_('cannot rebase %d:%s without '
1426 1426 'moving at least one of its parents')
1427 1427 % (rev, repo[rev]))
1428 1428
1429 1429 # Source should not be ancestor of dest. The check here guarantees it's
1430 1430 # impossible. With multi-dest, the initial check does not cover complex
1431 1431 # cases since we don't have abstractions to dry-run rebase cheaply.
1432 1432 if any(p != nullrev and isancestor(rev, p) for p in newps):
1433 1433 raise error.Abort(_('source is ancestor of destination'))
1434 1434
1435 1435 # "rebasenode" updates to new p1, use the corresponding merge base.
1436 1436 if bases[0] != nullrev:
1437 1437 base = bases[0]
1438 1438 else:
1439 1439 base = None
1440 1440
1441 1441 # Check if the merge will contain unwanted changes. That may happen if
1442 1442 # there are multiple special (non-changelog ancestor) merge bases, which
1443 1443 # cannot be handled well by the 3-way merge algorithm. For example:
1444 1444 #
1445 1445 # F
1446 1446 # /|
1447 1447 # D E # "rebase -r D+E+F -d Z", when rebasing F, if "D" was chosen
1448 1448 # | | # as merge base, the difference between D and F will include
1449 1449 # B C # C, so the rebased F will contain C surprisingly. If "E" was
1450 1450 # |/ # chosen, the rebased F will contain B.
1451 1451 # A Z
1452 1452 #
1453 1453 # But our merge base candidates (D and E in above case) could still be
1454 1454 # better than the default (ancestor(F, Z) == null). Therefore still
1455 1455 # pick one (so choose p1 above).
1456 1456 if sum(1 for b in bases if b != nullrev) > 1:
1457 1457 unwanted = [None, None] # unwanted[i]: unwanted revs if choose bases[i]
1458 1458 for i, base in enumerate(bases):
1459 1459 if base == nullrev:
1460 1460 continue
1461 1461 # Revisions in the side (not chosen as merge base) branch that
1462 1462 # might contain "surprising" contents
1463 1463 siderevs = list(repo.revs('((%ld-%d) %% (%d+%d))',
1464 1464 bases, base, base, dest))
1465 1465
1466 1466 # If those revisions are covered by rebaseset, the result is good.
1467 1467 # A merge in rebaseset would be considered to cover its ancestors.
1468 1468 if siderevs:
1469 1469 rebaseset = [r for r, d in state.items()
1470 1470 if d > 0 and r not in obsskipped]
1471 1471 merges = [r for r in rebaseset
1472 1472 if cl.parentrevs(r)[1] != nullrev]
1473 1473 unwanted[i] = list(repo.revs('%ld - (::%ld) - %ld',
1474 1474 siderevs, merges, rebaseset))
1475 1475
1476 1476 # Choose a merge base that has a minimal number of unwanted revs.
1477 1477 l, i = min((len(revs), i)
1478 1478 for i, revs in enumerate(unwanted) if revs is not None)
1479 1479 base = bases[i]
1480 1480
1481 1481 # newps[0] should match merge base if possible. Currently, if newps[i]
1482 1482 # is nullrev, the only case is newps[i] and newps[j] (j < i), one is
1483 1483 # the other's ancestor. In that case, it's fine to not swap newps here.
1484 1484 # (see CASE-1 and CASE-2 above)
1485 1485 if i != 0 and newps[i] != nullrev:
1486 1486 newps[0], newps[i] = newps[i], newps[0]
1487 1487
1488 1488 # The merge will include unwanted revisions. Abort now. Revisit this if
1489 1489 # we have a more advanced merge algorithm that handles multiple bases.
1490 1490 if l > 0:
1491 1491 unwanteddesc = _(' or ').join(
1492 1492 (', '.join('%d:%s' % (r, repo[r]) for r in revs)
1493 1493 for revs in unwanted if revs is not None))
1494 1494 raise error.Abort(
1495 1495 _('rebasing %d:%s will include unwanted changes from %s')
1496 1496 % (rev, repo[rev], unwanteddesc))
1497 1497
1498 1498 repo.ui.debug(" future parents are %d and %d\n" % tuple(newps))
1499 1499
1500 1500 return newps[0], newps[1], base
1501 1501
1502 1502 def isagitpatch(repo, patchname):
1503 1503 'Return true if the given patch is in git format'
1504 1504 mqpatch = os.path.join(repo.mq.path, patchname)
1505 1505 for line in patch.linereader(open(mqpatch, 'rb')):
1506 1506 if line.startswith('diff --git'):
1507 1507 return True
1508 1508 return False
1509 1509
1510 1510 def updatemq(repo, state, skipped, **opts):
1511 1511 'Update rebased mq patches - finalize and then import them'
1512 1512 mqrebase = {}
1513 1513 mq = repo.mq
1514 1514 original_series = mq.fullseries[:]
1515 1515 skippedpatches = set()
1516 1516
1517 1517 for p in mq.applied:
1518 1518 rev = repo[p.node].rev()
1519 1519 if rev in state:
1520 1520 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
1521 1521 (rev, p.name))
1522 1522 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
1523 1523 else:
1524 1524 # Applied but not rebased, not sure this should happen
1525 1525 skippedpatches.add(p.name)
1526 1526
1527 1527 if mqrebase:
1528 1528 mq.finish(repo, mqrebase.keys())
1529 1529
1530 1530 # We must start import from the newest revision
1531 1531 for rev in sorted(mqrebase, reverse=True):
1532 1532 if rev not in skipped:
1533 1533 name, isgit = mqrebase[rev]
1534 1534 repo.ui.note(_('updating mq patch %s to %d:%s\n') %
1535 1535 (name, state[rev], repo[state[rev]]))
1536 1536 mq.qimport(repo, (), patchname=name, git=isgit,
1537 1537 rev=["%d" % state[rev]])
1538 1538 else:
1539 1539 # Rebased and skipped
1540 1540 skippedpatches.add(mqrebase[rev][0])
1541 1541
1542 1542 # Patches were either applied and rebased and imported in
1543 1543 # order, applied and removed or unapplied. Discard the removed
1544 1544 # ones while preserving the original series order and guards.
1545 1545 newseries = [s for s in original_series
1546 1546 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
1547 1547 mq.fullseries[:] = newseries
1548 1548 mq.seriesdirty = True
1549 1549 mq.savedirty()
1550 1550
1551 1551 def storecollapsemsg(repo, collapsemsg):
1552 1552 'Store the collapse message to allow recovery'
1553 1553 collapsemsg = collapsemsg or ''
1554 1554 f = repo.vfs("last-message.txt", "w")
1555 1555 f.write("%s\n" % collapsemsg)
1556 1556 f.close()
1557 1557
1558 1558 def clearcollapsemsg(repo):
1559 1559 'Remove collapse message file'
1560 1560 repo.vfs.unlinkpath("last-message.txt", ignoremissing=True)
1561 1561
1562 1562 def restorecollapsemsg(repo, isabort):
1563 1563 'Restore previously stored collapse message'
1564 1564 try:
1565 1565 f = repo.vfs("last-message.txt")
1566 1566 collapsemsg = f.readline().strip()
1567 1567 f.close()
1568 1568 except IOError as err:
1569 1569 if err.errno != errno.ENOENT:
1570 1570 raise
1571 1571 if isabort:
1572 1572 # Oh well, just abort like normal
1573 1573 collapsemsg = ''
1574 1574 else:
1575 1575 raise error.Abort(_('missing .hg/last-message.txt for rebase'))
1576 1576 return collapsemsg
1577 1577
1578 1578 def clearstatus(repo):
1579 1579 'Remove the status files'
1580 1580 # Make sure the active transaction won't write the state file
1581 1581 tr = repo.currenttransaction()
1582 1582 if tr:
1583 1583 tr.removefilegenerator('rebasestate')
1584 1584 repo.vfs.unlinkpath("rebasestate", ignoremissing=True)
1585 1585
1586 1586 def needupdate(repo, state):
1587 1587 '''check whether we should `update --clean` away from a merge, or if
1588 1588 somehow the working dir got forcibly updated, e.g. by older hg'''
1589 1589 parents = [p.rev() for p in repo[None].parents()]
1590 1590
1591 1591 # Are we in a merge state at all?
1592 1592 if len(parents) < 2:
1593 1593 return False
1594 1594
1595 1595 # We should be standing on the first as-of-yet unrebased commit.
1596 1596 firstunrebased = min([old for old, new in state.iteritems()
1597 1597 if new == nullrev])
1598 1598 if firstunrebased in parents:
1599 1599 return True
1600 1600
1601 1601 return False
1602 1602
1603 1603 def abort(repo, originalwd, destmap, state, activebookmark=None, backup=True,
1604 1604 suppwarns=False):
1605 1605 '''Restore the repository to its original state. Additional args:
1606 1606
1607 1607 activebookmark: the name of the bookmark that should be active after the
1608 1608 restore'''
1609 1609
1610 1610 try:
1611 1611 # If the first commits in the rebased set get skipped during the rebase,
1612 1612 # their values within the state mapping will be the dest rev id. The
1613 1613 # rebased list must must not contain the dest rev (issue4896)
1614 1614 rebased = [s for r, s in state.items()
1615 1615 if s >= 0 and s != r and s != destmap[r]]
1616 1616 immutable = [d for d in rebased if not repo[d].mutable()]
1617 1617 cleanup = True
1618 1618 if immutable:
1619 1619 repo.ui.warn(_("warning: can't clean up public changesets %s\n")
1620 1620 % ', '.join(bytes(repo[r]) for r in immutable),
1621 1621 hint=_("see 'hg help phases' for details"))
1622 1622 cleanup = False
1623 1623
1624 1624 descendants = set()
1625 1625 if rebased:
1626 1626 descendants = set(repo.changelog.descendants(rebased))
1627 1627 if descendants - set(rebased):
1628 1628 repo.ui.warn(_("warning: new changesets detected on destination "
1629 1629 "branch, can't strip\n"))
1630 1630 cleanup = False
1631 1631
1632 1632 if cleanup:
1633 1633 shouldupdate = False
1634 1634 if rebased:
1635 1635 strippoints = [
1636 1636 c.node() for c in repo.set('roots(%ld)', rebased)]
1637 1637
1638 1638 updateifonnodes = set(rebased)
1639 1639 updateifonnodes.update(destmap.values())
1640 1640 updateifonnodes.add(originalwd)
1641 1641 shouldupdate = repo['.'].rev() in updateifonnodes
1642 1642
1643 1643 # Update away from the rebase if necessary
1644 1644 if shouldupdate or needupdate(repo, state):
1645 1645 mergemod.update(repo, originalwd, False, True)
1646 1646
1647 1647 # Strip from the first rebased revision
1648 1648 if rebased:
1649 1649 repair.strip(repo.ui, repo, strippoints, backup=backup)
1650 1650
1651 1651 if activebookmark and activebookmark in repo._bookmarks:
1652 1652 bookmarks.activate(repo, activebookmark)
1653 1653
1654 1654 finally:
1655 1655 clearstatus(repo)
1656 1656 clearcollapsemsg(repo)
1657 1657 if not suppwarns:
1658 1658 repo.ui.warn(_('rebase aborted\n'))
1659 1659 return 0
1660 1660
1661 1661 def sortsource(destmap):
1662 1662 """yield source revisions in an order that we only rebase things once
1663 1663
1664 1664 If source and destination overlaps, we should filter out revisions
1665 1665 depending on other revisions which hasn't been rebased yet.
1666 1666
1667 1667 Yield a sorted list of revisions each time.
1668 1668
1669 1669 For example, when rebasing A to B, B to C. This function yields [B], then
1670 1670 [A], indicating B needs to be rebased first.
1671 1671
1672 1672 Raise if there is a cycle so the rebase is impossible.
1673 1673 """
1674 1674 srcset = set(destmap)
1675 1675 while srcset:
1676 1676 srclist = sorted(srcset)
1677 1677 result = []
1678 1678 for r in srclist:
1679 1679 if destmap[r] not in srcset:
1680 1680 result.append(r)
1681 1681 if not result:
1682 1682 raise error.Abort(_('source and destination form a cycle'))
1683 1683 srcset -= set(result)
1684 1684 yield result
1685 1685
1686 1686 def buildstate(repo, destmap, collapse):
1687 1687 '''Define which revisions are going to be rebased and where
1688 1688
1689 1689 repo: repo
1690 1690 destmap: {srcrev: destrev}
1691 1691 '''
1692 1692 rebaseset = destmap.keys()
1693 1693 originalwd = repo['.'].rev()
1694 1694
1695 1695 # This check isn't strictly necessary, since mq detects commits over an
1696 1696 # applied patch. But it prevents messing up the working directory when
1697 1697 # a partially completed rebase is blocked by mq.
1698 1698 if 'qtip' in repo.tags():
1699 1699 mqapplied = set(repo[s.node].rev() for s in repo.mq.applied)
1700 1700 if set(destmap.values()) & mqapplied:
1701 1701 raise error.Abort(_('cannot rebase onto an applied mq patch'))
1702 1702
1703 1703 # Get "cycle" error early by exhausting the generator.
1704 1704 sortedsrc = list(sortsource(destmap)) # a list of sorted revs
1705 1705 if not sortedsrc:
1706 1706 raise error.Abort(_('no matching revisions'))
1707 1707
1708 1708 # Only check the first batch of revisions to rebase not depending on other
1709 1709 # rebaseset. This means "source is ancestor of destination" for the second
1710 1710 # (and following) batches of revisions are not checked here. We rely on
1711 1711 # "defineparents" to do that check.
1712 1712 roots = list(repo.set('roots(%ld)', sortedsrc[0]))
1713 1713 if not roots:
1714 1714 raise error.Abort(_('no matching revisions'))
1715 1715 def revof(r):
1716 1716 return r.rev()
1717 1717 roots = sorted(roots, key=revof)
1718 1718 state = dict.fromkeys(rebaseset, revtodo)
1719 1719 emptyrebase = (len(sortedsrc) == 1)
1720 1720 for root in roots:
1721 1721 dest = repo[destmap[root.rev()]]
1722 1722 commonbase = root.ancestor(dest)
1723 1723 if commonbase == root:
1724 1724 raise error.Abort(_('source is ancestor of destination'))
1725 1725 if commonbase == dest:
1726 1726 wctx = repo[None]
1727 1727 if dest == wctx.p1():
1728 1728 # when rebasing to '.', it will use the current wd branch name
1729 1729 samebranch = root.branch() == wctx.branch()
1730 1730 else:
1731 1731 samebranch = root.branch() == dest.branch()
1732 1732 if not collapse and samebranch and dest in root.parents():
1733 1733 # mark the revision as done by setting its new revision
1734 1734 # equal to its old (current) revisions
1735 1735 state[root.rev()] = root.rev()
1736 1736 repo.ui.debug('source is a child of destination\n')
1737 1737 continue
1738 1738
1739 1739 emptyrebase = False
1740 1740 repo.ui.debug('rebase onto %s starting from %s\n' % (dest, root))
1741 1741 if emptyrebase:
1742 1742 return None
1743 1743 for rev in sorted(state):
1744 1744 parents = [p for p in repo.changelog.parentrevs(rev) if p != nullrev]
1745 1745 # if all parents of this revision are done, then so is this revision
1746 1746 if parents and all((state.get(p) == p for p in parents)):
1747 1747 state[rev] = rev
1748 1748 return originalwd, destmap, state
1749 1749
1750 1750 def clearrebased(ui, repo, destmap, state, skipped, collapsedas=None,
1751 1751 keepf=False, fm=None, backup=True):
1752 1752 """dispose of rebased revision at the end of the rebase
1753 1753
1754 1754 If `collapsedas` is not None, the rebase was a collapse whose result if the
1755 1755 `collapsedas` node.
1756 1756
1757 1757 If `keepf` is not True, the rebase has --keep set and no nodes should be
1758 1758 removed (but bookmarks still need to be moved).
1759 1759
1760 1760 If `backup` is False, no backup will be stored when stripping rebased
1761 1761 revisions.
1762 1762 """
1763 1763 tonode = repo.changelog.node
1764 1764 replacements = {}
1765 1765 moves = {}
1766 1766 for rev, newrev in sorted(state.items()):
1767 1767 if newrev >= 0 and newrev != rev:
1768 1768 oldnode = tonode(rev)
1769 1769 newnode = collapsedas or tonode(newrev)
1770 1770 moves[oldnode] = newnode
1771 1771 if not keepf:
1772 1772 if rev in skipped:
1773 1773 succs = ()
1774 1774 else:
1775 1775 succs = (newnode,)
1776 1776 replacements[oldnode] = succs
1777 1777 scmutil.cleanupnodes(repo, replacements, 'rebase', moves, backup=backup)
1778 1778 if fm:
1779 1779 hf = fm.hexfunc
1780 1780 fl = fm.formatlist
1781 1781 fd = fm.formatdict
1782 1782 nodechanges = fd({hf(oldn): fl([hf(n) for n in newn], name='node')
1783 1783 for oldn, newn in replacements.iteritems()},
1784 1784 key="oldnode", value="newnodes")
1785 1785 fm.data(nodechanges=nodechanges)
1786 1786
1787 1787 def pullrebase(orig, ui, repo, *args, **opts):
1788 1788 'Call rebase after pull if the latter has been invoked with --rebase'
1789 1789 ret = None
1790 1790 if opts.get(r'rebase'):
1791 1791 if ui.configbool('commands', 'rebase.requiredest'):
1792 1792 msg = _('rebase destination required by configuration')
1793 1793 hint = _('use hg pull followed by hg rebase -d DEST')
1794 1794 raise error.Abort(msg, hint=hint)
1795 1795
1796 1796 with repo.wlock(), repo.lock():
1797 1797 if opts.get(r'update'):
1798 1798 del opts[r'update']
1799 1799 ui.debug('--update and --rebase are not compatible, ignoring '
1800 1800 'the update flag\n')
1801 1801
1802 1802 cmdutil.checkunfinished(repo)
1803 1803 cmdutil.bailifchanged(repo, hint=_('cannot pull with rebase: '
1804 1804 'please commit or shelve your changes first'))
1805 1805
1806 1806 revsprepull = len(repo)
1807 1807 origpostincoming = commands.postincoming
1808 1808 def _dummy(*args, **kwargs):
1809 1809 pass
1810 1810 commands.postincoming = _dummy
1811 1811 try:
1812 1812 ret = orig(ui, repo, *args, **opts)
1813 1813 finally:
1814 1814 commands.postincoming = origpostincoming
1815 1815 revspostpull = len(repo)
1816 1816 if revspostpull > revsprepull:
1817 1817 # --rev option from pull conflict with rebase own --rev
1818 1818 # dropping it
1819 1819 if r'rev' in opts:
1820 1820 del opts[r'rev']
1821 1821 # positional argument from pull conflicts with rebase's own
1822 1822 # --source.
1823 1823 if r'source' in opts:
1824 1824 del opts[r'source']
1825 1825 # revsprepull is the len of the repo, not revnum of tip.
1826 1826 destspace = list(repo.changelog.revs(start=revsprepull))
1827 1827 opts[r'_destspace'] = destspace
1828 1828 try:
1829 1829 rebase(ui, repo, **opts)
1830 1830 except error.NoMergeDestAbort:
1831 1831 # we can maybe update instead
1832 1832 rev, _a, _b = destutil.destupdate(repo)
1833 1833 if rev == repo['.'].rev():
1834 1834 ui.status(_('nothing to rebase\n'))
1835 1835 else:
1836 1836 ui.status(_('nothing to rebase - updating instead\n'))
1837 1837 # not passing argument to get the bare update behavior
1838 1838 # with warning and trumpets
1839 1839 commands.update(ui, repo)
1840 1840 else:
1841 1841 if opts.get(r'tool'):
1842 1842 raise error.Abort(_('--tool can only be used with --rebase'))
1843 1843 ret = orig(ui, repo, *args, **opts)
1844 1844
1845 1845 return ret
1846 1846
1847 1847 def _filterobsoleterevs(repo, revs):
1848 1848 """returns a set of the obsolete revisions in revs"""
1849 1849 return set(r for r in revs if repo[r].obsolete())
1850 1850
1851 1851 def _computeobsoletenotrebased(repo, rebaseobsrevs, destmap):
1852 1852 """Return (obsoletenotrebased, obsoletewithoutsuccessorindestination).
1853 1853
1854 1854 `obsoletenotrebased` is a mapping mapping obsolete => successor for all
1855 1855 obsolete nodes to be rebased given in `rebaseobsrevs`.
1856 1856
1857 1857 `obsoletewithoutsuccessorindestination` is a set with obsolete revisions
1858 1858 without a successor in destination.
1859 1859
1860 1860 `obsoleteextinctsuccessors` is a set of obsolete revisions with only
1861 1861 obsolete successors.
1862 1862 """
1863 1863 obsoletenotrebased = {}
1864 1864 obsoletewithoutsuccessorindestination = set([])
1865 1865 obsoleteextinctsuccessors = set([])
1866 1866
1867 1867 assert repo.filtername is None
1868 1868 cl = repo.changelog
1869 1869 nodemap = cl.nodemap
1870 1870 extinctrevs = set(repo.revs('extinct()'))
1871 1871 for srcrev in rebaseobsrevs:
1872 1872 srcnode = cl.node(srcrev)
1873 1873 # XXX: more advanced APIs are required to handle split correctly
1874 1874 successors = set(obsutil.allsuccessors(repo.obsstore, [srcnode]))
1875 1875 # obsutil.allsuccessors includes node itself
1876 1876 successors.remove(srcnode)
1877 1877 succrevs = {nodemap[s] for s in successors if s in nodemap}
1878 1878 if succrevs.issubset(extinctrevs):
1879 1879 # all successors are extinct
1880 1880 obsoleteextinctsuccessors.add(srcrev)
1881 1881 if not successors:
1882 1882 # no successor
1883 1883 obsoletenotrebased[srcrev] = None
1884 1884 else:
1885 1885 dstrev = destmap[srcrev]
1886 1886 for succrev in succrevs:
1887 1887 if cl.isancestorrev(succrev, dstrev):
1888 1888 obsoletenotrebased[srcrev] = succrev
1889 1889 break
1890 1890 else:
1891 1891 # If 'srcrev' has a successor in rebase set but none in
1892 1892 # destination (which would be catched above), we shall skip it
1893 1893 # and its descendants to avoid divergence.
1894 if any(s in destmap for s in succrevs):
1894 if srcrev in extinctrevs or any(s in destmap for s in succrevs):
1895 1895 obsoletewithoutsuccessorindestination.add(srcrev)
1896 1896
1897 1897 return (
1898 1898 obsoletenotrebased,
1899 1899 obsoletewithoutsuccessorindestination,
1900 1900 obsoleteextinctsuccessors,
1901 1901 )
1902 1902
1903 1903 def summaryhook(ui, repo):
1904 1904 if not repo.vfs.exists('rebasestate'):
1905 1905 return
1906 1906 try:
1907 1907 rbsrt = rebaseruntime(repo, ui, {})
1908 1908 rbsrt.restorestatus()
1909 1909 state = rbsrt.state
1910 1910 except error.RepoLookupError:
1911 1911 # i18n: column positioning for "hg summary"
1912 1912 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
1913 1913 ui.write(msg)
1914 1914 return
1915 1915 numrebased = len([i for i in state.itervalues() if i >= 0])
1916 1916 # i18n: column positioning for "hg summary"
1917 1917 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
1918 1918 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
1919 1919 ui.label(_('%d remaining'), 'rebase.remaining') %
1920 1920 (len(state) - numrebased)))
1921 1921
1922 1922 def uisetup(ui):
1923 1923 #Replace pull with a decorator to provide --rebase option
1924 1924 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
1925 1925 entry[1].append(('', 'rebase', None,
1926 1926 _("rebase working directory to branch head")))
1927 1927 entry[1].append(('t', 'tool', '',
1928 1928 _("specify merge tool for rebase")))
1929 1929 cmdutil.summaryhooks.add('rebase', summaryhook)
1930 1930 cmdutil.unfinishedstates.append(
1931 1931 ['rebasestate', False, False, _('rebase in progress'),
1932 1932 _("use 'hg rebase --continue' or 'hg rebase --abort'")])
1933 1933 cmdutil.afterresolvedstates.append(
1934 1934 ['rebasestate', _('hg rebase --continue')])
@@ -1,2139 +1,2137 b''
1 1 ==========================
2 2 Test rebase with obsolete
3 3 ==========================
4 4
5 5 Enable obsolete
6 6
7 7 $ cat >> $HGRCPATH << EOF
8 8 > [ui]
9 9 > logtemplate= {rev}:{node|short} {desc|firstline}{if(obsolete,' ({obsfate})')}
10 10 > [experimental]
11 11 > evolution.createmarkers=True
12 12 > evolution.allowunstable=True
13 13 > [phases]
14 14 > publish=False
15 15 > [extensions]
16 16 > rebase=
17 17 > drawdag=$TESTDIR/drawdag.py
18 18 > strip=
19 19 > EOF
20 20
21 21 Setup rebase canonical repo
22 22
23 23 $ hg init base
24 24 $ cd base
25 25 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
26 26 adding changesets
27 27 adding manifests
28 28 adding file changes
29 29 added 8 changesets with 7 changes to 7 files (+2 heads)
30 30 new changesets cd010b8cd998:02de42196ebe
31 31 (run 'hg heads' to see heads, 'hg merge' to merge)
32 32 $ hg up tip
33 33 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
34 34 $ hg log -G
35 35 @ 7:02de42196ebe H
36 36 |
37 37 | o 6:eea13746799a G
38 38 |/|
39 39 o | 5:24b6387c8c8c F
40 40 | |
41 41 | o 4:9520eea781bc E
42 42 |/
43 43 | o 3:32af7686d403 D
44 44 | |
45 45 | o 2:5fddd98957c8 C
46 46 | |
47 47 | o 1:42ccdea3bb16 B
48 48 |/
49 49 o 0:cd010b8cd998 A
50 50
51 51 $ cd ..
52 52
53 53 simple rebase
54 54 ---------------------------------
55 55
56 56 $ hg clone base simple
57 57 updating to branch default
58 58 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
59 59 $ cd simple
60 60 $ hg up 32af7686d403
61 61 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
62 62 $ hg rebase -d eea13746799a
63 63 rebasing 1:42ccdea3bb16 "B"
64 64 rebasing 2:5fddd98957c8 "C"
65 65 rebasing 3:32af7686d403 "D"
66 66 $ hg log -G
67 67 @ 10:8eeb3c33ad33 D
68 68 |
69 69 o 9:2327fea05063 C
70 70 |
71 71 o 8:e4e5be0395b2 B
72 72 |
73 73 | o 7:02de42196ebe H
74 74 | |
75 75 o | 6:eea13746799a G
76 76 |\|
77 77 | o 5:24b6387c8c8c F
78 78 | |
79 79 o | 4:9520eea781bc E
80 80 |/
81 81 o 0:cd010b8cd998 A
82 82
83 83 $ hg log --hidden -G
84 84 @ 10:8eeb3c33ad33 D
85 85 |
86 86 o 9:2327fea05063 C
87 87 |
88 88 o 8:e4e5be0395b2 B
89 89 |
90 90 | o 7:02de42196ebe H
91 91 | |
92 92 o | 6:eea13746799a G
93 93 |\|
94 94 | o 5:24b6387c8c8c F
95 95 | |
96 96 o | 4:9520eea781bc E
97 97 |/
98 98 | x 3:32af7686d403 D (rewritten using rebase as 10:8eeb3c33ad33)
99 99 | |
100 100 | x 2:5fddd98957c8 C (rewritten using rebase as 9:2327fea05063)
101 101 | |
102 102 | x 1:42ccdea3bb16 B (rewritten using rebase as 8:e4e5be0395b2)
103 103 |/
104 104 o 0:cd010b8cd998 A
105 105
106 106 $ hg debugobsolete
107 107 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 e4e5be0395b2cbd471ed22a26b1b6a1a0658a794 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
108 108 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 2327fea05063f39961b14cb69435a9898dc9a245 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
109 109 32af7686d403cf45b5d95f2d70cebea587ac806a 8eeb3c33ad33d452c89e5dcf611c347f978fb42b 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
110 110
111 111
112 112 $ cd ..
113 113
114 114 empty changeset
115 115 ---------------------------------
116 116
117 117 $ hg clone base empty
118 118 updating to branch default
119 119 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
120 120 $ cd empty
121 121 $ hg up eea13746799a
122 122 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
123 123
124 124 We make a copy of both the first changeset in the rebased and some other in the
125 125 set.
126 126
127 127 $ hg graft 42ccdea3bb16 32af7686d403
128 128 grafting 1:42ccdea3bb16 "B"
129 129 grafting 3:32af7686d403 "D"
130 130 $ hg rebase -s 42ccdea3bb16 -d .
131 131 rebasing 1:42ccdea3bb16 "B"
132 132 note: rebase of 1:42ccdea3bb16 created no changes to commit
133 133 rebasing 2:5fddd98957c8 "C"
134 134 rebasing 3:32af7686d403 "D"
135 135 note: rebase of 3:32af7686d403 created no changes to commit
136 136 $ hg log -G
137 137 o 10:5ae4c968c6ac C
138 138 |
139 139 @ 9:08483444fef9 D
140 140 |
141 141 o 8:8877864f1edb B
142 142 |
143 143 | o 7:02de42196ebe H
144 144 | |
145 145 o | 6:eea13746799a G
146 146 |\|
147 147 | o 5:24b6387c8c8c F
148 148 | |
149 149 o | 4:9520eea781bc E
150 150 |/
151 151 o 0:cd010b8cd998 A
152 152
153 153 $ hg log --hidden -G
154 154 o 10:5ae4c968c6ac C
155 155 |
156 156 @ 9:08483444fef9 D
157 157 |
158 158 o 8:8877864f1edb B
159 159 |
160 160 | o 7:02de42196ebe H
161 161 | |
162 162 o | 6:eea13746799a G
163 163 |\|
164 164 | o 5:24b6387c8c8c F
165 165 | |
166 166 o | 4:9520eea781bc E
167 167 |/
168 168 | x 3:32af7686d403 D (pruned using rebase)
169 169 | |
170 170 | x 2:5fddd98957c8 C (rewritten using rebase as 10:5ae4c968c6ac)
171 171 | |
172 172 | x 1:42ccdea3bb16 B (pruned using rebase)
173 173 |/
174 174 o 0:cd010b8cd998 A
175 175
176 176 $ hg debugobsolete
177 177 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'rebase', 'user': 'test'}
178 178 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
179 179 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'rebase', 'user': 'test'}
180 180
181 181
182 182 More complex case where part of the rebase set were already rebased
183 183
184 184 $ hg rebase --rev 'desc(D)' --dest 'desc(H)'
185 185 rebasing 9:08483444fef9 "D"
186 186 1 new orphan changesets
187 187 $ hg debugobsolete
188 188 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'rebase', 'user': 'test'}
189 189 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
190 190 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'rebase', 'user': 'test'}
191 191 08483444fef91d6224f6655ee586a65d263ad34c 4596109a6a4328c398bde3a4a3b6737cfade3003 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
192 192 $ hg log -G
193 193 @ 11:4596109a6a43 D
194 194 |
195 195 | * 10:5ae4c968c6ac C
196 196 | |
197 197 | x 9:08483444fef9 D (rewritten using rebase as 11:4596109a6a43)
198 198 | |
199 199 | o 8:8877864f1edb B
200 200 | |
201 201 o | 7:02de42196ebe H
202 202 | |
203 203 | o 6:eea13746799a G
204 204 |/|
205 205 o | 5:24b6387c8c8c F
206 206 | |
207 207 | o 4:9520eea781bc E
208 208 |/
209 209 o 0:cd010b8cd998 A
210 210
211 211 $ hg rebase --source 'desc(B)' --dest 'tip' --config experimental.rebaseskipobsolete=True
212 212 rebasing 8:8877864f1edb "B"
213 213 note: not rebasing 9:08483444fef9 "D", already in destination as 11:4596109a6a43 "D" (tip)
214 214 rebasing 10:5ae4c968c6ac "C"
215 215 $ hg debugobsolete
216 216 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'rebase', 'user': 'test'}
217 217 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
218 218 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'rebase', 'user': 'test'}
219 219 08483444fef91d6224f6655ee586a65d263ad34c 4596109a6a4328c398bde3a4a3b6737cfade3003 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
220 220 8877864f1edb05d0e07dc4ba77b67a80a7b86672 462a34d07e599b87ea08676a449373fe4e2e1347 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
221 221 5ae4c968c6aca831df823664e706c9d4aa34473d 98f6af4ee9539e14da4465128f894c274900b6e5 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
222 222 $ hg log --rev 'contentdivergent()'
223 223 $ hg log -G
224 224 o 13:98f6af4ee953 C
225 225 |
226 226 o 12:462a34d07e59 B
227 227 |
228 228 @ 11:4596109a6a43 D
229 229 |
230 230 o 7:02de42196ebe H
231 231 |
232 232 | o 6:eea13746799a G
233 233 |/|
234 234 o | 5:24b6387c8c8c F
235 235 | |
236 236 | o 4:9520eea781bc E
237 237 |/
238 238 o 0:cd010b8cd998 A
239 239
240 240 $ hg log --style default --debug -r 4596109a6a4328c398bde3a4a3b6737cfade3003
241 241 changeset: 11:4596109a6a4328c398bde3a4a3b6737cfade3003
242 242 phase: draft
243 243 parent: 7:02de42196ebee42ef284b6780a87cdc96e8eaab6
244 244 parent: -1:0000000000000000000000000000000000000000
245 245 manifest: 11:a91006e3a02f1edf631f7018e6e5684cf27dd905
246 246 user: Nicolas Dumazet <nicdumz.commits@gmail.com>
247 247 date: Sat Apr 30 15:24:48 2011 +0200
248 248 files+: D
249 249 extra: branch=default
250 250 extra: rebase_source=08483444fef91d6224f6655ee586a65d263ad34c
251 251 extra: source=32af7686d403cf45b5d95f2d70cebea587ac806a
252 252 description:
253 253 D
254 254
255 255
256 256 $ hg up -qr 'desc(G)'
257 257 $ hg graft 4596109a6a4328c398bde3a4a3b6737cfade3003
258 258 grafting 11:4596109a6a43 "D"
259 259 $ hg up -qr 'desc(E)'
260 260 $ hg rebase -s tip -d .
261 261 rebasing 14:9e36056a46e3 "D" (tip)
262 262 $ hg log --style default --debug -r tip
263 263 changeset: 15:627d4614809036ba22b9e7cb31638ddc06ab99ab
264 264 tag: tip
265 265 phase: draft
266 266 parent: 4:9520eea781bcca16c1e15acc0ba14335a0e8e5ba
267 267 parent: -1:0000000000000000000000000000000000000000
268 268 manifest: 15:648e8ede73ae3e497d093d3a4c8fcc2daa864f42
269 269 user: Nicolas Dumazet <nicdumz.commits@gmail.com>
270 270 date: Sat Apr 30 15:24:48 2011 +0200
271 271 files+: D
272 272 extra: branch=default
273 273 extra: intermediate-source=4596109a6a4328c398bde3a4a3b6737cfade3003
274 274 extra: rebase_source=9e36056a46e37c9776168c7375734eebc70e294f
275 275 extra: source=32af7686d403cf45b5d95f2d70cebea587ac806a
276 276 description:
277 277 D
278 278
279 279
280 280 Start rebase from a commit that is obsolete but not hidden only because it's
281 281 a working copy parent. We should be moved back to the starting commit as usual
282 282 even though it is hidden (until we're moved there).
283 283
284 284 $ hg --hidden up -qr 'first(hidden())'
285 285 updated to hidden changeset 42ccdea3bb16
286 286 (hidden revision '42ccdea3bb16' is pruned)
287 287 $ hg rebase --rev 13 --dest 15
288 288 rebasing 13:98f6af4ee953 "C"
289 289 $ hg log -G
290 290 o 16:294a2b93eb4d C
291 291 |
292 292 o 15:627d46148090 D
293 293 |
294 294 | o 12:462a34d07e59 B
295 295 | |
296 296 | o 11:4596109a6a43 D
297 297 | |
298 298 | o 7:02de42196ebe H
299 299 | |
300 300 +---o 6:eea13746799a G
301 301 | |/
302 302 | o 5:24b6387c8c8c F
303 303 | |
304 304 o | 4:9520eea781bc E
305 305 |/
306 306 | @ 1:42ccdea3bb16 B (pruned using rebase)
307 307 |/
308 308 o 0:cd010b8cd998 A
309 309
310 310
311 311 $ cd ..
312 312
313 313 collapse rebase
314 314 ---------------------------------
315 315
316 316 $ hg clone base collapse
317 317 updating to branch default
318 318 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
319 319 $ cd collapse
320 320 $ hg rebase -s 42ccdea3bb16 -d eea13746799a --collapse
321 321 rebasing 1:42ccdea3bb16 "B"
322 322 rebasing 2:5fddd98957c8 "C"
323 323 rebasing 3:32af7686d403 "D"
324 324 $ hg log -G
325 325 o 8:4dc2197e807b Collapsed revision
326 326 |
327 327 | @ 7:02de42196ebe H
328 328 | |
329 329 o | 6:eea13746799a G
330 330 |\|
331 331 | o 5:24b6387c8c8c F
332 332 | |
333 333 o | 4:9520eea781bc E
334 334 |/
335 335 o 0:cd010b8cd998 A
336 336
337 337 $ hg log --hidden -G
338 338 o 8:4dc2197e807b Collapsed revision
339 339 |
340 340 | @ 7:02de42196ebe H
341 341 | |
342 342 o | 6:eea13746799a G
343 343 |\|
344 344 | o 5:24b6387c8c8c F
345 345 | |
346 346 o | 4:9520eea781bc E
347 347 |/
348 348 | x 3:32af7686d403 D (rewritten using rebase as 8:4dc2197e807b)
349 349 | |
350 350 | x 2:5fddd98957c8 C (rewritten using rebase as 8:4dc2197e807b)
351 351 | |
352 352 | x 1:42ccdea3bb16 B (rewritten using rebase as 8:4dc2197e807b)
353 353 |/
354 354 o 0:cd010b8cd998 A
355 355
356 356 $ hg id --debug -r tip
357 357 4dc2197e807bae9817f09905b50ab288be2dbbcf tip
358 358 $ hg debugobsolete
359 359 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '13', 'operation': 'rebase', 'user': 'test'}
360 360 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '13', 'operation': 'rebase', 'user': 'test'}
361 361 32af7686d403cf45b5d95f2d70cebea587ac806a 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '13', 'operation': 'rebase', 'user': 'test'}
362 362
363 363 $ cd ..
364 364
365 365 Rebase set has hidden descendants
366 366 ---------------------------------
367 367
368 368 We rebase a changeset which has hidden descendants. Hidden changesets must not
369 369 be rebased.
370 370
371 371 $ hg clone base hidden
372 372 updating to branch default
373 373 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
374 374 $ cd hidden
375 375 $ hg log -G
376 376 @ 7:02de42196ebe H
377 377 |
378 378 | o 6:eea13746799a G
379 379 |/|
380 380 o | 5:24b6387c8c8c F
381 381 | |
382 382 | o 4:9520eea781bc E
383 383 |/
384 384 | o 3:32af7686d403 D
385 385 | |
386 386 | o 2:5fddd98957c8 C
387 387 | |
388 388 | o 1:42ccdea3bb16 B
389 389 |/
390 390 o 0:cd010b8cd998 A
391 391
392 392 $ hg rebase -s 5fddd98957c8 -d eea13746799a
393 393 rebasing 2:5fddd98957c8 "C"
394 394 rebasing 3:32af7686d403 "D"
395 395 $ hg log -G
396 396 o 9:cf44d2f5a9f4 D
397 397 |
398 398 o 8:e273c5e7d2d2 C
399 399 |
400 400 | @ 7:02de42196ebe H
401 401 | |
402 402 o | 6:eea13746799a G
403 403 |\|
404 404 | o 5:24b6387c8c8c F
405 405 | |
406 406 o | 4:9520eea781bc E
407 407 |/
408 408 | o 1:42ccdea3bb16 B
409 409 |/
410 410 o 0:cd010b8cd998 A
411 411
412 412 $ hg rebase -s 42ccdea3bb16 -d 02de42196ebe
413 413 rebasing 1:42ccdea3bb16 "B"
414 414 $ hg log -G
415 415 o 10:7c6027df6a99 B
416 416 |
417 417 | o 9:cf44d2f5a9f4 D
418 418 | |
419 419 | o 8:e273c5e7d2d2 C
420 420 | |
421 421 @ | 7:02de42196ebe H
422 422 | |
423 423 | o 6:eea13746799a G
424 424 |/|
425 425 o | 5:24b6387c8c8c F
426 426 | |
427 427 | o 4:9520eea781bc E
428 428 |/
429 429 o 0:cd010b8cd998 A
430 430
431 431 $ hg log --hidden -G
432 432 o 10:7c6027df6a99 B
433 433 |
434 434 | o 9:cf44d2f5a9f4 D
435 435 | |
436 436 | o 8:e273c5e7d2d2 C
437 437 | |
438 438 @ | 7:02de42196ebe H
439 439 | |
440 440 | o 6:eea13746799a G
441 441 |/|
442 442 o | 5:24b6387c8c8c F
443 443 | |
444 444 | o 4:9520eea781bc E
445 445 |/
446 446 | x 3:32af7686d403 D (rewritten using rebase as 9:cf44d2f5a9f4)
447 447 | |
448 448 | x 2:5fddd98957c8 C (rewritten using rebase as 8:e273c5e7d2d2)
449 449 | |
450 450 | x 1:42ccdea3bb16 B (rewritten using rebase as 10:7c6027df6a99)
451 451 |/
452 452 o 0:cd010b8cd998 A
453 453
454 454 $ hg debugobsolete
455 455 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b e273c5e7d2d29df783dce9f9eaa3ac4adc69c15d 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
456 456 32af7686d403cf45b5d95f2d70cebea587ac806a cf44d2f5a9f4297a62be94cbdd3dff7c7dc54258 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
457 457 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 7c6027df6a99d93f461868e5433f63bde20b6dfb 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
458 458
459 459 Test that rewriting leaving instability behind is allowed
460 460 ---------------------------------------------------------------------
461 461
462 462 $ hg log -r 'children(8)'
463 463 9:cf44d2f5a9f4 D (no-eol)
464 464 $ hg rebase -r 8
465 465 rebasing 8:e273c5e7d2d2 "C"
466 466 1 new orphan changesets
467 467 $ hg log -G
468 468 o 11:0d8f238b634c C
469 469 |
470 470 o 10:7c6027df6a99 B
471 471 |
472 472 | * 9:cf44d2f5a9f4 D
473 473 | |
474 474 | x 8:e273c5e7d2d2 C (rewritten using rebase as 11:0d8f238b634c)
475 475 | |
476 476 @ | 7:02de42196ebe H
477 477 | |
478 478 | o 6:eea13746799a G
479 479 |/|
480 480 o | 5:24b6387c8c8c F
481 481 | |
482 482 | o 4:9520eea781bc E
483 483 |/
484 484 o 0:cd010b8cd998 A
485 485
486 486 $ cd ..
487 487 $ cp -R hidden stabilize
488 488 $ cd stabilize
489 489 $ hg rebase --auto-orphans '0::' -d 10
490 490 abort: --auto-orphans is incompatible with --dest
491 491 [255]
492 492 $ hg rebase --auto-orphans '0::'
493 493 rebasing 9:cf44d2f5a9f4 "D"
494 494 $ hg log -G
495 495 o 12:7e3935feaa68 D
496 496 |
497 497 o 11:0d8f238b634c C
498 498 |
499 499 o 10:7c6027df6a99 B
500 500 |
501 501 @ 7:02de42196ebe H
502 502 |
503 503 | o 6:eea13746799a G
504 504 |/|
505 505 o | 5:24b6387c8c8c F
506 506 | |
507 507 | o 4:9520eea781bc E
508 508 |/
509 509 o 0:cd010b8cd998 A
510 510
511 511
512 512 $ cd ../hidden
513 513 $ rm -r ../stabilize
514 514
515 515 Test multiple root handling
516 516 ------------------------------------
517 517
518 518 $ hg rebase --dest 4 --rev '7+11+9'
519 519 rebasing 9:cf44d2f5a9f4 "D"
520 520 rebasing 7:02de42196ebe "H"
521 521 rebasing 11:0d8f238b634c "C" (tip)
522 522 $ hg log -G
523 523 o 14:1e8370e38cca C
524 524 |
525 525 @ 13:bfe264faf697 H
526 526 |
527 527 | o 12:102b4c1d889b D
528 528 |/
529 529 | * 10:7c6027df6a99 B
530 530 | |
531 531 | x 7:02de42196ebe H (rewritten using rebase as 13:bfe264faf697)
532 532 | |
533 533 +---o 6:eea13746799a G
534 534 | |/
535 535 | o 5:24b6387c8c8c F
536 536 | |
537 537 o | 4:9520eea781bc E
538 538 |/
539 539 o 0:cd010b8cd998 A
540 540
541 541 $ cd ..
542 542
543 543 Detach both parents
544 544
545 545 $ hg init double-detach
546 546 $ cd double-detach
547 547
548 548 $ hg debugdrawdag <<EOF
549 549 > F
550 550 > /|
551 551 > C E
552 552 > | |
553 553 > B D G
554 554 > \|/
555 555 > A
556 556 > EOF
557 557
558 558 $ hg rebase -d G -r 'B + D + F'
559 559 rebasing 1:112478962961 "B" (B)
560 560 rebasing 2:b18e25de2cf5 "D" (D)
561 561 rebasing 6:f15c3adaf214 "F" (F tip)
562 562 abort: cannot rebase 6:f15c3adaf214 without moving at least one of its parents
563 563 [255]
564 564
565 565 $ cd ..
566 566
567 567 test on rebase dropping a merge
568 568
569 569 (setup)
570 570
571 571 $ hg init dropmerge
572 572 $ cd dropmerge
573 573 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
574 574 adding changesets
575 575 adding manifests
576 576 adding file changes
577 577 added 8 changesets with 7 changes to 7 files (+2 heads)
578 578 new changesets cd010b8cd998:02de42196ebe
579 579 (run 'hg heads' to see heads, 'hg merge' to merge)
580 580 $ hg up 3
581 581 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
582 582 $ hg merge 7
583 583 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
584 584 (branch merge, don't forget to commit)
585 585 $ hg ci -m 'M'
586 586 $ echo I > I
587 587 $ hg add I
588 588 $ hg ci -m I
589 589 $ hg log -G
590 590 @ 9:4bde274eefcf I
591 591 |
592 592 o 8:53a6a128b2b7 M
593 593 |\
594 594 | o 7:02de42196ebe H
595 595 | |
596 596 | | o 6:eea13746799a G
597 597 | |/|
598 598 | o | 5:24b6387c8c8c F
599 599 | | |
600 600 | | o 4:9520eea781bc E
601 601 | |/
602 602 o | 3:32af7686d403 D
603 603 | |
604 604 o | 2:5fddd98957c8 C
605 605 | |
606 606 o | 1:42ccdea3bb16 B
607 607 |/
608 608 o 0:cd010b8cd998 A
609 609
610 610 (actual test)
611 611
612 612 $ hg rebase --dest 6 --rev '((desc(H) + desc(D))::) - desc(M)'
613 613 rebasing 3:32af7686d403 "D"
614 614 rebasing 7:02de42196ebe "H"
615 615 rebasing 9:4bde274eefcf "I" (tip)
616 616 1 new orphan changesets
617 617 $ hg log -G
618 618 @ 12:acd174b7ab39 I
619 619 |
620 620 o 11:6c11a6218c97 H
621 621 |
622 622 | o 10:b5313c85b22e D
623 623 |/
624 624 | * 8:53a6a128b2b7 M
625 625 | |\
626 626 | | x 7:02de42196ebe H (rewritten using rebase as 11:6c11a6218c97)
627 627 | | |
628 628 o---+ 6:eea13746799a G
629 629 | | |
630 630 | | o 5:24b6387c8c8c F
631 631 | | |
632 632 o---+ 4:9520eea781bc E
633 633 / /
634 634 x | 3:32af7686d403 D (rewritten using rebase as 10:b5313c85b22e)
635 635 | |
636 636 o | 2:5fddd98957c8 C
637 637 | |
638 638 o | 1:42ccdea3bb16 B
639 639 |/
640 640 o 0:cd010b8cd998 A
641 641
642 642
643 643 Test hidden changesets in the rebase set (issue4504)
644 644
645 645 $ hg up --hidden 9
646 646 3 files updated, 0 files merged, 1 files removed, 0 files unresolved
647 647 updated to hidden changeset 4bde274eefcf
648 648 (hidden revision '4bde274eefcf' was rewritten as: acd174b7ab39)
649 649 $ echo J > J
650 650 $ hg add J
651 651 $ hg commit -m J
652 652 1 new orphan changesets
653 653 $ hg debugobsolete `hg log --rev . -T '{node}'`
654 654 obsoleted 1 changesets
655 655
656 656 $ hg rebase --rev .~1::. --dest 'max(desc(D))' --traceback --config experimental.rebaseskipobsolete=off
657 657 rebasing 9:4bde274eefcf "I"
658 658 rebasing 13:06edfc82198f "J" (tip)
659 659 2 new content-divergent changesets
660 660 $ hg log -G
661 661 @ 15:5ae8a643467b J
662 662 |
663 663 * 14:9ad579b4a5de I
664 664 |
665 665 | * 12:acd174b7ab39 I
666 666 | |
667 667 | o 11:6c11a6218c97 H
668 668 | |
669 669 o | 10:b5313c85b22e D
670 670 |/
671 671 | * 8:53a6a128b2b7 M
672 672 | |\
673 673 | | x 7:02de42196ebe H (rewritten using rebase as 11:6c11a6218c97)
674 674 | | |
675 675 o---+ 6:eea13746799a G
676 676 | | |
677 677 | | o 5:24b6387c8c8c F
678 678 | | |
679 679 o---+ 4:9520eea781bc E
680 680 / /
681 681 x | 3:32af7686d403 D (rewritten using rebase as 10:b5313c85b22e)
682 682 | |
683 683 o | 2:5fddd98957c8 C
684 684 | |
685 685 o | 1:42ccdea3bb16 B
686 686 |/
687 687 o 0:cd010b8cd998 A
688 688
689 689 $ hg up 14 -C
690 690 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
691 691 $ echo "K" > K
692 692 $ hg add K
693 693 $ hg commit --amend -m "K"
694 694 1 new orphan changesets
695 695 $ echo "L" > L
696 696 $ hg add L
697 697 $ hg commit -m "L"
698 698 $ hg up '.^'
699 699 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
700 700 $ echo "M" > M
701 701 $ hg add M
702 702 $ hg commit --amend -m "M"
703 703 1 new orphan changesets
704 704 $ hg log -G
705 705 @ 18:bfaedf8eb73b M
706 706 |
707 707 | * 17:97219452e4bd L
708 708 | |
709 709 | x 16:fc37a630c901 K (rewritten using amend as 18:bfaedf8eb73b)
710 710 |/
711 711 | * 15:5ae8a643467b J
712 712 | |
713 713 | x 14:9ad579b4a5de I (rewritten using amend as 16:fc37a630c901)
714 714 |/
715 715 | * 12:acd174b7ab39 I
716 716 | |
717 717 | o 11:6c11a6218c97 H
718 718 | |
719 719 o | 10:b5313c85b22e D
720 720 |/
721 721 | * 8:53a6a128b2b7 M
722 722 | |\
723 723 | | x 7:02de42196ebe H (rewritten using rebase as 11:6c11a6218c97)
724 724 | | |
725 725 o---+ 6:eea13746799a G
726 726 | | |
727 727 | | o 5:24b6387c8c8c F
728 728 | | |
729 729 o---+ 4:9520eea781bc E
730 730 / /
731 731 x | 3:32af7686d403 D (rewritten using rebase as 10:b5313c85b22e)
732 732 | |
733 733 o | 2:5fddd98957c8 C
734 734 | |
735 735 o | 1:42ccdea3bb16 B
736 736 |/
737 737 o 0:cd010b8cd998 A
738 738
739 739 $ hg rebase -s 14 -d 17 --config experimental.rebaseskipobsolete=True
740 740 note: not rebasing 14:9ad579b4a5de "I", already in destination as 16:fc37a630c901 "K"
741 741 rebasing 15:5ae8a643467b "J"
742 742 1 new orphan changesets
743 743
744 744 $ cd ..
745 745
746 746 Skip obsolete changeset even with multiple hops
747 747 -----------------------------------------------
748 748
749 749 setup
750 750
751 751 $ hg init obsskip
752 752 $ cd obsskip
753 753 $ cat << EOF >> .hg/hgrc
754 754 > [experimental]
755 755 > rebaseskipobsolete = True
756 756 > [extensions]
757 757 > strip =
758 758 > EOF
759 759 $ echo A > A
760 760 $ hg add A
761 761 $ hg commit -m A
762 762 $ echo B > B
763 763 $ hg add B
764 764 $ hg commit -m B0
765 765 $ hg commit --amend -m B1
766 766 $ hg commit --amend -m B2
767 767 $ hg up --hidden 'desc(B0)'
768 768 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
769 769 updated to hidden changeset a8b11f55fb19
770 770 (hidden revision 'a8b11f55fb19' was rewritten as: 261e70097290)
771 771 $ echo C > C
772 772 $ hg add C
773 773 $ hg commit -m C
774 774 1 new orphan changesets
775 775 $ hg log -G
776 776 @ 4:212cb178bcbb C
777 777 |
778 778 | o 3:261e70097290 B2
779 779 | |
780 780 x | 1:a8b11f55fb19 B0 (rewritten using amend as 3:261e70097290)
781 781 |/
782 782 o 0:4a2df7238c3b A
783 783
784 784
785 785 Rebase finds its way in a chain of marker
786 786
787 787 $ hg rebase -d 'desc(B2)'
788 788 note: not rebasing 1:a8b11f55fb19 "B0", already in destination as 3:261e70097290 "B2"
789 789 rebasing 4:212cb178bcbb "C" (tip)
790 790
791 791 Even when the chain include missing node
792 792
793 793 $ hg up --hidden 'desc(B0)'
794 794 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
795 795 updated to hidden changeset a8b11f55fb19
796 796 (hidden revision 'a8b11f55fb19' was rewritten as: 261e70097290)
797 797 $ echo D > D
798 798 $ hg add D
799 799 $ hg commit -m D
800 800 1 new orphan changesets
801 801 $ hg --hidden strip -r 'desc(B1)'
802 802 saved backup bundle to $TESTTMP/obsskip/.hg/strip-backup/86f6414ccda7-b1c452ee-backup.hg
803 803 1 new orphan changesets
804 804 $ hg log -G
805 805 @ 5:1a79b7535141 D
806 806 |
807 807 | o 4:ff2c4d47b71d C
808 808 | |
809 809 | o 2:261e70097290 B2
810 810 | |
811 811 x | 1:a8b11f55fb19 B0 (rewritten using amend as 2:261e70097290)
812 812 |/
813 813 o 0:4a2df7238c3b A
814 814
815 815
816 816 $ hg rebase -d 'desc(B2)'
817 817 note: not rebasing 1:a8b11f55fb19 "B0", already in destination as 2:261e70097290 "B2"
818 818 rebasing 5:1a79b7535141 "D" (tip)
819 819 $ hg up 4
820 820 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
821 821 $ echo "O" > O
822 822 $ hg add O
823 823 $ hg commit -m O
824 824 $ echo "P" > P
825 825 $ hg add P
826 826 $ hg commit -m P
827 827 $ hg log -G
828 828 @ 8:8d47583e023f P
829 829 |
830 830 o 7:360bbaa7d3ce O
831 831 |
832 832 | o 6:9c48361117de D
833 833 | |
834 834 o | 4:ff2c4d47b71d C
835 835 |/
836 836 o 2:261e70097290 B2
837 837 |
838 838 o 0:4a2df7238c3b A
839 839
840 840 $ hg debugobsolete `hg log -r 7 -T '{node}\n'` --config experimental.evolution=true
841 841 obsoleted 1 changesets
842 842 1 new orphan changesets
843 843 $ hg rebase -d 6 -r "4::"
844 844 rebasing 4:ff2c4d47b71d "C"
845 845 note: not rebasing 7:360bbaa7d3ce "O", it has no successor
846 846 rebasing 8:8d47583e023f "P" (tip)
847 847
848 848 If all the changeset to be rebased are obsolete and present in the destination, we
849 849 should display a friendly error message
850 850
851 851 $ hg log -G
852 852 @ 10:121d9e3bc4c6 P
853 853 |
854 854 o 9:4be60e099a77 C
855 855 |
856 856 o 6:9c48361117de D
857 857 |
858 858 o 2:261e70097290 B2
859 859 |
860 860 o 0:4a2df7238c3b A
861 861
862 862
863 863 $ hg up 9
864 864 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
865 865 $ echo "non-relevant change" > nonrelevant
866 866 $ hg add nonrelevant
867 867 $ hg commit -m nonrelevant
868 868 created new head
869 869 $ hg debugobsolete `hg log -r 11 -T '{node}\n'` --config experimental.evolution=true
870 870 obsoleted 1 changesets
871 871 $ hg log -G
872 872 @ 11:f44da1f4954c nonrelevant (pruned)
873 873 |
874 874 | o 10:121d9e3bc4c6 P
875 875 |/
876 876 o 9:4be60e099a77 C
877 877 |
878 878 o 6:9c48361117de D
879 879 |
880 880 o 2:261e70097290 B2
881 881 |
882 882 o 0:4a2df7238c3b A
883 883
884 884 $ hg rebase -r . -d 10
885 885 note: not rebasing 11:f44da1f4954c "nonrelevant" (tip), it has no successor
886 886
887 887 If a rebase is going to create divergence, it should abort
888 888
889 889 $ hg log -G
890 890 @ 10:121d9e3bc4c6 P
891 891 |
892 892 o 9:4be60e099a77 C
893 893 |
894 894 o 6:9c48361117de D
895 895 |
896 896 o 2:261e70097290 B2
897 897 |
898 898 o 0:4a2df7238c3b A
899 899
900 900
901 901 $ hg up 9
902 902 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
903 903 $ echo "john" > doe
904 904 $ hg add doe
905 905 $ hg commit -m "john doe"
906 906 created new head
907 907 $ hg up 10
908 908 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
909 909 $ echo "foo" > bar
910 910 $ hg add bar
911 911 $ hg commit --amend -m "10'"
912 912 $ hg up 10 --hidden
913 913 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
914 914 updated to hidden changeset 121d9e3bc4c6
915 915 (hidden revision '121d9e3bc4c6' was rewritten as: 77d874d096a2)
916 916 $ echo "bar" > foo
917 917 $ hg add foo
918 918 $ hg commit -m "bar foo"
919 919 1 new orphan changesets
920 920 $ hg log -G
921 921 @ 14:73568ab6879d bar foo
922 922 |
923 923 | o 13:77d874d096a2 10'
924 924 | |
925 925 | | o 12:3eb461388009 john doe
926 926 | |/
927 927 x | 10:121d9e3bc4c6 P (rewritten using amend as 13:77d874d096a2)
928 928 |/
929 929 o 9:4be60e099a77 C
930 930 |
931 931 o 6:9c48361117de D
932 932 |
933 933 o 2:261e70097290 B2
934 934 |
935 935 o 0:4a2df7238c3b A
936 936
937 937 $ hg summary
938 938 parent: 14:73568ab6879d tip (orphan)
939 939 bar foo
940 940 branch: default
941 941 commit: (clean)
942 942 update: 2 new changesets, 3 branch heads (merge)
943 943 phases: 8 draft
944 944 orphan: 1 changesets
945 945 $ hg rebase -s 10 -d 12
946 946 abort: this rebase will cause divergences from: 121d9e3bc4c6
947 947 (to force the rebase please set experimental.evolution.allowdivergence=True)
948 948 [255]
949 949 $ hg log -G
950 950 @ 14:73568ab6879d bar foo
951 951 |
952 952 | o 13:77d874d096a2 10'
953 953 | |
954 954 | | o 12:3eb461388009 john doe
955 955 | |/
956 956 x | 10:121d9e3bc4c6 P (rewritten using amend as 13:77d874d096a2)
957 957 |/
958 958 o 9:4be60e099a77 C
959 959 |
960 960 o 6:9c48361117de D
961 961 |
962 962 o 2:261e70097290 B2
963 963 |
964 964 o 0:4a2df7238c3b A
965 965
966 966 With experimental.evolution.allowdivergence=True, rebase can create divergence
967 967
968 968 $ hg rebase -s 10 -d 12 --config experimental.evolution.allowdivergence=True
969 969 rebasing 10:121d9e3bc4c6 "P"
970 970 rebasing 14:73568ab6879d "bar foo" (tip)
971 971 2 new content-divergent changesets
972 972 $ hg summary
973 973 parent: 16:61bd55f69bc4 tip
974 974 bar foo
975 975 branch: default
976 976 commit: (clean)
977 977 update: 1 new changesets, 2 branch heads (merge)
978 978 phases: 8 draft
979 979 content-divergent: 2 changesets
980 980
981 981 rebase --continue + skipped rev because their successors are in destination
982 982 we make a change in trunk and work on conflicting changes to make rebase abort.
983 983
984 984 $ hg log -G -r 16::
985 985 @ 16:61bd55f69bc4 bar foo
986 986 |
987 987 ~
988 988
989 989 Create the two changes in trunk
990 990 $ printf "a" > willconflict
991 991 $ hg add willconflict
992 992 $ hg commit -m "willconflict first version"
993 993
994 994 $ printf "dummy" > C
995 995 $ hg commit -m "dummy change successor"
996 996
997 997 Create the changes that we will rebase
998 998 $ hg update -C 16 -q
999 999 $ printf "b" > willconflict
1000 1000 $ hg add willconflict
1001 1001 $ hg commit -m "willconflict second version"
1002 1002 created new head
1003 1003 $ printf "dummy" > K
1004 1004 $ hg add K
1005 1005 $ hg commit -m "dummy change"
1006 1006 $ printf "dummy" > L
1007 1007 $ hg add L
1008 1008 $ hg commit -m "dummy change"
1009 1009 $ hg debugobsolete `hg log -r ".^" -T '{node}'` `hg log -r 18 -T '{node}'` --config experimental.evolution=true
1010 1010 obsoleted 1 changesets
1011 1011 1 new orphan changesets
1012 1012
1013 1013 $ hg log -G -r 16::
1014 1014 @ 21:7bdc8a87673d dummy change
1015 1015 |
1016 1016 x 20:8b31da3c4919 dummy change (rewritten as 18:601db7a18f51)
1017 1017 |
1018 1018 o 19:b82fb57ea638 willconflict second version
1019 1019 |
1020 1020 | o 18:601db7a18f51 dummy change successor
1021 1021 | |
1022 1022 | o 17:357ddf1602d5 willconflict first version
1023 1023 |/
1024 1024 o 16:61bd55f69bc4 bar foo
1025 1025 |
1026 1026 ~
1027 1027 $ hg rebase -r ".^^ + .^ + ." -d 18
1028 1028 rebasing 19:b82fb57ea638 "willconflict second version"
1029 1029 merging willconflict
1030 1030 warning: conflicts while merging willconflict! (edit, then use 'hg resolve --mark')
1031 1031 unresolved conflicts (see hg resolve, then hg rebase --continue)
1032 1032 [1]
1033 1033
1034 1034 $ hg resolve --mark willconflict
1035 1035 (no more unresolved files)
1036 1036 continue: hg rebase --continue
1037 1037 $ hg rebase --continue
1038 1038 rebasing 19:b82fb57ea638 "willconflict second version"
1039 1039 note: not rebasing 20:8b31da3c4919 "dummy change", already in destination as 18:601db7a18f51 "dummy change successor"
1040 1040 rebasing 21:7bdc8a87673d "dummy change" (tip)
1041 1041 $ cd ..
1042 1042
1043 1043 Divergence cases due to obsolete changesets
1044 1044 -------------------------------------------
1045 1045
1046 1046 We should ignore branches with unstable changesets when they are based on an
1047 1047 obsolete changeset which successor is in rebase set.
1048 1048
1049 1049 $ hg init divergence
1050 1050 $ cd divergence
1051 1051 $ cat >> .hg/hgrc << EOF
1052 1052 > [extensions]
1053 1053 > strip =
1054 1054 > [alias]
1055 1055 > strip = strip --no-backup --quiet
1056 1056 > [templates]
1057 1057 > instabilities = '{rev}:{node|short} {desc|firstline}{if(instabilities," ({instabilities})")}\n'
1058 1058 > EOF
1059 1059
1060 1060 $ hg debugdrawdag <<EOF
1061 1061 > e f
1062 1062 > | |
1063 1063 > d' d # replace: d -> d'
1064 1064 > \ /
1065 1065 > c
1066 1066 > |
1067 1067 > x b
1068 1068 > \|
1069 1069 > a
1070 1070 > EOF
1071 1071 1 new orphan changesets
1072 1072 $ hg log -G -r 'a'::
1073 1073 * 7:1143e9adc121 f
1074 1074 |
1075 1075 | o 6:d60ebfa0f1cb e
1076 1076 | |
1077 1077 | o 5:027ad6c5830d d'
1078 1078 | |
1079 1079 x | 4:76be324c128b d (rewritten using replace as 5:027ad6c5830d)
1080 1080 |/
1081 1081 o 3:a82ac2b38757 c
1082 1082 |
1083 1083 | o 2:630d7c95eff7 x
1084 1084 | |
1085 1085 o | 1:488e1b7e7341 b
1086 1086 |/
1087 1087 o 0:b173517d0057 a
1088 1088
1089 1089
1090 1090 Changeset d and its descendants are excluded to avoid divergence of d, which
1091 1091 would occur because the successor of d (d') is also in rebaseset. As a
1092 1092 consequence f (descendant of d) is left behind.
1093 1093
1094 1094 $ hg rebase -b 'e' -d 'x'
1095 1095 rebasing 1:488e1b7e7341 "b" (b)
1096 1096 rebasing 3:a82ac2b38757 "c" (c)
1097 1097 rebasing 5:027ad6c5830d "d'" (d')
1098 1098 rebasing 6:d60ebfa0f1cb "e" (e)
1099 1099 note: not rebasing 4:76be324c128b "d" (d) and its descendants as this would cause divergence
1100 1100 $ hg log -G -r 'a'::
1101 1101 o 11:eb6d63fc4ed5 e
1102 1102 |
1103 1103 o 10:44d8c724a70c d'
1104 1104 |
1105 1105 o 9:d008e6b4d3fd c
1106 1106 |
1107 1107 o 8:67e8f4a16c49 b
1108 1108 |
1109 1109 | * 7:1143e9adc121 f
1110 1110 | |
1111 1111 | | x 6:d60ebfa0f1cb e (rewritten using rebase as 11:eb6d63fc4ed5)
1112 1112 | | |
1113 1113 | | x 5:027ad6c5830d d' (rewritten using rebase as 10:44d8c724a70c)
1114 1114 | | |
1115 1115 | x | 4:76be324c128b d (rewritten using replace as 5:027ad6c5830d)
1116 1116 | |/
1117 1117 | x 3:a82ac2b38757 c (rewritten using rebase as 9:d008e6b4d3fd)
1118 1118 | |
1119 1119 o | 2:630d7c95eff7 x
1120 1120 | |
1121 1121 | x 1:488e1b7e7341 b (rewritten using rebase as 8:67e8f4a16c49)
1122 1122 |/
1123 1123 o 0:b173517d0057 a
1124 1124
1125 1125 $ hg strip -r 8:
1126 1126 $ hg log -G -r 'a'::
1127 1127 * 7:1143e9adc121 f
1128 1128 |
1129 1129 | o 6:d60ebfa0f1cb e
1130 1130 | |
1131 1131 | o 5:027ad6c5830d d'
1132 1132 | |
1133 1133 x | 4:76be324c128b d (rewritten using replace as 5:027ad6c5830d)
1134 1134 |/
1135 1135 o 3:a82ac2b38757 c
1136 1136 |
1137 1137 | o 2:630d7c95eff7 x
1138 1138 | |
1139 1139 o | 1:488e1b7e7341 b
1140 1140 |/
1141 1141 o 0:b173517d0057 a
1142 1142
1143 1143
1144 1144 If the rebase set has an obsolete (d) with a successor (d') outside the rebase
1145 1145 set and none in destination, we still get the divergence warning.
1146 1146 By allowing divergence, we can perform the rebase.
1147 1147
1148 1148 $ hg rebase -r 'c'::'f' -d 'x'
1149 1149 abort: this rebase will cause divergences from: 76be324c128b
1150 1150 (to force the rebase please set experimental.evolution.allowdivergence=True)
1151 1151 [255]
1152 1152 $ hg rebase --config experimental.evolution.allowdivergence=true -r 'c'::'f' -d 'x'
1153 1153 rebasing 3:a82ac2b38757 "c" (c)
1154 1154 rebasing 4:76be324c128b "d" (d)
1155 1155 rebasing 7:1143e9adc121 "f" (f tip)
1156 1156 1 new orphan changesets
1157 1157 2 new content-divergent changesets
1158 1158 $ hg log -G -r 'a':: -T instabilities
1159 1159 o 10:e1744ea07510 f
1160 1160 |
1161 1161 * 9:e2b36ea9a0a0 d (content-divergent)
1162 1162 |
1163 1163 o 8:6a0376de376e c
1164 1164 |
1165 1165 | x 7:1143e9adc121 f
1166 1166 | |
1167 1167 | | * 6:d60ebfa0f1cb e (orphan)
1168 1168 | | |
1169 1169 | | * 5:027ad6c5830d d' (orphan content-divergent)
1170 1170 | | |
1171 1171 | x | 4:76be324c128b d
1172 1172 | |/
1173 1173 | x 3:a82ac2b38757 c
1174 1174 | |
1175 1175 o | 2:630d7c95eff7 x
1176 1176 | |
1177 1177 | o 1:488e1b7e7341 b
1178 1178 |/
1179 1179 o 0:b173517d0057 a
1180 1180
1181 1181 $ hg strip -r 8:
1182 1182
1183 1183 (Not skipping obsoletes means that divergence is allowed.)
1184 1184
1185 1185 $ hg rebase --config experimental.rebaseskipobsolete=false -r 'c'::'f' -d 'x'
1186 1186 rebasing 3:a82ac2b38757 "c" (c)
1187 1187 rebasing 4:76be324c128b "d" (d)
1188 1188 rebasing 7:1143e9adc121 "f" (f tip)
1189 1189 1 new orphan changesets
1190 1190 2 new content-divergent changesets
1191 1191
1192 1192 $ hg strip -r 0:
1193 1193
1194 1194 Similar test on a more complex graph
1195 1195
1196 1196 $ hg debugdrawdag <<EOF
1197 1197 > g
1198 1198 > |
1199 1199 > f e
1200 1200 > | |
1201 1201 > e' d # replace: e -> e'
1202 1202 > \ /
1203 1203 > c
1204 1204 > |
1205 1205 > x b
1206 1206 > \|
1207 1207 > a
1208 1208 > EOF
1209 1209 1 new orphan changesets
1210 1210 $ hg log -G -r 'a':
1211 1211 * 8:2876ce66c6eb g
1212 1212 |
1213 1213 | o 7:3ffec603ab53 f
1214 1214 | |
1215 1215 x | 6:e36fae928aec e (rewritten using replace as 5:63324dc512ea)
1216 1216 | |
1217 1217 | o 5:63324dc512ea e'
1218 1218 | |
1219 1219 o | 4:76be324c128b d
1220 1220 |/
1221 1221 o 3:a82ac2b38757 c
1222 1222 |
1223 1223 | o 2:630d7c95eff7 x
1224 1224 | |
1225 1225 o | 1:488e1b7e7341 b
1226 1226 |/
1227 1227 o 0:b173517d0057 a
1228 1228
1229 1229 $ hg rebase -b 'f' -d 'x'
1230 1230 rebasing 1:488e1b7e7341 "b" (b)
1231 1231 rebasing 3:a82ac2b38757 "c" (c)
1232 1232 rebasing 5:63324dc512ea "e'" (e')
1233 1233 rebasing 7:3ffec603ab53 "f" (f)
1234 1234 rebasing 4:76be324c128b "d" (d)
1235 1235 note: not rebasing 6:e36fae928aec "e" (e) and its descendants as this would cause divergence
1236 1236 $ hg log -G -r 'a':
1237 1237 o 13:a1707a5b7c2c d
1238 1238 |
1239 1239 | o 12:ef6251596616 f
1240 1240 | |
1241 1241 | o 11:b6f172e64af9 e'
1242 1242 |/
1243 1243 o 10:d008e6b4d3fd c
1244 1244 |
1245 1245 o 9:67e8f4a16c49 b
1246 1246 |
1247 1247 | * 8:2876ce66c6eb g
1248 1248 | |
1249 1249 | | x 7:3ffec603ab53 f (rewritten using rebase as 12:ef6251596616)
1250 1250 | | |
1251 1251 | x | 6:e36fae928aec e (rewritten using replace as 5:63324dc512ea)
1252 1252 | | |
1253 1253 | | x 5:63324dc512ea e' (rewritten using rebase as 11:b6f172e64af9)
1254 1254 | | |
1255 1255 | x | 4:76be324c128b d (rewritten using rebase as 13:a1707a5b7c2c)
1256 1256 | |/
1257 1257 | x 3:a82ac2b38757 c (rewritten using rebase as 10:d008e6b4d3fd)
1258 1258 | |
1259 1259 o | 2:630d7c95eff7 x
1260 1260 | |
1261 1261 | x 1:488e1b7e7341 b (rewritten using rebase as 9:67e8f4a16c49)
1262 1262 |/
1263 1263 o 0:b173517d0057 a
1264 1264
1265 1265
1266 1266 issue5782
1267 1267 $ hg strip -r 0:
1268 1268 $ hg debugdrawdag <<EOF
1269 1269 > d
1270 1270 > |
1271 1271 > c1 c # replace: c -> c1
1272 1272 > \ /
1273 1273 > b
1274 1274 > |
1275 1275 > a
1276 1276 > EOF
1277 1277 1 new orphan changesets
1278 1278 $ hg debugobsolete `hg log -T "{node}" --hidden -r 'desc("c1")'`
1279 1279 obsoleted 1 changesets
1280 1280 $ hg log -G -r 'a': --hidden
1281 1281 * 4:76be324c128b d
1282 1282 |
1283 1283 | x 3:ef8a456de8fa c1 (pruned)
1284 1284 | |
1285 1285 x | 2:a82ac2b38757 c (rewritten using replace as 3:ef8a456de8fa)
1286 1286 |/
1287 1287 o 1:488e1b7e7341 b
1288 1288 |
1289 1289 o 0:b173517d0057 a
1290 1290
1291 1291 $ hg rebase -d 0 -r 2
1292 1292 rebasing 2:a82ac2b38757 "c" (c)
1293 1293 $ hg log -G -r 'a': --hidden
1294 1294 o 5:69ad416a4a26 c
1295 1295 |
1296 1296 | * 4:76be324c128b d
1297 1297 | |
1298 1298 | | x 3:ef8a456de8fa c1 (pruned)
1299 1299 | | |
1300 1300 | x | 2:a82ac2b38757 c (rewritten using replace as 3:ef8a456de8fa rewritten using rebase as 5:69ad416a4a26)
1301 1301 | |/
1302 1302 | o 1:488e1b7e7341 b
1303 1303 |/
1304 1304 o 0:b173517d0057 a
1305 1305
1306 1306 $ cd ..
1307 1307
1308 1308 Rebase merge where successor of one parent is equal to destination (issue5198)
1309 1309
1310 1310 $ hg init p1-succ-is-dest
1311 1311 $ cd p1-succ-is-dest
1312 1312
1313 1313 $ hg debugdrawdag <<EOF
1314 1314 > F
1315 1315 > /|
1316 1316 > E D B # replace: D -> B
1317 1317 > \|/
1318 1318 > A
1319 1319 > EOF
1320 1320 1 new orphan changesets
1321 1321
1322 1322 $ hg rebase -d B -s D
1323 1323 note: not rebasing 2:b18e25de2cf5 "D" (D), already in destination as 1:112478962961 "B" (B)
1324 1324 rebasing 4:66f1a38021c9 "F" (F tip)
1325 1325 $ hg log -G
1326 1326 o 5:50e9d60b99c6 F
1327 1327 |\
1328 1328 | | x 4:66f1a38021c9 F (rewritten using rebase as 5:50e9d60b99c6)
1329 1329 | |/|
1330 1330 | o | 3:7fb047a69f22 E
1331 1331 | | |
1332 1332 | | x 2:b18e25de2cf5 D (rewritten using replace as 1:112478962961)
1333 1333 | |/
1334 1334 o | 1:112478962961 B
1335 1335 |/
1336 1336 o 0:426bada5c675 A
1337 1337
1338 1338 $ cd ..
1339 1339
1340 1340 Rebase merge where successor of other parent is equal to destination
1341 1341
1342 1342 $ hg init p2-succ-is-dest
1343 1343 $ cd p2-succ-is-dest
1344 1344
1345 1345 $ hg debugdrawdag <<EOF
1346 1346 > F
1347 1347 > /|
1348 1348 > E D B # replace: E -> B
1349 1349 > \|/
1350 1350 > A
1351 1351 > EOF
1352 1352 1 new orphan changesets
1353 1353
1354 1354 $ hg rebase -d B -s E
1355 1355 note: not rebasing 3:7fb047a69f22 "E" (E), already in destination as 1:112478962961 "B" (B)
1356 1356 rebasing 4:66f1a38021c9 "F" (F tip)
1357 1357 $ hg log -G
1358 1358 o 5:aae1787dacee F
1359 1359 |\
1360 1360 | | x 4:66f1a38021c9 F (rewritten using rebase as 5:aae1787dacee)
1361 1361 | |/|
1362 1362 | | x 3:7fb047a69f22 E (rewritten using replace as 1:112478962961)
1363 1363 | | |
1364 1364 | o | 2:b18e25de2cf5 D
1365 1365 | |/
1366 1366 o / 1:112478962961 B
1367 1367 |/
1368 1368 o 0:426bada5c675 A
1369 1369
1370 1370 $ cd ..
1371 1371
1372 1372 Rebase merge where successor of one parent is ancestor of destination
1373 1373
1374 1374 $ hg init p1-succ-in-dest
1375 1375 $ cd p1-succ-in-dest
1376 1376
1377 1377 $ hg debugdrawdag <<EOF
1378 1378 > F C
1379 1379 > /| |
1380 1380 > E D B # replace: D -> B
1381 1381 > \|/
1382 1382 > A
1383 1383 > EOF
1384 1384 1 new orphan changesets
1385 1385
1386 1386 $ hg rebase -d C -s D
1387 1387 note: not rebasing 2:b18e25de2cf5 "D" (D), already in destination as 1:112478962961 "B" (B)
1388 1388 rebasing 5:66f1a38021c9 "F" (F tip)
1389 1389
1390 1390 $ hg log -G
1391 1391 o 6:0913febf6439 F
1392 1392 |\
1393 1393 +---x 5:66f1a38021c9 F (rewritten using rebase as 6:0913febf6439)
1394 1394 | | |
1395 1395 | o | 4:26805aba1e60 C
1396 1396 | | |
1397 1397 o | | 3:7fb047a69f22 E
1398 1398 | | |
1399 1399 +---x 2:b18e25de2cf5 D (rewritten using replace as 1:112478962961)
1400 1400 | |
1401 1401 | o 1:112478962961 B
1402 1402 |/
1403 1403 o 0:426bada5c675 A
1404 1404
1405 1405 $ cd ..
1406 1406
1407 1407 Rebase merge where successor of other parent is ancestor of destination
1408 1408
1409 1409 $ hg init p2-succ-in-dest
1410 1410 $ cd p2-succ-in-dest
1411 1411
1412 1412 $ hg debugdrawdag <<EOF
1413 1413 > F C
1414 1414 > /| |
1415 1415 > E D B # replace: E -> B
1416 1416 > \|/
1417 1417 > A
1418 1418 > EOF
1419 1419 1 new orphan changesets
1420 1420
1421 1421 $ hg rebase -d C -s E
1422 1422 note: not rebasing 3:7fb047a69f22 "E" (E), already in destination as 1:112478962961 "B" (B)
1423 1423 rebasing 5:66f1a38021c9 "F" (F tip)
1424 1424 $ hg log -G
1425 1425 o 6:c6ab0cc6d220 F
1426 1426 |\
1427 1427 +---x 5:66f1a38021c9 F (rewritten using rebase as 6:c6ab0cc6d220)
1428 1428 | | |
1429 1429 | o | 4:26805aba1e60 C
1430 1430 | | |
1431 1431 | | x 3:7fb047a69f22 E (rewritten using replace as 1:112478962961)
1432 1432 | | |
1433 1433 o---+ 2:b18e25de2cf5 D
1434 1434 / /
1435 1435 o / 1:112478962961 B
1436 1436 |/
1437 1437 o 0:426bada5c675 A
1438 1438
1439 1439 $ cd ..
1440 1440
1441 1441 Rebase merge where successor of one parent is ancestor of destination
1442 1442
1443 1443 $ hg init p1-succ-in-dest-b
1444 1444 $ cd p1-succ-in-dest-b
1445 1445
1446 1446 $ hg debugdrawdag <<EOF
1447 1447 > F C
1448 1448 > /| |
1449 1449 > E D B # replace: E -> B
1450 1450 > \|/
1451 1451 > A
1452 1452 > EOF
1453 1453 1 new orphan changesets
1454 1454
1455 1455 $ hg rebase -d C -b F
1456 1456 rebasing 2:b18e25de2cf5 "D" (D)
1457 1457 note: not rebasing 3:7fb047a69f22 "E" (E), already in destination as 1:112478962961 "B" (B)
1458 1458 rebasing 5:66f1a38021c9 "F" (F tip)
1459 1459 note: rebase of 5:66f1a38021c9 created no changes to commit
1460 1460 $ hg log -G
1461 1461 o 6:8f47515dda15 D
1462 1462 |
1463 1463 | x 5:66f1a38021c9 F (pruned using rebase)
1464 1464 | |\
1465 1465 o | | 4:26805aba1e60 C
1466 1466 | | |
1467 1467 | | x 3:7fb047a69f22 E (rewritten using replace as 1:112478962961)
1468 1468 | | |
1469 1469 | x | 2:b18e25de2cf5 D (rewritten using rebase as 6:8f47515dda15)
1470 1470 | |/
1471 1471 o / 1:112478962961 B
1472 1472 |/
1473 1473 o 0:426bada5c675 A
1474 1474
1475 1475 $ cd ..
1476 1476
1477 1477 Rebase merge where successor of other parent is ancestor of destination
1478 1478
1479 1479 $ hg init p2-succ-in-dest-b
1480 1480 $ cd p2-succ-in-dest-b
1481 1481
1482 1482 $ hg debugdrawdag <<EOF
1483 1483 > F C
1484 1484 > /| |
1485 1485 > E D B # replace: D -> B
1486 1486 > \|/
1487 1487 > A
1488 1488 > EOF
1489 1489 1 new orphan changesets
1490 1490
1491 1491 $ hg rebase -d C -b F
1492 1492 note: not rebasing 2:b18e25de2cf5 "D" (D), already in destination as 1:112478962961 "B" (B)
1493 1493 rebasing 3:7fb047a69f22 "E" (E)
1494 1494 rebasing 5:66f1a38021c9 "F" (F tip)
1495 1495 note: rebase of 5:66f1a38021c9 created no changes to commit
1496 1496
1497 1497 $ hg log -G
1498 1498 o 6:533690786a86 E
1499 1499 |
1500 1500 | x 5:66f1a38021c9 F (pruned using rebase)
1501 1501 | |\
1502 1502 o | | 4:26805aba1e60 C
1503 1503 | | |
1504 1504 | | x 3:7fb047a69f22 E (rewritten using rebase as 6:533690786a86)
1505 1505 | | |
1506 1506 | x | 2:b18e25de2cf5 D (rewritten using replace as 1:112478962961)
1507 1507 | |/
1508 1508 o / 1:112478962961 B
1509 1509 |/
1510 1510 o 0:426bada5c675 A
1511 1511
1512 1512 $ cd ..
1513 1513
1514 1514 Rebase merge where extinct node has successor that is not an ancestor of
1515 1515 destination
1516 1516
1517 1517 $ hg init extinct-with-succ-not-in-dest
1518 1518 $ cd extinct-with-succ-not-in-dest
1519 1519
1520 1520 $ hg debugdrawdag <<EOF
1521 1521 > E C # replace: C -> E
1522 1522 > | |
1523 1523 > D B
1524 1524 > |/
1525 1525 > A
1526 1526 > EOF
1527 1527
1528 TODO: There doesn't seem to be much reason to fail this rebase. Make it pass?
1529 1528 $ hg rebase -d D -s B
1530 abort: this rebase will cause divergences from: 26805aba1e60
1531 (to force the rebase please set experimental.evolution.allowdivergence=True)
1532 [255]
1529 rebasing 1:112478962961 "B" (B)
1530 note: not rebasing 3:26805aba1e60 "C" (C) and its descendants as this would cause divergence
1533 1531
1534 1532 $ cd ..
1535 1533
1536 1534 $ hg init p2-succ-in-dest-c
1537 1535 $ cd p2-succ-in-dest-c
1538 1536
1539 1537 The scenario here was that B::D were developed on default. B was queued on
1540 1538 stable, but amended before being push to hg-committed. C was queued on default,
1541 1539 along with unrelated J.
1542 1540
1543 1541 $ hg debugdrawdag <<EOF
1544 1542 > J
1545 1543 > |
1546 1544 > F
1547 1545 > |
1548 1546 > E
1549 1547 > | D
1550 1548 > | |
1551 1549 > | C # replace: C -> F
1552 1550 > | | H I # replace: B -> H -> I
1553 1551 > | B |/
1554 1552 > |/ G
1555 1553 > A
1556 1554 > EOF
1557 1555 1 new orphan changesets
1558 1556
1559 1557 This strip seems to be the key to avoid an early divergence warning.
1560 1558 $ hg --config extensions.strip= --hidden strip -qr H
1561 1559 1 new orphan changesets
1562 1560
1563 1561 $ hg rebase -b 'desc("D")' -d 'desc("J")'
1564 1562 abort: this rebase will cause divergences from: 112478962961
1565 1563 (to force the rebase please set experimental.evolution.allowdivergence=True)
1566 1564 [255]
1567 1565
1568 1566 Rebase merge where both parents have successors in destination
1569 1567
1570 1568 $ hg init p12-succ-in-dest
1571 1569 $ cd p12-succ-in-dest
1572 1570 $ hg debugdrawdag <<'EOS'
1573 1571 > E F
1574 1572 > /| /| # replace: A -> C
1575 1573 > A B C D # replace: B -> D
1576 1574 > | |
1577 1575 > X Y
1578 1576 > EOS
1579 1577 1 new orphan changesets
1580 1578 $ hg rebase -r A+B+E -d F
1581 1579 note: not rebasing 4:a3d17304151f "A" (A), already in destination as 0:96cc3511f894 "C" (C)
1582 1580 note: not rebasing 5:b23a2cc00842 "B" (B), already in destination as 1:058c1e1fb10a "D" (D)
1583 1581 rebasing 7:dac5d11c5a7d "E" (E tip)
1584 1582 abort: rebasing 7:dac5d11c5a7d will include unwanted changes from 3:59c792af609c, 5:b23a2cc00842 or 2:ba2b7fa7166d, 4:a3d17304151f
1585 1583 [255]
1586 1584 $ cd ..
1587 1585
1588 1586 Rebase a non-clean merge. One parent has successor in destination, the other
1589 1587 parent moves as requested.
1590 1588
1591 1589 $ hg init p1-succ-p2-move
1592 1590 $ cd p1-succ-p2-move
1593 1591 $ hg debugdrawdag <<'EOS'
1594 1592 > D Z
1595 1593 > /| | # replace: A -> C
1596 1594 > A B C # D/D = D
1597 1595 > EOS
1598 1596 1 new orphan changesets
1599 1597 $ hg rebase -r A+B+D -d Z
1600 1598 note: not rebasing 0:426bada5c675 "A" (A), already in destination as 2:96cc3511f894 "C" (C)
1601 1599 rebasing 1:fc2b737bb2e5 "B" (B)
1602 1600 rebasing 3:b8ed089c80ad "D" (D)
1603 1601
1604 1602 $ rm .hg/localtags
1605 1603 $ hg log -G
1606 1604 o 6:e4f78693cc88 D
1607 1605 |
1608 1606 o 5:76840d832e98 B
1609 1607 |
1610 1608 o 4:50e41c1f3950 Z
1611 1609 |
1612 1610 o 2:96cc3511f894 C
1613 1611
1614 1612 $ hg files -r tip
1615 1613 B
1616 1614 C
1617 1615 D
1618 1616 Z
1619 1617
1620 1618 $ cd ..
1621 1619
1622 1620 $ hg init p1-move-p2-succ
1623 1621 $ cd p1-move-p2-succ
1624 1622 $ hg debugdrawdag <<'EOS'
1625 1623 > D Z
1626 1624 > /| | # replace: B -> C
1627 1625 > A B C # D/D = D
1628 1626 > EOS
1629 1627 1 new orphan changesets
1630 1628 $ hg rebase -r B+A+D -d Z
1631 1629 rebasing 0:426bada5c675 "A" (A)
1632 1630 note: not rebasing 1:fc2b737bb2e5 "B" (B), already in destination as 2:96cc3511f894 "C" (C)
1633 1631 rebasing 3:b8ed089c80ad "D" (D)
1634 1632
1635 1633 $ rm .hg/localtags
1636 1634 $ hg log -G
1637 1635 o 6:1b355ed94d82 D
1638 1636 |
1639 1637 o 5:a81a74d764a6 A
1640 1638 |
1641 1639 o 4:50e41c1f3950 Z
1642 1640 |
1643 1641 o 2:96cc3511f894 C
1644 1642
1645 1643 $ hg files -r tip
1646 1644 A
1647 1645 C
1648 1646 D
1649 1647 Z
1650 1648
1651 1649 $ cd ..
1652 1650
1653 1651 Test that bookmark is moved and working dir is updated when all changesets have
1654 1652 equivalents in destination
1655 1653 $ hg init rbsrepo && cd rbsrepo
1656 1654 $ echo "[experimental]" > .hg/hgrc
1657 1655 $ echo "evolution=true" >> .hg/hgrc
1658 1656 $ echo "rebaseskipobsolete=on" >> .hg/hgrc
1659 1657 $ echo root > root && hg ci -Am root
1660 1658 adding root
1661 1659 $ echo a > a && hg ci -Am a
1662 1660 adding a
1663 1661 $ hg up 0
1664 1662 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1665 1663 $ echo b > b && hg ci -Am b
1666 1664 adding b
1667 1665 created new head
1668 1666 $ hg rebase -r 2 -d 1
1669 1667 rebasing 2:1e9a3c00cbe9 "b" (tip)
1670 1668 $ hg log -r . # working dir is at rev 3 (successor of 2)
1671 1669 3:be1832deae9a b (no-eol)
1672 1670 $ hg book -r 2 mybook --hidden # rev 2 has a bookmark on it now
1673 1671 bookmarking hidden changeset 1e9a3c00cbe9
1674 1672 (hidden revision '1e9a3c00cbe9' was rewritten as: be1832deae9a)
1675 1673 $ hg up 2 && hg log -r . # working dir is at rev 2 again
1676 1674 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1677 1675 2:1e9a3c00cbe9 b (rewritten using rebase as 3:be1832deae9a) (no-eol)
1678 1676 $ hg rebase -r 2 -d 3 --config experimental.evolution.track-operation=1
1679 1677 note: not rebasing 2:1e9a3c00cbe9 "b" (mybook), already in destination as 3:be1832deae9a "b" (tip)
1680 1678 Check that working directory and bookmark was updated to rev 3 although rev 2
1681 1679 was skipped
1682 1680 $ hg log -r .
1683 1681 3:be1832deae9a b (no-eol)
1684 1682 $ hg bookmarks
1685 1683 mybook 3:be1832deae9a
1686 1684 $ hg debugobsolete --rev tip
1687 1685 1e9a3c00cbe90d236ac05ef61efcc5e40b7412bc be1832deae9ac531caa7438b8dcf6055a122cd8e 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
1688 1686
1689 1687 Obsoleted working parent and bookmark could be moved if an ancestor of working
1690 1688 parent gets moved:
1691 1689
1692 1690 $ hg init $TESTTMP/ancestor-wd-move
1693 1691 $ cd $TESTTMP/ancestor-wd-move
1694 1692 $ hg debugdrawdag <<'EOS'
1695 1693 > E D1 # rebase: D1 -> D2
1696 1694 > | |
1697 1695 > | C
1698 1696 > D2 |
1699 1697 > | B
1700 1698 > |/
1701 1699 > A
1702 1700 > EOS
1703 1701 $ hg update D1 -q
1704 1702 $ hg bookmark book -i
1705 1703 $ hg rebase -r B+D1 -d E
1706 1704 rebasing 1:112478962961 "B" (B)
1707 1705 note: not rebasing 5:15ecf15e0114 "D1" (book D1 tip), already in destination as 2:0807738e0be9 "D2" (D2)
1708 1706 1 new orphan changesets
1709 1707 $ hg log -G -T '{desc} {bookmarks}'
1710 1708 @ B book
1711 1709 |
1712 1710 | x D1
1713 1711 | |
1714 1712 o | E
1715 1713 | |
1716 1714 | * C
1717 1715 | |
1718 1716 o | D2
1719 1717 | |
1720 1718 | x B
1721 1719 |/
1722 1720 o A
1723 1721
1724 1722 Rebasing a merge with one of its parent having a hidden successor
1725 1723
1726 1724 $ hg init $TESTTMP/merge-p1-hidden-successor
1727 1725 $ cd $TESTTMP/merge-p1-hidden-successor
1728 1726
1729 1727 $ hg debugdrawdag <<'EOS'
1730 1728 > E
1731 1729 > |
1732 1730 > B3 B2 # amend: B1 -> B2 -> B3
1733 1731 > |/ # B2 is hidden
1734 1732 > | D
1735 1733 > | |\
1736 1734 > | B1 C
1737 1735 > |/
1738 1736 > A
1739 1737 > EOS
1740 1738 1 new orphan changesets
1741 1739
1742 1740 $ eval `hg tags -T '{tag}={node}\n'`
1743 1741 $ rm .hg/localtags
1744 1742
1745 1743 $ hg rebase -r $D -d $E
1746 1744 rebasing 5:9e62094e4d94 "D"
1747 1745
1748 1746 $ hg log -G
1749 1747 o 7:a699d059adcf D
1750 1748 |\
1751 1749 | o 6:ecc93090a95c E
1752 1750 | |
1753 1751 | o 4:0dc878468a23 B3
1754 1752 | |
1755 1753 o | 1:96cc3511f894 C
1756 1754 /
1757 1755 o 0:426bada5c675 A
1758 1756
1759 1757 For some reasons (--hidden, rebaseskipobsolete=0, directaccess, etc.),
1760 1758 rebasestate may contain hidden hashes. "rebase --abort" should work regardless.
1761 1759
1762 1760 $ hg init $TESTTMP/hidden-state1
1763 1761 $ cd $TESTTMP/hidden-state1
1764 1762 $ cat >> .hg/hgrc <<EOF
1765 1763 > [experimental]
1766 1764 > rebaseskipobsolete=0
1767 1765 > EOF
1768 1766
1769 1767 $ hg debugdrawdag <<'EOS'
1770 1768 > C
1771 1769 > |
1772 1770 > D B # prune: B, C
1773 1771 > |/ # B/D=B
1774 1772 > A
1775 1773 > EOS
1776 1774
1777 1775 $ eval `hg tags -T '{tag}={node}\n'`
1778 1776 $ rm .hg/localtags
1779 1777
1780 1778 $ hg update -q $C --hidden
1781 1779 updated to hidden changeset 7829726be4dc
1782 1780 (hidden revision '7829726be4dc' is pruned)
1783 1781 $ hg rebase -s $B -d $D
1784 1782 rebasing 1:2ec65233581b "B"
1785 1783 merging D
1786 1784 warning: conflicts while merging D! (edit, then use 'hg resolve --mark')
1787 1785 unresolved conflicts (see hg resolve, then hg rebase --continue)
1788 1786 [1]
1789 1787
1790 1788 $ cp -R . $TESTTMP/hidden-state2
1791 1789
1792 1790 $ hg log -G
1793 1791 @ 2:b18e25de2cf5 D
1794 1792 |
1795 1793 | @ 1:2ec65233581b B (pruned using prune)
1796 1794 |/
1797 1795 o 0:426bada5c675 A
1798 1796
1799 1797 $ hg summary
1800 1798 parent: 2:b18e25de2cf5 tip
1801 1799 D
1802 1800 parent: 1:2ec65233581b (obsolete)
1803 1801 B
1804 1802 branch: default
1805 1803 commit: 2 modified, 1 unknown, 1 unresolved (merge)
1806 1804 update: (current)
1807 1805 phases: 3 draft
1808 1806 rebase: 0 rebased, 2 remaining (rebase --continue)
1809 1807
1810 1808 $ hg rebase --abort
1811 1809 rebase aborted
1812 1810
1813 1811 Also test --continue for the above case
1814 1812
1815 1813 $ cd $TESTTMP/hidden-state2
1816 1814 $ hg resolve -m
1817 1815 (no more unresolved files)
1818 1816 continue: hg rebase --continue
1819 1817 $ hg rebase --continue
1820 1818 rebasing 1:2ec65233581b "B"
1821 1819 rebasing 3:7829726be4dc "C" (tip)
1822 1820 $ hg log -G
1823 1821 @ 5:1964d5d5b547 C
1824 1822 |
1825 1823 o 4:68deb90c12a2 B
1826 1824 |
1827 1825 o 2:b18e25de2cf5 D
1828 1826 |
1829 1827 o 0:426bada5c675 A
1830 1828
1831 1829 ====================
1832 1830 Test --stop option |
1833 1831 ====================
1834 1832 $ cd ..
1835 1833 $ hg init rbstop
1836 1834 $ cd rbstop
1837 1835 $ echo a>a
1838 1836 $ hg ci -Aqma
1839 1837 $ echo b>b
1840 1838 $ hg ci -Aqmb
1841 1839 $ echo c>c
1842 1840 $ hg ci -Aqmc
1843 1841 $ echo d>d
1844 1842 $ hg ci -Aqmd
1845 1843 $ hg up 0 -q
1846 1844 $ echo f>f
1847 1845 $ hg ci -Aqmf
1848 1846 $ echo D>d
1849 1847 $ hg ci -Aqm "conflict with d"
1850 1848 $ hg up 3 -q
1851 1849 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
1852 1850 o 5:00bfc9898aeb test
1853 1851 | conflict with d
1854 1852 |
1855 1853 o 4:dafd40200f93 test
1856 1854 | f
1857 1855 |
1858 1856 | @ 3:055a42cdd887 test
1859 1857 | | d
1860 1858 | |
1861 1859 | o 2:177f92b77385 test
1862 1860 | | c
1863 1861 | |
1864 1862 | o 1:d2ae7f538514 test
1865 1863 |/ b
1866 1864 |
1867 1865 o 0:cb9a9f314b8b test
1868 1866 a
1869 1867
1870 1868 $ hg rebase -s 1 -d 5
1871 1869 rebasing 1:d2ae7f538514 "b"
1872 1870 rebasing 2:177f92b77385 "c"
1873 1871 rebasing 3:055a42cdd887 "d"
1874 1872 merging d
1875 1873 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
1876 1874 unresolved conflicts (see hg resolve, then hg rebase --continue)
1877 1875 [1]
1878 1876 $ hg rebase --stop
1879 1877 1 new orphan changesets
1880 1878 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
1881 1879 o 7:7fffad344617 test
1882 1880 | c
1883 1881 |
1884 1882 o 6:b15528633407 test
1885 1883 | b
1886 1884 |
1887 1885 o 5:00bfc9898aeb test
1888 1886 | conflict with d
1889 1887 |
1890 1888 o 4:dafd40200f93 test
1891 1889 | f
1892 1890 |
1893 1891 | @ 3:055a42cdd887 test
1894 1892 | | d
1895 1893 | |
1896 1894 | x 2:177f92b77385 test
1897 1895 | | c
1898 1896 | |
1899 1897 | x 1:d2ae7f538514 test
1900 1898 |/ b
1901 1899 |
1902 1900 o 0:cb9a9f314b8b test
1903 1901 a
1904 1902
1905 1903 Test it aborts if unstable csets is not allowed:
1906 1904 ===============================================
1907 1905 $ cat >> $HGRCPATH << EOF
1908 1906 > [experimental]
1909 1907 > evolution.allowunstable=False
1910 1908 > EOF
1911 1909
1912 1910 $ hg strip 6 --no-backup -q
1913 1911 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
1914 1912 o 5:00bfc9898aeb test
1915 1913 | conflict with d
1916 1914 |
1917 1915 o 4:dafd40200f93 test
1918 1916 | f
1919 1917 |
1920 1918 | @ 3:055a42cdd887 test
1921 1919 | | d
1922 1920 | |
1923 1921 | o 2:177f92b77385 test
1924 1922 | | c
1925 1923 | |
1926 1924 | o 1:d2ae7f538514 test
1927 1925 |/ b
1928 1926 |
1929 1927 o 0:cb9a9f314b8b test
1930 1928 a
1931 1929
1932 1930 $ hg rebase -s 1 -d 5
1933 1931 rebasing 1:d2ae7f538514 "b"
1934 1932 rebasing 2:177f92b77385 "c"
1935 1933 rebasing 3:055a42cdd887 "d"
1936 1934 merging d
1937 1935 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
1938 1936 unresolved conflicts (see hg resolve, then hg rebase --continue)
1939 1937 [1]
1940 1938 $ hg rebase --stop
1941 1939 abort: cannot remove original changesets with unrebased descendants
1942 1940 (either enable obsmarkers to allow unstable revisions or use --keep to keep original changesets)
1943 1941 [255]
1944 1942 $ hg rebase --abort
1945 1943 saved backup bundle to $TESTTMP/rbstop/.hg/strip-backup/b15528633407-6eb72b6f-backup.hg
1946 1944 rebase aborted
1947 1945
1948 1946 Test --stop when --keep is passed:
1949 1947 ==================================
1950 1948 $ hg rebase -s 1 -d 5 --keep
1951 1949 rebasing 1:d2ae7f538514 "b"
1952 1950 rebasing 2:177f92b77385 "c"
1953 1951 rebasing 3:055a42cdd887 "d"
1954 1952 merging d
1955 1953 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
1956 1954 unresolved conflicts (see hg resolve, then hg rebase --continue)
1957 1955 [1]
1958 1956 $ hg rebase --stop
1959 1957 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
1960 1958 o 7:7fffad344617 test
1961 1959 | c
1962 1960 |
1963 1961 o 6:b15528633407 test
1964 1962 | b
1965 1963 |
1966 1964 o 5:00bfc9898aeb test
1967 1965 | conflict with d
1968 1966 |
1969 1967 o 4:dafd40200f93 test
1970 1968 | f
1971 1969 |
1972 1970 | @ 3:055a42cdd887 test
1973 1971 | | d
1974 1972 | |
1975 1973 | o 2:177f92b77385 test
1976 1974 | | c
1977 1975 | |
1978 1976 | o 1:d2ae7f538514 test
1979 1977 |/ b
1980 1978 |
1981 1979 o 0:cb9a9f314b8b test
1982 1980 a
1983 1981
1984 1982 Test --stop aborts when --collapse was passed:
1985 1983 =============================================
1986 1984 $ cat >> $HGRCPATH << EOF
1987 1985 > [experimental]
1988 1986 > evolution.allowunstable=True
1989 1987 > EOF
1990 1988
1991 1989 $ hg strip 6
1992 1990 saved backup bundle to $TESTTMP/rbstop/.hg/strip-backup/b15528633407-6eb72b6f-backup.hg
1993 1991 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
1994 1992 o 5:00bfc9898aeb test
1995 1993 | conflict with d
1996 1994 |
1997 1995 o 4:dafd40200f93 test
1998 1996 | f
1999 1997 |
2000 1998 | @ 3:055a42cdd887 test
2001 1999 | | d
2002 2000 | |
2003 2001 | o 2:177f92b77385 test
2004 2002 | | c
2005 2003 | |
2006 2004 | o 1:d2ae7f538514 test
2007 2005 |/ b
2008 2006 |
2009 2007 o 0:cb9a9f314b8b test
2010 2008 a
2011 2009
2012 2010 $ hg rebase -s 1 -d 5 --collapse -m "collapsed b c d"
2013 2011 rebasing 1:d2ae7f538514 "b"
2014 2012 rebasing 2:177f92b77385 "c"
2015 2013 rebasing 3:055a42cdd887 "d"
2016 2014 merging d
2017 2015 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
2018 2016 unresolved conflicts (see hg resolve, then hg rebase --continue)
2019 2017 [1]
2020 2018 $ hg rebase --stop
2021 2019 abort: cannot stop in --collapse session
2022 2020 [255]
2023 2021 $ hg rebase --abort
2024 2022 rebase aborted
2025 2023 $ hg diff
2026 2024 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
2027 2025 o 5:00bfc9898aeb test
2028 2026 | conflict with d
2029 2027 |
2030 2028 o 4:dafd40200f93 test
2031 2029 | f
2032 2030 |
2033 2031 | @ 3:055a42cdd887 test
2034 2032 | | d
2035 2033 | |
2036 2034 | o 2:177f92b77385 test
2037 2035 | | c
2038 2036 | |
2039 2037 | o 1:d2ae7f538514 test
2040 2038 |/ b
2041 2039 |
2042 2040 o 0:cb9a9f314b8b test
2043 2041 a
2044 2042
2045 2043 Test --stop raise errors with conflicting options:
2046 2044 =================================================
2047 2045 $ hg rebase -s 3 -d 5
2048 2046 rebasing 3:055a42cdd887 "d"
2049 2047 merging d
2050 2048 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
2051 2049 unresolved conflicts (see hg resolve, then hg rebase --continue)
2052 2050 [1]
2053 2051 $ hg rebase --stop --dry-run
2054 2052 abort: cannot specify both --dry-run and --stop
2055 2053 [255]
2056 2054
2057 2055 $ hg rebase -s 3 -d 5
2058 2056 abort: rebase in progress
2059 2057 (use 'hg rebase --continue' or 'hg rebase --abort')
2060 2058 [255]
2061 2059 $ hg rebase --stop --continue
2062 2060 abort: cannot use --stop with --continue
2063 2061 [255]
2064 2062
2065 2063 Test --stop moves bookmarks of original revisions to new rebased nodes:
2066 2064 ======================================================================
2067 2065 $ cd ..
2068 2066 $ hg init repo
2069 2067 $ cd repo
2070 2068
2071 2069 $ echo a > a
2072 2070 $ hg ci -Am A
2073 2071 adding a
2074 2072
2075 2073 $ echo b > b
2076 2074 $ hg ci -Am B
2077 2075 adding b
2078 2076 $ hg book X
2079 2077 $ hg book Y
2080 2078
2081 2079 $ echo c > c
2082 2080 $ hg ci -Am C
2083 2081 adding c
2084 2082 $ hg book Z
2085 2083
2086 2084 $ echo d > d
2087 2085 $ hg ci -Am D
2088 2086 adding d
2089 2087
2090 2088 $ hg up 0 -q
2091 2089 $ echo e > e
2092 2090 $ hg ci -Am E
2093 2091 adding e
2094 2092 created new head
2095 2093
2096 2094 $ echo doubt > d
2097 2095 $ hg ci -Am "conflict with d"
2098 2096 adding d
2099 2097
2100 2098 $ hg log -GT "{rev}: {node|short} '{desc}' bookmarks: {bookmarks}\n"
2101 2099 @ 5: 39adf30bc1be 'conflict with d' bookmarks:
2102 2100 |
2103 2101 o 4: 9c1e55f411b6 'E' bookmarks:
2104 2102 |
2105 2103 | o 3: 67a385d4e6f2 'D' bookmarks: Z
2106 2104 | |
2107 2105 | o 2: 49cb3485fa0c 'C' bookmarks: Y
2108 2106 | |
2109 2107 | o 1: 6c81ed0049f8 'B' bookmarks: X
2110 2108 |/
2111 2109 o 0: 1994f17a630e 'A' bookmarks:
2112 2110
2113 2111 $ hg rebase -s 1 -d 5
2114 2112 rebasing 1:6c81ed0049f8 "B" (X)
2115 2113 rebasing 2:49cb3485fa0c "C" (Y)
2116 2114 rebasing 3:67a385d4e6f2 "D" (Z)
2117 2115 merging d
2118 2116 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
2119 2117 unresolved conflicts (see hg resolve, then hg rebase --continue)
2120 2118 [1]
2121 2119 $ hg rebase --stop
2122 2120 1 new orphan changesets
2123 2121 $ hg log -GT "{rev}: {node|short} '{desc}' bookmarks: {bookmarks}\n"
2124 2122 o 7: 9c86c650b686 'C' bookmarks: Y
2125 2123 |
2126 2124 o 6: 9b87b54e5fd8 'B' bookmarks: X
2127 2125 |
2128 2126 @ 5: 39adf30bc1be 'conflict with d' bookmarks:
2129 2127 |
2130 2128 o 4: 9c1e55f411b6 'E' bookmarks:
2131 2129 |
2132 2130 | * 3: 67a385d4e6f2 'D' bookmarks: Z
2133 2131 | |
2134 2132 | x 2: 49cb3485fa0c 'C' bookmarks:
2135 2133 | |
2136 2134 | x 1: 6c81ed0049f8 'B' bookmarks:
2137 2135 |/
2138 2136 o 0: 1994f17a630e 'A' bookmarks:
2139 2137
General Comments 0
You need to be logged in to leave comments. Login now