GIT 从当前 HEAD 中删除特定提交的更改
GIT remove changes of specific commit from current HEAD
假设
我在索引中添加或未添加最近的更改。现在我正在挑选一个特定的提交,而不是在我的 HEAD 上创建一个新的提交 ...
git cherry-pick -n <commit>
如何从索引中删除精选更改?我可以做一个
git reset HEAD
但我必须重做之前添加的所有更改。
目的
如果一个人做了一个藏匿藏藏不能被推送到远程。当前的 WIP 无法从另一个系统上的远程系统中提取以供使用。所以我写了 shell 函数来模拟 git-stash 除了我为每个 stash 使用分支。
apply
或 pop
通常会将隐藏的更改应用到 WIP 而不是当前索引。当我使用 cherry-pick 应用来自 stash 分支的更改时,所有这些更改都将添加到索引中,之后我需要将它们从索引中删除。
编辑 (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 .
[ ] && git commit -m "WIP: " || 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 revert -n <commit>
,这与反向应用这些更改基本相同。不过,一般来说,这不是一个可逆操作,一开始就执行 git cherry-pick -n
是不明智的。
例如,考虑一下如果通过以下方式计算的增量会发生什么:
git diff $commit^ $commit
说 git cherry-pick $commit
应该在 README
中添加一行,从 f1.txt
中删除一行,并在 f2.txt
中更改一行("change"由删除旧添加新暗示,真的)。
但是如果您已经将该行添加到README
并将该更改更改为f2.txt
,实际上运行宁樱桃-pick 只会修改 f1.txt
。 (发生这种情况是因为 Git 使用其合并机制,它将发现您的更改和它们的更改重叠,从而减少重叠。)如果您现在决定取消 cherry-pick 和 运行 git revert -n $commit
、Git 将通过删除 README
中的行、将行添加回 f1.txt
并恢复 f2.txt
中的原始行来撤消它。 Git 不会 知道合并操作删除了这三个更改中的两个 "already in place",并将撤消所有三个。
更普遍的问题
If one does a stash the stash cannot be pushed to the remote.
这不完全正确(但也不完全错误)。 git stash
所做的是进行两次或有时三次提交,其中 none 在一个分支上:一个存储当前索引,一个存储当前工作树(但仅适用于那些在当前索引中)。如果第三次提交存在,它存储未跟踪的文件减去忽略的文件,或未跟踪的文件包括忽略的文件。
提交的安排使得工作树提交是最后一个提交,并且将另外两个(加上当前提交)作为其父项。然后最终提交的哈希 ID 是 "pushed onto" 使用 git update-ref
.
的存储堆栈
因为这些是提交,所以可以 git push
-ed。但是,您必须在遥控器上为它们起一个名称,遥控器将允许您设置该名称。 refs/stash
名称一般不可写。例如:
git push fred stash:refs/heads/sneaky
将使用 refs/stash
在远程 fred
上创建分支名称 sneaky
。
可以像上面那样用另一个名字发送提交,然后——通过登录到另一个系统——如果你想这样做的话,将它们偷偷带入 refs/stash
名字。不过,您甚至不必这样做,因为 git stash apply
等将采用任何解析为 "stash-like" 提交的标识符(特别是工作树提交,如果stash 是一个两次提交的实体,如果 stash 是一个三次提交的实体,则为三个父实体):
fred$ git stash apply sneaky
如果一切顺利,你不想再这样了,你可以强行删除分支:
fred$ git branch -D sneaky
另一篇技术说明
An apply
or pop
normally would apply the stashed changes to the WIP but not the current index.
这也是正确的,除非您使用 --index
选项。在这种情况下,隐藏代码尝试使用 git show <index-commit-hash> | git apply --index
.
恢复索引
应用这样的存储会调用内部 Git 合并机制,尽管通常您可以通过使用 git apply -3
获得非常相似的效果。 (请注意,这与实际的 cherry-pick 或 merge 或 stash 调用之间存在细微差别。)
假设
我在索引中添加或未添加最近的更改。现在我正在挑选一个特定的提交,而不是在我的 HEAD 上创建一个新的提交 ...
git cherry-pick -n <commit>
如何从索引中删除精选更改?我可以做一个
git reset HEAD
但我必须重做之前添加的所有更改。
目的
如果一个人做了一个藏匿藏藏不能被推送到远程。当前的 WIP 无法从另一个系统上的远程系统中提取以供使用。所以我写了 shell 函数来模拟 git-stash 除了我为每个 stash 使用分支。
apply
或 pop
通常会将隐藏的更改应用到 WIP 而不是当前索引。当我使用 cherry-pick 应用来自 stash 分支的更改时,所有这些更改都将添加到索引中,之后我需要将它们从索引中删除。
编辑 (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 .
[ ] && git commit -m "WIP: " || 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 revert -n <commit>
,这与反向应用这些更改基本相同。不过,一般来说,这不是一个可逆操作,一开始就执行 git cherry-pick -n
是不明智的。
例如,考虑一下如果通过以下方式计算的增量会发生什么:
git diff $commit^ $commit
说 git cherry-pick $commit
应该在 README
中添加一行,从 f1.txt
中删除一行,并在 f2.txt
中更改一行("change"由删除旧添加新暗示,真的)。
但是如果您已经将该行添加到README
并将该更改更改为f2.txt
,实际上运行宁樱桃-pick 只会修改 f1.txt
。 (发生这种情况是因为 Git 使用其合并机制,它将发现您的更改和它们的更改重叠,从而减少重叠。)如果您现在决定取消 cherry-pick 和 运行 git revert -n $commit
、Git 将通过删除 README
中的行、将行添加回 f1.txt
并恢复 f2.txt
中的原始行来撤消它。 Git 不会 知道合并操作删除了这三个更改中的两个 "already in place",并将撤消所有三个。
更普遍的问题
If one does a stash the stash cannot be pushed to the remote.
这不完全正确(但也不完全错误)。 git stash
所做的是进行两次或有时三次提交,其中 none 在一个分支上:一个存储当前索引,一个存储当前工作树(但仅适用于那些在当前索引中)。如果第三次提交存在,它存储未跟踪的文件减去忽略的文件,或未跟踪的文件包括忽略的文件。
提交的安排使得工作树提交是最后一个提交,并且将另外两个(加上当前提交)作为其父项。然后最终提交的哈希 ID 是 "pushed onto" 使用 git update-ref
.
因为这些是提交,所以可以 git push
-ed。但是,您必须在遥控器上为它们起一个名称,遥控器将允许您设置该名称。 refs/stash
名称一般不可写。例如:
git push fred stash:refs/heads/sneaky
将使用 refs/stash
在远程 fred
上创建分支名称 sneaky
。
可以像上面那样用另一个名字发送提交,然后——通过登录到另一个系统——如果你想这样做的话,将它们偷偷带入 refs/stash
名字。不过,您甚至不必这样做,因为 git stash apply
等将采用任何解析为 "stash-like" 提交的标识符(特别是工作树提交,如果stash 是一个两次提交的实体,如果 stash 是一个三次提交的实体,则为三个父实体):
fred$ git stash apply sneaky
如果一切顺利,你不想再这样了,你可以强行删除分支:
fred$ git branch -D sneaky
另一篇技术说明
An
apply
orpop
normally would apply the stashed changes to the WIP but not the current index.
这也是正确的,除非您使用 --index
选项。在这种情况下,隐藏代码尝试使用 git show <index-commit-hash> | git apply --index
.
应用这样的存储会调用内部 Git 合并机制,尽管通常您可以通过使用 git apply -3
获得非常相似的效果。 (请注意,这与实际的 cherry-pick 或 merge 或 stash 调用之间存在细微差别。)