D4664: mergecommit: add a new extension to merge in-memory and create a commit
pulkit (Pulkit Goyal)
phabricator at mercurial-scm.org
Tue Feb 26 14:53:10 UTC 2019
pulkit updated this revision to Diff 14248.
REPOSITORY
rHG Mercurial
CHANGES SINCE LAST UPDATE
https://phab.mercurial-scm.org/D4664?vs=11198&id=14248
REVISION DETAIL
https://phab.mercurial-scm.org/D4664
AFFECTED FILES
hgext/mergecommit.py
mercurial/filemerge.py
tests/test-mergecommit.t
CHANGE DETAILS
diff --git a/tests/test-mergecommit.t b/tests/test-mergecommit.t
new file mode 100644
--- /dev/null
+++ b/tests/test-mergecommit.t
@@ -0,0 +1,235 @@
+#testcases mergecommit normal-merge
+
+Testing the mergecommit extension. The test demonstrates how this extension can
+be used by hosting providers to handle merging of PRs. It has two testcases,
+'mergecommit' and 'normal-merge' so that the normal-merge workflow can be
+compared with the workflow implemented by mergecommit extension.
+
+The test is using named branches to denote user PRs.
+
+
+ $ cat << EOF >> $HGRCPATH
+ > [alias]
+ > glog = log -GT "{rev}:{node|short} {desc}\n({branch})"
+ > EOF
+
+#if mergecommit
+ $ cat << EOF >> $HGRCPATH
+ > [extensions]
+ > mergecommit =
+ > EOF
+#endif
+
+Initialize a server
+ $ hg init server
+ $ cd server
+ $ for ch in a b c d; do echo foo > $ch; hg ci -Aqm "added "$ch; done;
+ $ hg glog
+ @ 3:a44c3a524808 added d
+ | (default)
+ o 2:8be98ac1a569 added c
+ | (default)
+ o 1:80e6d2c47cfe added b
+ | (default)
+ o 0:f7ad41964313 added a
+ (default)
+
+Initialize a client, make some changes and create a PR
+
+ $ cd ..
+ $ hg clone server client1
+ updating to branch default
+ 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ cd client1
+ $ hg up 1
+ 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+ $ hg glog
+ o 3:a44c3a524808 added d
+ | (default)
+ o 2:8be98ac1a569 added c
+ | (default)
+ @ 1:80e6d2c47cfe added b
+ | (default)
+ o 0:f7ad41964313 added a
+ (default)
+ $ hg branch pr1
+ marked working directory as branch pr1
+ (branches are permanent and global, did you want a bookmark?)
+ $ echo bar > bar
+ $ hg ci -Aqm "added bar"
+ $ hg push -r . --new-branch
+ pushing to $TESTTMP/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 1 changesets with 1 changes to 1 files (+1 heads)
+
+Lets get back to server and handle the merge
+
+ $ cd ../server
+ $ hg glog
+ o 4:8933c09873b1 added bar
+ | (pr1)
+ | @ 3:a44c3a524808 added d
+ | | (default)
+ | o 2:8be98ac1a569 added c
+ |/ (default)
+ o 1:80e6d2c47cfe added b
+ | (default)
+ o 0:f7ad41964313 added a
+ (default)
+
+Merging default into user branch
+
+#if mergecommit
+
+ $ hg mergecommit -r default --dest pr1 --message "merge commit for pr1"
+ new commit formed is d7aed1d69d65
+
+#else
+
+ $ hg up pr1
+ 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
+ $ hg merge default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ (branch merge, don't forget to commit)
+ $ hg ci -m "merge commit for pr1"
+ $ hg up default
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+
+#endif
+
+ $ hg glog
+ o 5:d7aed1d69d65 merge commit for pr1
+ |\ (pr1)
+ | o 4:8933c09873b1 added bar
+ | | (pr1)
+ @ | 3:a44c3a524808 added d
+ | | (default)
+ o | 2:8be98ac1a569 added c
+ |/ (default)
+ o 1:80e6d2c47cfe added b
+ | (default)
+ o 0:f7ad41964313 added a
+ (default)
+
+Merging user branch into default (accepting the PR)
+
+#if mergecommit
+
+ $ hg mergecommit -r 4 --dest default --message "merged pr1 into default"
+ new commit formed is 083ccc50a6c2
+
+#else
+
+ $ hg merge 4
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ (branch merge, don't forget to commit)
+ $ hg ci -m "merged pr1 into default"
+ $ hg up 3
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+
+#endif
+
+ $ hg glog
+ o 6:083ccc50a6c2 merged pr1 into default
+ |\ (default)
+ +---o 5:d7aed1d69d65 merge commit for pr1
+ | |/ (pr1)
+ | o 4:8933c09873b1 added bar
+ | | (pr1)
+ @ | 3:a44c3a524808 added d
+ | | (default)
+ o | 2:8be98ac1a569 added c
+ |/ (default)
+ o 1:80e6d2c47cfe added b
+ | (default)
+ o 0:f7ad41964313 added a
+ (default)
+
+
+Creating a conflicting PR on a new client side
+
+ $ cd ..
+ $ hg clone server client2 -r 3
+ adding changesets
+ adding manifests
+ adding file changes
+ added 4 changesets with 4 changes to 4 files
+ new changesets f7ad41964313:a44c3a524808
+ updating to branch default
+ 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ cd client2
+ $ hg glog
+ @ 3:a44c3a524808 added d
+ | (default)
+ o 2:8be98ac1a569 added c
+ | (default)
+ o 1:80e6d2c47cfe added b
+ | (default)
+ o 0:f7ad41964313 added a
+ (default)
+
+ $ hg branch pr2
+ marked working directory as branch pr2
+ (branches are permanent and global, did you want a bookmark?)
+ $ echo foo > bar
+ $ hg ci -Aqm "added foo to bar"
+ $ hg push -r . --new-branch
+ pushing to $TESTTMP/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 1 changesets with 1 changes to 1 files (+1 heads)
+
+Go back on server and try to create a review by merging default into the branch
+which will lead to conflicts
+
+ $ cd ../server
+
+#if mergecommit
+
+ $ hg mergecommit -r default --dest pr2
+ merging bar
+ warning: conflicts while merging bar! (edit, then use 'hg resolve --mark')
+ list of unresolved files: bar
+ [1]
+
+#else
+
+ $ hg update pr2
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg merge default
+ merging bar
+ warning: conflicts while merging bar! (edit, then use 'hg resolve --mark')
+ 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+ use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
+ [1]
+ $ hg resolve -l
+ U bar
+ $ hg merge --abort
+ aborting the merge, updating back to 9e82a702b48b
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg update 3
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+#endif
+
+ $ hg glog
+ o 7:9e82a702b48b added foo to bar
+ | (pr2)
+ | o 6:083ccc50a6c2 merged pr1 into default
+ |/| (default)
+ +---o 5:d7aed1d69d65 merge commit for pr1
+ | |/ (pr1)
+ | o 4:8933c09873b1 added bar
+ | | (pr1)
+ @ | 3:a44c3a524808 added d
+ | | (default)
+ o | 2:8be98ac1a569 added c
+ |/ (default)
+ o 1:80e6d2c47cfe added b
+ | (default)
+ o 0:f7ad41964313 added a
+ (default)
diff --git a/mercurial/filemerge.py b/mercurial/filemerge.py
--- a/mercurial/filemerge.py
+++ b/mercurial/filemerge.py
@@ -959,7 +959,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,108 @@
+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)
+
+configitem('merge', 'inmemory', True)
+
+ 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: marcink, mercurial-devel
More information about the Mercurial-devel
mailing list