Patchwork [1,of,7,PoC] largefiles: introduce standintolargefiles decorator

login
register
mail settings
Submitter Mads Kiilerich
Date Sept. 22, 2014, 9:13 a.m.
Message ID <0a6268c1e3ef11406fba.1411377214@localhost.localdomain>
Download mbox | patch
Permalink /patch/5909/
State Deferred
Headers show

Comments

Mads Kiilerich - Sept. 22, 2014, 9:13 a.m.
# HG changeset patch
# User Mads Kiilerich <madski@unity3d.com>
# Date 1411342606 -7200
#      Mon Sep 22 01:36:46 2014 +0200
# Node ID 0a6268c1e3ef11406fbaf186f5941069a08e2073
# Parent  5e16fe6fdd32124c3295db5ec40b076084cc5bd4
largefiles: introduce standintolargefiles decorator

This is a proof of concept for an alternative way of synchronizing standins
with the large files. Instead of hooking deeply into Mercurial internals, we
keep it simple. We make sure to sync from largefile content to hashes in
standin files before running commands, and after running the command we fetch
largefiles for standinds that has changed.

This might be a simpler, less intrusive and more reliable approach than the
existing one. For example, it gets rid of _isrebasing and _istransplanting.

This will do the wrapping at command level. It would probably be better to do
it at a slightly lower level, such as if all commands had their core
functionality in a corresponding function in the hg module.

Most of the test changes are acceptable minor changes. Some failures are real
and should be investigated further. Some of the failures are caused by missing
subrepo handling.

Patch

diff --git a/hgext/largefiles/lfutil.py b/hgext/largefiles/lfutil.py
--- a/hgext/largefiles/lfutil.py
+++ b/hgext/largefiles/lfutil.py
@@ -363,6 +363,36 @@  def getstandinsstate(repo):
         standins.append((lfile, hash))
     return standins
 
+def standintolargefiles(printmessage=True, normallookup=False):
+    def decorate(f):
+        def new(orig, ui, repo, *a, **b):
+            wlock = repo.wlock()
+            try:
+                lfdirstate = openlfdirstate(ui, repo)
+                (modified, added, removed, missing, unknown, ignored, clean) = \
+                    lfdirstatestatus(lfdirstate, repo, repo['.'].rev())
+                lfdirstate.write()
+                for lfile in modified:
+                    updatestandin(repo, standin(lfile))
+                for lfile in missing:
+                    standinfile = repo.wjoin(standin(lfile))
+                    if os.path.exists(standinfile):
+                        os.unlink(standinfile)
+
+                oldstandins = getstandinsstate(repo)
+                ret = f(orig, ui, repo, *a, **b)
+                newstandins = getstandinsstate(repo)
+                filelist = getlfilestoupdate(oldstandins, newstandins)
+                import lfcommands
+                lfcommands.updatelfiles(ui, repo, filelist,
+                                        printmessage=printmessage,
+                                        normallookup=normallookup)
+                return ret
+            finally:
+                wlock.release()
+        return new
+    return decorate
+
 def synclfdirstate(repo, lfdirstate, lfile, normallookup):
     lfstandin = standin(lfile)
     if lfstandin in repo.dirstate:
