【问题标题】:Can I delete a git commit but keep the changes?我可以删除 git commit 但保留更改吗?
【发布时间】:2013-03-24 05:18:11
【问题描述】:

在我的一个开发分支中,我对我的代码库进行了一些更改。在我能够完成我正在处理的功能之前,我必须将我当前的分支切换到 master 来演示一些功能。但是仅使用“git checkout master”就保留了我在开发分支中所做的更改,从而破坏了 master 中的一些功能。所以我所做的就是在我的开发分支上提交更改,并使用提交消息“临时提交”,然后为演示签出 master。

现在我已经完成了演示并回到我的开发分支上工作,我想删除我所做的“临时提交”,同时仍然保留我所做的更改。这可能吗?

【问题讨论】:

  • 下次:git stash
  • @MattBall,不一定。虽然 git stash 是一个很好的工具,但“正在进行的工作”一次性提交也是一个相当合法的设备。
  • 这是来自 Github 的绝佳资源:How to undo (almost) anything with Git
  • @MattBall @kostix 是的,stash 特别不适合“长期”存储,因为它是整个 repo 的一个全局堆栈。我希望能够将更改存储在一个分支上,然后转到其他分支,做任何其他事情,几天后回来,而不必担心我可能在过渡期间在其他分支上使用了git stash
  • stash 值得注意的一点是它完全是本地的,并且很容易因 repo 删除或重新创建或硬件故障或丢失而导致代码丢失。 IMO,它真的应该只用于非常短期的 WIP。在去度假之前喜欢 WIP 提交 :P .. 称之为大脑转储提交!

标签: git undo git-reset


【解决方案1】:

就这么简单:

git reset HEAD^

注意:某些 shell 将 ^ 视为特殊字符(例如某些 Windows shell 或 ZSH with globbing enabled),因此在这些情况下您可能必须引用 "HEAD^" 或使用 HEAD~1

git reset 没有--hard--soft 将您的HEAD 移动到指定的提交,而不更改任何文件。 HEAD^ 指的是您当前提交的(第一个)父提交,在您的情况下是临时提交之前的提交。

请注意,另一种选择是照常进行,然后在下一个提交点运行:

git commit --amend [-m … etc]

这将改为编辑最近的提交,具有与上述相同的效果。

请注意,如果您已经将错误的提交推送到其他人可能已将其拉出的地方,这(几乎与每个 git 答案一样)可能会导致问题。尽量避免这种情况

【讨论】:

  • 这是迄今为止最简单的,但如果你已经将你的提交推送到一个遥控器并且其他人拉了它,我会非常犹豫,除了道歉。
  • 我在这样做后得到More?。无论我在那个提示符下输入什么,都会给我fatal: ambiguous argument 'HEADwhateverItypedIn': unknown revision or path not in the working tree.
  • @DaAwesomeP 听起来您正在使用将 ^ 视为特殊字符的外壳。您可以引用引用 "HEAD^",或使用替代语法 HEAD~1 unquoted
  • 为我工作,尽管git reset HEAD\^ 必须转义字符
  • @kostix FWIW 该链接已更改为 git-scm.com/book/en/v2/Git-Tools-Reset-Demystified
【解决方案2】:

有两种处理方法。哪个更容易取决于您的情况

重置

如果您要删除的提交是最后一次提交,并且您还没有做任何额外的工作,您可以简单地使用git-reset

git reset HEAD^

将您的分支返回到当前 HEAD 之前的提交。但是,它实际上并没有更改工作树中的文件。结果,该提交中的更改显示为已修改-就像“取消提交”命令。事实上,我有一个别名可以做到这一点。

git config --global alias.uncommit 'reset HEAD^'

那么你以后可以只用git uncommit来备份一个提交。

挤压

压缩提交意味着将两个或多个提交合并为一个。我经常这样做。在您的情况下,您提交了一半完成的功能,然后您将完成它并使用正确的永久提交消息再次提交。

git rebase -i <ref>

