Git:如何将现有的“合并"转换为“合并-壁球"? [英] Git: How to convert an existing `merge` to a `merge --squash`?

查看:124
本文介绍了Git:如何将现有的“合并"转换为“合并-壁球"?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我执行了多个merge提交,但应该是merge --squash.解决冲突花了一天多的时间,所以我无力手动重做合并.

I performed multiple merge commits but they should have been merge --squash instead. The conflict resolution took more than a day so I can't afford to redo the merging by hand.

是否可以将merge转换为merge --squash?

推荐答案

在这里值得注意的是git mergegit merge --squash密切相关,但是git merge --squash 不会创建合并.

It's worth noting here that git merge and git merge --squash are closely related, but git merge --squash does not create a merge.

这里的措辞非常重要,尤其是在合并"前面的文章"a":合并"是名词,而合并"是动词.这两个命令都执行合并操作.区别在于保存结果的方式.

The phrasing here is very important, particularly the article "a" in front of "merge": "a merge" is a noun, while "to merge" is a verb. Both commands perform the act of merging. The difference lies in how the result is saved.

同样值得一提的是,以提交图的形式来提醒一下合并的外观.这里的每个o圆形节点均表示一个提交,其中较早的提交朝左.分支名称是指向一个特定提交(分支的 tip 提交)的标签.您可以从此开始,例如:

It's also worth a quick reminder, in the form of a diagram of commits, of how merges look. Each round o node here represents a commit, with earlier commits towards the left. Branch names are labels that point to one specific commit (the tip commit of a branch). You might start with this, for instance:

...--o--*--o--o   <-- main
         \
          o--o--o   <-- feature

然后您决定将一个特定的feature分支合并回main分支,因此运行:

You then decide to merge the one specific feature branch back into the main branch, so you run:

$ git checkout main && git merge feature

这将执行合并(动词)并进行合并(名词),结果如下所示:

This does a merge (verb) and makes a merge (noun), and the result looks like this:

...--o--*--o--o---o   <-- main
         \       /
          o--o--o   <-- feature

Git向main添加了一个新的提交,而这个新的提交是 merge提交:它既指向main的上一个技巧,也指向(c情况,也向下)到feature的当前尖端. (名称feature继续指向与以前相同的提交.)

Git has added one new commit to main, and this new commit is a merge commit: it points back to both the previous tip of main, and also back (and in this case, downward as well) to the still-current tip of feature. (The name feature continues to point to the same commit as before.)

git merge --squash的作用是修改最后一步.它没有进行合并提交,而是完全抑制了提交(没有明显的原因),并迫使您运行git commit.当您执行运行git commit时,这将执行普通提交,而不是 merge 提交,因此结果如下所示:

What git merge --squash does is to modify the last step. Instead of making a merge commit, it suppresses committing entirely—for no obvious reason—and forces you to run git commit. When you do run git commit, this makes an ordinary commit, instead of a merge commit, so that the result looks like this:

...--o--*--o--o---o   <-- main
         \
          o--o--o   <-- feature

这里有两个关键项目:

  • 新提交的内容是相同的.这两个新的提交都是通过索引进行的(索引是Git的术语,它表示下一次提交的内容").该索引是通过动词合并过程设置的. first 父级是主线"提交,来自我们进行合并时所在的分支. second 父级是另一个提交,即我们刚刚合并的提交.

  • The contents of the new commits are the same. Both new commits are made from the index (the index is Git's term for "what goes into the next commit you make"). This index is set up by the merge-as-a-verb process. The first parent is the "main-line" commit, from the branch we are on when we do the merge. The second parent is the other commit, the one we just merged.

新提交的父链接不同.真正的合并具有两者先前的提交作为其父对象,但是壁球合并"仅具有一个先前的提交作为其父对象(主线"提交).这意味着它不能(不能不能)记住合并的提交.

The parent linkages of the new commits differ. A real merge has both previous commits as its parents, but a "squash merge" has only one previous commit as its parent—the "main-line" commit. This means it does not—it can not—remember which commit was merged-in.

在发生合并(但真实)合并的情况下,git merge无法独自进行新的提交,因此它将停止并迫使您解决冲突.解决这些冲突后,必须手动运行git commit,就像必须(无明显原因)始终使用git merge --squash及其伪合并一样.您还可以使用git merge --no-commit请求停止任何实际的合并并让您检查结果.

In the case of a conflicted (but real) merge, git merge cannot make the new commit on its own, so it stops and forces you to resolve the conflicts. Once you have finished resolving those conflicts, you must manually run git commit, just as you must always (for no obvious reason) do with git merge --squash and its fake merges. You can also request that any real merge stop and let you inspect the result, using git merge --no-commit.

这导致了一种简单的方法,只要还没有提交真正的合并,就可以将真正的合并转换为伪合并(壁球):

This leads to the easy method to turn a real merge into a fake (squash) merge, as long as the real merge is not yet committed:

  • 要让git commit知道要进行合并,它依赖于冲突的合并(或--no-commit)留下的文件.该文件名为.git/MERGE_HEAD. (尽管这个多余的文件是无害的,但它也留下了一个名为.git/MERGE_MSG的文件.)

  • For git commit to know to make a merge, it relies on a file left behind by the conflicted (or --no-commit) merge. This file is named .git/MERGE_HEAD. (It also leaves behind a file named .git/MERGE_MSG although this extra file is harmless.)

因此,您可以简单地删除 .git/MERGE_HEAD并运行git commit. (在编写带有其消息的提交后,您可能还希望删除MERGE_MSG文件.在此之前,您可以将其用作或用作消息的起点.) step将不再知道进行合并提交,而是进行普通的提交-瞧,您已经进行了壁球合并.

Therefore, you can simply remove .git/MERGE_HEAD and run git commit. (You may want to remove the MERGE_MSG file as well, once you've written your commit with its message. Until then you can use it as the message, or as its starting point.) The git commit step will no longer know to make a merge commit, and will instead make an ordinary commit—et voila, you have made a squash merge.

如果您已经进行了真正的合并提交,该过程可能会更困难.特别是,如果您已发布合并,则现在必须让获得此合并的每个人都将其取回.否则,真正的合并将返回(它们很可能会再次合并),或者为它们创建问题,或者甚至同时出现这两个问题.但是,如果可以这样做,或者发布了 ,则只需将合并推开",然后进行伪合并提交即可.

If you have already made a real merge commit, the process may be harder. In particular, if you have published the merge, you must now make everyone who has obtained this merge, take it back. Otherwise the real merge will return (they are likely to merge it again), or create problems for them, or even both. If you can do that, though, or if it has not been published, you merely need to "shove the merge aside", as it were, and put in a fake merge commit.

让我们重画一下我们所拥有的,以腾出一些空间.这是和以前一样的图,只是分布在更多行上:

Let's redraw what we have, to make some room. This is the same graph as before, just spread out onto more lines:

...--o--*----o----o 
         \         \
          \         o   <-- main
           \       /
            o--o--o   <-- feature

如果我们可以将main返回顶部,然后重新提交,该怎么办?好吧,我们将得到以下图形:

What if we could move main back up to the top line, then make a new commit? Well, we would get the following drawing:

...--o--*----o----o--o   <-- main
         \         \
          \         o   [abandoned]
           \       /
            o--o--o   <-- feature

请注意,所有箭头(包括该提交用于记录历史的内部提交箭头(此处未显示,因为它们在文本图形中很难生成)在此未显示)指向向左,因此在任何顶行提交中,都不知道其下的任何提交:这些都是正确的".只有最重要的提交以及我们将要放弃的那些,才知道有关最重要的提交的任何信息.

Note that all the arrows—including the internal commit arrows that the commits use to record history (not shown here as they're too hard to produce in text drawings)—point leftward, so there's no knowledge, in any of the top-line commits, of any of the commits below them: those are all "to their right". Only the bottom-line commits, plus the one we're going to abandon, know anything about the top-line commits.

此外,如果我们要完全放弃中线提交,我们就停止绘制它,以及它的两个合并箭头:

Moreover, if we're going to abandon the middle-line commit entirely, let's just stop drawing it, and its two merge arrows:

...--o--*----o----o--o   <-- main
         \
          \
           \
            o--o--o   <-- feature

然后看看:我们已经为那些假壁球合并"之一绘制了所需的提交图.只要我们在新提交中获得正确的内容,这就是我们想要的.

And look at that: we've just drawn the kind of commit graph we want for one of those fake squash "merge"s. This is just what we want, as long as we get the right contents in our new commit.

但是-这是一种练习;看看在跳入之前您是否知道答案-新提交的内容从何而来?答案在第一个要点上方以粗体显示. (如果忘记了,请返回并检查.)

But—this is a sort of an exercise; see if you know the answer before plunging on—where do the contents of a new commit come from? The answer is above, in boldface, in the first bullet point. (If you've forgotten it, go back and check.)

现在您知道git commit使用索引进行新的提交,让我们考虑一下我们现在拥有的真正的合并提交.它有内容. (所有提交都有内容.)它们来自何处?他们来自索引!如果我们能以某种方式将那些内容重新回到索引中,那我们就很成功了.实际上,我们可以:我们要做的就是检出并使用git checkout进行提交,或者已经将其作为当前提交.

Now that you know that git commit uses the index to make the new commit, let's consider the real merge commit we have now. It has contents. (All commits have contents.) Where did they come from? They came from the index! If we can just somehow get those contents back into the index, we're golden. And in fact, we can: all we have to do is check out that commit, with git checkout, or already have it as the current commit.

由于我们刚刚进行了新的合并提交,因此我们已经将合并提交作为当前提交.索引是干净的-如果我们运行git status,则表明没有要提交的内容.因此,现在我们使用git reset --soft将分支指针main重置 一步,以获取我们的中间图形. --soft参数告诉Git:移动分支指针,但不要更改索引和工作树."因此,我们仍将具有原始合并的索引(和工作树).现在,我们只需运行git commit进行普通的提交,提供一些适当的提交消息,就可以完成了:我们有一个壁球合并.现在原始的合并已被放弃,最终(默认情况下为30天后的某个时间),Git会注意到该合并已不再使用并将删除.

Since we just made the new merge commit, we already have the merge commit as the current commit. The index is clean—if we run git status, it says there's nothing to commit. So now we use git reset --soft to reset the branch pointer main back one step, to get our intermediate drawing. The --soft argument tells Git: "move the branch pointer, but don't change the index and work-tree." So we'll still have the index (and work-tree) from the original merge. Now we just run git commit to make an ordinary commit, supply some appropriate commit message, and we're done: we have a squash merge. The original merge is now abandoned, and eventually—some time after 30 days, by default—Git will notice that it's no longer used and will remove it.

(要返回上一步,可以使用HEAD~1HEAD^;两者含义相同.因此,命令序列仅为git reset --soft HEAD^ && git commit,假设当前提交是您希望替换为的真正合并.虚假合并.)

(To move back one step, you can use HEAD~1 or HEAD^; both mean the same thing. Hence the command sequence is just git reset --soft HEAD^ && git commit, assuming the current commit is the real merge you wish to replace with a fake merge.)

即使您进行了多次合并提交,也可以使用上述较长的方法 .但是,您必须决定是要进行多个伪合并还是一次大型伪合并.例如,假设您有以下内容:

The longer method above can be used even if you made multiple merge commits. You will have to decide, though, whether you want multiple fake merges, or one big fake merge. For instance, suppose you have this:

...--o--o---o--o--o   <-- develop
      \  \    /  /
       \  o--o  /   <-- feature1
        \      /
         o----o   <-- feature2

您是否希望最终图片看起来像这样?

Do you want your final picture to look like:

...--o--o---o--o--o   <-- develop
      \  \
       \  o--o    <-- feature1
        \
         o----o   <-- feature2

develop上最后提交的两次是两个假(南瓜)合并,还是只想要一个大的假南瓜:

where the last two commits on develop are the two fake (squash) merges, or do you just want one big fake-squash:

...--o--o---o--o   <-- develop
      \  \
       \  o--o    <-- feature1
        \
         o----o   <-- feature2

最后一次提交在哪里是最终结果?如果要两次假南瓜,则需要将两个原始合并提交保留足够长的时间,以使它们进入索引并从中进行两次普通提交.如果您只想要最后合并的一个大假南瓜,那只需要两个Git命令:

where that last commit is the final result? If you want two fake-squashes, you will need to retain both original merge commits long enough to get them into the index and make two ordinary commits from those. If you just want one big fake-squash of the final merge, that takes only two Git commands:

$ git reset --soft HEAD~2 && git commit

因为我们将保留 second 合并的索引,所以后退两个步骤,然后进行新的提交,以将两个搁置一旁.

since we'll retain the second merge's index, move back two steps, then make the new commit that shoves aside both merges.

同样,重要的是,没有任何 real 合并已经发布过,或者,如果发布了,请说服所有选择它们的人停止使用它们.

Again, it's important that none of the real merges have been published—or, if they are published, that you convince everyone else who has picked them up, to stop using them.

这篇关于Git:如何将现有的“合并"转换为“合并-壁球"?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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