I often have at least 3 remote branches: master, staging and production. I have 3 local branches that track those remote branches.
Updating all my local branches is
Here is a good answer: How to fetch all git branches
for remote in `git branch -r`; do git branch --track $remote; done
git pull --all
You can't do it with only one git command but you can automate it with one bash line.
To safely update all branches with one line, here is what I do:
git fetch --all && for branch in $(git branch | sed '/*/{$q;h;d};$G' | tr -d '*') ; do git checkout $branch && git merge --ff-only || break ; done
If it can't fast-forward one branch or encounter an error, it will stop and leave you in that branch so that you can take back control and merge manually.
If all branches can be fast-forwarded, it will end with the branch you were currently in, leaving you where you were before updating.
Explanations:
For a better readability, it can be split over several lines:
git fetch --all && \
for branch in $(git branch | sed '/*/{$q;h;d};$G' | tr -d '*')
do git checkout $branch && \
git merge --ff-only || break
done
git fetch --all && ...
=> Fetches all refs from all remotes and continue with the next command if there has been no error.
git branch | sed '/*/{$q;h;d};$G' | tr -d '*'
=> From the output of git branch
, sed
take the line with a *
and move it to the end (so that the current branch will be updated last). Then tr
simply remove the *
.
for branch in $(...) ; do git checkout $branch && git merge --ff-only || break ; done
= > For each branch name obtained from the previous command, checkout this branch and try to merge with a fast-forward. If it fails, break
is called and the command stops here.
Of course, you can replace git merge --ff-only
with git rebase
if it is what you want.
Finally, you can put it in your bashrc as an alias:
alias git-pull-all='git fetch --all && for branch in $(git branch | sed '\''/*/{$q;h;d};$G'\'' | tr -d "*") ; do git checkout $branch && git merge --ff-only || break ; done'
Or if you are afraid of messing up with the ' and ", or you simply prefer to keep syntactic readability in your editor, you can declare it as a function:
git-pull-all()
{
git fetch --all && for branch in $(git branch | sed '/*/{$q;h;d};$G' | tr -d '*') ; do git checkout $branch && git merge --ff-only || break ; done
}
Bonus:
For those who'd like the explanation on the sed '/*/{$q;h;d};$G'
part:
/*/
=> Search for the line with a *
.
{$q
=> If it is in the last line, quit (we don't need to do anything because the current branch is already the last one in the list).
;h;d}
=> Otherwise, store the line in the hold buffer and delete it in the current list position.
;$G
=> When it reaches the last line, append the content of the hold buffer.
If refs/heads/master can be fast-forwarded to refs/remotes/foo/master, the output of
git merge-base refs/heads/master refs/remotes/foo/master
should return the SHA1 id that refs/heads/master points to. With this, you can put together a script that automatically updates all local branches that have had no diverting commits applied to them.
This little shell script (I called it git-can-ff) illustrates how it can be done.
#!/bin/sh
set -x
usage() {
echo "usage: $(basename $0) <from-ref> <to-ref>" >&2
exit 2
}
[ $# -ne 2 ] && usage
FROM_REF=$1
TO_REF=$2
FROM_HASH=$(git show-ref --hash $FROM_REF)
TO_HASH=$(git show-ref --hash $TO_REF)
BASE_HASH=$(git merge-base $FROM_REF $TO_REF)
if [ "$BASE_HASH" = "$FROM_HASH" -o \
"$BASE_HASH" = "$FROM_REF" ]; then
exit 0
else
exit 1
fi
It can be done using below script... It will first fetch all branches and checkout one by one and update by itself.
#!/bin/bash
git branch -r | grep -v '\->' | while read remote; do git branch --track
"${remote#origin/}" "$remote"; done
set -x
CURRENT=`git rev-parse --abbrev-ref HEAD`
git fetch --all
branch_name=$(git branch | awk '{print $1" "}' | grep -v '*' | xargs)
for branch in $branch_name; do
git checkout "$branch" || exit 1
git rebase "origin/$branch" || exit 1
git pull origin $branch|| exit 1
done
git checkout "$CURRENT" || exit 1
git pull || exit 1
The behavior you describe for pull --all
is exactly as expected, though not necessarily useful. The option is passed along to git fetch, which then fetches all refs from all remotes, instead of just the needed one; pull
then merges (or in your case, rebases) the appropriate single branch.
If you want to check out other branches, you're going to have to check them out. And yes, merging (and rebasing) absolutely require a work tree, so they cannot be done without checking out the other branches. You could wrap up your described steps into a script/alias if you like, though I'd suggest joining the commands with &&
so that should one of them fail, it won't try to plow on.
There are plenty of acceptable answers here, but some of the plumbing may be be a little opaque to the uninitiated. Here's a much simpler example that can easily be customized:
$ cat ~/bin/git/git-update-all
#!/bin/bash
# Update all local branches, checking out each branch in succession.
# Eventually returns to the original branch. Use "-n" for dry-run.
git_update_all() {
local run br
br=$(git name-rev --name-only HEAD 2>/dev/null)
[ "$1" = "-n" ] && shift && run=echo
for x in $( git branch | cut -c3- ) ; do
$run git checkout $x && $run git pull --ff-only || return 2
done
[ ${#br} -gt 0 ] && $run git checkout "$br"
}
git_update_all "$@"
If you add ~/bin/git
to your PATH
(assuming the file is ~/bin/git/git-update-all
), you can just run:
$ git update-all