summaryrefslogtreecommitdiff
path: root/src/edit/git.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/edit/git.go')
-rw-r--r--src/edit/git.go146
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
+}