diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -6176,7 +6176,9 @@ _('restore shelved change with given name'), _('NAME')), ('t', 'tool', '', _('specify merge tool')), ('', 'date', '', - _('set date for temporary commits (DEPRECATED)'), _('DATE'))], + _('set date for temporary commits (DEPRECATED)'), _('DATE')), + ('', 'unresolved', None, + _('unshelve mergestate with unresolved files'))], _('hg unshelve [OPTION]... [FILE]... [-n SHELVED]'), helpcategory=command.CATEGORY_WORKING_DIRECTORY) def unshelve(ui, repo, *shelved, **opts): diff --git a/mercurial/shelve.py b/mercurial/shelve.py --- a/mercurial/shelve.py +++ b/mercurial/shelve.py @@ -58,6 +58,7 @@ ) _pack = struct.pack +_unpack = struct.unpack backupdir = 'shelve-backup' shelvedir = 'shelved' @@ -445,6 +446,10 @@ extra['shelve_unknown'] = '\0'.join(s.unknown) repo[None].add(s.unknown) +def _isunresolvedshelve(shelvectx): + """Checks whether the given shelve is unresolved or not""" + return shelvectx.extra().get('unresolved-merge') + def _finishshelve(repo, tr): if phases.supportinternal(repo): tr.close() @@ -731,6 +736,33 @@ ui.status(_('marked working directory as branch %s\n') % branchtorestore) +def restoreunresolvedshelve(ui, repo, shelvectx, basename): + """Change the parents of the dirstate to the parents of the stored + unresolved shelvectx. + + Rewrite the mergestate from changeset extra to restore the status + of the resolved files in the shelvectx. + """ + p1, p2 = shelvectx.parents() + repo.dirstate.setparents(p1.node(), p2.node()) + + data = shelvectx.extra().get('mergerecords') + records = [] + off = 0 + end = len(data) + while off < end: + rtype = data[off:off + 1] + off += 1 + length = _unpack('>I', data[off:(off + 4)])[0] + off += 4 + record = data[off:(off + length)] + off += length + if rtype == RECORD_OVERRIDE: + rtype, record = record[0:1], record[1:] + records.append((rtype, record)) + ms = merge.mergestate.clean(repo) + ms._writerecordsv2(records) + def unshelvecleanup(ui, repo, name, opts): """remove related files after an unshelve""" if not opts.get('keep'): @@ -977,6 +1009,7 @@ abortf = opts.get('abort') continuef = opts.get('continue') interactive = opts.get('interactive') + unresolved = opts.get('unresolved') if not abortf and not continuef: cmdutil.checkunfinished(repo) shelved = list(shelved) @@ -1014,6 +1047,8 @@ if not shelvedfile(repo, basename, patchextension).exists(): raise error.Abort(_("shelved change '%s' not found") % basename) + if unresolved: + cmdutil.bailifchanged(repo) repo = repo.unfiltered() lock = tr = None @@ -1034,17 +1069,35 @@ tmpwctx, addedbefore = _commitworkingcopychanges(ui, repo, opts, tmpwctx) repo, shelvectx = _unshelverestorecommit(ui, repo, tr, basename) + unresolvedshelve = _isunresolvedshelve(shelvectx) _checkunshelveuntrackedproblems(ui, repo, shelvectx) branchtorestore = '' if shelvectx.branch() != shelvectx.p1().branch(): branchtorestore = shelvectx.branch() - shelvectx, ispartialunshelve = _rebaserestoredcommit(ui, repo, opts, - tr, oldtiprev, basename, pctx, tmpwctx, shelvectx, - branchtorestore, activebookmark) + if unresolved: + ispartialunshelve = False + if not unresolvedshelve: + raise error.Abort(_('%s is not an unresolved shelve\n') % + basename) + p1, p2 = shelvectx.parents() + if pctx.node() not in [p1.node(), p2.node()]: + raise error.Abort(_('dirstate is not on either of the merge' + ' parents.\nuse hg update to one of the' + ' merge parents.')) + elif unresolvedshelve: + raise error.Abort(_('%s is an unresolved shelve, use' + ' --unresolved to unshelve it') % basename) + else: + 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) + if unresolved: + with repo.dirstate.parentchange(): + restoreunresolvedshelve(ui, repo, shelvectx, basename) restorebranch(ui, repo, branchtorestore) shelvedstate.clear(repo) _finishunshelve(repo, oldtiprev, tr, activebookmark) diff --git a/tests/test-completion.t b/tests/test-completion.t --- a/tests/test-completion.t +++ b/tests/test-completion.t @@ -354,7 +354,7 @@ tags: template tip: patch, git, style, template unbundle: update - unshelve: abort, continue, interactive, keep, name, tool, date + unshelve: abort, continue, interactive, keep, name, tool, date, unresolved update: clean, check, merge, date, rev, tool verify: full version: template diff --git a/tests/test-shelve-unresolved.t b/tests/test-shelve-unresolved.t --- a/tests/test-shelve-unresolved.t +++ b/tests/test-shelve-unresolved.t @@ -171,3 +171,290 @@ +======= +B +>>>>>>> merge rev: fd9a4049234b - test: B + +-- now, fix an urgent bug + $ echo fixed >> bug + $ ls + bar + bug + file1 + file1.orig + file2 + file2.orig + $ hg add bug + $ hg ci -m "fix bug" + $ hg log -G + @ changeset: 3:a53a9a7475b3 + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: fix bug + | + o changeset: 2:69004294ad57 + | parent: 0:c32ef6121744 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: C + | + | o changeset: 1:fd9a4049234b + |/ user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: B + | + o changeset: 0:c32ef6121744 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: A + +-- let's get back to the old mergestate +-- we need to update to one of the merge parents. otherwise, abort. + $ hg unshelve --unresolved + unshelving change 'default' + abort: dirstate is not on either of the merge parents. + use hg update to one of the merge parents. + [255] + + $ hg up 2 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ cat file2 + A + C + +-- flag --unshelve is not passed. but, the last shelve is unresolved + $ hg unshelve + unshelving change 'default' + abort: default is an unresolved shelve, use --unresolved to unshelve it + [255] + + $ hg unshelve --unresolved + unshelving change 'default' + + $ hg log -G + o changeset: 3:a53a9a7475b3 + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: fix bug + | + @ changeset: 2:69004294ad57 + | parent: 0:c32ef6121744 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: C + | + | @ changeset: 1:fd9a4049234b + |/ user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: B + | + o changeset: 0:c32ef6121744 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: A + + $ cat file1 + A + B + C + $ hg diff + diff -r 69004294ad57 file1 + --- a/file1 Thu Jan 01 00:00:00 1970 +0000 + +++ b/file1 Thu Jan 01 00:00:00 1970 +0000 + @@ -1,2 +1,3 @@ + A + +B + C + diff -r 69004294ad57 file2 + --- a/file2 Thu Jan 01 00:00:00 1970 +0000 + +++ b/file2 Thu Jan 01 00:00:00 1970 +0000 + @@ -1,2 +1,6 @@ + A + +<<<<<<< working copy: 69004294ad57 - test: C + C + +======= + +B + +>>>>>>> merge rev: fd9a4049234b - test: B + $ cat file2 + A + <<<<<<< working copy: 69004294ad57 - test: C + C + ======= + B + >>>>>>> merge rev: fd9a4049234b - test: B + $ hg resolve -l + R file1 + U file2 + +-- flag --unresolved is passed but the top most shelve is not unresolved + $ hg shelve --unresolved + shelved as default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + + $ echo garbage >> bug + $ hg st + ? bar + ? bug + ? file1.orig + ? file2.orig + $ hg add bug + $ hg shelve + shelved as default-01 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg unshelve --unresolved + unshelving change 'default-01' + abort: default-01 is not an unresolved shelve + + [255] + +-- now, unshelve default + $ hg unshelve -n default --unresolved + +-- commit the merge after completing conflict resolution + $ cat >> file2 < A + > B + > C + > EOF + $ hg resolve -m file2 + (no more unresolved files) + $ hg ci -m merge + $ hg verify + checking changesets + checking manifests + crosschecking files in changesets and manifests + checking files + checked 5 changesets with 9 changes to 3 files + $ hg log -G + @ changeset: 4:745dca2ee1f1 + |\ tag: tip + | | parent: 2:69004294ad57 + | | parent: 1:fd9a4049234b + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: merge + | | + | | o changeset: 3:a53a9a7475b3 + | |/ user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: fix bug + | | + | o changeset: 2:69004294ad57 + | | parent: 0:c32ef6121744 + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: C + | | + o | changeset: 1:fd9a4049234b + |/ user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: B + | + o changeset: 0:c32ef6121744 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: A + + +-- flag --unresolved is passed but we don’t have any unresolved shelve + $ hg unshelve --unresolved + unshelving change 'default-01' + abort: default-01 is not an unresolved shelve + + [255] + +-- when working directory is dirty + $ addunresolvedmerge file1 file2 5 + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + created new head + merging file1 + merging file2 + warning: conflicts while merging file1! (edit, then use 'hg resolve --mark') + warning: conflicts while merging file2! (edit, then use 'hg resolve --mark') + 0 files updated, 0 files merged, 0 files removed, 2 files unresolved + use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon + [1] + + $ hg shelve --unresolved + shelved as default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + + $ echo dirt >> bar + $ hg add bar + $ hg unshelve --unresolved + unshelving change 'default' + abort: uncommitted changes + [255] + +--- unshelve --unresolved when there is another merge going on + $ hg ci -m dirt + $ hg up 7 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ echo foo >> bug + $ hg add bug + $ hg ci -m foo2 + created new head + $ hg log -G + @ changeset: 9:c74a624102ed + | tag: tip + | parent: 7:974ec4298b79 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: foo2 + | + | o changeset: 8:4acf09fb3a59 + |/ user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: dirt + | + o changeset: 7:974ec4298b79 + | parent: 5:db68c6c84fe6 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: C + | + | o changeset: 6:e236d497f76b + |/ user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: B + | + o changeset: 5:db68c6c84fe6 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: A + | + o changeset: 4:745dca2ee1f1 + |\ parent: 2:69004294ad57 + | | parent: 1:fd9a4049234b + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: merge + | | + | | o changeset: 3:a53a9a7475b3 + | |/ user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: fix bug + | | + | o changeset: 2:69004294ad57 + | | parent: 0:c32ef6121744 + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: C + | | + o | changeset: 1:fd9a4049234b + |/ user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: B + | + o changeset: 0:c32ef6121744 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: A + + $ hg merge -r 8 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + + $ hg unshelve --unresolved + abort: outstanding uncommitted merge + (use 'hg commit' or 'hg merge --abort') + [255]