Git clean exclude嵌套子目录

fdbelqdn  于 2023-09-29  发布在  Git
关注(0)|答案(4)|浏览(113)

我在使用git clean和exclude选项时遇到了问题。
我想从仓库中清除所有未提交的文件,不包括vendor/bundle目录。我的测试仓库像这样:

debugg-dir/
  .git/
  file.txt
  not-commited-file
  not-commited-folder
      another-not-commited-file
  vendor/
    bundle/
      another-not-commited-file

使用以下内容复制测试存储库:

git init debugg-dir && cd debugg-dir
touch file.txt && g add . && git commit -m "Commit" 
mkdir -p not-commited-folder && touch not-commited-folder/another-not-commited-file
mkdir -p vendor/bundle && touch vendor/bundle/another-not-commited-file && touch not-commited-file

Git clean命令:

git clean -d -x -n -e vendor/bundle

清洁后预期有:

debugg-dir/
  .git/
  file.txt
  vendor/
    bundle/
      another-not-commited-file

有什么方法可以从git clean命令中排除嵌套目录吗?

#编辑:
说明:

对于这种情况没有“干净”的解决方案。Git clean排除了git clean -d -x -n -e dir_name的目录,但这不适用于嵌套的目录。这是git中的bug,还是有什么好的理由?更多的信息为什么这不工作,你可以在源代码中找到。长话短说,exclude模式只适用于字符串,直到模式中的第一个'/'。

我的解决方案:

cd vendor && git clean -dxf -e bundle && cd ..
git clean -dxf -e vendor

有了这个我设法只保留嵌套的目录和它的内容。

dgenwo3n

dgenwo3n1#

这是因为vendor是一个未跟踪的目录,并且您正在使用选项- d
the manual说:

除了删除未跟踪的文件外,还删除未跟踪的目录。如果一个未被跟踪的目录由另一个git仓库管理,默认情况下不会被删除。如果你真的想删除这样的目录,请使用-f选项两次。
我可以使用以下命令获得所需的输出:

git clean -x -n

这在真实的中行得通吗?如果没有,您可能希望在vendor/bundle中提交其他文件,然后查看。

23c0lvtd

23c0lvtd2#

这是我用来清理git仓库的代码,同时排除了venv/目录和子目录:

