aboutsummaryrefslogtreecommitdiff
path: root/repository
diff options
context:
space:
mode:
Diffstat (limited to 'repository')
-rw-r--r--repository/refs_test.go72
-rw-r--r--repository/stored_test.go103
-rw-r--r--repository/traversal_test.go79
3 files changed, 254 insertions, 0 deletions
diff --git a/repository/refs_test.go b/repository/refs_test.go
index 4418c707..8ebf93a6 100644
--- a/repository/refs_test.go
+++ b/repository/refs_test.go
@@ -94,3 +94,75 @@ func TestResolveRefErrorSurface(t *testing.T) {
}
})
}
+
+func TestListRefsLooseOverridesPacked(t *testing.T) {
+ t.Parallel()
+
+ testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
+ repoHarness := testgit.NewRepo(t, testgit.RepoOptions{
+ ObjectFormat: algo,
+ Bare: true,
+ RefFormat: "files",
+ })
+
+ repoHarness.SymbolicRef(t, "HEAD", "refs/heads/main")
+ _, _, commit1 := repoHarness.MakeCommit(t, "commit-one")
+ repoHarness.UpdateRef(t, "refs/heads/main", commit1)
+ repoHarness.UpdateRef(t, "refs/heads/feature", commit1)
+ repoHarness.PackRefs(t, "--all", "--prune")
+
+ _, _, commit2 := repoHarness.MakeCommit(t, "commit-two")
+ repoHarness.UpdateRef(t, "refs/heads/main", commit2)
+
+ repo, err := repository.Open(repoHarness.Dir())
+ if err != nil {
+ t.Fatalf("repository.Open: %v", err)
+ }
+ defer func() { _ = repo.Close() }()
+
+ mainRef, err := repo.ResolveRefFully("refs/heads/main")
+ if err != nil {
+ t.Fatalf("ResolveRefFully(main): %v", err)
+ }
+ if mainRef.ID != commit2 {
+ t.Fatalf("ResolveRefFully(main) id = %s, want %s", mainRef.ID, commit2)
+ }
+
+ refs, err := repo.ListRefs("refs/heads/*")
+ if err != nil {
+ t.Fatalf("ListRefs(refs/heads/*): %v", err)
+ }
+ byName := make(map[string]ref.Ref, len(refs))
+ for _, entry := range refs {
+ name := entry.Name()
+ if _, exists := byName[name]; exists {
+ t.Fatalf("duplicate ref %q in ListRefs output", name)
+ }
+ byName[name] = entry
+ }
+
+ main, ok := byName["refs/heads/main"]
+ if !ok {
+ t.Fatalf("missing refs/heads/main in ListRefs output")
+ }
+ mainDetached, ok := main.(ref.Detached)
+ if !ok {
+ t.Fatalf("refs/heads/main type = %T, want ref.Detached", main)
+ }
+ if mainDetached.ID != commit2 {
+ t.Fatalf("refs/heads/main id = %s, want %s", mainDetached.ID, commit2)
+ }
+
+ feature, ok := byName["refs/heads/feature"]
+ if !ok {
+ t.Fatalf("missing refs/heads/feature in ListRefs output")
+ }
+ featureDetached, ok := feature.(ref.Detached)
+ if !ok {
+ t.Fatalf("refs/heads/feature type = %T, want ref.Detached", feature)
+ }
+ if featureDetached.ID != commit1 {
+ t.Fatalf("refs/heads/feature id = %s, want %s", featureDetached.ID, commit1)
+ }
+ })
+}
diff --git a/repository/stored_test.go b/repository/stored_test.go
index da1d1392..3768d450 100644
--- a/repository/stored_test.go
+++ b/repository/stored_test.go
@@ -161,3 +161,106 @@ func TestResolveTreeEntryErrors(t *testing.T) {
})
})
}
+
+func TestResolveTreeEntryDeepPath(t *testing.T) {
+ t.Parallel()
+
+ testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
+ const depth = 50
+
+ repoHarness := testgit.NewRepo(t, testgit.RepoOptions{
+ ObjectFormat: algo,
+ Bare: true,
+ RefFormat: "files",
+ })
+
+ leafBlobID := repoHarness.HashObject(t, "blob", []byte("deep-content\n"))
+ currentTree := repoHarness.Mktree(t, fmt.Sprintf("100644 blob %s\tleaf.txt\n", leafBlobID))
+
+ parts := make([][]byte, 0, depth+1)
+ for i := depth - 1; i >= 0; i-- {
+ name := fmt.Sprintf("level%02d", i)
+ currentTree = repoHarness.Mktree(t, fmt.Sprintf("040000 tree %s\t%s\n", currentTree, name))
+ parts = append([][]byte{[]byte(name)}, parts...)
+ }
+ parts = append(parts, []byte("leaf.txt"))
+
+ repo, err := repository.Open(repoHarness.Dir())
+ if err != nil {
+ t.Fatalf("repository.Open: %v", err)
+ }
+ defer func() { _ = repo.Close() }()
+
+ rootTree, err := repo.ReadStoredTree(currentTree)
+ if err != nil {
+ t.Fatalf("ReadStoredTree(root): %v", err)
+ }
+
+ entry, err := repo.ResolveTreeEntry(rootTree, parts)
+ if err != nil {
+ t.Fatalf("ResolveTreeEntry(deep): %v", err)
+ }
+ if entry.Mode != object.FileModeRegular {
+ t.Fatalf("ResolveTreeEntry(deep) mode = %o, want %o", entry.Mode, object.FileModeRegular)
+ }
+ if entry.ID != leafBlobID {
+ t.Fatalf("ResolveTreeEntry(deep) id = %s, want %s", entry.ID, leafBlobID)
+ }
+ })
+}
+
+func TestReadStoredTreeMixedModes(t *testing.T) {
+ t.Parallel()
+
+ testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
+ repoHarness := testgit.NewRepo(t, testgit.RepoOptions{
+ ObjectFormat: algo,
+ Bare: true,
+ RefFormat: "files",
+ })
+
+ normalID := repoHarness.HashObject(t, "blob", []byte("normal-file\n"))
+ execID := repoHarness.HashObject(t, "blob", []byte("#!/bin/sh\necho hi\n"))
+ symID := repoHarness.HashObject(t, "blob", []byte("normal.txt"))
+ nestedBlobID := repoHarness.HashObject(t, "blob", []byte("nested\n"))
+ nestedTreeID := repoHarness.Mktree(t, fmt.Sprintf("100644 blob %s\tleaf.txt\n", nestedBlobID))
+
+ rootTreeID := repoHarness.Mktree(t,
+ fmt.Sprintf(
+ "100644 blob %s\tnormal.txt\n100755 blob %s\trun.sh\n120000 blob %s\tlink.txt\n040000 tree %s\tdir\n",
+ normalID,
+ execID,
+ symID,
+ nestedTreeID,
+ ),
+ )
+
+ repo, err := repository.Open(repoHarness.Dir())
+ if err != nil {
+ t.Fatalf("repository.Open: %v", err)
+ }
+ defer func() { _ = repo.Close() }()
+
+ rootTree, err := repo.ReadStoredTree(rootTreeID)
+ if err != nil {
+ t.Fatalf("ReadStoredTree(root): %v", err)
+ }
+
+ expect := map[string]object.FileMode{
+ "normal.txt": object.FileModeRegular,
+ "run.sh": object.FileModeExecutable,
+ "link.txt": object.FileModeSymlink,
+ "dir": object.FileModeDir,
+ }
+
+ for name, wantMode := range expect {
+ entry := rootTree.Tree().Entry([]byte(name))
+ if entry == nil {
+ t.Fatalf("Entry(%q) returned nil", name)
+ }
+ if entry.Mode != wantMode {
+ t.Fatalf("Entry(%q) mode = %o, want %o", name, entry.Mode, wantMode)
+ }
+ }
+ })
+}
diff --git a/repository/traversal_test.go b/repository/traversal_test.go
new file mode 100644
index 00000000..61560fde
--- /dev/null
+++ b/repository/traversal_test.go
@@ -0,0 +1,79 @@
+package repository_test
+
+import (
+ "testing"
+
+ "codeberg.org/lindenii/furgit/internal/testgit"
+ "codeberg.org/lindenii/furgit/object"
+ "codeberg.org/lindenii/furgit/objectid"
+ "codeberg.org/lindenii/furgit/repository"
+)
+
+func TestRepositoryDepthFirstEnumerationFromHEAD(t *testing.T) {
+ t.Parallel()
+
+ testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
+ repoHarness := testgit.NewRepo(t, testgit.RepoOptions{
+ ObjectFormat: algo,
+ Bare: true,
+ RefFormat: "files",
+ })
+
+ _, _, commit1 := repoHarness.MakeCommit(t, "walk-one")
+ blob2, tree2 := repoHarness.MakeSingleFileTree(t, "second.txt", []byte("second\n"))
+ commit2 := repoHarness.CommitTree(t, tree2, "walk-two", commit1)
+ _ = blob2
+ repoHarness.UpdateRef(t, "refs/heads/main", commit2)
+ repoHarness.SymbolicRef(t, "HEAD", "refs/heads/main")
+
+ repo, err := repository.Open(repoHarness.Dir())
+ if err != nil {
+ t.Fatalf("repository.Open: %v", err)
+ }
+ defer func() { _ = repo.Close() }()
+
+ head, err := repo.ResolveRefFully("HEAD")
+ if err != nil {
+ t.Fatalf("ResolveRefFully(HEAD): %v", err)
+ }
+
+ visited := make(map[objectid.ObjectID]bool)
+ queue := []objectid.ObjectID{head.ID}
+ objectsRead := 0
+
+ for len(queue) > 0 {
+ id := queue[0]
+ queue = queue[1:]
+
+ if visited[id] {
+ continue
+ }
+ visited[id] = true
+
+ stored, err := repo.ReadStored(id)
+ if err != nil {
+ t.Fatalf("ReadStored(%s): %v", id, err)
+ }
+ objectsRead++
+
+ switch obj := stored.Object().(type) {
+ case *object.Commit:
+ queue = append(queue, obj.Tree)
+ queue = append(queue, obj.Parents...)
+ case *object.Tree:
+ for _, entry := range obj.Entries {
+ queue = append(queue, entry.ID)
+ }
+ case *object.Tag:
+ queue = append(queue, obj.Target)
+ case *object.Blob:
+ default:
+ t.Fatalf("unexpected object type: %T", obj)
+ }
+ }
+
+ if objectsRead == 0 {
+ t.Fatalf("no objects were enumerated from HEAD")
+ }
+ })
+}