Can "git pull --all" update all my local branches?
Asked Answered
S

30

581

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 tedious:

git fetch --all
git rebase origin/master
git checkout staging
git rebase origin/staging
git checkout production
git rebase origin/production

I'd love to be able to just do a "git pull -all", but I haven't been able to get it to work. It seems to do a "fetch --all", then updates (fast forward or merges) the current working branch, but not the other local branches.

I'm still stuck manually switching to each local branch and updating.

Sheehan answered 30/11, 2010 at 20:20 Comment(11)
Do you want automated updating of local tracking branches only in fast-forward case? Ypu should, becaue merge can have conflicst you would have to resolve...Pentapody
Assuming a conservative $300 in consultancy time to mess around with this, this single issue has cost companies $23,242,800 using a view count of 77,476. Now consider this question stackoverflow.com/questions/179123/… and all the others. Wow.Valorievalorization
Here is also a good answer https://mcmap.net/q/12257/-how-do-i-fetch-all-git-branchesDiaspora
@Luke You're the first person I've heard point out how time spent trying to make git do what we want costs companies money. These simple things should be automatic and should be so simple I don't have to open a browser to read the forums, IMO.Upspring
@LukePuplett There are nearly ~9 times as many questions on git on SO compared to Mercurial, and the majority of the former seem to be "how do I do <simple operation> in git?". That indicates that git is either badly designed, poorly documented, unintuitive, or all three.Bechuana
@IanKemp I'm not sure its safe to make that claim without knowing the demographics of SO. If Mercurial is not as commonly used here, or if its users use other forums to ask about it, I'd expect to see the same result. :) There are ~51 times as many questions on Javascript compared to Assembly - so it may not always be accurate to judge tools just by these kinds of metrics.Sargasso
Lol... Yet another instance of Git taking a simple task and making it difficult.Appetite
@KyleStrand thanks for pointing out that git-up is no longer maintained. I used it happily for many years but not anymore. Somehow I haven't run into this old problem. As for the other answers, none seem good enough to accept. Do you have a favorite?Sheehan
I think jefromi's is the most useful. The key point is that the behavior of merging all local branches requires checking them out individually; anyone who wants this behavior should be aware of the risks of automating such an extensive set of merges. The decisions of whether it's worth it to do the automation and how the automation should be done (e.g. should it only do the merge in fast-forward cases? How should conflicts be handled?) will probably be specific to the individual use-case.Yankeeism
Best answer if you have many branches: rm -rf repo then git cloneImprison
@IanKemp Based on my personal experience, a big issue is that people can't be bothered to read the documentation before being stuck in their first bad situation. Not sure if valiant efforts of creating "Git for Dummies" tutorials (with nicer names) are any help in this regard. Git, at its heart, is rather simple: a DAG of revisions, and three trees in a checkout. Finding the right combination of command and options to manipulate these simple structures is not as simple, unfortunately -- that's where the git help-->Google-->SO pipeline comes into play.Tamelatameless
I
220

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.

Irreverent answered 30/11, 2010 at 20:24 Comment(7)
If you give an example command line, I would vote up. I have this problem on github. I created a branch on the UI. Now I need my local to show the branch. git pull --all; git branch... argh... the command: git branch -aGeorgeta
@Georgeta Depends what you're trying to do, and it's not really clear from your comment. You might be best off asking a new question.Irreverent
@Georgeta The point of this answer is that the built-in commands do not actually do what the OP asked for, so the sequence of steps they had is necessary. It's possible to automate those steps (see for example John's answer) but they have to be done. So if what you're trying to do is exactly the same as the OP, there's not really an example to give, and if you're trying to do something different, then you should ask a new question - that's how StackOverflow works! (And your comment is unclear, but my best guess is that you want something different from the OP here, so yeah, new question.)Irreverent
Yes, somewhat something different. But your answer was just perfect for the context. And I might not need to ask anymore just because of your answer. Just: The accepted answer uses git-up, which is simply an interface to git command line (I assume). I was hoping you could make it explicit in few lines of git commands. The current answer is NOT git.Georgeta
@Georgeta The way you do it with vanilla git is what the OP already said, or some variation of it, so I'm not really sure I can add anything there. (If you have questions about details of that, then you have a different question from the OP here, so please ask a new question!) If you're asking for examples using git-up... why not ask the person who suggested using it?Irreverent
How is it possible that merging and rebasing absolutely require a worktree when there are bare repositories?Ailing
The point of this answer is that you can't perform those operations in a bare repository. You can of course perform them in a full repository with worktree, and push the results into a bare repository, though.Irreverent
E
234

I use the sync subcommand of hub to automate this. I have alias git=hub in my .bash_profile, so the command I type is:

git sync

This updates all local branches that have a matching upstream branch. From the man page:

  • If the local branch is outdated, fast-forward it;
  • If the local branch contains unpushed work, warn about it;
  • If the branch seems merged and its upstream branch was deleted, delete it.

It also handles stashing/unstashing uncommitted changes on the current branch.

I used to use a similar tool called git-up, but it's no longer maintained, and git sync does almost exactly the same thing.

Entablement answered 20/3, 2012 at 5:10 Comment(7)
The issue with this script is that it doesn't fetch newly created remote branches.Corundum
Excellent. Another benefit is it automatically stashes and unstashes.Purehearted
I down-voted it. It is not a "pure" git solution; I did not like this: "So git pull merges by default, when it should really rebase." as it is not really-should obvious. It doesn't give details about git-up and git interaction. It looks like a just run it, it works, do not ask why.Georgeta
For people like me who might be confused by the caption "use GitHub from the command line" on the hub homepage: it works with other git repos as well, not only with GitHub. Thanks for the tip, not sure if the alias is a good idea however: with the alias you will eventually forget that you need hub for some of the subcommands and get confused when they're not available on another system...Macro
The "hub" command has been abandoned. It hasn't had a release in over 2 years and a "fixed" bug is making the "hub sync" command more or less useless. Any time a tracked branch is deleted on the remote (which in some repositories happen every time a branch is merged) then "hub sync" crashes with no error message.Sumach
it does not work. It gives me the following mistake: "git: 'sync' is not a git command. See 'git --help' "Susquehanna
Question is about git. Not githubGoodloe
I
220

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.

Irreverent answered 30/11, 2010 at 20:24 Comment(7)
If you give an example command line, I would vote up. I have this problem on github. I created a branch on the UI. Now I need my local to show the branch. git pull --all; git branch... argh... the command: git branch -aGeorgeta
@Georgeta Depends what you're trying to do, and it's not really clear from your comment. You might be best off asking a new question.Irreverent
@Georgeta The point of this answer is that the built-in commands do not actually do what the OP asked for, so the sequence of steps they had is necessary. It's possible to automate those steps (see for example John's answer) but they have to be done. So if what you're trying to do is exactly the same as the OP, there's not really an example to give, and if you're trying to do something different, then you should ask a new question - that's how StackOverflow works! (And your comment is unclear, but my best guess is that you want something different from the OP here, so yeah, new question.)Irreverent
Yes, somewhat something different. But your answer was just perfect for the context. And I might not need to ask anymore just because of your answer. Just: The accepted answer uses git-up, which is simply an interface to git command line (I assume). I was hoping you could make it explicit in few lines of git commands. The current answer is NOT git.Georgeta
@Georgeta The way you do it with vanilla git is what the OP already said, or some variation of it, so I'm not really sure I can add anything there. (If you have questions about details of that, then you have a different question from the OP here, so please ask a new question!) If you're asking for examples using git-up... why not ask the person who suggested using it?Irreverent
How is it possible that merging and rebasing absolutely require a worktree when there are bare repositories?Ailing
The point of this answer is that you can't perform those operations in a bare repository. You can of course perform them in a full repository with worktree, and push the results into a bare repository, though.Irreverent
B
55

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...

  1. it issues a git remote update to fetch the lates revs
  2. then uses 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)
  3. then it checks with git rev-list --count <REMOTE_BRANCH>..<LOCAL_BRANCH> how many commit the local branch is behind the remote (and ahead vice versa)
  4. if the local branch is 1 or more commits ahead, it can NOT be fast-forwarded and needs to be merged or rebased by hand
  5. if the local branch is 0 commits ahead and 1 or more commits behind, it can be fast-forwarded by 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 $@