我之所以这么说是因为我想明确表示这可以是任意数量的提交。运行git log 并找到您要删除的提交,复制其SHA1 并使用它来代替&lt;ref&gt;。 Git 将带您进入交互式变基模式。它将显示您当前状态与您代替&lt;ref&gt; 的任何内容之间的所有提交。因此,如果 &lt;ref&gt; 是 10 次提交前,它将显示所有 10 次提交。

在每次提交之前,都会有pick这个词。找到您要删除的提交并将其从pick 更改为fixupsquash。使用fixup 只需丢弃该提交消息并将更改合并到列表中的直接前任中。 squash 关键字做同样的事情,但允许您编辑新组合提交的提交消息。

请注意,当您退出编辑器时,提交将按照它们在列表中显示的顺序重新提交。因此,如果您进行了临时提交,然后在同一分支上进行了其他工作,并在以后的提交中完成了该功能,那么使用 rebase 将允许您重新排序提交并压缩它们。

警告:

变基会修改历史记录 - 不要对您已与其他开发人员共享的任何提交执行此操作。

暂存

未来,为避免此问题,请考虑使用git stash 临时存储未提交的工作。

git stash save 'some message'

这会将您当前的更改存储到存储列表的一侧。以上是最明确的 stash 命令版本,允许使用注释来描述您要存储的内容。您也可以简单地运行git stash 而不执行其他任何操作,但不会存储任何消息。

您可以使用...浏览您的存储列表

git stash list

