Add 'commit' command

This commit is contained in:
Nathan McRae 2024-07-11 19:29:44 -07:00
parent ff78648324
commit 3f49d9f7db

View File

@ -1342,3 +1342,119 @@ def add(repo, paths, delete=True, skip_missing=False):
index.entries.append(entry) index.entries.append(entry)
index_write(repo, index) index_write(repo, index)
argsp = argsubparsers.add_parser("commit", help="Record changes to the repository.")
argsp.add_argument("-m",
metavar="mesaage",
dest="message",
help="Message to associate with this commit.")
def gitconfig_read():
xdg_config_home = os.environ["XDG_CONFIG_HOME"] if "XDG_CONFIG_HOME" in os.environ else "~/.config"
config_files = [
os.path.expanduser(join_path(xdg_config_home, "git/config")).replace("\\", "/"),
#os.path.expanduser("~/.gitconfig").replace("\\", "/")
]
config = configparser.ConfigParser()
config.read(config_files)
return config
def gitconfig_user_get(config):
if "user" in config:
if "name" in config["user"] and "email" in config["user"]:
return f"{config['user']['name']} <{config['user']['email']}>"
return None
def tree_from_index(repo, index):
contents = dict()
contents[""] = list()
# Convert entries to dictionary where keys are directories, and values are
# lists of directory contents.
for entry in index.entries:
dirname = os.path.dirname(entry.name).replace("\\", "/")
# We create all dictionary entries up to root (""). We need them *all*
# because even if a directory holds no files it will contain at least
# a tree.
key = dirname
while key != "":
if not key in contents:
contents[key] = list()
key = os.path.dirname(key).replace("\\", "/")
contents[dirname].append(entry)
# Sort keys (= directories) by length, descending. This means that we'll
# always encounter a given path before its parent, which is all we need,
# since for each directory D we'll need to modify its parent P to add
# D's tree.
sorted_paths = sorted(contents.keys(), key=len, reverse=True)
sha = None
for path in sorted_paths:
tree = GitTree()
for entry in contents[path]:
# An entry can be a normal GitIndexEntry read from the index, or
# a tree we've created.
if isinstance(entry, GitIndexEntry):
leaf_mode = f"{entry.mode_type:02o}{entry.mode_perms:04o}".encode("ascii")
leaf = GitTreeLeaf(mode=leaf_mode, path=os.path.basename(entry.name), sha=entry.sha)
else: # Tree. We've stored it as a pair: (basename, SHA)
leaf = GitTreeLeaf(mode=b"040000", path=entry[0], sha=entry[1])
tree.items.append(leaf)
sha = object_write(tree, repo)
parent = os.path.dirname(path).replace("\\", "/")
base = os.path.basename(path)
contents[parent].append((base, sha))
return sha
def commit_create(repo, tree, parent, author, timestamp, message):
commit = GitCommit()
commit.kvlm[b"tree"] = tree.encode("ascii")
if parent:
commit.kvlm[b'parent'] = parent.encode("ascii")
offset = int(timestamp.astimezone().utcoffset().total_seconds())
hours = offset // 3600
minutes = (offset & 3600) // 60
tz = f"{'+' if offset > 0 else '-'}{hours:02}{minutes:02}"
if author == None:
author = ""
author = author + timestamp.strftime(" %S ") + tz
commit.kvlm[b"author"] = author.encode("utf8")
commit.kvlm[b"committer"] = author.encode("utf8")
commit.kvlm[None] = message.encode("utf8")
return object_write(commit, repo)
def cmd_commit(args):
repo = repo_find()
index = index_read(repo)
tree = tree_from_index(repo, index)
commit = commit_create(repo,
tree,
object_find(repo, "HEAD"),
gitconfig_user_get(gitconfig_read()),
datetime.now(),
args.message)
# Update HEAD so our commit is now the tip of the active branch.
active_branch = branch_get_active(repo)
if active_branch: # If on a branch, update that branch
with open(GitRepository.repo_file(repo, join_path("refs/heads", active_branch)), "w") as fd:
fd.write(commit + "\n")
else: # Otherwise, we update HEAD itself.
with open(repo_file(repo, "HEAD"), "w") as fd:
fd.write(commit + "\n")