如果未暂存file.txt,`git commit file.txt`会做什么? git commit的附加选项这似乎非常复杂

问题描述

我有一个文件夹,其中已将除一个文件(file.txt)以外的所有文件添加到了暂存区域。 此时,当我运行命令git commit file.txt时会发生什么?

git是否在提交前自动将其添加到暂存区? 到底犯了什么?只是文件还是所有文件?

解决方法

正如https://git-scm.com/docs/git-commit所解释的那样,提交是对file.txt的更改(无论它们是否已暂存),并且其他任何已暂存或未暂存的文件的状态都不会更改。

由于在提交中将包含分阶段的更改,所以已提交的文件将不再具有分阶段的更改。

在对许多文件进行更改后,可以通过为git commit提供路径名来更改更改的记录顺序。给定路径名后,该命令将仅记录对命名路径所做的更改:

$ edit hello.c hello.h
$ git add hello.c hello.h
$ edit Makefile
$ git commit Makefile

这将记录对Makefile的修改。针对hello.c和hello.h进行的更改未包含在结果提交中。但是,它们的更改并没有丢失-它们仍然处于上演阶段,只是被阻止。完成上述步骤后,请执行以下操作:

$ git commit

第二次提交将按预期记录对hello.c和hello.h的更改。

提交将是对file.txt所做的更改。在测试中,即使我已将变更暂存到c1.txt,已暂存和未暂存的更改都已作为提交的一部分提交。这是有道理的,因为在不同阶段进行的更改会立即全部提交,并且提交后没有区别。

$ git init ./
Initialized empty Git ...
$ echo a > a1.txt
$ echo b > b1.txt
$ echo c > c1.txt
$ git add --all
$ git status
On branch master

No commits yet

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

    new file:   a1.txt
    new file:   b1.txt
    new file:   c1.txt

$ echo 'more c' >> c1.txt
$ git commit c1.txt

(editor comes up)

[master (root-commit) 2849446] c1.txt
 1 file changed,2 insertions(+)
 create mode 100644 c1.txt
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   a1.txt
    new file:   b1.txt

,

这里的Git文档不是很好。遗漏的是首先准确地解释了Git对git commit的用途,在git commit --only file.txtgit commit --include file.txt变得有意义之前,人们需要什么。

您可能已经知道Git提交是用随机的哈希ID 而不是简单的顺序号编号的。每个提交的哈希ID实际上是该提交的全部内容的加密校验和,这是导致提交中的任何内容都不能被更改的关键原因,即使是Git本身也是如此。

您可能不知道,但应该知道,每次提交都会保存Git知道的每个文件的完整快照。也就是说,当您运行git commit时,Git会创建这些快照之一。该快照成为提交的主要数据。提交还包含元数据,包括您的姓名和电子邮件地址,以及上一个提交的提交编号(哈希ID)。

我们在这里讨论的有趣部分是Git知道的短语 files 。这些是什么文件?我们将在稍后讨论,但是请考虑以上两个事实的含义:

  • 没有提交可以更改;
  • 提交包含快照。

这意味着提交中的文件 是只读的。它们以特殊的仅Git格式存储,经过压缩和重复数据删除。重复数据删除可以解决许多提交重复使用相同文件的事实。仅Git格式将永久冻结,因此可以安全地重用这些文件,而这些文件实际上是无法更改的。但这也意味着这些文件不能用于进行新工作

要实际执行工作,您需要每个文件的常规,普通的读/写副本。 Git将从提交中提取提交的文件,并将这些提取的文件放入这些常规的读/写副本中。这些普通文件副本位于Git所说的工作树工作树中。

您可能会认为这些是Git知道的文件,但实际上并非如此。其他系统会执行类似的操作,但Git不会。取而代之的是,Git会在每个文件中保存一个第三个​​副本,这在Git中称为 index staging area 或(很少)天-缓存。当您检出一些提交时,Git会同时填充其索引(该文件以冻结,重复数据删除和仅Git格式保存文件)和您的工作树。由于已经进行了重复数据删除,因此索引中与当前提交中的文件匹配的所有“副本”均不使用空间。 1

您通常需要使用的git add命令告诉Git:使该文件的索引副本与工作树副本匹配。这意味着,此时,Git将工作树副本压缩为经过Git验证的,易于提交的表单,如果该副本是重复的,则将其重复数据删除。无论哪种方式,文件现在都准备好提交,并位于索引中:如果它以前位于索引中,那么现在文件的另一个 version 位于索引中,如果不是,以前的索引中的t,现在是。

因此,典型的git commit,没有任何额外的选项或参数,只需打包然后索引中的所有内容即可用作新快照。它还收集元数据所需的所有信息,例如您的姓名,当前时间和一条日志消息。然后commit命令将所有这些打包在一起以进行新的提交。

Git知道的文件正是Git索引中的那些文件。实际上,索引充当您的建议的下一次提交。这就是为什么Git称它为 staging区域:的下一次提交将获取Git索引中所有内容的快照。


1 文件的索引“副本”确实使用了一些空间:一些字节用于保存文件的名称,文件的模式,其缓存数据以及内部Blob哈希ID。长度是可变的,取决于文件名。


git commit的附加选项

短语索引表示只有一个Git索引。几乎是 是。 in实际上,每个工作树只有一个主索引,或更确切地说,有一个主索引(因为您可以使用git worktree add添加更多工作树)。但是,当您运行git commit时,Git可以创建一个临时索引,甚至可以创建两个。

Git创建一个或两个临时索引的方式取决于您提供的选项。命令:

git commit --only file.txt

或:

git commit file.txt

(意思相同)将:

  • 创建一个包含当前提交的临时索引,就像您刚刚签出该提交一样; 2
  • 在该临时索引中添加file.txt的工作树副本;
  • 创建另一个包含当前索引的临时索引,然后将file.txt的工作树副本添加到 临时索引中。
  • 使用 first 临时索引创建一个新提交;和
  • 如果可行,请用 second 临时索引替换普通索引(“ the index / staging-area”)。

最终结果是,除了替换或添加了file.txt之外,新提交包含与上一个提交相同的文件。如果可行,Git会像运行git add file.txt一样继续进行,因为第二个临时索引等于运行git add file.txt的结果。如果您告诉Git not 毕竟要进行提交-有几种方法可以做到这一点,包括使用预提交钩子-Git会丢弃两个临时索引文件,好像您从未拥有过Git完全运行git add file.txt

使用git commit --include时,Git仅创建一个临时索引,而不是两个。临时索引作为主索引的副本开始,然后Git使用临时索引执行git add,然后尝试使用临时索引进行提交。如果一切顺利,则临时索引将成为主要索引。如果没有,则Git删除临时索引,并且设置看起来像Git从未运行过git add

请注意,git commit -a等同于运行git commit --include并包含Git知道的每个文件的列表。也就是说,Git会创建此临时索引,然后对其进行git add -u并尝试提交。


2 如果您没有当前提交(例如在新的空存储库中的情况),Git将创建一个空的临时索引。


这似乎非常复杂

不幸的是,它很复杂。但这就是Git真正的工作,我们需要在这里了解所有点点滴滴,以解释为什么结果是这样,包括在您最终中止提交的情况下。

但是,如果要记住所有内容,只需记住git commit通常使用 the 索引,但是使用--include--only或{ {1}},它制作了一些多余的东西并使用了它们,然后,如果一切顺利的话,使它看起来像,它什么也没做。然后咨询the documentation以更详细地记住每个临时索引文件中的内容。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...