FindGitRootFrom finds the root directory of the git repository starting from the given directory. It traverses upward until it finds a .git entry (file or directory) or reaches the filesystem root. Returns an error if not in a git repository.
(startDir string)
| 106 | // directory) or reaches the filesystem root. |
| 107 | // Returns an error if not in a git repository. |
| 108 | func FindGitRootFrom(startDir string) (string, error) { |
| 109 | dir, err := filepath.Abs(startDir) |
| 110 | if err != nil { |
| 111 | return "", fmt.Errorf("failed to resolve absolute path for %q: %w", startDir, err) |
| 112 | } |
| 113 | dir = filepath.Clean(dir) |
| 114 | for { |
| 115 | gitPath := filepath.Join(dir, ".git") |
| 116 | info, err := os.Stat(gitPath) |
| 117 | if err == nil { |
| 118 | // .git exists — accept if it's a directory (normal repo) or a |
| 119 | // regular file (worktree / git-submodule pointer). |
| 120 | if info.IsDir() { |
| 121 | return dir, nil |
| 122 | } |
| 123 | // Worktree marker: must be a regular file beginning with "gitdir:" |
| 124 | if info.Mode().IsRegular() { |
| 125 | data, readErr := os.ReadFile(gitPath) |
| 126 | if readErr != nil { |
| 127 | return "", fmt.Errorf("failed to read .git file at %q: %w", gitPath, readErr) |
| 128 | } |
| 129 | if strings.HasPrefix(strings.TrimSpace(string(data)), "gitdir:") { |
| 130 | return dir, nil |
| 131 | } |
| 132 | } |
| 133 | } else if !errors.Is(err, os.ErrNotExist) { |
| 134 | // Unexpected error (e.g. permission denied) — surface it. |
| 135 | return "", fmt.Errorf("failed to stat %q: %w", gitPath, err) |
| 136 | } |
| 137 | parent := filepath.Dir(dir) |
| 138 | if parent == dir { |
| 139 | return "", ErrNotGitRepository |
| 140 | } |
| 141 | dir = parent |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | // ReadFileFromHEAD reads a file from git HEAD using a pre-computed repository root. |
| 146 | // filePath is resolved with filepath.Abs, so relative paths are interpreted from the |