public 存储库应该包含什么 .gitignore 文件?
What should contain .gitignore file when is a public repository?
我一直在学习有关 .gitignore 文件的所有知识,但有一个问题我想解决。 .gitignore 应该包含所有你想忽略的文件。因此,您应该忽略由 OS、您正在处理的 IDE 生成的文件...我的问题出现在存储库位于 Github 上,人们可以克隆它并推送更改.这些人可以使用其他操作系统,可以使用其他IDE。因此,gitignore 应该忽略这些其他 OS 和 IDEs.
生成的文件
你应该怎么办?你必须在 gitignore 中写入所有操作系统生成的所有文件以及所有 IDEs 生成的所有文件吗?
repository is on Github and people can clone it and push the changes
这是您设置某种质量门槛的地方,例如代码审查。它们旨在进行讨论,并让其他人审视这些变化。查看差异,您会注意到其他无用的东西,例如IDE 个文件。然后,您请贡献者删除那些并重新提交。
在大多数 OSS 的情况下,我认为,维护者有一个存储库,这是由贡献者 cloned/forked 提供的,当他们想要引入更改时,他们会进行 PR。因为您通常不希望 任何人 对您的代码进行更改,所以您将写入权限限制在您信任的人身上,因此其他人无法直接推送到主仓库。
对于较小的项目,您认识所有的贡献者,仍然有可能不小心引入不需要的文件,这仍然是为什么您需要在合并到主流之前进行代码审查之类的事情。
不管怎样,这是一个流程问题,不一定是 git
。视情况而定。就像任何其他重复性工作一样,当您注意到一种模式时,将其自动化。
您对必须考虑任何可以做出贡献的人的所有系统感到有点害怕,这是对的,但您不必考虑。
- 我认为大多数语言都有代码检查器,因此您可以强制执行编码风格(例如制表符与空格)。
- 此外,您通常知道一种语言可以生成哪些文件,例如
.exe
s,.dll
,因此您可以将它们添加到 .gitignore
文件中。
- 对于任何漏掉的内容,都有拉取请求。
我想马上强调两个背景要点:
如果您拥有存储库,则您设置规则。任何你为迎合他人而做的事情都只是一般的友善。
动词 ignore 充其量是……棘手。我稍后会描述我的意思。重要的是在 .gitignore
中列出一个文件并不完全 忽略 它,除非你对 "ignore".[=108= 这个词有一个奇怪的个人定义]
也就是说,友好的方法是让您的 存储库 仅忽略 您的项目 将生成的文件。然后,让您的个人忽略文件忽略您的系统将生成的文件。
让我们用一个具体的例子。假设您有一个带有 Python 的项目,其中 运行ning python foo.py
创建 foo.pyc
、foo.pyo
、and/or __pycache__/*
文件,none 其中应该永远提交。因此,您将从:
*.pyc
*.pyo
__pycache__/
在您的 .gitignore
中,因为任何使用您的项目的人——您或您的 co-workers,或其他任何人——最终都会得到这些 Python "object code" 文件,特定于特定 Python 版本,因此不应包括在内。
但假设您个人正在使用 MacOS 及其 Finder。 Finder 程序创建名为 .DS_Store
的文件。所以你很可能想添加:
.DS_Store
给你的.gitignore
。那不是 错误 ,但它对使用 Windows 的人没有任何好处。 Windows 人需要忽略哪些文件?我不确定,我不使用 Windows。然而,Linux 人可能想忽略 vim
编辑器创建的 .*.swp
文件。
如果你把 .DS_Store
放在你自己的 $HOME/.gitignore
中,而 Linux 人把 .*.swp
放在 他们的 $HOME/.gitignore
,你们所有人都会对你的项目有愉快的体验。此外,您会对 他们的 项目有愉快的体验,他们没有列出 .DS_Store
因为他们开始于 Linux.
这就是一般的想法:您的项目(存储库).gitignore
应该列出将在[=中找到的文件的名称或name-patterns 481=] 在处理您的项目时,但不应将其提交给项目。换句话说,不是OS-specific,而是project-specific。 其他文件名模式——OS-specific、editor-specific、IDE-specific, 等等——可以进入其他忽略文件,因此不需要在项目的 .gitignore
文件中列出。将它们列在项目文件中不一定伤害,但如果每个人对事情都很明智,那也无济于事。
Less-important 不属于实际答案的背景(您可以在这里停止阅读!)
人们发现 Git 的 .gitignore
文件令人困惑。 (我做了,从 Whosebug 上的数百个问题来看,几乎每个人都这样做了。)我认为其中很大一部分来自误解 Git 的存储模型。
关于 Git 的第一件事——可能是 最重要的事情——是 Git 不是关于 files,也不是关于 branches。 Git 实际上就是 提交 。 Git 存储库的核心由两个数据库组成。大数据库保存 提交 和支持提交所需的其他内部 Git 对象。
这个包含 Git 提交和其他 Git 对象的大数据库是 git clone
副本。还有一个较小的数据库,包含 names: 分支名称、标签名称等。此数据库对其他 Git 可见,因此 可以 被 git clone
复制,但通常它不仅仅是复制。相反,git clone
读取较小的数据库并 修改 它,完全丢弃一些名称并更改其他名称。因此,当您使用 git clone
时,您将获得大数据库的副本(所有提交)和小数据库的修改后的 sawed-down 副本。 (我们不会在此处仔细查看较小的文件,因为它不会影响 .gitignore
个文件。)
提交本身都有唯一的哈希ID。这些是由字母和 digit 组成的又大又丑的字符串,例如 b994622632154fc3b17fb40a38819ad954a5fb88
。 Git 存储库可以快速判断它是否与其他 Git 存储库具有相同的提交:发送 Git 仅列出哈希 ID。接收方 Git 只是检查:我是否有使用该哈希 ID 的提交? 如果是,接收方 Git 有 那个 提交。它不需要再次获得它。如果没有,接收方 Git 需要获得该提交。
这意味着您的第一个 git clone
可能会很慢:您可能必须获取许多兆字节的对象。不过,在那之后,更新 克隆只是获得任何新提交的问题 他们 有 你 还是需要的。你的 Git 打电话给他们Git,他们列出了一些哈希 ID,你的 Git 知道要得到什么,他们的 Git 知道你有什么。或者,如果您向他们提交了新的提交,您的 Git 会调用他们的 Git,为他们提供一些哈希 ID,他们可以说 我已经有了那个 [= =342=] 或 我没有那个,给我!
当然,还有更多内容。接下来要知道的是,每次提交都会存储 每个 文件的完整快照。这些文件以特殊的 read-only、Git-only 冻结格式存储,其中文件为 de-duplicated。提交存储文件的事实是 Git,它实际上只关心提交本身,最终为我们存储文件。冻结和 de-duplicated 格式 是存储库不会变得非常胖的原因,即使 每个 提交都有 [=206] 的完整副本=]每个文件:大多数提交只是re-use上一次提交的文件,这意味着Git不必存储新副本。
但是,如果提交中的文件是冻结的 Git-only 格式,您计算机上的其他程序都无法使用,您将如何实际 使用 这些文件?答案是:你不会。也就是说,您不会使用 这些 文件。 Git 将做的是提取 这些文件到某处。 "somewhere" 是您的 工作树 或 work-tree.
这里值得一提的是,虽然我们不会深入探讨,但每个提交不仅存储一个冻结的快照,而且还存储一些额外的 元数据 。这主要是您在 git log
输出中看到的内容:例如,谁进行了提交、何时提交以及为什么提交。 why 部分取决于提交的人:它是日志消息。一条好的日志消息很有价值。 Git 可以告诉你 发生了什么: Git 将比较以前的,或 parent,commit 的快照与当前或child 提交的快照,对于每个不同的文件,Git 将向您展示将父副本更改为子副本的配方。但是 Git 无法告诉您 为什么 添加或删除了某些行。只有这样做的人才能说出 为什么 他们那样做。
这意味着您看到和使用的文件根本不在 Git 中
如果你运行:
git clone https://github.com/git/git
并拥有 Git 的副本,您可以查看 Git 的来源:有一个 Makefile
、一个 README.md
,等等。但这些是您计算机上的普通文件。它们不是 in 提交的文件。 它们是 Git 通过从快照中提取提交的文件制作的副本。 这些副本在您的工作树或 work-tree 中。您可以使用文件查看器查看它们,在编辑器中打开它们,等等。但它们不在 Git 中。它们在你的work-tree,任你随心所欲。
Git 将在您要求时随时提取对您的 work-tree 的任何给定提交:
git checkout v2.21.0
例如,将使用 标签 v2.21.0
来查找特定的提交哈希 ID(8104ec994ea3849a968b4667d072fedd1e688642
,确切地说)并提取 那 承诺你的 work-tree。 (如果你有一个 2.23 或更高版本的 Git,你可以使用 git switch
而不是 git checkout
:它们在这里做完全相同的事情。)这个提取过程包括 从你的 work-tree 中删除 你的 文件,并根据你要切换到的提交创建新文件。但是所有这些文件都是你的文件,而不是Git的。
幸运的是,git checkout
/ git switch
进行了一些安全检查,以避免在您未保存所做的某些更改时删除 您的 文件。您可以关闭此功能(例如,git checkout --force
)或故意使用其他破坏性命令(git reset --hard
)来清除未保存的工作。在所有情况下,你基本上只是告诉 Git 删除 你 对 你的 文件所做的事情并取回其他版本,例如保存在其他提交中的版本,来自 Git 的 文件。
Git的索引或暂存区
如果 Git 只使用了两件事——它的提交,其中一个是 当前 提交,和你的 work-tree——那么 git commit
本身会很简单。不幸的是,Git 隐藏了 第三个 位置来保存每个文件。当您 select 通过 git checkout
或 git switch
进行某些提交时,成为 当前 提交,Git 不会 只需 将提交的快照提取到您的work-tree。相反,它首先将该提交的快照提取到 Git 的 index.
索引很复杂并且有多种用途,但它的主要用途实际上很容易描述,并且是您应该记住的开始:索引是您构建 next 你打算做的承诺 这就是它得名 暂存区 的原因。该索引包含每个文件的副本1,最初是从提交中获取的。您的 work-tree 也有一份副本。所以有三个个活动副本:
- 你可以看到
git show HEAD:README.md
的那个被冻结到提交中。
- 你可以用
git show :README.md
看到的那个在Git的索引里。它是冻结的格式,但它是可替换的,与提交中的不同。 (这些文件有点 half-in Git:准备提交,但尚未实际提交。)
- 您实际上可以 使用 的文件(在普通文件中)就是普通文件
README.md
。这是 你的 而它根本不在 Git 中。
当您 运行 git commit
、Git 收集适当的元数据时,立即冻结其索引 中的任何文件 ,并且使用这些作为新提交的新快照。
如果 :README.md
匹配 HEAD:README.md
,这两个文件是重复的,所以新提交只是 re-use 文件。如果不是,它可能与其他提交和 de-duplicates 匹配,或者它可能是全新的,并且实际上是真实存储的。在任何情况下,一旦您提交它,它就会被冻结并且现在真的完全在 Git 中。但是,如果您更改了 您的 work-tree 副本 README.md
,您可能希望 Git 冻结更新后的 README.md
。 这就是 git add
的用武之地。
git add
命令告诉Git:使索引副本匹配我的work-tree副本。即Git将从您的 work-tree 复制(并压缩成冻结格式)您更新后的 README.md
文件,并将副本放入其索引中的 :README.md
中。所以这就是为什么你总是需要 git add
文件:每次你改变 你的 副本,如果你想要 Git 改变它的 建议下一次提交复制,你必须再次git add
。
当你 运行 git commit
之后,Git 将获取所有 index 文件并将它们冻结到一个新的提交中。因为索引副本都是冻结的格式,所以这个过程可以而且通常确实非常快。
1从技术上讲,索引包含的不是数据的实际副本,而是文件名、模式和 blob 哈希 ID.除非并且直到您开始直接使用 git ls-files --stage
或 git update-index
深入研究索引,否则您无法真正分辨出区别。因此,可以将索引视为具有文件的完整副本:Git 隐藏了 blob-object 技巧,以至于您无需关心。
这就是 .gitignore
发挥作用的地方
Git 从其索引而不是您的 work-tree 进行新提交。你的 work-tree 是 你的 ,随心所欲。当您告诉 Git 覆盖它时,您只需要小心一点,因为 work-tree 中的 none 个文件是 在 Git(它们最多在旁边或在旁边Git)。但这也意味着您可以在 work-tree 中创建您不想 Git 存储到其任何提交中的文件。由于这些文件不在提交中,并且只有 提交 被 git clone
复制,这些文件不会出现在任何克隆中。
对于像 *.pyc
或 *.o
来自 cc
或 c++
的编译器输出文件,或者来自 Java 编译器的输出,或者其他什么,那是一件好事:您通常 不希望 这些文件出现在任何克隆中。
但是如果这些文件就在您的 work-tree 中,有两件事可能会出错:
git status
会唠叨他们.
- 如果您使用 en-masse
git 添加 <em>everything</em>
操作,git add
将 将这些文件作为新文件复制到Git的索引中,现在如果你git commit
. 它们就会被提交
在 .gitignore
中列出文件名是防止这两种情况发生的一种方法。但是这里有一个技巧:如果一个文件已经在 Git 的索引中,将它列在 .gitignore
中是没有效果的。
Git 索引中的文件被称为 tracked。 跟踪的文件 是 Git 的索引 现在 中的文件。 未跟踪 文件存在于您的 work-tree,但现在 不在 Git 的索引中。
记住,您现在可以使用 git add
将 all-new(至 Git)个文件放入 Git 的索引中。您现在还可以使用 git rm
从 Git 的索引中完全取出文件。所以索引的内容是不固定的。 git checkout
填充 索引,然后您可以——并且将会——修改它:您将替换 任何文件你想在下一次提交时更新。
当您 运行 git status
时,status
命令会进行两次单独的比较。首先它告诉你其他有用的东西,但我们将跳过它并进行两个比较:
两个比较中的第一个将当前提交或HEAD
与索引中的内容进行比较。对于每个完全匹配的文件,git status
什么都不说。如果有一些文件 不 匹配——或者是新的或丢失的——git status
说 changes staged for commit 并列出这些文件的名称。
第二次比较是将索引与您的 work-tree 进行比较。对于每个完全匹配的文件,git status
什么都不说。如果有一些文件 不 匹配或丢失,git status
会说 changes not staged for commit 并列出这些文件' 名称。
这里的一个特例是 未跟踪的 文件:对于每个未跟踪的文件,git status
列出文件名,2 调用这些 未跟踪的文件 。但是,如果您在 .gitignore
中列出这些名称,git status
闭嘴 。
请注意,已跟踪 文件不会发生任何特殊情况。这些已经在 Git 的索引中。它们包含在第一次比较中,Git 会将索引副本与 work-tree 副本进行比较,无论文件是否列在 .gitignore
.
中
所以从这个意义上说,这些 .gitignore
条目并不意味着 忽略 文件。他们的意思是在文件未被跟踪时闭嘴。当它被跟踪时,它们没有任何作用。
同时,git add
有 .
和 *
(以及其他)用于对许多或 en-masse add 操作所有的文件。如果所有文件包含未跟踪的文件,这些操作将非常不方便。因此,在 .gitignore
中列出文件名或模式会抑制 en-masse 添加操作。它甚至会抑制故意 git add
:
$ touch foo.pyo
$ git add foo.pyo
The following paths are ignored by one of your .gitignore files:
foo.pyo
Use -f if you really want to add them.
所以也许 .gitignore
应该被称为 .git-do-not-complain-about-these-untracked-files-and-do-not-automatically-add-them-when-using-en-masse-add-operations-or-even-explicit-requests
,或者类似的名称。但是谁愿意输入那种名字呢?所以 .gitignore
是。
2从技术上讲,每次需要 git status -uall
或 git status -u
时都可以得到它。否则,它有时会将物理上存储在单个文件夹中的一堆文件组合在一起,并且只麻烦提及文件夹名称。
一般来说,您在典型项目中放入 .gitignore
的内容包括您的构建系统可能生成的任何内容,以及任何特定于用户的配置文件(例如,如果您的程序需要用户生成配置文件 运行)。如果用户可能在开发中创建了其他构建产品或生成的文件(例如,来自 Markdown 或 AsciiDoc 的 HTML 文件)但通常不是构建的,那么您也应该忽略它们。
如果您的项目是每个人 必须 使用相同的 IDE 或 OS(例如,您的项目仅使用 Visual Studio 编译或者在 macOS 上,没有人会 ever 使用另一个 IDE 或 OS),那么你可以把 editor- 或 OS-特定文件也在那里。
人们可以在自己的系统上拥有自己的忽略文件(通过 core.excludesFile
),因此如果用户使用 Vim,他们应该调整自己的每个用户忽略文件,以便他们忽略交换文件。同样,macOS 用户应该忽略 .DS_Store
。您不负责处理某人可能使用的每个操作系统或编辑器。您可以选择使用预先创建的 gitignore 文件,其中包含其中的一些内容,但没有义务这样做。
话虽如此,通常项目会执行代码审查,因此如果用户没有在他们的系统上适当地配置 Git 并签入了不合适的文件,审查者可以要求他们修复它并调整他们的Git 配置。这通常是大多数大型项目使用的方法并且效果很好。
我一直在学习有关 .gitignore 文件的所有知识,但有一个问题我想解决。 .gitignore 应该包含所有你想忽略的文件。因此,您应该忽略由 OS、您正在处理的 IDE 生成的文件...我的问题出现在存储库位于 Github 上,人们可以克隆它并推送更改.这些人可以使用其他操作系统,可以使用其他IDE。因此,gitignore 应该忽略这些其他 OS 和 IDEs.
生成的文件你应该怎么办?你必须在 gitignore 中写入所有操作系统生成的所有文件以及所有 IDEs 生成的所有文件吗?
repository is on Github and people can clone it and push the changes
这是您设置某种质量门槛的地方,例如代码审查。它们旨在进行讨论,并让其他人审视这些变化。查看差异,您会注意到其他无用的东西,例如IDE 个文件。然后,您请贡献者删除那些并重新提交。
在大多数 OSS 的情况下,我认为,维护者有一个存储库,这是由贡献者 cloned/forked 提供的,当他们想要引入更改时,他们会进行 PR。因为您通常不希望 任何人 对您的代码进行更改,所以您将写入权限限制在您信任的人身上,因此其他人无法直接推送到主仓库。
对于较小的项目,您认识所有的贡献者,仍然有可能不小心引入不需要的文件,这仍然是为什么您需要在合并到主流之前进行代码审查之类的事情。
不管怎样,这是一个流程问题,不一定是 git
。视情况而定。就像任何其他重复性工作一样,当您注意到一种模式时,将其自动化。
您对必须考虑任何可以做出贡献的人的所有系统感到有点害怕,这是对的,但您不必考虑。
- 我认为大多数语言都有代码检查器,因此您可以强制执行编码风格(例如制表符与空格)。
- 此外,您通常知道一种语言可以生成哪些文件,例如
.exe
s,.dll
,因此您可以将它们添加到.gitignore
文件中。 - 对于任何漏掉的内容,都有拉取请求。
我想马上强调两个背景要点:
如果您拥有存储库,则您设置规则。任何你为迎合他人而做的事情都只是一般的友善。
动词 ignore 充其量是……棘手。我稍后会描述我的意思。重要的是在
.gitignore
中列出一个文件并不完全 忽略 它,除非你对 "ignore".[=108= 这个词有一个奇怪的个人定义]
也就是说,友好的方法是让您的 存储库 仅忽略 您的项目 将生成的文件。然后,让您的个人忽略文件忽略您的系统将生成的文件。
让我们用一个具体的例子。假设您有一个带有 Python 的项目,其中 运行ning python foo.py
创建 foo.pyc
、foo.pyo
、and/or __pycache__/*
文件,none 其中应该永远提交。因此,您将从:
*.pyc
*.pyo
__pycache__/
在您的 .gitignore
中,因为任何使用您的项目的人——您或您的 co-workers,或其他任何人——最终都会得到这些 Python "object code" 文件,特定于特定 Python 版本,因此不应包括在内。
但假设您个人正在使用 MacOS 及其 Finder。 Finder 程序创建名为 .DS_Store
的文件。所以你很可能想添加:
.DS_Store
给你的.gitignore
。那不是 错误 ,但它对使用 Windows 的人没有任何好处。 Windows 人需要忽略哪些文件?我不确定,我不使用 Windows。然而,Linux 人可能想忽略 vim
编辑器创建的 .*.swp
文件。
如果你把 .DS_Store
放在你自己的 $HOME/.gitignore
中,而 Linux 人把 .*.swp
放在 他们的 $HOME/.gitignore
,你们所有人都会对你的项目有愉快的体验。此外,您会对 他们的 项目有愉快的体验,他们没有列出 .DS_Store
因为他们开始于 Linux.
这就是一般的想法:您的项目(存储库).gitignore
应该列出将在[=中找到的文件的名称或name-patterns 481=] 在处理您的项目时,但不应将其提交给项目。换句话说,不是OS-specific,而是project-specific。 其他文件名模式——OS-specific、editor-specific、IDE-specific, 等等——可以进入其他忽略文件,因此不需要在项目的 .gitignore
文件中列出。将它们列在项目文件中不一定伤害,但如果每个人对事情都很明智,那也无济于事。
Less-important 不属于实际答案的背景(您可以在这里停止阅读!)
人们发现 Git 的 .gitignore
文件令人困惑。 (我做了,从 Whosebug 上的数百个问题来看,几乎每个人都这样做了。)我认为其中很大一部分来自误解 Git 的存储模型。
关于 Git 的第一件事——可能是 最重要的事情——是 Git 不是关于 files,也不是关于 branches。 Git 实际上就是 提交 。 Git 存储库的核心由两个数据库组成。大数据库保存 提交 和支持提交所需的其他内部 Git 对象。
这个包含 Git 提交和其他 Git 对象的大数据库是 git clone
副本。还有一个较小的数据库,包含 names: 分支名称、标签名称等。此数据库对其他 Git 可见,因此 可以 被 git clone
复制,但通常它不仅仅是复制。相反,git clone
读取较小的数据库并 修改 它,完全丢弃一些名称并更改其他名称。因此,当您使用 git clone
时,您将获得大数据库的副本(所有提交)和小数据库的修改后的 sawed-down 副本。 (我们不会在此处仔细查看较小的文件,因为它不会影响 .gitignore
个文件。)
提交本身都有唯一的哈希ID。这些是由字母和 digit 组成的又大又丑的字符串,例如 b994622632154fc3b17fb40a38819ad954a5fb88
。 Git 存储库可以快速判断它是否与其他 Git 存储库具有相同的提交:发送 Git 仅列出哈希 ID。接收方 Git 只是检查:我是否有使用该哈希 ID 的提交? 如果是,接收方 Git 有 那个 提交。它不需要再次获得它。如果没有,接收方 Git 需要获得该提交。
这意味着您的第一个 git clone
可能会很慢:您可能必须获取许多兆字节的对象。不过,在那之后,更新 克隆只是获得任何新提交的问题 他们 有 你 还是需要的。你的 Git 打电话给他们Git,他们列出了一些哈希 ID,你的 Git 知道要得到什么,他们的 Git 知道你有什么。或者,如果您向他们提交了新的提交,您的 Git 会调用他们的 Git,为他们提供一些哈希 ID,他们可以说 我已经有了那个 [= =342=] 或 我没有那个,给我!
当然,还有更多内容。接下来要知道的是,每次提交都会存储 每个 文件的完整快照。这些文件以特殊的 read-only、Git-only 冻结格式存储,其中文件为 de-duplicated。提交存储文件的事实是 Git,它实际上只关心提交本身,最终为我们存储文件。冻结和 de-duplicated 格式 是存储库不会变得非常胖的原因,即使 每个 提交都有 [=206] 的完整副本=]每个文件:大多数提交只是re-use上一次提交的文件,这意味着Git不必存储新副本。
但是,如果提交中的文件是冻结的 Git-only 格式,您计算机上的其他程序都无法使用,您将如何实际 使用 这些文件?答案是:你不会。也就是说,您不会使用 这些 文件。 Git 将做的是提取 这些文件到某处。 "somewhere" 是您的 工作树 或 work-tree.
这里值得一提的是,虽然我们不会深入探讨,但每个提交不仅存储一个冻结的快照,而且还存储一些额外的 元数据 。这主要是您在 git log
输出中看到的内容:例如,谁进行了提交、何时提交以及为什么提交。 why 部分取决于提交的人:它是日志消息。一条好的日志消息很有价值。 Git 可以告诉你 发生了什么: Git 将比较以前的,或 parent,commit 的快照与当前或child 提交的快照,对于每个不同的文件,Git 将向您展示将父副本更改为子副本的配方。但是 Git 无法告诉您 为什么 添加或删除了某些行。只有这样做的人才能说出 为什么 他们那样做。
这意味着您看到和使用的文件根本不在 Git 中
如果你运行:
git clone https://github.com/git/git
并拥有 Git 的副本,您可以查看 Git 的来源:有一个 Makefile
、一个 README.md
,等等。但这些是您计算机上的普通文件。它们不是 in 提交的文件。 它们是 Git 通过从快照中提取提交的文件制作的副本。 这些副本在您的工作树或 work-tree 中。您可以使用文件查看器查看它们,在编辑器中打开它们,等等。但它们不在 Git 中。它们在你的work-tree,任你随心所欲。
Git 将在您要求时随时提取对您的 work-tree 的任何给定提交:
git checkout v2.21.0
例如,将使用 标签 v2.21.0
来查找特定的提交哈希 ID(8104ec994ea3849a968b4667d072fedd1e688642
,确切地说)并提取 那 承诺你的 work-tree。 (如果你有一个 2.23 或更高版本的 Git,你可以使用 git switch
而不是 git checkout
:它们在这里做完全相同的事情。)这个提取过程包括 从你的 work-tree 中删除 你的 文件,并根据你要切换到的提交创建新文件。但是所有这些文件都是你的文件,而不是Git的。
幸运的是,git checkout
/ git switch
进行了一些安全检查,以避免在您未保存所做的某些更改时删除 您的 文件。您可以关闭此功能(例如,git checkout --force
)或故意使用其他破坏性命令(git reset --hard
)来清除未保存的工作。在所有情况下,你基本上只是告诉 Git 删除 你 对 你的 文件所做的事情并取回其他版本,例如保存在其他提交中的版本,来自 Git 的 文件。
Git的索引或暂存区
如果 Git 只使用了两件事——它的提交,其中一个是 当前 提交,和你的 work-tree——那么 git commit
本身会很简单。不幸的是,Git 隐藏了 第三个 位置来保存每个文件。当您 select 通过 git checkout
或 git switch
进行某些提交时,成为 当前 提交,Git 不会 只需 将提交的快照提取到您的work-tree。相反,它首先将该提交的快照提取到 Git 的 index.
索引很复杂并且有多种用途,但它的主要用途实际上很容易描述,并且是您应该记住的开始:索引是您构建 next 你打算做的承诺 这就是它得名 暂存区 的原因。该索引包含每个文件的副本1,最初是从提交中获取的。您的 work-tree 也有一份副本。所以有三个个活动副本:
- 你可以看到
git show HEAD:README.md
的那个被冻结到提交中。 - 你可以用
git show :README.md
看到的那个在Git的索引里。它是冻结的格式,但它是可替换的,与提交中的不同。 (这些文件有点 half-in Git:准备提交,但尚未实际提交。) - 您实际上可以 使用 的文件(在普通文件中)就是普通文件
README.md
。这是 你的 而它根本不在 Git 中。
当您 运行 git commit
、Git 收集适当的元数据时,立即冻结其索引 中的任何文件 ,并且使用这些作为新提交的新快照。
如果 :README.md
匹配 HEAD:README.md
,这两个文件是重复的,所以新提交只是 re-use 文件。如果不是,它可能与其他提交和 de-duplicates 匹配,或者它可能是全新的,并且实际上是真实存储的。在任何情况下,一旦您提交它,它就会被冻结并且现在真的完全在 Git 中。但是,如果您更改了 您的 work-tree 副本 README.md
,您可能希望 Git 冻结更新后的 README.md
。 这就是 git add
的用武之地。
git add
命令告诉Git:使索引副本匹配我的work-tree副本。即Git将从您的 work-tree 复制(并压缩成冻结格式)您更新后的 README.md
文件,并将副本放入其索引中的 :README.md
中。所以这就是为什么你总是需要 git add
文件:每次你改变 你的 副本,如果你想要 Git 改变它的 建议下一次提交复制,你必须再次git add
。
当你 运行 git commit
之后,Git 将获取所有 index 文件并将它们冻结到一个新的提交中。因为索引副本都是冻结的格式,所以这个过程可以而且通常确实非常快。
1从技术上讲,索引包含的不是数据的实际副本,而是文件名、模式和 blob 哈希 ID.除非并且直到您开始直接使用 git ls-files --stage
或 git update-index
深入研究索引,否则您无法真正分辨出区别。因此,可以将索引视为具有文件的完整副本:Git 隐藏了 blob-object 技巧,以至于您无需关心。
这就是 .gitignore
发挥作用的地方
Git 从其索引而不是您的 work-tree 进行新提交。你的 work-tree 是 你的 ,随心所欲。当您告诉 Git 覆盖它时,您只需要小心一点,因为 work-tree 中的 none 个文件是 在 Git(它们最多在旁边或在旁边Git)。但这也意味着您可以在 work-tree 中创建您不想 Git 存储到其任何提交中的文件。由于这些文件不在提交中,并且只有 提交 被 git clone
复制,这些文件不会出现在任何克隆中。
对于像 *.pyc
或 *.o
来自 cc
或 c++
的编译器输出文件,或者来自 Java 编译器的输出,或者其他什么,那是一件好事:您通常 不希望 这些文件出现在任何克隆中。
但是如果这些文件就在您的 work-tree 中,有两件事可能会出错:
git status
会唠叨他们.- 如果您使用 en-masse
git 添加 <em>everything</em>
操作,git add
将 将这些文件作为新文件复制到Git的索引中,现在如果你git commit
. 它们就会被提交
在 .gitignore
中列出文件名是防止这两种情况发生的一种方法。但是这里有一个技巧:如果一个文件已经在 Git 的索引中,将它列在 .gitignore
中是没有效果的。
Git 索引中的文件被称为 tracked。 跟踪的文件 是 Git 的索引 现在 中的文件。 未跟踪 文件存在于您的 work-tree,但现在 不在 Git 的索引中。
记住,您现在可以使用 git add
将 all-new(至 Git)个文件放入 Git 的索引中。您现在还可以使用 git rm
从 Git 的索引中完全取出文件。所以索引的内容是不固定的。 git checkout
填充 索引,然后您可以——并且将会——修改它:您将替换 任何文件你想在下一次提交时更新。
当您 运行 git status
时,status
命令会进行两次单独的比较。首先它告诉你其他有用的东西,但我们将跳过它并进行两个比较:
两个比较中的第一个将当前提交或
HEAD
与索引中的内容进行比较。对于每个完全匹配的文件,git status
什么都不说。如果有一些文件 不 匹配——或者是新的或丢失的——git status
说 changes staged for commit 并列出这些文件的名称。第二次比较是将索引与您的 work-tree 进行比较。对于每个完全匹配的文件,
git status
什么都不说。如果有一些文件 不 匹配或丢失,git status
会说 changes not staged for commit 并列出这些文件' 名称。
这里的一个特例是 未跟踪的 文件:对于每个未跟踪的文件,git status
列出文件名,2 调用这些 未跟踪的文件 。但是,如果您在 .gitignore
中列出这些名称,git status
闭嘴 。
请注意,已跟踪 文件不会发生任何特殊情况。这些已经在 Git 的索引中。它们包含在第一次比较中,Git 会将索引副本与 work-tree 副本进行比较,无论文件是否列在 .gitignore
.
所以从这个意义上说,这些 .gitignore
条目并不意味着 忽略 文件。他们的意思是在文件未被跟踪时闭嘴。当它被跟踪时,它们没有任何作用。
同时,git add
有 .
和 *
(以及其他)用于对许多或 en-masse add 操作所有的文件。如果所有文件包含未跟踪的文件,这些操作将非常不方便。因此,在 .gitignore
中列出文件名或模式会抑制 en-masse 添加操作。它甚至会抑制故意 git add
:
$ touch foo.pyo
$ git add foo.pyo
The following paths are ignored by one of your .gitignore files:
foo.pyo
Use -f if you really want to add them.
所以也许 .gitignore
应该被称为 .git-do-not-complain-about-these-untracked-files-and-do-not-automatically-add-them-when-using-en-masse-add-operations-or-even-explicit-requests
,或者类似的名称。但是谁愿意输入那种名字呢?所以 .gitignore
是。
2从技术上讲,每次需要 git status -uall
或 git status -u
时都可以得到它。否则,它有时会将物理上存储在单个文件夹中的一堆文件组合在一起,并且只麻烦提及文件夹名称。
一般来说,您在典型项目中放入 .gitignore
的内容包括您的构建系统可能生成的任何内容,以及任何特定于用户的配置文件(例如,如果您的程序需要用户生成配置文件 运行)。如果用户可能在开发中创建了其他构建产品或生成的文件(例如,来自 Markdown 或 AsciiDoc 的 HTML 文件)但通常不是构建的,那么您也应该忽略它们。
如果您的项目是每个人 必须 使用相同的 IDE 或 OS(例如,您的项目仅使用 Visual Studio 编译或者在 macOS 上,没有人会 ever 使用另一个 IDE 或 OS),那么你可以把 editor- 或 OS-特定文件也在那里。
人们可以在自己的系统上拥有自己的忽略文件(通过 core.excludesFile
),因此如果用户使用 Vim,他们应该调整自己的每个用户忽略文件,以便他们忽略交换文件。同样,macOS 用户应该忽略 .DS_Store
。您不负责处理某人可能使用的每个操作系统或编辑器。您可以选择使用预先创建的 gitignore 文件,其中包含其中的一些内容,但没有义务这样做。
话虽如此,通常项目会执行代码审查,因此如果用户没有在他们的系统上适当地配置 Git 并签入了不合适的文件,审查者可以要求他们修复它并调整他们的Git 配置。这通常是大多数大型项目使用的方法并且效果很好。