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  }