Git-gitignore / exclude中的最大深度

灰色K

简短的故事:我最近在PC上进行了Arch Linux的全新安装,因为我的旧安装由于不必要的软件包和配置目录而变得very肿。现在,我想保持主目录的简洁明了。我决定使用git来监督那里的每个文件和文件夹,但是我不能仅仅排除每个日志(或任何其他不断更新的dir / file),因为这太麻烦了。
我们的想法是包括文件和目录的只有第一层$HOME/$HOME/.config/$HOME/.local/share/例如,包含.config/foo/和排除其内容,即.config/foo/*在卸载软件包时可以检查git日志,并在目录中创建了一个目录并手动将其删除(当然,如果我不再使用它了)

我试图通过添加到我的 .git/info/exclude

*/*
*/*/*
*/*/*/*
*/*/*/*/*
.local/share/*/*
.local/share/*/*/*
.local/share/*/*/*/*
.local/share/*/*/*/*/*
.config/*/*
.config/*/*/*
.config/*/*/*/*
.config/*/*/*/*/*

因为我读到git在每个目录级别都需要一个单独的通配符。您可能已经了解了-这没有用。

所以,问题是-我怎么只能监视的文件和目录$HOME/$HOME/.config/以及$HOME/.local/share/没有监控他们的内容。谢谢!

星期二

TL; DR

您需要使用的.gitignore是专门忽略某些文件和子目录的方法:

*/
!.config
!.config/*
.config/*/
!.local
!.local/*
.local/*/

