Add ls-tree
This commit is contained in:
parent
7ef46efb82
commit
344119c099
109
libwyag.py
109
libwyag.py
@ -457,3 +457,112 @@ def log_graphviz(repo, sha, seen):
|
||||
p = p.decode("ascii")
|
||||
print (f" c_{sha} -> c_{p}")
|
||||
log_graphviz(repo, p, seen)
|
||||
|
||||
class GitTreeLeaf (object):
|
||||
def __init__(self, mode, path, sha):
|
||||
self.mode = mode
|
||||
self.path = path
|
||||
self.sha = sha
|
||||
|
||||
def tree_parse_one(raw, start=0):
|
||||
# Find the space terminator of the mode
|
||||
x = raw.find(b' ', start)
|
||||
assert x-start == 5 or x-start == 6
|
||||
|
||||
mode = raw[start:x]
|
||||
if len(mode) == 5:
|
||||
# Normalize to six bytes.
|
||||
mode = b" " + mode
|
||||
|
||||
# Find the NULL terminator of the path
|
||||
y = raw.find(b'\x00', x)
|
||||
path = raw[x+1:y]
|
||||
|
||||
sha = format(int.from_bytes(raw[y+1:y+21], "big"), "040x")
|
||||
return y+21, GitTreeLeaf(mode, path.decode('utf8'), sha)
|
||||
|
||||
def tree_parse(raw):
|
||||
pos = 0
|
||||
max = len(raw)
|
||||
ret = list()
|
||||
while pos < max:
|
||||
pos, data = tree_parse_one(raw, pos)
|
||||
ret.append(data)
|
||||
|
||||
return ret
|
||||
|
||||
# Notice: this isn't a comparison function, but a conversion function.
|
||||
# Python's default sort doesn't accept a custom comparison function,
|
||||
# like in most languages, but a 'key' argument that returns a new
|
||||
# value, which is compared using the default rules. So we just return
|
||||
# the leaf name, with an extra '/' if it's a directory.
|
||||
def tree_leaf_sort_key(leaf):
|
||||
if leaf.mode.startswith(b"10"):
|
||||
return leaf.path
|
||||
else:
|
||||
return leaf.path + "/"
|
||||
|
||||
def tree_serialize(obj):
|
||||
obj.items.sort(key=tree_leaf_sort_key)
|
||||
ret = b''
|
||||
for i in obj.items:
|
||||
ret += i.mode
|
||||
ret += b' '
|
||||
ret += i.path.encode("utf8")
|
||||
ret += b'\x00'
|
||||
sha = int(i.sha, 16)
|
||||
ret += sha.to_bytes(20, byteorder="big")
|
||||
return ret
|
||||
|
||||
class GitTree(GitObject):
|
||||
fmt=b'tree'
|
||||
|
||||
def deserialize(self, data):
|
||||
self.items = tree_parse(data)
|
||||
|
||||
def serialize(self):
|
||||
return tree_serialize(self)
|
||||
|
||||
def init(self):
|
||||
self.items = list()
|
||||
|
||||
argsp = argsubparsers.add_parser("ls-tree", help="Pretty-print a tree object.")
|
||||
argsp.add_argument("-r",
|
||||
dest="recursive",
|
||||
action="store_true",
|
||||
help="Recurse into sub-trees")
|
||||
|
||||
argsp.add_argument("tree",
|
||||
help="A tree-ish object.")
|
||||
|
||||
def cmd_ls_tree(args):
|
||||
repo = repo_find()
|
||||
ls_tree(repo, args.tree, args.recursive)
|
||||
|
||||
def ls_tree(repo, ref, recursive=None, prefix=""):
|
||||
sha = object_find(repo, ref, fmt=b"tree")
|
||||
obj = object_read(repo, sha)
|
||||
for item in obj.items:
|
||||
if len(item.mode) == 5:
|
||||
type = item.mode[0:1]
|
||||
else:
|
||||
type = item.mode[0:2]
|
||||
|
||||
match type:
|
||||
case b'04': type = "tree"
|
||||
case b'10': type = "blob"
|
||||
case b'12': type = "blob" # a symlink
|
||||
case b'16': type = "commit" # a submodule
|
||||
case _: raise Exception(f"Weird tree leaf mode {item.mode}")
|
||||
|
||||
if not (recursive and type=='tree'): # This is a leaf
|
||||
print("{0} {1} {2}\t{3}".format(
|
||||
"0" * (6 - len(item.mode)) + item.mode.decode("ascii"),
|
||||
# Git's ls-tree displays the type
|
||||
# of the object pointed to.
|
||||
type,
|
||||
item.sha,
|
||||
os.path.join(prefix, item.path)
|
||||
))
|
||||
else: # This is a branch (vs. leaf), recurse
|
||||
ls_tree(repo, item.sha, recursive, os.path.join(prefix, item.path))
|
||||
|
Loading…
Reference in New Issue
Block a user