使用稀疏检出更改仓库目录结构

73

使用 Git 1.7.0 中的新 稀疏检出功能,是否可以像 SVN 中一样只获取子目录的内容?我在这个示例中发现,但它保留了完整的目录结构。想象一下,如果我只想获取“perl”目录的内容,而不是实际命名为“perl”的目录。

-- 编辑 --

举个例子:

我的 Git 存储库包含以下路径:

repo/.git/
repo/perl/
repo/perl/script1.pl
repo/perl/script2.pl
repo/images/
repo/images/image1.jpg
repo/images/image2.jpg
repo/doc/
repo/doc/readme.txt
repo/doc/help.txt

我的需求是能够从上述代码库中生成以下布局:

repo/.git/
repo/script1.pl
repo/script2.pl

然而,目前的稀疏检出功能只能获取

repo/.git/
repo/perl/script1.pl
repo/perl/script2.pl

这并不是我想要的。


4
他们终于实施了!太棒了! - Mauricio Scheffer
为什么?有什么问题吗?为什么您希望在仓库中使用不同的目录结构并在本地使用不同的目录结构?乍一看没有多大意义。 - Jiri Klouda
2
@Jiri:我有一个包含ActionScript(客户端)和PHP(服务器端)代码的Web应用程序。这些文件密切相关,因此我想将它们放在单个repo/branch中。但是,我不希望将ActionScript源文件放在服务器上,只需要PHP文件。 - davr
2
@davr 这种情况并不是很罕见,我想要的就是这个。可惜我现在还无法得到它。 - preinheimer
@preinheimer,这也是我正在努力实现的事情。这将使我正在制作的主题的开发和测试变得更加容易。 - apokaliptis
7个回答

27

你仍然需要克隆整个代码库,这样会得到所有的文件。你可以使用 --depth 标记只检索有限数量的历史记录。

一旦代码库被克隆,read-tree 技巧将把你的“视图”限制在 .git/info/sparse-checkout 文件中列出的文件或目录中。

我写了一个快速脚本来帮助管理稀疏性,因为目前它有点不太友好:

#!/bin/sh
echo > .git/info/sparse-checkout
for i in "$@"
do
    echo "$i" >> .git/info/sparse-checkout
done
git read-tree -m -u HEAD

如果你将此脚本保存为 git-sparse.sh 并放到通过调用 git --exec-path 报告的路径中,那么你可以运行 git sparse foo/ bar/ 仅“检出” foo 和 bar 目录,或者运行 git sparse '*' 来再次获取所有内容。


谢谢你的帮助,但似乎并没有回答我的问题。请查看我更新后的问题以获得澄清。 - davr
3
是的,"sparse"只是一种筛选实际树形结构的方式,它不能移动文件。因此,你无法做到你想做的... - richq

15

简短的回答是否定的。Git将所有文件视为单个单位。

我建议您将存储库拆分为逻辑块。 一个用于perl,一个用于图像,一个用于文档。如果您还需要维护uber repo样式,则可以创建由子模块组成的存储库。


6
现在不深入探讨为什么要这样做,你的问题可能可以通过符号链接/快捷方式(symlink/shortcut)(很可能)轻松解决。
回答这个问题 - 不行,并且有一个有意义的原因。即使进行了“稀疏检出”,整个库的历史记录也会被下载。为了澄清为什么这是必要的 - 否则跟踪重命名的文件将会是一件麻烦的事情。想象一下,如果你将文件/repo_root/asd/file1.cpp移动到/repo_root/fgh/file1.cpp - 现在如果你只下载/repo_root/fgh增量,你将不知道file1.cpp的存在。因此,这意味着你必须下载所有增量。但是,这样你就拥有了一个完整的仓库,而不仅仅是其中的一个文件夹,因此仅仅/rero_root/fgh文件夹本身并不是一个仓库。当你进行提交时,这可能听起来不重要,但是Git可能不足够知道如何正常工作。
解决方法:如果你真的想这样做,你可以创建一个调用git-checkout的脚本(对于sh shell,Windows上的批处理不应该难以生成)。
!/bin/sh
curDir=`pwd`
cd $2
git-checkout $1
cp -R $3/* $4
cd $curDir

这里的第一个参数是要检出的分支,第二个参数是存储库当前所在的文件夹,第三个参数是您想要实际使用的子目录,第四个参数是您想要将其复制到的位置。

警告:我的shell技能几乎不存在,因此请在测试后使用此功能。应该很容易重新创建此脚本的反向副本,以便可以提交到存储库中。


拥有整个仓库的历史记录并不是问题,因为它不是一个大型仓库,而且我们有足够的磁盘空间。我想我们特定的用例并不常见,所以git开发人员从未考虑过添加它。这是SVN在我们身上做得更好的极少数事情之一(git在其他99件事情上做得更好,这就是为什么我们转换的原因,但仍然如此)。 - davr
Windows上的符号链接是一场噩梦 - 这是不可跟踪的。 - Alex Brown

6

博客文章已经消失了,所以我们永远不会知道。 - trash80
https://web.archive.org/web/20150819045120/http://blog.quilitz.de/2010/03/checkout-sub-directories-in-git-sparse-checkouts - Oromion

3

git filter-branch --subdirectory-filter 是你需要的命令,详见将子目录分离成独立的 Git 仓库

下面是一个小的 bash 脚本来实现这个功能。

它首先会创建原始仓库的工作副本,然后使用子目录过滤器进行筛选操作,以得到你想要的结果。

#!/bin/bash
#
# git-subdir.sh
#
git clone --no-hardlinks $1 $2

cd $2

git filter-branch --subdirectory-filter $2 --prune-empty --tag-name-filter cat HEAD -- --all

git reset --hard

git remote rm origin

refbak=$(git for-each-ref --format="%(refname)" refs/original/)

if [ -n "$refbak" ];then
    echo -n $refbak | xargs -n 1 git update-ref -d
fi

git reflog expire --expire=now --all

git repack -ad

git gc --aggressive --prune=now

针对问题中的示例,git-subdir.sh repo perl 是可行的。


3

0

看起来你想做的是将目录树重命名,以便文件最终出现在不同的地方。在我看来,你要求做的是代码/项目管理方面的反模板,因为它涉及到两个方面:模块分类(Java 模块在 Java 节点下,Perl 模块在 Perl 节点下),以及具有不同位置文件的项目,而开发人员却在其他地方查看它们。由于 Git 维护目录内容的哈希来查看更改内容,这也会破坏 Git。

Daemeon Reiydelle


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