aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/vcs
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2024-12-17 15:52:00 +0100
committerAleksandr Nogikh <nogikh@google.com>2025-01-09 08:59:30 +0000
commit9220929f371b5549fde0c05fdb17e18ec842f077 (patch)
treeea64eed11f0fc94d3f27745d5f0277d268b88e17 /pkg/vcs
parentf3558dbf032eab2b77c1cb11b9ce2baffe7838d3 (diff)
pkg/vcs: export GitWrapper
The current common vcs interface and its implementations are designed to support a very big number of situations (git modules, cleaning up complex state, etc), which is too heavy and restrictive when we just need a thin wrapper over the git command that supports a few basic operations. Refactor pkg/vcs to split out the wrapper code from the big git implementation of the vcs.Repo interface. Export the wrapper to enable reuse in other parts of the system.
Diffstat (limited to 'pkg/vcs')
-rw-r--r--pkg/vcs/fuchsia.go4
-rw-r--r--pkg/vcs/git.go396
-rw-r--r--pkg/vcs/git_repo_test.go2
-rw-r--r--pkg/vcs/git_test.go6
-rw-r--r--pkg/vcs/git_test_util.go6
-rw-r--r--pkg/vcs/linux.go18
-rw-r--r--pkg/vcs/linux_patches.go6
-rw-r--r--pkg/vcs/testos.go6
-rw-r--r--pkg/vcs/vcs.go10
9 files changed, 245 insertions, 209 deletions
diff --git a/pkg/vcs/fuchsia.go b/pkg/vcs/fuchsia.go
index 4a119baeb..ff5f5cd57 100644
--- a/pkg/vcs/fuchsia.go
+++ b/pkg/vcs/fuchsia.go
@@ -16,7 +16,7 @@ import (
// rather than fuchsia.git.
type fuchsia struct {
dir string
- repo *git
+ repo *gitRepo
}
func newFuchsia(dir string, opts []RepoOpt) *fuchsia {
@@ -26,7 +26,7 @@ func newFuchsia(dir string, opts []RepoOpt) *fuchsia {
opts = append(opts, OptPrecious)
return &fuchsia{
dir: dir,
- repo: newGit(dir, nil, opts),
+ repo: newGitRepo(dir, nil, opts),
}
}
diff --git a/pkg/vcs/git.go b/pkg/vcs/git.go
index d8a4967ba..fd71b8d36 100644
--- a/pkg/vcs/git.go
+++ b/pkg/vcs/git.go
@@ -22,25 +22,25 @@ import (
"github.com/google/syzkaller/pkg/osutil"
)
-type git struct {
- dir string
- ignoreCC map[string]bool
- precious bool
- sandbox bool
+type gitRepo struct {
+ *Git
}
-func newGit(dir string, ignoreCC map[string]bool, opts []RepoOpt) *git {
- git := &git{
- dir: dir,
- ignoreCC: ignoreCC,
- sandbox: true,
+func newGitRepo(dir string, ignoreCC map[string]bool, opts []RepoOpt) *gitRepo {
+ git := &gitRepo{
+ Git: &Git{
+ Dir: dir,
+ Sandbox: true,
+ Env: filterEnv(),
+ ignoreCC: ignoreCC,
+ },
}
for _, opt := range opts {
switch opt {
case OptPrecious:
git.precious = true
case OptDontSandbox:
- git.sandbox = false
+ git.Sandbox = false
}
}
return git
@@ -66,9 +66,9 @@ func filterEnv() []string {
return env
}
-func (git *git) Poll(repo, branch string) (*Commit, error) {
- git.reset()
- origin, err := git.git("remote", "get-url", "origin")
+func (git *gitRepo) Poll(repo, branch string) (*Commit, error) {
+ git.Reset()
+ origin, err := git.Run("remote", "get-url", "origin")
if err != nil || strings.TrimSpace(string(origin)) != repo {
// The repo is here, but it has wrong origin (e.g. repo in config has changed), re-clone.
if err := git.clone(repo, branch); err != nil {
@@ -78,28 +78,28 @@ func (git *git) Poll(repo, branch string) (*Commit, error) {
// Use origin/branch for the case the branch was force-pushed,
// in such case branch is not the same is origin/branch and we will
// stuck with the local version forever (git checkout won't fail).
- if _, err := git.git("checkout", "origin/"+branch); err != nil {
+ if _, err := git.Run("checkout", "origin/"+branch); err != nil {
// No such branch (e.g. branch in config has changed), re-clone.
if err := git.clone(repo, branch); err != nil {
return nil, err
}
}
- if _, err := git.git("fetch", "--force"); err != nil {
+ if _, err := git.Run("fetch", "--force"); err != nil {
// Something else is wrong, re-clone.
if err := git.clone(repo, branch); err != nil {
return nil, err
}
}
- if _, err := git.git("checkout", "origin/"+branch); err != nil {
+ if _, err := git.Run("checkout", "origin/"+branch); err != nil {
return nil, err
}
- if _, err := git.git("submodule", "update", "--init"); err != nil {
+ if _, err := git.Run("submodule", "update", "--init"); err != nil {
return nil, err
}
return git.Commit(HEAD)
}
-func (git *git) CheckoutBranch(repo, branch string) (*Commit, error) {
+func (git *gitRepo) CheckoutBranch(repo, branch string) (*Commit, error) {
if err := git.repair(); err != nil {
return nil, err
}
@@ -108,17 +108,17 @@ func (git *git) CheckoutBranch(repo, branch string) (*Commit, error) {
// remote when initializing.
// This sets "origin" to be the current remote.
// Ignore errors as we can double add or remove the same remote and that will fail.
- git.git("remote", "rm", "origin")
- git.git("remote", "add", "origin", repo)
- git.git("remote", "add", repoHash, repo)
- _, err := git.git("fetch", "--force", repoHash, branch)
+ git.Run("remote", "rm", "origin")
+ git.Run("remote", "add", "origin", repo)
+ git.Run("remote", "add", repoHash, repo)
+ _, err := git.Run("fetch", "--force", repoHash, branch)
if err != nil {
return nil, err
}
- if _, err := git.git("checkout", "FETCH_HEAD", "--force"); err != nil {
+ if _, err := git.Run("checkout", "FETCH_HEAD", "--force"); err != nil {
return nil, err
}
- if _, err := git.git("submodule", "update", "--init"); err != nil {
+ if _, err := git.Run("submodule", "update", "--init"); err != nil {
return nil, err
}
// If the branch checkout had to be "forced" the directory may
@@ -130,7 +130,7 @@ func (git *git) CheckoutBranch(repo, branch string) (*Commit, error) {
return git.Commit(HEAD)
}
-func (git *git) CheckoutCommit(repo, commit string) (*Commit, error) {
+func (git *gitRepo) CheckoutCommit(repo, commit string) (*Commit, error) {
if err := git.repair(); err != nil {
return nil, err
}
@@ -140,16 +140,16 @@ func (git *git) CheckoutCommit(repo, commit string) (*Commit, error) {
return git.SwitchCommit(commit)
}
-func (git *git) fetchRemote(repo, commit string) error {
+func (git *gitRepo) fetchRemote(repo, commit string) error {
repoHash := hash.String([]byte(repo))
// Ignore error as we can double add the same remote and that will fail.
- git.git("remote", "add", repoHash, repo)
+ git.Run("remote", "add", repoHash, repo)
fetchArgs := []string{"fetch", "--force", "--tags", repoHash}
if commit != "" && gitFullHashRe.MatchString(commit) {
// This trick only works with full commit hashes.
fetchArgs = append(fetchArgs, commit)
}
- _, err := git.git(fetchArgs...)
+ _, err := git.Run(fetchArgs...)
if err != nil {
var verbose *osutil.VerboseError
if errors.As(err, &verbose) &&
@@ -159,110 +159,75 @@ func (git *git) fetchRemote(repo, commit string) error {
// Try to fetch more, but this time prune tags, it should help.
// The --prune-tags option will remove all tags that are not present
// in this remote repo, so don't do it always. Only when necessary.
- _, err = git.git("fetch", "--force", "--tags", "--prune", "--prune-tags", repoHash)
+ _, err = git.Run("fetch", "--force", "--tags", "--prune", "--prune-tags", repoHash)
}
}
return err
}
-func (git *git) SwitchCommit(commit string) (*Commit, error) {
+func (git *gitRepo) SwitchCommit(commit string) (*Commit, error) {
if !git.precious {
- git.git("reset", "--hard")
- git.git("clean", "-fdx")
+ git.Run("reset", "--hard")
+ git.Run("clean", "-fdx")
}
- if _, err := git.git("checkout", commit); err != nil {
+ if _, err := git.Run("checkout", commit); err != nil {
return nil, err
}
- if _, err := git.git("submodule", "update", "--init"); err != nil {
+ if _, err := git.Run("submodule", "update", "--init"); err != nil {
return nil, err
}
return git.Commit(HEAD)
}
-func (git *git) clone(repo, branch string) error {
+func (git *gitRepo) clone(repo, branch string) error {
if git.precious {
return fmt.Errorf("won't reinit precious repo")
}
if err := git.initRepo(nil); err != nil {
return err
}
- if _, err := git.git("remote", "add", "origin", repo); err != nil {
+ if _, err := git.Run("remote", "add", "origin", repo); err != nil {
return err
}
- if _, err := git.git("fetch", "origin", branch); err != nil {
+ if _, err := git.Run("fetch", "origin", branch); err != nil {
return err
}
return nil
}
-func (git *git) reset() error {
- // This function tries to reset git repo state to a known clean state.
- if git.precious {
- return nil
- }
- git.git("reset", "--hard", "--recurse-submodules")
- git.git("clean", "-xfdf")
- git.git("submodule", "foreach", "--recursive", "git", "clean", "-xfdf")
- git.git("bisect", "reset")
- _, err := git.git("reset", "--hard", "--recurse-submodules")
- return err
-}
-
-func (git *git) repair() error {
- if err := git.reset(); err != nil {
+func (git *gitRepo) repair() error {
+ if err := git.Reset(); err != nil {
return git.initRepo(err)
}
return nil
}
-func (git *git) initRepo(reason error) error {
+func (git *gitRepo) initRepo(reason error) error {
if reason != nil {
- log.Logf(1, "git: initializing repo at %v: %v", git.dir, reason)
+ log.Logf(1, "git: initializing repo at %v: %v", git.Dir, reason)
}
- if err := os.RemoveAll(git.dir); err != nil {
+ if err := os.RemoveAll(git.Dir); err != nil {
return fmt.Errorf("failed to remove repo dir: %w", err)
}
- if err := osutil.MkdirAll(git.dir); err != nil {
+ if err := osutil.MkdirAll(git.Dir); err != nil {
return fmt.Errorf("failed to create repo dir: %w", err)
}
- if git.sandbox {
- if err := osutil.SandboxChown(git.dir); err != nil {
+ if git.Sandbox {
+ if err := osutil.SandboxChown(git.Dir); err != nil {
return err
}
}
- if _, err := git.git("init"); err != nil {
+ if _, err := git.Run("init"); err != nil {
return err
}
return nil
}
-func (git *git) Contains(commit string) (bool, error) {
- _, err := git.git("merge-base", "--is-ancestor", commit, HEAD)
+func (git *gitRepo) Contains(commit string) (bool, error) {
+ _, err := git.Run("merge-base", "--is-ancestor", commit, HEAD)
return err == nil, nil
}
-func (git *git) Commit(com string) (*Commit, error) {
- const patchSeparator = "---===syzkaller-patch-separator===---"
- output, err := git.git("log", "--format=%H%n%s%n%ae%n%an%n%ad%n%P%n%cd%n%b"+patchSeparator,
- "-n", "1", "-p", "-U0", com)
- if err != nil {
- return nil, err
- }
- pos := bytes.Index(output, []byte(patchSeparator))
- if pos == -1 {
- return nil, fmt.Errorf("git log output does not contain patch separator")
- }
- commit, err := gitParseCommit(output[:pos], nil, nil, git.ignoreCC)
- if err != nil {
- return nil, err
- }
- commit.Patch = output[pos+len(patchSeparator):]
- for len(commit.Patch) != 0 && commit.Patch[0] == '\n' {
- commit.Patch = commit.Patch[1:]
- }
- return commit, nil
-}
-
func gitParseCommit(output, user, domain []byte, ignoreCC map[string]bool) (*Commit, error) {
lines := bytes.Split(output, []byte{'\n'})
if len(lines) < 8 || len(lines[0]) != 40 {
@@ -340,7 +305,7 @@ func gitParseCommit(output, user, domain []byte, ignoreCC map[string]bool) (*Com
return com, nil
}
-func (git *git) GetCommitByTitle(title string) (*Commit, error) {
+func (git *gitRepo) GetCommitByTitle(title string) (*Commit, error) {
commits, _, err := git.GetCommitsByTitles([]string{title})
if err != nil || len(commits) == 0 {
return nil, err
@@ -352,7 +317,7 @@ const (
fetchCommitsMaxAgeInYears = 5
)
-func (git *git) GetCommitsByTitles(titles []string) ([]*Commit, []string, error) {
+func (git *gitRepo) GetCommitsByTitles(titles []string) ([]*Commit, []string, error) {
var greps []string
m := make(map[string]string)
for _, title := range titles {
@@ -381,12 +346,12 @@ func (git *git) GetCommitsByTitles(titles []string) ([]*Commit, []string, error)
return results, missing, nil
}
-func (git *git) ListCommitHashes(baseCommit string, from time.Time) ([]string, error) {
+func (git *gitRepo) ListCommitHashes(baseCommit string, from time.Time) ([]string, error) {
args := []string{"log", "--pretty=format:%h"}
if !from.IsZero() {
args = append(args, "--since", from.Format(time.RFC3339))
}
- output, err := git.git(append(args, baseCommit)...)
+ output, err := git.Run(append(args, baseCommit)...)
if err != nil {
return nil, err
}
@@ -396,7 +361,7 @@ func (git *git) ListCommitHashes(baseCommit string, from time.Time) ([]string, e
return strings.Split(string(output), "\n"), nil
}
-func (git *git) ExtractFixTagsFromCommits(baseCommit, email string) ([]*Commit, error) {
+func (git *gitRepo) ExtractFixTagsFromCommits(baseCommit, email string) ([]*Commit, error) {
user, domain, err := splitEmail(email)
if err != nil {
return nil, fmt.Errorf("failed to parse email %q: %w", email, err)
@@ -406,76 +371,6 @@ func (git *git) ExtractFixTagsFromCommits(baseCommit, email string) ([]*Commit,
return git.fetchCommits(since, baseCommit, user, domain, []string{grep}, false)
}
-func (git *git) fetchCommits(since, base, user, domain string, greps []string, fixedStrings bool) ([]*Commit, error) {
- const commitSeparator = "---===syzkaller-commit-separator===---"
- args := []string{"log", "--since", since, "--format=%H%n%s%n%ae%n%an%n%ad%n%P%n%cd%n%b%n" + commitSeparator}
- if fixedStrings {
- args = append(args, "--fixed-strings")
- }
- for _, grep := range greps {
- args = append(args, "--grep", grep)
- }
- args = append(args, base)
- cmd := exec.Command("git", args...)
- cmd.Dir = git.dir
- cmd.Env = filterEnv()
- if git.sandbox {
- if err := osutil.Sandbox(cmd, true, false); err != nil {
- return nil, err
- }
- }
- stdout, err := cmd.StdoutPipe()
- if err != nil {
- return nil, err
- }
- if err := cmd.Start(); err != nil {
- return nil, err
- }
- defer cmd.Wait()
- defer cmd.Process.Kill()
- var (
- s = bufio.NewScanner(stdout)
- buf = new(bytes.Buffer)
- separator = []byte(commitSeparator)
- commits []*Commit
- userBytes []byte
- domainBytes []byte
- )
- if user != "" {
- userBytes = []byte(user + "+")
- domainBytes = []byte(domain)
- }
- for s.Scan() {
- ln := s.Bytes()
- if !bytes.Equal(ln, separator) {
- buf.Write(ln)
- buf.WriteByte('\n')
- continue
- }
- com, err := gitParseCommit(buf.Bytes(), userBytes, domainBytes, git.ignoreCC)
- if err != nil {
- return nil, err
- }
- if user == "" || len(com.Tags) != 0 {
- commits = append(commits, com)
- }
- buf.Reset()
- }
- return commits, s.Err()
-}
-
-func (git *git) git(args ...string) ([]byte, error) {
- cmd := osutil.Command("git", args...)
- cmd.Dir = git.dir
- cmd.Env = filterEnv()
- if git.sandbox {
- if err := osutil.Sandbox(cmd, true, false); err != nil {
- return nil, err
- }
- }
- return osutil.Run(3*time.Hour, cmd)
-}
-
func splitEmail(email string) (user, domain string, err error) {
addr, err := mail.ParseAddress(email)
if err != nil {
@@ -493,18 +388,18 @@ func splitEmail(email string) (user, domain string, err error) {
return
}
-func (git *git) Bisect(bad, good string, dt debugtracer.DebugTracer, pred func() (BisectResult,
+func (git *gitRepo) Bisect(bad, good string, dt debugtracer.DebugTracer, pred func() (BisectResult,
error)) ([]*Commit, error) {
- git.reset()
+ git.Reset()
firstBad, err := git.Commit(bad)
if err != nil {
return nil, err
}
- output, err := git.git("bisect", "start", bad, good)
+ output, err := git.Run("bisect", "start", bad, good)
if err != nil {
return nil, err
}
- defer git.reset()
+ defer git.Reset()
dt.Log("# git bisect start %v %v\n%s", bad, good, output)
current, err := git.Commit(HEAD)
if err != nil {
@@ -518,14 +413,14 @@ func (git *git) Bisect(bad, good string, dt debugtracer.DebugTracer, pred func()
for {
res, err := pred()
// Linux EnvForCommit may cherry-pick some fixes, reset these before the next step.
- git.git("reset", "--hard")
+ git.Run("reset", "--hard")
if err != nil {
return nil, err
}
if res == BisectBad {
firstBad = current
}
- output, err = git.git("bisect", bisectTerms[res])
+ output, err = git.Run("bisect", bisectTerms[res])
dt.Log("# git bisect %v %v\n%s", bisectTerms[res], current.Hash, output)
if err != nil {
if bytes.Contains(output, []byte("There are only 'skip'ped commits left to test")) {
@@ -546,7 +441,7 @@ func (git *git) Bisect(bad, good string, dt debugtracer.DebugTracer, pred func()
var gitFullHashRe = regexp.MustCompile("[a-f0-9]{40}")
-func (git *git) bisectInconclusive(output []byte) ([]*Commit, error) {
+func (git *gitRepo) bisectInconclusive(output []byte) ([]*Commit, error) {
// For inconclusive bisection git prints the following message:
//
// There are only 'skip'ped commits left to test.
@@ -570,7 +465,7 @@ func (git *git) bisectInconclusive(output []byte) ([]*Commit, error) {
return commits, nil
}
-func (git *git) ReleaseTag(commit string) (string, error) {
+func (git *gitRepo) ReleaseTag(commit string) (string, error) {
tags, err := git.previousReleaseTags(commit, true, true, true)
if err != nil {
return "", err
@@ -581,10 +476,10 @@ func (git *git) ReleaseTag(commit string) (string, error) {
return tags[0], nil
}
-func (git *git) previousReleaseTags(commit string, self, onlyTop, includeRC bool) ([]string, error) {
+func (git *gitRepo) previousReleaseTags(commit string, self, onlyTop, includeRC bool) ([]string, error) {
var tags []string
if self {
- output, err := git.git("tag", "--list", "--points-at", commit, "--merged", commit, "v*.*")
+ output, err := git.Run("tag", "--list", "--points-at", commit, "--merged", commit, "v*.*")
if err != nil {
return nil, err
}
@@ -593,7 +488,7 @@ func (git *git) previousReleaseTags(commit string, self, onlyTop, includeRC bool
return tags, nil
}
}
- output, err := git.git("tag", "--no-contains", commit, "--merged", commit, "v*.*")
+ output, err := git.Run("tag", "--no-contains", commit, "--merged", commit, "v*.*")
if err != nil {
return nil, err
}
@@ -605,7 +500,7 @@ func (git *git) previousReleaseTags(commit string, self, onlyTop, includeRC bool
return tags, nil
}
-func (git *git) IsRelease(commit string) (bool, error) {
+func (git *gitRepo) IsRelease(commit string) (bool, error) {
tags1, err := git.previousReleaseTags(commit, true, false, false)
if err != nil {
return false, err
@@ -617,12 +512,12 @@ func (git *git) IsRelease(commit string) (bool, error) {
return len(tags1) != len(tags2), nil
}
-func (git *git) Object(name, commit string) ([]byte, error) {
- return git.git("show", fmt.Sprintf("%s:%s", commit, name))
+func (git *gitRepo) Object(name, commit string) ([]byte, error) {
+ return git.Run("show", fmt.Sprintf("%s:%s", commit, name))
}
-func (git *git) MergeBases(firstCommit, secondCommit string) ([]*Commit, error) {
- output, err := git.git("merge-base", firstCommit, secondCommit)
+func (git *gitRepo) MergeBases(firstCommit, secondCommit string) ([]*Commit, error) {
+ output, err := git.Run("merge-base", firstCommit, secondCommit)
if err != nil {
return nil, err
}
@@ -641,8 +536,8 @@ func (git *git) MergeBases(firstCommit, secondCommit string) ([]*Commit, error)
// If object exists its exit status is 0.
// If object doesn't exist its exit status is 1 (not documented).
// Otherwise, the exit status is 128 (not documented).
-func (git *git) CommitExists(commit string) (bool, error) {
- _, err := git.git("cat-file", "-e", commit)
+func (git *gitRepo) CommitExists(commit string) (bool, error) {
+ _, err := git.Run("cat-file", "-e", commit)
var vErr *osutil.VerboseError
if errors.As(err, &vErr) && vErr.ExitCode == 1 {
return false, nil
@@ -653,10 +548,10 @@ func (git *git) CommitExists(commit string) (bool, error) {
return true, nil
}
-func (git *git) PushCommit(repo, commit string) error {
+func (git *gitRepo) PushCommit(repo, commit string) error {
tagName := "tag-" + commit // assign tag to guarantee remote persistence
- git.git("tag", tagName) // ignore errors on re-tagging
- if _, err := git.git("push", repo, "tag", tagName); err != nil {
+ git.Run("tag", tagName) // ignore errors on re-tagging
+ if _, err := git.Run("push", repo, "tag", tagName); err != nil {
return fmt.Errorf("git push %s tag %s: %w", repo, tagName, err)
}
return nil
@@ -672,3 +567,144 @@ func ParseGitDiff(patch []byte) []string {
}
return files
}
+
+type Git struct {
+ Dir string
+ Sandbox bool
+ Env []string
+ precious bool
+ ignoreCC map[string]bool
+}
+
+func (git Git) Run(args ...string) ([]byte, error) {
+ cmd, err := git.command(args...)
+ if err != nil {
+ return nil, err
+ }
+ return osutil.Run(3*time.Hour, cmd)
+}
+
+func (git Git) command(args ...string) (*exec.Cmd, error) {
+ cmd := osutil.Command("git", args...)
+ cmd.Dir = git.Dir
+ cmd.Env = git.Env
+ if git.Sandbox {
+ if err := osutil.Sandbox(cmd, true, false); err != nil {
+ return nil, err
+ }
+ }
+ return cmd, nil
+}
+
+// Apply invokes git apply for a series of git patches.
+// It is different from Patch() in that it normally handles raw patch emails.
+func (git Git) Apply(patch []byte) error {
+ cmd, err := git.command("apply", "-")
+ if err != nil {
+ return err
+ }
+ stdin, err := cmd.StdinPipe()
+ if err != nil {
+ return err
+ }
+ go func() {
+ stdin.Write(patch)
+ stdin.Close()
+ }()
+ _, err = osutil.Run(3*time.Hour, cmd)
+ return err
+}
+
+// Reset resets the git repo to a known clean state.
+func (git Git) Reset() error {
+ if git.precious {
+ return nil
+ }
+ git.Run("reset", "--hard", "--recurse-submodules")
+ git.Run("clean", "-xfdf")
+ git.Run("submodule", "foreach", "--recursive", "git", "clean", "-xfdf")
+ git.Run("bisect", "reset")
+ _, err := git.Run("reset", "--hard", "--recurse-submodules")
+ return err
+}
+
+// Commit extracts the information about the particular git commit.
+func (git Git) Commit(hash string) (*Commit, error) {
+ const patchSeparator = "---===syzkaller-patch-separator===---"
+ output, err := git.Run("log", "--format=%H%n%s%n%ae%n%an%n%ad%n%P%n%cd%n%b"+patchSeparator,
+ "-n", "1", "-p", "-U0", hash)
+ if err != nil {
+ return nil, err
+ }
+ pos := bytes.Index(output, []byte(patchSeparator))
+ if pos == -1 {
+ return nil, fmt.Errorf("git log output does not contain patch separator")
+ }
+ commit, err := gitParseCommit(output[:pos], nil, nil, git.ignoreCC)
+ if err != nil {
+ return nil, err
+ }
+ commit.Patch = output[pos+len(patchSeparator):]
+ for len(commit.Patch) != 0 && commit.Patch[0] == '\n' {
+ commit.Patch = commit.Patch[1:]
+ }
+ return commit, nil
+}
+
+func (git Git) fetchCommits(since, base, user, domain string, greps []string, fixedStrings bool) ([]*Commit, error) {
+ const commitSeparator = "---===syzkaller-commit-separator===---"
+ args := []string{"log", "--since", since, "--format=%H%n%s%n%ae%n%an%n%ad%n%P%n%cd%n%b%n" + commitSeparator}
+ if fixedStrings {
+ args = append(args, "--fixed-strings")
+ }
+ for _, grep := range greps {
+ args = append(args, "--grep", grep)
+ }
+ args = append(args, base)
+ cmd := exec.Command("git", args...)
+ cmd.Dir = git.Dir
+ cmd.Env = filterEnv()
+ if git.Sandbox {
+ if err := osutil.Sandbox(cmd, true, false); err != nil {
+ return nil, err
+ }
+ }
+ stdout, err := cmd.StdoutPipe()
+ if err != nil {
+ return nil, err
+ }
+ if err := cmd.Start(); err != nil {
+ return nil, err
+ }
+ defer cmd.Wait()
+ defer cmd.Process.Kill()
+ var (
+ s = bufio.NewScanner(stdout)
+ buf = new(bytes.Buffer)
+ separator = []byte(commitSeparator)
+ commits []*Commit
+ userBytes []byte
+ domainBytes []byte
+ )
+ if user != "" {
+ userBytes = []byte(user + "+")
+ domainBytes = []byte(domain)
+ }
+ for s.Scan() {
+ ln := s.Bytes()
+ if !bytes.Equal(ln, separator) {
+ buf.Write(ln)
+ buf.WriteByte('\n')
+ continue
+ }
+ com, err := gitParseCommit(buf.Bytes(), userBytes, domainBytes, git.ignoreCC)
+ if err != nil {
+ return nil, err
+ }
+ if user == "" || len(com.Tags) != 0 {
+ commits = append(commits, com)
+ }
+ buf.Reset()
+ }
+ return commits, s.Err()
+}
diff --git a/pkg/vcs/git_repo_test.go b/pkg/vcs/git_repo_test.go
index 6409b59cf..2db10bb91 100644
--- a/pkg/vcs/git_repo_test.go
+++ b/pkg/vcs/git_repo_test.go
@@ -24,7 +24,7 @@ func TestGitRepo(t *testing.T) {
baseDir := t.TempDir()
repo1 := CreateTestRepo(t, baseDir, "repo1")
repo2 := CreateTestRepo(t, baseDir, "repo2")
- repo := newGit(filepath.Join(baseDir, "repo"), nil, nil)
+ repo := newGitRepo(filepath.Join(baseDir, "repo"), nil, nil)
{
com, err := repo.Poll(repo1.Dir, "master")
if err != nil {
diff --git a/pkg/vcs/git_test.go b/pkg/vcs/git_test.go
index 76c34dbd3..1c1320364 100644
--- a/pkg/vcs/git_test.go
+++ b/pkg/vcs/git_test.go
@@ -412,7 +412,7 @@ func TestGitCustomRefs(t *testing.T) {
// Create a local repo.
localRepoDir := t.TempDir()
- local := newGit(localRepoDir, nil, nil)
+ local := newGitRepo(localRepoDir, nil, nil)
// Fetch the commit from the custom ref.
_, err := local.CheckoutCommit(remoteRepoDir, refCommit.Hash)
@@ -433,7 +433,7 @@ func TestGitRemoteTags(t *testing.T) {
// Create a local repo.
localRepoDir := t.TempDir()
- local := newGit(localRepoDir, nil, nil)
+ local := newGitRepo(localRepoDir, nil, nil)
// Ensure all tags were fetched.
commit, err := local.CheckoutCommit(remoteRepoDir, "sub_branch")
@@ -456,7 +456,7 @@ func TestGitFetchShortHash(t *testing.T) {
// Create a local repo.
localRepoDir := t.TempDir()
- local := newGit(localRepoDir, nil, nil)
+ local := newGitRepo(localRepoDir, nil, nil)
// Fetch the commit from the custom ref.
_, err := local.CheckoutCommit(remoteRepoDir, refCommit.Hash[:12])
diff --git a/pkg/vcs/git_test_util.go b/pkg/vcs/git_test_util.go
index 79b2d958a..0595dc533 100644
--- a/pkg/vcs/git_test_util.go
+++ b/pkg/vcs/git_test_util.go
@@ -24,7 +24,7 @@ type TestRepo struct {
Dir string
name string
Commits map[string]map[string]*Commit
- repo *git
+ repo *gitRepo
}
func (repo *TestRepo) Git(args ...string) {
@@ -50,7 +50,7 @@ func MakeTestRepo(t *testing.T, dir string) *TestRepo {
Dir: dir,
name: filepath.Base(dir),
Commits: make(map[string]map[string]*Commit),
- repo: newGit(dir, ignoreCC, []RepoOpt{OptPrecious, OptDontSandbox}),
+ repo: newGitRepo(dir, ignoreCC, []RepoOpt{OptPrecious, OptDontSandbox}),
}
repo.Git("init")
repo.Git("config", "--add", "user.email", userEmail)
@@ -124,7 +124,7 @@ func CloneTestRepo(t *testing.T, baseDir, name string, originRepo *TestRepo) *Te
Dir: dir,
name: filepath.Base(dir),
Commits: make(map[string]map[string]*Commit),
- repo: newGit(dir, ignoreCC, []RepoOpt{OptPrecious, OptDontSandbox}),
+ repo: newGitRepo(dir, ignoreCC, []RepoOpt{OptPrecious, OptDontSandbox}),
}
repo.Git("clone", originRepo.Dir, repo.Dir)
return repo
diff --git a/pkg/vcs/linux.go b/pkg/vcs/linux.go
index 41716e50c..d348dd71e 100644
--- a/pkg/vcs/linux.go
+++ b/pkg/vcs/linux.go
@@ -23,7 +23,7 @@ import (
)
type linux struct {
- *git
+ *gitRepo
vmType string
}
@@ -38,13 +38,13 @@ func newLinux(dir string, opts []RepoOpt, vmType string) *linux {
}
return &linux{
- git: newGit(dir, ignoreCC, opts),
- vmType: vmType,
+ gitRepo: newGitRepo(dir, ignoreCC, opts),
+ vmType: vmType,
}
}
func (ctx *linux) PreviousReleaseTags(commit, compilerType string) ([]string, error) {
- tags, err := ctx.git.previousReleaseTags(commit, false, false, false)
+ tags, err := ctx.gitRepo.previousReleaseTags(commit, false, false, false)
if err != nil {
return nil, err
}
@@ -153,7 +153,7 @@ func (ctx *linux) EnvForCommit(
Compiler: compiler,
KernelConfig: cf.Serialize(),
}
- err = linuxFixBackports(ctx.git, backports...)
+ err = linuxFixBackports(ctx.gitRepo, backports...)
if err != nil {
return nil, fmt.Errorf("failed to cherry pick fixes: %w", err)
}
@@ -196,7 +196,7 @@ func (ctx *linux) PrepareBisect() error {
if ctx.vmType != targets.GVisor {
// Some linux repos we fuzz don't import the upstream release git tags. We need tags
// to decide which compiler versions to use. Let's fetch upstream for its tags.
- err := ctx.git.fetchRemote("https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git", "")
+ err := ctx.gitRepo.fetchRemote("https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git", "")
if err != nil {
return fmt.Errorf("fetching upstream linux failed: %w", err)
}
@@ -206,7 +206,7 @@ func (ctx *linux) PrepareBisect() error {
func (ctx *linux) Bisect(bad, good string, dt debugtracer.DebugTracer, pred func() (BisectResult,
error)) ([]*Commit, error) {
- commits, err := ctx.git.Bisect(bad, good, dt, pred)
+ commits, err := ctx.gitRepo.Bisect(bad, good, dt, pred)
if len(commits) == 1 {
ctx.addMaintainers(commits[0])
}
@@ -233,7 +233,7 @@ func (ctx *linux) getMaintainers(hash string, blame bool) Recipients {
if blame {
args += " --git-blame"
}
- output, err := osutil.RunCmd(time.Minute, ctx.git.dir, "bash", "-c", args)
+ output, err := osutil.RunCmd(time.Minute, ctx.gitRepo.Dir, "bash", "-c", args)
if err != nil {
return nil
}
@@ -290,7 +290,7 @@ func (ctx *linux) Minimize(target *targets.Target, original, baseline []byte, ty
dt.Log("# configuration already minimized\n")
return original, nil
}
- kconf, err := kconfig.Parse(target, filepath.Join(ctx.git.dir, "Kconfig"))
+ kconf, err := kconfig.Parse(target, filepath.Join(ctx.gitRepo.Dir, "Kconfig"))
if err != nil {
return nil, fmt.Errorf("%w: %w", ErrBadKconfig, err)
}
diff --git a/pkg/vcs/linux_patches.go b/pkg/vcs/linux_patches.go
index e1993d5c9..03b20e656 100644
--- a/pkg/vcs/linux_patches.go
+++ b/pkg/vcs/linux_patches.go
@@ -20,7 +20,7 @@ type BackportCommit struct {
}
// linuxFixBackports() cherry-picks the commits necessary to compile/run older Linux kernel releases.
-func linuxFixBackports(repo *git, extraCommits ...BackportCommit) error {
+func linuxFixBackports(repo *gitRepo, extraCommits ...BackportCommit) error {
return applyFixBackports(repo,
append(
append([]BackportCommit{}, pickLinuxCommits...),
@@ -29,7 +29,7 @@ func linuxFixBackports(repo *git, extraCommits ...BackportCommit) error {
)
}
-func applyFixBackports(repo *git, commits []BackportCommit) error {
+func applyFixBackports(repo *gitRepo, commits []BackportCommit) error {
for _, info := range commits {
if info.GuiltyHash != "" {
contains, err := repo.Contains(info.GuiltyHash)
@@ -49,7 +49,7 @@ func applyFixBackports(repo *git, commits []BackportCommit) error {
// The fix is already present.
continue
}
- _, err = repo.git("cherry-pick", "--no-commit", info.FixHash)
+ _, err = repo.Run("cherry-pick", "--no-commit", info.FixHash)
if err != nil {
return err
}
diff --git a/pkg/vcs/testos.go b/pkg/vcs/testos.go
index 68d9eeccd..2fd4dd151 100644
--- a/pkg/vcs/testos.go
+++ b/pkg/vcs/testos.go
@@ -12,19 +12,19 @@ import (
)
type testos struct {
- *git
+ *gitRepo
}
var _ ConfigMinimizer = new(testos)
func newTestos(dir string, opts []RepoOpt) *testos {
return &testos{
- git: newGit(dir, nil, opts),
+ gitRepo: newGitRepo(dir, nil, opts),
}
}
func (ctx *testos) PreviousReleaseTags(commit, compilerType string) ([]string, error) {
- return ctx.git.previousReleaseTags(commit, false, false, false)
+ return ctx.gitRepo.previousReleaseTags(commit, false, false, false)
}
func (ctx *testos) EnvForCommit(
diff --git a/pkg/vcs/vcs.go b/pkg/vcs/vcs.go
index 494e9a673..b0a2dc8eb 100644
--- a/pkg/vcs/vcs.go
+++ b/pkg/vcs/vcs.go
@@ -204,11 +204,11 @@ func NewRepo(os, vmType, dir string, opts ...RepoOpt) (Repo, error) {
case targets.Fuchsia:
return newFuchsia(dir, opts), nil
case targets.OpenBSD:
- return newGit(dir, nil, opts), nil
+ return newGitRepo(dir, nil, opts), nil
case targets.NetBSD:
- return newGit(dir, nil, opts), nil
+ return newGitRepo(dir, nil, opts), nil
case targets.FreeBSD:
- return newGit(dir, nil, opts), nil
+ return newGitRepo(dir, nil, opts), nil
case targets.TestOS:
return newTestos(dir, opts), nil
}
@@ -216,12 +216,12 @@ func NewRepo(os, vmType, dir string, opts ...RepoOpt) (Repo, error) {
}
func NewSyzkallerRepo(dir string, opts ...RepoOpt) Repo {
- git := newGit(dir, nil, append(opts, OptDontSandbox))
+ git := newGitRepo(dir, nil, append(opts, OptDontSandbox))
return git
}
func NewLKMLRepo(dir string) Repo {
- return newGit(dir, nil, []RepoOpt{OptDontSandbox})
+ return newGitRepo(dir, nil, []RepoOpt{OptDontSandbox})
}
func Patch(dir string, patch []byte) error {