Modified - Extended the custom-release maintenance helper with manual restore points and corrected its fetch behavior.
This commit is contained in:
+12
-2
@@ -755,7 +755,17 @@ Project Change ID[date-time] - application-version - Type - Summary:
|
||||
- 4 - I added both command-line usage and an interactive menu for fetch, list, import, import-range, continue, backup, rollback, restore, and status workflows.
|
||||
|
||||
150 - [2026-05-13 00:18:42] - v1.27.0-dev-143-g512e577c3f - Type: Added - Added an interactive custom-release maintenance helper for stable patch lines.
|
||||
- 1 - I added [`.maintain-custom-release.sh`](/config/workspace/gitea-dev/gitea/.maintain-custom-release.sh) to maintain a persistent custom branch such as `release/v1.26-custom`, starting from a base tag like `v1.26.0-rc0`, importing the missing upstream commits from a selected release branch such as `release/v1.26.0` or later `release/v1.26.1`, and then importing only the custom commits from a chosen local source branch.
|
||||
- 2 - I made the helper interactive with a menu plus a guided `configure` step, so the base tag, upstream release branch, maintenance branch, custom source branch, upstream compare branch, and custom tag suffix can all be adjusted from prompts instead of only through environment variables.
|
||||
- 1 - I added [`.maintain-custom-release.sh`](/config/workspace/gitea-dev/gitea/.maintain-custom-release.sh) to maintain a persistent custom branch such as `release/v1.26-custom`, starting from a base tag like `v1.26.0-rc0`, importing the missing upstream commits from the real upstream minor release branch such as `release/v1.26`, and then importing only the custom commits from a chosen local source branch.
|
||||
- 2 - I made the helper interactive with a menu plus a guided `configure` step, so the base tag, upstream release branch, maintenance branch, custom source branch, upstream compare branch, and custom tag suffix can all be adjusted from prompts instead of only through environment variables, and I added automatic fallback from mistyped patch-style branch names like `release/v1.26.0` to the real upstream branch `release/v1.26` when it exists.
|
||||
- 3 - I gave the script the same safety model as the other local Git helpers by creating a fresh backup branch, an exact worktree snapshot, and a saved tracked/untracked stash before every real import action, while also storing pending cherry-pick state so interrupted sequences can be continued.
|
||||
- 4 - I added recovery commands for `continue`, `rollback`, `restore-stash`, and `restore-snapshot`, and a `tag` command that creates annotated custom release tags such as `v1.26.0-custom` on the maintained custom release branch.
|
||||
|
||||
151 - [2026-05-13 00:34:10] - v1.27.0-dev-143-g512e577c3f - Type: Modified - Narrowed local upstream fetch scope and cleaned unused upstream tracking refs.
|
||||
- 1 - I updated [`.update-gitea.sh`](/config/workspace/gitea-dev/gitea/.update-gitea.sh) and [`.import-upstream-cherry-pick.sh`](/config/workspace/gitea-dev/gitea/.import-upstream-cherry-pick.sh) so they fetch only the explicitly requested upstream branch instead of all upstream branches.
|
||||
- 2 - I updated [`.maintain-custom-release.sh`](/config/workspace/gitea-dev/gitea/.maintain-custom-release.sh) so it fetches only the configured upstream release branch, the configured compare branch, and tags, while also normalizing mistaken patch-style release branch names like `release/v1.26.0` to the real upstream branch `release/v1.26`.
|
||||
- 3 - I narrowed the local `remote.upstream.fetch` refspec to `upstream/main` and `upstream/release/v1.26`, then removed the other local `upstream/*` remote-tracking refs so Git Graph no longer shows the unused upstream branches by default.
|
||||
|
||||
152 - [2026-05-13 20:23:40] - v1.27.0-dev-143-g512e577c3f - Type: Modified - Extended the custom-release maintenance helper with manual restore points and corrected its fetch behavior.
|
||||
- 1 - I updated [`.maintain-custom-release.sh`](/config/workspace/gitea-dev/gitea/.maintain-custom-release.sh) so it no longer auto-fetches upstream tags during release-branch synchronization, preventing patch tags like `v1.26.1` from being pulled in just because the release branch history was fetched.
|
||||
- 2 - I added manual restore-point management commands for `create-restore-point`, `list-restore-points`, `restore-point`, and `delete-restore-point`, each backed by an exact worktree snapshot plus an associated backup branch stored under `.git/.maintain-custom-release-restore-points`.
|
||||
- 3 - I integrated the new restore-point workflow into the interactive menu, the `help` text, and `status`, including support for restoring by full path or by the final restore-point directory name, automatically using the latest restore point for the current branch when no target is provided, and recognizing the older manual restore points already stored under `.git/.manual-restore-points`.
|
||||
|
||||
@@ -313,7 +313,7 @@ stash_current_changes() {
|
||||
|
||||
fetch_upstream() {
|
||||
say "Fetching latest changes from $REMOTE_NAME/$BRANCH..."
|
||||
git fetch --prune "$REMOTE_NAME"
|
||||
git fetch --prune "$REMOTE_NAME" "+refs/heads/$BRANCH:refs/remotes/$REMOTE_NAME/$BRANCH"
|
||||
git show-ref --verify --quiet "refs/remotes/$REMOTE_NAME/$BRANCH" || die "Remote branch '$REMOTE_NAME/$BRANCH' was not found."
|
||||
}
|
||||
|
||||
|
||||
+370
-39
@@ -6,7 +6,7 @@ set -euo pipefail
|
||||
# line while keeping your own local custom commits.
|
||||
# It is designed for flows such as:
|
||||
# - base tag: v1.26.0-rc0
|
||||
# - upstream release line: release/v1.26.0, later release/v1.26.1
|
||||
# - upstream release line: release/v1.26
|
||||
# - custom maintenance: release/v1.26-custom
|
||||
# - custom release tags: v1.26.0-custom, v1.26.1-custom
|
||||
# It:
|
||||
@@ -18,7 +18,7 @@ set -euo pipefail
|
||||
# original state can be restored safely if something is not OK.
|
||||
|
||||
BASE_TAG="${BASE_TAG:-v1.26.0-rc0}"
|
||||
UPSTREAM_RELEASE_BRANCH="${UPSTREAM_RELEASE_BRANCH:-release/v1.26.0}"
|
||||
UPSTREAM_RELEASE_BRANCH="${UPSTREAM_RELEASE_BRANCH:-release/v1.26}"
|
||||
MAINTENANCE_BRANCH="${MAINTENANCE_BRANCH:-release/v1.26-custom}"
|
||||
CUSTOM_SOURCE_BRANCH="${CUSTOM_SOURCE_BRANCH:-main}"
|
||||
UPSTREAM_COMPARE_BRANCH="${UPSTREAM_COMPARE_BRANCH:-main}"
|
||||
@@ -30,6 +30,8 @@ REPO_ROOT=""
|
||||
GIT_DIR=""
|
||||
STATE_FILE=""
|
||||
SNAPSHOT_ROOT=""
|
||||
RESTORE_POINT_ROOT=""
|
||||
LEGACY_RESTORE_POINT_ROOT=""
|
||||
CURRENT_BRANCH=""
|
||||
BACKUP_BRANCH=""
|
||||
STASH_REF=""
|
||||
@@ -75,6 +77,14 @@ Environment defaults:
|
||||
Available commands:
|
||||
configure
|
||||
Interactively update the current in-memory config values for this run.
|
||||
create-restore-point [label]
|
||||
Create a manual restore point for the current state, including a backup branch and an exact worktree snapshot.
|
||||
list-restore-points
|
||||
List available manual restore points created by this script.
|
||||
restore-point [restore-point-dir-or-name]
|
||||
Restore a manual restore point by path or by its final directory name. If omitted, the latest point for the current branch is used.
|
||||
delete-restore-point <restore-point-dir-or-name>
|
||||
Delete a manual restore point and its associated backup branch.
|
||||
status
|
||||
Show the current config, branch state, and saved recovery metadata.
|
||||
fetch
|
||||
@@ -107,16 +117,19 @@ menu is shown.
|
||||
|
||||
Recommended order:
|
||||
1. Run: ./.maintain-custom-release.sh status
|
||||
2. Run: ./.maintain-custom-release.sh plan
|
||||
3. First release on a new series:
|
||||
2. Optional but recommended: ./.maintain-custom-release.sh create-restore-point before-custom-release-work
|
||||
3. Run: ./.maintain-custom-release.sh plan
|
||||
4. First release on a new series:
|
||||
- run: ./.maintain-custom-release.sh bootstrap
|
||||
4. Later patch releases on the same series:
|
||||
- update UPSTREAM_RELEASE_BRANCH if needed
|
||||
5. Later patch releases on the same series:
|
||||
- keep the same upstream minor release branch, for example release/v1.26
|
||||
- run: ./.maintain-custom-release.sh sync-all
|
||||
5. If cherry-pick stops with conflicts:
|
||||
6. If cherry-pick stops with conflicts:
|
||||
- either resolve them and continue with: ./.maintain-custom-release.sh continue
|
||||
- or return safely with: ./.maintain-custom-release.sh rollback
|
||||
6. When the branch looks correct:
|
||||
7. If you want to return to an explicitly saved manual checkpoint:
|
||||
- run: ./.maintain-custom-release.sh restore-point
|
||||
8. When the branch looks correct:
|
||||
- run: ./.maintain-custom-release.sh tag v1.26.0
|
||||
which creates: v1.26.0-custom
|
||||
EOF
|
||||
@@ -134,6 +147,25 @@ prompt_with_default() {
|
||||
fi
|
||||
}
|
||||
|
||||
normalize_upstream_release_branch() {
|
||||
local fallback_branch=""
|
||||
|
||||
if git rev-parse --verify "refs/remotes/$REMOTE_NAME/$UPSTREAM_RELEASE_BRANCH" >/dev/null 2>&1; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$UPSTREAM_RELEASE_BRANCH" =~ ^release/v([0-9]+)\.([0-9]+)\.[0-9]+$ ]]; then
|
||||
fallback_branch="release/v${BASH_REMATCH[1]}.${BASH_REMATCH[2]}"
|
||||
if git rev-parse --verify "refs/remotes/$REMOTE_NAME/$fallback_branch" >/dev/null 2>&1; then
|
||||
say "Configured upstream release branch '$UPSTREAM_RELEASE_BRANCH' does not exist. Using '$fallback_branch' instead."
|
||||
UPSTREAM_RELEASE_BRANCH="$fallback_branch"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
die "Remote branch '$REMOTE_NAME/$UPSTREAM_RELEASE_BRANCH' was not found."
|
||||
}
|
||||
|
||||
ensure_git_repo() {
|
||||
local raw_git_dir
|
||||
|
||||
@@ -145,6 +177,8 @@ ensure_git_repo() {
|
||||
esac
|
||||
STATE_FILE="$GIT_DIR/.maintain-custom-release-state"
|
||||
SNAPSHOT_ROOT="$GIT_DIR/.maintain-custom-release-snapshots"
|
||||
RESTORE_POINT_ROOT="$GIT_DIR/.maintain-custom-release-restore-points"
|
||||
LEGACY_RESTORE_POINT_ROOT="$GIT_DIR/.manual-restore-points"
|
||||
}
|
||||
|
||||
write_state() {
|
||||
@@ -188,6 +222,16 @@ sanitize_ref_name() {
|
||||
printf '%s\n' "${1//\//-}"
|
||||
}
|
||||
|
||||
sanitize_storage_name() {
|
||||
local raw_value="${1:-}" sanitized=""
|
||||
|
||||
sanitized="${raw_value//\//-}"
|
||||
sanitized="${sanitized// /-}"
|
||||
sanitized="${sanitized//:/-}"
|
||||
sanitized="${sanitized//[^[:alnum:]._-]/-}"
|
||||
printf '%s\n' "$sanitized"
|
||||
}
|
||||
|
||||
ensure_rsync_available() {
|
||||
command -v rsync >/dev/null 2>&1 || die "rsync is required for exact repository snapshots."
|
||||
}
|
||||
@@ -248,6 +292,122 @@ find_latest_snapshot_for_current() {
|
||||
find "$snapshot_branch_dir" -mindepth 1 -maxdepth 1 -type d | sort -r | head -n 1
|
||||
}
|
||||
|
||||
find_latest_restore_point_for_current() {
|
||||
local restore_point_dir source_branch
|
||||
|
||||
while IFS= read -r restore_point_dir; do
|
||||
[ -n "$restore_point_dir" ] || continue
|
||||
source_branch="$(snapshot_metadata_value "$restore_point_dir" source_branch || true)"
|
||||
if [ "$source_branch" = "$CURRENT_BRANCH" ]; then
|
||||
printf '%s\n' "$restore_point_dir"
|
||||
return 0
|
||||
fi
|
||||
done < <(list_all_restore_points)
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
list_all_restore_points() {
|
||||
{
|
||||
if [ -d "$RESTORE_POINT_ROOT" ]; then
|
||||
find "$RESTORE_POINT_ROOT" -mindepth 2 -maxdepth 2 -type d
|
||||
fi
|
||||
if [ -d "$LEGACY_RESTORE_POINT_ROOT" ]; then
|
||||
find "$LEGACY_RESTORE_POINT_ROOT" -mindepth 1 -maxdepth 1 -type d
|
||||
fi
|
||||
} | sort -r
|
||||
}
|
||||
|
||||
is_valid_restore_point_dir() {
|
||||
local target_dir="$1"
|
||||
|
||||
[ -d "$target_dir" ] || return 1
|
||||
case "$target_dir" in
|
||||
"$RESTORE_POINT_ROOT"/*) ;;
|
||||
"$LEGACY_RESTORE_POINT_ROOT"/*) ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
is_valid_snapshot_dir "$target_dir"
|
||||
}
|
||||
|
||||
resolve_restore_point_dir() {
|
||||
local requested_target="${1:-}" direct_target="" branch_target=""
|
||||
local -a matches=()
|
||||
|
||||
if [ -z "$requested_target" ]; then
|
||||
find_latest_restore_point_for_current
|
||||
return
|
||||
fi
|
||||
|
||||
if [ -d "$requested_target" ]; then
|
||||
direct_target="$(cd "$requested_target" && pwd -P)"
|
||||
if is_valid_restore_point_dir "$direct_target"; then
|
||||
printf '%s\n' "$direct_target"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
branch_target="$RESTORE_POINT_ROOT/$(sanitize_ref_name "$CURRENT_BRANCH")/$requested_target"
|
||||
if [ -d "$branch_target" ] && is_valid_restore_point_dir "$branch_target"; then
|
||||
printf '%s\n' "$branch_target"
|
||||
return
|
||||
fi
|
||||
|
||||
mapfile -t matches < <(list_all_restore_points | grep -E "/${requested_target}$" || true)
|
||||
if [ "${#matches[@]}" -eq 1 ] && is_valid_restore_point_dir "${matches[0]}"; then
|
||||
printf '%s\n' "${matches[0]}"
|
||||
return
|
||||
fi
|
||||
if [ "${#matches[@]}" -gt 1 ]; then
|
||||
die "More than one restore point matches '$requested_target'. Use the full path."
|
||||
fi
|
||||
|
||||
die "Restore point '$requested_target' was not found."
|
||||
}
|
||||
|
||||
create_restore_point_branch_for_label() {
|
||||
local label="$1" safe_branch_name safe_label timestamp branch_name
|
||||
|
||||
safe_branch_name="$(sanitize_ref_name "$CURRENT_BRANCH")"
|
||||
safe_label="$(sanitize_storage_name "$label")"
|
||||
[ -n "$safe_label" ] || safe_label="manual"
|
||||
timestamp="$(date +%Y%m%d-%H%M%S)"
|
||||
branch_name="backup/restore-point-${safe_branch_name}-${safe_label}-${timestamp}"
|
||||
|
||||
git branch "$branch_name" HEAD >/dev/null
|
||||
printf '%s\n' "$branch_name"
|
||||
}
|
||||
|
||||
create_manual_restore_point() {
|
||||
local label="${1:-manual}" safe_branch_name safe_label timestamp restore_point_dir restore_point_repo_dir
|
||||
local restore_point_backup_branch current_head
|
||||
|
||||
ensure_rsync_available
|
||||
safe_branch_name="$(sanitize_ref_name "$CURRENT_BRANCH")"
|
||||
safe_label="$(sanitize_storage_name "$label")"
|
||||
[ -n "$safe_label" ] || safe_label="manual"
|
||||
timestamp="$(date +%Y%m%d-%H%M%S)"
|
||||
restore_point_dir="$RESTORE_POINT_ROOT/$safe_branch_name/${timestamp}-${safe_label}"
|
||||
restore_point_repo_dir="$(snapshot_repo_dir "$restore_point_dir")"
|
||||
restore_point_backup_branch="$(create_restore_point_branch_for_label "$safe_label")"
|
||||
current_head="$(git rev-parse HEAD)"
|
||||
|
||||
mkdir -p "$restore_point_repo_dir"
|
||||
rsync --archive --delete --exclude='.git' "$REPO_ROOT"/ "$restore_point_repo_dir"/
|
||||
|
||||
{
|
||||
printf 'branch=%s\n' "$CURRENT_BRANCH"
|
||||
printf 'source_branch=%s\n' "$CURRENT_BRANCH"
|
||||
printf 'head=%s\n' "$current_head"
|
||||
printf 'created_at=%s\n' "$(date +%Y-%m-%dT%H:%M:%S)"
|
||||
printf 'label=%s\n' "$label"
|
||||
printf 'backup_branch=%s\n' "$restore_point_backup_branch"
|
||||
printf 'reason=manual_restore_point\n'
|
||||
} >"$restore_point_dir/metadata.txt"
|
||||
|
||||
printf '%s\n' "$restore_point_dir"
|
||||
}
|
||||
|
||||
ensure_no_noncherrypick_git_operation_in_progress() {
|
||||
local git_path
|
||||
for git_path in MERGE_HEAD REVERT_HEAD BISECT_LOG rebase-merge rebase-apply; do
|
||||
@@ -305,7 +465,7 @@ ensure_upstream_remote() {
|
||||
|
||||
ensure_release_refs_exist() {
|
||||
git rev-parse --verify "$BASE_TAG" >/dev/null 2>&1 || die "Base tag '$BASE_TAG' does not exist locally. Fetch tags first."
|
||||
git rev-parse --verify "refs/remotes/$REMOTE_NAME/$UPSTREAM_RELEASE_BRANCH" >/dev/null 2>&1 || die "Remote branch '$REMOTE_NAME/$UPSTREAM_RELEASE_BRANCH' was not found."
|
||||
normalize_upstream_release_branch
|
||||
git rev-parse --verify "$CUSTOM_SOURCE_BRANCH" >/dev/null 2>&1 || die "Custom source branch '$CUSTOM_SOURCE_BRANCH' does not exist locally."
|
||||
git rev-parse --verify "refs/remotes/$REMOTE_NAME/$UPSTREAM_COMPARE_BRANCH" >/dev/null 2>&1 || die "Remote compare branch '$REMOTE_NAME/$UPSTREAM_COMPARE_BRANCH' was not found."
|
||||
}
|
||||
@@ -397,8 +557,14 @@ stash_current_changes() {
|
||||
}
|
||||
|
||||
fetch_upstream_refs() {
|
||||
local -a fetch_args
|
||||
|
||||
say "Fetching latest changes from $REMOTE_NAME..."
|
||||
git fetch --prune --tags "$REMOTE_NAME"
|
||||
fetch_args=(--prune --no-tags "$REMOTE_NAME" "+refs/heads/$UPSTREAM_RELEASE_BRANCH:refs/remotes/$REMOTE_NAME/$UPSTREAM_RELEASE_BRANCH")
|
||||
if [ "$UPSTREAM_COMPARE_BRANCH" != "$UPSTREAM_RELEASE_BRANCH" ]; then
|
||||
fetch_args+=("+refs/heads/$UPSTREAM_COMPARE_BRANCH:refs/remotes/$REMOTE_NAME/$UPSTREAM_COMPARE_BRANCH")
|
||||
fi
|
||||
git fetch "${fetch_args[@]}"
|
||||
}
|
||||
|
||||
update_state() {
|
||||
@@ -523,7 +689,7 @@ show_commit_list() {
|
||||
run_configure() {
|
||||
say "Interactive configuration"
|
||||
BASE_TAG="$(prompt_with_default "Base tag" "$BASE_TAG")"
|
||||
UPSTREAM_RELEASE_BRANCH="$(prompt_with_default "Upstream release branch" "$UPSTREAM_RELEASE_BRANCH")"
|
||||
UPSTREAM_RELEASE_BRANCH="$(prompt_with_default "Upstream release branch (for example release/v1.26)" "$UPSTREAM_RELEASE_BRANCH")"
|
||||
MAINTENANCE_BRANCH="$(prompt_with_default "Custom maintenance branch" "$MAINTENANCE_BRANCH")"
|
||||
CUSTOM_SOURCE_BRANCH="$(prompt_with_default "Custom source branch" "$CUSTOM_SOURCE_BRANCH")"
|
||||
UPSTREAM_COMPARE_BRANCH="$(prompt_with_default "Upstream compare branch" "$UPSTREAM_COMPARE_BRANCH")"
|
||||
@@ -632,7 +798,7 @@ collect_custom_commits_for_current_branch() {
|
||||
}
|
||||
|
||||
run_status() {
|
||||
local stash_count latest_backup latest_snapshot
|
||||
local stash_count latest_backup latest_snapshot latest_restore_point
|
||||
|
||||
ensure_git_repo
|
||||
ensure_current_branch_with_state_fallback
|
||||
@@ -641,6 +807,7 @@ run_status() {
|
||||
stash_count="$(git stash list | wc -l | tr -d ' ')"
|
||||
latest_backup="$(find_latest_backup_branch_for_current || true)"
|
||||
latest_snapshot="$(find_latest_snapshot_for_current || true)"
|
||||
latest_restore_point="$(find_latest_restore_point_for_current || true)"
|
||||
|
||||
say "Repository: $(git rev-parse --show-toplevel)"
|
||||
say "Current branch: $CURRENT_BRANCH"
|
||||
@@ -659,6 +826,7 @@ run_status() {
|
||||
say "Stash entries: $stash_count"
|
||||
say "Latest backup for current branch: ${latest_backup:-none}"
|
||||
say "Latest snapshot for current branch: ${latest_snapshot:-none}"
|
||||
say "Latest restore point for current branch: ${latest_restore_point:-none}"
|
||||
if load_state; then
|
||||
say "Saved state: $STATE_STATUS"
|
||||
say "Saved backup branch: ${STATE_BACKUP_BRANCH:-none}"
|
||||
@@ -672,6 +840,46 @@ run_status() {
|
||||
fi
|
||||
}
|
||||
|
||||
run_create_restore_point() {
|
||||
local label="${1:-manual}" restore_point_dir=""
|
||||
|
||||
ensure_git_repo
|
||||
ensure_no_git_operation_in_progress
|
||||
ensure_current_branch_with_state_fallback
|
||||
|
||||
restore_point_dir="$(create_manual_restore_point "$label")"
|
||||
say "Manual restore point created: $restore_point_dir"
|
||||
say "Associated backup branch: $(snapshot_metadata_value "$restore_point_dir" backup_branch || true)"
|
||||
}
|
||||
|
||||
run_list_restore_points() {
|
||||
local restore_point_dir source_branch head_value backup_branch created_at found_any=0
|
||||
|
||||
ensure_git_repo
|
||||
if [ ! -d "$RESTORE_POINT_ROOT" ] && [ ! -d "$LEGACY_RESTORE_POINT_ROOT" ]; then
|
||||
say "No restore points found."
|
||||
return
|
||||
fi
|
||||
|
||||
while IFS= read -r restore_point_dir; do
|
||||
[ -n "$restore_point_dir" ] || continue
|
||||
found_any=1
|
||||
source_branch="$(snapshot_metadata_value "$restore_point_dir" source_branch || true)"
|
||||
head_value="$(snapshot_metadata_value "$restore_point_dir" head || true)"
|
||||
backup_branch="$(snapshot_metadata_value "$restore_point_dir" backup_branch || true)"
|
||||
created_at="$(snapshot_metadata_value "$restore_point_dir" created_at || true)"
|
||||
say "- $restore_point_dir"
|
||||
say " source branch: ${source_branch:-unknown}"
|
||||
say " head: ${head_value:-unknown}"
|
||||
say " created at: ${created_at:-unknown}"
|
||||
say " backup branch: ${backup_branch:-none}"
|
||||
done < <(list_all_restore_points)
|
||||
|
||||
if [ "$found_any" -eq 0 ]; then
|
||||
say "No restore points found."
|
||||
fi
|
||||
}
|
||||
|
||||
run_fetch_only() {
|
||||
ensure_git_repo
|
||||
ensure_no_git_operation_in_progress
|
||||
@@ -978,6 +1186,104 @@ run_restore_snapshot() {
|
||||
fi
|
||||
}
|
||||
|
||||
run_restore_restore_point() {
|
||||
local requested_target="${1:-}" restore_point_dir=""
|
||||
local restore_source_branch="" restore_head="" restore_backup_branch=""
|
||||
local restore_safety_backup="" restore_safety_snapshot="" restore_safety_start_head=""
|
||||
local preserve_stash_hash=""
|
||||
|
||||
ensure_git_repo
|
||||
ensure_no_noncherrypick_git_operation_in_progress
|
||||
ensure_current_branch_with_state_fallback
|
||||
|
||||
restore_point_dir="$(resolve_restore_point_dir "$requested_target")"
|
||||
[ -n "$restore_point_dir" ] || die "No restore point was found."
|
||||
restore_source_branch="$(snapshot_metadata_value "$restore_point_dir" source_branch || true)"
|
||||
restore_head="$(snapshot_metadata_value "$restore_point_dir" head || true)"
|
||||
restore_backup_branch="$(snapshot_metadata_value "$restore_point_dir" backup_branch || true)"
|
||||
[ -n "$restore_head" ] || die "Restore point '$restore_point_dir' does not include a saved HEAD."
|
||||
|
||||
if has_cherry_pick_in_progress; then
|
||||
say "Aborting in-progress cherry-pick before restore point restore..."
|
||||
git cherry-pick --abort || die "Failed to abort the in-progress cherry-pick."
|
||||
fi
|
||||
|
||||
if [ -n "$(git diff --name-only --diff-filter=U)" ]; then
|
||||
die "There are still unmerged files in the working tree. Resolve or discard them before restoring the restore point."
|
||||
fi
|
||||
|
||||
prepare_safety_before_real_action "before-restore-point"
|
||||
restore_safety_backup="$BACKUP_BRANCH"
|
||||
restore_safety_snapshot="$STATE_SNAPSHOT_PATH"
|
||||
restore_safety_start_head="$STATE_START_HEAD"
|
||||
|
||||
if worktree_has_local_changes; then
|
||||
stash_current_changes "pre-restore-point-preserve"
|
||||
preserve_stash_hash="$STASH_HASH"
|
||||
fi
|
||||
|
||||
if [ -n "$restore_source_branch" ] && [ "$CURRENT_BRANCH" != "$restore_source_branch" ]; then
|
||||
if git show-ref --verify --quiet "refs/heads/$restore_source_branch"; then
|
||||
git switch "$restore_source_branch" >/dev/null
|
||||
else
|
||||
git switch -c "$restore_source_branch" "$restore_head" >/dev/null
|
||||
fi
|
||||
CURRENT_BRANCH="$restore_source_branch"
|
||||
fi
|
||||
|
||||
say "Resetting '$CURRENT_BRANCH' to '$restore_head' before restore point restore..."
|
||||
git reset --hard "$restore_head" >/dev/null
|
||||
|
||||
say "Restoring exact worktree from restore point: $restore_point_dir"
|
||||
restore_worktree_snapshot "$restore_point_dir"
|
||||
|
||||
STATE_BRANCH="$CURRENT_BRANCH"
|
||||
STATE_BACKUP_BRANCH="$restore_safety_backup"
|
||||
STATE_STASH_HASH="$preserve_stash_hash"
|
||||
STATE_REMOTE_NAME="$REMOTE_NAME"
|
||||
STATE_REMOTE_URL="$REMOTE_URL"
|
||||
STATE_BASE_TAG="$BASE_TAG"
|
||||
STATE_UPSTREAM_RELEASE_BRANCH="$UPSTREAM_RELEASE_BRANCH"
|
||||
STATE_UPSTREAM_COMPARE_BRANCH="$UPSTREAM_COMPARE_BRANCH"
|
||||
STATE_CUSTOM_SOURCE_BRANCH="$CUSTOM_SOURCE_BRANCH"
|
||||
STATE_MAINTENANCE_BRANCH="$MAINTENANCE_BRANCH"
|
||||
STATE_START_HEAD="$restore_safety_start_head"
|
||||
STATE_SNAPSHOT_PATH="$restore_safety_snapshot"
|
||||
STATE_PENDING_COMMITS=""
|
||||
STATE_ACTION_LABEL="restore-point"
|
||||
STATE_STATUS="restore_point_restored"
|
||||
write_state
|
||||
|
||||
say "Restore point restored: $restore_point_dir"
|
||||
say "Original restore-point backup branch: ${restore_backup_branch:-none}"
|
||||
say "Safety backup of the pre-restore state kept at: $restore_safety_backup"
|
||||
say "Safety snapshot of the pre-restore state kept at: $restore_safety_snapshot"
|
||||
if [ -n "$preserve_stash_hash" ]; then
|
||||
say "Safety stash of the pre-restore local changes kept at: $preserve_stash_hash"
|
||||
fi
|
||||
}
|
||||
|
||||
run_delete_restore_point() {
|
||||
local requested_target="${1:-}" restore_point_dir="" restore_point_backup_branch="" restore_point_parent=""
|
||||
|
||||
[ -n "$requested_target" ] || die "Provide the restore point path or name to delete."
|
||||
ensure_git_repo
|
||||
restore_point_dir="$(resolve_restore_point_dir "$requested_target")"
|
||||
restore_point_backup_branch="$(snapshot_metadata_value "$restore_point_dir" backup_branch || true)"
|
||||
restore_point_parent="$(dirname "$restore_point_dir")"
|
||||
|
||||
rm -rf "$restore_point_dir"
|
||||
if [ -n "$restore_point_backup_branch" ] && git show-ref --verify --quiet "refs/heads/$restore_point_backup_branch"; then
|
||||
git branch -D "$restore_point_backup_branch" >/dev/null
|
||||
fi
|
||||
rmdir "$restore_point_parent" >/dev/null 2>&1 || true
|
||||
|
||||
say "Deleted restore point: $restore_point_dir"
|
||||
if [ -n "$restore_point_backup_branch" ]; then
|
||||
say "Deleted associated backup branch: $restore_point_backup_branch"
|
||||
fi
|
||||
}
|
||||
|
||||
run_tag_release() {
|
||||
local upstream_version_tag="${1:-}" custom_tag=""
|
||||
|
||||
@@ -996,25 +1302,29 @@ run_tag_release() {
|
||||
}
|
||||
|
||||
show_menu() {
|
||||
local choice snapshot_target rollback_target tag_value
|
||||
local choice snapshot_target rollback_target tag_value restore_point_label restore_point_target delete_restore_point_target
|
||||
|
||||
while true; do
|
||||
cat <<EOF
|
||||
Available actions:
|
||||
1) Edit configuration
|
||||
2) Show status
|
||||
3) Fetch upstream refs
|
||||
4) Show import plan
|
||||
5) Bootstrap maintenance branch
|
||||
6) Sync upstream release commits
|
||||
7) Sync custom commits
|
||||
8) Sync all
|
||||
9) Continue interrupted import
|
||||
10) Rollback
|
||||
11) Restore saved stash
|
||||
12) Restore exact snapshot
|
||||
13) Create custom release tag
|
||||
14) Help
|
||||
2) Create manual restore point
|
||||
3) List manual restore points
|
||||
4) Restore manual restore point
|
||||
5) Delete manual restore point
|
||||
6) Show status
|
||||
7) Fetch upstream refs
|
||||
8) Show import plan
|
||||
9) Bootstrap maintenance branch
|
||||
10) Sync upstream release commits
|
||||
11) Sync custom commits
|
||||
12) Sync all
|
||||
13) Continue interrupted import
|
||||
14) Rollback
|
||||
15) Restore saved stash
|
||||
16) Restore exact snapshot
|
||||
17) Create custom release tag
|
||||
18) Help
|
||||
0) Exit
|
||||
EOF
|
||||
printf 'Choose an action: '
|
||||
@@ -1022,34 +1332,51 @@ EOF
|
||||
|
||||
case "$choice" in
|
||||
1) run_configure ;;
|
||||
2) run_status ;;
|
||||
3) run_fetch_only ;;
|
||||
4) show_plan ;;
|
||||
5) run_bootstrap; return ;;
|
||||
6) run_sync_upstream; return ;;
|
||||
7) run_sync_custom; return ;;
|
||||
8) run_sync_all; return ;;
|
||||
9) run_continue; return ;;
|
||||
10)
|
||||
2)
|
||||
printf 'Optional restore point label [manual]: '
|
||||
read -r restore_point_label
|
||||
run_create_restore_point "${restore_point_label:-manual}"
|
||||
;;
|
||||
3) run_list_restore_points ;;
|
||||
4)
|
||||
printf 'Optional restore point path or name (leave empty for latest on current branch): '
|
||||
read -r restore_point_target
|
||||
run_restore_restore_point "$restore_point_target"
|
||||
return
|
||||
;;
|
||||
5)
|
||||
printf 'Enter the restore point path or name to delete: '
|
||||
read -r delete_restore_point_target
|
||||
run_delete_restore_point "$delete_restore_point_target"
|
||||
;;
|
||||
6) run_status ;;
|
||||
7) run_fetch_only ;;
|
||||
8) show_plan ;;
|
||||
9) run_bootstrap; return ;;
|
||||
10) run_sync_upstream; return ;;
|
||||
11) run_sync_custom; return ;;
|
||||
12) run_sync_all; return ;;
|
||||
13) run_continue; return ;;
|
||||
14)
|
||||
printf 'Optional backup branch or snapshot path (leave empty for saved state): '
|
||||
read -r rollback_target
|
||||
run_rollback "$rollback_target"
|
||||
return
|
||||
;;
|
||||
11) run_restore_saved_stash; return ;;
|
||||
12)
|
||||
15) run_restore_saved_stash; return ;;
|
||||
16)
|
||||
printf 'Optional snapshot path (leave empty for saved state): '
|
||||
read -r snapshot_target
|
||||
run_restore_snapshot "$snapshot_target"
|
||||
return
|
||||
;;
|
||||
13)
|
||||
17)
|
||||
printf 'Enter the upstream version tag to mark, for example v1.26.0: '
|
||||
read -r tag_value
|
||||
run_tag_release "$tag_value"
|
||||
return
|
||||
;;
|
||||
14) show_usage ;;
|
||||
18) show_usage ;;
|
||||
0) exit 0 ;;
|
||||
*) say "Invalid menu selection: $choice" ;;
|
||||
esac
|
||||
@@ -1059,6 +1386,10 @@ EOF
|
||||
main() {
|
||||
case "${1:-menu}" in
|
||||
configure) run_configure ;;
|
||||
create-restore-point) run_create_restore_point "${2:-manual}" ;;
|
||||
list-restore-points) run_list_restore_points ;;
|
||||
restore-point|restore-restore-point) run_restore_restore_point "${2:-}" ;;
|
||||
delete-restore-point) run_delete_restore_point "${2:-}" ;;
|
||||
status) run_status ;;
|
||||
fetch) run_fetch_only ;;
|
||||
plan) show_plan ;;
|
||||
|
||||
+2
-2
@@ -429,7 +429,7 @@ stash_local_changes() {
|
||||
|
||||
fetch_upstream() {
|
||||
say "Fetching latest changes from $REMOTE_NAME/$BRANCH..."
|
||||
git fetch --prune "$REMOTE_NAME"
|
||||
git fetch --prune "$REMOTE_NAME" "+refs/heads/$BRANCH:refs/remotes/$REMOTE_NAME/$BRANCH"
|
||||
git show-ref --verify --quiet "refs/remotes/$REMOTE_NAME/$BRANCH" || die "Remote branch '$REMOTE_NAME/$BRANCH' was not found."
|
||||
}
|
||||
|
||||
@@ -926,7 +926,7 @@ run_dry_run_sync() {
|
||||
prepare_dry_run_workspace "$dry_run_branch_dir" "$temp_repo"
|
||||
|
||||
say "Testing upstream fetch in the dry-run clone..."
|
||||
git -C "$temp_repo" fetch --prune "$REMOTE_NAME"
|
||||
git -C "$temp_repo" fetch --prune "$REMOTE_NAME" "+refs/heads/$BRANCH:refs/remotes/$REMOTE_NAME/$BRANCH"
|
||||
git -C "$temp_repo" show-ref --verify --quiet "refs/remotes/$REMOTE_NAME/$BRANCH" || die "Remote branch '$REMOTE_NAME/$BRANCH' was not found during test."
|
||||
|
||||
say "Testing rebase in the dry-run clone..."
|
||||
|
||||
Reference in New Issue
Block a user