错误推送到主分支后如何恢复

4
我经常犯这个错误,想知道是否有更好的解决方案,以及我的当前解决方案是否存在风险。
以下是工作流程:
1. git checkout master 2. git pull 3. git submodule update --init --recursive 4. (编辑、调试、测试) 5. git commit -a 6. git push origin HEAD:refs/for/master 7. (更多编辑、测试等) 8. git commit -a 9. gitk 10. (咒骂) 11. git rebase -i HEAD~2 #将我的两个提交合并 12. git checkout -b task_id 13. git checkout master 14. git rebase -i HEAD~2 #选择先前用户的提交,删除自己的提交
我的错误在于,在第5步左右,我应该像第12步那样创建一个新分支,将这项工作与其他所有任务分开,以便我可以轻松地回到它或按所需顺序重新基于不同的补丁。相反,我将新代码推送到主分支,大约在第10步时,我意识到master不再指向公司批准的最新版本,而是指向我的努力。因此,在使用交互式rebase将自己的提交压缩为一个后,我又进行了另一个交互式rebase,将master指向它应该指向的东西--即已通过审查流程的其他人的工作。
编辑以澄清:当我“推送到主分支”时,这不会将我的更改发布给其他人。有一些提交钩子魔法会创建一个Gerrit代码审查任务,只有在从那里获得批准后,我的提交才会合并到远程存储库中。对于误导您所有人,我表示歉意。我的问题是双重的。这种做法有多糟糕,存在哪些风险,如何改进?我不是要求帮助避免最初的错误。我认为我只需要更加注意我正在推送到哪个分支。问题是如何最好地将master指向先前的提交,而不影响/冒险/丢失任何内容。

3
我的错误在于在第5步左右,我应该像第12步那样做一些事情。我建议在第4步之前,不管您是否编码,都创建一个新分支。 - Code-Apprentice
1
你可以撤销一个提交,这样你就可以将主分支指向你想要的提交,但是你当然会失去你所做的每一次推送。请查看此文档 https://help.github.com/desktop/guides/contributing-to-projects/reverting-a-commit/。 - Brank Victoria
@BrankVictoria 注意,还原与重置是不同的。还原不会丢失任何之前推送的历史记录,因为它只是在旧提交的顶部创建一个带有相反更改的新提交。 - Code-Apprentice
2个回答

7

如果你只提交了一个commit,可以按照以下步骤进行操作:

git checkout master
git reset --hard HEAD~
git push -f

这将把主分支恢复到先前的提交状态。如果您有更多的提交记录,可以用任何其他提交记录(SHA1哈希、分支名称、标签名称等)替换HEAD~

我的错误在于,在第5步左右,我应该做类似于第12步的操作。

建议在第4步之前,您先创建一个新分支。这里不应存在任何歧义。通常最佳实践是创建一个新的特性分支,以便将正在进行的工作与主分支分开,并且只有在工作完成后才将其合并到主分支中。在主分支上提交未完成的工作会使您的软件不稳定且难以发布。

请原谅我在问题表述上不够清晰。我只关心本地代码库的完整性,因为 Gerrit 代码审查过程会防止对远程代码库进行任何修改。这是否使问题更简单,还是更加复杂? - Tim Randall
@TimRandall 我对第6步感到困惑:git push origin HEAD:refs/for/master。这似乎将主分支推送到中央仓库(顺便说一句,这是一种非常冗长的方式)。至于您的本地仓库,从技术上讲,您可以随心所欲地进行操作,因为它独立于其他仓库。但是,为了保持清醒,您不应该提交到主分支。最终,您将希望从中央仓库拉取主分支以保持与其他开发人员的工作同步。如果您提交到主分支,您将增加合并冲突的可能性,而这在主分支上永远不应该发生。 - Code-Apprentice
是的,这就是我们所给出的语法。我不知道我们的代码审查系统具体是如何工作的,但该命令会将提交发送到Gerrit进行审查,并最终与远程主分支合并。 - Tim Randall
我接受这个答案,因为我认为上面的 git reset 语法正好符合我的要求,即将 master 指向父提交。 - Tim Randall

1
我正在将新代码推送到主分支,大约在第10步时意识到主分支不再指向公司批准的最新版本代码,而是我的自己的努力。问题的根源在于你甚至被允许在共享存储库上直接推送到主分支。你应该从共享存储库中拉取,但是推送到你自己的分叉并发出拉取请求。你的拉取请求应该由其他人审核后,由非你本人合并。是的,你可以通过早期创建自己的分支来避免推送到主分支,但真正的问题在于你(以及其他人)可以直接推送到主分支。
这种做法有多糟糕,涉及风险,如何改进?
这种做法相当糟糕,主要是因为(再次强调)你根本不应该被允许这样做。你怎么知道你的“修复”是正确的呢?如果在你意识到自己搞砸之前,其他人在你的基础上合并了他们的更改(可能已经被审核过,也可能没有),那该怎么办?
解决方法是禁止直接推送到主分支更新:鉴于您的评论提到您所说的主分支不是共享仓库中的主分支,而是您自己的分支中的主分支,那么您的解决方案似乎是可以的。您自己仓库中的主分支并没有太多意义,至少在我看来大部分工作流程都是这样。通常情况下,您会在自己的分支上工作,然后将该分支推送到您的仓库,并向共享仓库发起拉取请求,在此之前应该会进行代码审查和其他检查,以确保您的代码合并。因此,您修复自己的主分支的过程是正确的 - 将您的工作保存到一个新的分支中,然后重置或变基您的主分支,使其与远程主分支同步。

抱歉,Caleb。我没有解释清楚我们使用Gerrit和其他技巧/钩子来防止直接修改远程主分支。我尝试编辑问题以澄清这个问题。 - Tim Randall
在这种情况下,实际上并没有什么大问题...你自己 fork 的 master 分支不应该有太大的影响。从远程 master 拉取以便更新,然后为你的工作创建一个分支,将你的分支推送到你的 fork,并从该分支向远程 master 发起拉取请求。如果你使用不同的工作流程,可以在你的问题中解释一下是什么。 - Caleb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接