535

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.

James Wierzba
  • 14,715
  • 12
  • 69
  • 110
mpoisot
  • 7,141
  • 4
  • 26
  • 21
  • 9
    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... – Jakub Narębski Mar 07 '09 at 00:13
  • 3
    Sure, I'd happily settle for fast-forward case. – Norman Ramsey Mar 07 '09 at 05:09
  • 41
    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 http://stackoverflow.com/questions/179123/edit-an-incorrect-commit-message-in-git and all the others. Wow. – Luke Puplett Nov 21 '14 at 13:46
  • Here is also a good answer http://stackoverflow.com/a/10312587/1254718 – milkovsky Jan 23 '15 at 14:26
  • 25
    @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. – Samuel May 21 '15 at 22:43
  • 19
    @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 in git?". That indicates that git is either badly designed, poorly documented, unintuitive, or all three. – Ian Kemp Sep 10 '15 at 14:08
  • 33
    @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. – danShumway Nov 17 '15 at 17:37
  • 5
    Lol... Yet another instance of Git taking a simple task and making it difficult. – jww May 21 '16 at 00:58
  • 1
    I know this is an old question, OP, but would you consider un-accepting John's answer and accepting a different one? The accepted answer makes opinionated (and, per the comments, potentially harmful) claims about pull versus rebase that have nothing to do with the question, and it recommends using a software package that's no longer maintained and, apparently, only works on Linux. – Kyle Strand Nov 16 '16 at 15:03
  • @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? – mpoisot Dec 05 '16 at 18:42
  • 1
    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. – Kyle Strand Dec 05 '16 at 20:26
  • 2
    Best answer if you have many branches: `rm -rf repo` then `git clone` – William Entriken Aug 10 '18 at 15:49
  • @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. – Raphael Sep 12 '18 at 08:56
  • Please consider https://github.com/changyuheng/git-fast-forward-all – changyuheng Oct 14 '19 at 07:52

27 Answers27

227

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.

