是否可以在Git中查看存储库的子目录?
想象一下,我正在设置一个新的WordPress安装。我将为我的插件和主题自定义创建两个新目录:
wordpress/wp-content/plugins/myplugins/
wordpress/wp-content/themes/mytheme/
我想通过Git维护这些目录。在Subversion中,我将通过拥有trunk/myplugins/
和trunk/mytheme/
目录并检出子目录来实现此目的。 Git是否有办法使用单个存储库完成相同的任务?
我可能只是错过了一些Git范例的船,作为SVN的长期用户,很少接触Git。
编辑: Multiple branches存储不同的内容是处理此问题的有趣方式。
答案 0 :(得分:116)
Sparse checkouts是now in Git 1.7。
另请参阅问题“Is it possible to do a sparse checkout without checking out the whole repository first?”。
请注意,稀疏检出仍然需要您下载整个存储库,即使Git下载的某些文件不会在您的工作树中结束。
答案 1 :(得分:17)
在git中没有真正的方法可以做到这一点。如果您不会将同时影响两个树的更改作为单个工作单元进行更改,则没有充分的理由为这两个树使用单个存储库。我以为我会错过这个Subversion功能,但是我发现创建存储库的管理心理开销很小(仅仅是因为存储库存储在工作副本旁边,而不是要求我明确地选择一些位置。工作副本)我习惯于制作许多小型单用途存储库。
但是,如果您坚持(或确实需要),您可以使用mytheme
和myplugins
目录创建一个git存储库,并在WordPress安装中创建符号链接。
MDCore写道:
提交,例如 mytheme 将增加 myplugin 的修订号
请注意,如果您决定将两个目录放在一个存储库中,这不是git的问题,因为git完全不使用任何形式的单调增加修订号的概念。
在git的单个存储库中放置什么内容的唯一标准是它是否构成一个单元,即。在您的情况下,是否存在更改,在单独查看每个目录中的编辑时没有意义。如果您需要一次更改两个目录中的文件并且编辑属于一起的更改,则它们应该是一个存储库。如果没有,那就不要把它们放在一起了。
Git真的希望你为不同的实体使用单独的存储库。
子模块没有解决将两个目录保存在一个存储库中的愿望,因为它们实际上强制为每个目录设置一个单独的存储库,然后将它们放在另一个使用子模块的存储库。更糟糕的是,由于WordPress安装中的目录不是同一目录的直接子目录,并且也是包含许多其他文件的层次结构的一部分,因此使用每个目录存储库作为统一存储库中的子模块将无法提供任何好处,因为统一存储库不会反映任何用例/需求。
答案 2 :(得分:11)
我不喜欢稀疏检出的一件事是,如果要检查深度为几个目录的子目录,则目录结构必须包含通向它的所有目录。
我如何解决这个问题是在一个不是我工作区的地方克隆repo,然后在我的工作区目录中创建一个符号链接到存储库中的子目录。 Git工作得非常好,因为像git status这样的东西会显示相对于当前工作目录的更改文件。
答案 3 :(得分:10)
实际上,对于Git来说,“狭窄”或“部分”或“稀疏”的结账都是当前的重大发展。请注意,您仍会在.git
下拥有完整存储库。所以,其他两个帖子对于Git的当前状态是最新的,但是看起来,就像我们最终能够进行稀疏检查一样。 Checkout the mailing lists如果您对更多细节感兴趣 - 它们会迅速变化。
答案 4 :(得分:2)
git clone --filter
来自Git 2.19
此选项实际上将跳过从服务器获取不需要的对象:
git clone --depth 1 --no-checkout --filter=blob:none \
"file://$(pwd)/server_repo" local_repo
cd local_repo
git checkout master -- mdir/
服务器应配置为:
git config --local uploadpack.allowfilter 1
git config --local uploadpack.allowanysha1inwant 1
v2.19.0之前没有服务器支持,但是已经可以在本地进行测试。
file://$(path)
必须克服git clone
协议的恶作剧:How to shallow clone a local git repository with a relative path?
请记住,--depth 1
已经暗示--single-branch
,另请参见:How do I clone a single branch in Git?
TODO:--filter=blob:none
跳过所有blob,但仍获取所有树对象。但是在正常的仓库中,与文件本身相比,它应该很小,所以已经足够了。在以下位置被问到:https://www.spinics.net/lists/git/msg342006.html开发人员回答说,--filter=tree:0
正在这样做。
--filter
上记录了man git-rev-list
的格式。
对Git远程协议进行了扩展以支持此功能。
Git树上的文档:
进行测试
#!/usr/bin/env bash
set -eu
list-objects() (
git rev-list --all --objects
echo "master commit SHA: $(git log -1 --format="%H")"
echo "mybranch commit SHA: $(git log -1 --format="%H")"
git ls-tree master
git ls-tree mybranch | grep mybranch
git ls-tree master~ | grep root
)
# Reproducibility.
export GIT_COMMITTER_NAME='a'
export GIT_COMMITTER_EMAIL='a'
export GIT_AUTHOR_NAME='a'
export GIT_AUTHOR_EMAIL='a'
export GIT_COMMITTER_DATE='2000-01-01T00:00:00+0000'
export GIT_AUTHOR_DATE='2000-01-01T00:00:00+0000'
rm -rf server_repo local_repo
mkdir server_repo
cd server_repo
# Create repo.
git init --quiet
git config --local uploadpack.allowfilter 1
git config --local uploadpack.allowanysha1inwant 1
# First commit.
# Directories present in all branches.
mkdir d1 d2
printf 'd1/a' > ./d1/a
printf 'd1/b' > ./d1/b
printf 'd2/a' > ./d2/a
printf 'd2/b' > ./d2/b
# Present only in root.
mkdir 'root'
printf 'root' > ./root/root
git add .
git commit -m 'root' --quiet
# Second commit only on master.
git rm --quiet -r ./root
mkdir 'master'
printf 'master' > ./master/master
git add .
git commit -m 'master commit' --quiet
# Second commit only on mybranch.
git checkout -b mybranch --quiet master~
git rm --quiet -r ./root
mkdir 'mybranch'
printf 'mybranch' > ./mybranch/mybranch
git add .
git commit -m 'mybranch commit' --quiet
echo "# List and identify all objects"
list-objects
echo
# Restore master.
git checkout --quiet master
cd ..
# Clone. Don't checkout for now, only .git/ dir.
git clone --depth 1 --quiet --no-checkout --filter=blob:none "file://$(pwd)/server_repo" local_repo
cd local_repo
# List missing objects from master.
echo "# Missing objects after --no-checkout"
git rev-list --all --quiet --objects --missing=print
echo
echo "# Git checkout fails without internet"
mv ../server_repo ../server_repo.off
! git checkout master
echo
echo "# Git checkout fetches the missing directory from internet"
mv ../server_repo.off ../server_repo
git checkout master -- d1/
echo
echo "# Missing objects after checking out d1"
git rev-list --all --quiet --objects --missing=print
Git v2.19中的输出:
# List and identify all objects
c6fcdfaf2b1462f809aecdad83a186eeec00f9c1
fc5e97944480982cfc180a6d6634699921ee63ec
7251a83be9a03161acde7b71a8fda9be19f47128
62d67bce3c672fe2b9065f372726a11e57bade7e
b64bf435a3e54c5208a1b70b7bcb0fc627463a75 d1
308150e8fddde043f3dbbb8573abb6af1df96e63 d1/a
f70a17f51b7b30fec48a32e4f19ac15e261fd1a4 d1/b
84de03c312dc741d0f2a66df7b2f168d823e122a d2
0975df9b39e23c15f63db194df7f45c76528bccb d2/a
41484c13520fcbb6e7243a26fdb1fc9405c08520 d2/b
7d5230379e4652f1b1da7ed1e78e0b8253e03ba3 master
8b25206ff90e9432f6f1a8600f87a7bd695a24af master/master
ef29f15c9a7c5417944cc09711b6a9ee51b01d89
19f7a4ca4a038aff89d803f017f76d2b66063043 mybranch
1b671b190e293aa091239b8b5e8c149411d00523 mybranch/mybranch
c3760bb1a0ece87cdbaf9a563c77a45e30a4e30e
a0234da53ec608b54813b4271fbf00ba5318b99f root
93ca1422a8da0a9effc465eccbcb17e23015542d root/root
master commit SHA: fc5e97944480982cfc180a6d6634699921ee63ec
mybranch commit SHA: fc5e97944480982cfc180a6d6634699921ee63ec
040000 tree b64bf435a3e54c5208a1b70b7bcb0fc627463a75 d1
040000 tree 84de03c312dc741d0f2a66df7b2f168d823e122a d2
040000 tree 7d5230379e4652f1b1da7ed1e78e0b8253e03ba3 master
040000 tree 19f7a4ca4a038aff89d803f017f76d2b66063043 mybranch
040000 tree a0234da53ec608b54813b4271fbf00ba5318b99f root
# Missing objects after --no-checkout
?f70a17f51b7b30fec48a32e4f19ac15e261fd1a4
?8b25206ff90e9432f6f1a8600f87a7bd695a24af
?41484c13520fcbb6e7243a26fdb1fc9405c08520
?0975df9b39e23c15f63db194df7f45c76528bccb
?308150e8fddde043f3dbbb8573abb6af1df96e63
# Git checkout fails without internet
fatal: '/home/ciro/bak/git/test-git-web-interface/other-test-repos/partial-clone.tmp/server_repo' does not appear to be a git repository
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
# Git checkout fetches the missing directory from internet
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (1/1), 45 bytes | 45.00 KiB/s, done.
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (1/1), 45 bytes | 45.00 KiB/s, done.
# Missing objects after checking out d1
?8b25206ff90e9432f6f1a8600f87a7bd695a24af
?41484c13520fcbb6e7243a26fdb1fc9405c08520
?0975df9b39e23c15f63db194df7f45c76528bccb
结论:d1/
之外的所有斑点都丢失了。
请注意,root/root
和mybranch/mybranch
也丢失了,但是--depth 1
将其从丢失的文件列表中隐藏了。如果您删除--depth 1
,则它们将显示在丢失的文件列表中。
答案 5 :(得分:1)
您无法签出存储库的单个目录,因为整个存储库由项目根目录中的单个.git文件夹处理,而不是subversion的无数.svn目录。
在单个存储库中处理插件的问题是,提交例如 mytheme 会增加 myplugin 的修订版号,所以即使在颠覆它最好使用单独的存储库。
子项目的颠覆范例是svn:externals,它在某种程度上转化为git中的submodules(但不完全是因为你之前使用过svn:externals。)
答案 6 :(得分:1)
如您的编辑所指出的,您可以使用两个单独的分支来存储两个单独的目录。这确实将它们保存在同一个存储库中,但您仍然无法跨两个目录树进行提交。如果你需要对另一个进行更改,那么你必须将它们作为两个单独的提交进行更改,并且你可能会打开这两个目录的一对签出可能不同步的可能性。
如果你想将这对目录视为一个单元,你可以使用'wordpress / wp-content'作为你的repo的根目录,并使用顶级的.gitignore文件来忽略除了两个感兴趣的子目录之外的所有内容。这可能是目前最合理的解决方案。
据称,稀疏检查已经进行了两年,但在git开发回购中仍然没有任何迹象,也没有任何迹象表明必要的改变将会到达那里。我不会指望他们。答案 7 :(得分:0)
这里有一个启发。只需使用shell regex
或git regex
。
git checkout commit_id */*.bat # *.bat in 1-depth subdir exclude current dir, shell regex
git checkout commit_id '*.bat' # *.bat in all subdir include current dir, git regex
使用引号转义外壳正则表达式,并将通配符传递给git。
第一个不是递归的,只有深度为subdir
的文件。但是第二个是递归的。
根据您的情况,以下内容可能就足够了。
git checkout master */*/wp-content/*/*
git checkout master '*/wp-content/*'
只需根据需要修改行即可。
答案 8 :(得分:0)
您只能将未提交的更改还原到特定的文件或目录:
git checkout [some_dir|file.txt]