Brainsick answered 31/1, 2012 at 8:15 Comment(10)
Thank you for this script. Is it possible that someone can convert that script to windows batch ?Wanwand
@Wanwand why do you wan't to use git on a normal windows shell? If you use something like cygwin this script should just work fine... (though I have not tested it)Brainsick
@RyanWilcox thanks, I'm using it like every (work-)day... ;-) you might want to have a look at my dot-files for more git related scripts and aliases: github.com/muhqu/dotfilesBrainsick
@Brainsick I'm trying to use your script and I don't know why it worked the first time but it's not working "as expected" right now. For instance, take a look to this. Why is master still 78 commits behind after I run your script?Make
@Brainsick On newer git versions, -t and -l are not supposed to be used together within one git branch call. I removed the -l to change the call into git branch -f $LB -t $ARB >/dev/null; and now the script works as it should.Unbodied
@RadekLiska you're correct! Having -l in there, which lists branches, doesn't make any sense. Thanks for pointing this out, I'll update the answer!Brainsick
I think you could use git pull --ff-only $REMOTE $RB and git fetch $REMOTE $RB:$LB like this answer to rely on git's safety mechanisms: --ff-only to prevent merges from somehow occurring on CLB and fetch <refspec> to let fetch handle changing the branch to ensure we don't somehow go backwards. The script code already handles these cases, but it seems safer to also get git to double check our work.Vocoid
@Vocoid Haha, just realised I gave this answer over 10yrs ago and I still use this script for my daily git work… 😄 I never use git pull... just stick to what works for youBrainsick
I love the script but it seems to hang on the git remote update $REMOTE command if your connection is required to input a password to perform the fetch/update operation. is there any way to have that command handle user input correctly?Bromley
thank you, the command I was looking for was git branch -f <LOCAL_BRANCH> -t <REMOTE_BRANCH>. This way I don't have checkout every branch just to align it with the remote.Mallen
B
25

It's not so hard to automate:

#!/bin/sh
# Usage: fetchall.sh branch ...

set -x
git fetch --all
for branch in "$@"; do
    git checkout "$branch"      || exit 1
    git rebase "origin/$branch" || exit 1
done
Bary answered 30/11, 2010 at 20:37 Comment(4)
It's probably best not to use aliases in scripts. This also doesn't actually fetch anything, just rebases onto the already-fetched content. You should change git rebase origin/$branch to git pull, so that it will fetch from the appropriate tracking branch (presumably on origin) and either merge or rebase as determined by the config.Irreverent
@Jefromi: I had forgotten the fetch. Have edited; extra features/fixes whatever are up to the OP.Bary
I still think you may want to use pull (or check branch.<branch>.rebase), so that you don't accidentally rebase a branch which is set up to pull normally (merge).Irreverent
Consider using set -e instead of || exit 1 to make the interpreter exit on first error.Louvenialouver
M
22

This still isn't automatic, as I wish there was an option for - and there should be some checking to make sure that this can only happen for fast-forward updates (which is why manually doing a pull is far safer!!), but caveats aside you can:

git fetch origin
git update-ref refs/heads/other-branch origin/other-branch

to update the position of your local branch without having to check it out.

Note: you will be losing your current branch position and moving it to where the origin's branch is, which means that if you need to merge you will lose data!

Madi answered 14/6, 2011 at 2:37 Comment(3)
This is exactly the solution I was looking for. I don't usually have unpushed changes on multiple branches, and just want to update my various local branches to match the remote. This solution is much nicer than my usual delete/re-checkout method!Pudency
combined into one command: git fetch origin other-branch:other-branchXi
fatal: origin/other-branch: not a valid SHA1Word
S
19

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:

  • Doesn't require checking out branches
  • Updates branches only if they can be fast-forwarded
  • Will report when it can't fast-forward

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.

Beware that 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.

Strata answered 1/4, 2014 at 16:53 Comment(4)
"Updates branches only if they can be fast-forwarded" - what is the significance of fast-forward? If I want the latest sources in all my branches, then why should I care about fast forwarding or not? Its things like this that makes me laugh at Git and its Fanboi's. You can't do this in simply one command. Instead you need to perform c*n steps (instead of 1), where c is some number of repeated commands and n is the number of branches.Appetite
@Appetite It doesn't help to "laugh at Git and its Fanboi's" [sic] when it's the VCS most of the world uses. But I digress... I think in the context of this kind of a "global pull" script it is prudent to not attempt to make changes to non-current branches if they have merge conflicts.Valkyrie
This was helpful, thank you. Only thing I didn't like was that it created a branch locally for every remote branch (including ones in which I'm not interested), so I changed git branch -r | grep -v ' -> ' | while read remotebranch to git branch -r | grep -v ' -> ' | grep -f <(git branch | cut -c 3- | awk '{print "\\S*/"$0"$"}') | while read remotebranch to limit it to branches I already have locally. Also I added a git fetch --prune at the beginning to update the list of remote branches before doing anything, which avoids some warnings.Inexhaustible
@Appetite if you have unpushed changes in a branch, you probably do not want them to be lost and eventually garbage collected. To avoid losing commits, git pull will automatically run git merge instead of just overwriting the current branch to point to the incoming commits. If you don’t like automatic merges, you can specify fast-forward only so that you 1. do not have automatic, possibly unintentional, noisy merge commits from each time you issue git pull 2. so that you don’t remove and lose unpushed local-only commits.Sheetfed
C
14

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).

Cernuous answered 30/11, 2010 at 20:20 Comment(5)
Rather than only posting links, please post actual content, using links as references. That link is now dead. Too bad, sounded promising. (I realize this answer was from 2009, so this is just a note for future reference.)Progesterone
thanks (and wow, fast response after so many years). I now see that this thread is a "call for a simple solution" as opposed to my original misreading, "provides a simple solution".Progesterone
@michael_n: expanded... hmm, now I see that the post was not exactly about requested solution, but it was about the problem (assuming XY problem case).Pentapody
Hmm, peeking with detached head should be made easier, especially it should show useful information In status and alllow to fast forward the workspace with some feedback (like the pulled commits). Then it would be a replacement for readonly local branches.Telson
A more verbose description of the "no local dev" workflow: How to work with no local copies of any branches but your own.Vocoid
P
10

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
Progesterone answered 18/6, 2013 at 23:59 Comment(2)
Thanks for this, I tried on Git-Bash under Windows, but git complained saying git: 'update-all' is not a git command. See 'git --help'. Does anyone know the place to register the extension under Git-Bash?Dynamometry
According to atlassian.com/git/articles/extending-git anywhere on tha path will do. I created ~/bin and copied it there and now it appear to work, so thanks :)Dynamometry
D
7

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
Diaspora answered 23/1, 2015 at 14:27 Comment(4)
Why are you suggesting to do git fetch and git pull, instead of just git pull?Phagy
Thanks. It seems that pull fetches all branches from all remotes. Changed itDiaspora
This will fetch all remotes, but it will only merge the current branch. If you have 10 remotes you will need to manually checkout each one and merge.Sheehan
Doing this will create all remote branches locally with origin/ prefixKuibyshev
H
7

I came across the same issue of this question...

Wondering myself about it, I did a small alias function inside my .bashrc file:

gitPullAll() {
    for branch in `git branch | sed -E 's/^\*/ /' | awk '{print $1}'`; do
        git checkout $branch
        git pull -p
        printf "\n"
    done
    echo "Done"
}

Worked for me (:

Holstein answered 23/4, 2019 at 21:52 Comment(0)
S
5

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};
};
Sungod answered 16/7, 2013 at 13:48 Comment(1)
Shouldn't this stash all the changes first, and then restore them?Zita
M
5

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
  1. git fetch --all && ... => Fetches all refs from all remotes and continue with the next command if there has been no error.

  2. 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 *.

  3. 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.

Mathematics answered 17/1, 2020 at 19:49 Comment(3)
You can avoid all the craziness of endless lines and && by setting set -e in the top of the script.Lamp
Does not work on MacOS: sed: 1: "/*/{$q;h;d};$G": extra characters at the end of d commandAttribution
man git-pull says it calls merge FETCH_HEAD, why doesn't your script have FETCH_HEAD? I have a guess omitting FETCH_HEAD allows the script to merge from several remotes. P.S. good script, I've made mine on the basis of it (including FETCH_HEAD just in case).Dartmouth
S
4

A script I wrote for my GitBash. Accomplishes the following:

  • By default pulls from origin for all branches that are setup to track origin, allows you to specify a different remote if desired.
  • If your current branch is in a dirty state then it stashes your changes and will attempt to restore these changes at the end.
  • For each local branch that is set up to track a remote branch will:
    • git checkout branch
    • git pull origin
  • Finally, will return you to your original branch and restore state.

