Walks every git repo in a directory and brings its base branch up to date — once a day, unattended. Built for a multi-repo (microservices) setup where feature branches go stale overnight.
Companion to the blog post: The 200-Line Bash Script That Rebases My Repos While I Sleep.
For each */ directory under REPOS_DIR that contains a .git:
- Detached HEAD → skipped (no branch to update).
- On the base branch →
git pull --rebase. - On a feature branch → fast-forwards the local base ref without checking out
(
git fetch origin base:base). If that's refused because the local base diverged, it falls back to stash → checkout base → rebase → checkout back → unstash.
Plus the unglamorous parts that make it safe to run while you sleep:
- Single-run lock via atomic
mkdir— concurrent runs abort instead of clobbering. - Network wait — polls
github.comand backs off before giving up. - Reversible fallback — restores your branch and stash on any failure.
- Tri-state exit — success / failure / "rebase OK but stash needs manual resolve" (
return 2). - macOS notification on completion, naming any repos that failed or need attention.
- Optional parallelism via
PARALLEL=N, with output replayed in original order.
- macOS (uses
osascriptfor notifications) git,curl- Bash 3.2+ (ships with macOS; Bash 4+ enables
wait -nthrottling)
chmod +x update-all-repos.sh
# Run against the folder the script lives in:
./update-all-repos.sh
# Or point it elsewhere:
REPOS_DIR=/Users/me/code ./update-all-repos.sh
# Update 4 repos at a time:
PARALLEL=4 REPOS_DIR=/Users/me/code ./update-all-repos.sh| Var | Default | Meaning |
|---|---|---|
REPOS_DIR |
script's own directory | Folder whose immediate subdirs are repos |
PARALLEL |
1 |
Concurrent repo updates |
LOG_DIR |
~/.cache/update-all-repos |
Where last-run.log / prev-run.log go |
- Edit
com.me.update-all-repos.plist— replace/Users/me/codewith your paths. - Install and load:
cp com.me.update-all-repos.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/com.me.update-all-repos.plistUnload with launchctl unload ~/Library/LaunchAgents/com.me.update-all-repos.plist.
Prefer cron? It works but is deprecated on macOS and won't fire if the laptop was asleep at the trigger minute:
0 8 * * * REPOS_DIR=/Users/me/code /Users/me/code/update-all-repos.sh~/.cache/update-all-repos/last-run.log— most recent run~/.cache/update-all-repos/prev-run.log— the run before it