好的,cmets 需要的是存储尚未添加的所有更改(无论是在git add -p 期间排除,还是尚未添加)。
这样做的原因是在提交之前对暂存状态应用一些测试/可能的调整。
这是一个直接的git stash -k,像往常一样存储所有内容,但保留索引状态和相应的工作树,即从工作树中清除我不打算提交的所有内容。
所以:
git stash -k
git clang-format
git commit
并且存储库现在有四个有趣的快照:原始内容(即存储库)、快照索引、快照工作树和当前索引(、提交和工作树),它是 stash^2 的索引快照和清理应用。请注意,所有三个新快照(即此处的提交)都将存储库作为父级。
现在您希望您的工作树更改回来,但显然从基础到隐藏索引和工作树的更改与当前索引和工作树中的更改不匹配(以及新提交,这些都匹配),所以当 git 去弹出 stash 会发现冲突:从 stashed base 到 stashed index 的更改与从 stash base 到当前索引的更改不匹配。
如果 Git 直接提供了你想要的东西,“存储所有工作树更改除了索引中的那些”,你可以使用它,然后存储弹出不会有任何问题,一个直的git stash pop 会这样做。幸运的是,如果 Git 擅长任何事情,它就是合并、组合、拆分以及全面的差异化处理。
git cherry-pick -nm2 stash
# cherry-pick updated the index, too. maybe git reset here to unstage that stuff.
git stash drop
Stash pop 是从 stash base 到 stashed 状态的更改与从 stash base(通常与当前 base 非常相似)到当前状态的更改的合并。您希望将隐藏的工作树更改回您的工作树中,但只有您尚未添加的那些,因为您已添加的那些都还在这里,它们现在只是有点不同。
所以樱桃选择是-n,没有提交,-m2,用于更改的主线是它的第二个父级,即你在存储时所做但未添加的所有差异。
一个例子可能会有所帮助,
cd `mktemp -d`
git init
printf >file %s\\n 1 2 3 4 5
git add .;git commit -m1
printf >file %s\\n 1 2a 3 4 5
git add .
printf >file %s\\n 1 2a 3 4a 5
现在您已经有效地git add -p'd 2a 更改,而 4a 更改仅在您的工作树中。
$ git stash -k
$ cat file
1
2a
3
4
5
$ sed -i '2s,^,_,' file # indent the 2a line
$ git commit -am2
现在,初始提交 :/1 是 1 2 3 4 5,你当前的提交、索引和工作树都是 1 _2a 3 4 5,你的隐藏索引是 1 2a 3 4 5,你隐藏的工作树是 1 2a 3 4a 5。
您想要返回的更改是您的隐藏索引和隐藏的工作树之间的差异,这是隐藏提交与其第二个父项的差异。因此,那个樱桃采摘。
cherry-pick 的其他拼写方式包括
git cherry-pick -nm1 -Xours stash
它应用所有隐藏的工作树更改,但在发生冲突时采用本地版本(基本上它是发现并丢弃冲突的差异,而不是像 -m2 那样避免它们)和
git diff stash^2..stash|git apply -3
让自己更轻松的是脚本领域,谈论设置它的最简单方法是作为 git 别名,
git config --global alias.poptree '!git cherry-pick -nm2 stash; git reset; git stash pop'
你现在有一个git poptree 命令来做你想做的事。
编辑:作为进一步的补充,假设您在记住隐藏的工作树更改之前已经继续完成了一些工作,cherry-pick 将正确更新工作树和索引,但重置将取消您已经添加的任何更改到新索引。现在你处于核心指挥领域,
( index=`git write-tree` &&
git cherry-pick -nm2 stash &&
git read-tree $index &&
git stash drop
)
我是如何真正实现它的。