From: Rob Browning Date: Sat, 4 Nov 2017 20:55:23 +0000 (-0500) Subject: vfs2: report tree metadata for commits X-Git-Tag: 0.30~147 X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=bup.git;a=commitdiff_plain;h=38a49e9eb930e8a47e9de5a9715fad659a774e8b;hp=deb32e64f82e7ee5aa5bc8f6809267c25ff70b78 vfs2: report tree metadata for commits Don't synthesize metadata for a commit (like /foo, /foo/2017-..., or /.tag/some-commit) by reporting the author date as the mtime, instead use the metadata for the underlying commit tree. Among other things, this avoids an inconsistency between the metadata for ".../commit" and ".../commit/.". Signed-off-by: Rob Browning Tested-by: Rob Browning --- diff --git a/lib/bup/t/tvfs.py b/lib/bup/t/tvfs.py index c063012..d850dc8 100644 --- a/lib/bup/t/tvfs.py +++ b/lib/bup/t/tvfs.py @@ -226,15 +226,17 @@ def test_resolve(): tip_hash = exo(('git', 'show-ref', 'refs/heads/test'))[0] tip_oidx = tip_hash.strip().split()[0] tip_oid = tip_oidx.decode('hex') - tip_meta = Metadata() - tip_meta.mode = S_IFDIR | 0o755 - tip_meta.uid = tip_meta.gid = tip_meta.size = 0 - tip_meta.atime = tip_meta.mtime = tip_meta.ctime = save_time * 10**9 - test_revlist = vfs.RevList(meta=tip_meta, oid=tip_oid) tip_tree_oidx = exo(('git', 'log', '--pretty=%T', '-n1', tip_oidx))[0].strip() tip_tree_oid = tip_tree_oidx.decode('hex') tip_tree = tree_dict(repo, tip_tree_oid) + test_revlist = vfs.RevList(meta=S_IFDIR | 0o755, oid=tip_oid) + test_revlist_w_meta = vfs.RevList(meta=tip_tree['.'].meta, + oid=tip_oid) + expected_latest_item = vfs.Commit(meta=S_IFDIR | 0o755, + oid=tip_tree_oid, + coid=tip_oid) + expected_test_tag_item = expected_latest_item wvstart('resolve: /') res = resolve(repo, '/') @@ -255,7 +257,7 @@ def test_resolve(): ignore, tag_item = res[1] tag_content = frozenset(vfs.contents(repo, tag_item)) wvpasseq(frozenset([('.', tag_item), - ('test-tag', test_revlist)]), + ('test-tag', expected_test_tag_item)]), tag_content) wvstart('resolve: /test') @@ -264,10 +266,7 @@ 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.Commit(meta=S_IFDIR | 0o755, - oid=tip_tree_oid, - coid=tip_oid) - wvpasseq(frozenset([('.', test_revlist), + wvpasseq(frozenset([('.', test_revlist_w_meta), (save_time_str, expected_latest_item), ('latest', expected_latest_item)]), test_content) @@ -410,18 +409,18 @@ def test_duplicate_save_dates(): name, revlist = res[-1] wvpasseq('test', name) wvpasseq(('.', - '1970-01-02-034640-10', - '1970-01-02-034640-09', - '1970-01-02-034640-08', - '1970-01-02-034640-07', - '1970-01-02-034640-06', - '1970-01-02-034640-05', - '1970-01-02-034640-04', - '1970-01-02-034640-03', - '1970-01-02-034640-02', - '1970-01-02-034640-01', '1970-01-02-034640-00', + '1970-01-02-034640-01', + '1970-01-02-034640-02', + '1970-01-02-034640-03', + '1970-01-02-034640-04', + '1970-01-02-034640-05', + '1970-01-02-034640-06', + '1970-01-02-034640-07', + '1970-01-02-034640-08', + '1970-01-02-034640-09', + '1970-01-02-034640-10', 'latest'), - tuple(x[0] for x in vfs.contents(repo, revlist))) + tuple(sorted(x[0] for x in vfs.contents(repo, revlist)))) # FIXME: add tests for the want_meta=False cases. diff --git a/lib/bup/vfs2.py b/lib/bup/vfs2.py index 316d140..49f0c4a 100644 --- a/lib/bup/vfs2.py +++ b/lib/bup/vfs2.py @@ -284,12 +284,12 @@ def tree_data_and_bupm(repo, oid): break return data, None -def _find_dir_item_metadata(repo, item): - """Return the metadata for the tree or commit item, or None if the - tree has no metadata (i.e. older bup save, or non-bup tree). +def _find_treeish_oid_metadata(repo, oid): + """Return the metadata for the tree or commit oid, or None if the tree + has no metadata (i.e. older bup save, or non-bup tree). """ - tree_data, bupm_oid = tree_data_and_bupm(repo, item.oid) + tree_data, bupm_oid = tree_data_and_bupm(repo, oid) if bupm_oid: with _FileReader(repo, bupm_oid) as meta_stream: return _read_dir_meta(meta_stream) @@ -331,19 +331,29 @@ def fopen(repo, item): assert S_ISREG(item_mode(item)) return _FileReader(repo, item.oid) -def _commit_meta_from_auth_sec(author_sec): - m = Metadata() - m.mode = default_dir_mode - m.uid = m.gid = m.size = 0 - m.atime = m.mtime = m.ctime = author_sec * 10**9 - return m +def _commit_item_from_data(oid, data): + info = parse_commit(data) + return Commit(meta=default_dir_mode, + oid=info.tree.decode('hex'), + coid=oid) -def _commit_meta_from_oidx(repo, oidx): - it = repo.cat(oidx) +def _commit_item_from_oid(repo, oid, require_meta): + it = repo.cat(oid.encode('hex')) _, typ, size = next(it) assert typ == 'commit' - author_sec = parse_commit(''.join(it)).author_sec - return _commit_meta_from_auth_sec(author_sec) + commit = _commit_item_from_data(oid, ''.join(it)) + if require_meta: + meta = _find_treeish_oid_metadata(repo, commit.tree) + if meta: + commit = commit._replace(meta=meta) + return commit + +def _revlist_item_from_oid(repo, oid, require_meta): + if require_meta: + meta = _find_treeish_oid_metadata(repo, oid) or default_dir_mode + else: + meta = default_dir_mode + return RevList(oid=oid, meta=meta) def parse_rev_auth_secs(f): tree, author_secs = f.readline().split(None, 2) @@ -366,9 +376,7 @@ def root_items(repo, names=None): # in parallel (i.e. meta vs refs). for name, oid in tuple(repo.refs([], limit_to_heads=True)): assert(name.startswith('refs/heads/')) - name = name[11:] - m = _commit_meta_from_oidx(repo, oid.encode('hex')) - yield name, RevList(meta=m, oid=oid) + yield name[11:], _revlist_item_from_oid(repo, oid, False) return if '.' in names: @@ -385,8 +393,7 @@ def root_items(repo, names=None): continue assert typ == 'commit' commit = parse_commit(''.join(it)) - yield ref, RevList(meta=_commit_meta_from_auth_sec(commit.author_sec), - oid=oidx.decode('hex')) + yield ref, _revlist_item_from_oid(repo, oidx.decode('hex'), False) def ordered_tree_entries(tree_data, bupm=None): """Yields (name, mangled_name, kind, gitmode, oid) for each item in @@ -519,11 +526,10 @@ def revlist_items(repo, oid, names): oidx = oid.encode('hex') names = frozenset(name for name in (names or tuple()) \ if _save_name_rx.match(name) or name in ('.', 'latest')) - # Do this before we open the rev_list iterator so we're not nesting if (not names) or ('.' in names): - yield '.', RevList(oid=oid, meta=_commit_meta_from_oidx(repo, oidx)) - + yield '.', _revlist_item_from_oid(repo, oid, True) + revs = repo.rev_list((oidx,), format='%T %at', parse=parse_rev_auth_secs) rev_items, rev_names = tee(revs) revs = None # Don't disturb the tees @@ -565,10 +571,7 @@ def tags_items(repo, names): it = repo.cat(oidx) _, typ, size = next(it) if typ == 'commit': - tree_oid = parse_commit(''.join(it)).tree.decode('hex') - assert len(tree_oid) == 20 - # FIXME: more efficient/bulk? - return RevList(meta=_commit_meta_from_oidx(repo, oidx), oid=oid) + return _commit_item_from_data(oid, ''.join(it)) for _ in it: pass if typ == 'blob': return Item(meta=default_file_mode, oid=oid) @@ -714,7 +717,7 @@ def _resolve_path(repo, path, parent=None, want_meta=True, deref=False): return tuple(past) # It's treeish if want_meta and type(item) in real_tree_types: - dir_meta = _find_dir_item_metadata(repo, item) + dir_meta = _find_treeish_oid_metadata(repo, item.oid) if dir_meta: item = item._replace(meta=dir_meta) if not future: diff --git a/t/test-ls.sh b/t/test-ls.sh index cf9039e..8b87778 100755 --- a/t/test-ls.sh +++ b/t/test-ls.sh @@ -101,27 +101,27 @@ WVPASSEQ "$(WVPASS bup ls -d src/latest)" "src/latest" WVSTART "ls (long)" WVPASSEQ "$(WVPASS bup ls -l / | tr -s ' ' ' ')" \ -"drwxr-xr-x 0/0 0 1977-09-05 12:56 src" +"drwxr-xr-x 0/0 0 1970-01-01 00:00 src" WVPASSEQ "$(WVPASS bup ls -lA / | tr -s ' ' ' ')" \ "drwxr-xr-x 0/0 0 1970-01-01 00:00 .tag -drwxr-xr-x 0/0 0 1977-09-05 12:56 src" +drwxr-xr-x 0/0 0 1970-01-01 00:00 src" WVPASSEQ "$(WVPASS bup ls -lAF / | tr -s ' ' ' ')" \ "drwxr-xr-x 0/0 0 1970-01-01 00:00 .tag/ -drwxr-xr-x 0/0 0 1977-09-05 12:56 src/" +drwxr-xr-x 0/0 0 1970-01-01 00:00 src/" WVPASSEQ "$(WVPASS bup ls -la / | tr -s ' ' ' ')" \ "drwxr-xr-x 0/0 0 1970-01-01 00:00 . drwxr-xr-x 0/0 0 1970-01-01 00:00 .. drwxr-xr-x 0/0 0 1970-01-01 00:00 .tag -drwxr-xr-x 0/0 0 1977-09-05 12:56 src" +drwxr-xr-x 0/0 0 1970-01-01 00:00 src" WVPASSEQ "$(WVPASS bup ls -laF / | tr -s ' ' ' ')" \ "drwxr-xr-x 0/0 0 1970-01-01 00:00 ./ drwxr-xr-x 0/0 0 1970-01-01 00:00 ../ drwxr-xr-x 0/0 0 1970-01-01 00:00 .tag/ -drwxr-xr-x 0/0 0 1977-09-05 12:56 src/" +drwxr-xr-x 0/0 0 1970-01-01 00:00 src/" symlink_mode="$(WVPASS ls -l src/symlink | cut -b -10)" || exit $? socket_mode="$(WVPASS ls -l src/socket | cut -b -10)" || exit $? @@ -159,7 +159,7 @@ $symlink_mode $user/$group $symlink_size $symlink_date symlink -> file" WVPASSEQ "$(bup ls -la src/latest | tr -s ' ' ' ')" \ "drwx------ $user/$group 0 2009-10-03 23:48 . -drwxr-xr-x 0/0 0 1977-09-05 12:56 .. +drwxr-xr-x 0/0 0 1970-01-01 00:00 .. -rw------- $user/$group 0 2009-10-03 23:48 .dotfile -rwx------ $user/$group 0 2009-10-03 23:48 executable prw------- $user/$group 0 2009-10-03 23:48 fifo