Skip to content

Commit 0a47225

Browse files
sergeibbbd13
authored andcommitted
Explain branch comparing to its merge-target including all commits (wip)
1 parent d142f2b commit 0a47225

File tree

3 files changed

+101
-14
lines changed

3 files changed

+101
-14
lines changed

src/commands/explainBranch.ts

Lines changed: 85 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
1-
import type { TextEditor, Uri } from 'vscode';
1+
import type { CancellationToken, TextEditor, Uri } from 'vscode';
22
import { ProgressLocation } from 'vscode';
33
import type { Source } from '../constants.telemetry';
44
import type { Container } from '../container';
55
import { GitUri } from '../git/gitUri';
6-
import type { GitBranchReference } from '../git/models/reference';
6+
import type { GitBranch } from '../git/models/branch';
77
import { showGenericErrorMessage } from '../messages';
88
import type { AIExplainSource } from '../plus/ai/aiProviderService';
9-
import { showComparisonPicker } from '../quickpicks/comparisonPicker';
109
import { ReferencesQuickPickIncludes, showReferencePicker } from '../quickpicks/referencePicker';
1110
import { getBestRepositoryOrShowPicker, getRepositoryOrShowPicker } from '../quickpicks/repositoryPicker';
1211
import { command } from '../system/-webview/command';
1312
import { showMarkdownPreview } from '../system/-webview/markdown';
1413
import { Logger } from '../system/logger';
15-
import { getSettledValue, getSettledValues } from '../system/promise';
14+
import { getSettledValue } from '../system/promise';
1615
import { GlCommandBase } from './commandBase';
1716
import { getCommandUri } from './commandBase.utils';
1817
import type { CommandContext } from './commandContext';
@@ -34,10 +33,9 @@ export class ExplainBranchCommand extends GlCommandBase {
3433
}
3534

