diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -6158,6 +6158,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', '', diff --git a/mercurial/shelve.py b/mercurial/shelve.py --- a/mercurial/shelve.py +++ b/mercurial/shelve.py @@ -34,6 +34,8 @@ bundlerepo, changegroup, cmdutil, + context, + copies as copiesmod, discovery, error, exchange, @@ -747,6 +749,46 @@ tmpwctx = repo[node] return tmpwctx, addedbefore +def _commitfiltered(repo, ctx, match, keepcommit): + """Recommit ctx with changed files not in match. Return the new + node identifier, or None if nothing changed. + """ + base = ctx.p1() + # ctx + initialfiles = set(ctx.files()) + files = set(f for f in initialfiles if match(f)) + + # return the p1 so that we don't create an obsmarker later + if not keepcommit: + return ctx.p1().node() + + # Filter copies + copied = copiesmod.pathcopies(base, ctx) + copied = dict((dst, src) for dst, src in copied.iteritems() + if dst in files) + def filectxfn(repo, memctx, path, contentctx=ctx, redirect=()): + if path not in contentctx: + return None + fctx = contentctx[path] + mctx = context.memfilectx(repo, memctx, fctx.path(), fctx.data(), + fctx.islink(), + fctx.isexec(), + copysource=copied.get(path)) + return mctx + + if not files: + repo.ui.status(_("note: keeping empty commit\n")) + + new = context.memctx(repo, + parents=[base.node(), nodemod.nullid], + text=ctx.description(), + files=files, + filectxfn=filectxfn, + user=ctx.user(), + date=ctx.date(), + extra=ctx.extra()) + return repo[repo.commitctx(new)] + def _unshelverestorecommit(ui, repo, tr, basename): """Recreate commit in the repository during the unshelve""" repo = repo.unfiltered() @@ -852,6 +894,7 @@ 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) @@ -942,15 +985,37 @@ basename, pctx, tmpwctx, shelvectx, branchtorestore, activebookmark) + files = shelvectx.files() + filesinclude = [] + filesexclude = [] + if interactive: + for file in files: + choice = ui.promptchoice( + _('unshelve changes in file %s (Yn)?$$ &Yes $$ &No') + % file) + if choice == 0: + filesinclude.append(file) + else: + filesexclude.append(file) + + match = scmutil.match(shelvectx, filesinclude, {}) + match2 = scmutil.match(shelvectx, filesexclude, {}) + shelvectxbackup = shelvectx + shelvectx = _commitfiltered(repo, shelvectx, match, + keepcommit=True) + shelvectxnew = _commitfiltered(repo, shelvectxbackup, match2, + keepcommit=True) + _shelvecreatedcommit(repo, shelvectxnew.node(), basename, match2) 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 interactive or (interactive and files == filesinclude): + _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() diff --git a/tests/test-completion.t b/tests/test-completion.t --- a/tests/test-completion.t +++ b/tests/test-completion.t @@ -349,7 +349,7 @@ tags: template tip: patch, git, style, template unbundle: update - unshelve: abort, continue, keep, name, tool, date + unshelve: abort, continue, interactive, keep, name, tool, date update: clean, check, merge, date, rev, tool verify: full version: template diff --git a/tests/test-shelve.t b/tests/test-shelve.t --- a/tests/test-shelve.t +++ b/tests/test-shelve.t @@ -1155,3 +1155,67 @@ [255] $ cd .. + +-- unshelve --interactive + + $ 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< y + > n + > EOF + unshelving change 'default' + rebasing shelved changes + unshelve changes in file c (Yn)? y + unshelve changes in file d (Yn)? 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 + unshelving change 'default' + $ ls + b + c + d + e + $ hg shelve --list