如何在Git仓库中只稀疏地检出单个文件?

598

我该如何从git仓库中只检出一个文件?


10
"check out"是什么意思?是从远程代码库获取单个文件的副本吗? - Cascabel
2
如果相关的仓库正在使用gitweb,您可以直接从那里下载文件。正如我下面所解释的,您所要求的并不是标准的git操作。 - Cascabel
你可以使用Chrome扩展程序GitHub Mate,它使你能够点击文件图标来下载它。 - Cam Song
可能是从存储库检索单个文件的重复问题。 - Jacek Krysztofik
显示剩余4条评论
27个回答

276

最初在2012年提到了git archive(参见Jared ForsythanswerRobert Knightanswer),自从git1.7.9.5(2012年3月)以来,Paul Brannananswer

git archive --format=tar --remote=origin HEAD:path/to/directory -- filename | tar -O -xf -

但是在2013年,不再支持远程https://github.com URL的存档。
请查看旧页面"我可以存档一个仓库吗?"。

当前(2018年)页面"关于在GitHub上归档内容和数据"建议使用第三方服务,如GHTorrentGH Archive


所以您也可以处理本地副本/克隆:

如果您有裸仓库的本地副本,如此答案中所述,您也可以选择执行以下操作:

git --no-pager --git-dir /path/to/bar/repo.git show branch:path/to/file >file

或者你必须首先克隆仓库,这意味着你获取了完整的历史记录:

  • 在.git仓库中

  • 在工作树中。

  • 但是,如果你使用Git1.7+,你可以进行稀疏检出

    • 启用稀疏检出选项 (git config core.sparsecheckout true)
    • .git/info/sparse-checkout文件中添加你想要查看的内容
    • 重新读取工作树,只显示你需要的内容

要重新读取工作树:

$ git read-tree -m -u HEAD

这样,你最终会得到一个工作树,其中包括你想要的精确内容(即使只有一个文件)。


Richard Gomes指出(在评论中)"如何从git仓库克隆、提取或稀疏检出单个目录或目录列表?"

一个Bash函数,避免下载历史记录,检索一个分支以及检索您需要的文件或目录列表。


从 Git 2.40(2023年第一季度)开始,通过检查稀疏模式来判断是否使用“cone”模式的逻辑已经被加强,以避免将命名单个文件的模式误认为是指定 cone 模式。

请参见 commit 5842710(由 William Sprent (williams-unity) 于 2023 年 1 月 3 日提交)。
(由 Junio C Hamano -- gitster --commit ab85a7d 中合并,于 2023 年 1 月 16 日)

dir:检查单个文件 cone 模式

签名作者:William Sprent
确认作者:Victoria Dye

The sparse checkout documentation states that the cone mode pattern set is limited to patterns that either recursively include directories or patterns that match all files in a directory.
In the sparse checkout file, the former manifest in the form:

/A/B/C/

while the latter become a pair of patterns either in the form:

/A/B/
!/A/B/*/

or in the special case of matching the toplevel files:

/*
!/*/

The 'add_pattern_to_hashsets()' function contains checks which serve to disable cone-mode when non-cone patterns are encountered.
However, these do not catch when the pattern list attempts to match a single file or directory, e.g. a pattern in the form:

/A/B/C

This causes sparse-checkout to exhibit unexpected behaviour when such a pattern is in the sparse-checkout file and cone mode is enabled.

Concretely, with the pattern like the above, sparse-checkout, in non-cone mode, will only include the directory or file located at '/A/B/C'.
However, with cone mode enabled, sparse-checkout will instead just manifest the toplevel files but not any file located at '/A/B/C'.

Relatedly, issues occur when supplying the same kind of filter when partial cloning with '--filter=sparse:oid=<oid>'.
'upload-pack' will correctly just include the objects that match the non-cone pattern matching.
Which means that checking out the newly cloned repo with the same filter, but with cone mode enabled, fails due to missing objects.

To fix these issues, add a cone mode pattern check that asserts that every pattern is either a directory match or the pattern '/*'.
Add a test to verify the new pattern check and modify another to reflect that non-directory patterns are caught earlier.


2
@Tilo:不确定,但应该是可能的,因为克隆已经是完整的。 - VonC
3
其他答案中提到的“git checkout HASH path-to-file”方式相比之下有什么优势呢?那时这种方式不可用吗? - 0x6A75616E
2
@juand 的想法是在执行 git checkout 命令之前不必加载整个工作树。 - VonC
2
顺便说一句,我们现在可以使用 git archive 了。 - Jared Forsyth
1
这个 git show 命令可以从一个裸仓库中获取文件内容,正是我一直在寻找的。谢谢! - Gio
显示剩余12条评论

260

首先使用 -n 选项克隆仓库,该选项抑制了所有文件的默认检出,并使用 --depth 1 选项,这意味着它只获取每个文件的最新修订版本。

git clone -n git://path/to/the_repo.git --depth 1

然后只需像这样检查您想要的文件:

cd the_repo
git checkout HEAD name_of_file

12
字面意思是这将检查一个文件,但几乎肯定不是OP想要做的,因为他们将拥有所有文件(而且检出也是无操作的) - Cascabel
5
我不认为这个方法有效 - 使用-n选项后,工作树和索引将同步。也就是说,所有的内容都会显示为已删除。你需要使用git reset HEAD或者git checkout HEAD file命令来恢复文件。此时,如果你不真正了解git的工作原理,那么在操作这个代码库时会非常困难。 - Cascabel
3
如果 OP 和像 DanielElliott 这样的 OOP 只想要文件(而不是仓库),那么在 NickMoore 的脚本中添加另一个 rm -rf .git 将清除所有克隆仓库的痕迹,或许可以缓解 Jefromi 对留下难以使用的仓库的担忧。这对我来说非常有用,可以用于多个应用,比如今天我的挑战是构建一个 post-receive 钩子来自动更新另一个 post-receive 钩子的版本。 - hobs
10
这个回答比已被接受的答案好得多。很高兴我继续阅读了下去。 - Eric Uldall
8
这个答案是最好的(但git不适合这种工作)。这个答案也适用于这个问题,或者这个受欢迎的问题,以及许多其他问题:将name_of_file改为name_of_folder。现在(2014年),Git提供了子模块来为repo所有者提供一些友好的repo用户。 - Peter Krauss
显示剩余10条评论

119

如果你已经拥有git代码库的副本,那么你可以使用git log查找哈希ID(例如 3cdc61015724f9965575ba954c8cd4232c8b42e4)并使用以下命令检出文件的版本:

git checkout hash-id path-to-file

以下是一个实际的示例:

git checkout 3cdc61015724f9965575ba954c8cd4232c8b42e4 /var/www/css/page.css

13
你也可以使用标签或分支名称,不仅仅是哈希值。这些通常更容易。 - Rob Kennedy
4
好的解决方案。但是如果path-to-file是一个目录,并且当前的HEAD包含某些文件而target没有(或反之亦然),这将不能正确地更新文件。有没有一种处理方式? - MasterMind
2
更简单,更好。谢谢! - Kerem atam
错误:路径规范。该路径是本地PC上的路径还是远程PC上的路径? - Paul McCarthy
1
@PaulMcCarthy - 本地 - techexpert

98

通常情况下,无法仅从 git 下载单个文件,而不像第一篇回答建议的那样下载整个存储库。这是因为 Git 不会像 CVS/SVN 一样存储文件,而是基于整个项目的历史记录来生成它们。

但特定情况下有一些解决方法。以下是示例,其中包含 userprojectbranchfilename 的占位符。

GitHub

wget https://raw.githubusercontent.com/user/project/branch/filename

GitLab

wget https://gitlab.com/user/project/raw/branch/filename

GitWeb

如果您正在使用Git on the Server - GitWeb,那么您可以尝试以下示例(将其更改为正确的路径):

wget "http://example.com/gitweb/?p=example;a=blob_plain;f=README.txt;hb=HEAD"

在drupalcode.org上的GitWeb

示例:

wget "http://drupalcode.org/project/ads.git/blob_plain/refs/heads/master:/README.md"

googlesource.com

有一个未记录的功能可以让您下载base64编码版本的原始文件:

curl "https://chromium.googlesource.com/chromium/src/net/+/master/http/transport_security_state_static.json?format=TEXT" | base64 --decode

在其他情况下,请检查您的Git代码库是否使用任何Web界面。

如果没有使用任何Web界面,您可以考虑将代码推送到外部服务,例如GitHubBitbucket等等,并将其用作镜像。

如果您没有安装wget,也可以尝试替代性地使用curl -O (url)


6
如果我不使用Github呢? - Zulu
87
那就不要使用这种方法。 - Alan
8
我认为有必要反驳"git基于项目全部历史生成文件"这一说法。更准确的说,git以哈希树的形式存储文件状态的快照。显然,并没有生成文件的过程。 - Jay Sullivan
3
这个答案对我非常有用,因为我试图简单地恢复一个我有意删除的文件(与其他几个文件一起本地删除,但没有提交删除),但后来发现需要该文件,而其他文件仍然不需要。 - rshdev
14
你知道当你想要使用Git做一些简单的操作时,前往Stack Overflow查看如何操作,半途却感到脑袋一片混沌和悲伤的那种沉重感吗?然后你向下滚动,找到这个出色的wget答案,简单性和快乐也随之而来。谢谢你。 - pgr
显示剩余4条评论

52

进一步解释:broc.seib的回答解释了git-checkout命令手册中相关部分的含义。 - Shihe Zhang

45

git checkout 分支或版本 -- 路径/文件

示例:git checkout HEAD -- main.c


其他答案让我感到很困惑,但这个解决方案对我非常有用。在更新之前,我删除了要被替换的文件。然后执行了以下操作:git checkout HEAD abcd.cpp - veenus adiyodi

43

现在我们可以了!由于这是谷歌上的第一个结果,我想将其更新至最新状态。随着 Git 1.7.9.5 的推出,我们现在可以使用 git archive 命令从远程主机检索单个文件。

