>
-------------------------------------------------------------------------- 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.stefa...@nng.com>; mercurial- > de...@mercurial-scm.org > Subject: RE: [PATCH 10 of 11] graft: enable rotated-DAG copy tracing > (issue4028) > > > -----Original Message----- > > From: Mercurial-devel > > [mailto:mercurial-devel-boun...@mercurial-scm.org] > > On Behalf Of Gábor Stefanik > > Sent: Tuesday, October 4, 2016 4:40 PM > > To: mercurial-devel@mercurial-scm.org > > Subject: [PATCH 10 of 11] graft: enable rotated-DAG copy tracing > > (issue4028) > > > > # HG changeset patch > > # User G?bor Stefanik <gabor.stefa...@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 _______________________________________________ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel