]> arthur.barton.de Git - bup.git/commitdiff
vfs2: distinguish commits with a Commit type
authorRob Browning <rlb@defaultvalue.org>
Sat, 14 Oct 2017 16:58:08 +0000 (11:58 -0500)
committerRob Browning <rlb@defaultvalue.org>
Sat, 16 Dec 2017 23:29:16 +0000 (17:29 -0600)
Signed-off-by: Rob Browning <rlb@defaultvalue.org>
Tested-by: Rob Browning <rlb@defaultvalue.org>
lib/bup/t/tvfs.py
lib/bup/vfs2.py

index b8f813de302d8ae0ec410e1938691c4b46957076..c0630125d539de2069f371aa4210e544bb684639 100644 (file)
@@ -264,8 +264,9 @@ def test_resolve():
             wvpasseq((('', vfs._root), ('test', test_revlist)), res)
             ignore, test_item = res[1]
             test_content = frozenset(vfs.contents(repo, test_item))
-            expected_latest_item = vfs.Item(meta=S_IFDIR | 0o755,
-                                                    oid=tip_tree_oid)
+            expected_latest_item = vfs.Commit(meta=S_IFDIR | 0o755,
+                                              oid=tip_tree_oid,
+                                              coid=tip_oid)
             wvpasseq(frozenset([('.', test_revlist),
                                 (save_time_str, expected_latest_item),
                                 ('latest', expected_latest_item)]),
@@ -274,8 +275,9 @@ def test_resolve():
             wvstart('resolve: /test/latest')
             res = resolve(repo, '/test/latest')
             wvpasseq(3, len(res))
-            expected_latest_item_w_meta = vfs.Item(meta=tip_tree['.'].meta,
-                                                   oid=tip_tree_oid)
+            expected_latest_item_w_meta = vfs.Commit(meta=tip_tree['.'].meta,
+                                                     oid=tip_tree_oid,
+                                                     coid=tip_oid)
             expected = (('', vfs._root),
                         ('test', test_revlist),
                         ('latest', expected_latest_item_w_meta))
index 06266b58ed2c14d8c4016e4be3b388742761dcf2..978ce86ab8141c83d64d2c0568a8b0874621ae7b 100644 (file)
@@ -36,10 +36,12 @@ value for any directories other than '.' will be a default directory
 mode, not a Metadata object.  This is because the actual metadata for
 a directory is stored inside the directory.
 
-At the moment tagged commits (e.g. /.tag/some-commit) are represented
-as an item that is indistinguishable from a normal directory, so you
-cannot assume that the oid of an item satisfying
-S_ISDIR(item_mode(item)) refers to a tree.
+Commit items represent commits (e.g. /.tag/some-commit or
+/foo/latest), and for most purposes, they appear as the underlying
+tree.  S_ISDIR(item_mode(item)) will return true for both tree Items
+and Commits and the commit's oid is the tree hash.  The commit hash
+will be item.coid, and nominal_oid(item) will return coid for commits,
+oid for everything else.
 
 """
 
@@ -211,11 +213,24 @@ Chunky = namedtuple('Chunky', ('meta', 'oid'))
 Root = namedtuple('Root', ('meta'))
 Tags = namedtuple('Tags', ('meta'))
 RevList = namedtuple('RevList', ('meta', 'oid'))
-item_types = (Item, Chunky, Root, Tags, RevList)
+Commit = namedtuple('Commit', ('meta', 'oid', 'coid'))
+
+item_types = frozenset((Item, Chunky, Root, Tags, RevList, Commit))
+real_tree_types = frozenset((Item, Commit))
 
 _root = Root(meta=default_dir_mode)
 _tags = Tags(meta=default_dir_mode)
 
+
+def nominal_oid(item):
+    """If the item is a Commit, return its commit oid, otherwise return
+    the item's oid, if it has one.
+
+    """
+    if isinstance(item, Commit):
+        return item.coid
+    return getattr(item, 'oid', None)
+
 def copy_item(item):
     """Return a completely independent copy of item, such that
     modifications will not affect the original.
@@ -520,8 +535,11 @@ def _name_for_rev(rev):
 
 def _item_for_rev(rev):
     commit, (tree_oidx, utc) = rev
+    assert len(commit) == 40
     assert len(tree_oidx) == 40
-    return Item(meta=default_dir_mode, oid=tree_oidx.decode('hex'))
+    return Commit(meta=default_dir_mode,
+                  oid=tree_oidx.decode('hex'),
+                  coid=commit.decode('hex'))
 
 def revlist_items(repo, oid, names):
     assert len(oid) == 20
@@ -534,22 +552,23 @@ def revlist_items(repo, oid, names):
         yield '.', RevList(oid=oid, meta=_commit_meta_from_oidx(repo, oidx))
     
     revs = repo.rev_list((oidx,), format='%T %at', parse=parse_rev_auth_secs)
-    first_rev = next(revs, None)
-    revs = chain((first_rev,), revs)
     rev_items, rev_names = tee(revs)
     revs = None  # Don't disturb the tees
     rev_names = _reverse_suffix_duplicates(_name_for_rev(x) for x in rev_names)
     rev_items = (_item_for_rev(x) for x in rev_items)
+    first_commit = None
 
     if not names:
         for item in rev_items:
+            first_commit = first_commit or item
             yield next(rev_names), item
-        yield 'latest', _item_for_rev(first_rev)
+        yield 'latest', first_commit
         return
 
     # Revs are in reverse chronological order by default
     last_name = min(names)
     for item in rev_items:
+        first_commit = first_commit or item
         name = next(rev_names)  # Might have -N dup suffix
         if name < last_name:
             break
@@ -562,7 +581,7 @@ def revlist_items(repo, oid, names):
     for _ in rev_names: pass
         
     if 'latest' in names:
-        yield 'latest', _item_for_rev(first_rev)
+        yield 'latest', first_commit
 
 def tags_items(repo, names):
     global _tags
@@ -633,10 +652,12 @@ def contents(repo, item, names=None, want_meta=True):
 
     """
     # Q: are we comfortable promising '.' first when no names?
+    global _root, _tags
     assert repo
     assert S_ISDIR(item_mode(item))
     item_t = type(item)
-    if item_t == Item:
+
+    if item_t in real_tree_types:
         it = repo.cat(item.oid.encode('hex'))
         _, obj_type, size = next(it)
         data = ''.join(it)
@@ -687,7 +708,6 @@ def _resolve_path(repo, path, parent=None, want_meta=True, deref=False):
     if not future:  # e.g. if path was effectively '.'
         return tuple(past)
     hops = 0
-    result = None
     while True:
         segment = future.pop()
         if segment == '..':
@@ -715,7 +735,7 @@ def _resolve_path(repo, path, parent=None, want_meta=True, deref=False):
                     past.append((segment, item),)
                     return tuple(past)
                 # It's treeish
-                if want_meta and type(item) == Item:
+                if want_meta and type(item) in real_tree_types:
                     dir_meta = _find_dir_item_metadata(repo, item)
                     if dir_meta:
                         item = item._replace(meta=dir_meta)