如何将多个(已经推送)的Git提交还原到已发布的存储库中?


77

git的新手,已经搞砸了。

我已经提交了一些更改并将其推送到远程开发机器。我需要恢复较旧的版本,但到目前为止,要保持“不好的进展”,以便继续在单独的分支上工作。

我正在考虑这样做:

  1. 创建一个名为“ tested-thing”的本地分支
  2. 将本地存储库恢复到其工作状态(希望有意义的提交会有所帮助)
  3. 推送到远程

  4. 完成对被测物的测试

  5. “经过测试的东西”合并到开发人员中
  6. 推送到远程

在第3步和第5步之间,其他开发人员可能会提交并推送,恐怕这可能会导致“合并悲剧”-无论如何,这是否是正确的选择?

更新:

这里的主要问题在于2)

在这里,有关主题:“将工作分解为主题分支” http://learn.github.com/p/undoing.html

他们建议:

  1. $ git分支测试
  2. $ git reset --hard a6b4c974

这样,其他开发人员仍然可以:

$ git commit (在dev分支上)

我可以结帐以进行测试和解决,直到合并时间为止。

尽管您有所有选择,但这还是一个不错的选择。但是,没有说明是否可以在我们按下后完成?

请注意以下几点:由于我做了这些更改并且弄乱了所有内容,因此到目前为止,还没有其他人在存储库上工作过。因此,如果我还原工作目录,没有人会注意到。


作为注释,您不能还原合并提交,请检查此有用的答案stackoverflow.com/a/1470452/6891549和git checkout -f A-。解决方案
Rodrigo

Answers:


147

问题

您可以使用许多工作流程。重点是不要破坏已发布分支的历史记录,除非您已与可能消耗该分支并愿意对每个克隆进行手术的每个人进行过沟通。如果可以避免的话,最好不要这样做。

已发布分支机构的解决方案

您概述的步骤很有用。如果您需要dev分支立即保持稳定,请执行此操作。您有许多使用Git进行调试的工具,这些工具将帮助您找到正确的分支点,然后您可以还原最后一次稳定提交和HEAD之间的所有提交。

一次以相反的顺序还原一次提交,或使用 <first_bad_commit>..<last_bad_commit>范围。散列是指定提交范围的最简单方法,但是还有其他符号。例如,如果您推送了5个错误的提交,则可以使用以下命令还原它们:

# Revert a series using ancestor notation.
git revert --no-edit dev~5..dev

# Revert a series using commit hashes.
git revert --no-edit ffffffff..12345678

这将按顺序将反向修补程序应用到您的工作目录,向后执行已知的良好提交。随着--no-edit标志,在应用每个反向修补程序后,将自动提交对工作目录的更改。

看到 man 1 git-revert以获取更多选项以及man 7 gitrevisions指定要还原的提交的不同方法。

另外,您可以分支HEAD,以所需的方式进行修复,然后重新合并。同时,您的构建将被破坏,但这在某些情况下可能是有道理的。

危险地带

当然,如果您绝对确定自从您的错误推送以来没有人从存储库中撤出任何内容,并且如果远程服务器是裸存储库,那么您可以执行非快速提交。

git reset --hard <last_good_commit>
git push --force

这将使reflog在您的系统和上游主机上保持不变,但是您的错误提交将从可直接访问的历史记录中消失,并且不会在pull上传播。您的旧更改将一直存在,直到修剪存储库为止,但是只有Git忍者才能看到或恢复您错误地做出的提交。


如果人们已经克隆了错误的推送并收到通知,那么他们应该怎么做才能与远程服务器拥有相同的分支?他们也必须这样做git reset --hard <last_good_commit>吗?
andrybak

5
如果您使用的是提交标签,则不是<first_bad_commit>..<last_bad_commit>而是<last_good_commit>..<last_bad_commit>
Yerke

32

如果您已经将内容推送到远程服务器(并且您有其他开发人员在同一个远程分支中工作),则要牢记的一点是您不想重写历史记录

不要使用git reset --hard

您需要还原更改,否则任何在其历史记录中具有已删除提交的签出都将在下次推送时将它们重新添加到远程存储库中;然后其他结帐会将它们拉入下一个拉取中。

如果尚未将更改推送到遥控器,则可以使用

