##// END OF EJS Templates
rebase: change "result would have 3 parent" error message (BC)...
Jun Wu -
r33786:19f495fe default
parent child Browse files
Show More
@@ -1,1601 +1,1600 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 hex,
25 25 nullid,
26 26 nullrev,
27 27 short,
28 28 )
29 29 from mercurial import (
30 30 bookmarks,
31 31 cmdutil,
32 32 commands,
33 33 copies,
34 34 destutil,
35 35 dirstateguard,
36 36 error,
37 37 extensions,
38 38 hg,
39 39 lock,
40 40 merge as mergemod,
41 41 mergeutil,
42 42 obsolete,
43 43 obsutil,
44 44 patch,
45 45 phases,
46 46 registrar,
47 47 repair,
48 48 repoview,
49 49 revset,
50 50 scmutil,
51 51 smartset,
52 52 util,
53 53 )
54 54
55 55 release = lock.release
56 56 templateopts = cmdutil.templateopts
57 57
58 58 # The following constants are used throughout the rebase module. The ordering of
59 59 # their values must be maintained.
60 60
61 61 # Indicates that a revision needs to be rebased
62 62 revtodo = -1
63 63 nullmerge = -2
64 64 revignored = -3
65 65 # successor in rebase destination
66 66 revprecursor = -4
67 67 # plain prune (no successor)
68 68 revpruned = -5
69 69
70 70 cmdtable = {}
71 71 command = registrar.command(cmdtable)
72 72 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
73 73 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
74 74 # be specifying the version(s) of Mercurial they are tested with, or
75 75 # leave the attribute unspecified.
76 76 testedwith = 'ships-with-hg-core'
77 77
78 78 def _nothingtorebase():
79 79 return 1
80 80
81 81 def _savegraft(ctx, extra):
82 82 s = ctx.extra().get('source', None)
83 83 if s is not None:
84 84 extra['source'] = s
85 85 s = ctx.extra().get('intermediate-source', None)
86 86 if s is not None:
87 87 extra['intermediate-source'] = s
88 88
89 89 def _savebranch(ctx, extra):
90 90 extra['branch'] = ctx.branch()
91 91
92 92 def _makeextrafn(copiers):
93 93 """make an extrafn out of the given copy-functions.
94 94
95 95 A copy function takes a context and an extra dict, and mutates the
96 96 extra dict as needed based on the given context.
97 97 """
98 98 def extrafn(ctx, extra):
99 99 for c in copiers:
100 100 c(ctx, extra)
101 101 return extrafn
102 102
103 103 def _destrebase(repo, sourceset, destspace=None):
104 104 """small wrapper around destmerge to pass the right extra args
105 105
106 106 Please wrap destutil.destmerge instead."""
107 107 return destutil.destmerge(repo, action='rebase', sourceset=sourceset,
108 108 onheadcheck=False, destspace=destspace)
109 109
110 110 revsetpredicate = registrar.revsetpredicate()
111 111
112 112 @revsetpredicate('_destrebase')
113 113 def _revsetdestrebase(repo, subset, x):
114 114 # ``_rebasedefaultdest()``
115 115
116 116 # default destination for rebase.
117 117 # # XXX: Currently private because I expect the signature to change.
118 118 # # XXX: - bailing out in case of ambiguity vs returning all data.
119 119 # i18n: "_rebasedefaultdest" is a keyword
120 120 sourceset = None
121 121 if x is not None:
122 122 sourceset = revset.getset(repo, smartset.fullreposet(repo), x)
123 123 return subset & smartset.baseset([_destrebase(repo, sourceset)])
124 124
125 125 class rebaseruntime(object):
126 126 """This class is a container for rebase runtime state"""
127 127 def __init__(self, repo, ui, opts=None):
128 128 if opts is None:
129 129 opts = {}
130 130
131 131 self.repo = repo
132 132 self.ui = ui
133 133 self.opts = opts
134 134 self.originalwd = None
135 135 self.external = nullrev
136 136 # Mapping between the old revision id and either what is the new rebased
137 137 # revision or what needs to be done with the old revision. The state
138 138 # dict will be what contains most of the rebase progress state.
139 139 self.state = {}
140 140 self.activebookmark = None
141 141 self.dest = None
142 142 self.skipped = set()
143 143 self.destancestors = set()
144 144
145 145 self.collapsef = opts.get('collapse', False)
146 146 self.collapsemsg = cmdutil.logmessage(ui, opts)
147 147 self.date = opts.get('date', None)
148 148
149 149 e = opts.get('extrafn') # internal, used by e.g. hgsubversion
150 150 self.extrafns = [_savegraft]
151 151 if e:
152 152 self.extrafns = [e]
153 153
154 154 self.keepf = opts.get('keep', False)
155 155 self.keepbranchesf = opts.get('keepbranches', False)
156 156 # keepopen is not meant for use on the command line, but by
157 157 # other extensions
158 158 self.keepopen = opts.get('keepopen', False)
159 159 self.obsoletenotrebased = {}
160 160
161 161 def storestatus(self, tr=None):
162 162 """Store the current status to allow recovery"""
163 163 if tr:
164 164 tr.addfilegenerator('rebasestate', ('rebasestate',),
165 165 self._writestatus, location='plain')
166 166 else:
167 167 with self.repo.vfs("rebasestate", "w") as f:
168 168 self._writestatus(f)
169 169
170 170 def _writestatus(self, f):
171 171 repo = self.repo.unfiltered()
172 172 f.write(repo[self.originalwd].hex() + '\n')
173 173 f.write(repo[self.dest].hex() + '\n')
174 174 f.write(repo[self.external].hex() + '\n')
175 175 f.write('%d\n' % int(self.collapsef))
176 176 f.write('%d\n' % int(self.keepf))
177 177 f.write('%d\n' % int(self.keepbranchesf))
178 178 f.write('%s\n' % (self.activebookmark or ''))
179 179 for d, v in self.state.iteritems():
180 180 oldrev = repo[d].hex()
181 181 if v >= 0:
182 182 newrev = repo[v].hex()
183 183 elif v == revtodo:
184 184 # To maintain format compatibility, we have to use nullid.
185 185 # Please do remove this special case when upgrading the format.
186 186 newrev = hex(nullid)
187 187 else:
188 188 newrev = v
189 189 f.write("%s:%s\n" % (oldrev, newrev))
190 190 repo.ui.debug('rebase status stored\n')
191 191
192 192 def restorestatus(self):
193 193 """Restore a previously stored status"""
194 194 repo = self.repo
195 195 keepbranches = None
196 196 dest = None
197 197 collapse = False
198 198 external = nullrev
199 199 activebookmark = None
200 200 state = {}
201 201
202 202 try:
203 203 f = repo.vfs("rebasestate")
204 204 for i, l in enumerate(f.read().splitlines()):
205 205 if i == 0:
206 206 originalwd = repo[l].rev()
207 207 elif i == 1:
208 208 dest = repo[l].rev()
209 209 elif i == 2:
210 210 external = repo[l].rev()
211 211 elif i == 3:
212 212 collapse = bool(int(l))
213 213 elif i == 4:
214 214 keep = bool(int(l))
215 215 elif i == 5:
216 216 keepbranches = bool(int(l))
217 217 elif i == 6 and not (len(l) == 81 and ':' in l):
218 218 # line 6 is a recent addition, so for backwards
219 219 # compatibility check that the line doesn't look like the
220 220 # oldrev:newrev lines
221 221 activebookmark = l
222 222 else:
223 223 oldrev, newrev = l.split(':')
224 224 if newrev in (str(nullmerge), str(revignored),
225 225 str(revprecursor), str(revpruned)):
226 226 state[repo[oldrev].rev()] = int(newrev)
227 227 elif newrev == nullid:
228 228 state[repo[oldrev].rev()] = revtodo
229 229 # Legacy compat special case
230 230 else:
231 231 state[repo[oldrev].rev()] = repo[newrev].rev()
232 232
233 233 except IOError as err:
234 234 if err.errno != errno.ENOENT:
235 235 raise
236 236 cmdutil.wrongtooltocontinue(repo, _('rebase'))
237 237
238 238 if keepbranches is None:
239 239 raise error.Abort(_('.hg/rebasestate is incomplete'))
240 240
241 241 skipped = set()
242 242 # recompute the set of skipped revs
243 243 if not collapse:
244 244 seen = {dest}
245 245 for old, new in sorted(state.items()):
246 246 if new != revtodo and new in seen:
247 247 skipped.add(old)
248 248 seen.add(new)
249 249 repo.ui.debug('computed skipped revs: %s\n' %
250 250 (' '.join(str(r) for r in sorted(skipped)) or None))
251 251 repo.ui.debug('rebase status resumed\n')
252 252 _setrebasesetvisibility(repo, set(state.keys()) | {originalwd})
253 253
254 254 self.originalwd = originalwd
255 255 self.dest = dest
256 256 self.state = state
257 257 self.skipped = skipped
258 258 self.collapsef = collapse
259 259 self.keepf = keep
260 260 self.keepbranchesf = keepbranches
261 261 self.external = external
262 262 self.activebookmark = activebookmark
263 263
264 264 def _handleskippingobsolete(self, rebaserevs, obsoleterevs, dest):
265 265 """Compute structures necessary for skipping obsolete revisions
266 266
267 267 rebaserevs: iterable of all revisions that are to be rebased
268 268 obsoleterevs: iterable of all obsolete revisions in rebaseset
269 269 dest: a destination revision for the rebase operation
270 270 """
271 271 self.obsoletenotrebased = {}
272 272 if not self.ui.configbool('experimental', 'rebaseskipobsolete',
273 273 default=True):
274 274 return
275 275 rebaseset = set(rebaserevs)
276 276 obsoleteset = set(obsoleterevs)
277 277 self.obsoletenotrebased = _computeobsoletenotrebased(self.repo,
278 278 obsoleteset, dest)
279 279 skippedset = set(self.obsoletenotrebased)
280 280 _checkobsrebase(self.repo, self.ui, obsoleteset, rebaseset, skippedset)
281 281
282 282 def _prepareabortorcontinue(self, isabort):
283 283 try:
284 284 self.restorestatus()
285 285 self.collapsemsg = restorecollapsemsg(self.repo, isabort)
286 286 except error.RepoLookupError:
287 287 if isabort:
288 288 clearstatus(self.repo)
289 289 clearcollapsemsg(self.repo)
290 290 self.repo.ui.warn(_('rebase aborted (no revision is removed,'
291 291 ' only broken state is cleared)\n'))
292 292 return 0
293 293 else:
294 294 msg = _('cannot continue inconsistent rebase')
295 295 hint = _('use "hg rebase --abort" to clear broken state')
296 296 raise error.Abort(msg, hint=hint)
297 297 if isabort:
298 298 return abort(self.repo, self.originalwd, self.dest,
299 299 self.state, activebookmark=self.activebookmark)
300 300
301 301 obsrevs = (r for r, st in self.state.items() if st == revprecursor)
302 302 self._handleskippingobsolete(self.state.keys(), obsrevs, self.dest)
303 303
304 304 def _preparenewrebase(self, dest, rebaseset):
305 305 if dest is None:
306 306 return _nothingtorebase()
307 307
308 308 allowunstable = obsolete.isenabled(self.repo, obsolete.allowunstableopt)
309 309 if (not (self.keepf or allowunstable)
310 310 and self.repo.revs('first(children(%ld) - %ld)',
311 311 rebaseset, rebaseset)):
312 312 raise error.Abort(
313 313 _("can't remove original changesets with"
314 314 " unrebased descendants"),
315 315 hint=_('use --keep to keep original changesets'))
316 316
317 317 obsrevs = _filterobsoleterevs(self.repo, set(rebaseset))
318 318 self._handleskippingobsolete(rebaseset, obsrevs, dest.rev())
319 319
320 320 result = buildstate(self.repo, dest, rebaseset, self.collapsef,
321 321 self.obsoletenotrebased)
322 322
323 323 if not result:
324 324 # Empty state built, nothing to rebase
325 325 self.ui.status(_('nothing to rebase\n'))
326 326 return _nothingtorebase()
327 327
328 328 for root in self.repo.set('roots(%ld)', rebaseset):
329 329 if not self.keepf and not root.mutable():
330 330 raise error.Abort(_("can't rebase public changeset %s")
331 331 % root,
332 332 hint=_("see 'hg help phases' for details"))
333 333
334 334 (self.originalwd, self.dest, self.state) = result
335 335 if self.collapsef:
336 336 self.destancestors = self.repo.changelog.ancestors(
337 337 [self.dest],
338 338 inclusive=True)
339 339 self.external = externalparent(self.repo, self.state,
340 340 self.destancestors)
341 341
342 342 if dest.closesbranch() and not self.keepbranchesf:
343 343 self.ui.status(_('reopening closed branch head %s\n') % dest)
344 344
345 345 def _performrebase(self, tr):
346 346 repo, ui, opts = self.repo, self.ui, self.opts
347 347 if self.keepbranchesf:
348 348 # insert _savebranch at the start of extrafns so if
349 349 # there's a user-provided extrafn it can clobber branch if
350 350 # desired
351 351 self.extrafns.insert(0, _savebranch)
352 352 if self.collapsef:
353 353 branches = set()
354 354 for rev in self.state:
355 355 branches.add(repo[rev].branch())
356 356 if len(branches) > 1:
357 357 raise error.Abort(_('cannot collapse multiple named '
358 358 'branches'))
359 359
360 360 # Rebase
361 361 if not self.destancestors:
362 362 self.destancestors = repo.changelog.ancestors([self.dest],
363 363 inclusive=True)
364 364
365 365 # Keep track of the active bookmarks in order to reset them later
366 366 self.activebookmark = self.activebookmark or repo._activebookmark
367 367 if self.activebookmark:
368 368 bookmarks.deactivate(repo)
369 369
370 370 # Store the state before we begin so users can run 'hg rebase --abort'
371 371 # if we fail before the transaction closes.
372 372 self.storestatus()
373 373
374 374 sortedrevs = repo.revs('sort(%ld, -topo)', self.state)
375 375 cands = [k for k, v in self.state.iteritems() if v == revtodo]
376 376 total = len(cands)
377 377 pos = 0
378 378 for rev in sortedrevs:
379 379 ctx = repo[rev]
380 380 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
381 381 ctx.description().split('\n', 1)[0])
382 382 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
383 383 if names:
384 384 desc += ' (%s)' % ' '.join(names)
385 385 if self.state[rev] == rev:
386 386 ui.status(_('already rebased %s\n') % desc)
387 387 elif self.state[rev] == revtodo:
388 388 pos += 1
389 389 ui.status(_('rebasing %s\n') % desc)
390 390 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, ctx)),
391 391 _('changesets'), total)
392 392 p1, p2, base = defineparents(repo, rev, self.dest, self.state)
393 393 self.storestatus(tr=tr)
394 394 storecollapsemsg(repo, self.collapsemsg)
395 395 if len(repo[None].parents()) == 2:
396 396 repo.ui.debug('resuming interrupted rebase\n')
397 397 else:
398 398 try:
399 399 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
400 400 'rebase')
401 401 stats = rebasenode(repo, rev, p1, base, self.state,
402 402 self.collapsef, self.dest)
403 403 if stats and stats[3] > 0:
404 404 raise error.InterventionRequired(
405 405 _('unresolved conflicts (see hg '
406 406 'resolve, then hg rebase --continue)'))
407 407 finally:
408 408 ui.setconfig('ui', 'forcemerge', '', 'rebase')
409 409 if not self.collapsef:
410 410 merging = p2 != nullrev
411 411 editform = cmdutil.mergeeditform(merging, 'rebase')
412 412 editor = cmdutil.getcommiteditor(editform=editform, **opts)
413 413 newnode = concludenode(repo, rev, p1, p2,
414 414 extrafn=_makeextrafn(self.extrafns),
415 415 editor=editor,
416 416 keepbranches=self.keepbranchesf,
417 417 date=self.date)
418 418 if newnode is None:
419 419 # If it ended up being a no-op commit, then the normal
420 420 # merge state clean-up path doesn't happen, so do it
421 421 # here. Fix issue5494
422 422 mergemod.mergestate.clean(repo)
423 423 else:
424 424 # Skip commit if we are collapsing
425 425 repo.setparents(repo[p1].node())
426 426 newnode = None
427 427 # Update the state
428 428 if newnode is not None:
429 429 self.state[rev] = repo[newnode].rev()
430 430 ui.debug('rebased as %s\n' % short(newnode))
431 431 else:
432 432 if not self.collapsef:
433 433 ui.warn(_('note: rebase of %d:%s created no changes '
434 434 'to commit\n') % (rev, ctx))
435 435 self.skipped.add(rev)
436 436 self.state[rev] = p1
437 437 ui.debug('next revision set to %s\n' % p1)
438 438 elif self.state[rev] == nullmerge:
439 439 ui.debug('ignoring null merge rebase of %s\n' % rev)
440 440 elif self.state[rev] == revignored:
441 441 ui.status(_('not rebasing ignored %s\n') % desc)
442 442 elif self.state[rev] == revprecursor:
443 443 destctx = repo[self.obsoletenotrebased[rev]]
444 444 descdest = '%d:%s "%s"' % (destctx.rev(), destctx,
445 445 destctx.description().split('\n', 1)[0])
446 446 msg = _('note: not rebasing %s, already in destination as %s\n')
447 447 ui.status(msg % (desc, descdest))
448 448 elif self.state[rev] == revpruned:
449 449 msg = _('note: not rebasing %s, it has no successor\n')
450 450 ui.status(msg % desc)
451 451 else:
452 452 ui.status(_('already rebased %s as %s\n') %
453 453 (desc, repo[self.state[rev]]))
454 454
455 455 ui.progress(_('rebasing'), None)
456 456 ui.note(_('rebase merging completed\n'))
457 457
458 458 def _finishrebase(self):
459 459 repo, ui, opts = self.repo, self.ui, self.opts
460 460 if self.collapsef and not self.keepopen:
461 461 p1, p2, _base = defineparents(repo, min(self.state),
462 462 self.dest, self.state)
463 463 editopt = opts.get('edit')
464 464 editform = 'rebase.collapse'
465 465 if self.collapsemsg:
466 466 commitmsg = self.collapsemsg
467 467 else:
468 468 commitmsg = 'Collapsed revision'
469 469 for rebased in sorted(self.state):
470 470 if rebased not in self.skipped and\
471 471 self.state[rebased] > nullmerge:
472 472 commitmsg += '\n* %s' % repo[rebased].description()
473 473 editopt = True
474 474 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
475 475 revtoreuse = max(self.state)
476 476
477 477 dsguard = None
478 478 if ui.configbool('rebase', 'singletransaction'):
479 479 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
480 480 with util.acceptintervention(dsguard):
481 481 newnode = concludenode(repo, revtoreuse, p1, self.external,
482 482 commitmsg=commitmsg,
483 483 extrafn=_makeextrafn(self.extrafns),
484 484 editor=editor,
485 485 keepbranches=self.keepbranchesf,
486 486 date=self.date)
487 487 if newnode is None:
488 488 newrev = self.dest
489 489 else:
490 490 newrev = repo[newnode].rev()
491 491 for oldrev in self.state.iterkeys():
492 492 if self.state[oldrev] > nullmerge:
493 493 self.state[oldrev] = newrev
494 494
495 495 if 'qtip' in repo.tags():
496 496 updatemq(repo, self.state, self.skipped, **opts)
497 497
498 498 # restore original working directory
499 499 # (we do this before stripping)
500 500 newwd = self.state.get(self.originalwd, self.originalwd)
501 501 if newwd == revprecursor:
502 502 newwd = self.obsoletenotrebased[self.originalwd]
503 503 elif newwd < 0:
504 504 # original directory is a parent of rebase set root or ignored
505 505 newwd = self.originalwd
506 506 if newwd not in [c.rev() for c in repo[None].parents()]:
507 507 ui.note(_("update back to initial working directory parent\n"))
508 508 hg.updaterepo(repo, newwd, False)
509 509
510 510 if not self.keepf:
511 511 collapsedas = None
512 512 if self.collapsef:
513 513 collapsedas = newnode
514 514 clearrebased(ui, repo, self.dest, self.state, self.skipped,
515 515 collapsedas)
516 516
517 517 clearstatus(repo)
518 518 clearcollapsemsg(repo)
519 519
520 520 ui.note(_("rebase completed\n"))
521 521 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
522 522 if self.skipped:
523 523 skippedlen = len(self.skipped)
524 524 ui.note(_("%d revisions have been skipped\n") % skippedlen)
525 525
526 526 if (self.activebookmark and self.activebookmark in repo._bookmarks and
527 527 repo['.'].node() == repo._bookmarks[self.activebookmark]):
528 528 bookmarks.activate(repo, self.activebookmark)
529 529
530 530 @command('rebase',
531 531 [('s', 'source', '',
532 532 _('rebase the specified changeset and descendants'), _('REV')),
533 533 ('b', 'base', '',
534 534 _('rebase everything from branching point of specified changeset'),
535 535 _('REV')),
536 536 ('r', 'rev', [],
537 537 _('rebase these revisions'),
538 538 _('REV')),
539 539 ('d', 'dest', '',
540 540 _('rebase onto the specified changeset'), _('REV')),
541 541 ('', 'collapse', False, _('collapse the rebased changesets')),
542 542 ('m', 'message', '',
543 543 _('use text as collapse commit message'), _('TEXT')),
544 544 ('e', 'edit', False, _('invoke editor on commit messages')),
545 545 ('l', 'logfile', '',
546 546 _('read collapse commit message from file'), _('FILE')),
547 547 ('k', 'keep', False, _('keep original changesets')),
548 548 ('', 'keepbranches', False, _('keep original branch names')),
549 549 ('D', 'detach', False, _('(DEPRECATED)')),
550 550 ('i', 'interactive', False, _('(DEPRECATED)')),
551 551 ('t', 'tool', '', _('specify merge tool')),
552 552 ('c', 'continue', False, _('continue an interrupted rebase')),
553 553 ('a', 'abort', False, _('abort an interrupted rebase'))] +
554 554 templateopts,
555 555 _('[-s REV | -b REV] [-d REV] [OPTION]'))
556 556 def rebase(ui, repo, **opts):
557 557 """move changeset (and descendants) to a different branch
558 558
559 559 Rebase uses repeated merging to graft changesets from one part of
560 560 history (the source) onto another (the destination). This can be
561 561 useful for linearizing *local* changes relative to a master
562 562 development tree.
563 563
564 564 Published commits cannot be rebased (see :hg:`help phases`).
565 565 To copy commits, see :hg:`help graft`.
566 566
567 567 If you don't specify a destination changeset (``-d/--dest``), rebase
568 568 will use the same logic as :hg:`merge` to pick a destination. if
569 569 the current branch contains exactly one other head, the other head
570 570 is merged with by default. Otherwise, an explicit revision with
571 571 which to merge with must be provided. (destination changeset is not
572 572 modified by rebasing, but new changesets are added as its
573 573 descendants.)
574 574
575 575 Here are the ways to select changesets:
576 576
577 577 1. Explicitly select them using ``--rev``.
578 578
579 579 2. Use ``--source`` to select a root changeset and include all of its
580 580 descendants.
581 581
582 582 3. Use ``--base`` to select a changeset; rebase will find ancestors
583 583 and their descendants which are not also ancestors of the destination.
584 584
585 585 4. If you do not specify any of ``--rev``, ``source``, or ``--base``,
586 586 rebase will use ``--base .`` as above.
587 587
588 588 Rebase will destroy original changesets unless you use ``--keep``.
589 589 It will also move your bookmarks (even if you do).
590 590
591 591 Some changesets may be dropped if they do not contribute changes
592 592 (e.g. merges from the destination branch).
593 593
594 594 Unlike ``merge``, rebase will do nothing if you are at the branch tip of
595 595 a named branch with two heads. You will need to explicitly specify source
596 596 and/or destination.
597 597
598 598 If you need to use a tool to automate merge/conflict decisions, you
599 599 can specify one with ``--tool``, see :hg:`help merge-tools`.
600 600 As a caveat: the tool will not be used to mediate when a file was
601 601 deleted, there is no hook presently available for this.
602 602
603 603 If a rebase is interrupted to manually resolve a conflict, it can be
604 604 continued with --continue/-c or aborted with --abort/-a.
605 605
606 606 .. container:: verbose
607 607
608 608 Examples:
609 609
610 610 - move "local changes" (current commit back to branching point)
611 611 to the current branch tip after a pull::
612 612
613 613 hg rebase
614 614
615 615 - move a single changeset to the stable branch::
616 616
617 617 hg rebase -r 5f493448 -d stable
618 618
619 619 - splice a commit and all its descendants onto another part of history::
620 620
621 621 hg rebase --source c0c3 --dest 4cf9
622 622
623 623 - rebase everything on a branch marked by a bookmark onto the
624 624 default branch::
625 625
626 626 hg rebase --base myfeature --dest default
627 627
628 628 - collapse a sequence of changes into a single commit::
629 629
630 630 hg rebase --collapse -r 1520:1525 -d .
631 631
632 632 - move a named branch while preserving its name::
633 633
634 634 hg rebase -r "branch(featureX)" -d 1.3 --keepbranches
635 635
636 636 Configuration Options:
637 637
638 638 You can make rebase require a destination if you set the following config
639 639 option::
640 640
641 641 [commands]
642 642 rebase.requiredest = True
643 643
644 644 By default, rebase will close the transaction after each commit. For
645 645 performance purposes, you can configure rebase to use a single transaction
646 646 across the entire rebase. WARNING: This setting introduces a significant
647 647 risk of losing the work you've done in a rebase if the rebase aborts
648 648 unexpectedly::
649 649
650 650 [rebase]
651 651 singletransaction = True
652 652
653 653 Return Values:
654 654
655 655 Returns 0 on success, 1 if nothing to rebase or there are
656 656 unresolved conflicts.
657 657
658 658 """
659 659 rbsrt = rebaseruntime(repo, ui, opts)
660 660
661 661 with repo.wlock(), repo.lock():
662 662 # Validate input and define rebasing points
663 663 destf = opts.get('dest', None)
664 664 srcf = opts.get('source', None)
665 665 basef = opts.get('base', None)
666 666 revf = opts.get('rev', [])
667 667 # search default destination in this space
668 668 # used in the 'hg pull --rebase' case, see issue 5214.
669 669 destspace = opts.get('_destspace')
670 670 contf = opts.get('continue')
671 671 abortf = opts.get('abort')
672 672 if opts.get('interactive'):
673 673 try:
674 674 if extensions.find('histedit'):
675 675 enablehistedit = ''
676 676 except KeyError:
677 677 enablehistedit = " --config extensions.histedit="
678 678 help = "hg%s help -e histedit" % enablehistedit
679 679 msg = _("interactive history editing is supported by the "
680 680 "'histedit' extension (see \"%s\")") % help
681 681 raise error.Abort(msg)
682 682
683 683 if rbsrt.collapsemsg and not rbsrt.collapsef:
684 684 raise error.Abort(
685 685 _('message can only be specified with collapse'))
686 686
687 687 if contf or abortf:
688 688 if contf and abortf:
689 689 raise error.Abort(_('cannot use both abort and continue'))
690 690 if rbsrt.collapsef:
691 691 raise error.Abort(
692 692 _('cannot use collapse with continue or abort'))
693 693 if srcf or basef or destf:
694 694 raise error.Abort(
695 695 _('abort and continue do not allow specifying revisions'))
696 696 if abortf and opts.get('tool', False):
697 697 ui.warn(_('tool option will be ignored\n'))
698 698 if contf:
699 699 ms = mergemod.mergestate.read(repo)
700 700 mergeutil.checkunresolved(ms)
701 701
702 702 retcode = rbsrt._prepareabortorcontinue(abortf)
703 703 if retcode is not None:
704 704 return retcode
705 705 else:
706 706 dest, rebaseset = _definesets(ui, repo, destf, srcf, basef, revf,
707 707 destspace=destspace)
708 708 retcode = rbsrt._preparenewrebase(dest, rebaseset)
709 709 if retcode is not None:
710 710 return retcode
711 711
712 712 tr = None
713 713 dsguard = None
714 714
715 715 singletr = ui.configbool('rebase', 'singletransaction')
716 716 if singletr:
717 717 tr = repo.transaction('rebase')
718 718 with util.acceptintervention(tr):
719 719 if singletr:
720 720 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
721 721 with util.acceptintervention(dsguard):
722 722 rbsrt._performrebase(tr)
723 723
724 724 rbsrt._finishrebase()
725 725
726 726 def _definesets(ui, repo, destf=None, srcf=None, basef=None, revf=None,
727 727 destspace=None):
728 728 """use revisions argument to define destination and rebase set
729 729 """
730 730 if revf is None:
731 731 revf = []
732 732
733 733 # destspace is here to work around issues with `hg pull --rebase` see
734 734 # issue5214 for details
735 735 if srcf and basef:
736 736 raise error.Abort(_('cannot specify both a source and a base'))
737 737 if revf and basef:
738 738 raise error.Abort(_('cannot specify both a revision and a base'))
739 739 if revf and srcf:
740 740 raise error.Abort(_('cannot specify both a revision and a source'))
741 741
742 742 cmdutil.checkunfinished(repo)
743 743 cmdutil.bailifchanged(repo)
744 744
745 745 if ui.configbool('commands', 'rebase.requiredest') and not destf:
746 746 raise error.Abort(_('you must specify a destination'),
747 747 hint=_('use: hg rebase -d REV'))
748 748
749 749 if destf:
750 750 dest = scmutil.revsingle(repo, destf)
751 751
752 752 if revf:
753 753 rebaseset = scmutil.revrange(repo, revf)
754 754 if not rebaseset:
755 755 ui.status(_('empty "rev" revision set - nothing to rebase\n'))
756 756 return None, None
757 757 elif srcf:
758 758 src = scmutil.revrange(repo, [srcf])
759 759 if not src:
760 760 ui.status(_('empty "source" revision set - nothing to rebase\n'))
761 761 return None, None
762 762 rebaseset = repo.revs('(%ld)::', src)
763 763 assert rebaseset
764 764 else:
765 765 base = scmutil.revrange(repo, [basef or '.'])
766 766 if not base:
767 767 ui.status(_('empty "base" revision set - '
768 768 "can't compute rebase set\n"))
769 769 return None, None
770 770 if not destf:
771 771 dest = repo[_destrebase(repo, base, destspace=destspace)]
772 772 destf = str(dest)
773 773
774 774 roots = [] # selected children of branching points
775 775 bpbase = {} # {branchingpoint: [origbase]}
776 776 for b in base: # group bases by branching points
777 777 bp = repo.revs('ancestor(%d, %d)', b, dest).first()
778 778 bpbase[bp] = bpbase.get(bp, []) + [b]
779 779 if None in bpbase:
780 780 # emulate the old behavior, showing "nothing to rebase" (a better
781 781 # behavior may be abort with "cannot find branching point" error)
782 782 bpbase.clear()
783 783 for bp, bs in bpbase.iteritems(): # calculate roots
784 784 roots += list(repo.revs('children(%d) & ancestors(%ld)', bp, bs))
785 785
786 786 rebaseset = repo.revs('%ld::', roots)
787 787
788 788 if not rebaseset:
789 789 # transform to list because smartsets are not comparable to
790 790 # lists. This should be improved to honor laziness of
791 791 # smartset.
792 792 if list(base) == [dest.rev()]:
793 793 if basef:
794 794 ui.status(_('nothing to rebase - %s is both "base"'
795 795 ' and destination\n') % dest)
796 796 else:
797 797 ui.status(_('nothing to rebase - working directory '
798 798 'parent is also destination\n'))
799 799 elif not repo.revs('%ld - ::%d', base, dest):
800 800 if basef:
801 801 ui.status(_('nothing to rebase - "base" %s is '
802 802 'already an ancestor of destination '
803 803 '%s\n') %
804 804 ('+'.join(str(repo[r]) for r in base),
805 805 dest))
806 806 else:
807 807 ui.status(_('nothing to rebase - working '
808 808 'directory parent is already an '
809 809 'ancestor of destination %s\n') % dest)
810 810 else: # can it happen?
811 811 ui.status(_('nothing to rebase from %s to %s\n') %
812 812 ('+'.join(str(repo[r]) for r in base), dest))
813 813 return None, None
814 814
815 815 if not destf:
816 816 dest = repo[_destrebase(repo, rebaseset, destspace=destspace)]
817 817 destf = str(dest)
818 818
819 819 return dest, rebaseset
820 820
821 821 def externalparent(repo, state, destancestors):
822 822 """Return the revision that should be used as the second parent
823 823 when the revisions in state is collapsed on top of destancestors.
824 824 Abort if there is more than one parent.
825 825 """
826 826 parents = set()
827 827 source = min(state)
828 828 for rev in state:
829 829 if rev == source:
830 830 continue
831 831 for p in repo[rev].parents():
832 832 if (p.rev() not in state
833 833 and p.rev() not in destancestors):
834 834 parents.add(p.rev())
835 835 if not parents:
836 836 return nullrev
837 837 if len(parents) == 1:
838 838 return parents.pop()
839 839 raise error.Abort(_('unable to collapse on top of %s, there is more '
840 840 'than one external parent: %s') %
841 841 (max(destancestors),
842 842 ', '.join(str(p) for p in sorted(parents))))
843 843
844 844 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None,
845 845 keepbranches=False, date=None):
846 846 '''Commit the wd changes with parents p1 and p2. Reuse commit info from rev
847 847 but also store useful information in extra.
848 848 Return node of committed revision.'''
849 849 dsguard = util.nullcontextmanager()
850 850 if not repo.ui.configbool('rebase', 'singletransaction'):
851 851 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
852 852 with dsguard:
853 853 repo.setparents(repo[p1].node(), repo[p2].node())
854 854 ctx = repo[rev]
855 855 if commitmsg is None:
856 856 commitmsg = ctx.description()
857 857 keepbranch = keepbranches and repo[p1].branch() != ctx.branch()
858 858 extra = {'rebase_source': ctx.hex()}
859 859 if extrafn:
860 860 extrafn(ctx, extra)
861 861
862 862 destphase = max(ctx.phase(), phases.draft)
863 863 overrides = {('phases', 'new-commit'): destphase}
864 864 with repo.ui.configoverride(overrides, 'rebase'):
865 865 if keepbranch:
866 866 repo.ui.setconfig('ui', 'allowemptycommit', True)
867 867 # Commit might fail if unresolved files exist
868 868 if date is None:
869 869 date = ctx.date()
870 870 newnode = repo.commit(text=commitmsg, user=ctx.user(),
871 871 date=date, extra=extra, editor=editor)
872 872
873 873 repo.dirstate.setbranch(repo[newnode].branch())
874 874 return newnode
875 875
876 876 def rebasenode(repo, rev, p1, base, state, collapse, dest):
877 877 'Rebase a single revision rev on top of p1 using base as merge ancestor'
878 878 # Merge phase
879 879 # Update to destination and merge it with local
880 880 if repo['.'].rev() != p1:
881 881 repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
882 882 mergemod.update(repo, p1, False, True)
883 883 else:
884 884 repo.ui.debug(" already in destination\n")
885 885 repo.dirstate.write(repo.currenttransaction())
886 886 repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev]))
887 887 if base is not None:
888 888 repo.ui.debug(" detach base %d:%s\n" % (base, repo[base]))
889 889 # When collapsing in-place, the parent is the common ancestor, we
890 890 # have to allow merging with it.
891 891 stats = mergemod.update(repo, rev, True, True, base, collapse,
892 892 labels=['dest', 'source'])
893 893 if collapse:
894 894 copies.duplicatecopies(repo, rev, dest)
895 895 else:
896 896 # If we're not using --collapse, we need to
897 897 # duplicate copies between the revision we're
898 898 # rebasing and its first parent, but *not*
899 899 # duplicate any copies that have already been
900 900 # performed in the destination.
901 901 p1rev = repo[rev].p1().rev()
902 902 copies.duplicatecopies(repo, rev, p1rev, skiprev=dest)
903 903 return stats
904 904
905 905 def adjustdest(repo, rev, dest, state):
906 906 """adjust rebase destination given the current rebase state
907 907
908 908 rev is what is being rebased. Return a list of two revs, which are the
909 909 adjusted destinations for rev's p1 and p2, respectively. If a parent is
910 910 nullrev, return dest without adjustment for it.
911 911
912 912 For example, when doing rebase -r B+E -d F, rebase will first move B to B1,
913 913 and E's destination will be adjusted from F to B1.
914 914
915 915 B1 <- written during rebasing B
916 916 |
917 917 F <- original destination of B, E
918 918 |
919 919 | E <- rev, which is being rebased
920 920 | |
921 921 | D <- prev, one parent of rev being checked
922 922 | |
923 923 | x <- skipped, ex. no successor or successor in (::dest)
924 924 | |
925 925 | C
926 926 | |
927 927 | B <- rebased as B1
928 928 |/
929 929 A
930 930
931 931 Another example about merge changeset, rebase -r C+G+H -d K, rebase will
932 932 first move C to C1, G to G1, and when it's checking H, the adjusted
933 933 destinations will be [C1, G1].
934 934
935 935 H C1 G1
936 936 /| | /
937 937 F G |/
938 938 K | | -> K
939 939 | C D |
940 940 | |/ |
941 941 | B | ...
942 942 |/ |/
943 943 A A
944 944 """
945 945 result = []
946 946 for prev in repo.changelog.parentrevs(rev):
947 947 adjusted = dest
948 948 if prev != nullrev:
949 949 # pick already rebased revs from state
950 950 source = [s for s, d in state.items() if d > 0]
951 951 candidate = repo.revs('max(%ld and (::%d))', source, prev).first()
952 952 if candidate is not None:
953 953 adjusted = state[candidate]
954 954 result.append(adjusted)
955 955 return result
956 956
957 957 def _checkobsrebase(repo, ui, rebaseobsrevs, rebasesetrevs, rebaseobsskipped):
958 958 """
959 959 Abort if rebase will create divergence or rebase is noop because of markers
960 960
961 961 `rebaseobsrevs`: set of obsolete revision in source
962 962 `rebasesetrevs`: set of revisions to be rebased from source
963 963 `rebaseobsskipped`: set of revisions from source skipped because they have
964 964 successors in destination
965 965 """
966 966 # Obsolete node with successors not in dest leads to divergence
967 967 divergenceok = ui.configbool('experimental',
968 968 'allowdivergence')
969 969 divergencebasecandidates = rebaseobsrevs - rebaseobsskipped
970 970
971 971 if divergencebasecandidates and not divergenceok:
972 972 divhashes = (str(repo[r])
973 973 for r in divergencebasecandidates)
974 974 msg = _("this rebase will cause "
975 975 "divergences from: %s")
976 976 h = _("to force the rebase please set "
977 977 "experimental.allowdivergence=True")
978 978 raise error.Abort(msg % (",".join(divhashes),), hint=h)
979 979
980 980 def successorrevs(repo, rev):
981 981 """yield revision numbers for successors of rev"""
982 982 unfi = repo.unfiltered()
983 983 nodemap = unfi.changelog.nodemap
984 984 for s in obsutil.allsuccessors(unfi.obsstore, [unfi[rev].node()]):
985 985 if s in nodemap:
986 986 yield nodemap[s]
987 987
988 988 def defineparents(repo, rev, dest, state):
989 989 """Return new parents and optionally a merge base for rev being rebased
990 990
991 991 The destination specified by "dest" cannot always be used directly because
992 992 previously rebase result could affect destination. For example,
993 993
994 994 D E rebase -r C+D+E -d B
995 995 |/ C will be rebased to C'
996 996 B C D's new destination will be C' instead of B
997 997 |/ E's new destination will be C' instead of B
998 998 A
999 999
1000 1000 The new parents of a merge is slightly more complicated. See the comment
1001 1001 block below.
1002 1002 """
1003 1003 cl = repo.changelog
1004 1004 def isancestor(a, b):
1005 1005 # take revision numbers instead of nodes
1006 1006 if a == b:
1007 1007 return True
1008 1008 elif a > b:
1009 1009 return False
1010 1010 return cl.isancestor(cl.node(a), cl.node(b))
1011 1011
1012 1012 oldps = repo.changelog.parentrevs(rev) # old parents
1013 1013 newps = [nullrev, nullrev] # new parents
1014 1014 dests = adjustdest(repo, rev, dest, state) # adjusted destinations
1015 1015 bases = list(oldps) # merge base candidates, initially just old parents
1016 1016
1017 1017 if all(r == nullrev for r in oldps[1:]):
1018 1018 # For non-merge changeset, just move p to adjusted dest as requested.
1019 1019 newps[0] = dests[0]
1020 1020 else:
1021 1021 # For merge changeset, if we move p to dests[i] unconditionally, both
1022 1022 # parents may change and the end result looks like "the merge loses a
1023 1023 # parent", which is a surprise. This is a limit because "--dest" only
1024 1024 # accepts one dest per src.
1025 1025 #
1026 1026 # Therefore, only move p with reasonable conditions (in this order):
1027 1027 # 1. use dest, if dest is a descendent of (p or one of p's successors)
1028 1028 # 2. use p's rebased result, if p is rebased (state[p] > 0)
1029 1029 #
1030 1030 # Comparing with adjustdest, the logic here does some additional work:
1031 1031 # 1. decide which parents will not be moved towards dest
1032 1032 # 2. if the above decision is "no", should a parent still be moved
1033 1033 # because it was rebased?
1034 1034 #
1035 1035 # For example:
1036 1036 #
1037 1037 # C # "rebase -r C -d D" is an error since none of the parents
1038 1038 # /| # can be moved. "rebase -r B+C -d D" will move C's parent
1039 1039 # A B D # B (using rule "2."), since B will be rebased.
1040 1040 #
1041 1041 # The loop tries to be not rely on the fact that a Mercurial node has
1042 1042 # at most 2 parents.
1043 1043 for i, p in enumerate(oldps):
1044 1044 np = p # new parent
1045 1045 if any(isancestor(x, dests[i]) for x in successorrevs(repo, p)):
1046 1046 np = dests[i]
1047 1047 elif p in state and state[p] > 0:
1048 1048 np = state[p]
1049 1049
1050 1050 # "bases" only record "special" merge bases that cannot be
1051 1051 # calculated from changelog DAG (i.e. isancestor(p, np) is False).
1052 1052 # For example:
1053 1053 #
1054 1054 # B' # rebase -s B -d D, when B was rebased to B'. dest for C
1055 1055 # | C # is B', but merge base for C is B, instead of
1056 1056 # D | # changelog.ancestor(C, B') == A. If changelog DAG and
1057 1057 # | B # "state" edges are merged (so there will be an edge from
1058 1058 # |/ # B to B'), the merge base is still ancestor(C, B') in
1059 1059 # A # the merged graph.
1060 1060 #
1061 1061 # Also see https://bz.mercurial-scm.org/show_bug.cgi?id=1950#c8
1062 1062 # which uses "virtual null merge" to explain this situation.
1063 1063 if isancestor(p, np):
1064 1064 bases[i] = nullrev
1065 1065
1066 1066 # If one parent becomes an ancestor of the other, drop the ancestor
1067 1067 for j, x in enumerate(newps[:i]):
1068 1068 if x == nullrev:
1069 1069 continue
1070 1070 if isancestor(np, x):
1071 1071 np = nullrev
1072 1072 elif isancestor(x, np):
1073 1073 newps[j] = np
1074 1074 np = nullrev
1075 1075 bases[j], bases[i] = bases[i], bases[j]
1076 1076
1077 1077 newps[i] = np
1078 1078
1079 1079 # "rebasenode" updates to new p1, and the old p1 will be used as merge
1080 1080 # base. If only p2 changes, merging using unchanged p1 as merge base is
1081 1081 # suboptimal. Therefore swap parents to make the merge sane.
1082 1082 if newps[1] != nullrev and oldps[0] == newps[0]:
1083 1083 assert len(newps) == 2 and len(oldps) == 2
1084 1084 newps.reverse()
1085 1085 bases.reverse()
1086 1086
1087 1087 # No parent change might be an error because we fail to make rev a
1088 1088 # descendent of requested dest. This can happen, for example:
1089 1089 #
1090 1090 # C # rebase -r C -d D
1091 1091 # /| # None of A and B will be changed to D and rebase fails.
1092 1092 # A B D
1093 1093 if set(newps) == set(oldps) and dest not in newps:
1094 # The error message is for compatibility. It's a bit misleading
1095 # since rebase is not supposed to add new parents.
1096 raise error.Abort(_('cannot use revision %d as base, '
1097 'result would have 3 parents') % rev)
1094 raise error.Abort(_('cannot rebase %d:%s without '
1095 'moving at least one of its parents')
1096 % (rev, repo[rev]))
1098 1097
1099 1098 repo.ui.debug(" future parents are %d and %d\n" % tuple(newps))
1100 1099
1101 1100 # "rebasenode" updates to new p1, use the corresponding merge base.
1102 1101 if bases[0] != nullrev:
1103 1102 base = bases[0]
1104 1103 else:
1105 1104 base = None
1106 1105
1107 1106 # Check if the merge will contain unwanted changes. That may happen if
1108 1107 # there are multiple special (non-changelog ancestor) merge bases, which
1109 1108 # cannot be handled well by the 3-way merge algorithm. For example:
1110 1109 #
1111 1110 # F
1112 1111 # /|
1113 1112 # D E # "rebase -r D+E+F -d Z", when rebasing F, if "D" was chosen
1114 1113 # | | # as merge base, the difference between D and F will include
1115 1114 # B C # C, so the rebased F will contain C surprisingly. If "E" was
1116 1115 # |/ # chosen, the rebased F will contain B.
1117 1116 # A Z
1118 1117 #
1119 1118 # But our merge base candidates (D and E in above case) could still be
1120 1119 # better than the default (ancestor(F, Z) == null). Therefore still
1121 1120 # pick one (so choose p1 above).
1122 1121 if sum(1 for b in bases if b != nullrev) > 1:
1123 1122 assert base is not None
1124 1123
1125 1124 # Revisions in the side (not chosen as merge base) branch that might
1126 1125 # contain "surprising" contents
1127 1126 siderevs = list(repo.revs('((%ld-%d) %% (%d+%d))',
1128 1127 bases, base, base, dest))
1129 1128
1130 1129 # If those revisions are covered by rebaseset, the result is good.
1131 1130 # A merge in rebaseset would be considered to cover its ancestors.
1132 1131 if siderevs:
1133 1132 rebaseset = [r for r, d in state.items() if d > 0]
1134 1133 merges = [r for r in rebaseset if cl.parentrevs(r)[1] != nullrev]
1135 1134 unwantedrevs = list(repo.revs('%ld - (::%ld) - %ld',
1136 1135 siderevs, merges, rebaseset))
1137 1136
1138 1137 # For revs not covered, it is worth a warning.
1139 1138 if unwantedrevs:
1140 1139 repo.ui.warn(
1141 1140 _('warning: rebasing %d:%s may include unwanted changes '
1142 1141 'from %s\n')
1143 1142 % (rev, repo[rev], ', '.join('%d:%s' % (r, repo[r])
1144 1143 for r in unwantedrevs)))
1145 1144
1146 1145 return newps[0], newps[1], base
1147 1146
1148 1147 def isagitpatch(repo, patchname):
1149 1148 'Return true if the given patch is in git format'
1150 1149 mqpatch = os.path.join(repo.mq.path, patchname)
1151 1150 for line in patch.linereader(file(mqpatch, 'rb')):
1152 1151 if line.startswith('diff --git'):
1153 1152 return True
1154 1153 return False
1155 1154
1156 1155 def updatemq(repo, state, skipped, **opts):
1157 1156 'Update rebased mq patches - finalize and then import them'
1158 1157 mqrebase = {}
1159 1158 mq = repo.mq
1160 1159 original_series = mq.fullseries[:]
1161 1160 skippedpatches = set()
1162 1161
1163 1162 for p in mq.applied:
1164 1163 rev = repo[p.node].rev()
1165 1164 if rev in state:
1166 1165 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
1167 1166 (rev, p.name))
1168 1167 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
1169 1168 else:
1170 1169 # Applied but not rebased, not sure this should happen
1171 1170 skippedpatches.add(p.name)
1172 1171
1173 1172 if mqrebase:
1174 1173 mq.finish(repo, mqrebase.keys())
1175 1174
1176 1175 # We must start import from the newest revision
1177 1176 for rev in sorted(mqrebase, reverse=True):
1178 1177 if rev not in skipped:
1179 1178 name, isgit = mqrebase[rev]
1180 1179 repo.ui.note(_('updating mq patch %s to %s:%s\n') %
1181 1180 (name, state[rev], repo[state[rev]]))
1182 1181 mq.qimport(repo, (), patchname=name, git=isgit,
1183 1182 rev=[str(state[rev])])
1184 1183 else:
1185 1184 # Rebased and skipped
1186 1185 skippedpatches.add(mqrebase[rev][0])
1187 1186
1188 1187 # Patches were either applied and rebased and imported in
1189 1188 # order, applied and removed or unapplied. Discard the removed
1190 1189 # ones while preserving the original series order and guards.
1191 1190 newseries = [s for s in original_series
1192 1191 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
1193 1192 mq.fullseries[:] = newseries
1194 1193 mq.seriesdirty = True
1195 1194 mq.savedirty()
1196 1195
1197 1196 def storecollapsemsg(repo, collapsemsg):
1198 1197 'Store the collapse message to allow recovery'
1199 1198 collapsemsg = collapsemsg or ''
1200 1199 f = repo.vfs("last-message.txt", "w")
1201 1200 f.write("%s\n" % collapsemsg)
1202 1201 f.close()
1203 1202
1204 1203 def clearcollapsemsg(repo):
1205 1204 'Remove collapse message file'
1206 1205 repo.vfs.unlinkpath("last-message.txt", ignoremissing=True)
1207 1206
1208 1207 def restorecollapsemsg(repo, isabort):
1209 1208 'Restore previously stored collapse message'
1210 1209 try:
1211 1210 f = repo.vfs("last-message.txt")
1212 1211 collapsemsg = f.readline().strip()
1213 1212 f.close()
1214 1213 except IOError as err:
1215 1214 if err.errno != errno.ENOENT:
1216 1215 raise
1217 1216 if isabort:
1218 1217 # Oh well, just abort like normal
1219 1218 collapsemsg = ''
1220 1219 else:
1221 1220 raise error.Abort(_('missing .hg/last-message.txt for rebase'))
1222 1221 return collapsemsg
1223 1222
1224 1223 def clearstatus(repo):
1225 1224 'Remove the status files'
1226 1225 _clearrebasesetvisibiliy(repo)
1227 1226 # Make sure the active transaction won't write the state file
1228 1227 tr = repo.currenttransaction()
1229 1228 if tr:
1230 1229 tr.removefilegenerator('rebasestate')
1231 1230 repo.vfs.unlinkpath("rebasestate", ignoremissing=True)
1232 1231
1233 1232 def needupdate(repo, state):
1234 1233 '''check whether we should `update --clean` away from a merge, or if
1235 1234 somehow the working dir got forcibly updated, e.g. by older hg'''
1236 1235 parents = [p.rev() for p in repo[None].parents()]
1237 1236
1238 1237 # Are we in a merge state at all?
1239 1238 if len(parents) < 2:
1240 1239 return False
1241 1240
1242 1241 # We should be standing on the first as-of-yet unrebased commit.
1243 1242 firstunrebased = min([old for old, new in state.iteritems()
1244 1243 if new == nullrev])
1245 1244 if firstunrebased in parents:
1246 1245 return True
1247 1246
1248 1247 return False
1249 1248
1250 1249 def abort(repo, originalwd, dest, state, activebookmark=None):
1251 1250 '''Restore the repository to its original state. Additional args:
1252 1251
1253 1252 activebookmark: the name of the bookmark that should be active after the
1254 1253 restore'''
1255 1254
1256 1255 try:
1257 1256 # If the first commits in the rebased set get skipped during the rebase,
1258 1257 # their values within the state mapping will be the dest rev id. The
1259 1258 # dstates list must must not contain the dest rev (issue4896)
1260 1259 dstates = [s for s in state.values() if s >= 0 and s != dest]
1261 1260 immutable = [d for d in dstates if not repo[d].mutable()]
1262 1261 cleanup = True
1263 1262 if immutable:
1264 1263 repo.ui.warn(_("warning: can't clean up public changesets %s\n")
1265 1264 % ', '.join(str(repo[r]) for r in immutable),
1266 1265 hint=_("see 'hg help phases' for details"))
1267 1266 cleanup = False
1268 1267
1269 1268 descendants = set()
1270 1269 if dstates:
1271 1270 descendants = set(repo.changelog.descendants(dstates))
1272 1271 if descendants - set(dstates):
1273 1272 repo.ui.warn(_("warning: new changesets detected on destination "
1274 1273 "branch, can't strip\n"))
1275 1274 cleanup = False
1276 1275
1277 1276 if cleanup:
1278 1277 shouldupdate = False
1279 1278 rebased = filter(lambda x: x >= 0 and x != dest, state.values())
1280 1279 if rebased:
1281 1280 strippoints = [
1282 1281 c.node() for c in repo.set('roots(%ld)', rebased)]
1283 1282
1284 1283 updateifonnodes = set(rebased)
1285 1284 updateifonnodes.add(dest)
1286 1285 updateifonnodes.add(originalwd)
1287 1286 shouldupdate = repo['.'].rev() in updateifonnodes
1288 1287
1289 1288 # Update away from the rebase if necessary
1290 1289 if shouldupdate or needupdate(repo, state):
1291 1290 mergemod.update(repo, originalwd, False, True)
1292 1291
1293 1292 # Strip from the first rebased revision
1294 1293 if rebased:
1295 1294 # no backup of rebased cset versions needed
1296 1295 repair.strip(repo.ui, repo, strippoints)
1297 1296
1298 1297 if activebookmark and activebookmark in repo._bookmarks:
1299 1298 bookmarks.activate(repo, activebookmark)
1300 1299
1301 1300 finally:
1302 1301 clearstatus(repo)
1303 1302 clearcollapsemsg(repo)
1304 1303 repo.ui.warn(_('rebase aborted\n'))
1305 1304 return 0
1306 1305
1307 1306 def buildstate(repo, dest, rebaseset, collapse, obsoletenotrebased):
1308 1307 '''Define which revisions are going to be rebased and where
1309 1308
1310 1309 repo: repo
1311 1310 dest: context
1312 1311 rebaseset: set of rev
1313 1312 '''
1314 1313 originalwd = repo['.'].rev()
1315 1314 _setrebasesetvisibility(repo, set(rebaseset) | {originalwd})
1316 1315
1317 1316 # This check isn't strictly necessary, since mq detects commits over an
1318 1317 # applied patch. But it prevents messing up the working directory when
1319 1318 # a partially completed rebase is blocked by mq.
1320 1319 if 'qtip' in repo.tags() and (dest.node() in
1321 1320 [s.node for s in repo.mq.applied]):
1322 1321 raise error.Abort(_('cannot rebase onto an applied mq patch'))
1323 1322
1324 1323 roots = list(repo.set('roots(%ld)', rebaseset))
1325 1324 if not roots:
1326 1325 raise error.Abort(_('no matching revisions'))
1327 1326 roots.sort()
1328 1327 state = dict.fromkeys(rebaseset, revtodo)
1329 1328 detachset = set()
1330 1329 emptyrebase = True
1331 1330 for root in roots:
1332 1331 commonbase = root.ancestor(dest)
1333 1332 if commonbase == root:
1334 1333 raise error.Abort(_('source is ancestor of destination'))
1335 1334 if commonbase == dest:
1336 1335 wctx = repo[None]
1337 1336 if dest == wctx.p1():
1338 1337 # when rebasing to '.', it will use the current wd branch name
1339 1338 samebranch = root.branch() == wctx.branch()
1340 1339 else:
1341 1340 samebranch = root.branch() == dest.branch()
1342 1341 if not collapse and samebranch and dest in root.parents():
1343 1342 # mark the revision as done by setting its new revision
1344 1343 # equal to its old (current) revisions
1345 1344 state[root.rev()] = root.rev()
1346 1345 repo.ui.debug('source is a child of destination\n')
1347 1346 continue
1348 1347
1349 1348 emptyrebase = False
1350 1349 repo.ui.debug('rebase onto %s starting from %s\n' % (dest, root))
1351 1350 # Rebase tries to turn <dest> into a parent of <root> while
1352 1351 # preserving the number of parents of rebased changesets:
1353 1352 #
1354 1353 # - A changeset with a single parent will always be rebased as a
1355 1354 # changeset with a single parent.
1356 1355 #
1357 1356 # - A merge will be rebased as merge unless its parents are both
1358 1357 # ancestors of <dest> or are themselves in the rebased set and
1359 1358 # pruned while rebased.
1360 1359 #
1361 1360 # If one parent of <root> is an ancestor of <dest>, the rebased
1362 1361 # version of this parent will be <dest>. This is always true with
1363 1362 # --base option.
1364 1363 #
1365 1364 # Otherwise, we need to *replace* the original parents with
1366 1365 # <dest>. This "detaches" the rebased set from its former location
1367 1366 # and rebases it onto <dest>. Changes introduced by ancestors of
1368 1367 # <root> not common with <dest> (the detachset, marked as
1369 1368 # nullmerge) are "removed" from the rebased changesets.
1370 1369 #
1371 1370 # - If <root> has a single parent, set it to <dest>.
1372 1371 #
1373 1372 # - If <root> is a merge, we cannot decide which parent to
1374 1373 # replace, the rebase operation is not clearly defined.
1375 1374 #
1376 1375 # The table below sums up this behavior:
1377 1376 #
1378 1377 # +------------------+----------------------+-------------------------+
1379 1378 # | | one parent | merge |
1380 1379 # +------------------+----------------------+-------------------------+
1381 1380 # | parent in | new parent is <dest> | parents in ::<dest> are |
1382 1381 # | ::<dest> | | remapped to <dest> |
1383 1382 # +------------------+----------------------+-------------------------+
1384 1383 # | unrelated source | new parent is <dest> | ambiguous, abort |
1385 1384 # +------------------+----------------------+-------------------------+
1386 1385 #
1387 1386 # The actual abort is handled by `defineparents`
1388 1387 if len(root.parents()) <= 1:
1389 1388 # ancestors of <root> not ancestors of <dest>
1390 1389 detachset.update(repo.changelog.findmissingrevs([commonbase.rev()],
1391 1390 [root.rev()]))
1392 1391 if emptyrebase:
1393 1392 return None
1394 1393 for rev in sorted(state):
1395 1394 parents = [p for p in repo.changelog.parentrevs(rev) if p != nullrev]
1396 1395 # if all parents of this revision are done, then so is this revision
1397 1396 if parents and all((state.get(p) == p for p in parents)):
1398 1397 state[rev] = rev
1399 1398 for r in detachset:
1400 1399 if r not in state:
1401 1400 state[r] = nullmerge
1402 1401 if len(roots) > 1:
1403 1402 # If we have multiple roots, we may have "hole" in the rebase set.
1404 1403 # Rebase roots that descend from those "hole" should not be detached as
1405 1404 # other root are. We use the special `revignored` to inform rebase that
1406 1405 # the revision should be ignored but that `defineparents` should search
1407 1406 # a rebase destination that make sense regarding rebased topology.
1408 1407 rebasedomain = set(repo.revs('%ld::%ld', rebaseset, rebaseset))
1409 1408 for ignored in set(rebasedomain) - set(rebaseset):
1410 1409 state[ignored] = revignored
1411 1410 for r in obsoletenotrebased:
1412 1411 if obsoletenotrebased[r] is None:
1413 1412 state[r] = revpruned
1414 1413 else:
1415 1414 state[r] = revprecursor
1416 1415 return originalwd, dest.rev(), state
1417 1416
1418 1417 def clearrebased(ui, repo, dest, state, skipped, collapsedas=None):
1419 1418 """dispose of rebased revision at the end of the rebase
1420 1419
1421 1420 If `collapsedas` is not None, the rebase was a collapse whose result if the
1422 1421 `collapsedas` node."""
1423 1422 tonode = repo.changelog.node
1424 1423 # Move bookmark of skipped nodes to destination. This cannot be handled
1425 1424 # by scmutil.cleanupnodes since it will treat rev as removed (no successor)
1426 1425 # and move bookmark backwards.
1427 1426 bmchanges = [(name, tonode(max(adjustdest(repo, rev, dest, state))))
1428 1427 for rev in skipped
1429 1428 for name in repo.nodebookmarks(tonode(rev))]
1430 1429 if bmchanges:
1431 1430 with repo.transaction('rebase') as tr:
1432 1431 repo._bookmarks.applychanges(repo, tr, bmchanges)
1433 1432 mapping = {}
1434 1433 for rev, newrev in sorted(state.items()):
1435 1434 if newrev >= 0 and newrev != rev:
1436 1435 if rev in skipped:
1437 1436 succs = ()
1438 1437 elif collapsedas is not None:
1439 1438 succs = (collapsedas,)
1440 1439 else:
1441 1440 succs = (tonode(newrev),)
1442 1441 mapping[tonode(rev)] = succs
1443 1442 scmutil.cleanupnodes(repo, mapping, 'rebase')
1444 1443
1445 1444 def pullrebase(orig, ui, repo, *args, **opts):
1446 1445 'Call rebase after pull if the latter has been invoked with --rebase'
1447 1446 ret = None
1448 1447 if opts.get('rebase'):
1449 1448 if ui.configbool('commands', 'rebase.requiredest'):
1450 1449 msg = _('rebase destination required by configuration')
1451 1450 hint = _('use hg pull followed by hg rebase -d DEST')
1452 1451 raise error.Abort(msg, hint=hint)
1453 1452
1454 1453 with repo.wlock(), repo.lock():
1455 1454 if opts.get('update'):
1456 1455 del opts['update']
1457 1456 ui.debug('--update and --rebase are not compatible, ignoring '
1458 1457 'the update flag\n')
1459 1458
1460 1459 cmdutil.checkunfinished(repo)
1461 1460 cmdutil.bailifchanged(repo, hint=_('cannot pull with rebase: '
1462 1461 'please commit or shelve your changes first'))
1463 1462
1464 1463 revsprepull = len(repo)
1465 1464 origpostincoming = commands.postincoming
1466 1465 def _dummy(*args, **kwargs):
1467 1466 pass
1468 1467 commands.postincoming = _dummy
1469 1468 try:
1470 1469 ret = orig(ui, repo, *args, **opts)
1471 1470 finally:
1472 1471 commands.postincoming = origpostincoming
1473 1472 revspostpull = len(repo)
1474 1473 if revspostpull > revsprepull:
1475 1474 # --rev option from pull conflict with rebase own --rev
1476 1475 # dropping it
1477 1476 if 'rev' in opts:
1478 1477 del opts['rev']
1479 1478 # positional argument from pull conflicts with rebase's own
1480 1479 # --source.
1481 1480 if 'source' in opts:
1482 1481 del opts['source']
1483 1482 # revsprepull is the len of the repo, not revnum of tip.
1484 1483 destspace = list(repo.changelog.revs(start=revsprepull))
1485 1484 opts['_destspace'] = destspace
1486 1485 try:
1487 1486 rebase(ui, repo, **opts)
1488 1487 except error.NoMergeDestAbort:
1489 1488 # we can maybe update instead
1490 1489 rev, _a, _b = destutil.destupdate(repo)
1491 1490 if rev == repo['.'].rev():
1492 1491 ui.status(_('nothing to rebase\n'))
1493 1492 else:
1494 1493 ui.status(_('nothing to rebase - updating instead\n'))
1495 1494 # not passing argument to get the bare update behavior
1496 1495 # with warning and trumpets
1497 1496 commands.update(ui, repo)
1498 1497 else:
1499 1498 if opts.get('tool'):
1500 1499 raise error.Abort(_('--tool can only be used with --rebase'))
1501 1500 ret = orig(ui, repo, *args, **opts)
1502 1501
1503 1502 return ret
1504 1503
1505 1504 def _setrebasesetvisibility(repo, revs):
1506 1505 """store the currently rebased set on the repo object
1507 1506
1508 1507 This is used by another function to prevent rebased revision to because
1509 1508 hidden (see issue4504)"""
1510 1509 repo = repo.unfiltered()
1511 1510 repo._rebaseset = revs
1512 1511 # invalidate cache if visibility changes
1513 1512 hiddens = repo.filteredrevcache.get('visible', set())
1514 1513 if revs & hiddens:
1515 1514 repo.invalidatevolatilesets()
1516 1515
1517 1516 def _clearrebasesetvisibiliy(repo):
1518 1517 """remove rebaseset data from the repo"""
1519 1518 repo = repo.unfiltered()
1520 1519 if '_rebaseset' in vars(repo):
1521 1520 del repo._rebaseset
1522 1521
1523 1522 def _rebasedvisible(orig, repo):
1524 1523 """ensure rebased revs stay visible (see issue4504)"""
1525 1524 blockers = orig(repo)
1526 1525 blockers.update(getattr(repo, '_rebaseset', ()))
1527 1526 return blockers
1528 1527
1529 1528 def _filterobsoleterevs(repo, revs):
1530 1529 """returns a set of the obsolete revisions in revs"""
1531 1530 return set(r for r in revs if repo[r].obsolete())
1532 1531
1533 1532 def _computeobsoletenotrebased(repo, rebaseobsrevs, dest):
1534 1533 """return a mapping obsolete => successor for all obsolete nodes to be
1535 1534 rebased that have a successors in the destination
1536 1535
1537 1536 obsolete => None entries in the mapping indicate nodes with no successor"""
1538 1537 obsoletenotrebased = {}
1539 1538
1540 1539 # Build a mapping successor => obsolete nodes for the obsolete
1541 1540 # nodes to be rebased
1542 1541 allsuccessors = {}
1543 1542 cl = repo.changelog
1544 1543 for r in rebaseobsrevs:
1545 1544 node = cl.node(r)
1546 1545 for s in obsutil.allsuccessors(repo.obsstore, [node]):
1547 1546 try:
1548 1547 allsuccessors[cl.rev(s)] = cl.rev(node)
1549 1548 except LookupError:
1550 1549 pass
1551 1550
1552 1551 if allsuccessors:
1553 1552 # Look for successors of obsolete nodes to be rebased among
1554 1553 # the ancestors of dest
1555 1554 ancs = cl.ancestors([dest],
1556 1555 stoprev=min(allsuccessors),
1557 1556 inclusive=True)
1558 1557 for s in allsuccessors:
1559 1558 if s in ancs:
1560 1559 obsoletenotrebased[allsuccessors[s]] = s
1561 1560 elif (s == allsuccessors[s] and
1562 1561 allsuccessors.values().count(s) == 1):
1563 1562 # plain prune
1564 1563 obsoletenotrebased[s] = None
1565 1564
1566 1565 return obsoletenotrebased
1567 1566
1568 1567 def summaryhook(ui, repo):
1569 1568 if not repo.vfs.exists('rebasestate'):
1570 1569 return
1571 1570 try:
1572 1571 rbsrt = rebaseruntime(repo, ui, {})
1573 1572 rbsrt.restorestatus()
1574 1573 state = rbsrt.state
1575 1574 except error.RepoLookupError:
1576 1575 # i18n: column positioning for "hg summary"
1577 1576 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
1578 1577 ui.write(msg)
1579 1578 return
1580 1579 numrebased = len([i for i in state.itervalues() if i >= 0])
1581 1580 # i18n: column positioning for "hg summary"
1582 1581 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
1583 1582 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
1584 1583 ui.label(_('%d remaining'), 'rebase.remaining') %
1585 1584 (len(state) - numrebased)))
1586 1585
1587 1586 def uisetup(ui):
1588 1587 #Replace pull with a decorator to provide --rebase option
1589 1588 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
1590 1589 entry[1].append(('', 'rebase', None,
1591 1590 _("rebase working directory to branch head")))
1592 1591 entry[1].append(('t', 'tool', '',
1593 1592 _("specify merge tool for rebase")))
1594 1593 cmdutil.summaryhooks.add('rebase', summaryhook)
1595 1594 cmdutil.unfinishedstates.append(
1596 1595 ['rebasestate', False, False, _('rebase in progress'),
1597 1596 _("use 'hg rebase --continue' or 'hg rebase --abort'")])
1598 1597 cmdutil.afterresolvedstates.append(
1599 1598 ['rebasestate', _('hg rebase --continue')])
1600 1599 # ensure rebased rev are not hidden
1601 1600 extensions.wrapfunction(repoview, 'pinnedrevs', _rebasedvisible)
@@ -1,55 +1,55 b''
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [extensions]
3 3 > drawdag=$TESTDIR/drawdag.py
4 4 > bruterebase=$TESTDIR/bruterebase.py
5 5 > [experimental]
6 6 > evolution=createmarkers,allowunstable
7 7 > EOF
8 8 $ init() {
9 9 > N=`expr ${N:-0} + 1`
10 10 > cd $TESTTMP && hg init repo$N && cd repo$N
11 11 > hg debugdrawdag
12 12 > }
13 13
14 14 Source looks like "N"
15 15
16 16 $ init <<'EOS'
17 17 > C D
18 18 > |\|
19 19 > A B Z
20 20 > EOS
21 21
22 22 $ hg debugbruterebase 'all()-Z' Z
23 23 A: A':Z
24 24 B: B':Z
25 25 AB: A':Z B':Z
26 C: ABORT: cannot use revision 3 as base, result would have 3 parents
26 C: ABORT: cannot rebase 3:a35c07e8a2a4 without moving at least one of its parents
27 27 AC: A':Z C':A'B
28 28 BC: B':Z C':B'A
29 29 ABC: A':Z B':Z C':A'B'
30 30 D: D':Z
31 31 AD: A':Z D':Z
32 32 BD: B':Z D':B'
33 33 ABD: A':Z B':Z D':B'
34 CD: ABORT: cannot use revision 3 as base, result would have 3 parents
34 CD: ABORT: cannot rebase 3:a35c07e8a2a4 without moving at least one of its parents
35 35 ACD: A':Z C':A'B D':Z
36 36 BCD: B':Z C':B'A D':B'
37 37 ABCD: A':Z B':Z C':A'B' D':B'
38 38
39 39 Moving backwards
40 40
41 41 $ init <<'EOS'
42 42 > C
43 43 > |\
44 44 > A B
45 45 > |
46 46 > Z
47 47 > EOS
48 48 $ hg debugbruterebase 'all()-Z' Z
49 49 B: B':Z
50 50 A:
51 51 BA: B':Z
52 C: ABORT: cannot use revision 3 as base, result would have 3 parents
52 C: ABORT: cannot rebase 3:b8d7149b562b without moving at least one of its parents
53 53 BC: B':Z C':B'A
54 54 AC:
55 55 BAC: B':Z C':B'A
@@ -1,1168 +1,1168 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}
10 10 > [experimental]
11 11 > stabilization=createmarkers,allowunstable
12 12 > [phases]
13 13 > publish=False
14 14 > [extensions]
15 15 > rebase=
16 16 > drawdag=$TESTDIR/drawdag.py
17 17 > EOF
18 18
19 19 Setup rebase canonical repo
20 20
21 21 $ hg init base
22 22 $ cd base
23 23 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
24 24 adding changesets
25 25 adding manifests
26 26 adding file changes
27 27 added 8 changesets with 7 changes to 7 files (+2 heads)
28 28 (run 'hg heads' to see heads, 'hg merge' to merge)
29 29 $ hg up tip
30 30 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
31 31 $ hg log -G
32 32 @ 7:02de42196ebe H
33 33 |
34 34 | o 6:eea13746799a G
35 35 |/|
36 36 o | 5:24b6387c8c8c F
37 37 | |
38 38 | o 4:9520eea781bc E
39 39 |/
40 40 | o 3:32af7686d403 D
41 41 | |
42 42 | o 2:5fddd98957c8 C
43 43 | |
44 44 | o 1:42ccdea3bb16 B
45 45 |/
46 46 o 0:cd010b8cd998 A
47 47
48 48 $ cd ..
49 49
50 50 simple rebase
51 51 ---------------------------------
52 52
53 53 $ hg clone base simple
54 54 updating to branch default
55 55 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
56 56 $ cd simple
57 57 $ hg up 32af7686d403
58 58 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
59 59 $ hg rebase -d eea13746799a
60 60 rebasing 1:42ccdea3bb16 "B"
61 61 rebasing 2:5fddd98957c8 "C"
62 62 rebasing 3:32af7686d403 "D"
63 63 $ hg log -G
64 64 @ 10:8eeb3c33ad33 D
65 65 |
66 66 o 9:2327fea05063 C
67 67 |
68 68 o 8:e4e5be0395b2 B
69 69 |
70 70 | o 7:02de42196ebe H
71 71 | |
72 72 o | 6:eea13746799a G
73 73 |\|
74 74 | o 5:24b6387c8c8c F
75 75 | |
76 76 o | 4:9520eea781bc E
77 77 |/
78 78 o 0:cd010b8cd998 A
79 79
80 80 $ hg log --hidden -G
81 81 @ 10:8eeb3c33ad33 D
82 82 |
83 83 o 9:2327fea05063 C
84 84 |
85 85 o 8:e4e5be0395b2 B
86 86 |
87 87 | o 7:02de42196ebe H
88 88 | |
89 89 o | 6:eea13746799a G
90 90 |\|
91 91 | o 5:24b6387c8c8c F
92 92 | |
93 93 o | 4:9520eea781bc E
94 94 |/
95 95 | x 3:32af7686d403 D
96 96 | |
97 97 | x 2:5fddd98957c8 C
98 98 | |
99 99 | x 1:42ccdea3bb16 B
100 100 |/
101 101 o 0:cd010b8cd998 A
102 102
103 103 $ hg debugobsolete
104 104 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 e4e5be0395b2cbd471ed22a26b1b6a1a0658a794 0 (*) {'user': 'test'} (glob)
105 105 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 2327fea05063f39961b14cb69435a9898dc9a245 0 (*) {'user': 'test'} (glob)
106 106 32af7686d403cf45b5d95f2d70cebea587ac806a 8eeb3c33ad33d452c89e5dcf611c347f978fb42b 0 (*) {'user': 'test'} (glob)
107 107
108 108
109 109 $ cd ..
110 110
111 111 empty changeset
112 112 ---------------------------------
113 113
114 114 $ hg clone base empty
115 115 updating to branch default
116 116 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
117 117 $ cd empty
118 118 $ hg up eea13746799a
119 119 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
120 120
121 121 We make a copy of both the first changeset in the rebased and some other in the
122 122 set.
123 123
124 124 $ hg graft 42ccdea3bb16 32af7686d403
125 125 grafting 1:42ccdea3bb16 "B"
126 126 grafting 3:32af7686d403 "D"
127 127 $ hg rebase -s 42ccdea3bb16 -d .
128 128 rebasing 1:42ccdea3bb16 "B"
129 129 note: rebase of 1:42ccdea3bb16 created no changes to commit
130 130 rebasing 2:5fddd98957c8 "C"
131 131 rebasing 3:32af7686d403 "D"
132 132 note: rebase of 3:32af7686d403 created no changes to commit
133 133 $ hg log -G
134 134 o 10:5ae4c968c6ac C
135 135 |
136 136 @ 9:08483444fef9 D
137 137 |
138 138 o 8:8877864f1edb B
139 139 |
140 140 | o 7:02de42196ebe H
141 141 | |
142 142 o | 6:eea13746799a G
143 143 |\|
144 144 | o 5:24b6387c8c8c F
145 145 | |
146 146 o | 4:9520eea781bc E
147 147 |/
148 148 o 0:cd010b8cd998 A
149 149
150 150 $ hg log --hidden -G
151 151 o 10:5ae4c968c6ac C
152 152 |
153 153 @ 9:08483444fef9 D
154 154 |
155 155 o 8:8877864f1edb B
156 156 |
157 157 | o 7:02de42196ebe H
158 158 | |
159 159 o | 6:eea13746799a G
160 160 |\|
161 161 | o 5:24b6387c8c8c F
162 162 | |
163 163 o | 4:9520eea781bc E
164 164 |/
165 165 | x 3:32af7686d403 D
166 166 | |
167 167 | x 2:5fddd98957c8 C
168 168 | |
169 169 | x 1:42ccdea3bb16 B
170 170 |/
171 171 o 0:cd010b8cd998 A
172 172
173 173 $ hg debugobsolete
174 174 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (*) {'user': 'test'} (glob)
175 175 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (*) {'user': 'test'} (glob)
176 176 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (*) {'user': 'test'} (glob)
177 177
178 178
179 179 More complex case where part of the rebase set were already rebased
180 180
181 181 $ hg rebase --rev 'desc(D)' --dest 'desc(H)'
182 182 rebasing 9:08483444fef9 "D"
183 183 $ hg debugobsolete
184 184 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (*) {'user': 'test'} (glob)
185 185 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (*) {'user': 'test'} (glob)
186 186 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (*) {'user': 'test'} (glob)
187 187 08483444fef91d6224f6655ee586a65d263ad34c 4596109a6a4328c398bde3a4a3b6737cfade3003 0 (*) {'user': 'test'} (glob)
188 188 $ hg log -G
189 189 @ 11:4596109a6a43 D
190 190 |
191 191 | o 10:5ae4c968c6ac C
192 192 | |
193 193 | x 9:08483444fef9 D
194 194 | |
195 195 | o 8:8877864f1edb B
196 196 | |
197 197 o | 7:02de42196ebe H
198 198 | |
199 199 | o 6:eea13746799a G
200 200 |/|
201 201 o | 5:24b6387c8c8c F
202 202 | |
203 203 | o 4:9520eea781bc E
204 204 |/
205 205 o 0:cd010b8cd998 A
206 206
207 207 $ hg rebase --source 'desc(B)' --dest 'tip' --config experimental.rebaseskipobsolete=True
208 208 rebasing 8:8877864f1edb "B"
209 209 note: not rebasing 9:08483444fef9 "D", already in destination as 11:4596109a6a43 "D"
210 210 rebasing 10:5ae4c968c6ac "C"
211 211 $ hg debugobsolete
212 212 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (*) {'user': 'test'} (glob)
213 213 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (*) {'user': 'test'} (glob)
214 214 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (*) {'user': 'test'} (glob)
215 215 08483444fef91d6224f6655ee586a65d263ad34c 4596109a6a4328c398bde3a4a3b6737cfade3003 0 (*) {'user': 'test'} (glob)
216 216 8877864f1edb05d0e07dc4ba77b67a80a7b86672 462a34d07e599b87ea08676a449373fe4e2e1347 0 (*) {'user': 'test'} (glob)
217 217 5ae4c968c6aca831df823664e706c9d4aa34473d 98f6af4ee9539e14da4465128f894c274900b6e5 0 (*) {'user': 'test'} (glob)
218 218 $ hg log --rev 'contentdivergent()'
219 219 $ hg log -G
220 220 o 13:98f6af4ee953 C
221 221 |
222 222 o 12:462a34d07e59 B
223 223 |
224 224 @ 11:4596109a6a43 D
225 225 |
226 226 o 7:02de42196ebe H
227 227 |
228 228 | o 6:eea13746799a G
229 229 |/|
230 230 o | 5:24b6387c8c8c F
231 231 | |
232 232 | o 4:9520eea781bc E
233 233 |/
234 234 o 0:cd010b8cd998 A
235 235
236 236 $ hg log --style default --debug -r 4596109a6a4328c398bde3a4a3b6737cfade3003
237 237 changeset: 11:4596109a6a4328c398bde3a4a3b6737cfade3003
238 238 phase: draft
239 239 parent: 7:02de42196ebee42ef284b6780a87cdc96e8eaab6
240 240 parent: -1:0000000000000000000000000000000000000000
241 241 manifest: 11:a91006e3a02f1edf631f7018e6e5684cf27dd905
242 242 user: Nicolas Dumazet <nicdumz.commits@gmail.com>
243 243 date: Sat Apr 30 15:24:48 2011 +0200
244 244 files+: D
245 245 extra: branch=default
246 246 extra: rebase_source=08483444fef91d6224f6655ee586a65d263ad34c
247 247 extra: source=32af7686d403cf45b5d95f2d70cebea587ac806a
248 248 description:
249 249 D
250 250
251 251
252 252 $ hg up -qr 'desc(G)'
253 253 $ hg graft 4596109a6a4328c398bde3a4a3b6737cfade3003
254 254 grafting 11:4596109a6a43 "D"
255 255 $ hg up -qr 'desc(E)'
256 256 $ hg rebase -s tip -d .
257 257 rebasing 14:9e36056a46e3 "D" (tip)
258 258 $ hg log --style default --debug -r tip
259 259 changeset: 15:627d4614809036ba22b9e7cb31638ddc06ab99ab
260 260 tag: tip
261 261 phase: draft
262 262 parent: 4:9520eea781bcca16c1e15acc0ba14335a0e8e5ba
263 263 parent: -1:0000000000000000000000000000000000000000
264 264 manifest: 15:648e8ede73ae3e497d093d3a4c8fcc2daa864f42
265 265 user: Nicolas Dumazet <nicdumz.commits@gmail.com>
266 266 date: Sat Apr 30 15:24:48 2011 +0200
267 267 files+: D
268 268 extra: branch=default
269 269 extra: intermediate-source=4596109a6a4328c398bde3a4a3b6737cfade3003
270 270 extra: rebase_source=9e36056a46e37c9776168c7375734eebc70e294f
271 271 extra: source=32af7686d403cf45b5d95f2d70cebea587ac806a
272 272 description:
273 273 D
274 274
275 275
276 276 Start rebase from a commit that is obsolete but not hidden only because it's
277 277 a working copy parent. We should be moved back to the starting commit as usual
278 278 even though it is hidden (until we're moved there).
279 279
280 280 $ hg --hidden up -qr 'first(hidden())'
281 281 $ hg rebase --rev 13 --dest 15
282 282 rebasing 13:98f6af4ee953 "C"
283 283 $ hg log -G
284 284 o 16:294a2b93eb4d C
285 285 |
286 286 o 15:627d46148090 D
287 287 |
288 288 | o 12:462a34d07e59 B
289 289 | |
290 290 | o 11:4596109a6a43 D
291 291 | |
292 292 | o 7:02de42196ebe H
293 293 | |
294 294 +---o 6:eea13746799a G
295 295 | |/
296 296 | o 5:24b6387c8c8c F
297 297 | |
298 298 o | 4:9520eea781bc E
299 299 |/
300 300 | @ 1:42ccdea3bb16 B
301 301 |/
302 302 o 0:cd010b8cd998 A
303 303
304 304
305 305 $ cd ..
306 306
307 307 collapse rebase
308 308 ---------------------------------
309 309
310 310 $ hg clone base collapse
311 311 updating to branch default
312 312 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
313 313 $ cd collapse
314 314 $ hg rebase -s 42ccdea3bb16 -d eea13746799a --collapse
315 315 rebasing 1:42ccdea3bb16 "B"
316 316 rebasing 2:5fddd98957c8 "C"
317 317 rebasing 3:32af7686d403 "D"
318 318 $ hg log -G
319 319 o 8:4dc2197e807b Collapsed revision
320 320 |
321 321 | @ 7:02de42196ebe H
322 322 | |
323 323 o | 6:eea13746799a G
324 324 |\|
325 325 | o 5:24b6387c8c8c F
326 326 | |
327 327 o | 4:9520eea781bc E
328 328 |/
329 329 o 0:cd010b8cd998 A
330 330
331 331 $ hg log --hidden -G
332 332 o 8:4dc2197e807b Collapsed revision
333 333 |
334 334 | @ 7:02de42196ebe H
335 335 | |
336 336 o | 6:eea13746799a G
337 337 |\|
338 338 | o 5:24b6387c8c8c F
339 339 | |
340 340 o | 4:9520eea781bc E
341 341 |/
342 342 | x 3:32af7686d403 D
343 343 | |
344 344 | x 2:5fddd98957c8 C
345 345 | |
346 346 | x 1:42ccdea3bb16 B
347 347 |/
348 348 o 0:cd010b8cd998 A
349 349
350 350 $ hg id --debug -r tip
351 351 4dc2197e807bae9817f09905b50ab288be2dbbcf tip
352 352 $ hg debugobsolete
353 353 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (*) {'user': 'test'} (glob)
354 354 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (*) {'user': 'test'} (glob)
355 355 32af7686d403cf45b5d95f2d70cebea587ac806a 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (*) {'user': 'test'} (glob)
356 356
357 357 $ cd ..
358 358
359 359 Rebase set has hidden descendants
360 360 ---------------------------------
361 361
362 362 We rebase a changeset which has a hidden changeset. The hidden changeset must
363 363 not be rebased.
364 364
365 365 $ hg clone base hidden
366 366 updating to branch default
367 367 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
368 368 $ cd hidden
369 369 $ hg rebase -s 5fddd98957c8 -d eea13746799a
370 370 rebasing 2:5fddd98957c8 "C"
371 371 rebasing 3:32af7686d403 "D"
372 372 $ hg rebase -s 42ccdea3bb16 -d 02de42196ebe
373 373 rebasing 1:42ccdea3bb16 "B"
374 374 $ hg log -G
375 375 o 10:7c6027df6a99 B
376 376 |
377 377 | o 9:cf44d2f5a9f4 D
378 378 | |
379 379 | o 8:e273c5e7d2d2 C
380 380 | |
381 381 @ | 7:02de42196ebe H
382 382 | |
383 383 | o 6:eea13746799a G
384 384 |/|
385 385 o | 5:24b6387c8c8c F
386 386 | |
387 387 | o 4:9520eea781bc E
388 388 |/
389 389 o 0:cd010b8cd998 A
390 390
391 391 $ hg log --hidden -G
392 392 o 10:7c6027df6a99 B
393 393 |
394 394 | o 9:cf44d2f5a9f4 D
395 395 | |
396 396 | o 8:e273c5e7d2d2 C
397 397 | |
398 398 @ | 7:02de42196ebe H
399 399 | |
400 400 | o 6:eea13746799a G
401 401 |/|
402 402 o | 5:24b6387c8c8c F
403 403 | |
404 404 | o 4:9520eea781bc E
405 405 |/
406 406 | x 3:32af7686d403 D
407 407 | |
408 408 | x 2:5fddd98957c8 C
409 409 | |
410 410 | x 1:42ccdea3bb16 B
411 411 |/
412 412 o 0:cd010b8cd998 A
413 413
414 414 $ hg debugobsolete
415 415 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b e273c5e7d2d29df783dce9f9eaa3ac4adc69c15d 0 (*) {'user': 'test'} (glob)
416 416 32af7686d403cf45b5d95f2d70cebea587ac806a cf44d2f5a9f4297a62be94cbdd3dff7c7dc54258 0 (*) {'user': 'test'} (glob)
417 417 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 7c6027df6a99d93f461868e5433f63bde20b6dfb 0 (*) {'user': 'test'} (glob)
418 418
419 419 Test that rewriting leaving instability behind is allowed
420 420 ---------------------------------------------------------------------
421 421
422 422 $ hg log -r 'children(8)'
423 423 9:cf44d2f5a9f4 D (no-eol)
424 424 $ hg rebase -r 8
425 425 rebasing 8:e273c5e7d2d2 "C"
426 426 $ hg log -G
427 427 o 11:0d8f238b634c C
428 428 |
429 429 o 10:7c6027df6a99 B
430 430 |
431 431 | o 9:cf44d2f5a9f4 D
432 432 | |
433 433 | x 8:e273c5e7d2d2 C
434 434 | |
435 435 @ | 7:02de42196ebe H
436 436 | |
437 437 | o 6:eea13746799a G
438 438 |/|
439 439 o | 5:24b6387c8c8c F
440 440 | |
441 441 | o 4:9520eea781bc E
442 442 |/
443 443 o 0:cd010b8cd998 A
444 444
445 445
446 446
447 447 Test multiple root handling
448 448 ------------------------------------
449 449
450 450 $ hg rebase --dest 4 --rev '7+11+9'
451 451 rebasing 9:cf44d2f5a9f4 "D"
452 452 rebasing 7:02de42196ebe "H"
453 453 not rebasing ignored 10:7c6027df6a99 "B"
454 454 rebasing 11:0d8f238b634c "C" (tip)
455 455 $ hg log -G
456 456 o 14:1e8370e38cca C
457 457 |
458 458 @ 13:bfe264faf697 H
459 459 |
460 460 | o 12:102b4c1d889b D
461 461 |/
462 462 | o 10:7c6027df6a99 B
463 463 | |
464 464 | x 7:02de42196ebe H
465 465 | |
466 466 +---o 6:eea13746799a G
467 467 | |/
468 468 | o 5:24b6387c8c8c F
469 469 | |
470 470 o | 4:9520eea781bc E
471 471 |/
472 472 o 0:cd010b8cd998 A
473 473
474 474 $ cd ..
475 475
476 476 Detach both parents
477 477
478 478 $ hg init double-detach
479 479 $ cd double-detach
480 480
481 481 $ hg debugdrawdag <<EOF
482 482 > F
483 483 > /|
484 484 > C E
485 485 > | |
486 486 > B D G
487 487 > \|/
488 488 > A
489 489 > EOF
490 490
491 491 $ hg rebase -d G -r 'B + D + F'
492 492 rebasing 1:112478962961 "B" (B)
493 493 rebasing 2:b18e25de2cf5 "D" (D)
494 494 not rebasing ignored 4:26805aba1e60 "C" (C)
495 495 not rebasing ignored 5:4b61ff5c62e2 "E" (E)
496 496 rebasing 6:f15c3adaf214 "F" (F tip)
497 abort: cannot use revision 6 as base, result would have 3 parents
497 abort: cannot rebase 6:f15c3adaf214 without moving at least one of its parents
498 498 [255]
499 499
500 500 $ cd ..
501 501
502 502 test on rebase dropping a merge
503 503
504 504 (setup)
505 505
506 506 $ hg init dropmerge
507 507 $ cd dropmerge
508 508 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
509 509 adding changesets
510 510 adding manifests
511 511 adding file changes
512 512 added 8 changesets with 7 changes to 7 files (+2 heads)
513 513 (run 'hg heads' to see heads, 'hg merge' to merge)
514 514 $ hg up 3
515 515 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
516 516 $ hg merge 7
517 517 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
518 518 (branch merge, don't forget to commit)
519 519 $ hg ci -m 'M'
520 520 $ echo I > I
521 521 $ hg add I
522 522 $ hg ci -m I
523 523 $ hg log -G
524 524 @ 9:4bde274eefcf I
525 525 |
526 526 o 8:53a6a128b2b7 M
527 527 |\
528 528 | o 7:02de42196ebe H
529 529 | |
530 530 | | o 6:eea13746799a G
531 531 | |/|
532 532 | o | 5:24b6387c8c8c F
533 533 | | |
534 534 | | o 4:9520eea781bc E
535 535 | |/
536 536 o | 3:32af7686d403 D
537 537 | |
538 538 o | 2:5fddd98957c8 C
539 539 | |
540 540 o | 1:42ccdea3bb16 B
541 541 |/
542 542 o 0:cd010b8cd998 A
543 543
544 544 (actual test)
545 545
546 546 $ hg rebase --dest 6 --rev '((desc(H) + desc(D))::) - desc(M)'
547 547 rebasing 3:32af7686d403 "D"
548 548 rebasing 7:02de42196ebe "H"
549 549 not rebasing ignored 8:53a6a128b2b7 "M"
550 550 rebasing 9:4bde274eefcf "I" (tip)
551 551 $ hg log -G
552 552 @ 12:acd174b7ab39 I
553 553 |
554 554 o 11:6c11a6218c97 H
555 555 |
556 556 | o 10:b5313c85b22e D
557 557 |/
558 558 | o 8:53a6a128b2b7 M
559 559 | |\
560 560 | | x 7:02de42196ebe H
561 561 | | |
562 562 o---+ 6:eea13746799a G
563 563 | | |
564 564 | | o 5:24b6387c8c8c F
565 565 | | |
566 566 o---+ 4:9520eea781bc E
567 567 / /
568 568 x | 3:32af7686d403 D
569 569 | |
570 570 o | 2:5fddd98957c8 C
571 571 | |
572 572 o | 1:42ccdea3bb16 B
573 573 |/
574 574 o 0:cd010b8cd998 A
575 575
576 576
577 577 Test hidden changesets in the rebase set (issue4504)
578 578
579 579 $ hg up --hidden 9
580 580 3 files updated, 0 files merged, 1 files removed, 0 files unresolved
581 581 $ echo J > J
582 582 $ hg add J
583 583 $ hg commit -m J
584 584 $ hg debugobsolete `hg log --rev . -T '{node}'`
585 585 obsoleted 1 changesets
586 586
587 587 $ hg rebase --rev .~1::. --dest 'max(desc(D))' --traceback --config experimental.rebaseskipobsolete=off
588 588 rebasing 9:4bde274eefcf "I"
589 589 rebasing 13:06edfc82198f "J" (tip)
590 590 $ hg log -G
591 591 @ 15:5ae8a643467b J
592 592 |
593 593 o 14:9ad579b4a5de I
594 594 |
595 595 | o 12:acd174b7ab39 I
596 596 | |
597 597 | o 11:6c11a6218c97 H
598 598 | |
599 599 o | 10:b5313c85b22e D
600 600 |/
601 601 | o 8:53a6a128b2b7 M
602 602 | |\
603 603 | | x 7:02de42196ebe H
604 604 | | |
605 605 o---+ 6:eea13746799a G
606 606 | | |
607 607 | | o 5:24b6387c8c8c F
608 608 | | |
609 609 o---+ 4:9520eea781bc E
610 610 / /
611 611 x | 3:32af7686d403 D
612 612 | |
613 613 o | 2:5fddd98957c8 C
614 614 | |
615 615 o | 1:42ccdea3bb16 B
616 616 |/
617 617 o 0:cd010b8cd998 A
618 618
619 619 $ hg up 14 -C
620 620 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
621 621 $ echo "K" > K
622 622 $ hg add K
623 623 $ hg commit --amend -m "K"
624 624 $ echo "L" > L
625 625 $ hg add L
626 626 $ hg commit -m "L"
627 627 $ hg up '.^'
628 628 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
629 629 $ echo "M" > M
630 630 $ hg add M
631 631 $ hg commit --amend -m "M"
632 632 $ hg log -G
633 633 @ 20:bfaedf8eb73b M
634 634 |
635 635 | o 18:97219452e4bd L
636 636 | |
637 637 | x 17:fc37a630c901 K
638 638 |/
639 639 | o 15:5ae8a643467b J
640 640 | |
641 641 | x 14:9ad579b4a5de I
642 642 |/
643 643 | o 12:acd174b7ab39 I
644 644 | |
645 645 | o 11:6c11a6218c97 H
646 646 | |
647 647 o | 10:b5313c85b22e D
648 648 |/
649 649 | o 8:53a6a128b2b7 M
650 650 | |\
651 651 | | x 7:02de42196ebe H
652 652 | | |
653 653 o---+ 6:eea13746799a G
654 654 | | |
655 655 | | o 5:24b6387c8c8c F
656 656 | | |
657 657 o---+ 4:9520eea781bc E
658 658 / /
659 659 x | 3:32af7686d403 D
660 660 | |
661 661 o | 2:5fddd98957c8 C
662 662 | |
663 663 o | 1:42ccdea3bb16 B
664 664 |/
665 665 o 0:cd010b8cd998 A
666 666
667 667 $ hg rebase -s 14 -d 18 --config experimental.rebaseskipobsolete=True
668 668 note: not rebasing 14:9ad579b4a5de "I", already in destination as 17:fc37a630c901 "K"
669 669 rebasing 15:5ae8a643467b "J"
670 670
671 671 $ cd ..
672 672
673 673 Skip obsolete changeset even with multiple hops
674 674 -----------------------------------------------
675 675
676 676 setup
677 677
678 678 $ hg init obsskip
679 679 $ cd obsskip
680 680 $ cat << EOF >> .hg/hgrc
681 681 > [experimental]
682 682 > rebaseskipobsolete = True
683 683 > [extensions]
684 684 > strip =
685 685 > EOF
686 686 $ echo A > A
687 687 $ hg add A
688 688 $ hg commit -m A
689 689 $ echo B > B
690 690 $ hg add B
691 691 $ hg commit -m B0
692 692 $ hg commit --amend -m B1
693 693 $ hg commit --amend -m B2
694 694 $ hg up --hidden 'desc(B0)'
695 695 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
696 696 $ echo C > C
697 697 $ hg add C
698 698 $ hg commit -m C
699 699
700 700 Rebase finds its way in a chain of marker
701 701
702 702 $ hg rebase -d 'desc(B2)'
703 703 note: not rebasing 1:a8b11f55fb19 "B0", already in destination as 3:261e70097290 "B2"
704 704 rebasing 4:212cb178bcbb "C" (tip)
705 705
706 706 Even when the chain include missing node
707 707
708 708 $ hg up --hidden 'desc(B0)'
709 709 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
710 710 $ echo D > D
711 711 $ hg add D
712 712 $ hg commit -m D
713 713 $ hg --hidden strip -r 'desc(B1)'
714 714 saved backup bundle to $TESTTMP/obsskip/.hg/strip-backup/86f6414ccda7-b1c452ee-backup.hg (glob)
715 715
716 716 $ hg rebase -d 'desc(B2)'
717 717 note: not rebasing 1:a8b11f55fb19 "B0", already in destination as 2:261e70097290 "B2"
718 718 rebasing 5:1a79b7535141 "D" (tip)
719 719 $ hg up 4
720 720 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
721 721 $ echo "O" > O
722 722 $ hg add O
723 723 $ hg commit -m O
724 724 $ echo "P" > P
725 725 $ hg add P
726 726 $ hg commit -m P
727 727 $ hg log -G
728 728 @ 8:8d47583e023f P
729 729 |
730 730 o 7:360bbaa7d3ce O
731 731 |
732 732 | o 6:9c48361117de D
733 733 | |
734 734 o | 4:ff2c4d47b71d C
735 735 |/
736 736 o 2:261e70097290 B2
737 737 |
738 738 o 0:4a2df7238c3b A
739 739
740 740 $ hg debugobsolete `hg log -r 7 -T '{node}\n'` --config experimental.stabilization=all
741 741 obsoleted 1 changesets
742 742 $ hg rebase -d 6 -r "4::"
743 743 rebasing 4:ff2c4d47b71d "C"
744 744 note: not rebasing 7:360bbaa7d3ce "O", it has no successor
745 745 rebasing 8:8d47583e023f "P" (tip)
746 746
747 747 If all the changeset to be rebased are obsolete and present in the destination, we
748 748 should display a friendly error message
749 749
750 750 $ hg log -G
751 751 @ 10:121d9e3bc4c6 P
752 752 |
753 753 o 9:4be60e099a77 C
754 754 |
755 755 o 6:9c48361117de D
756 756 |
757 757 o 2:261e70097290 B2
758 758 |
759 759 o 0:4a2df7238c3b A
760 760
761 761
762 762 $ hg up 9
763 763 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
764 764 $ echo "non-relevant change" > nonrelevant
765 765 $ hg add nonrelevant
766 766 $ hg commit -m nonrelevant
767 767 created new head
768 768 $ hg debugobsolete `hg log -r 11 -T '{node}\n'` --config experimental.stabilization=all
769 769 obsoleted 1 changesets
770 770 $ hg rebase -r . -d 10
771 771 note: not rebasing 11:f44da1f4954c "nonrelevant" (tip), it has no successor
772 772
773 773 If a rebase is going to create divergence, it should abort
774 774
775 775 $ hg log -G
776 776 @ 11:f44da1f4954c nonrelevant
777 777 |
778 778 | o 10:121d9e3bc4c6 P
779 779 |/
780 780 o 9:4be60e099a77 C
781 781 |
782 782 o 6:9c48361117de D
783 783 |
784 784 o 2:261e70097290 B2
785 785 |
786 786 o 0:4a2df7238c3b A
787 787
788 788
789 789 $ hg up 9
790 790 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
791 791 $ echo "john" > doe
792 792 $ hg add doe
793 793 $ hg commit -m "john doe"
794 794 created new head
795 795 $ hg up 10
796 796 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
797 797 $ echo "foo" > bar
798 798 $ hg add bar
799 799 $ hg commit --amend -m "10'"
800 800 $ hg up 10 --hidden
801 801 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
802 802 $ echo "bar" > foo
803 803 $ hg add foo
804 804 $ hg commit -m "bar foo"
805 805 $ hg log -G
806 806 @ 15:73568ab6879d bar foo
807 807 |
808 808 | o 14:77d874d096a2 10'
809 809 | |
810 810 | | o 12:3eb461388009 john doe
811 811 | |/
812 812 x | 10:121d9e3bc4c6 P
813 813 |/
814 814 o 9:4be60e099a77 C
815 815 |
816 816 o 6:9c48361117de D
817 817 |
818 818 o 2:261e70097290 B2
819 819 |
820 820 o 0:4a2df7238c3b A
821 821
822 822 $ hg summary
823 823 parent: 15:73568ab6879d tip (orphan)
824 824 bar foo
825 825 branch: default
826 826 commit: (clean)
827 827 update: 2 new changesets, 3 branch heads (merge)
828 828 phases: 8 draft
829 829 orphan: 1 changesets
830 830 $ hg rebase -s 10 -d 12
831 831 abort: this rebase will cause divergences from: 121d9e3bc4c6
832 832 (to force the rebase please set experimental.allowdivergence=True)
833 833 [255]
834 834 $ hg log -G
835 835 @ 15:73568ab6879d bar foo
836 836 |
837 837 | o 14:77d874d096a2 10'
838 838 | |
839 839 | | o 12:3eb461388009 john doe
840 840 | |/
841 841 x | 10:121d9e3bc4c6 P
842 842 |/
843 843 o 9:4be60e099a77 C
844 844 |
845 845 o 6:9c48361117de D
846 846 |
847 847 o 2:261e70097290 B2
848 848 |
849 849 o 0:4a2df7238c3b A
850 850
851 851 With experimental.allowdivergence=True, rebase can create divergence
852 852
853 853 $ hg rebase -s 10 -d 12 --config experimental.allowdivergence=True
854 854 rebasing 10:121d9e3bc4c6 "P"
855 855 rebasing 15:73568ab6879d "bar foo" (tip)
856 856 $ hg summary
857 857 parent: 17:61bd55f69bc4 tip
858 858 bar foo
859 859 branch: default
860 860 commit: (clean)
861 861 update: 1 new changesets, 2 branch heads (merge)
862 862 phases: 8 draft
863 863 content-divergent: 2 changesets
864 864
865 865 rebase --continue + skipped rev because their successors are in destination
866 866 we make a change in trunk and work on conflicting changes to make rebase abort.
867 867
868 868 $ hg log -G -r 17::
869 869 @ 17:61bd55f69bc4 bar foo
870 870 |
871 871 ~
872 872
873 873 Create the two changes in trunk
874 874 $ printf "a" > willconflict
875 875 $ hg add willconflict
876 876 $ hg commit -m "willconflict first version"
877 877
878 878 $ printf "dummy" > C
879 879 $ hg commit -m "dummy change successor"
880 880
881 881 Create the changes that we will rebase
882 882 $ hg update -C 17 -q
883 883 $ printf "b" > willconflict
884 884 $ hg add willconflict
885 885 $ hg commit -m "willconflict second version"
886 886 created new head
887 887 $ printf "dummy" > K
888 888 $ hg add K
889 889 $ hg commit -m "dummy change"
890 890 $ printf "dummy" > L
891 891 $ hg add L
892 892 $ hg commit -m "dummy change"
893 893 $ hg debugobsolete `hg log -r ".^" -T '{node}'` `hg log -r 19 -T '{node}'` --config experimental.stabilization=all
894 894 obsoleted 1 changesets
895 895
896 896 $ hg log -G -r 17::
897 897 @ 22:7bdc8a87673d dummy change
898 898 |
899 899 x 21:8b31da3c4919 dummy change
900 900 |
901 901 o 20:b82fb57ea638 willconflict second version
902 902 |
903 903 | o 19:601db7a18f51 dummy change successor
904 904 | |
905 905 | o 18:357ddf1602d5 willconflict first version
906 906 |/
907 907 o 17:61bd55f69bc4 bar foo
908 908 |
909 909 ~
910 910 $ hg rebase -r ".^^ + .^ + ." -d 19
911 911 rebasing 20:b82fb57ea638 "willconflict second version"
912 912 merging willconflict
913 913 warning: conflicts while merging willconflict! (edit, then use 'hg resolve --mark')
914 914 unresolved conflicts (see hg resolve, then hg rebase --continue)
915 915 [1]
916 916
917 917 $ hg resolve --mark willconflict
918 918 (no more unresolved files)
919 919 continue: hg rebase --continue
920 920 $ hg rebase --continue
921 921 rebasing 20:b82fb57ea638 "willconflict second version"
922 922 note: not rebasing 21:8b31da3c4919 "dummy change", already in destination as 19:601db7a18f51 "dummy change successor"
923 923 rebasing 22:7bdc8a87673d "dummy change" (tip)
924 924 $ cd ..
925 925
926 926 Rebase merge where successor of one parent is equal to destination (issue5198)
927 927
928 928 $ hg init p1-succ-is-dest
929 929 $ cd p1-succ-is-dest
930 930
931 931 $ hg debugdrawdag <<EOF
932 932 > F
933 933 > /|
934 934 > E D B # replace: D -> B
935 935 > \|/
936 936 > A
937 937 > EOF
938 938
939 939 $ hg rebase -d B -s D
940 940 note: not rebasing 2:b18e25de2cf5 "D" (D), already in destination as 1:112478962961 "B"
941 941 rebasing 4:66f1a38021c9 "F" (F tip)
942 942 $ hg log -G
943 943 o 5:50e9d60b99c6 F
944 944 |\
945 945 | | x 4:66f1a38021c9 F
946 946 | |/|
947 947 | o | 3:7fb047a69f22 E
948 948 | | |
949 949 | | x 2:b18e25de2cf5 D
950 950 | |/
951 951 o | 1:112478962961 B
952 952 |/
953 953 o 0:426bada5c675 A
954 954
955 955 $ cd ..
956 956
957 957 Rebase merge where successor of other parent is equal to destination
958 958
959 959 $ hg init p2-succ-is-dest
960 960 $ cd p2-succ-is-dest
961 961
962 962 $ hg debugdrawdag <<EOF
963 963 > F
964 964 > /|
965 965 > E D B # replace: E -> B
966 966 > \|/
967 967 > A
968 968 > EOF
969 969
970 970 $ hg rebase -d B -s E
971 971 note: not rebasing 3:7fb047a69f22 "E" (E), already in destination as 1:112478962961 "B"
972 972 rebasing 4:66f1a38021c9 "F" (F tip)
973 973 $ hg log -G
974 974 o 5:aae1787dacee F
975 975 |\
976 976 | | x 4:66f1a38021c9 F
977 977 | |/|
978 978 | | x 3:7fb047a69f22 E
979 979 | | |
980 980 | o | 2:b18e25de2cf5 D
981 981 | |/
982 982 o / 1:112478962961 B
983 983 |/
984 984 o 0:426bada5c675 A
985 985
986 986 $ cd ..
987 987
988 988 Rebase merge where successor of one parent is ancestor of destination
989 989
990 990 $ hg init p1-succ-in-dest
991 991 $ cd p1-succ-in-dest
992 992
993 993 $ hg debugdrawdag <<EOF
994 994 > F C
995 995 > /| |
996 996 > E D B # replace: D -> B
997 997 > \|/
998 998 > A
999 999 > EOF
1000 1000
1001 1001 $ hg rebase -d C -s D
1002 1002 note: not rebasing 2:b18e25de2cf5 "D" (D), already in destination as 1:112478962961 "B"
1003 1003 rebasing 5:66f1a38021c9 "F" (F tip)
1004 1004
1005 1005 $ hg log -G
1006 1006 o 6:0913febf6439 F
1007 1007 |\
1008 1008 +---x 5:66f1a38021c9 F
1009 1009 | | |
1010 1010 | o | 4:26805aba1e60 C
1011 1011 | | |
1012 1012 o | | 3:7fb047a69f22 E
1013 1013 | | |
1014 1014 +---x 2:b18e25de2cf5 D
1015 1015 | |
1016 1016 | o 1:112478962961 B
1017 1017 |/
1018 1018 o 0:426bada5c675 A
1019 1019
1020 1020 $ cd ..
1021 1021
1022 1022 Rebase merge where successor of other parent is ancestor of destination
1023 1023
1024 1024 $ hg init p2-succ-in-dest
1025 1025 $ cd p2-succ-in-dest
1026 1026
1027 1027 $ hg debugdrawdag <<EOF
1028 1028 > F C
1029 1029 > /| |
1030 1030 > E D B # replace: E -> B
1031 1031 > \|/
1032 1032 > A
1033 1033 > EOF
1034 1034
1035 1035 $ hg rebase -d C -s E
1036 1036 note: not rebasing 3:7fb047a69f22 "E" (E), already in destination as 1:112478962961 "B"
1037 1037 rebasing 5:66f1a38021c9 "F" (F tip)
1038 1038 $ hg log -G
1039 1039 o 6:c6ab0cc6d220 F
1040 1040 |\
1041 1041 +---x 5:66f1a38021c9 F
1042 1042 | | |
1043 1043 | o | 4:26805aba1e60 C
1044 1044 | | |
1045 1045 | | x 3:7fb047a69f22 E
1046 1046 | | |
1047 1047 o---+ 2:b18e25de2cf5 D
1048 1048 / /
1049 1049 o / 1:112478962961 B
1050 1050 |/
1051 1051 o 0:426bada5c675 A
1052 1052
1053 1053 $ cd ..
1054 1054
1055 1055 Rebase merge where successor of one parent is ancestor of destination
1056 1056
1057 1057 $ hg init p1-succ-in-dest-b
1058 1058 $ cd p1-succ-in-dest-b
1059 1059
1060 1060 $ hg debugdrawdag <<EOF
1061 1061 > F C
1062 1062 > /| |
1063 1063 > E D B # replace: E -> B
1064 1064 > \|/
1065 1065 > A
1066 1066 > EOF
1067 1067
1068 1068 $ hg rebase -d C -b F
1069 1069 rebasing 2:b18e25de2cf5 "D" (D)
1070 1070 note: not rebasing 3:7fb047a69f22 "E" (E), already in destination as 1:112478962961 "B"
1071 1071 rebasing 5:66f1a38021c9 "F" (F tip)
1072 1072 warning: rebasing 5:66f1a38021c9 may include unwanted changes from 3:7fb047a69f22
1073 1073 $ hg log -G
1074 1074 o 7:9ed45af61fa0 F
1075 1075 |
1076 1076 o 6:8f47515dda15 D
1077 1077 |
1078 1078 | x 5:66f1a38021c9 F
1079 1079 | |\
1080 1080 o | | 4:26805aba1e60 C
1081 1081 | | |
1082 1082 | | x 3:7fb047a69f22 E
1083 1083 | | |
1084 1084 | x | 2:b18e25de2cf5 D
1085 1085 | |/
1086 1086 o / 1:112478962961 B
1087 1087 |/
1088 1088 o 0:426bada5c675 A
1089 1089
1090 1090 $ cd ..
1091 1091
1092 1092 Rebase merge where successor of other parent is ancestor of destination
1093 1093
1094 1094 $ hg init p2-succ-in-dest-b
1095 1095 $ cd p2-succ-in-dest-b
1096 1096
1097 1097 $ hg debugdrawdag <<EOF
1098 1098 > F C
1099 1099 > /| |
1100 1100 > E D B # replace: D -> B
1101 1101 > \|/
1102 1102 > A
1103 1103 > EOF
1104 1104
1105 1105 $ hg rebase -d C -b F
1106 1106 note: not rebasing 2:b18e25de2cf5 "D" (D), already in destination as 1:112478962961 "B"
1107 1107 rebasing 3:7fb047a69f22 "E" (E)
1108 1108 rebasing 5:66f1a38021c9 "F" (F tip)
1109 1109 warning: rebasing 5:66f1a38021c9 may include unwanted changes from 2:b18e25de2cf5
1110 1110
1111 1111 $ hg log -G
1112 1112 o 7:502540f44880 F
1113 1113 |
1114 1114 o 6:533690786a86 E
1115 1115 |
1116 1116 | x 5:66f1a38021c9 F
1117 1117 | |\
1118 1118 o | | 4:26805aba1e60 C
1119 1119 | | |
1120 1120 | | x 3:7fb047a69f22 E
1121 1121 | | |
1122 1122 | x | 2:b18e25de2cf5 D
1123 1123 | |/
1124 1124 o / 1:112478962961 B
1125 1125 |/
1126 1126 o 0:426bada5c675 A
1127 1127
1128 1128 $ cd ..
1129 1129
1130 1130 Test that bookmark is moved and working dir is updated when all changesets have
1131 1131 equivalents in destination
1132 1132 $ hg init rbsrepo && cd rbsrepo
1133 1133 $ echo "[experimental]" > .hg/hgrc
1134 1134 $ echo "stabilization=all" >> .hg/hgrc
1135 1135 $ echo "rebaseskipobsolete=on" >> .hg/hgrc
1136 1136 $ echo root > root && hg ci -Am root
1137 1137 adding root
1138 1138 $ echo a > a && hg ci -Am a
1139 1139 adding a
1140 1140 $ hg up 0
1141 1141 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1142 1142 $ echo b > b && hg ci -Am b
1143 1143 adding b
1144 1144 created new head
1145 1145 $ hg rebase -r 2 -d 1
1146 1146 rebasing 2:1e9a3c00cbe9 "b" (tip)
1147 1147 $ hg log -r . # working dir is at rev 3 (successor of 2)
1148 1148 3:be1832deae9a b (no-eol)
1149 1149 $ hg book -r 2 mybook --hidden # rev 2 has a bookmark on it now
1150 1150 $ hg up 2 && hg log -r . # working dir is at rev 2 again
1151 1151 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1152 1152 2:1e9a3c00cbe9 b (no-eol)
1153 1153 $ hg rebase -r 2 -d 3 --config experimental.stabilization.track-operation=1
1154 1154 note: not rebasing 2:1e9a3c00cbe9 "b" (mybook), already in destination as 3:be1832deae9a "b"
1155 1155 Check that working directory was updated to rev 3 although rev 2 was skipped
1156 1156 during the rebase operation
1157 1157 $ hg log -r .
1158 1158 3:be1832deae9a b (no-eol)
1159 1159
1160 1160 Check that bookmark was not moved to rev 3 if rev 2 was skipped during the
1161 1161 rebase operation. This makes sense because if rev 2 has a successor, the
1162 1162 operation generating that successor (ex. rebase) should be responsible for
1163 1163 moving bookmarks. If the bookmark is on a precursor, like rev 2, that means the
1164 1164 user manually moved it back. In that case we should not move it again.
1165 1165 $ hg bookmarks
1166 1166 mybook 2:1e9a3c00cbe9
1167 1167 $ hg debugobsolete --rev tip
1168 1168 1e9a3c00cbe90d236ac05ef61efcc5e40b7412bc be1832deae9ac531caa7438b8dcf6055a122cd8e 0 (*) {'user': 'test'} (glob)
@@ -1,949 +1,949 b''
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [extensions]
3 3 > rebase=
4 4 > drawdag=$TESTDIR/drawdag.py
5 5 >
6 6 > [phases]
7 7 > publish=False
8 8 >
9 9 > [alias]
10 10 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
11 11 > EOF
12 12
13 13
14 14 $ hg init a
15 15 $ cd a
16 16 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
17 17 adding changesets
18 18 adding manifests
19 19 adding file changes
20 20 added 8 changesets with 7 changes to 7 files (+2 heads)
21 21 (run 'hg heads' to see heads, 'hg merge' to merge)
22 22 $ hg up tip
23 23 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
24 24 $ cd ..
25 25
26 26
27 27 Rebasing
28 28 D onto H - simple rebase:
29 29 (this also tests that editor is invoked if '--edit' is specified, and that we
30 30 can abort or warn for colliding untracked files)
31 31
32 32 $ hg clone -q -u . a a1
33 33 $ cd a1
34 34
35 35 $ hg tglog
36 36 @ 7: 'H'
37 37 |
38 38 | o 6: 'G'
39 39 |/|
40 40 o | 5: 'F'
41 41 | |
42 42 | o 4: 'E'
43 43 |/
44 44 | o 3: 'D'
45 45 | |
46 46 | o 2: 'C'
47 47 | |
48 48 | o 1: 'B'
49 49 |/
50 50 o 0: 'A'
51 51
52 52
53 53 $ hg status --rev "3^1" --rev 3
54 54 A D
55 55 $ echo collide > D
56 56 $ HGEDITOR=cat hg rebase -s 3 -d 7 --edit --config merge.checkunknown=warn
57 57 rebasing 3:32af7686d403 "D"
58 58 D: replacing untracked file
59 59 D
60 60
61 61
62 62 HG: Enter commit message. Lines beginning with 'HG:' are removed.
63 63 HG: Leave message empty to abort commit.
64 64 HG: --
65 65 HG: user: Nicolas Dumazet <nicdumz.commits@gmail.com>
66 66 HG: branch 'default'
67 67 HG: added D
68 68 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/32af7686d403-6f7dface-rebase.hg (glob)
69 69 $ cat D.orig
70 70 collide
71 71 $ rm D.orig
72 72
73 73 $ hg tglog
74 74 o 7: 'D'
75 75 |
76 76 @ 6: 'H'
77 77 |
78 78 | o 5: 'G'
79 79 |/|
80 80 o | 4: 'F'
81 81 | |
82 82 | o 3: 'E'
83 83 |/
84 84 | o 2: 'C'
85 85 | |
86 86 | o 1: 'B'
87 87 |/
88 88 o 0: 'A'
89 89
90 90 $ cd ..
91 91
92 92
93 93 D onto F - intermediate point:
94 94 (this also tests that editor is not invoked if '--edit' is not specified, and
95 95 that we can ignore for colliding untracked files)
96 96
97 97 $ hg clone -q -u . a a2
98 98 $ cd a2
99 99 $ echo collide > D
100 100
101 101 $ HGEDITOR=cat hg rebase -s 3 -d 5 --config merge.checkunknown=ignore
102 102 rebasing 3:32af7686d403 "D"
103 103 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/32af7686d403-6f7dface-rebase.hg (glob)
104 104 $ cat D.orig
105 105 collide
106 106 $ rm D.orig
107 107
108 108 $ hg tglog
109 109 o 7: 'D'
110 110 |
111 111 | @ 6: 'H'
112 112 |/
113 113 | o 5: 'G'
114 114 |/|
115 115 o | 4: 'F'
116 116 | |
117 117 | o 3: 'E'
118 118 |/
119 119 | o 2: 'C'
120 120 | |
121 121 | o 1: 'B'
122 122 |/
123 123 o 0: 'A'
124 124
125 125 $ cd ..
126 126
127 127
128 128 E onto H - skip of G:
129 129 (this also tests that we can overwrite untracked files and don't create backups
130 130 if they have the same contents)
131 131
132 132 $ hg clone -q -u . a a3
133 133 $ cd a3
134 134 $ hg cat -r 4 E | tee E
135 135 E
136 136
137 137 $ hg rebase -s 4 -d 7
138 138 rebasing 4:9520eea781bc "E"
139 139 rebasing 6:eea13746799a "G"
140 140 note: rebase of 6:eea13746799a created no changes to commit
141 141 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/9520eea781bc-fcd8edd4-rebase.hg (glob)
142 142 $ f E.orig
143 143 E.orig: file not found
144 144
145 145 $ hg tglog
146 146 o 6: 'E'
147 147 |
148 148 @ 5: 'H'
149 149 |
150 150 o 4: 'F'
151 151 |
152 152 | o 3: 'D'
153 153 | |
154 154 | o 2: 'C'
155 155 | |
156 156 | o 1: 'B'
157 157 |/
158 158 o 0: 'A'
159 159
160 160 $ cd ..
161 161
162 162
163 163 F onto E - rebase of a branching point (skip G):
164 164
165 165 $ hg clone -q -u . a a4
166 166 $ cd a4
167 167
168 168 $ hg rebase -s 5 -d 4
169 169 rebasing 5:24b6387c8c8c "F"
170 170 rebasing 6:eea13746799a "G"
171 171 note: rebase of 6:eea13746799a created no changes to commit
172 172 rebasing 7:02de42196ebe "H" (tip)
173 173 saved backup bundle to $TESTTMP/a4/.hg/strip-backup/24b6387c8c8c-c3fe765d-rebase.hg (glob)
174 174
175 175 $ hg tglog
176 176 @ 6: 'H'
177 177 |
178 178 o 5: 'F'
179 179 |
180 180 o 4: 'E'
181 181 |
182 182 | o 3: 'D'
183 183 | |
184 184 | o 2: 'C'
185 185 | |
186 186 | o 1: 'B'
187 187 |/
188 188 o 0: 'A'
189 189
190 190 $ cd ..
191 191
192 192
193 193 G onto H - merged revision having a parent in ancestors of target:
194 194
195 195 $ hg clone -q -u . a a5
196 196 $ cd a5
197 197
198 198 $ hg rebase -s 6 -d 7
199 199 rebasing 6:eea13746799a "G"
200 200 saved backup bundle to $TESTTMP/a5/.hg/strip-backup/eea13746799a-883828ed-rebase.hg (glob)
201 201
202 202 $ hg tglog
203 203 o 7: 'G'
204 204 |\
205 205 | @ 6: 'H'
206 206 | |
207 207 | o 5: 'F'
208 208 | |
209 209 o | 4: 'E'
210 210 |/
211 211 | o 3: 'D'
212 212 | |
213 213 | o 2: 'C'
214 214 | |
215 215 | o 1: 'B'
216 216 |/
217 217 o 0: 'A'
218 218
219 219 $ cd ..
220 220
221 221
222 222 F onto B - G maintains E as parent:
223 223
224 224 $ hg clone -q -u . a a6
225 225 $ cd a6
226 226
227 227 $ hg rebase -s 5 -d 1
228 228 rebasing 5:24b6387c8c8c "F"
229 229 rebasing 6:eea13746799a "G"
230 230 rebasing 7:02de42196ebe "H" (tip)
231 231 saved backup bundle to $TESTTMP/a6/.hg/strip-backup/24b6387c8c8c-c3fe765d-rebase.hg (glob)
232 232
233 233 $ hg tglog
234 234 @ 7: 'H'
235 235 |
236 236 | o 6: 'G'
237 237 |/|
238 238 o | 5: 'F'
239 239 | |
240 240 | o 4: 'E'
241 241 | |
242 242 | | o 3: 'D'
243 243 | | |
244 244 +---o 2: 'C'
245 245 | |
246 246 o | 1: 'B'
247 247 |/
248 248 o 0: 'A'
249 249
250 250 $ cd ..
251 251
252 252
253 253 These will fail (using --source):
254 254
255 255 G onto F - rebase onto an ancestor:
256 256
257 257 $ hg clone -q -u . a a7
258 258 $ cd a7
259 259
260 260 $ hg rebase -s 6 -d 5
261 261 nothing to rebase
262 262 [1]
263 263
264 264 F onto G - rebase onto a descendant:
265 265
266 266 $ hg rebase -s 5 -d 6
267 267 abort: source is ancestor of destination
268 268 [255]
269 269
270 270 G onto B - merge revision with both parents not in ancestors of target:
271 271
272 272 $ hg rebase -s 6 -d 1
273 273 rebasing 6:eea13746799a "G"
274 abort: cannot use revision 6 as base, result would have 3 parents
274 abort: cannot rebase 6:eea13746799a without moving at least one of its parents
275 275 [255]
276 276 $ hg rebase --abort
277 277 rebase aborted
278 278
279 279 These will abort gracefully (using --base):
280 280
281 281 G onto G - rebase onto same changeset:
282 282
283 283 $ hg rebase -b 6 -d 6
284 284 nothing to rebase - eea13746799a is both "base" and destination
285 285 [1]
286 286
287 287 G onto F - rebase onto an ancestor:
288 288
289 289 $ hg rebase -b 6 -d 5
290 290 nothing to rebase
291 291 [1]
292 292
293 293 F onto G - rebase onto a descendant:
294 294
295 295 $ hg rebase -b 5 -d 6
296 296 nothing to rebase - "base" 24b6387c8c8c is already an ancestor of destination eea13746799a
297 297 [1]
298 298
299 299 C onto A - rebase onto an ancestor:
300 300
301 301 $ hg rebase -d 0 -s 2
302 302 rebasing 2:5fddd98957c8 "C"
303 303 rebasing 3:32af7686d403 "D"
304 304 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/5fddd98957c8-f9244fa1-rebase.hg (glob)
305 305 $ hg tglog
306 306 o 7: 'D'
307 307 |
308 308 o 6: 'C'
309 309 |
310 310 | @ 5: 'H'
311 311 | |
312 312 | | o 4: 'G'
313 313 | |/|
314 314 | o | 3: 'F'
315 315 |/ /
316 316 | o 2: 'E'
317 317 |/
318 318 | o 1: 'B'
319 319 |/
320 320 o 0: 'A'
321 321
322 322
323 323 Check rebasing public changeset
324 324
325 325 $ hg pull --config phases.publish=True -q -r 6 . # update phase of 6
326 326 $ hg rebase -d 0 -b 6
327 327 nothing to rebase
328 328 [1]
329 329 $ hg rebase -d 5 -b 6
330 330 abort: can't rebase public changeset e1c4361dd923
331 331 (see 'hg help phases' for details)
332 332 [255]
333 333 $ hg rebase -d 5 -r '1 + (6::)'
334 334 abort: can't rebase public changeset e1c4361dd923
335 335 (see 'hg help phases' for details)
336 336 [255]
337 337
338 338 $ hg rebase -d 5 -b 6 --keep
339 339 rebasing 6:e1c4361dd923 "C"
340 340 rebasing 7:c9659aac0000 "D" (tip)
341 341
342 342 Check rebasing mutable changeset
343 343 Source phase greater or equal to destination phase: new changeset get the phase of source:
344 344 $ hg id -n
345 345 5
346 346 $ hg rebase -s9 -d0
347 347 rebasing 9:2b23e52411f4 "D" (tip)
348 348 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2b23e52411f4-f942decf-rebase.hg (glob)
349 349 $ hg id -n # check we updated back to parent
350 350 5
351 351 $ hg log --template "{phase}\n" -r 9
352 352 draft
353 353 $ hg rebase -s9 -d1
354 354 rebasing 9:2cb10d0cfc6c "D" (tip)
355 355 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2cb10d0cfc6c-ddb0f256-rebase.hg (glob)
356 356 $ hg log --template "{phase}\n" -r 9
357 357 draft
358 358 $ hg phase --force --secret 9
359 359 $ hg rebase -s9 -d0
360 360 rebasing 9:c5b12b67163a "D" (tip)
361 361 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/c5b12b67163a-4e372053-rebase.hg (glob)
362 362 $ hg log --template "{phase}\n" -r 9
363 363 secret
364 364 $ hg rebase -s9 -d1
365 365 rebasing 9:2a0524f868ac "D" (tip)
366 366 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2a0524f868ac-cefd8574-rebase.hg (glob)
367 367 $ hg log --template "{phase}\n" -r 9
368 368 secret
369 369 Source phase lower than destination phase: new changeset get the phase of destination:
370 370 $ hg rebase -s8 -d9
371 371 rebasing 8:6d4f22462821 "C"
372 372 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/6d4f22462821-3441f70b-rebase.hg (glob)
373 373 $ hg log --template "{phase}\n" -r 'rev(9)'
374 374 secret
375 375
376 376 $ cd ..
377 377
378 378 Test for revset
379 379
380 380 We need a bit different graph
381 381 All destination are B
382 382
383 383 $ hg init ah
384 384 $ cd ah
385 385 $ hg unbundle "$TESTDIR/bundles/rebase-revset.hg"
386 386 adding changesets
387 387 adding manifests
388 388 adding file changes
389 389 added 9 changesets with 9 changes to 9 files (+2 heads)
390 390 (run 'hg heads' to see heads, 'hg merge' to merge)
391 391 $ hg tglog
392 392 o 8: 'I'
393 393 |
394 394 o 7: 'H'
395 395 |
396 396 o 6: 'G'
397 397 |
398 398 | o 5: 'F'
399 399 | |
400 400 | o 4: 'E'
401 401 |/
402 402 o 3: 'D'
403 403 |
404 404 o 2: 'C'
405 405 |
406 406 | o 1: 'B'
407 407 |/
408 408 o 0: 'A'
409 409
410 410 $ cd ..
411 411
412 412
413 413 Simple case with keep:
414 414
415 415 Source on have two descendant heads but ask for one
416 416
417 417 $ hg clone -q -u . ah ah1
418 418 $ cd ah1
419 419 $ hg rebase -r '2::8' -d 1
420 420 abort: can't remove original changesets with unrebased descendants
421 421 (use --keep to keep original changesets)
422 422 [255]
423 423 $ hg rebase -r '2::8' -d 1 -k
424 424 rebasing 2:c9e50f6cdc55 "C"
425 425 rebasing 3:ffd453c31098 "D"
426 426 rebasing 6:3d8a618087a7 "G"
427 427 rebasing 7:72434a4e60b0 "H"
428 428 rebasing 8:479ddb54a924 "I" (tip)
429 429 $ hg tglog
430 430 o 13: 'I'
431 431 |
432 432 o 12: 'H'
433 433 |
434 434 o 11: 'G'
435 435 |
436 436 o 10: 'D'
437 437 |
438 438 o 9: 'C'
439 439 |
440 440 | o 8: 'I'
441 441 | |
442 442 | o 7: 'H'
443 443 | |
444 444 | o 6: 'G'
445 445 | |
446 446 | | o 5: 'F'
447 447 | | |
448 448 | | o 4: 'E'
449 449 | |/
450 450 | o 3: 'D'
451 451 | |
452 452 | o 2: 'C'
453 453 | |
454 454 o | 1: 'B'
455 455 |/
456 456 o 0: 'A'
457 457
458 458
459 459 $ cd ..
460 460
461 461 Base on have one descendant heads we ask for but common ancestor have two
462 462
463 463 $ hg clone -q -u . ah ah2
464 464 $ cd ah2
465 465 $ hg rebase -r '3::8' -d 1
466 466 abort: can't remove original changesets with unrebased descendants
467 467 (use --keep to keep original changesets)
468 468 [255]
469 469 $ hg rebase -r '3::8' -d 1 --keep
470 470 rebasing 3:ffd453c31098 "D"
471 471 rebasing 6:3d8a618087a7 "G"
472 472 rebasing 7:72434a4e60b0 "H"
473 473 rebasing 8:479ddb54a924 "I" (tip)
474 474 $ hg tglog
475 475 o 12: 'I'
476 476 |
477 477 o 11: 'H'
478 478 |
479 479 o 10: 'G'
480 480 |
481 481 o 9: 'D'
482 482 |
483 483 | o 8: 'I'
484 484 | |
485 485 | o 7: 'H'
486 486 | |
487 487 | o 6: 'G'
488 488 | |
489 489 | | o 5: 'F'
490 490 | | |
491 491 | | o 4: 'E'
492 492 | |/
493 493 | o 3: 'D'
494 494 | |
495 495 | o 2: 'C'
496 496 | |
497 497 o | 1: 'B'
498 498 |/
499 499 o 0: 'A'
500 500
501 501
502 502 $ cd ..
503 503
504 504 rebase subset
505 505
506 506 $ hg clone -q -u . ah ah3
507 507 $ cd ah3
508 508 $ hg rebase -r '3::7' -d 1
509 509 abort: can't remove original changesets with unrebased descendants
510 510 (use --keep to keep original changesets)
511 511 [255]
512 512 $ hg rebase -r '3::7' -d 1 --keep
513 513 rebasing 3:ffd453c31098 "D"
514 514 rebasing 6:3d8a618087a7 "G"
515 515 rebasing 7:72434a4e60b0 "H"
516 516 $ hg tglog
517 517 o 11: 'H'
518 518 |
519 519 o 10: 'G'
520 520 |
521 521 o 9: 'D'
522 522 |
523 523 | o 8: 'I'
524 524 | |
525 525 | o 7: 'H'
526 526 | |
527 527 | o 6: 'G'
528 528 | |
529 529 | | o 5: 'F'
530 530 | | |
531 531 | | o 4: 'E'
532 532 | |/
533 533 | o 3: 'D'
534 534 | |
535 535 | o 2: 'C'
536 536 | |
537 537 o | 1: 'B'
538 538 |/
539 539 o 0: 'A'
540 540
541 541
542 542 $ cd ..
543 543
544 544 rebase subset with multiple head
545 545
546 546 $ hg clone -q -u . ah ah4
547 547 $ cd ah4
548 548 $ hg rebase -r '3::(7+5)' -d 1
549 549 abort: can't remove original changesets with unrebased descendants
550 550 (use --keep to keep original changesets)
551 551 [255]
552 552 $ hg rebase -r '3::(7+5)' -d 1 --keep
553 553 rebasing 3:ffd453c31098 "D"
554 554 rebasing 4:c01897464e7f "E"
555 555 rebasing 5:41bfcc75ed73 "F"
556 556 rebasing 6:3d8a618087a7 "G"
557 557 rebasing 7:72434a4e60b0 "H"
558 558 $ hg tglog
559 559 o 13: 'H'
560 560 |
561 561 o 12: 'G'
562 562 |
563 563 | o 11: 'F'
564 564 | |
565 565 | o 10: 'E'
566 566 |/
567 567 o 9: 'D'
568 568 |
569 569 | o 8: 'I'
570 570 | |
571 571 | o 7: 'H'
572 572 | |
573 573 | o 6: 'G'
574 574 | |
575 575 | | o 5: 'F'
576 576 | | |
577 577 | | o 4: 'E'
578 578 | |/
579 579 | o 3: 'D'
580 580 | |
581 581 | o 2: 'C'
582 582 | |
583 583 o | 1: 'B'
584 584 |/
585 585 o 0: 'A'
586 586
587 587
588 588 $ cd ..
589 589
590 590 More advanced tests
591 591
592 592 rebase on ancestor with revset
593 593
594 594 $ hg clone -q -u . ah ah5
595 595 $ cd ah5
596 596 $ hg rebase -r '6::' -d 2
597 597 rebasing 6:3d8a618087a7 "G"
598 598 rebasing 7:72434a4e60b0 "H"
599 599 rebasing 8:479ddb54a924 "I" (tip)
600 600 saved backup bundle to $TESTTMP/ah5/.hg/strip-backup/3d8a618087a7-b4f73f31-rebase.hg (glob)
601 601 $ hg tglog
602 602 o 8: 'I'
603 603 |
604 604 o 7: 'H'
605 605 |
606 606 o 6: 'G'
607 607 |
608 608 | o 5: 'F'
609 609 | |
610 610 | o 4: 'E'
611 611 | |
612 612 | o 3: 'D'
613 613 |/
614 614 o 2: 'C'
615 615 |
616 616 | o 1: 'B'
617 617 |/
618 618 o 0: 'A'
619 619
620 620 $ cd ..
621 621
622 622
623 623 rebase with multiple root.
624 624 We rebase E and G on B
625 625 We would expect heads are I, F if it was supported
626 626
627 627 $ hg clone -q -u . ah ah6
628 628 $ cd ah6
629 629 $ hg rebase -r '(4+6)::' -d 1
630 630 rebasing 4:c01897464e7f "E"
631 631 rebasing 5:41bfcc75ed73 "F"
632 632 rebasing 6:3d8a618087a7 "G"
633 633 rebasing 7:72434a4e60b0 "H"
634 634 rebasing 8:479ddb54a924 "I" (tip)
635 635 saved backup bundle to $TESTTMP/ah6/.hg/strip-backup/3d8a618087a7-aae93a24-rebase.hg (glob)
636 636 $ hg tglog
637 637 o 8: 'I'
638 638 |
639 639 o 7: 'H'
640 640 |
641 641 o 6: 'G'
642 642 |
643 643 | o 5: 'F'
644 644 | |
645 645 | o 4: 'E'
646 646 |/
647 647 | o 3: 'D'
648 648 | |
649 649 | o 2: 'C'
650 650 | |
651 651 o | 1: 'B'
652 652 |/
653 653 o 0: 'A'
654 654
655 655 $ cd ..
656 656
657 657 More complex rebase with multiple roots
658 658 each root have a different common ancestor with the destination and this is a detach
659 659
660 660 (setup)
661 661
662 662 $ hg clone -q -u . a a8
663 663 $ cd a8
664 664 $ echo I > I
665 665 $ hg add I
666 666 $ hg commit -m I
667 667 $ hg up 4
668 668 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
669 669 $ echo I > J
670 670 $ hg add J
671 671 $ hg commit -m J
672 672 created new head
673 673 $ echo I > K
674 674 $ hg add K
675 675 $ hg commit -m K
676 676 $ hg tglog
677 677 @ 10: 'K'
678 678 |
679 679 o 9: 'J'
680 680 |
681 681 | o 8: 'I'
682 682 | |
683 683 | o 7: 'H'
684 684 | |
685 685 +---o 6: 'G'
686 686 | |/
687 687 | o 5: 'F'
688 688 | |
689 689 o | 4: 'E'
690 690 |/
691 691 | o 3: 'D'
692 692 | |
693 693 | o 2: 'C'
694 694 | |
695 695 | o 1: 'B'
696 696 |/
697 697 o 0: 'A'
698 698
699 699 (actual test)
700 700
701 701 $ hg rebase --dest 'desc(G)' --rev 'desc(K) + desc(I)'
702 702 rebasing 8:e7ec4e813ba6 "I"
703 703 rebasing 10:23a4ace37988 "K" (tip)
704 704 saved backup bundle to $TESTTMP/a8/.hg/strip-backup/23a4ace37988-b06984b3-rebase.hg (glob)
705 705 $ hg log --rev 'children(desc(G))'
706 706 changeset: 9:adb617877056
707 707 parent: 6:eea13746799a
708 708 user: test
709 709 date: Thu Jan 01 00:00:00 1970 +0000
710 710 summary: I
711 711
712 712 changeset: 10:882431a34a0e
713 713 tag: tip
714 714 parent: 6:eea13746799a
715 715 user: test
716 716 date: Thu Jan 01 00:00:00 1970 +0000
717 717 summary: K
718 718
719 719 $ hg tglog
720 720 @ 10: 'K'
721 721 |
722 722 | o 9: 'I'
723 723 |/
724 724 | o 8: 'J'
725 725 | |
726 726 | | o 7: 'H'
727 727 | | |
728 728 o---+ 6: 'G'
729 729 |/ /
730 730 | o 5: 'F'
731 731 | |
732 732 o | 4: 'E'
733 733 |/
734 734 | o 3: 'D'
735 735 | |
736 736 | o 2: 'C'
737 737 | |
738 738 | o 1: 'B'
739 739 |/
740 740 o 0: 'A'
741 741
742 742
743 743 Test that rebase is not confused by $CWD disappearing during rebase (issue4121)
744 744
745 745 $ cd ..
746 746 $ hg init cwd-vanish
747 747 $ cd cwd-vanish
748 748 $ touch initial-file
749 749 $ hg add initial-file
750 750 $ hg commit -m 'initial commit'
751 751 $ touch dest-file
752 752 $ hg add dest-file
753 753 $ hg commit -m 'dest commit'
754 754 $ hg up 0
755 755 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
756 756 $ touch other-file
757 757 $ hg add other-file
758 758 $ hg commit -m 'first source commit'
759 759 created new head
760 760 $ mkdir subdir
761 761 $ cd subdir
762 762 $ touch subfile
763 763 $ hg add subfile
764 764 $ hg commit -m 'second source with subdir'
765 765
766 766 $ hg rebase -b . -d 1 --traceback
767 767 rebasing 2:779a07b1b7a0 "first source commit"
768 768 current directory was removed (rmcwd !)
769 769 (consider changing to repo root: $TESTTMP/cwd-vanish) (rmcwd !)
770 770 rebasing 3:a7d6f3a00bf3 "second source with subdir" (tip)
771 771 saved backup bundle to $TESTTMP/cwd-vanish/.hg/strip-backup/779a07b1b7a0-853e0073-rebase.hg (glob)
772 772
773 773 Get back to the root of cwd-vanish. Note that even though `cd ..`
774 774 works on most systems, it does not work on FreeBSD 10, so we use an
775 775 absolute path to get back to the repository.
776 776 $ cd $TESTTMP
777 777
778 778 Test that rebase is done in topo order (issue5370)
779 779
780 780 $ hg init order
781 781 $ cd order
782 782 $ touch a && hg add a && hg ci -m A
783 783 $ touch b && hg add b && hg ci -m B
784 784 $ touch c && hg add c && hg ci -m C
785 785 $ hg up 1
786 786 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
787 787 $ touch d && hg add d && hg ci -m D
788 788 created new head
789 789 $ hg up 2
790 790 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
791 791 $ touch e && hg add e && hg ci -m E
792 792 $ hg up 3
793 793 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
794 794 $ touch f && hg add f && hg ci -m F
795 795 $ hg up 0
796 796 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
797 797 $ touch g && hg add g && hg ci -m G
798 798 created new head
799 799
800 800 $ hg tglog
801 801 @ 6: 'G'
802 802 |
803 803 | o 5: 'F'
804 804 | |
805 805 | | o 4: 'E'
806 806 | | |
807 807 | o | 3: 'D'
808 808 | | |
809 809 | | o 2: 'C'
810 810 | |/
811 811 | o 1: 'B'
812 812 |/
813 813 o 0: 'A'
814 814
815 815
816 816 $ hg rebase -s 1 -d 6
817 817 rebasing 1:76035bbd54bd "B"
818 818 rebasing 2:d84f5cfaaf14 "C"
819 819 rebasing 4:82ae8dc7a9b7 "E"
820 820 rebasing 3:ab709c9f7171 "D"
821 821 rebasing 5:412b391de760 "F"
822 822 saved backup bundle to $TESTTMP/order/.hg/strip-backup/76035bbd54bd-e341bc99-rebase.hg (glob)
823 823
824 824 $ hg tglog
825 825 o 6: 'F'
826 826 |
827 827 o 5: 'D'
828 828 |
829 829 | o 4: 'E'
830 830 | |
831 831 | o 3: 'C'
832 832 |/
833 833 o 2: 'B'
834 834 |
835 835 @ 1: 'G'
836 836 |
837 837 o 0: 'A'
838 838
839 839
840 840 Test experimental revset
841 841 ========================
842 842
843 843 $ cd ../cwd-vanish
844 844
845 845 Make the repo a bit more interesting
846 846
847 847 $ hg up 1
848 848 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
849 849 $ echo aaa > aaa
850 850 $ hg add aaa
851 851 $ hg commit -m aaa
852 852 created new head
853 853 $ hg log -G
854 854 @ changeset: 4:5f7bc9025ed2
855 855 | tag: tip
856 856 | parent: 1:58d79cc1cf43
857 857 | user: test
858 858 | date: Thu Jan 01 00:00:00 1970 +0000
859 859 | summary: aaa
860 860 |
861 861 | o changeset: 3:1910d5ff34ea
862 862 | | user: test
863 863 | | date: Thu Jan 01 00:00:00 1970 +0000
864 864 | | summary: second source with subdir
865 865 | |
866 866 | o changeset: 2:82901330b6ef
867 867 |/ user: test
868 868 | date: Thu Jan 01 00:00:00 1970 +0000
869 869 | summary: first source commit
870 870 |
871 871 o changeset: 1:58d79cc1cf43
872 872 | user: test
873 873 | date: Thu Jan 01 00:00:00 1970 +0000
874 874 | summary: dest commit
875 875 |
876 876 o changeset: 0:e94b687f7da3
877 877 user: test
878 878 date: Thu Jan 01 00:00:00 1970 +0000
879 879 summary: initial commit
880 880
881 881
882 882 Testing from lower head
883 883
884 884 $ hg up 3
885 885 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
886 886 $ hg log -r '_destrebase()'
887 887 changeset: 4:5f7bc9025ed2
888 888 tag: tip
889 889 parent: 1:58d79cc1cf43
890 890 user: test
891 891 date: Thu Jan 01 00:00:00 1970 +0000
892 892 summary: aaa
893 893
894 894
895 895 Testing from upper head
896 896
897 897 $ hg log -r '_destrebase(4)'
898 898 changeset: 3:1910d5ff34ea
899 899 user: test
900 900 date: Thu Jan 01 00:00:00 1970 +0000
901 901 summary: second source with subdir
902 902
903 903 $ hg up 4
904 904 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
905 905 $ hg log -r '_destrebase()'
906 906 changeset: 3:1910d5ff34ea
907 907 user: test
908 908 date: Thu Jan 01 00:00:00 1970 +0000
909 909 summary: second source with subdir
910 910
911 911 Testing rebase being called inside another transaction
912 912
913 913 $ cd $TESTTMP
914 914 $ hg init tr-state
915 915 $ cd tr-state
916 916 $ cat > $TESTTMP/wraprebase.py <<EOF
917 917 > from __future__ import absolute_import
918 918 > from mercurial import extensions
919 919 > def _rebase(orig, ui, repo, *args, **kwargs):
920 920 > with repo.wlock():
921 921 > with repo.lock():
922 922 > with repo.transaction('wrappedrebase'):
923 923 > return orig(ui, repo, *args, **kwargs)
924 924 > def wraprebase(loaded):
925 925 > assert loaded
926 926 > rebasemod = extensions.find('rebase')
927 927 > extensions.wrapcommand(rebasemod.cmdtable, 'rebase', _rebase)
928 928 > def extsetup(ui):
929 929 > extensions.afterloaded('rebase', wraprebase)
930 930 > EOF
931 931
932 932 $ cat >> .hg/hgrc <<EOF
933 933 > [extensions]
934 934 > wraprebase=$TESTTMP/wraprebase.py
935 935 > [experimental]
936 936 > stabilization=all
937 937 > EOF
938 938
939 939 $ hg debugdrawdag <<'EOS'
940 940 > B C
941 941 > |/
942 942 > A
943 943 > EOS
944 944
945 945 $ hg rebase -s C -d B
946 946 rebasing 2:dc0947a82db8 "C" (C tip)
947 947
948 948 $ [ -f .hg/rebasestate ] && echo 'WRONG: rebasestate should not exist'
949 949 [1]
General Comments 0
You need to be logged in to leave comments. Login now