GIT从当前HEAD中删除特定提交的更改

时间:2018-01-25 15:05:46

标签: git git-stash git-reset git-cherry-pick git-rm

假设

我最近添加了或未添加到索引中的更改。现在,我正在挑选一个特定的提交而不在我的HEAD上创建新的提交......

git cherry-pick -n <commit>

如何从索引中删除cherry-pick更改?我可以做一个

git reset HEAD

但我必须重做之前添加的所有更改。

目的

如果有人藏匿藏匿物,则无法将藏匿物推送到遥控器。无法从另一个系统上的遥控器上拉出当前的WIP。所以我编写了shell函数来模拟git-stash,除了我为每个存储使用分支。

applypop通常会将隐藏的更改应用于WIP,但不会应用当前索引。虽然我使用cherry-pick来应用来自隐藏分支的更改,但所有这些更改都会添加到索引中,然后我需要将它们从索引中删除。

修改(2018-01-29)

我阅读了@torek的回答并理解它。不过我喜欢分享我以前用过的bash函数。

function git-stash {
    local gitbranch="$( git branch | grep \* )"
    local currentbranch="$( [ "${gitbranch}" == "* (HEAD"* ] && echo "${gitbranch}" | cut -d ' ' -f5 | cut -d ')' -f1 || echo "${gitbranch}" | cut -d ' ' -f2- )"
    local stashname="stash/$( date +%s )"
    git stash save -u ${stashname}
    git checkout -b ${stashname}
    git stash pop
    git add .
    [ ${1} ] && git commit -m "WIP: "$1 || git commit -m "WIP"
    git checkout ${currentbranch}
}

function git-stash-apply {
    local stashbranches="$( git branch | grep stash/ | cut -d ' ' -f3- | sort -r )"
    local stashbranches=(${stashbranches[@]})
    local lateststashbranch="${stashbranches[0]}"
    git cherry-pick -n "${lateststashbranch}"
}

function git-stash-pop {
    local stashbranches="$( git branch | grep stash/ | cut -d ' ' -f3- | sort -r )"
    local stashbranches=(${stashbranches[@]})
    local lateststashbranch="${stashbranches[0]}"
    git cherry-pick -n "${lateststashbranch}"
    git branch -D "${lateststashbranch}"
    git push origin :"${lateststashbranch}"
}

这还不是一个合适的解决方案,更不用说stash pop中缺少错误处理了。

1 个答案:

答案 0 :(得分:1)

当前问题

在这种特殊情况下,您可以尝试git revert -n <commit>,这与反向应用这些更改基本相同。但一般来说,这不是一个可逆的操作,首先完成git cherry-pick -n是不明智的。

例如,考虑如果delta计算的结果:

git diff $commit^ $commit

表示git cherry-pick $commit应向README添加一行,从f1.txt删除一行,然后更改f2.txt中的一行(&#34;更改&#34;被remove-old-add-new暗示,真的。)

但是,如果你已经将该行添加到README并将其更改为f2.txt,那么实际运行cherry-pick只会修改{{1} }}。 (这是因为Git使用其合并机制,它将发现您的更改及其更改重叠,从而减少重叠。)如果您现在决定取消选择并运行f1.txt,Git将从git revert -n $commit删除该行,将该行添加回README,然后在f1.txt中恢复原始行,撤消该行。 Git 赢了知道合并操作已经删除了这三个更改中的两个,因为&#34;已经到位&#34;,并将撤消所有这三个。

更普遍的问题

  

如果有藏匿者,则无法将藏匿物推送到遥控器。

这不太正确(但也不是很假)。 f2.txt做的是提交两个或三个提交,其中没有一个提交在一个分支上:一个存储当前索引,一个存储当前工作树(但仅用于当前索引中的文件) )。如果存在第三个提交,它会存储未删除文件减去忽略文件或未跟踪文件(包括忽略文件)。

安排提交,以便工作树提交是最后一个提交,并且另外两个(加上当前提交)作为其父项。然后,最终提交的哈希ID被&#34;推送到&#34;使用git stash的存储堆栈。

因为这些是提交,所以可以git update-ref - 编辑。但是,您必须在遥控器上为它们创建一个名称,遥控器将允许您设置该名称。 git push名称通常不可写。例如:

refs/stash

将使用git push fred stash:refs/heads/sneaky 在远程sneaky上创建分支名称fred

可以使用上面的其他名称发送提交,然后登录到另一个系统 - 如果你想这样做,就将它们偷偷带进refs/stash名称。但是,您甚至不必这样做,因为refs/stash等会将任何标识符解析为&#34; stash-like&#34; commit(特别是工作树提交,如果存储是两个提交实体,则有两个父项,如果存储是三个提交实体,则有三个父项):

git stash apply

如果一切顺利且你不再想要这个,那么你可以强行删除分支:

fred$ git stash apply sneaky

另一个技术说明

  

fred$ git branch -D sneaky apply通常会将隐藏的更改应用于WIP,但不会应用当前索引。

除非您使用pop选项,否则也是如此。在这种情况下,存储代码尝试使用--index恢复索引。

应用这样的存储会调用内部Git合并机制,但通常使用git show <index-commit-hash> | git apply --index可以获得非常相似的效果。 (请注意,这与实际的挑选或合并或存储调用之间存在细微差别。)