feat(cc): add git branch and dirty status to statusline#202
Conversation
Add git branch name and dirty status to the cc statusline output. The git information is displayed first in the statusline, showing the branch name with an asterisk suffix if there are uncommitted changes. - Add WorkingDirectory field to CCStatuslineInput (from Claude Code) - Extend CCInfoRequest/Response with git info fields - Create daemon/git.go using go-git for branch/dirty detection - Update statusline format: 🌿 main* | 🤖 model | 💰 $x | ... 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Summary of ChangesHello @AnnatarHe, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a significant enhancement to the Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request adds a great new feature to the statusline, showing the current Git branch and dirty status. The implementation using go-git is a good choice to avoid shelling out to an external git process. The code is well-structured, with the logic cleanly separated into a new daemon/git.go module and integrated into the existing daemon and client code. The addition of comprehensive tests for the new git functionality is also excellent.
I have a few suggestions for improvement:
- In
daemon/git.go, errors encountered while fetching git information are silently ignored. Adding debug logging would make it easier to diagnose issues in the future. - The new tests in
daemon/git_test.gocould be made more robust by checking for errors during the test setup phase. - The payload parsing in
daemon/socket.gocould be made more robust.
A general consideration is the performance of go-git's Worktree.Status() method, which can be slow on very large repositories. Given the tight 50ms timeout for the daemon request, this could potentially cause timeouts. This is a trade-off for using a pure Go implementation, but it's something to be aware of.
| head, err := repo.Head() | ||
| if err == nil { | ||
| info.Branch = head.Name().Short() | ||
| } | ||
|
|
||
| // Check dirty status by examining worktree status | ||
| worktree, err := repo.Worktree() | ||
| if err == nil { | ||
| status, err := worktree.Status() | ||
| if err == nil { | ||
| info.Dirty = !status.IsClean() | ||
| } | ||
| } |
There was a problem hiding this comment.
The errors from repo.Head(), repo.Worktree(), and worktree.Status() are silently ignored. While this ensures the function doesn't fail for the statusline, it can make debugging issues with git state detection difficult. For example, if getting the branch name fails for an unexpected reason, it will just be blank with no indication of why. Consider adding debug-level logging for these errors. You will also need to add import "log/slog" to this file.
head, err := repo.Head()
if err != nil {
slog.Debug("failed to get git HEAD", "err", err, "dir", workingDir)
} else {
info.Branch = head.Name().Short()
}
// Check dirty status by examining worktree status
worktree, err := repo.Worktree()
if err != nil {
slog.Debug("failed to get git worktree", "err", err, "dir", workingDir)
} else {
status, err := worktree.Status()
if err != nil {
slog.Debug("failed to get worktree status", "err", err, "dir", workingDir)
} else {
info.Dirty = !status.IsClean()
}
}| exec.Command("git", "-C", s.tempDir, "config", "user.email", "test@test.com").Run() | ||
| exec.Command("git", "-C", s.tempDir, "config", "user.name", "Test User").Run() |
There was a problem hiding this comment.
The exec.Command calls to configure the git user are not checking for errors. If these commands fail, the subsequent git commit will also fail, but the root cause will be less obvious. It's good practice to assert that setup commands in tests execute successfully to make tests more robust and easier to debug. This applies to the other test cases in this file as well.
| exec.Command("git", "-C", s.tempDir, "config", "user.email", "test@test.com").Run() | |
| exec.Command("git", "-C", s.tempDir, "config", "user.name", "Test User").Run() | |
| assert.NoError(s.T(), exec.Command("git", "-C", s.tempDir, "config", "user.email", "test@test.com").Run()) | |
| assert.NoError(s.T(), exec.Command("git", "-C", s.tempDir, "config", "user.name", "Test User").Run()) |
| timeRange := CCInfoTimeRangeToday | ||
| var workingDir string | ||
| if payload, ok := msg.Payload.(map[string]interface{}); ok { | ||
| if tr, ok := payload["timeRange"].(string); ok { | ||
| timeRange = CCInfoTimeRange(tr) | ||
| } | ||
| if wd, ok := payload["workingDirectory"].(string); ok { | ||
| workingDir = wd | ||
| } | ||
| } |
There was a problem hiding this comment.
Parsing the payload using type assertions on a map[string]interface{} is fragile. For example, if the client sends a non-string value for timeRange, it will be silently ignored. A more robust approach is to unmarshal the payload into the CCInfoRequest struct. This ensures proper type handling and validation. This pattern is already used in the tests (e.g., daemon/cc_info_handler_test.go:324-326).
| timeRange := CCInfoTimeRangeToday | |
| var workingDir string | |
| if payload, ok := msg.Payload.(map[string]interface{}); ok { | |
| if tr, ok := payload["timeRange"].(string); ok { | |
| timeRange = CCInfoTimeRange(tr) | |
| } | |
| if wd, ok := payload["workingDirectory"].(string); ok { | |
| workingDir = wd | |
| } | |
| } | |
| var req CCInfoRequest | |
| req.TimeRange = CCInfoTimeRangeToday // Default | |
| if payload, ok := msg.Payload.(map[string]interface{}); ok { | |
| if b, err := json.Marshal(payload); err == nil { | |
| // This is more robust than type assertions. | |
| // Ignore unmarshal error, fields will be zero values if it fails. | |
| _ = json.Unmarshal(b, &req) | |
| } | |
| } | |
| timeRange := req.TimeRange | |
| workingDir := req.WorkingDirectory |
Update test function signatures to match new formatStatuslineOutput and getDaemonInfoWithFallback APIs. Add tests for git branch display including dirty branch indicator. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 1 file with indirect coverage changes 🚀 New features to boost your workflow:
|
Summary
cc statuslineoutput🌿 main* | 🤖 model | 💰 $x | ...*suffix when repo has uncommitted changesChanges
model/cc_statusline_types.go: AddWorkingDirectoryto inputdaemon/socket.go: ExtendCCInfoRequest/CCInfoResponsewith git fieldsdaemon/git.go: New module for git branch/dirty detectiondaemon/git_test.go: Tests for git detectioncommands/cc_statusline.go: Display git info first in outputTest plan
go build ./...)🤖 Generated with Claude Code