Skip to content

Commit 7877900

Browse files
committed
#395 Added a "Force Fetch" option onto the "Fetch into Local Branch" Dialog, allowing any local branch (that's not checked out) to be reset to the remote branch.
1 parent ad1ba5c commit 7877900

8 files changed

Lines changed: 65 additions & 7 deletions

File tree

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,11 @@
525525
"default": false,
526526
"description": "Default state of the \"Force Delete\" checkbox."
527527
},
528+
"git-graph.dialog.fetchIntoLocalBranch.forceFetch": {
529+
"type": "boolean",
530+
"default": false,
531+
"description": "Default state of the \"Force Fetch\" checkbox."
532+
},
528533
"git-graph.dialog.fetchRemote.prune": {
529534
"type": "boolean",
530535
"default": false,

src/config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,9 @@ class Config {
192192
deleteBranch: {
193193
forceDelete: !!this.config.get('dialog.deleteBranch.forceDelete', false)
194194
},
195+
fetchIntoLocalBranch: {
196+
forceFetch: !!this.config.get('dialog.fetchIntoLocalBranch.forceFetch', false)
197+
},
195198
fetchRemote: {
196199
prune: !!this.config.get('dialog.fetchRemote.prune', false),
197200
pruneTags: !!this.config.get('dialog.fetchRemote.pruneTags', false)

src/dataSource.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -884,10 +884,16 @@ export class DataSource extends Disposable {
884884
* @param remote The name of the remote containing the remote branch.
885885
* @param remoteBranch The name of the remote branch.
886886
* @param localBranch The name of the local branch.
887+
* @param force Force fetch the remote branch.
887888
* @returns The ErrorInfo from the executed command.
888889
*/
889-
public fetchIntoLocalBranch(repo: string, remote: string, remoteBranch: string, localBranch: string) {
890-
return this.runGitCommand(['fetch', remote, remoteBranch + ':' + localBranch], repo);
890+
public fetchIntoLocalBranch(repo: string, remote: string, remoteBranch: string, localBranch: string, force: boolean) {
891+
const args = ['fetch'];
892+
if (force) {
893+
args.push('-f');
894+
}
895+
args.push(remote, remoteBranch + ':' + localBranch);
896+
return this.runGitCommand(args, repo);
891897
}
892898

893899
/**

src/gitGraphView.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ export class GitGraphView extends Disposable {
386386
case 'fetchIntoLocalBranch':
387387
this.sendMessage({
388388
command: 'fetchIntoLocalBranch',
389-
error: await this.dataSource.fetchIntoLocalBranch(msg.repo, msg.remote, msg.remoteBranch, msg.localBranch)
389+
error: await this.dataSource.fetchIntoLocalBranch(msg.repo, msg.remote, msg.remoteBranch, msg.localBranch, msg.force)
390390
});
391391
break;
392392
case 'endCodeReview':

src/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,9 @@ export interface DialogDefaults {
447447
readonly deleteBranch: {
448448
readonly forceDelete: boolean
449449
};
450+
readonly fetchIntoLocalBranch: {
451+
readonly forceFetch: boolean
452+
};
450453
readonly fetchRemote: {
451454
readonly prune: boolean,
452455
readonly pruneTags: boolean
@@ -859,6 +862,7 @@ export interface RequestFetchIntoLocalBranch extends RepoRequest {
859862
readonly remote: string;
860863
readonly remoteBranch: string;
861864
readonly localBranch: string;
865+
readonly force: boolean;
862866
}
863867
export interface ResponseFetchIntoLocalBranch extends ResponseWithErrorInfo {
864868
readonly command: 'fetchIntoLocalBranch';

tests/config.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,7 @@ describe('Config', () => {
810810
'dialog.cherryPick.recordOrigin',
811811
'dialog.createBranch.checkOut',
812812
'dialog.deleteBranch.forceDelete',
813+
'dialog.fetchIntoLocalBranch.forceFetch',
813814
'dialog.fetchRemote.prune',
814815
'dialog.fetchRemote.pruneTags',
815816
'dialog.merge.noCommit',
@@ -834,6 +835,7 @@ describe('Config', () => {
834835
expect(workspaceConfiguration.get).toBeCalledWith('dialog.cherryPick.recordOrigin', false);
835836
expect(workspaceConfiguration.get).toBeCalledWith('dialog.createBranch.checkOut', false);
836837
expect(workspaceConfiguration.get).toBeCalledWith('dialog.deleteBranch.forceDelete', false);
838+
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchIntoLocalBranch.forceFetch', false);
837839
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchRemote.prune', false);
838840
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchRemote.pruneTags', false);
839841
expect(workspaceConfiguration.get).toBeCalledWith('dialog.merge.noCommit', false);
@@ -865,6 +867,9 @@ describe('Config', () => {
865867
deleteBranch: {
866868
forceDelete: true
867869
},
870+
fetchIntoLocalBranch: {
871+
forceFetch: true
872+
},
868873
fetchRemote: {
869874
prune: true,
870875
pruneTags: true
@@ -906,6 +911,7 @@ describe('Config', () => {
906911
'dialog.cherryPick.recordOrigin',
907912
'dialog.createBranch.checkOut',
908913
'dialog.deleteBranch.forceDelete',
914+
'dialog.fetchIntoLocalBranch.forceFetch',
909915
'dialog.fetchRemote.prune',
910916
'dialog.fetchRemote.pruneTags',
911917
'dialog.merge.noCommit',
@@ -930,6 +936,7 @@ describe('Config', () => {
930936
expect(workspaceConfiguration.get).toBeCalledWith('dialog.cherryPick.recordOrigin', false);
931937
expect(workspaceConfiguration.get).toBeCalledWith('dialog.createBranch.checkOut', false);
932938
expect(workspaceConfiguration.get).toBeCalledWith('dialog.deleteBranch.forceDelete', false);
939+
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchIntoLocalBranch.forceFetch', false);
933940
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchRemote.prune', false);
934941
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchRemote.pruneTags', false);
935942
expect(workspaceConfiguration.get).toBeCalledWith('dialog.merge.noCommit', false);
@@ -961,6 +968,9 @@ describe('Config', () => {
961968
deleteBranch: {
962969
forceDelete: false
963970
},
971+
fetchIntoLocalBranch: {
972+
forceFetch: false
973+
},
964974
fetchRemote: {
965975
prune: false,
966976
pruneTags: false
@@ -1002,6 +1012,7 @@ describe('Config', () => {
10021012
'dialog.cherryPick.recordOrigin',
10031013
'dialog.createBranch.checkOut',
10041014
'dialog.deleteBranch.forceDelete',
1015+
'dialog.fetchIntoLocalBranch.forceFetch',
10051016
'dialog.fetchRemote.prune',
10061017
'dialog.fetchRemote.pruneTags',
10071018
'dialog.merge.noCommit',
@@ -1026,6 +1037,7 @@ describe('Config', () => {
10261037
expect(workspaceConfiguration.get).toBeCalledWith('dialog.cherryPick.recordOrigin', false);
10271038
expect(workspaceConfiguration.get).toBeCalledWith('dialog.createBranch.checkOut', false);
10281039
expect(workspaceConfiguration.get).toBeCalledWith('dialog.deleteBranch.forceDelete', false);
1040+
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchIntoLocalBranch.forceFetch', false);
10291041
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchRemote.prune', false);
10301042
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchRemote.pruneTags', false);
10311043
expect(workspaceConfiguration.get).toBeCalledWith('dialog.merge.noCommit', false);
@@ -1057,6 +1069,9 @@ describe('Config', () => {
10571069
deleteBranch: {
10581070
forceDelete: true
10591071
},
1072+
fetchIntoLocalBranch: {
1073+
forceFetch: true
1074+
},
10601075
fetchRemote: {
10611076
prune: true,
10621077
pruneTags: true
@@ -1098,6 +1113,7 @@ describe('Config', () => {
10981113
'dialog.cherryPick.recordOrigin',
10991114
'dialog.createBranch.checkOut',
11001115
'dialog.deleteBranch.forceDelete',
1116+
'dialog.fetchIntoLocalBranch.forceFetch',
11011117
'dialog.fetchRemote.prune',
11021118
'dialog.fetchRemote.pruneTags',
11031119
'dialog.merge.noCommit',
@@ -1122,6 +1138,7 @@ describe('Config', () => {
11221138
expect(workspaceConfiguration.get).toBeCalledWith('dialog.cherryPick.recordOrigin', false);
11231139
expect(workspaceConfiguration.get).toBeCalledWith('dialog.createBranch.checkOut', false);
11241140
expect(workspaceConfiguration.get).toBeCalledWith('dialog.deleteBranch.forceDelete', false);
1141+
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchIntoLocalBranch.forceFetch', false);
11251142
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchRemote.prune', false);
11261143
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchRemote.pruneTags', false);
11271144
expect(workspaceConfiguration.get).toBeCalledWith('dialog.merge.noCommit', false);
@@ -1153,6 +1170,9 @@ describe('Config', () => {
11531170
deleteBranch: {
11541171
forceDelete: false
11551172
},
1173+
fetchIntoLocalBranch: {
1174+
forceFetch: false
1175+
},
11561176
fetchRemote: {
11571177
prune: false,
11581178
pruneTags: false
@@ -1204,6 +1224,7 @@ describe('Config', () => {
12041224
expect(workspaceConfiguration.get).toBeCalledWith('dialog.cherryPick.recordOrigin', false);
12051225
expect(workspaceConfiguration.get).toBeCalledWith('dialog.createBranch.checkOut', false);
12061226
expect(workspaceConfiguration.get).toBeCalledWith('dialog.deleteBranch.forceDelete', false);
1227+
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchIntoLocalBranch.forceFetch', false);
12071228
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchRemote.prune', false);
12081229
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchRemote.pruneTags', false);
12091230
expect(workspaceConfiguration.get).toBeCalledWith('dialog.merge.noCommit', false);
@@ -1235,6 +1256,9 @@ describe('Config', () => {
12351256
deleteBranch: {
12361257
forceDelete: false
12371258
},
1259+
fetchIntoLocalBranch: {
1260+
forceFetch: false
1261+
},
12381262
fetchRemote: {
12391263
prune: false,
12401264
pruneTags: false
@@ -1279,6 +1303,7 @@ describe('Config', () => {
12791303
expect(workspaceConfiguration.get).toBeCalledWith('dialog.cherryPick.recordOrigin', false);
12801304
expect(workspaceConfiguration.get).toBeCalledWith('dialog.createBranch.checkOut', false);
12811305
expect(workspaceConfiguration.get).toBeCalledWith('dialog.deleteBranch.forceDelete', false);
1306+
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchIntoLocalBranch.forceFetch', false);
12821307
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchRemote.prune', false);
12831308
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchRemote.pruneTags', false);
12841309
expect(workspaceConfiguration.get).toBeCalledWith('dialog.merge.noCommit', false);
@@ -1310,6 +1335,9 @@ describe('Config', () => {
13101335
deleteBranch: {
13111336
forceDelete: false
13121337
},
1338+
fetchIntoLocalBranch: {
1339+
forceFetch: false
1340+
},
13131341
fetchRemote: {
13141342
prune: false,
13151343
pruneTags: false

tests/dataSource.test.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4945,19 +4945,31 @@ describe('DataSource', () => {
49454945
mockGitSuccessOnce();
49464946

49474947
// Run
4948-
const result = await dataSource.fetchIntoLocalBranch('/path/to/repo', 'origin', 'master', 'develop');
4948+
const result = await dataSource.fetchIntoLocalBranch('/path/to/repo', 'origin', 'master', 'develop', false);
49494949

49504950
// Assert
49514951
expect(result).toBe(null);
49524952
expect(spyOnSpawn).toBeCalledWith('/path/to/git', ['fetch', 'origin', 'master:develop'], expect.objectContaining({ cwd: '/path/to/repo' }));
49534953
});
49544954

4955+
it('Should (force) fetch a remote branch into a local branch', async () => {
4956+
// Setup
4957+
mockGitSuccessOnce();
4958+
4959+
// Run
4960+
const result = await dataSource.fetchIntoLocalBranch('/path/to/repo', 'origin', 'master', 'develop', true);
4961+
4962+
// Assert
4963+
expect(result).toBe(null);
4964+
expect(spyOnSpawn).toBeCalledWith('/path/to/git', ['fetch', '-f', 'origin', 'master:develop'], expect.objectContaining({ cwd: '/path/to/repo' }));
4965+
});
4966+
49554967
it('Should return an error message thrown by git', async () => {
49564968
// Setup
49574969
mockGitThrowingErrorOnce();
49584970

49594971
// Run
4960-
const result = await dataSource.fetchIntoLocalBranch('/path/to/repo', 'origin', 'master', 'develop');
4972+
const result = await dataSource.fetchIntoLocalBranch('/path/to/repo', 'origin', 'master', 'develop', false);
49614973

49624974
// Assert
49634975
expect(result).toBe('error message');

web/main.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,8 +1252,8 @@ class GitGraphView {
12521252
title: 'Fetch into local branch' + ELLIPSIS,
12531253
visible: visibility.fetch && remote !== '' && this.gitBranches.includes(branchName) && this.gitBranchHead !== branchName,
12541254
onClick: () => {
1255-
dialog.showConfirmation('Are you sure you want to fetch the remote branch <b><i>' + escapeHtml(refName) + '</i></b> into the local branch <b><i>' + escapeHtml(branchName) + '</i></b>?', 'Yes, fetch', () => {
1256-
runAction({ command: 'fetchIntoLocalBranch', repo: this.currentRepo, remote: remote, remoteBranch: branchName, localBranch: branchName }, 'Fetching Branch');
1255+
dialog.showCheckbox('Are you sure you want to fetch the remote branch <b><i>' + escapeHtml(refName) + '</i></b> into the local branch <b><i>' + escapeHtml(branchName) + '</i></b>?', 'Force Fetch<span class="dialogInfo" title="Force the local branch to be reset to this remote branch.">' + SVG_ICONS.info + '</span>', this.config.dialogDefaults.fetchIntoLocalBranch.forceFetch, 'Yes, fetch', (force) => {
1256+
runAction({ command: 'fetchIntoLocalBranch', repo: this.currentRepo, remote: remote, remoteBranch: branchName, localBranch: branchName, force: force }, 'Fetching Branch');
12571257
}, target);
12581258
}
12591259
}, {

0 commit comments

Comments
 (0)