git archive --remote=git://git.foo.com/project.git HEAD:path/in/repo filename | tar -x

完整答案请参见https://dev59.com/xHNA5IYBdhLWcg3wBo5m#5324532


@Benubird 是你的代码库的主机名。对于 GitHub(如果 GitHub 支持存档命令,但据我所知它不支持),它将是 github.com - Jared Forsyth
1
这对我有用,但只有在我指定分支(或refname)时才有效,例如仅使用HEADmaster而不是HEAD:directory - stormbeta
6
在Bitbucket上,我用以下命令成功实现:git archive --remote=git@bitbucket.org:user/repo branch:path/to file | tar -x - Dave

26

这里是关于如何在Git仓库中仅拉取和推送特定文件的完整解决方案:

  1. 首先,您需要使用特殊提示--no checkout克隆Git仓库
git clone --no-checkout <git url>
  1. 下一步是使用以下命令从索引中删除未暂存的文件:
git reset
  1. 现在您可以使用以下命令开始拉取想要更改的文件:
git checkout origin/master <path to file>
  1. 现在存储库文件夹包含您可以立即开始编辑的文件。编辑后,您需要执行简单且熟悉的命令序列。
git add <path to file>
git commit -m <message text>
git push

1
这个回答用简单易懂的工作流程回答了问题。此外,结果表明在检出时不需要使用 origin/master:对于第三步,只需使用简单的 git checkout <文件路径> 即可。 - John
2
git reset 在这里有点危险,因为它会将要删除的文件标记为撤消过程。而 'git commit ...' 和 'git push...' 也可能提交删除操作... 这可能会从存储库中删除大量文件。'git commit YOUR_FILE -m <message>' 应该是正确的做法... - Farrukh Waheed
1
我刚试着使用这个命令集,似乎它是最危险/错误的答案。 第一个命令实际上会带来整个树,但在运行“ls”命令时不会显示任何内容。 当您运行第二个命令时,您实际上会看到所有文件都已进入删除模式并已暂存。现在,如果您添加更新后的文件,它实际上会删除其他文件并保留此单个文件。 只是为了尝试,我走到第三步,尝试提交文件,结果实际上是通过我的当前文件重命名另一个文件,这完全是错误的。 - rkdove96

24

我使用GIT 1.7.2.2版本。

例如,您有一个名为 some_remote 的远程仓库,其中包含分支 branch1branch32

要检出特定文件,请运行以下命令:

git checkout remote/branch path/to/file

举个例子,它会是这样的:

git checkout some_remote/branch32 conf/en/myscript.conf
git checkout some_remote/branch1 conf/fr/load.wav

假设您之前已经运行过git init,那么这个checkout命令会将整个文件结构conf/en和conf/fr复制到您调用这些命令的当前目录中。


6
但在此之前,您需要运行 git fetch some_remote,是吗? - phihag

23

git clone --filter从Git 2.19实际上跳过了下载额外的文件

这个选项实际上会跳过从服务器获取大部分不需要的对象。例如,要仅获取此测试存储库中的文件small/0000https://github.com/cirosantilli/test-git-partial-clone-big-small-no-bigtree,我们可以执行以下操作:

git clone --depth 1 --no-checkout --filter=blob:none \
  https://github.com/cirosantilli/test-git-partial-clone-big-small-no-bigtree
cd test-git-partial-clone-big-small-no-bigtree
git checkout master -- small/0000

它还可以处理多个文件:
git checkout master -- small/0000 small/0001

但请注意,文件是逐个下载的,所以如果您要下载大量文件,速度会非常慢。然而,整个目录可以高效地下载,方法如下:如何仅克隆Git存储库的子目录? 该测试存储库除了小文件外,还有几个大文件,而小文件的下载基本上是即时的,因此我们可以确定,由于请求时间短和{{link2:ncdu上的小目录大小}},我们根本没有下载这些大文件。 --filter的格式在man git-rev-list中有详细说明。Git远程协议已扩展以支持此功能。
在Git 2.37.2、Ubuntu 22.10上进行了测试。

2
不错,我一定错过了那个选项。+1 - VonC
这在 git 2.39 上不起作用。使用 --depth 1 --no-checkout --filter=blob:none 时,所有文件都处于已删除/暂存状态。您需要首先运行 git reset,如上面提到的 https://dev59.com/WHE95IYBdhLWcg3wE52C#54968394 - Martin Ba
@MartinBa 这个使用案例是什么?文件处于暂存状态有什么影响吗?您想修改提交并推送回去吗? - Ciro Santilli OurBigBook.com
@Ciro... git checkout 命令因为文件被标记删除而无法创建该文件,会出现错误。如果没有添加重置,你的命令序列根本不起作用。 - Martin Ba
@MartinBa 嗯,我在从源代码构建的 Git 2.39.3 上没有复现这个问题,我的 Ubuntu 版本是22.10。该文件是存在的。如果您发现与版本问题有关的解决方法,请告诉我。 - Ciro Santilli OurBigBook.com

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