RunGitStatus returns the status of the git repo at path. If remoteBranch is non-empty, ahead/behind counts compare the local branch against ` / ` instead of ` / `.
(path, subpath, remoteName, remoteBranch string)
| 28 | // If remoteBranch is non-empty, ahead/behind counts compare the local branch against |
| 29 | // `<remoteName>/<remoteBranch>` instead of `<remoteName>/<localBranch>`. |
| 30 | func RunGitStatus(path, subpath, remoteName, remoteBranch string) (GitStatus, error) { |
| 31 | var args []string |
| 32 | if subpath == "" { |
| 33 | args = []string{"-C", path, "status", "--porcelain=v2", "--branch"} |
| 34 | } else { |
| 35 | args = []string{"-C", path, "status", "--porcelain=v2", "--branch", "--", subpath} |
| 36 | } |
| 37 | cmd := exec.Command("git", args...) |
| 38 | data, err := cmd.CombinedOutput() |
| 39 | if err != nil { |
| 40 | return GitStatus{}, err |
| 41 | } |
| 42 | |
| 43 | // parse the output |
| 44 | // Format is |
| 45 | // # branch.oid 4954f542d4b1f652bba02064aa8ee64ece38d02a |
| 46 | // # branch.head mgd_repo_poc |
| 47 | // # branch.upstream origin/mgd_repo_poc |
| 48 | // # branch.ab +0 -0 |
| 49 | // lines describing the status of the working tree |
| 50 | status := GitStatus{} |
| 51 | lines := strings.SplitSeq(strings.TrimSpace(string(data)), "\n") |
| 52 | for line := range lines { |
| 53 | line = strings.TrimSpace(line) |
| 54 | switch { |
| 55 | // standard headers - all may not be present |
| 56 | case strings.HasPrefix(line, "# branch.oid "): |
| 57 | case strings.HasPrefix(line, "# branch.head "): |
| 58 | if strings.HasSuffix(line, "(detached)") { |
| 59 | return status, errDetachedHead |
| 60 | } |
| 61 | status.Branch = strings.TrimPrefix(line, "# branch.head ") |
| 62 | case strings.HasPrefix(line, "# branch.upstream "): |
| 63 | case strings.HasPrefix(line, "# branch.ab "): |
| 64 | // do not use this as the remote tracking branch may not be set/may be set to a different remote |
| 65 | default: |
| 66 | // any non header line means staged, unstaged or untracked changes |
| 67 | status.LocalChanges = true |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | compareBranch := remoteBranch |
| 72 | if compareBranch == "" { |
| 73 | compareBranch = status.Branch |
| 74 | } |
| 75 | remoteRef := fmt.Sprintf("%s/%s", remoteName, compareBranch) |
| 76 | |
| 77 | ahead, behind, err := countAheadBehind(path, subpath, status.Branch, remoteRef) |
| 78 | if err == nil { |
| 79 | status.LocalCommits = ahead |
| 80 | status.RemoteCommits = behind |
| 81 | } |
| 82 | |
| 83 | // get the remote URL |
| 84 | data, err = exec.Command("git", "-C", path, "remote", "get-url", remoteName).Output() |
| 85 | if err == nil { |
| 86 | status.RemoteURL = strings.TrimSpace(string(data)) |
| 87 | } |