214 lines
8.3 KiB
Bash
Executable File
214 lines
8.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# sync-pull.sh — Import a git bundle from the shared folder into the current repo.
|
|
#
|
|
# Usage: ./sync-pull.sh [<share-dir>]
|
|
# 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/<branch>, 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/<branch> ────────────────────────
|
|
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/<branch> until"
|
|
echo "the next sync-pull run. You can inspect them anytime with:"
|
|
echo " git log refs/sync/incoming/<branch>"
|