TL;DR
请记住,git revert 的真正含义是取消更改,不恢复到(即恢复)某个特定版本。可以实现revert to / restore,但是执行此操作的命令是git checkout 和git read-tree(由于不同的原因,这两个命令都比较棘手)。请参阅 How to revert Git repository to a previous commit? 和 Rollback to an old Git commit in a public repo(特别是 jthill's answer)。
这里唯一真正棘手的部分是“恢复合并”。这相当于在合并提交上使用git revert -m 1,这很容易运行;但这意味着之后,您无法重新合并,因为 Git 非常确定(并且正确)您已经合并了所有这些工作并且正确的结果已经到位(就是这样,您只是后来解开了)。要将其全部还原,您可以还原还原。
Reverting 会创建一个新的提交,就像每次提交一样,只是将一个新的提交添加到当前分支。您像往常一样在自己的存储库中执行此操作。剩下的工作就是将新提交推送到某个 other Git 存储库,将其添加到另一个 Git 的集合中,作为 它们的 分支之一的新提示。
很长(涉及很多细节)
首先,让我们稍微备份一下,因为短语 remote branch 没有任何意义,或者更确切地说,对于太多不同的人来说,意味着太多不同的东西——无论哪种情况,最终都会导致失败交流。
Git 确实有分支,但即使是“分支”这个词也是模棱两可的(参见 What exactly do we mean by "branch"?)。我发现最好具体一点:我们有 分支名称,例如 master 和 staging,并且根据您的建议,cleaning。 Git 也有 remote-tracking 名称 或 remote-tracking 分支名称,例如 origin/staging,但令人困惑的是,Git 将这些名称存储在 本地,即在您自己的存储库中(仅限)。你的 Git 使用 your origin/staging 来记住你的 Git 在他们的 Git 上看到了什么,当你的 Git 上次与他们的 Git 交谈并询问他们一些关于 他们的 staging.
因此,问题的根源在于您的 Git 存储库是您的,但是还有第二个 Git 存储库不是您的,而您最终希望在那个 other Git 存储库上做某事。这里的限制是你的 Git 只允许你在 你的 存储库中做这些事情,之后你最终将运行git push。 git push 步骤会将一些提交或提交从您的 Git 存储库转移到他们的 Git 存储库。这时候,你可以让他们的Git设置他们的名字——他们的staging——他们要么会说是的,我已经设置了(你的 Git 现在将更新你的 origin/staging 以记住这一点),或者不,我拒绝设置它,原因如下:_____(在此处插入原因) .
因此,您要在 您的 存储库中执行的操作是设置适当的步骤,以便 他们的 Git,在 他们的 存储库中,将接受您git push 的提交并将更新他们的staging。在我们完成这些步骤时请记住这一点。有多种方法可以做到这一点,但这里是我会使用的方法。
-
运行git fetch。此命令始终可以安全运行。如果你有多个遥控器,你可以给它一个特定遥控器的名称,但大多数人只有一个,命名为origin,如果你只有一个,则无需命名。
(远程的名称——origin——主要是拼写你的计算机应该用来访问其他 Git 存储库的 URL 的一种简写方式。)
这个git fetch 可能什么都不做,或者可能会更新您的一些远程跟踪 (origin/*) 名称。 git fetch 所做的是调用另一个 Git,从中获取所有分支的列表以及与它们相关的提交哈希,然后带来他们拥有的任何你没有的提交。您的origin/staging 现在记住了他们的staging。您现在可以为此添加新的提交。
-
既然您的 origin/staging 与其他 Git 的 staging 同步,请根据需要创建或更新本地分支名称。此处使用的最佳名称是staging,但如果您愿意,也可以使用cleaning。因为这一步是创建或更新,所以有子步骤:
-
如果是“创建”,则可以使用git checkout -b staging origin/staging新建一个staging,其上游为origin/staging。
您可以将其缩短为 git checkout staging,因为您没有自己的 staging,它将搜索您的所有 origin/* 名称(以及任何其他远程跟踪名称,如果您有超过一个遥控器)来查找匹配的。然后它就像您使用了更长的命令一样。 (只有一个遥控器,唯一可以匹配的名称是origin/staging。如果你同时拥有origin 和xyzzy 作为遥控器,你可以同时拥有origin/staging 和xyzzy/staging;那么你需要更长的时间命令。)
-
或者,如果是“更新”,您将已经有一个 staging 已经将 origin/staging 设置为其上游,因为您之前已经这样做了。在这种情况下,只需运行:
git checkout staging
git merge --ff-only origin/staging
让您的登台与origin/staging 重新同步。如果快进合并失败,你有一些提交,他们没有,你将需要更复杂的东西,但我们在这里假设这会成功。
您也可以稍微缩写这些命令,但我将在此处将它们拼写出来。请注意,第一个命令与上述第一种情况的简短版本相同,您可以通过git checkout staging 的输出判断发生了哪个命令。 (我会留下其他问题或练习的详细信息。)
我们可以在您自己的存储库中绘制您现在拥有的图片,它看起来像这样:
...--o--o--o---M <-- staging (HEAD), origin/staging
\ /
o--o <-- feature/whatever
每一轮o 代表一个提交。 M 代表合并提交,它的结果你不喜欢,但它也存在于另一个 Git 中 origin 下 他们的 名称 staging,这就是为什么你自己的 Git 有名称origin/staging 指向提交M。
-
您现在想要创建一个撤消错误提交的提交。这可能会使用git revert,但请记住,revert 意味着撤消或退出,而不是切换到旧版本。你告诉 Git 撤消哪个提交,Git 通过弄清楚你做了什么来撤消它,然后做相反的事情。
例如,如果您说要恢复的提交显示“删除文件 README”,则更改将包括“将文件 README 恢复为删除时的形式”。如果您说要还原的提交显示“将此行添加到 Documentation/doc.txt”,则更改将包括“从 Documentation/doc.txt 中删除该行”。如果您说要恢复的提交在某个第三个文件中显示“将您好为再见”,那么恢复将做的更改是将第三个文件中的“再见”更改为“您好”,在同一行(找到一些魔法如果它移动了线)。
这意味着git revert 可以撤消任何提交,即使它不是最新的提交。但是,要这样做,它必须将该提交与其直接父级进行比较。如果您尝试恢复的提交是 merge 提交,它有多个父级,您需要指定 Git 应该使用哪个父级。
要使用的正确父级并不总是很明显。但是,对于大多数合并,它只是“父编号 1”。这是因为 Git 特别强调了合并的 first 父级:当您运行 git merge 时,它是 HEAD 的提交。所以这就是合并带来的一切,但还没有出现。
当git revert 成功时,它会进行一个新的提交,以撤消合并的效果:
W <-- staging (HEAD)
/
...--o--o--o---M <-- origin/staging
\ /
o--o <-- feature/whatever
这里,W 代表这个新的提交:它是 M 颠倒过来的。你现在要做的就是运行 git push origin staging 将你自己的新提交 W 发送到另一个 Git:
-
git push origin staging:这会调用另一个 Git 并提供它提交 W——这是我们拥有但他们没有的每一个提交;他们有M 和之前的所有内容(左侧),但没有W。
只要没有特别的限制,他们就会接受这个新的提交,并改变他们的 staging 指向新的提交W。您的 Git 会记住更改:
W <-- staging (HEAD), origin/staging
/
...--o--o--o---M
\ /
o--o <-- feature/whatever
(没有必要在单独的行上继续绘制W,但我在这里使用复制粘贴来保持形状不变。)
如您所见,您现在已经完成了。您和他们都同意您和他们的staging 都应该指向具有撤消提交M 的效果的提交W。如果您愿意,现在可以安全地删除您自己的 staging 名称:
git checkout <something-else>
git branch -d staging
产生:
...--o--o--o---M--W <-- origin/staging
\ /
o--o <-- feature/whatever