Files
mg/AGENTS.md
Tai Groot 18cb0e8f65 feat(cmd): implement push, diff, and commit commands
Implement three previously-stubbed commands following the existing
parallel execution pattern used by clone/pull/fetch:

- push: pushes all refs to origin for all configured repos
- diff: shows file-level uncommitted changes across all repos
- commit: commits staged changes across all repos with a shared message (-m)

All commands support parallel execution via -j flag.
Update AGENTS.md to reflect current implementation status.
2026-04-07 08:36:16 +00:00

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 -j flag
  • Compatible with existing ~/.mrconfig files (auto-migrates to mgconfig)
  • 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      # Push all repos (implemented)
│   │       ├── fetch.go     # Fetch all repos (implemented)
│   │       ├── status.go    # Status all repos (implemented)
│   │       ├── diff.go      # Diff all repos (implemented)
│   │       ├── commit.go    # Commit staged changes (implemented)
│   │       └── 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 Implemented Parallel via -j, pushes all refs
fetch Implemented Parallel via -j
status Implemented Parallel via -j, sorted output
diff Implemented Parallel via -j, file-level diff
commit Implemented Parallel via -j, -m message
config Stub Prints "config called"

Configuration

Config File Location

  1. $MGCONFIG environment variable (if set)
  2. $XDG_CONFIG_HOME/mgconfig
  3. ~/.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

  1. Create cmd/mg/cmd/<name>.go
  2. Define a cobra.Command variable
  3. Register in init() via rootCmd.AddCommand()
  4. For parallel operations, follow the pattern in clone.go or pull.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 $HOME prefix for portability
  • GetConfig() in common.go expands $HOME to actual home directory at runtime
  • register command stores paths with $HOME prefix

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 implementation
  • github.com/spf13/cobra - CLI framework

Testing

Tests are minimal. Only parse/myrepos_test.go exists with a skeleton structure:

go test ./...

Known Issues / TODOs

  1. config command is still a stub
  2. Test coverage is minimal for cmd package (parse package has good coverage)
  3. No integration tests for commands that interact with git repos

Error Handling Pattern

Commands typically:

  1. Log errors via log.Println(err)
  2. Exit with os.Exit(1) on fatal errors
  3. Collect errors during parallel operations and report summary at end