Skip to content

Commit 2732583

Browse files
committed
Lets user reset their choice of Merge Target
(#4224, #4258)
1 parent c4d0e8a commit 2732583

File tree

4 files changed

+137
-10
lines changed

4 files changed

+137
-10
lines changed

src/commands/changeBranchMergeTarget.ts

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import type { CancellationToken } from 'vscode';
12
import type { Container } from '../container';
23
import type { GitBranch } from '../git/models/branch';
34
import type { Repository } from '../git/models/repository';
5+
import { getSettledValue } from '../system/promise';
46
import type { ViewsWithRepositoryFolders } from '../views/viewBase';
57
import type { PartialStepState, StepGenerator, StepState } from './quickCommand';
68
import { endSteps, QuickCommand, StepResultBreak } from './quickCommand';
7-
import { pickBranchOrTagStep, pickBranchStep, pickRepositoryStep } from './quickCommand.steps';
9+
import { pickBranchStep, pickOrResetBranchStep, pickRepositoryStep } from './quickCommand.steps';
810

911
interface Context {
1012
repos: Repository[];
@@ -84,25 +86,68 @@ export class ChangeBranchMergeTargetCommand extends QuickCommand {
8486
state.branch = branches.name;
8587
}
8688

87-
const result = yield* pickBranchOrTagStep(state, context, {
89+
if (!state.mergeBranch) {
90+
state.mergeBranch = await this.container.git
91+
.branches(state.repo.path)
92+
.getBaseBranchName?.(state.branch);
93+
}
94+
95+
const gitBranch = await state.repo.git.branches().getBranch(state.branch);
96+
const detectedMergeTarget = gitBranch && (await getDetectedMergeTarget(this.container, gitBranch));
97+
98+
const result = yield* pickOrResetBranchStep(state, context, {
8899
picked: state.mergeBranch,
89100
placeholder: 'Pick a merge target branch',
90-
value: undefined,
91-
filter: {
92-
branches: (branch: GitBranch) => branch.remote && branch.name !== state.branch,
93-
tags: () => false,
94-
},
101+
filter: (branch: GitBranch) => branch.remote && branch.name !== state.branch,
102+
resetTitle: 'Reset Merge Target',
103+
resetDescription: `Reset to "${detectedMergeTarget}"`,
95104
});
96105
if (result === StepResultBreak) {
97106
continue;
98107
}
99-
if (result && state.branch) {
108+
if (state.branch) {
100109
await this.container.git
101110
.branches(state.repo.path)
102-
.setUserMergeTargetBranchName?.(state.branch, result.name);
111+
.setUserMergeTargetBranchName?.(state.branch, result?.name);
103112
}
104113

105114
endSteps(state);
106115
}
107116
}
108117
}
118+
119+
async function getDetectedMergeTarget(
120+
container: Container,
121+
branch: GitBranch,
122+
options?: { cancellation?: CancellationToken },
123+
): Promise<string | undefined> {
124+
const [baseResult, defaultResult, targetResult] = await Promise.allSettled([
125+
container.git.branches(branch.repoPath).getBaseBranchName?.(branch.name),
126+
container.git.branches(branch.repoPath).getDefaultBranchName(branch.getRemoteName()),
127+
container.git.branches(branch.repoPath).getTargetBranchName?.(branch.name),
128+
]);
129+
130+
const baseBranchName = getSettledValue(baseResult);
131+
const defaultBranchName = getSettledValue(defaultResult);
132+
const targetMaybeResult = getSettledValue(targetResult);
133+
const localValue = targetMaybeResult || baseBranchName || defaultBranchName;
134+
if (localValue) {
135+
return localValue;
136+
}
137+
138+
// only if nothing found locally, try value from integration
139+
return getIntegrationDefaultBranchName(container, branch.repoPath, options);
140+
}
141+
142+
async function getIntegrationDefaultBranchName(
143+
container: Container,
144+
repoPath: string,
145+
options?: { cancellation?: CancellationToken },
146+
): Promise<string | undefined> {
147+
const remote = await container.git.remotes(repoPath).getBestRemoteWithIntegration();
148+
if (remote == null) return undefined;
149+
150+
const integration = await remote.getIntegration();
151+
const defaultBranch = await integration?.getDefaultBranch?.(remote.provider.repoDesc, options);
152+
return defaultBranch && `${remote.name}/${defaultBranch?.name}`;
153+
}

src/commands/quickCommand.steps.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,84 @@ export function* pickBranchStep<
783783
return canPickStepContinue(step, state, selection) ? selection[0].item : StepResultBreak;
784784
}
785785

786+
export function* pickOrResetBranchStep<
787+
State extends PartialStepState & { repo: Repository },
788+
Context extends { repos: Repository[]; showTags?: boolean; title: string },
789+
>(
790+
state: State,
791+
context: Context,
792+
{
793+
filter,
794+
picked,
795+
placeholder,
796+
title,
797+
resetTitle,
798+
resetDescription,
799+
}: {
800+
filter?: (b: GitBranch) => boolean;
801+
picked?: string | string[];
802+
placeholder: string;
803+
title?: string;
804+
resetTitle: string;
805+
resetDescription: string;
806+
},
807+
): StepResultGenerator<GitBranchReference | undefined> {
808+
const items = getBranches(state.repo, {
809+
buttons: [RevealInSideBarQuickInputButton],
810+
filter: filter,
811+
picked: picked,
812+
}).then(branches =>
813+
branches.length === 0
814+
? [createDirectiveQuickPickItem(Directive.Back, true), createDirectiveQuickPickItem(Directive.Cancel)]
815+
: [
816+
createDirectiveQuickPickItem(Directive.Reset, false, {
817+
label: resetTitle,
818+
description: resetDescription,
819+
}),
820+
...branches,
821+
],
822+
);
823+
824+
const resetButton: QuickInputButton = {
825+
iconPath: new ThemeIcon('notebook-revert'),
826+
tooltip: resetDescription,
827+
};
828+
let resetButtonClicked = false;
829+
const step = createPickStep<BranchQuickPickItem>({
830+
title: appendReposToTitle(title ?? context.title, state, context),
831+
placeholder: count => (!count ? `No branches found in ${state.repo.formattedName}` : placeholder),
832+
matchOnDetail: true,
833+
items: items,
834+
additionalButtons: [resetButton],
835+
onDidClickButton: (_quickpick, button) => {
836+
if (button === resetButton) {
837+
resetButtonClicked = true;
838+
return true;
839+
}
840+
return false;
841+
},
842+
onDidClickItemButton: (_quickpick, button, { item }) => {
843+
if (button === RevealInSideBarQuickInputButton) {
844+
void BranchActions.reveal(item, { select: true, focus: false, expand: true });
845+
}
846+
},
847+
keys: ['right', 'alt+right', 'ctrl+right'],
848+
onDidPressKey: async (_quickpick, _key, { item }) => {
849+
await BranchActions.reveal(item, {
850+
select: true,
851+
focus: false,
852+
expand: true,
853+
});
854+
},
855+
});
856+
857+
const selection: StepSelection<typeof step> = yield step;
858+
if (resetButtonClicked) {
859+
return undefined;
860+
}
861+
return canPickStepContinue(step, state, selection) ? selection[0].item : StepResultBreak;
862+
}
863+
786864
export function* pickBranchesStep<
787865
State extends PartialStepState & { repo: Repository },
788866
Context extends { repos: Repository[]; showTags?: boolean; title: string },

src/git/gitProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ export interface GitBranchesSubProvider {
259259
getTargetBranchName?(repoPath: string, ref: string): Promise<string | undefined>;
260260
setTargetBranchName?(repoPath: string, ref: string, target: string): Promise<void>;
261261
getUserMergeTargetBranchName?(repoPath: string, ref: string): Promise<string | undefined>;
262-
setUserMergeTargetBranchName?(repoPath: string, ref: string, target: string): Promise<void>;
262+
setUserMergeTargetBranchName?(repoPath: string, ref: string, target: string | undefined): Promise<void>;
263263
renameBranch?(repoPath: string, oldName: string, newName: string): Promise<void>;
264264
}
265265

src/quickpicks/items/directive.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { pluralize } from '../../system/string';
55
export enum Directive {
66
Back,
77
Cancel,
8+
Reset,
89
LoadMore,
910
Noop,
1011
Reload,
@@ -57,6 +58,9 @@ export function createDirectiveQuickPickItem(
5758
case Directive.Reload:
5859
label = 'Refresh';
5960
break;
61+
case Directive.Reset:
62+
label = 'Reset';
63+
break;
6064
case Directive.SignIn:
6165
label = 'Sign In';
6266
break;

0 commit comments

Comments
 (0)