git clean -nXd -e \!venv -e \!venv/**

对于您的情况,第一个排除就足够了:

git clean -nXd -e \!vendor

第二个排除项\!venv/**用于.gitignore中的其他规则,这些规则可能适用于供应商内部的文件或文件夹。例如:

.gitignore    
*.log

vendor/
  bundle/
    another-not-commited-file.log
k0pti3hp

k0pti3hp3#

根据git clean --help
git-clean -从工作树中删除未跟踪的文件
如果你加上弗洛伊德Pink关于-d的解释(简单地说,该选项允许删除未跟踪的目录,而不仅仅是文件),那么这就是为什么你也会删除vendor
现在,假设你只想删除not-commited-file(所以,既不是任何未跟踪的目录,也不是another-not-commited-file),我认为你应该git clean交互模式,所以

git clean -i

这将询问你如何处理每个未跟踪的文件(只有文件,添加-d,如果你想要求的目录太)。
OP问题编辑后EDIT:你也想删除目录,所以运行

git clean -i -d

编辑2:由于我从手册中不清楚-e的含义,我在谷歌上搜索了一下,找到了this。我建议阅读对话,因为它解释了-e的真实的含义,这不是OP的意图(或者可以从手册中理解)
EDIT 3,更多关于-e交换机。在 edit 2 中找到的链接之后,我决定尝试一下。这里的结果,我希望这将有助于您了解-e.

.gitignore的内容,所以我不提交临时文件:

*.tmp

我下达了命令:

echo "Temporary file" > sample.tmp
git st //which of course shows *nothing to commit, working directory clean*
git clean -fX -e \!sample.tmp

其结果是,所有文件与tmp扩展名被删除(由于-X),但sample.tmp。所以,总而言之,-e真正做的,在我的理解中,如果我错了,请纠正我,不是从清洁过程中排除模式,
从清理规则中排除模式(在我的例子中,规则是删除所有.tmp文件,我手动排除了sample.tmp)。

bvn4nwqk

bvn4nwqk4#

Git 2.24(2019年第4季度)使**git clean**在嵌套的Git * 存储库 *(不仅仅是文件夹)方面更加强大
参见commit 69f272b(2019年10月1日)和Elijah Newren ( newren )commit 902b90ccommit ca8b539commit 09487f2commit e86bbcfcommit 3aca580commit 29b577bcommit 89a1f4acommit a3d89d8commit 404ebcecommit a5e916ccommit bbbb6b0commit 7541cc5(2019年9月17日)。
(由Junio C Hamano -- gitster --合并于commit aafb754,2019年10月11日)

clean:避免删除嵌套Git存储库中未跟踪的文件

用户希望嵌套的git仓库中的文件保持独立,除非足够强制(使用两个-f)。
不幸的是,在某些情况下,git会同时删除嵌套仓库中被跟踪的(可能是脏的)文件和未被跟踪的文件。
为了解释这是如何发生的,让我们对比几个案例。
首先,以下面的示例设置为例(假设我们已经在git repo中):

git init nested
cd nested
>tracked
git add tracked
git commit -m init
>untracked
cd ..

在这个设置中,一切都按预期工作;运行'git clean -fd'将导致fill_directory()返回以下路径:

nested/
nested/tracked
nested/untracked

然后correct_untracked_entries()会注意到这可以被压缩为:

nested/

然后,由于“nested/”是一个目录,我们将调用remove_dirs("nested/", ...),它将检查is_nonbare_repository_dir(),然后决定跳过它。
但是,如果有人也创建了一个忽略的文件:

>nested/ignored

那么运行'git clean -fd'会导致fill_directory()返回相同的路径:

nested/
nested/tracked
nested/untracked

correct_untracked_entries()会注意到我们忽略了nested/下的条目,因此将此列表简化为

nested/tracked
nested/untracked

因为这些不是目录,所以我们没有call remove_dirs(),因为call remove_dirs()是唯一进行is_nonbare_repository_dir()安全检查的地方--导致我们删除了未跟踪的文件和被跟踪的(可能是脏的)文件。
解决这个问题的一个可能的方法是遍历每个路径的父目录,检查它们是否代表非空存储库,但这将是一种浪费。
即使我们添加了某种缓存,它仍然是一种浪费,因为我们应该能够检查“nested/”表示一个非空的存储库,甚至在第一次进入它之前。
dir_struct.flags中添加一个DIR_SKIP_NESTED_GIT标志,并使用它来防止fill_directory()和朋友下降到嵌套的git repos中。
通过此更改,我们还修改了commit 91479b9中添加的两个回归测试(“t7300:add tests to document behavior of clean and nested git”,2015-06-15,Git v2.6.0-rc0).
那次提交、它的系列以及邮件列表上该系列的前六次迭代都讨论了为什么这些测试要对它们所做的期望进行编码。
事实上,他们的目的似乎只是测试 * 现有 * 行为,以确保性能变化不会改变行为。
然而,这两个测试直接反驳了manpage的声明,即需要两个-f来删除嵌套的git仓库下的文件/目录。
虽然有人可能会争辩说,用户给出了一个明确的路径,该路径与嵌套存储库中的文件/目录相匹配,但一旦你沿着这条路线走下去,用户就很难理解这条滑坡(例如如果他们指定“git clean -f -d '*.c'“呢)
也很难解释确切的行为是什么;通过使其变得非常简单来避免此类问题。
最后,-ffd仍然有一些bug没有清理干净(例如:缺少嵌套的.git)和-ffdX可能清理错误的文件(注意外部.gitignore而不是内部)。
这个补丁根本不解决这些情况(也不改变与这些标志相关的行为),它只修复了在给定单个-f时的处理。
有关-ffd[X?]错误的更多讨论,请参阅this thread
使用Git 2.25.1(Feb. 2020),“git clean”中的一个角落案例错误,该错误源于目录枚举API中的笨拙调用约定(出于性能原因)。
参见commit 0cbb605commit ad6f215(2020年1月16日)by Jeff King ( peff )
参见commit 2270533(2020年1月16日),作者Elijah Newren ( newren )
参见commit f365bf4(2020年1月16日),作者Derrick Stolee ( derrickstolee )
(由Junio C Hamano -- gitster --合并至commit 7ab963e,2020年2月5日)

dirtreat_leading_path()read_directory_recursive(),第2轮

签字人:伊莱贾·纽伦
我想把这个标题命名为“dirtreat_leading_path()read_directory_recursive()的更多同步”,向commit 777b42034764(“dir:synchronize treat_leading_path() and read_directory_recursive()“,2019-12-19,Git v2.25.0-rc 0--merge),但标题太长。
不管怎样,首先是背景故事…
fill_directory()总是有一个稍微容易出错的接口:它返回一个路径的子集,这些路径 * 可能 * 匹配指定的路径规范;它的目的是修剪掉一些不匹配指定路径规范的路径,并至少保留所有匹配它的路径。
给定这个接口,调用者负责对结果进行后处理,并检查每个结果是否实际匹配路径规范。
builtin/clean.c做了这个。
它将首先修剪掉重复项(例如如果返回“dir“以及“dir/“下的所有文件,则它会将其简化为“dir“),并且在修剪重复项之后,它会将剩余路径与指定的路径规范进行比较。

但是,这种后处理本身可能会遇到问题,如commit 404ebceda01c(“dir:also check directories for matching pathspecs”,2019-09-17,Git v2.24.0-rc0 -- merge listed in batch #8):
对于git clean和一组路径规范“dir/file”和“more”的情况,这导致了一个问题,因为我们最终会得到两个目录项:“dir”和“dir/file
然后,correct_untracked_entries()会尝试删除“dir/file”,因为它在“dir”下,为我们删除重复项,留下“dir”。
由于原始的路径规范只有“dir/file”,所以剩下的唯一条目不匹配,没有留下任何要删除的内容。
(Note如果仅指定一个路径规范,例如,只有“dir/file“,那么fill_directory中的common_prefix_len optimizations将使我们绕过这个问题,使它出现在简单的测试中,我们可以正确地删除手动指定的路径规范。
该提交修复了这个问题-当指定多个路径规范时-通过确保fill_directory()不会在common_prefix_len优化路径之外返回“dir”和“dir/file”。
这就是它开始变得有趣的地方。
commit b9670c1f5e6b(“dir:fix checks on common prefix directory”,2019-12-19,Git v2.25.0-rc 0--merge),我们注意到common_prefix_len没有做适当的检查,让各种东西通过,导致递归到.git/目录和其他疯狂的事情。
所以它开始锁定并检查代码路径中的路径名。
commit 777b42034764(“dir:synchronize treat_leading_path() and read_directory_recursive()“,2019-12-19,Git v2.25.0-rc 0--merge),其中指出:
我们的优化是避免在所有路径规范都有一个公共的引导目录时调用read_directory_recursive(),这意味着我们需要匹配read_directory_recursive()在从根目录调用时所使用的逻辑。
由于它不仅仅是调用treat_path(),因此我们需要复制相同的逻辑。
......然后,它更有力地解决了这个问题,用这个奇妙的讽刺声明:
需要像这样复制逻辑意味着它保证有人最终需要做进一步的更改,忘记更新两个位置。
为了避免这样的bug并简化代码,直接修改leading_directory的特殊 shell 是很有诱惑力的,但是unpack_treesverify_clean_subdirectory()也调用read_directory(),并且使用非空的前导路径,所以我不确定是否要进一步重构。
treat_leading_path()read_directory_recursive()中添加令人讨厌的警告,以尝试警告人们此类问题。
你可能会认为,有了这样一个措辞强烈的描述,它的作者实际上已经确保了treat_leading_path()read_directory_recursive()中的逻辑确实匹配,并且在写这一段的时候,所需要的一切至少已经被复制了。
但你错了,我弄错了部分逻辑。

相关问题