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

2019-01-01 09:44发布

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.

标签: git
20条回答
高级女魔头
2楼-- · 2019-01-01 10:10

Note: Even though I posted my own solution, I would recommend using git-up, which is also the accepted answer.


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 -l -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. reseting local branch to remote
 branch develop was 3 commit(s) behind of origin/develop. reseting 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 branch -l|awk '/^\*/{print $2}');
  echo "$REMOTES" | while read REMOTE; do
    git remote update $REMOTE
    git remote show $REMOTE -n \
    | awk '/merges with remote/{print $5" "$1}' \
    | while read line; do
      RB=$(echo "$line"|cut -f1 -d" ");
      ARB="refs/remotes/$REMOTE/$RB";
      LB=$(echo "$line"|cut -f2 -d" ");
      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. reseting local branch to remote";
          git branch -l -f $LB -t $ARB >/dev/null;
        fi
      fi
    done
  done
}

main $@
查看更多
公子世无双
3楼-- · 2019-01-01 10: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
查看更多
ら面具成の殇う
4楼-- · 2019-01-01 10:10

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
查看更多
浅入江南
5楼-- · 2019-01-01 10:10

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};
};
查看更多
人气声优
6楼-- · 2019-01-01 10:10

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
查看更多
笑指拈花
7楼-- · 2019-01-01 10:11

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
查看更多
登录 后发表回答