- def __init__(self, parent, name):
- Node.__init__(self, parent, name, GIT_MODE_TREE, EMPTY_SHA)
-
- def _mksubs(self):
- self._subs = {}
- refs = git.list_refs()
- for ref in refs:
- #debug2('ref name: %s\n' % ref[0])
- revs = git.rev_list(ref[1].encode('hex'))
- for (date, commit) in revs:
- #debug2('commit: %s date: %s\n' % (commit.encode('hex'), date))
- commithex = commit.encode('hex')
- containername = commithex[:2]
- dirname = commithex[2:]
- n1 = self._subs.get(containername)
- if not n1:
- n1 = CommitList(self, containername)
- self._subs[containername] = n1
-
- if n1.commits.get(dirname):
- # Stop work for this ref, the rest should already be present
- break
-
- n1.commits[dirname] = (commit, date)
-
-
-class CommitList(Node):
- """A list of commits with hashes that start with the current node's name."""
- def __init__(self, parent, name):
- Node.__init__(self, parent, name, GIT_MODE_TREE, EMPTY_SHA)
- self.commits = {}
-
- def _mksubs(self):
- self._subs = {}
- for (name, (hash, date)) in self.commits.items():
- n1 = Dir(self, name, GIT_MODE_TREE, hash)
- n1.ctime = n1.mtime = date
- self._subs[name] = n1
-
-
-class TagDir(Node):
- """A directory that contains all tags in the repository."""
- def __init__(self, parent, name):
- Node.__init__(self, parent, name, GIT_MODE_TREE, EMPTY_SHA)
-
- def _mksubs(self):
- self._subs = {}
- for (name, sha) in git.list_refs():
- if name.startswith('refs/tags/'):
- name = name[10:]
- date = git.rev_get_date(sha.encode('hex'))
- commithex = sha.encode('hex')
- target = '../.commit/%s/%s' % (commithex[:2], commithex[2:])
- tag1 = FakeSymlink(self, name, target)
- tag1.ctime = tag1.mtime = date
- self._subs[name] = tag1
-
-
-class BranchList(Node):
- """A list of links to commits reachable by a branch in bup's repository.
-
- Represents each commit as a symlink that points to the commit directory in
- /.commit/??/ . The symlink is named after the commit date.
+ # 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 in real_tree_types:
+ it = repo.cat(item.oid.encode('hex'))
+ _, obj_type, 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:
+ for _ in it: pass
+ raise Exception('unexpected git ' + obj_type)
+ elif item_t == RevList:
+ item_gen = revlist_items(repo, item.oid, names)
+ elif item_t == Root:
+ item_gen = root_items(repo, names, want_meta)
+ elif item_t == Tags:
+ item_gen = tags_items(repo, names)
+ else:
+ raise Exception('unexpected VFS item ' + str(item))
+ for x in item_gen:
+ yield x
+
+def _resolve_path(repo, path, parent=None, want_meta=True, deref=False):
+ def raise_dir_required_but_not_dir(path, parent, past):
+ raise IOError(ENOTDIR,
+ "path %r%s resolves to non-directory %r"
+ % (path,
+ ' (relative to %r)' % parent if parent else '',
+ past),
+ terminus=past)
+ global _root
+ assert repo
+ assert len(path)
+ if parent:
+ for x in parent:
+ assert len(x) == 2
+ assert type(x[0]) in (bytes, str)
+ assert type(x[1]) in item_types
+ assert parent[0][1] == _root
+ if not S_ISDIR(item_mode(parent[-1][1])):
+ raise IOError(ENOTDIR,
+ 'path resolution parent %r is not a directory'
+ % (parent,))
+ is_absolute, must_be_dir, future = _decompose_path(path)
+ if must_be_dir:
+ deref = True
+ if not future: # path was effectively '.' or '/'
+ if is_absolute:
+ return (('', _root),)
+ if parent:
+ return tuple(parent)
+ return [('', _root)]
+ if is_absolute:
+ past = [('', _root)]
+ else:
+ past = list(parent) if parent else [('', _root)]
+ hops = 0
+ while True:
+ 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)
+ segment = future.pop()
+ if segment == '..':
+ assert len(past) > 0
+ if len(past) > 1: # .. from / is /
+ assert S_ISDIR(item_mode(past[-1][1]))
+ past.pop()
+ else:
+ parent_name, parent_item = past[-1]
+ wanted = (segment,) if not want_meta else ('.', segment)
+ items = tuple(contents(repo, parent_item, names=wanted,
+ want_meta=want_meta))
+ if not want_meta:
+ item = items[0][1] if items else None
+ else: # First item will be '.' and have the metadata
+ item = items[1][1] if len(items) == 2 else None
+ dot, dot_item = items[0]
+ assert dot == '.'
+ past[-1] = parent_name, parent_item
+ if not item:
+ past.append((segment, None),)
+ return tuple(past)
+ mode = item_mode(item)
+ if not S_ISLNK(mode):
+ if not S_ISDIR(mode):
+ past.append((segment, item),)
+ if future:
+ raise IOError(ENOTDIR,
+ 'path %r%s ends internally in non-directory here: %r'
+ % (path,
+ ' (relative to %r)' % parent if parent else '',
+ past),
+ terminus=past)
+ if must_be_dir:
+ raise_dir_required_but_not_dir(path, parent, past)
+ return tuple(past)
+ # It's treeish
+ if want_meta and type(item) in real_tree_types:
+ dir_meta = _find_treeish_oid_metadata(repo, item.oid)
+ if dir_meta:
+ item = item._replace(meta=dir_meta)
+ past.append((segment, item))
+ else: # symlink
+ if not future and not deref:
+ past.append((segment, item),)
+ continue
+ if hops > 100:
+ raise IOError(ELOOP,
+ 'too many symlinks encountered while resolving %r%s'
+ % (path, ' relative to %r' % parent if parent else ''),
+ terminus=tuple(past + [(segment, item)]))
+ target = readlink(repo, item)
+ is_absolute, _, target_future = _decompose_path(target)
+ if is_absolute:
+ if not target_future: # path was effectively '/'
+ return (('', _root),)
+ past = [('', _root)]
+ future = target_future
+ else:
+ future.extend(target_future)
+ hops += 1
+
+def lresolve(repo, path, parent=None, want_meta=True):
+ """Perform exactly the same function as resolve(), except if the final
+ path element is a symbolic link, don't follow it, just return it
+ in the result.
+