Git - Add command to delete remote tags (#170415)

Add command to delete remote tags
This commit is contained in:
Ladislau Szomoru 2023-01-03 15:08:48 +01:00 committed by GitHub
parent 1d18dfc209
commit 1542e90126
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 114 additions and 7 deletions

View file

@ -420,6 +420,12 @@
"category": "Git",
"enablement": "!operationInProgress"
},
{
"command": "git.deleteRemoteTag",
"title": "%command.deleteRemoteTag%",
"category": "Git",
"enablement": "!operationInProgress"
},
{
"command": "git.fetch",
"title": "%command.fetch%",
@ -986,6 +992,10 @@
"command": "git.deleteTag",
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
},
{
"command": "git.deleteRemoteTag",
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
},
{
"command": "git.fetch",
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
@ -1929,6 +1939,10 @@
{
"command": "git.deleteTag",
"group": "tags@2"
},
{
"command": "git.deleteRemoteTag",
"group": "tags@3"
}
]
},

View file

@ -61,6 +61,7 @@
"command.rebase": "Rebase Branch...",
"command.createTag": "Create Tag",
"command.deleteTag": "Delete Tag",
"command.deleteRemoteTag": "Delete Remote Tag",
"command.fetch": "Fetch",
"command.fetchPrune": "Fetch (Prune)",
"command.fetchAll": "Fetch From All Remotes",

View file

