Skip to content

Instantly share code, notes, and snippets.

@bradfitz
Created July 16, 2016 22:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bradfitz/b64ca2917fbc7a447141c580ec820da5 to your computer and use it in GitHub Desktop.
Save bradfitz/b64ca2917fbc7a447141c580ec820da5 to your computer and use it in GitHub Desktop.
package main
import (
"bufio"
"flag"
"log"
"os/exec"
"sort"
"strings"
)
func main() {
flag.Parse()
if flag.NArg() != 1 {
log.Fatal("Usage: gitsync <dest>")
}
dest := flag.Arg(0)
log.Print(dest)
local, err := getLocalRefs()
if err != nil {
log.Fatalf("getting local refs: %v", err)
}
remote, err := getRemoteRefs(dest)
if err != nil {
log.Fatalf("getting local refs: %v", err)
}
var pushRefs []string
for ref, hash := range local {
if remote[ref] != hash {
pushRefs = append(pushRefs, ref)
}
}
sort.Sort(refByPriority(pushRefs))
for len(pushRefs) > 0 {
log.Printf("%d refs remain.", len(pushRefs))
args := []string{"push", "-f", dest}
n := 0
for _, ref := range pushRefs {
args = append(args, "+"+local[ref]+":"+ref)
n++
if n == 200 {
break
}
}
pushRefs = pushRefs[n:]
log.Printf("git %v", args)
err := exec.Command("git", args...).Run()
if err != nil {
log.Fatal(err)
}
}
}
func getLocalRefs() (map[string]string, error) {
return parseRefs(exec.Command("git", "show-ref"))
}
func getRemoteRefs(dest string) (map[string]string, error) {
return parseRefs(exec.Command("git", "ls-remote", dest))
}
func parseRefs(cmd *exec.Cmd) (map[string]string, error) {
refHash := map[string]string{}
out, err := cmd.StdoutPipe()
if err != nil {
return nil, err
}
bs := bufio.NewScanner(out)
if err := cmd.Start(); err != nil {
return nil, err
}
for bs.Scan() {
f := strings.Fields(bs.Text())
refHash[f[1]] = f[0]
}
return refHash, bs.Err()
}
type refByPriority []string
func (s refByPriority) Len() int { return len(s) }
func (s refByPriority) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s refByPriority) Less(i, j int) bool {
p1 := priority[refType(s[i])]
p2 := priority[refType(s[j])]
if p1 != p2 {
return p1 > p2
}
return s[i] <= s[j]
}
func refType(s string) string {
s = strings.TrimPrefix(s, "refs/")
if i := strings.IndexByte(s, '/'); i != -1 {
return s[:i]
}
return s
}
var priority = map[string]int{
"heads": 5,
"tags": 4,
"changes": 3,
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment