Add ref handling and ref-parse command

This commit is contained in:
Nathan McRae 2024-07-07 12:25:33 -07:00
parent 9365c473fd
commit 3a7bad0e47

View File

@ -634,3 +634,152 @@ def tree_checkout(repo, tree, path):
# TODO: Support symlinks (identified by mode 12*****)
with open(dest, 'wb') as f:
f.write(obj.blobdata)
def ref_resolve(repo, ref):
path = GitRepository.repo_file(repo, ref)
# Sometimes, an indirect reference may be broken. This is normal
# in one specific case: we're looking for HEAD on a new repository
# with no commits. In that case, .git/HEAD points to "ref:
# refs/heads/master", but .git/refs/heads/master doesn't exist yet
# (since there's no commit for it to refer to).
if not os.path.isfile(path):
return None
with open(path, "r") as fp:
data = fp.read()[:-1] # Drop final "\n"
if data.startswith("ref: "):
return ref_resolve(repo, data[len("ref: "):])
else:
return data
def ref_list(repo, path=None):
if not path:
path = repo_dir(repo, "refs")
ret = collections.OrderedDict()
for f in sorted(os.listdir(path)):
can = os.path.join(path, f)
if os.path.isdir(can):
ret[f] = ref_list(repo, can)
else:
ret[f] = ref_resolve(repo, can)
return ret
argsp = argsubparsers.add_parser(
"tag",
help="List and create tags")
argsp.add_argument("-a",
action="store_true",
dest="create_tag_object",
help="Whether to create a tag object")
argsp.add_argument("name",
nargs="?",
help="The new tag's name")
argsp.add_argument("object",
default="HEAD",
nargs="?",
help="The object the new tag will point to")
def cmd_tag(args):
repo = repo_find()
if args.name:
tag_create(repo,
args.name,
args.object,
type="object" if args.create_tag_object else "ref")
else:
refs = ref_list(repo)
show_ref(repo, refs["tags"], with_hash=False)
def tag_create(repo, name, ref, create_tag_object=False):
sha = object_find(repo, ref)
if create_tag_object:
tag = GitTag(repo)
tag.kvlm = collections.OrderedDict()
tag.kvlm[b'object'] = sha.encode()
tag.kvlm[b'type'] = b'commit'
tag.kvlm[b'tag'] = name.encode()
tag.kvlm[b'tagger'] = b'Wyag <wyag@example.com>'
tag.kvlm[None] = b'Tag generated by wyag'
tag_sha = object_write(tag)
ref_create(repo, "tags/" + name, tag_sha)
else:
ref_create(repo, "tags/" + name, sha)
def ref_create(repo, ref_name, sha):
with open(repo_file(repo, "refs/" + ref_name), "w") as fp:
fp.write(sha + "\n")
def object_resolve(repo, name):
"""Resolve a name to an object hash in repo.
This function is aware of:
- the HEAD literal
- short and long hashes
- tags
- branches
- remote branches"""
candidates = list()
hashRE = re.compile(r"[0-9A-Fa-f]{4,40}$")
# Abort on empty string
if not name.strip():
return None
if name == "HEAD":
return [ref_resolve(repo, "HEAD")]
if hashRE.match(name):
name = name.lower()
prefix = name[0:2]
path = GitRepository.repo_dir(repo, "objects", prefix, mkdir=False)
if path:
rem = name[2:]
for f in os.listdir(path):
if f.startswith(rem):
candidates.append(prefix + f)
# Try for references
as_tag = ref_resolve(repo, "refs/tags/" + name)
if as_tag:
candidates.append(as_tag)
as_branch = ref_resolve(repo, "refs/heads/" + name)
if as_branch:
candidates.append(as_branch)
return candidates
argsp = argsubparsers.add_parser(
"rev-parse",
help="Parse revision (or other objects) identifiers")
argsp.add_argument("--wyag-type",
metavar="type",
dest="type",
choices=["blob", "commit", "tag", "tree"],
default=None,
help="Specify the expected type")
argsp.add_argument("name",
help="The name to parse")
def cmd_rev_parse(args):
if args.type:
fmt = args.type.encode()
else:
fmt = None
repo = repo_find()
print(object_find(repo, args.name, fmt, follow=True))