git存储修改上次提交(在gui中) - 弹出窗口没有任何结果 [英] git stash while amending last commit (in gui) - pop pops nothing
问题描述
$ p $
欢迎使用Git(版本1.8.3-preview20130601)
$ git stash save --keep-index
保存的工作目录和索引状态主站上WIP:ab0d18d设置报警f
或网络服务+记录异常+长算术
HEAD现在在ab0d18d为网络服务设置报警+记录异常
+ long算术
$ git存储
保存的工作目录和索引状态在主WIP:ab0d18d设置报警f
或网络服务+记录异常+长算术
HEAD现在位于ab0d18d为网络服务设置报警+记录异常
+长算术
然后,我在gui中修改了上次提交 以将ab0d18d提交拆分为更小的提交。我取消了一些文件,然后我点击
$ p $ git stash save --keep-index
保存的工作目录和索引状态主站上WIP:ab0d18d设置报警f
或网络服务+记录异常+长算术
HEAD现在位于ab0d18d为网络服务设置报警+记录异常
+长算术
重复上述步骤:
$ git stash保存--keep-index
保存工作目录和索引状态在主WIP:ab0d18d设置警报f
或网络服务+记录异常+长算术
HEAD现在在ab0d18d为网络服务设置报警+记录异常
+长算术
<然后我编辑了提交信息,然后我承诺。然后,我发出了 git stash pop
开始取回我的藏品并逐一提交。
$ git stash pop
#在分支大师
#未经记录的文件:
#(使用git add< file> ...来包含什么已提交)
#
#TODO.txt
没有添加提交但未跟踪的文件存在(使用git add跟踪)
已删除的引用/存储{0}(43facd88ea3548071b196324523bd017d680d6dd )
灾难!
快乐(长寿保险箱)
2问题:
编辑:gitk after I恢复了更改(修改的提交是SETUP ALARMS之一)
编辑
找到了一个方法重现这个问题 - 将会对它进行修正(可能只有最后一部分是需要的 - 试图重现我的原始情况),但对于初学者:
mkdir test_stash; cd test_stash; git init
echo f1>> f1;回波f2>> f2; echo f3>> f3;回声f4>> f4;回声f5>> f5
git add f1 f2 f3 f4 f5
git commit -m base123
echo f1 to be revised>> f1
要修改的回波改变f2>> f2
要修改的回波变化f3>> f3
git add。
git commit -m tobeamended123
echo change f4>> F4; git add f4
echo change f5>> f5
git存储--keep-index
git存储
git gui&
现在点击gui中的修改提交 。不知道它对应的是哪个命令,但是 git commit --amend
并不能解决问题。
在gui中修改状态为unstree的文件f3 再次 (点击它以便它在未分区的区域移动) git reset HEAD f3
但这不起作用)然后
git stash保存--keep-index
git commit -m修改#没有真正修改 - 一次新的提交
git stash pop
获取:
#分支master
没有提交,工作目录clean
已删除的参考文献/隐藏文章@ {0}(898687d73b65ccc9e10cd826bc12fda1a4759651)
期待: f3修改显示
/我保留下面的第一个长答复(可能会尝试将其移至新问题后来),但现在有一个重现例子,我会通过它。不过,让我列出一些要点。
-
git存储
始终保存索引和工作目录。有人可能会认为- keep-index
会让它藏起来更多,或者改变在pop 。它不!默认情况下,
git stash apply
和git stash pop
会混合分离的索引更改。添加- keep-index
不会改变这个。只有- index
参数应用
和pop
尝试以避免混合它们。 到当前 -
应用存储器会尝试更改当前状态,以反映存储状态中的更改 。这可能会很复杂,因为当前状态不一定就像您保存存储时那样。
HEAD
的更改。这意味着如果 index 与 HEAD
有一个变化,但是当前工作目录不是,那么确实没有更改保存在WIP on 分支 ...提交中。 (这是我认为的 git stash
中的一个错误。我已经发送了一个测试用例并可能修复git邮件列表。对于正常情况,这很好,但如果你已经拆分了一些部分,然后想用 git stash branch
来恢复你的确切状态,那么它会丢失工作目录状态,并且它在这里引起你的问题。) 什么 git-gui
正在做。在你启动它的时候你有这个(实际的提交数字当然会有所不同)。未标记的WIP on master是第一藏匿处,现在存放@ {1}
。
$ git存储列表
存储@ {0}:在主WIP上:c93c8fe tobeamended123
存储@ {1}:在主上WIP:c93c8fe tobeamended123
$ git log --decorate --oneline --graph --all'stash @ {1}'
* 3d01942(refs / stash)WIP on master:c93c8fe tobeamended123
| \
| * 6be9135指数在主:c93c8fe tobeamended123
| /
| * de8038c在主人的WIP上:c93c8fe tobeamended123
| | \
| / /
| * 3db6cfc index master:c93c8fe tobeamended123
| /
* c93c8fe(HEAD,master)tobeamended123
* 828d5cf base123
现在在 git gui
中,当您选择修改上次提交时,它会找到HEAD提交的引用( c93c8fe
,在我的情况下)。它实际上并没有对它做任何事情(但)。但是,只要您点击 f3
来取消它,它就会执行某些操作:它会抓取 f3 $ c $ (我不确定gui在底下使用什么,我的猜测是
在索引中的版本,它不再有这条线。 HEAD ^
的副本)并将其填充到索引中。如果您检查 f3
,它仍然有额外的行,但是如果您<
请注意,由于gui-mouse-clicks没有修改,并且没有新的提交。所有的操作都发生在索引内。
接下来,您回到命令行并运行:
$ git stash save --keep-index
提交了第三对提交,其中一个提交索引,另一个提交当前目录。索引版本在 不幸的是,您使用了 从这里开始,问题依然存在(更改不见了, 我没有看到灾难,但我看到一些令人困惑的事情,我敢打赌你会感到困惑。我不知道你为什么使用上面的 [< sup> 1 我找到了预期的用例,就在文档中:用于在提交之前测试索引中的当前内容。但是,等等,huhwha?, 运行 项目。使用 apply-without-pop意味着您可以 如果你弄得特别大,你可以使用 当您完成所有存档时,使用 这些实际上有点独立。 您编辑并 你看不到(因为它没有分支标签) code> 67dec43 f1
和 f2
中有额外的行,并且缺少 f3中的额外行
。当前目录版本应该可以在所有三个文件中都有额外的行,但是,唉,它不会,因为 git stash save
比较当前目录和 HEAD
提交,额外的行在 HEAD
提交中,所以它不在
- keep-index
参数,所以现在工作目录版本与隐藏的索引版本相同。文件 f3
不再有额外的行。
- keep-index
抛出它)。你当然可以从原始提交中恢复它(tobeamended123)。但是在这种情况下,出错的地方在于:命令行 stash
保存了索引,然后将工作目录与 HEAD $ c进行比较$ c>,它没有改变,所以没有保存(不变)到
f3
。
- keep-index
。 (事实上,我不确定什么用例 - keep-index
可能用于 1 ,并且在我看来 apply
和 pop
应该可以默认为 - index
,但这完全是另一回事......)而且,你总共做了四次推,并且只弹出了一次,剩下三次。
- keep-index
确实在存储
ref。不管怎么样,只要使用 git checkout -b test -stash
就可以保证安全地分离,直到你满意为止。如果你测试它并且它失败,并且你需要修改它,那么这个存储会有冲突。如果你测试了它并且工作,你可以将你工作的提交拉到/快进 - 合并到你早期的分支中。] tl; dr简答
git存储列表
。您会看到一个列表:
存储@ {0}:在主设备上执行:ab0d18d设置报警...
stash @ {1}:主人WIP:...
git stash apply - index 'stash @ { n }'
( - -index
是可选的),以尝试通过名称和号码应用每个保存的存储,而不弹出它们中的任何一个。这是一个堆栈,最近推送了 stash @ {0}
,并且(到此为止) stash @ {3}
第一个(最长前)推送。
git reset --hard
回到 master
并准备好 git stash apply
一个不同的藏匿处。 (确保你用一个干净的工作目录开始整个序列,也许通过添加另外一个 git stash
,尽管这可能会让人感到困惑。:-))
git stash branch name 'stash @ { n }
。这是一个大的,快速的,有效的锤子,其主要缺点是你必须发明一个分支名称。 (你可以 git stash show
看看里面有什么,以帮助你想出名字。)不要让这吓倒你,因为你总是可以重命名分支甚至以后删除它。
git stash clear $ c
仍然存在
$ b $ h2>关于 git commit --amend
vs git stash
提交--amend
可以基于您所在的任何分支进行提交链。假设你在 master
上,并且链看起来像这样(在 git log --graph --oneline --decorate
或 gitk
):
* 67dec43(HEAD,master )changesmecommit
* 9c37840 previous commit
git add
有些东西 - 我将更改文件 f3
并添加它 - 然后运行 git commit --amend
。这需要索引并进行一次新的提交,但是新提交的父节点是从 master
所在的位置,即 previous commit
以上。现在日志输出如下所示:
* 68c51f3(HEAD,master)替换为modificationsmecommit
* 9c37840以前的提交
git log $ c
$ git log --graph --decorate --oneline master 67dec43
* 68c51f3(HEAD,master)替换为修改提交
| * 67dec43changesme提交
| /
* 9c37840先前提交
您有一个分支从先前的提交中脱颖而出,新的替换提交中的 master
标签和未标记的分支上的修改提交。
让我们再次这样做,这次隐藏了。我从修改提交中的 f3
中的已知错误文件开始。然后,我放入第二个(但仍然不正确) f3
并运行 git stash
。最后,我修正了 f3
真实,并使用 - 修改
。存储保持对现在未标记的分支的引用,因为存储是新提交(实际上是两个)。以下是最后几步:
$ git log --graph --decorate --oneline
* 3c97241( WPS on master:67dec43changesmecommit
| \
| * f3a50e9在master上的索引:67dec43修改提交
| /
* 67dec43(HEAD,master)修改提交
* 9c37840前一提交
* 84408ef基
$ echo'f3'的更好变化>> f3
$ git add f3
$ git commit --amend -m'replacementmecommitme'commit'
$ git log --graph --decorate --oneline --all
* c1f1042(HEAD,master)替换为修改提交
| * 3c97241(refs / stash)WIP on master:67dec43amendmecommit
| | \
| | * master3上的f3a50e9索引:67dec43修改提交
| | /
| * 67dec43changesme提交
| /
* 9c37840先前提交
* 84408ef基准
如果您尝试应用存储空间,将会有冲突(因为存储空间更改文件 f3
,使用我的中间存储空间,不完全不好,但是b
$ b
$ git stash apply
git stash apply
自动合并f3
CONFLICT(内容):在f3中合并冲突
$ git reset --hard master
HEAD现在位于c1f1042替换为modificationsme提交
$ git stash apply --index
自动合并f3
CONFLICT(内容):合并f3中的冲突
索引不是未分离的。
$ git reset --hard master
HEAD现在位于c1f1042替换为修改提交
这些与其他任何冲突一样,例如 cherry-pick
或 merge
,你可以用同样的方法解决它们。
如果你喜欢,你可以在修改提交上粘贴一个分支或标记标签:
$ git branch master-old 67dec43
$ git log --graph --oneline --decorate --all
* c1f1042 (HEAD,master)替换为修改提交
| * 3c97241(refs / stash)WIP on master:67dec43amendmecommit
| | \
| | * master3上的f3a50e9索引:67dec43修改提交
| | /
| * 67dec43(master-old)changesmecommit
| /
* 9c37840 previous commit
* 84408ef base
现在可以很方便地参考。然后你可以检查出来,并且 git stash pop --index
那个特定的藏匿处;这是保证工作(因此 pop
是安全的,尽管您可能希望应用
,直到您完成其中几个)。另请参阅下面的使用 git存储分支
,它自动执行此操作。
/ h2>
让我们稍微退一步。我想展示一个简单的例子,只有三个文件。
让我们制作一个临时目录和git repo并提交一个起点,包含三行一行的文件:
$ mkdir / tmp / tt; cd / tmp / tt; git init
...#创建文件f1,f2,f3; git add ...
$ git commit -m base
[master 84408ef] base
3个文件已更改,3个插入(+)
创建模式100644 f1
创建模式100644 f2
创建模式100644 f3
$ ls
f1 f2 f3
$ cat f1 f2 f3
此文件保持不变
此文件更改为指数
此文件在WIP
中变化
现在让我们进行更改:
$ echo for f2>> F2; git add f2
$ echo for f3>> f3
此时, f2
被更改并演示:
$ git diff --cached
diff --git a / f2 b / f2
索引78991d3..3a2f199 100644
--- a / f2
+++ b / f2
@@ -1 +1,2 @@
此文件在索引
+ f2以上
和 f3 $ c
$ git diff
diff --git a / f3 b / f3
index d5943ba..188fe9b 100644
--- a / f3
+++ b / f3
@@ -1 +1,2 @@
此文件在WIP中的变化
+ f3
这里 diff - 缓存
显示暂存的内容(位于索引中), diff
不存在 - 缓存
显示暂停的东西。
现在,让我们 git stash
(默认op为保存
)。 隐藏
会向repo添加两个提交。第一个是 到目前为止所展示的东西(如果没有任何变化, stash
在无变化的提交中强制执行),第二个是合并提交,即加工作目录。所以:
pre $ code $ git stash
保存的工作目录和索引状态master上的WIP:84408ef base
HEAD现在在84408ef基地
$ git log --graph --oneline --decorate --all
* 753a6c8(refs / stash)在主人在制品上:84408ef base
| \
| * 36b23f2 index master:84408ef base
| /
* 84408ef(HEAD,master)base
第一个,在master
上的索引,将我改为 f2
:
$ git show 36b23f2
[snip]
diff --git a / f2 b / f2
index 78991d3 .3a2f199 100644
--- a / f2
+++ b / f2
@@ -1 +1,2 @@
此文件在索引$ b中变化$ b + more for f2
第二个有两个变化( f2
和
f3
),但是是合并提交,因此 git show
仅显示组合diff显示 f3
:
$ git show 753a6c8
[snip ]
diff --cc f3
index d5943ba,d5943ba..188fe9b
--- a / f3
+++ b / f3
@@@ -1 ,1 -1,1 +1,2 @@@
此文件在WIP中更改
++更多用于f3
(另外:如果你想比较任何合并 M
对每个父对象,使用 git show -m M
。例如, git show -m 753a6c8
第一差异 753a6c8 ^ 1
-vs - 753a6c8
,然后 753a6c8
^ 2-vs - 753a6c8
。)
什么是 - keep-index
的东西?
通常情况下,做一个 git存储
,你有一个干净的目录,所以没有什么可以重新存储,因为它是:
$ git status
#分支master
没有提交,工作目录干净
$ git存储
没有本地更改以保存
但是您要求存入 - keep-index
。这仍然使通常的存储条目,但然后它提取索引提交的内容,将其放入工作目录和索引。让我们从当前存储中弹出,查看状态( git status
- pop
是否存在状态
自动 - 然后 git log --graph --oneline --decorate --all
),然后看到我们回到了工作正在进行状态,但这次没有任何进展:
$ git stash pop
...
$ git log --graph --oneline --decorate --all
* 84408ef(HEAD,master)base
现在我们重新执行 f2
并重新执行存储
,但是这次, - keep-index
:
$ git add f2
$ git stash save --keep-index
保存的工作目录和索引状态在主设备上WIP:84408ef base
HEAD现在是84408ef base
看起来和以前一样......但不完全:
$ git status
#在分支主机上
#要提交的更改:
#(使用git reset HEAD< file> ...取消)
#
#修改:f2
#
使用 git log --graph --oneline --decorate --all
,你将会看到基本上和以前一样的东西(使用不同的提交哈希): stash
提交索引,然后提交工作树的合并提交。但是这一次它也会重新提取索引,所以现在你需要改变被提交。这只是 f2
,而不是 f3
。
这意味着你可以(稍微毫无意义地) git stash save
。你做到了!让我们在前后使用这个log-graph-one-line-decorate(我使用它很多):
$ git log --graph --oneline --decorate --all
* 7efe9a6(refs / stash)WIP on master:84408ef base
| \
| * 76c840e在master上的索引:84408ef base
| /
* 84408ef(HEAD,master)base
$ git stash save
保存的工作目录和索引状态master上的WIP:84408ef base
HEAD现在在84408ef基础
$ git log --graph --oneline --decorate --all
$ git lola
* eb383e0(参考/隐藏)WIP on master :84408ef base
| \
| * master上的aba15e6索引:84408ef base
| /
* 84408ef(HEAD,master)base
乍一看,前后看起来一样。但仔细观察在SHA-1 ID处。他们改变了!在第二个 git stash save
之前, refs / stash
命名为commit 7efe9a6
。现在它名为 eb383e0
!
reflog(或者,注意,它变得复杂)
好吧,这不是 坏,但现在你必须了解reflog。其他隐藏
去哪了?答案是,它被推送并且已经消失在reflog中。还有一点额外的皱纹:藏匿不是普通的分支或标签。相反,它在 refs / stash
中。所以这里有一种方法可以看到它,使用 git log -g
,这意味着查看reflogs:
$ git log -g --oneline refs / stash
eb383e0 refs / stash @ {0}:在主人的WIP上:84408ef base
7efe9a6 refs / stash @ {1 }:在主人的WIP上:84408ef base
$ git log -g --pretty = format:%H refs / stash
这会抛出它们的完整散列,我们可以用它作为 git的参数为了得到这个:
$ git log --graph --decorate
-oneline --decorate $(git log -g --pretty = format:%H refs / stash)
* eb383e0(refs / stash)WIP on master:84408ef base
| \
| * master上的aba15e6索引:84408ef base
| /
| * 7efe9a6在主人的在制品:84408ef基础
| | \
| / /
| * 76c840e master:84408ef base
| /
* 84408ef(HEAD,master)base
这只是为了看看还有什么在那里,在回购中。
(或者,您可以使用 gitk
,就像你一样,看看它们。 gitk
足够聪明,可以查找存储引用日志。)
(另外:您还可以使用 git reflog show refs / stash
。 reflog show
子命令只运行 git log -g --oneline
。)
再回到我们的问题, / h3>
现在我们已经完成了第一个 git stash save --keep-index
,然后是一个毫无意义的 git stash保存
,现在是什么?
那么,我们可以 git stash pop $ c $
$ git stash pop
#在分支大师
#没有为commit提交更改:
#(使用git add< file> ...来更新将提交的内容)
#(使用git checkout - < file> ...放弃工作目录中的更改)
#
#修改:f2
#
no添加到提交的更改(使用git add和/或git commit -a)
丢弃的refs / stash @ {0}(eb383e050d150a8ce5b69a3662849ffdd7070c89)
f3
发生了什么?正如我们前面提到的,第二个 git stash只保存了
保存了 的保留索引,即只改变了 f2
。我们需要的是回到第一藏匿处。
$ git stash pop
错误:对以下文件的本地更改将被合并覆盖:
f2
请在提交更改或隐藏它们之前进行合并。
正在取消
这没什么帮助,是吗? : - )
如果您不确定自己在做什么,现在是制作保存东西分支的好时机(您可以稍后再删除它)。只需 git checkout -b help-me-spock
或其他,添加和提交。这些东西现在在一个分支上,更容易跟踪。但是我们知道我们在做什么,并且在其他存储区中有 f2
。所以我们可以把它擦掉:
$ git reset --hard
现在我们回到了原来的状态,如果我们只做了一个 git stash save
,没有 - keep-index
:我们在 master
上,工作目录干净,保存单个存储。我们可以 git存储列表
它, git存储显示
它等等。所以现在:
$ git stash pop --index
#分支主
#更改为提交:
#(使用git reset HEAD< file> ...停用)
#
#修改:f2
#
#对于提交:
#(使用git add< file> ...来更新将提交的内容)
#(使用git checkout - < file> ...工作目录中的变化)
#
#修改:f3
#
丢弃的refs / stash @ {0}(7efe9a65c44156921bbbcb6a3df4edc5cb44492b)
,我们已经把所有东西都回来了。 (或者,如果没有 - index
, stash
将只将所有更改应用于工作目录,而不是恢复索引)
使用 git stash apply
关于
git stash pop
的好处是它适用,然后删除最顶端的存储条目。令人讨厌的是,它适用,然后下降条目。如果您使用 git stash apply
,它会挂起它。 除此之外,这非常方便您拼错 - index
为 - keep-index
(我在输入时多次输入),或把它留下来,稍后再决定它会很好用。您可以 git reset --hard
并重新执行 apply
。
如果您完成了一个存储条目, git stash drop entry
会将其从reflog中删除。例如,假设你做了 git stash apply --index'stash @ {1}'
,然后决定它是否好,并且想要 add
和/或
commit
它,然后忘掉那个藏匿处。然后你可以 git stash drop'stash @ {1}'
。缺点是这会重新编号: stash @ {2}
变为 stash @ {1}
,以及等等。我发现将它们全部放在一起并且使用 git stash clear
可以一次性清除所有这些内容。
等一下,这些是什么 - index
-es?
默认, git stash apply
和 git stash pop
取已保存的索引(更改已提交进行提交)和work-in -progress(更改不会提交进行提交),并将它们同时作为工作进行中。通常情况下,这很好,但如果你仔细地进行了一些比赛并将其他人暂时搁置,那么你很可能需要这些。对应用
(和 pop
)的 - index
参数试图做到这一点。有时会变得太难。在这种情况下,你有两个选择:省略 - index
,或者使用 git stash branch
。
使用 git存储分支
我在上面提到过,在关于修改后的提交与stashes的部分,你可以添加一个新的分支标签到一个存放它的提交,然后 apply
甚至 pop
相应的存储空间,和 - index
,它会始终工作。原因很简单:存储是索引和WIP的合并提交,对应于它们的提交。如果你检查了这个提交(作为一个分离的HEAD),索引和WIP 将干净地应用。
git checkout -b newname
)。现在,使用 - index
应用(并弹出)藏匿处:您现在处于与您第一次使用时相同的状态运行 git stash save
,除了分支名称不同。 And that’s what git stash branch
does: you give it a new branch name and tell it which stash to use (the default is refs/stash
, A.K.A. stash@{0}
). It uses that stash entry to find the parent commit, attaches the branch name there, and then does a git stash pop --index
. At this point you can use git status
, git diff --cached
, git diff
, etc., to see what’s in the index and what’s not, decide what else if anything to add, then git commit
to add new stuff to the new branch you’ve created.
So I had some unstaged changes and some staged ones. I issued
Welcome to Git (version 1.8.3-preview20130601)
$ git stash save --keep-index
Saved working directory and index state WIP on master: ab0d18d Setup of alarms f
or network service + Logging exceptions + long arithmetic
HEAD is now at ab0d18d Setup of alarms for network service + Logging exceptions
+ long arithmetic
$ git stash
Saved working directory and index state WIP on master: ab0d18d Setup of alarms f
or network service + Logging exceptions + long arithmetic
HEAD is now at ab0d18d Setup of alarms for network service + Logging exceptions
+ long arithmetic
Then I hit amend last commit in the gui to split the ab0d18d commit into smaller ones. I unstaged some of the files and I hit
$ git stash save --keep-index
Saved working directory and index state WIP on master: ab0d18d Setup of alarms f
or network service + Logging exceptions + long arithmetic
HEAD is now at ab0d18d Setup of alarms for network service + Logging exceptions
+ long arithmetic
Repeated the above procedure :
$ git stash save --keep-index
Saved working directory and index state WIP on master: ab0d18d Setup of alarms f
or network service + Logging exceptions + long arithmetic
HEAD is now at ab0d18d Setup of alarms for network service + Logging exceptions
+ long arithmetic
Then I edited the commit message and I committed. Then I issued git stash pop
to start getting back my stashes and committing them one by one.
$ git stash pop
# On branch master
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# TODO.txt
nothing added to commit but untracked files present (use "git add" to track)
Dropped refs/stash@{0} (43facd88ea3548071b196324523bd017d680d6dd)
Disaster !
I had backups happily (long live dropbox)
2 questions :
- what did I do wrong ?
- how should one recover from such a scenario ?
EDIT : gitk after I restored the changes (the amended commit is the SETUP ALARMS one)
EDIT
Found a way to reproduce the problem - will be amending it (probably only the last part is needed - was trying to reproduce my original scenario exactly) but for starters :
mkdir test_stash; cd test_stash;git init
echo f1 >> f1 ; echo f2 >> f2 ; echo f3 >> f3 ; echo f4 >> f4 ; echo f5 >> f5
git add f1 f2 f3 f4 f5
git commit -m base123
echo change f1 to be amended >> f1
echo change f2 to be amended >> f2
echo change f3 to be amended >> f3
git add .
git commit -m tobeamended123
echo change f4 >> f4; git add f4
echo change f5 >> f5
git stash save --keep-index
git stash
git gui &
Now hit amend commit in the gui. Not sure to which command it corresponds to but git commit --amend
does not do the trick.
While in the amend state unstage file f3 in the gui again (click on it so it moves on the unstaged area - would be git reset HEAD f3
but this does not work either) then
git stash save --keep-index
git commit -m amended # not really amended - a new commit altogether
git stash pop
Getting :
# # On branch master
nothing to commit, working directory clean
Dropped refs/stash@{0} (898687d73b65ccc9e10cd826bc12fda1a4759651)
Expecting : the f3 modifications to show up
/I'm keeping the first long reply below (might try to move it to a new question later), but now that there's a "reproduce example" I'll go through that. Let me make a list of points here though.
git stash
always stashes both the index and the work dir. One might think--keep-index
makes it stash more, or changes the way the stashed value is handled on apop
. It doesn't! Bothgit stash apply
andgit stash pop
mix together the separated index change by default. Adding--keep-index
does not change this. Only the--index
argument toapply
andpop
tries to avoid mixing them.The "work directory" that
git stash
saves amounts, in effect, to the change from the currentHEAD
. This means that if the index has a change fromHEAD
, but the current work directory does not, there's really no change saved in the "WIP on branch..." commit. (This is, I think, a bug ingit stash
. I have sent a test case and possible fix to the git mailing list. For "normal" cases it's fine, but if you've split out some parts and then want to recover your exact state later withgit stash branch
, it drops working directory state. And it's causing your problem here.)Applying a stash tries to make changes to the current state that mirror the changes in the stashed state. This can be complicated, because the current state is not necessarily anything like it was when you saved the stash.
Here's what git-gui
is doing. At the time you fire it up you have this (actual commit numbers will of course vary). The unlabeled "WIP on master" is the "first" stash, now stash@{1}
.
$ git stash list
stash@{0}: WIP on master: c93c8fe tobeamended123
stash@{1}: WIP on master: c93c8fe tobeamended123
$ git log --decorate --oneline --graph --all 'stash@{1}'
* 3d01942 (refs/stash) WIP on master: c93c8fe tobeamended123
|\
| * 6be9135 index on master: c93c8fe tobeamended123
|/
| * de8038c WIP on master: c93c8fe tobeamended123
| |\
|/ /
| * 3db6cfc index on master: c93c8fe tobeamended123
|/
* c93c8fe (HEAD, master) tobeamended123
* 828d5cf base123
Now in git gui
, when you select "amend last commit", it finds the ref for the HEAD commit (c93c8fe
, in my case). It does not actually do anything to it (yet). But as soon as you click on f3
to unstage it, it does something: it grabs the previous version of f3
(I'm not sure what the gui uses underneath, my guess would be HEAD^
's copy) and stuffs it into the index. If you examine f3
it still has the extra line in it, but if you git show :0:f3
to see the version in the index, it no longer has that line.
Note that no refs have changed due to gui-mouse-clicks, and there are no new commits. All the action has taken place inside the index.
Next, you went back to the command line and ran:
$ git stash save --keep-index
This made a third pair of commits, one with the index and one with the current directory. The index version has the extra line in f1
and f2
and lacks the extra line in f3
. The current-directory version should (one would think) have the extra line in all three files—but, alas, it does not, because git stash save
compares current dir vs HEAD
commit, and the extra line is there in the HEAD
commit, so it's not in the stashed version.
Unforunately, you used that --keep-index
argument, so now the working directory version is the same as the stashed index version. File f3
no longer has the extra line.
From here on, the problem persists (the change is gone, --keep-index
tossed it). You can of course recover it from the original commit ("tobeamended123"). But that's where things went wrong in this case: the command-line stash
saved the index, and then compared the work directory against HEAD
, which had not changed, so did not save the (non-change) to f3
.
I don't see disaster, but I see something confusing, which I bet confused you. I don't know why you used --keep-index
above. (In fact, I'm not sure what use-case --keep-index
might be intended for1, and it seems to me that apply
and pop
should probably default to --index
, but that's another matter entirely....) And, you made four total stash "pushes", and only "popped" one, leaving three to go.
[1I found the intended use-case, right there in the documentation: for testing what is currently in the index, before committing it. But wait, huhwha?, --keep-index
does commit it, on the stash
ref. You might as well just commit anyway, using git checkout -b test-stash
to keep it safely segregated until you're happy with it. If you test it and it fails and you need to modify it, that stash is going to have conflicts. If you test and it works you can just pull / fast-forward-merge the commit that worked, into your earlier branch.]
The "tl;dr" short answer
Run git stash list
. You'll see a list of:
stash@{0}: WIP on master: ab0d18d Setup of alarms ...
stash@{1}: WIP on master: ...
items. Use git stash apply --index 'stash@{n}'
(the --index
is optional) to try to apply each saved stash by name-and-number, without popping any of them. It's a stack, with stash@{0}
the most recently pushed and (by this point) stash@{3}
the first (longest-ago) pushed.
The apply-without-pop means you can git reset --hard
to get back to master
and ready to git stash apply
a different stash. (Be sure you start the whole sequence with a clean work directory, perhaps by adding another git stash
, although that could get confusing again. :-) )
If you've made a particularly big mess, you can use use git stash branch name 'stash@{n}'
. This is a big, fast, effective hammer whose main drawback is that you have to invent a branch name. (You can git stash show
the stashes to see what's in them, to help you come up with names.) Don't let this scare you, as you can always rename the branch or even delete it later. See the long description for exactly how this works.
When you're all done with all your stashes, use git stash clear
to wipe them all out.
Regarding git commit --amend
vs git stash
These are actually somewhat independent. The commit --amend
works on a commit-chain based on whatever branch you're on. Let's say you're on master
and the chain looks like this (in git log --graph --oneline --decorate
, or gitk
):
* 67dec43 (HEAD, master) "amendme" commit
* 9c37840 previous commit
You edit and git add
some things—I will change file f3
and add it—and then run git commit --amend
. This takes the index and makes a new commit, but the new commit's parent is one back from where master
was, i.e., the previous commit
above. Now the log output looks like this:
* 68c51f3 (HEAD, master) replacement for "amendme" commit
* 9c37840 previous commit
What you can't see (because there's no branch label on it) is that 67dec43
is still in there (until it expires and gets garbage collected), but if you tell git log
to look there it will:
$ git log --graph --decorate --oneline master 67dec43
* 68c51f3 (HEAD, master) replacement for "amendme" commit
| * 67dec43 "amendme" commit
|/
* 9c37840 previous commit
You have a branch coming off "previous commit", with the master
label at the new replacement commit and the "amendme" commit on an unlabeled branch.
Let's do this again, with a stash in place this time. I start with a "known bad" file in f3
in the "amendme" commit. I then put in a second (but still not right) f3
and run git stash
. Finally, I fix f3
"for real" and use --amend
. The stash keeps a reference to the now-unlabeled branch, because a stash is a new commit (really, two). Here are the last few steps:
$ git log --graph --decorate --oneline
* 3c97241 (refs/stash) WIP on master: 67dec43 "amendme" commit
|\
| * f3a50e9 index on master: 67dec43 "amendme" commit
|/
* 67dec43 (HEAD, master) "amendme" commit
* 9c37840 previous commit
* 84408ef base
$ echo 'better changes for f3' > f3
$ git add f3
$ git commit --amend -m 'replacement for "amendme" commit'
$ git log --graph --decorate --oneline --all
* c1f1042 (HEAD, master) replacement for "amendme" commit
| * 3c97241 (refs/stash) WIP on master: 67dec43 "amendme" commit
| |\
| | * f3a50e9 index on master: 67dec43 "amendme" commit
| |/
| * 67dec43 "amendme" commit
|/
* 9c37840 previous commit
* 84408ef base
If you try to apply the stash, there will be a conflict (because the stash changes file f3
, with my intermediate, "not completely bad, but not better either" version):
$ git stash apply
git stash apply
Auto-merging f3
CONFLICT (content): Merge conflict in f3
$ git reset --hard master
HEAD is now at c1f1042 replacement for "amendme" commit
$ git stash apply --index
Auto-merging f3
CONFLICT (content): Merge conflict in f3
Index was not unstashed.
$ git reset --hard master
HEAD is now at c1f1042 replacement for "amendme" commit
These are the same as any other conflict when bringing commits in, such as cherry-pick
or merge
, and you resolve them the same way.
If you like, you can stick a branch or tag label on the "amendme" commit:
$ git branch master-old 67dec43
$ git log --graph --oneline --decorate --all
* c1f1042 (HEAD, master) replacement for "amendme" commit
| * 3c97241 (refs/stash) WIP on master: 67dec43 "amendme" commit
| |\
| | * f3a50e9 index on master: 67dec43 "amendme" commit
| |/
| * 67dec43 (master-old) "amendme" commit
|/
* 9c37840 previous commit
* 84408ef base
and now it's easily available for reference. You can then check it out and git stash pop --index
that particular stash; this is guaranteed to work (hence the pop
is safe, although you might want to apply
anyway until you've done several of these). See also "Using git stash branch
" below, which automates this.
How stash works, the long version
Let's step back a bit. I want to show a simplified example, with just three files.
Let's make a temp dir and git repo and commit a starting point, with three one-line-long files:
$ mkdir /tmp/tt; cd /tmp/tt; git init
... # create files f1, f2, f3; git add ...
$ git commit -m base
[master 84408ef] base
3 files changed, 3 insertions(+)
create mode 100644 f1
create mode 100644 f2
create mode 100644 f3
$ ls
f1 f2 f3
$ cat f1 f2 f3
this file stays the same
this file changes in the index
this file changes in the WIP
Now, let's make the changes happen:
$ echo more for f2 >> f2; git add f2
$ echo more for f3 >> f3
At this point, f2
is changed and staged:
$ git diff --cached
diff --git a/f2 b/f2
index 78991d3..3a2f199 100644
--- a/f2
+++ b/f2
@@ -1 +1,2 @@
this file changes in the index
+more for f2
and f3
is changed but not staged:
$ git diff
diff --git a/f3 b/f3
index d5943ba..188fe9b 100644
--- a/f3
+++ b/f3
@@ -1 +1,2 @@
this file changes in the WIP
+more for f3
Here diff --cached
shows the staged stuff (in the index) and diff
without --cached
shows the unstaged stuff.
Now, let's git stash
(the default op is to save
). The stash
will add two commits to the repo. The first one is just the stuff staged so far (if there's nothing staged, stash
forces in a no-changes commit) and the second is a merge commit, of that-plus-work-dir. So:
$ git stash
Saved working directory and index state WIP on master: 84408ef base
HEAD is now at 84408ef base
$ git log --graph --oneline --decorate --all
* 753a6c8 (refs/stash) WIP on master: 84408ef base
|\
| * 36b23f2 index on master: 84408ef base
|/
* 84408ef (HEAD, master) base
That first one, index on master
, has my change to f2
:
$ git show 36b23f2
[snip]
diff --git a/f2 b/f2
index 78991d3..3a2f199 100644
--- a/f2
+++ b/f2
@@ -1 +1,2 @@
this file changes in the index
+more for f2
The second has both changes (f2
and f3
), but is a merge commit, so git show
shows a combined diff, only showing f3
:
$ git show 753a6c8
[snip]
diff --cc f3
index d5943ba,d5943ba..188fe9b
--- a/f3
+++ b/f3
@@@ -1,1 -1,1 +1,2 @@@
this file changes in the WIP
++more for f3
(Aside: if you want to compare any merge M
against each parent, use git show -m M
. For instance, git show -m 753a6c8
first diffs 753a6c8^1
-vs-753a6c8
, then 753a6c8
^2-vs-753a6c8
.)
What's with this --keep-index
thing?
Normally, after you do a git stash
, you have a clean directory so there's nothing to "re-stash", as it were:
$ git status
# On branch master
nothing to commit, working directory clean
$ git stash
No local changes to save
But you asked stash to --keep-index
. That still makes the usual stash entry, but then it extracts the contents of the index commit, putting that into both the working directory and the index. Let's pop off the current stash, look at the state (git status
—pop
does the status
automatically—and then git log --graph --oneline --decorate --all
), and see that we're back to the work in progress state but there's nothing staged this time:
$ git stash pop
...
$ git log --graph --oneline --decorate --all
* 84408ef (HEAD, master) base
Now let's re-stage f2
and re-do the stash save
, but this time, with --keep-index
:
$ git add f2
$ git stash save --keep-index
Saved working directory and index state WIP on master: 84408ef base
HEAD is now at 84408ef base
Looks the same as before ... but not quite:
$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: f2
#
Use git log --graph --oneline --decorate --all
and you'll see basically the same thing (with different commit hashes) as before: stash
committed the index, then committed a merge-commit of the work tree. But this time it also re-extracted the index, so now you have "changes to be committed". This is just f2
, not f3
.
This means you can (somewhat pointlessly) git stash save
again. And you did! Let's use that log-graph-one-line-decorate thing (I use it a lot), both before and after:
$ git log --graph --oneline --decorate --all
* 7efe9a6 (refs/stash) WIP on master: 84408ef base
|\
| * 76c840e index on master: 84408ef base
|/
* 84408ef (HEAD, master) base
$ git stash save
Saved working directory and index state WIP on master: 84408ef base
HEAD is now at 84408ef base
$ git log --graph --oneline --decorate --all
$ git lola
* eb383e0 (refs/stash) WIP on master: 84408ef base
|\
| * aba15e6 index on master: 84408ef base
|/
* 84408ef (HEAD, master) base
Looks the same before-and-after, at first blush. But look closely at the SHA-1 IDs. They changed! Before the second git stash save
, refs/stash
named commit 7efe9a6
. Now it names eb383e0
!
The reflog (or, pay attention, it's getting complicated)
OK, it's not that bad, but now you have to learn about the "reflog". Where did the other stash
go? The answer is, it's been "pushed" and has disappeared into the reflog. There's a minor extra wrinkle, too: the "stash" is not a regular branch or tag. Instead, it's in refs/stash
. So here's one way to see it, using git log -g
, which means "look at reflogs":
$ git log -g --oneline refs/stash
eb383e0 refs/stash@{0}: WIP on master: 84408ef base
7efe9a6 refs/stash@{1}: WIP on master: 84408ef base
Aha, there they are, both 7efe9a6
and eb383e0
. They have "user form full names" (refs/stash@{1}
for instance) that are a bit of a pain to use. Fortunately stash
works (unless you name a branch stash
) to get the "top-most" {0}
one, and you can write stash@{1}
for the other. Or we can go for full-blown automation:
$ git log -g --pretty=format:%H refs/stash
This dumps out their full hashes, which we can use as arguments to git log --graph --decorate
, to get this:
$ git log --graph --oneline --decorate $(git log -g --pretty=format:%H refs/stash)
* eb383e0 (refs/stash) WIP on master: 84408ef base
|\
| * aba15e6 index on master: 84408ef base
|/
| * 7efe9a6 WIP on master: 84408ef base
| |\
|/ /
| * 76c840e index on master: 84408ef base
|/
* 84408ef (HEAD, master) base
That's all just to see what's still "in there", in the repo.
(Or, of course, you can use gitk
, as you did, to see them. gitk
is smart enough to look for the stash reflogs.)
(Aside: you can also use git reflog show refs/stash
. The reflog show
sub-command just runs git log -g --oneline
.)
Back to our problem, again
Now that we've done a first git stash save --keep-index
and then a pointless git stash save
, now what?
Well, we can git stash pop
to get the most recent (top-most of stack) stash back:
$ git stash pop
# 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: f2
#
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (eb383e050d150a8ce5b69a3662849ffdd7070c89)
What happened to f3
? As we noted earlier, the second git stash save
saved only the "kept index", i.e., just the changed f2
. What we need is to get back to the first stash.
$ git stash pop
error: Your local changes to the following files would be overwritten by merge:
f2
Please, commit your changes or stash them before you can merge.
Aborting
That's not much help, is it? :-)
If you're not sure what you're doing, now is a good time to make a "save stuff" branch (you can always delete it later). Just git checkout -b help-me-spock
or whatever, add, and commit. This stuff is now on a branch and easier to keep track of. But we know what we are doing, and that we have f2
in the other stash. So we can just wipe this out:
$ git reset --hard
Now we're back to the state we would have had, if we had done just one git stash save
, without --keep-index
: we're on master
, with the working directory clean, and a single stash saved. We can git stash list
it, git stash show
it, and so on. So now:
$ git stash pop --index
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: f2
#
# 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: f3
#
Dropped refs/stash@{0} (7efe9a65c44156921bbbcb6a3df4edc5cb44492b)
and we have everything back. (Or, without --index
, stash
will just apply all the changes to the working directory, rather than restoring index and work-dir.)
Using git stash apply
The nice thing about git stash pop
is that it applies and then drops the top-most stash entry. The annoying thing is, it applies and then drops the entry. If you use git stash apply
instead, it hangs on to it.
Among other things, that's quite handy if you misspell --index
as --keep-index
(I did more than once, while typing this), or leave it out and later decide it would have been nice to use it. You can git reset --hard
and re-do the apply
.
If you're done with a stash entry, git stash drop entry
will remove it from the reflog. For instance, suppose you do git stash apply --index 'stash@{1}'
and then decide it's all good and want to add
and/or commit
it and then forget about that stash. You can then git stash drop 'stash@{1}'
. The drawback is that this renumbers the rest: what was stash@{2}
becomes stash@{1}
, and so on. I find it's sometimes easier to keep them all around and use git stash clear
to get rid of all of them at once, at the end.
Wait a minute, what's with these --index
-es?
By default, git stash apply
and git stash pop
take the saved index ("changes staged for commit") and work-in-progress ("changes not staged for commit") and put them both into effect as work-in-progress only. Often that's fine, but if you've carefully staged some bits and left others unstaged, you might well want all that back. The --index
argument to apply
(and pop
) tries to do that. Sometimes it turns out to be "too hard". In that case, you have two options: leave out --index
, or use git stash branch
.
Using git stash branch
I mentioned above, in the section on amended commits vs stashes, that you can add a new branch label to a commit that has a stash on it, and then apply
or even pop
the corresponding stash, with --index
, and it will always work. The reason is simple: the stash is a merge commit of the index and WIP, corresponding to the commit they're on. If you check that commit out (as a "detached HEAD"), the index and WIP will apply cleanly.
So, suppose you add a new branch name at the commit in question, and get on the new branch (git checkout -b newname
). Now apply (and pop-off) the stash, using --index
: you're now in exactly the same state you were when you first ran git stash save
, except that the branch has a different name. And that's what git stash branch
does: you give it a new branch name and tell it which stash to use (the default is refs/stash
, A.K.A. stash@{0}
). It uses that stash entry to find the parent commit, attaches the branch name there, and then does a git stash pop --index
.
At this point you can use git status
, git diff --cached
, git diff
, etc., to see what's in the index and what's not, decide what else if anything to add, then git commit
to add new stuff to the new branch you've created.
这篇关于git存储修改上次提交(在gui中) - 弹出窗口没有任何结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!