Skip to main content

Overview

Codaph automatically detects and supports git worktrees, allowing you to capture agent sessions across multiple branches simultaneously.
When you run codaph push from a worktree, Codaph scans all worktrees in the repository and imports agent history from each.

What are Git Worktrees?

Git worktrees let you check out multiple branches of the same repository simultaneously:
# Main repository
cd ~/projects/myapp

# Create worktree for feature branch
git worktree add ../myapp-feature feature/new-ui

# Create worktree for hotfix
git worktree add ../myapp-hotfix hotfix/critical-bug
Now you can work on three branches at once without switching:
  • ~/projects/myapp (main)
  • ~/projects/myapp-feature (feature/new-ui)
  • ~/projects/myapp-hotfix (hotfix/critical-bug)

How Codaph Handles Worktrees

Automatic Detection

When you initialize Codaph or run codaph push, it:
  1. Detects the git repository root via git rev-parse --show-toplevel
  2. Lists all worktrees using git worktree list --porcelain
  3. Scopes the project path across all worktrees
  4. Imports agent history from each worktree’s agent directories
Source: src/lib/git-worktrees.ts:72

Scoped Project Paths

The resolveScopedProjectPathsForWorktrees() function resolves your project path to all matching worktrees:
// Example: if you run from ~/myapp/.codex/
const paths = resolveScopedProjectPathsForWorktrees("~/myapp");
// Returns:
[
  "/Users/you/myapp",
  "/Users/you/myapp-feature",
  "/Users/you/myapp-hotfix"
]
Source: src/lib/git-worktrees.ts:72

Parsing Worktree List

Codaph parses git worktree list --porcelain output:
export function parseGitWorktreeListPorcelain(raw: string): string[] {
  const out: string[] = [];
  const lines = raw.split("\n");
  for (const line of lines) {
    if (!line.startsWith("worktree ")) {
      continue;
    }
    const pathValue = line.slice("worktree ".length).trim();
    if (pathValue.length === 0) {
      continue;
    }
    out.push(resolve(pathValue));
  }
  return dedupePreserveOrder(out);
}
Source: src/lib/git-worktrees.ts:34 This extracts absolute paths to all worktrees and deduplicates them.

Relative Suffix Projection

Codaph projects the relative path from repo root to your current location across all worktrees:
export function scopeProjectPathAcrossWorktrees(
  worktreeRoots: string[],
  repoRoot: string,
  projectPath: string,
): string[] {
  const normalizedRepoRoot = resolve(repoRoot);
  const normalizedProjectPath = resolve(projectPath);
  const relativeSuffix = relative(normalizedRepoRoot, normalizedProjectPath);
  
  // If outside repo, return original path only
  if (relativeSuffix.startsWith("..") || isAbsolute(relativeSuffix)) {
    return [normalizedProjectPath];
  }

  // Map relative suffix to each worktree
  const scoped = worktreeRoots.map((worktreeRoot) =>
    relativeSuffix.length === 0 ? resolve(worktreeRoot) : resolve(worktreeRoot, relativeSuffix)
  );
  
  const normalizedScoped = dedupePreserveOrder(scoped);
  if (!normalizedScoped.includes(normalizedProjectPath)) {
    normalizedScoped.unshift(normalizedProjectPath);
  }
  return normalizedScoped;
}
Source: src/lib/git-worktrees.ts:50

Usage Examples

Enable Worktree Scanning (Default)

codaph push
Worktree scanning is enabled by default. Codaph imports agent history from:
  • Current worktree
  • All other worktrees in the same repository

Disable Worktree Scanning

codaph push --no-worktrees
Only the current directory is scanned (no worktree detection).

Example Workflow

# Setup repository with worktrees
cd ~/myapp
git worktree add ../myapp-feat1 feature/auth
git worktree add ../myapp-feat2 feature/payments

# Work in first worktree
cd ~/myapp-feat1
codex exec "add OAuth2 login"

# Work in second worktree
cd ~/myapp-feat2
codex exec "integrate Stripe payments"

# Push from main worktree imports BOTH sessions
cd ~/myapp
codaph push
Output:
Codex: scan 2/2 | match 2 | events 48 | sessions 2
Both Codex sessions (from myapp-feat1 and myapp-feat2) are imported.

How Import Works

When you run codaph push, Codaph:
  1. Resolves worktree paths
  2. Scans each worktree for agent marker directories:
    • .codex/ (Codex)
    • .claude/ (Claude Code)
    • ~/.config/gemini/ (Gemini CLI, not worktree-specific)
  3. Imports agent history from each location
  4. Deduplicates events by eventId in the local mirror
Source: src/index.ts:1376 (runSyncPushPhase)

Agent History Paths

For each worktree, Codaph checks:
~/myapp/.codex/history/
~/myapp-feat1/.codex/history/
~/myapp-feat2/.codex/history/

Deduplication

Codaph automatically deduplicates events across worktrees using eventId hashing:
export function createEventId(input: {
  source: AgentSource;
  threadId: string | null;
  sequence: number;
  eventType: string;
  ts: string;
}): string {
  const raw = [
    input.source,
    input.threadId ?? "no-thread",
    String(input.sequence),
    input.eventType,
    input.ts,
  ].join("|");
  return createHash("sha256").update(raw).digest("hex").slice(0, 24);
}
Source: src/lib/core-types.ts:113 If the same event appears in multiple worktrees (e.g., shared .codex/ directory), it’s imported only once.

Configuration

Disable Worktrees Globally

Add to your project settings:
.codaph/project.json
{
  "schema": "codaph.project.v2",
  "useWorktrees": false
}
Or via CLI:
codaph push --no-worktrees

Per-Command Override

# Enable for single command
codaph push --worktrees

# Disable for single command  
codaph push --no-worktrees

Limitations

Gemini CLI history is global (stored in ~/.config/gemini/), not per-worktree. All worktrees share the same Gemini history.
  • Codex and Claude Code are worktree-aware (history stored in .codex/ and .claude/ respectively)
  • Gemini CLI is not worktree-aware (global history)

Troubleshooting

”No worktrees detected”

Run git worktree list to verify worktrees exist:
git worktree list
If empty, you’re not using worktrees (this is fine — Codaph works normally).

Events imported multiple times

If worktrees share the same .codaph/ directory (symlink or bind mount), disable worktrees:
codaph push --no-worktrees

Missing sessions from other worktrees

Verify agent marker directories exist in each worktree:
ls -la ~/myapp-feat1/.codex/
ls -la ~/myapp-feat2/.codex/
If missing, the agent hasn’t run in that worktree yet.

Testing

Tests: test/lib-git-worktrees.test.ts Codaph includes unit tests for worktree parsing and scoping:
  • parseGitWorktreeListPorcelain — parsing git worktree list output
  • scopeProjectPathAcrossWorktrees — relative path projection
  • resolveScopedProjectPathsForWorktrees — end-to-end resolution

Build docs developers (and LLMs) love