@@ -8,6 +8,7 @@ import { API as GitAPI, RefType, Repository } from './typings/git.js';
88import { publishRepository } from './publish.js' ;
99import { DisposableStore , getRepositoryFromUrl } from './util.js' ;
1010import { LinkContext , getCommitLink , getLink , getVscodeDevHost } from './links.js' ;
11+ import { getOctokit } from './auth.js' ;
1112
1213async function copyVscodeDevLink ( gitAPI : GitAPI , useSelection : boolean , context : LinkContext , includeRange = true ) {
1314 try {
@@ -34,46 +35,95 @@ async function openVscodeDevLink(gitAPI: GitAPI): Promise<vscode.Uri | undefined
3435 }
3536}
3637
37- async function createPullRequest ( gitAPI : GitAPI , sessionResource : vscode . Uri | undefined , sessionMetadata : { worktreePath ?: string } | undefined ) : Promise < void > {
38- if ( ! sessionResource || ! sessionMetadata ?. worktreePath ) {
39- return ;
38+ interface ResolvedSessionRepo {
39+ repository : Repository ;
40+ remoteInfo : { owner : string ; repo : string } ;
41+ gitRemote : { name : string ; fetchUrl : string } ;
42+ head : { name : string ; upstream ?: { name : string ; remote : string ; commit : string } } ;
43+ }
44+
45+ function resolveSessionRepo ( gitAPI : GitAPI , sessionMetadata : { worktreePath ?: string } | undefined , showErrors : boolean ) : ResolvedSessionRepo | undefined {
46+ if ( ! sessionMetadata ?. worktreePath ) {
47+ return undefined ;
4048 }
4149
4250 const worktreeUri = vscode . Uri . file ( sessionMetadata . worktreePath ) ;
4351 const repository = gitAPI . getRepository ( worktreeUri ) ;
4452
4553 if ( ! repository ) {
46- vscode . window . showErrorMessage ( vscode . l10n . t ( 'Could not find a git repository for the session worktree.' ) ) ;
47- return ;
54+ if ( showErrors ) {
55+ vscode . window . showErrorMessage ( vscode . l10n . t ( 'Could not find a git repository for the session worktree.' ) ) ;
56+ }
57+ return undefined ;
4858 }
4959
50- // Find the GitHub remote
5160 const remotes = repository . state . remotes
5261 . filter ( remote => remote . fetchUrl && getRepositoryFromUrl ( remote . fetchUrl ) ) ;
5362
5463 if ( remotes . length === 0 ) {
55- vscode . window . showErrorMessage ( vscode . l10n . t ( 'Could not find a GitHub remote for this repository.' ) ) ;
56- return ;
64+ if ( showErrors ) {
65+ vscode . window . showErrorMessage ( vscode . l10n . t ( 'Could not find a GitHub remote for this repository.' ) ) ;
66+ }
67+ return undefined ;
5768 }
5869
59- // Prefer upstream -> origin -> first
6070 const gitRemote = remotes . find ( r => r . name === 'upstream' )
6171 ?? remotes . find ( r => r . name === 'origin' )
6272 ?? remotes [ 0 ] ;
6373
6474 const remoteInfo = getRepositoryFromUrl ( gitRemote . fetchUrl ! ) ;
6575 if ( ! remoteInfo ) {
66- vscode . window . showErrorMessage ( vscode . l10n . t ( 'Could not parse GitHub remote URL.' ) ) ;
67- return ;
76+ if ( showErrors ) {
77+ vscode . window . showErrorMessage ( vscode . l10n . t ( 'Could not parse GitHub remote URL.' ) ) ;
78+ }
79+ return undefined ;
6880 }
6981
70- // Get the current branch (the worktree branch)
7182 const head = repository . state . HEAD ;
7283 if ( ! head ?. name ) {
73- vscode . window . showErrorMessage ( vscode . l10n . t ( 'Could not determine the current branch.' ) ) ;
84+ if ( showErrors ) {
85+ vscode . window . showErrorMessage ( vscode . l10n . t ( 'Could not determine the current branch.' ) ) ;
86+ }
87+ return undefined ;
88+ }
89+
90+ return { repository, remoteInfo, gitRemote : { name : gitRemote . name , fetchUrl : gitRemote . fetchUrl ! } , head : head as ResolvedSessionRepo [ 'head' ] } ;
91+ }
92+
93+ async function checkOpenPullRequest ( gitAPI : GitAPI , _sessionResource : vscode . Uri | undefined , sessionMetadata : { worktreePath ?: string } | undefined ) : Promise < void > {
94+ const resolved = resolveSessionRepo ( gitAPI , sessionMetadata , false ) ;
95+ if ( ! resolved ) {
96+ vscode . commands . executeCommand ( 'setContext' , 'github.hasOpenPullRequest' , false ) ;
97+ return ;
98+ }
99+
100+ try {
101+ const octokit = await getOctokit ( ) ;
102+ const { data : openPRs } = await octokit . pulls . list ( {
103+ owner : resolved . remoteInfo . owner ,
104+ repo : resolved . remoteInfo . repo ,
105+ head : `${ resolved . remoteInfo . owner } :${ resolved . head . name } ` ,
106+ state : 'all' ,
107+ } ) ;
108+
109+ vscode . commands . executeCommand ( 'setContext' , 'github.hasOpenPullRequest' , openPRs . length > 0 ) ;
110+ } catch {
111+ vscode . commands . executeCommand ( 'setContext' , 'github.hasOpenPullRequest' , false ) ;
112+ }
113+ }
114+
115+ async function createPullRequest ( gitAPI : GitAPI , sessionResource : vscode . Uri | undefined , sessionMetadata : { worktreePath ?: string } | undefined ) : Promise < void > {
116+ if ( ! sessionResource ) {
117+ return ;
118+ }
119+
120+ const resolved = resolveSessionRepo ( gitAPI , sessionMetadata , true ) ;
121+ if ( ! resolved ) {
74122 return ;
75123 }
76124
125+ const { repository, remoteInfo, gitRemote, head } = resolved ;
126+
77127 // Ensure the branch is published to the remote
78128 if ( ! head . upstream ) {
79129 try {
@@ -96,6 +146,34 @@ async function createPullRequest(gitAPI: GitAPI, sessionResource: vscode.Uri | u
96146 vscode . env . openExternal ( vscode . Uri . parse ( prUrl ) ) ;
97147}
98148
149+ async function openPullRequest ( gitAPI : GitAPI , _sessionResource : vscode . Uri | undefined , sessionMetadata : { worktreePath ?: string } | undefined ) : Promise < void > {
150+ const resolved = resolveSessionRepo ( gitAPI , sessionMetadata , true ) ;
151+ if ( ! resolved ) {
152+ return ;
153+ }
154+
155+ try {
156+ const octokit = await getOctokit ( ) ;
157+ const { data : pullRequests } = await octokit . pulls . list ( {
158+ owner : resolved . remoteInfo . owner ,
159+ repo : resolved . remoteInfo . repo ,
160+ head : `${ resolved . remoteInfo . owner } :${ resolved . head . name } ` ,
161+ state : 'all' ,
162+ } ) ;
163+
164+ if ( pullRequests . length > 0 ) {
165+ vscode . env . openExternal ( vscode . Uri . parse ( pullRequests [ 0 ] . html_url ) ) ;
166+ return ;
167+ }
168+ } catch {
169+ // If the API call fails, fall through to open the repo page
170+ }
171+
172+ // Fallback: open the repository page
173+ const { remoteInfo } = resolved ;
174+ vscode . env . openExternal ( vscode . Uri . parse ( `https://github.com/${ remoteInfo . owner } /${ remoteInfo . repo } ` ) ) ;
175+ }
176+
99177async function openOnGitHub ( repository : Repository , commit : string ) : Promise < void > {
100178 // Get the unique remotes that contain the commit
101179 const branches = await repository . getBranches ( { contains : commit , remote : true } ) ;
@@ -181,5 +259,13 @@ export function registerCommands(gitAPI: GitAPI): vscode.Disposable {
181259 return createPullRequest ( gitAPI , sessionResource , sessionMetadata ) ;
182260 } ) ) ;
183261
262+ disposables . add ( vscode . commands . registerCommand ( 'github.openPullRequest' , async ( sessionResource : vscode . Uri | undefined , sessionMetadata : { worktreePath ?: string } | undefined ) => {
263+ return openPullRequest ( gitAPI , sessionResource , sessionMetadata ) ;
264+ } ) ) ;
265+
266+ disposables . add ( vscode . commands . registerCommand ( 'github.checkOpenPullRequest' , async ( sessionResource : vscode . Uri | undefined , sessionMetadata : { worktreePath ?: string } | undefined ) => {
267+ return checkOpenPullRequest ( gitAPI , sessionResource , sessionMetadata ) ;
268+ } ) ) ;
269+
184270 return disposables ;
185271}
0 commit comments