]> arthur.barton.de Git - bup.git/blobdiff - lib/bup/git.py
gc: restart catpipe after each new pack
[bup.git] / lib / bup / git.py
index 460d6b11a2d9273aa0b2dfd4f0bfcb2599df5fe4..fc9eb39cb2c35ec8b9d0cc67c4f8b1cdd0b84e24 100644 (file)
@@ -12,7 +12,8 @@ from bup.helpers import (Sha1, add_error, chunkyreader, debug1, debug2,
                          fdatasync,
                          hostname, localtime, log, merge_iter,
                          mmap_read, mmap_readwrite,
-                         progress, qprogress, unlink, username, userfullname,
+                         progress, qprogress, stat_if_exists,
+                         unlink, username, userfullname,
                          utc_offset_str)
 
 
@@ -675,15 +676,16 @@ class PackWriter:
         self._require_objcache()
         return self.objcache.exists(id, want_source=want_source)
 
-    def write(self, sha, type, content):
-        """Write an object to the pack file.  Fails if sha exists()."""
+    def just_write(self, sha, type, content):
+        """Write an object to the pack file, bypassing the objcache.  Fails if
+        sha exists()."""
         self._write(sha, type, content)
 
     def maybe_write(self, type, content):
         """Write an object to the pack file if not present and return its id."""
         sha = calc_hash(type, content)
         if not self.exists(sha):
-            self.write(sha, type, content)
+            self.just_write(sha, type, content)
             self._require_objcache()
             self.objcache.add(sha)
         return sha
@@ -842,13 +844,13 @@ def _gitenv(repo_dir = None):
     return env
 
 
-def list_refs(refname=None, repo_dir=None,
+def list_refs(refnames=None, repo_dir=None,
               limit_to_heads=False, limit_to_tags=False):
-    """Yield (refname, hash) tuples for all repository refs unless a ref
-    name is specified.  Given a ref name, only include tuples for that
-    particular ref.  The limits restrict the result items to
-    refs/heads or refs/tags.  If both limits are specified, items from
-    both sources will be included.
+    """Yield (refname, hash) tuples for all repository refs unless
+    refnames are specified.  In that case, only include tuples for
+    those refs.  The limits restrict the result items to refs/heads or
+    refs/tags.  If both limits are specified, items from both sources
+    will be included.
 
     """
     argv = ['git', 'show-ref']
@@ -857,8 +859,8 @@ def list_refs(refname=None, repo_dir=None,
     if limit_to_tags:
         argv.append('--tags')
     argv.append('--')
-    if refname:
-        argv += [refname]
+    if refnames:
+        argv += refnames
     p = subprocess.Popen(argv,
                          preexec_fn = _gitenv(repo_dir),
                          stdout = subprocess.PIPE)
@@ -874,7 +876,7 @@ def list_refs(refname=None, repo_dir=None,
 
 def read_ref(refname, repo_dir = None):
     """Get the commit id of the most recent commit made on a given ref."""
-    refs = list_refs(refname, repo_dir=repo_dir, limit_to_heads=True)
+    refs = list_refs(refnames=[refname], repo_dir=repo_dir, limit_to_heads=True)
     l = tuple(islice(refs, 2))
     if l:
         assert(len(l) == 1)
@@ -1013,21 +1015,20 @@ def init_repo(path=None):
 
 
 def check_repo_or_die(path=None):
-    """Make sure a bup repository exists, and abort if not.
-    If the path to a particular repository was not specified, this function
-    initializes the default repository automatically.
-    """
+    """Check to see if a bup repository probably exists, and abort if not."""
     guess_repo(path)
-    try:
-        os.stat(repo('objects/pack/.'))
-    except OSError as e:
-        if e.errno == errno.ENOENT:
-            log('error: %r is not a bup repository; run "bup init"\n'
-                % repo())
+    top = repo()
+    pst = stat_if_exists(top + '/objects/pack')
+    if pst and stat.S_ISDIR(pst.st_mode):
+        return
+    if not pst:
+        top_st = stat_if_exists(top)
+        if not top_st:
+            log('error: repository %r does not exist (see "bup help init")\n'
+                % top)
             sys.exit(15)
-        else:
-            log('error: %s\n' % e)
-            sys.exit(14)
+    log('error: %r is not a repository\n' % top)
+    sys.exit(14)
 
 
 _ver = None
@@ -1130,7 +1131,7 @@ class CatPipe:
         self.p = None
         self.inprogress = None
 
-    def _restart(self):
+    def restart(self):
         self._abort()
         self.p = subprocess.Popen(['git', 'cat-file', '--batch'],
                                   stdin=subprocess.PIPE,
@@ -1141,7 +1142,7 @@ class CatPipe:
 
     def _fast_get(self, id):
         if not self.p or self.p.poll() != None:
-            self._restart()
+            self.restart()
         assert(self.p)
         poll_result = self.p.poll()
         assert(poll_result == None)
@@ -1269,8 +1270,9 @@ def walk_object(cat_pipe, id,
                 include_data=None):
     """Yield everything reachable from id via cat_pipe as a WalkItem,
     stopping whenever stop_at(id) returns true.  Throw MissingObject
-    if a hash encountered is missing from the repository.
-
+    if a hash encountered is missing from the repository, and don't
+    read or return blob content in the data field unless include_data
+    is set.
     """
     # Maintain the pending stack on the heap to avoid stack overflow
     pending = [(id, [], [], None)]
@@ -1279,7 +1281,17 @@ def walk_object(cat_pipe, id,
         if stop_at and stop_at(id):
             continue
 
-        item_it = cat_pipe.get(id)  # FIXME: use include_data
+        if (not include_data) and mode and stat.S_ISREG(mode):
+            # If the object is a "regular file", then it's a leaf in
+            # the graph, so we can skip reading the data if the caller
+            # hasn't requested it.
+            yield WalkItem(id=id, type='blob',
+                           chunk_path=chunk_path, path=parent_path,
+                           mode=mode,
+                           data=None)
+            continue
+
+        item_it = cat_pipe.get(id)
         type = item_it.next()
         if type not in ('blob', 'commit', 'tree'):
             raise Exception('unexpected repository object type %r' % type)