]> arthur.barton.de Git - bup.git/blobdiff - lib/bup/vfs.py
vfs.contents: remove unreachable code to handle commit blobs
[bup.git] / lib / bup / vfs.py
index f231f4d790ddd48913dd1c2ad8025a33aa28b4db..7ad33e42625d7002c2e2c620b1773aa52dec6f58 100644 (file)
@@ -13,7 +13,8 @@ Each path is represented by an item that has least an item.meta which
 may be either a Metadata object, or an integer mode.  Functions like
 item_mode() and item_size() will return the mode and size in either
 case.  Any item.meta Metadata instances must not be modified directly.
-Make a copy to modify via item.meta.copy() if needed.
+Make a copy to modify via item.meta.copy() if needed, or call
+copy_item().
 
 The want_meta argument is advisory for calls that accept it, and it
 may not be honored.  Callers must be able to handle an item.meta value
@@ -49,11 +50,13 @@ from __future__ import absolute_import, print_function
 from collections import namedtuple
 from errno import ELOOP, ENOENT, ENOTDIR
 from itertools import chain, dropwhile, groupby, izip, tee
+from random import randrange
 from stat import S_IFDIR, S_IFLNK, S_IFREG, S_ISDIR, S_ISLNK, S_ISREG
 from time import localtime, strftime
 import exceptions, re, sys
 
 from bup import client, git, metadata
+from bup.compat import range
 from bup.git import BUP_CHUNKED, cp, get_commit_items, parse_commit, tree_decode
 from bup.helpers import debug2, last
 from bup.metadata import Metadata
@@ -234,8 +237,8 @@ _tags = Tags(meta=default_dir_mode)
 ### vfs cache
 
 ### A general purpose shared cache with (currently) cheap random
-### eviction.  There is currently no weighting so a single commit item
-### is just as likely to be evicted as an entire "rev-list".  See
+### eviction.  At the moment there is no weighting so a single commit
+### item is just as likely to be evicted as an entire "rev-list".  See
 ### is_valid_cache_key for a description of the expected content.
 
 _cache = {}
@@ -250,12 +253,15 @@ def clear_cache():
 def is_valid_cache_key(x):
     """Return logically true if x looks like it could be a valid cache key
     (with respect to structure).  Current valid cache entries:
+      (path, parent, want_meta, dref) -> resolution
       commit_oid -> commit
       commit_oid + ':r' -> rev-list
          i.e. rev-list -> {'.', commit, '2012...', next_commit, ...}
     """
     # Suspect we may eventually add "(container_oid, name) -> ...", and others.
     x_t = type(x)
+    if x_t is tuple:
+        return len(x) == 4
     if x_t is bytes:
         if len(x) == 20:
             return True
@@ -272,14 +278,15 @@ def cache_notice(key, value):
     assert is_valid_cache_key(key)
     if key in _cache:
         return
-    _cache[key] = value
     if len(_cache) < _cache_max_items:
+        _cache_keys.append(key)
+        _cache[key] = value
         return
-    victim_i = random.randrange(0, len(_cache_keys))
+    victim_i = randrange(0, len(_cache_keys))
     victim = _cache_keys[victim_i]
+    del _cache[victim]
     _cache_keys[victim_i] = key
-    _cache.pop(victim)
-
+    _cache[key] = value
 
 def cache_get_commit_item(oid, need_meta=True):
     """Return the requested tree item if it can be found in the cache.
@@ -301,16 +308,15 @@ def cache_get_revlist_item(oid, need_meta=True):
     if commit:
         return RevList(oid=oid, meta=commit.meta)
 
-
 def copy_item(item):
     """Return a completely independent copy of item, such that
     modifications will not affect the original.
 
     """
     meta = getattr(item, 'meta', None)
-    if not meta:
-        return item
-    return(item._replace(meta=meta.copy()))
+    if isinstance(meta, Metadata):
+        return(item._replace(meta=meta.copy()))
+    return item
 
 def item_mode(item):
     """Return the integer mode (stat st_mode) for item."""
@@ -576,7 +582,7 @@ def _reverse_suffix_duplicates(strs):
         else:
             ndig = len(str(ndup - 1))
             fmt = '%s-' + '%0' + str(ndig) + 'd'
-            for i in xrange(ndup - 1, -1, -1):
+            for i in range(ndup - 1, -1, -1):
                 yield fmt % (name, i)
 
 def parse_rev(f):
@@ -728,24 +734,19 @@ def contents(repo, item, names=None, want_meta=True):
     assert repo
     assert S_ISDIR(item_mode(item))
     item_t = type(item)
-
     if item_t in real_tree_types:
         it = repo.cat(item.oid.encode('hex'))
-        _, obj_type, size = next(it)
+        _, obj_t, size = next(it)
         data = ''.join(it)
-        if obj_type == 'tree':
-            if want_meta:
-                item_gen = tree_items_with_meta(repo, item.oid, data, names)
-            else:
-                item_gen = tree_items(item.oid, data, names)
-        elif obj_type == 'commit':
-            if want_meta:
-                item_gen = tree_items_with_meta(repo, item.oid, tree_data, names)
-            else:
-                item_gen = tree_items(item.oid, tree_data, names)
-        else:
+        if obj_t != 'tree':
             for _ in it: pass
-            raise Exception('unexpected git ' + obj_type)
+            # Note: it shouldn't be possible to see an Item with type
+            # 'commit' since a 'commit' should always produce a Commit.
+            raise Exception('unexpected git ' + obj_t)
+        if want_meta:
+            item_gen = tree_items_with_meta(repo, item.oid, data, names)
+        else:
+            item_gen = tree_items(item.oid, data, names)
     elif item_t == RevList:
         item_gen = revlist_items(repo, item.oid, names)
     elif item_t == Root:
@@ -758,6 +759,15 @@ def contents(repo, item, names=None, want_meta=True):
         yield x
 
 def _resolve_path(repo, path, parent=None, want_meta=True, deref=False):
+    cache_key = (tuple(path), parent, not not want_meta, not not deref)
+    resolution = cache_get(cache_key)
+    if resolution:
+        return resolution
+
+    def notice_resolution(r):
+        cache_notice(cache_key, r)
+        return r
+
     def raise_dir_required_but_not_dir(path, parent, past):
         raise IOError(ENOTDIR,
                       "path %r%s resolves to non-directory %r"
@@ -783,10 +793,10 @@ def _resolve_path(repo, path, parent=None, want_meta=True, deref=False):
         deref = True
     if not future:  # path was effectively '.' or '/'
         if is_absolute:
-            return (('', _root),)
+            return notice_resolution((('', _root),))
         if parent:
-            return tuple(parent)
-        return [('', _root)]
+            return notice_resolution(tuple(parent))
+        return notice_resolution((('', _root),))
     if is_absolute:
         past = [('', _root)]
     else:
@@ -796,7 +806,7 @@ def _resolve_path(repo, path, parent=None, want_meta=True, deref=False):
         if not future:
             if must_be_dir and not S_ISDIR(item_mode(past[-1][1])):
                 raise_dir_required_but_not_dir(path, parent, past)
-            return tuple(past)
+            return notice_resolution(tuple(past))
         segment = future.pop()
         if segment == '..':
             assert len(past) > 0
@@ -817,7 +827,7 @@ def _resolve_path(repo, path, parent=None, want_meta=True, deref=False):
                 past[-1] = parent_name, parent_item
             if not item:
                 past.append((segment, None),)
-                return tuple(past)
+                return notice_resolution(tuple(past))
             mode = item_mode(item)
             if not S_ISLNK(mode):
                 if not S_ISDIR(mode):
@@ -831,7 +841,7 @@ def _resolve_path(repo, path, parent=None, want_meta=True, deref=False):
                                       terminus=past)
                     if must_be_dir:
                         raise_dir_required_but_not_dir(path, parent, past)
-                    return tuple(past)
+                    return notice_resolution(tuple(past))
                 # It's treeish
                 if want_meta and type(item) in real_tree_types:
                     dir_meta = _find_treeish_oid_metadata(repo, item.oid)
@@ -851,7 +861,7 @@ def _resolve_path(repo, path, parent=None, want_meta=True, deref=False):
                 is_absolute, _, target_future = _decompose_path(target)
                 if is_absolute:
                     if not target_future:  # path was effectively '/'
-                        return (('', _root),)
+                        return notice_resolution((('', _root),))
                     past = [('', _root)]
                     future = target_future
                 else: