Git:在推送后还原包含意外丢弃文件的合并



没有经验的用户更改release,然后合并到dev。哦,不,合并冲突!合并状态显示他们自己的冲突文件,以及其他团队成员的所有其他修复。作为一个偏执和谨慎的用户,他们说:

那些不是我的文件,是我不小心修改的吗?啊哈,我应该discard他们!

合并提交现在只包含它们的更改,放弃分支中的所有其他更改(可能需要大量工作)。他们将dev向上游推送,直到另一个团队成员注意到有问题,错误才会被检测到。

大多数指南建议:

  1. 如果尚未推送分支:git reset
  2. 还原合并:git revert -m 1 <commitId>。然而,这只会还原数据(也就是说,只还原倒霉用户的更改),不会撤消历史记录。未来对merge的任何尝试都将忽略丢失的更改,因为历史表明它们已经集成
  3. 重写历史,rebasereset,后面跟git push origin -f。这意味着团队的其他成员必须同步到强制的dev分支。如果团队规模很大,或者没有很快发现错误,或者存在复杂的CI工具,这将是一项非常痛苦的工作

Imho这让我觉得git设计中的一个关键疏忽。很少有工具可以识别这种情况或从中恢复。git diff不显示丢弃的更改,而git revert不会撤消那些丢弃的更改。有没有更好的方法来预防和解决这个问题?

如Linus(https://mirrors.edge.kernel.org/pub/software/scm/git/docs/howto/revert-a-faulty-merge.txt):

恢复常规提交只会有效地撤消该提交做了,而且相当简单。但是恢复合并提交撤消提交更改的数据,但它绝对会这样做与合并对历史的影响无关。

因此,合并仍将存在,并且仍将被视为加入这两个分支在一起,将来的合并将把它看作最后一个共享状态-以及恢复合并的revertin根本不会影响这一点。

因此,"revert"撤消了数据更改,但它在很大程度上是而不是"撤消"是指它不会撤消对存储库历史记录。

好的,这就解释了为什么revert不是一个有效的策略,但我们能做什么?让我们考虑以下内容:

p---Q---r---s---M---t---u---   dev
         /
A---B---C-------D---E    feature
  • ABC是功能/发布分支工作
  • M是错误的合并,如果AB的更改被丢弃,但C被保留
  • DE稍后将开发feature
  • 其余为主线分支dev上不相关的更改

只要M存在于dev上,它就假设ABC的历史已经被整合,即使AB的delta缺失。为了在不更改dev历史记录的情况下恢复它们,我们需要在备用历史记录(即新的提交ID)中重新创建增量。

如果只有几个提交,您可以将每个cherrypick单独地复制到dev上,因为cherrypicking将数据复制到一个新的提交ID中。但是,这不能很好地扩展到大型或复杂的分支历史。

下一个选项是使用rebase --no-ff重新创建一个新的feature分支,从中可以合并丢失的更改。

git checkout E
git rebase --no-ff Q

它创建了以下内容:

A'--B'--C'-------D'--E'    feature-fixed
/                      
p---Q---r---s---M---t---u---M2   dev
         /
A---B---C--------D---E     feature

最初的合并M可能只是由于合并冲突而成为一个问题。这种方法的一个问题是,你不仅必须正确解决ABC中最初的冲突,而且现在你在DE和TU中有了一个新的可能的冲突来源。在紧急情况下,弄清楚发生了什么可能会很麻烦。

首选解决方案:

p---Q---r---S-------M---t---u-----M3      dev
            /              /
A---B------C----D---E     /        feature
               /
----M2----------          fix

使用您可能熟悉的工具,一个更简单的策略是使用squash提交(M2)正确地重新创建合并。这创建了一个新的提交ID(新历史),因此来自AB的delta可以成功地集成回主线中。此方法还关闭了合并冲突的可能来源,允许您首先更正错误,然后处理上游更改。

方法:

在坏合并(M)着陆之前从dev分支。

git checkout -b fix S

现在,您可以从头开始执行更正的合并。挤压标志将把这些更改压缩为一个提交,但更重要的是,它将生成一个新的提交ID

git merge --squash C

在这一点上,您可能需要解决冲突,但是M2现在表示M最初应该包含的所有数据。你现在可以像通常那样将其合并到dev中

git checkout dev
git merge fix

同样,合并冲突可能会出现,但在这一点(M3)之后,您已经恢复了丢失的数据。您现在可以按照正常操作进行,例如,您可以将DE从feature合并到dev或任何其他常规操作。这也意味着没有其他团队成员需要重置他们的本地dev分支,因为它将在他们下一次执行pull时恢复。

最新更新