要了解它是如何工作的,以及它为您做什么(和不做什么),请阅读较长的版本。!.config/*几乎可以肯定是不必要的;我在*不保存任何顶级文件的情况下将其放入,这与您要求的不完全相同。这同样适用!.local/*。不过,在没有实际测试的情况下,我不是确定是否.config/afile符合.config规则。)

(但是请注意,您可能确实希望对其他.config文件进行源代码控制。我也建议您采用完全不同的方式,对.foorc类型的文件使用符号链接,这就是我所做的。)

除了系统强加的最大深度(取决于您的操作系统)之外,没有任何最大深度。但是这里存在一个大问题:Git不存储目录。1个

Git的存储内容是在其顶层存储项(即commit)之下的文件(Git称为blobs),这些文件具有关联的路径名。如果您要求Git提取提交#1234567 ...,则Git会在该提交内部进行查找,找到各种Blob的路径名,并在必要时创建目录(新的空目录)以保存特定的Blob(即文件)。 ),Git正在使用该提交中存储的名称从该提交中提取。

这并不意味着您的想法注定要失败,只是您从一个误解入手。例如,Git根本不会保存目录 .config例如,它将仅保存文件 .config/Trolltech.conf如果Git在某个提交中保存了该文件,而您git checkout在特定的提交中保存了该文件,则Git将创建一个新的空文件(.config如果需要)。如果目录已经存在,则Git将不执行任何操作。在某些情况下,例如从该文件所在的提交转移到不存在的提交,Git也会删除该目录,但在某些情况下不会删除,您需要使用它git clean -d来使Git真正删除它(如果可能的话,即如果它是空的)。

保存了该特定文件后,如果指示Git忽略该子目录.config/git,则Git可能不会保存该文件 .config/git/ignore这就是事情变得复杂的地方。您需要了解Git提交如何工作,索引什么以及索引如何工作(在某种程度上)以及Git与工作树一起使用和维护的工作


1 Git确实存储条目,该条目可以用作保存空目录的标志,但是Git的其他部分以奇怪的方式组合在一起,使整个概念失败


Git围绕提交的概念构建

如上所述,Git从根本上存储的是提交。提交是某些文件集的完整快照,大多数情况下是独立快照,Git称之为blobs(这故意忽略了子模块和符号链接,但它们也使用类型区别于普通文件的树条目存储为Blob,它们之所以表示为blob。)我之所以说“大部分是独立的”,是因为每个提交记录了一定数量的提交哈希ID(尽管最常见)只有一个。存储三个父哈希ID的提交取决于这三个父提交的存在:缺少三个父提交的存储库某种程度上是不完整的。2父级链接对于此特定应用程序并不重要,但是最好知道它是如何工作的。

还有就是,虽然在生活中提交一个特别困难的事件:创建它。创建提交后,它是只读的。它具有唯一的哈希ID,仅由提交的内容(包括其所有父哈希ID)确定。但是哪些文件可以提交?这是关键问题,也是.gitignore最终出现在图中的地方。


2这是浅表克隆的本质一个不浅的克隆(因此是完整的)从每个分支尖端提交(以及所有带标记的提交或带注释的标记对象)开始。这些提交(或带注释的标记对象)通过其父哈希ID指向更早的祖先。由于存储库是完整的,因此这些对象也存在。它们包含父母的哈希ID和那些犯对象是否存在,等等。仅当我们到达一些没有父项的提交时,整个过程才会停止通常,这是有史以来的第一次提交,显然不能有父母。这样的提交称为 提交,并且在任何非空但完整的存储库中,始终至少有一个根提交。


新提交中文件在索引中设置

除了存储库本身(存储库是Git对象的数据库,即commit和blob)以及Git称为的中间事物(这些存储文件的名称以及其他数据)之外,Git还具有此密钥数据结构,其中包含三个不同的名称。它被不同地称为索引登台区域缓存

该索引通常几乎是不可见的。有一个Git命令,git ls-files可以直接向您显示索引的内容(git ls-files --stage,或者更详细地说,git ls-files --debug),但是对最终用户并没有真正的用处。不过,对索引的一个很好的顶层描述是,它是您构建下一个commit的地方

当您运行时git commit,Git将以索引中当前具有的任何形式获取索引中当前存在的每个文件,并从中进行新的提交。这些是存储在新提交中的文件。新提交的作者和提交人是您;时间戳为“现在”;新提交的父级是您之前签出的所有提交;但是文件-blob及其关联的名称-完全由索引中的内容设置。3同样,当您用于git checkout提取某些特定的提交时,Git首先要做的是将该提交的文件复制到索引中。

请注意,当您进行新提交时,该新提交将成为当前提交。发生这种情况时,Git会更新当前的分支名称(例如您已签出的分支)master,以便记录新的提交。实际上,每个分支名称记录一个哈希ID。Git将此称为分支尖端正如我们在上面的脚注2中所看到的,Git从分支技巧开始向后工作,以查找分支中包含的所有提交。因此,进行新提交会将新提交的哈希ID推入分支名称表中。


3即使您使用git commit -agit commit <file>,Git实际上也只是将文件复制到索引(有时(辅助)索引)中,并从该索引构建提交。


工作树

Git内部存储的所有文件,无论是在存储库中还是在索引中,都采用特殊的仅Git格式。计算机上几乎没有其他程序可以使用这些文件,因此Git将每个文件提取到可用的版本中,您可以在其中进行工作。这是您的工作树

通常,当前提交中的每个文件也会显示在工作树中。当然,当前的提交是您运行的git checkout提交。如果您只是运行git checkout master以检出master分支,那么就当前提交而言,您要做的是检出名称master标识的任何提交:该分支的尖端提交。

如上所述,所有文件(blob对象)都已复制到索引中。Git的是还能够使用任何在该指数知道什么是你的工作树前,这一点:对于任何文件在索引(因此在工作树),现在是不是在索引中,因为签出后,Git应该从工作树中删除该文件。确实如此!对于Git必须在索引中替换或添加到索引的任何文件,Git应该将索引版本复制到工作树中,并且确实如此。

之后的索引中的git checkout内容正是您检出的提交中的blob(通过任何中间树对象)到底是什么。这些文件的工作树版本将与那些文件的索引版本匹配,但实际上工作树版本可用。这些文件的索引版本将与那些文件的提交版本匹配-实际上,它们共享基础存储,因为索引仅存储路径名和Blob哈希ID。

现在,工作树中可能有一些Git知道的文件。根据定义,这些文件不在index中这些是未跟踪的文件在Git中,这就是未跟踪的文件:它是不在索引中的文件。没什么了。

(好吧,您可以从索引中删除文件。然后该文件不在索引中,因此无法跟踪。实际上并没有什么更多的,但是值得记住。)

忽略未跟踪的文件

未跟踪文件的问题在于,Git抱怨它们。:-)它一直困扰着您,告诉您文件A,B和C未被跟踪。因此,这就是问题所在.gitignore,但.gitignore工作树有关,与提交不同,工作树确实具有目录。

您可以在中列出特定文件.gitignore如果这些文件不在索引中(未跟踪),但在工作树中,则Git会抱怨它们……但随后它会看到它们已列出.gitignore并关闭。

您也可以git add使用git add .进行批量归档git add --all这使Git扫描工作树中的文件,并在找到文件时将它们git add每个都索引到索引中,以将工作树版本复制到builds-next-commit索引版本中。显然,如果文件A,B和C当前都未跟踪忽略,则Git不应添加它们。因此,.gitignore还告诉Git不要将现有的未被跟踪和忽略的文件添加到索引中。

索引中的现有文件将自动跟踪,因此任何git add可能添加这些文件的en-masse都将添加它们,而不管清单中列出了什么.gitignore换句话说,向其添加跟踪文件.gitignore不会对其产生任何影响。进入.gitignore只会影响未跟踪的文件。

但这是文件,而不是目录这就是一切变得松散的地方。文件存在于普通文件系统中的目录内部(即,不在Git中,而是在工作树中)。

Git拥有索引(并称其为cache)的主要原因之一是,查看大文件树中的每个文件往往非常慢。Git可以使用索引来记录有关所有跟踪文件的信息,包括可以加快en-massgit add --all样式操作的信息。这是该文件的罚款在索引中,但是如果是整个子目录(一)不在索引中,因此按照定义它们是未经跟踪和(b)将被忽略,所以他们不会去了索引,并保持追踪?

Git可以避免完全扫描那些子目录如果.config/dir/要忽略它,而Git刚遇到了这个名称,.config/dir并且它是一个目录,那么为什么Git可以跳过其中的阅读这比读取并检查每个文件以查看是否应该忽略它要快得多。

当Git扫描工作树时,它从顶部开始并读取树的全部内容:所有文件名和所有子目录名。它知道哪些是文件,哪些是子目录,但尚未查看任何子目录。

现在,Git检查所有文件:它们是否在索引中?如果是这样,则会对其进行跟踪:查看是否应对其进行更新。如果不是,那么它们就无法追踪:看看Git是否应该为它们抱怨。

接下来,Git检查所有子目录。对于每个子目录:索引中是否有任何文件?如果是这样,则必须检查子目录。但是,如果不是,子目录会被忽略吗?如果是这样,甚至不要看里面否则,请查看它的内部,就像在索引中有文件一样。

现在,对于每个文件或子目录,可以有一个或多个.gitignore条目。*匹配文件和目录结尾的条目*/match目录结尾的条目Mean开头的条目!明确地不被忽略

