我正在编写一个程序,用于检查一些克隆的 git 仓库的状态。
如何判断我的仓库是否需要进行 "git pull" 或 "git push" 操作呢?
我正在编写一个程序,用于检查一些克隆的 git 仓库的状态。
如何判断我的仓库是否需要进行 "git pull" 或 "git push" 操作呢?
首先,pull
只是 fetch
然后再加上 merge
(或者rebase
)。这很重要,因为你首先想要回答一个相关且更简单的问题:与某个远程仓库相比,你的本地仓库落后或领先多少?(如果你只想看简短回答,请跳到下面第一个标题化部分。)
还有其他潜在的复杂性,但在这里要做的简单事情就是检查你的分支末端与一些其他 Git 仓库的分支末端是否一致。我们给它们一些名称以进行说明。我们可以将你的仓库称为"L"(本地),并假设有两个附加的仓库“RA”(远程A) 和 "RB",它们的 URL 存储在本地仓库 L 中,使用 RA 和 RB 作为远程名称。
一次获取所有您可能需要的内容的简单方法是在 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
同步,后者与其dev
同步。因此,那里没有需要推送或提取的内容(尽管您已经提取了为了找出这个问题),因此也没有需要合并或变基的内容。
另一方面,远程RB不同步,因为它的master
指向提交badf00d
。这是否意味着您需要合并或推送某些内容?也许是,也许不是:这就是复杂性所在。如果需要手动合并,Git无法提供太多帮助,但您可以通过查看提交图形如何相互堆叠来确定RB是“超前”,“落后”还是两者都有。
如果仓库L严格“领先于”仓库RB,即您有可以推送的内容,则图形片段必须类似于以下内容:
... - o <-- RB/master: tip-most commit = badf00d
\
o - o <-- master: tip commit = feedbee
在 git status 命令中,L 是相对于 repo RB 的“2个提交”之前的位置。如果你向 RB 进行推送,git 会将最后两个提交交给 RB,并告诉它将其 master
设置为 feedbee
,以使其保持同步。
如果 repo L 严格落后于 RB,则图形片段将相同,但标签将被颠倒:
... - o <-- master
\
o - o <-- RB/master
在这种情况下,如果你进行合并以将 RB/master
合并到 master
中,Git 会看到一个快进并将你的主分支设置为 badf00d
。此时, RA 存储库将落后于 RB 存储库,你可能需要将更改推送到 RA 存储库中。
不过,还有两个可能性。例如,L 可以同时领先和落后:
... - o - o <-- master
\
o - o <-- RB/master
这需要进行变基或真正的合并,如果git不能自动合并各种更改(即使它可以,也可能会出错),则其中任何一个都可能需要手动协助。
最后,虽然不太可能,但两个分支末端完全不相关(在...
部分没有共同的祖先):
... - o <-- master
... - o <-- RB/master
由于下面所描述的内容会导致计算出错误的 ahead/behind 数量,因此这是最棘手的问题。如果克隆的版本库中有人重写了所有历史记录,这种情况只会出现在克隆版上。我们可以考虑增加一些偏执检查,例如使用 git merge-base
,但我会在此忽略这个问题。
解决了上述问题之后,下面是一种快速查找两个分支(一个本地的,一个远程的)之间 "ahead" 和 "behind" 数量的方法。我将切换到常规命名为“origin”的远程仓库,并使用分支名称的简写形式:
$ ahead=$(git rev-list --count origin/master..master)
$ behind=$(git rev-list --count master..origin/master)
这些使用gitrevisions
范围语法来选择可从本地分支尖端(由master
指定的提交)到达但不是远程分支尖端(由origin/master
指定)的修订版本。
如果你超前而不落后,可以安全地运行git push
。如果你落后而不超前,则可以安全地运行git merge
以获得快进(此时使用pull
没有意义,因为你已经完成了fetch
步骤)。如果你同时超前且落后,使两个计数都不为零,你必须在合并或变基之间做出决策,并决定在它们失败时该怎么办。当然,如果两个计数都为零,则两个分支尖端标识相同的SHA-1,不需要进行任何操作。
git fetch
怎么办?最终,你必须使用git fetch
。然而,如果你愿意,可以先使用git ls-remote
,它会输出一个SHA-1和引用名称列表:
$ 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。例如,上面显示远程的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。这显然会导致ahead和behind计数为零。
实际上,自Git 2.5发布以来(今天发布),您可以快速查看哪个分支需要推送(超前)或拉取(落后)
git for-each-ref --format="%(push:track)" refs/heads
这是因为<branch>@{push}
是一个新的快捷方式,它专门引用用于推送的上游分支(它不总是用于拉取的分支)。