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

A full editor can be used as git commit message editor #95266

Merged
3 changes: 2 additions & 1 deletion extensions/git/extension.webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ module.exports = withDefaults({
context: __dirname,
entry: {
main: './src/main.ts',
['askpass-main']: './src/askpass-main.ts'
['askpass-main']: './src/askpass-main.ts',
['git-editor-main']: './src/gitEditor/main.ts'
}
});
62 changes: 46 additions & 16 deletions extensions/git/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
"enabledApiProposals": [
"diffCommand",
"contribViewsWelcome",
"resolvers",
"scmActionButton",
"scmInput",
"scmSelectedProvider",
"scmValidation",
"timeline"
Expand Down Expand Up @@ -211,83 +213,99 @@
"command": "git.commit",
"title": "%command.commit%",
"category": "Git",
"icon": "$(check)"
"icon": "$(check)",
"enablement": "!commitInProgress"
},
{
"command": "git.commitStaged",
"title": "%command.commitStaged%",
"category": "Git"
"category": "Git",
"enablement": "!commitInProgress"
},
{
"command": "git.commitEmpty",
"title": "%command.commitEmpty%",
"category": "Git"
"category": "Git",
"enablement": "!commitInProgress"
},
{
"command": "git.commitStagedSigned",
"title": "%command.commitStagedSigned%",
"category": "Git"
"category": "Git",
"enablement": "!commitInProgress"
},
{
"command": "git.commitStagedAmend",
"title": "%command.commitStagedAmend%",
"category": "Git"
"category": "Git",
"enablement": "!commitInProgress"
},
{
"command": "git.commitAll",
"title": "%command.commitAll%",
"category": "Git"
"category": "Git",
"enablement": "!commitInProgress"
},
{
"command": "git.commitAllSigned",
"title": "%command.commitAllSigned%",
"category": "Git"
"category": "Git",
"enablement": "!commitInProgress"
},
{
"command": "git.commitAllAmend",
"title": "%command.commitAllAmend%",
"category": "Git"
"category": "Git",
"enablement": "!commitInProgress"
},
{
"command": "git.commitNoVerify",
"title": "%command.commitNoVerify%",
"category": "Git",
"icon": "$(check)"
"icon": "$(check)",
"enablement": "!commitInProgress"
},
{
"command": "git.commitStagedNoVerify",
"title": "%command.commitStagedNoVerify%",
"category": "Git"
"category": "Git",
"enablement": "!commitInProgress"
},
{
"command": "git.commitEmptyNoVerify",
"title": "%command.commitEmptyNoVerify%",
"category": "Git"
"category": "Git",
"enablement": "!commitInProgress"
},
{
"command": "git.commitStagedSignedNoVerify",
"title": "%command.commitStagedSignedNoVerify%",
"category": "Git"
"category": "Git",
"enablement": "!commitInProgress"
},
{
"command": "git.commitStagedAmendNoVerify",
"title": "%command.commitStagedAmendNoVerify%",
"category": "Git"
"category": "Git",
"enablement": "!commitInProgress"
},
{
"command": "git.commitAllNoVerify",
"title": "%command.commitAllNoVerify%",
"category": "Git"
"category": "Git",
"enablement": "!commitInProgress"
},
{
"command": "git.commitAllSignedNoVerify",
"title": "%command.commitAllSignedNoVerify%",
"category": "Git"
"category": "Git",
"enablement": "!commitInProgress"
},
{
"command": "git.commitAllAmendNoVerify",
"title": "%command.commitAllAmendNoVerify%",
"category": "Git"
"category": "Git",
"enablement": "!commitInProgress"
},
{
"command": "git.restoreCommitTemplate",
Expand Down Expand Up @@ -1986,6 +2004,18 @@
"scope": "machine",
"description": "%config.defaultCloneDirectory%"
},
"git.useEditorAsCommitInput": {
"type": "boolean",
"scope": "resource",
"description": "%config.useEditorAsCommitInput%",
"default": false
},
"git.verboseCommit": {
"type": "boolean",
"scope": "resource",
"markdownDescription": "%config.verboseCommit%",
"default": false
},
"git.enableSmartCommit": {
"type": "boolean",
"scope": "resource",
Expand Down
2 changes: 2 additions & 0 deletions extensions/git/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@
"config.ignoreLimitWarning": "Ignores the warning when there are too many changes in a repository.",
"config.ignoreRebaseWarning": "Ignores the warning when it looks like the branch might have been rebased when pulling.",
"config.defaultCloneDirectory": "The default location to clone a git repository.",
"config.useEditorAsCommitInput": "Use an editor to author the commit message.",
"config.verboseCommit": "Enable verbose output when `#git.useEditorAsCommitInput#` is enabled.",
"config.enableSmartCommit": "Commit all changes when there are no staged changes.",
"config.smartCommitChanges": "Control which changes are automatically staged by Smart Commit.",
"config.smartCommitChanges.all": "Automatically stage all changes.",
Expand Down
3 changes: 3 additions & 0 deletions extensions/git/src/api/git.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ export interface CommitOptions {
empty?: boolean;
noVerify?: boolean;
requireUserConfig?: boolean;
useEditor?: boolean;
verbose?: boolean;
}

export interface FetchOptions {
Expand Down Expand Up @@ -336,4 +338,5 @@ export const enum GitErrorCodes {
PatchDoesNotApply = 'PatchDoesNotApply',
NoPathFound = 'NoPathFound',
UnknownPath = 'UnknownPath',
EmptyCommitMessage = 'EmptyCommitMessage'
}
14 changes: 2 additions & 12 deletions extensions/git/src/askpass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,16 @@
import { window, InputBoxOptions, Uri, Disposable, workspace } from 'vscode';
import { IDisposable, EmptyDisposable, toDisposable } from './util';
import * as path from 'path';
import { IIPCHandler, IIPCServer, createIPCServer } from './ipc/ipcServer';
import { IIPCHandler, IIPCServer } from './ipc/ipcServer';
import { CredentialsProvider, Credentials } from './api/git';
import { OutputChannelLogger } from './log';

export class Askpass implements IIPCHandler {

private disposable: IDisposable = EmptyDisposable;
private cache = new Map<string, Credentials>();
private credentialsProviders = new Set<CredentialsProvider>();

static async create(outputChannelLogger: OutputChannelLogger, context?: string): Promise<Askpass> {
try {
return new Askpass(await createIPCServer(context));
} catch (err) {
outputChannelLogger.logError(`Failed to create git askpass IPC: ${err}`);
return new Askpass();
}
}

private constructor(private ipc?: IIPCServer) {
constructor(private ipc?: IIPCServer) {
if (ipc) {
this.disposable = ipc.registerHandler('askpass', this);
}
Expand Down
39 changes: 33 additions & 6 deletions extensions/git/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1443,6 +1443,14 @@ export class CommandCenter {
opts.signoff = true;
}

if (config.get<boolean>('useEditorAsCommitInput')) {
opts.useEditor = true;

if (config.get<boolean>('verboseCommit')) {
opts.verbose = true;
}
}

const smartCommitChanges = config.get<'all' | 'tracked'>('smartCommitChanges');

if (
Expand Down Expand Up @@ -1490,7 +1498,7 @@ export class CommandCenter {

let message = await getCommitMessage();

if (!message && !opts.amend) {
if (!message && !opts.amend && !opts.useEditor) {
return false;
}

Expand Down Expand Up @@ -1550,10 +1558,13 @@ export class CommandCenter {

private async commitWithAnyInput(repository: Repository, opts?: CommitOptions): Promise<void> {
const message = repository.inputBox.value;
const root = Uri.file(repository.root);
const config = workspace.getConfiguration('git', root);

const getCommitMessage = async () => {
let _message: string | undefined = message;

if (!_message) {
if (!_message && !config.get<boolean>('useEditorAsCommitInput')) {
let value: string | undefined = undefined;

if (opts && opts.amend && repository.HEAD && repository.HEAD.commit) {
Expand Down Expand Up @@ -2937,7 +2948,7 @@ export class CommandCenter {
};

let message: string;
let type: 'error' | 'warning' = 'error';
let type: 'error' | 'warning' | 'information' = 'error';

const choices = new Map<string, () => void>();
const openOutputChannelChoice = localize('open git log', "Open Git Log");
Expand Down Expand Up @@ -3000,6 +3011,12 @@ export class CommandCenter {
message = localize('missing user info', "Make sure you configure your 'user.name' and 'user.email' in git.");
choices.set(localize('learn more', "Learn More"), () => commands.executeCommand('vscode.open', Uri.parse('https://aka.ms/vscode-setup-git')));
break;
case GitErrorCodes.EmptyCommitMessage:
message = localize('empty commit', "Commit operation was cancelled due to empty commit message.");
choices.clear();
type = 'information';
options.modal = false;
break;
default: {
const hint = (err.stderr || err.message || String(err))
.replace(/^error: /mi, '')
Expand All @@ -3021,10 +3038,20 @@ export class CommandCenter {
return;
}

let result: string | undefined;
const allChoices = Array.from(choices.keys());
const result = type === 'error'
? await window.showErrorMessage(message, options, ...allChoices)
: await window.showWarningMessage(message, options, ...allChoices);

switch (type) {
case 'error':
result = await window.showErrorMessage(message, options, ...allChoices);
break;
case 'warning':
result = await window.showWarningMessage(message, options, ...allChoices);
break;
case 'information':
result = await window.showInformationMessage(message, options, ...allChoices);
break;
}

if (result) {
const resultFn = choices.get(result);
Expand Down
34 changes: 27 additions & 7 deletions extensions/git/src/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1397,20 +1397,37 @@ export class Repository {
}

async commit(message: string | undefined, opts: CommitOptions = Object.create(null)): Promise<void> {
const args = ['commit', '--quiet', '--allow-empty-message'];
const args = ['commit', '--quiet'];
const options: SpawnOptions = {};

if (message) {
options.input = message;
args.push('--file', '-');
}

if (opts.verbose) {
args.push('--verbose');
}

if (opts.all) {
args.push('--all');
}

if (opts.amend && message) {
if (opts.amend) {
args.push('--amend');
}

if (opts.amend && !message) {
args.push('--amend', '--no-edit');
} else {
args.push('--file', '-');
if (!opts.useEditor) {
if (!message) {
if (opts.amend) {
args.push('--no-edit');
} else {
options.input = '';
args.push('--file', '-');
}
}

args.push('--allow-empty-message');
}

if (opts.signoff) {
Expand All @@ -1435,7 +1452,7 @@ export class Repository {
}

try {
await this.exec(args, !opts.amend || message ? { input: message || '' } : {});
await this.exec(args, options);
} catch (commitErr) {
await this.handleCommitError(commitErr);
}
Expand All @@ -1459,6 +1476,9 @@ export class Repository {
if (/not possible because you have unmerged files/.test(commitErr.stderr || '')) {
commitErr.gitErrorCode = GitErrorCodes.UnmergedChanges;
throw commitErr;
} else if (/Aborting commit due to empty commit message/.test(commitErr.stderr || '')) {
commitErr.gitErrorCode = GitErrorCodes.EmptyCommitMessage;
throw commitErr;
}

try {
Expand Down
Loading