3635
async execute(editor?: TextEditor, uri?: Uri, args?: ExplainBranchCommandArgs): Promise<void> {
36+
//// Clarifying the repository
3737
uri = getCommandUri(uri, editor);
38-
3938
const gitUri = uri != null ? await GitUri.fromUri(uri) : undefined;
40-
4139
const repository = await getBestRepositoryOrShowPicker(
4240
gitUri,
4341
editor,
@@ -49,8 +47,9 @@ export class ExplainBranchCommand extends GlCommandBase {
4947
args = { ...args };
5048

5149
try {
52-
// If no ref is provided, show a picker to select a branch
50+
//// Clarifying the head branch
5351
if (args.ref == null) {
52+
// If no ref is provided, show a picker to select a branch
5453
const pick = await showReferencePicker(
5554
repository.path,
5655
'Explain Branch',
@@ -71,25 +70,66 @@ export class ExplainBranchCommand extends GlCommandBase {
7170
return;
7271
}
7372

73+
//// Clarifying the base branch
74+
const baseBranchName = await getMergeTarget(this.container, branch);
75+
const baseBranch = await repository.git.branches().getBranch(baseBranchName);
76+
if (!baseBranch) {
77+
void showGenericErrorMessage(
78+
'Unable to find the base branch for the specified branch. Probably it is undefined. Set it up and try again.',
79+
);
80+
return;
81+
}
82+
7483
// Get the diff between the branch and its upstream or base
7584
const diffService = repository.git.diff();
7685
if (diffService?.getDiff === undefined) {
7786
void showGenericErrorMessage('Unable to get diff service');
7887
return;
7988
}
8089

81-
const diff = await diffService.getDiff(branch.ref);
82-
if (!diff?.contents) {
90+
const commitsService = repository.git.commits();
91+
if (commitsService?.getLog === undefined) {
92+
void showGenericErrorMessage('Unable to get commits service');
93+
return;
94+
}
95+
96+
const [diffResult, logResult] = await Promise.allSettled([
97+
diffService.getDiff?.(branch.ref, baseBranch.ref, { notation: '...' }),
98+
commitsService.getLog(`${baseBranch.ref}..${branch.ref}`),
99+
]);
100+
101+
const diff = getSettledValue(diffResult);
102+
const log = getSettledValue(logResult);
103+
if (!diff?.contents || !log?.commits?.size) {
83104
void showGenericErrorMessage('No changes found to explain');
84105
return;
85106
}
86107

108+
const commitMessages: string[] = [];
109+
for (const commit of [...log.commits.values()].sort((a, b) => a.date.getTime() - b.date.getTime())) {
110+
const message = commit.message ?? commit.summary;
111+
if (message) {
112+
commitMessages.push(
113+
`<commit-message ${commit.date.toISOString()}>\n${
114+
commit.message ?? commit.summary
115+
}\n<end-of-commit-message>`,
116+
);
117+
}
118+
}
119+
120+
const changes = {
121+
diff: diff.contents,
122+
message: `Changes in branch ${branch.name}
123+
that is ahead of its target by number of commits with the following messages:\n\n
124+
<commits>
125+
${commitMessages.join('\n\n')}
126+
<end-of-commits>
127+
`,
128+
};
129+
87130
// Call the AI service to explain the changes
88131
const result = await this.container.ai.explainChanges(
89-
{
90-
diff: diff.contents,
91-
message: `Changes in branch ${branch.name}`,
92-
},
132+
changes,
93133
args.source ?? { source: 'commandPalette', type: 'commit' },
94134
{
95135
progress: { location: ProgressLocation.Notification, title: 'Explaining branch changes...' },
@@ -103,7 +143,9 @@ export class ExplainBranchCommand extends GlCommandBase {
103143
} else {
104144
content += `> No changes found to explain.`;
105145
}
106-
void showMarkdownPreview(content);
146+
// Add changes temporarily for debug purposes, so it's easier to review what content has been explained
147+
const changesMd = `${changes.message}\n\n${changes.diff}`;
148+
void showMarkdownPreview(`${content}\n\n\`\`\`\n${changesMd.replaceAll('`', '')}\n\`\`\`\n`);
107149
} catch (ex) {
108150
Logger.error(ex, 'ExplainBranchCommand', 'execute');
109151
void showGenericErrorMessage('Unable to explain branch');
@@ -192,6 +234,8 @@ export class ExplainBranchCommand2 extends GlCommandBase {
192234

193235
// async execute(args?: ExplainBranchCommandArgs): Promise<void> {
194236
// try {
237+
// // I'm declining it for now, because it can be a behaviour for "explain comparison" command,
238+
// // that can be called either from the command palette or from the compare view.
195239
// const comparisonResult = await showComparisonPicker(this.container, args?.repoPath, {
196240
// head: args?.branch,
197241
// getTitleAndPlaceholder: step => {
@@ -263,3 +307,30 @@ export class ExplainBranchCommand2 extends GlCommandBase {
263307
// }
264308
// }
265309
// }
310+
311+
async function getMergeTarget(
312+
container: Container,
313+
branch: GitBranch,
314+
options?: { cancellation?: CancellationToken },
315+
): Promise<string | undefined> {
316+
const localValue = await container.git.branches(branch.repoPath).getMergeTargetBranchName?.(branch);
317+
if (localValue) {
318+
return localValue;
319+
}
320+
return getIntegrationDefaultBranchName(container, branch.repoPath, options);
321+
}
322+
323+
// This is similar to what we have in changeBranchMergeTarget.ts
324+
// what is a proper utils files to put it to?
325+
async function getIntegrationDefaultBranchName(
326+
container: Container,
327+
repoPath: string,
328+
options?: { cancellation?: CancellationToken },
329+
): Promise<string | undefined> {
330+
const remote = await container.git.remotes(repoPath).getBestRemoteWithIntegration();
331+
if (remote == null) return undefined;
332+
333+
const integration = await remote.getIntegration();
334+
const defaultBranch = await integration?.getDefaultBranch?.(remote.provider.repoDesc, options);
335+
return defaultBranch && `${remote.name}/${defaultBranch?.name}`;
336+
}

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,21 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
707707
await this.provider.config.setConfig(repoPath, mergeTargetConfigKey, target);
708708
}
709709

710+
async getMergeTargetBranchName(repoPath: string, branch: GitBranch): Promise<string | undefined> {
711+
const [baseResult, defaultResult, targetResult, userTargetResult] = await Promise.allSettled([
712+
this.getBaseBranchName?.(repoPath, branch.name),
713+
this.getDefaultBranchName(repoPath, branch.getRemoteName()),
714+
this.getTargetBranchName?.(repoPath, branch.name),
715+
this.getUserMergeTargetBranchName?.(repoPath, branch.name),
716+
]);
717+
718+
const baseBranchName = getSettledValue(baseResult);
719+
const defaultBranchName = getSettledValue(defaultResult);
720+
const targetMaybeResult = getSettledValue(targetResult);
721+
const userTargetBranchName = getSettledValue(userTargetResult);
722+
return userTargetBranchName || targetMaybeResult || baseBranchName || defaultBranchName;
723+
}
724+
710725
private async getBaseBranchFromReflog(
711726
repoPath: string,
712727
ref: string,

src/git/gitProvider.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ export interface GitBranchesSubProvider {
279279
setTargetBranchName?(repoPath: string, ref: string, target: string): Promise<void>;
280280
getUserMergeTargetBranchName?(repoPath: string, ref: string): Promise<string | undefined>;
281281
setUserMergeTargetBranchName?(repoPath: string, ref: string, target: string | undefined): Promise<void>;
282+
getMergeTargetBranchName?(repoPath: string, branch: GitBranch): Promise<string | undefined>;
282283
renameBranch?(repoPath: string, oldName: string, newName: string): Promise<void>;
283284
}
284285

0 commit comments

Comments
 (0)