# HG changeset patch # User Martin von Zweigbergk # Date 2017-12-28 06:05:20 # Node ID c2165551eed6e94b40fabfffc3309e63761298d8 # Parent 36d70c14ed253ed2254dcb61aa081909b5a6a54d changelog: parse copy metadata if available in extras This lets read back the copy metadata we just started writing. There are still many places left to teach about getting the copy information from the changeset, but we have enough ({file_copies}, specifically) that we can add it now and have some test coverage of it. Differential Revision: https://phab.mercurial-scm.org/D6186 diff --git a/mercurial/changelog.py b/mercurial/changelog.py --- a/mercurial/changelog.py +++ b/mercurial/changelog.py @@ -87,6 +87,18 @@ def encodecopies(copies): ] return "\n".join(items) +def decodecopies(data): + try: + copies = {} + for l in data.split('\n'): + k, v = l.split('\0') + copies[k] = v + return copies + except ValueError: + # Perhaps someone had chosen the same key name (e.g. "p1copies") and + # used different syntax for the value. + return None + def stripdesc(desc): """strip trailing whitespace and leading and trailing empty lines""" return '\n'.join([l.rstrip() for l in desc.splitlines()]).strip('\n') @@ -286,6 +298,16 @@ class changelogrevision(object): return self._text[off[2] + 1:off[3]].split('\n') @property + def p1copies(self): + rawcopies = self.extra.get('p1copies') + return rawcopies and decodecopies(rawcopies) + + @property + def p2copies(self): + rawcopies = self.extra.get('p2copies') + return rawcopies and decodecopies(rawcopies) + + @property def description(self): return encoding.tolocal(self._text[self._offsets[3] + 2:]) diff --git a/mercurial/context.py b/mercurial/context.py --- a/mercurial/context.py +++ b/mercurial/context.py @@ -441,6 +441,21 @@ class changectx(basectx): return self._changeset.files @propertycache def _copies(self): + source = self._repo.ui.config('experimental', 'copies.read-from') + p1copies = self._changeset.p1copies + p2copies = self._changeset.p2copies + # If config says to get copy metadata only from changeset, then return + # that, defaulting to {} if there was no copy metadata. + # In compatibility mode, we return copy data from the changeset if + # it was recorded there, and otherwise we fall back to getting it from + # the filelogs (below). + if (source == 'changeset-only' or + (source == 'compatibility' and p1copies is not None)): + return p1copies or {}, p2copies or {} + + # Otherwise (config said to read only from filelog, or we are in + # compatiblity mode and there is not data in the changeset), we get + # the copy metadata from the filelogs. p1copies = {} p2copies = {} p1 = self.p1() diff --git a/mercurial/copies.py b/mercurial/copies.py --- a/mercurial/copies.py +++ b/mercurial/copies.py @@ -162,8 +162,8 @@ def _computeforwardmissing(a, b, match=N def usechangesetcentricalgo(repo): """Checks if we should use changeset-centric copy algorithms""" - return (repo.ui.config('experimental', 'copies.read-from') == - 'compatibility') + return (repo.ui.config('experimental', 'copies.read-from') in + ('changeset-only', 'compatibility')) def _committedforwardcopies(a, b, match): """Like _forwardcopies(), but b.rev() cannot be None (working copy)""" diff --git a/tests/test-copies-in-changeset.t b/tests/test-copies-in-changeset.t --- a/tests/test-copies-in-changeset.t +++ b/tests/test-copies-in-changeset.t @@ -2,9 +2,11 @@ $ cat >> $HGRCPATH << EOF > [experimental] > copies.write-to=changeset-only + > copies.read-from=changeset-only > [alias] > changesetcopies = log -r . -T 'files: {files} > {extras % "{ifcontains("copies", key, "{key}: {value}\n")}"}' + > showcopies = log -r . -T '{file_copies % "{source} -> {name}\n"}' > EOF Check that copies are recorded correctly @@ -23,6 +25,15 @@ Check that copies are recorded correctly p1copies: b\x00a (esc) c\x00a (esc) d\x00a (esc) + $ hg showcopies + a -> b + a -> c + a -> d + $ hg showcopies --config experimental.copies.read-from=compatibility + a -> b + a -> c + a -> d + $ hg showcopies --config experimental.copies.read-from=filelog-only Check that renames are recorded correctly @@ -31,6 +42,8 @@ Check that renames are recorded correctl $ hg changesetcopies files: b b2 p1copies: b2\x00b (esc) + $ hg showcopies + b -> b2 Rename onto existing file. This should get recorded in the changeset files list and in the extras, even though there is no filelog entry. @@ -46,6 +59,8 @@ even though there is no filelog entry. $ hg changesetcopies files: c p1copies: c\x00b2 (esc) + $ hg showcopies + b2 -> c $ hg debugindex c rev linkrev nodeid p1 p2 0 1 b789fdd96dc2 000000000000 000000000000 @@ -74,6 +89,10 @@ File 'f' exists only in p1, so 'i' shoul p1copies: g\x00a (esc) i\x00f (esc) p2copies: h\x00d (esc) + $ hg showcopies + a -> g + d -> h + f -> i Test writing to both changeset and filelog @@ -88,6 +107,12 @@ Test writing to both changeset and filel copyrev: b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3 \x01 (esc) a + $ hg showcopies + a -> j + $ hg showcopies --config experimental.copies.read-from=compatibility + a -> j + $ hg showcopies --config experimental.copies.read-from=filelog-only + a -> j Test writing only to filelog @@ -101,5 +126,10 @@ Test writing only to filelog copyrev: b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3 \x01 (esc) a + $ hg showcopies + $ hg showcopies --config experimental.copies.read-from=compatibility + a -> k + $ hg showcopies --config experimental.copies.read-from=filelog-only + a -> k $ cd .. diff --git a/tests/test-copies.t b/tests/test-copies.t --- a/tests/test-copies.t +++ b/tests/test-copies.t @@ -1,4 +1,4 @@ -#testcases filelog compatibility +#testcases filelog compatibility changeset $ cat >> $HGRCPATH << EOF > [extensions] @@ -14,6 +14,14 @@ > EOF #endif +#if changeset + $ cat >> $HGRCPATH << EOF + > [experimental] + > copies.read-from = changeset-only + > copies.write-to = changeset-only + > EOF +#endif + $ REPONUM=0 $ newrepo() { > cd $TESTTMP @@ -376,11 +384,13 @@ Copy file that exists on both sides of t o 0 add x on branch 1 x $ hg debugp1copies -r 2 + x -> z (changeset !) $ hg debugp2copies -r 2 - x -> z + x -> z (no-changeset !) $ hg debugpathcopies 1 2 + x -> z (changeset !) $ hg debugpathcopies 0 2 - x -> z + x -> z (no-changeset !) Copy x->y on one side of merge and copy x->z on the other side. Pathcopies from one parent of the merge to the merge should include the copy from the other side. @@ -539,6 +549,9 @@ test reflect that for this particular ca Grafting revision 4 on top of revision 2, showing that it respect the rename: +TODO: Make this work with copy info in changesets (probably by writing a +changeset-centric version of copies.mergecopies()) +#if no-changeset $ hg up 2 -q $ hg graft -r 4 --base 3 --hidden grafting 4:af28412ec03c "added d, modified b" (tip) @@ -554,6 +567,8 @@ Grafting revision 4 on top of revision 2 b +baba +#endif + Test to make sure that fullcopytracing algorithm don't fail when both the merging csets are dirty (a dirty cset is one who is not the descendant of merge base) -------------------------------------------------------------------------------------------------