diff options
Diffstat (limited to 'src/edit/git.go')
-rw-r--r-- | src/edit/git.go | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/src/edit/git.go b/src/edit/git.go new file mode 100644 index 0000000..010c928 --- /dev/null +++ b/src/edit/git.go @@ -0,0 +1,146 @@ +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 +} |