Git - 在拉取时自动快进所有跟踪分支

43

我使用--track选项设置了跟踪分支,当我在master上执行git pull时,它会将所有分支提取到origin/branchname,但不会与本地跟踪分支合并。这更加恼人,因为如果稍后在master上执行git push,它会说在跟踪分支上拒绝了非快进更新,因为它们在初始的git pull中没有进行快进合并。

我的问题是:如何使git pull获取所有分支并自动快进所有跟踪分支?

注意:以前git pull可以快进所有与我的GitHub存储库相关的跟踪分支,但现在我使用Gitolite设置自己的存储库时,这个问题出现了。


你能展示一下你正在使用的确切命令吗?在你的master分支上执行git push应该会推送除了你的master分支之外的任何内容,如果你没有对master进行任何更改,那么你不应该尝试推送它。 - CB Bailey
1
我不确定你的工作流程是什么,但我的第一反应是,如果你没有在本地跟踪分支上进行任何工作(即可以快速转发的情况),则不需要创建本地跟踪分支;远程跟踪分支(origin/*)会自动更新,并且在任何情况下都可供参考。 - CB Bailey
@CharlesBailey 这是一个例子:多个开发人员共享一组分支,并且偶尔在所有分支上工作,但通常在拉取更新时,大多数分支都可以快速转发。 - bames53
注意:Git 2.0将有助于确保仅进行快进式合并(fast-forward only)。请参见下面的我的编辑答案 - VonC
1
可能是重复的问题:Can "git pull --all" update all my local branches? - krlmlr
https://dev59.com/u3A75IYBdhLWcg3wlqET#17722977 - Vladimir Reshetnikov
6个回答

24

快速推进所有已设置其上游分支为匹配的 origin/ 分支的 Shell 脚本,而不进行任何检出操作。

  • 它不会在任何时候更改当前分支,无需处理工作副本更改和浪费的时间进行检出。

  • 它只执行快速前进,无法快速前进的分支将显示错误消息并被跳过。

通过运行 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;

如果您使用 git fetch --update-head-ok,那么您就不需要为当前分支设置特殊情况。 - hugomg
你必须在开头使用加号符号,例如 +origin/foo:foo,以强制进行非快进式的更新。(手册中提到,对于 git < 2.20 可能会有所不同。) - hugomg
1
我认为“--update-head-ok”在快进方面与当前答案的工作方式相同。它改变的是,如果您要更新当前分支,则允许命令获取发生。 - hugomg
制作了一个一行版本,可用作git别名(不太安全,没有检查上游名称):ffa =!“对于b in $(git for-each-ref refs / heads --format ='%(refname:short)'); do git config --get branch.$b.merge && git fetch . origin / $ b:$ b --update-head-ok; done” - qwertzguy
2
这种情况经常发生在我身上...但通常我不会在主分支上工作,而主分支是唯一需要快进的分支。这个答案中脚本中修改的一行对我非常有用:git fetch . origin/master:master。这是 git fetch <repository> <remote-branch>:<local-branch> - Jason

7
如果您真的想快速前进所有跟踪远程分支的本地分支,您可能想考虑将以下内容添加为别名到您的~/.gitconfig中:
[alias]
    pull-all = !"for b in $(git for-each-ref refs/heads --format='%(refname)') ; do git checkout ${b#refs/heads/} ; git pull --ff-only ; done"

您可以运行git pull-all,它会遍历您的本地分支并在每个分支上运行git pull --ff-only


6
checkout 可能不太理想,因为它需要更新工作树,这可能会触发 IDE 或其他监视文件的东西的更新。为避免这种情况,您可以使用 update-ref,在这里查看示例:https://dev59.com/um435IYBdhLWcg3w50f4 - joeytwiddle
我曾经天真地猜测,跟踪分支是 origin/$local_branch: merge-all =!"for local_branch in $(git for-each-ref refs/heads --format=\"%(refname:short)\") ; do remote_branch=\"origin/$local_branch\"; local_commit=$(git rev-parse --verify \"$local_branch\"); remote_commit=$(git rev-parse --verify \"$remote_branch\"); common_ancestor=$(git merge-base $local_branch $remote_branch); if [ ! \"$common_ancestor\" = \"$local_commit\" ]; then echo \"Could not be fast-forwarded: $local_branch\" >&2; else git update-ref refs/heads/$local_branch $remote_commit $local_commit; fi done" - joeytwiddle
如果编辑未能成功,您可以使用以下命令替换以返回到原来的分支: pull-all = !“current_branch=$(git rev-parse --abbrev-ref HEAD); for b in $(git for-each-ref refs/heads --format='%(refname)') ; do git checkout ${b#refs/heads/} ; git pull --ff-only ; done; echo $current_branch; git checkout $current_branch" - lauksas
不要执行 checkout,那是毫无必要的代价。也不要执行 update-ref,那并非针对快进操作。请使用 git fetch - CervEd

6
以下一行命令可快进所有有上游分支的分支,如不可能则打印错误信息:
git branch \
  --format "%(if)%(upstream:short)%(then)git push . %(upstream:short):%(refname:short)%(end)" |
  sh

它是如何工作的?

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

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

这可以直接通过管道符号 | 传输到 sh(假设分支名称是规范的)。如果想查看它的操作,请省略 | sh

注意事项

当前已检出的分支将不会被更新,并且不会显示任何消息。

! [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' -

别名是“快进式追踪(分支)”的简写。

已经在所谓的重复问题上发布了相同的答案:https://dev59.com/gG855IYBdhLWcg3weUQQ#55696044。一旦确认为重复问题,将删除此答案。 - krlmlr

6

但是,请注意:

注意:我假设你已经像“Track all remote git branches as local branches.”中所述跟踪了所有远程分支。


注意:Git 2.0(2014年第二季度)将引入一个名为pull.ff的配置项,详情请见commit b814da8

pull.ff::

By default, Git does not create an extra merge commit when merging a commit that is a descendant of the current commit. Instead, the tip of the current branch is fast-forwarded.

  • When set to false, this variable tells Git to create an extra merge commit in such a case (equivalent to giving the --no-ff option from the command line).
  • When set to only, only such fast-forward merges are allowed (equivalent to giving the --ff-only option from the command line).

一如既往,@VonC 给出了详尽可靠的答案,但有些细节令人困惑。push.ff 是打错字了吗?所以设置 git config --add pull.ff true 将会在所有跟踪分支上执行 ff 操作,如果可能的话,作为拉取的一部分?您是否知道它如何与 pull --rebase 和相关配置交互? - CervEd
@CervEd 感谢您的反馈。我已相应地编辑了答案。我尚未在rebase场景中进行测试,但我认为它仅适用于merge,而不是rebase。 - VonC

1
我刚刚写了一个小工具来实现这个功能。 https://github.com/changyuheng/git-fast-forward-all 这个工具的优点是:
  1. 支持在一个仓库中使用多个远程仓库。(目前 hub sync 不支持多个远程仓库)
  2. 支持本地分支和相应的远程跟踪分支有不同的名称。
  3. 比其他每个分支都获取远程的脚本快得多。
  4. 没有容易出错的正则表达式解析/编辑。

0

SmartGit,例如,有一个选项可以在您切换到分支时自动合并来自已跟踪分支的更改。这应该可以实现您想要的目标。


这个特定的脚本还有一个不良副作用,就是它会改变当前分支。有没有办法修改它以保持当前分支? - Zeke Hansell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接