Add status command
This commit is contained in:
parent
b2ee6d25be
commit
bad433b264
128
libwyag.py
128
libwyag.py
@ -1064,8 +1064,136 @@ def check_ignore(rules, path):
|
||||
if os.path.isabs(path):
|
||||
raise Exception("This function requires path to be relative to the repository's root")
|
||||
|
||||
# Eh, just hardcode it
|
||||
if (path.startswith(".git")):
|
||||
return True
|
||||
|
||||
result = check_ignore_scoped(rules.scoped, path)
|
||||
if result != None:
|
||||
return result
|
||||
|
||||
return check_ignore_absolute(rules.absolute, path)
|
||||
|
||||
argsp = argsubparsers.add_parser("status", help="Show the working tree status.")
|
||||
|
||||
def cmd_status(_):
|
||||
repo = repo_find()
|
||||
index = index_read(repo)
|
||||
|
||||
cmd_status_branch(repo)
|
||||
cmd_status_head_index(repo, index)
|
||||
print()
|
||||
cmd_status_index_worktree(repo, index)
|
||||
|
||||
def branch_get_active(repo):
|
||||
with open(GitRepository.repo_file(repo, "HEAD"), "r") as f:
|
||||
head = f.read()
|
||||
|
||||
if head.startswith("ref: refs/heads/"):
|
||||
return(head[16:-1])
|
||||
else:
|
||||
return False
|
||||
|
||||
def cmd_status_branch(repo):
|
||||
branch = branch_get_active(repo)
|
||||
if branch:
|
||||
print(f"On branch {branch}.")
|
||||
else:
|
||||
print("HEAD detached at {}".format(object_find(repo, "HEAD")))
|
||||
|
||||
def tree_to_dict(repo, ref, prefix=""):
|
||||
ret = dict()
|
||||
tree_sha = object_find(repo, ref, fmt=b"tree")
|
||||
tree = object_read(repo, tree_sha)
|
||||
|
||||
for leaf in tree.items:
|
||||
full_path = join_path(prefix, leaf.path)
|
||||
|
||||
# We read the object to extract its type (this is uselessly
|
||||
# expensive: we could just open it as a file and read the
|
||||
# first few bytes)
|
||||
is_subtree = leaf.mode.startswith(b'04')
|
||||
|
||||
# Depending on the type, we either store the path (if it's a
|
||||
# blob, so a regular file), or recurse (if it's another tree,
|
||||
# so a subdir)
|
||||
if is_subtree:
|
||||
ret.update(tree_to_dict(repo, leaf.sha, full_path))
|
||||
else:
|
||||
ret[full_path] = leaf.sha
|
||||
|
||||
return ret
|
||||
|
||||
def cmd_status_head_index(repo, index):
|
||||
print("Changes to be commited:")
|
||||
|
||||
head = tree_to_dict(repo, "HEAD")
|
||||
for entry in index.entries:
|
||||
if entry.name in head:
|
||||
if head[entry.name] != entry.sha:
|
||||
print(" modified:", entry.name)
|
||||
del head[entry.name]
|
||||
else:
|
||||
print(" added: ", entry.name)
|
||||
|
||||
# Keys still in HEAD are files that we haven't met in the index,
|
||||
# and thus have been deleted
|
||||
for entry in head.keys():
|
||||
print(" deleted: ", entry)
|
||||
|
||||
def cmd_status_index_worktree(repo, index):
|
||||
print("Changes not staged for commit:")
|
||||
|
||||
ignore = gitignore_read(repo)
|
||||
|
||||
gitdir_prefix = repo.gitdir + "/"
|
||||
|
||||
all_files = list()
|
||||
|
||||
# We begin by walking the filesystem
|
||||
for (root, _, files) in os.walk(repo.worktree, True):
|
||||
if root==repo.gitdir or root.startswith(gitdir_prefix):
|
||||
continue
|
||||
for f in files:
|
||||
full_path = join_path(root, f)
|
||||
rel_path = os.path.relpath(full_path, repo.worktree).replace("\\", "/")
|
||||
all_files.append(rel_path)
|
||||
|
||||
# We now traverse the index, and compare real files with the cached
|
||||
# versions.
|
||||
|
||||
for entry in index.entries:
|
||||
full_path = join_path(repo.worktree, entry.name)
|
||||
|
||||
# That file *name* is in the index
|
||||
|
||||
if not os.path.exists(full_path):
|
||||
print(" deleted: ", entry.name)
|
||||
else:
|
||||
stat = os.stat(full_path)
|
||||
|
||||
# Compare metadata
|
||||
ctime_ns = entry.ctime[0] * 10**9 + entry.ctime[1]
|
||||
mtime_ns = entry.mtime[0] * 10**9 + entry.mtime[1]
|
||||
if (stat.st_ctime_ns != ctime_ns) or (stat.st_mtime_ns != mtime_ns):
|
||||
# If different, deep compare.
|
||||
# @FIXME This *will* crash on symlinks to dir.
|
||||
with open(full_path, "rb") as fd:
|
||||
new_sha = object_hash(fd, b"blob", None)
|
||||
# If the hashes are the same, the files are actually the same.
|
||||
same = entry.sha == new_sha
|
||||
|
||||
if not same:
|
||||
print(" modified:", entry.name)
|
||||
|
||||
if entry.name in all_files:
|
||||
all_files.remove(entry.name)
|
||||
|
||||
print()
|
||||
print("Untracked files:")
|
||||
|
||||
for f in all_files:
|
||||
# @TODO If a full directory is untracked, we should display
|
||||
# its name without its contents.
|
||||
if not check_ignore(ignore, f):
|
||||
print(" ", f)
|
||||
|
Loading…
Reference in New Issue
Block a user