Skip to main content

It is often very handy to be working on multiple things in a single repository, and while magit makes unpicking local changes into coherent commits easy, testing those commits is kind of annoying. My primary use case for this was for testing changes in an Ansible tree - often I have a lot of things in flight, but it's pretty important to ensure that a commit is coherent. Specifically: the change refers to a new file and the new file itself needs to end up in the same commit. Just running tests or ansible-playbook --check --diff doesn't help in that case, because all the files are in the tree, but what you care about is whether they'll be committed together.

So I wrote a little script, the gist of which is this:

#!/usr/bin/env bash
set -euo pipefail

main_repo=$(git rev-parse --show-toplevel)
rel_dir=$(git rev-parse --show-prefix)
cache="$HOME/.cache/run-at-worktrees"

mode_index=0
commit=HEAD
while [[ $# -gt 0 ]]; do
  case "$1" in
    --index)   mode_index=1; shift ;;
    --)        shift; break ;;
    -*)        echo "run-at: unknown flag: $1" >&2; exit 2 ;;
    *)         commit="$1"; shift ;;
  esac
done

if [[ $# -eq 0 ]]; then
  echo "run-at: missing command" >&2
  exit 2
fi

if (( mode_index )); then
  tree=$(git -C "$main_repo" write-tree)
  sha=$(
    GIT_AUTHOR_DATE='@0 +0000' GIT_COMMITTER_DATE='@0 +0000' \
    GIT_AUTHOR_NAME=run-at GIT_AUTHOR_EMAIL=at@localhost \
    GIT_COMMITTER_NAME=run-at GIT_COMMITTER_EMAIL=at@localhost \
    git -C "$main_repo" commit-tree "$tree" -p HEAD \
      -m 'run-at index snapshot'
  )
  label="index snapshot on top of $(git -C "$main_repo" rev-parse --short HEAD)"
else
  sha=$(git -C "$main_repo" rev-parse --verify "$commit^{commit}")
  label=$(git -C "$main_repo" log -1 --format='%h %s' "$sha")
fi

wt="$cache/${sha:0:12}"
mkdir -p "$cache"

if [[ ! -d "$wt" ]]; then
  git -C "$main_repo" worktree add --detach "$wt" "$sha" >/dev/null
fi

echo "→ run-at: $wt" >&2
echo "  rev:  $sha" >&2
echo "  desc: $label" >&2

target_dir="$wt"
if [[ -n "$rel_dir" ]]; then
  target_dir="$wt/${rel_dir%/}"
fi
if [[ ! -d "$target_dir" ]]; then
  echo "run-at: target directory does not exist in selected revision: ${rel_dir:-.}" >&2
  exit 1
fi

cd "$target_dir"

exec "$@"

(the real version does some Ansible-specific stuff like symlinking the venv).

It's a lot of lines to:

Setting the commit metadata - GIT_AUTHOR_* and GIT_COMMITTER_* - to static values means the temporary commit's hash depends only on the current commit + contents of the index, so we will reuse any previous worktree that shared the same content (and HEAD). commit-tree ignores any configured gpg/ssh signing requirement, so it doesn't require any creds and the commit id doesn't depend on keys or timestamps.

This is one of those things that in the past I might have considered too slow to do, but on a 5-year-old Mac, running it on the Linux kernel:

 time run-at -- pwd
Preparing worktree (detached HEAD 9ecfb2f7287a)
Updating files: 100% (94643/94643), done.
→ run-at: /Users/rob/.cache/run-at-worktrees/9ecfb2f7287a
  rev:  9ecfb2f7287a967b418ba69f10d45ead0d360593
  desc: 9ecfb2f7287a Merge tag 'trace-ring-buffer-v7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace
/Users/rob/.cache/run-at-worktrees/9ecfb2f7287a
user=3.21s system=6.72s cpu=94% total=10.523

Since the worktree is named after the commit it's pointing at, subsequent runs are ~instant:

 time run-at -- pwd
→ run-at: /Users/rob/.cache/run-at-worktrees/9ecfb2f7287a
  rev:  9ecfb2f7287a967b418ba69f10d45ead0d360593
  desc: 9ecfb2f7287a Merge tag 'trace-ring-buffer-v7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace
/Users/rob/.cache/run-at-worktrees/9ecfb2f7287a
user=0.02s system=0.03s cpu=76% total=0.066

My code bases are much, much smaller than linux.git, so that's plenty fast enough for me for running tests or uv run ansible-playbook --check --diff .... It is not fully hermetic, and any changes or junk left in the worktree persist between runs, but it's turned out to be very handy.