** I use this but have not tested thoroughly, use at own risk. See an example of this script in a .bash_alias file here.

    # Do a pull on all branches that are tracking a remote branches, will from origin by default.
    # If current branch is dirty, will stash changes and reply after pull.
    # Usage: pullall [remoteName]
    alias pullall=pullAll
    function pullAll (){
     # if -h then show help
     if [[ $1 == '-h' ]]
    then
      echo "Description: Pulls new changes from upstream on all branches that are tracking remotes."
      echo 
      echo "Usage: "
      echo "- Default: pullall"
      echo "- Specify upstream to pull from: pullall [upstreamName]"
      echo "- Help: pull-all -h"
    else

     # default remote to origin
     remote="origin"
     if [ $1 != "" ]
     then
       remote=$1
     fi

     # list all branches that are tracking remote
     # git branch -vv : list branches with their upstreams
     # grep origin : keep only items that have upstream of origin
     # sed "s/^.."... : remove leading *
     # sed "s/^"..... : remove leading white spaces
     # cut -d" "..... : cut on spaces, take first item
     # cut -d splits on space, -f1 grabs first item
     branches=($(git branch -vv | grep $remote | sed "s/^[ *]*//" | sed "s/^[ /t]*//" | cut -d" " -f1))

     # get starting branch name
     startingBranch=$(git rev-parse --abbrev-ref HEAD)

     # get starting stash size
     startingStashSize=$(git stash list | wc -l)

     echo "Saving starting branch state: $startingBranch"
     git stash

     # get the new stash size
     newStashSize=$(git stash list | wc -l)

     # for each branch in the array of remote tracking branches
     for branch in ${branches[*]}
     do
       echo "Switching to $branch"
       git checkout $branch

       echo "Pulling $remote"
       git pull $remote

     done

     echo "Switching back to $startingBranch"
     git checkout $startingBranch

     # compare before and after stash size to see if anything was stashed
     if [ "$startingStashSize" -lt "$newStashSize" ]
     then
       echo "Restoring branch state"
       git stash pop
     fi
    fi
    }
Scott answered 23/4, 2015 at 16:43 Comment(2)
Can you provide equivalent Windows bat file?Cecrops
@Cecrops Not sure how much time I have on my hands and I'm not super fluent in batch but I can give it a go. I'll post my progress here, maybe others can step in and help?Scott
B
4

If you're on Windows you can use PyGitUp which is a clone of git-up for Python. You can install it using pip with pip install --user git-up or through Scoop using scoop install git-up

[4]

Beedon answered 25/1, 2016 at 2:22 Comment(0)
B
3

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;
Bellerophon answered 27/6, 2014 at 12:21 Comment(0)
C
3

Just posting an updated answer. git-up is no longer maintained and if you read the documentation, they mention the functionality is now available in git.

As of Git 2.9, git pull --rebase --autostash does basically the same thing.

Accordingly, if you update to Git 2.9 or later, you can use this alias instead of installing git-up:

git config --global alias.up 'pull --rebase --autostash'

You can also set this for every git pull as of Git 2.9 as well (thanks @VonC please see his answer here)

git config --global pull.rebase true
git config --global rebase.autoStash true
Czarra answered 5/9, 2017 at 17:52 Comment(4)
You don't need an alias. A simple git pull is enough, with the right config: https://mcmap.net/q/13036/-gt-you-asked-to-pull-from-the-remote-39-prune-39Babism
Great call out thanks @Babism I updated my answer :) also might submit a PR to the git-up documentation because they don't mention thatCzarra
This doesn't update all local branches at once, which is why I mainly used git-up.Lynnell
Documentation updated on git-up :)Czarra
A
3

The following one-liner fast-forwards all branches that have an upstream branch if possible, and prints an error otherwise:

git branch \
  --format "%(if)%(upstream:short)%(then)git push . %(upstream:short):%(refname:short)%(end)" |
  sh

How does it work?

It uses a custom format with the git branch command. For each branch that has an upstream branch, it prints a line with the following pattern:

git push . <remote-ref>:<branch>

This can be piped directly into sh (assuming that the branch names are well-formed). Omit the | sh to see what it's doing.

Caveats

The one-liner will not contact your remotes. Issue a git fetch or git fetch --all before running it.

The currently checked-out branch will not be updated with a message like

! [remote rejected] origin/master -> master (branch is currently checked out)

For this, you can resort to regular git pull --ff-only .

