diff options
Diffstat (limited to 'cli/internal/scm/git_go.go')
| -rw-r--r-- | cli/internal/scm/git_go.go | 111 |
1 files changed, 111 insertions, 0 deletions
diff --git a/cli/internal/scm/git_go.go b/cli/internal/scm/git_go.go new file mode 100644 index 0000000..0dac2bf --- /dev/null +++ b/cli/internal/scm/git_go.go @@ -0,0 +1,111 @@ +//go:build go || !rust +// +build go !rust + +// Package scm abstracts operations on various tools like git +// Currently, only git is supported. +// +// Adapted from https://github.com/thought-machine/please/tree/master/src/scm +// Copyright Thought Machine, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package scm + +import ( + "fmt" + "github.com/vercel/turbo/cli/internal/turbopath" + "os/exec" + "path/filepath" + "strings" + + "github.com/pkg/errors" +) + +// git implements operations on a git repository. +type git struct { + repoRoot turbopath.AbsoluteSystemPath +} + +// ChangedFiles returns a list of modified files since the given commit, optionally including untracked files. +func (g *git) ChangedFiles(fromCommit string, toCommit string, relativeTo string) ([]string, error) { + if relativeTo == "" { + relativeTo = g.repoRoot.ToString() + } + relSuffix := []string{"--", relativeTo} + command := []string{"diff", "--name-only", toCommit} + + out, err := exec.Command("git", append(command, relSuffix...)...).CombinedOutput() + if err != nil { + return nil, errors.Wrapf(err, "finding changes relative to %v", relativeTo) + } + files := strings.Split(string(out), "\n") + + if fromCommit != "" { + // Grab the diff from the merge-base to HEAD using ... syntax. This ensures we have just + // the changes that have occurred on the current branch. + command = []string{"diff", "--name-only", fromCommit + "..." + toCommit} + out, err = exec.Command("git", append(command, relSuffix...)...).CombinedOutput() + if err != nil { + // Check if we can provide a better error message for non-existent commits. + // If we error on the check or can't find it, fall back to whatever error git + // reported. + if exists, err := commitExists(fromCommit); err == nil && !exists { + return nil, fmt.Errorf("commit %v does not exist", fromCommit) + } + return nil, errors.Wrapf(err, "git comparing with %v", fromCommit) + } + committedChanges := strings.Split(string(out), "\n") + files = append(files, committedChanges...) + } + command = []string{"ls-files", "--other", "--exclude-standard"} + out, err = exec.Command("git", append(command, relSuffix...)...).CombinedOutput() + if err != nil { + return nil, errors.Wrap(err, "finding untracked files") + } + untracked := strings.Split(string(out), "\n") + files = append(files, untracked...) + // git will report changed files relative to the worktree: re-relativize to relativeTo + normalized := make([]string, 0) + for _, f := range files { + if f == "" { + continue + } + normalizedFile, err := g.fixGitRelativePath(strings.TrimSpace(f), relativeTo) + if err != nil { + return nil, err + } + normalized = append(normalized, normalizedFile) + } + return normalized, nil +} + +func (g *git) PreviousContent(fromCommit string, filePath string) ([]byte, error) { + if fromCommit == "" { + return nil, fmt.Errorf("Need commit sha to inspect file contents") + } + + out, err := exec.Command("git", "show", fmt.Sprintf("%s:%s", fromCommit, filePath)).CombinedOutput() + if err != nil { + return nil, errors.Wrapf(err, "unable to get contents of %s", filePath) + } + + return out, nil +} + +func commitExists(commit string) (bool, error) { + err := exec.Command("git", "cat-file", "-t", commit).Run() + if err != nil { + exitErr := &exec.ExitError{} + if errors.As(err, &exitErr) && exitErr.ExitCode() == 128 { + return false, nil + } + return false, err + } + return true, nil +} + +func (g *git) fixGitRelativePath(worktreePath, relativeTo string) (string, error) { + p, err := filepath.Rel(relativeTo, filepath.Join(g.repoRoot, worktreePath)) + if err != nil { + return "", errors.Wrapf(err, "unable to determine relative path for %s and %s", g.repoRoot, relativeTo) + } + return p, nil +} |