John
  • 27,926
  • 10
  • 75
  • 78
  • 2
    The issue with this script is that it doesn't fetch newly created remote branches. – marcamillion Jul 19 '13 at 20:47
  • How might I install that on OSX? I looked at the GitHub code, and it looks very useful. – Steve Warren Aug 02 '13 at 15:16
  • @SteveWarren Try `sudo gem install git-up` instead. Also you might need Command Line Tools installed. See http://stackoverflow.com/questions/7454361/rubygems-do-not-install-on-os-x-lion – Petr Peller Sep 05 '13 at 13:48
  • Excellent. Another benefit is it automatically stashes and unstashes. – Steve Bennett Mar 27 '15 at 02:40
  • so @TrentonD.Adams what;s the recommended course of action to take? – sirvon Jun 04 '15 at 18:19
  • For this specific problem, I don't really have a recommendation. I haven't had much of a problem, because I keep my remote branches pushed up to the repo automatically with push.matching. If someone updates one, and I do a fetch, that branch is auto-updated, as it was already in sync. The only time that I've seen that not be the case, is if I've made changes and haven't pushed them, or I've got the branch in question checked out. – Trenton D. Adams Jun 05 '15 at 20:33
  • 6
    @TrentonD.Adams commit dates and author dates are different concepts. A rebase will change the commit date but not the author date (except in conflicts, where author date changes as well). Author date reflects when commit's tree was authored and should not change during a unconflicted rebase. The commit date changes because rebase always creates a new commit. So commit dates will always be in the correct order. – Dev Jul 01 '15 at 14:19
  • 2
    @VioletGiraffe my preferred approach for windows: 1. Install git (I hope this is already done) 2. Install ruby (https://www.ruby-lang.org/) OR install ruby/rails using RailsInstaller (http://railsinstaller.org/en) 3. Open the git bash and run `gem install git-up` – HeyZiko Jul 09 '15 at 15:24
  • 16
    To turn off the automatic rebasing behavior of git-up, run `git config --global git-up.rebase.auto false`. – dwlz Jul 16 '15 at 21:41
  • 18
    @MaxYankov Rebasing shared history is generally to be avoided, there is nothing wrong with rebasing local commits during a pull. – Dev Jul 18 '15 at 17:33
  • 24
    Rebasing local commits is rewriting history and making it simpler than it actually is. With rebase, you may find yourself with code that merged automatically but doesn't compile, or worse, compiles but doesn't work. Merging acknowledges the way you worked: you implemented the changes and tested them before incorporating other people's changes, and merge commit is a very useful point: that's the place where you make sure that different chagesets play nicely together. Rebasing makes it look like this process never happenes, which is simply not true and is a very dangerous practice. – Max Yankov Jul 19 '15 at 11:25
  • Lost all my unstaged modifications with "git-up". – Jānis Elmeris Nov 04 '15 at 11:53
  • 4
    Please note this software is no longer being maintained. See warning section [link](http://aanandprasad.com/git-up/) for more information. – rodly Apr 13 '16 at 01:18
  • 4
    I think there is a misunderstanding of two different rebase types. 1) When you do git rebase 2) When you do git pull --rebase This is totally different processes, the article about "Rebase Considered Harmful" posted by @MaxYankov is about git rebase but most of guys in this thread not talking about this. We are talking about rebase during pull. Sorry for bad english :( – Dmitry Chirkin Apr 14 '16 at 08:43
  • 1
    As even the linked article says, "For long term changes, it is probably best to merge, but for smaller changesets history will stay cleaner with rebase" – Andy Jul 26 '16 at 16:10
  • 7
    Please note the following description: "This project is no longer maintained, for several reasons: I've stopped using the workflow that made it relevant to me. Git 2.0 updated the default behaviour to remove the main problem it was solving (by changing the default behaviour of `git push` so it acts only on the current branch, instead of all branches). As of Git 2.9, `git pull --rebase --autostash` does basically the same thing." – Martin van Driel Sep 05 '16 at 14:21
  • 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. – mariotti Oct 29 '16 at 20:14
  • 2
    Old, but let me toss in my preference for rebase over merge. Single-line history is easier to think about and write scripts for. As far as "harmful" / etc... the linux kernel (which git was developed to help build) heavily uses emailed patches, which contain no merge history. I think it's not as clear-cut right/wrong as many make it out to be. http://nickdesaulniers.github.io/blog/2017/05/16/submitting-your-first-patch-to-the-linux-kernel-and-responding-to-feedback/ – Groxx Aug 23 '17 at 19:27
  • 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... – rob74 Jul 29 '20 at 10:12
209

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.

Cascabel
  • 451,903
  • 67
  • 363
  • 314
  • 2
    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 -a – mariotti Oct 29 '16 at 20:17
  • @mariotti 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. – Cascabel Oct 29 '16 at 21:46
  • 1
    Or @Jefromi .. give an example. I actually did agree with you. – mariotti Nov 02 '16 at 23:47
  • 3
    @mariotti 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.) – Cascabel Nov 02 '16 at 23:58
  • 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. – mariotti Nov 03 '16 at 00:21
  • Tecnically it is like: can I cook spagetti, Yes! Just order them from the git-up. – mariotti Nov 03 '16 at 00:26
  • @mariotti 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? – Cascabel Nov 03 '16 at 00:53
  • How is it possible that merging and rebasing absolutely require a worktree when there are bare repositories? – Felix Dombek Aug 27 '19 at 13:14
  • 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. – Cascabel Aug 27 '19 at 17:00
  • The answer to a yes/no question should contains "yes" or "no". – Dinh Tran Apr 09 '21 at 07:56
50

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 $@
muhqu
  • 11,711
  • 6
  • 27
  • 29
  • 1
    Thank you for this script. Is it possible that someone can convert that script to windows batch ? – Saariko Aug 02 '12 at 10:35
  • @Saariko 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) – muhqu Aug 03 '12 at 06:05
  • @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: https://github.com/muhqu/dotfiles – muhqu Feb 25 '14 at 09:17
  • @muhqu 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](http://screencast.com/t/1bAgsT2D). Why is master still 78 commits behind after I run your script? – BPL Nov 03 '16 at 19:18
  • 1
    @muhqu 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. – Radek Liska Feb 13 '19 at 13:24
  • @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! – muhqu Feb 13 '19 at 15:37
24

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
Fred Foo
  • 342,876
  • 71
  • 713
  • 819
  • 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. – Cascabel Nov 30 '10 at 21:06
  • @Jefromi: I had forgotten the `fetch`. Have edited; extra features/fixes whatever are up to the OP. – Fred Foo Nov 30 '10 at 22:18
  • 8
    I still think you may want to use `pull` (or check `branch..rebase`), so that you don't accidentally rebase a branch which is set up to pull normally (merge). – Cascabel Nov 30 '10 at 22:54
  • 1
    Consider using `set -e` instead of `|| exit 1` to make the interpreter exit on first error. – crishoj May 02 '18 at 11:32
19

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!

Matt Connolly
  • 9,608
  • 2
  • 61
  • 61
  • 1
    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! – Dave Knight Jun 13 '15 at 20:32
  • 6
    combined into one command: `git fetch origin other-branch:other-branch` – fabb Sep 22 '18 at 14:43
14

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.

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.

quornian
  • 8,558
  • 5
  • 25
  • 26
  • 1
    *"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. – jww May 21 '16 at 01:04
  • @jww 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. – Ville Nov 13 '17 at 19:52
  • 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 – Nate Cook Mar 19 '18 at 19:17
  • @jww 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. – binki May 11 '22 at 15:36
12

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

Jakub Narębski
  • 289,171
  • 61
  • 216
  • 230
  • 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.) – michael Jan 27 '17 at 13:24
  • 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". – michael Jan 27 '17 at 13:42
  • @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). – Jakub Narębski Jan 27 '17 at 22:07
  • 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. – eckes Jun 15 '17 at 23:50
