diff hgext/largefiles/lfcommands.py @ 25325:fcd2f9b06629

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.
author Matt Harbison <matt_harbison@yahoo.com>
date Thu, 28 May 2015 13:34:37 -0400
parents b8c3a0994b37
children 238e5cd94bbc
line wrap: on
line diff
--- 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)