This document describes how an end user can set up a normal Git repository to automatically push and pull metadata whenever they push and pull code, using only commands the reference CLI already provides (git meta push, git meta pull, git meta show, git meta log) and Git's existing hook and alias mechanisms.
It is intentionally scoped to what works today with stock Git and the published reference CLI. Higher-level integrations that hide these moves entirely are covered in the Workflow document.
What we are wiring up
Two operations need to happen alongside normal Git for metadata to feel "automatic":
- on push: after (or before) the code push, run
git meta pushso the local metadata commits travel to the remote - on pull / fetch: after new remote refs land, run
git meta pullso the local metadata database materializes the new metadata commits
Git provides hooks for some of these moments and not others. The recommended setup uses the hooks Git does provide and falls back to a small set of aliases for the cases it does not.
Push side: pre-push
Git's pre-push hook runs on the client immediately before refs are sent. It receives the remote name and URL as arguments and the list of local-to-remote ref pairs on stdin. This is the natural place to push metadata refs alongside the code push.
Save the following as .git/hooks/pre-push and make it executable (chmod +x):
#!/usr/bin/env sh
remote_name="$1"
# Forward metadata to the same remote the user is pushing to.
# The metadata remote should already be configured via `git meta remote add`.
git meta push "$remote_name" || {
echo "git meta push to '$remote_name' failed; aborting git push" >&2
exit 1
}
Behavior notes:
pre-pushruns once pergit pushinvocation regardless of how many refs are being pushed, which matchesgit meta push's once-per-invocation semantics.- Returning a non-zero exit code from
pre-pushaborts the entiregit push. If you would rather the code push always succeed and metadata sync be best-effort, replace theexit 1with:(no-op) so failures are reported but do not block. - If
git metais not aware of the remote name passed to the hook, it falls back to the first configured meta remote.
There is no post-push hook in core Git. pre-push is the only client-side hook in the push lifecycle.
Pull side: post-merge
Git's post-merge hook runs after a successful merge, including the merge phase of git pull. This is the simplest way to materialize newly fetched metadata into the local database.
Save the following as .git/hooks/post-merge and make it executable:
#!/usr/bin/env sh
git meta pull || true
The trailing || true keeps a metadata sync failure from interrupting the rest of the merge workflow. Drop it if you want metadata problems to surface loudly.
Behavior notes:
post-mergeonly fires whengit pullactually merges. If the pull is a no-op (already up to date), the hook does not run, and there is nothing new to materialize anyway.post-mergedoes not fire forgit pull --rebaseorgit rebase. If you rebase by default, see thereference-transactionrecipe below or use the alias approach.
Fetch side: reference-transaction
Core Git does not provide a pre-fetch or post-fetch hook. The closest mechanism is reference-transaction, available since Git 2.28, which fires for every ref-update transaction including the one written by git fetch.
Save the following as .git/hooks/reference-transaction and make it executable:
#!/usr/bin/env sh
# This hook runs three times per transaction (`prepared`, `committed`, `aborted`).
# Only act once, after the transaction commits.
[ "$1" = "committed" ] || exit 0
# Read the ref updates from stdin and only react when remote-tracking refs
# changed (which is what `git fetch` writes). Skip otherwise to avoid
# materializing on every local commit, branch, or tag write.
if grep -q " refs/remotes/" ; then
git meta pull >/dev/null 2>&1 &
fi
Behavior notes:
- This hook is on the hot path for every ref update Git makes (including
git commitwritingHEAD). Therefs/remotes/filter scopes the work to fetches. - The
&runsgit meta pullin the background so it does not block the calling Git command. If you want synchronous behavior, drop the&. - This hook makes
post-mergeredundant for users who only fetch via the same Git invocation. Pick one or the other unless you have a reason to layer both.
reference-transaction is a relatively low-level mechanism. If a script in this hook fails noisily it can disrupt unrelated Git operations. The redirects to /dev/null and the background & above are deliberate. Test the hook with git fetch -v before relying on it.
Aliases as a simpler alternative
For users who prefer to avoid hooks entirely, Git aliases that combine the code and metadata operations work just as well. Add to ~/.gitconfig (global) or .git/config (per-repo):
[alias]
pushm = "!git push \"$@\" && git meta push"
pullm = "!git pull \"$@\" && git meta pull"
fetchm = "!git fetch \"$@\" && git meta pull"
Users then run git pushm, git pullm, or git fetchm instead of the bare commands. The advantages over hooks are:
- portable across machines without copying hook scripts
- explicit: only runs when the user opts in
- easy to debug because failures appear inline in the terminal
The disadvantage is that someone running plain git push or git pull will not get metadata sync. Hooks are appropriate when "always on" is the desired behavior.
Reading metadata alongside code history
Core Git also does not expose hooks for git log or git show, and Git aliases cannot override built-in commands. The reference CLI ships two existing read commands that are intended to be used directly instead:
git meta show <commit>displays the commit details and all metadata attached to that commitgit meta log [<ref>]walks the commit log and shows metadata for each commit, with--moto limit output to commits that actually have metadata
The recommended pattern is to use these as drop-in companions to git show and git log rather than trying to splice metadata into the built-in commands' output.
If you prefer the verbs show and log to live under your normal Git invocation, alias them:
[alias]
showm = "!f() { git show \"$@\" && git meta show \"${1:-HEAD}\" ; }; f"
logm = "!git meta log"
Now git showm <sha> prints the standard git show output followed by the metadata view, and git logm is a shortcut for git meta log.
Recommended starter setup
For a single user on a single machine that wants the round trip to feel automatic with the least moving parts:
- install the CLI:
cargo install git-meta-cli - configure the metadata remote once:
git meta remote add <url> - install the
pre-pushandpost-mergehooks above - add the
showmandlogmaliases above for read access
This covers git push, git pull, and inspection without any custom code. For git fetch without a follow-up merge, fall back to either the reference-transaction hook or the fetchm alias.
What this setup does not do
These recipes are deliberately thin. They do not:
- decide which metadata namespaces are eligible to publish (the meta remote configuration handles that)
- coordinate metadata pushes with multiple code remotes
- present unified
git log/git showoutput that interleaves metadata - handle conflicts beyond what
git meta pullalready does
A higher-level VCS that wants any of those behaviors should follow the Workflow document instead, which describes the integration model for tools like GitButler or Jujutsu.