Files
2025-11-29 18:48:55 +08:00

233 lines
7.0 KiB
Bash
Executable File

#!/bin/bash
# Git Worktree Sync
# Keep worktree branch up-to-date with main branch
set -e
# Colors
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
CYAN='\033[0;36m'
GRAY='\033[0;90m'
NC='\033[0m' # No Color
# Default values
TARGET_WORKTREE=""
BASE_BRANCH="main"
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--base)
BASE_BRANCH="$2"
shift 2
;;
-h|--help)
echo "Usage: $0 [WORKTREE_PATH] [OPTIONS]"
echo ""
echo "Syncs a worktree with the latest changes from main (or specified base branch)."
echo ""
echo "Arguments:"
echo " WORKTREE_PATH Path to worktree (optional if run from within worktree)"
echo ""
echo "Options:"
echo " --base <branch> Base branch to sync from (default: main)"
echo " -h, --help Show this help"
echo ""
echo "Examples:"
echo " # From within a worktree"
echo " $0"
echo ""
echo " # Specify worktree path"
echo " $0 ../repo-feature-api"
echo ""
echo " # Sync from develop instead of main"
echo " $0 --base develop"
exit 0
;;
*)
TARGET_WORKTREE="$1"
shift
;;
esac
done
echo -e "${BLUE}╔════════════════════════════════════════════════╗${NC}"
echo -e "${BLUE}║ Git Worktree Sync Tool ║${NC}"
echo -e "${BLUE}╚════════════════════════════════════════════════╝${NC}"
echo ""
# Check if we're in a git repository
if ! git rev-parse --git-dir > /dev/null 2>&1; then
echo -e "${RED}✗ Error: Not in a git repository${NC}"
exit 1
fi
# Determine target worktree
if [ -z "$TARGET_WORKTREE" ]; then
# Use current directory
TARGET_WORKTREE=$(pwd)
echo -e "${CYAN}${NC} Using current directory"
else
# Use specified path
if [ ! -d "$TARGET_WORKTREE" ]; then
echo -e "${RED}✗ Error: Directory not found: $TARGET_WORKTREE${NC}"
exit 1
fi
cd "$TARGET_WORKTREE"
echo -e "${CYAN}${NC} Using specified worktree: $TARGET_WORKTREE"
fi
# Verify we're in a git worktree
if ! git rev-parse --git-dir > /dev/null 2>&1; then
echo -e "${RED}✗ Error: Not a git worktree: $TARGET_WORKTREE${NC}"
exit 1
fi
CURRENT_BRANCH=$(git branch --show-current)
REPO_ROOT=$(git rev-parse --show-toplevel)
REPO_NAME=$(basename "$REPO_ROOT")
echo -e "${GREEN}${NC} Repository: ${CYAN}$REPO_NAME${NC}"
echo -e "${GREEN}${NC} Current branch: ${CYAN}$CURRENT_BRANCH${NC}"
echo ""
# Check if current branch is the base branch
if [ "$CURRENT_BRANCH" = "$BASE_BRANCH" ]; then
echo -e "${YELLOW}${NC} You're on the base branch ($BASE_BRANCH)"
echo " Nothing to sync!"
exit 0
fi
# Check for uncommitted changes
if ! git diff-index --quiet HEAD --; then
echo -e "${YELLOW}⚠ Warning: You have uncommitted changes${NC}"
echo ""
git status --short
echo ""
read -p "Stash changes and continue? (y/n): " STASH_CHANGES
if [[ ! "$STASH_CHANGES" =~ ^[Yy]$ ]]; then
echo -e "${GRAY}Cancelled${NC}"
exit 1
fi
echo -e "${CYAN}${NC} Stashing changes..."
git stash push -m "Auto-stash before sync at $(date)"
STASHED=true
echo -e "${GREEN}${NC} Changes stashed"
echo ""
else
STASHED=false
fi
# Fetch latest
echo -e "${CYAN}${NC} Fetching latest from remote..."
git fetch origin
if [ $? -ne 0 ]; then
echo -e "${RED}✗ Failed to fetch from remote${NC}"
if [ "$STASHED" = true ]; then
echo -e "${CYAN}${NC} Restoring stashed changes..."
git stash pop
fi
exit 1
fi
echo -e "${GREEN}${NC} Fetched latest"
echo ""
# Check if base branch exists locally
if ! git show-ref --verify --quiet "refs/heads/$BASE_BRANCH"; then
echo -e "${RED}✗ Base branch '$BASE_BRANCH' does not exist locally${NC}"
if [ "$STASHED" = true ]; then
echo -e "${CYAN}${NC} Restoring stashed changes..."
git stash pop
fi
exit 1
fi
# Show what we're about to do
BEHIND_COUNT=$(git rev-list --count HEAD..origin/$BASE_BRANCH 2>/dev/null || echo "0")
if [ "$BEHIND_COUNT" = "0" ]; then
echo -e "${GREEN}${NC} Already up-to-date with $BASE_BRANCH!"
if [ "$STASHED" = true ]; then
echo ""
echo -e "${CYAN}${NC} Restoring stashed changes..."
git stash pop
echo -e "${GREEN}${NC} Changes restored"
fi
exit 0
fi
echo -e "${YELLOW}Your branch is $BEHIND_COUNT commit(s) behind $BASE_BRANCH${NC}"
echo ""
echo -e "${CYAN}Recent commits in $BASE_BRANCH:${NC}"
git log --oneline HEAD..origin/$BASE_BRANCH | head -5
echo ""
read -p "Merge $BASE_BRANCH into $CURRENT_BRANCH? (y/n): " CONFIRM_MERGE
if [[ ! "$CONFIRM_MERGE" =~ ^[Yy]$ ]]; then
echo -e "${GRAY}Cancelled${NC}"
if [ "$STASHED" = true ]; then
echo -e "${CYAN}${NC} Restoring stashed changes..."
git stash pop
fi
exit 0
fi
# Perform the merge
echo ""
echo -e "${CYAN}${NC} Merging $BASE_BRANCH into $CURRENT_BRANCH..."
if git merge origin/$BASE_BRANCH -m "Merge $BASE_BRANCH into $CURRENT_BRANCH"; then
echo -e "${GREEN}${NC} Merge successful!"
# Show summary
echo ""
echo -e "${BLUE}═══════════════════════════════════════════════${NC}"
echo -e "Branch: ${CYAN}$CURRENT_BRANCH${NC}"
echo -e "Status: ${GREEN}Synced with $BASE_BRANCH${NC}"
echo -e "${BLUE}═══════════════════════════════════════════════${NC}"
# Restore stashed changes if any
if [ "$STASHED" = true ]; then
echo ""
echo -e "${CYAN}${NC} Restoring stashed changes..."
if git stash pop; then
echo -e "${GREEN}${NC} Changes restored successfully"
else
echo -e "${RED}${NC} Conflicts while restoring stashed changes"
echo " Resolve conflicts manually, then run: git stash drop"
exit 1
fi
fi
echo ""
echo -e "${GREEN}✓ Sync complete!${NC}"
echo ""
else
# Merge failed (likely conflicts)
echo -e "${RED}✗ Merge conflicts detected${NC}"
echo ""
echo -e "${YELLOW}Conflicting files:${NC}"
git diff --name-only --diff-filter=U
echo ""
echo -e "${CYAN}To resolve:${NC}"
echo " 1. Fix conflicts in the files above"
echo " 2. Stage resolved files: ${YELLOW}git add <file>${NC}"
echo " 3. Complete the merge: ${YELLOW}git commit${NC}"
if [ "$STASHED" = true ]; then
echo ""
echo -e "${YELLOW}Note: Stashed changes will be restored after merge is complete${NC}"
echo " Run: ${YELLOW}git stash pop${NC}"
fi
exit 1
fi