@@ -54,7 +54,7 @@ import { EventType } from '../common/timelineEvent';
5454import { Schemes } from '../common/uri' ;
5555import { AsyncPredicate , batchPromiseAll , compareIgnoreCase , formatError , Predicate } from '../common/utils' ;
5656import { PULL_REQUEST_OVERVIEW_VIEW_TYPE } from '../common/webview' ;
57- import { LAST_USED_EMAIL , NEVER_SHOW_PULL_NOTIFICATION , REPO_KEYS , ReposState } from '../extensionState' ;
57+ import { BRANCHES_ASSOCIATED_WITH_PRS , LAST_USED_EMAIL , NEVER_SHOW_PULL_NOTIFICATION , REPO_KEYS , ReposState } from '../extensionState' ;
5858import { git } from '../gitProviders/gitCommands' ;
5959import { IThemeWatcher } from '../themeWatcher' ;
6060import { CreatePullRequestHelper } from '../view/createPullRequestHelper' ;
@@ -576,6 +576,11 @@ export class FolderRepositoryManager extends Disposable {
576576 this . getAssignableUsers ( repositoriesAdded . length > 0 ) ;
577577 if ( isAuthenticated && activeRemotes . length ) {
578578 this . state = ReposManagerState . RepositoriesLoaded ;
579+ // On first activation, associate local branches with PRs
580+ // Do this asynchronously to not block the main flow
581+ this . associateLocalBranchesWithPRsOnFirstActivation ( ) . catch ( e => {
582+ Logger . error ( `Failed to associate branches with PRs: ${ e } ` , this . id ) ;
583+ } ) ;
579584 } else if ( ! isAuthenticated ) {
580585 this . state = ReposManagerState . NeedsAuthentication ;
581586 }
@@ -953,6 +958,112 @@ export class FolderRepositoryManager extends Disposable {
953958 return models . filter ( value => value !== undefined ) as PullRequestModel [ ] ;
954959 }
955960
961+ /**
962+ * On first activation, iterate through local branches and associate them with PRs if they match.
963+ * This helps discover PRs that were created before the extension was installed or in other ways.
964+ */
965+ private async associateLocalBranchesWithPRsOnFirstActivation ( ) : Promise < void > {
966+ const stateKey = `${ BRANCHES_ASSOCIATED_WITH_PRS } .${ this . repository . rootUri . fsPath } ` ;
967+ const hasRun = this . context . globalState . get < boolean > ( stateKey , false ) ;
968+
969+ if ( hasRun ) {
970+ Logger . debug ( 'Branch association has already run for this workspace folder' , this . id ) ;
971+ return ;
972+ }
973+
974+ Logger . appendLine ( 'First activation: associating local branches with PRs' , this . id ) ;
975+
976+ const githubRepositories = this . _githubRepositories ;
977+ if ( ! githubRepositories || ! githubRepositories . length || ! this . repository . getRefs ) {
978+ Logger . debug ( 'No GitHub repositories or getRefs not available, skipping branch association' , this . id ) ;
979+ await this . context . globalState . update ( stateKey , true ) ;
980+ return ;
981+ }
982+
983+ try {
984+ // Only check the 3 most recently used branches to minimize API calls
985+ const localBranches = ( await this . repository . getRefs ( {
986+ pattern : 'refs/heads/' ,
987+ sort : 'committerdate' ,
988+ count : 10
989+ } ) )
990+ . filter ( r => r . name !== undefined )
991+ . map ( r => r . name ! ) ;
992+
993+ Logger . debug ( `Found ${ localBranches . length } local branches to check` , this . id ) ;
994+
995+ const associationResults : boolean [ ] = [ ] ;
996+
997+ // Process all branches (max 3) in parallel
998+ const chunkResults = await Promise . all ( localBranches . map ( async branchName => {
999+ try {
1000+ // Check if this branch already has PR metadata
1001+ const existingMetadata = await PullRequestGitHelper . getMatchingPullRequestMetadataForBranch (
1002+ this . repository ,
1003+ branchName ,
1004+ ) ;
1005+
1006+ if ( existingMetadata ) {
1007+ // Branch already has PR metadata, skip
1008+ return false ;
1009+ }
1010+
1011+ // Get the branch to check its upstream
1012+ const branch = await this . repository . getBranch ( branchName ) ;
1013+ if ( ! branch . upstream ) {
1014+ // No upstream, can't match to a PR
1015+ return false ;
1016+ }
1017+
1018+ // Try to find a matching PR on GitHub
1019+ const remoteName = branch . upstream . remote ;
1020+ const upstreamBranchName = branch . upstream . name ;
1021+
1022+ const githubRepo = githubRepositories . find (
1023+ repo => repo . remote . remoteName === remoteName ,
1024+ ) ;
1025+
1026+ if ( ! githubRepo ) {
1027+ return false ;
1028+ }
1029+
1030+ // Get the metadata of the GitHub repository to find owner
1031+ const metadata = await githubRepo . getMetadata ( ) ;
1032+ if ( ! metadata ?. owner ) {
1033+ return false ;
1034+ }
1035+
1036+ // Search for a PR with this head branch
1037+ const matchingPR = await githubRepo . getPullRequestForBranch ( upstreamBranchName , metadata . owner . login ) ;
1038+
1039+ if ( matchingPR ) {
1040+ Logger . appendLine ( `Found PR #${ matchingPR . number } for branch ${ branchName } , associating...` , this . id ) ;
1041+ await PullRequestGitHelper . associateBranchWithPullRequest (
1042+ this . repository ,
1043+ matchingPR ,
1044+ branchName ,
1045+ ) ;
1046+ return true ;
1047+ }
1048+ return false ;
1049+ } catch ( e ) {
1050+ Logger . debug ( `Error checking branch ${ branchName } : ${ e } ` , this . id ) ;
1051+ // Continue with other branches even if one fails
1052+ return false ;
1053+ }
1054+ } ) ) ;
1055+ associationResults . push ( ...chunkResults ) ;
1056+
1057+ const associatedCount = associationResults . filter ( r => r ) . length ;
1058+ Logger . appendLine ( `Branch association complete: ${ associatedCount } branches associated with PRs` , this . id ) ;
1059+ } catch ( e ) {
1060+ Logger . error ( `Error during branch association: ${ e } ` , this . id ) ;
1061+ } finally {
1062+ // Mark as complete even if there were errors
1063+ await this . context . globalState . update ( stateKey , true ) ;
1064+ }
1065+ }
1066+
9561067 async getLabels ( issue ?: IssueModel , repoInfo ?: { owner : string ; repo : string } ) : Promise < ILabel [ ] > {
9571068 const repo = issue
9581069 ? issue . githubRepository
0 commit comments