如何从 git 存储库中克隆一些目录?

如何从 git 存储库中克隆一些目录?

例如我想下载PCL 3d_rec_framework。

这是 PCL 的 git 存储库:https://github.com/PointCloudLibrary/pcl.git

我如何下载该目录?

https://github.com/PointCloudLibrary/pcl/tree/master/apps

我尝试了这个,但是没有用:

sam@sam:~/code/pcl_standalone$ git clone https://github.com/PointCloudLibrary/pcl/tree/master/apps/3d_rec_framework
Cloning into '3d_rec_framework'...
error: The requested URL returned error: 403 while accessing https://github.com/PointCloudLibrary/pcl/tree/master/apps/3d_rec_framework/info/refs
fatal: HTTP request failed
sam@sam:~/code/pcl_standalone$ 

我不想下载 PCL 的 git 并删除所有其他我不想要的目录。

我如何仅下载单个目录?

答案1

自 git v1.7 以来,dobey 的答案不再如此。您现在可以从存储库中签出某些文件夹。完整说明位于这里

git init <repo>
cd <repo>
git remote add -f origin <url>

git config core.sparseCheckout true

echo "some/dir/" >> .git/info/sparse-checkout
echo "another/sub/tree" >> .git/info/sparse-checkout

这告诉 git 你想要检出哪些目录。然后你可以只拉取这些目录

git pull origin master

答案2

git clone --filter+git sparse-checkout仅下载所需文件

例如,仅克隆small/此测试存储库中子目录中的文件:https://github.com/cirosantilli/test-git-partial-clone-big-small-no-bigtree

git clone -n --depth=1 --filter=tree:0 \
  https://github.com/cirosantilli/test-git-partial-clone-big-small-no-bigtree
cd test-git-partial-clone-big-small-no-bigtree
git sparse-checkout set --no-cone small
git checkout

您还可以选择多个目录进行下载:

git sparse-checkout set --no-cone small small2

但是,这种方法不适用于单个文件,但这里有另一种方法可以:https://stackoverflow.com/questions/2466735/how-to-sparsely-checkout-only-one-single-file-from-a-git-repository/52270527#52270527

在这个测试中,克隆基本上是即时的,我们可以确认克隆的存储库非常小,正如我们所希望的那样:

du --apparent-size -hs * .* | sort -hs

给予:

2.0K    small
226K    .git

该测试存储库包含:

  • big/包含 10x 10MB 文件的子目录
  • 顶层有10x 10MB 文件0,,1... (这是因为之前的某些尝试会下载顶层文件)9
  • 包含 1000 个文件的子目录small/small2/每个文件大小为 1 字节

所有内容都是伪随机的,因此不可压缩,所以我们可以很容易地注意到是否下载了任何大文件,例如ncdu

因此,如果您下载了任何不想要的内容,您将会获得额外的 100 MB,而且这会非常明显。

在上面,git clone下载单个对象,大概是提交:

Cloning into 'test-git-partial-clone-big-small'...
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 1 (delta 0), pack-reused 0
Receiving objects: 100% (1/1), done.

然后最后的签出下载我们请求的文件:

remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 3 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), 10.19 KiB | 2.04 MiB/s, done.
remote: Enumerating objects: 253, done.
remote: Counting objects: 100% (253/253), done.
Receiving objects: 100% (253/253), 2.50 KiB | 2.50 MiB/s, done.
remote: Total 253 (delta 0), reused 253 (delta 0), pack-reused 0
Your branch is up to date with 'origin/master'.

2023 年 1 月在 git 2.37.2、Ubuntu 22.10 上测试。

TODO 还可以防止下载不需要的树对象

上述方法下载所有Git 树对象(即目录列表,但不是实际的文件内容)。我们可以通过运行以下命令来确认:

git ls-files

并发现该目录包含如下大文件:

big/0

在大多数项目中这不会是个问题,但我内心的完美主义者希望避免这些问题。

我还在目录下创建了一个非常极端的存储库,其中包含一些非常大的树对象(100 MB)big_treehttps://github.com/cirosantilli/test-git-partial-clone-big-small

