From 4a12ccb78d19dee04429b9512592ac84b6dc90c2 Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 25 Oct 2026 01:08:39 +0200 Subject: [PATCH] buildstep: deepen a merge preview's history from the mirror when it holds the trunk commit, so the preview fetches no history from the forge (issue 1921, try speed S6) --- go/buildstep/merge.go | 21 ++++++++++----- go/buildstep/merge_integration_test.go | 30 +++++++++++++++++++++ go/buildstep/merge_test.go | 36 ++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 6 deletions(-) diff --git a/go/buildstep/merge.go b/go/buildstep/merge.go index deee405..e6831bb 100644 --- a/go/buildstep/merge.go +++ b/go/buildstep/merge.go @@ -24,7 +24,8 @@ type MergeResult struct { // signature, no hook, nothing of the machine's git configuration that changes a merge. Nothing is pushed. // // A merge needs both histories, so it makes the fetch whole (the shallow clone FetchCommit made is deepened) and -// fetches base by its sha. A head that does not merge cleanly is an answer, not an error: the files in conflict, the +// fetches base by its sha: from the mirror WithMirror names, when it holds base (UpdateMirror to base first), else +// from remote. A head that does not merge cleanly is an answer, not an error: the files in conflict, the // merge undone and the workspace left at base. A head already in base answers a Merge equal to Base and no changed // paths. An error is that the merge could not be tried: a base or commit that is not a full sha, no base, a workspace // not at commit, or a fetch or git command that failed. remote and the options are those of FetchCommit; the runner's @@ -67,12 +68,20 @@ func MergeInto(ctx context.Context, run forgekey.Runner, dir, remote, base, comm if err != nil { return res, err } - fetch := []string{"fetch", "--no-tags", "origin", base} - if strings.TrimSpace(shallow) == "true" { - fetch = []string{"fetch", "--no-tags", "--unshallow", "origin", base} + fetchFrom := func(source string) error { + fetch := []string{"fetch", "--no-tags", source, base} + if strings.TrimSpace(shallow) == "true" { + fetch = []string{"fetch", "--no-tags", "--unshallow", source, base} + } + _, err := git(fetch...) + return err } - if _, err := git(fetch...); err != nil { - return res, fmt.Errorf("the trunk commit %s: %w", base, err) + // With a mirror brought to the trunk commit, the history is deepened from it, a local fetch (issue 1921, try speed + // S6); a mirror that cannot answer leaves it to the forge, as before. + if o.mirror == "" || fetchFrom(o.mirror) != nil { + if err := fetchFrom("origin"); err != nil { + return res, fmt.Errorf("the trunk commit %s: %w", base, err) + } } if _, err := git("checkout", "-q", "--detach", base); err != nil { return res, fmt.Errorf("the trunk commit %s cannot be checked out: %w", base, err) diff --git a/go/buildstep/merge_integration_test.go b/go/buildstep/merge_integration_test.go index 88b6d11..ed6a23c 100644 --- a/go/buildstep/merge_integration_test.go +++ b/go/buildstep/merge_integration_test.go @@ -170,3 +170,33 @@ func TestMergeIntoAHeadAlreadyInTheTrunkChangesNothing(t *testing.T) { t.Errorf("result = %+v, %v", res, err) } } + +// 1921/S6: with a mirror brought to the head and to the trunk commit, the merge asks the forge for nothing: it succeeds +// with the forge gone. +func TestMergeIntoWithAMirrorAtTheTrunkNeedsNoForge(t *testing.T) { + r := newMergeRig(t) + head := r.head(map[string]string{"b.txt": "head\n"}) + base := r.advance(map[string]string{"a.txt": "one\ntwo\nthree\nfour\n"}) + mirror := filepath.Join(t.TempDir(), "mirror.git") + if err := os.MkdirAll(mirror, 0o755); err != nil { + t.Fatal(err) + } + for _, c := range []string{head, base} { + if err := UpdateMirror(context.Background(), forgekey.ExecRunner, mirror, r.remote, c, "", AllowFileRemote()); err != nil { + t.Fatal(err) + } + } + if err := FetchCommit(context.Background(), forgekey.ExecRunner, r.ws, r.remote, head, "", AllowFileRemote(), WithMirror(mirror)); err != nil { + t.Fatal(err) + } + if err := os.RemoveAll(r.bare); err != nil { // the forge is gone + t.Fatal(err) + } + res, err := MergeInto(context.Background(), forgekey.ExecRunner, r.ws, r.remote, base, head, AllowFileRemote(), WithMirror(mirror)) + if err != nil { + t.Fatalf("the merge asked the forge: %v", err) + } + if len(res.Merge) != 40 || !reflect.DeepEqual(res.Changed, []string{"b.txt"}) { + t.Errorf("result = %+v", res) + } +} diff --git a/go/buildstep/merge_test.go b/go/buildstep/merge_test.go index 4eb0a88..e4ebe22 100644 --- a/go/buildstep/merge_test.go +++ b/go/buildstep/merge_test.go @@ -72,3 +72,39 @@ func TestMergeIntoRunsTheCheckMergesCommands(t *testing.T) { } } } + +// 1921/S6: with a mirror that holds the trunk commit, the merge deepens the workspace from the mirror, a local fetch, +// and asks the forge nothing; a mirror that cannot answer falls back to the forge. +func TestMergeIntoWithAMirrorFetchesTheTrunkFromTheMirror(t *testing.T) { + base := strings.Repeat("b", 40) + mirror := t.TempDir() + answer := func(failMirror bool) *fakeGit { + return &fakeGit{rule: func(l string) forgekey.Ran { + switch { + case strings.HasSuffix(l, "rev-parse HEAD"): + return forgekey.Ran{Stdout: sha + "\n"} + case strings.Contains(l, "--is-shallow-repository"): + return forgekey.Ran{Stdout: "true\n"} + case failMirror && strings.Contains(l, " fetch ") && strings.Contains(l, mirror): + return forgekey.Ran{Status: 128, Stderr: "fatal: not our ref"} + } + return forgekey.Ran{} + }} + } + f := answer(false) + dir := t.TempDir() + if _, err := MergeInto(context.Background(), f.run, dir, "ssh://mesh-forge/o/r.git", base, sha, WithMirror(mirror)); err != nil { + t.Fatal(err) + } + all := strings.Join(f.calls, "\n") + if !strings.Contains(all, "fetch --no-tags --unshallow "+mirror+" "+base) || strings.Contains(all, "--unshallow origin") { + t.Errorf("the trunk was not fetched from the mirror alone:\n%s", all) + } + g := answer(true) + if _, err := MergeInto(context.Background(), g.run, t.TempDir(), "ssh://mesh-forge/o/r.git", base, sha, WithMirror(mirror)); err != nil { + t.Fatal(err) + } + if all := strings.Join(g.calls, "\n"); !strings.Contains(all, "fetch --no-tags --unshallow origin "+base) { + t.Errorf("a mirror that could not answer did not fall back to the forge:\n%s", all) + } +}