“git stash”可以应用哪些更改,工作目录中的更改或/和阶段中的更改?

时间:2018-01-08 21:15:16

标签: git

git add对功能分支进行了一些更改,然后意识到我应该在一个新的功能分支上工作。

感谢jmargolisvt's reply,我找到了一个解决方案:然后我运行git stash而没有撤消git add(即我没有运行git reset HEAD .),然后{{1 },git checkout mastergit pullgit checkout -b newbranch。 现在更改在我的新分支上,而不是在我的原始分支上。

jmargolisvt指出git stash pop只能隐藏工作目录中的更改,而不是阶段中的更改(即git stash已应用的更改),因此我应该运行git add在运行git reset HEAD .之前撤消git add。但我的案例表明,git stash可以存储已应用git stash的更改。所以我想知道真相是什么?

感谢。

3 个答案:

答案 0 :(得分:2)

git stash隐藏任何未提交的内容。因此,这包括已暂存但尚未提交的更改(即git add编辑的所有内容。

但是:只要你没有推动改变,你就不应该为此烦恼。只需提交,执行git checkout -b newbranchname,如果您想将其他分支重置回原点,请执行前一个分支的checkout并将其重置为跟踪分支:{{ 1}}

如果你习惯于玩弄当地的分支机构,那么你就会变得更加灵活。 git checkout oldbranchname && git reset origin/oldbranchname只有在你想快速回到你所提交的内容时才真正有用。

记住:分支不是真正的分支。它只是一个指向提交的名称。

答案 1 :(得分:1)

默认情况下,

git stash会保留所有跟踪但不在HEAD上的内容(即无论是否在索引中都有更改的文件)。如果您想要存储但保持索引不变(即保持git add的效果),则可以使用git stash save --keep-index。如果您要将文件添加到git尚未跟踪的存储中,您可以使用git stash save --include-untracked。更多选项:https://git-scm.com/docs/git-stash

答案 2 :(得分:1)

git stash命令违反了一些关于Git的常规适用规则,但最终结果并不令人惊讶。让我们稍微回顾一下你可能已经知道的事情,但可能没有真正认识到重要的事情。在那之后,让我们看看git stash做了什么。

Git主要是关于提交

你需要了解的关于Git的第一件事是,它主要是关于提交,它们由哈希ID 标识。您将在git log输出中看到的这些哈希ID对于人类来说是无用的,因为没有办法让它们保持平直。因此,Git使用master之类的名称来扩充它们 - 这是一个分支名称 - 或v1.2,一个标记名称;或origin/feature,它是远程跟踪名称 1 每个此类名称都存储一个(且只有1个)哈希ID。我们说这些名称​​指向提交:

a123456   <-- master

每个提交还存储一个哈希ID,即提交的。这使得孩子提交回到&#34;它的父母。这让Git跟随一个链:从最近的提交开始 - 例如master指向的提交 - 并用它做一些事情;然后用那个提交的父母做同样的事情;然后再与父母的父母(祖父母)一起做,等等。例如,git log的作用。真的,我们有:

... <-3c39aef <-a123456   <-- master

名称master会导致提交a123456,然后提交a123456会导致返回某些早期提交,依此类推。换句话说,Git向后工作

1 这些也称为远程跟踪分支名称。我不喜欢远程跟踪分支这个词作为&#34;分支&#34;已经装得很重了。 (单词&#34; track&#34;也是重载的!至少单词 remote 通常非常一致地使用。)这些名字只记得存储在某些其他存储库。

索引

在Git中进行 new 提交的过程非常简单,但对于任何使用过几乎任何其他版本控制系统的人来说,最初都会感到惊讶。当您运行git commit时,Git会获取 index 中当前的所有内容,并使用它来进行新的提交。由于这个新提交是新的,它获得一个新的,唯一的哈希ID,不同于每个现有提交的哈希ID。新提交包含索引中的任何内容的快照。当然,你是新提交的作者;并且新提交的提交是刚刚在您创建新提交之前已经签出的提交。

换句话说,如果刚才a123456 当前提交,并且您运行了git commit,那么现在a123456就是新的当前提交的父,带有新的哈希ID。我们假设新的哈希ID是b789abc

... <-a123456 <-b789abc   <-- master

Git使用这个名为 index 的东西是第一个令人惊讶的部分。大多数版本控制系统都有类似的东西,但要隐藏它;但是Git 需要你知道索引。同时,master点变化的ID是第二个令人惊讶的部分。

分支机构名称

请注意,分支名称 master并不真正了解分支本身。它只是存储一个哈希ID!好了,还有一个功能:在您进行新提交时自动更改。这是关于分支名称的特别之处。它们在您提交时自动更改

要选择要更改的分支名称,请运行git checkout branch。这也会选择您签出的提交。您现在签出的提交与名称标识的提交相同。根据定义,这一切都是如此:如果您签出分支,分支名称&#34;指向&#34; (具有其值)提交哈希ID,以及您已签出的提交

工作树,跟踪,未跟踪和忽略的文件

存储在Git中的所有文件 - 甚至只是在索引中 - 都是特殊的,通常是高度压缩的形式。 Git命令通常是计算机上唯一可以处理这些压缩文件的东西。因此Git需要一种方法来获取这些普通形式的文件的副本,以便您可以使用它们。这是工作树

Git也有未跟踪文件的一般概念。即使您无法看到,索引也会在您的面前显示:Git将跟踪的文件定义为 in in索引。 Git将未跟踪的文件定义为索引中的文件。具体来说,它是工作树中 的文件,但索引中的不是

由于git commit使用索引中的任何内容进行新提交,我们可以从此定义中看到, 跟踪的内容不会进入新提交。 (这与git stash高度相关。)

当您第一次git checkout任何现有提交时,通常通过在分支名称上使用git checkout,Git通常会使用该提交中的任何内容填充索引。所以现在索引与提交匹配。然后Git从索引中提取所有内容到工作树中。所以现在工作树匹配索引,该索引与当前提交匹配。换句话说,您有每个文件的三个副本

当你运行git add filename时,你真正在做的是告诉Git:filename从工作树复制到索引中。如果文件已经在index,您只是替换数据,将其更新为您在工作树中放置的任何内容。如果该文件之前不在索引中,那么现在就是,现在它已被跟踪。它尚未提交 - 您只是将其复制到索引中。但现在它已经在索引中了,所以它被跟踪了。

当Git遇到未经跟踪的文件时 - 工作树中的文件,而不是索引中的文件 - Git倾向于抱怨。 Git可能会非常发牢骚和吵闹。所以.gitignore让你告诉Git:嘿,你知道这个文件未被跟踪吗?它的应该未跟踪。已经关闭了这个!这也告诉Git不要开始跟踪文件 - 即,不要将它添加到索引中 - 如果你使用一个集体git add .git add --all。如果它已经存在,它永远不会将索引的文件 out ,因此,列出.gitignore中的文件永远不会删除文件。它只对未跟踪的文件产生影响。

在任何情况下,一旦您使用git add将文件复制到索引中,您就可以在Git分配给新提交的哈希ID下创建一个永久保存该文件版本的新提交。如果运行git commit进行新提交,则新提交将添加到当前分支的末尾,因为Git会将新提交分配给分支名称。

Git stash

所以,最后,我们可以看一下git stash的作用。 (注意:我将在这里忽略git stash -ugit stash -a,只是覆盖&#34;正常&#34; stashes。)git stash save实际做的是做< em>两次提交。这个过程有点不寻常,但Git使用与以前相同的提交机制。

这两个提交中的第一个保存了索引。由于Git是围绕从索引提交而构建的,因此实际上这很容易。我们假设你的哈希ID只是H的提交:

...--G--H   <-- master

关于git stash首次提交的有趣之处在于它根本不会进入分支master。实际上,它会进入 no 分支。让我们用小写字母i表示此索引提交,使用小写字母:

...--G--H   <-- master
        |
        i

然后,git stash继续进行第二次提交。这个更难做,它的作用是使用备用的额外的临时索引来制作它。 Git通过将普通索引中的所有内容复制到新临时索引中来填充临时索引,但是根据工作树中的任何文件进行更新。也就是说,新的临时索引作为原始索引的副本开始,然后添加工作树文件(如果它们缺失则从中删除)。现在Git进行了第二次提交,但不是一个,而是两个父级。让我们为第二次提交w调用&#34; work-tree&#34;:

...--G--H   <-- master
        |\
        i-w

git stash的最后一步是设置一个名称来记住此w提交的哈希ID。 Git使用特殊名称refs/stash(不是分支名称):

...--G--H   <-- master
        |\
        i-w   <-- refs/stash

请注意,未跟踪的文件不会出现在中: iw都没有任何未跟踪的文件。但是,如果您对某些文件运行git add,那么iw中的 文件(以及i中的内容都是 w提交匹配,除非您在 {{1}将其添加到索引后再次更改工作树副本

稍后,当您git addgit stash apply git stash pop对时,Git将提取这些提交中的所有文件,并将其与{{1}中的内容进行比较提交,并使用它来构建索引和/或工作树中的更改。

结论

因此,您应该了解i-w

  • 提交。
  • 它提交的提交实际上就像你可以做的任何其他提交一样,除了它们不在任何分支上。
  • 因为他们在 no 分支上,他们不会被H复制(这主要是关于将旧提交复制到更新的,可能是改进的,提交)
  • 这些提交中的内容是您运行git stash时索引中的内容,这与您所做的任何普通提交的规则相同。
  • 您可以随时进行普通提交,而不是使用git rebase。除非你非常确定你有一个快速简便的案例,否则这通常是一种更好的工作方式。

还有一些更高级的事情需要了解,例如使用git stash(又名git stash)和-u(又名--include-untracked)和-a (又名--all),但正确处理这些是很棘手的。我已经看到很多人在不了解它们的情况下使用它们,以后会遇到麻烦。 (特别是,提取-k--keep-index stashes可能会有问题。-u选项,我认为主要用于测试,以任何自动方式使用也有点棘手。 )