# HG changeset patch # User Matt Harbison # Date 1432834477 14400 # Node ID fcd2f9b06629661a0d9b29f105ff53da33104511 # Parent deed9c6b12d29bc404433d1eabad762d974acaf5 largefiles: use the convert extension for 'lfconvert --to-normal' The logic in the convert extension is more advanced, supporting extra features like converting revision IDs in 'extras' (e.g. 'amend_source'), supports updating hashes in commit messages, and outputs an SHA map file. Rather than try to duplicate all of that, just use the existing code. Even though the convert extension supports user supplied options like filemap, etc, those features aren't available on the lfconvert interface. Therefore, it is safe to use the filemap mechanism (in memory) to handle the standin -> file rename. The convert extension handles the destination locking for this path. There was a comment in test-lfconvert.t about the hash on rev 5 being different because it was doing a better job than "hg remove" + "hg merge" + "hg commit". It isn't clear to me what was happening or why, but now the hashes match the original repo exactly after a roundtrip, which seems like a good idea. If there really was something beneficial about the previous behavior, perhaps merge can be changed so that everyone benefits. Converting to a largefiles repo still uses the original (limited) lfconvert logic. diff -r deed9c6b12d2 -r fcd2f9b06629 hgext/largefiles/lfcommands.py --- a/hgext/largefiles/lfcommands.py Wed May 27 00:22:29 2015 -0700 +++ b/hgext/largefiles/lfcommands.py Thu May 28 13:34:37 2015 -0400 @@ -16,6 +16,9 @@ from mercurial.i18n import _ from mercurial.lock import release +from hgext.convert import convcmd +from hgext.convert import filemap + import lfutil import basestore @@ -70,12 +73,6 @@ success = False dstwlock = dstlock = None try: - # Lock destination to prevent modification while it is converted to. - # Don't need to lock src because we are just reading from its history - # which can't change. - dstwlock = rdst.wlock() - dstlock = rdst.lock() - # Get a list of all changesets in the source. The easy way to do this # is to simply walk the changelog, using changelog.nodesbetween(). # Take a look at mercurial/revlog.py:639 for more details. @@ -84,6 +81,12 @@ rsrc.heads())[0]) revmap = {node.nullid: node.nullid} if tolfile: + # Lock destination to prevent modification while it is converted to. + # Don't need to lock src because we are just reading from its + # history which can't change. + dstwlock = rdst.wlock() + dstlock = rdst.lock() + lfiles = set() normalfiles = set() if not pats: @@ -118,16 +121,51 @@ rdst.requirements.add('largefiles') rdst._writerequirements() else: - for ctx in ctxs: - ui.progress(_('converting revisions'), ctx.rev(), - unit=_('revision'), total=rsrc['tip'].rev()) - _addchangeset(ui, rsrc, rdst, ctx, revmap) + class lfsource(filemap.filemap_source): + def __init__(self, ui, source): + super(lfsource, self).__init__(ui, source, None) + self.filemapper.rename[lfutil.shortname] = '.' + + def getfile(self, name, rev): + realname, realrev = rev + f = super(lfsource, self).getfile(name, rev) + + if (not realname.startswith(lfutil.shortnameslash) + or f[0] is None): + return f + + # Substitute in the largefile data for the hash + hash = f[0].strip() + path = lfutil.findfile(rsrc, hash) - ui.progress(_('converting revisions'), None) + if path is None: + raise util.Abort(_("missing largefile for \'%s\' in %s") + % (realname, realrev)) + fp = open(path, 'rb') + + try: + return (fp.read(), f[1]) + finally: + fp.close() + + class converter(convcmd.converter): + def __init__(self, ui, source, dest, revmapfile, opts): + src = lfsource(ui, source) + + super(converter, self).__init__(ui, src, dest, revmapfile, + opts) + + found, missing = downloadlfiles(ui, rsrc) + if missing != 0: + raise util.Abort(_("all largefiles must be present locally")) + + convcmd.converter = converter + convcmd.convert(ui, src, dest) success = True finally: - rdst.dirstate.clear() - release(dstlock, dstwlock) + if tolfile: + rdst.dirstate.clear() + release(dstlock, dstwlock) if not success: # we failed, remove the new directory shutil.rmtree(rdst.root) diff -r deed9c6b12d2 -r fcd2f9b06629 tests/test-lfconvert.t --- a/tests/test-lfconvert.t Wed May 27 00:22:29 2015 -0700 +++ b/tests/test-lfconvert.t Thu May 28 13:34:37 2015 -0400 @@ -226,6 +226,7 @@ $ hg commit -m "add anotherlarge (should be a largefile)" $ cat .hglf/anotherlarge 3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3 + $ hg tag mytag $ cd .. round-trip: converting back to a normal (non-largefiles) repo with @@ -233,25 +234,30 @@ $ cd largefiles-repo $ hg lfconvert --to-normal . ../normal-repo initializing destination ../normal-repo + 0 additional largefiles cached + scanning source... + sorting... + converting... + 7 add large, normal1 + 6 add sub/* + 5 rename sub/ to stuff/ + 4 add normal3, modify sub/* + 3 remove large, normal3 + 2 merge + 1 add anotherlarge (should be a largefile) + 0 Added tag mytag for changeset abacddda7028 $ cd ../normal-repo $ cat >> .hg/hgrc < [extensions] > largefiles = ! > EOF -# Hmmm: the changeset ID for rev 5 is different from the original -# normal repo (../bigfile-repo), because the changelog filelist -# differs between the two incarnations of rev 5: this repo includes -# 'large' in the list, but ../bigfile-repo does not. Since rev 5 -# removes 'large' relative to the first parent in both repos, it seems -# to me that lfconvert is doing a *better* job than -# "hg remove" + "hg merge" + "hg commit". -# $ hg -R ../bigfile-repo debugdata -c 5 -# $ hg debugdata -c 5 $ hg log -G --template "{rev}:{node|short} {desc|firstline}\n" - o 6:1635824e6f59 add anotherlarge (should be a largefile) + o 7:b5fedc110b9d Added tag mytag for changeset 867ab992ecf4 | - o 5:7215f8deeaaf merge + o 6:867ab992ecf4 add anotherlarge (should be a largefile) + | + o 5:4884f215abda merge |\ | o 4:7285f817b77e remove large, normal3 | | @@ -264,8 +270,9 @@ o 0:117b8328f97a add large, normal1 $ hg update - 4 files updated, 0 files merged, 0 files removed, 0 files unresolved + 5 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg locate + .hgtags anotherlarge normal1 stuff/maybelarge.dat @@ -284,15 +291,18 @@ scanning source... sorting... converting... - 6 add large, normal1 - 5 add sub/* - 4 rename sub/ to stuff/ - 3 add normal3, modify sub/* - 2 remove large, normal3 - 1 merge - 0 add anotherlarge (should be a largefile) + 7 add large, normal1 + 6 add sub/* + 5 rename sub/ to stuff/ + 4 add normal3, modify sub/* + 3 remove large, normal3 + 2 merge + 1 add anotherlarge (should be a largefile) + 0 Added tag mytag for changeset abacddda7028 $ hg -R largefiles-repo-hg log -G --template "{rev}:{node|short} {desc|firstline}\n" + o 7:2f08f66459b7 Added tag mytag for changeset 17126745edfd + | o 6:17126745edfd add anotherlarge (should be a largefile) | o 5:9cc5aa7204f0 merge @@ -315,8 +325,8 @@ checking manifests crosschecking files in changesets and manifests checking files - 8 files, 7 changesets, 12 total revisions - searching 7 changesets for largefiles + 9 files, 8 changesets, 13 total revisions + searching 8 changesets for largefiles changeset 0:d4892ec57ce2: large references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/2e000fa7e85759c7f4c254d4d9c33ef481e459a7 (glob) changeset 1:334e5237836d: sub/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c (glob) changeset 2:261ad3f3f037: stuff/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c (glob) @@ -336,6 +346,18 @@ $ rm -f "${USERCACHE}"/* $ hg lfconvert --to-normal issue3519 normalized3519 initializing destination normalized3519 + 4 additional largefiles cached + scanning source... + sorting... + converting... + 7 add large, normal1 + 6 add sub/* + 5 rename sub/ to stuff/ + 4 add normal3, modify sub/* + 3 remove large, normal3 + 2 merge + 1 add anotherlarge (should be a largefile) + 0 Added tag mytag for changeset abacddda7028 Ensure the abort message is useful if a largefile is entirely unavailable $ rm -rf normalized3519 @@ -344,8 +366,20 @@ $ rm largefiles-repo/.hg/largefiles/* $ hg lfconvert --to-normal issue3519 normalized3519 initializing destination normalized3519 + anotherlarge: largefile 3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3 not available from file:/*/$TESTTMP/largefiles-repo (glob) + stuff/maybelarge.dat: largefile 76236b6a2c6102826c61af4297dd738fb3b1de38 not available from file:/*/$TESTTMP/largefiles-repo (glob) + stuff/maybelarge.dat: largefile 76236b6a2c6102826c61af4297dd738fb3b1de38 not available from file:/*/$TESTTMP/largefiles-repo (glob) + sub/maybelarge.dat: largefile 76236b6a2c6102826c61af4297dd738fb3b1de38 not available from file:/*/$TESTTMP/largefiles-repo (glob) large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file:/*/$TESTTMP/largefiles-repo (glob) - abort: missing largefile 'large' from revision d4892ec57ce212905215fad1d9018f56b99202ad + sub/maybelarge.dat: largefile 76236b6a2c6102826c61af4297dd738fb3b1de38 not available from file:/*/$TESTTMP/largefiles-repo (glob) + large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file:/*/$TESTTMP/largefiles-repo (glob) + stuff/maybelarge.dat: largefile 34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c not available from file:/*/$TESTTMP/largefiles-repo (glob) + large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file:/*/$TESTTMP/largefiles-repo (glob) + sub/maybelarge.dat: largefile 34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c not available from file:/*/$TESTTMP/largefiles-repo (glob) + large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file:/*/$TESTTMP/largefiles-repo (glob) + 0 additional largefiles cached + 11 largefiles failed to download + abort: all largefiles must be present locally [255]