Alias

Add the following to your .gitconfig so that git fft performs this command:

[alias]
        fft = !sh -c 'git branch --format \"%(if)%(upstream:short)%(then)git push . %(upstream:short):%(refname:short)%(end)\" | sh' -

See also my .gitconfig. The alias is a shorthand to "fast-forward tracking (branches)".

Abortifacient answered 15/4, 2019 at 19:27 Comment(6)
This is a nice solution, though I think I will be using the hub soluction propsed by @John for it's better output.Tanganyika
This is fast, simple, and actually works! I'm baffled though by the git push as it is totally opposite of what you would expect. What's the secret?Alimentation
@BrandonLWhite: I don't understand the question. What do you expect from git push?Abortifacient
git push has upload semantics -- I have some commits locally I want to send upstream. git pull has download semantics -- I want to get some upstream remote commits into my local branch. Since we're talking about downloading new commits from the remote to local, git pull is the obvious choice. But no, this trick uses git push. How does git push result in pulling remote changes to my local branch?!Alimentation
git push can also be used to update local branches, as long as this is a fast-forward update.Abortifacient
@Alimentation The thing you're missing here is the "." in git push . ref:ref. git push . <remote-ref>:<local-ref> Translated into plain language it reads: In local working copy (that's the .) push the contents of <remote-ref> (i.e. the stuff that's in a remote repo), into (that's what the : is for) my <local-ref>Housewarming
S
2

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
Sang answered 8/3, 2009 at 22:26 Comment(4)
What do you imply by that comment?Sang
I myself am not capable of writing the script hillu suggests, and I am not confident enough of my git knowledge to use git-merge-base.Kingcup
I'm afraid I don't understand the model well enough to exploit the script so kindly provided. It's enough to make a person want to switch to mercurcial.Kingcup
I personally found Tommi Virtanen's article "Git for computer scientists" quite helpful in getting familiar with git's model and terminology.Sang
D
2

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
Darceydarci answered 18/4, 2013 at 9:14 Comment(0)
W
2

The script from @larsmans, a bit improved:

#!/bin/sh

set -x
CURRENT=`git rev-parse --abbrev-ref HEAD`
git fetch --all
for branch in "$@"; do
  if ["$branch" -ne "$CURRENT"]; then
    git checkout "$branch" || exit 1
    git rebase "origin/$branch" || exit 1
  fi
done
git checkout "$CURRENT" || exit 1
git rebase "origin/$CURRENT" || exit 1

This, after it finishes, leaves working copy checked out from the same branch as it was before the script was called.

The git pull version:

#!/bin/sh

set -x
CURRENT=`git rev-parse --abbrev-ref HEAD`
git fetch --all
for branch in "$@"; do
  if ["$branch" -ne "$CURRENT"]; then
    git checkout "$branch" || exit 1
    git pull || exit 1
  fi
done
git checkout "$CURRENT" || exit 1
git pull || exit 1
Witham answered 4/2, 2014 at 8:22 Comment(0)
K
2

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
Keener answered 16/1, 2018 at 11:36 Comment(1)
Add some explanation for scoring your answerPact
G
1

It looks like many others have contributed similar solutions, but I thought I'd share what I came up with and invite others to contribute. This solution has a nice colorful output, gracefully handles your current working directory, and is fast because it doesn't do any checkouts, and leaves your working directory in tact. Also, it is just a shell script with no dependencies other than git. (only tested on OSX so far)

#!/usr/bin/env bash

gitup(){    
RED='\033[33;31m'
YELLO='\033[33;33m'
GREEN='\033[33;32m'
NC='\033[0m' # No Color

HEAD=$(git rev-parse HEAD)
CHANGED=$(git status --porcelain | wc -l)

echo "Fetching..."
git fetch --all --prune &>/dev/null
for branch in `git for-each-ref --format='%(refname:short)' refs/heads`; do

    LOCAL=$(git rev-parse --quiet --verify $branch)
    if [ "$HEAD" = "$LOCAL" ] && [ $CHANGED -gt 0 ]; then
        echo -e "${YELLO}WORKING${NC}\t\t$branch"
    elif git rev-parse --verify --quiet $branch@{u}&>/dev/null; then
        REMOTE=$(git rev-parse --quiet --verify $branch@{u})
        BASE=$(git merge-base $branch $branch@{u})

        if [ "$LOCAL" = "$REMOTE" ]; then
           echo -e "${GREEN}OK${NC}\t\t$branch" 
        elif [ "$LOCAL" = "$BASE" ]; then
            if [ "$HEAD" = "$LOCAL" ]; then
                git merge $REMOTE&>/dev/null
            else
                git branch -f $branch $REMOTE
            fi
            echo -e "${GREEN}UPDATED${NC}\t\t$branch"
        elif [ "$REMOTE" = "$BASE" ]; then
            echo -e "${RED}AHEAD${NC}\t\t$branch"
        else
            echo -e "${RED}DIVERGED${NC}\t\t$branch"
        fi
    else
        echo -e "${RED}NO REMOTE${NC}\t$branch"
    fi
done
}

