Implement git commit

This commit is contained in:
Sam Atkins 2024-05-17 16:56:52 +01:00
parent 32c172d145
commit b4e2ba4544
3 changed files with 128 additions and 0 deletions

View File

@ -70,3 +70,15 @@ export const find_repo_root = async (fs, pwd) => {
throw new Error('not a git repository (or any of the parent directories): .git'); throw new Error('not a git repository (or any of the parent directories): .git');
} }
/**
* Produce a shortened version of the given hash, which is still unique within the repo.
* TODO: Ensure that whatever we produce is unique within the repo.
* For now this is just a convenience function, so there's one place to change later.
* @param hash
* @returns {String} The shortened hash
*/
export const shorten_hash = (hash) => {
// TODO: Ensure that whatever we produce is unique within the repo
return hash.slice(0, 7);
}

View File

@ -18,6 +18,7 @@
*/ */
// Generated by /tools/gen.js // Generated by /tools/gen.js
import module_add from './add.js' import module_add from './add.js'
import module_commit from './commit.js'
import module_help from './help.js' import module_help from './help.js'
import module_init from './init.js' import module_init from './init.js'
import module_status from './status.js' import module_status from './status.js'
@ -25,6 +26,7 @@ import module_version from './version.js'
export default { export default {
"add": module_add, "add": module_add,
"commit": module_commit,
"help": module_help, "help": module_help,
"init": module_init, "init": module_init,
"status": module_status, "status": module_status,

View File

@ -0,0 +1,114 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
import git from 'isomorphic-git';
import path from 'path-browserify';
import { ErrorCodes } from '@heyputer/puter-js-common/src/PosixError.js';
import { find_repo_root, shorten_hash } from '../git-helpers.js';
export default {
name: 'commit',
usage: 'git commit [-m|--message <message>] [-a|--author <author>]',
description: 'Commit staged changes to the repository.',
args: {
allowPositionals: false,
options: {
message: {
description: 'Specify the commit message',
type: 'string',
short: 'm',
},
author: {
description: 'Specify the commit author, as `A U Thor <author@example.com>`',
type: 'string',
},
},
},
execute: async (ctx) => {
const { io, fs, env, args } = ctx;
const { stdout, stderr } = io;
const { options, positionals } = args;
if (!options.message) {
// TODO: Support opening a temporary file in an editor,
// where the user can edit the commit message if it's not specified.
stderr('You must specify a commit message with --message or -m');
return 1;
}
const { repository_dir, git_dir } = await find_repo_root(fs, env.PWD);
let user_name;
let user_email;
if (options.author) {
const author_regex = /(.+?)\s+<(.+)>/;
const matches = options.author.match(author_regex);
if (!matches)
throw new Error('Failed to parse author string');
user_name = matches[1];
user_email = matches[2];
} else {
user_name = await git.getConfig({
fs,
dir: repository_dir,
gitdir: git_dir,
path: 'user.name',
});
user_email = await git.getConfig({
fs,
dir: repository_dir,
gitdir: git_dir,
path: 'user.email',
});
}
if (!user_name || !user_email) {
throw new Error('Missing author information. Either provide --author="A <a@b.c>" or set user.name and user.email in the git config');
}
const commit_hash = await git.commit({
fs,
dir: repository_dir,
gitdir: git_dir,
message: options.message,
author: {
name: user_name,
email: user_email,
},
});
const branch = await git.currentBranch({
fs,
dir: repository_dir,
gitdir: git_dir,
});
const commit_title = options.message.split('\n')[0];
const short_hash = shorten_hash(commit_hash);
let output = `[${branch} ${short_hash}] ${commit_title}\n`;
// TODO: --amend prints out the date of the original commit here, as:
// ` Date: Fri May 17 15:45:47 2024 +0100`
// TODO: Print out file change count, insertion count, and deletion count
// (Seems if insertions and deletions are both 0, we should print both.
// Otherwise we just print nonzero ones.)
// TODO: Print out each file created or deleted. eg:
// create mode 100644 bar
// delete mode 100644 foo.txt
stdout(output);
}
}