Files
Agent-System/scripts/rewrite-history-epics.sh
weichenandCursor 9e8ff2ff4a chore(scripts): add epic history rewrite tooling and SHA map docs
Replay 358 linear commits as 14 feature/fix epics with merge --no-ff;
include manifest, rewrite script, SHA map, and git-workflow-helper alignment.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-13 02:51:33 +00:00

105 lines
3.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# Replay linear dev history as feature/fix branches with merge --no-ff.
# Requires: scripts/history-epic-manifest.tsv and tag backup/pre-rewrite-2026-09-13
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
MANIFEST="${ROOT_DIR}/scripts/history-epic-manifest.tsv"
BACKUP_TAG="backup/pre-rewrite-2026-09-13"
SOURCE_BRANCH="dev"
TARGET_BRANCH="dev-rewritten"
DRY_RUN="${DRY_RUN:-0}"
declare -A MSG_OVERRIDE=(
[d27b0ad]="docs(ops): 建立仓库基础架构、三分支规范与环境运行文档"
[91985a2]="fix(quality): 闭环 Go 质量门禁(鉴权富化/V0.5/vet/health/KB gate/记忆轮询)"
[546de52]="ci(qa): add QA-001 gate with go vet, short tests, and migration check."
[e82b624]="docs(plan): add production governance plan and Phase 0 authz matrix"
[77d3da1]="docs(plan): add Phase 1 IAM and authorization implementation plan"
[ba249a5]="chore(scripts): fix backfill script import and run projection for ws_intake"
)
log() { echo "[rewrite] $*"; }
die() { echo "[rewrite] ERROR: $*" >&2; exit 1; }
[[ -f "${MANIFEST}" ]] || die "missing manifest: ${MANIFEST}"
git rev-parse "${BACKUP_TAG}" >/dev/null 2>&1 || die "missing backup tag ${BACKUP_TAG}"
cd "${ROOT_DIR}"
if [[ "${DRY_RUN}" == "1" ]]; then
log "DRY_RUN=1 — manifest entries:"
tail -n +2 "${MANIFEST}"
exit 0
fi
current_branch="$(git branch --show-current)"
[[ "${current_branch}" == "${SOURCE_BRANCH}" ]] || die "checkout ${SOURCE_BRANCH} first (on ${current_branch})"
if git show-ref --verify --quiet "refs/heads/${TARGET_BRANCH}"; then
die "branch ${TARGET_BRANCH} already exists; delete it before rerun"
fi
log "creating orphan branch ${TARGET_BRANCH}"
git checkout --orphan "${TARGET_BRANCH}"
git rm -rf . >/dev/null 2>&1 || true
git commit --allow-empty -m "chore: history rewrite root anchor"
commit_range() {
local start="$1" end="$2"
if [[ "${start}" == "${end}" ]]; then
echo "${start}"
return
fi
if git rev-parse "${start}^" >/dev/null 2>&1; then
git rev-list --reverse "${start}^..${end}"
else
echo "${start}"
git rev-list --reverse "${start}..${end}"
fi
}
cherry_pick_commit() {
local hash="$1"
if ! git cherry-pick "${hash}"; then
die "cherry-pick failed at ${hash}; run git cherry-pick --abort and fix manifest/script"
fi
if [[ -n "${MSG_OVERRIDE[${hash}]+x}" ]]; then
git commit --amend -m "${MSG_OVERRIDE[${hash}]}"
fi
}
cherry_pick_range() {
local start="$1" end="$2"
local hash
while read -r hash; do
[[ -n "${hash}" ]] || continue
log " cherry-pick ${hash}"
cherry_pick_commit "${hash}"
done < <(commit_range "${start}" "${end}")
}
while IFS=$'\t' read -r slug branch_type start end merge_msg; do
branch="${branch_type}/${slug}"
log "epic ${slug}: ${branch} (${start}..${end})"
git checkout -b "${branch}" "${TARGET_BRANCH}"
cherry_pick_range "${start}" "${end}"
git checkout "${TARGET_BRANCH}"
git merge --no-ff "${branch}" -m "${merge_msg}"
git branch -d "${branch}"
done < <(tail -n +2 "${MANIFEST}")
log "verifying tree matches ${BACKUP_TAG}"
if ! git diff --quiet "${BACKUP_TAG}" HEAD; then
die "tree mismatch after rewrite — aborting before branch swap"
fi
log "replacing ${SOURCE_BRANCH} with rewritten history"
git branch -m "${SOURCE_BRANCH}" "${SOURCE_BRANCH}-old-linear"
git branch -m "${TARGET_BRANCH}" "${SOURCE_BRANCH}"
log "done. old linear history preserved on ${SOURCE_BRANCH}-old-linear"
log "verify: git diff ${BACKUP_TAG} ${SOURCE_BRANCH} (should be empty)"