Patchwork [v2] largefiles: Commit directories that only contain largefiles (issue3548)

login
register
mail settings
Submitter Levi Bard
Date Dec. 12, 2012, 2:48 p.m.
Message ID <e7e98a8bfe36017d3296.1355323703@retina-monoteam>
Download mbox | patch
Permalink /patch/69/
State Accepted
Commit 7e2b9f6a2cd043966fca50a29a9867fb12387a22
Headers show

Comments

Levi Bard - Dec. 12, 2012, 2:48 p.m.
# HG changeset patch
# User Levi Bard <levi at unity3d.com>
# Date 1355147922 -3600
# Node ID e7e98a8bfe36017d32965856e8ef9a8cb4929353
# Parent  40374059d227850ec2f5fb4f21a1b619136e2a6a
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.
Mads Kiilerich - Dec. 13, 2012, 8:44 p.m.
On Wed, Dec 12, 2012 at 3:48 PM, Levi Bard <
taktaktaktaktaktaktaktaktaktak at gmail.com> wrote:

> # HG changeset patch
> # User Levi Bard <levi at unity3d.com>
> # Date 1355147922 -3600
> # Node ID e7e98a8bfe36017d32965856e8ef9a8cb4929353
> # Parent  40374059d227850ec2f5fb4f21a1b619136e2a6a
> largefiles: Commit directories that only contain largefiles (issue3548)
>

Thanks, pushed to crew stable with a couple of minor changes: A lower case
subject line and ...

  def isstandin(filename):
>      '''Return true if filename is a big file standin. filename must be
>      in Mercurial's internal form (slash-separated).'''
> -    return filename.startswith(shortname + '/')
> +    return filename.startswith(shortname + '/') or filename == shortname
>

This function is used in a lot of places and invoked a lot of times. I
would rather not change it in stable.

+                if lfutil.isstandin(f):
>

Instead I pushed
  if lfutil.isstandin(f + '/'):

/Mads
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://selenic.com/pipermail/mercurial-devel/attachments/20121213/7a31002d/attachment.html>

Patch

diff -r 40374059d227 -r e7e98a8bfe36 hgext/largefiles/lfutil.py
--- a/hgext/largefiles/lfutil.py	Tue Nov 27 22:24:02 2012 +0100
+++ b/hgext/largefiles/lfutil.py	Mon Dec 10 14:58:42 2012 +0100
@@ -306,7 +306,7 @@ 
 def isstandin(filename):
     '''Return true if filename is a big file standin. filename must be
     in Mercurial's internal form (slash-separated).'''
-    return filename.startswith(shortname + '/')
+    return filename.startswith(shortname + '/') or filename == shortname
 
 def splitstandin(filename):
     # Split on / because that's what dirstate always uses, even on Windows.
diff -r 40374059d227 -r e7e98a8bfe36 hgext/largefiles/reposetup.py
--- a/hgext/largefiles/reposetup.py	Tue Nov 27 22:24:02 2012 +0100
+++ b/hgext/largefiles/reposetup.py	Mon Dec 10 14:58:42 2012 +0100
@@ -359,11 +359,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.
@@ -394,7 +391,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
 
@@ -467,6 +463,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):
diff -r 40374059d227 -r e7e98a8bfe36 tests/test-largefiles.t
--- a/tests/test-largefiles.t	Tue Nov 27 22:24:02 2012 +0100
+++ 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: