changeset 18064:7e2b9f6a2cd0 stable

largefiles: commit directories that only contain largefiles (issue3548) If we pass a directory to commit whose only commitable files are largefiles, the core commit code aborts before finding the largefiles. So we do the following: For directories that only have largefiles as matches, we explicitly add the largefiles to the matchlist and remove the directory. In other cases, we leave the match list unmodified.
author Levi Bard <levi@unity3d.com>
date Mon, 10 Dec 2012 14:58:42 +0100
parents ebc0fa067c07
children 2acd9c14b0d7 3bc21f6daac4
files hgext/largefiles/reposetup.py tests/test-largefiles.t
diffstat 2 files changed, 113 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/hgext/largefiles/reposetup.py	Thu Dec 06 13:21:27 2012 -0600
+++ b/hgext/largefiles/reposetup.py	Mon Dec 10 14:58:42 2012 +0100
@@ -355,11 +355,8 @@
                     lfdirstate.write()
                     return result
 
-                for f in match.files():
-                    if lfutil.isstandin(f):
-                        raise util.Abort(
-                            _('file "%s" is a largefile standin') % f,
-                            hint=('commit the largefile itself instead'))
+                lfiles = lfutil.listlfiles(self)
+                match._files = self._subdirlfs(match.files(), lfiles)
 
                 # Case 2: user calls commit with specified patterns: refresh
                 # any matching big files.
@@ -390,7 +387,6 @@
                 # standins corresponding to the big files requested by the
                 # user.  Have to modify _files to prevent commit() from
                 # complaining "not tracked" for big files.
-                lfiles = lfutil.listlfiles(self)
                 match = copy.copy(match)
                 origmatchfn = match.matchfn
 
@@ -463,6 +459,60 @@
             return super(lfilesrepo, self).push(remote, force, revs,
                 newbranch)
 
+        def _subdirlfs(self, files, lfiles):
+            '''
+            Adjust matched file list
+            If we pass a directory to commit whose only commitable files
+            are largefiles, the core commit code aborts before finding
+            the largefiles.
+            So we do the following:
+            For directories that only have largefiles as matches,
+            we explicitly add the largefiles to the matchlist and remove
+            the directory.
+            In other cases, we leave the match list unmodified.
+            '''
+            actualfiles = []
+            dirs = []
+            regulars = []
+
+            for f in files:
+                if lfutil.isstandin(f + '/'):
+                    raise util.Abort(
+                        _('file "%s" is a largefile standin') % f,
+                        hint=('commit the largefile itself instead'))
+                # Scan directories
+                if os.path.isdir(self.wjoin(f)):
+                    dirs.append(f)
+                else:
+                    regulars.append(f)
+
+            for f in dirs:
+                matcheddir = False
+                d = self.dirstate.normalize(f) + '/'
+                # Check for matched normal files
+                for mf in regulars:
+                    if self.dirstate.normalize(mf).startswith(d):
+                        actualfiles.append(f)
+                        matcheddir = True
+                        break
+                if not matcheddir:
+                    # If no normal match, manually append
+                    # any matching largefiles
+                    for lf in lfiles:
+                        if self.dirstate.normalize(lf).startswith(d):
+                            actualfiles.append(lf)
+                            if not matcheddir:
+                                actualfiles.append(lfutil.standin(f))
+                                matcheddir = True
+                # Nothing in dir, so readd it
+                # and let commit reject it
+                if not matcheddir:
+                    actualfiles.append(f)
+
+            # Always add normal files
+            actualfiles += regulars
+            return actualfiles
+
     repo.__class__ = lfilesrepo
 
     def checkrequireslfiles(ui, repo, **kwargs):
--- a/tests/test-largefiles.t	Thu Dec 06 13:21:27 2012 -0600
+++ b/tests/test-largefiles.t	Mon Dec 10 14:58:42 2012 +0100
@@ -345,6 +345,63 @@
   A sub2/large6
   A sub2/large7
 
+Committing directories containing only largefiles.
+
+  $ mkdir -p z/y/x/m
+  $ touch z/y/x/m/large1
+  $ touch z/y/x/large2
+  $ hg add --large z/y/x/m/large1 z/y/x/large2
+  $ hg commit -m "Subdir with directory only containing largefiles" z
+  Invoking status precommit hook
+  M large3
+  A large5
+  A sub2/large6
+  A sub2/large7
+  A z/y/x/large2
+  A z/y/x/m/large1
+  $ hg rollback --quiet
+  $ touch z/y/x/m/normal
+  $ hg add z/y/x/m/normal
+  $ hg commit -m "Subdir with mixed contents" z
+  Invoking status precommit hook
+  M large3
+  A large5
+  A sub2/large6
+  A sub2/large7
+  A z/y/x/large2
+  A z/y/x/m/large1
+  A z/y/x/m/normal
+  $ hg st
+  M large3
+  A large5
+  A sub2/large6
+  A sub2/large7
+  $ hg rollback --quiet
+  $ hg revert z/y/x/large2 z/y/x/m/large1
+  $ rm z/y/x/large2 z/y/x/m/large1
+  $ hg commit -m "Subdir with normal contents" z
+  Invoking status precommit hook
+  M large3
+  A large5
+  A sub2/large6
+  A sub2/large7
+  A z/y/x/m/normal
+  $ hg st
+  M large3
+  A large5
+  A sub2/large6
+  A sub2/large7
+  $ hg rollback --quiet
+  $ hg revert --quiet z
+  $ hg commit -m "Empty subdir" z
+  abort: z: no match under directory!
+  [255]
+  $ rm -rf z
+  $ hg ci -m "standin" .hglf
+  abort: file ".hglf" is a largefile standin
+  (commit the largefile itself instead)
+  [255]
+
 Test "hg status" with combination of 'file pattern' and 'directory
 pattern' for largefiles: