6 Commits

Author SHA1 Message Date
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
6bd40cb1d2 Merge pull request #4 from taigrr/cd/dep-updates-go126
feat(cmd): implement fetch and status commands, update deps
2026-03-07 02:43:48 -05:00
348b87702d feat(cmd): implement fetch and status commands, update deps to go1.26.1
- Implement fetch command: fetches all registered repos without merging,
  supports parallel execution via -j/--jobs flag
- Implement status command: shows uncommitted changes across all repos
  with per-file-type counts, supports parallel execution
- Update Go to 1.26.1
- Update go-git v5.16.5 -> v5.17.0, go-crypto v1.3.0 -> v1.4.0,
  go-billy v5.7.0 -> v5.8.0, x/net v0.50.0 -> v0.51.0
- Fix unregister command description (said 'add' instead of 'remove')
- Fix goimports formatting in mgconf_test.go
2026-03-07 07:03:49 +00:00
9d4ac7bc47 Merge pull request #2 from taigrr/burrow/refactor-alias-loading
refactor: dynamically load all [DEFAULT] section aliases
2026-02-23 00:04:21 -05:00
a4044ba47a Merge pull request #3 from taigrr/burrow/dep-update-feb-2026
chore(deps): update Go dependencies
2026-02-22 20:59:39 -05:00
9ad9412a8c refactor: dynamically load all [DEFAULT] section aliases
Removed hardcoded alias handling (unregister, git_gc) in favor of
dynamically loading all key-value pairs from the [DEFAULT] section
into the Aliases map. This allows mrconfig files to define custom
aliases without code changes.

- Simplified switch statement in LoadMRConfig
- Updated function documentation
- All tests passing
2026-02-12 13:33:30 +00:00
11 changed files with 560 additions and 62 deletions

View File

@@ -44,11 +44,11 @@ mg/
│ │ ├── 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
│ │ ├── 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
@@ -67,11 +67,11 @@ mg/
| `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" |
| `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
@@ -221,10 +221,9 @@ go test ./...
## Known Issues / TODOs
1. Several commands are stubs (push, fetch, status, diff, commit, config)
2. `parse/mgconf.go:61` has a hint about inefficient string concatenation in a loop
3. Test coverage is minimal
4. `unregister` command short description incorrectly says "add current path" (copy-paste error)
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

View File

