Daily Hack: Sync Every Git Repo Before You Start Work
A daily bash hack I run every morning to fetch and sync all my git repos at once, before code reviews and standup. One command, every branch current.
Six developers. Eight repos. You already have a sync problem.
Day one is one repo, one engineer. Day thirty is three services, four engineers, four feature branches per service, a code review every afternoon. Every time someone reviews a PR in one service, they need their local copy of that service current. They need the API service it calls current. They need the shared types library current. Same on the next PR. Same on the one after.
Multiply across a team of five committing daily across six repos and you get the real problem. It is not "scaling git." It is keeping enough things current that nobody is reviewing yesterday's code or testing against last week's API.
The gaming project that taught me this
A while back I worked on a multiplayer game project split the way most modern products are game-service for the core game loop, payment-service for in-app purchase, matchmaking-service, leaderboard-service, a web client, a mobile client. Each in its own repo, each deploying independently.
I made one decision early that paid off more than any architectural choice. Every repo for that project lived in a single folder on my machine: ~/game/. That folder structure is what made the next part possible. A single folder of sibling repos is the unit of work. Run a script at the folder level, it knows what to do across every project inside.
What I run every morning
The first thing I do every morning, before standup, before opening PRs, before opening anything else, is cd into the project folder and run one script: git_fetch_all.sh. By the time my coffee is cool enough to drink, every repo is fetched, every long-running branch is current, and the active branch in each repo has whatever changes my teammates pushed overnight.
For each .git directory inside the folder, the script does six things in order:
- Stashes any uncommitted work, including untracked files
- Runs
git fetch --prune, so deleted remote branches stop showing up locally - Pulls the active branch
- Always pulls
development,production,staging, even when they are not checked out, and creates them locally if they do not exist - Sweeps every other local branch with an upstream and tries to pull
- Restores the stash
One command. Every repo handled the same way. No tabs.
One fetch per repo, not one per branch
The first version of my script ran git fetch origin <branch> per pinned branch. Three pinned branches per repo, six repos, that was eighteen network calls. Eighteen "From github.com..." headers in the output. Eighteen places it could fail.
A single fetch with prune does the same job in one call per repo:
git fetch --prune origin || return 1
Everything after this line is local. The pinned-branch loop just calls git update-ref to advance refs without ever touching the working tree. The script went from feeling slow to feeling instant.
Stash before, pop after, always
This is the part nobody warns you about. git pull on a working tree with uncommitted changes is not safe. Modified files are usually fine. Untracked files are not.
Imagine you have a new file notes.md you have not added yet, and the upstream pull brings in its own notes.md. git silently overwrites yours. No conflict. No warning. Your file is gone.
The script handles this with git stash push -u:
local stashed=0
if [ -n "$(git status --porcelain)" ]; then
if git stash push -u -m "git_fetch_all auto-stash" > /dev/null 2>&1; then
stashed=1
fi
fi
do_repo_sync
local rc=$?
if [ "$stashed" = "1" ]; then
if git stash pop > /dev/null 2>&1; then
echo "restored stashed changes"
else
echo "stash pop had conflicts, your changes remain in 'git stash list'"
fi
fi
The -u flag is the important one. It pulls untracked files into the stash too. Without it, new files you have not committed are left where they are, and git happily moves things around them. With it, the script is safe to run on any tree, mid-feature or not.
If pop conflicts (rare, only when the pull moved a file you had modified), the stash entry is left in git stash list. Nothing is destroyed. You resolve it manually.
update-ref is the trick
development, production, staging. The branches I want at HEAD whether or not I have them checked out. development for daily syncing. production for hotfix branches. staging for QA reviews.
The naive way to keep them current is checkout, pull, checkout back. Three commands per branch, working tree shuffled twice, a chance of a merge prompt if local diverged.
The better way:
if git merge-base --is-ancestor "$local_sha" "$remote_sha"; then
git update-ref "refs/heads/$branch" "$remote_sha"
fi
git update-ref moves a branch ref forward without ever checking it out. The feature branch I am on stays the active one. development quietly advances underneath. No working tree shuffle. No merge prompts. Fast-forward only by design: if local diverged from remote, the is-ancestor check fails and the script leaves the branch alone.
Most developers never reach for update-ref. That is what makes the script feel surgical instead of clumsy.
Grab the script
Download it from GitHub: github.com/kapoorsahil/git-scripts.
or run
git clone https://github.com/kapoorsahil/git-scripts.git ~/git-scripts
chmod +x ~/git-scripts/git_fetch_all.sh
ln -s ~/git-scripts/git_fetch_all.sh /usr/local/bin/git-fetch-all
I will keep adding small productivity scripts to the same repo as I write them. A read-only status dashboard. A generic command-runner. A "my commits this week" aggregator for standups. If you have a script you want to add, or an improvement to this one, open a PR. I will read every one.
The smaller idea inside the bigger one
The script is a hundred lines of bash. The bash is not the interesting part.
What I want you to notice is the folder. One folder per project, every repo as a sibling. That single decision is what turned six independent repos into something I could operate on as a unit. Without it, I would still be running git pull six times, and the script would not exist.
Small organizational decisions compound. A project folder makes a sync script possible. A sync script makes a morning routine possible. A morning routine means I never sit down to review yesterday's code by accident.