@@ -4,6 +4,7 @@ import SwiftUI
44struct BranchListView : View {
55 @EnvironmentObject var appState : AppState
66 @StateObject private var viewModel = BranchListViewModel ( )
7+ @ObservedObject private var prTracker = BranchPRTracker . shared
78 @State private var searchText = " "
89 @State private var showNewBranchSheet = false
910 @State private var selectedBranch : Branch ?
@@ -115,18 +116,31 @@ struct BranchListView: View {
115116 . task {
116117 if let repo = appState. currentRepository {
117118 viewModel. loadBranches ( from: repo)
119+ // Configure PR tracker for this repository
120+ await prTracker. configure ( forRepoAt: repo. path)
118121 }
119122 }
120- . onChange ( of: appState. currentRepository? . path) { _, _ in
123+ . onChange ( of: appState. currentRepository? . path) { _, newPath in
121124 if let repo = appState. currentRepository {
122125 viewModel. loadBranches ( from: repo)
126+ // Reconfigure PR tracker when repo changes
127+ Task { await prTracker. configure ( forRepoAt: repo. path) }
123128 }
124129 }
125130 . onReceive ( NotificationCenter . default. publisher ( for: . repositoryDidRefresh) ) { notification in
126131 if let repo = appState. currentRepository {
127132 viewModel. loadBranches ( from: repo)
128133 }
129134 }
135+ . onReceive ( NotificationCenter . default. publisher ( for: . branchPRsDidUpdate) ) { _ in
136+ // Force view refresh when PR data updates
137+ // Trigger cache update to force SwiftUI re-render
138+ updateFilterCache ( )
139+ }
140+ . onChange ( of: prTracker. branchPRs. count) { _, _ in
141+ // Also react to PR count changes for immediate UI update
142+ updateFilterCache ( )
143+ }
130144 // Phase 0.2: Cache updates for performance
131145 . onChange ( of: searchText) { _, newValue in
132146 updateFilterCache ( )
@@ -227,7 +241,10 @@ struct BranchListView: View {
227241
228242
229243 private func branchRowView( for branch: Branch ) -> some View {
230- Group {
244+ // Get PR for this branch (if any)
245+ let pr = prTracker. getPR ( for: branch. name)
246+
247+ return Group {
231248 BranchRow (
232249 branch: branch,
233250 isSelected: selectedBranch? . id == branch. id,
@@ -254,10 +271,29 @@ struct BranchListView: View {
254271 selectedBranch = branch
255272 showRebaseSheet = true
256273 } ,
274+ // PR integration
275+ pullRequest: pr,
276+ onCreatePR: branch. isRemote ? nil : {
277+ selectedBranch = branch
278+ showPRSheet = true
279+ } ,
280+ onViewPR: { prItem in
281+ // Open PR in browser
282+ if let url = URL ( string: prItem. htmlUrl) {
283+ NSWorkspace . shared. open ( url)
284+ }
285+ } ,
286+ onMergePR: { prItem, method in
287+ Task {
288+ try ? await prTracker. mergePR ( prItem, method: method)
289+ }
290+ } ,
257291 onBranchDropped: { droppedBranch in
258292 handleBranchDrop ( dragged: droppedBranch, onto: branch)
259293 }
260294 )
295+ // Force re-render when PR state changes for this branch
296+ . id ( " \( branch. id) - \( pr? . number ?? 0 ) " )
261297 }
262298 }
263299
@@ -434,6 +470,9 @@ class BranchListViewModel: ObservableObject {
434470 // Post both notifications for full sync
435471 NotificationCenter . default. post ( name: . branchDidCheckout, object: branchName)
436472 NotificationCenter . default. post ( name: . repositoryDidRefresh, object: path)
473+
474+ // Refresh PR tracker after checkout
475+ await BranchPRTracker . shared. refresh ( )
437476 } catch let gitError as GitError {
438477 self . error = gitError. localizedDescription
439478 if let fix = gitError. suggestedFix {
@@ -537,6 +576,9 @@ class BranchListViewModel: ObservableObject {
537576 // Post notifications for full sync
538577 NotificationCenter . default. post ( name: . branchDidCheckout, object: localName)
539578 NotificationCenter . default. post ( name: . repositoryDidRefresh, object: path)
579+
580+ // Refresh PR tracker after checkout
581+ await BranchPRTracker . shared. refresh ( )
540582 } catch {
541583 self . error = error. localizedDescription
542584 }
@@ -568,6 +610,9 @@ class BranchListViewModel: ObservableObject {
568610
569611 // Notify UI
570612 NotificationCenter . default. post ( name: . repositoryDidRefresh, object: path)
613+
614+ // Refresh PR tracker after delete (PR may have been closed)
615+ await BranchPRTracker . shared. refresh ( )
571616 } catch {
572617 self . error = error. localizedDescription
573618 }
@@ -584,6 +629,9 @@ class BranchListViewModel: ObservableObject {
584629 // Notify UI
585630 NotificationCenter . default. post ( name: . repositoryDidRefresh, object: path)
586631
632+ // Refresh PR tracker after merge
633+ await BranchPRTracker . shared. refresh ( )
634+
587635 // Track successful merge
588636 RemoteOperationTracker . shared. recordMerge (
589637 success: true ,
@@ -622,24 +670,27 @@ class BranchListViewModel: ObservableObject {
622670 guard let path = currentRepoPath else { return }
623671 isLoading = true
624672 do {
625- if branch. isHead {
626- try await engine. push ( at: path)
673+ // Push the specific branch (works for both HEAD and non-HEAD branches)
674+ let options = PushOptions ( branch: branch. name)
675+ try await engine. push ( options: options, at: path)
627676
628- // Notify UI
629- NotificationCenter . default. post ( name: . remoteOperationCompleted, object: " push " )
630- NotificationCenter . default. post ( name: . repositoryDidRefresh, object: path)
631- GitHubSyncManager . shared. notifyOperationCompleted ( type: . push, details: branch. name)
677+ // Notify UI
678+ NotificationCenter . default. post ( name: . remoteOperationCompleted, object: " push " )
679+ NotificationCenter . default. post ( name: . repositoryDidRefresh, object: path)
680+ GitHubSyncManager . shared. notifyOperationCompleted ( type: . push, details: branch. name)
632681
633- NotificationManager . shared. success (
634- " Pushed ' \( branch. name) ' " ,
635- detail: " Changes pushed to remote "
636- )
637- RemoteOperationTracker . shared. recordPush (
638- success: true ,
639- branch: branch. name,
640- remote: " origin "
641- )
642- }
682+ // Refresh PR tracker immediately after push
683+ await BranchPRTracker . shared. refresh ( )
684+
685+ NotificationManager . shared. success (
686+ " Pushed ' \( branch. name) ' " ,
687+ detail: " Changes pushed to remote "
688+ )
689+ RemoteOperationTracker . shared. recordPush (
690+ success: true ,
691+ branch: branch. name,
692+ remote: " origin "
693+ )
643694 } catch let gitError as GitError {
644695 self . error = gitError. localizedDescription
645696 RemoteOperationTracker . shared. recordPush (
@@ -690,6 +741,9 @@ class BranchListViewModel: ObservableObject {
690741 NotificationCenter . default. post ( name: . repositoryDidRefresh, object: path)
691742 GitHubSyncManager . shared. notifyOperationCompleted ( type: . pull, details: branch. name)
692743
744+ // Refresh PR tracker immediately after pull
745+ await BranchPRTracker . shared. refresh ( )
746+
693747 NotificationManager . shared. success (
694748 " Pulled ' \( branch. name) ' " ,
695749 detail: " Updated from remote "
@@ -1118,6 +1172,9 @@ struct CreatePullRequestSheet: View {
11181172 detail: title
11191173 )
11201174
1175+ // Refresh PR tracker immediately so branch context menu updates
1176+ await BranchPRTracker . shared. refresh ( )
1177+
11211178 // Post notification to refresh PR data across the app
11221179 NotificationCenter . default. post ( name: . pullRequestCreated, object: newPR)
11231180 NotificationCenter . default. post ( name: . repositoryDidRefresh, object: repo. path)
0 commit comments