mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 19:10:14 +01:00
It had a few subtle GNU extensions in it that didn't work on macOS. There are simple portable alternatives so it was easy to adapt. Assisted-by: Antigravity with Gemini
113 lines
3.6 KiB
Bash
Executable File
113 lines
3.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
# Exceptions. See /LICENSE for license information.
|
|
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
#
|
|
# Runs prek over the commits `jj git push` would send, and pushes only if they
|
|
# pass. Takes the same arguments as `jj git push`.
|
|
|
|
set -eu
|
|
|
|
JJ_PREK="$(dirname "${BASH_SOURCE[0]}")/jj_prek.sh"
|
|
|
|
# Succeeds if the working-copy commit is in the given revset. This snapshots the
|
|
# working copy, so it sees anything the hooks rewrote.
|
|
working_copy_is() {
|
|
[[ -n "$(jj log --no-graph -r "@ & ($1)" --template 'commit_id')" ]]
|
|
}
|
|
|
|
# Succeeds if the working copy is an empty, undescribed child of the target,
|
|
# which hooks can run in and write their fixes into.
|
|
working_copy_sits_on() {
|
|
working_copy_is "empty() & description(exact:\"\") & children($1)"
|
|
}
|
|
|
|
# Returns the working copy to where it started. jj discards an empty,
|
|
# undescribed commit when the working copy moves off it, so the original may be
|
|
# gone. Build a new one on the same parents in that case.
|
|
restore_working_copy() {
|
|
if [[ -n "$(jj log --no-graph --ignore-working-copy \
|
|
-r "present($ORIG_CHANGE)" --template 'commit_id')" ]]; then
|
|
jj edit --quiet "$ORIG_CHANGE"
|
|
else
|
|
jj new --quiet $ORIG_PARENTS
|
|
fi
|
|
}
|
|
|
|
# `--help` describes `jj git push`, and has nothing to check.
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
-h | --help)
|
|
exec jj git push "$@"
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Ask jj what the push would do. This checks the arguments and lists the commits
|
|
# being sent, without contacting the remote. A `--dry-run` already in `$@` is
|
|
# harmless here, and still suppresses the push at the end.
|
|
if ! PLAN="$(jj git push --dry-run "$@" 2>&1)"; then
|
|
echo "$PLAN" >&2
|
|
exit 1
|
|
fi
|
|
|
|
REMOTE="$(sed -n 's/^Changes to push to \(.*\):$/\1/p' <<<"$PLAN")"
|
|
|
|
# Each updated bookmark or tag reports the commit it moves to. Deletions have no
|
|
# such commit, and send nothing to check.
|
|
TARGETS="$(sed -E -n 's/^ (bookmark|tag): .* to ([0-9a-f]{8,})\]$/\2/p' <<<"$PLAN" |
|
|
paste -sd '|' -)"
|
|
|
|
# Nothing to check, so just push.
|
|
if [[ -z "$REMOTE" || -z "$TARGETS" ]]; then
|
|
exec jj git push "$@"
|
|
fi
|
|
|
|
# Only the heads need checking; a head's range covers everything below it.
|
|
HEADS="$(jj log --no-graph --ignore-working-copy -r "heads($TARGETS)" \
|
|
--template 'commit_id ++ "\n"')"
|
|
|
|
ORIG_CHANGE="$(jj log --no-graph -r @ --template 'change_id')"
|
|
ORIG_PARENTS="$(jj log --no-graph -r 'parents(@)' --template 'commit_id ++ " "')"
|
|
|
|
for target in $HEADS; do
|
|
# Check with the working copy on top of the target.
|
|
made_scratch=0
|
|
if ! working_copy_sits_on "$target"; then
|
|
jj new --quiet "$target"
|
|
made_scratch=1
|
|
fi
|
|
|
|
# Check from the newest ancestor already on the remote. When there is none,
|
|
# there is no range to diff, so check every file.
|
|
base="$(jj log --no-graph --ignore-working-copy \
|
|
-r "heads(::$target & ::remote_bookmarks(remote=exact:$REMOTE))" \
|
|
--template 'commit_id ++ "\n"' | head -n 1)"
|
|
if [[ -n "$base" ]]; then
|
|
check=(--from-ref "$base" --to-ref "$target")
|
|
else
|
|
check=(--all-files)
|
|
fi
|
|
|
|
result=0
|
|
"$JJ_PREK" "${check[@]}" || result=$?
|
|
|
|
# Discard the scratch commit unless the hooks wrote something into it.
|
|
if ((made_scratch)) && working_copy_is 'empty()'; then
|
|
restore_working_copy
|
|
fi
|
|
|
|
if ((result)); then
|
|
echo >&2
|
|
if ! working_copy_is 'empty()'; then
|
|
change="$(jj log --no-graph -r @ --template 'change_id.shortest()')"
|
|
echo "Hooks changed files. They are in $change." >&2
|
|
fi
|
|
echo "Error: checks failed, nothing pushed." >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
exec jj git push "$@"
|