如何在 `git stash -p` 中忽略添加的帅哥

How to ignore added hunks in `git stash -p`

想象一下这个场景:

# edit two files
git add -p // add hunks from one file

现在当你 运行 git stash -p 时,它会再次询问你是否要隐藏你刚刚通过 git add -p 选择的帅哥。有什么方法可以配置 git 默认情况下忽略这些已经添加的帅哥吗?大多数时候,我不想隐藏我已经添加的东西。

当谈到git stash push时(因为不带任何参数调用git stash等同于git stash push),考虑添加--keep-index选项。

这意味着所有已添加到索引的更改都保持不变。

因此 -p (patch) 选项不应查询那些(已缓存的)帅哥。

注意:--patch 选项意味着 --keep-index,因此(为了测试)请确保您使用的是最新的 Git 可用版本 (2.17) 并尝试 git stash push -p .

如果问题仍然存在,那么,如评论所述,首先进行提交将允许 stash -p 使用干净的索引进行操作。
git reset --soft @~ 将恢复提交的索引。

联机帮助页中有一个类似的示例:

man git 藏匿:

"Testing partial commits
You can use git stash save --keep-index when you want 
to make two or more commits out of the changes in the 
work tree, and you want to test each change before 
committing:

# ... hack hack hack ...
$ git add --patch foo            # add just first part to the index
$ git stash save --keep-index    # save all other changes to the stash"*

我可以确认:

如果你使用 git stash -p(这意味着 --keep-index),你仍然会被问及索引中已经存在的更改是否应该被隐藏(如你所描述的)。

因此,联机帮助页似乎令人困惑,其他地方也提到了这一点:https://github.com/progit/progit2/issues/822

总结一下:

--keep-index(或 -p 暗示 --keep-index)只是保持索引完好无损。已经上演的更改仍然会插入到存储中。 AFAIK,没有办法按照您的描述进行操作。

或者,更准确地说(再次来自联机帮助页):

With --patch, you can interactively select hunks from 
the diff between HEAD and the working tree to be stashed. 

The stash entry is constructed such that its index state 
is the same as the index state of your repository, and its 
worktree contains only the changes you selected interactively.

备选方案:

至少有 3 种方法可以实现您想要的(或多或少):

  • 不要将 -p 与 git 存储一起使用。把所有东西都藏起来(用 --keep-index 和可能的 --all,以确保你已经安全地把所有东西都藏起来了)。
  • 在存储之前提交您的暂存更改。这样你就不会在 HEAD 和工作树之间有差异,因为你想从存储中忽略这些更改。但是,如果您还不确定要提交这个怎么办?您以后可以随时进行更改并使用 --amend 更改现有提交。
  • 取消暂存您的更改(从索引中删除)然后隐藏。

好的,根据评论,我们需要的是 所有 尚未添加的更改(无论是在 git add -p 期间排除,还是只是未添加)还没有)。

这样做的原因是在提交之前对暂存状态应用一些 tests/potential 调整。

这是一个直接的 git stash -k,像往常一样存储所有内容,但保留索引状态和相应的工作树,即从工作树中清除我不打算提交的所有内容。

所以:

git stash -k
git clang-format
git commit

并且存储库现在有四个有趣的快照:原始内容又名存储库、快照索引、快照工作树和当前索引(、提交和工作树),即 [=20= 处的索引快照] 应用清理。请注意,此处提交的所有三个新快照都将存储库作为父级。

现在你想要你的工作树改变回来,但显然从基础到隐藏索引和工作树的变化与当前索引和工作树中的变化不匹配(和新提交,那些都匹配),所以当git 去 pop stash 它会发现冲突:从 stashed base 到 stashed index 的变化与从 stash base 到当前索引的变化不匹配。

如果 Git 直接提供你想要的东西,"stash all the worktree changes except the ones in the index",你可以使用它,然后 stash pop 不会有任何问题,直接 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 state 的变化与从 stash base(通常与当前 base 非常相似)到当前状态的变化的合并。您希望隐藏的工作树更改回到您的工作树中,但只有您尚未添加的那些,因为您 已经 添加的那些都还在这里,它们只是有点不同现在。

所以 cherry-pick 是 -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 进行了 2a 更改,而 4a 更改仅在您的工作树中。

 $ git stash -k
 $ cat file
 1
 2a
 3
 4
 5
 $ sed -i '2s,^,_,' file   # indent the 2a line
 $ git commit -am2

现在,初始提交 :/11 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
)

这就是我真正实现它的方式。