Skip to content

Commit 1d9c808

Browse files
committed
mhutchie#390 Display a new dialog when adding a tag with a name that already exists, allowing the user to force-add the tag.
1 parent a5bc795 commit 1d9c808

File tree

8 files changed

+152
-73
lines changed

8 files changed

+152
-73
lines changed

src/config.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ import {
2424
ReferenceLabelsConfig,
2525
RepoDropdownOrder,
2626
SquashMessageFormat,
27-
TabIconColourTheme
27+
TabIconColourTheme,
28+
TagType
2829
} from './types';
2930

3031
const VIEW_COLUMN_MAPPING: { [column: string]: vscode.ViewColumn } = {
@@ -176,7 +177,7 @@ class Config {
176177
return {
177178
addTag: {
178179
pushToRemote: !!this.config.get('dialog.addTag.pushToRemote', false),
179-
type: this.config.get<string>('dialog.addTag.type', 'Annotated') === 'Lightweight' ? 'lightweight' : 'annotated'
180+
type: this.config.get<string>('dialog.addTag.type', 'Annotated') === 'Lightweight' ? TagType.Lightweight : TagType.Annotated
180181
},
181182
applyStash: {
182183
reinstateIndex: !!this.config.get('dialog.applyStash.reinstateIndex', false)

src/dataSource.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import * as vscode from 'vscode';
66
import { AskpassEnvironment, AskpassManager } from './askpass/askpassManager';
77
import { getConfig } from './config';
88
import { Logger } from './logger';
9-
import { CommitOrdering, DateType, DeepWriteable, ErrorInfo, GitCommit, GitCommitDetails, GitCommitStash, GitConfigLocation, GitFileChange, GitFileStatus, GitPushBranchMode, GitRepoConfig, GitRepoConfigBranches, GitResetMode, GitSignatureStatus, GitStash, MergeActionOn, RebaseActionOn, SquashMessageFormat, Writeable } from './types';
9+
import { CommitOrdering, DateType, DeepWriteable, ErrorInfo, GitCommit, GitCommitDetails, GitCommitStash, GitConfigLocation, GitFileChange, GitFileStatus, GitPushBranchMode, GitRepoConfig, GitRepoConfigBranches, GitResetMode, GitSignatureStatus, GitStash, MergeActionOn, RebaseActionOn, SquashMessageFormat, TagType, Writeable } from './types';
1010
import { GitExecutable, UNABLE_TO_FIND_GIT_MSG, UNCOMMITTED, abbrevCommit, constructIncompatibleGitVersionMessage, getPathFromStr, getPathFromUri, isGitAtLeastVersion, openGitTerminal, pathWithTrailingSlash, realpath, resolveSpawnOutput } from './utils';
1111
import { Disposable } from './utils/disposable';
1212
import { Event } from './utils/event';
@@ -257,9 +257,15 @@ export class DataSource extends Disposable {
257257
}
258258
}
259259

260-
return { commits: commitNodes, head: refData.head, moreCommitsAvailable: moreCommitsAvailable, error: null };
260+
return {
261+
commits: commitNodes,
262+
head: refData.head,
263+
tags: unique(refData.tags.map((tag) => tag.name)),
264+
moreCommitsAvailable: moreCommitsAvailable,
265+
error: null
266+
};
261267
}).catch((errorMessage) => {
262-
return { commits: [], head: null, moreCommitsAvailable: false, error: errorMessage };
268+
return { commits: [], head: null, tags: [], moreCommitsAvailable: false, error: errorMessage };
263269
});
264270
}
265271

