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

前端 未结 25 2151
失恋的感觉
失恋的感觉 2020-11-22 16:33

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

相关标签:
25条回答
  • 2020-11-22 17:12

    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
    
    0 讨论(0)
  • 2020-11-22 17:13

    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.

    0 讨论(0)
  • 2020-11-22 17:14

    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
    
    0 讨论(0)
  • 2020-11-22 17:14

    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
    
    0 讨论(0)
  • 2020-11-22 17:15

    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.

    0 讨论(0)
  • 2020-11-22 17:16

    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
    
    0 讨论(0)
提交回复
热议问题