我通常至少有3个远程分支:master、staging和production。我有3个本地分支来跟踪这些远程分支。

更新我所有的本地分支是乏味的:

git fetch --all
git rebase origin/master
git checkout staging
git rebase origin/staging
git checkout production
git rebase origin/production

我很想做一个“git pull -all”,但我还没能让它工作。它似乎做了一个“fetch -all”,然后更新(快进或合并)当前工作的分支,但不包括其他本地分支。

我仍然需要手动切换到每个本地分支并进行更新。


当前回答

为了完成Matt Connolly的回答,这是一种更安全的更新本地分支引用的方法,可以快进,而不签出分支。它不会更新不能快进的分支(即已经分离的分支),也不会更新当前签出的分支(因为这样工作副本也应该更新)。

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

其他回答

一个略有不同的脚本,它只快进与上游分支名称匹配的分支。如果可以快进,它还会更新当前分支。

通过运行git branch -vv,确保所有分支的上游分支都被正确设置。使用git branch -u origin/yourbanchname设置上游分支

复制粘贴到一个文件并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;

这个问题(目前)还没有解决,至少在没有脚本的情况下不容易解决:参见Junio C Hamano在git邮件列表上发布的这篇文章,解释了这种情况并提供了一个简单的解决方案。

主要的理由是你不需要这个:

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

解决方案需要一个选项或外部脚本,以修剪当前远程跟踪分支的本地分支,而不是像最初的海报所要求的那样,通过快进来保持它们的最新状态。

那么,“git branch -prune -remote=<upstream>”如何呢 本地分支,如果 (1)不是目前的分支机构;而且 (2)标记为从<上游>取的某个分支;而且 (三)自身无过错; 然后把那根树枝去掉?“git remote——prune-local-forks <upstream>”是 还好;我不关心哪个命令实现了这个特性 多。

注意:从git 2.10开始就没有这样的解决方案了。注意,git remote prune子命令和git fetch——prune是关于删除不再存在于远程上的分支的远程跟踪分支,而不是关于删除跟踪远程跟踪分支的本地分支(其中远程跟踪分支是上游分支)。

“git pull -all”可以更新我所有的本地分支吗?

不,它不能。对于快进,我只是编写了一个小工具。https://github.com/changyuheng/git-fast-forward-all

该工具的优点:

在一个存储库中支持多个远程。(集线器同步目前不支持多个遥控器。) 支持在本地分支和相应的远程跟踪分支上使用不同的名称。 比其他获取每个分支的远程脚本快得多。 没有容易出错的正则表达式解析/编辑。

如果refs/heads/master可以快进到refs/remotes/foo/master,则

git merge-base refs/heads/master refs/remotes/foo/master

应该返回refs/heads/master指向的SHA1 id。这样,您就可以将一个脚本组合在一起,自动更新没有应用转移提交的所有本地分支。

这个小shell脚本(我称之为git-can-ff)说明了如何做到这一点。

#!/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

以上答案都没有考虑存在多个工作树的可能性。使用git update-ref或git branch -f更新当前在其他工作树中签出的分支会产生意想不到的副作用。

考虑一下我处理工作树的解决方案:

#! /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