Files
MovePatchesFromToMahle/sync-init-import.sh
2026-02-21 16:44:57 +01:00

181 lines
7.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# sync-init-import.sh — Bootstrap a repository from scratch using an INIT bundle
# placed in the shared folder by sync-init-export.sh.
#
# Usage: ./sync-init-import.sh [<share-dir>] [<target-dir>]
#
# <share-dir> Shared folder containing the bundle (default: $SYNC_SHARE or ~/fifiletrans)
# <target-dir> Where to create the new repo (default: prompted interactively)
#
# After completion:
# - A fully functional git repo exists at <target-dir>
# - All branches and tags from the bundle are present
# - sync-push.sh and sync-pull.sh are copied into <target-dir>
# - Export checkpoints are set so the first sync-push is incremental
# - Both machines are now in sync
set -euo pipefail
# ── Config ────────────────────────────────────────────────────────────────────
SHARE_DIR="${1:-${SYNC_SHARE:-$HOME/fifiletrans}}"
TARGET_DIR="${2:-}"
# ── Sanity checks ─────────────────────────────────────────────────────────────
if [ ! -d "$SHARE_DIR" ]; then
echo "ERROR: Share directory not found: $SHARE_DIR" >&2
exit 1
fi
# ── Find INIT bundles ─────────────────────────────────────────────────────────
BUNDLES=()
while IFS= read -r f; do
BUNDLES+=("$f")
done < <(find "$SHARE_DIR" -maxdepth 1 -name "*-INIT-from-*.bundle" | sort)
if [ ${#BUNDLES[@]} -eq 0 ]; then
echo "ERROR: No INIT bundle found in $SHARE_DIR" >&2
echo " (looking for: *-INIT-from-*.bundle)" >&2
echo " Run sync-init-export.sh on the source machine first." >&2
exit 1
fi
# ── Let user pick a bundle ─────────────────────────────────────────────────────
echo "Available INIT bundles:"
echo ""
for i in "${!BUNDLES[@]}"; do
b="${BUNDLES[$i]}"
size=$(du -sh "$b" | cut -f1)
name=$(basename "$b")
echo " [$((i+1))] $name ($size)"
done
echo ""
DEFAULT=${#BUNDLES[@]}
read -rp "Pick bundle [1-${#BUNDLES[@]}, default $DEFAULT]: " CHOICE
CHOICE="${CHOICE:-$DEFAULT}"
if ! [[ "$CHOICE" =~ ^[0-9]+$ ]] || [ "$CHOICE" -lt 1 ] || [ "$CHOICE" -gt "${#BUNDLES[@]}" ]; then
echo "Invalid choice." >&2
exit 1
fi
BUNDLE_PATH="${BUNDLES[$((CHOICE-1))]}"
BUNDLE_NAME=$(basename "$BUNDLE_PATH")
# Derive a default target dir name from bundle name: <repo>-INIT-from-...
REPO_NAME=$(echo "$BUNDLE_NAME" | sed 's/-INIT-from-.*//')
echo ""
echo "Using bundle : $BUNDLE_NAME"
# ── Determine target directory ─────────────────────────────────────────────────
if [ -z "$TARGET_DIR" ]; then
read -rp "Target directory [$HOME/$REPO_NAME]: " TARGET_DIR
TARGET_DIR="${TARGET_DIR:-$HOME/$REPO_NAME}"
fi
TARGET_DIR="$(realpath -m "$TARGET_DIR")"
echo "Target dir : $TARGET_DIR"
echo ""
if [ -e "$TARGET_DIR" ]; then
echo "ERROR: Target already exists: $TARGET_DIR" >&2
echo " Remove it first or choose a different path." >&2
exit 1
fi
# ── Verify the bundle ──────────────────────────────────────────────────────────
echo "Verifying bundle..."
# INIT bundles are full — they have no prerequisites, so verify should always pass
git bundle verify "$BUNDLE_PATH"
echo ""
# ── List branches in bundle ────────────────────────────────────────────────────
echo "Branches in bundle:"
BUNDLE_BRANCHES=()
DEFAULT_BRANCH=""
while IFS= read -r line; do
ref=$(echo "$line" | awk '{print $2}')
if [[ "$ref" == refs/heads/* ]]; then
branch="${ref#refs/heads/}"
BUNDLE_BRANCHES+=("$branch")
echo " $branch"
# Prefer 'main', then 'master' as default checkout
if [ -z "$DEFAULT_BRANCH" ] || [ "$branch" = "main" ] || \
{ [ "$DEFAULT_BRANCH" != "main" ] && [ "$branch" = "master" ]; }; then
DEFAULT_BRANCH="$branch"
fi
fi
done < <(git bundle list-heads "$BUNDLE_PATH")
if [ ${#BUNDLE_BRANCHES[@]} -eq 0 ]; then
echo "ERROR: No branches found in bundle." >&2
exit 1
fi
echo ""
echo "Will check out: $DEFAULT_BRANCH"
echo ""
# ── Create and populate the repository ───────────────────────────────────────
echo "[1/4] Initializing repository at $TARGET_DIR..."
mkdir -p "$TARGET_DIR"
cd "$TARGET_DIR"
git init
git checkout -b "$DEFAULT_BRANCH" 2>/dev/null || true
echo ""
echo "[2/4] Fetching all branches and tags from bundle..."
git fetch "$BUNDLE_PATH" 'refs/heads/*:refs/heads/*' 'refs/tags/*:refs/tags/*'
# Check out the default branch (fetch leaves us detached or on empty branch)
git checkout "$DEFAULT_BRANCH"
# Report what we got
echo ""
echo " Branches:"
git branch | sed 's/^/ /'
echo " Tags:"
git tag | sed 's/^/ /' || echo " (none)"
# ── Copy sync scripts into the new repo ───────────────────────────────────────
echo ""
echo "[3/4] Installing sync scripts..."
SCRIPTS=("sync-push.sh" "sync-pull.sh")
for script in "${SCRIPTS[@]}"; do
src="${SHARE_DIR}/${script}"
if [ -f "$src" ]; then
cp "$src" "${TARGET_DIR}/${script}"
chmod +x "${TARGET_DIR}/${script}"
echo " Installed : $script"
else
echo " WARNING: $script not found in $SHARE_DIR — copy it manually." >&2
fi
done
# ── Set export checkpoints so first sync-push is incremental ──────────────────
echo ""
echo "[4/4] Setting sync checkpoints..."
for branch in "${BUNDLE_BRANCHES[@]}"; do
tip=$(git rev-parse "$branch")
git config "sync.exported.${branch}" "$tip"
echo " sync.exported.$branch = ${tip:0:8}..."
done
# ── Done ───────────────────────────────────────────────────────────────────────
echo ""
echo "══════════════════════════════════════════════════════"
echo " Repository ready at: $TARGET_DIR"
echo "══════════════════════════════════════════════════════"
echo ""
echo "Both machines are now in sync."
echo ""
echo "Ongoing workflow:"
echo " cd $TARGET_DIR"
echo " ./sync-push.sh # after committing — export changes"
echo " ./sync-pull.sh # to receive changes from the other machine"
echo ""
echo "Tip: verify everything looks correct:"
echo " cd $TARGET_DIR && git log --oneline --graph --all"