Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5344bed1ea |
@@ -92,11 +92,26 @@ func isGitRepo(dir string) bool {
|
|||||||
|
|
||||||
// syncRepo performs the full sync sequence on a single repository:
|
// syncRepo performs the full sync sequence on a single repository:
|
||||||
//
|
//
|
||||||
// fetch --prune stash (if dirty) pull --rebase stash pop push
|
// fetch --all --prune → stash (if dirty) → pull --rebase each remote →
|
||||||
|
// stash pop → push each remote → push tags each remote
|
||||||
func syncRepo(r repo) error {
|
func syncRepo(r repo) error {
|
||||||
fmt.Printf(" %s (%s)\n", r.name, r.path)
|
fmt.Printf(" %s (%s)\n", r.name, r.path)
|
||||||
|
|
||||||
// 1. Fetch and prune.
|
rems, err := remotes(r.path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("listing remotes: %w", err)
|
||||||
|
}
|
||||||
|
if len(rems) == 0 {
|
||||||
|
fmt.Println(" no remotes configured, skipping")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
branch, err := currentBranch(r.path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("determining current branch: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Fetch all remotes.
|
||||||
if err := git(r.path, "fetch", "--all", "--prune"); err != nil {
|
if err := git(r.path, "fetch", "--all", "--prune"); err != nil {
|
||||||
return fmt.Errorf("fetch: %w", err)
|
return fmt.Errorf("fetch: %w", err)
|
||||||
}
|
}
|
||||||
@@ -108,13 +123,19 @@ func syncRepo(r repo) error {
|
|||||||
}
|
}
|
||||||
if dirty {
|
if dirty {
|
||||||
fmt.Println(" stashing uncommitted changes")
|
fmt.Println(" stashing uncommitted changes")
|
||||||
if err := git(r.path, "stash", "push", "-m", "sync-repos auto-stash"); err != nil {
|
if err := git(r.path, "stash", "push", "-m", "git-sync auto-stash"); err != nil {
|
||||||
return fmt.Errorf("stash push: %w", err)
|
return fmt.Errorf("stash push: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Pull with rebase.
|
// 3. Pull --rebase from each remote.
|
||||||
pullErr := git(r.path, "pull", "--rebase")
|
var pullErr error
|
||||||
|
for _, rem := range rems {
|
||||||
|
if err := git(r.path, "pull", "--rebase", rem, branch); err != nil {
|
||||||
|
fmt.Printf(" pull from %s failed: %v\n", rem, err)
|
||||||
|
pullErr = fmt.Errorf("pull from %s: %w", rem, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 4. Pop stash regardless of pull outcome (best effort to restore state).
|
// 4. Pop stash regardless of pull outcome (best effort to restore state).
|
||||||
if dirty {
|
if dirty {
|
||||||
@@ -125,18 +146,55 @@ func syncRepo(r repo) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if pullErr != nil {
|
if pullErr != nil {
|
||||||
return fmt.Errorf("pull: %w", pullErr)
|
return pullErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Push.
|
// 5. Push to each remote.
|
||||||
if err := git(r.path, "push"); err != nil {
|
for _, rem := range rems {
|
||||||
return fmt.Errorf("push: %w", err)
|
if err := git(r.path, "push", rem); err != nil {
|
||||||
|
return fmt.Errorf("push to %s: %w", rem, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Push tags to each remote.
|
||||||
|
for _, rem := range rems {
|
||||||
|
if err := git(r.path, "push", "--tags", rem); err != nil {
|
||||||
|
return fmt.Errorf("push tags to %s: %w", rem, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(" synced")
|
fmt.Println(" synced")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remotes returns the list of git remote names for a repository.
|
||||||
|
func remotes(dir string) ([]string, error) {
|
||||||
|
cmd := exec.Command("git", "remote")
|
||||||
|
cmd.Dir = dir
|
||||||
|
out, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var names []string
|
||||||
|
for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
|
||||||
|
if line != "" {
|
||||||
|
names = append(names, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return names, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// currentBranch returns the current branch name.
|
||||||
|
func currentBranch(dir string) (string, error) {
|
||||||
|
cmd := exec.Command("git", "symbolic-ref", "--short", "HEAD")
|
||||||
|
cmd.Dir = dir
|
||||||
|
out, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(string(out)), nil
|
||||||
|
}
|
||||||
|
|
||||||
// isDirty returns true if the working tree or index has uncommitted changes.
|
// isDirty returns true if the working tree or index has uncommitted changes.
|
||||||
func isDirty(dir string) (bool, error) {
|
func isDirty(dir string) (bool, error) {
|
||||||
cmd := exec.Command("git", "status", "--porcelain")
|
cmd := exec.Command("git", "status", "--porcelain")
|
||||||
|
|||||||
Reference in New Issue
Block a user