mirror of
https://github.com/taigrr/mg.git
synced 2026-04-02 03:28:42 -07:00
6.2 KiB
6.2 KiB
AGENTS.md
Agent guide for the mg codebase - a Go replacement for myrepos that only supports git repos.
Project Overview
mg is a CLI tool for managing multiple git repositories simultaneously. It uses go-git/go-git for pure Go git operations (no external git dependency required) and spf13/cobra for CLI structure.
Key Features
- Parallel operations via
-jflag - Compatible with existing
~/.mrconfigfiles (auto-migrates tomgconfig) - Pure Go implementation - no external git binary needed
- Embeddable as a library
Commands
# Build
go build ./...
# Run tests
go test ./...
# Install the binary
go install ./cmd/mg
# Run directly
go run ./cmd/mg <command>
Project Structure
mg/
├── cmd/
│ ├── mg/
│ │ ├── main.go # Entry point
│ │ └── cmd/ # Cobra commands
│ │ ├── root.go # Root command setup
│ │ ├── common.go # Shared utilities (GetConfig)
│ │ ├── clone.go # Clone all repos (implemented)
│ │ ├── pull.go # Pull all repos (implemented)
│ │ ├── register.go # Register repo (implemented)
│ │ ├── unregister.go# Unregister repo (implemented)
│ │ ├── import.go # Merge configs (implemented)
│ │ ├── push.go # Stub
│ │ ├── fetch.go # Stub
│ │ ├── status.go # Stub
│ │ ├── diff.go # Stub
│ │ ├── commit.go # Stub
│ │ └── config.go # Stub
│ └── paths/
│ └── mrpaths.go # Utility to list repo paths from mrconfig
└── parse/
├── mgconf.go # MGConfig: JSON-based config format
├── myrepos.go # MRConfig: Parse ~/.mrconfig (INI-style)
└── myrepos_test.go # Tests (skeleton)
Implementation Status
| Command | Status | Notes |
|---|---|---|
clone |
Implemented | Parallel via -j, creates dirs |
pull |
Implemented | Parallel via -j |
register |
Implemented | Detects git root, stores $HOME |
unregister |
Implemented | By path or current dir |
import |
Implemented | Merge configs, supports stdin - |
push |
Stub | Prints "push called" |
fetch |
Stub | Prints "fetch called" |
status |
Stub | Prints "status called" |
diff |
Stub | Prints "diff called" |
commit |
Stub | Prints "commit called" |
config |
Stub | Prints "config called" |
Configuration
Config File Location
$MGCONFIGenvironment variable (if set)$XDG_CONFIG_HOME/mgconfig~/.config/mgconfig
Config Format (JSON)
{
"Repos": [
{
"Path": "$HOME/code/project",
"Remote": "git@github.com:user/project.git"
}
],
"Aliases": {}
}
Migration from myrepos
If no mgconfig exists but ~/.mrconfig does, mg auto-migrates on first run. The MRConfig.ToMGConfig() method handles conversion.
Code Patterns
Adding a New Command
- Create
cmd/mg/cmd/<name>.go - Define a
cobra.Commandvariable - Register in
init()viarootCmd.AddCommand() - For parallel operations, follow the pattern in
clone.goorpull.go:
var myCmd = &cobra.Command{
Use: "mycommand",
Short: "description",
Run: func(_ *cobra.Command, args []string) {
conf := GetConfig() // Load config with fallback to mrconfig
// Implementation...
},
}
func init() {
rootCmd.AddCommand(myCmd)
myCmd.Flags().IntVarP(&jobs, "jobs", "j", 1, "number of parallel jobs")
}
Parallel Execution Pattern
Used in clone.go and pull.go:
repoChan := make(chan RepoType, len(repos))
wg := sync.WaitGroup{}
mutex := sync.Mutex{}
errs := []Error{}
wg.Add(len(repos))
for i := 0; i < jobs; i++ {
go func() {
for repo := range repoChan {
// Do work
// Use mutex for shared state (errs, counters)
wg.Done()
}
}()
}
for _, repo := range repos {
repoChan <- repo
}
close(repoChan)
wg.Wait()
Git Operations
Use go-git/go-git/v5:
import git "github.com/go-git/go-git/v5"
// Open repo (detects .git in parent dirs)
r, err := git.PlainOpenWithOptions(path, &git.PlainOpenOptions{DetectDotGit: true})
// Clone
_, err = git.PlainClone(path, false, &git.CloneOptions{URL: remote})
// Pull
w, _ := r.Worktree()
err = w.Pull(&git.PullOptions{})
// Check: err == git.NoErrAlreadyUpToDate
Path Handling
- Paths in config use
$HOMEprefix for portability GetConfig()incommon.goexpands$HOMEto actual home directory at runtimeregistercommand stores paths with$HOMEprefix
Key Types
parse.MGConfig
type MGConfig struct {
Repos []Repo
Aliases map[string]string
}
// Methods
func LoadMGConfig() (MGConfig, error)
func (m *MGConfig) AddRepo(path, remote string) error
func (m *MGConfig) DelRepo(path string) error
func (m *MGConfig) Merge(m2 MGConfig) (Stats, error)
func (m MGConfig) Save() error
parse.Repo
type Repo struct {
Path string
Remote string
Aliases map[string]string `json:"aliases,omitempty"`
}
Dependencies
github.com/go-git/go-git/v5- Pure Go git implementationgithub.com/spf13/cobra- CLI framework
Testing
Tests are minimal. Only parse/myrepos_test.go exists with a skeleton structure:
go test ./...
Known Issues / TODOs
- Several commands are stubs (push, fetch, status, diff, commit, config)
parse/mgconf.go:61has a hint about inefficient string concatenation in a loop- Test coverage is minimal
unregistercommand short description incorrectly says "add current path" (copy-paste error)
Error Handling Pattern
Commands typically:
- Log errors via
log.Println(err) - Exit with
os.Exit(1)on fatal errors - Collect errors during parallel operations and report summary at end