#!/usr/bin/env bash # sync-pull.sh — Import a git bundle from the shared folder into the current repo. # # Usage: ./sync-pull.sh [] # Default share dir: $SYNC_SHARE or ~/fifiletrans # # The script lists available bundles for this repo, lets you choose one, # fetches all branches from it into refs/sync/incoming/, shows # what's new, and offers to merge or rebase each branch. set -euo pipefail # ── Config ──────────────────────────────────────────────────────────────────── SHARE_DIR="${1:-${SYNC_SHARE:-$HOME/fifiletrans}}" # ── Sanity checks ───────────────────────────────────────────────────────────── if ! git rev-parse --git-dir &>/dev/null; then echo "ERROR: Not inside a git repository." >&2 exit 1 fi if [ ! -d "$SHARE_DIR" ]; then echo "ERROR: Share directory not found: $SHARE_DIR" >&2 exit 1 fi REPO_ROOT=$(git rev-parse --show-toplevel) REPO_NAME=$(basename "$REPO_ROOT") # ── Find bundles for this repo ──────────────────────────────────────────────── BUNDLES=() while IFS= read -r f; do BUNDLES+=("$f") done < <(find "$SHARE_DIR" -maxdepth 1 -name "${REPO_NAME}-from-*.bundle" | sort) if [ ${#BUNDLES[@]} -eq 0 ]; then echo "No bundles found for repo '$REPO_NAME' in $SHARE_DIR" echo " (looking for: ${REPO_NAME}-from-*.bundle)" exit 0 fi # ── Let user pick a bundle ───────────────────────────────────────────────────── echo "Available bundles for '$REPO_NAME':" 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: newest bundle (last in sorted list) 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") echo "" echo "Using: $BUNDLE_NAME" # ── Verify the bundle ────────────────────────────────────────────────────────── echo "" echo "Verifying bundle..." if ! git bundle verify "$BUNDLE_PATH"; then echo "" echo "ERROR: Bundle verification failed." >&2 echo "This usually means the bundle is incremental and your repo is missing" >&2 echo "the prerequisite commits. Make sure you have applied earlier bundles first." >&2 exit 1 fi # ── List refs in bundle ──────────────────────────────────────────────────────── echo "" echo "Branches in bundle:" BUNDLE_REFS=() while IFS= read -r line; do ref=$(echo "$line" | awk '{print $2}') BUNDLE_REFS+=("$ref") branch="${ref#refs/heads/}" echo " $branch" done < <(git bundle list-heads "$BUNDLE_PATH" | grep 'refs/heads/') if [ ${#BUNDLE_REFS[@]} -eq 0 ]; then echo "No branch refs found in bundle." >&2 exit 1 fi # ── Fetch each branch into refs/sync/incoming/ ──────────────────────── echo "" echo "Fetching..." INCOMING_BRANCHES=() for ref in "${BUNDLE_REFS[@]}"; do branch="${ref#refs/heads/}" incoming_ref="refs/sync/incoming/${branch}" git fetch "$BUNDLE_PATH" "${ref}:${incoming_ref}" INCOMING_BRANCHES+=("$branch") echo " Fetched: $branch -> $incoming_ref" done # ── Show what's new per branch ──────────────────────────────────────────────── echo "" echo "── New commits ──────────────────────────────────────────────────────────" for branch in "${INCOMING_BRANCHES[@]}"; do incoming_ref="refs/sync/incoming/${branch}" if git rev-parse --verify "$branch" &>/dev/null; then # Branch exists locally ahead=$(git log --oneline "${branch}..${incoming_ref}" | wc -l) behind=$(git log --oneline "${incoming_ref}..${branch}" | wc -l) if [ "$ahead" -eq 0 ]; then echo " $branch: already up to date" continue fi echo " $branch: $ahead new commit(s) incoming, $behind local-only commit(s)" git log --oneline "${branch}..${incoming_ref}" | sed 's/^/ + /' if [ "$behind" -gt 0 ]; then echo " (your local commits not in bundle:)" git log --oneline "${incoming_ref}..${branch}" | sed 's/^/ * /' fi else n=$(git log --oneline "${incoming_ref}" | wc -l) echo " $branch: new branch with $n commit(s)" git log --oneline "${incoming_ref}" | head -10 | sed 's/^/ + /' fi done echo "" # ── Integrate each branch ───────────────────────────────────────────────────── CURRENT_BRANCH=$(git branch --show-current) for branch in "${INCOMING_BRANCHES[@]}"; do incoming_ref="refs/sync/incoming/${branch}" if ! git rev-parse --verify "$branch" &>/dev/null; then # New branch — just create it read -rp "Create new local branch '$branch' from bundle? [Y/n] " ans ans="${ans:-Y}" if [[ "$ans" =~ ^[Yy] ]]; then git branch "$branch" "$incoming_ref" echo " Created branch '$branch'" fi continue fi ahead=$(git log --oneline "${branch}..${incoming_ref}" | wc -l) if [ "$ahead" -eq 0 ]; then continue # Nothing to do fi behind=$(git log --oneline "${incoming_ref}..${branch}" | wc -l) echo "── Integrating: $branch ─────────────────────────────────────────────" if [ "$behind" -eq 0 ]; then # Fast-forward possible echo " Fast-forward possible." read -rp " Fast-forward '$branch'? [Y/n] " ans ans="${ans:-Y}" if [[ "$ans" =~ ^[Yy] ]]; then # Switch to branch if needed if [ "$CURRENT_BRANCH" != "$branch" ]; then git checkout "$branch" CURRENT_BRANCH="$branch" fi git merge --ff-only "$incoming_ref" echo " Done." fi else # Diverged — offer merge or rebase echo " Branches have diverged (you have $behind local commit(s))." echo " Options:" echo " [m] merge — creates a merge commit" echo " [r] rebase — replays your commits on top of incoming" echo " [s] skip — leave for manual handling" read -rp " Choice [m/r/s, default s]: " ans ans="${ans:-s}" case "$ans" in m|M) if [ "$CURRENT_BRANCH" != "$branch" ]; then git checkout "$branch" CURRENT_BRANCH="$branch" fi git merge "$incoming_ref" --no-edit echo " Merged." ;; r|R) if [ "$CURRENT_BRANCH" != "$branch" ]; then git checkout "$branch" CURRENT_BRANCH="$branch" fi git rebase "$incoming_ref" echo " Rebased." ;; *) echo " Skipped. Run manually:" echo " git checkout $branch && git merge refs/sync/incoming/$branch" ;; esac fi done echo "" echo "Import complete." echo "" echo "Tip: The fetched commits remain in refs/sync/incoming/ until" echo "the next sync-pull run. You can inspect them anytime with:" echo " git log refs/sync/incoming/"