Git Utilities API Reference¶
Technical documentation for the git utilities module (amplihack-utils crate) providing git worktree detection and shared directory utilities.
Overview¶
The git utilities module provides utilities for detecting git worktrees and finding shared directories across worktree configurations. These functions enable Power Steering and other tools to work correctly in both standard repositories and worktree environments.
Installation¶
The module is included in amplihack core:
Functions¶
is_worktree¶
Detect if the current directory is a git worktree.
Parameters:
cwd(Optional[str]): Working directory to check. Defaults toos.getcwd().
Returns: bool - True if working in a worktree, False otherwise.
How It Works:
- Runs
git rev-parse --git-common-dirandgit rev-parse --git-dir - Compares resolved paths
- If paths differ, directory is a worktree
- If paths match, directory is standard repo or main repo
Example:
// Rust: use amplihack_utils::git_utils::{ is_worktree
# Check current directory
if is_worktree():
print("Working in a worktree")
else:
print("Working in standard repo")
# Check specific directory
if is_worktree("/path/to/worktree"):
print("Path is a worktree")
Error Handling:
# Non-git directory
is_worktree("/tmp/not-a-repo") # Returns False
# Permission denied
is_worktree("/root/restricted") # Returns False
get_common_git_dir¶
Get the common git directory shared across all worktrees.
Parameters:
cwd(Optional[str]): Working directory. Defaults toos.getcwd().
Returns: str - Absolute path to common git directory.
How It Works:
- Runs
git rev-parse --git-common-dir - Resolves to absolute path
- In standard repos: Returns
.git/ - In worktrees: Returns main repo's
.git/
Example:
// Rust: use amplihack_utils::git_utils::{ get_common_git_dir
# Standard repo
common_dir = get_common_git_dir()
# Returns: /path/to/repo/.git
# Worktree
common_dir = get_common_git_dir("/path/to/worktree")
# Returns: /path/to/main-repo/.git
Use Cases:
# Store shared state
import os
// Rust: use amplihack_utils::git_utils::{ get_common_git_dir
common_dir = get_common_git_dir()
state_dir = os.path.join(common_dir, ".claude/runtime/power-steering/")
os.makedirs(state_dir, exist_ok=True)
# Write counter
counter_file = os.path.join(state_dir, "counter.json")
with open(counter_file, "w") as f:
json.dump({"count": 5}, f)
Error Handling:
# Non-git directory
try:
common_dir = get_common_git_dir("/tmp/not-a-repo")
except subprocess.CalledProcessError:
print("Not a git repository")
find_disabled_file¶
Find .disabled file in multiple locations (worktree-aware).
Parameters:
cwd(Optional[str]): Working directory. Defaults toos.getcwd().
Returns: Optional[str] - Absolute path to .disabled file if found, None otherwise.
Search Order:
- Current working directory:
{cwd}/.disabled - Shared runtime directory:
{common_git_dir}/.claude/runtime/power-steering/.disabled - Project root:
{project_root}/.disabled
Example:
// Rust: use amplihack_utils::git_utils::{ find_disabled_file
# Check if Power Steering is disabled
disabled_file = find_disabled_file()
if disabled_file:
print(f"Power Steering disabled via: {disabled_file}")
else:
print("Power Steering enabled")
Creating .disabled Files:
# Local disable (current worktree only)
touch .disabled
# Global disable (all worktrees)
touch "$(git rev-parse --git-common-dir)/.claude/runtime/power-steering/.disabled"
# Root disable (backward compatible)
cd "$(git rev-parse --show-toplevel)"
touch .disabled
Use Cases:
# Conditional execution based on .disabled file
// Rust: use amplihack_utils::git_utils::{ find_disabled_file
def should_run_power_steering() -> bool:
"""Check if Power Steering should run."""
return find_disabled_file() is None
if should_run_power_steering():
# Run Power Steering
pass
else:
# Skip Power Steering
print("Power Steering disabled")
Shared State Pattern¶
Recommended pattern for storing shared state across worktrees:
import os
import json
from pathlib import Path
// Rust: use amplihack_utils::git_utils::{ get_common_git_dir
def get_state_directory() -> Path:
"""Get shared state directory for tool."""
common_dir = get_common_git_dir()
state_dir = Path(common_dir) / ".claude" / "runtime" / "my-tool"
state_dir.mkdir(parents=True, exist_ok=True)
return state_dir
def load_state() -> dict:
"""Load state from shared directory."""
state_file = get_state_directory() / "state.json"
if state_file.exists():
return json.loads(state_file.read_text())
return {}
def save_state(state: dict) -> None:
"""Save state to shared directory."""
state_file = get_state_directory() / "state.json"
state_file.write_text(json.dumps(state, indent=2))
Implementation Details¶
Worktree Detection Algorithm¶
def is_worktree(cwd: Optional[str] = None) -> bool:
"""
Algorithm:
1. Run: git rev-parse --git-common-dir
2. Run: git rev-parse --git-dir
3. Resolve both to absolute paths
4. Compare:
- Same path → Standard repo or main repo
- Different paths → Worktree
"""
common_dir = subprocess.check_output(
["git", "rev-parse", "--git-common-dir"],
cwd=cwd,
text=True
).strip()
git_dir = subprocess.check_output(
["git", "rev-parse", "--git-dir"],
cwd=cwd,
text=True
).strip()
return os.path.abspath(common_dir) != os.path.abspath(git_dir)
Directory Structure¶
Standard repository:
Worktree configuration:
main-repo/.git/ # Common git directory
├── config
├── objects/ # Shared objects
├── refs/ # Shared refs
├── worktrees/
│ └── feature-branch/
│ ├── gitdir # Points to worktree .git
│ └── HEAD # Worktree-specific HEAD
└── .claude/
└── runtime/
└── power-steering/
└── counter.json # Shared state
worktree-dir/.git # File containing: gitdir: /path/to/main-repo/.git/worktrees/feature-branch
Common Directory Resolution¶
# Standard repo
os.getcwd() # /path/to/repo
git rev-parse --git-dir # .git
git rev-parse --git-common-dir # .git
→ Common: /path/to/repo/.git
# Worktree
os.getcwd() # /path/to/worktree
git rev-parse --git-dir # /path/to/main/.git/worktrees/feature
git rev-parse --git-common-dir # /path/to/main/.git
→ Common: /path/to/main/.git
Error Handling¶
Non-Git Directory¶
// Rust: use amplihack_utils::git_utils::{ is_worktree, get_common_git_dir
# is_worktree returns False
is_worktree("/tmp/not-a-repo") # False
# get_common_git_dir raises exception
try:
get_common_git_dir("/tmp/not-a-repo")
except subprocess.CalledProcessError as e:
print(f"Not a git repository: {e}")
Permission Errors¶
# Handle permission errors
import os
// Rust: use amplihack_utils::git_utils::{ find_disabled_file
try:
disabled = find_disabled_file()
except PermissionError:
print("Permission denied checking .disabled file")
disabled = None
Git Command Failures¶
# Graceful degradation
// Rust: use amplihack_utils::git_utils::{ get_common_git_dir
try:
common_dir = get_common_git_dir()
except Exception:
# Fallback to current directory
common_dir = ".git"
Testing¶
Unit Tests¶
import unittest
from unittest.mock import patch, MagicMock
// Rust: use amplihack_utils::git_utils::{ is_worktree
class TestGitUtils(unittest.TestCase):
@patch('subprocess.check_output')
def test_is_worktree_standard_repo(self, mock_check_output):
"""Test standard repository detection."""
mock_check_output.return_value = b'/repo/.git\n'
self.assertFalse(is_worktree())
@patch('subprocess.check_output')
def test_is_worktree_detects_worktree(self, mock_check_output):
"""Test worktree detection."""
mock_check_output.side_effect = [
b'/main/.git\n', # --git-common-dir
b'/main/.git/worktrees/feature\n' # --git-dir
]
self.assertTrue(is_worktree())
Integration Tests¶
# Test worktree detection
git worktree add ../test-worktree feature
cd ../test-worktree
python3 << 'EOF'
// Rust: use amplihack_utils::git_utils::{ is_worktree, get_common_git_dir
assert is_worktree(), "Should detect worktree"
print(f"Common dir: {get_common_git_dir()}")
EOF
Performance¶
Benchmarks¶
| Function | Standard Repo | Worktree | Overhead |
|---|---|---|---|
is_worktree() |
~2ms | ~2ms | < 0.1% |
get_common_git_dir() |
~1ms | ~1ms | < 0.1% |
find_disabled_file() |
~3ms | ~5ms | 3 file checks |
Caching¶
For performance-critical code, cache results:
from functools import lru_cache
// Rust: use amplihack_utils::git_utils::{ get_common_git_dir
@lru_cache(maxsize=1)
def get_cached_common_dir() -> str:
"""Cache common git directory (doesn't change during execution)."""
return get_common_git_dir()
Migration Guide¶
Upgrading from Pre-Worktree Code¶
Before (broken in worktrees):
After (works in worktrees):
# Use git_utils
// Rust: use amplihack_utils::git_utils::{ get_common_git_dir
import os
common_dir = get_common_git_dir()
state_dir = os.path.join(common_dir, ".claude/runtime/power-steering/")
Updating .disabled Checks¶
Before (only checks CWD):
After (checks multiple locations):
# Checks CWD, shared dir, and project root
// Rust: use amplihack_utils::git_utils::{ find_disabled_file
if find_disabled_file():
return
Related Documentation¶
- Power Steering Worktree Support - User-facing guide
- Power Steering Configuration - Configuration options
- Git Worktree Documentation - Official git docs