【问题标题】:GIT remove changes of specific commit from current HEADGIT 从当前 HEAD 中删除特定提交的更改
【发布时间】:2018-07-04 21:16:43
【问题描述】:

假设

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

git cherry-pick -n <commit>

如何从索引中删除精选更改?我可以做一个

git reset HEAD

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


目的

如果有人进行了 stash,则无法将 stash 推送到遥控器。当前的 WIP 无法从另一个系统上的远程提取以使用。所以我编写了 shell 函数来模拟 git-stash,除了我为每个 stash 使用分支。

applypop 通常会将隐藏的更改应用于 WIP,但不会应用于当前索引。当我使用樱桃挑选来应用存储分支中的更改时,所有这些更改都将添加到索引中,之后我需要将它们从索引中删除。


编辑 (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 中缺少的错误处理。

【问题讨论】:

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


    【解决方案1】:

    眼前的问题

    在这种特殊情况下,您可以尝试git revert -n &lt;commit&gt;,这与反向应用这些更改基本相同。不过,一般来说,这不是一个可逆的操作,而且一开始就执行git cherry-pick -n 是不明智的。

    例如,考虑一下,如果 delta 通过以下方式计算会发生什么:

    git diff $commit^ $commit
    

    git cherry-pick $commit 应该在README 中添加一行,从f1.txt 中删除一行,并在f2.txt 中更改一行(“更改”实际上是由remove-old-add-new 暗示的) .

    但如果您已经将该行添加到README 并将更改更改为f2.txt,则实际运行cherry-pick 只会修改f1.txt。 (发生这种情况是因为 Git 使用它的合并机制,它会发现您的更改及其更改重叠,从而减少重叠。)如果您现在决定取消执行樱桃挑选并运行 git revert -n $commit,Git 将撤消通过从README 中删除该行,将该行添加回f1.txt,并在f2.txt 中恢复原始行。 Git 不会知道合并操作将这三个更改中的两个删除为“已经到位”,并将撤消所有三个。

    更普遍的问题

    如果有人进行了 stash,则无法将 stash 推送到遥控器。

    这并不完全正确(但也不完全错误)。 git stash 所做的是进行两次或有时三次提交,其中没有一个在分支上:一个存储当前索引,一个存储当前工作树(但仅适用于当前索引中的文件)。如果第三次提交存在,它存储未跟踪文件减去忽略文件,或未跟踪文件包括忽略文件。

    提交的安排使得工作树提交是最后一个提交,并且其他两个(加上当前提交)作为其父级。然后使用 git update-ref 将最终提交的哈希 ID “推送到”存储堆栈。

    因为这些是提交,所以它们可以是git push-ed。但是,您必须在遥控器上为它们命名,遥控器将允许您设置。 refs/stash 名称通常不可写。比如:

    git push fred stash:refs/heads/sneaky
    

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

    可以如上所述以另一个名称发送提交,然后(通过登录到另一个系统)将它们偷运到 refs/stash 名称中,如果你想这样做的话。不过,您甚至不必这样做,因为 git stash apply 等将采用任何解析为“类似存储”的提交的标识符(特别是工作树提交,如果存储是如果存储是三提交实体,则为两提交实体或三父实体):

    fred$ git stash apply sneaky
    

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

    fred$ git branch -D sneaky
    

    其他技术说明

    applypop 通常会将隐藏的更改应用于 WIP,但不会应用于当前索引。

    这也是正确的,除非您使用--index 选项。在这种情况下,存储代码尝试使用 git show &lt;index-commit-hash&gt; | git apply --index 恢复索引。

    应用这样的存储会调用内部 Git 合并机制,但通常使用 git apply -3 可以获得非常相似的效果。 (请注意,这与实际的cherry-pick 或merge 或stash 调用之间存在细微差别。)

    【讨论】:

    • 对 gits 处理交叉更改的非常好的技术见解。我没有想到这一点。我将尝试深入了解最后两个技术说明以完全理解它们。
    猜你喜欢
    • 2015-12-01
    • 1970-01-01
    • 2015-02-23
    • 2021-02-24
    • 2015-12-22
    • 1970-01-01
    • 2012-03-23
    • 2021-10-21
    • 2021-12-24
    相关资源
    最近更新 更多