在合并分支时避免还原提交对另一个Git分支产生影响

12

使用git flow工作。我们有一个不熟悉Git的同事,昨天意外地将develop合并到了master中。

Develop有很多功能即将在我们的下一个版本中发布,需要撤销合并。这会创建一个撤销所有更改的提交。当我们将主分支(master)合并回develop时,撤销提交会移除我们的功能所生成的代码。

在保留新功能的情况下,如何最好地进行同步开发分支(develop)和主分支(master)的修复?

--编辑--仅澄清,撤销是一个撤销(revert)操作。即已经将提交推送到远程存储库。git revert -m 1 <sha>

发布此文后,我想到了一种可能的解决方法,即在主分支(master)上创建一个分支并撤销撤销,但我很想知道是否有其他可能性可以最小化冲突。


请详细说明如何执行还原操作(尤其是使用的具体命令)。强制重置master到其先前状态(而不是还原)是否不可行? - user456814
此外,“将 X 分支意外合并到 Y 分支”不应该成为问题,如果您的同事无法将合并结果推送到您的主仓库。请解释一下您的开发人员是是否都在推送到同一个仓库,或者是否您们每个人都使用自己的私有分支。您是否正在使用 GitHub 上的拉取请求? - user456814
这不是Github,而是使用Git Flow框架的Gitlab,并提供标准分支建议。 - DivinusVox
确切的命令是 git revert -m 1 <sha>,以强制回滚,因为提交已经推送到全局存储库。 - DivinusVox
2
如果你将master重置到合并之前的状态并强制推送,会发生什么?这将要求每个人再次同步,但它会保留你的历史记录。 - CharlesB
显示剩余2条评论
2个回答

14

选项1:硬重置和强制推送

如果你可以在上游仓库中对你的master分支进行非快进式强制更新,那么你可以直接对master进行硬重置,而不是还原develop合并到master的操作:

# On master branch, do a hard reset back to the commit before the merge
git reset --hard <commit of master before the merge>

# Force push to upstream ONLY IF IT'S OK WITH OTHER DEVELOPERS
git push <remote> master --force

进行硬重置和强制推送的一个可能的缺点是,如果其他开发人员已经基于合并提交(例如在其之上进行了提交)进行了工作,则他们需要在master的重置头部上重新执行相同的工作。这对他们来说可能是一个困难/代价高昂的任务。

选项2:撤销还原

我用一个快速测试存储库测试了一下。我必须强调它可能有效,我不能100%自信地说没有任何我没有考虑到的情况。因此,请务必使用存储库的备份副本在本地测试它。如果您选择在实际存储库中使用它,请自行承担风险。

另外,这可能不是最简单/最简单的解决方案。但与硬重置选项相比,优点在于它不会强迫开发人员必须在重置master分支后重新执行工作。

好的,既然这些都搞定了,你可以尝试将master合并到develop中,然后还原从developmaster合并的撤销,最后在准备好的时候将develop合并到master中。命令如下:

# Coworker accidentally merges develop into master before it's ready
git merge --no-ff develop

# You revert the merge in the master branch (this creates commit "ABCDEFG"
git revert -m 1 <sha of merge commit>

# You want to merge fixes from master into develop
git checkout develop
git merge --no-ff master

# But now all that work in develop is reverted, so revert the revert "ABCDEFG"
git revert ABCDEFG

# When you're ready to merge develop into master...
git checkout master
git merge --no-ff develop

这是我在测试仓库中用来测试的一系列命令:

mkdir practice
cd practice/
git init

touch readme.txt
git add practice.txt
git commit -m "Add practice.txt"

git checkout -b develop

touch feature1.txt
git add feature1.txt
git commit -m "Add feature 1"

touch feature2.txt
git add feature2.txt
git commit -m "Add feature 2"

git checkout master

touch hotfix1.txt
git add hotfix1.txt
git commit -m "Fix issue 1"

git merge --no-ff develop