https://github.com/davestimpert/gitup

Sorry I also seem to have come up with the same name as the other tool above.

Gainer answered 15/10, 2015 at 0:2 Comment(2)
Are you the one what wrote this? If so, please disclose your affiliation i.e. tell us how you are related to it. Please read more on this for more information. Specifically Don't tell - show!; Tell us what parts of your script and how/why it solves the problem.Amah
Yes I wrote it. I've included the source above for a quick copy-paste into your .bashrc or .zshrc.Gainer
Q
1

Don't know if this is okay, but if i'd like to fast-forward multiple branches I usually call

git pull origin master staging production

Also if I'd like to push multiple branches I'm going to call

git push origin master staging production

But both only work if all mentioned branches don't need any kind of merge.

Quadriplegic answered 16/11, 2021 at 13:24 Comment(0)
S
1

I use the following alias:

[alias]
sync = !git pull --prune --ff-only && git fetch . $(git for-each-ref --format='%(push):%(refname)' refs/heads --exclude="refs/heads/$(git rev-parse --abbrev-ref HEAD)")

This alias works both on Unix-es and Windows. It just runs the following two commands:

git pull --ff-only
git fetch . $(git for-each-ref --format='%(push):%(refname)' refs/heads --exclude="refs/heads/$(git rev-parse --abbrev-ref HEAD)")
  • The first command updates all the remote branches, and updates the current branch if the update is a fast-forward.
  • The second fetch command allows to update all existing local branches to their remote counterpart if they have one and the update is a fast-forward. It operates purely locally since the period . tells git to fetch from the current repository itself. See the documentation.
    • The git for-each-ref --format="%(push):%(refname)" refs/heads produces a line with a <refspec> for each existing local branch, thus telling git fetch to update that local branch using the local copy of the remote branch it pushes to. For instance, the <refspec> produced for the master branch is the following:
      • refs/remotes/origin/master:refs/heads/master
    • The --exclude="refs/heads/$(git rev-parse --abbrev-ref HEAD)" is used to exclude the current branch from being selected by git for-each-ref, so that git fetch does not report that it cannot update that branch which is currently checked-out.

This alias may not work with git versions prior to 2.42 In particular, I've tested git 2.36.1 and wasn't able to make it work.


Old answer

I use the following alias:

[alias]
sync = !git fetch && git fetch -u . $(git for-each-ref --format='%(push):%(refname)' refs/heads)

This alias works both on Unix-es and Windows. It just runs the following two commands:

git fetch
git fetch -u . $(git for-each-ref --format="%(push):%(refname)" refs/heads)
  • The first fetch command updates the remote branches.
  • The second fetch command allows to update all existing local branches to their remote counterpart if they have one and the update is a fast-forward. The second command operates purely locally since the period . tells git to fetch from the current repository itself.
    • The -u (--update-head-ok) tells git that it is fine to fast-forward the current branch.
      • Please note that git will not update the files in your working directory, so all file changes that have happened in your branch will be negatively displayed as staged changes. You'll thus have to unstage them and to discard the changes in your repository.
Satterlee answered 16/5, 2023 at 23:53 Comment(0)
E
0

Can “git pull --all” update all my local branches?

No it cannot. For fast-forwarding, I just wrote a small tool to do so. https://github.com/changyuheng/git-fast-forward-all

