From 6d72372960241f776e2c667b7debdc4b509e015e Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Sat, 21 Feb 2026 16:37:33 +0800 Subject: repository: Add current repo traversal --- repository/traversal_test.go | 150 +++++++++++++++++++++++++++++++------------ 1 file changed, 108 insertions(+), 42 deletions(-) diff --git a/repository/traversal_test.go b/repository/traversal_test.go index 61560fde..0e5f5ec4 100644 --- a/repository/traversal_test.go +++ b/repository/traversal_test.go @@ -1,6 +1,10 @@ package repository_test import ( + "fmt" + "os" + "path/filepath" + "strings" "testing" "codeberg.org/lindenii/furgit/internal/testgit" @@ -26,54 +30,116 @@ func TestRepositoryDepthFirstEnumerationFromHEAD(t *testing.T) { 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() }() + walkRepositoryFromHead(t, repoHarness.Dir()) + }) +} - head, err := repo.ResolveRefFully("HEAD") - if err != nil { - t.Fatalf("ResolveRefFully(HEAD): %v", err) - } +func TestRepositoryDepthFirstEnumerationCurrentWorktree(t *testing.T) { + t.Parallel() - visited := make(map[objectid.ObjectID]bool) - queue := []objectid.ObjectID{head.ID} - objectsRead := 0 + worktreeRoot := filepath.Clean("..") + gitPath := filepath.Join(worktreeRoot, ".git") - for len(queue) > 0 { - id := queue[0] - queue = queue[1:] + info, err := os.Stat(gitPath) + if err != nil { + t.Fatalf("stat %q: %v", gitPath, err) + } - if visited[id] { - continue - } - visited[id] = true + if info.IsDir() { + walkRepositoryFromHead(t, gitPath) + return + } - 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 !info.Mode().IsRegular() { + t.Fatalf("%q is neither a directory nor a regular file", gitPath) + } + + content, err := os.ReadFile(gitPath) + if err != nil { + t.Fatalf("read %q: %v", gitPath, err) + } + line := strings.TrimSpace(string(content)) + prefix := "gitdir: " + if !strings.HasPrefix(line, prefix) { + t.Fatalf("%q file does not begin with %q", gitPath, prefix) + } + + gitdirRel := strings.TrimSpace(line[len(prefix):]) + if gitdirRel == "" { + t.Fatalf("%q contains empty gitdir path", gitPath) + } + gitdirPath := gitdirRel + if !filepath.IsAbs(gitdirPath) { + gitdirPath = filepath.Join(worktreeRoot, gitdirPath) + } + commondirPath := filepath.Join(gitdirPath, "commondir") + commondirContent, err := os.ReadFile(commondirPath) + if err != nil { + t.Fatalf("read %q: %v", commondirPath, err) + } + repoPath := strings.TrimSpace(string(commondirContent)) + if repoPath == "" { + t.Fatalf("%q contains empty repo path", commondirPath) + } + if filepath.IsAbs(repoPath) { + walkRepositoryFromHead(t, repoPath) + return + } + repoPath = filepath.Join(gitdirPath, repoPath) + + walkRepositoryFromHead(t, repoPath) +} + +func walkRepositoryFromHead(t *testing.T, repoPath string) { + t.Helper() + + repo, err := repository.Open(repoPath) + if err != nil { + t.Fatalf("repository.Open(%q): %v", repoPath, 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 - if objectsRead == 0 { - t.Fatalf("no objects were enumerated from HEAD") + 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 (%s)", fmt.Sprintf("%q", repoPath)) + } } -- cgit v1.3.1-10-gc9f91