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

Git - commit action button #153031

Merged
merged 10 commits into from Jun 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
37 changes: 23 additions & 14 deletions extensions/git/package.json
Expand Up @@ -2427,20 +2427,29 @@
"description": "%config.timeline.showUncommitted%",
"scope": "window"
},
"git.showUnpublishedCommitsButton": {
"type": "string",
"enum": [
"always",
"whenEmpty",
"never"
],
"enumDescriptions": [
"%config.showUnpublishedCommitsButton.always%",
"%config.showUnpublishedCommitsButton.whenEmpty%",
"%config.showUnpublishedCommitsButton.never%"
],
"default": "whenEmpty",
"description": "%config.showUnpublishedCommitsButton%",
"git.showActionButton": {
"type": "object",
"additionalProperties": false,
"description": "%config.showActionButton%",
"properties": {
"commit": {
"type": "boolean",
"description": "%config.showActionButton.commit%"
},
"publish": {
"type": "boolean",
"description": "%config.showActionButton.publish%"
},
"sync": {
"type": "boolean",
"description": "%config.showActionButton.sync%"
}
},
"default": {
"commit": true,
"publish": true,
"sync": true
},
"scope": "resource"
},
"git.statusLimit": {
Expand Down
8 changes: 4 additions & 4 deletions extensions/git/package.nls.json
Expand Up @@ -222,10 +222,10 @@
"config.timeline.date.committed": "Use the committed date",
"config.timeline.date.authored": "Use the authored date",
"config.useCommitInputAsStashMessage": "Controls whether to use the message from the commit input box as the default stash message.",
"config.showUnpublishedCommitsButton": "Controls whether to show an action button to sync or publish, if there are unpublished commits.",
"config.showUnpublishedCommitsButton.always": "Always shows the action button, if there are unpublished commits.",
"config.showUnpublishedCommitsButton.whenEmpty": "Only shows the action button if there are no other changes and there are unpublished commits.",
"config.showUnpublishedCommitsButton.never": "Never shows the action button.",
"config.showActionButton": "Controls whether an action button can be shown in the Source Control view.",
"config.showActionButton.commit": "Show an action button to commit changes.",
"config.showActionButton.publish": "Show an action button to publish a local branch.",
"config.showActionButton.sync": "Show an action button to sync changes.",
"config.statusLimit": "Controls how to limit the number of changes that can be parsed from Git status command. Can be set to 0 for no limit.",
"config.experimental.installGuide": "Experimental improvements for the git setup flow.",
"config.repositoryScanIgnoredFolders": "List of folders that are ignored while scanning for Git repositories when `#git.autoRepositoryDetection#` is set to `true` or `subFolders`.",
Expand Down
129 changes: 106 additions & 23 deletions extensions/git/src/actionButton.ts
Expand Up @@ -37,47 +37,128 @@ export class ActionButtonCommand {

repository.onDidRunGitStatus(this.onDidRunGitStatus, this, this.disposables);
repository.onDidChangeOperations(this.onDidChangeOperations, this, this.disposables);

const root = Uri.file(repository.root);
this.disposables.push(workspace.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('git.postCommitCommand', root) ||
e.affectsConfiguration('git.showActionButton', root)
) {
this._onDidChange.fire();
}
}));
}