Advantages of this tool:

  1. Supports multiple remotes in one repository. (hub sync doesn't support multiple remotes at the moment.)
  2. Supports having different names on the local branch and the corresponding remote tracking branche.
  3. Much faster than other scripts that fetches remote for every single branch.
  4. No error-prone regex parsing/editing.
Exscind answered 14/10, 2019 at 7:51 Comment(1)
You can avoid hitting the network by using git fetch . refspec. The . says to fetch from the current repository instead of from the remote one.Lemus
N
0

None of the above answers considers the possibility that there might be multiple worktrees. Updating branches with git update-ref or git branch -f that are currently checked out in other worktrees will have unintended side-effects.

Consider my solution that handles worktrees:

#! /usr/bin/env bash
set -euo pipefail

# Read the default remote from config, defaulting to "origin".
DEFAULT_REMOTE=$(git config --default origin --get clone.defaultRemoteName)

# Use first argument as remote name, fallback to default.
REMOTE=${1:-$DEFAULT_REMOTE}

# Resolve the rev that HEAD points at, so that we can give it
# a special treatment.
HEAD_REV=$(git rev-parse HEAD)

# Format that allows us to easily grep for local branches that are behind,
# and have an upstream at $REMOTE.
FORMAT="%(upstream:trackshort)%(upstream:remotename)|%(refname:short)"

# Get a list of revs that are checked out. We don't want to
# update refs that are pointing at them.
set +e
WORKTREE_REVS=$(git worktree list --porcelain | grep -Po "HEAD \K(.+)" | grep -v "$HEAD_REV")
set -e

git fetch $REMOTE

for BRANCH in $(git for-each-ref refs/heads --format="$FORMAT" | grep -Po "<$REMOTE\|\K(.+)")
do
    BRANCH_REV=$(git rev-parse $BRANCH)
    if [ "$BRANCH_REV" = "$HEAD_REV" ]
    then
        # This branch is currently checked out "here". Forward it carefully.
        set +e
        git merge --no-autostash --ff-only $BRANCH@{u}
        set -e
    elif grep -q "$BRANCH_REV" <<< "$WORKTREE_REVS"
    then
        # This branch is currently checked out by another. Leave it alone.
        echo "$BRANCH skipped, because it is checked out in another worktree. Use 'git worktree list' to diagnose."
    else
        # This branch is not checked out. Just update it!
        git update-ref refs/heads/$BRANCH $BRANCH@{u}
        echo "$BRANCH forwarded"
        fi
done
Nonresistant answered 3/2, 2022 at 18:30 Comment(0)
M
0

This command will help.

for branch in `git branch | sed 's/\*//g'`; do git checkout $branch && git rebase origin/$branch; done

for branch in `git branch | sed 's/\*//g'`; do git checkout $branch && git reset --hard origin/$branch; done

for branch in `git branch | sed 's/\*//g'`; do git checkout $branch && git reset --hard origin/$branch && git pull; done
Mcdougal answered 6/2, 2023 at 6:27 Comment(0)
C
0

I build this alias that need one more step to be perfect, but could help someone:

sync = !"stashed=\"$( git stash )\" && \
  for branch in main live next $( git branch --show-current ); \
  do git checkout $branch && git pull; \
  done && [ -z \"${stashed##Saved*}\" ] && git stash pop"

This alias:

  1. makes an autostash on the current branch.
  2. Checkout the listed branches in order and pull.
  3. Checkout the original branch and pull it also.
  4. Pop the stash if it was done (with a complex matching logic to work with ash).

Hope somebody find it useful.

Creativity answered 30/5, 2023 at 14:11 Comment(0)
R
-1

As of git 2.9:

git pull --rebase --autostash

See https://git-scm.com/docs/git-rebase

Automatically create a temporary stash before the operation begins, and apply it after the operation ends. This means that you can run rebase on a dirty worktree. However, use with care: the final stash application after a successful rebase might result in non-trivial conflicts.

Roo answered 30/9, 2016 at 9:14 Comment(0)
A
-1

In fact, with git version 1.8.3.1, it works:

[root@test test]# git br
* master
  release/0.1
  update
[root@test test]# git pull --rebase
remote: Enumerating objects: 9, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 9 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (9/9), done.
From http://xxx/scm/csdx/test-git
   d32ca6d..2caa393  release/0.1 -> origin/release/0.1
Current branch master is up to date.
[root@test test]# git --version
git version 1.8.3.1

In master branch, you can update all other branches. @Cascabel

I do not know which version break/fix it, in 2.17(which i use), it can work.

Accommodative answered 9/10, 2019 at 8:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.