为什么在没有任何更改的情况下创建另一个提交?

时间:2019-12-17 23:21:51

标签: git

在下面的脚本中,创建一个新项目。提交了一个文件。进行了更改,但已将其从舞台中删除。在这一点上进行提交应该什么都不做。为什么还要创建另一个提交?

++ git init
Initialized empty Git repository in C:/src/newproject/.git/
++ echo asdf
++ git status
On branch master

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        file1.txt

nothing added to commit but untracked files present (use "git add" to track)
++ git add file1.txt
warning: LF will be replaced by CRLF in file1.txt.
The file will have its original line endings in your working directory
++ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

        new file:   file1.txt

++ git commit '--message=this is the message'
[master (root-commit) c3f5d0f] this is the message
 1 file changed, 1 insertion(+)
 create mode 100644 file1.txt
++ git log
commit c3f5d0f7da49b4eacc8df2b6e3e1efda4fc33cad (HEAD -> master)
Author: lit <lit@example.com>
Date:   Tue Dec 17 17:04:30 2019 -0600

    this is the message
++ echo another line
++ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   file1.txt

no changes added to commit (use "git add" and/or "git commit -a")
++ git add file1.txt
warning: LF will be replaced by CRLF in file1.txt.
The file will have its original line endings in your working directory
++ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   file1.txt

++ git rm --cached file1.txt
rm 'file1.txt'
++ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        deleted:    file1.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        file1.txt

++ git commit '--message=this is the second message'
[master a28bb98] this is the second message
 1 file changed, 1 deletion(-)
 delete mode 100644 file1.txt
++ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)

        file1.txt

nothing added to commit but untracked files present (use "git add" to track)
++ git log
commit a28bb987b69c69fabe92154b5f6929fd65819bfd (HEAD -> master)
Author: lit <lit@example.com>
Date:   Tue Dec 17 17:04:36 2019 -0600

    this is the second message

commit c3f5d0f7da49b4eacc8df2b6e3e1efda4fc33cad
Author: lit <lit@example.com>
Date:   Tue Dec 17 17:04:30 2019 -0600

    this is the message

2 个答案:

答案 0 :(得分:1)

更改不会从登台区域中删除整个文件将从临时区域中删除。

git rm --cached file1.txt
rm 'file1.txt'
++ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        deleted:    file1.txt