get button(): SourceControlActionButton | undefined {
if (!this.state.HEAD || !this.state.HEAD.name || !this.state.HEAD.commit) { return undefined; }

const config = workspace.getConfiguration('git', Uri.file(this.repository.root));
const showActionButton = config.get<string>('showUnpublishedCommitsButton', 'whenEmpty');
const postCommitCommand = config.get<string>('postCommitCommand');
const noPostCommitCommand = postCommitCommand !== 'sync' && postCommitCommand !== 'push';

let actionButton: SourceControlActionButton | undefined;
if (showActionButton === 'always' || (showActionButton === 'whenEmpty' && this.state.repositoryHasNoChanges && noPostCommitCommand)) {
if (this.state.repositoryHasNoChanges) {
if (this.state.HEAD.upstream) {
// Sync Changes
actionButton = this.getSyncChangesActionButton();
} else {
// Publish Branch
actionButton = this.getPublishBranchActionButton();
}
} else {
// Commit Changes
actionButton = this.getCommitActionButton();
}

return actionButton;
}

private getPublishBranchActionButton(): SourceControlActionButton {
return {
command: {
command: 'git.publish',
title: localize('scm publish branch action button title', "{0} Publish Branch", '$(cloud-upload)'),
tooltip: this.state.isActionRunning ?
localize('scm button publish branch running', "Publishing Branch...") :
localize('scm button publish branch', "Publish Branch"),
arguments: [this.repository.sourceControl],
},
enabled: !this.state.isActionRunning
};
private getCommitActionButton(): SourceControlActionButton | undefined {
const config = workspace.getConfiguration('git', Uri.file(this.repository.root));
const showActionButton = config.get<{ commit: boolean }>('showActionButton', { commit: true });

if (showActionButton.commit) {
let title: string, tooltip: string;
const postCommitCommand = config.get<string>('postCommitCommand');

switch (postCommitCommand) {
case 'push': {
title = localize('scm button commit and push title', "$(arrow-up) Commit & Push");
tooltip = this.state.isActionRunning ?
localize('scm button committing pushing tooltip', "Committing & Pushing Changes...") :
localize('scm button commit push tooltip', "Commit & Push Changes");
break;
}
case 'sync': {
title = localize('scm button commit and sync title', "$(sync) Commit & Sync");
tooltip = this.state.isActionRunning ?
localize('scm button committing synching tooltip', "Committing & Synching Changes...") :
localize('scm button commit sync tooltip', "Commit & Sync Changes");
break;
}
default: {
title = localize('scm button commit title', "$(check) Commit");
tooltip = this.state.isActionRunning ?
localize('scm button committing tooltip', "Committing Changes...") :
localize('scm button commit tooltip', "Commit Changes");
break;
}
}

return {
command: {
command: 'git.commit',
title: title,
tooltip: tooltip,
arguments: [this.repository.sourceControl],
},
secondaryCommands: [
[
{
command: 'git.commit',
title: 'Commit',
arguments: [this.repository.sourceControl, ''],
},
{
command: 'git.commit',
title: 'Commit & Push',
arguments: [this.repository.sourceControl, 'push'],
},
{
command: 'git.commit',
title: 'Commit & Sync',
arguments: [this.repository.sourceControl, 'sync'],
},
]
],
enabled: !this.state.isActionRunning
};
}

return undefined;
}

private getPublishBranchActionButton(): SourceControlActionButton | undefined {
const config = workspace.getConfiguration('git', Uri.file(this.repository.root));
const showActionButton = config.get<{ publish: boolean }>('showActionButton', { publish: true });

if (showActionButton.publish) {
return {
command: {
command: 'git.publish',
title: localize('scm publish branch action button title', "{0} Publish Branch", '$(cloud-upload)'),
tooltip: this.state.isActionRunning ?
localize('scm button publish branch running', "Publishing Branch...") :
localize('scm button publish branch', "Publish Branch"),
arguments: [this.repository.sourceControl],
},
enabled: !this.state.isActionRunning
};
}

return undefined;
}

private getSyncChangesActionButton(): SourceControlActionButton | undefined {
if (this.state.HEAD?.ahead) {
const config = workspace.getConfiguration('git', Uri.file(this.repository.root));
const config = workspace.getConfiguration('git', Uri.file(this.repository.root));
const showActionButton = config.get<{ sync: boolean }>('showActionButton', { sync: true });

if (this.state.HEAD?.ahead && showActionButton.sync) {
const rebaseWhenSync = config.get<string>('rebaseWhenSync');

const ahead = `${this.state.HEAD.ahead}$(arrow-up)`;
Expand All @@ -102,11 +183,13 @@ export class ActionButtonCommand {
}

private onDidChangeOperations(): void {
const isActionRunning = this.repository.operations.isRunning(Operation.Sync) ||
const isActionRunning =
this.repository.operations.isRunning(Operation.Sync) ||
this.repository.operations.isRunning(Operation.Push) ||
this.repository.operations.isRunning(Operation.Pull);
this.repository.operations.isRunning(Operation.Pull) ||
this.repository.operations.isRunning(Operation.Commit);

this.state = { ...this.state, isActionRunning: isActionRunning };
this.state = { ...this.state, isActionRunning };
}

private onDidRunGitStatus(): void {
Expand Down
3 changes: 3 additions & 0 deletions extensions/git/src/api/git.d.ts
Expand Up @@ -129,6 +129,8 @@ export interface LogOptions {
readonly path?: string;
}

export type PostCommitCommand = 'push' | 'sync' | string;

export interface CommitOptions {
all?: boolean | 'tracked';
amend?: boolean;
Expand All @@ -139,6 +141,7 @@ export interface CommitOptions {
requireUserConfig?: boolean;
useEditor?: boolean;
verbose?: boolean;
postCommitCommand?: PostCommitCommand;
}

export interface FetchOptions {
Expand Down
19 changes: 8 additions & 11 deletions extensions/git/src/commands.ts
Expand Up @@ -9,7 +9,7 @@ import { Command, commands, Disposable, LineChange, MessageOptions, Position, Pr
import TelemetryReporter from '@vscode/extension-telemetry';
import * as nls from 'vscode-nls';
import { uniqueNamesGenerator, adjectives, animals, colors, NumberDictionary } from '@joaomoreno/unique-names-generator';
import { Branch, ForcePushMode, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourcePublisher } from './api/git';
import { Branch, ForcePushMode, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourcePublisher, PostCommitCommand } from './api/git';
import { Git, Stash } from './git';
import { Model } from './model';
import { Repository, Resource, ResourceGroupType } from './repository';
Expand Down Expand Up @@ -1614,14 +1614,11 @@ export class CommandCenter {
await repository.commit(message, opts);

const postCommitCommand = config.get<'none' | 'push' | 'sync'>('postCommitCommand');

switch (postCommitCommand) {
case 'push':
await this._push(repository, { pushType: PushType.Push, silent: true });
break;
case 'sync':
await this.sync(repository);
break;
if ((opts.postCommitCommand === undefined && postCommitCommand === 'push') || opts.postCommitCommand === 'push') {
await this._push(repository, { pushType: PushType.Push, silent: true });
}
if ((opts.postCommitCommand === undefined && postCommitCommand === 'sync') || opts.postCommitCommand === 'sync') {
await this.sync(repository);
}

return true;
Expand Down Expand Up @@ -1670,8 +1667,8 @@ export class CommandCenter {
}

@command('git.commit', { repository: true })
async commit(repository: Repository): Promise<void> {
await this.commitWithAnyInput(repository);
async commit(repository: Repository, postCommitCommand?: PostCommitCommand): Promise<void> {
await this.commitWithAnyInput(repository, { postCommitCommand });
}

@command('git.commitStaged', { repository: true })
Expand Down
2 changes: 1 addition & 1 deletion extensions/git/src/repository.ts
Expand Up @@ -948,7 +948,7 @@ export class Repository implements Disposable {
|| e.affectsConfiguration('git.ignoreSubmodules', root)
|| e.affectsConfiguration('git.openDiffOnClick', root)
|| e.affectsConfiguration('git.rebaseWhenSync', root)
|| e.affectsConfiguration('git.showUnpublishedCommitsButton', root)
|| e.affectsConfiguration('git.showActionButton', root)
)(this.updateModelState, this, this.disposables);

const updateInputBoxVisibility = () => {
Expand Down
3 changes: 2 additions & 1 deletion src/vs/base/browser/ui/button/button.ts
Expand Up @@ -238,6 +238,7 @@ export interface IButtonWithDropdownOptions extends IButtonOptions {
readonly contextMenuProvider: IContextMenuProvider;
readonly actions: IAction[];
readonly actionRunner?: IActionRunner;
readonly addPrimaryActionToDropdown?: boolean;
}

export class ButtonWithDropdown extends Disposable implements IButton {
Expand Down Expand Up @@ -267,7 +268,7 @@ export class ButtonWithDropdown extends Disposable implements IButton {
this._register(this.dropdownButton.onDidClick(e => {
options.contextMenuProvider.showContextMenu({
getAnchor: () => this.dropdownButton.element,
getActions: () => [this.action, ...options.actions],
getActions: () => options.addPrimaryActionToDropdown === false ? [...options.actions] : [this.action, ...options.actions],
actionRunner: options.actionRunner,
onHide: () => this.dropdownButton.element.setAttribute('aria-expanded', 'false')
});
Expand Down
1 change: 1 addition & 0 deletions src/vs/workbench/api/common/extHost.protocol.ts
Expand Up @@ -1143,6 +1143,7 @@ export interface SCMProviderFeatures {

export interface SCMActionButtonDto {
command: ICommandDto;
secondaryCommands?: ICommandDto[][];
description?: string;
enabled: boolean;
}
Expand Down
3 changes: 3 additions & 0 deletions src/vs/workbench/api/common/extHostSCM.ts
Expand Up @@ -537,6 +537,9 @@ class ExtHostSourceControl implements vscode.SourceControl {
const internal = actionButton !== undefined ?
{
command: this._commands.converter.toInternal(actionButton.command, this._actionButtonDisposables.value),
secondaryCommands: actionButton.secondaryCommands?.map(commandGroup => {
return commandGroup.map(command => this._commands.converter.toInternal(command, this._actionButtonDisposables.value!));
}),
description: actionButton.description,
enabled: actionButton.enabled
} : undefined;
Expand Down
10 changes: 10 additions & 0 deletions src/vs/workbench/contrib/scm/browser/media/scm.css
Expand Up @@ -241,6 +241,16 @@
margin: 0 0.2em 0 0;
}

.scm-view .button-container > .monaco-button-dropdown {
flex-grow: 1;
}

.scm-view .button-container > .monaco-button-dropdown > .monaco-dropdown-button {
display:flex;
align-items: center;
padding: 0 4px;
}

.scm-view .scm-editor.hidden {
display: none;
}
Expand Down