这将显示你所有的存储,它们在哪些分支上完成,以及每行开头的消息和该存储的标识符,看起来像这样 stash@{#} 其中 # 是它在数组中的位置藏匿处。

要恢复存储(可以在任何分支上完成,无论最初创建存储的位置如何),您只需运行...

git stash apply stash@{#}

同样,# 是存储数组中的位置。如果您要恢复的存储在0 位置 - 也就是说,如果它是最近的存储。然后你可以直接运行命令而不指定存储位置,git 会假设你的意思是最后一个:git stash apply

因此,例如,如果我发现自己在错误的分支上工作 - 我可能会运行以下命令序列。

git stash
git checkout <correct_branch>
git stash apply

在你的情况下,你在分支上移动了一点,但同样的想法仍然适用。

希望这会有所帮助。

【讨论】:

  • git config --global alias.uncommit reset HEAD^ 只是取消提交重置的别名。相反,请git config --global alias.uncommit 'reset HEAD^'
  • 请注意,如果在 Windows 命令提示符中使用,则必须使用 ^^ 而不是 ^。
【解决方案3】:

我想你正在寻找这个

git reset --soft HEAD~1

它撤消最近的提交,同时将该提交中所做的更改保持在暂存状态。

【讨论】:

  • 谢谢。这对我有用。在 Windows 上调用 git reset HEAD^ 只会提示“更多?” - 不管是什么意思
  • @Tyron ^ 是 DOS 中的转义字符。当与新行配对时,它用作前面命令的继续提示。键入 git reset HEAD^^ 应该可以在 Windows 上工作。
【解决方案4】:

是的,您可以在不删除更改的情况下删除您的提交:

git reset @~

【讨论】:

  • 有趣,简洁的语法,我没见过也没用过。这与git reset --softgit reset --keep 有何不同?
  • 它对我不起作用: git reset @~ commit_hash 提交保持原样并且永远不会被删除
【解决方案5】:

您正在寻找git reset HEAD^ --softgit reset HEAD^ --mixed

docs 中所述的重置命令有 3 种模式:

git reset HEAD^ --soft

撤消git commit。工作树(项目文件夹)+索引(--cached)中仍然存在更改

git reset HEAD^ --mixed

撤消git commit + git add。工作树中仍然存在更改

git reset HEAD^ --hard

就像您从未对代码库进行过这些更改一样。更改已从工作树中消失。

【讨论】:

  • 所有主要细节的最佳答案。
【解决方案6】:

2020 简单方法:

git reset <commit_hash>

(您要保留的最后一次提交的提交哈希)。

如果提交被推送,你可以这样做:

git push -f

您将在本地保留现在未提交的更改

【讨论】:

  • 当您说这是“2020 简单方法”时,是否暗示此方法在以前的版本中是不可能的?如果是,需要哪个版本的 Git 才能使用此方法?
【解决方案7】:

就我而言,我已经推送到了仓库。哎哟!

您可以通过以下方式恢复特定提交,同时将更改保留在本地文件中:

git revert -n <sha>

通过这种方式,我能够保留我需要的更改并撤消已经推送的提交。

【讨论】:

  • 就我而言,我需要恢复已推送到远程存储库的内容。更哎哟!最好的方法是git revert bad-commit-sha,然后git revert -n revert-commit-just-created-sha,然后从那里修复。你让我半途而废。谢谢!
  • 这似乎适得其反,不是吗?它会创建新的更改,这些更改对应于所选提交中所做更改的还原。如果您要提交这些新更改,您将撤消您实际上想要保留的工作。
  • 因此使用了-n 选项,它将还原将执行的更改暂存在但未提交。然后,您可以选择保留或丢弃哪些还原更改
【解决方案8】:

对于那些使用 zsh 的人,您必须使用以下内容:

git reset --soft HEAD\^

在此解释:https://github.com/robbyrussell/oh-my-zsh/issues/449

万一网址失效,重要的部分是:

在你的命令中转义 ^

您也可以使用 HEAD~ 这样您就不必每次都转义它。

【讨论】:

  • 我永远记不住这个命令,不得不谷歌搜索并找到我自己的答案哈哈哈
  • git reset HEAD^ 为我在 zsh 中工作,可能已修复。
  • @BenKolyaMansley 你运行的是什么版本的 zsh?
  • 我正在使用zsh 5.3 (x86_64-apple-darwin18.0)
  • 大家好,我又来了,我已经看了很多次了,最后把它扔到了我的.zshrc,很多人可能会从中受益。示例:github.com/GregHilston/toolbox/blob/master/dot/zshrc#L147
【解决方案9】:

使用 git 2.9(准确地说是 2.9.2.windows.1) git reset HEAD^ 提示更多;不确定这里的预期输入。请参考下面的截图

找到了其他解决方案git reset HEAD~#numberOfCommits,我们可以使用它来选择要重置的本地提交数量,方法是保持更改不变。因此,我们有机会丢弃所有本地提交以及有限数量的本地提交。

请参阅下面显示 git reset HEAD~1 的屏幕截图:

【讨论】:

  • 您可能需要转义 ^ 字符 - 尝试 git reset "HEAD^" 或 git reset HEAD\^
  • 除此之外,git reset HEAD^^ 的工作方式是单个 ^ 被视为新行。
【解决方案10】:

另一种方法。

在临时提交的顶部添加提交,然后执行:

git rebase -i

将两个提交合并为一个(命令将打开带有明确说明的文本文件,编辑它)。

【讨论】:

  • 技术上是正确的,但远不如简单地做git reset HEAD^那么优雅。 Git rebase 在这里有很大的错误空间。
【解决方案11】:

在某些情况下,我只想在第一次提交时撤消对特定文件的更改,以将它们添加到第二次提交并获得更清晰的 git 日志。

在这种情况下,我要做的是:

git checkout HEAD~1 <path_to_file_to_put_in_different_commit>
git add -u
git commit --amend --no-edit
git checkout HEAD@{1} <path_to_file_to_put_in_different_commit>
git commit -m "This is the new commit"

当然,即使在rebase -i 的中间,这也很有效,在提交拆分时有一个编辑选项。

【讨论】:

    猜你喜欢
    • 2018-12-07
    • 2021-09-17
    • 1970-01-01
    • 2014-06-08
    • 2016-08-10
    • 2016-09-03
    • 2011-03-20
    • 2013-05-25
    相关资源
    最近更新 更多