【发布时间】:2015-07-28 02:59:39
【问题描述】:
我正在编写一个程序来检查几个克隆的 git 存储库的状态。
如何判断我的存储库需要“git pull”还是“git push”?
【问题讨论】:
标签: git version-control synchronization status
我正在编写一个程序来检查几个克隆的 git 存储库的状态。
如何判断我的存储库需要“git pull”还是“git push”?
【问题讨论】:
标签: git version-control synchronization status
首先,pull 只是 fetch 后跟 merge(或 rebase)。这很重要,因为您首先想要的是一个相关的、更简单的问题的答案:与某个远程存储库相比,您的本地存储库领先和/或落后多远? (对于 TL;DR 答案,请跳到下面的第一个标题部分。)
还有其他潜在的复杂性,但在这里要做的简单的事情是检查您的分支提示与其他一些 git 存储库/ies 的那些提示。为了便于说明,我们给它们起一些名字。我们可以将您的存储库称为“L”(表示本地),假设有两个附加存储库“RA”(远程 A)和“RB”,它们的 URL 以远程名称 RA 和 RB 存储在本地存储库 L 中。
一次获得您可能需要的一切的简单方法是在 RA 和 RB 上运行 git fetch(或 git remote update)。 (我们稍后会看到一种延迟获取的方法,尽管我不确定它有什么真正的价值。)
在典型设置下,获取两个遥控器会将它们的本地分支复制到您自己的“远程分支”。假设 RA 有 master 和 dev 并且 RB 有 master 和 feature,所以在你的提取完成后:
L$ git rev-parse --short refs/remotes/RA/master
feedbee
L$ git rev-parse --short refs/remotes/RA/dev
feedbee
L$ git rev-parse --short refs/remotes/RB/master
badf00d
L$ git rev-parse --short refs/remotes/RB/feature
c0ffee1
(这些数字是虚构的,但仅用于说明。此外,如果您在实际代码中执行此操作,您不会想要 --short。而且,您可以缩写分支名称,省略 refs/remotes/ ——以及下面的refs/heads/——只要它们是明确的。不过,如果你正在编写脚本,以防万一,使用全名可能更明智。)
现在让我们来看看你自己当地分支机构的提示:
L$ git rev-parse --short refs/heads/master
feedbee
这意味着您的 master 与 RA 的 master 同步,RA 的 master 与其 dev 同步。因此,那里必须没有什么可以推送或获取(尽管您已经获取了以找出这一点),因此也没有什么可以合并或变基。
另一方面,远程 RB 不同步,因为它的 master 指向提交 badf00d。这是否意味着你有一些东西要合并,或者有些东西要推动?也许,也许不是:这就是复杂的地方。如果需要手动合并,Git 并不能真正提供多大帮助,但是您可以找出 RB 是“领先”、“落后”还是两者,通过查看提交图如何相互叠加。
如果 repo L 严格“领先于”repo RB,即你有可以推送的东西,那么图形片段必须看起来像这样:
... - o <-- RB/master: tip-most commit = badf00d
\
o - o <-- master: tip commit = feedbee
这里的 L 是“提前 2 个”,用 git status 给你的术语来说,repo RB 在哪里。如果你推送到 RB,git 会将最后两次提交交给 RB,并告诉它请将其 master 设置为 feedbee,这样会赶上它。
如果 repo L 严格“落后”于 RB,则图形片段看起来相同,但标签会颠倒:
... - o <-- master
\
o - o <-- RB/master
在这种情况下,如果您将RB/master 合并到master,git 会看到快进并将您的主设置为badf00d。此时,repo RA 将落后,您可能想推到那里。
还有两种可能性。 L 可以同时领先和,例如:
... - o - o <-- master
\
o - o <-- RB/master
这需要 rebase 或真正的合并,如果 git 无法自行组合各种更改(或者即使可以,也可能会出错),这两者都可能需要手动处理。
最后,尽管不太可能,这两个分支提示可能完全不相关(... 部分中没有共同祖先):
... - o <-- master
... - o <-- RB/master
这是最难处理的,因为我在下面描述的内容给出了错误的前后计数。 (如果有人已经重写了其中一个克隆的所有历史记录,它也只会发生在克隆的仓库中。例如,使用git merge-base 为这种情况添加一个偏执检查可能是合理的。但我会从这里开始忽略它。)
除此之外,这里有一个快速的方法来查找您知道相关的两个分支(一个本地,一个远程)的“前面”和“后面”计数。我将切换到远程“原点”,这是(单个)克隆源的常用名称,并使用分支名称的缩写形式:
$ ahead=$(git rev-list --count origin/master..master)
$ behind=$(git rev-list --count master..origin/master)
这些使用gitrevisions range syntax 选择可从本地分支提示(master 标识的提交)而不是远程分支提示(由origin/master 标识)访问的修订。
如果您领先但不落后,您可以安全地git push。如果您落后但不领先,您可以安全地git merge 快速前进(此时使用pull 没有意义,因为您已经完成了fetch 步骤)。如果您同时领先和,因此两个计数均非零,则您必须在合并或变基之间做出决定,以及如果失败该怎么办。当然,如果两个计数都为零,则两个分支提示标识相同的 SHA-1,无需执行任何操作。
git fetch怎么办?最终你有使用git fetch。但是,如果您愿意,可以从 git ls-remote 开始,它会输出 SHA-1 和 refnames 的列表:
$ git ls-remote
From [url redacted]
a17c56c056d5fea0843b429132904c429a900229 HEAD
ca00f80b58d679e59fc271650f68cd25cdb72b09 refs/heads/maint
a17c56c056d5fea0843b429132904c429a900229 refs/heads/master
0029c496ce1b91f10b75ade16604b8e9f5d8d20b refs/heads/next
fcd56459647e0c41f2ea9c5b7e2ed827f701fc95 refs/heads/pu
e8f6847178db882bd42d5572439333ca4cb3222e refs/heads/todo
d5aef6e4d58cfe1549adef5b436f3ace984e8c86 refs/tags/gitgui-0.10.0
3d654be48f65545c4d3e35f5d3bbed5489820930 refs/tags/gitgui-0.10.0^{}
[mass snippage]
如果远程 SHA-1 与本地 SHA-1 不同,这些 SHA-1不携带您需要的所有图形信息,但如果您关心的 SHA-1 匹配,然后你可以说没有什么可以获取或推送。此外,如果它们不同,您可以查看是否有相应的 SHA-1。例如,上面显示远程的master 指向提交a17c56c056d5fea0843b429132904c429a900229。如果我有它(我没有),我可以将它用作git rev-list --count 中的说明符之一,以找出遥控器落后多远。因为我没有它,所以我几乎肯定落后了:我不知道多少,但我需要获取,然后可能合并或变基(我不知道我是否也领先,直到我获取)。
这可能最好提前确定,而不是只遍历所有可能的分支。但是,如果您确实想要遍历分支,则可以使用git for-each-ref,它需要很多参数。例如,要查找您自己的本地分支机构:
$ git for-each-ref --format='%(refname)' refs/heads
refs/heads/master
refs/heads/precious
refs/heads/stash-exp
要查找远程origin 的分支:
$ git for-each-ref --format='%(refname)' refs/remotes/origin
refs/remotes/origin/maint
refs/remotes/origin/master
refs/remotes/origin/next
refs/remotes/origin/pu
refs/remotes/origin/todo
很容易剥离足够多的这些字符串并匹配分支名称,以便能够比较它们。如果您想检查(并跳过)匹配的 SHA-1,使用其他选项可以获得分支名称和 SHA-1,这显然会为前后计数提供零。
【讨论】:
其实从Git 2.5(今天发布)开始,你可以很快的看到哪个分支在前面(需要推)或者后面(需要拉)
见“Show git ahead and behind info for all branches, including remotes”
git for-each-ref --format="%(push:track)" refs/heads
那是因为<branch>@{push} 是一个新的快捷方式,它专门引用了用于推送的上游分支(它并非总是用于拉取的分支)。
【讨论】: