Преглед на файлове

Adds remote command to Git Command Palette

main
Eric Amodio преди 1 година
родител
ревизия
0a0db1fc38
променени са 18 файла, в които са добавени 1211 реда и са изтрити 471 реда
  1. +1
    -0
      CHANGELOG.md
  2. +4
    -4
      package.json
  3. +461
    -0
      src/commands/git/remote.ts
  4. +37
    -45
      src/commands/gitCommands.actions.ts
  5. +19
    -4
      src/commands/gitCommands.ts
  6. +2
    -0
      src/commands/gitCommands.utils.ts
  7. +239
    -22
      src/commands/quickCommand.steps.ts
  8. +12
    -4
      src/env/node/git/git.ts
  9. +8
    -2
      src/env/node/git/localGitProvider.ts
  10. +3
    -2
      src/git/gitProvider.ts
  11. +15
    -13
      src/git/gitProviderService.ts
  12. +17
    -0
      src/git/models/repository.ts
  13. +5
    -2
      src/plus/github/githubGitProvider.ts
  14. +4
    -3
      src/quickpicks/commitPicker.ts
  15. +368
    -354
      src/quickpicks/items/gitCommands.ts
  16. +4
    -4
      src/quickpicks/referencePicker.ts
  17. +3
    -2
      src/quickpicks/repositoryPicker.ts
  18. +9
    -10
      src/views/viewCommands.ts

+ 1
- 0
CHANGELOG.md Целия файл

@ -22,6 +22,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
- Adds a `worktrees.openAfterCreate` setting to specify how and when to open a worktree after it is created
- Creates new worktrees from the "main" repo, if already in a worktree
- Shows the _Worktrees_ view after creating a new worktree
- Adds a new _remote_ command to the _Git Command Palette_ to add, prune, and remove remotes
### Changed

+ 4
- 4
package.json Целия файл

@ -5582,8 +5582,8 @@
"icon": "$(discard)"
},
{
"command": "gitlens.views.terminalRemoveRemote",
"title": "Remove Remote (via Terminal)",
"command": "gitlens.views.removeRemote",
"title": "Remove Remote...",
"category": "GitLens"
},
{
@ -7928,7 +7928,7 @@
"when": "false"
},
{
"command": "gitlens.views.terminalRemoveRemote",
"command": "gitlens.views.removeRemote",
"when": "false"
},
{
@ -10617,7 +10617,7 @@
"alt": "gitlens.copyRemoteBranchesUrl"
},
{
"command": "gitlens.views.terminalRemoveRemote",
"command": "gitlens.views.removeRemote",
"when": "!gitlens:readonly && !gitlens:untrusted && !gitlens:hasVirtualFolders && viewItem =~ /gitlens:remote\\b/",
"group": "6_gitlens_terminal@1"
},

+ 461
- 0
src/commands/git/remote.ts Целия файл

@ -0,0 +1,461 @@
import type { QuickPickItem } from 'vscode';
import { QuickInputButtons } from 'vscode';
import type { Container } from '../../container';
import type { GitRemote } from '../../git/models/remote';
import { Repository } from '../../git/models/repository';
import { Logger } from '../../logger';
import { showGenericErrorMessage } from '../../messages';
import type { QuickPickItemOfT } from '../../quickpicks/items/common';
import { FlagsQuickPickItem } from '../../quickpicks/items/flags';
import type { ViewsWithRepositoryFolders } from '../../views/viewBase';
import { GitActions } from '../gitCommands.actions';
import type {
AsyncStepResultGenerator,
PartialStepState,
QuickPickStep,
StepGenerator,
StepResultGenerator,
StepSelection,
StepState,
} from '../quickCommand';
import {
appendReposToTitle,
inputRemoteNameStep,
inputRemoteUrlStep,
pickRemoteStep,
pickRepositoryStep,
QuickCommand,
StepResult,
} from '../quickCommand';
interface Context {
repos: Repository[];
associatedView: ViewsWithRepositoryFolders;
title: string;
}
type AddFlags = '-f';
interface AddState {
subcommand: 'add';
repo: string | Repository;
name: string;
url: string;
flags: AddFlags[];
}
interface RemoveState {
subcommand: 'remove';
repo: string | Repository;
remote: string | GitRemote;
}
interface PruneState {
subcommand: 'prune';
repo: string | Repository;
remote: string | GitRemote;
}
type State = AddState | RemoveState | PruneState;
type RemoteStepState<T extends State> = SomeNonNullable<StepState<T>, 'subcommand'>;
type AddStepState<T extends AddState = AddState> = RemoteStepState<ExcludeSome<T, 'repo', string>>;
function assertStateStepAdd(state: PartialStepState<State>): asserts state is AddStepState {
if (state.repo instanceof Repository && state.subcommand === 'add') return;
debugger;
throw new Error('Missing repository');
}
type RemoveStepState<T extends RemoveState = RemoveState> = RemoteStepState<ExcludeSome<T, 'repo', string>>;
function assertStateStepRemove(state: PartialStepState<State>): asserts state is RemoveStepState {
if (state.repo instanceof Repository && state.subcommand === 'remove') return;
debugger;
throw new Error('Missing repository');
}
type PruneStepState<T extends PruneState = PruneState> = RemoteStepState<ExcludeSome<T, 'repo', string>>;
function assertStateStepPrune(state: PartialStepState<State>): asserts state is PruneStepState {
if (state.repo instanceof Repository && state.subcommand === 'prune') return;
debugger;
throw new Error('Missing repository');
}
function assertStateStepRemoveRemotes(
state: RemoveStepState,
): asserts state is ExcludeSome<typeof state, 'remote', string> {
if (typeof state.remote !== 'string') return;
debugger;
throw new Error('Missing remote');
}
function assertStateStepPruneRemotes(
state: PruneStepState,
): asserts state is ExcludeSome<typeof state, 'remote', string> {
if (typeof state.remote !== 'string') return;
debugger;
throw new Error('Missing remote');
}
const subcommandToTitleMap = new Map<State['subcommand'], string>([
['add', 'Add'],
['prune', 'Prune'],
['remove', 'Remove'],
]);
function getTitle(title: string, subcommand: State['subcommand'] | undefined) {
return subcommand == null ? title : `${subcommandToTitleMap.get(subcommand)} ${title}`;
}
export interface RemoteGitCommandArgs {
readonly command: 'remote';
confirm?: boolean;
state?: Partial<State>;
}
export class RemoteGitCommand extends QuickCommand<State> {
private subcommand: State['subcommand'] | undefined;
constructor(container: Container, args?: RemoteGitCommandArgs) {
super(container, 'remote', 'remote', 'Remote', {
description: 'add, prune, or remove remotes',
});
let counter = 0;
if (args?.state?.subcommand != null) {
counter++;
switch (args?.state.subcommand) {
case 'add':
if (args.state.name != null) {
counter++;
}
if (args.state.url != null) {
counter++;
}
break;
case 'prune':
case 'remove':
if (args.state.remote != null) {
counter++;
}
break;
}
}
if (args?.state?.repo != null) {
counter++;
}
this.initialState = {
counter: counter,
confirm: args?.confirm,
...args?.state,
};
}
override get canConfirm(): boolean {
return this.subcommand != null;
}
override get canSkipConfirm(): boolean {
return this.subcommand === 'remove' || this.subcommand === 'prune' ? false : super.canSkipConfirm;
}
override get skipConfirmKey() {
return `${this.key}${this.subcommand == null ? '' : `-${this.subcommand}`}:${this.pickedVia}`;
}
protected async *steps(state: PartialStepState<State>): StepGenerator {
const context: Context = {
repos: this.container.git.openRepositories,
associatedView: this.container.remotesView,
title: this.title,
};
let skippedStepTwo = false;
while (this.canStepsContinue(state)) {
context.title = this.title;
if (state.counter < 1 || state.subcommand == null) {
this.subcommand = undefined;
const result = yield* this.pickSubcommandStep(state);
// Always break on the first step (so we will go back)
if (result === StepResult.Break) break;
state.subcommand = result;
}
this.subcommand = state.subcommand;
context.title = getTitle(this.title, state.subcommand);
if (state.counter < 2 || state.repo == null || typeof state.repo === 'string') {
skippedStepTwo = false;
if (context.repos.length === 1) {
skippedStepTwo = true;
state.counter++;
state.repo = context.repos[0];
} else {
const result = yield* pickRepositoryStep(state, context);
if (result === StepResult.Break) continue;
state.repo = result;
}
}
switch (state.subcommand) {
case 'add':
assertStateStepAdd(state);
yield* this.addCommandSteps(state, context);
// Clear any chosen name, since we are exiting this subcommand
state.name = undefined!;
state.url = undefined!;
break;
case 'prune':
assertStateStepPrune(state);
yield* this.pruneCommandSteps(state, context);
break;
case 'remove':
assertStateStepRemove(state);
yield* this.removeCommandSteps(state, context);
break;
default:
QuickCommand.endSteps(state);
break;
}
// If we skipped the previous step, make sure we back up past it
if (skippedStepTwo) {
state.counter--;
}
}
return state.counter < 0 ? StepResult.Break : undefined;
}
private *pickSubcommandStep(state: PartialStepState<State>): StepResultGenerator<State['subcommand']> {
const step = QuickCommand.createPickStep<QuickPickItemOfT<State['subcommand']>>({
title: this.title,
placeholder: `Choose a ${this.label} command`,
items: [
{
label: 'add',
description: 'adds a new remote',
picked: state.subcommand === 'add',
item: 'add',
},
{
label: 'prune',
description: 'prunes remote branches on the specified remote',
picked: state.subcommand === 'prune',
item: 'prune',
},
{
label: 'remove',
description: 'removes the specified remote',
picked: state.subcommand === 'remove',
item: 'remove',
},
],
buttons: [QuickInputButtons.Back],
});
const selection: StepSelection<typeof step> = yield step;
return QuickCommand.canPickStepContinue(step, state, selection) ? selection[0].item : StepResult.Break;
}
private async *addCommandSteps(state: AddStepState, context: Context): AsyncStepResultGenerator<void> {
if (state.flags == null) {
state.flags = ['-f'];
}
let alreadyExists = (await state.repo.getRemotes({ filter: r => r.name === state.name })).length !== 0;
while (this.canStepsContinue(state)) {
if (state.counter < 3 || state.name == null || alreadyExists) {
const result = yield* inputRemoteNameStep(state, context, {
placeholder: 'Please provide a name for the remote',
value: state.name,
});
if (result === StepResult.Break) continue;
alreadyExists = (await state.repo.getRemotes({ filter: r => r.name === result })).length !== 0;
if (alreadyExists) {
state.counter--;
continue;
}
state.name = result;
}
if (state.counter < 4 || state.url == null) {
const result = yield* inputRemoteUrlStep(state, context, {
placeholder: 'Please provide a URL for the remote',
value: state.url,
});
if (result === StepResult.Break) continue;
state.url = result;
}
if (this.confirm(state.confirm)) {
const result = yield* this.addCommandConfirmStep(state, context);
if (result === StepResult.Break) continue;
state.flags = result;
}
QuickCommand.endSteps(state);
const remote = await state.repo.addRemote(
state.name,
state.url,
state.flags.includes('-f') ? { fetch: true } : undefined,
);
queueMicrotask(
() =>
void GitActions.Remote.reveal(remote, {
focus: true,
select: true,
}),
);
}
}
private *addCommandConfirmStep(state: AddStepState<AddState>, context: Context): StepResultGenerator<AddFlags[]> {
const step: QuickPickStep<FlagsQuickPickItem<AddFlags>> = QuickCommand.createConfirmStep(
appendReposToTitle(`Confirm ${context.title}`, state, context),
[
FlagsQuickPickItem.create<AddFlags>(state.flags, [], {
label: context.title,
detail: `Will add remote '${state.name}' for ${state.url}`,
}),
FlagsQuickPickItem.create<AddFlags>(state.flags, ['-f'], {
label: `${context.title} and Fetch`,
description: '-f',
detail: `Will add and fetch remote '${state.name}' for ${state.url}`,
}),
],
context,
);
const selection: StepSelection<typeof step> = yield step;
return QuickCommand.canPickStepContinue(step, state, selection) ? selection[0].item : StepResult.Break;
}
private async *removeCommandSteps(state: RemoveStepState, context: Context): AsyncStepResultGenerator<void> {
while (this.canStepsContinue(state)) {
if (state.remote != null) {
if (typeof state.remote === 'string') {
const [remote] = await state.repo.getRemotes({ filter: r => r.name === state.remote });
if (remote != null) {
state.remote = remote;
} else {
state.remote = undefined!;
}
}
}
if (state.counter < 3 || state.remote == null) {
context.title = getTitle('Remotes', state.subcommand);
const result = yield* pickRemoteStep(state, context, {
picked: state.remote?.name,
placeholder: 'Choose remote to remove',
});
// Always break on the first step (so we will go back)
if (result === StepResult.Break) break;
state.remote = result;
}
assertStateStepRemoveRemotes(state);
const result = yield* this.removeCommandConfirmStep(state, context);
if (result === StepResult.Break) continue;
QuickCommand.endSteps(state);
try {
await state.repo.removeRemote(state.remote.name);
} catch (ex) {
Logger.error(ex);
void showGenericErrorMessage('Unable to remove remote');
}
}
}
private *removeCommandConfirmStep(
state: RemoveStepState<ExcludeSome<RemoveState, 'remote', string>>,
context: Context,
): StepResultGenerator<void> {
const step: QuickPickStep<QuickPickItem> = QuickCommand.createConfirmStep(
appendReposToTitle(`Confirm ${context.title}`, state, context),
[
{
label: context.title,
detail: `Will remove remote '${state.remote.name}'`,
},
],
context,
);
const selection: StepSelection<typeof step> = yield step;
return QuickCommand.canPickStepContinue(step, state, selection) ? undefined : StepResult.Break;
}
private async *pruneCommandSteps(state: PruneStepState, context: Context): AsyncStepResultGenerator<void> {
while (this.canStepsContinue(state)) {
if (state.remote != null) {
if (typeof state.remote === 'string') {
const [remote] = await state.repo.getRemotes({ filter: r => r.name === state.remote });
if (remote != null) {
state.remote = remote;
} else {
state.remote = undefined!;
}
}
}
if (state.counter < 3 || state.remote == null) {
const result = yield* pickRemoteStep(state, context, {
picked: state.remote?.name,
placeholder: 'Choose a remote to prune',
});
// Always break on the first step (so we will go back)
if (result === StepResult.Break) break;
state.remote = result;
}
assertStateStepPruneRemotes(state);
const result = yield* this.pruneCommandConfirmStep(state, context);
if (result === StepResult.Break) continue;
QuickCommand.endSteps(state);
void state.repo.pruneRemote(state.remote.name);
}
}
private *pruneCommandConfirmStep(
state: PruneStepState<ExcludeSome<PruneState, 'remote', string>>,
context: Context,
): StepResultGenerator<void> {
const step: QuickPickStep<QuickPickItem> = QuickCommand.createConfirmStep(
appendReposToTitle(`Confirm ${context.title}`, state, context),
[
{
label: context.title,
detail: `Will prune remote '${state.remote.name}'`,
},
],
context,
);
const selection: StepSelection<typeof step> = yield step;
return QuickCommand.canPickStepContinue(step, state, selection) ? undefined : StepResult.Break;
}
}

+ 37
- 45
src/commands/gitCommands.actions.ts Целия файл

@ -6,6 +6,7 @@ import type {
DiffWithPreviousCommandArgs,
DiffWithWorkingCommandArgs,
GitCommandsCommandArgs,
GitCommandsCommandArgsWithCompletion,
OpenFileOnRemoteCommandArgs,
OpenWorkingFileCommandArgs,
ShowQuickCommitCommandArgs,
@ -30,22 +31,21 @@ import type { GitRemote } from '../git/models/remote';
import type { Repository } from '../git/models/repository';
import type { GitWorktree } from '../git/models/worktree';
import type { ShowInCommitGraphCommandArgs } from '../plus/webviews/graph/graphWebview';
import { RepositoryPicker } from '../quickpicks/repositoryPicker';
import { ensure } from '../system/array';
import { executeCommand, executeCoreCommand, executeEditorCommand } from '../system/command';
import { defer } from '../system/promise';
import type { OpenWorkspaceLocation } from '../system/utils';
import { findOrOpenEditor, findOrOpenEditors, openWorkspace } from '../system/utils';
import type { ViewsWithRepositoryFolders } from '../views/viewBase';
import type { ResetGitCommandArgs } from './git/reset';
export async function executeGitCommand(args: GitCommandsCommandArgs): Promise<void> {
void (await executeCommand<GitCommandsCommandArgs>(Commands.GitCommands, args));
}
function ensureRepo(repo: string | Repository): Repository {
const repository = typeof repo === 'string' ? Container.instance.git.getRepository(repo) : repo;
if (repository == null) throw new Error('Repository not found');
return repository;
const deferred = defer<void>();
void (await executeCommand<GitCommandsCommandArgsWithCompletion>(Commands.GitCommands, {
...args,
completion: deferred,
}));
return deferred.promise;
}
export namespace GitActions {
@ -822,38 +822,17 @@ export namespace GitActions {
}
export namespace Remote {
export async function add(repo?: string | Repository) {
if (repo == null) {
repo = Container.instance.git.highlander;
if (repo == null) {
const pick = await RepositoryPicker.show(undefined, 'Choose a repository to add a remote to');
repo = pick?.item;
if (repo == null) return undefined;
}
}
const name = await window.showInputBox({
prompt: 'Please provide a name for the remote',
placeHolder: 'Remote name',
value: undefined,
ignoreFocusOut: true,
});
if (name == null || name.length === 0) return undefined;
const url = await window.showInputBox({
prompt: 'Please provide the repository url for the remote',
placeHolder: 'Remote repository url',
value: undefined,
ignoreFocusOut: true,
export function add(repo?: string | Repository, name?: string, url?: string, options?: { fetch?: boolean }) {
return executeGitCommand({
command: 'remote',
state: {
repo: repo,
subcommand: 'add',
name: name,
url: url,
flags: options?.fetch ? ['-f'] : undefined,
},
});
if (url == null || url.length === 0) return undefined;
repo = ensureRepo(repo);
await Container.instance.git.addRemote(repo.path, name, url);
await repo.fetch({ remote: name });
return name;
}
export async function fetch(repo: string | Repository, remote: string) {
@ -868,11 +847,21 @@ export namespace GitActions {
}
export async function prune(repo: string | Repository, remote: string) {
await Container.instance.git.pruneRemote(typeof repo === 'string' ? repo : repo.path, remote);
return executeGitCommand({
command: 'remote',
state: { repo: repo, subcommand: 'prune', remote: remote },
});
}
export async function remove(repo: string | Repository, remote: string) {
return executeGitCommand({
command: 'remote',
state: { repo: repo, subcommand: 'remove', remote: remote },
});
}
export async function reveal(
remote: GitRemote,
remote: GitRemote | undefined,
options?: {
select?: boolean;
focus?: boolean;
@ -880,9 +869,12 @@ export namespace GitActions {
},
) {
const view = Container.instance.remotesView;
const node = view.canReveal
? await view.revealRemote(remote, options)
: await Container.instance.repositoriesView.revealRemote(remote, options);
const node =
remote != null
? view.canReveal
? await view.revealRemote(remote, options)
: await Container.instance.repositoriesView.revealRemote(remote, options)
: undefined;
if (node == null) {
void view.show({ preserveFocus: !options?.focus });
}
@ -890,7 +882,7 @@ export namespace GitActions {
}
}
export namespace Repository {
export namespace Repo {
export async function reveal(
repoPath: string,
view?: ViewsWithRepositoryFolders,

+ 19
- 4
src/commands/gitCommands.ts Целия файл

@ -1,5 +1,5 @@
import type { Disposable, InputBox, QuickInputButton, QuickPick, QuickPickItem } from 'vscode';
import { QuickInputButtons, window } from 'vscode';
import { InputBoxValidationSeverity, QuickInputButtons, window } from 'vscode';
import { configuration } from '../configuration';
import { Commands } from '../constants';
import { Container } from '../container';
@ -7,6 +7,7 @@ import type { KeyMapping } from '../keyboard';
import { Directive, DirectiveQuickPickItem } from '../quickpicks/items/directive';
import { command } from '../system/command';
import { log } from '../system/decorators/log';
import type { Deferred } from '../system/promise';
import { isPromise } from '../system/promise';
import type { CommandContext } from './base';
import { Command } from './base';
@ -19,6 +20,7 @@ import type { MergeGitCommandArgs } from './git/merge';
import type { PullGitCommandArgs } from './git/pull';
import type { PushGitCommandArgs } from './git/push';
import type { RebaseGitCommandArgs } from './git/rebase';
import type { RemoteGitCommandArgs } from './git/remote';
import type { ResetGitCommandArgs } from './git/reset';
import type { RevertGitCommandArgs } from './git/revert';
import type { SearchGitCommandArgs } from './git/search';
@ -46,6 +48,7 @@ export type GitCommandsCommandArgs =
| PullGitCommandArgs
| PushGitCommandArgs
| RebaseGitCommandArgs
| RemoteGitCommandArgs
| ResetGitCommandArgs
| RevertGitCommandArgs
| SearchGitCommandArgs
@ -56,6 +59,8 @@ export type GitCommandsCommandArgs =
| TagGitCommandArgs
| WorktreeGitCommandArgs;
export type GitCommandsCommandArgsWithCompletion = GitCommandsCommandArgs & { completion?: Deferred<void> };
@command()
export class GitCommandsCommand extends Command {
private startedWith: 'menu' | 'command' = 'menu';
@ -75,7 +80,7 @@ export class GitCommandsCommand extends Command {
]);
}
protected override preExecute(context: CommandContext, args?: GitCommandsCommandArgs) {
protected override preExecute(context: CommandContext, args?: GitCommandsCommandArgsWithCompletion) {
switch (context.command) {
case Commands.GitCommandsBranch:
args = { command: 'branch' };
@ -110,7 +115,7 @@ export class GitCommandsCommand extends Command {
}
@log({ args: false, scoped: true, singleLine: true, timed: false })
async execute(args?: GitCommandsCommandArgs) {
async execute(args?: GitCommandsCommandArgsWithCompletion) {
const commandsStep = new PickCommandStep(this.container, args);
const command = args?.command != null ? commandsStep.find(args.command) : undefined;
@ -170,6 +175,8 @@ export class GitCommandsCommand extends Command {
break;
}
args?.completion?.fulfill();
}
private async showLoadingIfNeeded(
@ -322,7 +329,8 @@ export class GitCommandsCommand extends Command {
const disposables: Disposable[] = [];
try {
return await new Promise<QuickPickStep | QuickInputStep | undefined>(resolve => {
// eslint-disable-next-line no-async-promise-executor
return await new Promise<QuickPickStep | QuickInputStep | undefined>(async resolve => {
const goBack = async () => {
input.value = '';
if (commandsStep.command != null) {
@ -407,6 +415,13 @@ export class GitCommandsCommand extends Command {
input.prompt = step.prompt;
if (step.value != null) {
input.value = step.value;
if (step.validate != null) {
const [valid, message] = await step.validate(step.value);
if (!valid && message != null) {
input.validationMessage = { severity: InputBoxValidationSeverity.Error, message: message };
}
}
}
// If we are starting over clear the previously active command

+ 2
- 0
src/commands/gitCommands.utils.ts Целия файл

@ -13,6 +13,7 @@ import { MergeGitCommand } from './git/merge';
import { PullGitCommand } from './git/pull';
import { PushGitCommand } from './git/push';
import { RebaseGitCommand } from './git/rebase';
import { RemoteGitCommand } from './git/remote';
import { ResetGitCommand } from './git/reset';
import { RevertGitCommand } from './git/revert';
import { SearchGitCommand } from './git/search';
@ -74,6 +75,7 @@ export class PickCommandStep implements QuickPickStep {
readonly ? undefined : new PullGitCommand(container, args?.command === 'pull' ? args : undefined),
readonly ? undefined : new PushGitCommand(container, args?.command === 'push' ? args : undefined),
readonly ? undefined : new RebaseGitCommand(container, args?.command === 'rebase' ? args : undefined),
readonly ? undefined : new RemoteGitCommand(container, args?.command === 'remote' ? args : undefined),
readonly ? undefined : new ResetGitCommand(container, args?.command === 'reset' ? args : undefined),
readonly ? undefined : new RevertGitCommand(container, args?.command === 'revert' ? args : undefined),
new SearchGitCommand(container, args?.command === 'search' || args?.command === 'grep' ? args : undefined),

+ 239
- 22
src/commands/quickCommand.steps.ts Целия файл

@ -48,16 +48,26 @@ import {
} from '../quickpicks/items/commits';
import { CommandQuickPickItem, QuickPickSeparator } from '../quickpicks/items/common';
import { Directive, DirectiveQuickPickItem } from '../quickpicks/items/directive';
import {
import type {
BranchQuickPickItem,
CommitQuickPickItem,
ContributorQuickPickItem,
GitCommandQuickPickItem,
RefQuickPickItem,
RemoteQuickPickItem,
RepositoryQuickPickItem,
TagQuickPickItem,
WorktreeQuickPickItem,
} from '../quickpicks/items/gitCommands';
import {
createBranchQuickPickItem,
createCommitQuickPickItem,
createContributorQuickPickItem,
createRefQuickPickItem,
createRemoteQuickPickItem,
createRepositoryQuickPickItem,
createTagQuickPickItem,
createWorktreeQuickPickItem,
GitCommandQuickPickItem,
} from '../quickpicks/items/gitCommands';
import type { ReferencesQuickPickItem } from '../quickpicks/referencePicker';
import {
CopyRemoteResourceCommandQuickPickItem,
@ -123,6 +133,29 @@ export async function getBranches(
}) as Promise<BranchQuickPickItem[]>;
}
export async function getRemotes(
repo: Repository,
options: {
buttons?: QuickInputButton[];
filter?: (b: GitRemote) => boolean;
picked?: string | string[];
},
): Promise<RemoteQuickPickItem[]> {
if (repo == null) return [];
const remotes = (await repo.getRemotes(options?.filter != null ? { filter: options.filter } : undefined)).map(r =>
createRemoteQuickPickItem(
r,
options?.picked != null &&
(typeof options.picked === 'string' ? r.name === options.picked : options.picked.includes(r.name)),
{
buttons: options?.buttons,
},
),
);
return remotes;
}
export async function getTags(
repos: Repository | Repository[],
options?: {
@ -159,7 +192,7 @@ export async function getWorktrees(
...worktrees
.filter(w => filter == null || filter(w))
.map(async w =>
WorktreeQuickPickItem.create(
createWorktreeQuickPickItem(
w,
picked != null &&
(typeof picked === 'string' ? w.uri.toString() === picked : picked.includes(w.uri.toString())),
@ -266,7 +299,7 @@ export async function getBranchesAndOrTags(
branches
.filter(b => !b.remote)
.map(b =>
BranchQuickPickItem.create(
createBranchQuickPickItem(
b,
picked != null && (typeof picked === 'string' ? b.ref === picked : picked.includes(b.ref)),
{
@ -284,7 +317,7 @@ export async function getBranchesAndOrTags(
branches
.filter(b => b.remote)
.map(b =>
BranchQuickPickItem.create(
createBranchQuickPickItem(
b,
picked != null && (typeof picked === 'string' ? b.ref === picked : picked.includes(b.ref)),
{
@ -302,7 +335,7 @@ export async function getBranchesAndOrTags(
if (tags != null && tags.length !== 0 && (branches == null || branches.length === 0)) {
return tags.map(t =>
TagQuickPickItem.create(
createTagQuickPickItem(
t,
picked != null && (typeof picked === 'string' ? t.ref === picked : picked.includes(t.ref)),
{
@ -320,7 +353,7 @@ export async function getBranchesAndOrTags(
branches!
.filter(b => !b.remote)
.map(b =>
BranchQuickPickItem.create(
createBranchQuickPickItem(
b,
picked != null && (typeof picked === 'string' ? b.ref === picked : picked.includes(b.ref)),
{
@ -334,7 +367,7 @@ export async function getBranchesAndOrTags(
)),
QuickPickSeparator.create('Tags'),
...tags!.map(t =>
TagQuickPickItem.create(
createTagQuickPickItem(
t,
picked != null && (typeof picked === 'string' ? t.ref === picked : picked.includes(t.ref)),
{
@ -350,7 +383,7 @@ export async function getBranchesAndOrTags(
branches!
.filter(b => b.remote)
.map(b =>
BranchQuickPickItem.create(
createBranchQuickPickItem(
b,
picked != null && (typeof picked === 'string' ? b.ref === picked : picked.includes(b.ref)),
{
@ -386,7 +419,7 @@ export function getValidateGitReferenceFn(
if (inRefMode && options?.ranges && GitRevision.isRange(value)) {
quickpick.items = [
RefQuickPickItem.create(value, repos.path, true, {
createRefQuickPickItem(value, repos.path, true, {
alwaysShow: true,
buttons: options?.buttons,
ref: false,
@ -421,7 +454,7 @@ export function getValidateGitReferenceFn(
const commit = await Container.instance.git.getCommit(repos.path, value);
quickpick.items = [
CommitQuickPickItem.create(commit!, true, {
createCommitQuickPickItem(commit!, true, {
alwaysShow: true,
buttons: options?.buttons,
compact: true,
@ -480,6 +513,85 @@ export async function* inputBranchNameStep<
return value;
}
export async function* inputRemoteNameStep<
State extends PartialStepState & ({ repo: Repository } | { repos: Repository[] }),
Context extends { repos: Repository[]; title: string },
>(
state: State,
context: Context,
options: { placeholder: string; titleContext?: string; value?: string },
): AsyncStepResultGenerator<string> {
const step = QuickCommand.createInputStep({
title: appendReposToTitle(`${context.title}${options.titleContext ?? ''}`, state, context),
placeholder: options.placeholder,
value: options.value,
prompt: 'Enter remote name',
validate: async (value: string | undefined): Promise<[boolean, string | undefined]> => {
if (value == null) return [false, undefined];
value = value.trim();
if (value.length === 0) return [false, 'Please enter a valid remote name'];
const valid = !/[^a-zA-Z0-9-_.]/.test(value);
if (!valid) return [false, `'${value}' isn't a valid remote name`];
if ('repo' in state) {
const alreadyExists = (await state.repo.getRemotes({ filter: r => r.name === value })).length !== 0;
if (alreadyExists) {
return [false, `Remote named '${value}' already exists`];
}
}
return [true, undefined];
},
});
const value: StepSelection<typeof step> = yield step;
if (
!QuickCommand.canStepContinue(step, state, value) ||
!(await QuickCommand.canInputStepContinue(step, state, value))
) {
return StepResult.Break;
}
return value;
}
export async function* inputRemoteUrlStep<
State extends PartialStepState & ({ repo: Repository } | { repos: Repository[] }),
Context extends { repos: Repository[]; title: string },
>(
state: State,
context: Context,
options: { placeholder: string; titleContext?: string; value?: string },
): AsyncStepResultGenerator<string> {
const step = QuickCommand.createInputStep({
title: appendReposToTitle(`${context.title}${options.titleContext ?? ''}`, state, context),
placeholder: options.placeholder,
value: options.value,
prompt: 'Enter remote URL',
validate: (value: string | undefined): [boolean, string | undefined] => {
if (value == null) return [false, undefined];
value = value.trim();
if (value.length === 0) return [false, 'Please enter a valid remote URL'];
const valid = /^(https?|git|ssh|rsync):\/\//.test(value);
return [valid, valid ? undefined : `'${value}' isn't a valid remote URL`];
},
});
const value: StepSelection<typeof step> = yield step;
if (
!QuickCommand.canStepContinue(step, state, value) ||
!(await QuickCommand.canInputStepContinue(step, state, value))
) {
return StepResult.Break;
}
return value;
}
export async function* inputTagNameStep<
State extends PartialStepState & ({ repo: Repository } | { repos: Repository[] }),
Context extends { repos: Repository[]; showTags?: boolean; title: string },
@ -899,7 +1011,7 @@ export async function* pickCommitStep<
? [DirectiveQuickPickItem.create(Directive.Back, true), DirectiveQuickPickItem.create(Directive.Cancel)]
: [
...map(log.commits.values(), commit =>
CommitQuickPickItem.create(
createCommitQuickPickItem(
commit,
picked != null &&
(typeof picked === 'string' ? commit.ref === picked : picked.includes(commit.ref)),
@ -1024,7 +1136,7 @@ export function* pickCommitsStep<
? [DirectiveQuickPickItem.create(Directive.Back, true), DirectiveQuickPickItem.create(Directive.Cancel)]
: [
...map(log.commits.values(), commit =>
CommitQuickPickItem.create(
createCommitQuickPickItem(
commit,
picked != null &&
(typeof picked === 'string' ? commit.ref === picked : picked.includes(commit.ref)),
@ -1111,7 +1223,7 @@ export async function* pickContributorsStep<
placeholder: placeholder,
matchOnDescription: true,
items: (await Container.instance.git.getContributors(state.repo.path)).map(c =>
ContributorQuickPickItem.create(c, message?.includes(c.getCoauthor()), {
createContributorQuickPickItem(c, message?.includes(c.getCoauthor()), {
buttons: [QuickCommandButtons.RevealInSideBar],
}),
),
@ -1135,6 +1247,111 @@ export async function* pickContributorsStep<
return QuickCommand.canPickStepContinue(step, state, selection) ? selection.map(i => i.item) : StepResult.Break;
}
export async function* pickRemoteStep<
State extends PartialStepState & { repo: Repository },
Context extends { repos: Repository[]; title: string },
>(
state: State,
context: Context,
{
filter,
picked,
placeholder,
titleContext,
}: {
filter?: (r: GitRemote) => boolean;
picked?: string | string[];
placeholder: string;
titleContext?: string;
},
): AsyncStepResultGenerator<GitRemote> {
const remotes = await getRemotes(state.repo, {
buttons: [QuickCommandButtons.RevealInSideBar],
filter: filter,
picked: picked,
});
const step = QuickCommand.createPickStep<RemoteQuickPickItem>({
title: appendReposToTitle(`${context.title}${titleContext ?? ''}`, state, context),
placeholder: remotes.length === 0 ? `No remotes found in ${state.repo.formattedName}` : placeholder,
matchOnDetail: true,
items:
remotes.length === 0
? [DirectiveQuickPickItem.create(Directive.Back, true), DirectiveQuickPickItem.create(Directive.Cancel)]
: remotes,
onDidClickItemButton: (quickpick, button, { item }) => {
if (button === QuickCommandButtons.RevealInSideBar) {
void GitActions.Remote.reveal(item, { select: true, focus: false, expand: true });
}
},
keys: ['right', 'alt+right', 'ctrl+right'],
onDidPressKey: async quickpick => {
if (quickpick.activeItems.length === 0) return;
await GitActions.Remote.reveal(quickpick.activeItems[0].item, {
select: true,
focus: false,
expand: true,
});
},
});
const selection: StepSelection<typeof step> = yield step;
return QuickCommand.canPickStepContinue(step, state, selection) ? selection[0].item : StepResult.Break;
}
export async function* pickRemotesStep<
State extends PartialStepState & { repo: Repository },
Context extends { repos: Repository[]; title: string },
>(
state: State,
context: Context,
{
filter,
picked,
placeholder,
titleContext,
}: {
filter?: (b: GitRemote) => boolean;
picked?: string | string[];
placeholder: string;
titleContext?: string;
},
): AsyncStepResultGenerator<GitRemote[]> {
const remotes = await getRemotes(state.repo, {
buttons: [QuickCommandButtons.RevealInSideBar],
filter: filter,
picked: picked,
});
const step = QuickCommand.createPickStep<RemoteQuickPickItem>({
multiselect: remotes.length !== 0,
title: appendReposToTitle(`${context.title}${titleContext ?? ''}`, state, context),
placeholder: remotes.length === 0 ? `No remotes found in ${state.repo.formattedName}` : placeholder,
matchOnDetail: true,
items:
remotes.length === 0
? [DirectiveQuickPickItem.create(Directive.Back, true), DirectiveQuickPickItem.create(Directive.Cancel)]
: remotes,
onDidClickItemButton: (quickpick, button, { item }) => {
if (button === QuickCommandButtons.RevealInSideBar) {
void GitActions.Remote.reveal(item, { select: true, focus: false, expand: true });
}
},
keys: ['right', 'alt+right', 'ctrl+right'],
onDidPressKey: async quickpick => {
if (quickpick.activeItems.length === 0) return;
await GitActions.Remote.reveal(quickpick.activeItems[0].item, {
select: true,
focus: false,
expand: true,
});
},
});
const selection: StepSelection<typeof step> = yield step;
return QuickCommand.canPickStepContinue(step, state, selection) ? selection.map(i => i.item) : StepResult.Break;
}
export async function* pickRepositoryStep<
State extends PartialStepState & { repo?: string | Repository },
Context extends { repos: Repository[]; title: string; associatedView: ViewsWithRepositoryFolders },
@ -1153,7 +1370,7 @@ export async function* pickRepositoryStep<
? [DirectiveQuickPickItem.create(Directive.Cancel)]
: await Promise.all(
context.repos.map(r =>
RepositoryQuickPickItem.create(r, r.id === active?.id, {
createRepositoryQuickPickItem(r, r.id === active?.id, {
branch: true,
buttons: [QuickCommandButtons.RevealInSideBar],
fetched: true,
@ -1163,7 +1380,7 @@ export async function* pickRepositoryStep<
),
onDidClickItemButton: (quickpick, button, { item }) => {
if (button === QuickCommandButtons.RevealInSideBar) {
void GitActions.Repository.reveal(item.path, context.associatedView, {
void GitActions.Repo.reveal(item.path, context.associatedView, {
select: true,
focus: false,
expand: true,
@ -1174,7 +1391,7 @@ export async function* pickRepositoryStep<
onDidPressKey: quickpick => {
if (quickpick.activeItems.length === 0) return;
void GitActions.Repository.reveal(quickpick.activeItems[0].item.path, context.associatedView, {
void GitActions.Repo.reveal(quickpick.activeItems[0].item.path, context.associatedView, {
select: true,
focus: false,
expand: true,
@ -1219,7 +1436,7 @@ export async function* pickRepositoriesStep<
? [DirectiveQuickPickItem.create(Directive.Cancel)]
: await Promise.all(
context.repos.map(repo =>
RepositoryQuickPickItem.create(
createRepositoryQuickPickItem(
repo,
actives.some(r => r.id === repo.id),
{
@ -1233,7 +1450,7 @@ export async function* pickRepositoriesStep<
),
onDidClickItemButton: (quickpick, button, { item }) => {
if (button === QuickCommandButtons.RevealInSideBar) {
void GitActions.Repository.reveal(item.path, context.associatedView, {
void GitActions.Repo.reveal(item.path, context.associatedView, {
select: true,
focus: false,
expand: true,
@ -1244,7 +1461,7 @@ export async function* pickRepositoriesStep<
onDidPressKey: quickpick => {
if (quickpick.activeItems.length === 0) return;
void GitActions.Repository.reveal(quickpick.activeItems[0].item.path, context.associatedView, {
void GitActions.Repo.reveal(quickpick.activeItems[0].item.path, context.associatedView, {
select: true,
focus: false,
expand: true,
@ -1286,7 +1503,7 @@ export function* pickStashStep<
? [DirectiveQuickPickItem.create(Directive.Back, true), DirectiveQuickPickItem.create(Directive.Cancel)]
: [
...map(stash.commits.values(), commit =>
CommitQuickPickItem.create(
createCommitQuickPickItem(
commit,
picked != null &&
(typeof picked === 'string' ? commit.ref === picked : picked.includes(commit.ref)),

+ 12
- 4
src/env/node/git/git.ts Целия файл

@ -1332,12 +1332,20 @@ export class Git {
return this.git<string>({ cwd: repoPath }, 'remote', '-v');
}
remote__add(repoPath: string, name: string, url: string) {
return this.git<string>({ cwd: repoPath }, 'remote', 'add', name, url);
remote__add(repoPath: string, name: string, url: string, options?: { fetch?: boolean }) {
const params = ['remote', 'add'];
if (options?.fetch) {
params.push('-f');
}
return this.git<string>({ cwd: repoPath }, ...params, name, url);
}
remote__prune(repoPath: string, name: string) {
return this.git<string>({ cwd: repoPath }, 'remote', 'prune', name);
}
remote__prune(repoPath: string, remoteName: string) {
return this.git<string>({ cwd: repoPath }, 'remote', 'prune', remoteName);
remote__remove(repoPath: string, name: string) {
return this.git<string>({ cwd: repoPath }, 'remote', 'remove', name);
}
remote__get_url(repoPath: string, remote: string): Promise<string> {

+ 8
- 2
src/env/node/git/localGitProvider.ts Целия файл

@ -847,8 +847,8 @@ export class LocalGitProvider implements GitProvider, Disposable {
}
@log()
async addRemote(repoPath: string, name: string, url: string): Promise<void> {
await this.git.remote__add(repoPath, name, url);
async addRemote(repoPath: string, name: string, url: string, options?: { fetch?: boolean }): Promise<void> {
await this.git.remote__add(repoPath, name, url, options);
this.container.events.fire('git:cache:reset', { repoPath: repoPath, caches: ['remotes'] });
}
@ -859,6 +859,12 @@ export class LocalGitProvider implements GitProvider, Disposable {
}
@log()
async removeRemote(repoPath: string, name: string): Promise<void> {
await this.git.remote__remove(repoPath, name);
this.container.events.fire('git:cache:reset', { repoPath: repoPath, caches: ['remotes'] });
}
@log()
async applyChangesToWorkingFile(uri: GitUri, ref1?: string, ref2?: string) {
const scope = getLogScope();

+ 3
- 2
src/git/gitProvider.ts Целия файл

@ -132,8 +132,9 @@ export interface GitProvider extends Disposable {
// getRootUri(pathOrUri: string | Uri): Uri;
getWorkingUri(repoPath: string, uri: Uri): Promise<Uri | undefined>;
addRemote(repoPath: string, name: string, url: string): Promise<void>;
pruneRemote(repoPath: string, remoteName: string): Promise<void>;
addRemote(repoPath: string, name: string, url: string, options?: { fetch?: boolean }): Promise<void>;
pruneRemote(repoPath: string, name: string): Promise<void>;
removeRemote(repoPath: string, name: string): Promise<void>;
applyChangesToWorkingFile(uri: GitUri, ref1?: string, ref2?: string): Promise<void>;
checkout(
repoPath: string,

+ 15
- 13
src/git/gitProviderService.ts Целия файл

@ -996,35 +996,37 @@ export class GitProviderService implements Disposable {
}
@log()
async getWorkingUri(repoPath: string | Uri, uri: Uri) {
getWorkingUri(repoPath: string | Uri, uri: Uri) {
const { provider, path } = this.getProvider(repoPath);
return provider.getWorkingUri(path, uri);
}
@log()
addRemote(repoPath: string | Uri, name: string, url: string): Promise<void> {
addRemote(repoPath: string | Uri, name: string, url: string, options?: { fetch?: boolean }): Promise<void> {
const { provider, path } = this.getProvider(repoPath);
return provider.addRemote(path, name, url);
return provider.addRemote(path, name, url, options);
}
@log()
pruneRemote(repoPath: string | Uri, remoteName: string): Promise<void> {
pruneRemote(repoPath: string | Uri, name: string): Promise<void> {
const { provider, path } = this.getProvider(repoPath);
return provider.pruneRemote(path, remoteName);
return provider.pruneRemote(path, name);
}
@log()
async applyChangesToWorkingFile(uri: GitUri, ref1?: string, ref2?: string): Promise<void> {
removeRemote(repoPath: string | Uri, name: string): Promise<void> {
const { provider, path } = this.getProvider(repoPath);
return provider.removeRemote(path, name);
}
@log()
applyChangesToWorkingFile(uri: GitUri, ref1?: string, ref2?: string): Promise<void> {
const { provider } = this.getProvider(uri);
return provider.applyChangesToWorkingFile(uri, ref1, ref2);
}
@log()
async checkout(
repoPath: string,
ref: string,
options?: { createBranch?: string } | { path?: string },
): Promise<void> {
checkout(repoPath: string, ref: string, options?: { createBranch?: string } | { path?: string }): Promise<void> {
const { provider, path } = this.getProvider(repoPath);
return provider.checkout(path, ref, options);
}
@ -1039,14 +1041,14 @@ export class GitProviderService implements Disposable {
}
@log<GitProviderService['excludeIgnoredUris']>({ args: { 1: uris => uris.length } })
async excludeIgnoredUris(repoPath: string, uris: Uri[]): Promise<Uri[]> {
excludeIgnoredUris(repoPath: string, uris: Uri[]): Promise<Uri[]> {
const { provider, path } = this.getProvider(repoPath);
return provider.excludeIgnoredUris(path, uris);
}
@gate()
@log()
async fetch(
fetch(
repoPath: string,
options?: { all?: boolean; branch?: GitBranchReference; prune?: boolean; pull?: boolean; remote?: string },
): Promise<void> {

+ 17
- 0
src/git/models/repository.ts Целия файл

@ -459,6 +459,23 @@ export class Repository implements Disposable {
}
@log()
async addRemote(name: string, url: string, options?: { fetch?: boolean }): Promise<GitRemote | undefined> {
await this.container.git.addRemote(this.path, name, url, options);
const [remote] = await this.getRemotes({ filter: r => r.url === url });
return remote;
}
@log()
pruneRemote(name: string): Promise<void> {
return this.container.git.pruneRemote(this.path, name);
}
@log()
removeRemote(name: string): Promise<void> {
return this.container.git.removeRemote(this.path, name);
}
@log()
branch(...args: string[]) {
this.runTerminalCommand('branch', ...args);
}

+ 5
- 2
src/plus/github/githubGitProvider.ts Целия файл

@ -358,10 +358,13 @@ export class GitHubGitProvider implements GitProvider, Disposable {
}
@log()
async addRemote(_repoPath: string, _name: string, _url: string): Promise<void> {}
async addRemote(_repoPath: string, _name: string, _url: string, _options?: { fetch?: boolean }): Promise<void> {}
@log()
async pruneRemote(_repoPath: string, _remoteName: string): Promise<void> {}
async pruneRemote(_repoPath: string, _name: string): Promise<void> {}
@log()
async removeRemote(_repoPath: string, _name: string): Promise<void> {}
@log()
async applyChangesToWorkingFile(_uri: GitUri, _ref1?: string, _ref2?: string): Promise<void> {}

+ 4
- 3
src/quickpicks/commitPicker.ts Целия файл

@ -11,7 +11,8 @@ import { isPromise } from '../system/promise';
import { getQuickPickIgnoreFocusOut } from '../system/utils';
import { CommandQuickPickItem } from './items/common';
import { Directive, DirectiveQuickPickItem } from './items/directive';
import { CommitQuickPickItem } from './items/gitCommands';
import type { CommitQuickPickItem } from './items/gitCommands';
import { createCommitQuickPickItem } from './items/gitCommands';
export namespace CommitPicker {
export async function show(
@ -56,7 +57,7 @@ export namespace CommitPicker {
: [
...(options?.showOtherReferences ?? []),
...map(log.commits.values(), commit =>
CommitQuickPickItem.create(commit, options?.picked === commit.ref, {
createCommitQuickPickItem(commit, options?.picked === commit.ref, {
compact: true,
icon: true,
}),
@ -215,7 +216,7 @@ export namespace StashPicker {
...map(
options?.filter != null ? filter(stash.commits.values(), options.filter) : stash.commits.values(),
commit =>
CommitQuickPickItem.create(commit, options?.picked === commit.ref, {
createCommitQuickPickItem(commit, options?.picked === commit.ref, {
compact: true,
icon: true,
}),

+ 368
- 354
src/quickpicks/items/gitCommands.ts Целия файл

@ -9,6 +9,7 @@ import type { GitCommit } from '../../git/models/commit';
import { isStash } from '../../git/models/commit';
import type { GitContributor } from '../../git/models/contributor';
import { GitReference, GitRevision } from '../../git/models/reference';
import type { GitRemote } from '../../git/models/remote';
import { GitRemoteType } from '../../git/models/remote';
import type { Repository } from '../../git/models/repository';
import type { GitStatus } from '../../git/models/status';
@ -37,105 +38,103 @@ export interface BranchQuickPickItem extends QuickPickItemOfT {
readonly remote: boolean;
}
export namespace BranchQuickPickItem {
export async function create(
branch: GitBranch,
picked?: boolean,
options?: {
alwaysShow?: boolean;
buttons?: QuickInputButton[];
checked?: boolean;
current?: boolean | 'checkmark';
ref?: boolean;
status?: boolean;
type?: boolean | 'remote';
},
): Promise<BranchQuickPickItem> {
let description = '';
if (options?.type === true) {
if (options.current === true && branch.current) {
description = 'current branch';
} else {
description = 'branch';
}
} else if (options?.type === 'remote') {
if (branch.remote) {
description = 'remote branch';
}
} else if (options?.current === true && branch.current) {
export async function createBranchQuickPickItem(
branch: GitBranch,
picked?: boolean,
options?: {
alwaysShow?: boolean;
buttons?: QuickInputButton[];
checked?: boolean;
current?: boolean | 'checkmark';
ref?: boolean;
status?: boolean;
type?: boolean | 'remote';
},
): Promise<BranchQuickPickItem> {
let description = '';
if (options?.type === true) {
if (options.current === true && branch.current) {
description = 'current branch';
} else {
description = 'branch';
}
} else if (options?.type === 'remote') {
if (branch.remote) {
description = 'remote branch';
}
} else if (options?.current === true && branch.current) {
description = 'current branch';
}
if (options?.status && !branch.remote && branch.upstream != null) {
let arrows = GlyphChars.Dash;
if (options?.status && !branch.remote && branch.upstream != null) {
let arrows = GlyphChars.Dash;
if (!branch.upstream.missing) {
const remote = await branch.getRemote();
if (remote != null) {
let left;
let right;
for (const { type } of remote.urls) {
if (type === GitRemoteType.Fetch) {
left = true;
if (!branch.upstream.missing) {
const remote = await branch.getRemote();
if (remote != null) {
let left;
let right;
for (const { type } of remote.urls) {
if (type === GitRemoteType.Fetch) {
left = true;
if (right) break;
} else if (type === GitRemoteType.Push) {
right = true;
if (right) break;
} else if (type === GitRemoteType.Push) {
right = true;
if (left) break;
}
if (left) break;
}
}
if (left && right) {
arrows = GlyphChars.ArrowsRightLeft;
} else if (right) {
arrows = GlyphChars.ArrowRight;
} else if (left) {
arrows = GlyphChars.ArrowLeft;
}
if (left && right) {
arrows = GlyphChars.ArrowsRightLeft;
} else if (right) {
arrows = GlyphChars.ArrowRight;
} else if (left) {
arrows = GlyphChars.ArrowLeft;
}
} else {
arrows = GlyphChars.Warning;
}
const status = `${branch.getTrackingStatus({ suffix: `${GlyphChars.Space} ` })}${arrows}${
GlyphChars.Space
} ${branch.upstream.name}`;
description = `${description ? `${description}${GlyphChars.Space.repeat(2)}${status}` : status}`;
} else {
arrows = GlyphChars.Warning;
}
if (options?.ref) {
if (branch.sha) {
description = description
? `${description} $(git-commit)${GlyphChars.Space}${GitRevision.shorten(branch.sha)}`
: `$(git-commit)${GlyphChars.Space}${GitRevision.shorten(branch.sha)}`;
}
const status = `${branch.getTrackingStatus({ suffix: `${GlyphChars.Space} ` })}${arrows}${GlyphChars.Space} ${
branch.upstream.name
}`;
description = `${description ? `${description}${GlyphChars.Space.repeat(2)}${status}` : status}`;
}
if (branch.date !== undefined) {
description = description
? `${description}${pad(GlyphChars.Dot, 2, 2)}${branch.formattedDate}`
: branch.formattedDate;
}
if (options?.ref) {
if (branch.sha) {
description = description
? `${description} $(git-commit)${GlyphChars.Space}${GitRevision.shorten(branch.sha)}`
:pan> `$(git-commit)${GlyphChars.Space}${GitRevision.shorten(branch.sha)}`;
}
const checked =
options?.checked || (options?.checked == null && options?.current === 'checkmark' && branch.current);
const item: BranchQuickPickItem = {
label: `$(git-branch)${GlyphChars.Space}${branch.starred ? `$(star-full)${GlyphChars.Space}` : ''}${
branch.name
}${checked ? pad('$(check)', 2) : ''}`,
description: description,
alwaysShow: options?.alwaysShow,
buttons: options?.buttons,
picked: picked ?? branch.current,
item: branch,
current: branch.current,
ref: branch.name,
remote: branch.remote,
};
return item;
if (branch.date !== undefined) {
description = description
? `${description}${pad(GlyphChars.Dot, 2, 2)}${branch.formattedDate}`
: branch.formattedDate;
}
}
const checked =
options?.checked || (options?.checked == null && options?.current === 'checkmark' && branch.current);
const item: BranchQuickPickItem = {
label: `$(git-branch)${GlyphChars.Space}${branch.starred ? `$(star-full)${GlyphChars.Space}` : ''}${
branch.name
}${checked ? pad('$(check)', 2) : ''}`,
description: description,
alwaysShow: options?.alwaysShow,
buttons: options?.buttons,
picked: picked ?? branch.current,
item: branch,
current: branch.current,
ref: branch.name,
remote: branch.remote,
};
return item;
}
export class CommitLoadMoreQuickPickItem implements QuickPickItem {
@ -145,98 +144,94 @@ export class CommitLoadMoreQuickPickItem implements QuickPickItem {
export type CommitQuickPickItem<T extends GitCommit = GitCommit> = QuickPickItemOfT<T>;
export namespace CommitQuickPickItem {
export function create<T extends GitCommit = GitCommit>(
commit: T,
picked?: boolean,
options?: { alwaysShow?: boolean; buttons?: QuickInputButton[]; compact?: boolean; icon?: boolean },
) {
if (isStash(commit)) {
const number = commit.number == null ? '' : `${commit.number}: `;
if (options?.compact) {
const item: CommitQuickPickItem<T> = {
label: `${options.icon ? `$(archive)${GlyphChars.Space}` : ''}${number}${commit.summary}`,
description: `${commit.formattedDate}${pad(GlyphChars.Dot, 2, 2)}${commit.formatStats({
compact: true,
})}`,
alwaysShow: options.alwaysShow,
buttons: options.buttons,
picked: picked,
item: commit,
};
return item;
}
const item: CommitQuickPickItem<T> = {
label: `${options?.icon ? `$(archive)${GlyphChars.Space}` : ''}${number}${commit.summary}`,
description: '',
detail: `${GlyphChars.Space.repeat(2)}${commit.formattedDate}${pad(
GlyphChars.Dot,
2,
2,
)}${commit.formatStats({ compact: true })}`,
alwaysShow: options?.alwaysShow,
buttons: options?.buttons,
picked: picked,
item: commit,
};
return item;
}
export function createCommitQuickPickItem<T extends GitCommit = GitCommit>(
commit: T,
picked?: boolean,
options?: { alwaysShow?: boolean; buttons?: QuickInputButton[]; compact?: boolean; icon?: boolean },
) {
if (isStash(commit)) {
const number = commit.number == null ? '' : `${commit.number}: `;
if (options?.compact) {
const item: CommitQuickPickItem<T> = {
label: `${options.icon ? `$(git-commit)${GlyphChars.Space}` : ''}${commit.summary}`,
description: `${commit.author.name}, ${commit.formattedDate}${pad('$(git-commit)', 2, 1)}${
commit.shortSha
}${pad(GlyphChars.Dot, 2, 2)}${commit.formatStats({ compact: true })}`,
label: `${options.icon ? `$(archive)${GlyphChars.Space}` : ''}${number}${commit.summary}`,
description: `${commit.formattedDate}${pad(GlyphChars.Dot, 2, 2)}${commit.formatStats({
compact: true,
})}`,
alwaysShow: options.alwaysShow,
buttons: options.buttons,
picked: picked,
item: commit,
};
return item;
}
const item: CommitQuickPickItem<T> = {
label: `${options?.icon ? `$(git-commit)${GlyphChars.Space}` : ''}${commit.summary}`,
label: `${options?.icon ? `$(archive)${GlyphChars.Space}` : ''}${number}${commit.summary}`,
description: '',
detail: `${GlyphChars.Space.repeat(2)}${commit.author.name}, ${commit.formattedDate}${pad(
'$(git-commit)',
detail: `${GlyphChars.Space.repeat(2)}${commit.formattedDate}${pad(
GlyphChars.Dot,
2,
1,
)}${commit.shortSha}${pad(GlyphChars.Dot, 2, 2)}${commit.formatStats({
compact: true,
})}`,
2,
)}${commit.formatStats({ compact: true })}`,
alwaysShow: options?.alwaysShow,
buttons: options?.buttons,
picked: picked,
item: commit,
};
return item;
}
}
export type ContributorQuickPickItem = QuickPickItemOfT<GitContributor>;
export namespace ContributorQuickPickItem {
export function create(
contributor: GitContributor,
picked?: boolean,
options?: { alwaysShow?: boolean; buttons?: QuickInputButton[] },
): ContributorQuickPickItem {
const item: ContributorQuickPickItem = {
label: contributor.label,
description: contributor.email,
alwaysShow: options?.alwaysShow,
buttons: options?.buttons,
if (options?.compact) {
const item: CommitQuickPickItem<T> = {
label: `${options.icon ? `$(git-commit)${GlyphChars.Space}` : ''}${commit.summary}`,
description: `${commit.author.name}, ${commit.formattedDate}${pad('$(git-commit)', 2, 1)}${
commit.shortSha
}${pad(GlyphChars.Dot, 2, 2)}${commit.formatStats({ compact: true })}`,
alwaysShow: options.alwaysShow,
buttons: options.buttons,
picked: picked,
item: contributor,
item: commit,
};
return item;
}
const item: CommitQuickPickItem<T> = {
label: `${options?.icon ? `$(git-commit)${GlyphChars.Space}` : ''}${commit.summary}`,
description: '',
detail: `${GlyphChars.Space.repeat(2)}${commit.author.name}, ${commit.formattedDate}${pad(
'$(git-commit)',
2,
1,
)}${commit.shortSha}${pad(GlyphChars.Dot, 2, 2)}${commit.formatStats({
compact: true,
})}`,
alwaysShow: options?.alwaysShow,
buttons: options?.buttons,
picked: picked,
item: commit,
};
return item;
}
export type ContributorQuickPickItem = QuickPickItemOfT<GitContributor>;
export function createContributorQuickPickItem(
contributor: GitContributor,
picked?: boolean,
options?: { alwaysShow?: boolean; buttons?: QuickInputButton[] },
): ContributorQuickPickItem {
const item: ContributorQuickPickItem = {
label: contributor.label,
description: contributor.email,
alwaysShow: options?.alwaysShow,
buttons: options?.buttons,
picked: picked,
item: contributor,
};
return item;
}
export interface RefQuickPickItem extends QuickPickItemOfT<GitReference> {
@ -245,66 +240,52 @@ export interface RefQuickPickItem extends QuickPickItemOfT {
readonly remote: boolean;
}
export namespace RefQuickPickItem {
export function create(
ref: string | GitReference,
repoPath: string,
picked?: boolean,
options?: { alwaysShow?: boolean; buttons?: QuickInputButton[]; icon?: boolean; ref?: boolean },
): RefQuickPickItem {
if (ref === '') {
return {
label: `${options?.icon ? `$(file-directory)${GlyphChars.Space}` : ''}Working Tree`,
description: '',
alwaysShow: options?.alwaysShow,
buttons: options?.buttons,
picked: picked,
item: GitReference.create(ref, repoPath, { refType: 'revision', name: 'Working Tree' }),
current: false,
ref: ref,
remote: false,
};
}
if (ref === 'HEAD') {
return {
label: `${options?.icon ? `$(git-branch)${GlyphChars.Space}` : ''}HEAD`,
description: '',
alwaysShow: options?.alwaysShow,
buttons: options?.buttons,
picked: picked,
item: GitReference.create(ref, repoPath, { refType: 'revision', name: 'HEAD' }),
current: false,
ref: ref,
remote: false,
};
}
export function createRefQuickPickItem(
ref: string | GitReference,
repoPath: string,
picked?: boolean,
options?: { alwaysShow?: boolean; buttons?: QuickInputButton[]; icon?: boolean; ref?: boolean },
): RefQuickPickItem {
if (ref === '') {
return {
label: `${options?.icon ? `$(file-directory)${GlyphChars.Space}` : ''}Working Tree`,
description: '',
alwaysShow: options?.alwaysShow,
buttons: options?.buttons,
picked: picked,
item: GitReference.create(ref, repoPath, { refType: 'revision', name: 'Working Tree' }),
current: false,
ref: ref,
remote: false,
};
}
let gitRef;
if (typeof ref === 'string') {
gitRef = GitReference.create(ref, repoPath);
} else {
gitRef = ref;
ref = gitRef.ref;
}
if (ref === 'HEAD') {
return {
label: `${options?.icon ? `$(git-branch)${GlyphChars.Space}` : ''}HEAD`,
description: '',
alwaysShow: options?.alwaysShow,
buttons: options?.buttons,
picked: picked,
item: GitReference.create(ref, repoPath, { refType: 'revision', name: 'HEAD' }),
current: false,
ref: ref,
remote: false,
};
}
if (GitRevision.isRange(ref)) {
return {
label: `Range ${gitRef.name}`,
description: '',
alwaysShow: options?.alwaysShow,
buttons: options?.buttons,
picked: picked,
item: gitRef,
current: false,
ref: ref,
remote: false,
};
}
let gitRef;
if (typeof ref === 'string') {
gitRef = GitReference.create(ref, repoPath);
} else {
gitRef = ref;
ref = gitRef.ref;
}
const item: RefQuickPickItem = {
label: `Commit ${gitRef.name}`,
description: options?.ref ? `$(git-commit)${GlyphChars.Space}${ref}` : '',
if (GitRevision.isRange(ref)) {
return {
label: `Range ${gitRef.name}`,
description: '',
alwaysShow: options?.alwaysShow,
buttons: options?.buttons,
picked: picked,
@ -313,76 +294,115 @@ export namespace RefQuickPickItem {
ref: ref,
remote: false,
};
}
return item;
const item: RefQuickPickItem = {
label: `Commit ${gitRef.name}`,
description: options?.ref ? `$(git-commit)${GlyphChars.Space}${ref}` : '',
alwaysShow: options?.alwaysShow,
buttons: options?.buttons,
picked: picked,
item: gitRef,
current: false,
ref: ref,
remote: false,
};
return item;
}
export type RemoteQuickPickItem = QuickPickItemOfT<GitRemote>;
export function createRemoteQuickPickItem(
remote: GitRemote,
picked?: boolean,
options?: {
alwaysShow?: boolean;
buttons?: QuickInputButton[];
checked?: boolean;
type?: boolean;
},
) {
let description = '';
if (options?.type) {
description = 'remote';
}
const item: RemoteQuickPickItem = {
label: `$(cloud)${GlyphChars.Space}${remote.name}${options?.checked ? pad('$(check)', 2) : ''}`,
description: description,
alwaysShow: options?.alwaysShow,
buttons: options?.buttons,
picked: picked,
item: remote,
};
return item;
}
export interface RepositoryQuickPickItem extends QuickPickItemOfT<Repository> {
readonly repoPath: string;
}
export namespace RepositoryQuickPickItem {
export async function create(
repository: Repository,
picked?: boolean,
options?: {
alwaysShow?: boolean;
branch?: boolean;
buttons?: QuickInputButton[];
fetched?: boolean;
status?: boolean;
},
) {
let repoStatus;
if (options?.branch || options?.status) {
repoStatus = await repository.getStatus();
}
let description = '';
if (options?.branch && repoStatus != null) {
description = repoStatus.branch;
}
export async function createRepositoryQuickPickItem(
repository: Repository,
picked?: boolean,
options?: {
alwaysShow?: boolean;
branch?: boolean;
buttons?: QuickInputButton[];
fetched?: boolean;
status?: boolean;
},
) {
let repoStatus;
if (options?.branch || options?.status) {
repoStatus = await repository.getStatus();
}
if (options?.status && repoStatus != null) {
let workingStatus = '';
if (repoStatus.files.length !== 0) {
workingStatus = repoStatus.getFormattedDiffStatus({
compact: true,
prefix: pad(GlyphChars.Dot, 2, 2),
});
}
let description = '';
if (options?.branch && repoStatus != null) {
description = repoStatus.branch;
}
const upstreamStatus = repoStatus.getUpstreamStatus({
prefix: description ? `${GlyphChars.Space} ` : '',
if (options?.status && repoStatus != null) {
let workingStatus = '';
if (repoStatus.files.length !== 0) {
workingStatus = repoStatus.getFormattedDiffStatus({
compact: true,
prefix: pad(GlyphChars.Dot, 2, 2),
});
const status = `${upstreamStatus}${workingStatus}`;
if (status) {
description = `${description ? `${description}${status}` : status}`;
}
}
if (options?.fetched) {
const lastFetched = await repository.getLastFetched();
if (lastFetched !== 0) {
const fetched = `Last fetched ${fromNow(new Date(lastFetched))}`;
description = `${description ? `${description}${pad(GlyphChars.Dot, 2, 2)}${fetched}` : fetched}`;
}
}
const upstreamStatus = repoStatus.getUpstreamStatus({
prefix: description ? `${GlyphChars.Space} ` : '',
});
const item: RepositoryQuickPickItem = {
label: repository.formattedName,
description: description,
alwaysShow: options?.alwaysShow,
buttons: options?.buttons,
picked: picked,
item: repository,
repoPath: repository.path,
};
const status = `${upstreamStatus}${workingStatus}`;
if (status) {
description = `${description ? `${description}${status}` : status}`;
}
}
return item;
if (options?.fetched) {
const lastFetched = await repository.getLastFetched();
if (lastFetched !== 0) {
const fetched = `Last fetched ${fromNow(new Date(lastFetched))}`;
description = `${description ? `${description}${pad(GlyphChars.Dot, 2, 2)}${fetched}` : fetched}`;
}
}
const item: RepositoryQuickPickItem = {
label: repository.formattedName,
description: description,
alwaysShow: options?.alwaysShow,
buttons: options?.buttons,
picked: picked,
item: repository,
repoPath: repository.path,
};
return item;
}
export interface TagQuickPickItem extends QuickPickItemOfT<GitTag> {
@ -391,51 +411,47 @@ export interface TagQuickPickItem extends QuickPickItemOfT {
readonly remote: boolean;
}
export namespace TagQuickPickItem {
export function create(
tag: GitTag,
picked?: boolean,
options?: {
alwaysShow?: boolean;
buttons?: QuickInputButton[];
checked?: boolean;
message?: boolean;
ref?: boolean;
type?: boolean;
},
) {
let description = '';
if (options?.type) {
description = 'tag';
}
if (options?.ref) {
description = `${description}${pad('$(git-commit)', description ? 2 : 0, 1)}${GitRevision.shorten(
tag.sha,
)}`;
description = `${description ? `${description}${pad(GlyphChars.Dot, 2, 2)}` : ''}${tag.formattedDate}`;
}
export function createTagQuickPickItem(
tag: GitTag,
picked?: boolean,
options?: {
alwaysShow?: boolean;
buttons?: QuickInputButton[];
checked?: boolean;
message?: boolean;
ref?: boolean;
type?: boolean;
},
) {
let description = '';
if (options?.type) {
description = 'tag';
}
if (options?.message) {
const message = emojify(tag.message);
description = description ? `${description}${pad(GlyphChars.Dot, 2, 2)}${message}` : message;
}
if (options?.ref) {
description = `${description}${pad('$(git-commit)', description ? 2 : 0, 1)}${GitRevision.shorten(tag.sha)}`;
const item: TagQuickPickItem = {
label: `$(tag)${GlyphChars.Space}${tag.name}${options?.checked ? pad('$(check)', 2) : ''}`,
description: description,
alwaysShow: options?.alwaysShow,
buttons: options?.buttons,
picked: picked,
item: tag,
current: false,
ref: tag.name,
remote: false,
};
description = `${description ? `${description}${pad(GlyphChars.Dot, 2, 2)}` : ''}${tag.formattedDate}`;
}
return item;
if (options?.message) {
const message = emojify(tag.message);
description = description ? `${description}${pad(GlyphChars.Dot, 2, 2)}${message}` : message;
}
const item: TagQuickPickItem = {
label: `$(tag)${GlyphChars.Space}${tag.name}${options?.checked ? pad('$(check)', 2) : ''}`,
description: description,
alwaysShow: options?.alwaysShow,
buttons: options?.buttons,
picked: picked,
item: tag,
current: false,
ref: tag.name,
remote: false,
};
return item;
}
export interface WorktreeQuickPickItem extends QuickPickItemOfT<GitWorktree> {
@ -443,60 +459,58 @@ export interface WorktreeQuickPickItem extends QuickPickItemOfT {
readonly hasChanges: boolean | undefined;
}
export namespace WorktreeQuickPickItem {
export function create(
worktree: GitWorktree,
picked?: boolean,
options?: {
alwaysShow?: boolean;
buttons?: QuickInputButton[];
checked?: boolean;
message?: boolean;
path?: boolean;
type?: boolean;
status?: GitStatus;
},
) {
let description = '';
if (options?.type) {
description = 'worktree';
}
if (options?.status != null) {
description += options.status.hasChanges
? pad(`Uncommited changes (${options.status.getFormattedDiffStatus()})`, description ? 2 : 0, 0)
: pad('No changes', description ? 2 : 0, 0);
}
let icon;
let label;
switch (worktree.type) {
case 'bare':
label = '(bare)';
icon = '$(folder)';
break;
case 'branch':
label = worktree.branch!;
icon = '$(git-branch)';
break;
case 'detached':
label = GitRevision.shorten(worktree.sha);
icon = '$(git-commit)';
break;
}
export function createWorktreeQuickPickItem(
worktree: GitWorktree,
picked?: boolean,
options?: {
alwaysShow?: boolean;
buttons?: QuickInputButton[];
checked?: boolean;
message?: boolean;
path?: boolean;
type?: boolean;
status?: GitStatus;
},
) {
let description = '';
if (options?.type) {
description = 'worktree';
}
const item: WorktreeQuickPickItem = {
label: `${icon}${GlyphChars.Space}${label}${options?.checked ? pad('$(check)', 2) : ''}`,
description: description,
detail: options?.path ? `In $(folder) ${worktree.friendlyPath}` : undefined,
alwaysShow: options?.alwaysShow,
buttons: options?.buttons,
picked: picked,
item: worktree,
opened: worktree.opened,
hasChanges: options?.status?.hasChanges,
};
if (options?.status != null) {
description += options.status.hasChanges
? pad(`Uncommited changes (${options.status.getFormattedDiffStatus()})`, description ? 2 : 0, 0)
: pad('No changes', description ? 2 : 0, 0);
}
return item;
let icon;
let label;
switch (worktree.type) {
case 'bare':
label = '(bare)';
icon = '$(folder)';
break;
case 'branch':
label = worktree.branch!;
icon = '$(git-branch)';
break;
case 'detached':
label = GitRevision.shorten(worktree.sha);
icon = '$(git-commit)';
break;
}
const item: WorktreeQuickPickItem = {
label: `${icon}${GlyphChars.Space}${label}${options?.checked ? pad('$(check)', 2) : ''}`,
description: description,
detail: options?.path ? `In $(folder) ${worktree.friendlyPath}` : undefined,
alwaysShow: options?.alwaysShow,
buttons: options?.buttons,
picked: picked,
item: worktree,
opened: worktree.opened,
hasChanges: options?.status?.hasChanges,
};
return item;
}

+ 4
- 4
src/quickpicks/referencePicker.ts Целия файл

@ -9,8 +9,8 @@ import { GitReference } from '../git/models/reference';
import type { GitTag, TagSortOptions } from '../git/models/tag';
import type { KeyboardScope, Keys } from '../keyboard';
import { getQuickPickIgnoreFocusOut } from '../system/utils';
import type { BranchQuickPickItem, TagQuickPickItem } from './items/gitCommands';
import { RefQuickPickItem } from './items/gitCommands';
import type { BranchQuickPickItem, RefQuickPickItem, TagQuickPickItem } from './items/gitCommands';
import { createRefQuickPickItem } from './items/gitCommands';
export type ReferencesQuickPickItem = BranchQuickPickItem | TagQuickPickItem | RefQuickPickItem;
@ -190,11 +190,11 @@ export namespace ReferencePicker {
}
if (include & ReferencesQuickPickIncludes.HEAD) {
items.splice(0, 0, RefQuickPickItem.create('HEAD', repoPath, undefined, { icon: true }));
items.splice(0, 0, createRefQuickPickItem('HEAD', repoPath, undefined, { icon: true }));
}
if (include & ReferencesQuickPickIncludes.WorkingTree) {
items.splice(0, 0, RefQuickPickItem.create('', repoPath, undefined, { icon: true }));
items.splice(0, 0, createRefQuickPickItem('', repoPath, undefined, { icon: true }));
}
return items;

+ 3
- 2
src/quickpicks/repositoryPicker.ts Целия файл

@ -5,7 +5,8 @@ import type { Repository } from '../git/models/repository';
import { map } from '../system/iterable';
import { getQuickPickIgnoreFocusOut } from '../system/utils';
import { CommandQuickPickItem } from './items/common';
import { RepositoryQuickPickItem } from './items/gitCommands';
import type { RepositoryQuickPickItem } from './items/gitCommands';
import { createRepositoryQuickPickItem } from './items/gitCommands';
export namespace RepositoryPicker {
export async function getBestRepositoryOrShow(
@ -50,7 +51,7 @@ export namespace RepositoryPicker {
): Promise<RepositoryQuickPickItem | undefined> {
const items = await Promise.all<Promise<RepositoryQuickPickItem>>([
...map(repositories ?? Container.instance.git.openRepositories, r =>
RepositoryQuickPickItem.create(r, undefined, { branch: true, status: true }),
createRepositoryQuickPickItem(r, undefined, { branch: true, status: true }),
),
]);

+ 9
- 10
src/views/viewCommands.ts Целия файл

@ -26,7 +26,6 @@ import {
import { debug } from '../system/decorators/log';
import { sequentialize } from '../system/function';
import { OpenWorkspaceLocation } from '../system/utils';
import { runGitCommandInTerminal } from '../terminal';
import type { BranchesNode } from './nodes/branchesNode';
import { BranchNode } from './nodes/branchNode';
import { BranchTrackingStatusNode } from './nodes/branchTrackingStatusNode';
@ -219,6 +218,7 @@ export class ViewCommands {
registerViewCommand('gitlens.views.switchToTag', this.switchTo, this);
registerViewCommand('gitlens.views.addRemote', this.addRemote, this);
registerViewCommand('gitlens.views.pruneRemote', this.pruneRemote, this);
registerViewCommand('gitlens.views.removeRemote', this.removeRemote, this);
registerViewCommand('gitlens.views.stageDirectory', this.stageDirectory, this);
registerViewCommand('gitlens.views.stageFile', this.stageFile, this);
@ -271,8 +271,6 @@ export class ViewCommands {
registerViewCommand('gitlens.views.revert', this.revert, this);
registerViewCommand('gitlens.views.undoCommit', this.undoCommit, this);
registerViewCommand('gitlens.views.terminalRemoveRemote', this.terminalRemoveRemote, this);
registerViewCommand('gitlens.views.createPullRequest', this.createPullRequest, this);
registerViewCommand('gitlens.views.openPullRequest', this.openPullRequest, this);
@ -570,13 +568,20 @@ export class ViewCommands {
}
@debug()
private async pruneRemote(node: RemoteNode) {
private pruneRemote(node: RemoteNode) {
if (!(node instanceof RemoteNode)) return Promise.resolve();
return GitActions.Remote.prune(node.repo, node.remote.name);
}
@debug()
private async removeRemote(node: RemoteNode) {
if (!(node instanceof RemoteNode)) return Promise.resolve();
return GitActions.Remote.remove(node.repo, node.remote.name);
}
@debug()
private publishBranch(node: BranchNode | BranchTrackingStatusNode) {
if (node instanceof BranchNode || node instanceof BranchTrackingStatusNode) {
return GitActions.push(node.repoPath, undefined, node.branch);
@ -1247,10 +1252,4 @@ export class ViewCommands {
return GitActions.Commit.openFilesAtRevision(node.commit);
}
private terminalRemoveRemote(node: RemoteNode) {
if (!(node instanceof RemoteNode)) return;
runGitCommandInTerminal('remote', `remove ${node.remote.name}`, node.remote.repoPath);
}
}

Зареждане…
Отказ
Запис