简体   繁体   English

为什么git无冲突合并仍会产生合并提交?

[英]Why does git merge with no conflicts still produce a merge commit?

I'm trying to understand why git will produce a commit with a unique SHA1 for a merge between two branches with no conflicts. 我试图理解为什么git会生成一个具有唯一SHA1的提交,以便在两个分支之间合并而没有冲突。 Is storing the information that there were no conflicts really worth the extra clutter to the revision history? 存储没有冲突的信息真的值得使修订历史更加混乱吗?

The short answer is that a commit records a specific state of your working directory, and a merge will create a state that doesn't match either of the parents, so a new commit is required, as opposed to just moving a branch pointer. 简短的答案是,提交记录了工作目录的特定状态,而合并将创建一个与父目录都不匹配的状态,因此需要一个新的提交,而不是仅移动分支指针。 To elaborate a bit more, though, it helps to understand exactly what it means to merge two branches. 不过,要详细说明,它有助于准确理解合并两个分支的含义。

The two most common ways git merge is used are to a) catch up a local branch to a remote branch and b) actually merge two separate branches together. git merge的两种最常用的方式是:a)将本地分支追赶到远程分支,以及b)实际上将两个单独的分支合并在一起。

In the first case, you have something like this: 在第一种情况下,您将具有以下内容:

A--B--C--D--E--F--G
      ^           ^
      |           +-- origin/master
      +-- master

In this case, git merge origin/master simply moves the pointer for master to the new location. 在这种情况下, git merge origin/master简单移动指针master到新的位置。 This is what's called a "fast forward" merge, and usually does not result in a new commit (although you can explicitly request one if you have a reason to). 这就是所谓的“快进”合并,通常不会导致新的提交(尽管如果有理由您可以显式地请求一个)。

On the other hand, if you have something like this: 另一方面,如果您有这样的事情:

A--B--C--D--E--F--G <-- branch1
      \
       J--K--L--M--N <-- branch2

and you are on branch2 and want to merge in branch1 , simply moving your branch2 pointer would not make sense. 并且您在branch2并且想要在branch1合并,仅移动branch2指针是没有意义的。 If you just moved branch2 to point to G , you would lose the changes made in J through N . 如果仅将branch2移至指向G ,则将丢失在JN所做的更改。 In this case git merge must create a new commit, where the resulting working tree would look like you took a copy of C (the merge base of G and N , which you can see by running git merge-base branch1 branch2 ), and then applied all of the changes in D through G and in J through N . 在这种情况下, git merge必须创建一个新的提交,结果工作树看起来像是您获取了C的副本( GN的合并基础,您可以通过运行git merge-base branch1 branch2看到)。应用了DGJN所有更改。 Even if this results in no conflicts (the two branches either modified different sets of files or at least just different areas of the files), the resulting working directory state does not match either G or N , so a new commit is required. 即使这不会导致冲突(两个分支修改了不同的文件集或至少只是文件的不同区域),生成的工作目录状态也不匹配GN ,因此需要新的提交。 So git will create a new commit with a working directory that contains the results of applying the changes from both branches, and will mark that commit as having both G and N as parents. 因此, git将使用工作目录创建一个新的提交,该工作目录包含应用来自两个分支的更改的结果,并将该提交标记为同时具有GN作为父项。

So, it's not really "storing the information that there were no conflicts", it's storing a new unique state of your project. 因此,它并不是真正地“存储没有冲突的信息”,而是存储了项目的新的唯一状态。 Sure, you can look at that commit with git log , git diff , etc. and see that either there were or weren't conflicts, but that's not the reason the new commit was made. 当然,您可以使用git loggit diff等查看该提交,并查看是否存在冲突,但这不是进行新提交的原因。

The point of a merge commit is to include two different trees of commits in the parent of the resulting HEAD. 合并提交的要点是在生成的HEAD的父级中包括两个不同的提交树。

There are three possible ways to perform a merge: 有三种执行合并的方法:

  1. Create a merge commit with a separate parent for each thing you merged. 为您合并的每件事创建一个具有单独父项的合并提交。
    This means that all of the original commits from the branch you merged will still show up in your history as a separate parallel track. 这意味着来自合并分支的所有原始提交仍将在历史记录中显示为单独的并行轨道。

  2. Create a fake merge commit with only one parent which contains all of the merged changes. 仅与一个包含所有合并更改的父级一起创建伪合并提交。
    This results in a simpler history (no separate tree), but it loses all of the commits of the merged branch. 这样可以简化历史记录(没有单独的树),但是会丢失合并分支的所有提交。
    This is a bad idea. 这是一个坏主意。

  3. Create new commits for the merged changes which have the original branch as their parents. 为合并的更改创建新的提交,这些更改以原始分支为父级。
    This results in a linear history with both sets of commits interspersed. 这将导致线性历史记录,其中散布着两组提交。
    This is what git rebase does. 这就是git rebase所做的。

Is storing the information that there were no conflicts really worth the extra clutter to the revision history? 存储没有冲突的信息真的值得使修订历史更加混乱吗?

You want git rebase , which does this. 您想要git rebase ,这样做。 One simple way to think of using rebase to merge and avoid a merge commit is, "pretend that work happened first instead of in parallel." 考虑使用rebase进行合并并避免合并提交的一种简单方法是,“假装工作先发生而不是并行发生”。

Rebase can be dangerous to "play with" before git makes sense to you as elegant, but it does solve the problem you have intuited. 在git对您来说不那么优雅之前,Rebase可能会很危险,但是它确实解决了您直觉的问题。 In general rebase is used when the "raw" commit history does not make sense to you. 通常,当“原始”提交历史记录对您没有意义时,将使用rebase。 In the case of a merge, it's used when one of the parent histories you think is irrelevant and should never have really happened (it "rewrites history"). 在合并的情况下,当您认为其中一个父历史记录是无关紧要的,并且不应该真正发生时使用它(它“重写历史记录”)。 In the case of some major merges, you really do want both histories; 在一些主要的合并的情况下,你真的想同时历史; in others, you don't. 在其他人中,你没有。 I'm omitting a fuller explanation as you can find many online, some better than others. 我省略了更完整的解释,因为您可以在网上找到很多,有些比其他的要好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM