github.com/nkprince007/lab@v0.6.2-0.20171218071646-19d68b56f403/internal/git/git.go (about) 1 package git 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "strings" 11 12 "github.com/davecgh/go-spew/spew" 13 "github.com/pkg/errors" 14 "github.com/tcnksm/go-gitconfig" 15 ) 16 17 // IsHub is true when using "hub" as the git binary 18 var IsHub bool 19 20 func init() { 21 _, err := exec.LookPath("hub") 22 if err == nil { 23 IsHub = true 24 } 25 } 26 27 // New looks up the hub or git binary and returns a cmd which outputs to stdout 28 func New(args ...string) *exec.Cmd { 29 gitPath, err := exec.LookPath("hub") 30 if err != nil { 31 gitPath, err = exec.LookPath("git") 32 if err != nil { 33 log.Fatal(err) 34 } 35 } 36 37 cmd := exec.Command(gitPath, args...) 38 cmd.Stdin = os.Stdin 39 cmd.Stdout = os.Stdout 40 cmd.Stderr = os.Stderr 41 return cmd 42 } 43 44 // GitDir returns the full path to the .git directory 45 func GitDir() (string, error) { 46 cmd := New("rev-parse", "-q", "--git-dir") 47 cmd.Stdout = nil 48 d, err := cmd.Output() 49 if err != nil { 50 return "", err 51 } 52 dir := string(d) 53 dir = strings.TrimSpace(dir) 54 if !filepath.IsAbs(dir) { 55 dir, err = filepath.Abs(dir) 56 if err != nil { 57 return "", err 58 } 59 } 60 61 return filepath.Clean(dir), nil 62 } 63 64 // WorkingDir returns the full pall to the root of the current git repository 65 func WorkingDir() (string, error) { 66 cmd := New("rev-parse", "--show-toplevel") 67 cmd.Stdout = nil 68 d, err := cmd.Output() 69 if err != nil { 70 return "", err 71 } 72 return strings.TrimSpace(string(d)), nil 73 } 74 75 // CommentChar returns active comment char and defaults to '#' 76 func CommentChar() string { 77 char, err := gitconfig.Entire("core.commentchar") 78 if err == nil { 79 return char 80 } 81 82 return "#" 83 } 84 85 // LastCommitMessage returns the last commits message as one line 86 func LastCommitMessage() (string, error) { 87 cmd := New("show", "-s", "--format=%s%n%+b", "HEAD") 88 cmd.Stdout = nil 89 msg, err := cmd.Output() 90 if err != nil { 91 return "", err 92 } 93 return strings.TrimSpace(string(msg)), nil 94 } 95 96 // Log produces a a formatted gitlog between 2 git shas 97 func Log(sha1, sha2 string) (string, error) { 98 cmd := New("-c", "log.showSignature=false", 99 "log", 100 "--no-color", 101 "--format=%h (%aN, %ar)%n%w(78,3,3)%s%n", 102 "--cherry", 103 fmt.Sprintf("%s...%s", sha1, sha2)) 104 cmd.Stdout = nil 105 outputs, err := cmd.Output() 106 if err != nil { 107 return "", errors.Errorf("Can't load git log %s..%s", sha1, sha2) 108 } 109 110 return string(outputs), nil 111 } 112 113 // CurrentBranch returns the currently checked out branch and strips away all 114 // but the branchname itself. 115 func CurrentBranch() (string, error) { 116 cmd := New("branch") 117 cmd.Stdout = nil 118 gBranches, err := cmd.Output() 119 if err != nil { 120 return "", err 121 } 122 branches := strings.Split(string(gBranches), "\n") 123 if os.Getenv("DEBUG") != "" { 124 spew.Dump(branches) 125 } 126 var branch string 127 for _, b := range branches { 128 if strings.HasPrefix(b, "* ") { 129 branch = b 130 break 131 } 132 } 133 if branch == "" { 134 return "", errors.New("current branch could not be determined") 135 } 136 branch = strings.TrimPrefix(branch, "* ") 137 branch = strings.TrimSpace(branch) 138 return branch, nil 139 } 140 141 // PathWithNameSpace returns the owner/repository for the current repo 142 // Such as zaquestion/lab 143 func PathWithNameSpace(remote string) (string, error) { 144 remoteURL, err := gitconfig.Local("remote." + remote + ".url") 145 if err != nil { 146 return "", err 147 } 148 parts := strings.Split(remoteURL, ":") 149 if len(parts) == 0 { 150 return "", errors.New("remote." + remote + ".url missing repository") 151 } 152 return strings.TrimSuffix(parts[len(parts)-1:][0], ".git"), nil 153 } 154 155 // RepoName returns the name of the repository, such as "lab" 156 func RepoName() (string, error) { 157 o, err := PathWithNameSpace("origin") 158 if err != nil { 159 return "", err 160 } 161 parts := strings.Split(o, "/") 162 return parts[len(parts)-1:][0], nil 163 } 164 165 // RemoteAdd both adds a remote and fetches it 166 func RemoteAdd(name, url, dir string) error { 167 cmd := New("remote", "add", name, url) 168 cmd.Dir = dir 169 if err := cmd.Run(); err != nil { 170 return err 171 } 172 fmt.Println("Updating", name) 173 cmd = New("fetch", name) 174 cmd.Dir = dir 175 if err := cmd.Run(); err != nil { 176 return err 177 } 178 fmt.Println("new remote:", name) 179 return nil 180 } 181 182 // IsRemote returns true when passed a valid remote in the git repo 183 func IsRemote(remote string) (bool, error) { 184 cmd := New("remote") 185 cmd.Stdout = nil 186 remotes, err := cmd.Output() 187 if err != nil { 188 return false, err 189 } 190 191 return bytes.Contains(remotes, []byte(remote+"\n")), nil 192 }