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

前端 未结 25 2129
失恋的感觉
失恋的感觉 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:16

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

    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.

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

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

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

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

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

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