@@ -659,13 +665,17 @@ export class DataSource extends Disposable {
659665
* @param repo The path of the repository.
660666
* @param tagName The name of the tag.
661667
* @param commitHash The hash of the commit the tag should be added to.
662-
* @param lightweight Is the tag lightweight.
668+
* @param type Is the tag annotated or lightweight.
663669
* @param message The message of the tag (if it is an annotated tag).
670+
* @param force Force add the tag, replacing an existing tag with the same name (if it exists).
664671
* @returns The ErrorInfo from the executed command.
665672
*/
666-
public addTag(repo: string, tagName: string, commitHash: string, lightweight: boolean, message: string) {
673+
public addTag(repo: string, tagName: string, commitHash: string, type: TagType, message: string, force: boolean) {
667674
const args = ['tag'];
668-
if (lightweight) {
675+
if (force) {
676+
args.push('-f');
677+
}
678+
if (type === TagType.Lightweight) {
669679
args.push(tagName);
670680
} else {
671681
args.push(getConfig().signTags ? '-s' : '-a', tagName, '-m', message);
@@ -1783,6 +1793,12 @@ function removeTrailingBlankLines(lines: string[]) {
17831793
return lines;
17841794
}
17851795

1796+
function unique(items: ReadonlyArray<string>) {
1797+
const uniqueItems: { [item: string]: true } = {};
1798+
items.forEach((item) => uniqueItems[item] = true);
1799+
return Object.keys(uniqueItems);
1800+
}
1801+
17861802

17871803
/* Types */
17881804

@@ -1816,6 +1832,7 @@ interface GitCommitRecord {
18161832
interface GitCommitData {
18171833
commits: GitCommit[];
18181834
head: string | null;
1835+
tags: string[];
18191836
moreCommitsAvailable: boolean;
18201837
error: ErrorInfo;
18211838
}

src/gitGraphView.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ export class GitGraphView extends Disposable {
179179
});
180180
break;
181181
case 'addTag':
182-
errorInfos = [await this.dataSource.addTag(msg.repo, msg.tagName, msg.commitHash, msg.lightweight, msg.message)];
182+
errorInfos = [await this.dataSource.addTag(msg.repo, msg.tagName, msg.commitHash, msg.type, msg.message, msg.force)];
183183
if (errorInfos[0] === null && msg.pushToRemote !== null) {
184184
errorInfos.push(await this.dataSource.pushTag(msg.repo, msg.tagName, msg.pushToRemote));
185185
}

src/types.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ export interface DefaultColumnVisibility {
428428
export interface DialogDefaults {
429429
readonly addTag: {
430430
readonly pushToRemote: boolean,
431-
readonly type: 'annotated' | 'lightweight'
431+
readonly type: TagType
432432
};
433433
readonly applyStash: {
434434
readonly reinstateIndex: boolean
@@ -518,6 +518,11 @@ export const enum TabIconColourTheme {
518518
Grey
519519
}
520520

521+
export const enum TagType {
522+
Annotated,
523+
Lightweight
524+
}
525+
521526

522527
/* Base Interfaces for Request / Response Messages */
523528

@@ -557,9 +562,10 @@ export interface RequestAddTag extends RepoRequest {
557562
readonly command: 'addTag';
558563
readonly commitHash: string;
559564
readonly tagName: string;
560-
readonly lightweight: boolean;
565+
readonly type: TagType;
561566
readonly message: string;
562567
readonly pushToRemote: string | null; // string => name of the remote to push the tag to, null => don't push to a remote
568+
readonly force: boolean;
563569
}
564570
export interface ResponseAddTag extends ResponseWithMultiErrorInfo {
565571
readonly command: 'addTag';
@@ -872,6 +878,7 @@ export interface ResponseLoadCommits extends ResponseWithErrorInfo {
872878
readonly refreshId: number;
873879
readonly commits: GitCommit[];
874880
readonly head: string | null;
881+
readonly tags: string[];
875882
readonly moreCommitsAvailable: boolean;
876883
readonly onlyFollowFirstParent: boolean;
877884
}

tests/config.test.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import * as vscode from './mocks/vscode';
44
jest.mock('vscode', () => vscode, { virtual: true });
55

66
import { getConfig } from '../src/config';
7-
import { CommitDetailsViewLocation, CommitOrdering, DateFormatType, DateType, FileViewType, GitResetMode, GraphStyle, GraphUncommittedChangesStyle, RepoDropdownOrder, SquashMessageFormat, TabIconColourTheme } from '../src/types';
7+
import { CommitDetailsViewLocation, CommitOrdering, DateFormatType, DateType, FileViewType, GitResetMode, GraphStyle, GraphUncommittedChangesStyle, RepoDropdownOrder, SquashMessageFormat, TabIconColourTheme, TagType } from '../src/types';
88

99
const workspaceConfiguration = vscode.mocks.workspaceConfiguration;
1010

@@ -838,7 +838,7 @@ describe('Config', () => {
838838
expect(value).toStrictEqual({
839839
addTag: {
840840
pushToRemote: true,
841-
type: 'annotated'
841+
type: TagType.Annotated
842842
},
843843
applyStash: {
844844
reinstateIndex: true
@@ -934,7 +934,7 @@ describe('Config', () => {
934934
expect(value).toStrictEqual({
935935
addTag: {
936936
pushToRemote: false,
937-
type: 'annotated'
937+
type: TagType.Annotated
938938
},
939939
applyStash: {
940940
reinstateIndex: false
@@ -1030,7 +1030,7 @@ describe('Config', () => {
10301030
expect(value).toStrictEqual({
10311031
addTag: {
10321032
pushToRemote: true,
1033-
type: 'annotated'
1033+
type: TagType.Annotated
10341034
},
10351035
applyStash: {
10361036
reinstateIndex: true
@@ -1126,7 +1126,7 @@ describe('Config', () => {
11261126
expect(value).toStrictEqual({
11271127
addTag: {
11281128
pushToRemote: false,
1129-
type: 'annotated'
1129+
type: TagType.Annotated
11301130
},
11311131
applyStash: {
11321132
reinstateIndex: false
@@ -1208,7 +1208,7 @@ describe('Config', () => {
12081208
expect(value).toStrictEqual({
12091209
addTag: {
12101210
pushToRemote: false,
1211-
type: 'annotated'
1211+
type: TagType.Annotated
12121212
},
12131213
applyStash: {
12141214
reinstateIndex: false
@@ -1283,7 +1283,7 @@ describe('Config', () => {
12831283
expect(value).toStrictEqual({
12841284
addTag: {
12851285
pushToRemote: false,
1286-
type: 'annotated'
1286+
type: TagType.Annotated
12871287
},
12881288
applyStash: {
12891289
reinstateIndex: false
@@ -1339,7 +1339,7 @@ describe('Config', () => {
13391339
const value = config.dialogDefaults;
13401340

13411341
// Assert
1342-
expect(value.addTag.type).toBe('annotated');
1342+
expect(value.addTag.type).toBe(TagType.Annotated);
13431343
});
13441344

13451345
it('Should return "lightweight" the configuration value is "Annotated"', () => {
@@ -1350,7 +1350,7 @@ describe('Config', () => {
13501350
const value = config.dialogDefaults;
13511351

13521352
// Assert
1353-
expect(value.addTag.type).toBe('lightweight');
1353+
expect(value.addTag.type).toBe(TagType.Lightweight);
13541354
});
13551355
});
13561356

0 commit comments

Comments
 (0)