From 10f715f726c2626251608cbdd96abdce1134aca0 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Tue, 21 May 2024 14:57:39 +0100 Subject: [PATCH] Implement `git show` --- packages/git/src/format.js | 37 ++++++++ packages/git/src/subcommands/__exports__.js | 2 + packages/git/src/subcommands/show.js | 96 +++++++++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 packages/git/src/subcommands/show.js diff --git a/packages/git/src/format.js b/packages/git/src/format.js index fe79d582..967919f2 100644 --- a/packages/git/src/format.js +++ b/packages/git/src/format.js @@ -175,3 +175,40 @@ export const format_commit = (commit, oid, options = {}) => { } } } + +/** + * Produce a string representation of a tree. + * @param oid + * @param tree + * @param options + * @returns {string} + */ +export const format_tree = (oid, tree, options = {}) => { + let s = ''; + s += `tree ${oid}\n`; + s += '\n'; + for (const tree_entry of tree) { + s += `${tree_entry.path}\n`; + } + s += '\n'; + return s; +} + +/** + * Produce a string representation of a tag. + * Note that this only includes the tag itself, and not the tag's target, + * which must be separately retrieved and formatted. + * @param tag + * @param options + * @returns {string} + */ +export const format_tag = (tag, options = {}) => { + let s = ''; + s += `tag ${tag.tag}\n`; + s += `Tagger: ${format_person(tag.tagger)}\n`; + s += `Date: ${format_date(tag.tagger, options)}\n`; + if (tag.message) { + s += `\n${tag.message}\n\n`; + } + return s; +} diff --git a/packages/git/src/subcommands/__exports__.js b/packages/git/src/subcommands/__exports__.js index 1785defe..789c592a 100644 --- a/packages/git/src/subcommands/__exports__.js +++ b/packages/git/src/subcommands/__exports__.js @@ -23,6 +23,7 @@ import module_config from './config.js' import module_help from './help.js' import module_init from './init.js' import module_log from './log.js' +import module_show from './show.js' import module_status from './status.js' import module_version from './version.js' @@ -33,6 +34,7 @@ export default { "help": module_help, "init": module_init, "log": module_log, + "show": module_show, "status": module_status, "version": module_version, }; diff --git a/packages/git/src/subcommands/show.js b/packages/git/src/subcommands/show.js new file mode 100644 index 00000000..ef89e23b --- /dev/null +++ b/packages/git/src/subcommands/show.js @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2024 Puter Technologies Inc. + * + * This file is part of Puter's Git client. + * + * Puter's Git client is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import git from 'isomorphic-git'; +import { find_repo_root } from '../git-helpers.js'; +import { commit_formatting_options, process_commit_formatting_options, format_commit, format_tag, format_tree } from '../format.js'; + +export default { + name: 'show', + usage: 'git show [...] ', + description: 'Show information about an object (commit, tree, tag, blob, etc.) in git.', + args: { + allowPositionals: true, + options: { + ...commit_formatting_options, + }, + }, + execute: async (ctx) => { + const { io, fs, env, args } = ctx; + const { stdout, stderr } = io; + const { options, positionals } = args; + + process_commit_formatting_options(options); + + const { repository_dir, git_dir } = await find_repo_root(fs, env.PWD); + + const objects = [...positionals]; + + const cache = {}; + + const format_object = async (parsed_object, options) => { + switch (parsed_object.type) { + case 'blob': + return parsed_object.object; + case 'commit': + return format_commit(parsed_object.object, parsed_object.oid, options); + case 'tree': + return format_tree(parsed_object.oid, parsed_object.object, options); + case 'tag': { + const tag = parsed_object.object; + let s = format_tag(tag, options); + // Formatting a tag also outputs the formatted object it points to. + // That may also be a tag, so we recurse. + const target = await git.readObject({ + fs, + dir: repository_dir, + gitdir: git_dir, + oid: tag.object, + format: 'parsed', + cache, + }); + s += await format_object(target, options); + return s; + } + } + } + + for (const ref of objects) { + // Could be any ref, so first get the oid that's referred to. + const oid = await git.resolveRef({ + fs, + dir: repository_dir, + gitdir: git_dir, + ref, + }); + + // Then obtain the object and parse it. + const parsed_object = await git.readObject({ + fs, + dir: repository_dir, + gitdir: git_dir, + oid, + format: 'parsed', + cache, + }); + + // Then, print it out + stdout(await format_object(parsed_object, options)); + } + } +}