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

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
查看更多
高级女魔头
3楼-- · 2019-01-01 10:02

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.

查看更多
与君花间醉酒
4楼-- · 2019-01-01 10:03

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
查看更多
浪荡孟婆
5楼-- · 2019-01-01 10:04

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!

查看更多
初与友歌
6楼-- · 2019-01-01 10:04

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
    }
查看更多
无色无味的生活
7楼-- · 2019-01-01 10:09

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