@@ -2,19 +2,122 @@ package cmd
import (
"fmt"
"log"
"os"
"sync"
git "github.com/go-git/go-git/v5"
"github.com/spf13/cobra"
)
var commitMessage string
// commitCmd represents the commit command
var commitCmd = &cobra.Command{
Use: "commit",
Short: "commit all current repos with the same message",
Short: "commit staged changes across all repos with the same message",
Run: func(_ *cobra.Command, args []string) {
fmt.Println("commit called")
type RepoError struct {
Error error
Repo string
}
if jobs < 1 {
log.Println("jobs must be greater than 0")
os.Exit(1)
}
if commitMessage == "" {
log.Println("commit message is required (-m)")
os.Exit(1)
}
conf := GetConfig()
if len(args) > 0 {
log.Println("too many arguments")
os.Exit(1)
}
repoChan := make(chan string, len(conf.Repos))
var (
errs []RepoError
skipped int
mutex sync.Mutex
wg sync.WaitGroup
)
wg.Add(len(conf.Repos))
for i := 0; i < jobs; i++ {
go func() {
for repo := range repoChan {
r, err := git.PlainOpenWithOptions(repo, &git.PlainOpenOptions{DetectDotGit: true})
if err != nil {
mutex.Lock()
errs = append(errs, RepoError{Error: err, Repo: repo})
mutex.Unlock()
wg.Done()
continue
}
w, err := r.Worktree()
if err != nil {
mutex.Lock()
errs = append(errs, RepoError{Error: err, Repo: repo})
mutex.Unlock()
wg.Done()
continue
}
st, err := w.Status()
if err != nil {
mutex.Lock()
errs = append(errs, RepoError{Error: err, Repo: repo})
mutex.Unlock()
wg.Done()
continue
}
// Check if there are any staged changes
hasStagedChanges := false
for _, s := range st {
if s.Staging != git.Unmodified && s.Staging != git.Untracked {
hasStagedChanges = true
break
}
}
if !hasStagedChanges {
mutex.Lock()
skipped++
mutex.Unlock()
fmt.Printf("repo %s: nothing staged to commit\n", repo)
wg.Done()
continue
}
_, err = w.Commit(commitMessage, &git.CommitOptions{})
if err != nil {
mutex.Lock()
errs = append(errs, RepoError{Error: err, Repo: repo})
mutex.Unlock()
log.Printf("commit failed for %s: %v\n", repo, err)
wg.Done()
continue
}
fmt.Printf("successfully committed in %s\n", repo)
wg.Done()
}
}()
}
for _, repo := range conf.Repos {
repoChan <- repo.Path
}
close(repoChan)
wg.Wait()
for _, err := range errs {
log.Printf("error committing %s: %s\n", err.Repo, err.Error)
}
lenErrs := len(errs)
committed := len(conf.Repos) - lenErrs - skipped
fmt.Println()
fmt.Printf("successfully committed %d/%d repos\n", committed, len(conf.Repos))
fmt.Printf("%d repos had nothing staged\n", skipped)
fmt.Printf("failed to commit %d/%d repos\n", lenErrs, len(conf.Repos))
},
}
func init() {
rootCmd.AddCommand(commitCmd)
commitCmd.Flags().IntVarP(&jobs, "jobs", "j", 1, "number of jobs to run in parallel")
commitCmd.Flags().StringVarP(&commitMessage, "message", "m", "", "commit message")
}

View File

@@ -2,19 +2,140 @@ package cmd
import (
"fmt"
"log"
"os"
"sort"
"sync"
git "github.com/go-git/go-git/v5"
"github.com/spf13/cobra"
)
type repoDiff struct {
Path string
Changes []string
}
// diffCmd represents the diff command
var diffCmd = &cobra.Command{
Use: "diff",
Short: "compute a collective diff",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("diff called")
Short: "show uncommitted changes across all repos",
Run: func(_ *cobra.Command, args []string) {
type RepoError struct {
Error error
Repo string
}
if jobs < 1 {
log.Println("jobs must be greater than 0")
os.Exit(1)
}
conf := GetConfig()
if len(args) > 0 {
log.Println("too many arguments")
os.Exit(1)
}
repoChan := make(chan string, len(conf.Repos))
var (
errs []RepoError
diffs []repoDiff
mutex sync.Mutex
wg sync.WaitGroup
)
wg.Add(len(conf.Repos))
for i := 0; i < jobs; i++ {
go func() {
for repo := range repoChan {
r, err := git.PlainOpenWithOptions(repo, &git.PlainOpenOptions{DetectDotGit: true})
if err != nil {
mutex.Lock()
errs = append(errs, RepoError{Error: err, Repo: repo})
mutex.Unlock()
wg.Done()
continue
}
w, err := r.Worktree()
if err != nil {
mutex.Lock()
errs = append(errs, RepoError{Error: err, Repo: repo})
mutex.Unlock()
wg.Done()
continue
}
st, err := w.Status()
if err != nil {
mutex.Lock()
errs = append(errs, RepoError{Error: err, Repo: repo})
mutex.Unlock()
wg.Done()
continue
}
if st.IsClean() {
wg.Done()
continue
}
rd := repoDiff{Path: repo}
for file, status := range st {
code := status.Worktree
if code == git.Unmodified {
code = status.Staging
}
var prefix string
switch code {
case git.Modified:
prefix = "M"
case git.Added:
prefix = "A"
case git.Deleted:
prefix = "D"
case git.Renamed:
prefix = "R"
case git.Copied:
prefix = "C"
case git.Untracked:
prefix = "?"
default:
continue
}
rd.Changes = append(rd.Changes, fmt.Sprintf(" %s %s", prefix, file))
}
sort.Strings(rd.Changes)
mutex.Lock()
diffs = append(diffs, rd)
mutex.Unlock()
wg.Done()
}
}()
}
for _, repo := range conf.Repos {
repoChan <- repo.Path
}
close(repoChan)
wg.Wait()
sort.Slice(diffs, func(i, j int) bool {
return diffs[i].Path < diffs[j].Path
})
for _, rd := range diffs {
fmt.Printf("%s:\n", rd.Path)
for _, change := range rd.Changes {
fmt.Println(change)
}
fmt.Println()
}
for _, err := range errs {
log.Printf("error reading %s: %s\n", err.Repo, err.Error)
}
fmt.Printf("%d/%d repos have changes\n", len(diffs), len(conf.Repos))
if len(errs) > 0 {
fmt.Printf("failed to read %d/%d repos\n", len(errs), len(conf.Repos))
}
},
}
func init() {
rootCmd.AddCommand(diffCmd)
diffCmd.Flags().IntVarP(&jobs, "jobs", "j", 1, "number of jobs to run in parallel")
}

View File

@@ -2,18 +2,90 @@ package cmd
import (
"fmt"
"log"
"os"
"sync"
git "github.com/go-git/go-git/v5"
"github.com/spf13/cobra"
)
var fetchCmd = &cobra.Command{
Use: "fetch",
Short: "fetch all git repos without merging",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("fetch called")
Run: func(_ *cobra.Command, args []string) {
type RepoError struct {
Error error
Repo string
}
if jobs < 1 {
log.Println("jobs must be greater than 0")
os.Exit(1)
}
conf := GetConfig()
if len(args) > 0 {
log.Println("too many arguments")
os.Exit(1)
}
repoChan := make(chan string, len(conf.Repos))
var (
errs []RepoError
alreadyFetched int
mutex sync.Mutex
wg sync.WaitGroup
)
wg.Add(len(conf.Repos))
for i := 0; i < jobs; i++ {
go func() {
for repo := range repoChan {
log.Printf("attempting fetch: %s\n", repo)
r, err := git.PlainOpenWithOptions(repo, &git.PlainOpenOptions{DetectDotGit: true})
if err != nil {
mutex.Lock()
errs = append(errs, RepoError{Error: err, Repo: repo})
mutex.Unlock()
log.Printf("fetch failed for %s: %v\n", repo, err)
wg.Done()
continue
}
err = r.Fetch(&git.FetchOptions{})
if err == git.NoErrAlreadyUpToDate {
mutex.Lock()
alreadyFetched++
mutex.Unlock()
fmt.Printf("repo %s: already up to date\n", repo)
wg.Done()
continue
} else if err != nil {
mutex.Lock()
errs = append(errs, RepoError{Error: err, Repo: repo})
mutex.Unlock()
log.Printf("fetch failed for %s: %v\n", repo, err)
wg.Done()
continue
}
fmt.Printf("successfully fetched %s\n", repo)
wg.Done()
}
}()
}
for _, repo := range conf.Repos {
repoChan <- repo.Path
}
close(repoChan)
wg.Wait()
for _, err := range errs {
log.Printf("error fetching %s: %s\n", err.Repo, err.Error)
}
lenErrs := len(errs)
fmt.Println()
fmt.Printf("successfully fetched %d/%d repos\n", len(conf.Repos)-lenErrs, len(conf.Repos))
fmt.Printf("%d repos already up to date\n", alreadyFetched)
fmt.Printf("failed to fetch %d/%d repos\n", lenErrs, len(conf.Repos))
},
}
func init() {
rootCmd.AddCommand(fetchCmd)
fetchCmd.Flags().IntVarP(&jobs, "jobs", "j", 1, "number of jobs to run in parallel")
}