diff --git a/hgext/largefiles/overrides.py b/hgext/largefiles/overrides.py
--- a/hgext/largefiles/overrides.py
+++ b/hgext/largefiles/overrides.py
@@ -655,64 +655,41 @@  def overridecopy(orig, ui, repo, pats, o
 # commits. Update the standins then run the original revert, changing
 # the matcher to hit standins instead of largefiles. Based on the
 # resulting standins update the largefiles.
+#
+# lfdirstate should be 'normallookup'-ed for updated files,
+# because reverting doesn't touch dirstate for 'normal' files
+# when target revision is explicitly specified: in such case,
+# 'n' and valid timestamp in dirstate doesn't ensure 'clean'
+# of target (standin) file.
+@lfutil.standintolargefiles(printmessage=False, normallookup=True)
 def overriderevert(orig, ui, repo, *pats, **opts):
-    # Because we put the standins in a bad state (by updating them)
-    # and then return them to a correct state we need to lock to
-    # prevent others from changing them in their incorrect state.
-    wlock = repo.wlock()
+    def overridematch(ctx, pats=[], opts={}, globbed=False,
+            default='relpath'):
+        match = oldmatch(ctx, pats, opts, globbed, default)
+        m = copy.copy(match)
+        def tostandin(f):
+            if lfutil.standin(f) in ctx:
+                return lfutil.standin(f)
+            elif lfutil.standin(f) in repo[None]:
+                return None
+            return f
+        m._files = [tostandin(f) for f in m._files]
+        m._files = [f for f in m._files if f is not None]
+        m._fmap = set(m._files)
+        m._always = False
+        origmatchfn = m.matchfn
+        def matchfn(f):
+            if lfutil.isstandin(f):
+                return (origmatchfn(lfutil.splitstandin(f)) and
+                        (f in repo[None] or f in ctx))
+            return origmatchfn(f)
+        m.matchfn = matchfn
+        return m
+    oldmatch = installmatchfn(overridematch)
     try:
-        lfdirstate = lfutil.openlfdirstate(ui, repo)
-        (modified, added, removed, missing, unknown, ignored, clean) = \
-            lfutil.lfdirstatestatus(lfdirstate, repo, repo['.'].rev())
-        lfdirstate.write()
-        for lfile in modified:
-            lfutil.updatestandin(repo, lfutil.standin(lfile))
-        for lfile in missing:
-            if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
-                os.unlink(repo.wjoin(lfutil.standin(lfile)))
-
-        oldstandins = lfutil.getstandinsstate(repo)
-
-        def overridematch(ctx, pats=[], opts={}, globbed=False,
-                default='relpath'):
-            match = oldmatch(ctx, pats, opts, globbed, default)
-            m = copy.copy(match)
-            def tostandin(f):
-                if lfutil.standin(f) in ctx:
-                    return lfutil.standin(f)
-                elif lfutil.standin(f) in repo[None]:
-                    return None
-                return f
-            m._files = [tostandin(f) for f in m._files]
-            m._files = [f for f in m._files if f is not None]
-            m._fmap = set(m._files)
-            m._always = False
-            origmatchfn = m.matchfn
-            def matchfn(f):
-                if lfutil.isstandin(f):
-                    return (origmatchfn(lfutil.splitstandin(f)) and
-                            (f in repo[None] or f in ctx))
-                return origmatchfn(f)
-            m.matchfn = matchfn
-            return m
-        oldmatch = installmatchfn(overridematch)
-        try:
-            orig(ui, repo, *pats, **opts)
-        finally:
-            restorematchfn()
-
-        newstandins = lfutil.getstandinsstate(repo)
-        filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
-        # lfdirstate should be 'normallookup'-ed for updated files,
-        # because reverting doesn't touch dirstate for 'normal' files
-        # when target revision is explicitly specified: in such case,
-        # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
-        # of target (standin) file.
-        lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
-                                normallookup=True)
-
+        orig(ui, repo, *pats, **opts)
     finally:
-        wlock.release()
+        restorematchfn()
 
 # When we rebase a repository with remotely changed largefiles, we need to
 # take some extra care so that the largefiles are correctly updated in the
@@ -817,6 +794,7 @@  def hgclone(orig, ui, opts, *args, **kwa
 
     return result
 
+@lfutil.standintolargefiles(printmessage=False)
 def overriderebase(orig, ui, repo, **opts):
     repo._isrebasing = True
     try:
@@ -1196,15 +1174,11 @@  def overriderollback(orig, ui, repo, **o
         wlock.release()
     return result
 
+@lfutil.standintolargefiles(printmessage=True)
 def overridetransplant(orig, ui, repo, *revs, **opts):
     try:
-        oldstandins = lfutil.getstandinsstate(repo)
         repo._istransplanting = True
         result = orig(ui, repo, *revs, **opts)
-        newstandins = lfutil.getstandinsstate(repo)
-        filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
-        lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
-                                printmessage=True)
     finally:
         repo._istransplanting = False
     return result