git
作为背景,我在 hp-ux 11.11 (11v1) 系统上使用。我正在做一个广泛的 .gitignore 文件来排除许多文件。我的 .gitignore 基本上是“忽略除这些目录之外的所有内容,但也忽略这些目录下的这些模式。”确切地说,如何以及为什么与问题并不完全相关。
我的目标是确保我在运行git add
之前得到了我期望得到的结果git add
。为了实现这一目标,我正在对给定目录的文件运行测试git check-ignore
。
我的方法通常如下所示:
find test2 -type f -exec git check-ignore -v -n {} \; grep !
为了清楚起见,存储库存在于根目录 (/.git) 中(它实际上符号链接到 /baz/dev/myRepo/.git),因此我git status
从 / 进行调用。我还从 / 调用 find ,以便路径匹配期望看到的内容以及操作匹配git
返回的路径的格式。find
-exec
标准输出如下所示:
.gitignore:208:!test2/backupScripts/** test2/backupScripts/CV_PostJob.sh
.gitignore:208:!test2/backupScripts/** test2/backupScripts/CV_PostJob.sh.old
.gitignore:208:!test2/backupScripts/** test2/backupScripts/CV_PreJob.sh
.gitignore:208:!test2/backupScripts/** test2/backupScripts/CV_PreJob.sh.old
.gitignore:208:!test2/backupScripts/** test2/backupScripts/CV_ScanCheck.sh
.gitignore:208:!test2/backupScripts/** test2/backupScripts/oldCH.txt
.gitignore:212:!test2/scripts/** test2/scripts/deprecated/CommvaultBackupScript.sh
.gitignore:212:!test2/scripts/** test2/scripts/deprecated/NetBackupScript.sh
.gitignore:212:!test2/scripts/** test2/scripts/benchmark.sh
.gitignore:212:!test2/scripts/** test2/scripts/migrateVolumeSync.sh
.gitignore:212:!test2/scripts/** test2/scripts/test.sh
.gitignore:206:!test2/* test2/CommvaultBackupScript.sh
.gitignore:206:!test2/* test2/NetBackupScript.sh
.gitignore:206:!test2/* test2/benchmark.sh
.gitignore:206:!test2/* test2/test.sh
一般来说,这种技术效果很好,但在某些情况下,诸如/test2/foo
和/test2/bar
之类的目录都包含大量文件,这些文件将被 .gitignore 中的模式排除:
#Ignore everything
*
#add back /test2
!test2
!test2/**
#reignore foo and bar
test2/foo
test2/bar
所以,核心问题是:我希望能够find test2 -exec git check-ignore
在不枚举 test2/foo 和 test2/bar 的情况下运行,并且不必编写命令,find test2 -name foo -prune -o -name bar -prune -o -type f -exec git check-ignore {} \;
特别是对于被忽略的子目录可能有十几个的情况,而实际上,我只是想验证 test2/ 中的文件是否会被正确包含,然后再运行一次以验证单个小子目录,例如 test2/backupScripts。
最终,我想调整 POSIX 兼容命令find . -type f -print -o -name . -o -prune
,以便它可以与 -exec 一起运行,这样 find 就会将路径传递给 git check-ignore,这是绝对的而不是相对的。但是,我不知道该命令到底是如何工作的。我可以确认它确实只扫描运行它的根目录。
我的工作受到没有现代版本的 findutils 的限制,并且该平台没有明显可用的 GNU findutils,因此 -maxdepth 选项不可用。
此外,/test2 是一个挂载点,因此从 test2 中为位于 /.git 的存储库运行 git 将需要修改 Git 的环境 ( export GIT_DISCOVERY_ACROSS_FILESYSTEM=1
) 以允许遍历挂载点边界(此时cd test2 && find . -type f -exec git check-ignore --no-index -v -n {} \; -o -name . -o -prune | grep !
工作正常)。如果可能的话我不想这样做(为了系统上 Git 的其他用途)。
那么,它究竟是如何find . -type f -print -o -name . -o -prune
工作的?是否可以对其进行修改,用一些能够从 / 调用它的路径规范替换一个或多个点?或者这是某种“魔法”(例如:只有按此顺序提供选项时才能引发所需的行为)序列,当按此特定顺序提供选项时,它会执行特定功能?在 Google 上搜索这个特定命令似乎除了以多种语言转发这个特定答案本身之外,没有返回任何其他内容:不使用递归查找
我尝试通过执行以下操作来解构此特定命令的行为:
cd /
find test2 -type f -print -o -name . -o prune
#no output
cd /test2
find . -type f -print -o -name . -o -prune
#get files in CWD as expected
find . -type f -print
#gives me all files and directories below this, as you would expect
find . -type f -print -o -name .
#gives the same as the prior command
find . -name .
#gives just the CWD entry (.)
find . -name . -o -prune
#gives just the files and folders in CWD, including the (.) entry
cd /
find test2 -name test2 -o -prune
#also gives the files and folders directly in test2, including test2 itself
find test2 -name test2 -o -prune -o -type f -print
#no output
find test2 -name test2 -prune
#returns just test2 itself
find test2 -type f -print -o -name test2 -prune
#predictably gives no output, as test2 is pruned
find test2 -type f -print -o -name foo -prune
#gives full directory list except for /test2/foo, as expected.
find test2 -type f -print -o -name "test2/*" -prune
#gives full directory list, as a / will never be in the "base" name which find tests.
cd /
find test2/* -type f -print -o -prune
#this is the closest, same as "find . -name . -o -prune" but with cd to test2 first.
答案1
find . -type f -print -o -name . -o -prune
与更规范的方法相比,这是在当前目录而不是子目录中查找常规文件的更复杂的方法:
find . ! -name . -prune -type f
也可以使用 GNU 实现find
(或复制 GNUfind
的-mindepth
/的实现-maxdepth
)来编写:
find . -mindepth 1 -maxdepth 1 -type f
(-mindepth
在这种情况下是多余的,无论如何-type f
都会排除.
(深度0))。
在:
find . -type f -print -o -name . -o -prune
.
find
是将开始搜索文件的文件。
.
本身不是常规文件,因此不会打印它,并且-o
将测试第一个文件的右侧部分。-name .
匹配,因此表达式解析为 true 并且-prune
不会运行。因为整个find
表达式包括一个-print
which是一个行动谓词,即使该文件的整个表达式解析为 true,也没有隐式打印。
现在,由于.
没有修剪,并且由于它是一个目录,因此find
将读取其内容并将表达式应用于其中的每个文件(不包括.
和..
特殊条目)。
对于那些,对于类型的文件常规的,-print
将运行。对于任何其他类型的文件,由于-name .
无法匹配,-prune
将被执行。对于那些目录类型的文件,这意味着find
不会下降到它们中。
所以其中重要的事情是:
- 常规的(
f
类型)文件被打印 - 除
.
(顶级目录)之外的目录都被修剪,因此 find 不会下降到子目录中。
答案2
从man find
:
-prune
True; if the file is a directory, do not descend into it.
它通常与-path
选项一起使用:
find $HOME -path $HOME/.config -prune \
-type f -mtime 0
将搜索$HOME
,忽略$HOME/.config
今天修改的文件。