Skip to content

Improve git worktree support: hook installer + storage path collision #48

@fazleelahhee

Description

@fazleelahhee

Context

CCE works per-directory, so each git worktree gets its own index under ~/.cce/projects/<basename>/. That covers the common case, but there are two known rough edges that make worktrees feel second-class.

1. Auto-reindex git hooks don't install in worktrees

install_hooks in src/context_engine/indexer/git_hooks.py:40-50 writes to <project_dir>/.git/hooks/:

hooks_dir = Path(project_dir) / ".git" / "hooks"
if not hooks_dir.exists():
    return []

In a git worktree, <project_dir>/.git is a file pointing at <main-repo>/.git/worktrees/<name>/, not a directory. The check fails silently and no hook is installed. Manual cce index still works, but post-commit/post-checkout/post-merge auto-reindex never fires from inside a worktree.

Fix: resolve the hooks dir via git itself instead of assuming layout:

result = subprocess.run(
    ["git", "rev-parse", "--git-path", "hooks"],
    cwd=project_dir, capture_output=True, text=True, check=True,
)
hooks_dir = Path(result.stdout.strip())
if not hooks_dir.is_absolute():
    hooks_dir = Path(project_dir) / hooks_dir

git rev-parse --git-path hooks returns the right directory in both regular checkouts and worktrees. Worktrees share the main repo's hooks by default, which is the right behavior — installing once from any worktree fires the hook for all of them.

2. Storage key is directory basename, not absolute path

Index storage resolves to Path(config.storage_path) / project_dir.name (see e.g. tests/indexer/test_pipeline_durability.py:38, cli.py callers). Two worktrees with the same basename — for example ~/work/myrepo and ~/scratch/myrepo — would clobber each other's index, embedding cache, and memory db.

The collision is unusual in practice because worktrees are typically given suffixed names (myrepo-feat-x, myrepo-bugfix-y), but the failure mode is silent and corrupts state when it does happen.

Fix: the codebase already solves this for editor configs in src/context_engine/editors.py:145-168 via a <basename>-<6hex> slug derived from Path.resolve(). Apply the same scheme to the storage key, with a one-time migration that moves an existing <basename>/ directory to <basename>-<hash>/ if and only if the resolved path matches.

Test plan

  • Hook installs successfully when run from a worktree, using the right shared hooks dir
  • Re-running cce install from any worktree is idempotent (no duplicate marker)
  • Indexing two worktrees of the same repo with the same basename keeps separate stores
  • Existing single-checkout users keep their current storage path (migration is no-op when basename is unique)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions