Git恢复已发布的提交,同时保持将来的合并能力? [英] Git revert on published commits while maintaining ability to merge in future?

查看:45
本文介绍了Git恢复已发布的提交,同时保持将来的合并能力?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们有两个同时存在的分支,就像这样:

  A--B --- C -------- D--H --->(支行A)\/\ --- E--F--G-/->(B支) 

问题是我们决定暂时不希望将B分支合并到A分支,这是一个错误.

因此,我们(在分支A上)还原了合并提交:

  git checkout分支-agit还原H 

结果:

  A--B --- C -------- D--H--I->(支行A)\/\ --- E--F--G-/->(B支) 

将分支A恢复到其正确状态.(通过还原提交 I )然后我们在两个分支上做了更多工作,现在想再次合并:

  A--B --- C -------- D--H--I--J--K ------?>(支行A)\//\ --- E--F--G-/--- L --- M ---- N-/(B分支) 

问题是,根据Git,当我们进行合并时,还原提交 I 比提交 E F G ,并且比合并提交 H 更新,因此它不会从分支B添加新文件(和更改).

我们可以对恢复(yike)进行一些快速恢复,但我们希望避免在将来发生这种情况.是否有任何适当的解决方案可以撤消合并(到公共存储库),同时仍然保持将来合并该分支的能力?

此Git博客文章:

撤消合并的直接方法

撤消合并的更好方法是使用 git reset .如果您不希望保存任何合并结果,并假定使用的是干净的工作副本,则只需对已签出的分支A进行以下操作即可.

  git reset --hard D 

如果工作区不整洁,或者出于某种原因要保存合并结果,则可以执行以下操作:

  git reset --mixed D#实际上,您不必指定'mixed',因为这是默认设置 

这两个命令都会使您的图形如下:

  A--B --- C -------- D --->(支行A)\\ --- E--F--G->(B支) 

但是,第二条命令会将更改保留在您的工作副本中.有关更多信息,请参见 git help reset 手册页.以下是关键部分:

  git reset [< mode>] [< commit>]该表格将当前分支头重置为< commit>.并可能会更新索引(将其重置为< commit>的树),然后工作树取决于< mode>.如果< mode>省略,默认为"--mixed".< mode>必须为以下之一:-混合重置索引但不重置工作树(即,已更改的文件将保留但未标记为提交)并报告什么尚未更新.这是默认操作.- 难的重置索引和工作树.自< commit>以来,对工作树中跟踪文件的任何更改均已更改.被丢弃. 

以这种方式进行操作会使将来的合并完全正常,因为没有还原的历史记录.就git而言,下次您进行合并将是第一次,您的生活将会继续.

请注意,原始合并提交H实际上仍会保留在您的存储库中-但是:如果设置了 gc.auto ,则某些命令将导致 gc ,该修剪将从您的存储库中删除无法访问的提交,其中包括H.您出于某种原因想要挂起合并提交,则需要创建另一个分支来保存对它的引用,或者关闭 gc.auto 并写下其提交的前7-8个字符id,以便您可以再次找到它.

糟糕,我是在公共分支上完成的

官方指南将您是否不惜一切代价避免重写已发布分支上的历史记录.我认为对此进行思考的正确方法是评估您的费用.您有几个下游克隆?他们中的任何人实际上是在做工作吗,还是只是镜子?真的会打扰您太多,可能在日志历史记录中重复提交(希望只有一次)吗?

问题的症结所在:

是否有任何适当的解决方案来撤消合并(向公众公开)存储库),同时仍保持合并该分支的能力回到未来?

没有一个整洁的答案.一种方法是按照建议还原合并,而使您处于提交I:

  A--B --- C -------- D--H--I->(支行A)\/\ --- E--F--G-/->(B支) 

为了使这一点更加明显,让我们将还原标记为H'.当您准备好再次合并时,请首先启用rerere( rerere.enabled ),然后还原该还原:

  A--B --- C -------- D--H--H'-J--K--H''-O-?>(支行A)\//\ --- E--F--G-/--- L --- M ---- N ----/(B分支) 

如果存在冲突,请尽最大努力解决这些冲突,并让其重新记录结果(有关详细信息以及正确的处理方法,请参见手册页).

Junio Hamano和Linus Torvalds 对这种还原还原"方案进行了详尽的解释,自编写此答案以来,我认为还原还原"是您方案的正确答案.

We had two branches that existed simultaneously, like so:

A--B---C--------D--H---> (Branch A)
    \             /
     \---E--F--G-/--> (Branch B)

The issue is that we decided that we didn't want to merge Branch B into Branch A just yet, this was a mistake.

So, we (on Branch A) reverted the merge commit:

git checkout branch-a
git revert H

Resulting in:

A--B---C--------D--H--I--> (Branch A)
    \             /
     \---E--F--G-/--> (Branch B)

Which restored Branch A to its proper state. (Via the revert commit I) We then did a bit more work on both branches, and now want to merge in again:

A--B---C--------D--H--I--J--K------?> (Branch A)
    \             /               /
     \---E--F--G-/---L---M----N--/ (Branch B)

The problem is that according to Git, when we do the merge, the revert commit I is newer than the commits E, F, and G, and also newer than the merge commit H so it's not adding the new files (and changes) from Branch B.

We can do some quick reverting of the reverting (yikes), but we'd like to prevent this from happening in the future. Is there any proper solution for undoing a merge (to a public repository), while still maintaining the ability to merge that branch back in in the future?

Edit: This Git blog post: http://git-scm.com/blog/2010/03/02/undoing-merges.html suggests that we should just 'revert the revert', as we're doing. But we'd like to find a better way, if possible, to undo a merge while maintaining the ability to merge the branch back in in the future - without having to remember that we reverted a similar merge sometime in the past.

解决方案

The straightforward way to undo a merge

A better way to undo a merge is to use git reset. If you don't care to save any of the results of the merge, and assuming a clean working copy, you can just do the following with branch A checked out to your working copy.

git reset --hard D

If the workspace is not clean, or you want to save the results of the merge for some reason, you can do this:

git reset --mixed D # actually, you don't have to specify 'mixed', as this is the default

Both commands will leave your graph like this:

A--B---C--------D---> (Branch A)
    \             
     \---E--F--G--> (Branch B)

However, the second command will leave changes in your working copy. See the git help reset manpage for more info. Here are the key parts:

git reset [<mode>] [<commit>]
           This form resets the current branch head to <commit> and possibly updates the index (resetting it to the tree of <commit>) and
           the working tree depending on <mode>. If <mode> is omitted, defaults to "--mixed". The <mode> must be one of the following:

--mixed
               Resets the index but not the working tree (i.e., the changed files are preserved but not marked for commit) and reports what
               has not been updated. This is the default action.

--hard
               Resets the index and working tree. Any changes to tracked files in the working tree since <commit> are discarded.

Doing it this way makes future merges perfectly normal because there is no history of reversion. As far as git is concerned, the next time you do a merge will be the first time and your life will carry on.

As a side note, the original merge commit, H, will still actually remain in your repository - however: if you have gc.auto set, then some commands will cause a gc, which prunes unreachable commits from your repository, which would include H. If you for some reason want to hang on to the merge commit, you need to create another branch to hold a reference to it or turn off gc.auto and write down the first 7-8 characters of its commit id so you can find it again.

Oh crap, I did this on a public branch

Official guidance would have you avoid rewriting history on a published branch at all costs. I think the right way to think about it is to evaluate your costs. How many downstream clones do you have? Do any of them actually contribute work, or are they just mirrors? Is it really going to bother you that much to possibly have duplicate commits in your log history (only one time, hopefully)?

The nut of the question:

Is there any proper solution for undoing a merge (to a public repository), while still maintaining the ability to merge that branch back in in the future?

doesn't have a nice and tidy answer. One way is to revert the merge as suggested, leaving you with commit I:

A--B---C--------D--H--I--> (Branch A)
    \             /
     \---E--F--G-/--> (Branch B)

To make this more obvious, let's relabel the revert as H'. When you're ready to merge again, first enable rerere (rerere.enabled) and then revert the revert:

A--B---C--------D--H--H'--J--K--H''--O-?> (Branch A)
    \             /                 /
     \---E--F--G-/---L---M----N----/ (Branch B)

If there are conflicts, then work through them the best you can and let rerere record the results (see the man page for details and how to do it right).

Junio Hamano and Linus Torvalds wrote a thorough explanation of this "revert-the-revert" scenario, and since writing this answer, I think "revert-the-revert" is the right answer for your scenario.

这篇关于Git恢复已发布的提交,同时保持将来的合并能力?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