[PATCH V2] convert: support incremental conversion with hg subrepos
Augie Fackler
raf at durin42.com
Fri Jun 12 22:06:44 UTC 2015
On Fri, Jun 12, 2015 at 12:12:37PM -0400, Matt Harbison wrote:
> # HG changeset patch
> # User Matt Harbison <matt_harbison at yahoo.com>
> # Date 1432920334 14400
> # Fri May 29 13:25:34 2015 -0400
> # Node ID 386fabac4a15250e3700cfd6505a030be662c2d2
> # Parent c0995cd8ff6fdc44ff20835e005771f08452a353
> convert: support incremental conversion with hg subrepos
Queued this, many thanks.
>
> This was implied in issue3486, which specifically asked for subrepo support in
> lfconvert. Now that lfconvert uses the convert extension internally when going
> to normal files, the issue is half fixed. But now even non largefile repos
> benefit when other transformations are needed.
>
> Supporting a full subrepo tree conversion from a single command doesn't seem
> reasonable, given the number of options that can be provided, and the
> transformations that would need to occur when entering a subrepo (consider
> 'filemap' paths). Instead, this allows the user to incrementally convert each
> hg subrepo from bottom up like so:
>
> # so convert knows the dest type when it sees a non empty dest dir
> $ hg init converted
>
> $ hg convert orig/sub1 converted/sub1
> $ hg convert orig/sub2 converted/sub2
> $ hg convert orig converted
>
> This allows different options to be applied to different subrepos more readily.
> It assumes the shamap is in the default location in each converted subrepo for
> simplicity. It also allows for a subrepo to be cloned into place, in case _it_
> doesn't need a conversion. I was able to convert away from using
> largefiles/bfiles in several subrepos with this mechanism.
>
> diff --git a/hgext/convert/__init__.py b/hgext/convert/__init__.py
> --- a/hgext/convert/__init__.py
> +++ b/hgext/convert/__init__.py
> @@ -328,6 +328,23 @@ def convert(ui, src, dest=None, revmapfi
> Mercurial Destination
> #####################
>
> + The Mercurial destination will recognize Mercurial subrepositories in the
> + destination directory, and update the .hgsubstate file automatically if the
> + destination subrepositories contain the <dest>/<sub>/.hg/shamap file.
> + Converting a repository with subrepositories requires converting a single
> + repository at a time, from the bottom up.
> +
> + .. container:: verbose
> +
> + An example showing how to convert a repository with subrepositories::
> +
> + # so convert knows the type when it sees a non empty destination
> + $ hg init converted
> +
> + $ hg convert orig/sub1 converted/sub1
> + $ hg convert orig/sub2 converted/sub2
> + $ hg convert orig converted
> +
> The following options are supported:
>
> :convert.hg.clonebranches: dispatch source branches in separate
> diff --git a/hgext/convert/hg.py b/hgext/convert/hg.py
> --- a/hgext/convert/hg.py
> +++ b/hgext/convert/hg.py
> @@ -23,7 +23,7 @@ from mercurial.i18n import _
> from mercurial.node import bin, hex, nullid
> from mercurial import hg, util, context, bookmarks, error, scmutil, exchange
>
> -from common import NoRepo, commit, converter_source, converter_sink
> +from common import NoRepo, commit, converter_source, converter_sink, mapfile
>
> import re
> sha1re = re.compile(r'\b[0-9a-f]{12,40}\b')
> @@ -59,6 +59,7 @@ class mercurial_sink(converter_sink):
> self.lock = None
> self.wlock = None
> self.filemapmode = False
> + self.subrevmaps = {}
>
> def before(self):
> self.ui.debug('run hg sink pre-conversion action\n')
> @@ -135,6 +136,45 @@ class mercurial_sink(converter_sink):
> fp.write('%s %s\n' % (revid, s[1]))
> return fp.getvalue()
>
> + def _rewritesubstate(self, source, data):
> + fp = cStringIO.StringIO()
> + for line in data.splitlines():
> + s = line.split(' ', 1)
> + if len(s) != 2:
> + continue
> +
> + revid = s[0]
> + subpath = s[1]
> + if revid != hex(nullid):
> + revmap = self.subrevmaps.get(subpath)
> + if revmap is None:
> + revmap = mapfile(self.ui,
> + self.repo.wjoin(subpath, '.hg/shamap'))
> + self.subrevmaps[subpath] = revmap
> +
> + # It is reasonable that one or more of the subrepos don't
> + # need to be converted, in which case they can be cloned
> + # into place instead of converted. Therefore, only warn
> + # once.
> + msg = _('no ".hgsubstate" updates will be made for "%s"\n')
> + if len(revmap) == 0:
> + sub = self.repo.wvfs.reljoin(subpath, '.hg')
> +
> + if self.repo.wvfs.exists(sub):
> + self.ui.warn(msg % subpath)
> +
> + newid = revmap.get(revid)
> + if not newid:
> + if len(revmap) > 0:
> + self.ui.warn(_("%s is missing from %s/.hg/shamap\n") %
> + (revid, subpath))
> + else:
> + revid = newid
> +
> + fp.write('%s %s\n' % (revid, subpath))
> +
> + return fp.getvalue()
> +
> def putcommit(self, files, copies, parents, commit, source, revmap, full,
> cleanp2):
> files = dict(files)
> @@ -152,6 +192,8 @@ class mercurial_sink(converter_sink):
> return None
> if f == '.hgtags':
> data = self._rewritetags(source, revmap, data)
> + if f == '.hgsubstate':
> + data = self._rewritesubstate(source, data)
> return context.memfilectx(self.repo, f, data, 'l' in mode,
> 'x' in mode, copies.get(f))
>
> diff --git a/tests/test-convert.t b/tests/test-convert.t
> --- a/tests/test-convert.t
> +++ b/tests/test-convert.t
> @@ -279,6 +279,12 @@
> Mercurial Destination
> #####################
>
> + The Mercurial destination will recognize Mercurial subrepositories in the
> + destination directory, and update the .hgsubstate file automatically if
> + the destination subrepositories contain the <dest>/<sub>/.hg/shamap file.
> + Converting a repository with subrepositories requires converting a single
> + repository at a time, from the bottom up.
> +
> The following options are supported:
>
> convert.hg.clonebranches
> diff --git a/tests/test-fileset.t b/tests/test-fileset.t
> --- a/tests/test-fileset.t
> +++ b/tests/test-fileset.t
> @@ -180,16 +180,54 @@ Test subrepo predicate
> $ hg -R sub add sub/suba
> $ hg -R sub ci -m sub
> $ echo 'sub = sub' > .hgsub
> + $ hg init sub2
> + $ echo b > sub2/b
> + $ hg -R sub2 ci -Am sub2
> + adding b
> + $ echo 'sub2 = sub2' >> .hgsub
> $ fileset 'subrepo()'
> $ hg add .hgsub
> $ fileset 'subrepo()'
> sub
> + sub2
> $ fileset 'subrepo("sub")'
> sub
> $ fileset 'subrepo("glob:*")'
> sub
> + sub2
> $ hg ci -m subrepo
>
> +Test that .hgsubstate is updated as appropriate during a conversion. The
> +saverev property is enough to alter the hashes of the subrepo.
> +
> + $ hg init ../converted
> + $ hg --config extensions.convert= convert --config convert.hg.saverev=True \
> + > sub ../converted/sub
> + initializing destination ../converted/sub repository
> + scanning source...
> + sorting...
> + converting...
> + 0 sub
> + $ hg clone -U sub2 ../converted/sub2
> + $ hg --config extensions.convert= convert --config convert.hg.saverev=True \
> + > . ../converted
> + scanning source...
> + sorting...
> + converting...
> + 4 addfiles
> + 3 manychanges
> + 2 diverging
> + 1 merge
> + 0 subrepo
> + no ".hgsubstate" updates will be made for "sub2"
> + $ hg up -q -R ../converted -r tip
> + $ hg --cwd ../converted cat sub/suba sub2/b -r tip
> + a
> + b
> + $ oldnode=`hg log -r tip -T "{node}\n"`
> + $ newnode=`hg log -R ../converted -r tip -T "{node}\n"`
> + $ [[ "$oldnode" != "$newnode" ]] || echo "nothing changed"
> +
> Test with a revision
>
> $ hg log -G --template '{rev} {desc}\n'
> @@ -241,6 +279,7 @@ Test with a revision
>
> $ fileset -r4 'subrepo("re:su.*")'
> sub
> + sub2
> $ fileset -r4 'subrepo("sub")'
> sub
> $ fileset -r4 'b2 or c1'
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel at selenic.com
> https://selenic.com/mailman/listinfo/mercurial-devel
More information about the Mercurial-devel
mailing list