aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/vcs
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2023-03-30 16:44:25 +0200
committerAleksandr Nogikh <wp32pw@gmail.com>2023-04-06 13:59:25 +0200
commit139c4ef69fd5289588228c700717631d8f1731d0 (patch)
tree05712e02c03b1269f11cd89d696de38e07c95702 /pkg/vcs
parent0870752005424bd8737bdea302071167f4f0026d (diff)
pkg/vcs: add two more vcs.Repo methods
1) ListCommitHashes, which lists all commit hashes reachable from the specified commit. 2) Object, which allows to query the contents of an object at the specified revision.
Diffstat (limited to 'pkg/vcs')
-rw-r--r--pkg/vcs/fuchsia.go8
-rw-r--r--pkg/vcs/git.go12
-rw-r--r--pkg/vcs/git_test.go79
-rw-r--r--pkg/vcs/git_test_util.go1
-rw-r--r--pkg/vcs/vcs.go10
5 files changed, 110 insertions, 0 deletions
diff --git a/pkg/vcs/fuchsia.go b/pkg/vcs/fuchsia.go
index 188c5c839..14fb743f9 100644
--- a/pkg/vcs/fuchsia.go
+++ b/pkg/vcs/fuchsia.go
@@ -95,3 +95,11 @@ func (ctx *fuchsia) ReleaseTag(commit string) (string, error) {
func (ctx *fuchsia) Contains(commit string) (bool, error) {
return false, fmt.Errorf("not implemented for fuchsia")
}
+
+func (ctx *fuchsia) ListCommitHashes(base string) ([]string, error) {
+ return ctx.repo.ListCommitHashes(base)
+}
+
+func (ctx *fuchsia) Object(name, commit string) ([]byte, error) {
+ return ctx.repo.Object(name, commit)
+}
diff --git a/pkg/vcs/git.go b/pkg/vcs/git.go
index 4872e88cf..bd9738517 100644
--- a/pkg/vcs/git.go
+++ b/pkg/vcs/git.go
@@ -358,6 +358,14 @@ func (git *git) ListRecentCommits(baseCommit string) ([]string, error) {
return strings.Split(string(output), "\n"), nil
}
+func (git *git) ListCommitHashes(baseCommit string) ([]string, error) {
+ output, err := git.git("log", "--pretty=format:%h", baseCommit)
+ if err != nil {
+ return nil, err
+ }
+ return strings.Split(string(output), "\n"), nil
+}
+
func (git *git) ExtractFixTagsFromCommits(baseCommit, email string) ([]*Commit, error) {
user, domain, err := splitEmail(email)
if err != nil {
@@ -576,3 +584,7 @@ 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))
+}
diff --git a/pkg/vcs/git_test.go b/pkg/vcs/git_test.go
index b216ddf75..24d4cab58 100644
--- a/pkg/vcs/git_test.go
+++ b/pkg/vcs/git_test.go
@@ -4,6 +4,7 @@
package vcs
import (
+ "os"
"reflect"
"testing"
"time"
@@ -236,3 +237,81 @@ func TestContains(t *testing.T) {
t.Fatalf("contains found commit that is not in current branch")
}
}
+
+func TestCommitHashes(t *testing.T) {
+ baseDir := t.TempDir()
+ repo := MakeTestRepo(t, baseDir)
+
+ repo.Git("checkout", "-b", "branch-a")
+ repo.Git("commit", "--no-edit", "--allow-empty", "-m", "target")
+ repo.Git("checkout", "-b", "branch-b")
+ repo.Git("commit", "--no-edit", "--allow-empty", "-m", "target")
+ got, err := repo.repo.ListCommitHashes("HEAD")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(got) != 2 {
+ t.Fatalf("expected 2 commits")
+ }
+ for i, commit := range got {
+ if contained, _ := repo.repo.Contains(commit); !contained {
+ t.Fatalf("commit %d is not contained", i)
+ }
+ }
+
+ // Now change HEAD.
+ repo.Git("checkout", "branch-a")
+ got, err = repo.repo.ListCommitHashes("HEAD")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(got) != 1 {
+ t.Fatalf("expected 1 commit, got %d", len(got))
+ }
+ if contained, _ := repo.repo.Contains(got[0]); !contained {
+ t.Fatalf("commit in branch-b is not contained")
+ }
+}
+
+func TestObject(t *testing.T) {
+ baseDir := t.TempDir()
+ repo := MakeTestRepo(t, baseDir)
+ firstRev := []byte("First revision")
+ secondRev := []byte("Second revision")
+
+ if err := os.WriteFile(baseDir+"/object.txt", firstRev, 0644); err != nil {
+ t.Fatal(err)
+ }
+ repo.Git("add", "object.txt")
+ repo.Git("commit", "--no-edit", "--allow-empty", "-m", "target")
+
+ if err := os.WriteFile(baseDir+"/object.txt", secondRev, 0644); err != nil {
+ t.Fatal(err)
+ }
+ repo.Git("add", "object.txt")
+ repo.Git("commit", "--no-edit", "--allow-empty", "-m", "target")
+
+ commits, err := repo.repo.ListCommitHashes("HEAD")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(commits) != 2 {
+ t.Fatalf("expected 2 commits, got %d", len(commits))
+ }
+ // Verify file's contents at the first revision.
+ data, err := repo.repo.Object("object.txt", commits[1])
+ if err != nil {
+ t.Fatal(err)
+ }
+ if diff := cmp.Diff(data, firstRev); diff != "" {
+ t.Fatal(diff)
+ }
+ // And at the second one.
+ data, err = repo.repo.Object("object.txt", commits[0])
+ if err != nil {
+ t.Fatal(err)
+ }
+ if diff := cmp.Diff(data, secondRev); diff != "" {
+ t.Fatal(diff)
+ }
+}
diff --git a/pkg/vcs/git_test_util.go b/pkg/vcs/git_test_util.go
index 3d3de8907..d341c6144 100644
--- a/pkg/vcs/git_test_util.go
+++ b/pkg/vcs/git_test_util.go
@@ -28,6 +28,7 @@ type TestRepo struct {
}
func (repo *TestRepo) Git(args ...string) {
+ repo.t.Helper()
cmd := osutil.Command("git", args...)
cmd.Dir = repo.Dir
cmd.Env = filterEnv()
diff --git a/pkg/vcs/vcs.go b/pkg/vcs/vcs.go
index 4b4f26e06..8d8c0302b 100644
--- a/pkg/vcs/vcs.go
+++ b/pkg/vcs/vcs.go
@@ -61,6 +61,12 @@ type Repo interface {
// Remote is not fetched and only commits reachable from the checked out HEAD are searched
// (e.g. do CheckoutBranch before).
Contains(commit string) (bool, error)
+
+ // ListCommitHashes lists all commit hashes reachable from baseCommit.
+ ListCommitHashes(baseCommit string) ([]string, error)
+
+ // Object returns the contents of a git repository object at the particular moment in history.
+ Object(name, commit string) ([]byte, error)
}
// Bisecter may be optionally implemented by Repo.
@@ -202,6 +208,10 @@ func NewSyzkallerRepo(dir string, opts ...RepoOpt) Repo {
return git
}
+func NewLKMLRepo(dir string) Repo {
+ return newGit(dir, nil, []RepoOpt{OptDontSandbox})
+}
+
func Patch(dir string, patch []byte) error {
// Do --dry-run first to not mess with partially consistent state.
cmd := osutil.Command("patch", "-p1", "--force", "--ignore-whitespace", "--dry-run")