【问题标题】:What should contain .gitignore file when is a public repository?公共存储库时应该包含什么 .gitignore 文件?
【发布时间】:2020-05-24 07:25:21
【问题描述】:

我一直在学习有关 .gitignore 文件的所有信息,但有一个问题我想解决。 .gitignore 应该包含您要忽略的所有文件。因此,您应该忽略操作系统生成的文件,您正在使用的 IDE...当存储库在 Github 上时,我的问题就会出现,人们可以克隆它并推送更改。这些人可以使用其他操作系统,也可以使用其他 IDE。因此,gitignore 应该忽略这些其他操作系统和 IDE 生成的文件。

你应该怎么做?是不是所有操作系统生成的,所有IDE生成的文件都要写进gitignore?

【问题讨论】:

  • 有标准的 .gitignore 文件。或者您可以拒绝引入不相关文件的拉取请求并要求添加它们 .gitignore (或自己做)。或两者兼而有之。
  • 存储库是否公开有什么关系?忽略不应该跟踪的东西,句号。

标签: git github repository gitignore


【解决方案1】:

我想立即强调两个背景点:

  1. 如果您拥有存储库,设置规则。您为适应他人所做的任何事情都只是一般的友善。

  2. 动词 ignore 是......最棘手的。我稍后会描述我的意思。重要的是,在 .gitignore 中列出文件并不会完全忽略它,除非您对“忽略”这个词有一个奇怪的个人定义。

也就是说,友好的方式是让你的存储库只忽略你的项目将产生的文件。然后,让您的个人忽略文件忽略您的系统将产生的文件。

