From 43995b04fc9795de8791e8a9d82206f95d8acaf1 Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Fri, 31 Jan 2025 05:56:38 +0100 Subject: [PATCH] feat: merge-base option --- patch.go | 46 ++++++++++++++++++++++++++++++++++++++++------ patch_test.go | 2 +- revgrep.go | 14 +++++++++++--- 3 files changed, 52 insertions(+), 10 deletions(-) diff --git a/patch.go b/patch.go index c0cbf76..81a2acd 100644 --- a/patch.go +++ b/patch.go @@ -12,6 +12,12 @@ import ( "strings" ) +type patchOption struct { + revisionFrom string + revisionTo string + mergeBase string +} + // GitPatch returns a patch from a git repository. // If no git repository was found and no errors occurred, nil is returned, // else an error is returned revisionFrom and revisionTo defines the git diff parameters, @@ -20,7 +26,7 @@ import ( // If revisionFrom is set but revisionTo is not, // untracked files will be included, to exclude untracked files set revisionTo to HEAD~. // It's incorrect to specify revisionTo without a revisionFrom. -func GitPatch(ctx context.Context, revisionFrom, revisionTo string) (io.Reader, []string, error) { +func GitPatch(ctx context.Context, option patchOption) (io.Reader, []string, error) { // check if git repo exists if err := exec.CommandContext(ctx, "git", "status", "--porcelain").Run(); err != nil { // don't return an error, we assume the error is not repo exists @@ -45,11 +51,23 @@ func GitPatch(ctx context.Context, revisionFrom, revisionTo string) (io.Reader, newFiles = append(newFiles, string(file)) } - if revisionFrom != "" { - args := []string{revisionFrom} + if option.mergeBase != "" { + var base string + base, err = getMergeBase(ctx, option.mergeBase) + if err != nil { + return nil, nil, err + } + + if base != "" { + option.revisionFrom = base + } + } + + if option.revisionFrom != "" { + args := []string{option.revisionFrom} - if revisionTo != "" { - args = append(args, revisionTo) + if option.revisionTo != "" { + args = append(args, option.revisionTo) } args = append(args, "--") @@ -59,7 +77,7 @@ func GitPatch(ctx context.Context, revisionFrom, revisionTo string) (io.Reader, return nil, nil, errDiff } - if revisionTo == "" { + if option.revisionTo == "" { return patch, newFiles, nil } @@ -159,3 +177,19 @@ func isSupportedByGit(ctx context.Context, major, minor, patch int) bool { return currentMajor*1_000_000_000+currentMinor*1_000_000+currentPatch*1_000 >= major*1_000_000_000+minor*1_000_000+patch*1_000 } + +func getMergeBase(ctx context.Context, base string) (string, error) { + cmd := exec.CommandContext(ctx, "git", "merge-base", base, "HEAD") + + patch := new(bytes.Buffer) + errBuff := new(bytes.Buffer) + + cmd.Stdout = patch + cmd.Stderr = errBuff + + if err := cmd.Run(); err != nil { + return "", fmt.Errorf("error executing %q: %w: %w", strings.Join(cmd.Args, " "), err, readAsError(errBuff)) + } + + return strings.TrimSpace(patch.String()), nil +} diff --git a/patch_test.go b/patch_test.go index 988e698..99cfc10 100644 --- a/patch_test.go +++ b/patch_test.go @@ -20,7 +20,7 @@ func TestGitPatch_nonGitDir(t *testing.T) { t.Cleanup(func() { _ = os.Chdir(wd) }) - patch, newFiles, err := GitPatch(context.Background(), "", "") + patch, newFiles, err := GitPatch(context.Background(), patchOption{}) if err != nil { t.Errorf("error expected nil, got: %v", err) } diff --git a/revgrep.go b/revgrep.go index 184db1c..ca4ac79 100644 --- a/revgrep.go +++ b/revgrep.go @@ -29,10 +29,12 @@ type Checker struct { Debug io.Writer // RevisionFrom check revision starting at, leave blank for auto-detection ignored if patch is set. RevisionFrom string - // WholeFiles indicates that the user wishes to see all issues that comes up anywhere in any file that has been changed in this revision or patch. - WholeFiles bool // RevisionTo checks revision finishing at, leave blank for auto-detection ignored if patch is set. RevisionTo string + // MergeBase checks revision starting at the best common ancestor, leave blank for auto-detection ignored if patch is set. + MergeBase string + // WholeFiles indicates that the user wishes to see all issues that comes up anywhere in any file that has been changed in this revision or patch. + WholeFiles bool // Regexp to match path, line number, optional column number, and message. Regexp string // AbsPath is used to make an absolute path of an issue's filename to be relative in order to match patch file. @@ -228,8 +230,14 @@ func (c *Checker) loadPatch(ctx context.Context) error { return nil } + option := patchOption{ + revisionFrom: c.RevisionFrom, + revisionTo: c.RevisionTo, + mergeBase: c.MergeBase, + } + var err error - c.Patch, c.NewFiles, err = GitPatch(ctx, c.RevisionFrom, c.RevisionTo) + c.Patch, c.NewFiles, err = GitPatch(ctx, option) if err != nil { return fmt.Errorf("could not read git repo: %w", err) }