diff --git a/libtft.py b/libtft.py index 18986d0..50ecd3a 100644 --- a/libtft.py +++ b/libtft.py @@ -27,6 +27,9 @@ argsp.add_argument("-w", dest="write", action="store_true", help="Actually write the object into the database") argsp.add_argument("path", help="Read object from ") +#subparser for commit +argsp = argsubparsers.add_parser("commit", help="Record changes to the repository.") +argsp.add_argument("-m", metavar="message", dest="message", help="Message to associate with this commit.") def main(argv=sys.argv[1:]): args = argparser.parse_args(argv) @@ -364,7 +367,77 @@ def kvlm_serialize(kvlm): def cat_file(repo, obj, fmt=None): obj = object_read(repo, object_find(repo, obj, fmt=fmt)) sys.stdout.buffer.write(obj.serialize()) - + +def gitconfig_user_get(config): + if "user" in config: + if "name" in config["user"] and "email" in config["user"]: + return "{} <{}>".format(config["user"]["name"], config["user"]["email"]) + return None + +def gitconfig_read(): + xdg_config_home = os.environ["XDG_CONFIG_HOME"] if "XDG_CONFIG_HOME" in os.environ else "~/.config" + configfiles = [ + os.path.expanduser(os.path.join(xdg_config_home, "git/config")), + os.path.expanduser("~/.gitconfig") + ] + config = configparser.ConfigParser() + config.read(configfiles) + return config + +def tree_from_index(repo, index): + contents = dict() + contents[""] = list() + + for entry in index.entries: + dirname = os.path.dirname(entry.name) + + key = dirname + + while key != "": + if not key in contents: + contents[key] = list() + key = os.path.dirname(key) + contents[dirname].append(entry) + + sorted_paths = sorted(contents.keys(), key=len, reverse=True) + + sha = None + + for path in sorted_paths: + tree = GitTree() + + for entry in contents[path]: + if isinstance(entry, GitIndexEntry): + leaf_mode = "{:02o}{:04o}".format(entry.mode_type, entry.mode_perms).encode("ascii") + leaf = GitTreeLeaf(leaf_mode, path=os.path.basename(entry.name), sha=entry.sha) + else: + 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) + 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") + + # Timezone + offset = int(timestamp.astimezone().utcoffset().total_seconds()) + hours = offset // 3600 + minutes = (offset % 3600) // 60 + tz = "{}{:02}{:02}".format("+" if offset > 0 else "-", hours, minutes) + + 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) + #Bride functions def cmd_init(args): """Bridge function to initialize a new repository.""" @@ -380,3 +453,26 @@ def cmd_hash_object(args): with open(args.path, "rb") as fd: sha = object_hash(fd, args.type.encode(), repo) print(sha) + +def cmd_commit(args): + repo = repo_find() + index = index_read(repo) + # Create trees, grab back SHA for the root tree. + tree = tree_from_index(repo, index) + + # Create the commit object itself + 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 we're on a branch, we update refs/heads/BRANCH + with open(repo_file(repo, os.path.join("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("\n") \ No newline at end of file