Skip to content

entire and lefthook fight over the pre-push hook; session push gets silently disabledΒ #1349

@jaxondk

Description

@jaxondk

What happened?

In a repo that also uses lefthook to manage git hooks, entire and lefthook fight over .git/hooks/pre-push. After any change to lefthook.yml, lefthook silently reclaims the hook and entire's session-log-on-push stops working β€” with no error β€” until someone manually re-wraps it.

How the conflict arises

  1. lefthook installs and manages pre-push. Its installed hooks self-sync on every run, gated by a checksum of lefthook.yml stored in .git/info/lefthook.checksum.
  2. entire enable / entire configure --force wraps the existing hook: it renames the current pre-push to pre-push.pre-entire and installs its own wrapper that runs the entire handler then chains down:
    # Entire CLI hooks
    if command -v entire >/dev/null 2>&1; then entire hooks git pre-push "$1" || true; else :; fi
    # Chain: run pre-existing hook
    "$(dirname "$0")/pre-push.pre-entire" "$@"
    At this point both run on push β€” entire pushes session logs, then chains to lefthook. πŸ‘
  3. But whenever lefthook.yml changes, lefthook's checksum goes stale. Its next self-sync (at the top of any lefthook-managed hook, e.g. the next commit's pre-commit) sees that pre-push is no longer its hook, backs entire's wrapper up to pre-push.old, and reinstalls its own bare hook. entire's session push is now silently gone.
  4. On the next lefthook.yml change after that, lefthook also emits a recurring warning, because its earlier backup collides:
    sync hooks: ❌
    could not replace the hook: can't rename pre-push to pre-push.old - file already exists
    

Why there is no clean user-side fix (verified on 0.7.3)

The natural fix β€” make lefthook the sole owner of pre-push and run entire's push as a lefthook job/script β€” is blocked:

  • entire configure --force re-installs entire's pre-push wrapper even with push_sessions: false β†’ you cannot stop entire owning the hook.
  • entire hooks git pre-push <remote> is itself gated by push_sessions (no-ops when off) β†’ you cannot disable the wrapper and still call the handler from a lefthook script.
  • entire configure --force re-wraps whatever is at pre-push with no detection β€” if lefthook reclaimed it, entire silently wraps lefthook's bare hook again, with no idempotency check or "another manager owns this" warning.

The only local mitigation is a self-heal step that re-wraps after each re-sync, which still can't cover a push that happens with no intervening commit.

Proposed fixes (any one resolves it)

  1. Idempotent / manager-aware install β€” if pre-push already chains to pre-push.pre-entire (or contains entire's wrapper), don't blindly re-wrap; detect known managers (lefthook, husky, pre-commit, core.hooksPath).
  2. Decouple handler from install β€” an option to install the handler behavior without owning the pre-push file, so managers like lefthook can invoke entire hooks git pre-push as a job/script. (Today --skip-push-sessions disables the handler too, defeating this.)
  3. Register via the host manager β€” when lefthook is detected, register entire's push as a lefthook script (lefthook scripts receive the hook's $@, including the remote) instead of taking the file.
  4. Self-repair marker β€” let entire re-assert/repair its wrapper automatically so a reclaim heals without manual entire configure --force.

Steps to reproduce

  1. A repo using lefthook with a pre-push: section; run lefthook install.
  2. entire enable (wraps pre-push, chaining to lefthook).
  3. Edit lefthook.yml (any change) and make a commit or push.
  4. lefthook's self-sync reclaims pre-push; entire's session push silently stops (no error). Confirm with head .git/hooks/pre-push β€” the entire wrapper is gone.

Entire CLI version

Entire CLI 0.7.3

OS and architecture

macOS 26.5, arm64 (Darwin 25.5.0)

Agent

claude code

Terminal

ghostty

Logs / debug output

Additional context

  • lefthook 2.0.12; .entire/settings.json committed (team-wide setup), so this affects every macOS dev on the team using entire.
  • The 0.7.3 pre-push wrapper added a command -v entire guard (no-ops cleanly when entire is off-PATH) β€” good, but it does not address this ownership conflict.
  • Also reproduces across git worktrees, which share a single .git/hooks dir.
  • No log output to attach: the failure is silent (entire's hook is simply removed; nothing is logged).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions