* [PATCH v1 0/3] worktree: add post-worktree-add and post-worktree-remove hooks
@ 2026-07-09 23:36 Domen Kožar
2026-07-10 9:34 ` Phillip Wood
0 siblings, 1 reply; 3+ messages in thread
From: Domen Kožar @ 2026-07-09 23:36 UTC (permalink / raw)
To: git
Cc: Eric Sunshine, Patrick Steinhardt,
Ævar Arnfjörð Bjarmason, Caleb White,
Junio C Hamano, Domen Kožar
Hi everyone,
I maintain devenv, a developer environment manager, and lately the
workflow we see most is people letting AI coding agents loose on a
repository, one linked worktree per task, created and discarded at a
pace no human would type. Each of those worktrees expects a working
environment: processes, sockets, and stateful services such as a
database seeded from a dump.
Today there is no reliable trigger to set that up when a worktree
appears: post-checkout does not fire for --no-checkout or --orphan
and cannot be told apart from a plain checkout. Nothing at all fires
when a worktree goes away, so stale databases and services pile up
after "git worktree remove" or a manual rm followed by "git worktree
prune". Wrapping the worktree commands only helps when every tool,
human or agent, goes through the wrapper.
Patch 1 adds a post-worktree-add hook that fires after the working
tree is fully set up. Patch 2 adds post-worktree-remove for "git
worktree remove". Patch 3 extends it to "git worktree prune" so that
manually deleted worktrees are also observed.
Two design points I would especially appreciate feedback on:
* post-worktree-add runs after post-checkout and is skipped when
post-checkout fails. An argument could be made that it should run
whenever the worktree was created, regardless of the earlier
hook's exit status, since tooling registering worktrees would
otherwise miss one that does exist.
* for entries pruned because their gitdir file points to a location
that no longer exists, the hook receives the recorded path; when
the path cannot be determined at all (missing or corrupt gitdir
file) it receives an empty string.
Thanks,
Domen
Domen Kožar (3):
worktree: add post-worktree-add hook
worktree: add post-worktree-remove hook
worktree: run post-worktree-remove hook when pruning
Documentation/githooks.adoc | 41 +++++++++++++
builtin/worktree.c | 73 ++++++++++++++++++-----
t/t2400-worktree-add.sh | 113 ++++++++++++++++++++++++++++++++++++
t/t2401-worktree-prune.sh | 88 ++++++++++++++++++++++++++++
t/t2403-worktree-move.sh | 44 ++++++++++++++
worktree.c | 1 -
worktree.h | 6 +-
7 files changed, 347 insertions(+), 19 deletions(-)
base-commit: f85a7e662054a7b0d9070e432508831afa214b47
--
2.54.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH v1 0/3] worktree: add post-worktree-add and post-worktree-remove hooks
2026-07-09 23:36 [PATCH v1 0/3] worktree: add post-worktree-add and post-worktree-remove hooks Domen Kožar
@ 2026-07-10 9:34 ` Phillip Wood
[not found] ` <CAMvcdZS=ZYbLmjKaGJvjQ_fWYhVbOzwMvYq+MMENWPYi_RiqvQ@mail.gmail.com>
0 siblings, 1 reply; 3+ messages in thread
From: Phillip Wood @ 2026-07-10 9:34 UTC (permalink / raw)
To: domen, git
Cc: Eric Sunshine, Patrick Steinhardt,
Ævar Arnfjörð Bjarmason, Caleb White,
Junio C Hamano
Hi Domen
On 10/07/2026 00:36, Domen Kožar wrote:
>
> Today there is no reliable trigger to set that up when a worktree
> appears: post-checkout does not fire for --no-checkout or --orphan
> and cannot be told apart from a plain checkout. Nothing at all fires
> when a worktree goes away, so stale databases and services pile up
> after "git worktree remove" or a manual rm followed by "git worktree
> prune". Wrapping the worktree commands only helps when every tool,
> human or agent, goes through the wrapper.
I agree a hook that's run after the worktree is added is useful (I have
a patch for it that I've never got round to cleaning up and sending so
thank you for working on this). It is useful for copying across
untracked files to the new worktree like "config.mak".
> Patch 1 adds a post-worktree-add hook that fires after the working
> tree is fully set up. Patch 2 adds post-worktree-remove for "git
> worktree remove". Patch 3 extends it to "git worktree prune" so that
> manually deleted worktrees are also observed.
I don't have a strong opinion on a hook running when a worktree is
removed - an IDE that cares about that could set up a filesystem watch
on the directory but I guess adding a hook doesn't do any harm.
> Two design points I would especially appreciate feedback on:
>
> * post-worktree-add runs after post-checkout and is skipped when
> post-checkout fails. An argument could be made that it should run
> whenever the worktree was created, regardless of the earlier
> hook's exit status, since tooling registering worktrees would
> otherwise miss one that does exist.
Looking at the existing code, if the checkout fails then we remove the
worktree because "is_junk == 1" when remove_junk() is called via
atexit() so I think it is correct to skip the new hook in that case.
The new hook is run after the checkout, but before the post-checkout
hook - we should document their relative order. I see the hook is run in
the new worktree and passed the absolute directory and worktree id. I'm
wondering if either of those is useful if we're running the hook in the
new worktree.
> * for entries pruned because their gitdir file points to a location
> that no longer exists, the hook receives the recorded path; when
> the path cannot be determined at all (missing or corrupt gitdir
> file) it receives an empty string.
So the hook knows a worktree was removed but not which one?
Thanks
Phillip
> Thanks,
> Domen
>
> Domen Kožar (3):
> worktree: add post-worktree-add hook
> worktree: add post-worktree-remove hook
> worktree: run post-worktree-remove hook when pruning
>
> Documentation/githooks.adoc | 41 +++++++++++++
> builtin/worktree.c | 73 ++++++++++++++++++-----
> t/t2400-worktree-add.sh | 113 ++++++++++++++++++++++++++++++++++++
> t/t2401-worktree-prune.sh | 88 ++++++++++++++++++++++++++++
> t/t2403-worktree-move.sh | 44 ++++++++++++++
> worktree.c | 1 -
> worktree.h | 6 +-
> 7 files changed, 347 insertions(+), 19 deletions(-)
>
>
> base-commit: f85a7e662054a7b0d9070e432508831afa214b47
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH v1 0/3] worktree: add post-worktree-add and post-worktree-remove hooks
[not found] ` <CAMvcdZS=ZYbLmjKaGJvjQ_fWYhVbOzwMvYq+MMENWPYi_RiqvQ@mail.gmail.com>
@ 2026-07-13 13:19 ` Phillip Wood
0 siblings, 0 replies; 3+ messages in thread
From: Phillip Wood @ 2026-07-13 13:19 UTC (permalink / raw)
To: Domen Kožar, phillip.wood
Cc: git, Eric Sunshine, Patrick Steinhardt,
Ævar Arnfjörð Bjarmason, Caleb White,
Junio C Hamano
Hi Domen
Unfortunately it doesn't look like your message appeared on the list,
sadly I'm not sure it accepts multipart/alternative messages even when
they contain a plain-text version of the message.
On 10/07/2026 18:20, Domen Kožar wrote:
> Hi Phillip,
>
> thanks for the quick and careful read.
>
> > It is useful for copying across untracked files to the new worktree
> > like "config.mak".
>
> That is a nice example, and it needs the hook to also fire for
> --no-checkout and --orphan, which post-checkout does not cover.
>
> > Looking at the existing code, if the checkout fails then we remove
> > the worktree because "is_junk == 1" when remove_junk() is called via
> > atexit() so I think it is correct to skip the new hook in that case.
>
> Right, when the checkout itself fails the worktree is removed as junk
> and neither hook runs; no disagreement there. The case I was asking
> about is the post-checkout hook itself failing: that runs after
> is_junk is cleared, so the worktree survives, but post-worktree-add
> is currently skipped and tooling that registers worktrees would miss
> one that exists. I kept the skip because a failing post-checkout
> already signals a broken setup, but I am happy to run
> post-worktree-add whenever the worktree was created, regardless of
> the earlier hook's exit status, if that is preferred.
Oh sorry I'd misunderstood the question. I think I'd lean towards
running the hook anyway because we've still populated a new worktree,
even if the post-checkout hooks fails.
> > The new hook is run after the checkout, but before the post-checkout
> > hook - we should document their relative order.
>
> Unless I am misreading my own series, it is the other way around:
> add_worktree() invokes post-checkout first and post-worktree-add
> after it, t2400 has a test pinning that order ('"add" runs
> post-worktree-add after post-checkout'), and githooks.adoc says "It
> runs after the post-checkout hook, and is skipped if that hook
> fails." If that did not come across I am happy to reword the
> documentation.
Oops, when I wrote that I was looking at the wrong branch - I had my
"add-worktree-hook" checked out and confused it with this patch. It's
great to see that there is a test and documentation for this.
> > I'm wondering if either of those is useful if we're running the
> > hook in the new worktree.
>
> Strictly they are derivable from inside, --show-toplevel for the path
> and the basename of --git-dir for the id.
Isn't the worktree path the current working directory of the hook script?
> I passed them anyway so
> that one script can serve both hooks: post-worktree-remove has to
> receive them as arguments because the worktree is gone by the time it
> runs, and keeping the two signatures identical makes shared hook code
> simpler. I can drop them from post-worktree-add if the symmetry is
> not considered worth it.
Oh right, as a counter argument I wonder if having a different argument
count for the two hooks makes it easier for a script that's shared
between the two hooks to determine which hook has invoked it. Is the
worktree id useful for anything apart from accessing on worktree's local
refs from another worktree?
> > So the hook knows a worktree was removed but not which one?
>
> It always gets the worktree id as $2; only the path in $1 can be
> empty, and only for entries whose gitdir file is missing or
> unreadable, where git itself no longer knows the path either. Tooling
> that recorded the id at post-worktree-add time can still match the
> removal.
That answers why you want the id.
One thought I had after I wrote my mail was that worktrees can be
renamed - do we want a hook for that so that external tools can move any
services they've started and update their id -> path mapping.
Thanks
Phillip
> Thanks,
> Domen
>
> On Fri, Jul 10, 2026 at 3:34 AM Phillip Wood <phillip.wood123@gmail.com
> <mailto:phillip.wood123@gmail.com>> wrote:
>
> Hi Domen
>
> On 10/07/2026 00:36, Domen Kožar wrote:
> >
> > Today there is no reliable trigger to set that up when a worktree
> > appears: post-checkout does not fire for --no-checkout or --orphan
> > and cannot be told apart from a plain checkout. Nothing at all fires
> > when a worktree goes away, so stale databases and services pile up
> > after "git worktree remove" or a manual rm followed by "git worktree
> > prune". Wrapping the worktree commands only helps when every tool,
> > human or agent, goes through the wrapper.
>
> I agree a hook that's run after the worktree is added is useful (I have
> a patch for it that I've never got round to cleaning up and sending so
> thank you for working on this). It is useful for copying across
> untracked files to the new worktree like "config.mak".
>
> > Patch 1 adds a post-worktree-add hook that fires after the working
> > tree is fully set up. Patch 2 adds post-worktree-remove for "git
> > worktree remove". Patch 3 extends it to "git worktree prune" so that
> > manually deleted worktrees are also observed.
>
> I don't have a strong opinion on a hook running when a worktree is
> removed - an IDE that cares about that could set up a filesystem watch
> on the directory but I guess adding a hook doesn't do any harm.
> > Two design points I would especially appreciate feedback on:
> >
> > * post-worktree-add runs after post-checkout and is skipped when
> > post-checkout fails. An argument could be made that it should run
> > whenever the worktree was created, regardless of the earlier
> > hook's exit status, since tooling registering worktrees would
> > otherwise miss one that does exist.
>
> Looking at the existing code, if the checkout fails then we remove the
> worktree because "is_junk == 1" when remove_junk() is called via
> atexit() so I think it is correct to skip the new hook in that case.
>
> The new hook is run after the checkout, but before the post-checkout
> hook - we should document their relative order. I see the hook is
> run in
> the new worktree and passed the absolute directory and worktree id. I'm
> wondering if either of those is useful if we're running the hook in the
> new worktree.
>
> > * for entries pruned because their gitdir file points to a location
> > that no longer exists, the hook receives the recorded path; when
> > the path cannot be determined at all (missing or corrupt gitdir
> > file) it receives an empty string.
>
> So the hook knows a worktree was removed but not which one?
>
> Thanks
>
> Phillip
>
> > Thanks,
> > Domen
> >
> > Domen Kožar (3):
> > worktree: add post-worktree-add hook
> > worktree: add post-worktree-remove hook
> > worktree: run post-worktree-remove hook when pruning
> >
> > Documentation/githooks.adoc | 41 +++++++++++++
> > builtin/worktree.c | 73 ++++++++++++++++++-----
> > t/t2400-worktree-add.sh | 113 +++++++++++++++++++++++++++++
> +++++++
> > t/t2401-worktree-prune.sh | 88 ++++++++++++++++++++++++++++
> > t/t2403-worktree-move.sh | 44 ++++++++++++++
> > worktree.c | 1 -
> > worktree.h | 6 +-
> > 7 files changed, 347 insertions(+), 19 deletions(-)
> >
> >
> > base-commit: f85a7e662054a7b0d9070e432508831afa214b47
>
>
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-07-13 13:19 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-09 23:36 [PATCH v1 0/3] worktree: add post-worktree-add and post-worktree-remove hooks Domen Kožar
2026-07-10 9:34 ` Phillip Wood
[not found] ` <CAMvcdZS=ZYbLmjKaGJvjQ_fWYhVbOzwMvYq+MMENWPYi_RiqvQ@mail.gmail.com>
2026-07-13 13:19 ` Phillip Wood
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox