feat(git): Add --color and --no-color options

These allow the user to force color on or off. The chalk library is used
for the output, because it's already used elsewhere in Puter and seems
like a good choice.

Ideally, the default will be based on whether stdout is a tty, but Puter
doesn't yet have that concept, so we just default to color.
This commit is contained in:
Sam Atkins 2024-06-20 14:17:15 +01:00 committed by Eric Dubé
parent 364d580ff8
commit d6dd1a5bb0
9 changed files with 94 additions and 7 deletions

13
package-lock.json generated
View File

@ -12130,6 +12130,7 @@
"dependencies": {
"@pkgjs/parseargs": "^0.11.0",
"buffer": "^6.0.3",
"chalk": "^5.3.0",
"diff": "^5.2.0",
"isomorphic-git": "^1.25.10"
},
@ -12165,6 +12166,18 @@
"ieee754": "^1.2.1"
}
},
"packages/git/node_modules/chalk": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
"integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
"license": "MIT",
"engines": {
"node": "^12.17.0 || ^14.13 || >=16.0.0"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"packages/git/node_modules/diff": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",

View File

@ -20,6 +20,7 @@
"dependencies": {
"@pkgjs/parseargs": "^0.11.0",
"buffer": "^6.0.3",
"chalk": "^5.3.0",
"diff": "^5.2.0",
"isomorphic-git": "^1.25.10"
}

51
packages/git/src/color.js Normal file
View File

@ -0,0 +1,51 @@
/*
* 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 chalk from 'chalk';
export const color_options = {
'color': {
// TODO: '--color[=<when>]' syntax, once we have an args parser that supports optional string option-arguments.
description: 'Force colored output.',
type: 'boolean',
},
'no-color': {
description: 'Disable colored output.',
type: 'boolean',
},
}
/**
* Process command-line options related to color, and modify them in place.
* Sets the chalk color level based on whether color is enabled or disabled.
* @param options Parsed command-line options, which will be modified in place.
*/
export const process_color_options = (options) => {
if (!options['color'] && !options['no-color']) {
// TODO: Default to whether we're running in a TTY, once we have that concept.
options['color'] = true;
}
if (options['no-color']) {
options['color'] = false;
delete options['no-color'];
}
chalk.level = options.color ? 3 : 0;
}

View File

@ -17,6 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { shorten_hash } from './git-helpers.js';
import chalk from 'chalk';
export const commit_formatting_options = {
'abbrev-commit': {
@ -428,15 +429,15 @@ export const format_diffs = (diffs, options) => {
s += `+++ ${b_path}\n`;
for (const hunk of diff.hunks) {
s += `\x1b[36;1m@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@\x1b[0m\n`;
s += chalk.blueBright(`@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@\n`);
for (const line of hunk.lines) {
switch (line[0]) {
case '+':
s += `\x1b[32;1m${line}\x1b[0m\n`;
s += chalk.greenBright(`${line}\n`);
break;
case '-':
s += `\x1b[31;1m${line}\x1b[0m\n`;
s += chalk.redBright(`${line}\n`);
break;
default:
s += `${line}\n`;

View File

@ -19,6 +19,8 @@
import git from 'isomorphic-git';
import { find_repo_root, shorten_hash } from '../git-helpers.js';
import { SHOW_USAGE } from '../help.js';
import { color_options, process_color_options } from '../color.js';
import chalk from 'chalk';
const BRANCH = {
name: 'branch',
@ -64,7 +66,8 @@ const BRANCH = {
description: 'Perform the action forcefully. For --delete, ignores whether the branches are fully merged. For --move, --copy, and creating new branches, ignores whether a branch already exists with that name.',
type: 'boolean',
short: 'f',
}
},
...color_options,
},
},
execute: async (ctx) => {
@ -108,6 +111,8 @@ const BRANCH = {
}
}
process_color_options(options);
const { dir, gitdir } = await find_repo_root(fs, env.PWD);
const get_current_branch = async () => git.currentBranch({
@ -266,7 +271,7 @@ const BRANCH = {
for (const branch of branches) {
if (branch === current_branch) {
stdout(`\x1b[32;1m* ${branch}\x1b[0m`);
stdout(chalk.greenBright(`* ${branch}`));
} else {
stdout(` ${branch}`);
}

View File

@ -23,6 +23,7 @@ import * as Diff from 'diff';
import path from 'path-browserify';
import { diff_formatting_options, format_diffs, process_diff_formatting_options } from '../format.js';
import { diff_git_trees } from '../diff.js';
import { color_options, process_color_options } from '../color.js';
export default {
name: 'diff',
@ -55,6 +56,7 @@ export default {
description: 'Compare files, ignoring git.',
type: 'boolean',
},
...color_options,
},
},
execute: async (ctx) => {
@ -63,6 +65,8 @@ export default {
const { options, positionals, tokens } = args;
const cache = {};
process_color_options(options);
const diff_options = process_diff_formatting_options(options);
if (diff_options.no_patch && !options['exit-code'])
return;

View File

@ -28,6 +28,7 @@ import {
import path from 'path-browserify';
import { SHOW_USAGE } from '../help.js';
import { diff_git_trees } from '../diff.js';
import { color_options, process_color_options } from '../color.js';
export default {
name: 'log',
@ -39,6 +40,7 @@ export default {
options: {
...commit_formatting_options,
...diff_formatting_options,
...color_options,
'max-count': {
description: 'Maximum number of commits to output.',
type: 'string',
@ -54,6 +56,7 @@ export default {
process_commit_formatting_options(options);
const diff_options = process_diff_formatting_options(options, { show_patch_by_default: false });
process_color_options(options);
const depth = Number(options['max-count']) || undefined;

View File

@ -21,6 +21,8 @@ import http from 'isomorphic-git/http/web';
import { determine_fetch_remote, find_repo_root, shorten_hash } from '../git-helpers.js';
import { SHOW_USAGE } from '../help.js';
import { authentication_options, Authenticator } from '../auth.js';
import { color_options, process_color_options } from '../color.js';
import chalk from 'chalk';
export default {
name: 'push',
@ -37,6 +39,7 @@ export default {
short: 'f',
},
...authentication_options,
...color_options,
},
},
execute: async (ctx) => {
@ -45,6 +48,8 @@ export default {
const { options, positionals } = args;
const cache = {};
process_color_options(options);
const { dir, gitdir } = await find_repo_root(fs, env.PWD);
const remotes = await git.listRemotes({
@ -265,12 +270,13 @@ export default {
stdout(`To ${remote_url}`);
let any_failed = false;
for (const { flag, summary, source, dest, reason } of results) {
stdout(`${flag === '!' ? '\x1b[31;1m' : ''} ${flag} ${summary.padEnd(19, ' ')}\x1b[0m ${source} -> ${dest}${reason ? ` (${reason})` : ''}`);
const flag_and_summary = `${flag} ${summary.padEnd(19, ' ')}`;
stdout(` ${ (flag === '!') ? chalk.redBright(flag_and_summary) : flag_and_summary } ${source} -> ${dest}${reason ? ` (${reason})` : ''}`);
if (reason)
any_failed = true;
}
if (any_failed) {
stderr(`\x1b[31;1merror: Failed to push some refs to '${remote_url}'\x1b[0m`);
stderr(chalk.redBright(`error: Failed to push some refs to '${remote_url}'`));
}
},
};

View File

@ -28,6 +28,7 @@ import {
process_diff_formatting_options,
} from '../format.js';
import { diff_git_trees } from '../diff.js';
import { color_options, process_color_options } from '../color.js';
export default {
name: 'show',
@ -38,6 +39,7 @@ export default {
options: {
...commit_formatting_options,
...diff_formatting_options,
...color_options,
},
},
execute: async (ctx) => {
@ -47,6 +49,7 @@ export default {
process_commit_formatting_options(options);
const diff_options = process_diff_formatting_options(options);
process_color_options(options);
const { dir, gitdir } = await find_repo_root(fs, env.PWD);