Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use commit message template value as prefix #64

Draft
wants to merge 19 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions bin/reset_sandbox.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,7 @@ mkdir -p my_subdir
mv bazz.js my_subdir
# Delete.
rm buzz.js

# Commit template.
git config commit.template _COMMIT_MESSAGE
echo 'My commit prefix' >_COMMIT_MESSAGE
10 changes: 9 additions & 1 deletion src/autofill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import * as vscode from "vscode";
import { Repository } from "./api/git";
import { getChanges } from "./git/cli";
import { getCommitTemplateValue } from "./git/commitTemplate";
import { getCommitMsg, setCommitMsg } from "./gitExtension";
import { generateMsg } from "./prepareCommitMsg";

Expand All @@ -21,7 +22,11 @@ Try saving your files or stage any new (untracked) files.\
* 2. Generate a message.
* 3. Push message value to the commit message box.
*
* This is based on `prefixCommit` from the `git-prefix` extension.
* New functionality in the extension - the commit message file is read
* explicitly and the content used. Any content in the Git pane that was the
* "old message" is ignored then.
*
* This function is based on `prefixCommit` from the `git-prefix` extension.
*/
export async function makeAndFillCommitMsg(repository: Repository) {
const fileChanges = await getChanges();
Expand All @@ -39,5 +44,8 @@ export async function makeAndFillCommitMsg(repository: Repository) {
const newMsg = generateMsg(fileChanges, oldMsg);
console.debug("New message: ", newMsg);

const commitMessageValue = await getCommitTemplateValue();
console.debug({ commitMessageValue });

setCommitMsg(repository, newMsg);
}
5 changes: 5 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ import { generateMsg } from "./prepareCommitMsg";
*
* Expect multi-line text from `git diff-index` command as the first item in the shell arguments.
*
* TODO: How to reverse the order so reading the value and using it as old message can appear _first_.
* Note that, unlike the VS Code extension, the CLI logic here cannot read an
* "old message" from the Git pane so that is not passed to `generateMsg`.
* Though maybe this can be modified using `commitTemplate.ts`.
*
* Returns a suitable generated commit message as text.
*/
function main(args: string[]) {
Expand Down
14 changes: 8 additions & 6 deletions src/git/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ const exec = util.promisify(childProcess.exec);
/**
* Run a `git` subcommand with options and return output.
*/
function _execute(cwd: string, subcommand: string, options: string[] = []) {
export function execute(
cwd: string,
subcommand: string,
options: string[] = []
) {
const command = `git ${subcommand} ${options.join(" ")}`;

const result = exec(command, { cwd });
Expand Down Expand Up @@ -44,11 +48,9 @@ async function _diffIndex(options: string[] = []): Promise<Array<string>> {
...options,
"HEAD",
];
const { stdout, stderr } = await _execute(
getWorkspaceFolder(),
cmd,
fullOptions
);

const workspace = getWorkspaceFolder();
const { stdout, stderr } = await execute(workspace, cmd, fullOptions);

if (stderr) {
console.debug(`stderr for 'git ${cmd}' command:`, stderr);
Expand Down
113 changes: 113 additions & 0 deletions src/git/commitTemplate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/**
* Commit template module.
*
* TODO: Move to docs and link there.
*
* Read the message from the a configured commit template file and use it as a
* prefix in the message.
*
* A commit template is built-in Git behavior to see a value for the start of
* each commit message. This is useful if you have a ticket number, project
* name, or similar to add at the start of each commit.
*
* Note that VS Code and Git CLI both automatically read from this file when
* generating a commit. However, the value is hard to use. There is behavior in
* this extension to move the old message to the end and enter a commit type
* prefix and commit message before it, but there is no way to know from the
* content of the message for sure whether the old message is a commit template
* value or just a hand-typed message.
*
* To avoid making an extra config value for the extension that one has to
* manage say in a Settings file or internal data, the approach is rather to use
* the existing commit template pattern in Git.
*/
import * as fs from "fs";
import * as path from "path";
import { getWorkspaceFolder } from "../workspace";
import { execute } from "./cli";

const CONFIG_SUBCOMMAND = "config";
const COMMIT_TEMPLATE_IDENTIFIER = "commit.template";

/**
* Get a value from the local Git config.
*/
export async function _getConfigValue(options: string[]) {
const workspace = getWorkspaceFolder();

const { stdout, stderr } = await execute(
workspace,
CONFIG_SUBCOMMAND,
options
);

if (stderr) {
console.debug(`stderr for 'git ${CONFIG_SUBCOMMAND}' command:`, stderr);
}

return stdout;
}

/**
* Get commit template path.
*
* Get the configured value for a commit template path if set.
*/
async function _getCommitTemplatePath() {
try {
const options = [COMMIT_TEMPLATE_IDENTIFIER];

return await _getConfigValue(options);
} catch (_e) {
console.error(_e);

return null;
}
}

/**
* Read a file.
*
* @param filePath Path to a file to read, relative to the workspace root.
* e.g. "abc.txt" or "abc/def.txt"
*/
function _readFile(filePath: string) {
const workspace = getWorkspaceFolder();
const p = path.join(workspace, filePath);

let value;

try {
value = fs.readFileSync(p, "utf-8");
} catch (err: any) {
console.error(`Could not find template file: ${p}. ${err.toString()}`);

return null;
}

if (!value) {
return null;
}

console.debug(`Found file: ${p}. Found contents: ${value}`);

return value;
}

/**
* Get value of commit template file.
*
* @return Contents of a configured commit message template file, or `null` if
* file is not configured or file is missing.
*/
export async function getCommitTemplateValue() {
const filePath = await _getCommitTemplatePath();

if (!filePath) {
console.error(`Could not read missing file: ${filePath}`);

return null;
}

return _readFile(filePath);
}
2 changes: 1 addition & 1 deletion src/prepareCommitMsg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ export function _generateMsgWithOld(lines: string[], oldMsg: string) {
*
* Old message could be the current commit message value in the UI box (which might be a commit
* message template that VS Code has filled in), or a commit message template read from a file in
* the case of a hook flow without VS Code.
* the case of a hook flow without VS Code (built-in Git functionality).
*
* @param lines A list of text values describing how files changes.
*/
Expand Down