9

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
michael
  • 7,805
  • 2
  • 45
  • 48
  • 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? – mrswadge Jan 15 '22 at 09:52
  • According to https://www.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 :) – mrswadge Jan 15 '22 at 10:03
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};
};
Mike
  • 9,522
  • 5
  • 42
  • 61
5

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
Community
  • 1
  • 1
milkovsky
  • 8,352
  • 4
  • 26
  • 31
5

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

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
    }
  • Can you provide equivalent Windows bat file? – Jaffy Jul 16 '15 at 07:09
  • 1
    @Jaffy 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](https://github.com/philosowaffle/NewDeviceSetup/blob/master/Windows/scripts/pullAll.bat), maybe others can step in and help? – philosowaffle Jul 16 '15 at 13:27
4

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.

Bouty
  • 174
  • 1
  • 5
  • 1
    You can avoid all the craziness of endless lines and ``&&`` by setting ``set -e`` in the top of the script. – mcepl Mar 19 '20 at 09:26
3

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]

Simon Hartcher
  • 3,025
  • 2
  • 27
  • 33
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
Community
  • 1
  • 1
aug
  • 10,442
  • 6
  • 70
  • 90
  • 1
    You don't need an alias. A simple git pull is enough, with the right config: https://stackoverflow.com/a/40067353/6309 – VonC Sep 05 '17 at 20:25
  • Great call out thanks @VonC I updated my answer :) also might submit a PR to the `git-up` documentation because they don't mention that – aug Sep 05 '17 at 21:03
  • This doesn't update all local branches at once, which is why I mainly used `git-up`. – ray Nov 02 '17 at 19:40
  • Documentation updated on [`git-up`](https://github.com/aanand/git-up/pull/120) :) – aug Nov 07 '17 at 23:12
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
Bruno De Fraine
  • 42,799
  • 8
  • 52
  • 64
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
Saran
  • 3,787
  • 3
  • 35
  • 57
2

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;
qwertzguy
  • 12,947
  • 7
  • 60
  • 63
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
Vikash Singh
  • 819
  • 8
  • 11
2

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

krlmlr
  • 23,618
  • 14
  • 112
  • 204
  • This is a nice solution, though I think I will be using [the `hub` soluction propsed by @John](https://stackoverflow.com/a/9781639/525036) for it's better output. – Didier L Jul 25 '19 at 13:00
  • 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? – BrandonLWhite May 19 '20 at 15:13
  • @BrandonLWhite: I don't understand the question. What do you expect from `git push`? – krlmlr May 19 '20 at 19:18
  • `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?! – BrandonLWhite May 19 '20 at 19:24
  • `git push` can also be used to update local branches, as long as this is a fast-forward update. – krlmlr May 20 '20 at 11:48
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
hillu
  • 9,145
  • 4
  • 23
  • 28
  • What do you imply by that comment? – hillu Mar 09 '09 at 07:37
  • 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. – Norman Ramsey Mar 10 '09 at 02:43
  • 2
    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. – Norman Ramsey Mar 22 '09 at 03:45
  • I personally found Tommi Virtanen's article "Git for computer scientists" quite helpful in getting familiar with git's model and terminology. – hillu Mar 22 '09 at 15:13
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.

Stimp
  • 519
  • 6
  • 10
  • 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](http://stackoverflow.com/help/promotion) for more information. Specifically **Don't tell - show!**; Tell us what parts of your script and how/why it solves the problem. – Keale Oct 15 '15 at 00:31
  • 1
    Yes I wrote it. I've included the source above for a quick copy-paste into your .bashrc or .zshrc. – Stimp Oct 16 '15 at 13:20
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.
changyuheng
  • 1,172
  • 2
  • 13
  • 25
  • 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. – hugomg Jun 09 '20 at 23:42
0

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.

Oliver
  • 41,265
  • 7
  • 89
  • 144
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
Lorenz Leutgeb
  • 434
  • 7
  • 11
-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.

nha
  • 16,863
  • 11
  • 86
  • 120
-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.

daixiang0
  • 153
  • 2
  • 10