git reset --hard <hash>

如果您推送更改,但确保没有人撤消更改,则可以使用

git reset --hard
git push -f

如果您推送更改,并且有人将其添加到他们的结帐中,您仍然可以执行此操作,但其他团队成员/结帐则需要进行协作:

(you) git reset --hard <hash>
(you) git push -f

(them) git fetch
(them) git reset --hard origin/branch

但总的来说,这变成了一团糟。因此,还原:

要删除的提交是最新的

这可能是最常见的情况,您已经做了一些事情-将它们推出,然后意识到它们不应该存在。

首先,您需要确定要返回的提交,您可以使用以下方法:

git log

只需在更改前先查找提交,然后记下提交哈希即可。您可以使用-n标志将日志限制为最近发送的提交:git log -n 5

然后将分支重置为您希望其他开发人员看到的状态:

git revert  <hash of first borked commit>..HEAD

最后一步是创建您自己的本地分支,重新应用您的还原更改:

git branch my-new-branch
git checkout my-new-branch
git revert <hash of each revert commit> .

继续进行工作,my-new-branch直到完成,然后将其合并到您的主要开发分支。

要删除的提交与其他提交混合在一起

如果您要还原的提交不是全部在一起,则最简单的是分别还原它们。再次使用git log查找要删除的提交,然后:

git revert <hash>
git revert <another hash>
..

然后,再次创建分支以继续工作:

git branch my-new-branch
git checkout my-new-branch
git revert <hash of each revert commit> .

再说一遍,完成后再砍掉并合并。

您应该以一个类似以下内容的提交历史作为结尾 my-new-branch

2012-05-28 10:11 AD7six             o [my-new-branch] Revert "Revert "another mistake""
2012-05-28 10:11 AD7six             o Revert "Revert "committing a mistake""
2012-05-28 10:09 AD7six             o [master] Revert "committing a mistake"
2012-05-28 10:09 AD7six             o Revert "another mistake"
2012-05-28 10:08 AD7six             o another mistake
2012-05-28 10:08 AD7six             o committing a mistake
2012-05-28 10:05 Bob                I XYZ nearly works

更好的方法

尤其是既然您已经意识到了多个开发人员在同一个分支中工作的危险,请考虑始终为您的工作使用功能分支。这意味着在分支中工作,直到完成某件事为止,然后将其合并到您的主分支中。还可以考虑使用诸如git-flow之类的工具以一致的方式自动创建分支。


+1表示git reset不带--hard标志,但是我认为大多数人都不会理解您正在重置工作目录和HEAD,这将导致! [rejected] master -> master (non-fast-forward)推送。另外,如果更改了许多文件,即使您强制提交,它也构成了一个真正的补丁程序。
托德·雅各布斯 Todd A. Jacobs)

@ AD7Six:感谢您使用Better Way注册商标。实际上,分支是如此便宜,以至于我在做出这一愚蠢的举动之后就正确了。由于我已经搞砸了所有这些事情,因此知道除了我之外没有其他人可以触摸存储库,这可能有助于在我的问题更新中使用该方法吗?
MEM

@CodeGnome您说得对,距离我发现自己处于这个位置
已经很久了

@MEM,是否没有其他人更新该回购关系并不重要-自从“错误”被提交以来,是否有人更新了结帐就很重要。因为如果有它们,当它们拉时,它们的结帐仍将包含您已强制删除的提交,并与所有新更改合并。如果git reset --hard在功能分支中使用,则在合并它时,很可能会因为重新应用删除的提交而产生冲突-基本上,不,这样做不是一个好主意。
AD7six 2012年

-1
git revert HEAD -m 1

在上面的代码行中。“最后一个参数代表”

  • 1-恢复一次提交。2-恢复上一次提交。n-恢复最后n次提交

要么

git reset --hard siriwjdd


1
引发错误:提交a17dc423957324b2b537c1caa9d9476c9b443f48没有父级n,其中n是最后一个参数
MyrionSC2 '12

如果HEAD是合并提交,则该代码有效。但是,此操作的描述不正确。-m实际上指定哪个父提交是从ainline分支。-m 2会将您还原到功能分支顶端的版本,并丢弃所有功能变更,因为您的功能分支与主要功能有所不同。
Chaim Leib Halbert,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.