简短的故事:我最近在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/
没有监控他们的内容。谢谢!
您需要使用的.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称之为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 -a
或git 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] 删除。
我来说两句