#!/usr/bin/env bash # Rebuild work/ from scratch: pristine upstream@upstream.rev + every patch in # patches/, each replayed as its own commit. # # This is the CraftBukkit/Spigot `applyPatches.sh` step. work/ is a DERIVED # artifact -- anything in it that is not a commit is discarded. # # usage: apply-patches.sh [-f|--force] # -f discard uncommitted changes in work/ without asking source "$(dirname "${BASH_SOURCE[0]}")/lib.sh" FORCE="${FORCE:-0}" case "${1:-}" in -f|--force) FORCE=1 ;; "") ;; *) die "unknown argument: $1" ;; esac ensure_upstream REV="$(read_rev)" if [[ ! -d "$WORK_DIR/.git" ]]; then info "Creating work/ from upstream/" git clone --origin upstream --no-checkout "$UPSTREAM_DIR" "$WORK_DIR" else # Refuse to nuke work the developer has not saved anywhere. patches/ is the # source of truth, so anything only present in work/ is about to be lost. if [[ "$FORCE" != "1" ]]; then assert_no_am_in_progress if [[ -n "$(git -C "$WORK_DIR" status --porcelain)" ]]; then die "work/ has uncommitted changes; they would be destroyed. Commit them (cd work && git add -A && git commit) then 'make rebuild', or re-run with: make apply FORCE=1" fi if git -C "$WORK_DIR" rev-parse -q --verify "refs/tags/$BASE_TAG" >/dev/null; then have="$(git -C "$WORK_DIR" rev-list --count "$BASE_TAG..HEAD" 2>/dev/null || echo 0)" want="$(count_patches)" if [[ "$have" != "$want" ]]; then die "work/ has $have commit(s) above $BASE_TAG but patches/ has $want patch file(s). You probably forgot to run 'make rebuild' after committing. To discard the work/ commits and replay patches/ as-is: make apply FORCE=1" fi fi fi step "Fetching into work/" git -C "$WORK_DIR" remote set-url upstream "$UPSTREAM_DIR" git -C "$WORK_DIR" fetch --tags --prune upstream fi cd "$WORK_DIR" git cat-file -e "${REV}^{commit}" 2>/dev/null \ || die "pinned revision $REV not found in upstream. Run 'make update' to re-pin." # Abandon any half-finished am from a previous run before resetting. gitdir="$(git rev-parse --git-dir)" [[ -d "$gitdir/rebase-apply" ]] && git am --abort >/dev/null 2>&1 || true info "Resetting work/ to upstream $(git rev-parse --short "$REV")" git checkout -q -B "$WORK_BRANCH" "$REV" git reset -q --hard "$REV" # -x removes ignored files too, which is what we want for a pristine tree -- # EXCEPT target/, which holds the Rust incremental cache. Wiping it would turn # every apply into a 30-minute cold rebuild. git clean -qfdx -e /target # The boundary between "upstream" and "ours". rebuild-patches.sh formats # everything after this tag. git tag -f "$BASE_TAG" HEAD >/dev/null n="$(count_patches)" if [[ "$n" -eq 0 ]]; then info "No patches to apply -- work/ is pristine upstream." exit 0 fi info "Applying $n patch(es)" if ! git am --3way --keep-cr --whitespace=nowarn "$PATCHES_DIR"/*.patch; then failed="$(basename "$(cat "$(git rev-parse --git-dir)/rebase-apply/original-commit" 2>/dev/null || true)" 2>/dev/null || true)" cat >&2 < patch application failed${C_RESET} This is normal after an upstream sync that touched the same code. Resolve it the same way you would a rebase: cd work git status # see the conflicting files \$EDITOR # fix the conflict markers git add -A git am --continue # or: git am --skip / git am --abort When 'git am' finishes cleanly, write the result back to patches/: make rebuild EOF exit 1 fi applied="$(git rev-list --count "$BASE_TAG..HEAD")" info "work/ is ready: upstream $(git rev-parse --short "$BASE_TAG") + $applied patch(es)"