D6479: shelve: first prototype of storing/restoring unresolved changes
navaneeth.suresh (Navaneeth Suresh)
phabricator at mercurial-scm.org
Tue Jun 11 19:45:44 UTC 2019
navaneeth.suresh edited the summary of this revision.
navaneeth.suresh retitled this revision from "unshelve: first prototype of restoring unresolved changes" to "shelve: first prototype of storing/restoring unresolved changes".
navaneeth.suresh updated this revision to Diff 15451.
REPOSITORY
rHG Mercurial
CHANGES SINCE LAST UPDATE
https://phab.mercurial-scm.org/D6479?vs=15449&id=15451
CHANGES SINCE LAST ACTION
https://phab.mercurial-scm.org/D6479/new/
REVISION DETAIL
https://phab.mercurial-scm.org/D6479
AFFECTED FILES
hgext/shelve.py
tests/test-shelve-unresolved.t
tests/test-shelve.t
CHANGE DETAILS
diff --git a/tests/test-shelve.t b/tests/test-shelve.t
--- a/tests/test-shelve.t
+++ b/tests/test-shelve.t
@@ -85,6 +85,7 @@
--stat output diffstat-style summary of changes (provide
the names of the shelved changes as positional
arguments)
+ --unresolved unshelve mergestate with unresolved files
-I --include PATTERN [+] include names matching the given patterns
-X --exclude PATTERN [+] exclude names matching the given patterns
--mq operate on patch repository
diff --git a/tests/test-shelve-unresolved.t b/tests/test-shelve-unresolved.t
new file mode 100644
--- /dev/null
+++ b/tests/test-shelve-unresolved.t
@@ -0,0 +1,452 @@
+ $ 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 <<EOF
+ > [extensions]
+ > shelve =
+ > EOF
+
+ $ hg init shelve-unresolved
+ $ cd shelve-unresolved
+ $ echo A >> file1
+ $ echo A >> file2
+ $ hg ci -Am A
+ adding file1
+ adding file2
+ $ echo foo >> bar
+ $ hg add bar
+
+-- should abort on absence of mergestate
+ $ hg shelve --unresolved
+ abort: no active mergestate found
+ [255]
+
+ $ hg forget bar
+ $ echo B >> file1
+ $ echo B >> file2
+ $ hg ci -m B
+ $ hg up 0
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ echo C >> file1
+ $ echo C >> file2
+ $ hg ci -m C
+ created new head
+ $ hg merge
+ 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]
+
+-- let's partially solve the conflicts
+ $ cat > file1 <<EOF
+ > A
+ > B
+ > C
+ > EOF
+ $ hg resolve -m file1
+
+-- mark file2 as resolved to check abort
+ $ hg resolve -m file2
+ (no more unresolved files)
+ $ hg log -G
+ @ changeset: 2:69004294ad57
+ | tag: tip
+ | 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
+
+-- should abort on absence of conflicts
+ $ hg shelve
+ abort: mergestate found
+ try with --unresolved to shelve conflicts
+ [255]
+ $ hg shelve --unresolved
+ abort: no unresolved files found in the mergestate
+ [255]
+ $ hg resolve -u file2
+ $ hg resolve -l
+ R file1
+ U file2
+
+-- should suggest --unresolved on shelving with mergestate
+ $ hg shelve
+ abort: mergestate found
+ try with --unresolved to shelve conflicts
+ [255]
+
+ $ hg shelve --unresolved
+ shelved as default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ cat file1
+ A
+ C
+ $ hg log -G
+ @ changeset: 2:69004294ad57
+ | tag: tip
+ | 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 shelve --list
+ default (unresolved) (1s ago) changes to: C
+ $ hg shelve --patch
+ default (unresolved) (1s ago) changes to: C
+
+ diff --git a/file1 b/file1
+ --- a/file1
+ +++ b/file1
+ @@ -1,2 +1,3 @@
+ A
+ +B
+ C
+ diff --git a/file2 b/file2
+ --- a/file2
+ +++ b/file2
+ @@ -1,2 +1,6 @@
+ A
+ +<<<<<<< working copy: 69004294ad57 - test: C
+ C
+ +=======
+ +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 <<EOF
+ > 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]
diff --git a/hgext/shelve.py b/hgext/shelve.py
--- a/hgext/shelve.py
+++ b/hgext/shelve.py
@@ -432,6 +432,17 @@
cmdutil.exportfile(repo, [node], fp, opts=mdiff.diffopts(git=True),
match=match)
+def _storeunresolvedmerge(repo, name):
+ """Move the usual mergestate information from `.hg/merge` to
+ `.hg/merge-unresolved/<name>`.
+
+ This will clear the mergestate and also stores the mergestate
+ information for later restoration.
+ """
+ if not repo.vfs.exists('merge-unresolved'):
+ repo.vfs.mkdir('merge-unresolved')
+ repo.vfs.rename('merge', 'merge-unresolved/%s' % name)
+
def _includeunknownfiles(repo, pats, opts, extra):
s = repo.status(match=scmutil.match(repo[None], pats, opts),
unknown=True)
@@ -439,6 +450,13 @@
extra['shelve_unknown'] = '\0'.join(s.unknown)
repo[None].add(s.unknown)
+def _isunresolvedshelve(repo, sname, shelvectx=None):
+ """Checks whether the given shelve is unresolved or not"""
+ if shelvectx is not None:
+ return shelvectx.extra().get('unresolved-merge')
+
+ return repo.vfs.exists('merge-unresolved/%s' % sname)
+
def _finishshelve(repo, tr):
if phases.supportinternal(repo):
tr.close()
@@ -454,10 +472,19 @@
def _docreatecmd(ui, repo, pats, opts):
wctx = repo[None]
parents = wctx.parents()
- if len(parents) > 1:
- raise error.Abort(_('cannot shelve while merging'))
+ unresolved = opts.get('unresolved')
parent = parents[0]
origbranch = wctx.branch()
+ ms = merge.mergestate.read(repo)
+ if unresolved:
+ if not ms.active():
+ raise error.Abort(_('no active mergestate found'))
+ elif not list(ms.unresolved()):
+ raise error.Abort(_('no unresolved '
+ 'files found in the mergestate'))
+ elif ms.active():
+ raise error.Abort(_('mergestate found\n'
+ 'try with --unresolved to shelve conflicts'))
if parent.node() != nodemod.nullid:
desc = "changes to: %s" % parent.description().split('\n', 1)[0]
@@ -482,6 +509,9 @@
name = getshelvename(repo, parent, opts)
activebookmark = _backupactivebookmark(repo)
extra = {'internal': 'shelve'}
+ if unresolved:
+ extra['unresolved-merge'] = True
+ _storeunresolvedmerge(repo, name)
if includeunknown:
_includeunknownfiles(repo, pats, opts, extra)
@@ -577,6 +607,7 @@
"""subcommand that displays the list of shelves"""
pats = set(pats)
width = 80
+ namewidth = 16
if not ui.plain():
width = ui.termwidth()
namelabel = 'shelve.newest'
@@ -585,13 +616,16 @@
sname = util.split(name)[1]
if pats and sname not in pats:
continue
+ if _isunresolvedshelve(repo, sname):
+ sname += ' (unresolved)'
+ namewidth += len(' (unresolved)')
ui.write(sname, label=namelabel)
namelabel = 'shelve.name'
if ui.quiet:
ui.write('\n')
continue
- ui.write(' ' * (16 - len(sname)))
- used = 16
+ ui.write(' ' * (namewidth - len(sname)))
+ used = namewidth
date = dateutil.makedate(mtime)
age = '(%s)' % templatefilters.age(date, abbrev=True)
ui.write(age, label='shelve.age')
@@ -694,6 +728,18 @@
ui.status(_('marked working directory as branch %s\n')
% branchtorestore)
+def restoreunresolvedshelve(repo, ui, basename, pctx, shelvectx):
+ """Change the parents of the dirstate to the parents of the stored
+ unresolved shelvectx.
+
+ Also, replace `.hg/merge` with `.hg/merge-unresolved/<basename>`
+ to restore the status of the resolved files in the shelvectx.
+ """
+ p1, p2 = shelvectx.parents()
+ repo.dirstate.setparents(p1.node(), p2.node())
+
+ 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'):
@@ -898,7 +944,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):
@@ -944,6 +992,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)
@@ -999,6 +1048,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
@@ -1019,18 +1070,35 @@
tmpwctx, addedbefore = _commitworkingcopychanges(ui, repo, opts,
tmpwctx)
repo, shelvectx = _unshelverestorecommit(ui, repo, tr, basename)
+ unresolvedshelve = _isunresolvedshelve(repo, basename, shelvectx)
_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:
+ with repo.dirstate.parentchange():
+ restoreunresolvedshelve(repo, ui, basename, pctx, shelvectx)
restorebranch(ui, repo, branchtorestore)
_forgetunknownfiles(repo, shelvectx, addedbefore)
@@ -1068,7 +1136,9 @@
_('interactive mode, only works while creating a shelve')),
('', 'stat', None,
_('output diffstat-style summary of changes (provide the names of '
- 'the shelved changes as positional arguments)')
+ 'the shelved changes as positional arguments)')),
+ ('', 'unresolved', None,
+ _('unshelve mergestate with unresolved files')
)] + cmdutil.walkopts,
_('hg shelve [OPTION]... [FILE]...'),
helpcategory=command.CATEGORY_WORKING_DIRECTORY)
To: navaneeth.suresh, #hg-reviewers
Cc: pulkit, mercurial-devel
More information about the Mercurial-devel
mailing list