我git add
对功能分支进行了一些更改,然后意识到我应该在一个新的功能分支上工作。
感谢jmargolisvt's reply,我找到了一个解决方案:然后我运行git stash
而没有撤消git add
(即我没有运行git reset HEAD .
),然后{{1 },git checkout master
,git pull
和git checkout -b newbranch
。
现在更改在我的新分支上,而不是在我的原始分支上。
jmargolisvt指出git stash pop
只能隐藏工作目录中的更改,而不是阶段中的更改(即git stash
已应用的更改),因此我应该运行git add
在运行git reset HEAD .
之前撤消git add
。但我的案例表明,git stash
可以存储已应用git stash
的更改。所以我想知道真相是什么?
感谢。
答案 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的第一件事是,它主要是关于提交,它们由哈希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 -u
和git 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
请注意,未跟踪的文件不会出现在中: i
和w
都没有任何未跟踪的文件。但是,如果您对某些文件运行git add
,那么i
和w
中的 文件(以及i
中的内容都是 w
提交匹配,除非您在 {{1}将其添加到索引后再次更改工作树副本。
稍后,当您git add
或git stash apply
git stash pop
对时,Git将提取这些提交中的所有文件,并将其与{{1}中的内容进行比较提交,并使用它来构建索引和/或工作树中的更改。
因此,您应该了解i-w
:
H
复制(这主要是关于将旧提交复制到更新的,可能是改进的,提交) git stash
时索引中的内容,这与您所做的任何普通提交的规则相同。git rebase
。除非你非常确定你有一个快速简便的案例,否则这通常是一种更好的工作方式。还有一些更高级的事情需要了解,例如使用git stash
(又名git stash
)和-u
(又名--include-untracked
)和-a
(又名--all
),但正确处理这些是很棘手的。我已经看到很多人在不了解它们的情况下使用它们,以后会遇到麻烦。 (特别是,提取-k
和--keep-index
stashes可能会有问题。-u
选项,我认为主要用于测试,以任何自动方式使用也有点棘手。 )