Skip to content

Commit 0d54fac

Browse files
committed
Adds explain WIP to branch cards
1 parent 95b8f36 commit 0d54fac

File tree

5 files changed

+84
-11
lines changed

5 files changed

+84
-11
lines changed

src/commands/commandContext.utils.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { isRemote } from '../git/models/remote';
1515
import { Repository } from '../git/models/repository';
1616
import type { GitTag } from '../git/models/tag';
1717
import { isTag } from '../git/models/tag';
18+
import { GitWorktree } from '../git/models/worktree';
1819
import { CloudWorkspace } from '../plus/workspaces/models/cloudWorkspace';
1920
import { LocalWorkspace } from '../plus/workspaces/models/localWorkspace';
2021
import { isScm, isScmResourceGroup, isScmResourceState } from '../system/-webview/scm';
@@ -121,6 +122,14 @@ export function isCommandContextViewNodeHasRemote(
121122
return isRemote((context.node as ViewNode & { remote: GitRemote }).remote);
122123
}
123124

125+
export function isCommandContextViewNodeHasWorktree(
126+
context: CommandContext,
127+
): context is CommandViewNodeContext & { node: ViewNode & { worktree: GitWorktree } } {
128+
if (context.type !== 'viewItem') return false;
129+
130+
return (context.node as ViewNode & { worktree?: GitWorktree }).worktree instanceof GitWorktree;
131+
}
132+
124133
export function isCommandContextViewNodeHasRepository(
125134
context: CommandContext,
126135
): context is CommandViewNodeContext & { node: ViewNode & { repo: Repository } } {

src/commands/explainWip.ts

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,17 @@ import { Logger } from '../system/logger';
1212
import { GlCommandBase } from './commandBase';
1313
import { getCommandUri } from './commandBase.utils';
1414
import type { CommandContext } from './commandContext';
15-
import { isCommandContextViewNodeHasRepoPath, isCommandContextViewNodeHasRepository } from './commandContext.utils';
15+
import {
16+
isCommandContextViewNodeHasRepoPath,
17+
isCommandContextViewNodeHasRepository,
18+
isCommandContextViewNodeHasWorktree,
19+
} from './commandContext.utils';
1620

1721
export interface ExplainWipCommandArgs {
1822
repoPath?: string | Uri;
1923
staged?: boolean;
2024
source?: AIExplainSource;
25+
worktreePath?: string;
2126
}
2227

2328
@command()
@@ -27,7 +32,12 @@ export class ExplainWipCommand extends GlCommandBase {
2732
}
2833

2934
protected override preExecute(context: CommandContext, args?: ExplainWipCommandArgs): Promise<void> {
30-
if (isCommandContextViewNodeHasRepository(context)) {
35+
if (isCommandContextViewNodeHasWorktree(context)) {
36+
args = { ...args };
37+
args.repoPath = context.node.worktree.repoPath;
38+
args.worktreePath = context.node.worktree.path;
39+
args.source = args.source ?? { source: 'view', type: 'wip' };
40+
} else if (isCommandContextViewNodeHasRepository(context)) {
3141
args = { ...args };
3242
args.repoPath = context.node.repo.path;
3343
args.source = args.source ?? { source: 'view', type: 'wip' };
@@ -45,10 +55,13 @@ export class ExplainWipCommand extends GlCommandBase {
4555

4656
let repository;
4757
if (args?.repoPath != null) {
48-
repository = this.container.git.getRepository(args.repoPath);
49-
}
50-
51-
if (repository == null) {
58+
if (args.worktreePath) {
59+
// TODO use await this.container.git.diff(args.worktreePath) instead, this will be more performant
60+
repository = await this.container.git.getOrOpenRepository(args.worktreePath, { closeOnOpen: true });
61+
} else {
62+
repository = this.container.git.getRepository(args.repoPath);
63+
}
64+
} else {
5265
uri = getCommandUri(uri, editor);
5366
const gitUri = uri != null ? await GitUri.fromUri(uri) : undefined;
5467
repository = await getBestRepositoryOrShowPicker(
@@ -80,26 +93,50 @@ export class ExplainWipCommand extends GlCommandBase {
8093
to = uncommitted;
8194
}
8295

83-
const diff = await diffService.getDiff(to);
96+
// If a worktree path is specified, use it for the diff
97+
// const options = args?.worktreePath ? { uris: [Uri.file(args.worktreePath)] } : undefined;
98+
const options = undefined;
99+
const diff = await diffService.getDiff(to, undefined, options);
84100
if (!diff?.contents) {
85101
void showGenericErrorMessage('No working changes found to explain');
86102
return;
87103
}
88104

105+
// Get worktree info
106+
let worktreeInfo = '';
107+
let worktreeDisplayName = '';
108+
109+
if (args?.worktreePath) {
110+
// Get the worktree name if available
111+
const worktrees = await repository.git.worktrees()?.getWorktrees();
112+
const worktree = worktrees?.find(w => w.path === args.worktreePath);
113+
114+
if (worktree) {
115+
worktreeInfo = ` in ${worktree.name}`;
116+
worktreeDisplayName = ` (${worktree.name})`;
117+
} else {
118+
worktreeInfo = ` in worktree`;
119+
worktreeDisplayName = ` (${args.worktreePath})`;
120+
}
121+
}
122+
89123
// Call the AI service to explain the changes
90124
const result = await this.container.ai.explainChanges(
91125
{
92126
diff: diff.contents,
93-
message: `${stagedLabel} working changes`,
127+
message: `${stagedLabel} working changes${worktreeInfo}`,
94128
},
95129
args.source ?? { source: 'commandPalette', type: 'wip' },
96130
{
97-
progress: { location: ProgressLocation.Notification, title: 'Explaining working changes...' },
131+
progress: {
132+
location: ProgressLocation.Notification,
133+
title: `Explaining working changes${worktreeInfo}...`,
134+
},
98135
},
99136
);
100137

101-
// Display the result
102-
let content = `# Working Changes Summary\n\n`;
138+
const title = `Working Changes Summary${worktreeDisplayName}`;
139+
let content = `# ${title}\n\n`;
103140
if (result != null) {
104141
content += `> Generated by ${result.model.name}\n\n## ${stagedLabel} Changes\n\n${result?.parsed.summary}\n\n${result?.parsed.body}`;
105142
} else {

src/constants.commands.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ type InternalHomeWebviewCommands =
5454
| 'gitlens.home.skipPausedOperation'
5555
| 'gitlens.home.continuePausedOperation'
5656
| 'gitlens.home.abortPausedOperation'
57+
| 'gitlens.home.explainWip'
5758
| 'gitlens.home.openRebaseEditor';
5859

5960
type InternalHomeWebviewViewCommands =

src/webviews/apps/plus/home/components/branch-card.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,16 @@ export class GlBranchCard extends GlBranchCardBase {
10001000
href=${this.createCommandLink('gitlens.home.openWorktree')}
10011001
></action-item>`,
10021002
);
1003+
// add explain WIP
1004+
if (this.wip?.workingTreeState != null) {
1005+
actions.push(
1006+
html`<action-item
1007+
label="Explain Changes"
1008+
icon="sparkle"
1009+
href=${this.createCommandLink('gitlens.home.explainWip')}
1010+
></action-item>`,
1011+
);
1012+
}
10031013
} else {
10041014
actions.push(
10051015
html`<action-item

src/webviews/home/homeWebview.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { CreatePullRequestActionContext } from '../../api/gitlens';
55
import type { EnrichedAutolink } from '../../autolinks/models/autolinks';
66
import { getAvatarUriFromGravatarEmail } from '../../avatars';
77
import type { ChangeBranchMergeTargetCommandArgs } from '../../commands/changeBranchMergeTarget';
8+
import type { ExplainWipCommandArgs } from '../../commands/explainWip';
89
import type { BranchGitCommandArgs } from '../../commands/git/branch';
910
import type { OpenPullRequestOnRemoteCommandArgs } from '../../commands/openPullRequestOnRemote';
1011
import { GlyphChars, urls } from '../../constants';
@@ -352,6 +353,7 @@ export class HomeWebviewProvider implements WebviewProvider<State, State, HomeWe
352353
registerCommand('gitlens.home.continuePausedOperation', this.continuePausedOperation, this),
353354
registerCommand('gitlens.home.abortPausedOperation', this.abortPausedOperation, this),
354355
registerCommand('gitlens.home.openRebaseEditor', this.openRebaseEditor, this),
356+
registerCommand('gitlens.home.explainWip', this.explainWip, this),
355357
];
356358
}
357359

@@ -517,6 +519,20 @@ export class HomeWebviewProvider implements WebviewProvider<State, State, HomeWe
517519
void RepoActions.rebase(repo, getReferenceFromBranch(branch));
518520
}
519521

522+
@log<HomeWebviewProvider['explainWip']>({ args: { 0: r => r.branchId } })
523+
private async explainWip(ref: BranchRef) {
524+
const { repo, branch } = await this.getRepoInfoFromRef(ref);
525+
if (repo == null) return;
526+
527+
const worktree = await branch?.getWorktree();
528+
529+
void executeCommand<ExplainWipCommandArgs>('gitlens.ai.explainWip', {
530+
repoPath: repo.path,
531+
worktreePath: worktree?.path,
532+
source: { source: 'home', type: 'wip' },
533+
});
534+
}
535+
520536
@log()
521537
private startWork() {
522538
this.container.telemetry.sendEvent('home/startWork');

0 commit comments

Comments
 (0)