Git获取单个提交: Git获取单个提交

5

我想从远程���库获取一次提交(当然包括历史记录)到本地仓库。有些 Stack Overflow 的回答建议使用以下方法,但实际上并没有成功:

md foo
cd foo
git init
git fetch https://github.com/ld4apps/lda-serverlib.git f1e32e18a90e10c150221af55c69aeafaa42c57a

这会产生以下错误:

错误:没有这样的远程引用f1e32e18a90e10c150221af55c69aeafaa42c57a

令人惊讶的是,以下内容(在某种程度上)可以工作:

git fetch https://github.com/ld4apps/lda-serverlib.git :f1e32e18a90e10c150221af55c69aeafaa42c57a

这将创建一个名为f1e32e18a90e10c150221af55c69aeafaa42c57a的本地分支(我并不想要它),但至少新分支具有我尝试获取的内容。我可能期望下面的命令会做同样的事情,但它得到了与第一个示例相同的错误:

git fetch https://github.com/ld4apps/lda-serverlib.git f1e32e18a90e10c150221af55c69aeafaa42c57a:f1e32e18a90e10c150221af55c69aeafaa42c57a

我在Windows和Linux上使用了几个不同版本的Git,并看到了相同的行为。
有人能解释一下这是怎么回事吗?我对Git、fetch和refspecs的理解有哪些不足之处?我真正想要的是一个单独的git fetch命令,我可以向其中提供远程分支或标签的ID或提交ID,它将获取相应的提交。

“a single commit with history” 是什么意思?其他提交记录都是历史记录… - Ry-
我只是指通常的git行为,没有什么特别的。我想获取一个特定的提交,我知道git也会获取构成我指定提交历史的提交。 - Martin Nally
如果您使用 git fetch --depth=1,那么就不会出现这种情况(因为在您的示例中,f1e32e18a90e10c150221af55c69aeafaa42c57a 是当前的最新提交)。但这并不是问题所在,对吧? - Ry-
我不知道depth选项 - 感谢您提供的信息。正如您所说,虽然有趣,但似乎与我的问题无关。 - Martin Nally
这个问题不是 https://dev59.com/MmUp5IYBdhLWcg3w_rvT 的重复吗? - vorburger
是否应该删除 Github 标签?它会让页面标题变得混乱。 - tuxayo
1个回答

7
更新:自Git 2.5(大约在2015年中期)以来,服务器现在可以公开原始哈希。这是一个服务器端的配置项。您必须在服务器上具有足够的控制权才能设置它,并且在设置之前应考虑潜在的安全问题。有关详细信息,请参见this answerRetrieve specific commit from a remote Git repository。(适用于2.5之前或未设置配置选项的原始答案如下。)

git fetch 命令将引用(名称,而不是原始提交ID)传递给远程主机。更具体地说,使用 git ls-remote remotename 查看远程主机愿意提供的名称列表。该命令在左侧生成SHA-1列表,在右侧生成名称列表,您的fetch 只能请求右侧的名称。如果远程上的名称仍然指向该ID,则会获取左侧的ID,因此这取决于远程主机的更新活跃程度。

有多种方式可以将原始提交ID传递给远程,并要求远程从该点开始查看可见内容,有时也可以向后工作通过历史记录,但不能通过git fetch实现。(您可以使用git archive,但是远程可以决定是否允许您通过原始提交ID访问;或者对于具有Web服务器访问权限的远程,包括特定提交,您通常可以只查看提交的顶级内容,并使用它来“钻取”,正如他们所说,到各种不同的部分。但这是一种非常缓慢的方法。)
如果您想使用git fetch获取某个特定的提交,可能最简单的方法是让具有远程访问权限的人将名称(很可能是标签)附加到该提交ID。然后,您可以让git fetch将该refspec带到您喜欢的任何其他refspec下。例如,假设您可以直接ssh到任何主机的源:
$ ssh our.origin.host 'cd /repos/repo.git; git tag temporary f1e32e1'
[enter password, etc; observe tag created]
$ git fetch origin refs/tags/temporary:refs/heads/newbranch
[observe fetch happen; now you have local branch 'newbranch']
$ ssh our.origin.host 'cd /repos/repo.git; git tag -d temporary'

请注意,名称无需是一个分支,只需是您可以使用 git fetch 拉取并使用 git ls-remote 查看的引用。然后在获取时使用与左侧 refspec 匹配的名称。在上面的示例中,您的 存储库中创建的名称由 refspec 的右侧(refs/heads/newbranch)控制。
这也是您上一段问题的答案:您只能为具有远程名称的事物命名(部分原因是为了避免在垃圾回收之前保留在存储库中的未命名提交 "泄漏",因此被认为是一种功能而不是错误)。 这些名称放在 refspec 的左侧。 您自己的名称位于右侧。
您右侧的名称被认为是分支或标签名称(基于左侧名称的匹配,尽管您可以明确拼写refs/heads/refs/tags/以覆盖它),因此即使f1e32e1...是有效的SHA-1,它在这里被视为分支名称 - 左侧缺失的名称几乎总是被翻译为HEAD - git fetch创建的分支名称类似于SHA-1。 (顺便说一句,我曾经创建过一个看起来像SHA-1的分支名称,后来让自己感到困惑。 我忘记确切的名称是什么,像de-bead但没有连字符。 我将其重命名为带连字符的版本,只是为了表明我并不是指原始提交ID! :-))

谢谢,Torek,这非常有帮助,似乎解释了我看到的大部分内容。但是我没有看到它解释了为什么/如何 <code> git fetch https://github.com/ld4apps/lda-serverlib.git :f1e32e18a90e10c150221af55c69aeafaa42c57a </code> 起作用。那里发生了什么? - Martin Nally
添加了一个最后的段落,包含“意图”。我会看看是否可以澄清创建分支的问题... - torek
也许我在这里有点迟钝。我知道 RHS 控制我的自己的名称,所以我并不惊讶 'git fetch repo :f1e32e18a90e10c150221af55c69aeafaa42c57a' 创建了一个名为 f1e32e18a90e10c150221af55c69aeafaa42c57a 的本地分支。令我惊讶的是,它还获取了相同 id 的提交,即使当我将其放在 LHS 上时,该提交是不可寻址的。 - Martin Nally
啊,是空的 LHS 让你感到困惑了。请查看重新编辑的最后一段。 - torek

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