largefiles: update in two steps, handle interrupted updates better stable
authorMads Kiilerich <madski@unity3d.com>
Thu, 07 Nov 2013 01:56:40 +0100
branchstable
changeset 20063 8a021cd38719
parent 20062 452f68738f69
child 20064 99c4b8f79324
largefiles: update in two steps, handle interrupted updates better An update would try to fetch any missing largefiles after having updated normal files and standins. That could fail or be interrupted and would leave the working directory in a state where the largefiles not only were missing but also were scheduled for remove ... and where the old largefile was left in place. Instead we now remove old largefiles before starting to download and update missing largefiles.
hgext/largefiles/lfcommands.py
tests/test-largefiles-small-disk.t
--- a/hgext/largefiles/lfcommands.py	Thu Nov 07 01:49:48 2013 +0100
+++ b/hgext/largefiles/lfcommands.py	Thu Nov 07 01:56:40 2013 +0100
@@ -438,45 +438,27 @@
         if filelist is not None:
             lfiles = [f for f in lfiles if f in filelist]
 
-        if lfiles:
-            if printmessage:
-                ui.status(_('getting changed largefiles\n'))
-            cachelfiles(ui, repo, None, lfiles)
-
+        update = {}
         updated, removed = 0, 0
         for lfile in lfiles:
-            # updates a single largefile and copies the state of its standin from
-            # the repository's dirstate to its state in the lfdirstate.
             abslfile = repo.wjoin(lfile)
             absstandin = repo.wjoin(lfutil.standin(lfile))
             if os.path.exists(absstandin):
                 if (os.path.exists(absstandin + '.orig') and
                     os.path.exists(abslfile)):
                     shutil.copyfile(abslfile, abslfile + '.orig')
-                update1 = 0
                 expecthash = lfutil.readstandin(repo, lfile)
                 if (expecthash != '' and
                     (not os.path.exists(abslfile) or
                      expecthash != lfutil.hashfile(abslfile))):
-                    if not lfutil.copyfromcache(repo, expecthash, lfile):
-                        # use normallookup() to allocate entry in largefiles
-                        # dirstate, because lack of it misleads
-                        # lfilesrepo.status() into recognition that such cache
-                        # missing files are REMOVED.
-                        if lfile not in repo[None]: # not switched to normal
-                            util.unlinkpath(abslfile, ignoremissing=True)
-                        lfdirstate.normallookup(lfile)
-                        continue # don't try to set the mode
-                    else:
-                        # Synchronize largefile dirstate to the last modified
-                        # time of the file
-                        lfdirstate.normal(lfile)
-                    update1 = 1
-                mode = os.stat(absstandin).st_mode
-                if mode != os.stat(abslfile).st_mode:
-                    os.chmod(abslfile, mode)
-                    update1 = 1
-                updated += update1
+                    if lfile not in repo[None]: # not switched to normal file
+                        util.unlinkpath(abslfile, ignoremissing=True)
+                    # use normallookup() to allocate entry in largefiles
+                    # dirstate, because lack of it misleads
+                    # lfilesrepo.status() into recognition that such cache
+                    # missing files are REMOVED.
+                    lfdirstate.normallookup(lfile)
+                    update[lfile] = expecthash
             else:
                 # Remove lfiles for which the standin is deleted, unless the
                 # lfile is added to the repository again. This happens when a
@@ -486,6 +468,40 @@
                     repo.dirstate.normalize(lfile) not in repo[None]):
                     util.unlinkpath(abslfile)
                     removed += 1
+
+        # largefile processing might be slow and be interrupted - be prepared
+        lfdirstate.write()
+
+        if lfiles:
+            if printmessage:
+                ui.status(_('getting changed largefiles\n'))
+            cachelfiles(ui, repo, None, lfiles)
+
+        for lfile in lfiles:
+            update1 = 0
+
+            expecthash = update.get(lfile)
+            if expecthash:
+                if not lfutil.copyfromcache(repo, expecthash, lfile):
+                    # failed ... but already removed and set to normallookup
+                    continue
+                # Synchronize largefile dirstate to the last modified
+                # time of the file
+                lfdirstate.normal(lfile)
+                update1 = 1
+
+            # copy the state of largefile standin from the repository's
+            # dirstate to its state in the lfdirstate.
+            abslfile = repo.wjoin(lfile)
+            absstandin = repo.wjoin(lfutil.standin(lfile))
+            if os.path.exists(absstandin):
+                mode = os.stat(absstandin).st_mode
+                if mode != os.stat(abslfile).st_mode:
+                    os.chmod(abslfile, mode)
+                    update1 = 1
+
+            updated += update1
+
             state = repo.dirstate[lfutil.standin(lfile)]
             if state == 'n':
                 # When rebasing, we need to synchronize the standin and the
--- a/tests/test-largefiles-small-disk.t	Thu Nov 07 01:49:48 2013 +0100
+++ b/tests/test-largefiles-small-disk.t	Thu Nov 07 01:56:40 2013 +0100
@@ -64,3 +64,4 @@
 The largefile is not created in .hg/largefiles:
 
   $ ls bob/.hg/largefiles
+  dirstate