请注意,这显示为要提交。这意味着文件{em>是在HEAD提交中(请参阅git status的最后一节)。

对此的思考方式是:

  • Git始终承诺存储整个文件。它们不存储更改。
  • 每个提交都有自己独立的文件集,与其他每个提交完全不同。 (但是,由于提交中的文件 完全是只读的,并且一直冻结,因此,只要这些文件的内容与其他任何提交都可以 share 文件共享匹配。您永远都不能更改任何提交,而不是一个位,就可以实现这一点。)
  • 下一个 提交中的文件是当前存储在 index 中的文件。

索引是如此重要(和/或名称如此糟糕),以至于Git实际上有三个名称。有时,Git将其称为 index 。有时,Git将其称为临时区域。有时(最近很少见)Git将其称为缓存。这些不同的名称反映了使用此事物(索引/暂存区/缓存)的不同方式,但在大多数情况下,它们全都是一回事。

尽管它很重要,但是Git很少让您看到,至少是不能直接看到。您可以轻松地看到您的工作树(或工作树或任意数量的相似术语-再次都指同一件事)中的内容,因为您的工作树我喜欢用连字号将日常文件保存为日常格式,以便计算机上的每个程序都可以看到它们并使用它们。对于提交中的文件或索引中的文件,情况并非如此。

通常,当Git向您显示一个提交时,它通过将该提交与其他提交进行比较来显示它。最常见的比较是子提交和其直接父提交。当您有一个只有两个提交的新仓库时,一个是父提交,另一个是孩子,git show通过以下方式向您显示孩子中的内容:

  • 从父级提取所有文件到临时工作区; 1
  • 将孩子的所有文件提取到临时工作区;和
  • 比较这两个工作区域中的所有文件。

然后,它只是告诉您有关不同的文件,并且默认情况下,还会向您显示它所具有的区别。

提交中的文件采用特殊的,只读的,冻结的,仅Git的格式,Git将该格式称为 blob对象。您真的不需要知道这一点(不会有任何测验?)来使用Git。但这很有用,因为您确实需要了解索引,才能使用Git。存储在Git的 index 中的文件具有相同的只读Git只读格式。 2 这意味着您实际上不能看到它们-至少并非没有Git将它们提取到某个地方。

当您git checkout提交时,Git会将该提交的文件复制到索引中(但有关技术上的严格性,请参见脚注2)。然后,它将冻结格式的文件复制(并消除Git化)到您的工作树中,以便您可以查看并使用它。

您现在可以使用工作树文件。如果您以任何方式更改(无论是完全替换还是已修改),这对索引没有影响。不过,您可能需要在新提交中更改文件,因此现在应该在该文件上运行git addgit add的工作是将文件的工作树副本打包为内部仅Git格式,然后将其写入索引(有关技术准确性,请再次参见脚注2)。

当您进行 new 提交时,Git将 index的文件打包为新的提交。因此,现在新提交和索引匹配。新的提交成为 当前的提交。如果您在更新索引的过程中,所有三个存储区域都将匹配:当前提交,索引和您的工作树。

如果愿意,可以从索引中删除文件。您既可以从工作树中删除它,也可以将其保留在工作树中。无论哪种方式,您要做的都是安排您提交的 next 提交,使其完全没有文件。


1 此临时工作区不是您的工作树,它主要是供您使用的。实际上,考虑到内部存储提交的方式,Git通常可以免去烦恼,根本不去提取很多东西:对于Git来说,很容易知道提交F中的文件P是完全相同的例如,在提交F中作为文件C的文件,因此对于所有未更改的文件,Git根本无法执行任何操作。

2 从技术上讲,索引仅包含文件名和对Git用于存储文件内容的内部Blob对象的引用。但是您可以在不知道这一点的情况下使用Git:可以想象到包含整个文件内容的索引,至少直到您开始深入了解Git内部并直接使用git ls-files --stagegit update-index为止。


以上摘要

以上所有内容的简短版本是索引充当在其中构建下一个提交的地方。它具有每个文件的副本,或更准确地说,是对该副本的引用,格式为该文件在新的或现有的提交中会或确实具有的形式。

运行git commit时,Git将索引打包成一个新的提交。创建新提交后,新提交将尽快成为当前提交。 3 因此,现在索引和提交匹配。 git checkout之后也是正常情况:索引和提交通常匹配。您可以使用git add和/或git rm使它们不匹配。然后,您从索引 进行新的提交,它们再次匹配。索引以当前提交的副本开始。然后,您对其进行更改(将整个新文件放入或取出整个文件)来构建建议的 new 提交。然后提交并匹配。 4 所有这些操作几乎都是不可见的,因为您可以看到一起使用的只有这些文件。在您的工作树中。


3 这是如此之快,以至于不可能不将其视为单个操作。但这实际上是单独的操作:“写出提交”,然后“更新一些引用”。在大多数情况下,参考更新需要在参考的 reflog 中添加,在这里您可以-至少从理论上讲,如果您足够快的话-可以看到这些步骤。

4 此规则有一些 例外。参见例如Checkout another branch when there are uncommitted changes on the current branch。最终,也要研究git commit --only。但这至少是相对可靠的。


使用git status查看索引

请记住,索引(或临时区域,如果您更喜欢该名称)实际上位于当前提交(Git称为HEAD)和工作树之间。也就是说,您可以在左侧绘制当前提交,在中间绘制索引,在右侧绘制工作树:

  HEAD        index     work-tree
---------   ---------   ---------
README.md   README.md   READNE.md
file.txt    file.txt    file.txt

HEAD副本是只读的。您可以将复制到索引和/或工作树,但是不能将复制到 index 副本可以批量替换(git add)或完全删除(git rm)。工作树副本是一个常规文件,因此您可以执行计算机可以执行的任何操作,甚至根本不需要使用Git。

您无法直接看到文件的索引副本,但是git status会进行比较并告诉您什么是不同的。实际上,git status运行两个比较:

  • 首先,它将HEAD与索引进行比较。对于每个相同的文件,它什么也没有说。对于不同的文件,它会报告已暂存以提交的内容。

  • 然后,它将索引与您的工作树进行比较。对于每个相同的文件,它什么也没有说。对于不同的文件,它将报告未上演提交的内容

这以一种非常有效的方式告诉您索引中的内容:即下一次提交中的内容。如果它与当前提交中的内容不同,则会看到已更改提交内容。如果它与工作树中的内容不同,则会看到为提交而进行的更改 not

这里有最后一道皱纹。由于您的工作树是您的您的,因此您可以对其进行任何操作,因此可以将其中没有 的文件放入索引中。或者,您可以将文件放在HEAD,索引和工作树这三个位置中,并将其从索引中删除,而无需在其他位置删除它。您无法将其从提交中删除-不能更改任何提交-因此它仍保留在其中,但它也可以保留在工作树中,并且/或者您可以在工作树中更改文件。

Git称为索引中未跟踪文件的任何文件都是索引中的不是,但在工作树中是 。这是未跟踪文件的实际定义:,它只是工作树中存在的文件,而在索引中不存在。

由于您可以更改索引(将文件放入,或git rm --cached取出文件),因此可以随时更改任何文件的未跟踪状态。无轨状态总是与索引中的内容相关。

无论如何,当您要做拥有未跟踪的文件时,git status通常会抱怨它们。要进行关闭(例如,不要抱怨您的所有构建工件都未被跟踪),您可以在.gitignore文件中列出文件名或glob模式。 .gitignore 中的这些条目不会使文件无法跟踪。他们只是告诉git status对他们进行关闭,并告诉git add不要对它们进行添加默认情况下。不过,如果已经跟踪了与.gitignore行匹配的文件,它将保持跟踪状态。

答案 1 :(得分:1)

您想要git reset file1.txt,它将重置file1.txt索引条目以指向您指定的提交(默认值为HEAD,即您当前签出的提交,此处)的内容,以替换无论您用git add放在哪里。您要做的是完全删除该路径。索引快照是没有file1.txt的树,提交的快照是没有file1.txt的树。它们是不同的,因此,从字面上“当然”,git commit很高兴提交新快照。