2014-09-24 17:10:29 +00:00
|
|
|
// Package git contains various commands that shell out to git
|
2014-09-23 15:40:23 +00:00
|
|
|
package git
|
|
|
|
|
|
|
|
import (
|
2014-09-23 21:42:47 +00:00
|
|
|
"bufio"
|
|
|
|
"bytes"
|
|
|
|
"errors"
|
2014-09-23 15:40:23 +00:00
|
|
|
"fmt"
|
2014-10-04 16:10:02 +00:00
|
|
|
"github.com/rubyist/tracerx"
|
2014-09-23 15:40:23 +00:00
|
|
|
"io"
|
|
|
|
"os/exec"
|
2014-09-23 21:42:47 +00:00
|
|
|
"regexp"
|
2014-10-02 19:14:06 +00:00
|
|
|
"strconv"
|
2014-09-23 15:40:23 +00:00
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
2014-09-24 17:10:29 +00:00
|
|
|
// CatFile is equivalent to `git cat-file -p <sha1>`
|
2014-09-23 21:42:47 +00:00
|
|
|
func CatFile(sha1 string) (string, error) {
|
|
|
|
return simpleExec(nil, "git", "cat-file", "-p", sha1)
|
|
|
|
}
|
|
|
|
|
2014-10-02 19:14:06 +00:00
|
|
|
func CatFileBatchCheck(r io.Reader) ([]*GitObject, error) {
|
|
|
|
objects := make([]*GitObject, 0)
|
|
|
|
|
|
|
|
output, err := simpleExec(r, "git", "cat-file", "--batch-check")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
scanner := bufio.NewScanner(bytes.NewBufferString(output))
|
|
|
|
for scanner.Scan() {
|
|
|
|
line := strings.Split(scanner.Text(), " ")
|
|
|
|
size, err := strconv.Atoi(line[2])
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
objects = append(objects, &GitObject{Sha1: line[0], Type: line[1], Size: size})
|
|
|
|
}
|
|
|
|
|
|
|
|
return objects, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func CatFileBatch(r io.Reader) (string, error) {
|
|
|
|
return simpleExec(r, "git", "cat-file", "--batch")
|
|
|
|
}
|
|
|
|
|
2014-09-24 17:10:29 +00:00
|
|
|
// Grep is equivalent to `git grep --full-name --name-only --cached <pattern>`
|
2014-09-23 21:42:47 +00:00
|
|
|
func Grep(pattern string) (string, error) {
|
|
|
|
return simpleExec(nil, "git", "grep", "--full-name", "--name-only", "--cached", pattern)
|
|
|
|
}
|
|
|
|
|
2014-09-24 17:10:29 +00:00
|
|
|
// HashObject is equivalent to `git hash-object --stdin` where data is passed
|
|
|
|
// to stdin.
|
2014-09-24 13:58:33 +00:00
|
|
|
func HashObject(data []byte) (string, error) {
|
2014-09-23 21:42:47 +00:00
|
|
|
buf := bytes.NewBuffer(data)
|
|
|
|
return simpleExec(buf, "git", "hash-object", "--stdin")
|
|
|
|
}
|
|
|
|
|
2014-09-29 19:35:59 +00:00
|
|
|
func LsRemote(repo, refspec string) (string, error) {
|
|
|
|
if repo == "" {
|
|
|
|
return "", errors.New("repo required")
|
|
|
|
}
|
|
|
|
if refspec == "" {
|
|
|
|
return simpleExec(nil, "git", "ls-remote", repo)
|
|
|
|
|
|
|
|
}
|
|
|
|
return simpleExec(nil, "git", "ls-remote", repo, refspec)
|
|
|
|
}
|
|
|
|
|
2014-09-23 21:42:47 +00:00
|
|
|
var z40 = regexp.MustCompile(`\^?0{40}`)
|
|
|
|
|
|
|
|
type GitObject struct {
|
|
|
|
Sha1 string
|
|
|
|
Name string
|
2014-10-02 19:14:06 +00:00
|
|
|
Type string
|
|
|
|
Size int
|
2014-09-23 21:42:47 +00:00
|
|
|
}
|
|
|
|
|
2014-09-24 17:10:29 +00:00
|
|
|
// RevListObjects has two modes:
|
|
|
|
// When all is true, left and right are ignored, equivalent to `git rev-list --objects --all`
|
|
|
|
// When all is false, it is equivalent to `git rev-list --objects <left> <right>`
|
|
|
|
// If right is 40 0s, it is removed from the arguments.
|
2014-09-23 21:42:47 +00:00
|
|
|
func RevListObjects(left, right string, all bool) ([]*GitObject, error) {
|
|
|
|
objects := make([]*GitObject, 0)
|
|
|
|
|
|
|
|
refArgs := []string{"rev-list", "--objects"}
|
|
|
|
if all {
|
|
|
|
refArgs = append(refArgs, "--all")
|
|
|
|
} else {
|
|
|
|
if left == "" {
|
|
|
|
return nil, errors.New("Left commit required")
|
|
|
|
}
|
|
|
|
|
|
|
|
refArgs = append(refArgs, left)
|
|
|
|
|
|
|
|
if right != "" && !z40.MatchString(right) {
|
|
|
|
refArgs = append(refArgs, right)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
output, err := simpleExec(nil, "git", refArgs...)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
scanner := bufio.NewScanner(bytes.NewBufferString(output))
|
|
|
|
for scanner.Scan() {
|
|
|
|
line := strings.Split(scanner.Text(), " ")
|
2014-10-02 19:14:06 +00:00
|
|
|
objects = append(objects, &GitObject{Sha1: line[0], Name: strings.Join(line[1:len(line)], " ")})
|
2014-09-23 21:42:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return objects, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type gitConfig struct {
|
|
|
|
}
|
|
|
|
|
|
|
|
var Config = &gitConfig{}
|
|
|
|
|
2014-09-24 17:10:29 +00:00
|
|
|
// Find returns the git config value for the key
|
2014-09-23 21:42:47 +00:00
|
|
|
func (c *gitConfig) Find(val string) string {
|
|
|
|
output, _ := simpleExec(nil, "git", "config", val)
|
|
|
|
return output
|
|
|
|
}
|
|
|
|
|
2014-09-24 17:10:29 +00:00
|
|
|
// SetGlobal sets the git config value for the key in the global config
|
2014-09-23 21:42:47 +00:00
|
|
|
func (c *gitConfig) SetGlobal(key, val string) {
|
|
|
|
simpleExec(nil, "git", "config", "--global", "--add", key, val)
|
|
|
|
}
|
|
|
|
|
2014-09-24 17:10:29 +00:00
|
|
|
// SetGlobal removes the git config value for the key from the global config
|
2014-09-23 21:42:47 +00:00
|
|
|
func (c *gitConfig) UnsetGlobal(key string) {
|
|
|
|
simpleExec(nil, "git", "config", "--global", "--unset", key)
|
|
|
|
}
|
|
|
|
|
2014-09-24 17:10:29 +00:00
|
|
|
// List lists all of the git config values
|
2014-09-23 21:42:47 +00:00
|
|
|
func (c *gitConfig) List() (string, error) {
|
|
|
|
return simpleExec(nil, "git", "config", "-l")
|
|
|
|
}
|
|
|
|
|
2014-09-24 17:10:29 +00:00
|
|
|
// ListFromFile lists all of the git config values in the given config file
|
2014-09-23 21:42:47 +00:00
|
|
|
func (c *gitConfig) ListFromFile() (string, error) {
|
|
|
|
return simpleExec(nil, "git", "config", "-l", "-f", ".gitconfig")
|
|
|
|
}
|
|
|
|
|
2014-09-24 17:10:29 +00:00
|
|
|
// Version returns the git version
|
2014-09-23 21:42:47 +00:00
|
|
|
func (c *gitConfig) Version() (string, error) {
|
|
|
|
return simpleExec(nil, "git", "version")
|
|
|
|
}
|
|
|
|
|
2014-09-24 17:10:29 +00:00
|
|
|
// simpleExec is a small wrapper around os/exec.Command. If the passed stdin
|
|
|
|
// is not nil it will be hooked up to the subprocess stdin.
|
2014-09-23 15:40:23 +00:00
|
|
|
func simpleExec(stdin io.Reader, name string, arg ...string) (string, error) {
|
2014-10-04 16:10:02 +00:00
|
|
|
tracerx.Printf("run_command: '%s' %s", name, strings.Join(arg, " "))
|
2014-09-23 15:40:23 +00:00
|
|
|
cmd := exec.Command(name, arg...)
|
|
|
|
if stdin != nil {
|
|
|
|
cmd.Stdin = stdin
|
|
|
|
}
|
|
|
|
|
|
|
|
output, err := cmd.Output()
|
|
|
|
if _, ok := err.(*exec.ExitError); ok {
|
|
|
|
return "", nil
|
|
|
|
} else if err != nil {
|
|
|
|
return fmt.Sprintf("Error running %s %s", name, arg), err
|
|
|
|
}
|
|
|
|
|
|
|
|
return strings.Trim(string(output), " \n"), nil
|
|
|
|
}
|