##// END OF EJS Templates
rebase: choose default destination the same way as 'hg merge' (BC)...
Pierre-Yves David -
r28189:fac3a24b default
parent child Browse files
Show More
@@ -1,1312 +1,1328
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 mercurial import hg, util, repair, merge, cmdutil, commands, bookmarks
18 18 from mercurial import extensions, patch, scmutil, phases, obsolete, error
19 19 from mercurial import copies, destutil, repoview, revset
20 20 from mercurial.commands import templateopts
21 21 from mercurial.node import nullrev, nullid, hex, short
22 22 from mercurial.lock import release
23 23 from mercurial.i18n import _
24 24 import os, errno
25 25
26 26 # The following constants are used throughout the rebase module. The ordering of
27 27 # their values must be maintained.
28 28
29 29 # Indicates that a revision needs to be rebased
30 30 revtodo = -1
31 31 nullmerge = -2
32 32 revignored = -3
33 33 # successor in rebase destination
34 34 revprecursor = -4
35 35 # plain prune (no successor)
36 36 revpruned = -5
37 37 revskipped = (revignored, revprecursor, revpruned)
38 38
39 39 cmdtable = {}
40 40 command = cmdutil.command(cmdtable)
41 41 # Note for extension authors: ONLY specify testedwith = 'internal' for
42 42 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
43 43 # be specifying the version(s) of Mercurial they are tested with, or
44 44 # leave the attribute unspecified.
45 45 testedwith = 'internal'
46 46
47 47 def _nothingtorebase():
48 48 return 1
49 49
50 50 def _savegraft(ctx, extra):
51 51 s = ctx.extra().get('source', None)
52 52 if s is not None:
53 53 extra['source'] = s
54 54 s = ctx.extra().get('intermediate-source', None)
55 55 if s is not None:
56 56 extra['intermediate-source'] = s
57 57
58 58 def _savebranch(ctx, extra):
59 59 extra['branch'] = ctx.branch()
60 60
61 61 def _makeextrafn(copiers):
62 62 """make an extrafn out of the given copy-functions.
63 63
64 64 A copy function takes a context and an extra dict, and mutates the
65 65 extra dict as needed based on the given context.
66 66 """
67 67 def extrafn(ctx, extra):
68 68 for c in copiers:
69 69 c(ctx, extra)
70 70 return extrafn
71 71
72 def _destrebase(repo):
73 # Destination defaults to the latest revision in the
74 # current branch
75 branch = repo[None].branch()
76 return repo[branch].rev()
72 def _destrebase(repo, sourceset):
73 """small wrapper around destmerge to pass the right extra args
74
75 Please wrap destutil.destmerge instead."""
76 return destutil.destmerge(repo, action='rebase', sourceset=sourceset,
77 onheadcheck=False)
77 78
78 79 revsetpredicate = revset.extpredicate()
79 80
80 81 @revsetpredicate('_destrebase')
81 82 def _revsetdestrebase(repo, subset, x):
82 83 # ``_rebasedefaultdest()``
83 84
84 85 # default destination for rebase.
85 86 # # XXX: Currently private because I expect the signature to change.
86 # # XXX: - taking rev as arguments,
87 87 # # XXX: - bailing out in case of ambiguity vs returning all data.
88 # # XXX: - probably merging with the merge destination.
89 88 # i18n: "_rebasedefaultdest" is a keyword
90 revset.getargs(x, 0, 0, _("_rebasedefaultdest takes no arguments"))
91 return subset & revset.baseset([_destrebase(repo)])
89 sourceset = None
90 if x is not None:
91 sourceset = revset.getset(repo, revset.fullreposet(repo), x)
92 return subset & revset.baseset([_destrebase(repo, sourceset)])
92 93
93 94 @command('rebase',
94 95 [('s', 'source', '',
95 96 _('rebase the specified changeset and descendants'), _('REV')),
96 97 ('b', 'base', '',
97 98 _('rebase everything from branching point of specified changeset'),
98 99 _('REV')),
99 100 ('r', 'rev', [],
100 101 _('rebase these revisions'),
101 102 _('REV')),
102 103 ('d', 'dest', '',
103 104 _('rebase onto the specified changeset'), _('REV')),
104 105 ('', 'collapse', False, _('collapse the rebased changesets')),
105 106 ('m', 'message', '',
106 107 _('use text as collapse commit message'), _('TEXT')),
107 108 ('e', 'edit', False, _('invoke editor on commit messages')),
108 109 ('l', 'logfile', '',
109 110 _('read collapse commit message from file'), _('FILE')),
110 111 ('k', 'keep', False, _('keep original changesets')),
111 112 ('', 'keepbranches', False, _('keep original branch names')),
112 113 ('D', 'detach', False, _('(DEPRECATED)')),
113 114 ('i', 'interactive', False, _('(DEPRECATED)')),
114 115 ('t', 'tool', '', _('specify merge tool')),
115 116 ('c', 'continue', False, _('continue an interrupted rebase')),
116 117 ('a', 'abort', False, _('abort an interrupted rebase'))] +
117 118 templateopts,
118 119 _('[-s REV | -b REV] [-d REV] [OPTION]'))
119 120 def rebase(ui, repo, **opts):
120 121 """move changeset (and descendants) to a different branch
121 122
122 123 Rebase uses repeated merging to graft changesets from one part of
123 124 history (the source) onto another (the destination). This can be
124 125 useful for linearizing *local* changes relative to a master
125 126 development tree.
126 127
127 128 Published commits cannot be rebased (see :hg:`help phases`).
128 129 To copy commits, see :hg:`help graft`.
129 130
130 If you don't specify a destination changeset (``-d/--dest``),
131 rebase uses the current branch tip as the destination. (The
132 destination changeset is not modified by rebasing, but new
133 changesets are added as its descendants.)
131 If you don't specify a destination changeset (``-d/--dest``), rebase
132 will use the same logic as :hg:`merge` to pick a destination. if
133 the current branch contains exactly one other head, the other head
134 is merged with by default. Otherwise, an explicit revision with
135 which to merge with must be provided. (destination changeset is not
136 modified by rebasing, but new changesets are added as its
137 descendants.)
134 138
135 139 Here are the ways to select changesets:
136 140
137 141 1. Explicitly select them using ``--rev``.
138 142
139 143 2. Use ``--source`` to select a root changeset and include all of its
140 144 descendants.
141 145
142 146 3. Use ``--base`` to select a changeset; rebase will find ancestors
143 147 and their descendants which are not also ancestors of the destination.
144 148
145 149 4. If you do not specify any of ``--rev``, ``source``, or ``--base``,
146 150 rebase will use ``--base .`` as above.
147 151
148 152 Rebase will destroy original changesets unless you use ``--keep``.
149 153 It will also move your bookmarks (even if you do).
150 154
151 155 Some changesets may be dropped if they do not contribute changes
152 156 (e.g. merges from the destination branch).
153 157
154 158 Unlike ``merge``, rebase will do nothing if you are at the branch tip of
155 159 a named branch with two heads. You will need to explicitly specify source
156 160 and/or destination.
157 161
158 162 If you need to use a tool to automate merge/conflict decisions, you
159 163 can specify one with ``--tool``, see :hg:`help merge-tools`.
160 164 As a caveat: the tool will not be used to mediate when a file was
161 165 deleted, there is no hook presently available for this.
162 166
163 167 If a rebase is interrupted to manually resolve a conflict, it can be
164 168 continued with --continue/-c or aborted with --abort/-a.
165 169
166 170 .. container:: verbose
167 171
168 172 Examples:
169 173
170 174 - move "local changes" (current commit back to branching point)
171 175 to the current branch tip after a pull::
172 176
173 177 hg rebase
174 178
175 179 - move a single changeset to the stable branch::
176 180
177 181 hg rebase -r 5f493448 -d stable
178 182
179 183 - splice a commit and all its descendants onto another part of history::
180 184
181 185 hg rebase --source c0c3 --dest 4cf9
182 186
183 187 - rebase everything on a branch marked by a bookmark onto the
184 188 default branch::
185 189
186 190 hg rebase --base myfeature --dest default
187 191
188 192 - collapse a sequence of changes into a single commit::
189 193
190 194 hg rebase --collapse -r 1520:1525 -d .
191 195
192 196 - move a named branch while preserving its name::
193 197
194 198 hg rebase -r "branch(featureX)" -d 1.3 --keepbranches
195 199
196 200 Returns 0 on success, 1 if nothing to rebase or there are
197 201 unresolved conflicts.
198 202
199 203 """
200 204 originalwd = target = None
201 205 activebookmark = None
202 206 external = nullrev
203 207 # Mapping between the old revision id and either what is the new rebased
204 208 # revision or what needs to be done with the old revision. The state dict
205 209 # will be what contains most of the rebase progress state.
206 210 state = {}
207 211 skipped = set()
208 212 targetancestors = set()
209 213
210 214
211 215 lock = wlock = None
212 216 try:
213 217 wlock = repo.wlock()
214 218 lock = repo.lock()
215 219
216 220 # Validate input and define rebasing points
217 221 destf = opts.get('dest', None)
218 222 srcf = opts.get('source', None)
219 223 basef = opts.get('base', None)
220 224 revf = opts.get('rev', [])
221 225 contf = opts.get('continue')
222 226 abortf = opts.get('abort')
223 227 collapsef = opts.get('collapse', False)
224 228 collapsemsg = cmdutil.logmessage(ui, opts)
225 229 date = opts.get('date', None)
226 230 e = opts.get('extrafn') # internal, used by e.g. hgsubversion
227 231 extrafns = [_savegraft]
228 232 if e:
229 233 extrafns = [e]
230 234 keepf = opts.get('keep', False)
231 235 keepbranchesf = opts.get('keepbranches', False)
232 236 # keepopen is not meant for use on the command line, but by
233 237 # other extensions
234 238 keepopen = opts.get('keepopen', False)
235 239
236 240 if opts.get('interactive'):
237 241 try:
238 242 if extensions.find('histedit'):
239 243 enablehistedit = ''
240 244 except KeyError:
241 245 enablehistedit = " --config extensions.histedit="
242 246 help = "hg%s help -e histedit" % enablehistedit
243 247 msg = _("interactive history editing is supported by the "
244 248 "'histedit' extension (see \"%s\")") % help
245 249 raise error.Abort(msg)
246 250
247 251 if collapsemsg and not collapsef:
248 252 raise error.Abort(
249 253 _('message can only be specified with collapse'))
250 254
251 255 if contf or abortf:
252 256 if contf and abortf:
253 257 raise error.Abort(_('cannot use both abort and continue'))
254 258 if collapsef:
255 259 raise error.Abort(
256 260 _('cannot use collapse with continue or abort'))
257 261 if srcf or basef or destf:
258 262 raise error.Abort(
259 263 _('abort and continue do not allow specifying revisions'))
260 264 if abortf and opts.get('tool', False):
261 265 ui.warn(_('tool option will be ignored\n'))
262 266
263 267 try:
264 268 (originalwd, target, state, skipped, collapsef, keepf,
265 269 keepbranchesf, external, activebookmark) = restorestatus(repo)
266 270 collapsemsg = restorecollapsemsg(repo)
267 271 except error.RepoLookupError:
268 272 if abortf:
269 273 clearstatus(repo)
270 274 clearcollapsemsg(repo)
271 275 repo.ui.warn(_('rebase aborted (no revision is removed,'
272 276 ' only broken state is cleared)\n'))
273 277 return 0
274 278 else:
275 279 msg = _('cannot continue inconsistent rebase')
276 280 hint = _('use "hg rebase --abort" to clear broken state')
277 281 raise error.Abort(msg, hint=hint)
278 282 if abortf:
279 283 return abort(repo, originalwd, target, state,
280 284 activebookmark=activebookmark)
281 285 else:
282 286 dest, rebaseset = _definesets(ui, repo, destf, srcf, basef, revf)
283 287 if dest is None:
284 288 return _nothingtorebase()
285 289
286 290 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
287 291 if (not (keepf or allowunstable)
288 292 and repo.revs('first(children(%ld) - %ld)',
289 293 rebaseset, rebaseset)):
290 294 raise error.Abort(
291 295 _("can't remove original changesets with"
292 296 " unrebased descendants"),
293 297 hint=_('use --keep to keep original changesets'))
294 298
295 299 obsoletenotrebased = {}
296 300 if ui.configbool('experimental', 'rebaseskipobsolete'):
297 301 rebasesetrevs = set(rebaseset)
298 302 rebaseobsrevs = _filterobsoleterevs(repo, rebasesetrevs)
299 303 obsoletenotrebased = _computeobsoletenotrebased(repo,
300 304 rebaseobsrevs,
301 305 dest)
302 306 rebaseobsskipped = set(obsoletenotrebased)
303 307
304 308 # Obsolete node with successors not in dest leads to divergence
305 309 divergenceok = ui.configbool('rebase',
306 310 'allowdivergence')
307 311 divergencebasecandidates = rebaseobsrevs - rebaseobsskipped
308 312
309 313 if divergencebasecandidates and not divergenceok:
310 314 divhashes = (str(repo[r])
311 315 for r in divergencebasecandidates)
312 316 msg = _("this rebase will cause "
313 317 "divergences from: %s")
314 318 h = _("to force the rebase please set "
315 319 "rebase.allowdivergence=True")
316 320 raise error.Abort(msg % (",".join(divhashes),), hint=h)
317 321
318 322 # - plain prune (no successor) changesets are rebased
319 323 # - split changesets are not rebased if at least one of the
320 324 # changeset resulting from the split is an ancestor of dest
321 325 rebaseset = rebasesetrevs - rebaseobsskipped
322 326 if rebasesetrevs and not rebaseset:
323 327 msg = _('all requested changesets have equivalents '
324 328 'or were marked as obsolete')
325 329 hint = _('to force the rebase, set the config '
326 330 'experimental.rebaseskipobsolete to False')
327 331 raise error.Abort(msg, hint=hint)
328 332
329 333 result = buildstate(repo, dest, rebaseset, collapsef,
330 334 obsoletenotrebased)
331 335
332 336 if not result:
333 337 # Empty state built, nothing to rebase
334 338 ui.status(_('nothing to rebase\n'))
335 339 return _nothingtorebase()
336 340
337 341 root = min(rebaseset)
338 342 if not keepf and not repo[root].mutable():
339 343 raise error.Abort(_("can't rebase public changeset %s")
340 344 % repo[root],
341 345 hint=_('see "hg help phases" for details'))
342 346
343 347 originalwd, target, state = result
344 348 if collapsef:
345 349 targetancestors = repo.changelog.ancestors([target],
346 350 inclusive=True)
347 351 external = externalparent(repo, state, targetancestors)
348 352
349 353 if dest.closesbranch() and not keepbranchesf:
350 354 ui.status(_('reopening closed branch head %s\n') % dest)
351 355
352 356 if keepbranchesf:
353 357 # insert _savebranch at the start of extrafns so if
354 358 # there's a user-provided extrafn it can clobber branch if
355 359 # desired
356 360 extrafns.insert(0, _savebranch)
357 361 if collapsef:
358 362 branches = set()
359 363 for rev in state:
360 364 branches.add(repo[rev].branch())
361 365 if len(branches) > 1:
362 366 raise error.Abort(_('cannot collapse multiple named '
363 367 'branches'))
364 368
365 369 # Rebase
366 370 if not targetancestors:
367 371 targetancestors = repo.changelog.ancestors([target], inclusive=True)
368 372
369 373 # Keep track of the current bookmarks in order to reset them later
370 374 currentbookmarks = repo._bookmarks.copy()
371 375 activebookmark = activebookmark or repo._activebookmark
372 376 if activebookmark:
373 377 bookmarks.deactivate(repo)
374 378
375 379 extrafn = _makeextrafn(extrafns)
376 380
377 381 sortedstate = sorted(state)
378 382 total = len(sortedstate)
379 383 pos = 0
380 384 for rev in sortedstate:
381 385 ctx = repo[rev]
382 386 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
383 387 ctx.description().split('\n', 1)[0])
384 388 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
385 389 if names:
386 390 desc += ' (%s)' % ' '.join(names)
387 391 pos += 1
388 392 if state[rev] == revtodo:
389 393 ui.status(_('rebasing %s\n') % desc)
390 394 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, ctx)),
391 395 _('changesets'), total)
392 396 p1, p2, base = defineparents(repo, rev, target, state,
393 397 targetancestors)
394 398 storestatus(repo, originalwd, target, state, collapsef, keepf,
395 399 keepbranchesf, external, activebookmark)
396 400 storecollapsemsg(repo, collapsemsg)
397 401 if len(repo[None].parents()) == 2:
398 402 repo.ui.debug('resuming interrupted rebase\n')
399 403 else:
400 404 try:
401 405 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
402 406 'rebase')
403 407 stats = rebasenode(repo, rev, p1, base, state,
404 408 collapsef, target)
405 409 if stats and stats[3] > 0:
406 410 raise error.InterventionRequired(
407 411 _('unresolved conflicts (see hg '
408 412 'resolve, then hg rebase --continue)'))
409 413 finally:
410 414 ui.setconfig('ui', 'forcemerge', '', 'rebase')
411 415 if not collapsef:
412 416 merging = p2 != nullrev
413 417 editform = cmdutil.mergeeditform(merging, 'rebase')
414 418 editor = cmdutil.getcommiteditor(editform=editform, **opts)
415 419 newnode = concludenode(repo, rev, p1, p2, extrafn=extrafn,
416 420 editor=editor,
417 421 keepbranches=keepbranchesf,
418 422 date=date)
419 423 else:
420 424 # Skip commit if we are collapsing
421 425 repo.dirstate.beginparentchange()
422 426 repo.setparents(repo[p1].node())
423 427 repo.dirstate.endparentchange()
424 428 newnode = None
425 429 # Update the state
426 430 if newnode is not None:
427 431 state[rev] = repo[newnode].rev()
428 432 ui.debug('rebased as %s\n' % short(newnode))
429 433 else:
430 434 if not collapsef:
431 435 ui.warn(_('note: rebase of %d:%s created no changes '
432 436 'to commit\n') % (rev, ctx))
433 437 skipped.add(rev)
434 438 state[rev] = p1
435 439 ui.debug('next revision set to %s\n' % p1)
436 440 elif state[rev] == nullmerge:
437 441 ui.debug('ignoring null merge rebase of %s\n' % rev)
438 442 elif state[rev] == revignored:
439 443 ui.status(_('not rebasing ignored %s\n') % desc)
440 444 elif state[rev] == revprecursor:
441 445 targetctx = repo[obsoletenotrebased[rev]]
442 446 desctarget = '%d:%s "%s"' % (targetctx.rev(), targetctx,
443 447 targetctx.description().split('\n', 1)[0])
444 448 msg = _('note: not rebasing %s, already in destination as %s\n')
445 449 ui.status(msg % (desc, desctarget))
446 450 elif state[rev] == revpruned:
447 451 msg = _('note: not rebasing %s, it has no successor\n')
448 452 ui.status(msg % desc)
449 453 else:
450 454 ui.status(_('already rebased %s as %s\n') %
451 455 (desc, repo[state[rev]]))
452 456
453 457 ui.progress(_('rebasing'), None)
454 458 ui.note(_('rebase merging completed\n'))
455 459
456 460 if collapsef and not keepopen:
457 461 p1, p2, _base = defineparents(repo, min(state), target,
458 462 state, targetancestors)
459 463 editopt = opts.get('edit')
460 464 editform = 'rebase.collapse'
461 465 if collapsemsg:
462 466 commitmsg = collapsemsg
463 467 else:
464 468 commitmsg = 'Collapsed revision'
465 469 for rebased in state:
466 470 if rebased not in skipped and state[rebased] > nullmerge:
467 471 commitmsg += '\n* %s' % repo[rebased].description()
468 472 editopt = True
469 473 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
470 474 newnode = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
471 475 extrafn=extrafn, editor=editor,
472 476 keepbranches=keepbranchesf,
473 477 date=date)
474 478 if newnode is None:
475 479 newrev = target
476 480 else:
477 481 newrev = repo[newnode].rev()
478 482 for oldrev in state.iterkeys():
479 483 if state[oldrev] > nullmerge:
480 484 state[oldrev] = newrev
481 485
482 486 if 'qtip' in repo.tags():
483 487 updatemq(repo, state, skipped, **opts)
484 488
485 489 if currentbookmarks:
486 490 # Nodeids are needed to reset bookmarks
487 491 nstate = {}
488 492 for k, v in state.iteritems():
489 493 if v > nullmerge:
490 494 nstate[repo[k].node()] = repo[v].node()
491 495 # XXX this is the same as dest.node() for the non-continue path --
492 496 # this should probably be cleaned up
493 497 targetnode = repo[target].node()
494 498
495 499 # restore original working directory
496 500 # (we do this before stripping)
497 501 newwd = state.get(originalwd, originalwd)
498 502 if newwd < 0:
499 503 # original directory is a parent of rebase set root or ignored
500 504 newwd = originalwd
501 505 if newwd not in [c.rev() for c in repo[None].parents()]:
502 506 ui.note(_("update back to initial working directory parent\n"))
503 507 hg.updaterepo(repo, newwd, False)
504 508
505 509 if not keepf:
506 510 collapsedas = None
507 511 if collapsef:
508 512 collapsedas = newnode
509 513 clearrebased(ui, repo, state, skipped, collapsedas)
510 514
511 515 with repo.transaction('bookmark') as tr:
512 516 if currentbookmarks:
513 517 updatebookmarks(repo, targetnode, nstate, currentbookmarks, tr)
514 518 if activebookmark not in repo._bookmarks:
515 519 # active bookmark was divergent one and has been deleted
516 520 activebookmark = None
517 521 clearstatus(repo)
518 522 clearcollapsemsg(repo)
519 523
520 524 ui.note(_("rebase completed\n"))
521 525 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
522 526 if skipped:
523 527 ui.note(_("%d revisions have been skipped\n") % len(skipped))
524 528
525 529 if (activebookmark and
526 530 repo['.'].node() == repo._bookmarks[activebookmark]):
527 531 bookmarks.activate(repo, activebookmark)
528 532
529 533 finally:
530 534 release(lock, wlock)
531 535
532 536 def _definesets(ui, repo, destf=None, srcf=None, basef=None, revf=[]):
533 537 """use revisions argument to define destination and rebase set
534 538 """
535 539 if srcf and basef:
536 540 raise error.Abort(_('cannot specify both a source and a base'))
537 541 if revf and basef:
538 542 raise error.Abort(_('cannot specify both a revision and a base'))
539 543 if revf and srcf:
540 544 raise error.Abort(_('cannot specify both a revision and a source'))
541 545
542 546 cmdutil.checkunfinished(repo)
543 547 cmdutil.bailifchanged(repo)
544 548
545 549 if destf:
546 550 dest = scmutil.revsingle(repo, destf)
547 else:
548 dest = repo[_destrebase(repo)]
549 destf = str(dest)
550 551
551 552 if revf:
552 553 rebaseset = scmutil.revrange(repo, revf)
553 554 if not rebaseset:
554 555 ui.status(_('empty "rev" revision set - nothing to rebase\n'))
555 556 return None, None
556 557 elif srcf:
557 558 src = scmutil.revrange(repo, [srcf])
558 559 if not src:
559 560 ui.status(_('empty "source" revision set - nothing to rebase\n'))
560 561 return None, None
561 562 rebaseset = repo.revs('(%ld)::', src)
562 563 assert rebaseset
563 564 else:
564 565 base = scmutil.revrange(repo, [basef or '.'])
565 566 if not base:
566 567 ui.status(_('empty "base" revision set - '
567 568 "can't compute rebase set\n"))
568 569 return None, None
570 if not destf:
571 dest = repo[_destrebase(repo, base)]
572 destf = str(dest)
573
569 574 commonanc = repo.revs('ancestor(%ld, %d)', base, dest).first()
570 575 if commonanc is not None:
571 576 rebaseset = repo.revs('(%d::(%ld) - %d)::',
572 577 commonanc, base, commonanc)
573 578 else:
574 579 rebaseset = []
575 580
576 581 if not rebaseset:
577 582 # transform to list because smartsets are not comparable to
578 583 # lists. This should be improved to honor laziness of
579 584 # smartset.
580 585 if list(base) == [dest.rev()]:
581 586 if basef:
582 587 ui.status(_('nothing to rebase - %s is both "base"'
583 588 ' and destination\n') % dest)
584 589 else:
585 590 ui.status(_('nothing to rebase - working directory '
586 591 'parent is also destination\n'))
587 592 elif not repo.revs('%ld - ::%d', base, dest):
588 593 if basef:
589 594 ui.status(_('nothing to rebase - "base" %s is '
590 595 'already an ancestor of destination '
591 596 '%s\n') %
592 597 ('+'.join(str(repo[r]) for r in base),
593 598 dest))
594 599 else:
595 600 ui.status(_('nothing to rebase - working '
596 601 'directory parent is already an '
597 602 'ancestor of destination %s\n') % dest)
598 603 else: # can it happen?
599 604 ui.status(_('nothing to rebase from %s to %s\n') %
600 605 ('+'.join(str(repo[r]) for r in base), dest))
601 606 return None, None
607
608 if not destf:
609 dest = repo[_destrebase(repo, rebaseset)]
610 destf = str(dest)
611
602 612 return dest, rebaseset
603 613
604 614 def externalparent(repo, state, targetancestors):
605 615 """Return the revision that should be used as the second parent
606 616 when the revisions in state is collapsed on top of targetancestors.
607 617 Abort if there is more than one parent.
608 618 """
609 619 parents = set()
610 620 source = min(state)
611 621 for rev in state:
612 622 if rev == source:
613 623 continue
614 624 for p in repo[rev].parents():
615 625 if (p.rev() not in state
616 626 and p.rev() not in targetancestors):
617 627 parents.add(p.rev())
618 628 if not parents:
619 629 return nullrev
620 630 if len(parents) == 1:
621 631 return parents.pop()
622 632 raise error.Abort(_('unable to collapse on top of %s, there is more '
623 633 'than one external parent: %s') %
624 634 (max(targetancestors),
625 635 ', '.join(str(p) for p in sorted(parents))))
626 636
627 637 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None,
628 638 keepbranches=False, date=None):
629 639 '''Commit the wd changes with parents p1 and p2. Reuse commit info from rev
630 640 but also store useful information in extra.
631 641 Return node of committed revision.'''
632 642 dsguard = cmdutil.dirstateguard(repo, 'rebase')
633 643 try:
634 644 repo.setparents(repo[p1].node(), repo[p2].node())
635 645 ctx = repo[rev]
636 646 if commitmsg is None:
637 647 commitmsg = ctx.description()
638 648 keepbranch = keepbranches and repo[p1].branch() != ctx.branch()
639 649 extra = {'rebase_source': ctx.hex()}
640 650 if extrafn:
641 651 extrafn(ctx, extra)
642 652
643 653 backup = repo.ui.backupconfig('phases', 'new-commit')
644 654 try:
645 655 targetphase = max(ctx.phase(), phases.draft)
646 656 repo.ui.setconfig('phases', 'new-commit', targetphase, 'rebase')
647 657 if keepbranch:
648 658 repo.ui.setconfig('ui', 'allowemptycommit', True)
649 659 # Commit might fail if unresolved files exist
650 660 if date is None:
651 661 date = ctx.date()
652 662 newnode = repo.commit(text=commitmsg, user=ctx.user(),
653 663 date=date, extra=extra, editor=editor)
654 664 finally:
655 665 repo.ui.restoreconfig(backup)
656 666
657 667 repo.dirstate.setbranch(repo[newnode].branch())
658 668 dsguard.close()
659 669 return newnode
660 670 finally:
661 671 release(dsguard)
662 672
663 673 def rebasenode(repo, rev, p1, base, state, collapse, target):
664 674 'Rebase a single revision rev on top of p1 using base as merge ancestor'
665 675 # Merge phase
666 676 # Update to target and merge it with local
667 677 if repo['.'].rev() != p1:
668 678 repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
669 679 merge.update(repo, p1, False, True)
670 680 else:
671 681 repo.ui.debug(" already in target\n")
672 682 repo.dirstate.write(repo.currenttransaction())
673 683 repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev]))
674 684 if base is not None:
675 685 repo.ui.debug(" detach base %d:%s\n" % (base, repo[base]))
676 686 # When collapsing in-place, the parent is the common ancestor, we
677 687 # have to allow merging with it.
678 688 stats = merge.update(repo, rev, True, True, base, collapse,
679 689 labels=['dest', 'source'])
680 690 if collapse:
681 691 copies.duplicatecopies(repo, rev, target)
682 692 else:
683 693 # If we're not using --collapse, we need to
684 694 # duplicate copies between the revision we're
685 695 # rebasing and its first parent, but *not*
686 696 # duplicate any copies that have already been
687 697 # performed in the destination.
688 698 p1rev = repo[rev].p1().rev()
689 699 copies.duplicatecopies(repo, rev, p1rev, skiprev=target)
690 700 return stats
691 701
692 702 def nearestrebased(repo, rev, state):
693 703 """return the nearest ancestors of rev in the rebase result"""
694 704 rebased = [r for r in state if state[r] > nullmerge]
695 705 candidates = repo.revs('max(%ld and (::%d))', rebased, rev)
696 706 if candidates:
697 707 return state[candidates.first()]
698 708 else:
699 709 return None
700 710
701 711 def defineparents(repo, rev, target, state, targetancestors):
702 712 'Return the new parent relationship of the revision that will be rebased'
703 713 parents = repo[rev].parents()
704 714 p1 = p2 = nullrev
705 715
706 716 p1n = parents[0].rev()
707 717 if p1n in targetancestors:
708 718 p1 = target
709 719 elif p1n in state:
710 720 if state[p1n] == nullmerge:
711 721 p1 = target
712 722 elif state[p1n] in revskipped:
713 723 p1 = nearestrebased(repo, p1n, state)
714 724 if p1 is None:
715 725 p1 = target
716 726 else:
717 727 p1 = state[p1n]
718 728 else: # p1n external
719 729 p1 = target
720 730 p2 = p1n
721 731
722 732 if len(parents) == 2 and parents[1].rev() not in targetancestors:
723 733 p2n = parents[1].rev()
724 734 # interesting second parent
725 735 if p2n in state:
726 736 if p1 == target: # p1n in targetancestors or external
727 737 p1 = state[p2n]
728 738 elif state[p2n] in revskipped:
729 739 p2 = nearestrebased(repo, p2n, state)
730 740 if p2 is None:
731 741 # no ancestors rebased yet, detach
732 742 p2 = target
733 743 else:
734 744 p2 = state[p2n]
735 745 else: # p2n external
736 746 if p2 != nullrev: # p1n external too => rev is a merged revision
737 747 raise error.Abort(_('cannot use revision %d as base, result '
738 748 'would have 3 parents') % rev)
739 749 p2 = p2n
740 750 repo.ui.debug(" future parents are %d and %d\n" %
741 751 (repo[p1].rev(), repo[p2].rev()))
742 752
743 753 if not any(p.rev() in state for p in parents):
744 754 # Case (1) root changeset of a non-detaching rebase set.
745 755 # Let the merge mechanism find the base itself.
746 756 base = None
747 757 elif not repo[rev].p2():
748 758 # Case (2) detaching the node with a single parent, use this parent
749 759 base = repo[rev].p1().rev()
750 760 else:
751 761 # Assuming there is a p1, this is the case where there also is a p2.
752 762 # We are thus rebasing a merge and need to pick the right merge base.
753 763 #
754 764 # Imagine we have:
755 765 # - M: current rebase revision in this step
756 766 # - A: one parent of M
757 767 # - B: other parent of M
758 768 # - D: destination of this merge step (p1 var)
759 769 #
760 770 # Consider the case where D is a descendant of A or B and the other is
761 771 # 'outside'. In this case, the right merge base is the D ancestor.
762 772 #
763 773 # An informal proof, assuming A is 'outside' and B is the D ancestor:
764 774 #
765 775 # If we pick B as the base, the merge involves:
766 776 # - changes from B to M (actual changeset payload)
767 777 # - changes from B to D (induced by rebase) as D is a rebased
768 778 # version of B)
769 779 # Which exactly represent the rebase operation.
770 780 #
771 781 # If we pick A as the base, the merge involves:
772 782 # - changes from A to M (actual changeset payload)
773 783 # - changes from A to D (with include changes between unrelated A and B
774 784 # plus changes induced by rebase)
775 785 # Which does not represent anything sensible and creates a lot of
776 786 # conflicts. A is thus not the right choice - B is.
777 787 #
778 788 # Note: The base found in this 'proof' is only correct in the specified
779 789 # case. This base does not make sense if is not D a descendant of A or B
780 790 # or if the other is not parent 'outside' (especially not if the other
781 791 # parent has been rebased). The current implementation does not
782 792 # make it feasible to consider different cases separately. In these
783 793 # other cases we currently just leave it to the user to correctly
784 794 # resolve an impossible merge using a wrong ancestor.
785 795 for p in repo[rev].parents():
786 796 if state.get(p.rev()) == p1:
787 797 base = p.rev()
788 798 break
789 799 else: # fallback when base not found
790 800 base = None
791 801
792 802 # Raise because this function is called wrong (see issue 4106)
793 803 raise AssertionError('no base found to rebase on '
794 804 '(defineparents called wrong)')
795 805 return p1, p2, base
796 806
797 807 def isagitpatch(repo, patchname):
798 808 'Return true if the given patch is in git format'
799 809 mqpatch = os.path.join(repo.mq.path, patchname)
800 810 for line in patch.linereader(file(mqpatch, 'rb')):
801 811 if line.startswith('diff --git'):
802 812 return True
803 813 return False
804 814
805 815 def updatemq(repo, state, skipped, **opts):
806 816 'Update rebased mq patches - finalize and then import them'
807 817 mqrebase = {}
808 818 mq = repo.mq
809 819 original_series = mq.fullseries[:]
810 820 skippedpatches = set()
811 821
812 822 for p in mq.applied:
813 823 rev = repo[p.node].rev()
814 824 if rev in state:
815 825 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
816 826 (rev, p.name))
817 827 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
818 828 else:
819 829 # Applied but not rebased, not sure this should happen
820 830 skippedpatches.add(p.name)
821 831
822 832 if mqrebase:
823 833 mq.finish(repo, mqrebase.keys())
824 834
825 835 # We must start import from the newest revision
826 836 for rev in sorted(mqrebase, reverse=True):
827 837 if rev not in skipped:
828 838 name, isgit = mqrebase[rev]
829 839 repo.ui.note(_('updating mq patch %s to %s:%s\n') %
830 840 (name, state[rev], repo[state[rev]]))
831 841 mq.qimport(repo, (), patchname=name, git=isgit,
832 842 rev=[str(state[rev])])
833 843 else:
834 844 # Rebased and skipped
835 845 skippedpatches.add(mqrebase[rev][0])
836 846
837 847 # Patches were either applied and rebased and imported in
838 848 # order, applied and removed or unapplied. Discard the removed
839 849 # ones while preserving the original series order and guards.
840 850 newseries = [s for s in original_series
841 851 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
842 852 mq.fullseries[:] = newseries
843 853 mq.seriesdirty = True
844 854 mq.savedirty()
845 855
846 856 def updatebookmarks(repo, targetnode, nstate, originalbookmarks, tr):
847 857 'Move bookmarks to their correct changesets, and delete divergent ones'
848 858 marks = repo._bookmarks
849 859 for k, v in originalbookmarks.iteritems():
850 860 if v in nstate:
851 861 # update the bookmarks for revs that have moved
852 862 marks[k] = nstate[v]
853 863 bookmarks.deletedivergent(repo, [targetnode], k)
854 864 marks.recordchange(tr)
855 865
856 866 def storecollapsemsg(repo, collapsemsg):
857 867 'Store the collapse message to allow recovery'
858 868 collapsemsg = collapsemsg or ''
859 869 f = repo.vfs("last-message.txt", "w")
860 870 f.write("%s\n" % collapsemsg)
861 871 f.close()
862 872
863 873 def clearcollapsemsg(repo):
864 874 'Remove collapse message file'
865 875 util.unlinkpath(repo.join("last-message.txt"), ignoremissing=True)
866 876
867 877 def restorecollapsemsg(repo):
868 878 'Restore previously stored collapse message'
869 879 try:
870 880 f = repo.vfs("last-message.txt")
871 881 collapsemsg = f.readline().strip()
872 882 f.close()
873 883 except IOError as err:
874 884 if err.errno != errno.ENOENT:
875 885 raise
876 886 raise error.Abort(_('no rebase in progress'))
877 887 return collapsemsg
878 888
879 889 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
880 890 external, activebookmark):
881 891 'Store the current status to allow recovery'
882 892 f = repo.vfs("rebasestate", "w")
883 893 f.write(repo[originalwd].hex() + '\n')
884 894 f.write(repo[target].hex() + '\n')
885 895 f.write(repo[external].hex() + '\n')
886 896 f.write('%d\n' % int(collapse))
887 897 f.write('%d\n' % int(keep))
888 898 f.write('%d\n' % int(keepbranches))
889 899 f.write('%s\n' % (activebookmark or ''))
890 900 for d, v in state.iteritems():
891 901 oldrev = repo[d].hex()
892 902 if v >= 0:
893 903 newrev = repo[v].hex()
894 904 elif v == revtodo:
895 905 # To maintain format compatibility, we have to use nullid.
896 906 # Please do remove this special case when upgrading the format.
897 907 newrev = hex(nullid)
898 908 else:
899 909 newrev = v
900 910 f.write("%s:%s\n" % (oldrev, newrev))
901 911 f.close()
902 912 repo.ui.debug('rebase status stored\n')
903 913
904 914 def clearstatus(repo):
905 915 'Remove the status files'
906 916 _clearrebasesetvisibiliy(repo)
907 917 util.unlinkpath(repo.join("rebasestate"), ignoremissing=True)
908 918
909 919 def restorestatus(repo):
910 920 'Restore a previously stored status'
911 921 keepbranches = None
912 922 target = None
913 923 collapse = False
914 924 external = nullrev
915 925 activebookmark = None
916 926 state = {}
917 927
918 928 try:
919 929 f = repo.vfs("rebasestate")
920 930 for i, l in enumerate(f.read().splitlines()):
921 931 if i == 0:
922 932 originalwd = repo[l].rev()
923 933 elif i == 1:
924 934 target = repo[l].rev()
925 935 elif i == 2:
926 936 external = repo[l].rev()
927 937 elif i == 3:
928 938 collapse = bool(int(l))
929 939 elif i == 4:
930 940 keep = bool(int(l))
931 941 elif i == 5:
932 942 keepbranches = bool(int(l))
933 943 elif i == 6 and not (len(l) == 81 and ':' in l):
934 944 # line 6 is a recent addition, so for backwards compatibility
935 945 # check that the line doesn't look like the oldrev:newrev lines
936 946 activebookmark = l
937 947 else:
938 948 oldrev, newrev = l.split(':')
939 949 if newrev in (str(nullmerge), str(revignored),
940 950 str(revprecursor), str(revpruned)):
941 951 state[repo[oldrev].rev()] = int(newrev)
942 952 elif newrev == nullid:
943 953 state[repo[oldrev].rev()] = revtodo
944 954 # Legacy compat special case
945 955 else:
946 956 state[repo[oldrev].rev()] = repo[newrev].rev()
947 957
948 958 except IOError as err:
949 959 if err.errno != errno.ENOENT:
950 960 raise
951 961 cmdutil.wrongtooltocontinue(repo, _('rebase'))
952 962
953 963 if keepbranches is None:
954 964 raise error.Abort(_('.hg/rebasestate is incomplete'))
955 965
956 966 skipped = set()
957 967 # recompute the set of skipped revs
958 968 if not collapse:
959 969 seen = set([target])
960 970 for old, new in sorted(state.items()):
961 971 if new != revtodo and new in seen:
962 972 skipped.add(old)
963 973 seen.add(new)
964 974 repo.ui.debug('computed skipped revs: %s\n' %
965 975 (' '.join(str(r) for r in sorted(skipped)) or None))
966 976 repo.ui.debug('rebase status resumed\n')
967 977 _setrebasesetvisibility(repo, state.keys())
968 978 return (originalwd, target, state, skipped,
969 979 collapse, keep, keepbranches, external, activebookmark)
970 980
971 981 def needupdate(repo, state):
972 982 '''check whether we should `update --clean` away from a merge, or if
973 983 somehow the working dir got forcibly updated, e.g. by older hg'''
974 984 parents = [p.rev() for p in repo[None].parents()]
975 985
976 986 # Are we in a merge state at all?
977 987 if len(parents) < 2:
978 988 return False
979 989
980 990 # We should be standing on the first as-of-yet unrebased commit.
981 991 firstunrebased = min([old for old, new in state.iteritems()
982 992 if new == nullrev])
983 993 if firstunrebased in parents:
984 994 return True
985 995
986 996 return False
987 997
988 998 def abort(repo, originalwd, target, state, activebookmark=None):
989 999 '''Restore the repository to its original state. Additional args:
990 1000
991 1001 activebookmark: the name of the bookmark that should be active after the
992 1002 restore'''
993 1003
994 1004 try:
995 1005 # If the first commits in the rebased set get skipped during the rebase,
996 1006 # their values within the state mapping will be the target rev id. The
997 1007 # dstates list must must not contain the target rev (issue4896)
998 1008 dstates = [s for s in state.values() if s >= 0 and s != target]
999 1009 immutable = [d for d in dstates if not repo[d].mutable()]
1000 1010 cleanup = True
1001 1011 if immutable:
1002 1012 repo.ui.warn(_("warning: can't clean up public changesets %s\n")
1003 1013 % ', '.join(str(repo[r]) for r in immutable),
1004 1014 hint=_('see "hg help phases" for details'))
1005 1015 cleanup = False
1006 1016
1007 1017 descendants = set()
1008 1018 if dstates:
1009 1019 descendants = set(repo.changelog.descendants(dstates))
1010 1020 if descendants - set(dstates):
1011 1021 repo.ui.warn(_("warning: new changesets detected on target branch, "
1012 1022 "can't strip\n"))
1013 1023 cleanup = False
1014 1024
1015 1025 if cleanup:
1016 1026 shouldupdate = False
1017 1027 rebased = filter(lambda x: x >= 0 and x != target, state.values())
1018 1028 if rebased:
1019 1029 strippoints = [
1020 1030 c.node() for c in repo.set('roots(%ld)', rebased)]
1021 1031 shouldupdate = len([
1022 1032 c.node() for c in repo.set('. & (%ld)', rebased)]) > 0
1023 1033
1024 1034 # Update away from the rebase if necessary
1025 1035 if shouldupdate or needupdate(repo, state):
1026 1036 merge.update(repo, originalwd, False, True)
1027 1037
1028 1038 # Strip from the first rebased revision
1029 1039 if rebased:
1030 1040 # no backup of rebased cset versions needed
1031 1041 repair.strip(repo.ui, repo, strippoints)
1032 1042
1033 1043 if activebookmark and activebookmark in repo._bookmarks:
1034 1044 bookmarks.activate(repo, activebookmark)
1035 1045
1036 1046 finally:
1037 1047 clearstatus(repo)
1038 1048 clearcollapsemsg(repo)
1039 1049 repo.ui.warn(_('rebase aborted\n'))
1040 1050 return 0
1041 1051
1042 1052 def buildstate(repo, dest, rebaseset, collapse, obsoletenotrebased):
1043 1053 '''Define which revisions are going to be rebased and where
1044 1054
1045 1055 repo: repo
1046 1056 dest: context
1047 1057 rebaseset: set of rev
1048 1058 '''
1049 1059 _setrebasesetvisibility(repo, rebaseset)
1050 1060
1051 1061 # This check isn't strictly necessary, since mq detects commits over an
1052 1062 # applied patch. But it prevents messing up the working directory when
1053 1063 # a partially completed rebase is blocked by mq.
1054 1064 if 'qtip' in repo.tags() and (dest.node() in
1055 1065 [s.node for s in repo.mq.applied]):
1056 1066 raise error.Abort(_('cannot rebase onto an applied mq patch'))
1057 1067
1058 1068 roots = list(repo.set('roots(%ld)', rebaseset))
1059 1069 if not roots:
1060 1070 raise error.Abort(_('no matching revisions'))
1061 1071 roots.sort()
1062 1072 state = {}
1063 1073 detachset = set()
1064 1074 for root in roots:
1065 1075 commonbase = root.ancestor(dest)
1066 1076 if commonbase == root:
1067 1077 raise error.Abort(_('source is ancestor of destination'))
1068 1078 if commonbase == dest:
1069 1079 samebranch = root.branch() == dest.branch()
1070 1080 if not collapse and samebranch and root in dest.children():
1071 1081 repo.ui.debug('source is a child of destination\n')
1072 1082 return None
1073 1083
1074 1084 repo.ui.debug('rebase onto %d starting from %s\n' % (dest, root))
1075 1085 state.update(dict.fromkeys(rebaseset, revtodo))
1076 1086 # Rebase tries to turn <dest> into a parent of <root> while
1077 1087 # preserving the number of parents of rebased changesets:
1078 1088 #
1079 1089 # - A changeset with a single parent will always be rebased as a
1080 1090 # changeset with a single parent.
1081 1091 #
1082 1092 # - A merge will be rebased as merge unless its parents are both
1083 1093 # ancestors of <dest> or are themselves in the rebased set and
1084 1094 # pruned while rebased.
1085 1095 #
1086 1096 # If one parent of <root> is an ancestor of <dest>, the rebased
1087 1097 # version of this parent will be <dest>. This is always true with
1088 1098 # --base option.
1089 1099 #
1090 1100 # Otherwise, we need to *replace* the original parents with
1091 1101 # <dest>. This "detaches" the rebased set from its former location
1092 1102 # and rebases it onto <dest>. Changes introduced by ancestors of
1093 1103 # <root> not common with <dest> (the detachset, marked as
1094 1104 # nullmerge) are "removed" from the rebased changesets.
1095 1105 #
1096 1106 # - If <root> has a single parent, set it to <dest>.
1097 1107 #
1098 1108 # - If <root> is a merge, we cannot decide which parent to
1099 1109 # replace, the rebase operation is not clearly defined.
1100 1110 #
1101 1111 # The table below sums up this behavior:
1102 1112 #
1103 1113 # +------------------+----------------------+-------------------------+
1104 1114 # | | one parent | merge |
1105 1115 # +------------------+----------------------+-------------------------+
1106 1116 # | parent in | new parent is <dest> | parents in ::<dest> are |
1107 1117 # | ::<dest> | | remapped to <dest> |
1108 1118 # +------------------+----------------------+-------------------------+
1109 1119 # | unrelated source | new parent is <dest> | ambiguous, abort |
1110 1120 # +------------------+----------------------+-------------------------+
1111 1121 #
1112 1122 # The actual abort is handled by `defineparents`
1113 1123 if len(root.parents()) <= 1:
1114 1124 # ancestors of <root> not ancestors of <dest>
1115 1125 detachset.update(repo.changelog.findmissingrevs([commonbase.rev()],
1116 1126 [root.rev()]))
1117 1127 for r in detachset:
1118 1128 if r not in state:
1119 1129 state[r] = nullmerge
1120 1130 if len(roots) > 1:
1121 1131 # If we have multiple roots, we may have "hole" in the rebase set.
1122 1132 # Rebase roots that descend from those "hole" should not be detached as
1123 1133 # other root are. We use the special `revignored` to inform rebase that
1124 1134 # the revision should be ignored but that `defineparents` should search
1125 1135 # a rebase destination that make sense regarding rebased topology.
1126 1136 rebasedomain = set(repo.revs('%ld::%ld', rebaseset, rebaseset))
1127 1137 for ignored in set(rebasedomain) - set(rebaseset):
1128 1138 state[ignored] = revignored
1129 1139 for r in obsoletenotrebased:
1130 1140 if obsoletenotrebased[r] is None:
1131 1141 state[r] = revpruned
1132 1142 else:
1133 1143 state[r] = revprecursor
1134 1144 return repo['.'].rev(), dest.rev(), state
1135 1145
1136 1146 def clearrebased(ui, repo, state, skipped, collapsedas=None):
1137 1147 """dispose of rebased revision at the end of the rebase
1138 1148
1139 1149 If `collapsedas` is not None, the rebase was a collapse whose result if the
1140 1150 `collapsedas` node."""
1141 1151 if obsolete.isenabled(repo, obsolete.createmarkersopt):
1142 1152 markers = []
1143 1153 for rev, newrev in sorted(state.items()):
1144 1154 if newrev >= 0:
1145 1155 if rev in skipped:
1146 1156 succs = ()
1147 1157 elif collapsedas is not None:
1148 1158 succs = (repo[collapsedas],)
1149 1159 else:
1150 1160 succs = (repo[newrev],)
1151 1161 markers.append((repo[rev], succs))
1152 1162 if markers:
1153 1163 obsolete.createmarkers(repo, markers)
1154 1164 else:
1155 1165 rebased = [rev for rev in state if state[rev] > nullmerge]
1156 1166 if rebased:
1157 1167 stripped = []
1158 1168 for root in repo.set('roots(%ld)', rebased):
1159 1169 if set(repo.changelog.descendants([root.rev()])) - set(state):
1160 1170 ui.warn(_("warning: new changesets detected "
1161 1171 "on source branch, not stripping\n"))
1162 1172 else:
1163 1173 stripped.append(root.node())
1164 1174 if stripped:
1165 1175 # backup the old csets by default
1166 1176 repair.strip(ui, repo, stripped, "all")
1167 1177
1168 1178
1169 1179 def pullrebase(orig, ui, repo, *args, **opts):
1170 1180 'Call rebase after pull if the latter has been invoked with --rebase'
1171 1181 ret = None
1172 1182 if opts.get('rebase'):
1173 1183 wlock = lock = None
1174 1184 try:
1175 1185 wlock = repo.wlock()
1176 1186 lock = repo.lock()
1177 1187 if opts.get('update'):
1178 1188 del opts['update']
1179 1189 ui.debug('--update and --rebase are not compatible, ignoring '
1180 1190 'the update flag\n')
1181 1191
1182 1192 revsprepull = len(repo)
1183 1193 origpostincoming = commands.postincoming
1184 1194 def _dummy(*args, **kwargs):
1185 1195 pass
1186 1196 commands.postincoming = _dummy
1187 1197 try:
1188 1198 ret = orig(ui, repo, *args, **opts)
1189 1199 finally:
1190 1200 commands.postincoming = origpostincoming
1191 1201 revspostpull = len(repo)
1192 1202 if revspostpull > revsprepull:
1193 1203 # --rev option from pull conflict with rebase own --rev
1194 1204 # dropping it
1195 1205 if 'rev' in opts:
1196 1206 del opts['rev']
1197 1207 # positional argument from pull conflicts with rebase's own
1198 1208 # --source.
1199 1209 if 'source' in opts:
1200 1210 del opts['source']
1201 if rebase(ui, repo, **opts) == _nothingtorebase():
1211 try:
1212 rebase(ui, repo, **opts)
1213 except error.NoMergeDestAbort:
1214 # we can maybe update instead
1202 1215 rev, _a, _b = destutil.destupdate(repo)
1203 if rev != repo['.'].rev(): # we could update
1216 if rev == repo['.'].rev():
1217 ui.status(_('nothing to rebase\n'))
1218 else:
1219 ui.status(_('nothing to rebase - updating instead\n'))
1204 1220 # not passing argument to get the bare update behavior
1205 1221 # with warning and trumpets
1206 1222 commands.update(ui, repo)
1207 1223 finally:
1208 1224 release(lock, wlock)
1209 1225 else:
1210 1226 if opts.get('tool'):
1211 1227 raise error.Abort(_('--tool can only be used with --rebase'))
1212 1228 ret = orig(ui, repo, *args, **opts)
1213 1229
1214 1230 return ret
1215 1231
1216 1232 def _setrebasesetvisibility(repo, revs):
1217 1233 """store the currently rebased set on the repo object
1218 1234
1219 1235 This is used by another function to prevent rebased revision to because
1220 1236 hidden (see issue4505)"""
1221 1237 repo = repo.unfiltered()
1222 1238 revs = set(revs)
1223 1239 repo._rebaseset = revs
1224 1240 # invalidate cache if visibility changes
1225 1241 hiddens = repo.filteredrevcache.get('visible', set())
1226 1242 if revs & hiddens:
1227 1243 repo.invalidatevolatilesets()
1228 1244
1229 1245 def _clearrebasesetvisibiliy(repo):
1230 1246 """remove rebaseset data from the repo"""
1231 1247 repo = repo.unfiltered()
1232 1248 if '_rebaseset' in vars(repo):
1233 1249 del repo._rebaseset
1234 1250
1235 1251 def _rebasedvisible(orig, repo):
1236 1252 """ensure rebased revs stay visible (see issue4505)"""
1237 1253 blockers = orig(repo)
1238 1254 blockers.update(getattr(repo, '_rebaseset', ()))
1239 1255 return blockers
1240 1256
1241 1257 def _filterobsoleterevs(repo, revs):
1242 1258 """returns a set of the obsolete revisions in revs"""
1243 1259 return set(r for r in revs if repo[r].obsolete())
1244 1260
1245 1261 def _computeobsoletenotrebased(repo, rebaseobsrevs, dest):
1246 1262 """return a mapping obsolete => successor for all obsolete nodes to be
1247 1263 rebased that have a successors in the destination
1248 1264
1249 1265 obsolete => None entries in the mapping indicate nodes with no succesor"""
1250 1266 obsoletenotrebased = {}
1251 1267
1252 1268 # Build a mapping successor => obsolete nodes for the obsolete
1253 1269 # nodes to be rebased
1254 1270 allsuccessors = {}
1255 1271 cl = repo.changelog
1256 1272 for r in rebaseobsrevs:
1257 1273 node = cl.node(r)
1258 1274 for s in obsolete.allsuccessors(repo.obsstore, [node]):
1259 1275 try:
1260 1276 allsuccessors[cl.rev(s)] = cl.rev(node)
1261 1277 except LookupError:
1262 1278 pass
1263 1279
1264 1280 if allsuccessors:
1265 1281 # Look for successors of obsolete nodes to be rebased among
1266 1282 # the ancestors of dest
1267 1283 ancs = cl.ancestors([repo[dest].rev()],
1268 1284 stoprev=min(allsuccessors),
1269 1285 inclusive=True)
1270 1286 for s in allsuccessors:
1271 1287 if s in ancs:
1272 1288 obsoletenotrebased[allsuccessors[s]] = s
1273 1289 elif (s == allsuccessors[s] and
1274 1290 allsuccessors.values().count(s) == 1):
1275 1291 # plain prune
1276 1292 obsoletenotrebased[s] = None
1277 1293
1278 1294 return obsoletenotrebased
1279 1295
1280 1296 def summaryhook(ui, repo):
1281 1297 if not os.path.exists(repo.join('rebasestate')):
1282 1298 return
1283 1299 try:
1284 1300 state = restorestatus(repo)[2]
1285 1301 except error.RepoLookupError:
1286 1302 # i18n: column positioning for "hg summary"
1287 1303 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
1288 1304 ui.write(msg)
1289 1305 return
1290 1306 numrebased = len([i for i in state.itervalues() if i >= 0])
1291 1307 # i18n: column positioning for "hg summary"
1292 1308 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
1293 1309 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
1294 1310 ui.label(_('%d remaining'), 'rebase.remaining') %
1295 1311 (len(state) - numrebased)))
1296 1312
1297 1313 def uisetup(ui):
1298 1314 #Replace pull with a decorator to provide --rebase option
1299 1315 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
1300 1316 entry[1].append(('', 'rebase', None,
1301 1317 _("rebase working directory to branch head")))
1302 1318 entry[1].append(('t', 'tool', '',
1303 1319 _("specify merge tool for rebase")))
1304 1320 cmdutil.summaryhooks.add('rebase', summaryhook)
1305 1321 cmdutil.unfinishedstates.append(
1306 1322 ['rebasestate', False, False, _('rebase in progress'),
1307 1323 _("use 'hg rebase --continue' or 'hg rebase --abort'")])
1308 1324 cmdutil.afterresolvedstates.append(
1309 1325 ['rebasestate', _('hg rebase --continue')])
1310 1326 # ensure rebased rev are not hidden
1311 1327 extensions.wrapfunction(repoview, '_getdynamicblockers', _rebasedvisible)
1312 1328 revsetpredicate.setup()
@@ -1,344 +1,376
1 1 # destutil.py - Mercurial utility function for command destination
2 2 #
3 3 # Copyright Matt Mackall <mpm@selenic.com> and other
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 from __future__ import absolute_import
9 9
10 10 from .i18n import _
11 11 from . import (
12 12 bookmarks,
13 13 error,
14 14 obsolete,
15 15 )
16 16
17 17 def _destupdatevalidate(repo, rev, clean, check):
18 18 """validate that the destination comply to various rules
19 19
20 20 This exists as its own function to help wrapping from extensions."""
21 21 wc = repo[None]
22 22 p1 = wc.p1()
23 23 if not clean:
24 24 # Check that the update is linear.
25 25 #
26 26 # Mercurial do not allow update-merge for non linear pattern
27 27 # (that would be technically possible but was considered too confusing
28 28 # for user a long time ago)
29 29 #
30 30 # See mercurial.merge.update for details
31 31 if p1.rev() not in repo.changelog.ancestors([rev], inclusive=True):
32 32 dirty = wc.dirty(missing=True)
33 33 foreground = obsolete.foreground(repo, [p1.node()])
34 34 if not repo[rev].node() in foreground:
35 35 if dirty:
36 36 msg = _("uncommitted changes")
37 37 hint = _("commit and merge, or update --clean to"
38 38 " discard changes")
39 39 raise error.UpdateAbort(msg, hint=hint)
40 40 elif not check: # destination is not a descendant.
41 41 msg = _("not a linear update")
42 42 hint = _("merge or update --check to force update")
43 43 raise error.UpdateAbort(msg, hint=hint)
44 44
45 45 def _destupdateobs(repo, clean, check):
46 46 """decide of an update destination from obsolescence markers"""
47 47 node = None
48 48 wc = repo[None]
49 49 p1 = wc.p1()
50 50 movemark = None
51 51
52 52 if p1.obsolete() and not p1.children():
53 53 # allow updating to successors
54 54 successors = obsolete.successorssets(repo, p1.node())
55 55
56 56 # behavior of certain cases is as follows,
57 57 #
58 58 # divergent changesets: update to highest rev, similar to what
59 59 # is currently done when there are more than one head
60 60 # (i.e. 'tip')
61 61 #
62 62 # replaced changesets: same as divergent except we know there
63 63 # is no conflict
64 64 #
65 65 # pruned changeset: no update is done; though, we could
66 66 # consider updating to the first non-obsolete parent,
67 67 # similar to what is current done for 'hg prune'
68 68
69 69 if successors:
70 70 # flatten the list here handles both divergent (len > 1)
71 71 # and the usual case (len = 1)
72 72 successors = [n for sub in successors for n in sub]
73 73
74 74 # get the max revision for the given successors set,
75 75 # i.e. the 'tip' of a set
76 76 node = repo.revs('max(%ln)', successors).first()
77 77 if bookmarks.isactivewdirparent(repo):
78 78 movemark = repo['.'].node()
79 79 return node, movemark, None
80 80
81 81 def _destupdatebook(repo, clean, check):
82 82 """decide on an update destination from active bookmark"""
83 83 # we also move the active bookmark, if any
84 84 activemark = None
85 85 node, movemark = bookmarks.calculateupdate(repo.ui, repo, None)
86 86 if node is not None:
87 87 activemark = node
88 88 return node, movemark, activemark
89 89
90 90 def _destupdatebranch(repo, clean, check):
91 91 """decide on an update destination from current branch"""
92 92 wc = repo[None]
93 93 movemark = node = None
94 94 try:
95 95 node = repo.revs('max(.::(head() and branch(%s)))'
96 96 , wc.branch()).first()
97 97 if bookmarks.isactivewdirparent(repo):
98 98 movemark = repo['.'].node()
99 99 except error.RepoLookupError:
100 100 if wc.branch() == 'default': # no default branch!
101 101 node = repo.lookup('tip') # update to tip
102 102 else:
103 103 raise error.Abort(_("branch %s not found") % wc.branch())
104 104 return node, movemark, None
105 105
106 106 # order in which each step should be evalutated
107 107 # steps are run until one finds a destination
108 108 destupdatesteps = ['evolution', 'bookmark', 'branch']
109 109 # mapping to ease extension overriding steps.
110 110 destupdatestepmap = {'evolution': _destupdateobs,
111 111 'bookmark': _destupdatebook,
112 112 'branch': _destupdatebranch,
113 113 }
114 114
115 115 def destupdate(repo, clean=False, check=False):
116 116 """destination for bare update operation
117 117
118 118 return (rev, movemark, activemark)
119 119
120 120 - rev: the revision to update to,
121 121 - movemark: node to move the active bookmark from
122 122 (cf bookmark.calculate update),
123 123 - activemark: a bookmark to activate at the end of the update.
124 124 """
125 125 node = movemark = activemark = None
126 126
127 127 for step in destupdatesteps:
128 128 node, movemark, activemark = destupdatestepmap[step](repo, clean, check)
129 129 if node is not None:
130 130 break
131 131 rev = repo[node].rev()
132 132
133 133 _destupdatevalidate(repo, rev, clean, check)
134 134
135 135 return rev, movemark, activemark
136 136
137 137 msgdestmerge = {
138 138 # too many matching divergent bookmark
139 139 'toomanybookmarks':
140 140 {'merge':
141 141 (_("multiple matching bookmarks to merge -"
142 142 " please merge with an explicit rev or bookmark"),
143 143 _("run 'hg heads' to see all heads")),
144 'rebase':
145 (_("multiple matching bookmarks to rebase -"
146 " please rebase to an explicit rev or bookmark"),
147 _("run 'hg heads' to see all heads")),
144 148 },
145 149 # no other matching divergent bookmark
146 150 'nootherbookmarks':
147 151 {'merge':
148 152 (_("no matching bookmark to merge - "
149 153 "please merge with an explicit rev or bookmark"),
150 154 _("run 'hg heads' to see all heads")),
155 'rebase':
156 (_("no matching bookmark to rebase - "
157 "please rebase to an explicit rev or bookmark"),
158 _("run 'hg heads' to see all heads")),
151 159 },
152 160 # branch have too many unbookmarked heads, no obvious destination
153 161 'toomanyheads':
154 162 {'merge':
155 163 (_("branch '%s' has %d heads - please merge with an explicit rev"),
156 164 _("run 'hg heads .' to see heads")),
165 'rebase':
166 (_("branch '%s' has %d heads - please rebase to an explicit rev"),
167 _("run 'hg heads .' to see heads")),
157 168 },
158 169 # branch have no other unbookmarked heads
159 170 'bookmarkedheads':
160 171 {'merge':
161 172 (_("heads are bookmarked - please merge with an explicit rev"),
162 173 _("run 'hg heads' to see all heads")),
174 'rebase':
175 (_("heads are bookmarked - please rebase to an explicit rev"),
176 _("run 'hg heads' to see all heads")),
163 177 },
164 178 # branch have just a single heads, but there is other branches
165 179 'nootherbranchheads':
166 180 {'merge':
167 181 (_("branch '%s' has one head - please merge with an explicit rev"),
168 182 _("run 'hg heads' to see all heads")),
183 'rebase':
184 (_("branch '%s' has one head - please rebase to an explicit rev"),
185 _("run 'hg heads' to see all heads")),
169 186 },
170 187 # repository have a single head
171 188 'nootherheads':
172 {'merge':
173 (_('nothing to merge'),
174 None),
189 {'merge':
190 (_('nothing to merge'),
191 None),
192 'rebase':
193 (_('nothing to rebase'),
194 None),
175 195 },
176 196 # repository have a single head and we are not on it
177 197 'nootherheadsbehind':
178 198 {'merge':
179 199 (_('nothing to merge'),
180 200 _("use 'hg update' instead")),
201 'rebase':
202 (_('nothing to rebase'),
203 _("use 'hg update' instead")),
181 204 },
182 205 # We are not on a head
183 206 'notatheads':
184 207 {'merge':
185 208 (_('working directory not at a head revision'),
186 _("use 'hg update' or merge with an explicit revision"))
209 _("use 'hg update' or merge with an explicit revision")),
210 'rebase':
211 (_('working directory not at a head revision'),
212 _("use 'hg update' or rebase to an explicit revision"))
187 213 },
188 214 'emptysourceset':
189 215 {'merge':
190 216 (_('source set is empty'),
191 None)
217 None),
218 'rebase':
219 (_('source set is empty'),
220 None),
192 221 },
193 222 'multiplebranchessourceset':
194 223 {'merge':
195 224 (_('source set is rooted in multiple branches'),
196 None)
225 None),
226 'rebase':
227 (_('rebaseset is rooted in multiple named branches'),
228 _('specify an explicit destination with --dest')),
197 229 },
198 230 }
199 231
200 232 def _destmergebook(repo, action='merge', sourceset=None):
201 233 """find merge destination in the active bookmark case"""
202 234 node = None
203 235 bmheads = repo.bookmarkheads(repo._activebookmark)
204 236 curhead = repo[repo._activebookmark].node()
205 237 if len(bmheads) == 2:
206 238 if curhead == bmheads[0]:
207 239 node = bmheads[1]
208 240 else:
209 241 node = bmheads[0]
210 242 elif len(bmheads) > 2:
211 243 msg, hint = msgdestmerge['toomanybookmarks'][action]
212 244 raise error.ManyMergeDestAbort(msg, hint=hint)
213 245 elif len(bmheads) <= 1:
214 246 msg, hint = msgdestmerge['nootherbookmarks'][action]
215 247 raise error.NoMergeDestAbort(msg, hint=hint)
216 248 assert node is not None
217 249 return node
218 250
219 251 def _destmergebranch(repo, action='merge', sourceset=None, onheadcheck=True):
220 252 """find merge destination based on branch heads"""
221 253 node = None
222 254
223 255 if sourceset is None:
224 256 sourceset = [repo[repo.dirstate.p1()].rev()]
225 257 branch = repo.dirstate.branch()
226 258 elif not sourceset:
227 259 msg, hint = msgdestmerge['emptysourceset'][action]
228 260 raise error.NoMergeDestAbort(msg, hint=hint)
229 261 else:
230 262 branch = None
231 263 for ctx in repo.set('roots(%ld::%ld)', sourceset, sourceset):
232 264 if branch is not None and ctx.branch() != branch:
233 265 msg, hint = msgdestmerge['multiplebranchessourceset'][action]
234 266 raise error.ManyMergeDestAbort(msg, hint=hint)
235 267 branch = ctx.branch()
236 268
237 269 bheads = repo.branchheads(branch)
238 270 onhead = repo.revs('%ld and %ln', sourceset, bheads)
239 271 if onheadcheck and not onhead:
240 272 # Case A: working copy if not on a head. (merge only)
241 273 #
242 274 # This is probably a user mistake We bailout pointing at 'hg update'
243 275 if len(repo.heads()) <= 1:
244 276 msg, hint = msgdestmerge['nootherheadsbehind'][action]
245 277 else:
246 278 msg, hint = msgdestmerge['notatheads'][action]
247 279 raise error.Abort(msg, hint=hint)
248 280 # remove heads descendants of source from the set
249 281 bheads = list(repo.revs('%ln - (%ld::)', bheads, sourceset))
250 282 # filters out bookmarked heads
251 283 nbhs = list(repo.revs('%ld - bookmark()', bheads))
252 284 if len(nbhs) > 1:
253 285 # Case B: There is more than 1 other anonymous heads
254 286 #
255 287 # This means that there will be more than 1 candidate. This is
256 288 # ambiguous. We abort asking the user to pick as explicit destination
257 289 # instead.
258 290 msg, hint = msgdestmerge['toomanyheads'][action]
259 291 msg %= (branch, len(bheads) + 1)
260 292 raise error.ManyMergeDestAbort(msg, hint=hint)
261 293 elif not nbhs:
262 294 # Case B: There is no other anonymous heads
263 295 #
264 296 # This means that there is no natural candidate to merge with.
265 297 # We abort, with various messages for various cases.
266 298 if bheads:
267 299 msg, hint = msgdestmerge['bookmarkedheads'][action]
268 300 elif len(repo.heads()) > 1:
269 301 msg, hint = msgdestmerge['nootherbranchheads'][action]
270 302 msg %= branch
271 303 elif not onhead:
272 304 # if 'onheadcheck == False' (rebase case),
273 305 # this was not caught in Case A.
274 306 msg, hint = msgdestmerge['nootherheadsbehind'][action]
275 307 else:
276 308 msg, hint = msgdestmerge['nootherheads'][action]
277 309 raise error.NoMergeDestAbort(msg, hint=hint)
278 310 else:
279 311 node = nbhs[0]
280 312 assert node is not None
281 313 return node
282 314
283 315 def destmerge(repo, action='merge', sourceset=None, onheadcheck=True):
284 316 """return the default destination for a merge
285 317
286 318 (or raise exception about why it can't pick one)
287 319
288 320 :action: the action being performed, controls emitted error message
289 321 """
290 322 if repo._activebookmark:
291 323 node = _destmergebook(repo, action=action, sourceset=sourceset)
292 324 else:
293 325 node = _destmergebranch(repo, action=action, sourceset=sourceset,
294 326 onheadcheck=onheadcheck)
295 327 return repo[node].rev()
296 328
297 329 histeditdefaultrevset = 'reverse(only(.) and not public() and not ::merge())'
298 330
299 331 def desthistedit(ui, repo):
300 332 """Default base revision to edit for `hg histedit`."""
301 333 # Avoid cycle: scmutil -> revset -> destutil
302 334 from . import scmutil
303 335
304 336 default = ui.config('histedit', 'defaultrev', histeditdefaultrevset)
305 337 if default:
306 338 revs = scmutil.revrange(repo, [default])
307 339 if revs:
308 340 # The revset supplied by the user may not be in ascending order nor
309 341 # take the first revision. So do this manually.
310 342 revs.sort()
311 343 return revs.first()
312 344
313 345 return None
314 346
315 347 def _statusotherbook(ui, repo):
316 348 bmheads = repo.bookmarkheads(repo._activebookmark)
317 349 curhead = repo[repo._activebookmark].node()
318 350 if repo.revs('%n and parents()', curhead):
319 351 # we are on the active bookmark
320 352 bmheads = [b for b in bmheads if curhead != b]
321 353 if bmheads:
322 354 msg = _('%i other divergent bookmarks for "%s"\n')
323 355 ui.status(msg % (len(bmheads), repo._activebookmark))
324 356
325 357 def _statusotherbranchheads(ui, repo):
326 358 currentbranch = repo.dirstate.branch()
327 359 heads = repo.branchheads(currentbranch)
328 360 l = len(heads)
329 361 if repo.revs('%ln and parents()', heads):
330 362 # we are on a head
331 363 heads = repo.revs('%ln - parents()', heads)
332 364 if heads and l != len(heads):
333 365 ui.status(_('%i other heads for branch "%s"\n') %
334 366 (len(heads), currentbranch))
335 367
336 368 def statusotherdests(ui, repo):
337 369 """Print message about other head"""
338 370 # XXX we should probably include a hint:
339 371 # - about what to do
340 372 # - how to see such heads
341 373 if repo._activebookmark:
342 374 _statusotherbook(ui, repo)
343 375 else:
344 376 _statusotherbranchheads(ui, repo)
@@ -1,1101 +1,1101
1 1 This file contains testcases that tend to be related to special cases or less
2 2 common commands affecting largefile.
3 3
4 4 Each sections should be independent of each others.
5 5
6 6 $ USERCACHE="$TESTTMP/cache"; export USERCACHE
7 7 $ mkdir "${USERCACHE}"
8 8 $ cat >> $HGRCPATH <<EOF
9 9 > [extensions]
10 10 > largefiles=
11 11 > purge=
12 12 > rebase=
13 13 > transplant=
14 14 > [phases]
15 15 > publish=False
16 16 > [largefiles]
17 17 > minsize=2
18 18 > patterns=glob:**.dat
19 19 > usercache=${USERCACHE}
20 20 > [hooks]
21 21 > precommit=sh -c "echo \\"Invoking status precommit hook\\"; hg status"
22 22 > EOF
23 23
24 24
25 25
26 26 Test copies and moves from a directory other than root (issue3516)
27 27 =========================================================================
28 28
29 29 $ hg init lf_cpmv
30 30 $ cd lf_cpmv
31 31 $ mkdir dira
32 32 $ mkdir dira/dirb
33 33 $ touch dira/dirb/largefile
34 34 $ hg add --large dira/dirb/largefile
35 35 $ hg commit -m "added"
36 36 Invoking status precommit hook
37 37 A dira/dirb/largefile
38 38 $ cd dira
39 39 $ hg cp dirb/largefile foo/largefile
40 40
41 41 TODO: Ideally, this should mention the largefile, not the standin
42 42 $ hg log -T '{rev}\n' --stat 'set:clean()'
43 43 0
44 44 .hglf/dira/dirb/largefile | 1 +
45 45 1 files changed, 1 insertions(+), 0 deletions(-)
46 46
47 47 $ hg ci -m "deep copy"
48 48 Invoking status precommit hook
49 49 A dira/foo/largefile
50 50 $ find . | sort
51 51 .
52 52 ./dirb
53 53 ./dirb/largefile
54 54 ./foo
55 55 ./foo/largefile
56 56 $ hg mv foo/largefile baz/largefile
57 57 $ hg ci -m "moved"
58 58 Invoking status precommit hook
59 59 A dira/baz/largefile
60 60 R dira/foo/largefile
61 61 $ find . | sort
62 62 .
63 63 ./baz
64 64 ./baz/largefile
65 65 ./dirb
66 66 ./dirb/largefile
67 67 $ cd ..
68 68 $ hg mv dira dirc
69 69 moving .hglf/dira/baz/largefile to .hglf/dirc/baz/largefile (glob)
70 70 moving .hglf/dira/dirb/largefile to .hglf/dirc/dirb/largefile (glob)
71 71 $ find * | sort
72 72 dirc
73 73 dirc/baz
74 74 dirc/baz/largefile
75 75 dirc/dirb
76 76 dirc/dirb/largefile
77 77
78 78 $ hg clone -q . ../fetch
79 79 $ hg --config extensions.fetch= fetch ../fetch
80 80 abort: uncommitted changes
81 81 [255]
82 82 $ hg up -qC
83 83 $ cd ..
84 84
85 85 Clone a local repository owned by another user
86 86 ===================================================
87 87
88 88 #if unix-permissions
89 89
90 90 We have to simulate that here by setting $HOME and removing write permissions
91 91 $ ORIGHOME="$HOME"
92 92 $ mkdir alice
93 93 $ HOME="`pwd`/alice"
94 94 $ cd alice
95 95 $ hg init pubrepo
96 96 $ cd pubrepo
97 97 $ dd if=/dev/zero bs=1k count=11k > a-large-file 2> /dev/null
98 98 $ hg add --large a-large-file
99 99 $ hg commit -m "Add a large file"
100 100 Invoking status precommit hook
101 101 A a-large-file
102 102 $ cd ..
103 103 $ chmod -R a-w pubrepo
104 104 $ cd ..
105 105 $ mkdir bob
106 106 $ HOME="`pwd`/bob"
107 107 $ cd bob
108 108 $ hg clone --pull ../alice/pubrepo pubrepo
109 109 requesting all changes
110 110 adding changesets
111 111 adding manifests
112 112 adding file changes
113 113 added 1 changesets with 1 changes to 1 files
114 114 updating to branch default
115 115 getting changed largefiles
116 116 1 largefiles updated, 0 removed
117 117 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
118 118 $ cd ..
119 119 $ chmod -R u+w alice/pubrepo
120 120 $ HOME="$ORIGHOME"
121 121
122 122 #endif
123 123
124 124
125 125 Symlink to a large largefile should behave the same as a symlink to a normal file
126 126 =====================================================================================
127 127
128 128 #if symlink
129 129
130 130 $ hg init largesymlink
131 131 $ cd largesymlink
132 132 $ dd if=/dev/zero bs=1k count=10k of=largefile 2>/dev/null
133 133 $ hg add --large largefile
134 134 $ hg commit -m "commit a large file"
135 135 Invoking status precommit hook
136 136 A largefile
137 137 $ ln -s largefile largelink
138 138 $ hg add largelink
139 139 $ hg commit -m "commit a large symlink"
140 140 Invoking status precommit hook
141 141 A largelink
142 142 $ rm -f largelink
143 143 $ hg up >/dev/null
144 144 $ test -f largelink
145 145 [1]
146 146 $ test -L largelink
147 147 [1]
148 148 $ rm -f largelink # make next part of the test independent of the previous
149 149 $ hg up -C >/dev/null
150 150 $ test -f largelink
151 151 $ test -L largelink
152 152 $ cd ..
153 153
154 154 #endif
155 155
156 156
157 157 test for pattern matching on 'hg status':
158 158 ==============================================
159 159
160 160
161 161 to boost performance, largefiles checks whether specified patterns are
162 162 related to largefiles in working directory (NOT to STANDIN) or not.
163 163
164 164 $ hg init statusmatch
165 165 $ cd statusmatch
166 166
167 167 $ mkdir -p a/b/c/d
168 168 $ echo normal > a/b/c/d/e.normal.txt
169 169 $ hg add a/b/c/d/e.normal.txt
170 170 $ echo large > a/b/c/d/e.large.txt
171 171 $ hg add --large a/b/c/d/e.large.txt
172 172 $ mkdir -p a/b/c/x
173 173 $ echo normal > a/b/c/x/y.normal.txt
174 174 $ hg add a/b/c/x/y.normal.txt
175 175 $ hg commit -m 'add files'
176 176 Invoking status precommit hook
177 177 A a/b/c/d/e.large.txt
178 178 A a/b/c/d/e.normal.txt
179 179 A a/b/c/x/y.normal.txt
180 180
181 181 (1) no pattern: no performance boost
182 182 $ hg status -A
183 183 C a/b/c/d/e.large.txt
184 184 C a/b/c/d/e.normal.txt
185 185 C a/b/c/x/y.normal.txt
186 186
187 187 (2) pattern not related to largefiles: performance boost
188 188 $ hg status -A a/b/c/x
189 189 C a/b/c/x/y.normal.txt
190 190
191 191 (3) pattern related to largefiles: no performance boost
192 192 $ hg status -A a/b/c/d
193 193 C a/b/c/d/e.large.txt
194 194 C a/b/c/d/e.normal.txt
195 195
196 196 (4) pattern related to STANDIN (not to largefiles): performance boost
197 197 $ hg status -A .hglf/a
198 198 C .hglf/a/b/c/d/e.large.txt
199 199
200 200 (5) mixed case: no performance boost
201 201 $ hg status -A a/b/c/x a/b/c/d
202 202 C a/b/c/d/e.large.txt
203 203 C a/b/c/d/e.normal.txt
204 204 C a/b/c/x/y.normal.txt
205 205
206 206 verify that largefiles doesn't break filesets
207 207
208 208 $ hg log --rev . --exclude "set:binary()"
209 209 changeset: 0:41bd42f10efa
210 210 tag: tip
211 211 user: test
212 212 date: Thu Jan 01 00:00:00 1970 +0000
213 213 summary: add files
214 214
215 215 verify that large files in subrepos handled properly
216 216 $ hg init subrepo
217 217 $ echo "subrepo = subrepo" > .hgsub
218 218 $ hg add .hgsub
219 219 $ hg ci -m "add subrepo"
220 220 Invoking status precommit hook
221 221 A .hgsub
222 222 ? .hgsubstate
223 223 $ echo "rev 1" > subrepo/large.txt
224 224 $ hg add --large subrepo/large.txt
225 225 $ hg sum
226 226 parent: 1:8ee150ea2e9c tip
227 227 add subrepo
228 228 branch: default
229 229 commit: 1 subrepos
230 230 update: (current)
231 231 phases: 2 draft
232 232 $ hg st
233 233 $ hg st -S
234 234 A subrepo/large.txt
235 235 $ hg ci -S -m "commit top repo"
236 236 committing subrepository subrepo
237 237 Invoking status precommit hook
238 238 A large.txt
239 239 Invoking status precommit hook
240 240 M .hgsubstate
241 241 # No differences
242 242 $ hg st -S
243 243 $ hg sum
244 244 parent: 2:ce4cd0c527a6 tip
245 245 commit top repo
246 246 branch: default
247 247 commit: (clean)
248 248 update: (current)
249 249 phases: 3 draft
250 250 $ echo "rev 2" > subrepo/large.txt
251 251 $ hg st -S
252 252 M subrepo/large.txt
253 253 $ hg sum
254 254 parent: 2:ce4cd0c527a6 tip
255 255 commit top repo
256 256 branch: default
257 257 commit: 1 subrepos
258 258 update: (current)
259 259 phases: 3 draft
260 260 $ hg ci -m "this commit should fail without -S"
261 261 abort: uncommitted changes in subrepository 'subrepo'
262 262 (use --subrepos for recursive commit)
263 263 [255]
264 264
265 265 Add a normal file to the subrepo, then test archiving
266 266
267 267 $ echo 'normal file' > subrepo/normal.txt
268 268 $ touch large.dat
269 269 $ mv subrepo/large.txt subrepo/renamed-large.txt
270 270 $ hg addremove -S --dry-run
271 271 adding large.dat as a largefile
272 272 removing subrepo/large.txt
273 273 adding subrepo/normal.txt
274 274 adding subrepo/renamed-large.txt
275 275 $ hg status -S
276 276 ! subrepo/large.txt
277 277 ? large.dat
278 278 ? subrepo/normal.txt
279 279 ? subrepo/renamed-large.txt
280 280
281 281 $ hg addremove --dry-run subrepo
282 282 removing subrepo/large.txt (glob)
283 283 adding subrepo/normal.txt (glob)
284 284 adding subrepo/renamed-large.txt (glob)
285 285 $ hg status -S
286 286 ! subrepo/large.txt
287 287 ? large.dat
288 288 ? subrepo/normal.txt
289 289 ? subrepo/renamed-large.txt
290 290 $ cd ..
291 291
292 292 $ hg -R statusmatch addremove --dry-run statusmatch/subrepo
293 293 removing statusmatch/subrepo/large.txt (glob)
294 294 adding statusmatch/subrepo/normal.txt (glob)
295 295 adding statusmatch/subrepo/renamed-large.txt (glob)
296 296 $ hg -R statusmatch status -S
297 297 ! subrepo/large.txt
298 298 ? large.dat
299 299 ? subrepo/normal.txt
300 300 ? subrepo/renamed-large.txt
301 301
302 302 $ hg -R statusmatch addremove --dry-run -S
303 303 adding large.dat as a largefile
304 304 removing subrepo/large.txt
305 305 adding subrepo/normal.txt
306 306 adding subrepo/renamed-large.txt
307 307 $ cd statusmatch
308 308
309 309 $ mv subrepo/renamed-large.txt subrepo/large.txt
310 310 $ hg addremove subrepo
311 311 adding subrepo/normal.txt (glob)
312 312 $ hg forget subrepo/normal.txt
313 313
314 314 $ hg addremove -S
315 315 adding large.dat as a largefile
316 316 adding subrepo/normal.txt
317 317 $ rm large.dat
318 318
319 319 $ hg addremove subrepo
320 320 $ hg addremove -S
321 321 removing large.dat
322 322
323 323 Lock in subrepo, otherwise the change isn't archived
324 324
325 325 $ hg ci -S -m "add normal file to top level"
326 326 committing subrepository subrepo
327 327 Invoking status precommit hook
328 328 M large.txt
329 329 A normal.txt
330 330 Invoking status precommit hook
331 331 M .hgsubstate
332 332 $ hg archive -S ../lf_subrepo_archive
333 333 $ find ../lf_subrepo_archive | sort
334 334 ../lf_subrepo_archive
335 335 ../lf_subrepo_archive/.hg_archival.txt
336 336 ../lf_subrepo_archive/.hgsub
337 337 ../lf_subrepo_archive/.hgsubstate
338 338 ../lf_subrepo_archive/a
339 339 ../lf_subrepo_archive/a/b
340 340 ../lf_subrepo_archive/a/b/c
341 341 ../lf_subrepo_archive/a/b/c/d
342 342 ../lf_subrepo_archive/a/b/c/d/e.large.txt
343 343 ../lf_subrepo_archive/a/b/c/d/e.normal.txt
344 344 ../lf_subrepo_archive/a/b/c/x
345 345 ../lf_subrepo_archive/a/b/c/x/y.normal.txt
346 346 ../lf_subrepo_archive/subrepo
347 347 ../lf_subrepo_archive/subrepo/large.txt
348 348 ../lf_subrepo_archive/subrepo/normal.txt
349 349 $ cat ../lf_subrepo_archive/.hg_archival.txt
350 350 repo: 41bd42f10efa43698cc02052ea0977771cba506d
351 351 node: d56a95e6522858bc08a724c4fe2bdee066d1c30b
352 352 branch: default
353 353 latesttag: null
354 354 latesttagdistance: 4
355 355 changessincelatesttag: 4
356 356
357 357 Test update with subrepos.
358 358
359 359 $ hg update 0
360 360 getting changed largefiles
361 361 0 largefiles updated, 1 removed
362 362 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
363 363 $ hg status -S
364 364 $ hg update tip
365 365 getting changed largefiles
366 366 1 largefiles updated, 0 removed
367 367 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
368 368 $ hg status -S
369 369 # modify a large file
370 370 $ echo "modified" > subrepo/large.txt
371 371 $ hg st -S
372 372 M subrepo/large.txt
373 373 # update -C should revert the change.
374 374 $ hg update -C
375 375 getting changed largefiles
376 376 1 largefiles updated, 0 removed
377 377 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
378 378 $ hg status -S
379 379
380 380 $ hg forget -v subrepo/large.txt
381 381 removing subrepo/large.txt (glob)
382 382
383 383 Test reverting a forgotten file
384 384 $ hg revert -R subrepo subrepo/large.txt
385 385 $ hg status -SA subrepo/large.txt
386 386 C subrepo/large.txt
387 387
388 388 $ hg rm -v subrepo/large.txt
389 389 removing subrepo/large.txt (glob)
390 390 $ hg revert -R subrepo subrepo/large.txt
391 391 $ rm subrepo/large.txt
392 392 $ hg addremove -S
393 393 removing subrepo/large.txt
394 394 $ hg st -S
395 395 R subrepo/large.txt
396 396
397 397 Test archiving a revision that references a subrepo that is not yet
398 398 cloned (see test-subrepo-recursion.t):
399 399
400 400 $ hg clone -U . ../empty
401 401 $ cd ../empty
402 402 $ hg archive --subrepos -r tip ../archive.tar.gz
403 403 cloning subrepo subrepo from $TESTTMP/statusmatch/subrepo
404 404 $ cd ..
405 405
406 406
407 407
408 408
409 409
410 410
411 411 Test addremove, forget and others
412 412 ==============================================
413 413
414 414 Test that addremove picks up largefiles prior to the initial commit (issue3541)
415 415
416 416 $ hg init addrm2
417 417 $ cd addrm2
418 418 $ touch large.dat
419 419 $ touch large2.dat
420 420 $ touch normal
421 421 $ hg add --large large.dat
422 422 $ hg addremove -v
423 423 adding large2.dat as a largefile
424 424 adding normal
425 425
426 426 Test that forgetting all largefiles reverts to islfilesrepo() == False
427 427 (addremove will add *.dat as normal files now)
428 428 $ hg forget large.dat
429 429 $ hg forget large2.dat
430 430 $ hg addremove -v
431 431 adding large.dat
432 432 adding large2.dat
433 433
434 434 Test commit's addremove option prior to the first commit
435 435 $ hg forget large.dat
436 436 $ hg forget large2.dat
437 437 $ hg add --large large.dat
438 438 $ hg ci -Am "commit"
439 439 adding large2.dat as a largefile
440 440 Invoking status precommit hook
441 441 A large.dat
442 442 A large2.dat
443 443 A normal
444 444 $ find .hglf | sort
445 445 .hglf
446 446 .hglf/large.dat
447 447 .hglf/large2.dat
448 448
449 449 Test actions on largefiles using relative paths from subdir
450 450
451 451 $ mkdir sub
452 452 $ cd sub
453 453 $ echo anotherlarge > anotherlarge
454 454 $ hg add --large anotherlarge
455 455 $ hg st
456 456 A sub/anotherlarge
457 457 $ hg st anotherlarge
458 458 A anotherlarge
459 459 $ hg commit -m anotherlarge anotherlarge
460 460 Invoking status precommit hook
461 461 A sub/anotherlarge
462 462 $ hg log anotherlarge
463 463 changeset: 1:9627a577c5e9
464 464 tag: tip
465 465 user: test
466 466 date: Thu Jan 01 00:00:00 1970 +0000
467 467 summary: anotherlarge
468 468
469 469 $ hg --debug log -T '{rev}: {desc}\n' ../sub/anotherlarge
470 470 updated patterns: ['../.hglf/sub/../sub/anotherlarge', '../sub/anotherlarge']
471 471 1: anotherlarge
472 472
473 473 $ hg log -G anotherlarge
474 474 @ changeset: 1:9627a577c5e9
475 475 | tag: tip
476 476 | user: test
477 477 | date: Thu Jan 01 00:00:00 1970 +0000
478 478 | summary: anotherlarge
479 479 |
480 480
481 481 $ hg log glob:another*
482 482 changeset: 1:9627a577c5e9
483 483 tag: tip
484 484 user: test
485 485 date: Thu Jan 01 00:00:00 1970 +0000
486 486 summary: anotherlarge
487 487
488 488 $ hg --debug log -T '{rev}: {desc}\n' -G glob:another*
489 489 updated patterns: ['glob:../.hglf/sub/another*', 'glob:another*']
490 490 @ 1: anotherlarge
491 491 |
492 492
493 493 #if no-msys
494 494 $ hg --debug log -T '{rev}: {desc}\n' 'glob:../.hglf/sub/another*' # no-msys
495 495 updated patterns: ['glob:../.hglf/sub/another*']
496 496 1: anotherlarge
497 497
498 498 $ hg --debug log -G -T '{rev}: {desc}\n' 'glob:../.hglf/sub/another*' # no-msys
499 499 updated patterns: ['glob:../.hglf/sub/another*']
500 500 @ 1: anotherlarge
501 501 |
502 502 #endif
503 503
504 504 $ echo more >> anotherlarge
505 505 $ hg st .
506 506 M anotherlarge
507 507 $ hg cat anotherlarge
508 508 anotherlarge
509 509 $ hg revert anotherlarge
510 510 $ hg st
511 511 ? sub/anotherlarge.orig
512 512
513 513 Test orig files go where we want them
514 514 $ echo moremore >> anotherlarge
515 515 $ hg revert anotherlarge -v --config 'ui.origbackuppath=.hg/origbackups'
516 516 creating directory: $TESTTMP/addrm2/.hg/origbackups/.hglf/sub (glob)
517 517 saving current version of ../.hglf/sub/anotherlarge as $TESTTMP/addrm2/.hg/origbackups/.hglf/sub/anotherlarge.orig (glob)
518 518 reverting ../.hglf/sub/anotherlarge (glob)
519 519 creating directory: $TESTTMP/addrm2/.hg/origbackups/sub (glob)
520 520 found 90c622cf65cebe75c5842f9136c459333faf392e in store
521 521 found 90c622cf65cebe75c5842f9136c459333faf392e in store
522 522 $ ls ../.hg/origbackups/sub
523 523 anotherlarge.orig
524 524 $ cd ..
525 525
526 526 Test glob logging from the root dir
527 527 $ hg log glob:**another*
528 528 changeset: 1:9627a577c5e9
529 529 tag: tip
530 530 user: test
531 531 date: Thu Jan 01 00:00:00 1970 +0000
532 532 summary: anotherlarge
533 533
534 534 $ hg log -G glob:**another*
535 535 @ changeset: 1:9627a577c5e9
536 536 | tag: tip
537 537 | user: test
538 538 | date: Thu Jan 01 00:00:00 1970 +0000
539 539 | summary: anotherlarge
540 540 |
541 541
542 542 $ cd ..
543 543
544 544 Log from outer space
545 545 $ hg --debug log -R addrm2 -T '{rev}: {desc}\n' 'addrm2/sub/anotherlarge'
546 546 updated patterns: ['addrm2/.hglf/sub/anotherlarge', 'addrm2/sub/anotherlarge']
547 547 1: anotherlarge
548 548 $ hg --debug log -R addrm2 -T '{rev}: {desc}\n' 'addrm2/.hglf/sub/anotherlarge'
549 549 updated patterns: ['addrm2/.hglf/sub/anotherlarge']
550 550 1: anotherlarge
551 551
552 552
553 553 Check error message while exchange
554 554 =========================================================
555 555
556 556 issue3651: summary/outgoing with largefiles shows "no remote repo"
557 557 unexpectedly
558 558
559 559 $ mkdir issue3651
560 560 $ cd issue3651
561 561
562 562 $ hg init src
563 563 $ echo a > src/a
564 564 $ hg -R src add --large src/a
565 565 $ hg -R src commit -m '#0'
566 566 Invoking status precommit hook
567 567 A a
568 568
569 569 check messages when no remote repository is specified:
570 570 "no remote repo" route for "hg outgoing --large" is not tested here,
571 571 because it can't be reproduced easily.
572 572
573 573 $ hg init clone1
574 574 $ hg -R clone1 -q pull src
575 575 $ hg -R clone1 -q update
576 576 $ hg -R clone1 paths | grep default
577 577 [1]
578 578
579 579 $ hg -R clone1 summary --large
580 580 parent: 0:fc0bd45326d3 tip
581 581 #0
582 582 branch: default
583 583 commit: (clean)
584 584 update: (current)
585 585 phases: 1 draft
586 586 largefiles: (no remote repo)
587 587
588 588 check messages when there is no files to upload:
589 589
590 590 $ hg -q clone src clone2
591 591 $ hg -R clone2 paths | grep default
592 592 default = $TESTTMP/issue3651/src (glob)
593 593
594 594 $ hg -R clone2 summary --large
595 595 parent: 0:fc0bd45326d3 tip
596 596 #0
597 597 branch: default
598 598 commit: (clean)
599 599 update: (current)
600 600 phases: 1 draft
601 601 largefiles: (no files to upload)
602 602 $ hg -R clone2 outgoing --large
603 603 comparing with $TESTTMP/issue3651/src (glob)
604 604 searching for changes
605 605 no changes found
606 606 largefiles: no files to upload
607 607 [1]
608 608
609 609 $ hg -R clone2 outgoing --large --graph --template "{rev}"
610 610 comparing with $TESTTMP/issue3651/src (glob)
611 611 searching for changes
612 612 no changes found
613 613 largefiles: no files to upload
614 614
615 615 check messages when there are files to upload:
616 616
617 617 $ echo b > clone2/b
618 618 $ hg -R clone2 add --large clone2/b
619 619 $ hg -R clone2 commit -m '#1'
620 620 Invoking status precommit hook
621 621 A b
622 622 $ hg -R clone2 summary --large
623 623 parent: 1:1acbe71ce432 tip
624 624 #1
625 625 branch: default
626 626 commit: (clean)
627 627 update: (current)
628 628 phases: 2 draft
629 629 largefiles: 1 entities for 1 files to upload
630 630 $ hg -R clone2 outgoing --large
631 631 comparing with $TESTTMP/issue3651/src (glob)
632 632 searching for changes
633 633 changeset: 1:1acbe71ce432
634 634 tag: tip
635 635 user: test
636 636 date: Thu Jan 01 00:00:00 1970 +0000
637 637 summary: #1
638 638
639 639 largefiles to upload (1 entities):
640 640 b
641 641
642 642 $ hg -R clone2 outgoing --large --graph --template "{rev}"
643 643 comparing with $TESTTMP/issue3651/src (glob)
644 644 searching for changes
645 645 @ 1
646 646
647 647 largefiles to upload (1 entities):
648 648 b
649 649
650 650
651 651 $ cp clone2/b clone2/b1
652 652 $ cp clone2/b clone2/b2
653 653 $ hg -R clone2 add --large clone2/b1 clone2/b2
654 654 $ hg -R clone2 commit -m '#2: add largefiles referring same entity'
655 655 Invoking status precommit hook
656 656 A b1
657 657 A b2
658 658 $ hg -R clone2 summary --large
659 659 parent: 2:6095d0695d70 tip
660 660 #2: add largefiles referring same entity
661 661 branch: default
662 662 commit: (clean)
663 663 update: (current)
664 664 phases: 3 draft
665 665 largefiles: 1 entities for 3 files to upload
666 666 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
667 667 comparing with $TESTTMP/issue3651/src (glob)
668 668 searching for changes
669 669 1:1acbe71ce432
670 670 2:6095d0695d70
671 671 largefiles to upload (1 entities):
672 672 b
673 673 b1
674 674 b2
675 675
676 676 $ hg -R clone2 cat -r 1 clone2/.hglf/b
677 677 89e6c98d92887913cadf06b2adb97f26cde4849b
678 678 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug --config progress.debug=true
679 679 comparing with $TESTTMP/issue3651/src (glob)
680 680 query 1; heads
681 681 searching for changes
682 682 all remote heads known locally
683 683 1:1acbe71ce432
684 684 2:6095d0695d70
685 685 finding outgoing largefiles: 0/2 revision (0.00%)
686 686 finding outgoing largefiles: 1/2 revision (50.00%)
687 687 largefiles to upload (1 entities):
688 688 b
689 689 89e6c98d92887913cadf06b2adb97f26cde4849b
690 690 b1
691 691 89e6c98d92887913cadf06b2adb97f26cde4849b
692 692 b2
693 693 89e6c98d92887913cadf06b2adb97f26cde4849b
694 694
695 695
696 696 $ echo bbb > clone2/b
697 697 $ hg -R clone2 commit -m '#3: add new largefile entity as existing file'
698 698 Invoking status precommit hook
699 699 M b
700 700 $ echo bbbb > clone2/b
701 701 $ hg -R clone2 commit -m '#4: add new largefile entity as existing file'
702 702 Invoking status precommit hook
703 703 M b
704 704 $ cp clone2/b1 clone2/b
705 705 $ hg -R clone2 commit -m '#5: refer existing largefile entity again'
706 706 Invoking status precommit hook
707 707 M b
708 708 $ hg -R clone2 summary --large
709 709 parent: 5:036794ea641c tip
710 710 #5: refer existing largefile entity again
711 711 branch: default
712 712 commit: (clean)
713 713 update: (current)
714 714 phases: 6 draft
715 715 largefiles: 3 entities for 3 files to upload
716 716 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
717 717 comparing with $TESTTMP/issue3651/src (glob)
718 718 searching for changes
719 719 1:1acbe71ce432
720 720 2:6095d0695d70
721 721 3:7983dce246cc
722 722 4:233f12ada4ae
723 723 5:036794ea641c
724 724 largefiles to upload (3 entities):
725 725 b
726 726 b1
727 727 b2
728 728
729 729 $ hg -R clone2 cat -r 3 clone2/.hglf/b
730 730 c801c9cfe94400963fcb683246217d5db77f9a9a
731 731 $ hg -R clone2 cat -r 4 clone2/.hglf/b
732 732 13f9ed0898e315bf59dc2973fec52037b6f441a2
733 733 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug --config progress.debug=true
734 734 comparing with $TESTTMP/issue3651/src (glob)
735 735 query 1; heads
736 736 searching for changes
737 737 all remote heads known locally
738 738 1:1acbe71ce432
739 739 2:6095d0695d70
740 740 3:7983dce246cc
741 741 4:233f12ada4ae
742 742 5:036794ea641c
743 743 finding outgoing largefiles: 0/5 revision (0.00%)
744 744 finding outgoing largefiles: 1/5 revision (20.00%)
745 745 finding outgoing largefiles: 2/5 revision (40.00%)
746 746 finding outgoing largefiles: 3/5 revision (60.00%)
747 747 finding outgoing largefiles: 4/5 revision (80.00%)
748 748 largefiles to upload (3 entities):
749 749 b
750 750 13f9ed0898e315bf59dc2973fec52037b6f441a2
751 751 89e6c98d92887913cadf06b2adb97f26cde4849b
752 752 c801c9cfe94400963fcb683246217d5db77f9a9a
753 753 b1
754 754 89e6c98d92887913cadf06b2adb97f26cde4849b
755 755 b2
756 756 89e6c98d92887913cadf06b2adb97f26cde4849b
757 757
758 758
759 759 Pushing revision #1 causes uploading entity 89e6c98d9288, which is
760 760 shared also by largefiles b1, b2 in revision #2 and b in revision #5.
761 761
762 762 Then, entity 89e6c98d9288 is not treated as "outgoing entity" at "hg
763 763 summary" and "hg outgoing", even though files in outgoing revision #2
764 764 and #5 refer it.
765 765
766 766 $ hg -R clone2 push -r 1 -q
767 767 $ hg -R clone2 summary --large
768 768 parent: 5:036794ea641c tip
769 769 #5: refer existing largefile entity again
770 770 branch: default
771 771 commit: (clean)
772 772 update: (current)
773 773 phases: 6 draft
774 774 largefiles: 2 entities for 1 files to upload
775 775 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
776 776 comparing with $TESTTMP/issue3651/src (glob)
777 777 searching for changes
778 778 2:6095d0695d70
779 779 3:7983dce246cc
780 780 4:233f12ada4ae
781 781 5:036794ea641c
782 782 largefiles to upload (2 entities):
783 783 b
784 784
785 785 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug --config progress.debug=true
786 786 comparing with $TESTTMP/issue3651/src (glob)
787 787 query 1; heads
788 788 searching for changes
789 789 all remote heads known locally
790 790 2:6095d0695d70
791 791 3:7983dce246cc
792 792 4:233f12ada4ae
793 793 5:036794ea641c
794 794 finding outgoing largefiles: 0/4 revision (0.00%)
795 795 finding outgoing largefiles: 1/4 revision (25.00%)
796 796 finding outgoing largefiles: 2/4 revision (50.00%)
797 797 finding outgoing largefiles: 3/4 revision (75.00%)
798 798 largefiles to upload (2 entities):
799 799 b
800 800 13f9ed0898e315bf59dc2973fec52037b6f441a2
801 801 c801c9cfe94400963fcb683246217d5db77f9a9a
802 802
803 803
804 804 $ cd ..
805 805
806 806 merge action 'd' for 'local renamed directory to d2/g' which has no filename
807 807 ==================================================================================
808 808
809 809 $ hg init merge-action
810 810 $ cd merge-action
811 811 $ touch l
812 812 $ hg add --large l
813 813 $ mkdir d1
814 814 $ touch d1/f
815 815 $ hg ci -Aqm0
816 816 Invoking status precommit hook
817 817 A d1/f
818 818 A l
819 819 $ echo > d1/f
820 820 $ touch d1/g
821 821 $ hg ci -Aqm1
822 822 Invoking status precommit hook
823 823 M d1/f
824 824 A d1/g
825 825 $ hg up -qr0
826 826 $ hg mv d1 d2
827 827 moving d1/f to d2/f (glob)
828 828 $ hg ci -qm2
829 829 Invoking status precommit hook
830 830 A d2/f
831 831 R d1/f
832 832 $ hg merge
833 833 merging d2/f and d1/f to d2/f
834 834 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
835 835 (branch merge, don't forget to commit)
836 836 $ cd ..
837 837
838 838
839 839 Merge conflicts:
840 840 =====================
841 841
842 842 $ hg init merge
843 843 $ cd merge
844 844 $ echo 0 > f-different
845 845 $ echo 0 > f-same
846 846 $ echo 0 > f-unchanged-1
847 847 $ echo 0 > f-unchanged-2
848 848 $ hg add --large *
849 849 $ hg ci -m0
850 850 Invoking status precommit hook
851 851 A f-different
852 852 A f-same
853 853 A f-unchanged-1
854 854 A f-unchanged-2
855 855 $ echo tmp1 > f-unchanged-1
856 856 $ echo tmp1 > f-unchanged-2
857 857 $ echo tmp1 > f-same
858 858 $ hg ci -m1
859 859 Invoking status precommit hook
860 860 M f-same
861 861 M f-unchanged-1
862 862 M f-unchanged-2
863 863 $ echo 2 > f-different
864 864 $ echo 0 > f-unchanged-1
865 865 $ echo 1 > f-unchanged-2
866 866 $ echo 1 > f-same
867 867 $ hg ci -m2
868 868 Invoking status precommit hook
869 869 M f-different
870 870 M f-same
871 871 M f-unchanged-1
872 872 M f-unchanged-2
873 873 $ hg up -qr0
874 874 $ echo tmp2 > f-unchanged-1
875 875 $ echo tmp2 > f-unchanged-2
876 876 $ echo tmp2 > f-same
877 877 $ hg ci -m3
878 878 Invoking status precommit hook
879 879 M f-same
880 880 M f-unchanged-1
881 881 M f-unchanged-2
882 882 created new head
883 883 $ echo 1 > f-different
884 884 $ echo 1 > f-unchanged-1
885 885 $ echo 0 > f-unchanged-2
886 886 $ echo 1 > f-same
887 887 $ hg ci -m4
888 888 Invoking status precommit hook
889 889 M f-different
890 890 M f-same
891 891 M f-unchanged-1
892 892 M f-unchanged-2
893 893 $ hg merge
894 894 largefile f-different has a merge conflict
895 895 ancestor was 09d2af8dd22201dd8d48e5dcfcaed281ff9422c7
896 896 keep (l)ocal e5fa44f2b31c1fb553b6021e7360d07d5d91ff5e or
897 897 take (o)ther 7448d8798a4380162d4b56f9b452e2f6f9e24e7a? l
898 898 getting changed largefiles
899 899 1 largefiles updated, 0 removed
900 900 0 files updated, 4 files merged, 0 files removed, 0 files unresolved
901 901 (branch merge, don't forget to commit)
902 902 $ cat f-different
903 903 1
904 904 $ cat f-same
905 905 1
906 906 $ cat f-unchanged-1
907 907 1
908 908 $ cat f-unchanged-2
909 909 1
910 910 $ cd ..
911 911
912 912 Test largefile insulation (do not enabled a side effect
913 913 ========================================================
914 914
915 915 Check whether "largefiles" feature is supported only in repositories
916 916 enabling largefiles extension.
917 917
918 918 $ mkdir individualenabling
919 919 $ cd individualenabling
920 920
921 921 $ hg init enabledlocally
922 922 $ echo large > enabledlocally/large
923 923 $ hg -R enabledlocally add --large enabledlocally/large
924 924 $ hg -R enabledlocally commit -m '#0'
925 925 Invoking status precommit hook
926 926 A large
927 927
928 928 $ hg init notenabledlocally
929 929 $ echo large > notenabledlocally/large
930 930 $ hg -R notenabledlocally add --large notenabledlocally/large
931 931 $ hg -R notenabledlocally commit -m '#0'
932 932 Invoking status precommit hook
933 933 A large
934 934
935 935 $ cat >> $HGRCPATH <<EOF
936 936 > [extensions]
937 937 > # disable globally
938 938 > largefiles=!
939 939 > EOF
940 940 $ cat >> enabledlocally/.hg/hgrc <<EOF
941 941 > [extensions]
942 942 > # enable locally
943 943 > largefiles=
944 944 > EOF
945 945 $ hg -R enabledlocally root
946 946 $TESTTMP/individualenabling/enabledlocally (glob)
947 947 $ hg -R notenabledlocally root
948 948 abort: repository requires features unknown to this Mercurial: largefiles!
949 949 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
950 950 [255]
951 951
952 952 $ hg init push-dst
953 953 $ hg -R enabledlocally push push-dst
954 954 pushing to push-dst
955 955 abort: required features are not supported in the destination: largefiles
956 956 [255]
957 957
958 958 $ hg init pull-src
959 959 $ hg -R pull-src pull enabledlocally
960 960 pulling from enabledlocally
961 961 abort: required features are not supported in the destination: largefiles
962 962 [255]
963 963
964 964 $ hg clone enabledlocally clone-dst
965 965 abort: repository requires features unknown to this Mercurial: largefiles!
966 966 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
967 967 [255]
968 968 $ test -d clone-dst
969 969 [1]
970 970 $ hg clone --pull enabledlocally clone-pull-dst
971 971 abort: required features are not supported in the destination: largefiles
972 972 [255]
973 973 $ test -d clone-pull-dst
974 974 [1]
975 975
976 976 #if serve
977 977
978 978 Test largefiles specific peer setup, when largefiles is enabled
979 979 locally (issue4109)
980 980
981 981 $ hg showconfig extensions | grep largefiles
982 982 extensions.largefiles=!
983 983 $ mkdir -p $TESTTMP/individualenabling/usercache
984 984
985 985 $ hg serve -R enabledlocally -d -p $HGPORT --pid-file hg.pid
986 986 $ cat hg.pid >> $DAEMON_PIDS
987 987
988 988 $ hg init pull-dst
989 989 $ cat > pull-dst/.hg/hgrc <<EOF
990 990 > [extensions]
991 991 > # enable locally
992 992 > largefiles=
993 993 > [largefiles]
994 994 > # ignore system cache to force largefiles specific wire proto access
995 995 > usercache=$TESTTMP/individualenabling/usercache
996 996 > EOF
997 997 $ hg -R pull-dst -q pull -u http://localhost:$HGPORT
998 998
999 999 $ killdaemons.py
1000 1000 #endif
1001 1001
1002 1002 Test overridden functions work correctly even for repos disabling
1003 1003 largefiles (issue4547)
1004 1004
1005 1005 $ hg showconfig extensions | grep largefiles
1006 1006 extensions.largefiles=!
1007 1007
1008 1008 (test updating implied by clone)
1009 1009
1010 1010 $ hg init enabled-but-no-largefiles
1011 1011 $ echo normal1 > enabled-but-no-largefiles/normal1
1012 1012 $ hg -R enabled-but-no-largefiles add enabled-but-no-largefiles/normal1
1013 1013 $ hg -R enabled-but-no-largefiles commit -m '#0@enabled-but-no-largefiles'
1014 1014 Invoking status precommit hook
1015 1015 A normal1
1016 1016 $ cat >> enabled-but-no-largefiles/.hg/hgrc <<EOF
1017 1017 > [extensions]
1018 1018 > # enable locally
1019 1019 > largefiles=
1020 1020 > EOF
1021 1021 $ hg clone -q enabled-but-no-largefiles no-largefiles
1022 1022
1023 1023 $ echo normal2 > enabled-but-no-largefiles/normal2
1024 1024 $ hg -R enabled-but-no-largefiles add enabled-but-no-largefiles/normal2
1025 1025 $ hg -R enabled-but-no-largefiles commit -m '#1@enabled-but-no-largefiles'
1026 1026 Invoking status precommit hook
1027 1027 A normal2
1028 1028
1029 1029 $ echo normal3 > no-largefiles/normal3
1030 1030 $ hg -R no-largefiles add no-largefiles/normal3
1031 1031 $ hg -R no-largefiles commit -m '#1@no-largefiles'
1032 1032 Invoking status precommit hook
1033 1033 A normal3
1034 1034
1035 1035 $ hg -R no-largefiles -q pull --rebase
1036 1036 Invoking status precommit hook
1037 1037 A normal3
1038 1038
1039 1039 (test reverting)
1040 1040
1041 1041 $ hg init subrepo-root
1042 1042 $ cat >> subrepo-root/.hg/hgrc <<EOF
1043 1043 > [extensions]
1044 1044 > # enable locally
1045 1045 > largefiles=
1046 1046 > EOF
1047 1047 $ echo large > subrepo-root/large
1048 1048 $ hg -R subrepo-root add --large subrepo-root/large
1049 1049 $ hg clone -q no-largefiles subrepo-root/no-largefiles
1050 1050 $ cat > subrepo-root/.hgsub <<EOF
1051 1051 > no-largefiles = no-largefiles
1052 1052 > EOF
1053 1053 $ hg -R subrepo-root add subrepo-root/.hgsub
1054 1054 $ hg -R subrepo-root commit -m '#0'
1055 1055 Invoking status precommit hook
1056 1056 A .hgsub
1057 1057 A large
1058 1058 ? .hgsubstate
1059 1059 $ echo dirty >> subrepo-root/large
1060 1060 $ echo dirty >> subrepo-root/no-largefiles/normal1
1061 1061 $ hg -R subrepo-root status -S
1062 1062 M large
1063 1063 M no-largefiles/normal1
1064 1064 $ hg -R subrepo-root revert --all
1065 1065 reverting subrepo-root/.hglf/large (glob)
1066 1066 reverting subrepo no-largefiles
1067 1067 reverting subrepo-root/no-largefiles/normal1 (glob)
1068 1068
1069 1069 $ cd ..
1070 1070
1071 1071
1072 1072 Test "pull --rebase" when rebase is enabled before largefiles (issue3861)
1073 1073 =========================================================================
1074 1074
1075 1075 $ hg showconfig extensions | grep largefiles
1076 1076 extensions.largefiles=!
1077 1077
1078 1078 $ mkdir issue3861
1079 1079 $ cd issue3861
1080 1080 $ hg init src
1081 1081 $ hg clone -q src dst
1082 1082 $ echo a > src/a
1083 1083 $ hg -R src commit -Aqm "#0"
1084 1084 Invoking status precommit hook
1085 1085 A a
1086 1086
1087 1087 $ cat >> dst/.hg/hgrc <<EOF
1088 1088 > [extensions]
1089 1089 > largefiles=
1090 1090 > EOF
1091 1091 $ hg -R dst pull --rebase
1092 1092 pulling from $TESTTMP/issue3861/src (glob)
1093 1093 requesting all changes
1094 1094 adding changesets
1095 1095 adding manifests
1096 1096 adding file changes
1097 1097 added 1 changesets with 1 changes to 1 files
1098 nothing to rebase - working directory parent is already an ancestor of destination bf5e395ced2c
1098 nothing to rebase - updating instead
1099 1099 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1100 1100
1101 1101 $ cd ..
@@ -1,390 +1,390
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [extensions]
3 3 > rebase=
4 4 >
5 5 > [phases]
6 6 > publish=False
7 7 >
8 8 > [alias]
9 9 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
10 10 > EOF
11 11
12 12 $ hg init a
13 13 $ cd a
14 14 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
15 15 adding changesets
16 16 adding manifests
17 17 adding file changes
18 18 added 8 changesets with 7 changes to 7 files (+2 heads)
19 19 (run 'hg heads' to see heads, 'hg merge' to merge)
20 20 $ hg up tip
21 21 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
22 22 $ cd ..
23 23
24 24 $ hg clone -q -u . a a1
25 25
26 26 $ cd a1
27 27
28 28 $ hg update 3
29 29 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
30 30 $ hg branch dev-one
31 31 marked working directory as branch dev-one
32 32 (branches are permanent and global, did you want a bookmark?)
33 33 $ hg ci -m 'dev-one named branch'
34 34
35 35 $ hg update 7
36 36 2 files updated, 0 files merged, 3 files removed, 0 files unresolved
37 37 $ hg branch dev-two
38 38 marked working directory as branch dev-two
39 39
40 40 $ echo x > x
41 41
42 42 $ hg add x
43 43
44 44 $ hg ci -m 'dev-two named branch'
45 45
46 46 $ hg tglog
47 47 @ 9: 'dev-two named branch' dev-two
48 48 |
49 49 | o 8: 'dev-one named branch' dev-one
50 50 | |
51 51 o | 7: 'H'
52 52 | |
53 53 +---o 6: 'G'
54 54 | | |
55 55 o | | 5: 'F'
56 56 | | |
57 57 +---o 4: 'E'
58 58 | |
59 59 | o 3: 'D'
60 60 | |
61 61 | o 2: 'C'
62 62 | |
63 63 | o 1: 'B'
64 64 |/
65 65 o 0: 'A'
66 66
67 67
68 68 Branch name containing a dash (issue3181)
69 69
70 70 $ hg rebase -b dev-two -d dev-one --keepbranches
71 71 rebasing 5:24b6387c8c8c "F"
72 72 rebasing 6:eea13746799a "G"
73 73 rebasing 7:02de42196ebe "H"
74 74 rebasing 9:cb039b7cae8e "dev-two named branch" (tip)
75 75 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/24b6387c8c8c-24cb8001-backup.hg (glob)
76 76
77 77 $ hg tglog
78 78 @ 9: 'dev-two named branch' dev-two
79 79 |
80 80 o 8: 'H'
81 81 |
82 82 | o 7: 'G'
83 83 |/|
84 84 o | 6: 'F'
85 85 | |
86 86 o | 5: 'dev-one named branch' dev-one
87 87 | |
88 88 | o 4: 'E'
89 89 | |
90 90 o | 3: 'D'
91 91 | |
92 92 o | 2: 'C'
93 93 | |
94 94 o | 1: 'B'
95 95 |/
96 96 o 0: 'A'
97 97
98 98 $ hg rebase -s dev-one -d 0 --keepbranches
99 99 rebasing 5:643fc9128048 "dev-one named branch"
100 100 rebasing 6:24de4aff8e28 "F"
101 101 rebasing 7:4b988a958030 "G"
102 102 rebasing 8:31d0e4ba75e6 "H"
103 103 rebasing 9:9e70cd31750f "dev-two named branch" (tip)
104 104 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/643fc9128048-c4ee9ef5-backup.hg (glob)
105 105
106 106 $ hg tglog
107 107 @ 9: 'dev-two named branch' dev-two
108 108 |
109 109 o 8: 'H'
110 110 |
111 111 | o 7: 'G'
112 112 |/|
113 113 o | 6: 'F'
114 114 | |
115 115 o | 5: 'dev-one named branch' dev-one
116 116 | |
117 117 | o 4: 'E'
118 118 |/
119 119 | o 3: 'D'
120 120 | |
121 121 | o 2: 'C'
122 122 | |
123 123 | o 1: 'B'
124 124 |/
125 125 o 0: 'A'
126 126
127 127 $ hg update 3
128 128 3 files updated, 0 files merged, 3 files removed, 0 files unresolved
129 129 $ hg branch -f dev-one
130 130 marked working directory as branch dev-one
131 131 $ hg ci -m 'dev-one named branch'
132 132 created new head
133 133
134 134 $ hg tglog
135 135 @ 10: 'dev-one named branch' dev-one
136 136 |
137 137 | o 9: 'dev-two named branch' dev-two
138 138 | |
139 139 | o 8: 'H'
140 140 | |
141 141 | | o 7: 'G'
142 142 | |/|
143 143 | o | 6: 'F'
144 144 | | |
145 145 | o | 5: 'dev-one named branch' dev-one
146 146 | | |
147 147 | | o 4: 'E'
148 148 | |/
149 149 o | 3: 'D'
150 150 | |
151 151 o | 2: 'C'
152 152 | |
153 153 o | 1: 'B'
154 154 |/
155 155 o 0: 'A'
156 156
157 157 $ hg rebase -b 'max(branch("dev-two"))' -d dev-one --keepbranches
158 158 rebasing 5:bc8139ee757c "dev-one named branch"
159 159 note: rebase of 5:bc8139ee757c created no changes to commit
160 160 rebasing 6:42aa3cf0fa7a "F"
161 161 rebasing 7:1a1e6f72ec38 "G"
162 162 rebasing 8:904590360559 "H"
163 163 rebasing 9:59c2e59309fe "dev-two named branch"
164 164 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/bc8139ee757c-f11c1080-backup.hg (glob)
165 165
166 166 $ hg tglog
167 167 o 9: 'dev-two named branch' dev-two
168 168 |
169 169 o 8: 'H'
170 170 |
171 171 | o 7: 'G'
172 172 |/|
173 173 o | 6: 'F'
174 174 | |
175 175 @ | 5: 'dev-one named branch' dev-one
176 176 | |
177 177 | o 4: 'E'
178 178 | |
179 179 o | 3: 'D'
180 180 | |
181 181 o | 2: 'C'
182 182 | |
183 183 o | 1: 'B'
184 184 |/
185 185 o 0: 'A'
186 186
187 187 $ hg rebase -s 'max(branch("dev-one"))' -d 0 --keepbranches
188 188 rebasing 5:643fc9128048 "dev-one named branch"
189 189 rebasing 6:679f28760620 "F"
190 190 rebasing 7:549f007a9f5f "G"
191 191 rebasing 8:12b2bc666e20 "H"
192 192 rebasing 9:71325f8bc082 "dev-two named branch" (tip)
193 193 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/643fc9128048-6cdd1a52-backup.hg (glob)
194 194
195 195 $ hg tglog
196 196 o 9: 'dev-two named branch' dev-two
197 197 |
198 198 o 8: 'H'
199 199 |
200 200 | o 7: 'G'
201 201 |/|
202 202 o | 6: 'F'
203 203 | |
204 204 @ | 5: 'dev-one named branch' dev-one
205 205 | |
206 206 | o 4: 'E'
207 207 |/
208 208 | o 3: 'D'
209 209 | |
210 210 | o 2: 'C'
211 211 | |
212 212 | o 1: 'B'
213 213 |/
214 214 o 0: 'A'
215 215
216 216 $ hg up -r 0 > /dev/null
217 217
218 218 Rebasing descendant onto ancestor across different named branches
219 219
220 220 $ hg rebase -s 1 -d 9 --keepbranches
221 221 rebasing 1:42ccdea3bb16 "B"
222 222 rebasing 2:5fddd98957c8 "C"
223 223 rebasing 3:32af7686d403 "D"
224 224 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/42ccdea3bb16-3cb021d3-backup.hg (glob)
225 225
226 226 $ hg tglog
227 227 o 9: 'D'
228 228 |
229 229 o 8: 'C'
230 230 |
231 231 o 7: 'B'
232 232 |
233 233 o 6: 'dev-two named branch' dev-two
234 234 |
235 235 o 5: 'H'
236 236 |
237 237 | o 4: 'G'
238 238 |/|
239 239 o | 3: 'F'
240 240 | |
241 241 o | 2: 'dev-one named branch' dev-one
242 242 | |
243 243 | o 1: 'E'
244 244 |/
245 245 @ 0: 'A'
246 246
247 247 $ hg rebase -s 5 -d 6
248 248 abort: source is ancestor of destination
249 249 [255]
250 250
251 251 $ hg rebase -s 6 -d 5
252 252 rebasing 6:3944801ae4ea "dev-two named branch"
253 253 rebasing 7:3bdb949809d9 "B"
254 254 rebasing 8:a0d543090fa4 "C"
255 255 rebasing 9:e9f862ce8bad "D" (tip)
256 256 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/3944801ae4ea-fb46ed74-backup.hg (glob)
257 257
258 258 $ hg tglog
259 259 o 9: 'D'
260 260 |
261 261 o 8: 'C'
262 262 |
263 263 o 7: 'B'
264 264 |
265 265 o 6: 'dev-two named branch'
266 266 |
267 267 o 5: 'H'
268 268 |
269 269 | o 4: 'G'
270 270 |/|
271 271 o | 3: 'F'
272 272 | |
273 273 o | 2: 'dev-one named branch' dev-one
274 274 | |
275 275 | o 1: 'E'
276 276 |/
277 277 @ 0: 'A'
278 278
279 279
280 280 Reopen branch by rebase
281 281
282 282 $ hg up -qr3
283 283 $ hg branch -q b
284 284 $ hg ci -m 'create b'
285 285 $ hg ci -m 'close b' --close
286 286 $ hg rebase -b 8 -d b
287 287 reopening closed branch head 2b586e70108d
288 288 rebasing 5:8e279d293175 "H"
289 289 rebasing 6:c57724c84928 "dev-two named branch"
290 290 rebasing 7:160b0930ccc6 "B"
291 291 rebasing 8:810110211f50 "C"
292 292 rebasing 9:e522577ccdbd "D"
293 293 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/8e279d293175-b023e27c-backup.hg (glob)
294 294
295 295 $ cd ..
296 296
297 297 Rebase to other head on branch
298 298
299 299 Set up a case:
300 300
301 301 $ hg init case1
302 302 $ cd case1
303 303 $ touch f
304 304 $ hg ci -qAm0
305 305 $ hg branch -q b
306 306 $ echo >> f
307 307 $ hg ci -qAm 'b1'
308 308 $ hg up -qr -2
309 309 $ hg branch -qf b
310 310 $ hg ci -qm 'b2'
311 311 $ hg up -qr -3
312 312 $ hg branch -q c
313 313 $ hg ci -m 'c1'
314 314
315 315 $ hg tglog
316 316 @ 3: 'c1' c
317 317 |
318 318 | o 2: 'b2' b
319 319 |/
320 320 | o 1: 'b1' b
321 321 |/
322 322 o 0: '0'
323 323
324 324 $ hg clone -q . ../case2
325 325
326 326 rebase 'b2' to another lower branch head
327 327
328 328 $ hg up -qr 2
329 329 $ hg rebase
330 nothing to rebase - working directory parent is also destination
331 [1]
330 rebasing 2:792845bb77ee "b2"
331 note: rebase of 2:792845bb77ee created no changes to commit
332 saved backup bundle to $TESTTMP/case1/.hg/strip-backup/792845bb77ee-627120ee-backup.hg (glob)
332 333 $ hg tglog
333 o 3: 'c1' c
334 o 2: 'c1' c
334 335 |
335 | @ 2: 'b2' b
336 |/
337 | o 1: 'b1' b
336 | @ 1: 'b1' b
338 337 |/
339 338 o 0: '0'
340 339
341 340
342 341 rebase 'b1' on top of the tip of the branch ('b2') - ignoring the tip branch ('c1')
343 342
344 343 $ cd ../case2
345 344 $ hg up -qr 1
346 345 $ hg rebase
347 346 rebasing 1:40039acb7ca5 "b1"
348 347 saved backup bundle to $TESTTMP/case2/.hg/strip-backup/40039acb7ca5-342b72d1-backup.hg (glob)
349 348 $ hg tglog
350 349 @ 3: 'b1' b
351 350 |
352 351 | o 2: 'c1' c
353 352 | |
354 353 o | 1: 'b2' b
355 354 |/
356 355 o 0: '0'
357 356
358 357
359 358 rebase 'c1' to the branch head 'c2' that is closed
360 359
361 360 $ hg branch -qf c
362 361 $ hg ci -qm 'c2 closed' --close
363 362 $ hg up -qr 2
364 363 $ hg tglog
365 364 _ 4: 'c2 closed' c
366 365 |
367 366 o 3: 'b1' b
368 367 |
369 368 | @ 2: 'c1' c
370 369 | |
371 370 o | 1: 'b2' b
372 371 |/
373 372 o 0: '0'
374 373
375 374 $ hg rebase
376 nothing to rebase - working directory parent is also destination
377 [1]
375 abort: branch 'c' has one head - please rebase to an explicit rev
376 (run 'hg heads' to see all heads)
377 [255]
378 378 $ hg tglog
379 379 _ 4: 'c2 closed' c
380 380 |
381 381 o 3: 'b1' b
382 382 |
383 383 | @ 2: 'c1' c
384 384 | |
385 385 o | 1: 'b2' b
386 386 |/
387 387 o 0: '0'
388 388
389 389
390 390 $ cd ..
@@ -1,311 +1,311
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [extensions]
3 3 > rebase=
4 4 >
5 5 > [alias]
6 6 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
7 7 > EOF
8 8
9 9
10 10 $ hg init a
11 11 $ cd a
12 12
13 13 $ echo C1 > C1
14 14 $ hg ci -Am C1
15 15 adding C1
16 16
17 17 $ echo C2 > C2
18 18 $ hg ci -Am C2
19 19 adding C2
20 20
21 21 $ cd ..
22 22
23 23 $ hg clone a b
24 24 updating to branch default
25 25 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
26 26
27 27 $ hg clone a c
28 28 updating to branch default
29 29 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
30 30
31 31 $ cd b
32 32
33 33 $ echo L1 > L1
34 34 $ hg ci -Am L1
35 35 adding L1
36 36
37 37
38 38 $ cd ../a
39 39
40 40 $ echo R1 > R1
41 41 $ hg ci -Am R1
42 42 adding R1
43 43
44 44
45 45 $ cd ../b
46 46
47 47 Now b has one revision to be pulled from a:
48 48
49 49 $ hg pull --rebase
50 50 pulling from $TESTTMP/a (glob)
51 51 searching for changes
52 52 adding changesets
53 53 adding manifests
54 54 adding file changes
55 55 added 1 changesets with 1 changes to 1 files (+1 heads)
56 56 rebasing 2:ff8d69a621f9 "L1"
57 57 saved backup bundle to $TESTTMP/b/.hg/strip-backup/ff8d69a621f9-160fa373-backup.hg (glob)
58 58
59 59 $ hg tglog
60 60 @ 3: 'L1'
61 61 |
62 62 o 2: 'R1'
63 63 |
64 64 o 1: 'C2'
65 65 |
66 66 o 0: 'C1'
67 67
68 68 Re-run:
69 69
70 70 $ hg pull --rebase
71 71 pulling from $TESTTMP/a (glob)
72 72 searching for changes
73 73 no changes found
74 74
75 75
76 76 Invoke pull --rebase and nothing to rebase:
77 77
78 78 $ cd ../c
79 79
80 80 $ hg book norebase
81 81 $ hg pull --rebase
82 82 pulling from $TESTTMP/a (glob)
83 83 searching for changes
84 84 adding changesets
85 85 adding manifests
86 86 adding file changes
87 87 added 1 changesets with 1 changes to 1 files
88 nothing to rebase - working directory parent is already an ancestor of destination 77ae9631bcca
88 nothing to rebase - updating instead
89 89 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 90 updating bookmark norebase
91 91
92 92 $ hg tglog -l 1
93 93 @ 2: 'R1'
94 94 |
95 95
96 96 pull --rebase --update should ignore --update:
97 97
98 98 $ hg pull --rebase --update
99 99 pulling from $TESTTMP/a (glob)
100 100 searching for changes
101 101 no changes found
102 102
103 103 pull --rebase doesn't update if nothing has been pulled:
104 104
105 105 $ hg up -q 1
106 106
107 107 $ hg pull --rebase
108 108 pulling from $TESTTMP/a (glob)
109 109 searching for changes
110 110 no changes found
111 111
112 112 $ hg tglog -l 1
113 113 o 2: 'R1'
114 114 |
115 115
116 116 $ cd ..
117 117
118 118 pull --rebase works when a specific revision is pulled (issue3619)
119 119
120 120 $ cd a
121 121 $ hg tglog
122 122 @ 2: 'R1'
123 123 |
124 124 o 1: 'C2'
125 125 |
126 126 o 0: 'C1'
127 127
128 128 $ echo R2 > R2
129 129 $ hg ci -Am R2
130 130 adding R2
131 131 $ echo R3 > R3
132 132 $ hg ci -Am R3
133 133 adding R3
134 134 $ cd ../c
135 135 $ hg tglog
136 136 o 2: 'R1'
137 137 |
138 138 @ 1: 'C2'
139 139 |
140 140 o 0: 'C1'
141 141
142 142 $ echo L1 > L1
143 143 $ hg ci -Am L1
144 144 adding L1
145 145 created new head
146 146 $ hg pull --rev tip --rebase
147 147 pulling from $TESTTMP/a (glob)
148 148 searching for changes
149 149 adding changesets
150 150 adding manifests
151 151 adding file changes
152 152 added 2 changesets with 2 changes to 2 files
153 153 rebasing 3:ff8d69a621f9 "L1"
154 154 saved backup bundle to $TESTTMP/c/.hg/strip-backup/ff8d69a621f9-160fa373-backup.hg (glob)
155 155 $ hg tglog
156 156 @ 5: 'L1'
157 157 |
158 158 o 4: 'R3'
159 159 |
160 160 o 3: 'R2'
161 161 |
162 162 o 2: 'R1'
163 163 |
164 164 o 1: 'C2'
165 165 |
166 166 o 0: 'C1'
167 167
168 168 pull --rebase works with bundle2 turned on
169 169
170 170 $ cd ../a
171 171 $ echo R4 > R4
172 172 $ hg ci -Am R4
173 173 adding R4
174 174 $ hg tglog
175 175 @ 5: 'R4'
176 176 |
177 177 o 4: 'R3'
178 178 |
179 179 o 3: 'R2'
180 180 |
181 181 o 2: 'R1'
182 182 |
183 183 o 1: 'C2'
184 184 |
185 185 o 0: 'C1'
186 186
187 187 $ cd ../c
188 188 $ hg pull --rebase
189 189 pulling from $TESTTMP/a (glob)
190 190 searching for changes
191 191 adding changesets
192 192 adding manifests
193 193 adding file changes
194 194 added 1 changesets with 1 changes to 1 files (+1 heads)
195 195 rebasing 5:518d153c0ba3 "L1"
196 196 saved backup bundle to $TESTTMP/c/.hg/strip-backup/518d153c0ba3-73407f14-backup.hg (glob)
197 197 $ hg tglog
198 198 @ 6: 'L1'
199 199 |
200 200 o 5: 'R4'
201 201 |
202 202 o 4: 'R3'
203 203 |
204 204 o 3: 'R2'
205 205 |
206 206 o 2: 'R1'
207 207 |
208 208 o 1: 'C2'
209 209 |
210 210 o 0: 'C1'
211 211
212 212
213 213 pull --rebase only update if there is nothing to rebase
214 214
215 215 $ cd ../a
216 216 $ echo R5 > R5
217 217 $ hg ci -Am R5
218 218 adding R5
219 219 $ hg tglog
220 220 @ 6: 'R5'
221 221 |
222 222 o 5: 'R4'
223 223 |
224 224 o 4: 'R3'
225 225 |
226 226 o 3: 'R2'
227 227 |
228 228 o 2: 'R1'
229 229 |
230 230 o 1: 'C2'
231 231 |
232 232 o 0: 'C1'
233 233
234 234 $ cd ../c
235 235 $ echo L2 > L2
236 236 $ hg ci -Am L2
237 237 adding L2
238 238 $ hg up 'desc(L1)'
239 239 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
240 240 $ hg pull --rebase
241 241 pulling from $TESTTMP/a (glob)
242 242 searching for changes
243 243 adding changesets
244 244 adding manifests
245 245 adding file changes
246 246 added 1 changesets with 1 changes to 1 files (+1 heads)
247 247 rebasing 6:0d0727eb7ce0 "L1"
248 248 rebasing 7:c1f58876e3bf "L2"
249 249 saved backup bundle to $TESTTMP/c/.hg/strip-backup/0d0727eb7ce0-ef61ccb2-backup.hg (glob)
250 250 $ hg tglog
251 251 o 8: 'L2'
252 252 |
253 253 @ 7: 'L1'
254 254 |
255 255 o 6: 'R5'
256 256 |
257 257 o 5: 'R4'
258 258 |
259 259 o 4: 'R3'
260 260 |
261 261 o 3: 'R2'
262 262 |
263 263 o 2: 'R1'
264 264 |
265 265 o 1: 'C2'
266 266 |
267 267 o 0: 'C1'
268 268
269 269
270 270 pull --rebase update (no rebase) use proper update:
271 271
272 272 - warn about other head.
273 273
274 274 $ cd ../a
275 275 $ echo R6 > R6
276 276 $ hg ci -Am R6
277 277 adding R6
278 278 $ cd ../c
279 279 $ hg up 'desc(R5)'
280 280 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
281 281 $ hg pull --rebase
282 282 pulling from $TESTTMP/a (glob)
283 283 searching for changes
284 284 adding changesets
285 285 adding manifests
286 286 adding file changes
287 287 added 1 changesets with 1 changes to 1 files (+1 heads)
288 nothing to rebase - working directory parent is already an ancestor of destination 65bc164c1d9b
288 nothing to rebase - updating instead
289 289 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
290 290 1 other heads for branch "default"
291 291 $ hg tglog
292 292 @ 9: 'R6'
293 293 |
294 294 | o 8: 'L2'
295 295 | |
296 296 | o 7: 'L1'
297 297 |/
298 298 o 6: 'R5'
299 299 |
300 300 o 5: 'R4'
301 301 |
302 302 o 4: 'R3'
303 303 |
304 304 o 3: 'R2'
305 305 |
306 306 o 2: 'R1'
307 307 |
308 308 o 1: 'C2'
309 309 |
310 310 o 0: 'C1'
311 311
@@ -1,830 +1,834
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [extensions]
3 3 > rebase=
4 4 >
5 5 > [phases]
6 6 > publish=False
7 7 >
8 8 > [alias]
9 9 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
10 10 > EOF
11 11
12 12
13 13 $ hg init a
14 14 $ cd a
15 15 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
16 16 adding changesets
17 17 adding manifests
18 18 adding file changes
19 19 added 8 changesets with 7 changes to 7 files (+2 heads)
20 20 (run 'hg heads' to see heads, 'hg merge' to merge)
21 21 $ hg up tip
22 22 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
23 23 $ cd ..
24 24
25 25
26 26 Rebasing
27 27 D onto H - simple rebase:
28 28 (this also tests that editor is invoked if '--edit' is specified, and that we
29 29 can abort or warn for colliding untracked files)
30 30
31 31 $ hg clone -q -u . a a1
32 32 $ cd a1
33 33
34 34 $ hg tglog
35 35 @ 7: 'H'
36 36 |
37 37 | o 6: 'G'
38 38 |/|
39 39 o | 5: 'F'
40 40 | |
41 41 | o 4: 'E'
42 42 |/
43 43 | o 3: 'D'
44 44 | |
45 45 | o 2: 'C'
46 46 | |
47 47 | o 1: 'B'
48 48 |/
49 49 o 0: 'A'
50 50
51 51
52 52 $ hg status --rev "3^1" --rev 3
53 53 A D
54 54 $ echo collide > D
55 55 $ HGEDITOR=cat hg rebase -s 3 -d 7 --edit --config merge.checkunknown=warn
56 56 rebasing 3:32af7686d403 "D"
57 57 D: replacing untracked file
58 58 D
59 59
60 60
61 61 HG: Enter commit message. Lines beginning with 'HG:' are removed.
62 62 HG: Leave message empty to abort commit.
63 63 HG: --
64 64 HG: user: Nicolas Dumazet <nicdumz.commits@gmail.com>
65 65 HG: branch 'default'
66 66 HG: added D
67 67 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/32af7686d403-6f7dface-backup.hg (glob)
68 68 $ cat D.orig
69 69 collide
70 70 $ rm D.orig
71 71
72 72 $ hg tglog
73 73 o 7: 'D'
74 74 |
75 75 @ 6: 'H'
76 76 |
77 77 | o 5: 'G'
78 78 |/|
79 79 o | 4: 'F'
80 80 | |
81 81 | o 3: 'E'
82 82 |/
83 83 | o 2: 'C'
84 84 | |
85 85 | o 1: 'B'
86 86 |/
87 87 o 0: 'A'
88 88
89 89 $ cd ..
90 90
91 91
92 92 D onto F - intermediate point:
93 93 (this also tests that editor is not invoked if '--edit' is not specified, and
94 94 that we can ignore for colliding untracked files)
95 95
96 96 $ hg clone -q -u . a a2
97 97 $ cd a2
98 98 $ echo collide > D
99 99
100 100 $ HGEDITOR=cat hg rebase -s 3 -d 5 --config merge.checkunknown=ignore
101 101 rebasing 3:32af7686d403 "D"
102 102 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/32af7686d403-6f7dface-backup.hg (glob)
103 103 $ cat D.orig
104 104 collide
105 105 $ rm D.orig
106 106
107 107 $ hg tglog
108 108 o 7: 'D'
109 109 |
110 110 | @ 6: 'H'
111 111 |/
112 112 | o 5: 'G'
113 113 |/|
114 114 o | 4: 'F'
115 115 | |
116 116 | o 3: 'E'
117 117 |/
118 118 | o 2: 'C'
119 119 | |
120 120 | o 1: 'B'
121 121 |/
122 122 o 0: 'A'
123 123
124 124 $ cd ..
125 125
126 126
127 127 E onto H - skip of G:
128 128 (this also tests that we can overwrite untracked files and don't create backups
129 129 if they have the same contents)
130 130
131 131 $ hg clone -q -u . a a3
132 132 $ cd a3
133 133 $ hg cat -r 4 E | tee E
134 134 E
135 135
136 136 $ hg rebase -s 4 -d 7
137 137 rebasing 4:9520eea781bc "E"
138 138 rebasing 6:eea13746799a "G"
139 139 note: rebase of 6:eea13746799a created no changes to commit
140 140 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/9520eea781bc-fcd8edd4-backup.hg (glob)
141 141 $ f E.orig
142 142 E.orig: file not found
143 143
144 144 $ hg tglog
145 145 o 6: 'E'
146 146 |
147 147 @ 5: 'H'
148 148 |
149 149 o 4: 'F'
150 150 |
151 151 | o 3: 'D'
152 152 | |
153 153 | o 2: 'C'
154 154 | |
155 155 | o 1: 'B'
156 156 |/
157 157 o 0: 'A'
158 158
159 159 $ cd ..
160 160
161 161
162 162 F onto E - rebase of a branching point (skip G):
163 163
164 164 $ hg clone -q -u . a a4
165 165 $ cd a4
166 166
167 167 $ hg rebase -s 5 -d 4
168 168 rebasing 5:24b6387c8c8c "F"
169 169 rebasing 6:eea13746799a "G"
170 170 note: rebase of 6:eea13746799a created no changes to commit
171 171 rebasing 7:02de42196ebe "H" (tip)
172 172 saved backup bundle to $TESTTMP/a4/.hg/strip-backup/24b6387c8c8c-c3fe765d-backup.hg (glob)
173 173
174 174 $ hg tglog
175 175 @ 6: 'H'
176 176 |
177 177 o 5: 'F'
178 178 |
179 179 o 4: 'E'
180 180 |
181 181 | o 3: 'D'
182 182 | |
183 183 | o 2: 'C'
184 184 | |
185 185 | o 1: 'B'
186 186 |/
187 187 o 0: 'A'
188 188
189 189 $ cd ..
190 190
191 191
192 192 G onto H - merged revision having a parent in ancestors of target:
193 193
194 194 $ hg clone -q -u . a a5
195 195 $ cd a5
196 196
197 197 $ hg rebase -s 6 -d 7
198 198 rebasing 6:eea13746799a "G"
199 199 saved backup bundle to $TESTTMP/a5/.hg/strip-backup/eea13746799a-883828ed-backup.hg (glob)
200 200
201 201 $ hg tglog
202 202 o 7: 'G'
203 203 |\
204 204 | @ 6: 'H'
205 205 | |
206 206 | o 5: 'F'
207 207 | |
208 208 o | 4: 'E'
209 209 |/
210 210 | o 3: 'D'
211 211 | |
212 212 | o 2: 'C'
213 213 | |
214 214 | o 1: 'B'
215 215 |/
216 216 o 0: 'A'
217 217
218 218 $ cd ..
219 219
220 220
221 221 F onto B - G maintains E as parent:
222 222
223 223 $ hg clone -q -u . a a6
224 224 $ cd a6
225 225
226 226 $ hg rebase -s 5 -d 1
227 227 rebasing 5:24b6387c8c8c "F"
228 228 rebasing 6:eea13746799a "G"
229 229 rebasing 7:02de42196ebe "H" (tip)
230 230 saved backup bundle to $TESTTMP/a6/.hg/strip-backup/24b6387c8c8c-c3fe765d-backup.hg (glob)
231 231
232 232 $ hg tglog
233 233 @ 7: 'H'
234 234 |
235 235 | o 6: 'G'
236 236 |/|
237 237 o | 5: 'F'
238 238 | |
239 239 | o 4: 'E'
240 240 | |
241 241 | | o 3: 'D'
242 242 | | |
243 243 +---o 2: 'C'
244 244 | |
245 245 o | 1: 'B'
246 246 |/
247 247 o 0: 'A'
248 248
249 249 $ cd ..
250 250
251 251
252 252 These will fail (using --source):
253 253
254 254 G onto F - rebase onto an ancestor:
255 255
256 256 $ hg clone -q -u . a a7
257 257 $ cd a7
258 258
259 259 $ hg rebase -s 6 -d 5
260 260 nothing to rebase
261 261 [1]
262 262
263 263 F onto G - rebase onto a descendant:
264 264
265 265 $ hg rebase -s 5 -d 6
266 266 abort: source is ancestor of destination
267 267 [255]
268 268
269 269 G onto B - merge revision with both parents not in ancestors of target:
270 270
271 271 $ hg rebase -s 6 -d 1
272 272 rebasing 6:eea13746799a "G"
273 273 abort: cannot use revision 6 as base, result would have 3 parents
274 274 [255]
275 275
276 276
277 277 These will abort gracefully (using --base):
278 278
279 279 G onto G - rebase onto same changeset:
280 280
281 281 $ hg rebase -b 6 -d 6
282 282 nothing to rebase - eea13746799a is both "base" and destination
283 283 [1]
284 284
285 285 G onto F - rebase onto an ancestor:
286 286
287 287 $ hg rebase -b 6 -d 5
288 288 nothing to rebase
289 289 [1]
290 290
291 291 F onto G - rebase onto a descendant:
292 292
293 293 $ hg rebase -b 5 -d 6
294 294 nothing to rebase - "base" 24b6387c8c8c is already an ancestor of destination eea13746799a
295 295 [1]
296 296
297 297 C onto A - rebase onto an ancestor:
298 298
299 299 $ hg rebase -d 0 -s 2
300 300 rebasing 2:5fddd98957c8 "C"
301 301 rebasing 3:32af7686d403 "D"
302 302 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/5fddd98957c8-f9244fa1-backup.hg (glob)
303 303 $ hg tglog
304 304 o 7: 'D'
305 305 |
306 306 o 6: 'C'
307 307 |
308 308 | @ 5: 'H'
309 309 | |
310 310 | | o 4: 'G'
311 311 | |/|
312 312 | o | 3: 'F'
313 313 |/ /
314 314 | o 2: 'E'
315 315 |/
316 316 | o 1: 'B'
317 317 |/
318 318 o 0: 'A'
319 319
320 320
321 321 Check rebasing public changeset
322 322
323 323 $ hg pull --config phases.publish=True -q -r 6 . # update phase of 6
324 324 $ hg rebase -d 0 -b 6
325 325 nothing to rebase
326 326 [1]
327 327 $ hg rebase -d 5 -b 6
328 328 abort: can't rebase public changeset e1c4361dd923
329 329 (see "hg help phases" for details)
330 330 [255]
331 331
332 332 $ hg rebase -d 5 -b 6 --keep
333 333 rebasing 6:e1c4361dd923 "C"
334 334 rebasing 7:c9659aac0000 "D" (tip)
335 335
336 336 Check rebasing mutable changeset
337 337 Source phase greater or equal to destination phase: new changeset get the phase of source:
338 338 $ hg id -n
339 339 5
340 340 $ hg rebase -s9 -d0
341 341 rebasing 9:2b23e52411f4 "D" (tip)
342 342 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2b23e52411f4-f942decf-backup.hg (glob)
343 343 $ hg id -n # check we updated back to parent
344 344 5
345 345 $ hg log --template "{phase}\n" -r 9
346 346 draft
347 347 $ hg rebase -s9 -d1
348 348 rebasing 9:2cb10d0cfc6c "D" (tip)
349 349 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2cb10d0cfc6c-ddb0f256-backup.hg (glob)
350 350 $ hg log --template "{phase}\n" -r 9
351 351 draft
352 352 $ hg phase --force --secret 9
353 353 $ hg rebase -s9 -d0
354 354 rebasing 9:c5b12b67163a "D" (tip)
355 355 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/c5b12b67163a-4e372053-backup.hg (glob)
356 356 $ hg log --template "{phase}\n" -r 9
357 357 secret
358 358 $ hg rebase -s9 -d1
359 359 rebasing 9:2a0524f868ac "D" (tip)
360 360 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2a0524f868ac-cefd8574-backup.hg (glob)
361 361 $ hg log --template "{phase}\n" -r 9
362 362 secret
363 363 Source phase lower than destination phase: new changeset get the phase of destination:
364 364 $ hg rebase -s8 -d9
365 365 rebasing 8:6d4f22462821 "C"
366 366 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/6d4f22462821-3441f70b-backup.hg (glob)
367 367 $ hg log --template "{phase}\n" -r 'rev(9)'
368 368 secret
369 369
370 370 $ cd ..
371 371
372 372 Test for revset
373 373
374 374 We need a bit different graph
375 375 All destination are B
376 376
377 377 $ hg init ah
378 378 $ cd ah
379 379 $ hg unbundle "$TESTDIR/bundles/rebase-revset.hg"
380 380 adding changesets
381 381 adding manifests
382 382 adding file changes
383 383 added 9 changesets with 9 changes to 9 files (+2 heads)
384 384 (run 'hg heads' to see heads, 'hg merge' to merge)
385 385 $ hg tglog
386 386 o 8: 'I'
387 387 |
388 388 o 7: 'H'
389 389 |
390 390 o 6: 'G'
391 391 |
392 392 | o 5: 'F'
393 393 | |
394 394 | o 4: 'E'
395 395 |/
396 396 o 3: 'D'
397 397 |
398 398 o 2: 'C'
399 399 |
400 400 | o 1: 'B'
401 401 |/
402 402 o 0: 'A'
403 403
404 404 $ cd ..
405 405
406 406
407 407 Simple case with keep:
408 408
409 409 Source on have two descendant heads but ask for one
410 410
411 411 $ hg clone -q -u . ah ah1
412 412 $ cd ah1
413 413 $ hg rebase -r '2::8' -d 1
414 414 abort: can't remove original changesets with unrebased descendants
415 415 (use --keep to keep original changesets)
416 416 [255]
417 417 $ hg rebase -r '2::8' -d 1 -k
418 418 rebasing 2:c9e50f6cdc55 "C"
419 419 rebasing 3:ffd453c31098 "D"
420 420 rebasing 6:3d8a618087a7 "G"
421 421 rebasing 7:72434a4e60b0 "H"
422 422 rebasing 8:479ddb54a924 "I" (tip)
423 423 $ hg tglog
424 424 o 13: 'I'
425 425 |
426 426 o 12: 'H'
427 427 |
428 428 o 11: 'G'
429 429 |
430 430 o 10: 'D'
431 431 |
432 432 o 9: 'C'
433 433 |
434 434 | o 8: 'I'
435 435 | |
436 436 | o 7: 'H'
437 437 | |
438 438 | o 6: 'G'
439 439 | |
440 440 | | o 5: 'F'
441 441 | | |
442 442 | | o 4: 'E'
443 443 | |/
444 444 | o 3: 'D'
445 445 | |
446 446 | o 2: 'C'
447 447 | |
448 448 o | 1: 'B'
449 449 |/
450 450 o 0: 'A'
451 451
452 452
453 453 $ cd ..
454 454
455 455 Base on have one descendant heads we ask for but common ancestor have two
456 456
457 457 $ hg clone -q -u . ah ah2
458 458 $ cd ah2
459 459 $ hg rebase -r '3::8' -d 1
460 460 abort: can't remove original changesets with unrebased descendants
461 461 (use --keep to keep original changesets)
462 462 [255]
463 463 $ hg rebase -r '3::8' -d 1 --keep
464 464 rebasing 3:ffd453c31098 "D"
465 465 rebasing 6:3d8a618087a7 "G"
466 466 rebasing 7:72434a4e60b0 "H"
467 467 rebasing 8:479ddb54a924 "I" (tip)
468 468 $ hg tglog
469 469 o 12: 'I'
470 470 |
471 471 o 11: 'H'
472 472 |
473 473 o 10: 'G'
474 474 |
475 475 o 9: 'D'
476 476 |
477 477 | o 8: 'I'
478 478 | |
479 479 | o 7: 'H'
480 480 | |
481 481 | o 6: 'G'
482 482 | |
483 483 | | o 5: 'F'
484 484 | | |
485 485 | | o 4: 'E'
486 486 | |/
487 487 | o 3: 'D'
488 488 | |
489 489 | o 2: 'C'
490 490 | |
491 491 o | 1: 'B'
492 492 |/
493 493 o 0: 'A'
494 494
495 495
496 496 $ cd ..
497 497
498 498 rebase subset
499 499
500 500 $ hg clone -q -u . ah ah3
501 501 $ cd ah3
502 502 $ hg rebase -r '3::7' -d 1
503 503 abort: can't remove original changesets with unrebased descendants
504 504 (use --keep to keep original changesets)
505 505 [255]
506 506 $ hg rebase -r '3::7' -d 1 --keep
507 507 rebasing 3:ffd453c31098 "D"
508 508 rebasing 6:3d8a618087a7 "G"
509 509 rebasing 7:72434a4e60b0 "H"
510 510 $ hg tglog
511 511 o 11: 'H'
512 512 |
513 513 o 10: 'G'
514 514 |
515 515 o 9: 'D'
516 516 |
517 517 | o 8: 'I'
518 518 | |
519 519 | o 7: 'H'
520 520 | |
521 521 | o 6: 'G'
522 522 | |
523 523 | | o 5: 'F'
524 524 | | |
525 525 | | o 4: 'E'
526 526 | |/
527 527 | o 3: 'D'
528 528 | |
529 529 | o 2: 'C'
530 530 | |
531 531 o | 1: 'B'
532 532 |/
533 533 o 0: 'A'
534 534
535 535
536 536 $ cd ..
537 537
538 538 rebase subset with multiple head
539 539
540 540 $ hg clone -q -u . ah ah4
541 541 $ cd ah4
542 542 $ hg rebase -r '3::(7+5)' -d 1
543 543 abort: can't remove original changesets with unrebased descendants
544 544 (use --keep to keep original changesets)
545 545 [255]
546 546 $ hg rebase -r '3::(7+5)' -d 1 --keep
547 547 rebasing 3:ffd453c31098 "D"
548 548 rebasing 4:c01897464e7f "E"
549 549 rebasing 5:41bfcc75ed73 "F"
550 550 rebasing 6:3d8a618087a7 "G"
551 551 rebasing 7:72434a4e60b0 "H"
552 552 $ hg tglog
553 553 o 13: 'H'
554 554 |
555 555 o 12: 'G'
556 556 |
557 557 | o 11: 'F'
558 558 | |
559 559 | o 10: 'E'
560 560 |/
561 561 o 9: 'D'
562 562 |
563 563 | o 8: 'I'
564 564 | |
565 565 | o 7: 'H'
566 566 | |
567 567 | o 6: 'G'
568 568 | |
569 569 | | o 5: 'F'
570 570 | | |
571 571 | | o 4: 'E'
572 572 | |/
573 573 | o 3: 'D'
574 574 | |
575 575 | o 2: 'C'
576 576 | |
577 577 o | 1: 'B'
578 578 |/
579 579 o 0: 'A'
580 580
581 581
582 582 $ cd ..
583 583
584 584 More advanced tests
585 585
586 586 rebase on ancestor with revset
587 587
588 588 $ hg clone -q -u . ah ah5
589 589 $ cd ah5
590 590 $ hg rebase -r '6::' -d 2
591 591 rebasing 6:3d8a618087a7 "G"
592 592 rebasing 7:72434a4e60b0 "H"
593 593 rebasing 8:479ddb54a924 "I" (tip)
594 594 saved backup bundle to $TESTTMP/ah5/.hg/strip-backup/3d8a618087a7-b4f73f31-backup.hg (glob)
595 595 $ hg tglog
596 596 o 8: 'I'
597 597 |
598 598 o 7: 'H'
599 599 |
600 600 o 6: 'G'
601 601 |
602 602 | o 5: 'F'
603 603 | |
604 604 | o 4: 'E'
605 605 | |
606 606 | o 3: 'D'
607 607 |/
608 608 o 2: 'C'
609 609 |
610 610 | o 1: 'B'
611 611 |/
612 612 o 0: 'A'
613 613
614 614 $ cd ..
615 615
616 616
617 617 rebase with multiple root.
618 618 We rebase E and G on B
619 619 We would expect heads are I, F if it was supported
620 620
621 621 $ hg clone -q -u . ah ah6
622 622 $ cd ah6
623 623 $ hg rebase -r '(4+6)::' -d 1
624 624 rebasing 4:c01897464e7f "E"
625 625 rebasing 5:41bfcc75ed73 "F"
626 626 rebasing 6:3d8a618087a7 "G"
627 627 rebasing 7:72434a4e60b0 "H"
628 628 rebasing 8:479ddb54a924 "I" (tip)
629 629 saved backup bundle to $TESTTMP/ah6/.hg/strip-backup/3d8a618087a7-aae93a24-backup.hg (glob)
630 630 $ hg tglog
631 631 o 8: 'I'
632 632 |
633 633 o 7: 'H'
634 634 |
635 635 o 6: 'G'
636 636 |
637 637 | o 5: 'F'
638 638 | |
639 639 | o 4: 'E'
640 640 |/
641 641 | o 3: 'D'
642 642 | |
643 643 | o 2: 'C'
644 644 | |
645 645 o | 1: 'B'
646 646 |/
647 647 o 0: 'A'
648 648
649 649 $ cd ..
650 650
651 651 More complex rebase with multiple roots
652 652 each root have a different common ancestor with the destination and this is a detach
653 653
654 654 (setup)
655 655
656 656 $ hg clone -q -u . a a8
657 657 $ cd a8
658 658 $ echo I > I
659 659 $ hg add I
660 660 $ hg commit -m I
661 661 $ hg up 4
662 662 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
663 663 $ echo I > J
664 664 $ hg add J
665 665 $ hg commit -m J
666 666 created new head
667 667 $ echo I > K
668 668 $ hg add K
669 669 $ hg commit -m K
670 670 $ hg tglog
671 671 @ 10: 'K'
672 672 |
673 673 o 9: 'J'
674 674 |
675 675 | o 8: 'I'
676 676 | |
677 677 | o 7: 'H'
678 678 | |
679 679 +---o 6: 'G'
680 680 | |/
681 681 | o 5: 'F'
682 682 | |
683 683 o | 4: 'E'
684 684 |/
685 685 | o 3: 'D'
686 686 | |
687 687 | o 2: 'C'
688 688 | |
689 689 | o 1: 'B'
690 690 |/
691 691 o 0: 'A'
692 692
693 693 (actual test)
694 694
695 695 $ hg rebase --dest 'desc(G)' --rev 'desc(K) + desc(I)'
696 696 rebasing 8:e7ec4e813ba6 "I"
697 697 rebasing 10:23a4ace37988 "K" (tip)
698 698 saved backup bundle to $TESTTMP/a8/.hg/strip-backup/23a4ace37988-b06984b3-backup.hg (glob)
699 699 $ hg log --rev 'children(desc(G))'
700 700 changeset: 9:adb617877056
701 701 parent: 6:eea13746799a
702 702 user: test
703 703 date: Thu Jan 01 00:00:00 1970 +0000
704 704 summary: I
705 705
706 706 changeset: 10:882431a34a0e
707 707 tag: tip
708 708 parent: 6:eea13746799a
709 709 user: test
710 710 date: Thu Jan 01 00:00:00 1970 +0000
711 711 summary: K
712 712
713 713 $ hg tglog
714 714 @ 10: 'K'
715 715 |
716 716 | o 9: 'I'
717 717 |/
718 718 | o 8: 'J'
719 719 | |
720 720 | | o 7: 'H'
721 721 | | |
722 722 o---+ 6: 'G'
723 723 |/ /
724 724 | o 5: 'F'
725 725 | |
726 726 o | 4: 'E'
727 727 |/
728 728 | o 3: 'D'
729 729 | |
730 730 | o 2: 'C'
731 731 | |
732 732 | o 1: 'B'
733 733 |/
734 734 o 0: 'A'
735 735
736 736
737 737 Test that rebase is not confused by $CWD disappearing during rebase (issue4121)
738 738
739 739 $ cd ..
740 740 $ hg init cwd-vanish
741 741 $ cd cwd-vanish
742 742 $ touch initial-file
743 743 $ hg add initial-file
744 744 $ hg commit -m 'initial commit'
745 745 $ touch dest-file
746 746 $ hg add dest-file
747 747 $ hg commit -m 'dest commit'
748 748 $ hg up 0
749 749 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
750 750 $ touch other-file
751 751 $ hg add other-file
752 752 $ hg commit -m 'first source commit'
753 753 created new head
754 754 $ mkdir subdir
755 755 $ cd subdir
756 756 $ touch subfile
757 757 $ hg add subfile
758 758 $ hg commit -m 'second source with subdir'
759 759 $ hg rebase -b . -d 1 --traceback
760 760 rebasing 2:779a07b1b7a0 "first source commit"
761 761 rebasing 3:a7d6f3a00bf3 "second source with subdir" (tip)
762 762 saved backup bundle to $TESTTMP/cwd-vanish/.hg/strip-backup/779a07b1b7a0-853e0073-backup.hg (glob)
763 763
764 764 Test experimental revset
765 765 ========================
766 766
767 767 $ cd ..
768 768
769 769 Make the repo a bit more interresting
770 770
771 771 $ hg up 1
772 772 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
773 773 $ echo aaa > aaa
774 774 $ hg add aaa
775 775 $ hg commit -m aaa
776 776 created new head
777 777 $ hg log -G
778 778 @ changeset: 4:5f7bc9025ed2
779 779 | tag: tip
780 780 | parent: 1:58d79cc1cf43
781 781 | user: test
782 782 | date: Thu Jan 01 00:00:00 1970 +0000
783 783 | summary: aaa
784 784 |
785 785 | o changeset: 3:1910d5ff34ea
786 786 | | user: test
787 787 | | date: Thu Jan 01 00:00:00 1970 +0000
788 788 | | summary: second source with subdir
789 789 | |
790 790 | o changeset: 2:82901330b6ef
791 791 |/ user: test
792 792 | date: Thu Jan 01 00:00:00 1970 +0000
793 793 | summary: first source commit
794 794 |
795 795 o changeset: 1:58d79cc1cf43
796 796 | user: test
797 797 | date: Thu Jan 01 00:00:00 1970 +0000
798 798 | summary: dest commit
799 799 |
800 800 o changeset: 0:e94b687f7da3
801 801 user: test
802 802 date: Thu Jan 01 00:00:00 1970 +0000
803 803 summary: initial commit
804 804
805 805
806 806 Testing from lower head
807 807
808 808 $ hg up 3
809 809 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
810 810 $ hg log -r '_destrebase()'
811 811 changeset: 4:5f7bc9025ed2
812 812 tag: tip
813 813 parent: 1:58d79cc1cf43
814 814 user: test
815 815 date: Thu Jan 01 00:00:00 1970 +0000
816 816 summary: aaa
817 817
818 818
819 819 Testing from upper head
820 820
821 $ hg log -r '_destrebase(4)'
822 changeset: 3:1910d5ff34ea
823 user: test
824 date: Thu Jan 01 00:00:00 1970 +0000
825 summary: second source with subdir
826
821 827 $ hg up 4
822 828 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
823 829 $ hg log -r '_destrebase()'
824 changeset: 4:5f7bc9025ed2
825 tag: tip
826 parent: 1:58d79cc1cf43
830 changeset: 3:1910d5ff34ea
827 831 user: test
828 832 date: Thu Jan 01 00:00:00 1970 +0000
829 summary: aaa
833 summary: second source with subdir
830 834
General Comments 0
You need to be logged in to leave comments. Login now