From acfd774f464736ec3db293e9205f8e7acb839f04 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Mon, 14 May 2018 11:17:23 +0200 Subject: pkg/git: add bisection functionality Bisect bisects good..bad commit range against the provided predicate (wrapper around git bisect). The predicate should return an error only if there is no way to proceed (it will abort the process), if possible it should prefer to return BisectSkip. Progress of the process is streamed to the provided trace. Returns the first commit on which the predicate returns BisectBad. Update #501 --- pkg/git/git.go | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) (limited to 'pkg/git') diff --git a/pkg/git/git.go b/pkg/git/git.go index f7e93825f..59715ed07 100644 --- a/pkg/git/git.go +++ b/pkg/git/git.go @@ -30,6 +30,7 @@ const ( // This involves fetching/resetting/cloning as necessary to recover from all possible problems. // Returns hash of the HEAD commit in the specified branch. func Poll(dir, repo, branch string) (*Commit, error) { + runSandboxed(dir, "git", "bisect", "reset") runSandboxed(dir, "git", "reset", "--hard") origin, err := runSandboxed(dir, "git", "remote", "get-url", "origin") if err != nil || strings.TrimSpace(string(origin)) != repo { @@ -61,6 +62,7 @@ func Poll(dir, repo, branch string) (*Commit, error) { // CheckoutBranch checkouts the specified repository/branch in dir. func CheckoutBranch(dir, repo, branch string) (*Commit, error) { + runSandboxed(dir, "git", "bisect", "reset") if _, err := runSandboxed(dir, "git", "reset", "--hard"); err != nil { if err := initRepo(dir); err != nil { return nil, err @@ -78,6 +80,7 @@ func CheckoutBranch(dir, repo, branch string) (*Commit, error) { // CheckoutCommit checkouts the specified repository on the specified commit in dir. func CheckoutCommit(dir, repo, commit string) (*Commit, error) { + runSandboxed(dir, "git", "bisect", "reset") if _, err := runSandboxed(dir, "git", "reset", "--hard"); err != nil { if err := initRepo(dir); err != nil { return nil, err @@ -318,6 +321,65 @@ func Patch(dir string, patch []byte) error { return nil } +type BisectResult int + +const ( + BisectBad BisectResult = iota + BisectGood + BisectSkip +) + +// Bisect bisects good..bad commit range against the provided predicate (wrapper around git bisect). +// The predicate should return an error only if there is no way to proceed +// (it will abort the process), if possible it should prefer to return BisectSkip. +// Progress of the process is streamed to the provided trace. +// Returns the first commit on which the predicate returns BisectBad. +func Bisect(dir, bad, good string, trace io.Writer, pred func() (BisectResult, error)) (*Commit, error) { + runSandboxed(dir, "git", "bisect", "reset") + runSandboxed(dir, "git", "reset", "--hard") + firstBad, err := GetCommit(dir, bad) + if err != nil { + return nil, err + } + output, err := runSandboxed(dir, "git", "bisect", "start", bad, good) + if err != nil { + return nil, err + } + defer runSandboxed(dir, "git", "bisect", "reset") + fmt.Fprintf(trace, "# git bisect start %v %v\n%s", bad, good, output) + current, err := HeadCommit(dir) + if err != nil { + return nil, err + } + var bisectTerms = [...]string{ + BisectBad: "bad", + BisectGood: "good", + BisectSkip: "skip", + } + for { + res, err := pred() + if err != nil { + return nil, err + } + if res == BisectBad { + firstBad = current + } + output, err = runSandboxed(dir, "git", "bisect", bisectTerms[res]) + if err != nil { + return nil, err + } + fmt.Fprintf(trace, "# git bisect %v %v\n%s", bisectTerms[res], current.Hash, output) + next, err := HeadCommit(dir) + if err != nil { + return nil, err + } + if current.Hash == next.Hash { + return firstBad, nil + } + current = next + } +} + // PreviousReleaseTags returns list of preceding release tags that are reachable from the given commit. // Note: linux-specific. func PreviousReleaseTags(dir, commit string) ([]string, error) { -- cgit mrf-deployment