在Git和Mercurial中,是否可以克隆一个分支(或从给定的提交)?我的意思是,我想克隆一个中央存储库,但由于它非常庞大,我只想获取其中的一部分,并仍然能够向其做出贡献。这可能吗?比如,我只需要从标签130开始之后的内容?
如果可以,如何操作?
在Git中,有三种不同类型的部分克隆:
浅层克隆:我想从某个修订点X开始获取历史记录。
使用git clone --depth <n> <url>
来进行浅层克隆,但请记住,浅层克隆在与其他仓库交互方面有一定的限制。你只能生成补丁并通过电子邮件发送。
按文件路径部分克隆:我想要某个目录 /path
中的所有修订历史。
在Git中不可能。不过,在现代的Git中,你可以使用稀疏检出,即你拥有整个历史记录,但只检出(在工作区域内)所有文件的子集。
仅克隆选定的分支:我只想克隆一个分支(或选定的分支子集)。
是可行的。
在git 1.7.10之前不太简单:你需要手动执行克隆所做的操作,即git init [<directory>]
,然后git remote add origin <url>
,编辑.git/config
将remote.origin.fetch
中的*
替换为请求的分支(可能是'master'),然后git fetch
。
从Git 1.7.10开始,git clone
提供了--single-branch
选项,似乎是专门为此目的添加的,并且非常简单。
但请注意,因为分支通常共享大部分历史记录,所以仅克隆分支子集的获益可能比你想象的要小。
您还可以进行浅层克隆,只选择部分分支。
如果您知道人们将如何通过文件路径(在同一存储库中的多个项目)对其进行拆分,您可以使用子模块(类似于svn:externals)将存储库预先拆分为可分别克隆的部分。
hg help sparse
)
- 基于分支的部分克隆: 我需要分支Y上的所有修订历史记录:使用clone -r
如果你知道人们希望按文件路径拆分(在同一个repo中有多个项目(真没谁了)),那么可以使用子仓库(类似于svn externals)将仓库预先拆分为可单独克隆的部分。pull
有效地获取增量版本。如果你需要另一个克隆副本,只需克隆第一个克隆副本即可。从哪里获得副本并不重要(本地副本不占用额外的磁盘空间,因为它们在内部是硬链接)。所选答案提供了一个很好的概述,但缺乏一个完整的示例。
git clone --no-checkout --depth 1 --single-branch --branch (name) (repo) (folder)
cd (folder)
git config core.sparseCheckout true
echo "target/path/1" >>.git/info/sparse-checkout
echo "target/path/2" >>.git/info/sparse-checkout
git checkout
定期优化您的本地存储库占用空间 (c) (可选,谨慎使用):
git clean --dry-run # consider and tweak results then switch to --force
git gc
git repack -Ad
git prune
另请参阅:如何使用Git处理大型仓库
该方法创建一个没有子存储库的非版本化存档:
hg clone -U ssh://machine//directory/path/to/repo/project projecttemp
cd projecttemp
hg archive -r tip ../project-no-subrepos
不带子仓库的非版本化源代码位于project-no-subrepos目录中
关于Git,可能具有历史意义的是Linus Torvalds在2007年的一次演讲中从概念角度回答了这个问题,并记录并可在线观看。
问题是是否可以仅检出Git存储库中的某些文件。
Tech Talk: Linus Torvalds on git t=43:10
总之,他说Git的设计决策之一使其与其他源管理系统(他引用了BitKeeper和SVN)不同,即Git管理内容而不是文件。这意味着例如,在两个版本的子文件集上进行差异比较是通过首先获取整个差异,然后仅将其修剪为所请求的文件来计算的。另一个是您必须以全有或全无的方式检出整个历史记录。因此,他建议将松散相关的组件分成多个存储库,并提到正在进行的努力实现用于管理结构化为超级项目持有较小存储库的存储库的用户界面。
据我所知,这个基本的设计决策至今仍然适用。超级项目可能成为了现在的子模块。如果像Brent Bradburn的答案中所述,在Git部分克隆中进行重打包,请确保:
read-tree
而不是checkout
;git clone --filter=blob:none --no-checkout https://github.com/me/myRepo
cd myRepo
git sparse-checkout init
# Add the expected pattern, to include just a subfolder without top files:
git sparse-checkout set /mySubFolder/
# populate working-tree with only the right files:
git read-tree -mu HEAD
git clean --dry-run # consider and tweak results then switch to --force
git gc
git repack -Ad
git prune
使用 Git 2.32 (Q2 2021),其中 "git repack -A -d
"(man) 在部分克隆中不必要地放松了 2.32 之前承诺打包中的对象:已修复。
请查看 提交记录 a643157(2021年4月21日),作者为 Rafael Silva (raffs
)。
(由Junio C Hamano -- gitster
--于提交记录 a0f521b中合并,日期为2021年5月10日)
重新打包
:避免在部分克隆中松动承诺对象报告者:SZEDER Gábor
协助者:Jeff King
协助者:Jonathan Tan
签名作者:Rafael Silva
When
git repack -A -d
(man) is run in a partial clone,pack-objects
is invoked twice: once to repack all promisor objects, and once to repack all non-promisor objects.
The latterpack-objects
invocation is with--exclude-promisor-objects
and--unpack-unreachable
, which loosens all objects unused during this invocation.
Unfortunately, this includes promisor objects.Because the
-d
argument togit repack
(man) subsequently deletes all loose objects also in packs, these just-loosened promisor objects will be immediately deleted.
However, this extra disk churn is unnecessary in the first place.
For example, in a newly-cloned partial repo that filters all blob objects (e.g.--filter=blob:none
),repack
ends up unpacking all trees and commits into the filesystem because every object, in this particular case, is a promisor object.
Depending on the repo size, this increases the disk usage considerably: In my copy of the linux.git, the object directory peaked 26GB of more disk usage.In order to avoid this extra disk churn, pass the names of the promisor packfiles as
--keep-pack
arguments to the second invocation ofpack-objects
.
This informspack-objects
that the promisor objects are already in a safe packfile and, therefore, do not need to be loosened.For testing, we need to validate whether any object was loosened.
However, the "evidence" (loosened objects) is deleted during the process which prevents us from inspecting the object directory.
Instead, let's teachpack-objects
to count loosened objects and emit via trace2 thus allowing inspecting the debug events after the process is finished.
This new event is used on the added regression test.Lastly, add a new perf test to evaluate the performance impact made by this changes (tested on git.git):
Test HEAD^ HEAD ---------------------------------------------------------- 5600.3: gc 134.38(41.93+90.95) 7.80(6.72+1.35) -94.2%
For a bigger repository, such as linux.git, the improvement is even bigger:
Test HEAD^ HEAD ------------------------------------------------------------------- 5600.3: gc 6833.00(918.07+3162.74) 268.79(227.02+39.18) -96.1%
These improvements are particular big because every object in the newly-cloned partial repository is a promisor object.
git-repack
(man)文档清楚地说明了它在指定"-a
"时确实可以操作承诺pack文件(在独立分区中)。
可以推测,这里的陈述已经过时,因为它们来自2017年的第一个文档(而重打包支持是在2018年添加的)
请查看提交 ace6d8e(2021年6月2日)由Tao Klerks (TaoK
)提交。
(由Junio C Hamano -- gitster
--在提交4009809中合并,2021年7月8日)
签署者:Tao Klerks
审核者:Taylor Blau
确认者:Jonathan Tan
请查看technical/partial-clone
手册页面。
另外,自 Git 2.33(2021年第3季度)开始, "git read-tree
"(手册)存在一种代码路径,其中单个blob会从承诺者远程一个接一个地获取,这已被更正为批量获取。
请查看提交d3da223,提交b2896d2(2021年7月23日),由Jonathan Tan(jhowtan
)创建。
(由Junio C Hamano -- gitster
--于2021年8月2日合并至提交8230107)
cache-tree
: 部分克隆读树中的预取由Jonathan Tan签署
"git read-tree
"(man) 检查给定树引用的 blob 是否存在,但不会批量预取它们。$DAYJOB
注意到这里缺少预取,但我找不到一个最小的合并示例,它不会触发 unpack-trees.c
中的 check_updates()
中的预取(在所有这些情况下,cache-tree.c
中的预取不重要,因为所有相关的 blob 在那时都已经被预取了)。Git 2.39(2022年第四季度)避免在冗余时调用'cache_tree_update()
'。
请查看提交 652bd02, 提交 dc5d40f, 提交 0e47bca, 提交 68fcd48, 提交 94fcf0e (2022年11月10日) 由Victoria Dye (vdye
)提交。
(由Taylor Blau -- ttaylorr
--合并于提交 a92fce4, 2022年11月18日)
read-tree
:使用“skip_cache_tree_update
”选项署名:Victoria Dye
署名:Taylor Blau
When running 'read-tree' with a single tree and no prefix, '
prime_cache_tree()
' is called after the tree is unpacked.
In that situation, skip a redundant call to 'cache_tree_update()
' in 'unpack_trees()
' by enabling the 'skip_cache_tree_update
' unpack option.Removing the redundant cache tree update provides a substantial performance improvement to '
git read-tree
'(man)<tree-ish>
, as shown by a test added to 'p0006-read-tree-checkout.sh':
Test before after ---------------------------------------------------------------------- read-tree `br_ballast_plus_1` 3.94(1.80+1.57) 3.00(1.14+1.28) -23.9%
Note that the '
read-tree
' in 't1022-read-tree-partial-clone.sh
' is updated to read two trees, rather than one.
The test was first introduced in d3da223 ("cache-tree
: prefetch in partial clone read-tree", 2021-07-23, Git v2.33.0-rc0 -- merge) to exercise the 'cache_tree_update()
' code path, as used in 'git merge
'(man).
Since this patch drops the call to 'cache_tree_update()
' in single-tree 'git read-tree
', change the test to use the two-tree variant so that 'cache_tree_update()
' is called as intended.
在Mercurial中,你应该能够使用以下方法之一:
hg convert --banchmap FILE SOURCEDEST REVMAP
您可能还需要:
--config convert.hg.startrev=REV
源代码可以使用git、mercurial或其他各种系统。
我没有尝试过,但convert非常丰富。