Skip to content

Commit

Permalink
#395 Added a "Force Fetch" option onto the "Fetch into Local Branch" …
Browse files Browse the repository at this point in the history
…Dialog, allowing any local branch (that's not checked out) to be reset to the remote branch.
  • Loading branch information
mhutchie committed Mar 3, 2021
1 parent ad1ba5c commit 7877900
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 7 deletions.
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,11 @@
"default": false,
"description": "Default state of the \"Force Delete\" checkbox."
},
"git-graph.dialog.fetchIntoLocalBranch.forceFetch": {
"type": "boolean",
"default": false,
"description": "Default state of the \"Force Fetch\" checkbox."
},
"git-graph.dialog.fetchRemote.prune": {
"type": "boolean",
"default": false,
Expand Down
3 changes: 3 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,9 @@ class Config {
deleteBranch: {
forceDelete: !!this.config.get('dialog.deleteBranch.forceDelete', false)
},
fetchIntoLocalBranch: {
forceFetch: !!this.config.get('dialog.fetchIntoLocalBranch.forceFetch', false)
},
fetchRemote: {
prune: !!this.config.get('dialog.fetchRemote.prune', false),
pruneTags: !!this.config.get('dialog.fetchRemote.pruneTags', false)
Expand Down
10 changes: 8 additions & 2 deletions src/dataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -884,10 +884,16 @@ export class DataSource extends Disposable {
* @param remote The name of the remote containing the remote branch.
* @param remoteBranch The name of the remote branch.
* @param localBranch The name of the local branch.
* @param force Force fetch the remote branch.
* @returns The ErrorInfo from the executed command.
*/
public fetchIntoLocalBranch(repo: string, remote: string, remoteBranch: string, localBranch: string) {
return this.runGitCommand(['fetch', remote, remoteBranch + ':' + localBranch], repo);
public fetchIntoLocalBranch(repo: string, remote: string, remoteBranch: string, localBranch: string, force: boolean) {
const args = ['fetch'];
if (force) {
args.push('-f');
}
args.push(remote, remoteBranch + ':' + localBranch);
return this.runGitCommand(args, repo);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/gitGraphView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ export class GitGraphView extends Disposable {
case 'fetchIntoLocalBranch':
this.sendMessage({
command: 'fetchIntoLocalBranch',
error: await this.dataSource.fetchIntoLocalBranch(msg.repo, msg.remote, msg.remoteBranch, msg.localBranch)
error: await this.dataSource.fetchIntoLocalBranch(msg.repo, msg.remote, msg.remoteBranch, msg.localBranch, msg.force)
});
break;
case 'endCodeReview':
Expand Down
4 changes: 4 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,9 @@ export interface DialogDefaults {
readonly deleteBranch: {
readonly forceDelete: boolean
};
readonly fetchIntoLocalBranch: {
readonly forceFetch: boolean
};
readonly fetchRemote: {
readonly prune: boolean,
readonly pruneTags: boolean
Expand Down Expand Up @@ -859,6 +862,7 @@ export interface RequestFetchIntoLocalBranch extends RepoRequest {
readonly remote: string;
readonly remoteBranch: string;
readonly localBranch: string;
readonly force: boolean;
}
export interface ResponseFetchIntoLocalBranch extends ResponseWithErrorInfo {
readonly command: 'fetchIntoLocalBranch';
Expand Down
28 changes: 28 additions & 0 deletions tests/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,7 @@ describe('Config', () => {
'dialog.cherryPick.recordOrigin',
'dialog.createBranch.checkOut',
'dialog.deleteBranch.forceDelete',
'dialog.fetchIntoLocalBranch.forceFetch',
'dialog.fetchRemote.prune',
'dialog.fetchRemote.pruneTags',
'dialog.merge.noCommit',
Expand All @@ -834,6 +835,7 @@ describe('Config', () => {
expect(workspaceConfiguration.get).toBeCalledWith('dialog.cherryPick.recordOrigin', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.createBranch.checkOut', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.deleteBranch.forceDelete', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchIntoLocalBranch.forceFetch', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchRemote.prune', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchRemote.pruneTags', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.merge.noCommit', false);
Expand Down Expand Up @@ -865,6 +867,9 @@ describe('Config', () => {
deleteBranch: {
forceDelete: true
},
fetchIntoLocalBranch: {
forceFetch: true
},
fetchRemote: {
prune: true,
pruneTags: true
Expand Down Expand Up @@ -906,6 +911,7 @@ describe('Config', () => {
'dialog.cherryPick.recordOrigin',
'dialog.createBranch.checkOut',
'dialog.deleteBranch.forceDelete',
'dialog.fetchIntoLocalBranch.forceFetch',
'dialog.fetchRemote.prune',
'dialog.fetchRemote.pruneTags',
'dialog.merge.noCommit',
Expand All @@ -930,6 +936,7 @@ describe('Config', () => {
expect(workspaceConfiguration.get).toBeCalledWith('dialog.cherryPick.recordOrigin', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.createBranch.checkOut', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.deleteBranch.forceDelete', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchIntoLocalBranch.forceFetch', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchRemote.prune', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchRemote.pruneTags', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.merge.noCommit', false);
Expand Down Expand Up @@ -961,6 +968,9 @@ describe('Config', () => {
deleteBranch: {
forceDelete: false
},
fetchIntoLocalBranch: {
forceFetch: false
},
fetchRemote: {
prune: false,
pruneTags: false
Expand Down Expand Up @@ -1002,6 +1012,7 @@ describe('Config', () => {
'dialog.cherryPick.recordOrigin',
'dialog.createBranch.checkOut',
'dialog.deleteBranch.forceDelete',
'dialog.fetchIntoLocalBranch.forceFetch',
'dialog.fetchRemote.prune',
'dialog.fetchRemote.pruneTags',
'dialog.merge.noCommit',
Expand All @@ -1026,6 +1037,7 @@ describe('Config', () => {
expect(workspaceConfiguration.get).toBeCalledWith('dialog.cherryPick.recordOrigin', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.createBranch.checkOut', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.deleteBranch.forceDelete', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchIntoLocalBranch.forceFetch', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchRemote.prune', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchRemote.pruneTags', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.merge.noCommit', false);
Expand Down Expand Up @@ -1057,6 +1069,9 @@ describe('Config', () => {
deleteBranch: {
forceDelete: true
},
fetchIntoLocalBranch: {
forceFetch: true
},
fetchRemote: {
prune: true,
pruneTags: true
Expand Down Expand Up @@ -1098,6 +1113,7 @@ describe('Config', () => {
'dialog.cherryPick.recordOrigin',
'dialog.createBranch.checkOut',
'dialog.deleteBranch.forceDelete',
'dialog.fetchIntoLocalBranch.forceFetch',
'dialog.fetchRemote.prune',
'dialog.fetchRemote.pruneTags',
'dialog.merge.noCommit',
Expand All @@ -1122,6 +1138,7 @@ describe('Config', () => {
expect(workspaceConfiguration.get).toBeCalledWith('dialog.cherryPick.recordOrigin', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.createBranch.checkOut', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.deleteBranch.forceDelete', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchIntoLocalBranch.forceFetch', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchRemote.prune', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchRemote.pruneTags', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.merge.noCommit', false);
Expand Down Expand Up @@ -1153,6 +1170,9 @@ describe('Config', () => {
deleteBranch: {
forceDelete: false
},
fetchIntoLocalBranch: {
forceFetch: false
},
fetchRemote: {
prune: false,
pruneTags: false
Expand Down Expand Up @@ -1204,6 +1224,7 @@ describe('Config', () => {
expect(workspaceConfiguration.get).toBeCalledWith('dialog.cherryPick.recordOrigin', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.createBranch.checkOut', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.deleteBranch.forceDelete', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchIntoLocalBranch.forceFetch', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchRemote.prune', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchRemote.pruneTags', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.merge.noCommit', false);
Expand Down Expand Up @@ -1235,6 +1256,9 @@ describe('Config', () => {
deleteBranch: {
forceDelete: false
},
fetchIntoLocalBranch: {
forceFetch: false
},
fetchRemote: {
prune: false,
pruneTags: false
Expand Down Expand Up @@ -1279,6 +1303,7 @@ describe('Config', () => {
expect(workspaceConfiguration.get).toBeCalledWith('dialog.cherryPick.recordOrigin', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.createBranch.checkOut', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.deleteBranch.forceDelete', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchIntoLocalBranch.forceFetch', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchRemote.prune', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.fetchRemote.pruneTags', false);
expect(workspaceConfiguration.get).toBeCalledWith('dialog.merge.noCommit', false);
Expand Down Expand Up @@ -1310,6 +1335,9 @@ describe('Config', () => {
deleteBranch: {
forceDelete: false
},
fetchIntoLocalBranch: {
forceFetch: false
},
fetchRemote: {
prune: false,
pruneTags: false
Expand Down
16 changes: 14 additions & 2 deletions tests/dataSource.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4945,19 +4945,31 @@ describe('DataSource', () => {
mockGitSuccessOnce();

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

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

it('Should (force) fetch a remote branch into a local branch', async () => {
// Setup
mockGitSuccessOnce();

// Run
const result = await dataSource.fetchIntoLocalBranch('/path/to/repo', 'origin', 'master', 'develop', true);

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

it('Should return an error message thrown by git', async () => {
// Setup
mockGitThrowingErrorOnce();

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

// Assert
expect(result).toBe('error message');
Expand Down
4 changes: 2 additions & 2 deletions web/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1252,8 +1252,8 @@ class GitGraphView {
title: 'Fetch into local branch' + ELLIPSIS,
visible: visibility.fetch && remote !== '' && this.gitBranches.includes(branchName) && this.gitBranchHead !== branchName,
onClick: () => {
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', () => {
runAction({ command: 'fetchIntoLocalBranch', repo: this.currentRepo, remote: remote, remoteBranch: branchName, localBranch: branchName }, 'Fetching Branch');
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) => {
runAction({ command: 'fetchIntoLocalBranch', repo: this.currentRepo, remote: remote, remoteBranch: branchName, localBranch: branchName, force: force }, 'Fetching Branch');
}, target);
}
}, {
Expand Down

0 comments on commit 7877900

Please sign in to comment.