1- import type { TextEditor , Uri } from 'vscode' ;
1+ import type { CancellationToken , TextEditor , Uri } from 'vscode' ;
22import { ProgressLocation } from 'vscode' ;
33import type { Source } from '../constants.telemetry' ;
44import type { Container } from '../container' ;
55import { GitUri } from '../git/gitUri' ;
6- import type { GitBranchReference } from '../git/models/reference ' ;
6+ import type { GitBranch } from '../git/models/branch ' ;
77import { showGenericErrorMessage } from '../messages' ;
88import type { AIExplainSource } from '../plus/ai/aiProviderService' ;
9- import { showComparisonPicker } from '../quickpicks/comparisonPicker' ;
109import { ReferencesQuickPickIncludes , showReferencePicker } from '../quickpicks/referencePicker' ;
1110import { getBestRepositoryOrShowPicker , getRepositoryOrShowPicker } from '../quickpicks/repositoryPicker' ;
1211import { command } from '../system/-webview/command' ;
1312import { showMarkdownPreview } from '../system/-webview/markdown' ;
1413import { Logger } from '../system/logger' ;
15- import { getSettledValue , getSettledValues } from '../system/promise' ;
14+ import { getSettledValue } from '../system/promise' ;
1615import { GlCommandBase } from './commandBase' ;
1716import { getCommandUri } from './commandBase.utils' ;
1817import 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+ }
0 commit comments