From 040b572d95e4ca27e1ada6113c405b8a1eb4a669 Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Wed, 11 Mar 2026 20:41:32 +0800 Subject: commitquery: Merge from ancestor and mergebases --- mergebase/base.go | 30 ---- mergebase/compute.go | 73 ---------- mergebase/integration_test.go | 309 --------------------------------------- mergebase/mergebase.go | 20 --- mergebase/query.go | 24 --- mergebase/unit_test.go | 332 ------------------------------------------ 6 files changed, 788 deletions(-) delete mode 100644 mergebase/base.go delete mode 100644 mergebase/compute.go delete mode 100644 mergebase/integration_test.go delete mode 100644 mergebase/mergebase.go delete mode 100644 mergebase/query.go delete mode 100644 mergebase/unit_test.go (limited to 'mergebase') diff --git a/mergebase/base.go b/mergebase/base.go deleted file mode 100644 index 278fbed2..00000000 --- a/mergebase/base.go +++ /dev/null @@ -1,30 +0,0 @@ -package mergebase - -import ( - commitgraphread "codeberg.org/lindenii/furgit/commitgraph/read" - "codeberg.org/lindenii/furgit/objectid" - "codeberg.org/lindenii/furgit/objectstore" -) - -// Base reports one merge base between left and right, if any. -// -// Both inputs are peeled through annotated tags before commit traversal. -func Base( - store objectstore.Store, - graph *commitgraphread.Reader, - left objectid.ObjectID, - right objectid.ObjectID, -) (objectid.ObjectID, bool, error) { - query := Query(store, graph, left, right) - - bases, err := query.All() - if err != nil { - return objectid.ObjectID{}, false, err - } - - if len(bases) == 0 { - return objectid.ObjectID{}, false, nil - } - - return bases[0], true, nil -} diff --git a/mergebase/compute.go b/mergebase/compute.go deleted file mode 100644 index 76d2294c..00000000 --- a/mergebase/compute.go +++ /dev/null @@ -1,73 +0,0 @@ -package mergebase - -import ( - "slices" - - "codeberg.org/lindenii/furgit/internal/commitquery" - "codeberg.org/lindenii/furgit/internal/peel" - "codeberg.org/lindenii/furgit/objectid" -) - -// All returns all merge bases in Git's merge-base --all order. -func (query *Bases) All() ([]objectid.ObjectID, error) { - if query.computed { - return slices.Clone(query.bases), query.err - } - - query.computed = true - - leftCommit, err := peel.ToCommit(query.store, query.left) - if err != nil { - query.err = err - - return nil, err - } - - rightCommit, err := peel.ToCommit(query.store, query.right) - if err != nil { - query.err = err - - return nil, err - } - - ctx := commitquery.NewContext(query.store, query.graph) - - leftIdx, err := ctx.ResolveOID(leftCommit) - if err != nil { - query.err = err - - return nil, err - } - - rightIdx, err := ctx.ResolveOID(rightCommit) - if err != nil { - query.err = err - - return nil, err - } - - candidates, err := commitquery.MergeBases(ctx, leftIdx, rightIdx) - if err != nil { - query.err = err - - return nil, err - } - - slices.SortFunc(candidates, func(left, right commitquery.NodeIndex) int { - switch { - case ctx.CommitTime(left) > ctx.CommitTime(right): - return -1 - case ctx.CommitTime(left) < ctx.CommitTime(right): - return 1 - default: - return objectid.Compare(ctx.ID(left), ctx.ID(right)) - } - }) - - query.bases = make([]objectid.ObjectID, 0, len(candidates)) - for _, idx := range candidates { - query.bases = append(query.bases, ctx.ID(idx)) - } - - return slices.Clone(query.bases), nil -} diff --git a/mergebase/integration_test.go b/mergebase/integration_test.go deleted file mode 100644 index 3d85409d..00000000 --- a/mergebase/integration_test.go +++ /dev/null @@ -1,309 +0,0 @@ -package mergebase_test - -import ( - "maps" - "slices" - "strings" - "testing" - - "codeberg.org/lindenii/furgit/internal/testgit" - "codeberg.org/lindenii/furgit/mergebase" - "codeberg.org/lindenii/furgit/objectid" -) - -func TestQueryMatchesGitMergeBaseAll(t *testing.T) { - t.Parallel() - - testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper - testRepo := testgit.NewRepo(t, testgit.RepoOptions{ - ObjectFormat: algo, - Bare: true, - RefFormat: "files", - }) - - _, tree1 := testRepo.MakeSingleFileTree(t, "base.txt", []byte("base\n")) - base := testRepo.CommitTree(t, tree1, "base") - - _, tree2 := testRepo.MakeSingleFileTree(t, "left.txt", []byte("left\n")) - left := testRepo.CommitTree(t, tree2, "left", base) - - _, tree3 := testRepo.MakeSingleFileTree(t, "right.txt", []byte("right\n")) - right := testRepo.CommitTree(t, tree3, "right", base) - - tag := testRepo.TagAnnotated(t, "right-tag", right, "right-tag") - - store := testRepo.OpenObjectStore(t) - - query := mergebase.Query(store, nil, left, tag) - - all, err := query.All() - if err != nil { - t.Fatalf("query.All(): %v", err) - } - - got := oidSetFromSlice(all) - - want := gitMergeBaseAllSet(t, testRepo, left, tag) - if !maps.Equal(got, want) { - t.Fatalf("Query(left, tag) mismatch:\n got=%v\nwant=%v", sortedOIDStrings(got), sortedOIDStrings(want)) - } - }) -} - -func TestQueryCrissCrossMatchesGitMergeBaseAll(t *testing.T) { - t.Parallel() - - testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper - testRepo := testgit.NewRepo(t, testgit.RepoOptions{ - ObjectFormat: algo, - Bare: true, - RefFormat: "files", - }) - - _, tree1 := testRepo.MakeSingleFileTree(t, "root.txt", []byte("root\n")) - root := testRepo.CommitTree(t, tree1, "root") - - _, tree2 := testRepo.MakeSingleFileTree(t, "base1.txt", []byte("base1\n")) - base1 := testRepo.CommitTree(t, tree2, "base1", root) - - _, tree3 := testRepo.MakeSingleFileTree(t, "base2.txt", []byte("base2\n")) - base2 := testRepo.CommitTree(t, tree3, "base2", root) - - _, tree4 := testRepo.MakeSingleFileTree(t, "left.txt", []byte("left\n")) - left := testRepo.CommitTree(t, tree4, "left", base1, base2) - - _, tree5 := testRepo.MakeSingleFileTree(t, "right.txt", []byte("right\n")) - right := testRepo.CommitTree(t, tree5, "right", base2, base1) - - store := testRepo.OpenObjectStore(t) - - query := mergebase.Query(store, nil, left, right) - - all, err := query.All() - if err != nil { - t.Fatalf("query.All(): %v", err) - } - - got := oidSetFromSlice(all) - - want := gitMergeBaseAllSet(t, testRepo, left, right) - if !maps.Equal(got, want) { - t.Fatalf("Query(left, right) mismatch:\n got=%v\nwant=%v", sortedOIDStrings(got), sortedOIDStrings(want)) - } - - first, ok, err := mergebase.Base(store, nil, left, right) - if err != nil { - t.Fatalf("Base(left, right): %v", err) - } - - if !ok { - t.Fatal("Base(left, right) unexpectedly reported no base") - } - - if !containsID(want, first) { - t.Fatalf("Base(left, right)=%s, want one of %v", first, slices.Collect(maps.Keys(want))) - } - }) -} - -func TestQueryMatchesGitMergeBaseAllWithCommitGraph(t *testing.T) { - t.Parallel() - - testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper - testRepo := testgit.NewRepo(t, testgit.RepoOptions{ - ObjectFormat: algo, - Bare: true, - RefFormat: "files", - }) - - _, tree1 := testRepo.MakeSingleFileTree(t, "root.txt", []byte("root\n")) - root := testRepo.CommitTree(t, tree1, "root") - - _, tree2 := testRepo.MakeSingleFileTree(t, "base1.txt", []byte("base1\n")) - base1 := testRepo.CommitTree(t, tree2, "base1", root) - - _, tree3 := testRepo.MakeSingleFileTree(t, "base2.txt", []byte("base2\n")) - base2 := testRepo.CommitTree(t, tree3, "base2", root) - - _, tree4 := testRepo.MakeSingleFileTree(t, "left.txt", []byte("left\n")) - left := testRepo.CommitTree(t, tree4, "left", base1, base2) - - _, tree5 := testRepo.MakeSingleFileTree(t, "right.txt", []byte("right\n")) - right := testRepo.CommitTree(t, tree5, "right", base2, base1) - - testRepo.UpdateRef(t, "refs/heads/main", right) - testRepo.SymbolicRef(t, "HEAD", "refs/heads/main") - testRepo.CommitGraphWrite(t, "--reachable") - - store := testRepo.OpenObjectStore(t) - graph := testRepo.OpenCommitGraph(t) - - query := mergebase.Query(store, graph, left, right) - - all, err := query.All() - if err != nil { - t.Fatalf("query.All(): %v", err) - } - - got := oidSetFromSlice(all) - - want := gitMergeBaseAllSet(t, testRepo, left, right) - if !maps.Equal(got, want) { - t.Fatalf("Query(left, right) with commit-graph mismatch:\n got=%v\nwant=%v", sortedOIDStrings(got), sortedOIDStrings(want)) - } - - first, ok, err := mergebase.Base(store, graph, left, right) - if err != nil { - t.Fatalf("Base(left, right): %v", err) - } - - if !ok { - t.Fatal("Base(left, right) unexpectedly reported no base") - } - - if !containsID(want, first) { - t.Fatalf("Base(left, right)=%s, want one of %v", first, slices.Collect(maps.Keys(want))) - } - }) -} - -func TestBaseMatchesGitMergeBaseWithoutAll(t *testing.T) { - t.Parallel() - - testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper - testRepo := testgit.NewRepo(t, testgit.RepoOptions{ - ObjectFormat: algo, - Bare: true, - RefFormat: "files", - }) - - _, tree1 := testRepo.MakeSingleFileTree(t, "root.txt", []byte("root\n")) - root := testRepo.CommitTree(t, tree1, "root") - - _, tree2 := testRepo.MakeSingleFileTree(t, "base1.txt", []byte("base1\n")) - base1 := testRepo.CommitTreeWithEnv(t, []string{ - "GIT_AUTHOR_DATE=1234567890 +0000", - "GIT_COMMITTER_DATE=1234567890 +0000", - }, tree2, "base1", root) - - _, tree3 := testRepo.MakeSingleFileTree(t, "base2.txt", []byte("base2\n")) - base2 := testRepo.CommitTreeWithEnv(t, []string{ - "GIT_AUTHOR_DATE=1234567990 +0000", - "GIT_COMMITTER_DATE=1234567990 +0000", - }, tree3, "base2", root) - - _, tree4 := testRepo.MakeSingleFileTree(t, "left.txt", []byte("left\n")) - left := testRepo.CommitTree(t, tree4, "left", base1, base2) - - _, tree5 := testRepo.MakeSingleFileTree(t, "right.txt", []byte("right\n")) - right := testRepo.CommitTree(t, tree5, "right", base2, base1) - - store := testRepo.OpenObjectStore(t) - - got, ok, err := mergebase.Base(store, nil, left, right) - if err != nil { - t.Fatalf("Base(left, right): %v", err) - } - - if !ok { - t.Fatal("Base(left, right) unexpectedly reported no base") - } - - want := gitMergeBaseOne(t, testRepo, left, right) - if got != want { - t.Fatalf("Base(left, right)=%s, want %s", got, want) - } - - testRepo.UpdateRef(t, "refs/heads/main", right) - testRepo.SymbolicRef(t, "HEAD", "refs/heads/main") - testRepo.CommitGraphWrite(t, "--reachable") - - graph := testRepo.OpenCommitGraph(t) - - got, ok, err = mergebase.Base(store, graph, left, right) - if err != nil { - t.Fatalf("Base(left, right) with commit-graph: %v", err) - } - - if !ok { - t.Fatal("Base(left, right) with commit-graph unexpectedly reported no base") - } - - if got != want { - t.Fatalf("Base(left, right) with commit-graph=%s, want %s", got, want) - } - }) -} - -// oidSetFromSlice collects one object ID slice into a set. -func oidSetFromSlice(ids []objectid.ObjectID) map[objectid.ObjectID]struct{} { - out := make(map[objectid.ObjectID]struct{}) - - for _, id := range ids { - out[id] = struct{}{} - } - - return out -} - -// gitMergeBaseAllSet returns Git's merge-base --all output as a set. -func gitMergeBaseAllSet( - t *testing.T, - testRepo *testgit.TestRepo, - left objectid.ObjectID, - right objectid.ObjectID, -) map[objectid.ObjectID]struct{} { - t.Helper() - - out := testRepo.Run(t, "merge-base", "--all", left.String(), right.String()) - set := make(map[objectid.ObjectID]struct{}) - - for line := range strings.SplitSeq(strings.TrimSpace(out), "\n") { - line = strings.TrimSpace(line) - if line == "" { - continue - } - - id, err := objectid.ParseHex(testRepo.Algorithm(), line) - if err != nil { - t.Fatalf("parse merge-base oid %q: %v", line, err) - } - - set[id] = struct{}{} - } - - return set -} - -// gitMergeBaseOne returns Git's merge-base output without --all. -func gitMergeBaseOne( - t *testing.T, - testRepo *testgit.TestRepo, - left objectid.ObjectID, - right objectid.ObjectID, -) objectid.ObjectID { - t.Helper() - - out := strings.TrimSpace(testRepo.Run(t, "merge-base", left.String(), right.String())) - if out == "" { - t.Fatal("git merge-base returned no output") - } - - id, err := objectid.ParseHex(testRepo.Algorithm(), out) - if err != nil { - t.Fatalf("parse merge-base oid %q: %v", out, err) - } - - return id -} - -func sortedOIDStrings(set map[objectid.ObjectID]struct{}) []string { - out := make([]string, 0, len(set)) - for id := range set { - out = append(out, id.String()) - } - - slices.Sort(out) - - return out -} diff --git a/mergebase/mergebase.go b/mergebase/mergebase.go deleted file mode 100644 index 772dc355..00000000 --- a/mergebase/mergebase.go +++ /dev/null @@ -1,20 +0,0 @@ -// Package mergebase computes best common ancestors between commits. -package mergebase - -import ( - commitgraphread "codeberg.org/lindenii/furgit/commitgraph/read" - "codeberg.org/lindenii/furgit/objectid" - "codeberg.org/lindenii/furgit/objectstore" -) - -// Bases is one merge-base query over two commit roots. -type Bases struct { - store objectstore.Store - graph *commitgraphread.Reader - left objectid.ObjectID - right objectid.ObjectID - - computed bool - bases []objectid.ObjectID - err error -} diff --git a/mergebase/query.go b/mergebase/query.go deleted file mode 100644 index 9a934377..00000000 --- a/mergebase/query.go +++ /dev/null @@ -1,24 +0,0 @@ -package mergebase - -import ( - commitgraphread "codeberg.org/lindenii/furgit/commitgraph/read" - "codeberg.org/lindenii/furgit/objectid" - "codeberg.org/lindenii/furgit/objectstore" -) - -// Query builds one merge-base query over two commit roots. -// -// Both inputs are peeled through annotated tags before commit traversal. -func Query( - store objectstore.Store, - graph *commitgraphread.Reader, - left objectid.ObjectID, - right objectid.ObjectID, -) *Bases { - return &Bases{ - store: store, - graph: graph, - left: left, - right: right, - } -} diff --git a/mergebase/unit_test.go b/mergebase/unit_test.go deleted file mode 100644 index 7ba1ed66..00000000 --- a/mergebase/unit_test.go +++ /dev/null @@ -1,332 +0,0 @@ -package mergebase_test - -import ( - "errors" - "fmt" - "maps" - "slices" - "testing" - - giterrors "codeberg.org/lindenii/furgit/errors" - "codeberg.org/lindenii/furgit/internal/testgit" - "codeberg.org/lindenii/furgit/mergebase" - "codeberg.org/lindenii/furgit/object" - "codeberg.org/lindenii/furgit/objectid" - "codeberg.org/lindenii/furgit/objectstore/memory" - "codeberg.org/lindenii/furgit/objecttype" -) - -// commitBody serializes one minimal commit body. -func commitBody(tree objectid.ObjectID, parents ...objectid.ObjectID) []byte { - buf := fmt.Appendf(nil, "tree %s\n", tree.String()) - for _, parent := range parents { - buf = append(buf, fmt.Appendf(nil, "parent %s\n", parent.String())...) - } - - buf = append(buf, []byte("\nmsg\n")...) - - return buf -} - -// tagBody serializes one minimal annotated tag body. -func tagBody(target objectid.ObjectID, targetType objecttype.Type) []byte { - targetName, ok := objecttype.Name(targetType) - if !ok { - panic("invalid tag target type") - } - - return fmt.Appendf(nil, "object %s\ntype %s\ntag t\n\nmsg\n", target.String(), targetName) -} - -// toSet converts one slice of object IDs into a set. -func toSet(ids []objectid.ObjectID) map[objectid.ObjectID]struct{} { - set := make(map[objectid.ObjectID]struct{}, len(ids)) - for _, id := range ids { - set[id] = struct{}{} - } - - return set -} - -// containsID reports whether one set contains one object ID. -func containsID(set map[objectid.ObjectID]struct{}, id objectid.ObjectID) bool { - _, ok := set[id] - - return ok -} - -// mustSerializeTree serializes one tree or fails the test. -func mustSerializeTree(tb testing.TB, tree *object.Tree) []byte { - tb.Helper() - - body, err := tree.SerializeWithoutHeader() - if err != nil { - tb.Fatalf("SerializeWithoutHeader: %v", err) - } - - return body -} - -// TestQueryLinearHistory reports one linear-history merge base. -func TestQueryLinearHistory(t *testing.T) { - t.Parallel() - - testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper - store := memory.New(algo) - blob := store.AddObject(objecttype.TypeBlob, []byte("blob\n")) - tree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &object.Tree{Entries: []object.TreeEntry{{ - Mode: object.FileModeRegular, - Name: []byte("f"), - ID: blob, - }}})) - base := store.AddObject(objecttype.TypeCommit, commitBody(tree)) - left := store.AddObject(objecttype.TypeCommit, commitBody(tree, base)) - right := store.AddObject(objecttype.TypeCommit, commitBody(tree, left)) - - query := mergebase.Query(store, nil, left, right) - - got, err := query.All() - if err != nil { - t.Fatalf("query.All(): %v", err) - } - - if !slices.Equal(got, []objectid.ObjectID{left}) { - t.Fatalf("Query(left, right)=%v, want [%s]", got, left) - } - - first, ok, err := mergebase.Base(store, nil, left, right) - if err != nil { - t.Fatalf("Base(left, right): %v", err) - } - - if !ok { - t.Fatal("Base(left, right) unexpectedly reported no base") - } - - if first != left { - t.Fatalf("Base(left, right)=%s, want %s", first, left) - } - }) -} - -func TestQueryPeelsAnnotatedTags(t *testing.T) { - t.Parallel() - - testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper - store := memory.New(algo) - blob := store.AddObject(objecttype.TypeBlob, []byte("blob\n")) - leftTree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &object.Tree{Entries: []object.TreeEntry{{ - Mode: object.FileModeRegular, - Name: []byte("left"), - ID: blob, - }}})) - rightTree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &object.Tree{Entries: []object.TreeEntry{{ - Mode: object.FileModeRegular, - Name: []byte("right"), - ID: blob, - }}})) - base := store.AddObject(objecttype.TypeCommit, commitBody(leftTree)) - left := store.AddObject(objecttype.TypeCommit, commitBody(leftTree, base)) - right := store.AddObject(objecttype.TypeCommit, commitBody(rightTree, base)) - tag := store.AddObject(objecttype.TypeTag, tagBody(right, objecttype.TypeCommit)) - - query := mergebase.Query(store, nil, left, tag) - - got, err := query.All() - if err != nil { - t.Fatalf("query.All(): %v", err) - } - - if !slices.Equal(got, []objectid.ObjectID{base}) { - t.Fatalf("Query(left, tag)=%v, want [%s]", got, base) - } - }) -} - -func TestQueryCrissCrossReturnsAllBestCommonAncestors(t *testing.T) { - t.Parallel() - - testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper - store := memory.New(algo) - blob := store.AddObject(objecttype.TypeBlob, []byte("blob\n")) - rootTree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &object.Tree{Entries: []object.TreeEntry{{ - Mode: object.FileModeRegular, - Name: []byte("root"), - ID: blob, - }}})) - base1Tree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &object.Tree{Entries: []object.TreeEntry{{ - Mode: object.FileModeRegular, - Name: []byte("base1"), - ID: blob, - }}})) - base2Tree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &object.Tree{Entries: []object.TreeEntry{{ - Mode: object.FileModeRegular, - Name: []byte("base2"), - ID: blob, - }}})) - leftTree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &object.Tree{Entries: []object.TreeEntry{{ - Mode: object.FileModeRegular, - Name: []byte("left"), - ID: blob, - }}})) - rightTree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &object.Tree{Entries: []object.TreeEntry{{ - Mode: object.FileModeRegular, - Name: []byte("right"), - ID: blob, - }}})) - root := store.AddObject(objecttype.TypeCommit, commitBody(rootTree)) - base1 := store.AddObject(objecttype.TypeCommit, commitBody(base1Tree, root)) - base2 := store.AddObject(objecttype.TypeCommit, commitBody(base2Tree, root)) - left := store.AddObject(objecttype.TypeCommit, commitBody(leftTree, base1, base2)) - right := store.AddObject(objecttype.TypeCommit, commitBody(rightTree, base2, base1)) - - query := mergebase.Query(store, nil, left, right) - - all, err := query.All() - if err != nil { - t.Fatalf("query.All(): %v", err) - } - - got := toSet(all) - - want := map[objectid.ObjectID]struct{}{base1: {}, base2: {}} - if !maps.Equal(got, want) { - t.Fatalf("Query(left, right)=%v, want %v", slices.Collect(maps.Keys(got)), slices.Collect(maps.Keys(want))) - } - - first, ok, err := mergebase.Base(store, nil, left, right) - if err != nil { - t.Fatalf("Base(left, right): %v", err) - } - - if !ok { - t.Fatal("Base(left, right) unexpectedly reported no base") - } - - if !containsID(want, first) { - t.Fatalf("Base(left, right)=%s, want one of %v", first, slices.Collect(maps.Keys(want))) - } - }) -} - -func TestQueryReturnsNoResultWhenNoCommonAncestorExists(t *testing.T) { - t.Parallel() - - testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper - store := memory.New(algo) - leftBlob := store.AddObject(objecttype.TypeBlob, []byte("left\n")) - leftTree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &object.Tree{Entries: []object.TreeEntry{{ - Mode: object.FileModeRegular, - Name: []byte("left"), - ID: leftBlob, - }}})) - rightBlob := store.AddObject(objecttype.TypeBlob, []byte("right\n")) - rightTree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &object.Tree{Entries: []object.TreeEntry{{ - Mode: object.FileModeRegular, - Name: []byte("right"), - ID: rightBlob, - }}})) - left := store.AddObject(objecttype.TypeCommit, commitBody(leftTree)) - right := store.AddObject(objecttype.TypeCommit, commitBody(rightTree)) - - query := mergebase.Query(store, nil, left, right) - - got, err := query.All() - if err != nil { - t.Fatalf("query.All(): %v", err) - } - - if len(got) != 0 { - t.Fatalf("Query(left, right)=%v, want no results", got) - } - - _, ok, err := mergebase.Base(store, nil, left, right) - if err != nil { - t.Fatalf("Base(left, right): %v", err) - } - - if ok { - t.Fatal("Base(left, right) unexpectedly reported a base") - } - }) -} - -func TestQueryRejectsNonCommitAfterPeel(t *testing.T) { - t.Parallel() - - testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper - store := memory.New(algo) - blob := store.AddObject(objecttype.TypeBlob, []byte("blob\n")) - tree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &object.Tree{Entries: []object.TreeEntry{{ - Mode: object.FileModeRegular, - Name: []byte("f"), - ID: blob, - }}})) - commit := store.AddObject(objecttype.TypeCommit, commitBody(tree)) - tagToTree := store.AddObject(objecttype.TypeTag, tagBody(tree, objecttype.TypeTree)) - - query := mergebase.Query(store, nil, commit, tagToTree) - - _, err := query.All() - if err == nil { - t.Fatal("expected error") - } - - typeErr, ok := errors.AsType[*giterrors.ObjectTypeError](err) - if !ok { - t.Fatalf("expected ObjectTypeError, got %T (%v)", err, err) - } - - if typeErr.Got != objecttype.TypeTree || typeErr.Want != objecttype.TypeCommit { - t.Fatalf("unexpected type error: %+v", typeErr) - } - }) -} - -func TestQueryAllIsRepeatable(t *testing.T) { - t.Parallel() - - testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper - store := memory.New(algo) - blob := store.AddObject(objecttype.TypeBlob, []byte("blob\n")) - tree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &object.Tree{Entries: []object.TreeEntry{{ - Mode: object.FileModeRegular, - Name: []byte("f"), - ID: blob, - }}})) - base := store.AddObject(objecttype.TypeCommit, commitBody(tree)) - left := store.AddObject(objecttype.TypeCommit, commitBody(tree, base)) - right := store.AddObject(objecttype.TypeCommit, commitBody(tree, left)) - - query := mergebase.Query(store, nil, left, right) - - first, err := query.All() - if err != nil { - t.Fatalf("query.All() first call: %v", err) - } - - again, err := query.All() - if err != nil { - t.Fatalf("query.All() second call: %v", err) - } - - if !slices.Equal(again, first) { - t.Fatalf("second All()=%v, want %v", again, first) - } - - if len(first) == 0 { - t.Fatal("first All() unexpectedly returned no results") - } - - first[0] = objectid.ObjectID{} - - third, err := query.All() - if err != nil { - t.Fatalf("query.All() third call: %v", err) - } - - if third[0] == (objectid.ObjectID{}) { - t.Fatal("query.All() exposed internal slice state") - } - }) -} -- cgit v1.3.1-10-gc9f91