changeset 37517:491edf2435a0

lfs: add the ability to disable the usercache While the usercache is important for real world uses, I've been tripped up more than a couple of times by it in tests- thinking a file was being downloaded, but it was simply linked from the local cache. The syntax for setting it is the same as for setting a null remote endpoint, and like that endpoint, is left undocumented. This may or may not be a useful feature in the real world (I'd expect any sane filesystem to support hardlinks at this point).
author Matt Harbison <matt_harbison@yahoo.com>
date Sat, 07 Apr 2018 22:40:11 -0400
parents 20808ddb4990
children 092eff6833a7
files hgext/lfs/blobstore.py tests/test-lfs-serve.t
diffstat 2 files changed, 36 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/hgext/lfs/blobstore.py	Tue Apr 10 22:57:55 2018 -0400
+++ b/hgext/lfs/blobstore.py	Sat Apr 07 22:40:11 2018 -0400
@@ -7,6 +7,7 @@
 
 from __future__ import absolute_import
 
+import errno
 import hashlib
 import json
 import os
@@ -59,6 +60,26 @@
 
         yield ('', [], oids)
 
+class nullvfs(lfsvfs):
+    def __init__(self):
+        pass
+
+    def exists(self, oid):
+        return False
+
+    def read(self, oid):
+        # store.read() calls into here if the blob doesn't exist in its
+        # self.vfs.  Raise the same error as a normal vfs when asked to read a
+        # file that doesn't exist.  The only difference is the full file path
+        # isn't available in the error.
+        raise IOError(errno.ENOENT, '%s: No such file or directory' % oid)
+
+    def walk(self, path=None, onerror=None):
+        return ('', [], [])
+
+    def write(self, oid, data):
+        pass
+
 class filewithprogress(object):
     """a file-like object that supports __len__ and read.
 
@@ -97,8 +118,14 @@
     def __init__(self, repo):
         fullpath = repo.svfs.join('lfs/objects')
         self.vfs = lfsvfs(fullpath)
-        usercache = lfutil._usercachedir(repo.ui, 'lfs')
-        self.cachevfs = lfsvfs(usercache)
+        usercache = util.url(lfutil._usercachedir(repo.ui, 'lfs'))
+        if usercache.scheme in (None, 'file'):
+            self.cachevfs = lfsvfs(usercache.localpath())
+        elif usercache.scheme == 'null':
+            self.cachevfs = nullvfs()
+        else:
+            raise error.Abort(_('unknown lfs cache scheme: %s')
+                              % usercache.scheme)
         self.ui = repo.ui
 
     def open(self, oid):
@@ -129,11 +156,7 @@
             if realoid != oid:
                 raise error.Abort(_('corrupt remote lfs object: %s') % oid)
 
-        # XXX: should we verify the content of the cache, and hardlink back to
-        # the local store on success, but truncate, write and link on failure?
-        if not self.cachevfs.exists(oid):
-            self.ui.note(_('lfs: adding %s to the usercache\n') % oid)
-            lfutil.link(self.vfs.join(oid), self.cachevfs.join(oid))
+        self._linktousercache(oid)
 
     def write(self, oid, data):
         """Write blob to local blobstore.
@@ -144,9 +167,13 @@
         with self.vfs(oid, 'wb', atomictemp=True) as fp:
             fp.write(data)
 
+        self._linktousercache(oid)
+
+    def _linktousercache(self, oid):
         # XXX: should we verify the content of the cache, and hardlink back to
         # the local store on success, but truncate, write and link on failure?
-        if not self.cachevfs.exists(oid):
+        if (not self.cachevfs.exists(oid)
+            and not isinstance(self.cachevfs, nullvfs)):
             self.ui.note(_('lfs: adding %s to the usercache\n') % oid)
             lfutil.link(self.vfs.join(oid), self.cachevfs.join(oid))
 
--- a/tests/test-lfs-serve.t	Tue Apr 10 22:57:55 2018 -0400
+++ b/tests/test-lfs-serve.t	Sat Apr 07 22:40:11 2018 -0400
@@ -35,6 +35,7 @@
   $ cat >> $HGRCPATH <<EOF
   > [lfs]
   > url=file:$TESTTMP/dummy-remote/
+  > usercache = null://
   > threshold=10
   > [web]
   > allow_push=*