[PATCH 10 of 11] graft: enable rotated-DAG copy tracing (issue4028)
# HG changeset patch # User Gábor Stefanik# Date 1475512693 -7200 # Mon Oct 03 18:38:13 2016 +0200 # Node ID 4b963243504357812d13fa5c824f60253e0ba448 # Parent bbc77d871f41a85db381b57acfe6dc550bb40506 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 bbc77d871f41 -r 4b9632435043 mercurial/copies.py --- a/mercurial/copies.py Tue Oct 04 12:51:54 2016 +0200 +++ b/mercurial/copies.py Mon 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 bbc77d871f41 -r 4b9632435043 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,
RE: [PATCH 10 of 11] graft: enable rotated-DAG copy tracing (issue4028)
> -- 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: Pierre-Yves David [mailto:pierre-yves.da...@ens-lyon.org] > Sent: Tuesday, October 4, 2016 11:42 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) > > > > On 10/04/2016 04:48 PM, Gábor STEFANIK wrote: > > -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... > > I did not looked in details, but this seems to work fine for me. So started to > take some of your series. I meant the patchbomb setup didn't work properly, as it mangled my name. Problem is it sends the mail as Latin-2 (or UTF-8 if I specify HGENCODING), but hardcodedly says "encoding=us-ascii" in the headers. So strictly-conforming mail readers will reject all 8-bit characters in the mail. > > -- > Pierre-Yves David ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH 10 of 11] graft: enable rotated-DAG copy tracing (issue4028)
On 10/04/2016 04:48 PM, Gábor STEFANIK wrote: -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... I did not looked in details, but this seems to work fine for me. So started to take some of your series. -- Pierre-Yves David ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
RE: [PATCH 10 of 11] graft: enable rotated-DAG copy tracing (issue4028)
> -- 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: 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... > # 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: > -
[PATCH 10 of 11] graft: enable rotated-DAG copy tracing (issue4028)
# HG changeset patch # User Gábor Stefanik# Date 1475512693 -7200 # 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.py Tue Oct 04 12:51:54 2016 +0200 +++ b/mercurial/copies.py Mon 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,