因此,假设Git正在扫描顶层并遇到name .a,它是一个文件。Git将查找任何忽略条目匹配.a如果有一个条目*/,那不匹配.a因此.a添加,除非有稍后的条目将其覆盖。没有,因此我们添加了文件.a

接下来,Git遇到.adir一个目录。.adir索引中没有文件,因此不会强制进行扫描,因此Git将检查是否有忽略项匹配.adir由于*/是唯一的匹配项,因此Git可以忽略目录。现在,它根本不会出现在内部.adir(除非,除非您以某种方式添加.adir/file到索引,这迫使Git读取.adir以检查是否.adir/file仍然存在)。

当Git遇到.config(这是一个目录)时,会有一个*/表示忽略它的消息,但是被!.config表示忽略它的消息覆盖了有一个,.config/*但这只是.config-the-directory,不是.config/something所以!.config是最后一个适用条目,和Git必须扫描.config

迟早会有4个Git出现在内部.config它可能找到.config/afile; 这匹配!.config/*它匹配的最后一个条目告诉Git,该文件不会被忽略,因此它将被添加到索引中。然后Git遇到了.config/git,它是一个目录。!.config/*然后匹配.config/*/; 所以它被忽略了。Git从不向内看.config/git

其余的重复此过程.config可能会有更多.文件,Git会照常处理这些文件,直到遇到Git为止.local,其工作原理与.config此处相同。

与往常一样,请记住,这不会影响任何现有的提交。检出任何包含某些违反.gitignore此处规则的文件的现有提交,将使Git提取该文件,并在需要时创建其父目录。从该提交移到缺少该文件的提交,Git将删除该文件,并且如果包含该目录的目录为空,通常也将5删除该目录。


4这就是深度优先与广度优先扫描进入的地方。由于Git组织索引的方式,Git当前进行了ASCII排序,深度优先的目录遍历(因此实际上是“现在”)。不过,从我们的“什么被忽略而什么不被忽略”的角度来看,这并不重要。

5我偶尔会在这里看到奇怪的行为,这使我确信其中一定有一些错误。偶尔git clean -ndf查看将要清洗的内容,或者随后git clean -df实际进行清洗,这很有用。但是我永远都无法复制它,而且它永远也不够重要,无法尝试... :-)

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章