【问题标题】:Git commit commits staged and unstaged fileGit commit 提交暂存文件和未暂存文件
【发布时间】:2019-05-06 01:30:17
【问题描述】:

TL;DR:当一个文件已暂存和未暂存更改时,提交将同时提交两个版本,以及对文件的最新更改。为什么?我认为提交只会提交暂存版本,如此处所述:https://stackoverflow.com/a/19892657/279516

假设我们的工作目录中有一个文件。 (它之前已提交给 repo。)

$ ls
foo.txt

文件内容目前只有一个字符(数字 1):

$ cat foo.txt
1

让我们将文件的内容更改为“12”。现在我们有了这个:

$ cat foo.txt
12

我们的工作目录显示了更改(为简洁起见,删除了指导性 git 输出):

$ git status
modified:   foo.txt

现在git add 会将该文件添加到暂存索引中。

$ git add foo.txt

你在这里看不到它,但文件名现在是绿色的,表明它已经被暂存:

$ git status
modified:   foo.txt

此时,我们可以提交文件,它将成为我们本地存储库的一部分。不过,让我们先更改foo.txt,看看会发生什么。

$ cat foo.txt
123

如果我们检查git status,我们会看到 两个 版本的 foo.txt:

$ git status
On branch master
Changes to be committed:

        modified:   foo.txt

Changes not staged for commit:

        modified:   foo.txt

第一个foo.txt 是绿色的。第二个是红色的。第一个的内容为“12”。第二个有“123”。如果我们现在提交会发生什么?只应提交暂存的foo.txt。因此,以“12”为主体的foo.txt 将在我们的本地仓库中。 foo.txt 的另一个版本仍然在我们的工作目录中。

foo.txt 提交到我们的本地存储库,就像我们添加它时一样:

$ git commit -m "Added 2 to foo." foo.txt

然而,事实并非如此。我们的工作目录现在没有任何变化。两个版本都已提交。为什么?

$ git status
On branch master
nothing to commit, working tree clean

