How witr Detects Git Repositories and Branches from a Working Directory
witr detects Git repositories and branches by traversing up the directory tree from a process's working directory, parsing .git entries and HEAD files to extract repository names and current branches.
witr is an open-source process monitoring tool that enriches process listings with contextual Git metadata. Understanding how witr detects Git repositories and branches from a working directory reveals a lightweight filesystem traversal approach that handles standard repositories, worktrees, and submodules without invoking the git binary.
The Detection Algorithm in internal/proc/git.go
The core detection logic resides in internal/proc/git.go, specifically within the detectGitInfo function. This implementation performs an iterative upward search from the supplied working directory to locate Git metadata.
Early Exit and Directory Traversal
The function first validates the input path. If the supplied working directory (cwd) is empty or equals the placeholder string "unknown", the function returns empty strings for both the repository name and branch immediately.
For valid paths, witr implements a bounded upward search:
- Starting at
cwd, the code climbs up to 10 parent directories (for depth := 0; depth < 10; depth++) - At each level, it checks for the existence of a
.gitentry using filesystem operations - If found, it proceeds to extract metadata; if the loop completes without finding
.git, it returns empty strings
Resolving Git Directories for Worktrees and Submodules
When witr encounters a .git entry, it must determine whether this represents a standard repository root or a pointer to an external Git directory (used by worktrees and submodules):
- If
.gitis a directory: The current search directory is the repository root - If
.gitis a file: The file contains agitdir:pointer that must be resolved
The helper function gitDirFromFile(gitFile, baseDir string) string handles the second case. It reads the .git file, extracts the path following the gitdir: prefix, and converts relative paths to absolute paths by joining them with baseDir.
Extracting Repository and Branch Names
Once a valid Git directory is identified, witr extracts two pieces of metadata:
Repository name: Derived from filepath.Base(searchDir), returning the directory name that contained the .git entry.
Branch name: Obtained via gitBranchFromHEAD(gitDir string), which reads the <gitDir>/HEAD file. If the file content starts with ref: refs/heads/, the function returns the suffix as the branch name. For detached HEAD states or unreadable files, it returns an empty string.
Platform Integration via Process Collectors
The Git detection integrates with platform-specific process collection through the ReadProcess functions implemented in files like internal/proc/process_linux.go (with equivalents for macOS and Windows).
ReadProcess obtains a process's working directory via platform-native mechanisms—such as reading /proc/<pid>/cwd on Linux—and passes this path to detectGitInfo(cwd). The resulting strings populate the GitRepo and GitBranch fields of the model.Process struct defined in pkg/model/process.go, making the metadata available for display.
Code Implementation Examples
The following Go code demonstrates the helper functions that enable Git metadata extraction:
// Resolves a .git file pointer (used by worktrees/submodules)
func gitDirFromFile(gitFile, baseDir string) string {
data, err := os.ReadFile(gitFile)
if err != nil { return "" }
for _, line := range strings.Split(string(data), "\n") {
if rest, ok := strings.CutPrefix(strings.TrimSpace(line), "gitdir:"); ok {
dir := strings.TrimSpace(rest)
if !filepath.IsAbs(dir) {
dir = filepath.Join(baseDir, dir)
}
return dir
}
}
return ""
}
// Extracts branch name from the Git HEAD file
func gitBranchFromHEAD(gitDir string) string {
head, err := os.ReadFile(filepath.Join(gitDir, "HEAD"))
if err != nil { return "" }
if ref, ok := strings.CutPrefix(strings.TrimSpace(string(head)), "ref: "); ok {
return strings.TrimPrefix(ref, "refs/heads/")
}
return "" // detached HEAD
}
// Usage example within process detection
cwd := "/home/alice/projects/witr/submodule"
repo, branch := detectGitInfo(cwd)
// repo => "submodule"
// branch => "feature/awesome"
Summary
- Bounded traversal: witr searches up to 10 parent directories from the working directory to locate
.gitentries, preventing infinite loops in deeply nested filesystems. - Worktree support: The
gitDirFromFilehelper resolvesgitdir:pointers in.gitfiles, enabling detection for Git worktrees and submodules. - Direct file parsing: Branch detection reads the
HEADfile directly rather than executinggitcommands, minimizing overhead. - Platform abstraction: Git detection integrates with OS-specific process collectors via
ReadProcess, supporting Linux, macOS, and Windows through/procfilesystem or equivalent APIs. - Graceful degradation: When running outside a Git repository or in a detached HEAD state, witr returns empty strings rather than failing.
Frequently Asked Questions
How does witr handle Git worktrees?
witr handles Git worktrees by detecting when .git is a file rather than a directory. According to the source code in internal/proc/git.go, the gitDirFromFile function parses the gitdir: path inside this file to locate the actual Git directory, enabling accurate branch detection even when the working directory is a linked worktree.
What happens if a process runs outside a Git repository?
If no .git entry is found within 10 parent directories of the process's working directory, detectGitInfo returns empty strings for both the repository name and branch. This is stored in the model.Process struct, resulting in blank Git metadata for that process in the witr output.
Which file contains the core Git detection logic?
The core Git detection logic is implemented in internal/proc/git.go. This file contains the detectGitInfo function and its helpers gitDirFromFile and gitBranchFromHEAD, which together handle repository discovery, worktree resolution, and branch extraction.
How does witr determine the current branch name?
witr determines the branch name by reading the HEAD file within the Git directory via gitBranchFromHEAD. If the file contains a reference like ref: refs/heads/main, the function extracts and returns main. For detached HEAD states or if the file cannot be read, it returns an empty string.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →