在Git中如何检出子目录?

184

在 Git 中,是否可以检出仓库的子目录?

假设我正在设置一个新的 WordPress 网站。我将为我的插件和主题自定义创建两个新目录:

  • wordpress/wp-content/plugins/myplugins/
  • wordpress/wp-content/themes/mytheme/

我想通过 Git 来维护这些目录。在 Subversion 中,我会通过拥有 trunk/myplugins/trunk/mytheme/ 目录并检出子目录来实现此目标。那么,在 Git 中是否存在一种使用单个仓库完成相同任务的方式呢?

可能是因为作为一个长期使用 SVN 用户,很少接触 Git 的原因,我可能没有理解 Git 的某些范例。

编辑: 多个分支 存储不同内容是处理此问题的一种有趣的方式。


3
为什么不检查整个代码库,并为想要处理的子目录创建符号链接? - randomness2077
这里有简单的答案 - Peter Krauss
能否在 Git 仓库中进行 稀疏检出 并引用它? - luka5z
相关内容:https://dev59.com/fHE95IYBdhLWcg3wY81l - Gabriel Devillers
10个回答

134

9
有没有改名这些文件夹的方法?如果我用稀疏检出命令选择了 /foo/bar/foobar,是否有可能让它在本地仓库里只显示为 /foobar - graywolf
5
Git文档似乎是唯一一个完全毫无意义的。例如Sparse checkouts链接根本没有描述什么是稀疏检出或者它们的用途。 - Neutrino

29

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 子目录?

在 2021 年 1 月测试于 git 2.30.0。


18
无法在Git中真正实现这一点。如果您不会以单个工作单元同时影响两个树,则没有使用单个存储库的充分理由。我原以为会错过这个Subversion功能,但我发现创建存储库几乎没有管理心理负担(仅因为存储库存储在其工作副本旁边,而不需要我明确选择工作副本之外的某个地方),所以我习惯于只创建许多小型的单一用途存储库。
如果您坚持(或确实需要),则可以使用仅具有mytheme和myplugins目录的git存储库,并从WordPress安装中的符号链接中链接它们。

MDCore写道:

对于例如mytheme的提交将会增加myplugin的版本号。

请注意,如果您决定将这两个目录放在单个存储库中,这对Git并不是一个问题,因为Git完全取消了任何形式的单调递增的修订号概念。

在Git中,将哪些内容放在单个存储库中的唯一标准是它是否构成单个单位,即在您的情况下,是否存在更改,其中查看每个目录的编辑时没有意义。如果您需要同时编辑两个目录中的文件并且编辑是相关的,则它们应该在一个存储库中。否则,请不要将它们组合在一起。

Git确实非常希望您为不同的实体使用单独的存储库。

子模块

子模块并不解决将这两个目录保留在一个存储库中的愿望,因为它们实际上会强制使用单独的存储库来管理每个目录,并通过子模块在另一个存储库中集成它们。更糟糕的是,由于WordPress安装中的目录不是同一个目录的直接子目录,也是具有许多其他文件的层次结构的一部分,将每个目录的存储库用作统一存储库中的子模块实际上没有任何好处,因为统一存储库不会反映任何使用情况/需求。


16

我不喜欢稀疏检出的一件事情是,如果你想要检出深度为几个目录的子目录,那么你的目录结构必须包含通往该目录的所有目录。

我是这样解决的:在非工作区域克隆repo,然后在我的工作区域目录中创建一个符号链接到库中的子目录。 Git能够很好地处理这种情况,因为像git status这样的操作将显示相对于当前工作目录的更改文件。


1
这只能在支持符号链接的操作系统上工作。他们需要改变稀疏检出的工作方式。 - Anders Lindén
1
对于在检出目录上使用符号链接的想法表示赞同。然而,稀疏检出和符号链接并不是互斥的:您不需要完整的克隆。 - apitsch

10

实际上,Git正在进行“狭窄”、“部分”或“稀疏”检出的开发。请注意,您仍将在.git下拥有完整的存储库。因此,其他两篇文章适用于Git的当前状态,但看起来我们最终将能够进行稀疏检出。查看邮件列表,如果您对更多细节感兴趣-它们正在快速变化。


好的,知道了!我喜欢在一个仓库下拥有这样密切相关的目录,如果可能的话,我会这样做。 - Annika Backstrom

1

正如您的编辑所指出的那样,您可以使用两个单独的分支来存储两个单独的目录。这确实将它们都保留在同一个存储库中,但您仍然无法跨越两个目录树进行提交。如果您在其中一个目录中进行更改需要对另一个目录进行更改,则必须将其作为两个单独的提交进行,并且您会打开两个目录的一对检出不同步的可能性。

如果您想将这对目录视为一个单位,则可以使用“wordpress/wp-content”作为您的存储库的根,并在顶层使用.gitignore文件来忽略除感兴趣的两个子目录以外的所有内容。这可能是目前最合理的解决方案。

稀疏检出据说已经出现了两年,但是在git开发存储库中仍然没有迹象表明它们将到达那里,也没有任何迹象表明必要的更改将永远到达那里。我不会指望它们。


1

我看了不同的回答,包括来自如何仅克隆Git存储库的子目录?的回答。

它们并不是非常简单的答案,所以我决定编写一个小的shell脚本来帮助简化这个过程。 请参见https://gist.github.com/hiranp/a26e334369386211709f4846929a6157

#!/bin/env bash

# This script clones the remote repository using the --filter=blob:none option to avoid downloading any file contents. 
#   It then checks out the specified remote branch and enables sparse-checkout. The sparse-checkout pattern is set to only 
#   include the desired folder, and finally, the latest changes are pulled from the remote branch.

# NOTE: 
# Customize this script by setting the REMOTE_REPO_URL, REMOTE_BRANCH, GIT_FOLDER_PATH, and LOCAL_REPO_PATH variables.

# Set the remote repository URL
REMOTE_REPO_URL="<URL>"

# Set the remote branch name
REMOTE_BRANCH="branch-name"

# Set the path to the folder you want to copy
GIT_FOLDER_PATH="path/to/folder"

# Set the path to the local repository
LOCAL_REPO_PATH="path/to/local/repo"

echo "Cloning the remote repository...to ${LOCAL_REPO_PATH}"
if [ ! -d "${LOCAL_REPO_PATH}" ]; then
    mkdir -p "${LOCAL_REPO_PATH}"
    # Shadow clone the remote repository
    git clone --depth 1 --no-checkout --filter=blob:none "${REMOTE_REPO_URL}" "${LOCAL_REPO_PATH}"
fi

# Change to the repository directory
cd "${LOCAL_REPO_PATH}"

# Checkout the remote branch
git checkout "${REMOTE_BRANCH}"

if [ ! -f ".git/info/sparse-checkout" ]; then
    # Enable sparse-checkout
    git sparse-checkout init

    # Set the sparse-checkout pattern to only include the desired folder
    git sparse-checkout set "${LOCAL_REPO_PATH}"
fi

# Pull the latest changes from the remote branch
git pull origin "${REMOTE_BRANCH}"

1

你无法检出存储库的单个目录,因为整个存储库由项目根目录中的单个 .git 文件夹处理,而不是像子版本控制系统中的众多 .svn 目录。

在单个存储库中开发插件的问题在于,对于例如 mytheme 的提交将增加 myplugin 的修订号,因此即使在子版本控制系统中,最好也使用单独的存储库。

子项目的子版本控制系统范例是 svn:externals,在 git 中有些类似于 submodules(但如果您以前使用过 svn:externals,则不完全相同)。


1

这里有一个灵感。只需利用 shell regexgit 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

使用引号来转义 shell 正则表达式并将通配符传递给 git。

第一个命令不是递归的,只会在 1 级子目录中查找文件。但第二个命令是递归的。

对于您的情况,以下命令可能已经足够了。

git checkout master */*/wp-content/*/*
git checkout master '*/wp-content/*'

根据需要修改这些代码行。


0
您只能将未提交的更改还原到特定的文件或目录:
git checkout [some_dir|file.txt]

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