[PATCH 10 of 11] graft: enable rotated-DAG copy tracing (issue4028)
Gábor STEFANIK
Gabor.STEFANIK at nng.com
Tue Oct 4 11:01:25 EDT 2016
>
--------------------------------------------------------------------------
This message, including its attachments, is confidential. For more information please read NNG's email policy here:
http://www.nng.com/emailpolicy/
By responding to this email you accept the email policy.
-----Original Message-----
> From: Gábor STEFANIK
> Sent: Tuesday, October 4, 2016 4:49 PM
> To: Gábor STEFANIK <Gabor.STEFANIK at nng.com>; mercurial-
> devel at mercurial-scm.org
> Subject: RE: [PATCH 10 of 11] graft: enable rotated-DAG copy tracing
> (issue4028)
>
> > -----Original Message-----
> > From: Mercurial-devel
> > [mailto:mercurial-devel-bounces at mercurial-scm.org]
> > On Behalf Of Gábor Stefanik
> > Sent: Tuesday, October 4, 2016 4:40 PM
> > To: mercurial-devel at mercurial-scm.org
> > Subject: [PATCH 10 of 11] graft: enable rotated-DAG copy tracing
> > (issue4028)
> >
> > # HG changeset patch
> > # User G?bor Stefanik <gabor.stefanik at nng.com> # Date 1475512693 -7200
>
> Alright, that didn't work either, sorry for the spam...
Looks like it is rendered correctly in the archives. In Outlook, it reads "G?bor".
>
> > # Mon Oct 03 18:38:13 2016 +0200
> > # Node ID 9e59cd55604c5e30b38c66c502c8c982c01a4a01
> > # Parent 5977d6569b2e22487134af7be281e5d60cc987f5
> > graft: enable rotated-DAG copy tracing (issue4028)
> >
> > Graft performs a merge in a rotated DAG (with a false common
> > ancestor), which must be taken into account when tracking copies. Find
> > the real common ancestor in this case, and track copies between the
> > real and false common ancestors in reverse.
> >
> > Using this change, when grafting a commit with a change to a file
> > moved earlier on the graft's source branch, the change is merged as
> > expected into the original
> > (unmoved) file, rather than recreating it under its new name.
> > It should also eventually make it possible to support cross-branch
> > updates that preserve changes in a dirty working copy.
> >
> > diff -r 5977d6569b2e -r 9e59cd55604c mercurial/copies.py
> > --- a/mercurial/copies.pyTue Oct 04 12:51:54 2016 +0200
> > +++ b/mercurial/copies.pyMon Oct 03 18:38:13 2016 +0200
> > @@ -321,7 +321,23 @@
> > if repo.ui.configbool('experimental', 'disablecopytrace'):
> > return {}, {}, {}, {}
> >
> > - dirtyc1 = False # dummy bool for later use
> > + # In certain scenarios (e.g. graft, update or rebase), ca can be
> overridden
> > + # We still need to know a real common ancestor in this case
> > + # We can't just compute _c1.ancestor(_c2) and compare it to ca,
> because
> > + # there can be multiple common ancestors, e.g. in case of bidmerge.
> > + # Because our caller may not know if the revision passed in lieu of the
> CA
> > + # is a genuine common ancestor or not without explicitly checking it, it's
> > + # better to determine that here.
> > + tca = ca
> > + # ca.descendant(wc) and ca.descendant(ca) are False, work around that
> > + _c1 = c1.p1() if c1.rev() is None else c1
> > + _c2 = c2.p1() if c2.rev() is None else c2
> > + dirtyc1 = not (ca == _c1 or ca.descendant(_c1))
> > + dirtyc2 = not (ca == _c2 or ca.descendant(_c2))
> > + graft = dirtyc1 or dirtyc2
> > + if graft:
> > + tca = _c1.ancestor(_c2)
> > +
> > limit = _findlimit(repo, c1.rev(), c2.rev())
> > if limit is None:
> > # no common ancestor, no copies @@ -331,6 +347,7 @@
> > m1 = c1.manifest()
> > m2 = c2.manifest()
> > ma = ca.manifest()
> > + mta = tca.manifest()
> >
> > # see _checkcopies documentation below for these dicts
> > copy1, copy2 = {}, {}
> > @@ -340,18 +357,26 @@
> > diverge, incompletediverge = {}, {}
> >
> > # find interesting file sets from manifests
> > + if graft:
> > + repo.ui.debug(" computing unmatched files in rotated DAG\n")
> > addedinm1 = m1.filesnotin(ma)
> > addedinm2 = m2.filesnotin(ma)
> > u1r, u2r = _computenonoverlap(repo, c1, c2, addedinm1, addedinm2)
> > - u1u, u2u = u1r, u2r
> > + if not graft:
> > + u1u, u2u = u1r, u2r
> > + else: # need to recompute this for directory move handling when
> grafting
> > + repo.ui.debug(" computing unmatched files in unrotated DAG\n")
> > + u1u, u2u = _computenonoverlap(repo, c1, c2, m1.filesnotin(mta),
> > + m2.filesnotin(mta))
> > +
> > bothnew = sorted(addedinm1 & addedinm2)
> >
> > for f in u1u:
> > - _checkcopies(c1, f, m1, m2, ca, ca, False, limit, diverge, copy1,
> > + _checkcopies(c1, f, m1, m2, ca, tca, dirtyc1, limit, diverge,
> > + copy1,
> > fullcopy1, incomplete1, incompletediverge)
> >
> > for f in u2u:
> > - _checkcopies(c2, f, m2, m1, ca, ca, False, limit, diverge, copy2,
> > + _checkcopies(c2, f, m2, m1, ca, tca, dirtyc2, limit, diverge,
> > + copy2,
> > fullcopy2, incomplete2, incompletediverge)
> >
> > copy = dict(copy1.items() + copy2.items()) @@ -401,9 +426,9 @@
> > # reset incomplete dicts for bothdiverge generation
> > incomplete1, incomplete2, incompletediverge = {}, {}, {}
> > for f in bothnew:
> > - _checkcopies(c1, f, m1, m2, ca, ca, False, limit, bothdiverge, _copy,
> > + _checkcopies(c1, f, m1, m2, ca, tca, dirtyc1, limit,
> > + bothdiverge, _copy,
> > _fullcopy, incomplete1, incompletediverge)
> > - _checkcopies(c2, f, m2, m1, ca, ca, False, limit, bothdiverge, _copy,
> > + _checkcopies(c2, f, m2, m1, ca, tca, dirtyc2, limit,
> > + bothdiverge, _copy,
> > _fullcopy, incomplete2, incompletediverge)
> > if dirtyc1:
> > assert incomplete2 == {}
> > diff -r 5977d6569b2e -r 9e59cd55604c tests/test-graft.t
> > --- a/tests/test-graft.tTue Oct 04 12:51:54 2016 +0200
> > +++ b/tests/test-graft.tMon Oct 03 18:38:13 2016 +0200
> > @@ -179,6 +179,13 @@
> > committing changelog
> > grafting 5:97f8bfe72746 "5"
> > searching for copies back to rev 1
> > + computing unmatched files in rotated DAG
> > + computing unmatched files in unrotated DAG
> > + unmatched files in other:
> > + c
> > + all copies found (* = to merge, ! = divergent, % = renamed and
> deleted):
> > + src: 'c' -> dst: 'b' *
> > + checking for directory renames
> > resolving manifests
> > branchmerge: True, force: True, partial: False
> > ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746
> > @@ -
> > 193,6 +200,13 @@
> > scanning for duplicate grafts
> > grafting 4:9c233e8e184d "4"
> > searching for copies back to rev 1
> > + computing unmatched files in rotated DAG
> > + computing unmatched files in unrotated DAG
> > + unmatched files in other:
> > + c
> > + all copies found (* = to merge, ! = divergent, % = renamed and
> deleted):
> > + src: 'c' -> dst: 'b' *
> > + checking for directory renames
> > resolving manifests
> > branchmerge: True, force: True, partial: False
> > ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d
> > @@ -
> > 842,3 +856,431 @@
> > |/
> > o 0
> >
> > +Graft from behind a move or rename
> > +==================================
> > +
> > +NOTE: This is affected by issue5343, and will need updating when it's
> > +fixed
> > +
> > +Possible cases during a regular graft (when ca is between cta and c2):
> > +
> > +name | c1<-cta | cta<->ca | ca->c2
> > +A.0 | | |
> > +A.1 | X | |
> > +A.2 | | X |
> > +A.3 | | | X
> > +A.4 | X | X |
> > +A.5 | X | | X
> > +A.6 | | X | X
> > +A.7 | X | X | X
> > +
> > +A.0 is trivial, and doesn't need copy tracking.
> > +For A.1, a forward rename is recorded in the c1 pass, to be followed later.
> > +In A.2, the rename is recorded in the c2 pass and followed backwards.
> > +A.3 is recorded in the c2 pass as a forward rename to be duplicated
> > +on
> > target.
> > +In A.4, both passes of checkcopies record incomplete renames, which
> > +are then joined in mergecopies to record a rename to be followed.
> > +In A.5 and A.7, the c1 pass records an incomplete rename, while the
> > +c2 pass records an incomplete divergence. The incomplete rename is
> > +then joined to the appropriate side of the incomplete divergence, and
> > +the result is recorded as a divergence. The code doesn't distinguish
> > +at all between these two cases, since the end result of them is the
> > +same: an incomplete divergence joined with an incomplete rename into
> > +a
> > divergence.
> > +Finally, A.6 records a divergence entirely in the c2 pass.
> > +
> > +A.4 has a degenerate case a<-b<-a->a, where checkcopies isn't needed
> > +at
> > all.
> > +A.5 has a special case a<-b<-b->a, which is treated like a<-b->a in a merge.
> > +A.6 has a special case a<-a<-b->a. Here, checkcopies will find a
> > +spurious incomplete divergence, which is in fact complete. This is
> > +handled later in mergecopies.
> > +A.7 has 4 special cases: a<-b<-a->b (the "ping-pong" case),
> > +a<-b<-c->b, a<-b<-a->c and a<-b<-c->a. Of these, only the "ping-pong"
> > +case is interesting, the others are fairly trivial (a<-b<-c->b and
> > +a<-b<-a->c proceed like the base case, a<-b<-c->a is treated the same as
> a<-b<-b->a).
> > +
> > +f5a therefore tests the "ping-pong" rename case, where a file is
> > +renamed to the same name on both branches, then the rename is backed
> > +out on one branch, and the backout is grafted to the other branch.
> > +This creates a challenging rename sequence of a<-b<-a->b in the graft
> > +target, topological CA, graft CA and graft source, respectively.
> > +Since rename detection will run on the c1 side for such a sequence
> > +(as for technical reasons, we split the c1 and c2 sides not at the
> > +graft CA, but rather at the topological CA), it will pick up a false
> > +rename, and cause a spurious merge conflict. This false rename is
> > +always exactly the reverse of the true rename that would be detected
> > +on the c2 side, so
> > we can correct for it by detecting this condition and reversing as necessary.
> > +
> > +First, set up the repository with commits to be grafted
> > +
> > + $ hg init ../graftmove
> > + $ cd ../graftmove
> > + $ echo c1a > f1a
> > + $ echo c2a > f2a
> > + $ echo c3a > f3a
> > + $ echo c4a > f4a
> > + $ echo c5a > f5a
> > + $ hg ci -qAm a
> > + $ hg mv f1a f1b
> > + $ hg mv f3a f3b
> > + $ hg mv f5a f5b
> > + $ hg ci -qAm b
> > + $ echo c1c > f1b
> > + $ hg mv f2a f2c
> > + $ hg mv f5b f5a
> > + $ echo c5c > f5a
> > + $ hg ci -qAm c
> > + $ hg mv f3b f3d
> > + $ echo c4d > f4a
> > + $ hg ci -qAm d
> > + $ hg log -G
> > + @ changeset: 3:aa2584f6dee9
> > + | tag: tip
> > + | user: test
> > + | date: Thu Jan 01 00:00:00 1970 +0000
> > + | summary: d
> > + |
> > + o changeset: 2:c8d3926d7649
> > + | user: test
> > + | date: Thu Jan 01 00:00:00 1970 +0000
> > + | summary: c
> > + |
> > + o changeset: 1:7e9aff31b586
> > + | user: test
> > + | date: Thu Jan 01 00:00:00 1970 +0000
> > + | summary: b
> > + |
> > + o changeset: 0:3340a7726e9e
> > + user: test
> > + date: Thu Jan 01 00:00:00 1970 +0000
> > + summary: a
> > +
> > +
> > +Test the cases A.2 (f1x), A.3 (f2x) and a special case of A.6 (f5x)
> > +where the two renames actually converge to the same name (thus no
> > actual divergence).
> > +
> > + $ hg up -q 0 # commit "a"
> > + $ hg graft -r 2
> > + grafting 2:c8d3926d7649 "c"
> > + merging f1a and f1b to f1a
> > + merging f5a
> > + warning: can't find ancestor for 'f5a' copied from 'f5b'!
> > + $ hg status --change .
> > + M f1a
> > + M f5a
> > + A f2c
> > + R f2a
> > + $ hg cat f1a
> > + c1c
> > + $ hg cat f1b
> > + f1b: no such file in rev 68af396ea7bf [1]
> > +
> > +Test the cases A.0 (f4x) and A.6 (f3x)
> > +
> > + $ hg graft -r 3
> > + grafting 3:aa2584f6dee9 "d"
> > + note: possible conflict - f3b was renamed multiple times to:
> > + f3d
> > + f3a
> > + warning: can't find ancestor for 'f3d' copied from 'f3b'!
> > +
> > +Set up the repository for some further tests
> > +
> > + $ hg up -q 0
> > + $ hg mv f1a f1e
> > + $ echo c2e > f2a
> > + $ hg mv f3a f3e
> > + $ hg mv f4a f4e
> > + $ hg mv f5a f5b
> > + $ hg ci -qAm e
> > + $ hg log -G
> > + @ changeset: 6:52db7f4dcf33
> > + | tag: tip
> > + | parent: 0:3340a7726e9e
> > + | user: test
> > + | date: Thu Jan 01 00:00:00 1970 +0000
> > + | summary: e
> > + |
> > + | o changeset: 5:29f6ffdbca28
> > + | | user: test
> > + | | date: Thu Jan 01 00:00:00 1970 +0000
> > + | | summary: d
> > + | |
> > + | o changeset: 4:68af396ea7bf
> > + |/ parent: 0:3340a7726e9e
> > + | user: test
> > + | date: Thu Jan 01 00:00:00 1970 +0000
> > + | summary: c
> > + |
> > + | o changeset: 3:aa2584f6dee9
> > + | | user: test
> > + | | date: Thu Jan 01 00:00:00 1970 +0000
> > + | | summary: d
> > + | |
> > + | o changeset: 2:c8d3926d7649
> > + | | user: test
> > + | | date: Thu Jan 01 00:00:00 1970 +0000
> > + | | summary: c
> > + | |
> > + | o changeset: 1:7e9aff31b586
> > + |/ user: test
> > + | date: Thu Jan 01 00:00:00 1970 +0000
> > + | summary: b
> > + |
> > + o changeset: 0:3340a7726e9e
> > + user: test
> > + date: Thu Jan 01 00:00:00 1970 +0000
> > + summary: a
> > +
> > +
> > +Test the cases A.4 (f1x), the "ping-pong" special case of A.7 (f5x),
> > +and A.3 with a local content change to be preserved (f2x).
> > +
> > + $ hg graft -r 2
> > + grafting 2:c8d3926d7649 "c"
> > + merging f1e and f1b to f1e
> > + merging f2a and f2c to f2c
> > + merging f5b and f5a to f5a
> > +
> > +Test the cases A.1 (f4x) and A.7 (f3x).
> > +
> > + $ hg graft -r 3
> > + grafting 3:aa2584f6dee9 "d"
> > + note: possible conflict - f3b was renamed multiple times to:
> > + f3e
> > + f3d
> > + merging f4e and f4a to f4e
> > + warning: can't find ancestor for 'f3d' copied from 'f3b'!
> > +
> > +Check the results of the grafts tested
> > +
> > + $ hg log -CGv --patch --git
> > + @ changeset: 8:4c243d7a2f50
> > + | tag: tip
> > + | user: test
> > + | date: Thu Jan 01 00:00:00 1970 +0000
> > + | files: f3d f4e
> > + | description:
> > + | d
> > + |
> > + |
> > + | diff --git a/f3d b/f3d
> > + | new file mode 100644
> > + | --- /dev/null
> > + | +++ b/f3d
> > + | @@ -0,0 +1,1 @@
> > + | +c3a
> > + | diff --git a/f4e b/f4e
> > + | --- a/f4e
> > + | +++ b/f4e
> > + | @@ -1,1 +1,1 @@
> > + | -c4a
> > + | +c4d
> > + |
> > + o changeset: 7:9b7ee4960a74
> > + | user: test
> > + | date: Thu Jan 01 00:00:00 1970 +0000
> > + | files: f1e f2a f2c f5a f5b
> > + | copies: f2c (f2a) f5a (f5b)
> > + | description:
> > + | c
> > + |
> > + |
> > + | diff --git a/f1e b/f1e
> > + | --- a/f1e
> > + | +++ b/f1e
> > + | @@ -1,1 +1,1 @@
> > + | -c1a
> > + | +c1c
> > + | diff --git a/f2a b/f2c
> > + | rename from f2a
> > + | rename to f2c
> > + | diff --git a/f5b b/f5a
> > + | rename from f5b
> > + | rename to f5a
> > + | --- a/f5b
> > + | +++ b/f5a
> > + | @@ -1,1 +1,1 @@
> > + | -c5a
> > + | +c5c
> > + |
> > + o changeset: 6:52db7f4dcf33
> > + | parent: 0:3340a7726e9e
> > + | user: test
> > + | date: Thu Jan 01 00:00:00 1970 +0000
> > + | files: f1a f1e f2a f3a f3e f4a f4e f5a f5b
> > + | copies: f1e (f1a) f3e (f3a) f4e (f4a) f5b (f5a)
> > + | description:
> > + | e
> > + |
> > + |
> > + | diff --git a/f1a b/f1e
> > + | rename from f1a
> > + | rename to f1e
> > + | diff --git a/f2a b/f2a
> > + | --- a/f2a
> > + | +++ b/f2a
> > + | @@ -1,1 +1,1 @@
> > + | -c2a
> > + | +c2e
> > + | diff --git a/f3a b/f3e
> > + | rename from f3a
> > + | rename to f3e
> > + | diff --git a/f4a b/f4e
> > + | rename from f4a
> > + | rename to f4e
> > + | diff --git a/f5a b/f5b
> > + | rename from f5a
> > + | rename to f5b
> > + |
> > + | o changeset: 5:29f6ffdbca28
> > + | | user: test
> > + | | date: Thu Jan 01 00:00:00 1970 +0000
> > + | | files: f3d f4a
> > + | | description:
> > + | | d
> > + | |
> > + | |
> > + | | diff --git a/f3d b/f3d
> > + | | new file mode 100644
> > + | | --- /dev/null
> > + | | +++ b/f3d
> > + | | @@ -0,0 +1,1 @@
> > + | | +c3a
> > + | | diff --git a/f4a b/f4a
> > + | | --- a/f4a
> > + | | +++ b/f4a
> > + | | @@ -1,1 +1,1 @@
> > + | | -c4a
> > + | | +c4d
> > + | |
> > + | o changeset: 4:68af396ea7bf
> > + |/ parent: 0:3340a7726e9e
> > + | user: test
> > + | date: Thu Jan 01 00:00:00 1970 +0000
> > + | files: f1a f2a f2c f5a
> > + | copies: f2c (f2a)
> > + | description:
> > + | c
> > + |
> > + |
> > + | diff --git a/f1a b/f1a
> > + | --- a/f1a
> > + | +++ b/f1a
> > + | @@ -1,1 +1,1 @@
> > + | -c1a
> > + | +c1c
> > + | diff --git a/f2a b/f2c
> > + | rename from f2a
> > + | rename to f2c
> > + | diff --git a/f5a b/f5a
> > + | --- a/f5a
> > + | +++ b/f5a
> > + | @@ -1,1 +1,1 @@
> > + | -c5a
> > + | +c5c
> > + |
> > + | o changeset: 3:aa2584f6dee9
> > + | | user: test
> > + | | date: Thu Jan 01 00:00:00 1970 +0000
> > + | | files: f3b f3d f4a
> > + | | copies: f3d (f3b)
> > + | | description:
> > + | | d
> > + | |
> > + | |
> > + | | diff --git a/f3b b/f3d
> > + | | rename from f3b
> > + | | rename to f3d
> > + | | diff --git a/f4a b/f4a
> > + | | --- a/f4a
> > + | | +++ b/f4a
> > + | | @@ -1,1 +1,1 @@
> > + | | -c4a
> > + | | +c4d
> > + | |
> > + | o changeset: 2:c8d3926d7649
> > + | | user: test
> > + | | date: Thu Jan 01 00:00:00 1970 +0000
> > + | | files: f1b f2a f2c f5a f5b
> > + | | copies: f2c (f2a) f5a (f5b)
> > + | | description:
> > + | | c
> > + | |
> > + | |
> > + | | diff --git a/f1b b/f1b
> > + | | --- a/f1b
> > + | | +++ b/f1b
> > + | | @@ -1,1 +1,1 @@
> > + | | -c1a
> > + | | +c1c
> > + | | diff --git a/f2a b/f2c
> > + | | rename from f2a
> > + | | rename to f2c
> > + | | diff --git a/f5b b/f5a
> > + | | rename from f5b
> > + | | rename to f5a
> > + | | --- a/f5b
> > + | | +++ b/f5a
> > + | | @@ -1,1 +1,1 @@
> > + | | -c5a
> > + | | +c5c
> > + | |
> > + | o changeset: 1:7e9aff31b586
> > + |/ user: test
> > + | date: Thu Jan 01 00:00:00 1970 +0000
> > + | files: f1a f1b f3a f3b f5a f5b
> > + | copies: f1b (f1a) f3b (f3a) f5b (f5a)
> > + | description:
> > + | b
> > + |
> > + |
> > + | diff --git a/f1a b/f1b
> > + | rename from f1a
> > + | rename to f1b
> > + | diff --git a/f3a b/f3b
> > + | rename from f3a
> > + | rename to f3b
> > + | diff --git a/f5a b/f5b
> > + | rename from f5a
> > + | rename to f5b
> > + |
> > + o changeset: 0:3340a7726e9e
> > + user: test
> > + date: Thu Jan 01 00:00:00 1970 +0000
> > + files: f1a f2a f3a f4a f5a
> > + description:
> > + a
> > +
> > +
> > + diff --git a/f1a b/f1a
> > + new file mode 100644
> > + --- /dev/null
> > + +++ b/f1a
> > + @@ -0,0 +1,1 @@
> > + +c1a
> > + diff --git a/f2a b/f2a
> > + new file mode 100644
> > + --- /dev/null
> > + +++ b/f2a
> > + @@ -0,0 +1,1 @@
> > + +c2a
> > + diff --git a/f3a b/f3a
> > + new file mode 100644
> > + --- /dev/null
> > + +++ b/f3a
> > + @@ -0,0 +1,1 @@
> > + +c3a
> > + diff --git a/f4a b/f4a
> > + new file mode 100644
> > + --- /dev/null
> > + +++ b/f4a
> > + @@ -0,0 +1,1 @@
> > + +c4a
> > + diff --git a/f5a b/f5a
> > + new file mode 100644
> > + --- /dev/null
> > + +++ b/f5a
> > + @@ -0,0 +1,1 @@
> > + +c5a
> > +
> > + $ hg cat f2c
> > + c2e
> > diff -r 5977d6569b2e -r 9e59cd55604c tests/test-rebase-conflicts.t
> > --- a/tests/test-rebase-conflicts.tTue Oct 04 12:51:54 2016 +0200
> > +++ b/tests/test-rebase-conflicts.tMon Oct 03 18:38:13 2016 +0200
> > @@ -238,6 +238,10 @@
> > merge against 9:e31216eec445
> > detach base 8:8e4e2c1a07ae
> > searching for copies back to rev 3
> > + computing unmatched files in rotated DAG
> > + computing unmatched files in unrotated DAG
> > + unmatched files in other:
> > + f2.txt
> > resolving manifests
> > branchmerge: True, force: True, partial: False
> > ancestor: 8e4e2c1a07ae, local: 4bc80088dc6b+, remote: e31216eec445
> > @@
> > -255,6 +259,10 @@
> > merge against 10:2f2496ddf49d
> > detach base 9:e31216eec445
> > searching for copies back to rev 3
> > + computing unmatched files in rotated DAG
> > + computing unmatched files in unrotated DAG
> > + unmatched files in other:
> > + f2.txt
> > resolving manifests
> > branchmerge: True, force: True, partial: False
> > ancestor: e31216eec445, local: 19c888675e13+, remote: 2f2496ddf49d
More information about the Mercurial-devel
mailing list