In many projects, developers maintain multiple Git repositories—one for internal development and another for a client or a secondary system. Keeping these repositories in sync manually can be tedious and error-prone. In this blog, we’ll explore how to automate the synchronization of two different Git repositories using a simple Bash script.
Why Automate Git Sync?
Manually copying changes between repositories is inefficient and prone to mistakes. With an automated script, you can:
✅ Keep two repositories in sync effortlessly
✅ Ensure no files are missed during transfers
✅ Maintain version control history separately
✅ Backup data before syncing for added safety
Understanding the Script
The script follows a structured approach:
- Cloning Repositories: If the local directory does not contain a
.git
folder, it clones the repository. - Creating Backups: Before syncing, it saves backups of both repositories, keeping only the last two versions.
- Syncing Files: Uses
rsync
to mirror changes from the source to the target repository while excluding.git
. - Pushing Changes: The script commits the changes and pushes them to the client repository.
Key Commands Used
git clone
– Clones repositories if they don’t exist locally.rsync -av --delete
– Syncs files between repositories while deleting outdated ones.git pull --rebase
– Ensures an up-to-date local branch before pushing changes.git stash
– Saves uncommitted changes temporarily to prevent conflicts.
Automating the Process
This script can be added as a cron job to run at scheduled intervals, ensuring seamless synchronization.
#!/bin/bash
set -e # Exit on error
set -x # Enable verbose mode to show command execution
# Variables
YOUR_GIT_URL="git@gitlab.com:nodejs2483017/tasks.git"
CLIENT_GIT_URL="git@github.com:devprojects2023/nodejs-taskapp.git"
YOUR_LOCAL_DIR="/home/dev/Music/LocalGit"
CLIENT_LOCAL_DIR="/home/dev/Music/ClientGit"
VERBOSE_MODE=true # Set to true for more detailed output
YOUR_BRANCH="main" # Your source branch (e.g., main, develop)
CLIENT_BRANCH="main" # Client's target branch (e.g., main, staging)
BACKUP_DIR="/home/dev/Music/backup"
# Function to clone or update the source repository
clone_or_update_source_repo() {
local repo_url=$1
local local_dir=$2
local branch=$3
if [ ! -d "$local_dir/.git" ]; then
echo "Cloning $repo_url to $local_dir..."
rm -rf "$local_dir"
git clone -b "$branch" "$repo_url" "$local_dir"
else
echo "Updating existing repository in $local_dir..."
cd "$local_dir" || exit
git fetch origin
git checkout "$branch"
git pull --rebase origin "$branch"
fi
}
# Function to clone the client repository (handle missing branch or empty repo)
clone_client_repo() {
local repo_url=$1
local local_dir=$2
local branch=$3
echo "Cloning $repo_url to $local_dir (branch: $branch)..."
# Always remove and reclone to ensure a clean state
rm -rf "$local_dir"
# Try to clone the specified branch
if git clone -b "$branch" "$repo_url" "$local_dir" 2>/dev/null; then
echo "Successfully cloned branch $branch."
else
echo "Branch $branch not found. Cloning default branch or initializing..."
# Clone the repository without specifying a branch (gets default branch)
git clone "$repo_url" "$local_dir"
cd "$local_dir" || exit
# Check if any branches exist
if [ -z "$(git branch)" ]; then
echo "No branches found. Initializing empty repository..."
git checkout --orphan "$branch"
git commit --allow-empty -m "Initial commit"
git push origin "$branch"
else
# Use the default branch and create the target branch if needed
git checkout -b "$branch"
git push origin "$branch"
fi
fi
}
# Function to create backups (keeps last 2)
backup_repos() {
echo "Creating backups..."
mkdir -p "$BACKUP_DIR"
local timestamp
timestamp=$(date '+%Y-%m-%d_%H-%M-%S')
tar -czf "$BACKUP_DIR/your_repo_backup_$timestamp.tar.gz" -C "$(dirname "$YOUR_LOCAL_DIR")" "$(basename "$YOUR_LOCAL_DIR")"
tar -czf "$BACKUP_DIR/client_repo_backup_$timestamp.tar.gz" -C "$(dirname "$CLIENT_LOCAL_DIR")" "$(basename "$CLIENT_LOCAL_DIR")"
# Keep only the last 2 backups
ls -t "$BACKUP_DIR"/*.tar.gz | tail -n +3 | xargs rm -f 2>/dev/null
}
# Function to sync repositories
sync_repos() {
echo "Syncing $YOUR_LOCAL_DIR to $CLIENT_LOCAL_DIR..."
# Sync files, excluding the .git folder in CLIENT_LOCAL_DIR to preserve Git history
rsync -av --delete "$YOUR_LOCAL_DIR/" "$CLIENT_LOCAL_DIR/" --exclude ".git"
}
# Function to update client repository
push_to_client() {
echo "Pushing changes to client repository..."
cd "$CLIENT_LOCAL_DIR" || exit
if [ ! -d ".git" ]; then
echo "Error: .git directory not found in $CLIENT_LOCAL_DIR. Cannot push changes."
exit 1
fi
git checkout "$CLIENT_BRANCH" || exit
git add .
# Check if there are changes to commit
if git diff-index --quiet HEAD --; then
echo "No changes to commit."
else
git commit -m "Sync from source repository" || true
git push origin "$CLIENT_BRANCH" || exit
fi
}
# Main execution
clone_or_update_source_repo "$YOUR_GIT_URL" "$YOUR_LOCAL_DIR" "$YOUR_BRANCH"
clone_client_repo "$CLIENT_GIT_URL" "$CLIENT_LOCAL_DIR" "$CLIENT_BRANCH"
backup_repos
sync_repos
push_to_client
echo "Sync and push completed successfully."