View File

@@ -2,7 +2,12 @@ package cmd
import (
"fmt"
"log"
"os"
"sync"
git "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config"
"github.com/spf13/cobra"
)
@@ -10,11 +15,81 @@ import (
var pushCmd = &cobra.Command{
Use: "push",
Short: "push all git repos",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("push called")
Run: func(_ *cobra.Command, args []string) {
type RepoError struct {
Error error
Repo string
}
if jobs < 1 {
log.Println("jobs must be greater than 0")
os.Exit(1)
}
conf := GetConfig()
if len(args) > 0 {
log.Println("too many arguments")
os.Exit(1)
}
repoChan := make(chan string, len(conf.Repos))
var (
errs []RepoError
alreadyUpToDate int
mutex sync.Mutex
wg sync.WaitGroup
)
wg.Add(len(conf.Repos))
for i := 0; i < jobs; i++ {
go func() {
for repo := range repoChan {
log.Printf("attempting push: %s\n", repo)
r, err := git.PlainOpenWithOptions(repo, &git.PlainOpenOptions{DetectDotGit: true})
if err != nil {
mutex.Lock()
errs = append(errs, RepoError{Error: err, Repo: repo})
mutex.Unlock()
log.Printf("push failed for %s: %v\n", repo, err)
wg.Done()
continue
}
err = r.Push(&git.PushOptions{
RefSpecs: []config.RefSpec{"refs/heads/*:refs/heads/*"},
})
if err == git.NoErrAlreadyUpToDate {
mutex.Lock()
alreadyUpToDate++
mutex.Unlock()
fmt.Printf("repo %s: already up to date\n", repo)
wg.Done()
continue
} else if err != nil {
mutex.Lock()
errs = append(errs, RepoError{Error: err, Repo: repo})
mutex.Unlock()
log.Printf("push failed for %s: %v\n", repo, err)
wg.Done()
continue
}
fmt.Printf("successfully pushed %s\n", repo)
wg.Done()
}
}()
}
for _, repo := range conf.Repos {
repoChan <- repo.Path
}
close(repoChan)
wg.Wait()
for _, err := range errs {
log.Printf("error pushing %s: %s\n", err.Repo, err.Error)
}
lenErrs := len(errs)
fmt.Println()
fmt.Printf("successfully pushed %d/%d repos\n", len(conf.Repos)-lenErrs, len(conf.Repos))
fmt.Printf("%d repos already up to date\n", alreadyUpToDate)
fmt.Printf("failed to push %d/%d repos\n", lenErrs, len(conf.Repos))
},
}
func init() {
rootCmd.AddCommand(pushCmd)
pushCmd.Flags().IntVarP(&jobs, "jobs", "j", 1, "number of jobs to run in parallel")
}

View File

@@ -2,19 +2,156 @@ package cmd
import (
"fmt"
"log"
"os"
"sort"
"sync"
git "github.com/go-git/go-git/v5"
"github.com/spf13/cobra"
)
// statusCmd represents the status command
type repoStatus struct {
Path string
Modified int
Added int
Deleted int
Renamed int
Copied int
Untrack int
Clean bool
}
var statusCmd = &cobra.Command{
Use: "status",
Short: "get the combined git status for all git repos",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("status called")
Run: func(_ *cobra.Command, args []string) {
type RepoError struct {
Error error
Repo string
}
if jobs < 1 {
log.Println("jobs must be greater than 0")
os.Exit(1)
}
conf := GetConfig()
if len(args) > 0 {
log.Println("too many arguments")
os.Exit(1)
}
repoChan := make(chan string, len(conf.Repos))
var (
errs []RepoError
statuses []repoStatus
mutex sync.Mutex
wg sync.WaitGroup
)
wg.Add(len(conf.Repos))
for i := 0; i < jobs; i++ {
go func() {
for repo := range repoChan {
r, err := git.PlainOpenWithOptions(repo, &git.PlainOpenOptions{DetectDotGit: true})
if err != nil {
mutex.Lock()
errs = append(errs, RepoError{Error: err, Repo: repo})
mutex.Unlock()
wg.Done()
continue
}
w, err := r.Worktree()
if err != nil {
mutex.Lock()
errs = append(errs, RepoError{Error: err, Repo: repo})
mutex.Unlock()
wg.Done()
continue
}
st, err := w.Status()
if err != nil {
mutex.Lock()
errs = append(errs, RepoError{Error: err, Repo: repo})
mutex.Unlock()
wg.Done()
continue
}
rs := repoStatus{Path: repo, Clean: st.IsClean()}
for _, s := range st {
code := s.Worktree
if code == git.Unmodified {
code = s.Staging
}
switch code {
case git.Modified:
rs.Modified++
case git.Added:
rs.Added++
case git.Deleted:
rs.Deleted++
case git.Renamed:
rs.Renamed++
case git.Copied:
rs.Copied++
case git.Untracked:
rs.Untrack++
}
}
mutex.Lock()
statuses = append(statuses, rs)
mutex.Unlock()
wg.Done()
}
}()
}
for _, repo := range conf.Repos {
repoChan <- repo.Path
}
close(repoChan)
wg.Wait()
sort.Slice(statuses, func(i, j int) bool {
return statuses[i].Path < statuses[j].Path
})
dirtyCount := 0
for _, rs := range statuses {
if rs.Clean {
continue
}
dirtyCount++
fmt.Printf("%s:\n", rs.Path)
if rs.Modified > 0 {
fmt.Printf(" modified: %d\n", rs.Modified)
}
if rs.Added > 0 {
fmt.Printf(" added: %d\n", rs.Added)
}
if rs.Deleted > 0 {
fmt.Printf(" deleted: %d\n", rs.Deleted)
}
if rs.Renamed > 0 {
fmt.Printf(" renamed: %d\n", rs.Renamed)
}
if rs.Copied > 0 {
fmt.Printf(" copied: %d\n", rs.Copied)
}
if rs.Untrack > 0 {
fmt.Printf(" untracked: %d\n", rs.Untrack)
}
}
for _, err := range errs {
log.Printf("error reading %s: %s\n", err.Repo, err.Error)
}
fmt.Println()
fmt.Printf("%d/%d repos have uncommitted changes\n", dirtyCount, len(conf.Repos))
if len(errs) > 0 {
fmt.Printf("failed to read %d/%d repos\n", len(errs), len(conf.Repos))
}
},
}
func init() {
rootCmd.AddCommand(statusCmd)
statusCmd.Flags().IntVarP(&jobs, "jobs", "j", 1, "number of jobs to run in parallel")
}

View File

@@ -11,7 +11,7 @@ import (
// unregisterCmd represents the unregister command
var unregisterCmd = &cobra.Command{
Use: "unregister",
Short: "add current path to list of repos",
Short: "remove current path from list of repos",
Run: func(_ *cobra.Command, args []string) {
conf := GetConfig()
path, err := os.Getwd()

10
go.mod
View File

@@ -1,21 +1,21 @@
module github.com/taigrr/mg
go 1.25.5
go 1.26.1
require (
github.com/go-git/go-git/v5 v5.16.5
github.com/go-git/go-git/v5 v5.17.0
github.com/spf13/cobra v1.10.2
)
require (
dario.cat/mergo v1.0.2 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.3.0 // indirect
github.com/ProtonMail/go-crypto v1.4.0 // indirect
github.com/cloudflare/circl v1.6.3 // indirect
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.7.0 // indirect
github.com/go-git/go-billy/v5 v5.8.0 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
@@ -27,7 +27,7 @@ require (
github.com/spf13/pflag v1.0.10 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
golang.org/x/crypto v0.48.0 // indirect
golang.org/x/net v0.50.0 // indirect
golang.org/x/net v0.51.0 // indirect
golang.org/x/sys v0.41.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)

16
go.sum
View File

@@ -3,8 +3,8 @@ dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
github.com/ProtonMail/go-crypto v1.4.0 h1:Zq/pbM3F5DFgJiMouxEdSVY44MVoQNEKp5d5QxIQceQ=
github.com/ProtonMail/go-crypto v1.4.0/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
@@ -25,12 +25,12 @@ github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.7.0 h1:83lBUJhGWhYp0ngzCMSgllhUSuoHP1iEWYjsPl9nwqM=
github.com/go-git/go-billy/v5 v5.7.0/go.mod h1:/1IUejTKH8xipsAcdfcSAlUlo2J7lkYV8GTKxAT/L3E=
github.com/go-git/go-billy/v5 v5.8.0 h1:I8hjc3LbBlXTtVuFNJuwYuMiHvQJDq1AT6u4DwDzZG0=
github.com/go-git/go-billy/v5 v5.8.0/go.mod h1:RpvI/rw4Vr5QA+Z60c6d6LXH0rYJo0uD5SqfmrrheCY=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.16.5 h1:mdkuqblwr57kVfXri5TTH+nMFLNUxIj9Z7F5ykFbw5s=
github.com/go-git/go-git/v5 v5.16.5/go.mod h1:QOMLpNf1qxuSY4StA/ArOdfFR2TrKEjJiye2kel2m+M=
github.com/go-git/go-git/v5 v5.17.0 h1:AbyI4xf+7DsjINHMu35quAh4wJygKBKBuXVjV/pxesM=
github.com/go-git/go-git/v5 v5.17.0/go.mod h1:f82C4YiLx+Lhi8eHxltLeGC5uBTXSFa6PC5WW9o4SjI=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
@@ -85,8 +85,8 @@ golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVo
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@@ -51,8 +51,8 @@ func TestExpandPaths(t *testing.T) {
expected: []string{"/absolute/path/to/repo"},
},
{
name: "empty repos",
input: []Repo{},
name: "empty repos",
input: []Repo{},
expected: []string{},
},
{
@@ -138,8 +138,8 @@ func TestCollapsePaths(t *testing.T) {
},
},
{
name: "empty repos",
input: []Repo{},
name: "empty repos",
input: []Repo{},
expected: []string{},
},
{
@@ -328,12 +328,12 @@ func TestParseMGConfig(t *testing.T) {
func TestAddRepo(t *testing.T) {
tests := []struct {
name string
initial []Repo
addPath string
addRemote string
wantErr bool
wantCount int
name string
initial []Repo
addPath string
addRemote string
wantErr bool
wantCount int
}{
{
name: "add to empty",

View File

@@ -44,8 +44,7 @@ func (m MRConfig) ToMGConfig() MGConfig {
}
// LoadMRConfig loads the mrconfig file from the user's home directory
// and returns a MRConfig struct
// TODO: load aliases into map instead of hardcoded Unregister prop
// and returns a MRConfig struct with all repos and aliases from the [DEFAULT] section
func LoadMRConfig() (MRConfig, error) {
home, err := os.UserHomeDir()
if err != nil {
@@ -107,16 +106,8 @@ func LoadMRConfig() (MRConfig, error) {
config.Repos[length].Remote = split[1]
case "default":
// TODO load text into Aliases map instead of hardcoded Unregister prop
switch split[0] {
case "unregister":
config.Aliases["unregister"] = split[1]
case "git_gc":
config.Aliases["gc"] = split[1]
default:
return MRConfig{}, fmt.Errorf("unexpected argument on line %d: %s", n, line)
}
// Load all DEFAULT section aliases into the map
config.Aliases[split[0]] = split[1]
}
}
return config, nil