D4664: mergecommit: add a new extension to merge in-memory and create a commit
pulkit (Pulkit Goyal)
phabricator at mercurial-scm.org
Wed Sep 19 12:52:09 UTC 2018
pulkit created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.
REVISION SUMMARY
This patch adds a new mergecommit command which is very useful for workflows
where there is automated merging involved. This patch uses existing in-memory
merge API to merge in-memory and create a commit.
I was thinking about --commit flag to merge command, but then we need other
commitopts too, so created a new command.
This patches filemerge.py to not raise IMMConflictsError on first conflict
because we need a list of conflicts in the end.
REPOSITORY
rHG Mercurial
REVISION DETAIL
https://phab.mercurial-scm.org/D4664
AFFECTED FILES
hgext/mergecommit.py
mercurial/filemerge.py
CHANGE DETAILS
diff --git a/mercurial/filemerge.py b/mercurial/filemerge.py
--- a/mercurial/filemerge.py
+++ b/mercurial/filemerge.py
@@ -910,7 +910,7 @@
if r:
if onfailure:
- if wctx.isinmemory():
+ if wctx.isinmemory() and not ui.configbool('merge', 'inmemory'):
raise error.InMemoryMergeConflictsError('in-memory merge '
'does not support '
'merge conflicts')
diff --git a/hgext/mergecommit.py b/hgext/mergecommit.py
new file mode 100644
--- /dev/null
+++ b/hgext/mergecommit.py
@@ -0,0 +1,106 @@
+from mercurial import (
+ context,
+ destutil,
+ error,
+ extensions,
+ hg,
+ merge as mergemod,
+ phases,
+ registrar,
+ scmutil,
+)
+from mercurial.i18n import _
+
+cmdtable = {}
+command = registrar.command(cmdtable)
+
+configtable = {}
+configitem = registrar.configitem(configtable)
+
+ at command('mergecommit',
+ [('', 'dest', '', _('merge destination')),
+ ('', 'message', '', _('description of the merge commit')),
+ ('', 'date', '', _('date of the merge commit')),
+ ('r', 'rev', '', _('revision to merge')),
+ ('', 'tool', '', _('specify merge tool')),
+ ], ())
+def mergecommit(ui, repo, node=None, **opts):
+ """Merge the rev passed into dest (or working directory parent) and
+ creates a commit with specified options if no conflicts occur.
+
+ If conlicts occur, returns 1 and print the list of unresolved files on
+ stderr. Also, the conflicted state is not applied to the working directory.
+ To do that, you should run `hg merge`
+
+ This does not update to destination node to merge the rev, it uses in-memory
+ merging and also creates in-memory commit. This also does not update to the
+ new commit formed."""
+
+ if opts.get('rev') and node:
+ raise error.Abort(_("please specify just one revision"))
+ if not node:
+ node = opts.get('rev')
+
+ if node:
+ node = scmutil.revsingle(repo, node).node()
+ else:
+ node = repo[destutil.destmerge(repo)].node()
+
+ destnode = None
+ if opts.get('dest'):
+ destnode = scmutil.revsingle(repo, opts.get('dest')).node()
+
+ overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
+ with ui.configoverride(overrides, 'mergecommit'):
+ return merge(repo, node, destnode, date=opts['date'],
+ message=opts['message'])
+
+def merge(repo, node, destnode, date, message):
+ """merges the node into destnode or parent of working directory and creates
+ a commit if no conflicts occur.
+
+ If conflicts are there, it returns 1 and prints the list of unresolved files
+ on stderr"""
+
+ # create a memwctx to merge
+ wctx = context.overlayworkingctx(repo)
+ # setting destnode as p1 if passed
+ if destnode:
+ currentp1 = repo[destnode]
+ else:
+ currentp1 = repo['.']
+ wctx.setbase(currentp1)
+
+ stats = None
+ try:
+ # actual merging
+ stats = mergemod.update(repo, node, True, None, wc=wctx)
+ except error.InMemoryMergeConflictsError:
+ pass
+
+ if stats is None or stats.unresolvedcount > 0:
+ # there were conflicts
+ ms = mergemod.mergestate.read(repo)
+ unresolved = list(ms.unresolved())
+ repo.ui.warn("list of unresolved files: %s\n" % ', '.join(unresolved))
+ mergemod.mergestate.clean(repo)
+ return 1
+
+ branch = currentp1.branch()
+ desc = message
+ if not desc:
+ desc = "in-memory merge commit"
+ if not date:
+ date = None
+ p1 = currentp1.node()
+ p2 = node
+
+ # creating a memctx and then commiting it
+ memctx = wctx.tomemctx(desc, parents=(p1, p2), branch=branch, date=date)
+ overrides = {('phases', 'new-commit'): phases.secret}
+ with repo.ui.configoverride(overrides, 'memorymerge'):
+ newctx = repo.commitctx(memctx)
+ wctx.clean()
+ mergemod.mergestate.clean(repo)
+ repo.ui.status("new commit formed is %s\n" % repo[newctx].hex()[:12])
+ return 0
To: pulkit, #hg-reviewers
Cc: mercurial-devel
More information about the Mercurial-devel
mailing list