##// END OF EJS Templates
shelve: use rebase instead of merge (issue4068)...
Durham Goode -
r19961:1d7a36ff stable
parent child Browse files
Show More
@@ -27,6 +27,7 b' from mercurial import changegroup, cmdut'
27 27 from mercurial import error, hg, mdiff, merge, patch, repair, util
28 28 from mercurial import templatefilters
29 29 from mercurial import lock as lockmod
30 from hgext import rebase
30 31 import errno
31 32
32 33 cmdtable = {}
@@ -95,25 +96,35 b' class shelvedstate(object):'
95 96 raise util.Abort(_('this version of shelve is incompatible '
96 97 'with the version used in this repo'))
97 98 name = fp.readline().strip()
99 wctx = fp.readline().strip()
100 pendingctx = fp.readline().strip()
98 101 parents = [bin(h) for h in fp.readline().split()]
99 102 stripnodes = [bin(h) for h in fp.readline().split()]
103 unknownfiles = fp.readline()[:-1].split('\0')
100 104 finally:
101 105 fp.close()
102 106
103 107 obj = cls()
104 108 obj.name = name
109 obj.wctx = repo[bin(wctx)]
110 obj.pendingctx = repo[bin(pendingctx)]
105 111 obj.parents = parents
106 112 obj.stripnodes = stripnodes
113 obj.unknownfiles = unknownfiles
107 114
108 115 return obj
109 116
110 117 @classmethod
111 def save(cls, repo, name, stripnodes):
118 def save(cls, repo, name, originalwctx, pendingctx, stripnodes,
119 unknownfiles):
112 120 fp = repo.opener(cls._filename, 'wb')
113 121 fp.write('%i\n' % cls._version)
114 122 fp.write('%s\n' % name)
123 fp.write('%s\n' % hex(originalwctx.node()))
124 fp.write('%s\n' % hex(pendingctx.node()))
115 125 fp.write('%s\n' % ' '.join([hex(p) for p in repo.dirstate.parents()]))
116 126 fp.write('%s\n' % ' '.join([hex(n) for n in stripnodes]))
127 fp.write('%s\n' % '\0'.join(unknownfiles))
117 128 fp.close()
118 129
119 130 @classmethod
@@ -368,44 +379,55 b' def unshelveabort(ui, repo, state, opts)'
368 379 lock = None
369 380 try:
370 381 checkparents(repo, state)
382
383 util.rename(repo.join('unshelverebasestate'),
384 repo.join('rebasestate'))
385 try:
386 rebase.rebase(ui, repo, **{
387 'abort' : True
388 })
389 except Exception:
390 util.rename(repo.join('rebasestate'),
391 repo.join('unshelverebasestate'))
392 raise
393
371 394 lock = repo.lock()
372 merge.mergestate(repo).reset()
373 if opts['keep']:
374 repo.setparents(repo.dirstate.parents()[0])
375 else:
376 revertfiles = readshelvedfiles(repo, state.name)
377 wctx = repo.parents()[0]
378 cmdutil.revert(ui, repo, wctx, [wctx.node(), nullid],
379 *pathtofiles(repo, revertfiles),
380 **{'no_backup': True})
381 # fix up the weird dirstate states the merge left behind
382 mf = wctx.manifest()
383 dirstate = repo.dirstate
384 for f in revertfiles:
385 if f in mf:
386 dirstate.normallookup(f)
387 else:
388 dirstate.drop(f)
389 dirstate._pl = (wctx.node(), nullid)
390 dirstate._dirty = True
395
396 mergefiles(ui, repo, state.wctx, state.pendingctx, state.unknownfiles)
397
391 398 repair.strip(ui, repo, state.stripnodes, backup='none', topic='shelve')
392 399 shelvedstate.clear(repo)
393 400 ui.warn(_("unshelve of '%s' aborted\n") % state.name)
394 401 finally:
395 402 lockmod.release(lock, wlock)
396 403
404 def mergefiles(ui, repo, wctx, shelvectx, unknownfiles):
405 """updates to wctx and merges the changes from shelvectx into the
406 dirstate. drops any files in unknownfiles from the dirstate."""
407 oldquiet = ui.quiet
408 try:
409 ui.quiet = True
410 hg.update(repo, wctx.node())
411 files = []
412 files.extend(shelvectx.files())
413 files.extend(shelvectx.parents()[0].files())
414 cmdutil.revert(ui, repo, shelvectx, repo.dirstate.parents(),
415 *pathtofiles(repo, files),
416 **{'no_backup': True})
417 finally:
418 ui.quiet = oldquiet
419
420 # Send untracked files back to being untracked
421 dirstate = repo.dirstate
422 for f in unknownfiles:
423 dirstate.drop(f)
424
397 425 def unshelvecleanup(ui, repo, name, opts):
398 426 """remove related files after an unshelve"""
399 427 if not opts['keep']:
400 428 for filetype in 'hg files patch'.split():
401 429 shelvedfile(repo, name, filetype).unlink()
402 430
403 def finishmerge(ui, repo, ms, stripnodes, name, opts):
404 # Reset the working dir so it's no longer in a merge state.
405 dirstate = repo.dirstate
406 dirstate.setparents(dirstate._pl[0])
407 shelvedstate.clear(repo)
408
409 431 def unshelvecontinue(ui, repo, state, opts):
410 432 """subcommand to continue an in-progress unshelve"""
411 433 # We're finishing off a merge. First parent is our original
@@ -419,9 +441,30 b' def unshelvecontinue(ui, repo, state, op'
419 441 raise util.Abort(
420 442 _("unresolved conflicts, can't continue"),
421 443 hint=_("see 'hg resolve', then 'hg unshelve --continue'"))
422 finishmerge(ui, repo, ms, state.stripnodes, state.name, opts)
444
423 445 lock = repo.lock()
446
447 util.rename(repo.join('unshelverebasestate'),
448 repo.join('rebasestate'))
449 try:
450 rebase.rebase(ui, repo, **{
451 'continue' : True
452 })
453 except Exception:
454 util.rename(repo.join('rebasestate'),
455 repo.join('unshelverebasestate'))
456 raise
457
458 shelvectx = repo['tip']
459 if not shelvectx in state.pendingctx.children():
460 # rebase was a no-op, so it produced no child commit
461 shelvectx = state.pendingctx
462
463 mergefiles(ui, repo, state.wctx, shelvectx, state.unknownfiles)
464
465 state.stripnodes.append(shelvectx.node())
424 466 repair.strip(ui, repo, state.stripnodes, backup='none', topic='shelve')
467 shelvedstate.clear(repo)
425 468 unshelvecleanup(ui, repo, state.name, opts)
426 469 ui.status(_("unshelve of '%s' complete\n") % state.name)
427 470 finally:
@@ -491,71 +534,96 b' def unshelve(ui, repo, *shelved, **opts)'
491 534
492 535 shelvedfiles = readshelvedfiles(repo, basename)
493 536
494 m, a, r, d = repo.status()[:4]
495 unsafe = set(m + a + r + d).intersection(shelvedfiles)
496 if unsafe:
497 ui.warn(_('the following shelved files have been modified:\n'))
498 for f in sorted(unsafe):
499 ui.warn(' %s\n' % f)
500 ui.warn(_('you must commit, revert, or shelve your changes before you '
501 'can proceed\n'))
502 raise util.Abort(_('cannot unshelve due to local changes\n'))
503
504 537 wlock = lock = tr = None
505 538 try:
506 539 lock = repo.lock()
540 wlock = repo.wlock()
507 541
508 542 tr = repo.transaction('unshelve', report=lambda x: None)
509 543 oldtiprev = len(repo)
544
545 wctx = repo['.']
546 tmpwctx = wctx
547 # The goal is to have a commit structure like so:
548 # ...-> wctx -> tmpwctx -> shelvectx
549 # where tmpwctx is an optional commit with the user's pending changes
550 # and shelvectx is the unshelved changes. Then we merge it all down
551 # to the original wctx.
552
553 # Store pending changes in a commit
554 m, a, r, d, u = repo.status(unknown=True)[:5]
555 if m or a or r or d or u:
556 def commitfunc(ui, repo, message, match, opts):
557 hasmq = util.safehasattr(repo, 'mq')
558 if hasmq:
559 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
560
561 try:
562 return repo.commit(message, 'shelve@localhost',
563 opts.get('date'), match)
564 finally:
565 if hasmq:
566 repo.mq.checkapplied = saved
567
568 tempopts = {}
569 tempopts['message'] = "pending changes temporary commit"
570 tempopts['addremove'] = True
571 oldquiet = ui.quiet
572 try:
573 ui.quiet = True
574 node = cmdutil.commit(ui, repo, commitfunc, None, tempopts)
575 finally:
576 ui.quiet = oldquiet
577 tmpwctx = repo[node]
578
510 579 try:
511 580 fp = shelvedfile(repo, basename, 'hg').opener()
512 581 gen = changegroup.readbundle(fp, fp.name)
513 582 repo.addchangegroup(gen, 'unshelve', 'bundle:' + fp.name)
514 583 nodes = [ctx.node() for ctx in repo.set('%d:', oldtiprev)]
515 584 phases.retractboundary(repo, phases.secret, nodes)
516 tr.close()
517 585 finally:
518 586 fp.close()
519 587
520 tip = repo['tip']
521 wctx = repo['.']
522 ancestor = tip.ancestor(wctx)
523
524 wlock = repo.wlock()
588 shelvectx = repo['tip']
525 589
526 if ancestor.node() != wctx.node():
527 conflicts = hg.merge(repo, tip.node(), force=True, remind=False)
528 ms = merge.mergestate(repo)
529 stripnodes = [repo.changelog.node(rev)
530 for rev in xrange(oldtiprev, len(repo))]
531 if conflicts:
532 shelvedstate.save(repo, basename, stripnodes)
533 # Fix up the dirstate entries of files from the second
534 # parent as if we were not merging, except for those
535 # with unresolved conflicts.
536 parents = repo.parents()
537 revertfiles = set(parents[1].files()).difference(ms)
538 cmdutil.revert(ui, repo, parents[1],
539 (parents[0].node(), nullid),
540 *pathtofiles(repo, revertfiles),
541 **{'no_backup': True})
590 # If the shelve is not immediately on top of the commit
591 # we'll be merging with, rebase it to be on top.
592 if tmpwctx.node() != shelvectx.parents()[0].node():
593 try:
594 rebase.rebase(ui, repo, **{
595 'rev' : [shelvectx.rev()],
596 'dest' : str(tmpwctx.rev()),
597 'keep' : True,
598 })
599 except error.InterventionRequired:
600 tr.close()
601
602 stripnodes = [repo.changelog.node(rev)
603 for rev in xrange(oldtiprev, len(repo))]
604 shelvedstate.save(repo, basename, wctx, tmpwctx, stripnodes, u)
605
606 util.rename(repo.join('rebasestate'),
607 repo.join('unshelverebasestate'))
542 608 raise error.InterventionRequired(
543 609 _("unresolved conflicts (see 'hg resolve', then "
544 610 "'hg unshelve --continue')"))
545 finishmerge(ui, repo, ms, stripnodes, basename, opts)
546 else:
547 parent = tip.parents()[0]
548 hg.update(repo, parent.node())
549 cmdutil.revert(ui, repo, tip, repo.dirstate.parents(),
550 *pathtofiles(repo, tip.files()),
551 **{'no_backup': True})
611
612 # refresh ctx after rebase completes
613 shelvectx = repo['tip']
614
615 if not shelvectx in tmpwctx.children():
616 # rebase was a no-op, so it produced no child commit
617 shelvectx = tmpwctx
552 618
553 prevquiet = ui.quiet
554 ui.quiet = True
555 try:
556 repo.rollback(force=True)
557 finally:
558 ui.quiet = prevquiet
619 mergefiles(ui, repo, wctx, shelvectx, u)
620 shelvedstate.clear(repo)
621
622 # The transaction aborting will strip all the commits for us,
623 # but it doesn't update the inmemory structures, so addchangegroup
624 # hooks still fire and try to operate on the missing commits.
625 # Clean up manually to prevent this.
626 repo.changelog.strip(oldtiprev, tr)
559 627
560 628 unshelvecleanup(ui, repo, basename, opts)
561 629 finally:
@@ -27,7 +27,6 b' shelving in an empty repo should be poss'
27 27 adding manifests
28 28 adding file changes
29 29 added 1 changesets with 5 changes to 5 files
30 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
31 30
32 31 $ hg commit -q -m 'initial commit'
33 32
@@ -100,19 +99,19 b' delete our older shelved change'
100 99 $ hg shelve -d default
101 100 $ hg qfinish -a -q
102 101
103 local edits should prevent a shelved change from applying
102 local edits should not prevent a shelved change from applying
104 103
105 $ echo e>>a/a
106 $ hg unshelve
104 $ printf "z\na\n" > a/a
105 $ hg unshelve --keep
107 106 unshelving change 'default-01'
108 the following shelved files have been modified:
109 a/a
110 you must commit, revert, or shelve your changes before you can proceed
111 abort: cannot unshelve due to local changes
112
113 [255]
107 adding changesets
108 adding manifests
109 adding file changes
110 added 1 changesets with 3 changes to 8 files (+1 heads)
111 merging a/a
114 112
115 $ hg revert -C a/a
113 $ hg revert --all -q
114 $ rm a/a.orig b.rename/b c.copy
116 115
117 116 apply it and make sure our state is as expected
118 117
@@ -122,7 +121,6 b' apply it and make sure our state is as e'
122 121 adding manifests
123 122 adding file changes
124 123 added 1 changesets with 3 changes to 8 files
125 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
126 124 $ hg status -C
127 125 M a/a
128 126 A b.rename/b
@@ -201,24 +199,21 b' force a conflicted merge to occur'
201 199 merging a/a
202 200 warning: conflicts during merge.
203 201 merging a/a incomplete! (edit conflicts, then use 'hg resolve --mark')
204 2 files updated, 0 files merged, 1 files removed, 1 files unresolved
205 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
206 202 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
207 203 [1]
208 204
209 205 ensure that we have a merge with unresolved conflicts
210 206
211 $ hg heads -q
212 4:cebf2b8de087
213 3:2e69b451d1ea
214 $ hg parents -q
215 3:2e69b451d1ea
216 4:cebf2b8de087
207 $ hg heads -q --template '{rev}\n'
208 5
209 4
210 $ hg parents -q --template '{rev}\n'
211 4
212 5
217 213 $ hg status
218 214 M a/a
219 215 M b.rename/b
220 216 M c.copy
221 A foo/foo
222 217 R b/b
223 218 ? a/a.orig
224 219 $ hg diff
@@ -248,12 +243,6 b' ensure that we have a merge with unresol'
248 243 +++ b/c.copy
249 244 @@ -0,0 +1,1 @@
250 245 +c
251 diff --git a/foo/foo b/foo/foo
252 new file mode 100644
253 --- /dev/null
254 +++ b/foo/foo
255 @@ -0,0 +1,1 @@
256 +foo
257 246 $ hg resolve -l
258 247 U a/a
259 248
@@ -268,10 +257,10 b' abort the unshelve and be happy'
268 257 M a/a
269 258 M b.rename/b
270 259 M c.copy
271 A foo/foo
272 260 R b/b
273 261 ? a/a.orig
274 262 $ hg unshelve -a
263 rebase aborted
275 264 unshelve of 'default' aborted
276 265 $ hg heads -q
277 266 3:2e69b451d1ea
@@ -330,9 +319,9 b' ensure the repo is as we hope'
330 319 3:2e69b451d1ea
331 320
332 321 $ hg status -C
333 M b.rename/b
322 A b.rename/b
334 323 b/b
335 M c.copy
324 A c.copy
336 325 c
337 326 A foo/foo
338 327 R b/b
@@ -372,6 +361,7 b' ensure that metadata-only changes are sh'
372 361 set up another conflict between a commit and a shelved change
373 362
374 363 $ hg revert -q -C -a
364 $ rm a/a.orig b.rename/b c.copy
375 365 $ echo a >> a/a
376 366 $ hg shelve -q
377 367 $ echo x >> a/a
@@ -387,7 +377,6 b' if we resolve a conflict while unshelvin'
387 377 adding file changes
388 378 added 1 changesets with 1 changes to 6 files (+1 heads)
389 379 merging a/a
390 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
391 380 $ hg parents -q
392 381 4:33f7f61e6c5e
393 382 $ hg shelve -l
@@ -411,7 +400,6 b' test keep and cleanup'
411 400 adding manifests
412 401 adding file changes
413 402 added 1 changesets with 1 changes to 7 files
414 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
415 403 $ hg shelve --list
416 404 default (*) create conflict (glob)
417 405 $ hg shelve --cleanup
@@ -433,7 +421,6 b' test bookmarks'
433 421 adding manifests
434 422 adding file changes
435 423 added 1 changesets with 1 changes to 7 files
436 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
437 424 $ hg bookmark
438 425 * test 4:33f7f61e6c5e
439 426
@@ -450,7 +437,6 b' shelve should still work even if mq is d'
450 437 adding manifests
451 438 adding file changes
452 439 added 1 changesets with 1 changes to 7 files
453 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
454 440
455 441 shelve should leave dirstate clean (issue 4055)
456 442
@@ -479,8 +465,52 b' shelve should leave dirstate clean (issu'
479 465 adding manifests
480 466 adding file changes
481 467 added 2 changesets with 2 changes to 2 files (+1 heads)
482 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
483 468 $ hg status
484 469 M z
485 470
486 471 $ cd ..
472
473 shelve should only unshelve pending changes (issue 4068)
474
475 $ hg init onlypendingchanges
476 $ cd onlypendingchanges
477 $ touch a
478 $ hg ci -Aqm a
479 $ touch b
480 $ hg ci -Aqm b
481 $ hg up -q 0
482 $ touch c
483 $ hg ci -Aqm c
484
485 $ touch d
486 $ hg add d
487 $ hg shelve
488 shelved as default
489 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
490 $ hg up -q 1
491 $ hg unshelve
492 unshelving change 'default'
493 adding changesets
494 adding manifests
495 adding file changes
496 added 1 changesets with 1 changes to 3 files
497 $ hg status
498 A d
499
500 unshelve should work on an ancestor of the original commit
501
502 $ hg shelve
503 shelved as default
504 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
505 $ hg up 0
506 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
507 $ hg unshelve
508 unshelving change 'default'
509 adding changesets
510 adding manifests
511 adding file changes
512 added 1 changesets with 1 changes to 3 files
513 $ hg status
514 A d
515
516 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now