ff
This commit is contained in:
@@ -172,21 +172,67 @@ fetch_remote_state() {
|
|||||||
git fetch "${ORIGIN_REMOTE}" "${DEV_BRANCH}" "${MAIN_BRANCH}" --tags >/dev/null
|
git fetch "${ORIGIN_REMOTE}" "${DEV_BRANCH}" "${MAIN_BRANCH}" --tags >/dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
require_branch_matches_remote() {
|
branch_divergence_counts() {
|
||||||
local branch="$1"
|
local branch="$1"
|
||||||
local local_sha=""
|
local remote_only=""
|
||||||
local remote_sha=""
|
local local_only=""
|
||||||
|
|
||||||
if ! git show-ref --verify --quiet "refs/remotes/${ORIGIN_REMOTE}/${branch}"; then
|
if ! git show-ref --verify --quiet "refs/remotes/${ORIGIN_REMOTE}/${branch}"; then
|
||||||
fail "Remote branch '${ORIGIN_REMOTE}/${branch}' does not exist."
|
fail "Remote branch '${ORIGIN_REMOTE}/${branch}' does not exist."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local_sha="$(git rev-parse "refs/heads/${branch}")"
|
read -r remote_only local_only < <(
|
||||||
remote_sha="$(git rev-parse "refs/remotes/${ORIGIN_REMOTE}/${branch}")"
|
git rev-list --left-right --count \
|
||||||
|
"refs/remotes/${ORIGIN_REMOTE}/${branch}...refs/heads/${branch}"
|
||||||
|
)
|
||||||
|
|
||||||
if [ "${local_sha}" != "${remote_sha}" ]; then
|
printf '%s %s\n' "${remote_only}" "${local_only}"
|
||||||
fail "Local branch '${branch}' is not up to date with '${ORIGIN_REMOTE}/${branch}'. Pull, rebase, or push first."
|
}
|
||||||
|
|
||||||
|
require_branch_contains_remote() {
|
||||||
|
local branch="$1"
|
||||||
|
local remote_only=""
|
||||||
|
local local_only=""
|
||||||
|
|
||||||
|
read -r remote_only local_only < <(branch_divergence_counts "${branch}")
|
||||||
|
|
||||||
|
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."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ "${remote_only}" -ne 0 ]; then
|
||||||
|
fail "Local branch '${branch}' is behind '${ORIGIN_REMOTE}/${branch}' by ${remote_only} commit(s). Pull or rebase first."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${local_only}" -ne 0 ]; then
|
||||||
|
printf "Notice: local branch '%s' is ahead of '%s/%s' by %s commit(s); release will use the local tip.\n" \
|
||||||
|
"${branch}" \
|
||||||
|
"${ORIGIN_REMOTE}" \
|
||||||
|
"${branch}" \
|
||||||
|
"${local_only}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
require_branch_matches_remote_exactly() {
|
||||||
|
local branch="$1"
|
||||||
|
local remote_only=""
|
||||||
|
local local_only=""
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
if [ "${remote_only}" -ne 0 ]; then
|
||||||
|
fail "Local branch '${branch}' is behind '${ORIGIN_REMOTE}/${branch}' by ${remote_only} commit(s). Pull or rebase first."
|
||||||
|
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."
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve_release_version() {
|
resolve_release_version() {
|
||||||
@@ -249,13 +295,13 @@ 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 and %s exactly match %s/%s and %s/%s.\n' \
|
printf '2. Fetch %s and verify local %s contains %s/%s while local %s exactly matches %s/%s.\n' \
|
||||||
|
"${ORIGIN_REMOTE}" \
|
||||||
|
"${DEV_BRANCH}" \
|
||||||
"${ORIGIN_REMOTE}" \
|
"${ORIGIN_REMOTE}" \
|
||||||
"${DEV_BRANCH}" \
|
"${DEV_BRANCH}" \
|
||||||
"${MAIN_BRANCH}" \
|
"${MAIN_BRANCH}" \
|
||||||
"${ORIGIN_REMOTE}" \
|
"${ORIGIN_REMOTE}" \
|
||||||
"${DEV_BRANCH}" \
|
|
||||||
"${ORIGIN_REMOTE}" \
|
|
||||||
"${MAIN_BRANCH}"
|
"${MAIN_BRANCH}"
|
||||||
if [ "${SKIP_TESTS}" -eq 1 ]; then
|
if [ "${SKIP_TESTS}" -eq 1 ]; then
|
||||||
printf '3. Skip the pre-release test gate.\n'
|
printf '3. Skip the pre-release test gate.\n'
|
||||||
@@ -304,8 +350,8 @@ require_repo_state
|
|||||||
require_dev_checkout
|
require_dev_checkout
|
||||||
require_clean_worktree
|
require_clean_worktree
|
||||||
fetch_remote_state
|
fetch_remote_state
|
||||||
require_branch_matches_remote "${DEV_BRANCH}"
|
require_branch_contains_remote "${DEV_BRANCH}"
|
||||||
require_branch_matches_remote "${MAIN_BRANCH}"
|
require_branch_matches_remote_exactly "${MAIN_BRANCH}"
|
||||||
|
|
||||||
RELEASE_VERSION="$(resolve_release_version)"
|
RELEASE_VERSION="$(resolve_release_version)"
|
||||||
RELEASE_TAG="v${RELEASE_VERSION}"
|
RELEASE_TAG="v${RELEASE_VERSION}"
|
||||||
@@ -341,8 +387,8 @@ fi
|
|||||||
run_pre_release_tests
|
run_pre_release_tests
|
||||||
require_clean_worktree
|
require_clean_worktree
|
||||||
fetch_remote_state
|
fetch_remote_state
|
||||||
require_branch_matches_remote "${DEV_BRANCH}"
|
require_branch_contains_remote "${DEV_BRANCH}"
|
||||||
require_branch_matches_remote "${MAIN_BRANCH}"
|
require_branch_matches_remote_exactly "${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
|
||||||
|
|||||||
Reference in New Issue
Block a user