Git Operations
Package: forge.lthn.ai/core/go-scm/git
Multi-repository git utilities providing parallel status checks, push/pull operations, and integration with the Core framework as a registered service.
Overview
The git package wraps git CLI commands to operate across multiple repositories simultaneously. It is used by the core dev CLI commands (work, status, push, pull) to manage the federated monorepo.
RepoStatus
The RepoStatus struct captures a snapshot of a single repository's state:
type RepoStatus struct {
Name string // Display name
Path string // Filesystem path
Modified int // Files modified in working tree
Untracked int // Untracked files
Staged int // Files staged in index
Ahead int // Commits ahead of upstream
Behind int // Commits behind upstream
Branch string // Current branch name
Error error // Any error during status check
}
Helper Methods
| Method | Returns | Description |
|---|---|---|
IsDirty() |
bool |
Has uncommitted changes (Modified > 0 or Untracked > 0 or Staged > 0) |
HasUnpushed() |
bool |
Has commits to push (Ahead > 0) |
HasUnpulled() |
bool |
Has commits to pull (Behind > 0) |
Parallel Status Checks
The Status function checks multiple repositories concurrently using goroutines and a WaitGroup:
statuses := git.Status(ctx, git.StatusOptions{
Paths: []string{
"/Users/snider/Code/host-uk/core-php",
"/Users/snider/Code/host-uk/core-tenant",
"/Users/snider/Code/host-uk/core-admin",
},
Names: map[string]string{
"/Users/snider/Code/host-uk/core-php": "core-php",
"/Users/snider/Code/host-uk/core-tenant": "core-tenant",
"/Users/snider/Code/host-uk/core-admin": "core-admin",
},
})
for _, s := range statuses {
if s.Error != nil {
fmt.Printf("%s: error: %v\n", s.Name, s.Error)
continue
}
if s.IsDirty() {
fmt.Printf("%s [%s]: %d modified, %d untracked, %d staged\n",
s.Name, s.Branch, s.Modified, s.Untracked, s.Staged)
}
if s.HasUnpushed() {
fmt.Printf("%s: %d commits ahead\n", s.Name, s.Ahead)
}
}
StatusOptions
type StatusOptions struct {
Paths []string // Repository paths to check
Names map[string]string // Maps paths to display names
}
Push and Pull
Push and pull operations use interactive mode to support SSH passphrase prompts:
// Push a single repository
err := git.Push(ctx, "/path/to/repo")
// Pull a single repository (with --rebase)
err := git.Pull(ctx, "/path/to/repo")
// Check if error is a non-fast-forward rejection
if git.IsNonFastForward(err) {
fmt.Println("Need to pull first")
}
PushMultiple
Push multiple repositories sequentially (sequential because SSH passphrase prompts need user interaction):
results := git.PushMultiple(ctx, paths, names)
for _, r := range results {
if r.Success {
fmt.Printf("%s: pushed successfully\n", r.Name)
} else {
fmt.Printf("%s: push failed: %v\n", r.Name, r.Error)
}
}
PushResult
type PushResult struct {
Name string
Path string
Success bool
Error error
}
GitError
Git command errors include captured stderr output for better diagnostics:
type GitError struct {
Err error // Underlying exec error
Stderr string // Captured stderr output
}
func (e *GitError) Error() string // Returns stderr if available, else Err.Error()
func (e *GitError) Unwrap() error // Returns Err for error chain inspection
Core Service Integration
The git package integrates with the Core framework as a registered service, exposing git operations via the query/task message-passing system:
import (
"forge.lthn.ai/core/go/pkg/framework"
"forge.lthn.ai/core/go-scm/git"
)
core, err := framework.New(
framework.WithService(git.NewService(git.ServiceOptions{
WorkDir: "/Users/snider/Code/host-uk",
})),
)
Queries
| Query Type | Returns | Description |
|---|---|---|
QueryStatus{Paths, Names} |
[]RepoStatus |
Run parallel status check |
QueryDirtyRepos{} |
[]RepoStatus |
Get repos with uncommitted changes (from last status) |
QueryAheadRepos{} |
[]RepoStatus |
Get repos with unpushed commits (from last status) |
Tasks
| Task Type | Description |
|---|---|
TaskPush{Path, Name} |
Push a single repository |
TaskPull{Path, Name} |
Pull a single repository |
TaskPushMultiple{Paths, Names} |
Push multiple repositories sequentially |
Service Methods
The service also exposes direct method access:
svc := framework.ServiceFor[*git.Service](core)
// Get last status results
statuses := svc.Status()
// Get filtered views
dirty := svc.DirtyRepos()
ahead := svc.AheadRepos()
Implementation Details
- Parallel execution:
Statususes one goroutine per repository withsync.WaitGroup, maintaining result order via indexed slice - Branch detection: Uses
git rev-parse --abbrev-ref HEAD - Status parsing: Uses
git status --porcelainand parses the two-character status codes (X = index, Y = working tree) - Ahead/behind: Uses
git rev-list --count @{u}..HEADandHEAD..@{u}respectively; silently returns 0 if no upstream is configured - Interactive push/pull: Connects stdin/stdout/stderr to the terminal for SSH passphrase prompts, while also capturing stderr via
io.MultiWriterfor error reporting
See Also
- Home -- Package overview and quick start
- Job-Runner -- Automated pipeline that may trigger push operations