我通常至少有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”,然后更新(快进或合并)当前工作的分支,但不包括其他本地分支。

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


当前回答

这里有很多答案,但没有一个是使用git-fetch直接更新本地ref,这比检查分支简单得多,也比git-update-ref更安全。

这里我们使用git-fetch来更新非当前分支,而git pull——ff-only用于当前分支。它:

不需要检查分支机构 仅在可以快进的情况下更新分支 当它不能快进时会报告吗

就是这样:

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

从git-fetch的manpage:

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

通过指定git fetch <remote> <ref>:<ref>(没有任何+),我们得到一个只在本地ref可以快进时更新它的fetch。

注意,这假设本地分支和远程分支的名称相同(并且您希望跟踪所有分支),它实际上应该使用关于您拥有哪些本地分支以及它们被设置为跟踪的内容的信息。

其他回答

我使用hub的sync子命令来实现自动化。我在我的.bash_profile中有别名git=hub,所以我键入的命令是:

git sync

这将更新具有匹配上游分支的所有本地分支。从手册页:

如果本地分支已经过时,快进; 如果本地分支包含未推送的工作,请对此发出警告; 如果分支显示为已合并,且上游分支已被删除,则删除该分支。

它还处理在当前分支上存储/解存储未提交的更改。

我曾经使用过一个类似的工具,叫做git-up,但它不再被维护,git sync几乎做了完全相同的事情。

为了完成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命令,但是你可以用一个bash行自动化它。

为了用一行安全地更新所有分支,我是这样做的:

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

如果它不能快进一个分支或遇到错误,它将停止并将您留在该分支中,以便您可以收回控制权并手动合并。 如果所有分支都可以快进,它将以您当前所在的分支结束,使您停留在更新之前的位置。

解释:

为了更好的可读性,可以将它分成几行:

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

git fetch --all && ... => Fetches all refs from all remotes and continue with the next command if there has been no error. 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 *. 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.

当然,如果你想要的话,你可以用git rebase替换git merge——ff-only。

最后,你可以把它作为一个别名放在bashrc中:

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'

或者如果你害怕混淆' and ',或者你只是喜欢在编辑器中保持语法的可读性,你可以将它声明为一个函数:

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
}

奖金:

对于那些想要sed '/*/{$q;h;d};$G'部分的解释:

/*/ =>查找带*的行。 {$q =>如果它在最后一行,退出(我们不需要做任何事情,因为当前分支已经是列表中的最后一个)。 d} =>否则,将该行保存在保持缓冲区中,并将其删除在当前列表位置。 $G =>当它到达最后一行时,追加保持缓冲区的内容。

我也遇到过同样的问题……

想知道自己,我做了一个小的别名函数在我的.bashrc文件:

gitPullAll() {
    for branch in `git branch | sed -E 's/^\*/ /' | awk '{print $1}'`; do
        git checkout $branch
        git pull -p
        printf "\n"
    done
    echo "Done"
}

为我工作过

如果可能,以下一行代码将快速前进所有具有上游分支的分支,否则打印错误:

git branch \
  --format "%(if)%(upstream:short)%(then)git push . %(upstream:short):%(refname:short)%(end)" |
  sh

它是如何工作的?

它使用git分支命令的自定义格式。对于每个具有上游分支的分支,它打印一行具有以下模式的代码:

git push . <remote-ref>:<branch>

这可以直接通过管道传输到sh(假设分支名称是格式良好的)。忽略| sh,看看它在做什么。

警告

单行程序不会联系你的遥控器。在运行它之前,发出git fetch或git fetch。

当前签出的分支将不会用类似于

! [remote rejected] origin/master -> master (branch is currently checked out)

为此,您可以求助于常规的git pull -ff-only。

别名

将以下内容添加到你的.gitconfig中,以便git fft执行此命令:

[alias]
        fft = !sh -c 'git branch --format \"%(if)%(upstream:short)%(then)git push . %(upstream:short):%(refname:short)%(end)\" | sh' -

参见my .gitconfig。别名是“快进跟踪(分支)”的缩写。