Posted on

This is a bit of a silly post: it's relatively trivial, but someone asked me if this was possible the other day and I realized that it's perhaps not so trivial if you're unfamiliar with the various bits of necessary tooling.

You can have uv sync (or npm install. etc.) run automatically every time you change branches by adding an executable1 .git/hooks/post-checkout file to the repository in question with the following contents:

#!/usr/bin/env bash

# capture changed files between two refs ($1 and $2)
changed_files="$(git diff-tree -r --name-only --no-commit-id $1 $2)"

# Exit early if no files changed
[ -z "$changed_files" ] && exit 0

# Note that $0, $1, and $2 in the body of the function are not the same as the above,
# due to argument scoping rules for bash functions:
check_run() {
    echo "$changed_files" | grep --quiet "$1" && echo "Running $2..." && eval "$2" || true
}

check_run uv.lock "uv sync"

This will execute when git-checkout or git-switch is run, after having updated the worktree.

The git docs provide additional details, but essentially the hook is given three parameters: the previous ref, the new ref, and a flag that indicates whether the checkout was a branch checkout or a file checkout.

The script2 runs a git diff-tree filter to extract out the names of all files that have a delta between the two refs. These filenames are then fed to a simple check() function that compares that list against a target file (in our case, uv.lock). If the target filename is found within the list of changed filenames, then a command is eval'ed: in this case, uv sync.

You can also adjust the code to watch for any other kind of file, such as a package-lock.json, and then run the relevant npm commands as necessary.


1

Don't forget to chmod +x the file after you've created it, otherwise git will not execute the script.

2

You can and should read up on bash scripting! It's a bit weird, but shell scripts glue the world together.

2

There are some scenarios where you may be in the middle of merging/rebasing/etc. with a target lockfile that is in conflict, so this isn't a panacea, but it should be handy enough in the most common branch switching cases to merit its implementation.