# Creates commit "ABCDEFG" that reverts the merge
git revert -m 1 head
git checkout develop
git merge --no-ff master
git revert ABCDEFG
git checkout master
git merge --no-ff develop

您可以在官方Linux内核Git文档的git revert中了解更多关于“还原还原”技术的信息:

-m parent-number

--mainline parent-number

通常情况下,您无法还原合并操作,因为您不知道哪一侧的合并应该被视为主线。此选项指定主线的父级编号(从1开始),并允许还原相对于指定的父级进行更改。

还原合并提交意味着您永远不希望树更改由合并带入。因此,后续合并将仅引入先前还原合并的祖先之外的提交引入的树更改。这可能是您想要的,也可能不是。

有关详细信息,请参见revert-a-faulty-merge How-To

如果您完全想了解此技术的工作原理,强烈推荐访问如何撤销错误合并的链接,它并不难理解,实际上有些有趣和迷人。


2
是的,这是正确的,撤销还原是正确的方法。原始还原会删除内容但保留历史记录;在从同一分支重新合并之前,通过撤销还原来重新引入历史记录的内容。 - ben_h
谢谢!它节省了我很多时间。 - excelsiorious

5

我的团队也遇到了类似的问题。实际上,我已经有一个相对简单的解决方案了,但我只是在研究防止这种情况发生的方法(目前还没有解决方案)时找到了这个主题。

以下是我修复它的方式,假设在与主分支进行“糟糕”的合并之前,子分支(“develop”)已经更新(提交 M1):

问题状态

           ... <-- Work after revert that needs merged to develop
            |
            R  <-- Revert Bad Merge
            |
            A  <-- Commits after merge,
            |    /   but before revert 
           ... </    and needs merged to develop
            |
           M2  <-"bad" merge
  ... ____/ |
   | /      |
   M1       |
   | \____  |
  ...     \...
develop   master 

步骤1

# Get latest from both parent and child branches locally

git checkout master
git pull
git checkout develop
git pull


# Merge all code from before revert in master branch to develop
# (not necessary if "bad" merge into master was immediately reverted)

git merge A

第一步后的状态:

           ... <-- Work after revert that needs merged to develop
   M3       |
   | \____  R  <-- Revert Bad Merge
   |      \ |
   |        A  <-- Commits after merge,
   |        |    /   but before revert
   |       ... </    and needs merged to develop
   |        |
   |       M2  <-"bad" merge
  ... ____/ |
   | /      |
   M1       |
   | \____  |
  ...     \...
develop   master 

第二步 - 重要部分!

# Use "ours" strategy to merge revert commit to develop.
# This doesn't change any files in develop. 
# It simplly tells git that we've already accounted for that change.

git merge R -s ours

第二步后的状态

   M4
   | \____  ... <-- Work after revert that needs merged to develop
   M3     \ |
   | \____  R  <-- Revert Bad Merge
   |      \ |
   |        A  <-- Commits after merge,
   |        |    /   but before revert
   |       ... </    and needs merged to develop
   |        |
   |       M2  <-"bad" merge
  ... ____/ |
   | /      |
   M1       |
   | \____  |
  ...     \...
develop   master 

第三步

# Merge as normal, from the tip of master to develop.
# This should now be an "easy" merge, with only "real" conflicts.
#  (Those that have changed in both branches)
#
# Note: I've had issues using origin master to merge from latest on remote, 
#   so instead I just ensure I've pulled the latest from master locally and 
#   merge from there

git merge master

第三步后的状态

   M5
   | \_____
   M4      \
   | \____  ... <-- Work after revert that needs merged to develop
   M3     \ |
   | \____  R  <-- Revert Bad Merge
   |      \ |
   |        A  <-- Commits after merge,
   |        |    /   but before revert
   |       ... </    and needs merged to develop
   |        |
   |       M2  <-"bad" merge
  ... ____/ |
   | /      |
   M1       |
   | \____  |
  ...     \...
develop   master 

现在,develop已经更新到最新的master版本,无需解决重复或无意义的合并冲突。未来的合并也将像平常一样进行。

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