small/如果有人找到从中克隆目录的方法,请告诉我!

关于命令

--filter选项与远程协议的更新一起添加,它确实可以防止从服务器下载对象。

不幸的是,这个sparse-checkout部分也是需要的。您还可以仅下载某些文件,这些文件更容易理解:

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

但这种方法不知为何逐个下载文件,速度非常慢,除非目录中的文件很少,否则它将无法使用。

另一个不太冗长但失败的尝试是:

git clone --depth 1 --filter=blob:none --sparse \
  https://github.com/cirosantilli/test-git-partial-clone-big-small
cd test-git-partial-clone-big-small
git sparse-checkout set small

但会下载顶层目录中的所有文件:https://stackoverflow.com/questions/75311408/how-to-prevent-git-clone-filter-blobnone-sparse-from-downloading-files-on-t

梦想:任何目录都可以有 Web 界面元数据

这个功能可能会彻底改变 Git。

想象一下,拥有企业的所有代码库在单个 monorepo 中没有丑陋的第三方工具,例如repo

想象直接在 repo 中存储大量数据,无需任何第三方扩展

想象一下,如果 GitHub 允许每个文件/目录的元数据比如星星和权限,这样你就可以在单个仓库下存储你所有的个人资料。

想象一下子模块的处理方式与常规目录完全相同:只需请求一个树 SHA,以及一个类似 DNS 的机制解析您的请求,首先查看你的当地的~/.git,然后首先到达更近的服务器(您企业的镜像/缓存)并最终到达 GitHub。

我有一个梦想。

测试锥 monorepo 哲学

这是无需子模块的 monorepo 维护的一种可能理念。

我们希望避免使用子模块,因为每次进行具有子模块和非子模块组件的更改时都必须提交到两个单独的存储库,这很烦人。

每个带有 Makefile 或类似文件的目录都应该自行构建和测试。

此类目录可以依赖于:

  • 其下的每个文件和子目录都直接显示其最新版本
  • 外部目录只能在指定版本时才可依赖

直到 git 开始原生支持此功能(即只能跟踪子目录的子模块)之前,我们可以使用 git 跟踪文件中的一些元数据来支持此功能:

monorepo.json

{
    "path": "some/useful/lib",
    "sha": 12341234123412341234,
}

其中指sha的是整个存储库的常用 SHA。然后我们需要脚本来检出这些目录,例如在 gitignoredmonorepo文件夹下:

monorepo/som/useful/lib

每当更改文件时,您都必须向上遍历树并测试所有包含 Makefile 的目录。这是因为目录可能依赖于最新版本的子目录,因此您总是可能会破坏您上面的某些内容。

有关的:

答案3

首先,执行以下操作:

git clone --depth 1 [repo root] [name of destination directory]

然后:

cd [name of destination directory]

...最后:

git filter-branch --prune-empty --subdirectory-filter [path to sub-dir] HEAD

就是这么简单。Git 将重写存储库,以便仅包含所需的子目录。即使子目录有几层深,这种方法也能奏效。只需将目标目录命名为子目录的名称。然后在“git filter-branch”命令中输入子目录的相对路径。哦,告诉--depth 1git下载头顶的 (基本上删除了历史记录)。

答案4

简洁、现代(2020+)的答案

是的,可以使用已有好几年历史的 git 2.19+ 来完成。

稀疏克隆:

git clone --no-checkout --depth 1 --sparse --filter=blob:none \
    ssh://[email protected]:7999/$ORG/$REPO.git
cd $REPO

# git config ... # as needed

稀疏结帐:

git sparse-checkout init --cone
git sparse-checkout add relevant/dir/  # trailing / said important
cat .git/info/sparse-checkout          # to verify

git checkout $BRANCH  # should take only a moment

git status
    On branch $BRANCH
    Your branch is up to date with 'origin/$BRANCH'.

    You are in a sparse checkout with '2%' of tracked files present.

    nothing to commit, working tree clean

相关内容