@ -312,7 +312,7 @@ function sanitizeRemoteName(name: string) {
}
class TagItem implements QuickPickItem {
get label(): string { return this.ref.name ?? ''; }
get label(): string { return `$(tag) ${this.ref.name ?? ''}`; }
get description(): string { return this.ref.commit?.substr(0, 8) ?? ''; }
constructor(readonly ref: Ref) { }
}
@ -2380,6 +2380,42 @@ export class CommandCenter {
await repository.deleteTag(choice.label);
}
@command('git.deleteRemoteTag', { repository: true })
async deleteRemoteTag(repository: Repository): Promise<void> {
const remotePicks = repository.remotes
.filter(r => r.pushUrl !== undefined)
.map(r => new RemoteItem(repository, r));
if (remotePicks.length === 0) {
window.showErrorMessage(l10n.t("Your repository has no remotes configured to push to."));
return;
}
let remoteName = remotePicks[0].remoteName;
if (remotePicks.length > 1) {
const remotePickPlaceholder = l10n.t('Select a remote to delete a tag from');
const remotePick = await window.showQuickPick(remotePicks, { placeHolder: remotePickPlaceholder });
if (!remotePick) {
return;
}
remoteName = remotePick.remoteName;
}
const remoteTagPicks = async (): Promise<TagItem[] | QuickPickItem[]> => {
const remoteTags = await repository.getRemoteRefs(remoteName, { tags: true });
return remoteTags.length === 0 ? [{ label: l10n.t('Remote "{0}" has no tags.', remoteName) }] : remoteTags.map(ref => new TagItem(ref));
};
const tagPickPlaceholder = l10n.t('Select a tag to delete');
const remoteTagPick = await window.showQuickPick<TagItem | QuickPickItem>(remoteTagPicks(), { placeHolder: tagPickPlaceholder });
if (remoteTagPick && remoteTagPick instanceof TagItem && remoteTagPick.ref.name) {
await repository.deleteRemoteTag(remoteName, remoteTagPick.ref.name);
}
}
@command('git.fetch', { repository: true })
async fetch(repository: Repository): Promise<void> {
if (repository.remotes.length === 0) {

View file

@ -1635,6 +1635,11 @@ export class Repository {
await this.exec(args);
}
async deleteRemoteTag(remoteName: string, tagName: string): Promise<void> {
const args = ['push', '--delete', remoteName, tagName];
await this.exec(args);
}
async clean(paths: string[]): Promise<void> {
const pathsByGroup = groupBy(paths.map(sanitizePath), p => path.dirname(p));
const groups = Object.keys(pathsByGroup).map(k => pathsByGroup[k]);
@ -2207,6 +2212,43 @@ export class Repository {
.filter(ref => !!ref) as Ref[];
}
async getRemoteRefs(remote: string, opts?: { heads?: boolean; tags?: boolean; cancellationToken?: CancellationToken }): Promise<Ref[]> {
if (opts?.cancellationToken && opts?.cancellationToken.isCancellationRequested) {
throw new CancellationError();
}
const args = ['ls-remote'];
if (opts?.heads) {
args.push('--heads');
}
if (opts?.tags) {
args.push('--tags');
}
args.push(remote);
const result = await this.exec(args, { cancellationToken: opts?.cancellationToken });
const fn = (line: string): Ref | null => {
let match: RegExpExecArray | null;
if (match = /^([0-9a-f]{40})\trefs\/heads\/([^ ]+)$/.exec(line)) {
return { name: match[1], commit: match[2], type: RefType.Head };
} else if (match = /^([0-9a-f]{40})\trefs\/tags\/([^ ]+)$/.exec(line)) {
return { name: match[2], commit: match[1], type: RefType.Tag };
}
return null;
};
return result.stdout.split('\n')
.filter(line => !!line)
.map(fn)
.filter(ref => !!ref) as Ref[];
}
async getStashes(): Promise<Stash[]> {
const result = await this.exec(['stash', 'list']);
const regex = /^stash@{(\d+)}:(.+)$/;

View file

@ -21,6 +21,7 @@ export const enum OperationKind {
Config = 'Config',
DeleteBranch = 'DeleteBranch',
DeleteRef = 'DeleteRef',
DeleteRemoteTag = 'DeleteRemoteTag',
DeleteTag = 'DeleteTag',
Diff = 'Diff',
Fetch = 'Fetch',
@ -30,6 +31,7 @@ export const enum OperationKind {
GetBranches = 'GetBranches',
GetCommitTemplate = 'GetCommitTemplate',
GetObjectDetails = 'GetObjectDetails',
GetRemoteRefs = 'GetRemoteRefs',
HashObject = 'HashObject',
Ignore = 'Ignore',
Log = 'Log',
@ -62,12 +64,12 @@ export const enum OperationKind {
export type Operation = AddOperation | ApplyOperation | BlameOperation | BranchOperation | CheckIgnoreOperation | CherryPickOperation |
CheckoutOperation | CheckoutTrackingOperation | CleanOperation | CommitOperation | ConfigOperation | DeleteBranchOperation |
DeleteRefOperation | DeleteTagOperation | DiffOperation | FetchOperation | FindTrackingBranchesOperation | GetBranchOperation |
GetBranchesOperation | GetCommitTemplateOperation | GetObjectDetailsOperation | HashObjectOperation | IgnoreOperation | LogOperation |
LogFileOperation | MergeOperation | MergeAbortOperation | MergeBaseOperation | MoveOperation | PostCommitCommandOperation |
PullOperation | PushOperation | RemoteOperation | RenameBranchOperation | RemoveOperation | ResetOperation | RebaseOperation |
RebaseAbortOperation | RebaseContinueOperation | RevertFilesOperation | SetBranchUpstreamOperation | ShowOperation | StageOperation |
StatusOperation | StashOperation | SubmoduleUpdateOperation | SyncOperation | TagOperation;
DeleteRefOperation | DeleteRemoteTagOperation | DeleteTagOperation | DiffOperation | FetchOperation | FindTrackingBranchesOperation |
GetBranchOperation | GetBranchesOperation | GetCommitTemplateOperation | GetObjectDetailsOperation | GetRemoteRefsOperation | HashObjectOperation |
IgnoreOperation | LogOperation | LogFileOperation | MergeOperation | MergeAbortOperation | MergeBaseOperation | MoveOperation |
PostCommitCommandOperation | PullOperation | PushOperation | RemoteOperation | RenameBranchOperation | RemoveOperation | ResetOperation |
RebaseOperation | RebaseAbortOperation | RebaseContinueOperation | RevertFilesOperation | SetBranchUpstreamOperation | ShowOperation |
StageOperation | StatusOperation | StashOperation | SubmoduleUpdateOperation | SyncOperation | TagOperation;
type BaseOperation = { kind: OperationKind; blocking: boolean; readOnly: boolean; remote: boolean; retry: boolean; showProgress: boolean };
export type AddOperation = BaseOperation & { kind: OperationKind.Add };
@ -83,6 +85,7 @@ export type CommitOperation = BaseOperation & { kind: OperationKind.Commit };
export type ConfigOperation = BaseOperation & { kind: OperationKind.Config };
export type DeleteBranchOperation = BaseOperation & { kind: OperationKind.DeleteBranch };
export type DeleteRefOperation = BaseOperation & { kind: OperationKind.DeleteRef };
export type DeleteRemoteTagOperation = BaseOperation & { kind: OperationKind.DeleteRemoteTag };
export type DeleteTagOperation = BaseOperation & { kind: OperationKind.DeleteTag };
export type DiffOperation = BaseOperation & { kind: OperationKind.Diff };
export type FetchOperation = BaseOperation & { kind: OperationKind.Fetch };
@ -91,6 +94,7 @@ export type GetBranchOperation = BaseOperation & { kind: OperationKind.GetBranch
export type GetBranchesOperation = BaseOperation & { kind: OperationKind.GetBranches };
export type GetCommitTemplateOperation = BaseOperation & { kind: OperationKind.GetCommitTemplate };
export type GetObjectDetailsOperation = BaseOperation & { kind: OperationKind.GetObjectDetails };
export type GetRemoteRefsOperation = BaseOperation & { kind: OperationKind.GetRemoteRefs };
export type HashObjectOperation = BaseOperation & { kind: OperationKind.HashObject };
export type IgnoreOperation = BaseOperation & { kind: OperationKind.Ignore };
export type LogOperation = BaseOperation & { kind: OperationKind.Log };
@ -133,6 +137,7 @@ export const Operation = {
Config: { kind: OperationKind.Config, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as ConfigOperation,
DeleteBranch: { kind: OperationKind.DeleteBranch, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as DeleteBranchOperation,
DeleteRef: { kind: OperationKind.DeleteRef, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as DeleteRefOperation,
DeleteRemoteTag: { kind: OperationKind.DeleteRemoteTag, blocking: false, readOnly: false, remote: true, retry: false, showProgress: true } as DeleteRemoteTagOperation,
DeleteTag: { kind: OperationKind.DeleteTag, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as DeleteTagOperation,
Diff: { kind: OperationKind.Diff, blocking: false, readOnly: true, remote: false, retry: false, showProgress: true } as DiffOperation,
Fetch: (showProgress: boolean) => ({ kind: OperationKind.Fetch, blocking: false, readOnly: false, remote: true, retry: true, showProgress } as FetchOperation),
@ -141,6 +146,7 @@ export const Operation = {
GetBranches: { kind: OperationKind.GetBranches, blocking: false, readOnly: true, remote: false, retry: false, showProgress: true } as GetBranchesOperation,
GetCommitTemplate: { kind: OperationKind.GetCommitTemplate, blocking: false, readOnly: true, remote: false, retry: false, showProgress: true } as GetCommitTemplateOperation,
GetObjectDetails: { kind: OperationKind.GetObjectDetails, blocking: false, readOnly: true, remote: false, retry: false, showProgress: false } as GetObjectDetailsOperation,
GetRemoteRefs: { kind: OperationKind.GetRemoteRefs, blocking: false, readOnly: true, remote: true, retry: false, showProgress: false } as GetRemoteRefsOperation,
HashObject: { kind: OperationKind.HashObject, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as HashObjectOperation,
Ignore: { kind: OperationKind.Ignore, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as IgnoreOperation,
Log: { kind: OperationKind.Log, blocking: false, readOnly: true, remote: false, retry: false, showProgress: true } as LogOperation,

View file

@ -1378,6 +1378,10 @@ export class Repository implements Disposable {
return await this.run(Operation.GetBranches, () => this.repository.getBranches(query));
}
async getRemoteRefs(remote: string, opts?: { heads?: boolean; tags?: boolean }): Promise<Ref[]> {
return await this.run(Operation.GetRemoteRefs, () => this.repository.getRemoteRefs(remote, opts));
}
async setBranchUpstream(name: string, upstream: string): Promise<void> {
await this.run(Operation.SetBranchUpstream, () => this.repository.setBranchUpstream(name, upstream));
}
@ -1402,6 +1406,10 @@ export class Repository implements Disposable {
await this.run(Operation.DeleteTag, () => this.repository.deleteTag(name));
}
async deleteRemoteTag(remoteName: string, tagName: string): Promise<void> {
await this.run(Operation.DeleteRemoteTag, () => this.repository.deleteRemoteTag(remoteName, tagName));
}
async checkout(treeish: string, opts?: { detached?: boolean; pullBeforeCheckout?: boolean }): Promise<void> {
const refLabel = opts?.detached ? treeish.substring(0, 8) : treeish;