Skip to content

Commit 64f14d4

Browse files
andyrewleeclaude
andauthored
Fix git status pipeline issues (#4)
* Fix git status pipeline issues - amux-0em: Avoid duplicate git fetch on cache miss by adding UpdateCache() method to StatusManager and using it instead of RequestRefresh() - amux-206: Rebuild dashboard rows when GitStatusResult arrives and dirty filter is active, so filtered view stays accurate - amux-pkp: Fix sidebar refresh to use messages.GitStatusResult instead of unhandled local StatusRefreshed type - amux-8ix: Activate FileWatcher by wiring callback to emit events via channel and implementing startFileWatcher() to listen 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Fix gofmt formatting issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 0179555 commit 64f14d4

File tree

4 files changed

+38
-16
lines changed

4 files changed

+38
-16
lines changed

internal/app/app.go

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ type App struct {
6969
// Git status management
7070
statusManager *git.StatusManager
7171
fileWatcher *git.FileWatcher
72+
fileWatcherCh chan messages.FileWatcherEvent
7273

7374
// Layout
7475
width, height int
@@ -100,8 +101,17 @@ func New() (*App, error) {
100101
// Create status manager (callback will be nil, we use it for caching only)
101102
statusManager := git.NewStatusManager(nil)
102103

103-
// Create file watcher (may fail, that's ok)
104-
fileWatcher, _ := git.NewFileWatcher(nil)
104+
// Create file watcher event channel
105+
fileWatcherCh := make(chan messages.FileWatcherEvent, 10)
106+
107+
// Create file watcher with callback that sends to channel
108+
fileWatcher, _ := git.NewFileWatcher(func(root string) {
109+
select {
110+
case fileWatcherCh <- messages.FileWatcherEvent{Root: root}:
111+
default:
112+
// Channel full, drop event (will catch on next change)
113+
}
114+
})
105115

106116
return &App{
107117
config: cfg,
@@ -110,6 +120,7 @@ func New() (*App, error) {
110120
scripts: scripts,
111121
statusManager: statusManager,
112122
fileWatcher: fileWatcher,
123+
fileWatcherCh: fileWatcherCh,
113124
layout: layout.NewManager(),
114125
dashboard: dashboard.New(),
115126
center: center.New(cfg),
@@ -145,10 +156,12 @@ func (a *App) startGitStatusTicker() tea.Cmd {
145156

146157
// startFileWatcher starts watching for file changes and returns events
147158
func (a *App) startFileWatcher() tea.Cmd {
148-
if a.fileWatcher == nil {
159+
if a.fileWatcher == nil || a.fileWatcherCh == nil {
149160
return nil
150161
}
151-
return nil // File watcher runs in background, we'll handle it differently
162+
return func() tea.Msg {
163+
return <-a.fileWatcherCh
164+
}
152165
}
153166

154167
// Update handles all messages
@@ -441,6 +454,8 @@ func (a *App) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
441454
// File changed, invalidate cache and refresh
442455
a.statusManager.Invalidate(msg.Root)
443456
cmds = append(cmds, a.requestGitStatus(msg.Root))
457+
// Continue listening for file changes
458+
cmds = append(cmds, a.startFileWatcher())
444459

445460
case messages.Error:
446461
a.err = msg.Err
@@ -801,9 +816,9 @@ func (a *App) loadProjects() tea.Cmd {
801816
func (a *App) requestGitStatus(root string) tea.Cmd {
802817
return func() tea.Msg {
803818
status, err := git.GetStatus(root)
804-
// Update cache
819+
// Update cache directly (no async refresh needed, we just fetched)
805820
if a.statusManager != nil && err == nil {
806-
a.statusManager.RequestRefresh(root)
821+
a.statusManager.UpdateCache(root, status)
807822
}
808823
return messages.GitStatusResult{
809824
Root: root,

internal/git/status_manager.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,16 @@ func (m *StatusManager) Invalidate(root string) {
136136
delete(m.cache, root)
137137
}
138138

139+
// UpdateCache directly updates the cache with a status result (no fetch)
140+
func (m *StatusManager) UpdateCache(root string, status *StatusResult) {
141+
m.mu.Lock()
142+
defer m.mu.Unlock()
143+
m.cache[root] = &StatusCache{
144+
Status: status,
145+
FetchedAt: time.Now(),
146+
}
147+
}
148+
139149
// InvalidateAll clears the entire cache
140150
func (m *StatusManager) InvalidateAll() {
141151
m.mu.Lock()

internal/ui/dashboard/model.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,10 @@ func (m *Model) Update(msg tea.Msg) (*Model, tea.Cmd) {
149149
if msg.Err == nil {
150150
m.statusCache[msg.Root] = msg.Status
151151
}
152+
// Rebuild rows if dirty filter is active (status change may affect visibility)
153+
if m.filterDirty {
154+
m.rebuildRows()
155+
}
152156

153157
case messages.WorktreeActivated:
154158
if msg.Worktree != nil {

internal/ui/sidebar/model.go

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313

1414
"github.com/andyrewlee/amux/internal/data"
1515
"github.com/andyrewlee/amux/internal/git"
16+
"github.com/andyrewlee/amux/internal/messages"
1617
"github.com/andyrewlee/amux/internal/ui/common"
1718
)
1819

@@ -96,9 +97,6 @@ func (m *Model) Update(msg tea.Msg) (*Model, tea.Cmd) {
9697
case key.Matches(msg, key.NewBinding(key.WithKeys("g"))):
9798
cmds = append(cmds, m.refreshStatus())
9899
}
99-
100-
case git.StatusResult:
101-
// This would come from a refresh command
102100
}
103101

104102
return m, tea.Batch(cmds...)
@@ -428,16 +426,11 @@ func (m *Model) refreshStatus() tea.Cmd {
428426

429427
root := m.worktree.Root
430428
return func() tea.Msg {
431-
status, _ := git.GetStatus(root)
432-
return StatusRefreshed{Status: status}
429+
status, err := git.GetStatus(root)
430+
return messages.GitStatusResult{Root: root, Status: status, Err: err}
433431
}
434432
}
435433

436-
// StatusRefreshed is sent when status is refreshed
437-
type StatusRefreshed struct {
438-
Status *git.StatusResult
439-
}
440-
441434
// SetSize sets the sidebar size
442435
func (m *Model) SetSize(width, height int) {
443436
m.width = width

0 commit comments

Comments
 (0)