shithub: furgit

Download patch

ref: 6d72372960241f776e2c667b7debdc4b509e015e
parent: 6a7fc936c4a969aa05b3941feedafe59f4bd2ffd
author: Runxi Yu <me@runxiyu.org>
date: Sat Feb 21 11:37:33 EST 2026

repository: Add current repo traversal

--- 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 @@
 		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++
+	if !info.Mode().IsRegular() {
+		t.Fatalf("%q is neither a directory nor a regular file", gitPath)
+	}
 
-			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)
-			}
+	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))
+	}
 }
--