--- a/mercurial/commands.py Sun Jul 07 10:54:41 2019 -0400
+++ b/mercurial/commands.py Tue Jul 02 18:02:12 2019 +0530
@@ -6168,6 +6168,8 @@
_('abort an incomplete unshelve operation')),
('c', 'continue', None,
_('continue an incomplete unshelve operation')),
+ ('i', 'interactive', None,
+ _('use interactive mode')),
('k', 'keep', None,
_('keep shelve after unshelving')),
('n', 'name', '',
@@ -6175,7 +6177,7 @@
('t', 'tool', '', _('specify merge tool')),
('', 'date', '',
_('set date for temporary commits (DEPRECATED)'), _('DATE'))],
- _('hg unshelve [[-n] SHELVED]'),
+ _('hg unshelve [OPTION]... [FILE]... [-n SHELVED]'),
helpcategory=command.CATEGORY_WORKING_DIRECTORY)
def unshelve(ui, repo, *shelved, **opts):
"""restore a shelved change to the working directory
--- a/mercurial/shelve.py Sun Jul 07 10:54:41 2019 -0400
+++ b/mercurial/shelve.py Tue Jul 02 18:02:12 2019 +0530
@@ -694,10 +694,11 @@
if shfile.exists():
shfile.movetobackup()
cleanupoldbackups(repo)
-def unshelvecontinue(ui, repo, state, opts):
+def unshelvecontinue(ui, repo, state, opts, basename=None):
"""subcommand to continue an in-progress unshelve"""
# We're finishing off a merge. First parent is our original
# parent, second is the temporary "fake" commit we're unshelving.
+ interactive = opts.get('interactive')
with repo.lock():
checkparents(repo, state)
ms = merge.mergestate.read(repo)
@@ -720,10 +721,15 @@
with repo.ui.configoverride(overrides, 'unshelve'):
with repo.dirstate.parentchange():
repo.setparents(state.parents[0], nodemod.nullid)
- newnode = repo.commit(text=shelvectx.description(),
- extra=shelvectx.extra(),
- user=shelvectx.user(),
- date=shelvectx.date())
+ if not interactive:
+ ispartialunshelve = False
+ newnode = repo.commit(text=shelvectx.description(),
+ extra=shelvectx.extra(),
+ user=shelvectx.user(),
+ date=shelvectx.date())
+ else:
+ newnode, ispartialunshelve = _dounshelveinteractive(ui,
+ repo, shelvectx, basename, opts)
if newnode is None:
# If it ended up being a no-op commit, then the normal
@@ -743,12 +749,13 @@
mergefiles(ui, repo, state.wctx, shelvectx)
restorebranch(ui, repo, state.branchtorestore)
- if not phases.supportinternal(repo):
- repair.strip(ui, repo, state.nodestoremove, backup=False,
- topic='shelve')
+ if not ispartialunshelve:
+ if not phases.supportinternal(repo):
+ repair.strip(ui, repo, state.nodestoremove, backup=False,
+ topic='shelve')
+ shelvedstate.clear(repo)
+ unshelvecleanup(ui, repo, state.name, opts)
_restoreactivebookmark(repo, state.activebookmark)
- shelvedstate.clear(repo)
- unshelvecleanup(ui, repo, state.name, opts)
ui.status(_("unshelve of '%s' complete\n") % state.name)
def hgcontinueunshelve(ui, repo):
@@ -797,14 +804,40 @@
return repo, shelvectx
+def _dounshelveinteractive(ui, repo, shelvectx, basename, opts):
+ """The user might want to unshelve certain changes only from the stored
+ shelve. So, we would create two commits. One with requested changes to
+ unshelve at that time and the latter is shelved for future.
+ """
+ opts['message'] = shelvectx.description()
+ opts['interactive-unshelve'] = True
+ pats = []
+ commitfunc = getcommitfunc(shelvectx.extra(), interactive=True,
+ editor=True)
+ newnode = cmdutil.dorecord(ui, repo, commitfunc, None, False,
+ cmdutil.recordfilter, *pats,
+ **pycompat.strkwargs(opts))
+ snode = repo.commit(text=shelvectx.description(),
+ extra=shelvectx.extra(),
+ user=shelvectx.user(),
+ date=shelvectx.date())
+ m = scmutil.matchfiles(repo, repo[snode].files())
+ if snode:
+ _shelvecreatedcommit(repo, snode, basename, m)
+
+ return newnode, bool(snode)
+
def _rebaserestoredcommit(ui, repo, opts, tr, oldtiprev, basename, pctx,
tmpwctx, shelvectx, branchtorestore,
activebookmark):
"""Rebase restored commit from its original location to a destination"""
# If the shelve is not immediately on top of the commit
# we'll be merging with, rebase it to be on top.
- if tmpwctx.node() == shelvectx.p1().node():
- return shelvectx
+ interactive = opts.get('interactive')
+ if tmpwctx.node() == shelvectx.p1().node() and not interactive:
+ # We won't skip on interactive mode because, the user might want to
+ # unshelve certain changes only.
+ return shelvectx, False
overrides = {
('ui', 'forcemerge'): opts.get('tool', ''),
@@ -828,10 +861,15 @@
with repo.dirstate.parentchange():
repo.setparents(tmpwctx.node(), nodemod.nullid)
- newnode = repo.commit(text=shelvectx.description(),
- extra=shelvectx.extra(),
- user=shelvectx.user(),
- date=shelvectx.date())
+ if not interactive:
+ ispartialunshelve = False
+ newnode = repo.commit(text=shelvectx.description(),
+ extra=shelvectx.extra(),
+ user=shelvectx.user(),
+ date=shelvectx.date())
+ else:
+ newnode, ispartialunshelve = _dounshelveinteractive(ui, repo,
+ shelvectx, basename, opts)
if newnode is None:
# If it ended up being a no-op commit, then the normal
@@ -846,7 +884,7 @@
shelvectx = repo[newnode]
hg.updaterepo(repo, tmpwctx.node(), False)
- return shelvectx
+ return shelvectx, ispartialunshelve
def _forgetunknownfiles(repo, shelvectx, addedbefore):
# Forget any files that were unknown before the shelve, unknown before
@@ -883,13 +921,14 @@
opts = pycompat.byteskwargs(opts)
abortf = opts.get('abort')
continuef = opts.get('continue')
+ interactive = opts.get('interactive')
if not abortf and not continuef:
cmdutil.checkunfinished(repo)
shelved = list(shelved)
if opts.get("name"):
shelved.append(opts["name"])
- if abortf or continuef:
+ if abortf or continuef and not interactive:
if abortf and continuef:
raise error.Abort(_('cannot use both abort and continue'))
if shelved:
@@ -911,8 +950,11 @@
raise error.Abort(_('no shelved changes to apply!'))
basename = util.split(shelved[0][1])[1]
ui.status(_("unshelving change '%s'\n") % basename)
- else:
+ elif shelved:
basename = shelved[0]
+ if continuef and interactive:
+ state = _loadshelvedstate(ui, repo, opts)
+ return unshelvecontinue(ui, repo, state, opts, basename)
if not shelvedfile(repo, basename, patchextension).exists():
raise error.Abort(_("shelved change '%s' not found") % basename)
@@ -941,19 +983,19 @@
if shelvectx.branch() != shelvectx.p1().branch():
branchtorestore = shelvectx.branch()
- shelvectx = _rebaserestoredcommit(ui, repo, opts, tr, oldtiprev,
- basename, pctx, tmpwctx,
- shelvectx, branchtorestore,
- activebookmark)
+ shelvectx, ispartialunshelve = _rebaserestoredcommit(ui, repo, opts,
+ tr, oldtiprev, basename, pctx, tmpwctx, shelvectx,
+ branchtorestore, activebookmark)
overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
with ui.configoverride(overrides, 'unshelve'):
mergefiles(ui, repo, pctx, shelvectx)
restorebranch(ui, repo, branchtorestore)
- _forgetunknownfiles(repo, shelvectx, addedbefore)
+ if not ispartialunshelve:
+ _forgetunknownfiles(repo, shelvectx, addedbefore)
- shelvedstate.clear(repo)
- _finishunshelve(repo, oldtiprev, tr, activebookmark)
- unshelvecleanup(ui, repo, basename, opts)
+ shelvedstate.clear(repo)
+ _finishunshelve(repo, oldtiprev, tr, activebookmark)
+ unshelvecleanup(ui, repo, basename, opts)
finally:
if tr:
tr.release()
--- a/tests/test-shelve.t Sun Jul 07 10:54:41 2019 -0400
+++ b/tests/test-shelve.t Tue Jul 02 18:02:12 2019 +0530
@@ -1158,3 +1158,228 @@
[255]
$ cd ..
+
+-- test for interactive mode on unshelve
+
+ $ hg init a
+ $ cd a
+ $ echo > b
+ $ hg ci -Am b
+ adding b
+ $ echo > c
+ $ echo > d
+ $ hg add .
+ adding c
+ adding d
+ $ hg shelve
+ shelved as default
+ 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+ $ echo > e
+ $ hg add e
+ $ hg ci -m e
+ $ hg shelve --patch
+ default (1s ago) changes to: b
+
+ diff --git a/c b/c
+ new file mode 100644
+ --- /dev/null
+ +++ b/c
+ @@ -0,0 +1,1 @@
+ +
+ diff --git a/d b/d
+ new file mode 100644
+ --- /dev/null
+ +++ b/d
+ @@ -0,0 +1,1 @@
+ +
+ $ hg unshelve -i <<EOF
+ > y
+ > y
+ > y
+ > n
+ > EOF
+ unshelving change 'default'
+ rebasing shelved changes
+ diff --git a/c b/c
+ new file mode 100644
+ examine changes to 'c'?
+ (enter ? for help) [Ynesfdaq?] y
+
+ @@ -0,0 +1,1 @@
+ +
+ record change 1/2 to 'c'?
+ (enter ? for help) [Ynesfdaq?] y
+
+ diff --git a/d b/d
+ new file mode 100644
+ examine changes to 'd'?
+ (enter ? for help) [Ynesfdaq?] y
+
+ @@ -0,0 +1,1 @@
+ +
+ record change 2/2 to 'd'?
+ (enter ? for help) [Ynesfdaq?] n
+
+ $ ls
+ b
+ c
+ e
+-- shelve should not contain `c` now
+ $ hg shelve --patch
+ default (1s ago) changes to: b
+
+ diff --git a/d b/d
+ new file mode 100644
+ --- /dev/null
+ +++ b/d
+ @@ -0,0 +1,1 @@
+ +
+ $ hg unshelve -i <<EOF
+ > y
+ > y
+ > EOF
+ unshelving change 'default'
+ rebasing shelved changes
+ diff --git a/d b/d
+ new file mode 100644
+ examine changes to 'd'?
+ (enter ? for help) [Ynesfdaq?] y
+
+ @@ -0,0 +1,1 @@
+ +
+ record this change to 'd'?
+ (enter ? for help) [Ynesfdaq?] y
+
+ $ ls
+ b
+ c
+ d
+ e
+ $ hg shelve --list
+
+-- now, unshelve selected changes from a file
+
+ $ echo B > foo
+ $ hg add foo
+ $ hg ci -m 'add B to foo'
+ $ cat > foo <<EOF
+ > A
+ > B
+ > C
+ > EOF
+ $ hg shelve
+ shelved as default
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ cat foo
+ B
+ $ hg unshelve -i <<EOF
+ > y
+ > y
+ > n
+ > EOF
+ unshelving change 'default'
+ rebasing shelved changes
+ diff --git a/foo b/foo
+ 2 hunks, 2 lines changed
+ examine changes to 'foo'?
+ (enter ? for help) [Ynesfdaq?] y
+
+ @@ -1,1 +1,2 @@
+ +A
+ B
+ record change 1/2 to 'foo'?
+ (enter ? for help) [Ynesfdaq?] y
+
+ @@ -1,1 +2,2 @@
+ B
+ +C
+ record change 2/2 to 'foo'?
+ (enter ? for help) [Ynesfdaq?] n
+
+ $ cat foo
+ A
+ B
+ $ hg shelve --patch
+ default (1s ago) changes to: add B to foo
+
+ diff --git a/foo b/foo
+ --- a/foo
+ +++ b/foo
+ @@ -1,2 +1,3 @@
+ A
+ B
+ +C
+
+-- unshelve interactive on conflicts
+
+ $ echo A >> bar1
+ $ echo A >> bar2
+ $ hg add bar1 bar2
+ $ hg ci -m 'add A to bars'
+ $ echo B >> bar1
+ $ echo B >> bar2
+ $ hg shelve
+ shelved as default-01
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ echo C >> bar1
+ $ echo C >> bar2
+ $ hg ci -m 'add C to bars'
+ $ hg unshelve -i
+ unshelving change 'default-01'
+ rebasing shelved changes
+ merging bar1
+ merging bar2
+ warning: conflicts while merging bar1! (edit, then use 'hg resolve --mark')
+ warning: conflicts while merging bar2! (edit, then use 'hg resolve --mark')
+ unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
+ [1]
+
+ $ cat > bar1 <<EOF
+ > A
+ > B
+ > C
+ > EOF
+ $ cat > bar2 <<EOF
+ > A
+ > B
+ > C
+ > EOF
+ $ hg resolve -m bar1 bar2
+ (no more unresolved files)
+ continue: hg unshelve --continue
+ $ cat bar1
+ A
+ B
+ C
+ $ hg unshelve --continue -i <<EOF
+ > y
+ > y
+ > y
+ > y
+ > EOF
+ unshelving change 'default-01'
+ diff --git a/bar1 b/bar1
+ 1 hunks, 1 lines changed
+ examine changes to 'bar1'?
+ (enter ? for help) [Ynesfdaq?] y
+
+ @@ -1,2 +1,3 @@
+ A
+ +B
+ C
+ record change 1/2 to 'bar1'?
+ (enter ? for help) [Ynesfdaq?] y
+
+ diff --git a/bar2 b/bar2
+ 1 hunks, 1 lines changed
+ examine changes to 'bar2'?
+ (enter ? for help) [Ynesfdaq?] y
+
+ @@ -1,2 +1,3 @@
+ A
+ +B
+ C
+ record change 2/2 to 'bar2'?
+ (enter ? for help) [Ynesfdaq?] y
+
+ unshelve of 'default-01' complete