This commit is contained in:
Javanaut
2026-04-16 19:36:40 +02:00
parent 9fe2a842e9
commit 1bead05d19

View File

@@ -189,7 +189,27 @@ branch_divergence_counts() {
printf '%s %s\n' "${remote_only}" "${local_only}" printf '%s %s\n' "${remote_only}" "${local_only}"
} }
require_branch_contains_remote() { fast_forward_branch_to_remote() {
local branch="$1"
local remote_ref="refs/remotes/${ORIGIN_REMOTE}/${branch}"
local current_head=""
current_head="$(git rev-parse --abbrev-ref HEAD)"
printf "Fast-forwarding local branch '%s' to '%s/%s'...\n" \
"${branch}" \
"${ORIGIN_REMOTE}" \
"${branch}"
if [ "${current_head}" = "${branch}" ]; then
git merge --ff-only "${remote_ref}" >/dev/null
return 0
fi
git branch -f "${branch}" "${remote_ref}" >/dev/null
}
sync_release_source_branch() {
local branch="$1" local branch="$1"
local remote_only="" local remote_only=""
local local_only="" local local_only=""
@@ -201,7 +221,7 @@ require_branch_contains_remote() {
fi fi
if [ "${remote_only}" -ne 0 ]; then if [ "${remote_only}" -ne 0 ]; then
fail "Local branch '${branch}' is behind '${ORIGIN_REMOTE}/${branch}' by ${remote_only} commit(s). Pull or rebase first." fast_forward_branch_to_remote "${branch}"
fi fi
if [ "${local_only}" -ne 0 ]; then if [ "${local_only}" -ne 0 ]; then
@@ -213,26 +233,24 @@ require_branch_contains_remote() {
fi fi
} }
require_branch_matches_remote_exactly() { sync_release_target_branch() {
local branch="$1" local branch="$1"
local remote_only="" local remote_only=""
local local_only="" local local_only=""
read -r remote_only local_only < <(branch_divergence_counts "${branch}") read -r remote_only local_only < <(branch_divergence_counts "${branch}")
if [ "${remote_only}" -eq 0 ] && [ "${local_only}" -eq 0 ]; then
return 0
fi
if [ "${remote_only}" -ne 0 ] && [ "${local_only}" -ne 0 ]; then if [ "${remote_only}" -ne 0 ] && [ "${local_only}" -ne 0 ]; then
fail "Local branch '${branch}' has diverged from '${ORIGIN_REMOTE}/${branch}' (${local_only} local-only commit(s), ${remote_only} remote-only commit(s)). Reconcile the branches first." fail "Local branch '${branch}' has diverged from '${ORIGIN_REMOTE}/${branch}' (${local_only} local-only commit(s), ${remote_only} remote-only commit(s)). Reconcile the branches first."
fi fi
if [ "${remote_only}" -ne 0 ]; then if [ "${local_only}" -ne 0 ]; then
fail "Local branch '${branch}' is behind '${ORIGIN_REMOTE}/${branch}' by ${remote_only} commit(s). Pull or rebase first." fail "Local branch '${branch}' is ahead of '${ORIGIN_REMOTE}/${branch}' by ${local_only} commit(s). Push or reconcile first so the release starts from the published ${branch} tip."
fi fi
fail "Local branch '${branch}' is ahead of '${ORIGIN_REMOTE}/${branch}' by ${local_only} commit(s). Push first so the release starts from the published ${branch} tip." if [ "${remote_only}" -ne 0 ]; then
fast_forward_branch_to_remote "${branch}"
fi
} }
resolve_release_version() { resolve_release_version() {
@@ -295,9 +313,7 @@ print_release_plan() {
printf 'Dry run only. Planned steps:\n' printf 'Dry run only. Planned steps:\n'
printf '1. Ensure current branch is %s and the worktree is clean.\n' "${DEV_BRANCH}" printf '1. Ensure current branch is %s and the worktree is clean.\n' "${DEV_BRANCH}"
printf '2. Fetch %s and verify local %s contains %s/%s while local %s exactly matches %s/%s.\n' \ printf '2. Fetch %s, fast-forward local %s and %s from %s when safe, and fail on divergence or unpublished local %s commits.\n' \
"${ORIGIN_REMOTE}" \
"${DEV_BRANCH}" \
"${ORIGIN_REMOTE}" \ "${ORIGIN_REMOTE}" \
"${DEV_BRANCH}" \ "${DEV_BRANCH}" \
"${MAIN_BRANCH}" \ "${MAIN_BRANCH}" \
@@ -350,8 +366,8 @@ require_repo_state
require_dev_checkout require_dev_checkout
require_clean_worktree require_clean_worktree
fetch_remote_state fetch_remote_state
require_branch_contains_remote "${DEV_BRANCH}" sync_release_source_branch "${DEV_BRANCH}"
require_branch_matches_remote_exactly "${MAIN_BRANCH}" sync_release_target_branch "${MAIN_BRANCH}"
RELEASE_VERSION="$(resolve_release_version)" RELEASE_VERSION="$(resolve_release_version)"
RELEASE_TAG="v${RELEASE_VERSION}" RELEASE_TAG="v${RELEASE_VERSION}"
@@ -387,8 +403,8 @@ fi
run_pre_release_tests run_pre_release_tests
require_clean_worktree require_clean_worktree
fetch_remote_state fetch_remote_state
require_branch_contains_remote "${DEV_BRANCH}" sync_release_source_branch "${DEV_BRANCH}"
require_branch_matches_remote_exactly "${MAIN_BRANCH}" sync_release_target_branch "${MAIN_BRANCH}"
require_release_tag_available "${RELEASE_VERSION}" require_release_tag_available "${RELEASE_VERSION}"
git switch "${MAIN_BRANCH}" >/dev/null git switch "${MAIN_BRANCH}" >/dev/null