Git - 推送一个目录然后忽略它
Git - Push a directory and then ignore it
我有一个文件夹,其中包含我在 PhpStorm 上执行的所有 http 请求文件。
我首先将此文件夹添加到我的 gitignore 文件中:
/app/http/
然后强制推送到我的远程仓库:
git add -f app/http
git commit -m 'http folder added'
git push
但在那之后,我的文件夹没有被忽略。
推送后如何忽略它?
TL;DR
你得不到你想要的。不要那样做。如果可以,请考虑存储一个 dot-file,例如 .gitignore
列出 *
。如果没有,请考虑让 不是 Git 的程序在需要时自动创建空文件夹。
长
您开始时有许多不正确的假设:
- 您认为您添加了一个文件夹,但 Git 提交仅存储 文件 ,而不存储文件夹。稍后我们将了解这意味着什么,以及为什么 Git 是这样的。
- 您认为您将文件推送到文件夹 and/or。 Git 不推送文件。 Git 推送 提交.
- 您认为在
.gitignore
中列出某些内容会使 Git 忽略它。事实并非如此。
这些都导致了您遇到的问题。
您需要从这个开始:Git 的基本存储单元——无论如何您将与之交互的单元——是 提交。 Git 完全是关于 提交 ,因此您需要详细了解提交的具体内容和作用。
提交
Git 提交由两部分组成:数据和元数据:
每次提交中的数据由文件的完整快照组成。这里没有文件夹,只有名称可能包含嵌入斜杠的文件,例如 path/to/file.ext
.
每次提交中的元数据包含诸如您的姓名和电子邮件地址、提交时的 date-and-time 戳记以及解释 原因 你做出了承诺。包含在这个元数据中的是 是 Git 使用的一些元数据=609=]:父提交哈希 ID 的列表。通常这个列表中只有一个commit hash ID。
每个提交都有一个唯一的哈希 ID。这个哈希 ID 实际上是提交的真实名称。这就是 Git 检索数据(您的文件)和元数据的方式。因此,您需要使用提交哈希,以便 Git 可以 找到 提交。但是这里有一个问题:提交哈希 ID 又大又丑,人类无法使用。
分支名称
这个问题的解决方案很明显:我们有一台计算机;我们可以让计算机为我们存储哈希ID,使用一个简单的名称,例如master
或 develop
,人类可以处理,以记住正确的哈希 ID。这就是分支名称:一个分支名称包含一个 - 并且 仅 一个 - 提交的哈希 ID。根据定义,我们在分支名称中 Git 记住的提交是分支中的 newest 或 latest 提交。 Git 称之为 提示提交 。
如果我们说 git checkout master
,Git 将使用存储在名称 master
中的哈希 ID 来检查 master
的 tip 提交。如果我们添加一个 new 提交 到 分支,Git 将写出新提交,其(单个)parent hash ID 设置为旧的分支提示。新提交获得一个新的、唯一的哈希 ID,Git 写入分支名称,现在 new 提交是分支提示。
之前的commit还存在,Git可以自己找到:Git使用分支名查找last[=414=的hash ID ] 提交,然后读取该提交并使用其元数据来查找父项的哈希 ID。 Git 然后使用该哈希 ID 读取父提交。如果合适,Git 使用 that commit 的存储父级再返回一步。
画出你的提交和分支
在视觉上,如果我们画这个,我们得到:
... <-F <-G <-H <--branch
其中 branch
是分支名称,它存储一些哈希 ID,我们将在这里调用 H
。这允许 Git 到 find H
。 H
本身存储,作为其父散列 ID,我们将调用 G
的提交的 ID。所以 H
指向 G
,允许 Git 从它的数据库中检索 G
。 G
依次指向较早的提交 F
,依此类推。整个过程有效,因为 分支名称 指向 last 提交,从那里,Git 可以向后工作。
你的work-tree
任何现有提交中的任何内容都不能更改!一旦完成,每次提交都会被永久冻结。这包括存储在该提交中的所有文件。因为提交是冻结的——read-only——它们非常适合归档。但是提交中的文件以特殊的、冻结的、压缩的、read-only 和 Git-only 格式存储,只有 Git 可以读取。这些文件实际上是无法更改的,就像 Git 提交永远无法更改一样。这使得这些文件对于获取任何新 工作完成。
这个问题的解决方案是 Git 提取 冻结的提交文件,从提交到工作区,在那里文件被转回您的计算机可以使用的普通文件。这就是 git checkout
——或者在 Git 2.23 及更高版本中,git switch
——所做的:你告诉它你想使用哪个分支,Git 使用分支 name 以找到正确的 哈希 ID,Git 将所有冻结的文件从该提交中提取到您的 work-tree 或 working tree,这是您可以看到它们并使用它们的地方。1
1这掩盖了很多关于git checkout
的细节,所以它只是一个概述,而不是一个精确的定义。
索引
因此,正如您到目前为止从这张图片中看到的那样,当您处理提交时,实际上有 两组 文件处于活动状态:提交的副本,它们一直被冻结(并且根本不可见),以及您可以看到和处理的工作副本。一些版本控制系统到此为止,每个文件只有两个副本。 Git,然而,没有。
Git 有不同的名称,index,或 staging area,或有时(很少有这些天)缓存。它有两个(或三个)名称的原因可能是多种因素的结合:索引本身很复杂,但大多数时候使用它的方式相对简单。术语暂存区指的是您使用它的方式。我喜欢称它为索引,因为它在合并过程中发挥了更大的作用,您最终需要了解这一点。
无论如何,考虑索引的一个好方法是假设它拥有每个文件的 third 副本。2 这个额外的索引副本采用 冻结格式 ,但实际上并未冻结:您可以用新副本覆盖它。索引中每个文件的副本是 git commit
进行新提交时将使用的副本。这意味着您可以将索引视为 将进入下一次提交的文件 ,或者更短,索引是 您建议的下一次提交 .
因此,git checkout
或 git switch
的作用实际上是将文件从您选择的提交复制到 both 索引 和你的work-tree。索引副本现在已准备好进行新的提交,work-tree 副本是一个普通文件,存储在一个文件夹中,因为你的 OS 需要它。 Git 的 个文件副本,在提交和索引中,根本没有存储在文件夹中。它们采用特殊的 read-only、Git-only 格式。3
git add
的作用——这就是您在 work-tree 中修改文件后必须继续使用它的原因——是复制 work-tree 将文件复制到索引中。也就是说,它获取更新后的 work-tree 文件,re-compresses 并将其转换为 Git 的内部冻结格式,并 替换 旧索引与新副本一起复制。如果您 git add
一个根本不在索引中的文件,则会向索引添加一个 new 文件,但是如果您 git add
一个 在索引中是,它只是替换了frozen-format副本。
索引不能保存文件夹名称。索引中的文件只有长名称,如 path/to/file.ext
。因此,当 Git 从 索引构建提交 时,新提交中唯一的东西就是文件。4 这就是阻止你的原因存储一个文件夹。
当 Git 去 提取 一个提交时,如果那个提交中的一个文件被命名为 path/to/file.ext
而你的 work-tree 没有path
文件夹,或者 path
文件夹缺少 to
文件夹,Git 将 创建 path
和 path/to
根据需要。所以 Git 一开始没有存储 path
和 path/to
的事实并不重要,除了以一种方式:不可能将空文件夹(空目录)存储在Git 提交。5
2从技术上讲,索引保存的不是文件的副本,而是文件模式的副本,其名称,以及一个 blob 散列 ID。但是,除非您使用 git ls-files --stage
和 git update-index
深入了解索引的内部工作原理,否则将索引视为持有副本就足够了。
3从技术上讲,这些实际上是 blob 对象,它们具有哈希 ID,就像提交一样。 blob 对象的哈希 ID 取决于文件内容,如果内容匹配,文件可以 共享 多个不同的提交,因为每个提交实际上只存储 blob 哈希 ID。这一切都隐藏在另一层间接层之下:提交存储 tree 哈希 ID,树对象存储name-and-mode-and-hash ID。但是您不需要知道这些就可以使用 Git。您确实需要了解提交哈希 ID 和索引。
4从技术上讲,索引可以存储:
- 普通文件(模式
100644
或100755
),或
- 一个符号link(模式
120000
),或
- a gitlink (模式
160000
).
所有这三个都与哈希 ID 相关联:文件或 sym 的 blob 哈希link,或 git 的子模块提交哈希 IDlink。
5有一些技巧,none完全令人满意:见How can I add an empty directory to a Git repository? The only one of these that really works is the empty submodule trick.
跟踪和未跟踪的文件
当您提取一个提交时,您会得到它的每个文件的三个副本。一个是冻结副本,在当前或 HEAD
提交中。第二个是索引中的 frozen-format 但可替换的副本。第三个是您实际上可以 使用 的唯一副本,在您的 work-tree 中。您可以查看和触摸 work-tree 副本,因为它们是您计算机上实际文件夹中的实际文件,而不是以某种特殊 Git 方式存储的内部 Git 实体。
因为您的 work-tree 是 您的 ,这意味着您可以创建和删除文件和文件夹,而无需 Git 参与。在您将这些文件复制到 Git 的索引中之前,Git 不会 使用 任何这些文件。使用 git checkout
(或 git switch
and/or git restore
),您可以命令 Git 覆盖 您的 work-tree 文件具有 Git 的副本,但索引副本将进入 next 提交。
那么,如果您创建了一个 work-tree 文件并且 没有 将它添加到索引中,会发生什么情况?答案是:Git 称其为 未跟踪文件 .
未跟踪的文件非常简单,就是现在在您的 work-tree 中,但现在不在 Git 的索引中的文件。现在 部分很关键,因为您不仅可以将新文件复制到 Git 的索引中——使用 git add
,当然——你也可以使用 git rm
.
告诉 Git 从其索引中删除 文件
运行:
git rm path/to/file
告诉Git:从你的索引和我的work-tree中删除那个文件的副本。现在它已经从两者中消失了,它 不会 在 next 提交中。 运行:
git rm --cached path/to/file
告诉Git:从你的索引中删除该文件的副本,但不要触摸work-tree副本。现在它已经从索引,它不会在下一次提交中。
因此,如果您希望某个文件不在提交中,您必须将其从索引中删除。就目前而言,这很好。如果您将它从索引中删除,或者一开始就没有在索引中, 和 该文件现在存在于您的 work-tree 中,该文件是一个 未跟踪文件.
如我们所见,未跟踪的文件可能很烦人,但也可能非常重要。
git status
当你运行git status
、Git运行s两[=414=]组比较。第一个比较将 HEAD
提交与索引进行比较。对于每个 相同 的文件,Git 什么也没说。对于不同的文件——或者是新文件或已被删除的文件——Git 表示此文件 暂存用于提交 。请注意,您不能更改 HEAD
提交的内容,6 因此,根据定义,此处的任何更改都是您在索引中更改的内容。
有了 运行 这第一次比较,HEAD
-vs-index,Git 现在 运行 是 第二 比较。它将索引中的所有文件与 work-tree 中的文件进行比较。对于每个 相同 的文件,Git 什么也没说。对于不同的文件或已被删除的文件,Git 表示此文件 未暂存提交 。
请注意,我们还没有提到新文件。对于 work-tree 中但不在索引中的每个文件...嗯,这些正是您的 未跟踪文件 。 git status
对这些所做的是 抱怨 ,将它们列为 "untracked".
6虽然您无法更改 any 提交的内容,但您可以使用 git checkout
—select 一个 不同的 提交成为 HEAD
提交。或者,当然,您可以 运行 git commit
并进行 new 提交,现在新提交 是 HEAD
提交。
让git status
闭嘴
.gitignore
文件的大约一半目的是让 git status
停止抱怨 未跟踪的文件。在 gitignore
文件中列出文件名或模式使得 git status
而不是 抱怨该文件未被跟踪。
这不会将输入的任何文件输出前任。 如果文件已经在 Git 的索引中,则在 .gitignore
中列出该文件无效。 文件 是 在索引中,以便该文件的副本 将在下一次提交中 ...除非,当然,你自己删除它,使用 git rm
.
避免 automatically-adding 个您不想提交的文件
.gitignore
文件的其余目的是使您能够使用 en-masse git add
操作,而无需 also 复制一些当前未跟踪到 Git 的索引中的文件。例如,列出 *.o
或 *.pyc
意味着您现在可以 git add .
或 git add *
。 Git 将 跳过 untracked-and-ignored 文件,同时将其他更新的或新的 work-tree 文件复制到 Git 的索引中。
Untracked-and-ignored 因此有两个用处
由于这些文件不会被复制到索引中,它们不会在下一次提交中。 运行 git status
这些文件都未被跟踪 和 被忽略,你根本不会看到它们被提及:它们不在索引中,所以它们是未被跟踪的, 但 git status
不会抱怨。 运行 git add .
如果这些文件既未被跟踪又被忽略,git add
不会将它们复制到索引中。
但是你已经在一些现有的提交中有了这些文件
在你的例子中,有一些提交在 app/http/
中的某处有一些文件。当你 运行:
git checkout <commit-specifier-or-branch-name>
并且您通过此操作选择的提交是包含 app/http/foo.html
、Git 等文件的提交将:
- 如果需要,创建
app/http/
- 将
foo.html
写入该文件夹
- 将
app/http/foo.html
的冻结副本复制到它的索引中
然后您就可以处理/使用该文件了。但是现在文件 是 在 Git 的索引中,所以它是 tracked 并且 git status
会告诉你这件事并且任何 en-masse git add
都会将 work-tree 文件复制到索引中,以便更新的 foo.html
将在下一次提交中。
即使您不将foo.html
复制到Git的索引中,这样索引副本仍然是旧的 副本,那个 old 副本将在您所做的新提交中。 Git 从索引提交,索引有旧副本。
明显的解决方法是 完全删除 索引副本,然后进行新的提交。现在索引副本不存在,新提交中就没有 app/http/foo.html
了。如果app/http/
在.gitignore
,Git不会,从现在开始在新作品中,添加 foo.html
。
但是如果您对 所有 app/http/
文件执行此操作,那么将来克隆此存储库并将此提交提取到新的 otherwise-empty work-tree,你不会得到一个名为app/http
的文件夹,因为不会有名字以app/http/
开头的文件使Git 注意它需要创建 app
文件夹,然后在 app
文件夹中创建 http
文件夹。
此外,假设你现在有一个状态,在这个状态下你已经提交 a123456...
(无论如何是一些哈希 ID) 有 app/http/foo.html
,并提交 b6789ab...
中没有 app/http/foo.html
。如果你:
git checkout a123456
Git 将从该提交中提取所有文件,包括 app/http/foo.html
,到您的 work-tree 和 Git 的索引中......然后,如果你:
git checkout b789abc
Git 将从 that commit 中提取文件,注意 app/http/foo.html
需要 removed 因为它在之前的提交中——因此现在在索引中——并且不是在你现在selecting的提交中,因此必须从指数。所以 Git 将从索引 和你的 work-tree.
中删除 app/http/foo.html
在top-level.gitignore
中列出app/http/
,或在app/http/.gitignore
中列出*
,不仅告诉Git它不应该自动添加这些文件到索引,它也给Git权限销毁这些文件在某些情况下 work-tree 中,包括这个特定的情况。因此,如果您 select 到 .gitignore
它们在某些现有提交中拥有这些文件,那么事情就会很危险。在你确定所有 new 提交后 不 有并且忽略这些文件,在检查历史提交时要小心!
我有一个文件夹,其中包含我在 PhpStorm 上执行的所有 http 请求文件。 我首先将此文件夹添加到我的 gitignore 文件中:
/app/http/
然后强制推送到我的远程仓库:
git add -f app/http
git commit -m 'http folder added'
git push
但在那之后,我的文件夹没有被忽略。 推送后如何忽略它?
TL;DR
你得不到你想要的。不要那样做。如果可以,请考虑存储一个 dot-file,例如 .gitignore
列出 *
。如果没有,请考虑让 不是 Git 的程序在需要时自动创建空文件夹。
长
您开始时有许多不正确的假设:
- 您认为您添加了一个文件夹,但 Git 提交仅存储 文件 ,而不存储文件夹。稍后我们将了解这意味着什么,以及为什么 Git 是这样的。
- 您认为您将文件推送到文件夹 and/or。 Git 不推送文件。 Git 推送 提交.
- 您认为在
.gitignore
中列出某些内容会使 Git 忽略它。事实并非如此。
这些都导致了您遇到的问题。
您需要从这个开始:Git 的基本存储单元——无论如何您将与之交互的单元——是 提交。 Git 完全是关于 提交 ,因此您需要详细了解提交的具体内容和作用。
提交
Git 提交由两部分组成:数据和元数据:
每次提交中的数据由文件的完整快照组成。这里没有文件夹,只有名称可能包含嵌入斜杠的文件,例如
path/to/file.ext
.每次提交中的元数据包含诸如您的姓名和电子邮件地址、提交时的 date-and-time 戳记以及解释 原因 你做出了承诺。包含在这个元数据中的是 是 Git 使用的一些元数据=609=]:父提交哈希 ID 的列表。通常这个列表中只有一个commit hash ID。
每个提交都有一个唯一的哈希 ID。这个哈希 ID 实际上是提交的真实名称。这就是 Git 检索数据(您的文件)和元数据的方式。因此,您需要使用提交哈希,以便 Git 可以 找到 提交。但是这里有一个问题:提交哈希 ID 又大又丑,人类无法使用。
分支名称
这个问题的解决方案很明显:我们有一台计算机;我们可以让计算机为我们存储哈希ID,使用一个简单的名称,例如master
或 develop
,人类可以处理,以记住正确的哈希 ID。这就是分支名称:一个分支名称包含一个 - 并且 仅 一个 - 提交的哈希 ID。根据定义,我们在分支名称中 Git 记住的提交是分支中的 newest 或 latest 提交。 Git 称之为 提示提交 。
如果我们说 git checkout master
,Git 将使用存储在名称 master
中的哈希 ID 来检查 master
的 tip 提交。如果我们添加一个 new 提交 到 分支,Git 将写出新提交,其(单个)parent hash ID 设置为旧的分支提示。新提交获得一个新的、唯一的哈希 ID,Git 写入分支名称,现在 new 提交是分支提示。
之前的commit还存在,Git可以自己找到:Git使用分支名查找last[=414=的hash ID ] 提交,然后读取该提交并使用其元数据来查找父项的哈希 ID。 Git 然后使用该哈希 ID 读取父提交。如果合适,Git 使用 that commit 的存储父级再返回一步。
画出你的提交和分支
在视觉上,如果我们画这个,我们得到:
... <-F <-G <-H <--branch
其中 branch
是分支名称,它存储一些哈希 ID,我们将在这里调用 H
。这允许 Git 到 find H
。 H
本身存储,作为其父散列 ID,我们将调用 G
的提交的 ID。所以 H
指向 G
,允许 Git 从它的数据库中检索 G
。 G
依次指向较早的提交 F
,依此类推。整个过程有效,因为 分支名称 指向 last 提交,从那里,Git 可以向后工作。
你的work-tree
任何现有提交中的任何内容都不能更改!一旦完成,每次提交都会被永久冻结。这包括存储在该提交中的所有文件。因为提交是冻结的——read-only——它们非常适合归档。但是提交中的文件以特殊的、冻结的、压缩的、read-only 和 Git-only 格式存储,只有 Git 可以读取。这些文件实际上是无法更改的,就像 Git 提交永远无法更改一样。这使得这些文件对于获取任何新 工作完成。
这个问题的解决方案是 Git 提取 冻结的提交文件,从提交到工作区,在那里文件被转回您的计算机可以使用的普通文件。这就是 git checkout
——或者在 Git 2.23 及更高版本中,git switch
——所做的:你告诉它你想使用哪个分支,Git 使用分支 name 以找到正确的 哈希 ID,Git 将所有冻结的文件从该提交中提取到您的 work-tree 或 working tree,这是您可以看到它们并使用它们的地方。1
1这掩盖了很多关于git checkout
的细节,所以它只是一个概述,而不是一个精确的定义。
索引
因此,正如您到目前为止从这张图片中看到的那样,当您处理提交时,实际上有 两组 文件处于活动状态:提交的副本,它们一直被冻结(并且根本不可见),以及您可以看到和处理的工作副本。一些版本控制系统到此为止,每个文件只有两个副本。 Git,然而,没有。
Git 有不同的名称,index,或 staging area,或有时(很少有这些天)缓存。它有两个(或三个)名称的原因可能是多种因素的结合:索引本身很复杂,但大多数时候使用它的方式相对简单。术语暂存区指的是您使用它的方式。我喜欢称它为索引,因为它在合并过程中发挥了更大的作用,您最终需要了解这一点。
无论如何,考虑索引的一个好方法是假设它拥有每个文件的 third 副本。2 这个额外的索引副本采用 冻结格式 ,但实际上并未冻结:您可以用新副本覆盖它。索引中每个文件的副本是 git commit
进行新提交时将使用的副本。这意味着您可以将索引视为 将进入下一次提交的文件 ,或者更短,索引是 您建议的下一次提交 .
因此,git checkout
或 git switch
的作用实际上是将文件从您选择的提交复制到 both 索引 和你的work-tree。索引副本现在已准备好进行新的提交,work-tree 副本是一个普通文件,存储在一个文件夹中,因为你的 OS 需要它。 Git 的 个文件副本,在提交和索引中,根本没有存储在文件夹中。它们采用特殊的 read-only、Git-only 格式。3
git add
的作用——这就是您在 work-tree 中修改文件后必须继续使用它的原因——是复制 work-tree 将文件复制到索引中。也就是说,它获取更新后的 work-tree 文件,re-compresses 并将其转换为 Git 的内部冻结格式,并 替换 旧索引与新副本一起复制。如果您 git add
一个根本不在索引中的文件,则会向索引添加一个 new 文件,但是如果您 git add
一个 在索引中是,它只是替换了frozen-format副本。
索引不能保存文件夹名称。索引中的文件只有长名称,如 path/to/file.ext
。因此,当 Git 从 索引构建提交 时,新提交中唯一的东西就是文件。4 这就是阻止你的原因存储一个文件夹。
当 Git 去 提取 一个提交时,如果那个提交中的一个文件被命名为 path/to/file.ext
而你的 work-tree 没有path
文件夹,或者 path
文件夹缺少 to
文件夹,Git 将 创建 path
和 path/to
根据需要。所以 Git 一开始没有存储 path
和 path/to
的事实并不重要,除了以一种方式:不可能将空文件夹(空目录)存储在Git 提交。5
2从技术上讲,索引保存的不是文件的副本,而是文件模式的副本,其名称,以及一个 blob 散列 ID。但是,除非您使用 git ls-files --stage
和 git update-index
深入了解索引的内部工作原理,否则将索引视为持有副本就足够了。
3从技术上讲,这些实际上是 blob 对象,它们具有哈希 ID,就像提交一样。 blob 对象的哈希 ID 取决于文件内容,如果内容匹配,文件可以 共享 多个不同的提交,因为每个提交实际上只存储 blob 哈希 ID。这一切都隐藏在另一层间接层之下:提交存储 tree 哈希 ID,树对象存储name-and-mode-and-hash ID。但是您不需要知道这些就可以使用 Git。您确实需要了解提交哈希 ID 和索引。
4从技术上讲,索引可以存储:
- 普通文件(模式
100644
或100755
),或 - 一个符号link(模式
120000
),或 - a gitlink (模式
160000
).
所有这三个都与哈希 ID 相关联:文件或 sym 的 blob 哈希link,或 git 的子模块提交哈希 IDlink。
5有一些技巧,none完全令人满意:见How can I add an empty directory to a Git repository? The only one of these that really works is the empty submodule trick.
跟踪和未跟踪的文件
当您提取一个提交时,您会得到它的每个文件的三个副本。一个是冻结副本,在当前或 HEAD
提交中。第二个是索引中的 frozen-format 但可替换的副本。第三个是您实际上可以 使用 的唯一副本,在您的 work-tree 中。您可以查看和触摸 work-tree 副本,因为它们是您计算机上实际文件夹中的实际文件,而不是以某种特殊 Git 方式存储的内部 Git 实体。
因为您的 work-tree 是 您的 ,这意味着您可以创建和删除文件和文件夹,而无需 Git 参与。在您将这些文件复制到 Git 的索引中之前,Git 不会 使用 任何这些文件。使用 git checkout
(或 git switch
and/or git restore
),您可以命令 Git 覆盖 您的 work-tree 文件具有 Git 的副本,但索引副本将进入 next 提交。
那么,如果您创建了一个 work-tree 文件并且 没有 将它添加到索引中,会发生什么情况?答案是:Git 称其为 未跟踪文件 .
未跟踪的文件非常简单,就是现在在您的 work-tree 中,但现在不在 Git 的索引中的文件。现在 部分很关键,因为您不仅可以将新文件复制到 Git 的索引中——使用 git add
,当然——你也可以使用 git rm
.
运行:
git rm path/to/file
告诉Git:从你的索引和我的work-tree中删除那个文件的副本。现在它已经从两者中消失了,它 不会 在 next 提交中。 运行:
git rm --cached path/to/file
告诉Git:从你的索引中删除该文件的副本,但不要触摸work-tree副本。现在它已经从索引,它不会在下一次提交中。
因此,如果您希望某个文件不在提交中,您必须将其从索引中删除。就目前而言,这很好。如果您将它从索引中删除,或者一开始就没有在索引中, 和 该文件现在存在于您的 work-tree 中,该文件是一个 未跟踪文件.
如我们所见,未跟踪的文件可能很烦人,但也可能非常重要。
git status
当你运行git status
、Git运行s两[=414=]组比较。第一个比较将 HEAD
提交与索引进行比较。对于每个 相同 的文件,Git 什么也没说。对于不同的文件——或者是新文件或已被删除的文件——Git 表示此文件 暂存用于提交 。请注意,您不能更改 HEAD
提交的内容,6 因此,根据定义,此处的任何更改都是您在索引中更改的内容。
有了 运行 这第一次比较,HEAD
-vs-index,Git 现在 运行 是 第二 比较。它将索引中的所有文件与 work-tree 中的文件进行比较。对于每个 相同 的文件,Git 什么也没说。对于不同的文件或已被删除的文件,Git 表示此文件 未暂存提交 。
请注意,我们还没有提到新文件。对于 work-tree 中但不在索引中的每个文件...嗯,这些正是您的 未跟踪文件 。 git status
对这些所做的是 抱怨 ,将它们列为 "untracked".
6虽然您无法更改 any 提交的内容,但您可以使用 git checkout
—select 一个 不同的 提交成为 HEAD
提交。或者,当然,您可以 运行 git commit
并进行 new 提交,现在新提交 是 HEAD
提交。
让git status
闭嘴
.gitignore
文件的大约一半目的是让 git status
停止抱怨 未跟踪的文件。在 gitignore
文件中列出文件名或模式使得 git status
而不是 抱怨该文件未被跟踪。
这不会将输入的任何文件输出前任。 如果文件已经在 Git 的索引中,则在 .gitignore
中列出该文件无效。 文件 是 在索引中,以便该文件的副本 将在下一次提交中 ...除非,当然,你自己删除它,使用 git rm
.
避免 automatically-adding 个您不想提交的文件
.gitignore
文件的其余目的是使您能够使用 en-masse git add
操作,而无需 also 复制一些当前未跟踪到 Git 的索引中的文件。例如,列出 *.o
或 *.pyc
意味着您现在可以 git add .
或 git add *
。 Git 将 跳过 untracked-and-ignored 文件,同时将其他更新的或新的 work-tree 文件复制到 Git 的索引中。
Untracked-and-ignored 因此有两个用处
由于这些文件不会被复制到索引中,它们不会在下一次提交中。 运行 git status
这些文件都未被跟踪 和 被忽略,你根本不会看到它们被提及:它们不在索引中,所以它们是未被跟踪的, 但 git status
不会抱怨。 运行 git add .
如果这些文件既未被跟踪又被忽略,git add
不会将它们复制到索引中。
但是你已经在一些现有的提交中有了这些文件
在你的例子中,有一些提交在 app/http/
中的某处有一些文件。当你 运行:
git checkout <commit-specifier-or-branch-name>
并且您通过此操作选择的提交是包含 app/http/foo.html
、Git 等文件的提交将:
- 如果需要,创建
app/http/
- 将
foo.html
写入该文件夹 - 将
app/http/foo.html
的冻结副本复制到它的索引中
然后您就可以处理/使用该文件了。但是现在文件 是 在 Git 的索引中,所以它是 tracked 并且 git status
会告诉你这件事并且任何 en-masse git add
都会将 work-tree 文件复制到索引中,以便更新的 foo.html
将在下一次提交中。
即使您不将foo.html
复制到Git的索引中,这样索引副本仍然是旧的 副本,那个 old 副本将在您所做的新提交中。 Git 从索引提交,索引有旧副本。
明显的解决方法是 完全删除 索引副本,然后进行新的提交。现在索引副本不存在,新提交中就没有 app/http/foo.html
了。如果app/http/
在.gitignore
,Git不会,从现在开始在新作品中,添加 foo.html
。
但是如果您对 所有 app/http/
文件执行此操作,那么将来克隆此存储库并将此提交提取到新的 otherwise-empty work-tree,你不会得到一个名为app/http
的文件夹,因为不会有名字以app/http/
开头的文件使Git 注意它需要创建 app
文件夹,然后在 app
文件夹中创建 http
文件夹。
此外,假设你现在有一个状态,在这个状态下你已经提交 a123456...
(无论如何是一些哈希 ID) 有 app/http/foo.html
,并提交 b6789ab...
中没有 app/http/foo.html
。如果你:
git checkout a123456
Git 将从该提交中提取所有文件,包括 app/http/foo.html
,到您的 work-tree 和 Git 的索引中......然后,如果你:
git checkout b789abc
Git 将从 that commit 中提取文件,注意 app/http/foo.html
需要 removed 因为它在之前的提交中——因此现在在索引中——并且不是在你现在selecting的提交中,因此必须从指数。所以 Git 将从索引 和你的 work-tree.
app/http/foo.html
在top-level.gitignore
中列出app/http/
,或在app/http/.gitignore
中列出*
,不仅告诉Git它不应该自动添加这些文件到索引,它也给Git权限销毁这些文件在某些情况下 work-tree 中,包括这个特定的情况。因此,如果您 select 到 .gitignore
它们在某些现有提交中拥有这些文件,那么事情就会很危险。在你确定所有 new 提交后 不 有并且忽略这些文件,在检查历史提交时要小心!