Skip to content

Commit ba61eeb

Browse files
committed
Rebases branch over its target rather than over common commit.
(#4443, #4522)
1 parent 6e4d0d2 commit ba61eeb

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
@@ -3280,16 +3280,20 @@ export class GraphWebviewProvider implements WebviewProvider<State, State, Graph
32803280
@log()
32813281
private aiRebaseBranch(item?: GraphItemContext) {
32823282
if (isGraphItemRefContext(item, 'branch')) {
3283-
const { ref, mergeBaseCommit } = item.webviewItemValue;
3283+
const { ref, mergeBase } = item.webviewItemValue;
32843284

3285-
if (!mergeBaseCommit) {
3285+
if (!mergeBase) {
32863286
return Promise.resolve();
32873287
}
32883288

32893289
return executeCommand<GenerateRebaseCommandArgs>('gitlens.ai.generateRebase', {
32903290
repoPath: ref.repoPath,
32913291
head: ref,
3292-
base: createReference(mergeBaseCommit, ref.repoPath, { refType: 'revision' }),
3292+
base: createReference(mergeBase.branch, ref.repoPath, {
3293+
refType: 'branch',
3294+
name: mergeBase.branch,
3295+
remote: false,
3296+
}),
32933297
source: { source: 'graph' },
32943298
});
32953299
}

src/webviews/plus/graph/protocol.ts

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

576576
export interface GraphCommitContextValue {

0 commit comments

Comments
 (0)