diff --git a/hgext/shelve.py b/hgext/shelve.py --- a/hgext/shelve.py +++ b/hgext/shelve.py @@ -714,6 +714,15 @@ ui.status(_('marked working directory as branch %s\n') % branchtorestore) +def restoreunresolvedshelve(repo, ui, basename, pctx, shelvectx): + p1, p2 = shelvectx.parents() + with repo.dirstate.parentchange(): + repo.dirstate.setparents(p1.node(), p2.node()) + + # Replace `merge/` with `merge-unresolved//` to + # restore the status of resolved files in shelvectx. + repo.vfs.rename('merge-unresolved/%s' % basename, 'merge') + def unshelvecleanup(ui, repo, name, opts): """remove related files after an unshelve""" if not opts.get('keep'): @@ -918,7 +927,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 [[-n] SHELVED]'), helpcategory=command.CATEGORY_WORKING_DIRECTORY) def unshelve(ui, repo, *shelved, **opts): @@ -964,6 +975,7 @@ opts = pycompat.byteskwargs(opts) abortf = opts.get('abort') continuef = opts.get('continue') + unresolved = opts.get('unresolved') if not abortf and not continuef: cmdutil.checkunfinished(repo) shelved = list(shelved) @@ -1019,6 +1031,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 @@ -1039,18 +1053,34 @@ tmpwctx, addedbefore = _commitworkingcopychanges(ui, repo, opts, tmpwctx) repo, shelvectx = _unshelverestorecommit(ui, repo, tr, basename) + unresolvedshelve = shelvectx.extra().get('unresolved-merge') _checkunshelveuntrackedproblems(ui, repo, shelvectx) branchtorestore = '' if shelvectx.branch() != shelvectx.p1().branch(): branchtorestore = shelvectx.branch() - shelvectx = _rebaserestoredcommit(ui, repo, opts, tr, oldtiprev, - basename, pctx, tmpwctx, - shelvectx, branchtorestore, - activebookmark) + if unresolved: + 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 = _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: + restoreunresolvedshelve(repo, ui, basename, pctx, shelvectx) restorebranch(ui, repo, branchtorestore) _forgetunknownfiles(repo, shelvectx, addedbefore) 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 @@ -1,3 +1,17 @@ + $ addunresolvedmerge() { + > echo A >> $1 + > echo A >> $2 + > hg ci -m A + > echo B >> $1 + > echo B >> $2 + > hg ci -m B + > hg up $3 + > echo C >> $1 + > echo C >> $2 + > hg ci -m C + > hg merge -r $(($3+1)) + > } + Test shelve with unresolved mergestate $ cat >> $HGRCPATH <>>>>>> 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 --unsresolved 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: cannot unshelve while merging + [255]