Skip to content

Commit 92c34aa

Browse files
committed
Rebases branch over its target rather than over common commit.
(#4443, #4522)
1 parent 3aff869 commit 92c34aa

File tree

3 files changed

+36
-16
lines changed

3 files changed

+36
-16
lines changed

src/env/node/git/sub-providers/graph.ts

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
349349
branchId = branch?.id ?? getBranchId(repoPath, false, tip);
350350

351351
// Check if branch has commits that can be recomposed and get merge base
352-
const mergeBaseCommit = await this.getMergeBaseCommit(branch, repoPath);
352+
const mergeBase = await this.getMergeBase(branch, repoPath);
353353

354354
context = {
355355
webviewItem: `gitlens:branch${head ? '+current' : ''}${
@@ -362,7 +362,7 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
362362
: ''
363363
}${branch?.starred ? '+starred' : ''}${branch?.upstream?.state.ahead ? '+ahead' : ''}${
364364
branch?.upstream?.state.behind ? '+behind' : ''
365-
}${mergeBaseCommit ? '+recomposable' : ''}`,
365+
}${mergeBase?.commit ? '+recomposable' : ''}`,
366366
webviewItemValue: {
367367
type: 'branch',
368368
ref: createReference(tip, repoPath, {
@@ -372,7 +372,9 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
372372
remote: false,
373373
upstream: branch?.upstream,
374374
}),
375-
mergeBaseCommit: mergeBaseCommit,
375+
mergeBase: mergeBase && {
376+
...mergeBase,
377+
},
376378
},
377379
};
378380

@@ -622,7 +624,10 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
622624
return getCommitsForGraphCore.call(this, defaultLimit, selectSha, undefined, cancellation);
623625
}
624626

625-
private async getMergeBaseCommit(branch: GitBranch | undefined, repoPath: string): Promise<string | undefined> {
627+
private async getMergeBase(
628+
branch: GitBranch | undefined,
629+
repoPath: string,
630+
): Promise<{ commit: string; branch: string; remote: boolean } | undefined> {
626631
if (!branch || branch.remote) return undefined;
627632

628633
try {
@@ -642,10 +647,10 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
642647

643648
// Select target with most recent common commit (closest to branch tip)
644649
const validTargets = [validStoredTarget, validStoredMergeBase];
645-
const targetCommit = await this.selectMostRecentMergeBase(branch.name, validTargets, svc);
650+
const mergeBase = await this.selectMostRecentMergeBase(branch.name, validTargets, svc);
646651

647-
const isRecomposable = Boolean(targetCommit && targetCommit !== branch.sha);
648-
return isRecomposable ? targetCommit : undefined;
652+
const isRecomposable = Boolean(mergeBase && mergeBase.commit !== branch.sha);
653+
return isRecomposable ? mergeBase : undefined;
649654
} catch {
650655
// If we can't determine, assume not recomposable
651656
return undefined;
@@ -656,18 +661,29 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
656661
branchName: string,
657662
targets: (string | undefined)[],
658663
svc: ReturnType<typeof this.container.git.getRepositoryService>,
659-
): Promise<string | undefined> {
664+
): Promise<{ commit: string; branch: string; remote: boolean } | undefined> {
665+
const isString = (t: string | undefined): t is string => Boolean(t);
660666
const mergeBaseResults = await Promise.allSettled(
661-
targets.map(target => target && svc.refs.getMergeBase(branchName, target)),
667+
targets.filter(isString).map(async target => {
668+
const commit = await svc.refs.getMergeBase(branchName, target);
669+
return {
670+
commit: commit,
671+
branch: target,
672+
};
673+
}),
662674
);
663-
const isString = (t: string | undefined): t is string => Boolean(t);
664-
const mergeBases = mergeBaseResults.map(result => getSettledValue(result)).filter(isString);
675+
const mergeBases = mergeBaseResults
676+
.map(result => getSettledValue(result))
677+
.filter((r): r is { commit: string; branch: string; remote: boolean } => isString(r?.commit));
665678

666679
if (mergeBases.length === 0) return undefined;
667680

668681
let mostRecentMergeBase = mergeBases[0];
669682
for (let i = 1; i < mergeBases.length; i++) {
670-
const isCurrentMoreRecent = await svc.commits.isAncestorOf(mostRecentMergeBase, mergeBases[i]);
683+
const isCurrentMoreRecent = await svc.commits.isAncestorOf(
684+
mostRecentMergeBase?.commit,
685+
mergeBases[i].commit,
686+
);
671687
if (isCurrentMoreRecent) {
672688
mostRecentMergeBase = mergeBases[i];
673689
}

src/webviews/plus/graph/graphWebview.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3247,16 +3247,20 @@ export class GraphWebviewProvider implements WebviewProvider<State, State, Graph
32473247
@log()
32483248
private aiRebaseBranch(item?: GraphItemContext) {
32493249
if (isGraphItemRefContext(item, 'branch')) {
3250-
const { ref, mergeBaseCommit } = item.webviewItemValue;
3250+
const { ref, mergeBase } = item.webviewItemValue;
32513251

3252-
if (!mergeBaseCommit) {
3252+
if (!mergeBase) {
32533253
return Promise.resolve();
32543254
}
32553255

32563256
return executeCommand<GenerateRebaseCommandArgs>('gitlens.ai.generateRebase', {
32573257
repoPath: ref.repoPath,
32583258
head: ref,
3259-
base: createReference(mergeBaseCommit, ref.repoPath, { refType: 'revision' }),
3259+
base: createReference(mergeBase.branch, ref.repoPath, {
3260+
refType: 'branch',
3261+
name: mergeBase.branch,
3262+
remote: false,
3263+
}),
32603264
source: { source: 'graph' },
32613265
});
32623266
}

src/webviews/plus/graph/protocol.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,7 @@ export interface GraphIssueContextValue {
567567
export interface GraphBranchContextValue {
568568
type: 'branch';
569569
ref: GitBranchReference;
570-
mergeBaseCommit?: string;
570+
mergeBase?: { commit: string; branch: string };
571571
}
572572

573573
export interface GraphCommitContextValue {

0 commit comments

Comments
 (0)