- def __init__(self, parent, name, repo_dir=None):
- Node.__init__(self, parent, name, GIT_MODE_TREE, EMPTY_SHA, repo_dir)
-
- def _mksubs(self):
- self._subs = {}
- refs = git.list_refs(repo_dir = self._repo_dir)
- for ref in refs:
- #debug2('ref name: %s\n' % ref[0])
- revs = git.rev_list(ref[1].encode('hex'), repo_dir = self._repo_dir)
- 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._repo_dir)
- 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, repo_dir=None):
- Node.__init__(self, parent, name, GIT_MODE_TREE, EMPTY_SHA, repo_dir)
- self.commits = {}
-
- def _mksubs(self):
- self._subs = {}
- for (name, (hash, date)) in self.commits.items():
- n1 = Dir(self, name, GIT_MODE_TREE, hash, self._repo_dir)
- 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, repo_dir = None):
- Node.__init__(self, parent, name, GIT_MODE_TREE, EMPTY_SHA, repo_dir)
-
- def _mksubs(self):
- self._subs = {}
- for (name, sha) in git.list_refs(repo_dir = self._repo_dir):
- if name.startswith('refs/tags/'):
- name = name[10:]
- date = git.get_commit_dates([sha.encode('hex')],
- repo_dir=self._repo_dir)[0]
- commithex = sha.encode('hex')
- target = '../.commit/%s/%s' % (commithex[:2], commithex[2:])
- tag1 = FakeSymlink(self, name, target, repo_dir, self._repo_dir)
- 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.
+ return _resolve_path(repo, path, parent=parent, want_meta=want_meta,
+ deref=False)
+
+def resolve(repo, path, parent=None, want_meta=True):
+ """Follow the path in the virtual filesystem and return a tuple
+ representing the location, if any, denoted by the path. Each
+ element in the result tuple will be (name, info), where info will
+ be a VFS item that can be passed to functions like item_mode().
+
+ If a path segment that does not exist is encountered during
+ resolution, the result will represent the location of the missing
+ item, and that item in the result will be None.
+
+ Any attempt to traverse a non-directory will raise a VFS ENOTDIR
+ IOError exception.
+
+ Any symlinks along the path, including at the end, will be
+ resolved. A VFS IOError with the errno attribute set to ELOOP
+ will be raised if too many symlinks are traversed while following
+ the path. That exception is effectively like a normal
+ ELOOP IOError exception, but will include a terminus element
+ describing the location of the failure, which will be a tuple of
+ (name, info) elements.
+
+ The parent, if specified, must be a sequence of (name, item)
+ tuples, and will provide the starting point for the resolution of
+ the path. If no parent is specified, resolution will start at
+ '/'.
+
+ The result may include elements of parent directly, so they must
+ not be modified later. If this is a concern, pass in "name,
+ copy_item(item) for name, item in parent" instead.
+
+ When want_meta is true, detailed metadata will be included in each
+ result item if it's avaiable, otherwise item.meta will be an
+ integer mode. The metadata size may or may not be provided, but
+ can be computed by item_size() or augment_item_meta(...,
+ include_size=True). Setting want_meta=False is rarely desirable
+ since it can limit the VFS to just the metadata git itself can
+ represent, and so, as an example, fifos and sockets will appear to
+ be regular files (e.g. S_ISREG(item_mode(item)) will be true) .
+ But the option is provided because it may be more efficient when
+ only the path names or the more limited metadata is sufficient.
+
+ Do not modify any item.meta Metadata instances directly. If
+ needed, make a copy via item.meta.copy() and modify that instead.
+
+ """
+ result = _resolve_path(repo, path, parent=parent, want_meta=want_meta,
+ deref=True)
+ _, leaf_item = result[-1]
+ if leaf_item:
+ assert not S_ISLNK(item_mode(leaf_item))
+ return result
+
+def try_resolve(repo, path, parent=None, want_meta=True):
+ """If path does not refer to a symlink, does not exist, or refers to a
+ valid symlink, behave exactly like resolve(). If path refers to
+ an invalid symlink, behave like lresolve.
+
+ """
+ res = lresolve(repo, path, parent=parent, want_meta=want_meta)
+ leaf_name, leaf_item = res[-1]
+ if not leaf_item:
+ return res
+ if not S_ISLNK(item_mode(leaf_item)):
+ return res
+ deref = resolve(repo, leaf_name, parent=res[:-1], want_meta=want_meta)
+ deref_name, deref_item = deref[-1]
+ if deref_item:
+ return deref
+ return res
+
+def augment_item_meta(repo, item, include_size=False):
+ """Ensure item has a Metadata instance for item.meta. If item.meta is
+ currently a mode, replace it with a compatible "fake" Metadata
+ instance. If include_size is true, ensure item.meta.size is
+ correct, computing it if needed. If item.meta is a Metadata
+ instance, this call may modify it in place or replace it.
+
+ """
+ # If we actually had parallelism, we'd need locking...
+ assert repo
+ m = item.meta
+ if isinstance(m, Metadata):
+ if include_size and m.size is None:
+ m.size = _compute_item_size(repo, item)
+ return item._replace(meta=m)
+ return item
+ # m is mode
+ meta = Metadata()
+ meta.mode = m
+ meta.uid = meta.gid = meta.atime = meta.mtime = meta.ctime = 0
+ if S_ISLNK(m):
+ target = _readlink(repo, item.oid)
+ meta.symlink_target = target
+ meta.size = len(target)
+ elif include_size:
+ meta.size = _compute_item_size(repo, item)
+ return item._replace(meta=meta)
+
+def fill_in_metadata_if_dir(repo, item):
+ """If item is a directory and item.meta is not a Metadata instance,
+ attempt to find the metadata for the directory. If found, return
+ a new item augmented to include that metadata. Otherwise, return
+ item. May be useful for the output of contents().
+