github.com/jonsyu1/godel@v0.0.0-20171017211503-64567a0cf169/apps/distgo/pkg/git/git.go (about) 1 // Copyright 2016 Palantir Technologies, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package git 16 17 import ( 18 "os/exec" 19 "regexp" 20 "strings" 21 22 "github.com/pkg/errors" 23 ) 24 25 const Unspecified = "unspecified" 26 27 type ProjectInfo struct { 28 Version string 29 Branch string 30 Revision string 31 } 32 33 const snapshotRegexp = `.+g[-+.]?[a-fA-F0-9]{3,}$` 34 35 func IsSnapshotVersion(version string) bool { 36 return regexp.MustCompile(snapshotRegexp).MatchString(version) 37 } 38 39 func NewProjectInfo(gitDir string) (ProjectInfo, error) { 40 version, err := ProjectVersion(gitDir) 41 if err != nil { 42 return ProjectInfo{}, err 43 } 44 45 branch, err := ProjectBranch(gitDir) 46 if err != nil { 47 return ProjectInfo{}, err 48 } 49 50 revision, err := ProjectRevision(gitDir) 51 if err != nil { 52 return ProjectInfo{}, err 53 } 54 55 return ProjectInfo{ 56 Version: version, 57 Branch: branch, 58 Revision: revision, 59 }, nil 60 } 61 62 // ProjectVersion returns the version string for the git repository that the provided directory is in. The output is the output 63 // of "git describe --tags" followed by ".dirty" if the repository currently has any uncommitted changes. Returns 64 // an error if the provided path is not in a git root or if the git repository has no commits or no tags. 65 func ProjectVersion(gitDir string) (string, error) { 66 tags, err := tags(gitDir) 67 if err != nil { 68 return "", err 69 } 70 71 // if no tags exist, return Unspecified as the version 72 if tags == "" { 73 return Unspecified, nil 74 } 75 76 result, err := trimmedCombinedGitCmdOutput(gitDir, "describe", "--tags", "--first-parent") 77 if err != nil { 78 return "", err 79 } 80 81 // trim "v" prefix in tags 82 if strings.HasPrefix(result, "v") { 83 result = result[1:] 84 } 85 86 // handle untracked files as well as "actual" dirtiness 87 dirtyFiles, err := trimmedCombinedGitCmdOutput(gitDir, "status", "--porcelain") 88 if err != nil { 89 return "", err 90 } 91 if dirtyFiles != "" { 92 result += ".dirty" 93 } 94 return result, nil 95 } 96 97 func ProjectBranch(gitDir string) (string, error) { 98 tags, err := tags(gitDir) 99 if err != nil { 100 return "", err 101 } 102 103 // if no tags exist, return Unspecified as the branch 104 if tags == "" { 105 return Unspecified, nil 106 } 107 108 branch, err := branch(gitDir) 109 if err != nil { 110 return "", err 111 } 112 113 if strings.HasPrefix(branch, "v") { 114 branch = branch[1:] 115 } 116 117 return branch, nil 118 } 119 120 func ProjectRevision(gitDir string) (string, error) { 121 tags, err := tags(gitDir) 122 if err != nil { 123 return "", err 124 } 125 126 // if no tags exist, return revision count from first commit 127 if tags == "" { 128 return trimmedCombinedGitCmdOutput(gitDir, "rev-list", "HEAD", "--count") 129 } 130 131 branch, err := branch(gitDir) 132 if err != nil { 133 return "", err 134 } 135 return trimmedCombinedGitCmdOutput(gitDir, "rev-list", branch+"..HEAD", "--count") 136 } 137 138 func tags(gitDir string) (string, error) { 139 return trimmedCombinedGitCmdOutput(gitDir, "tag", "-l") 140 } 141 142 func branch(gitDir string) (string, error) { 143 return trimmedCombinedGitCmdOutput(gitDir, "describe", "--abbrev=0", "--tags", "--first-parent") 144 } 145 146 func trimmedCombinedGitCmdOutput(gitDir string, args ...string) (string, error) { 147 cmd := exec.Command("git", args...) 148 cmd.Dir = gitDir 149 out, err := cmd.CombinedOutput() 150 if err != nil { 151 return "", errors.Wrapf(err, "Command %v failed. Output: %v", cmd.Args, string(out)) 152 } 153 return strings.TrimSpace(string(out)), err 154 }