【问题讨论】:

    标签: git


    【解决方案1】:

    除了现有的(正确的)答案之外,值得注意的是,在使用git commit [flags] file1 file2 ... fileN 时,您可以输入标志--only--include

    • --only 是默认值,这意味着 忽略我到目前为止的内容,只需添加这些文件进行提交即可

    • --include 的意思是到目前为止,也添加这些文件

    这很简单,但有点错误,因为--only 也必须采取提交后 操作。

    正确理解需要知道 Git 的 index 是什么,以及 git commit 实际上如何提交 index 中的内容,而不是工作树中的内容。

    Git 程序员设想您使用 Git 的方式

    索引是一个相当复杂的东西,但大部分归结为:索引包含将进入您的下一个的文件集 也就是说,如果您现在运行git commit(不列出任何文件),new 提交将是索引右侧所有文件的快照现在,现在保存索引中的内容

    this 的意思是紧接着:

    $ git clone <some-url>
    $ cd <repository>
    $ git checkout <branch>
    

    在您的索引中,具有相同内容的所有相同文件,与您在工作树中看到的一样。

    也就是说,每个提交都是该提交中所有文件的完整快照,以一种特殊的、压缩的、仅限 Git 的形式永久冻结。这意味着您始终可以通过让 Git 提取和解压缩它们来取回所有这些文件的原始形式,这就是 git checkout 所做的:它在分支上找到 latest 提交,并提取和解压缩文件。 (这太简单了:git checkout 真的很花哨。但这是它所做的最基本的事情。)

    有用的格式文件位于您的工作树中,您可以在其中查看并处理它们。这很好,因为已提交的已冻结且仅限 Git,这显然是个问题。 :-)

    但是,要进行 new 提交,而不是重新压缩所有工作树文件(在很多情况下这需要很长时间),Git 所做的是 save 这个东西中的(解冻但仍然压缩且仅 Git)文件,不同地称为 index暂存区缓存。因此,如果您的工作树中有 README.txtmain.py,那么您的当前提交(它们被冻结的地方)中也有它们(仅 Git 形式) (解冻的地方,但仍然仅限 Git)。

    运行 git add README.txt 告诉 Git 用工作树副本覆盖索引副本:获取普通格式文件并将其重新压缩为仅 Git 格式,替换 README.txt在工作树中的索引中。它还没有冻结——你可以在提交之前再次覆盖它——但它现在准备好了

    运行git commit,不指定任何文件,告诉Git:立即打包索引中的任何内容,并从中进行新的提交。很快,因为文件已经是正确的形式:Git 只需要将它们冻结到新的提交中。 (当然,除非您将索引中的文件 不同于 中的文件与 current 提交中的文件,否则没有意义。)

    请注意,在git commit 从索引进行new 提交后,您通常会回到索引和当前提交匹配的正常情况。如果您 git add-ed 更改了所有文件,则每个文件的所有 三个 副本(HEAD、索引和工作树)都匹配。

    介绍--only--include

    运行git commit 列出的一些文件有点不同,这就是--only--include 的用武之地。如果你使用--include,Git 基本上是@987654346 @ 在列出的文件上。它只是git add &lt;those files&gt; &amp;&amp; git commit 的简写,或多或少。

    但是,如果你使用--only——或者不指定,这意味着--only——Git 所做的就是将常规索引排除在外,而是创建一个新的临时索引来自冻结提交中的任何内容。对于这个新的临时索引,Git 将 git add 您列出的每个文件。然后 Git 将从这个 other 索引进行新的提交。但是现在出现了一个问题,因为 Git 现在需要回到正常的索引,这就是它变得有点棘手的地方。

    从临时索引提交 new 后,Git 现在需要以同样的方式更新 real 索引。本质上,在从临时索引提交后,Git 会将您列出的 same 文件集重新添加到 real 索引中,以便它们再次匹配。

    让我们再次使用README.txtmain.py 的两个文件示例。这一次,让我们在每个文件后添加一个版本号。它不是文件名称的一部分,只是为了帮助我们记住:

        HEAD           index          work-tree
    -------------   -------------   -------------
    README.txt(1)   README.txt(1)   README.txt(1)
    main.py(1)      main.py(1)      main.py(1)
    

    它们开始时每个文件的所有三个版本都相同。 (请注意,您不能更改HEAD 副本。您只能进行 提交,然后变成 HEAD复制,因为 HEAD 命名了新的提交。)

    现在您在工作树中编辑两个文件:

        HEAD           index          work-tree
    -------------   -------------   -------------
    README.txt(1)   README.txt(1)   README.txt(2)
    main.py(1)      main.py(1)      main.py(2)
    

    假设您现在使用git add main.py 将工作树版本复制到索引中:

        HEAD           index          work-tree
    -------------   -------------   -------------
    README.txt(1)   README.txt(1)   README.txt(2)
    main.py(1)      main.py(2)      main.py(2)
    

    如果您现在运行一个普通的git commit,新的HEAD 将有旧的README.txt,因为索引 有旧的README.txt。但是,让我们运行git commit --only README.txt。这会创建一个 临时 索引,因此我们有:

        HEAD         temp-index       work-tree
    -------------   -------------   -------------
    README.txt(1)   README.txt(2)   README.txt(2)
    main.py(1)      main.py(1)      main.py(2)
    

    接下来,这会从临时索引中创建一个新的提交

        HEAD         temp-index       work-tree
    -------------   -------------   -------------
    README.txt(2)   README.txt(2)   README.txt(2)
    main.py(1)      main.py(1)      main.py(2)
    

    同时,real 索引还没有改变。向上滚动一下看看:main.py 是哪个版本的?其中README.txt是哪个版本的?

    如果 Git 现在只是切换回真正的索引,同时保留你刚刚所做的提交,这就是你所拥有的:

        HEAD         ugly-index       work-tree
    -------------   -------------   -------------
    README.txt(2)   README.txt(1)   README.txt(2)
    main.py(1)      main.py(2)      main.py(2)
    

    也就是说,您的工作树是所有最新文件。您的提交有更新的README.txt。但是这种丑陋的状态意味着下一个提交将使用README.txt旧/错误版本!所以这就是为什么 Git 现在将 README.txt 重新添加到真正的索引中,这样你就可以得到:

        HEAD            index         work-tree
    -------------   -------------   -------------
    README.txt(2)   README.txt(2)   README.txt(2)
    main.py(1)      main.py(2)      main.py(2)
    

    现在您可以根据需要使用更新后的main.py 进行第二次提交。

    【讨论】:

    • 当。谢谢(你的)信息。这比我想象的要多。 :)
    【解决方案2】:

    如果您只想提交暂存版本,请运行 git commit 而不指定任何文件。

    例子:

    $ echo 2 > foo
    $ git add foo
    $ echo 3 > foo
    $ git commit -m haha
    

    现在已提交暂存版本,未暂存的更改仍保留在您的工作目录中。这很容易验证:

    $ git show HEAD:foo
    2
    $ git diff
    --- a/foo
    +++ b/foo
    @@ -1 +1 @@
    -2
    +3
    

    也许让我用另一个例子来演示这种行为(git commit 带有文件):

    执行以下操作:

    $ git init
    $ echo 1 > foo
    $ echo 1 > bar
    $ git add foo bar
    $ git commit -m 1
    

    现在foobar 都已提交

    $ echo 2 > foo
    $ echo 2 > bar
    

    现在两者都更改了,让我们暂存foo 并提交bar

    $ git add foo
    $ git commit -m 2 bar
    $ git status
    Changes to be committed:
        modified: foo
    $ git diff --name-only HEAD~ HEAD
    bar
    

    你看到foo在提交中没有改变,但是暂存状态被保留了。

    【讨论】:

      【解决方案3】:

      因为你使用git commit [...] foo.txt

      https://git-scm.com/docs/git-commit

      git 提交 [...] [&lt;file&gt;…​]

      &lt;file&gt;…​ 在命令行上给出文件时,该命令将提交命名文件的内容,而不记录已经暂存的更改。这些文件的内容也会在之前暂存的内容之上暂存以供下一次提交。

      【讨论】:

      • 那么默认行为是提交甚至未暂存的更改?如果是这样,为什么还要麻烦做git add?以及如何只提交分阶段的版本?
      • @BobHorn 要仅提交暂存版本,请运行 git commit 而不指定任何文件。
      • 啊,这就是我所缺少的。谢谢。
      猜你喜欢
      • 2020-07-18
      • 2012-08-26
      • 1970-01-01
      • 2021-03-26
      • 2015-01-09
      • 1970-01-01
      • 1970-01-01
      • 2015-09-02
      • 2018-10-04
      相关资源
      最近更新 更多