让我们用一个具体的例子。假设您有一个使用 Python 的项目,其中运行 python foo.py 创建 foo.pycfoo.pyo 和/或 __pycache__/* 文件,这些文件都不应该提交。因此,您应该从:

*.pyc
*.pyo
__pycache__/

在您的.gitignore 中,因为使用您项目的任何人——您、您的同事或其他任何人——最终都会得到这些 Python“目标代码”文件,这些文件特定于特定的 Python 版本,因此应该不包括在内。

但假设您个人使用的是 MacOS 及其 Finder。 Finder 程序创建名为.DS_Store 的文件。所以你很可能会想补充:

.DS_Store

到您的.gitignore。这没有,但它对任何使用 Windows 的人都没有任何好处。 Windows 人员需要忽略哪些文件?我不确定,我不使用 Windows。但是,Linux 人员可能希望忽略 .*.swp 编辑器创建的 vim 文件。

如果您将.DS_Store 放入您自己的$HOME/.gitignore,而Linux 人员将.*.swp 放入他们的 $HOME/.gitignore,那么你们所有人都会在您的项目中获得愉快的体验。此外,您将在他们的项目中获得愉快的体验,他们没有列出 .DS_Store,因为他们是在 Linux 上开始的。

这就是一般的想法:您的项目(存储库).gitignore 应该列出在处理您的项目时将在工作树中找到的文件的名称或名称模式,但是不应该致力于该项目。换句话说,它不是特定于操作系统的,而是特定于项目的。 其他文件名模式——特定于操作系统、特定于编辑器、特定于 IDE 等等——可以放在其他忽略文件中,因此不需要在项目的.gitignore 文件中列出。将他们列在项目文件中不一定伤害,但如果每个都对事情敏感,那也无济于事。

不属于实际答案的不太重要的背景(您可以在此处停止阅读!)

人们发现 Git 的 .gitignore 文件令人困惑。 (我做到了,从 StackOverflow 上的数百个问题来看,几乎每个人都做到了。)我认为其中很大一部分来自对 Git 的存储模型的误解。

要了解 Git 的第一件事——可能是 最重要的事情——是 Git 与 文件 无关,也与 无关>分支机构。 Git 真的是关于commits。 Git 存储库的核心是由两个数据库组成。大型数据库包含 commits 和支持提交所需的其他内部 Git 对象。

这个包含 Git 提交和其他 Git 对象的大型数据库是 git clone 复制的内容。还有第二个较小的names: 分支名称、标签名称等数据库。这个数据库对其他 Git 是可见的,所以它可以git clone 复制,但通常它不仅仅是复制的。相反,git clone 读取较小的数据库并修改它,完全丢弃一些名称并更改其他名称。因此,当您使用git clone 时,您将获得一个大数据库的副本(所有提交)和一个修改后的小数据库副本。 (这里我们不会仔细研究较小的那个,因为它不会影响.gitignore 文件。)

提交本身都有唯一的哈希 ID。这些是大而难看的字母和数字字符串,例如b994622632154fc3b17fb40a38819ad954a5fb88。 Git 存储库可以快速判断它是否具有与其他 Git 存储库相同的提交:发送 Git 只是列出哈希 ID。接收 Git 只是检查:我是否有一个具有该哈希 ID 的提交? 如果是,接收 Git 有 那个 提交。它不需要再次得到它。如果没有,接收 Git 需要获得该提交。

这意味着您的第一个git clone 可能会很慢:您可能需要获取许多兆字节的对象。然而,在那之后,更新一个克隆只是获取任何新的提交他们仍然需要的问题。你的 Git 调用他们的 Git,他们列出一些哈希 ID,你的 Git 知道要获取什么,他们的 Git 知道你拥有什么。或者,如果您向他们提交了新的提交,您的 Git 会调用他们的 Git,为他们提供一些哈希 ID,他们可以说 我已经有了那个我没有'没有那个,给我!

当然,还有比这更多的东西。接下来要知道的是,每个提交都存储了 每个 文件的完整且完整的快照。这些文件以一种特殊的、只读的、仅限 Git 的冻结格式存储,其中文件被删除了重复数据。提交存储文件的事实是 Git,它实际上只关心提交本身,最终为我们存储文件。冻结和去重的 格式 是存储库不会变得非常庞大的原因,即使 每个 提交都有 每个 文件的完整副本:大多数提交只是重新使用上一次提交中的文件,这意味着 Git 不必存储新副本。

但如果提交中的文件是冻结的、仅限 Git 的格式,而您的计算机上没有其他程序可以使用,那么您将如何真正使用这些文件?答案是:你不会。也就是说,您不会使用 这些 文件。 Git 会做的是在某处提取这些文件。那个“某处”是你的工作树工作树

这里值得一提的是,虽然我们不会深入讨论,但每个提交不仅存储一个冻结的快照,还存储一些额外的元数据。这主要是您在git log 输出中看到的内容:例如,提交的人、时间和原因。 why 部分取决于提交的人:它是日志消息。一条好的日志消息非常有价值。 Git 可以告诉你发生了什么:Git 会将前一个提交或提交的快照与当前提交或提交的快照进行比较,并且对于每个文件不同,Git 将向您显示一个将父副本更改为子副本的配方。但是 Git 无法告诉您为什么添加或删除了某些行。只有这样做的人才能说出为什么他们这样做。

这意味着你看到和使用的文件根本不在 Git 中

如果你跑过:

git clone https://github.com/git/git

如果有 Git 的副本,您可以查看 Git 的源代码:有 MakefileREADME.md,等等。但这些是您计算机上的普通文件。它们不是提交中的文件它们是 Git 通过从快照中提取提交的文件而制作的副本。 这些副本位于您的工作树或工作树中。您可以使用文件查看器查看它们,在编辑器中打开它们等等。但它们不在 Git 中。它们位于您的工作树中,供您随意使用。

Git 会在您要求时提取任何给定的提交到您的工作树:

git checkout v2.21.0

例如,将使用 tag v2.21.0 来查找特定的提交哈希 ID(确切地说是8104ec994ea3849a968b4667d072fedd1e688642)并提取 that 提交到您的工作 -树。 (如果你有一个 2.23 或更高版本的 Git,你可以使用 git switch 而不是 git checkout:它们在这里做的完全一样。)这个提取过程包括删除 你的 从您的工作树中创建文件,并根据您切换到的提交创建新文件。但所有这些文件都是你的文件,而不是 Git 的。

幸运的是,git checkout / git switch 进行了一些安全检查,以避免在您尚未保存所做的某些更改时删除您的文件。您可以将其关闭(例如git checkout --force)或故意使用其他破坏性命令(git reset --hard)来擦除未保存的工作。在所有情况下,您基本上只是告诉 Git 删除 youyour 文件所做的内容并取回其他版本,例如保存在其他提交中的版本,来自 Git 的 文件。

Git 的 index暂存区

如果 Git 只使用两件事——它的提交,其中一个是 当前 提交,以及你的工作树——那么 git commit 本身就很简单。不幸的是,Git 隐藏了 第三个​​ 位置来保存每个文件。当您通过git checkoutgit switch 选择某个提交作为当前 提交时,Git 不会只是 将该提交的快照提取到您的工作树中。相反,它首先将该提交的快照提取到 Git 的 index

索引很复杂并且有多种用途,但它的主要用途实际上很容易描述,并且是您应该记住的开始:索引是您构建下一个 提交您计划进行的。 这就是为什么它有名称​​暂存区。索引保存每个文件的副本1,最初取自提交。您的工作树也包含一个副本。所以有三个活动副本:

  • git show HEAD:README.md 可以看到的那个被冻结在提交中。
  • git show :README.md 可以看到的那个在Git 的索引中。它是冻结的格式,但它是可替换的,与提交中的不同。 (这些文件是 Git 的一半:准备好提交,但尚未真正提交。)
  • 您实际上可以使用的文件(位于普通文件中)就是普通的README.md。这是你的,它根本不在 Git 中。

当您运行 git commit 时,Git 会收集适当的元数据,并立即冻结其索引中的所有文件,并将这些文件用作新提交的新快照。

如果:README.mdHEAD:README.md 匹配,则这两个文件是重复的,因此新提交只是重新使用该文件。如果不是,它可能会匹配其他一些提交并以这种方式进行重复数据删除,或者它可能是全新的,并且实际上是真实存储的。无论如何,一旦你提交它,它就会被冻结并且现在完全在 Git 中。但是,如果您更改了README.md您的 工作树 副本,您可能希望Git 冻结更新的README.md这就是git add 的用武之地。

git add 命令告诉 Git:使索引副本与我的工作树副本匹配。 也就是说,Git 将从您的工作树,并将副本放入其索引中的:README.md。所以这就是为什么你经常被要求git add 文件的原因:每次你改变你的副本,如果你想让Git改变它提议的下一次提交副本,你必须再次git add

当您稍后运行git commit 时,Git 将获取所有 index 文件并将它们冻结到一个新的提交中。因为索引副本都是冻结的格式,所以这个过程可以而且通常确实非常很快。


1从技术上讲,索引包含的不是数据的实际副本,而是文件的名称、模式和 blob 哈希 ID。除非并且直到您开始使用git ls-files --stagegit update-index 直接挖掘索引,否则您无法真正分辨出区别。因此,可以将索引视为拥有文件的完整副本:Git 将 blob-object 技巧隐藏得如此之好,以至于您无需关心。


这就是.gitignore 的用武之地

Git 从它的索引而不是你的工作树中进行新的提交。你的工作树是你的,你可以随意处理。当你告诉 Git 覆盖它时,你只需要小心一点,因为你的工作树中的所有文件都不是 in Git(它们最多 next to在 Git 旁边)。但这也意味着你可以在你的工作树中创建你不希望 Git 存储到它的任何提交中的文件。由于这些文件不在提交中,并且只有 commitsgit clone 复制,因此这些文件不会出现在任何克隆中。

对于像*.pyc 这样的编译器输出文件,或者来自ccc++*.o,或者来自Java 编译器的输出,或者其他什么,这是一件好事:你通常不会想要 这些文件将出现在任何克隆中。

但如果这些文件只是在你的工作树中,有两件事可能会出错:

  1. git status唠叨你
  2. 如果您使用集体git add <em>everything</em> 操作,git add 会将这些文件复制到 Git 的索引中作为新文件,现在如果您使用git commit,它们将被提交。

.gitignore 中列出文件名是防止这两种情况的一种方法。但是这里有一个技巧:如果文件已经在 Git 的索引中,则在 .gitignore 中列出它是无效的。

Git 索引中的文件称为已跟踪跟踪的文件是在 Git 的索引中的文件现在untracked 文件是存在于您的工作树中但不在 Git 的索引中的文件现在

请记住,您现在可以使用 git add 将全新的(对 Git 的)文件放入 Git 的索引中。您现在还可以使用 git rm 将文件完全从 Git 的索引中取出。所以索引的内容不是固定的。 git checkout 填充索引,然后,您可以并且将会修改它:您将替换您想要在下一次提交中更新的任何文件。

当您运行git status 时,status 命令会进行两个单独的比较。首先它会告诉你其他有用的东西,但我们会跳过它并进行两个比较:

  1. 两个比较中的第一个将当前提交HEAD与索引中的内容进行比较。对于每个完全匹配的文件,git status 什么都不说。如果有一些文件匹配——或者是新的或丢失的——git status 会说为提交暂存的更改并列出这些文件的名称。

  2. 第二个比较将索引与您的工作树进行比较。对于每个完全匹配的文件,git status 什么都不说。如果有一些文件匹配或丢失,git status 会说更改未暂存并列出这些文件的名称。

这里的一种特殊情况是未跟踪文件:对于每个未跟踪文件,git status 列出文件名,2调用这些未跟踪文件时间>。但如果你在.gitignore 中列出这些名字,git status 闭嘴 对他们说。

请注意,已跟踪 文件不会发生任何特殊情况。这些已经在 Git 的索引中。第一次比较涵盖了它们,Git 会将索引副本与工作树副本进行比较,无论该文件是否列在 .gitignore 中。

所以从这个意义上说,这些.gitignore 条目并不意味着忽略该文件。他们的意思是在文件未被跟踪时闭嘴。当它被跟踪时,它们没有任何效果。

同时,git add 具有 .*(以及其他)用于对许多或所有文件进行整体添加操作。如果所有文件包含未跟踪的文件,这些操作将非常不方便。因此,在.gitignore 中列出文件名或模式会抑制整体添加操作。它甚至压制了故意的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 -uallgit status -u 时都能获得此信息。否则,它有时会合并一堆物理存储在单个文件夹中的文件,并且只需提及文件夹名称。

【讨论】:

    【解决方案2】:

    一般来说,您在典型项目中放入 .gitignore 的内容包括您的构建系统可能生成的任何内容,以及任何特定于用户的配置文件(例如,如果您的程序需要用户生成配置文件跑步)。如果用户可能在开发过程中创建了其他构建产品或生成的文件(例如,来自 Markdown 或 AsciiDoc 的 HTML 文件)但通常不是构建的,那么您也应该忽略这些。

    如果您的项目要求每个人必须使用相同的 IDE 或操作系统(例如,您的项目只能在 Visual Studio 或 macOS 上编译,而没有人永远使用另一个IDE 或操作系统),那么您也可以将特定于编辑器或操作系统的文件放在那里。

    人们可以在自己的系统上拥有自己的忽略文件(通过core.excludesFile),因此如果用户使用 Vim,他们应该调整自己的每个用户的忽略文件,以便忽略交换文件。同样,macOS 用户应该忽略.DS_Store。您无需负责处理某人可能使用的每个操作系统或编辑器。您可以选择使用预先创建的 gitignore 文件,其中包含其中的一些内容,但没有义务这样做。

    话虽如此,通常项目会执行代码审查,因此如果用户没有在他们的系统上正确配置 Git 并签入了不合适的文件,审查者可以要求他们修复它并调整他们的 Git 配置。这通常是大多数大型项目使用的方法,并且效果很好。

    【讨论】:

      【解决方案3】:

      存储库在 Github 上,人们可以克隆它并推送更改

      这是您设置某种质量门的地方,例如代码审查。它们是用来讨论的,并且让其他一些眼睛检查这些变化。查看差异,您会注意到其他无用的东西,例如IDE 文件。然后,请您要求贡献者删除这些内容并重新提交。

      在大多数 OSS 的情况下,我认为,维护者有一个存储库,由贡献者克隆/分叉,当他们想要引入更改时,他们会进行 PR。因为您通常不希望任何人 更改您的代码,所以您将写入权限限制为您信任的人,因此其他人无法直接推送到主存储库。

      对于较小的项目,您认识所有的贡献者,仍然有可能意外引入不需要的文件,这仍然是您在融入主流之前需要进行代码审查之类的原因。

      不管怎样,这是一个流程问题,不一定是git 问题。视情况而定。与任何其他重复性工作一样,当您注意到某种模式时,将其自动化。

      您对不得不考虑任何可以做出贡献的人的所有系统感到有点害怕是对的,但您不必这样做。

      1. 我认为大多数语言都有代码检查器,因此您可以强制执行编码样式(例如制表符与空格)。
      2. 此外,您通常知道一种语言可以生成哪些文件,例如.exes、.dll,因此您可以将它们添加到您的 .gitignore 文件中。
      3. 对于任何漏掉的东西,都有拉取请求。

      【讨论】:

        猜你喜欢
        • 2020-11-25
        • 1970-01-01
        • 2015-10-23
        • 1970-01-01
        • 2021-08-29
        • 2011-08-18
        • 1970-01-01
        • 1970-01-01
        • 2013-08-10
        相关资源
        最近更新 更多