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
A slightly different script that only fast-forwards branches who's names matches their upstream branch. It also updates the current branch if fast-forward is possible.
Make sure all your branches' upstream branches are set correctly by running git branch -vv
. Set the upstream branch with git branch -u origin/yourbanchname
Copy-paste into a file and chmod 755:
#!/bin/sh
curbranch=$(git rev-parse --abbrev-ref HEAD)
for branch in $(git for-each-ref refs/heads --format="%(refname:short)"); do
upbranch=$(git config --get branch.$branch.merge | sed 's:refs/heads/::');
if [ "$branch" = "$upbranch" ]; then
if [ "$branch" = "$curbranch" ]; then
echo Fast forwarding current branch $curbranch
git merge --ff-only origin/$upbranch
else
echo Fast forwarding $branch with origin/$upbranch
git fetch . origin/$upbranch:$branch
fi
fi
done;
There are a lot of answers here but none that use git-fetch
to update the local ref directly, which is a lot simpler than checking out branches, and safer than git-update-ref
.
Here we use git-fetch
to update non-current branches and git pull --ff-only
for the current branch. It:
and here it is:
#!/bin/bash
currentbranchref="$(git symbolic-ref HEAD 2>&-)"
git branch -r | grep -v ' -> ' | while read remotebranch
do
# Split <remote>/<branch> into remote and branchref parts
remote="${remotebranch%%/*}"
branchref="refs/heads/${remotebranch#*/}"
if [ "$branchref" == "$currentbranchref" ]
then
echo "Updating current branch $branchref from $remote..."
git pull --ff-only
else
echo "Updating non-current ref $branchref from $remote..."
git fetch "$remote" "$branchref:$branchref"
fi
done
From the manpage for git-fetch
:
<refspec>
The format of a <refspec> parameter is an optional plus +, followed by the source ref <src>,
followed by a colon :, followed by the destination ref <dst>.
The remote ref that matches <src> is fetched, and if <dst> is not empty string, the local ref
that matches it is fast-forwarded using <src>. If the optional plus + is used, the local ref is
updated even if it does not result in a fast-forward update.
By specifying git fetch <remote> <ref>:<ref>
(without any +
) we get a fetch that updates the local ref only when it can be fast-forwarded.
Note: this assumes the local and remote branches are named the same (and that you want to track all branches), it should really use information about which local branches you have and what they are set up to track.
This issue is not solved (yet), at least not easily / without scripting: see this post on git mailing list by Junio C Hamano explaining situation and providing call for a simple solution.
The major reasoning is that you shouldn't need this:
With git that is not ancient (i.e. v1.5.0 or newer), there is no reason to have local "dev" that purely track the remote anymore. If you only want to go-look-and-see, you can check out the remote tracking branch directly on a detached HEAD with "
git checkout origin/dev
".Which means that the only cases we need to make it convenient for users are to handle these local branches that "track" remote ones when you do have local changes, or when you plan to have some.
If you do have local changes on "dev" that is marked to track the remove "dev", and if you are on a branch different from "dev", then we should not do anything after "
git fetch
" updates the remote tracking "dev". It won't fast forward anyway
The call for a solution was for an option or external script to prune local branches that follow now remote-tracking branches, rather than to keep them up-to-date by fast-forwarding, like original poster requested.
So how about "
git branch --prune --remote=<upstream>
" that iterates over local branches, and if(1) it is not the current branch; and
(2) it is marked to track some branch taken from the <upstream>; and
(3) it does not have any commits on its own;then remove that branch? "
git remote --prune-local-forks <upstream>
" is also fine; I do not care about which command implements the feature that much.
Note: as of git 2.10 no such solution exists. Note that the git remote prune
subcommand, and git fetch --prune
are about removing remote-tracking branch for branch that no longer exists on remote, not about removing local branch that tracks remote-tracking branch (for which remote-tracking branch is upstream branch).
Add this script to .profile
on Mac OS X:
# Usage:
# `git-pull-all` to pull all your local branches from origin
# `git-pull-all remote` to pull all your local branches from a named remote
function git-pull-all() {
START=$(git symbolic-ref --short -q HEAD);
for branch in $(git branch | sed 's/^.//'); do
git checkout $branch;
git pull ${1:-origin} $branch || break;
done;
git checkout $START;
};
function git-push-all() {
git push --all ${1:-origin};
};
To complete the answer by Matt Connolly, this is a safer way to update local branch references that can be fast-forwarded, without checking out the branch. It does not update branches that cannot be fast-forwarded (i.e. that have diverged), and it does not update the branch that is currently checked out (because then the working copy should be updated as well).
git fetch
head="$(git symbolic-ref HEAD)"
git for-each-ref --format="%(refname) %(upstream)" refs/heads | while read ref up; do
if [ -n "$up" -a "$ref" != "$head" ]; then
mine="$(git rev-parse "$ref")"
theirs="$(git rev-parse "$up")"
base="$(git merge-base "$ref" "$up")"
if [ "$mine" != "$theirs" -a "$mine" == "$base" ]; then
git update-ref "$ref" "$theirs"
fi
fi
done
I know this question is almost 3 years old, but I asked myself the very same question and did not found any ready made solution. So, I created a custom git command shell script my self.
Here it goes, the git-ffwd-update
script does the following...
git remote update
to fetch the lates revs git remote show
to get a list of local branches that track a remote branch (e.g. branches that can be used with git pull
)git rev-list --count <REMOTE_BRANCH>..<LOCAL_BRANCH>
how many commit the local branch is behind the remote (and ahead vice versa)git branch -f <LOCAL_BRANCH> -t <REMOTE_BRANCH>
the script can be called like:
$ git ffwd-update
Fetching origin
branch bigcouch was 10 commit(s) behind of origin/bigcouch. resetting local branch to remote
branch develop was 3 commit(s) behind of origin/develop. resetting local branch to remote
branch master is 6 commit(s) behind and 1 commit(s) ahead of origin/master. could not be fast-forwarded
The full script, should be saved as git-ffwd-update
and needs to be on the PATH
.
#!/bin/bash
main() {
REMOTES="$@";
if [ -z "$REMOTES" ]; then
REMOTES=$(git remote);
fi
REMOTES=$(echo "$REMOTES" | xargs -n1 echo)
CLB=$(git rev-parse --abbrev-ref HEAD);
echo "$REMOTES" | while read REMOTE; do
git remote update $REMOTE
git remote show $REMOTE -n \
| awk '/merges with remote/{print $5" "$1}' \
| while read RB LB; do
ARB="refs/remotes/$REMOTE/$RB";
ALB="refs/heads/$LB";
NBEHIND=$(( $(git rev-list --count $ALB..$ARB 2>/dev/null) +0));
NAHEAD=$(( $(git rev-list --count $ARB..$ALB 2>/dev/null) +0));
if [ "$NBEHIND" -gt 0 ]; then
if [ "$NAHEAD" -gt 0 ]; then
echo " branch $LB is $NBEHIND commit(s) behind and $NAHEAD commit(s) ahead of $REMOTE/$RB. could not be fast-forwarded";
elif [ "$LB" = "$CLB" ]; then
echo " branch $LB was $NBEHIND commit(s) behind of $REMOTE/$RB. fast-forward merge";
git merge -q $ARB;
else
echo " branch $LB was $NBEHIND commit(s) behind of $REMOTE/$RB. resetting local branch to remote";
git branch -f $LB -t $ARB >/dev/null;
fi
fi
done
done
}
main $@