package main import ( "strconv" "io" "bytes" "fmt" "os/exec" "time" "errors" ) func GitPull() error { return exec.Command("git", "pull").Run() } func GitPush() error { return exec.Command("git", "push").Run() } type Edit struct { UserName string UserEmail string Time time.Time Message string Files map[string][]byte } func gitTime(t time.Time) string { return fmt.Sprintf("%d %s", t.Unix(), t.Format("-0700")) } func (edit Edit) WriteTo(w io.Writer) error { var err error commit := make([]string, len(edit.Files)) i := 0 for name, content := range edit.Files { commit[i] = name if content != nil { if _, err = fmt.Fprintf(w, "blob\nmark :%d\ndata %d\n%s", i+1, len(content), content); err != nil { return err } } } _, err = fmt.Fprintf(w, `commit HEAD author %s <%s> %s committer %s <%s> %s data %d %sfrom HEAD `, edit.UserName, edit.UserEmail, gitTime(edit.Time), edit.UserName, edit.UserEmail, gitTime(edit.Time), len(edit.Message), edit.Message) if err != nil { return err } for i, filename := range commit { if edit.Files[filename] != nil { if _, err = fmt.Fprintf(w, "M 100644 :%d %s\n", i+1, filename); err != nil { return err } } else { if _, err = fmt.Fprintf(w, "D %s\n", filename); err != nil { return err } } } return nil } func GitCommit(edit Edit) error { cmd := exec.Command("git", "fast-import") pip, err := cmd.StdinPipe() if err != nil { return err } if err = cmd.Start(); err != nil { return err } werr := edit.WriteTo(pip) if err = cmd.Wait(); err == nil { err = werr } return err } type GitFile struct { Mode int32 // 18 bits unsigned Type string Hash string Size int64 } type GitTree map[string]GitFile var ParseError = errors.New("git ls-tree parse error") func GitLsTree() (GitTree, error) { data, err := exec.Command("git", "ls-tree", "-trlz", "HEAD").Output() if err != nil { return nil, err } lines := bytes.Split(data, []byte{0}) ret := make(GitTree, len(lines)-1) for _, line := range lines[:len(ret)] { // trim the trailing "\0" if line[len(line)-1] != 0 { return nil, ParseError } line = line[:len(line)-1] // line = mode SP type SP hash SP size TAB name a := bytes.SplitN(line, []byte{' '}, 4) if len(a) != 4 { return nil, ParseError } b := bytes.SplitN(a[3], []byte{'\t'}, 2) if len(b) != 2 { return nil, ParseError } fmode := a[0] ftype := a[1] fhash := a[2] fsize := b[0] fname := b[1] fmodeN, err := strconv.ParseInt(string(fmode), 10, 19) if err != nil { return nil, err } fsizeN := int64(-1) fsizeS := string(fsize) if fsizeS != "-" { fsizeN, err = strconv.ParseInt(fsizeS, 10, 64) if err != nil { return nil, err } } ret[string(fname)] = GitFile{ Mode: int32(fmodeN), Type: string(ftype), Hash: string(fhash), Size: fsizeN, } } return ret, nil }