Created
May 17, 2016 01:26
-
-
Save bruschill/ec9ded1954fb387e391630ca8a3ca660 to your computer and use it in GitHub Desktop.
multithreaded repo updating
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import "os/exec" | |
//GitCommand is the base structure for an executable git command | |
type gitCommand struct { | |
//name of bin | |
binName string | |
//additional args for call, appended to reqArgs | |
addlArgs []string | |
//path to exec command in | |
execPath string | |
} | |
//NewGitCommand returns a new instance of GitCommand | |
func NewGitCommand(execPath string, addlArgs []string) *gitCommand { | |
return &gitCommand{ | |
binName: "git", | |
addlArgs: addlArgs, | |
execPath: execPath + "/.git", | |
} | |
} | |
func (gc *gitCommand) reqArgs() []string { | |
return []string{"-C", gc.execPath} | |
} | |
//Run executes GitCommand based on its members | |
func (gc *gitCommand) Run() error { | |
args := append(gc.reqArgs(), gc.addlArgs...) | |
return exec.Command(gc.binName, args...).Run() | |
} | |
//Output executes GitCommand based on its members | |
func (gc *gitCommand) Output() (string, error) { | |
args := append(gc.reqArgs(), gc.addlArgs...) | |
out, err := exec.Command(gc.binName, args...).Output() | |
if err != nil { | |
return "", err | |
} | |
return string(out), nil | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"log" | |
"os" | |
"sync" | |
) | |
func updateRepos() { | |
//get $REPO_ROOT env var | |
rootPath := os.ExpandEnv("$REPO_ROOT") | |
if rootPath == "" { | |
log.Fatalln("$REPO_ROOT must be defined") | |
} | |
//open dir defined by rootPath | |
rootDir, err := os.Open(rootPath) | |
defer rootDir.Close() | |
if err != nil { | |
log.Fatal(err) | |
} | |
//get slice of all dir names from rootDir | |
repoDirs, err := rootDir.Readdirnames(0) | |
if err != nil { | |
log.Fatal(err) | |
} | |
//initialize WaitGroup | |
var wg sync.WaitGroup | |
//iterate through all repo dirs and update them | |
for _, repoDir := range repoDirs { | |
wg.Add(1) | |
repo := NewRepo(repoDir, rootPath) | |
go func(repo *Repo) { | |
updateStatus := repo.Update() | |
wg.Done() | |
fmt.Printf("%s: %s\n", repo.Name, updateStatus.Message) | |
}(repo) | |
} | |
wg.Wait() | |
} | |
func main() { | |
updateRepos() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"log" | |
"path/filepath" | |
) | |
//repo represents a git repository | |
type Repo struct { | |
//name of the repository | |
Name string | |
//full path to repository | |
fullPath string | |
//name of branch initially selected | |
origBranch string | |
//tracks whether or not unstaged changes were stashed | |
hasStashedChanges bool | |
} | |
type updateStatus struct { | |
Success bool | |
Message string | |
} | |
//NewRepo returns a new instance of Repo | |
func NewRepo(dirName string, rootPath string) *Repo { | |
repo := &Repo{ | |
Name: dirName, | |
fullPath: filepath.Join(rootPath, dirName), | |
} | |
branch, err := repo.currentBranch() | |
if err != nil { | |
log.Fatal(err) | |
} | |
repo.origBranch = branch | |
return repo | |
} | |
//hasUnstagedChanges returns true if the repo has unstaged changes, false otherwise | |
func (r *Repo) hasUnstagedChanges() bool { | |
addlArgs := []string{"diff", "--exit-code"} | |
gc := NewGitCommand(r.fullPath, addlArgs) | |
err := gc.Run() | |
if err != nil { | |
return true | |
} | |
return false | |
} | |
//stashChanges stashes changes for current branch | |
func (r *Repo) stashChanges() { | |
if r.hasUnstagedChanges() { | |
r.hasStashedChanges = true | |
addlArgs := []string{"stash"} | |
gc := NewGitCommand(r.fullPath, addlArgs) | |
gc.Run() | |
} | |
} | |
//retrieveChanges retrieves changes that were previously stashed | |
func (r *Repo) retrieveChanges() { | |
if r.hasStashedChanges { | |
addlArgs := []string{"stash", "pop"} | |
gc := NewGitCommand(r.fullPath, addlArgs) | |
gc.Run() | |
} | |
} | |
//currentBranch returns name of current branch as string | |
func (r *Repo) currentBranch() (string, error) { | |
addlArgs := []string{"rev-parse", "--abbrev-ref", "HEAD"} | |
gc := NewGitCommand(r.fullPath, addlArgs) | |
branchName, err := gc.Output() | |
if err != nil { | |
return "", err | |
} | |
return branchName, nil | |
} | |
//checkoutBranch checks out branch specified by branchName | |
func (r *Repo) checkoutBranch(branchName string) { | |
curBranch, _ := r.currentBranch() | |
if branchName != curBranch { | |
addlArgs := []string{"checkout", branchName} | |
gc := NewGitCommand(r.fullPath, addlArgs) | |
gc.Run() | |
} | |
} | |
//Update updates repo's master branch and returns repo back to original state before the update | |
func (r *Repo) Update() *updateStatus { | |
var statusMsg = "Updated." | |
r.stashChanges() | |
r.checkoutBranch("master") | |
addlArgs := []string{"pull"} | |
gc := NewGitCommand(r.fullPath, addlArgs) | |
err := gc.Run() | |
// if err != nil, there were no changes | |
if err != nil { | |
statusMsg = "No changes." | |
} | |
r.checkoutBranch(r.origBranch) | |
r.retrieveChanges() | |
return &updateStatus{Success: true, Message: statusMsg} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment