Skip to content

Commit e32f100

Browse files
committed
Generates PR title and message using AI:
- prompt is from createCommitMessage - only diff is used (vscode-gitlens-private#43, #4177)
1 parent 35fc3ac commit e32f100

File tree

6 files changed

+88
-9
lines changed

6 files changed

+88
-9
lines changed

src/commands/createPullRequestOnRemote.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,22 @@ export class CreatePullRequestOnRemoteCommand extends GlCommandBase {
7272
},
7373
compare: {
7474
branch: args.compare,
75-
remote: { path: compareRemote.path, url: compareRemote.url },
75+
remote: { path: compareRemote.path, url: compareRemote.url, name: compareRemote.name },
76+
},
77+
describePullRequest: async (
78+
completedResource: RemoteResource & { type: RemoteResourceType.CreatePullRequest },
79+
) => {
80+
const base = completedResource.base;
81+
const compare = completedResource.compare;
82+
if (!base?.remote || !compare?.remote || !base?.branch || !compare?.branch) {
83+
return undefined;
84+
}
85+
const baseRef = `${base.remote.name}/${base.branch}`;
86+
const compareRef = `${compare.remote.name}/${compare.branch}`;
87+
const result = await this.container.ai.generatePullRequestMessage(repo, baseRef, compareRef, {
88+
source: 'home', // TODO provide the real source
89+
});
90+
return result?.parsed;
7691
},
7792
};
7893

src/git/models/remoteResource.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,15 @@ export type RemoteResource =
3535
type: RemoteResourceType.CreatePullRequest;
3636
base: {
3737
branch: string | undefined;
38-
remote: { path: string; url: string };
38+
remote: { path: string; url: string; name: string };
3939
};
4040
compare: {
4141
branch: string;
42-
remote: { path: string; url: string };
42+
remote: { path: string; url: string; name: string };
4343
};
44+
describePullRequest?: (
45+
completedResource: RemoteResource & { type: RemoteResourceType.CreatePullRequest },
46+
) => Promise<{ summary: string; body: string } | undefined>;
4447
}
4548
| {
4649
type: RemoteResourceType.File;

src/git/remotes/github.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -280,11 +280,15 @@ export class GitHubRemote extends RemoteProvider<GitHubRepositoryDescriptor> {
280280
return this.encodeUrl(`${this.baseUrl}/compare/${base}${notation}${head}`);
281281
}
282282

283-
protected override getUrlForCreatePullRequest(
283+
protected override async getUrlForCreatePullRequest(
284284
base: { branch?: string; remote: { path: string; url: string } },
285285
head: { branch: string; remote: { path: string; url: string } },
286-
options?: { title?: string; description?: string },
287-
): string | undefined {
286+
options?: {
287+
title?: string;
288+
description?: string;
289+
describePullRequest?: () => Promise<{ summary: string; body: string } | undefined>;
290+
},
291+
): Promise<string | undefined> {
288292
const query = new URLSearchParams({ expand: '1' });
289293
if (options?.title) {
290294
query.set('title', options.title);
@@ -293,6 +297,16 @@ export class GitHubRemote extends RemoteProvider<GitHubRepositoryDescriptor> {
293297
query.set('body', options.description);
294298
}
295299

300+
if ((!options?.title || !options?.description) && options?.describePullRequest) {
301+
const result = await options.describePullRequest();
302+
if (result?.summary) {
303+
query.set('title', result.summary);
304+
}
305+
if (result?.body) {
306+
query.set('body', result.body);
307+
}
308+
}
309+
296310
if (base.remote.url === head.remote.url) {
297311
return base.branch
298312
? `${this.encodeUrl(`${this.baseUrl}/compare/${base.branch}...${head.branch}`)}?${query.toString()}`

src/git/remotes/remoteProvider.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,9 @@ export abstract class RemoteProvider<T extends ResourceDescriptor = ResourceDesc
132132
return this.getUrlForComparison(resource.base, resource.compare, resource.notation ?? '...');
133133
}
134134
case RemoteResourceType.CreatePullRequest: {
135-
return this.getUrlForCreatePullRequest(resource.base, resource.compare);
135+
return this.getUrlForCreatePullRequest(resource.base, resource.compare, {
136+
describePullRequest: resource.describePullRequest?.bind(null, resource),
137+
});
136138
}
137139
case RemoteResourceType.File:
138140
return this.getUrlForFile(
@@ -193,7 +195,11 @@ export abstract class RemoteProvider<T extends ResourceDescriptor = ResourceDesc
193195
protected abstract getUrlForCreatePullRequest(
194196
base: { branch?: string; remote: { path: string; url: string } },
195197
head: { branch: string; remote: { path: string; url: string } },
196-
options?: { title?: string; description?: string },
198+
options?: {
199+
title?: string;
200+
description?: string;
201+
describePullRequest?: () => Promise<{ summary: string; body: string } | undefined>;
202+
},
197203
): Promise<string | undefined> | string | undefined;
198204

199205
protected abstract getUrlForFile(fileName: string, branch?: string, sha?: string, range?: Range): string;

src/plus/ai/aiProviderService.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,44 @@ export class AIProviderService implements Disposable {
518518
return result != null ? { ...result, parsed: parseSummarizeResult(result.content) } : undefined;
519519
}
520520

521+
async generatePullRequestMessage(
522+
repo: Repository,
523+
baseRef: string,
524+
compareRef: string,
525+
source: Source,
526+
options?: {
527+
cancellation?: CancellationToken;
528+
context?: string;
529+
generating?: Deferred<AIModel>;
530+
progress?: ProgressOptions;
531+
},
532+
): Promise<AISummarizeResult | undefined> {
533+
const diff = await repo.git.diff().getDiff?.(compareRef, baseRef);
534+
535+
const result = await this.sendRequest(
536+
'generate-commitMessage',
537+
() => ({
538+
diff: diff?.contents ?? '[no code changes]',
539+
context: options?.context ?? '',
540+
instructions: configuration.get('ai.generateCommitMessage.customInstructions') ?? '',
541+
}),
542+
m => `Generating commit message with ${m.name}...`,
543+
source,
544+
m => ({
545+
key: 'ai/generate',
546+
data: {
547+
type: 'commitMessage',
548+
'model.id': m.id,
549+
'model.provider.id': m.provider.id,
550+
'model.provider.name': m.provider.name,
551+
'retry.count': 0,
552+
},
553+
}),
554+
options,
555+
);
556+
return result != null ? { ...result, parsed: parseSummarizeResult(result.content) } : undefined;
557+
}
558+
521559
async generateDraftMessage(
522560
changesOrRepo: string | string[] | Repository,
523561
sourceContext: Source & { type: AIGenerateDraftEventData['draftType'] },

src/quickpicks/remoteProviderPicker.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,10 @@ export class CopyOrOpenRemoteCommandQuickPickItem extends CommandQuickPickItem {
7171

7272
resource = {
7373
...resource,
74-
base: { branch: branch, remote: { path: this.remote.path, url: this.remote.url } },
74+
base: {
75+
branch: branch,
76+
remote: { path: this.remote.path, url: this.remote.url, name: this.remote.name },
77+
},
7578
};
7679

7780
if (

0 commit comments

Comments
 (0)