How witr Extracts and Displays Git Repository Context for Running Processes

witr identifies the Git repository and branch associated with a process by walking up from its working directory, detecting .git folders or gitdir files, parsing HEAD references, and rendering the results in formatted CLI output.

The witr command-line tool provides runtime visibility into running processes, including the Git context that owns them. By examining process working directories and their parent paths, witr resolves repository names and active branches without external Git binary calls. This article explains the complete detection pipeline as implemented in the pranshuparmar/witr repository.

Git Context Detection Algorithm

The core detection logic resides in internal/proc/git.go. The detectGitInfo function implements a directory traversal algorithm with a maximum depth of 10 levels to locate and parse Git metadata.

Finding the Git Directory

Starting from the process current working directory (cwd), the algorithm examines each parent level:

// Simplified logic from internal/proc/git.go
func detectGitInfo(cwd string) (repoName, branch string) {
    // Walk up directory tree (max 10 levels)
    for i := 0; i < 10 && dir != ""; i++ {
        gitPath := filepath.Join(dir, ".git")
        info, err := os.Stat(gitPath)
        if err == nil {
            if info.IsDir() {
                // Standard .git directory found
                gitDir = gitPath
            } else {
                // .git file (worktree/submodule) - parse gitdir: path
                gitDir = gitDirFromFile(gitPath)
            }
            break
        }
        dir = filepath.Dir(dir) // Move up one level
    }
    // ...
}

The gitDirFromFile helper handles worktrees and submodules where .git is a file rather than directory. It parses the gitdir: <path> directive to resolve the actual Git metadata location.

Resolving the Current Branch

Once the Git directory is located, gitBranchFromHEAD extracts branch information:

func gitBranchFromHEAD(gitDir string) string {
    headPath := filepath.Join(gitDir, "HEAD")
    data, err := os.ReadFile(headPath)
    if err != nil {
        return ""
    }
    content := strings.TrimSpace(string(data))
    
    // Parse "ref: refs/heads/main" format
    if strings.HasPrefix(content, "ref: refs/heads/") {
        return strings.TrimPrefix(content, "ref: refs/heads/")
    }
    // Detached HEAD or other state returns empty string
    return ""
}

The function returns the branch name only when HEAD references a named branch. Detached HEAD states and unreadable files result in empty branch strings, with only the repository name displayed.

Platform-Specific Process Integration

The Git detector is invoked uniformly across all supported platforms through platform-specific process reading implementations.

Linux Implementation

In internal/proc/process_linux.go (line 48), the detector receives the resolved working directory:

gitRepo, gitBranch := detectGitInfo(cwd)

The returned values populate the model.Process struct fields.

Windows Implementation

internal/proc/process_windows.go (lines 51-53) assigns the same values during process construction:

GitRepo:   gitRepo,
GitBranch: gitBranch,

This pattern repeats identically in process_darwin.go for macOS and process_freebsd.go for FreeBSD, ensuring cross-platform consistency in Git context extraction.

Storing Git Context in Process Models

The model.Process struct in pkg/model/process.go defines dedicated fields for Git information:

type Process struct {
    PID       int
    PPID      int
    // ... other fields ...
    GitRepo   string  // Base name of repository directory
    GitBranch string  // Current branch name (may be empty)
}

These fields enable downstream consumers to access Git context without re-executing detection logic.

Rendering Git Information in CLI Output

The standard.go output formatter in internal/output/standard.go conditionally renders Git repository and branch information:

if proc.GitRepo != "" {
    if proc.GitBranch != "" {
        out.Printf("%sGit Repo%s    : %s (%s)\n", 
            ColorCyan, ColorReset,
            proc.GitRepo, proc.GitBranch)
    } else {
        out.Printf("%sGit Repo%s    : %s\n", 
            ColorCyan, ColorReset,
            proc.GitRepo)
    }
}

The formatter applies terminal sanitization (SanitizeTerminal) to prevent control-character injection from malicious repository paths.

Practical Usage Examples

Command-Line Inspection

Display Git context for a specific process:

$ witr info 1234
...
Git Repo    : my-app (main)
...

The output shows repository name with branch in parentheses when available.

Programmatic Library Usage

Access Git context directly through the witr internal API:

package main

import (
    "fmt"
    "github.com/pranshuparmar/witr/internal/proc"
)

func main() {
    p, err := proc.ReadProcess(1234)
    if err != nil {
        panic(err)
    }
    fmt.Printf("Process %d runs in repo %s on branch %s\n",
        p.PID, p.GitRepo, p.GitBranch)
}

This pattern enables building custom tooling atop witr's process inspection capabilities.

Key Source Files

File Purpose
internal/proc/git.go Core detection: detectGitInfo, gitDirFromFile, gitBranchFromHEAD
internal/proc/process_linux.go Linux process reading with Git context
internal/proc/process_windows.go Windows process reading with Git context
internal/proc/process_darwin.go macOS process reading with Git context
internal/proc/process_freebsd.go FreeBSD process reading with Git context
pkg/model/process.go Process struct definition with GitRepo and GitBranch fields
internal/output/standard.go CLI formatter rendering Git repository and branch

Summary

  • witr extracts Git context by walking up to 10 parent directories from a process working directory, locating .git entries, and parsing HEAD references.

  • Dual detection modes handle standard repositories (.git directories) and worktrees/submodules (.git files with gitdir: pointers).

  • Cross-platform invocation occurs in process_linux.go, process_windows.go, and other OS-specific files, populating model.Process fields consistently.

  • Conditional output rendering in standard.go displays repository names with optional branch names, with safety sanitization applied.

Frequently Asked Questions

How does witr handle processes running outside any Git repository?

According to the detectGitInfo implementation, when no .git entry is found within 10 directory levels, the function returns empty strings for both repository and branch. The output formatter skips Git information entirely in this case, showing only standard process details.

Does witr require the Git binary to be installed?

No. The detection algorithm in internal/proc/git.go performs direct filesystem inspection of .git directories and HEAD files without shelling out to Git commands. This eliminates external dependencies and improves performance.

What happens when a process runs in a Git worktree or submodule?

The gitDirFromFile function specifically handles these cases. When .git is a file rather than directory, it parses the gitdir: <path> line to locate the actual Git metadata, then proceeds with normal HEAD resolution from that path.

Can witr detect Git context for processes with changed working directories?

witr reads the current working directory at the time of inspection. If a process has changed its working directory since startup and the original directory no longer exists in the process state, detection may fail or return incorrect context based on the newly resolved path.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →