[英]Git multiple merge branches- how to avoid multiple commit while merging to master branch
My Git repository looks like below:我的 Git 存储库如下所示:
I have created 2 branches- Branch_1 & Branch_2.我创建了 2 个分支 - Branch_1 和 Branch_2。 Now finally I am ready to merge this Branch_2 into Master branch.
现在终于准备好将这个 Branch_2 合并到 Master 分支中。 But when I did merge it showed all the commits for Branch_1 & Branch_2 because of multiple merge in between.
但是当我合并时,它显示了 Branch_1 和 Branch_2 的所有提交,因为它们之间有多次合并。 Can anyone suggest how to proceed in this case to have a single commit before merging my code to master branch?
谁能建议在这种情况下如何在将我的代码合并到主分支之前进行一次提交?
git log --oneline --graph --color --all --decorate
* 36dbb26 (origin/Branch_2) changed abc
* 1a7bf25 changed T
* 110095a changed Z
* 1087d5d Merge remote-tracking branch 'origin/Branch_1' into Branch_2
|\
| * 8c9d02a (origin/Branch_1) sleep added between each processing to discover partitions
| * ca401cb changed S
| * 20a4edd changed R
* 3f472ef install package
* 1087d5d Merge remote-tracking branch 'origin/Branch_1' into Branch_2
|\
| * 8c9d02a (origin/Branch_1) adding y
| * ca401cb changed g
| * 97c326d changed f
* | fd543bf changed c
* | 7b24330 (HEAD -> master, origin/master, origin/HEAD) fix D
* | 53aecb4 adding x
|/
* 49d3bda changed e
| * 213ea18 (origin/Feature_branch) changed d
| * 0b3b675 changed c
|/
* df6ac90 Adding c
* 96699ff Adding b
* 99f165f Adding a
I want end result like below:(all the commits from fd543bf to be merged into 1 commit)我希望最终结果如下:(来自 fd543bf 的所有提交都合并到 1 个提交中)
* 36dbb26 (HEAD -> master, origin/Branch_2) changed R-All consolidated
* 7b24330 (origin/master) Fix D
You probably just want git log --first-parent
.您可能只想要
git log --first-parent
。
... But when I did merge it showed all the commits for Branch_1 & Branch_2 because of multiple merge in between.
...但是当我合并时,它显示了 Branch_1 和 Branch_2 的所有提交,因为它们之间有多次合并。
No, that's not why.不,这不是为什么。 The reason you see all these commits is because you do in fact have all these commits.
您看到所有这些提交的原因是因为您实际上拥有所有这些提交。
The thing to understand here is that, in the end, Git is all about commits .这里要理解的是,最后,Git都是关于 commits的。 Commits are the unit of storage in Git.
提交是 Git 中的存储单元。 1 Commits are what you have, and they are what you want.
1提交是您所拥有的,也是您想要的。 If you don't want these commits, what you want must be some other commits.
如果您不想要这些提交,那么您想要的必须是其他一些提交。 Commits are all you get, so you'd better want commits.
提交就是你得到的全部,所以你最好想要提交。 (If you want something else, don't use Git. But many other version control systems are commit-oriented as well, so you may find that you still get commits anyway, so you might as well stick with Git, unless... well, read the next paragraph.)
(如果你想要别的东西,不要使用 Git。但是许多其他版本控制系统也是面向提交的,所以你可能会发现你仍然得到提交,所以你最好坚持使用 Git,除非......好吧,阅读下一段。)
Branch names, in Git, exist for one primary reason: to find commits. Git 中的分支名称存在一个主要原因:查找提交。 This is where Git differs from other version control systems.
这是 Git 与其他版本控制系统不同的地方。 In many version control systems, a branch is a container for commits, and you can inspect commits by inspecting branches: the set of commits contained in the branch is the set of commits you'll see, if that's how you ask.
在许多版本控制系统中,分支是提交的容器,您可以通过检查分支来检查提交:分支中包含的提交集就是您将看到的提交集,如果您这样问的话。 But that's not how branch names work in Git.
但这不是 Git 中分支名称的工作方式。
In Git, a commit can be—and often is—on many, or perhaps even all , branches at the same time .在 Git 中,提交可以——而且经常是——同时出现在许多甚至所有分支上。 That's because Git's branch names are not containers.
那是因为 Git 的分支名称不是容器。 They do not hold commits.
他们不持有提交。 They simply let you find commits.
他们只是让你找到提交。 Each name finds one commit.
每个名称都会找到一个提交。 It's the commits themselves that find the rest of the commits.
是提交本身找到提交的rest 。
Each Git commit is made up of two parts, which we'll describe later.每个 Git 提交由两部分组成,我们将在后面介绍。 Each commit is found by its unique hash ID.
每个提交都通过其唯一的 hash ID找到。 Each commit has one of these hash IDs;
每个提交都有这些 hash ID 之一; that hash ID is the "true name", as it were, of the commit.
hash ID 是提交的“真实姓名”。 Without the hash ID, Git literally can't find the commit.
如果没有 hash ID,Git 根本找不到提交。 2 So a branch name holds one hash ID, which is, by definition, the last commit that is contained within that branch.
2因此,分支名称包含一个 hash ID,根据定义,它是该分支中包含的最后一次提交。 That commit in turn holds some set of hash IDs—usually just one—of earlier commits that are also part of the branch .
该提交又包含一组 hash ID(通常只是一个),这些 ID 也是早期提交的一部分,它们也是分支的一部分。
When we have a branch name, like main
or feature
, that holds some hash ID, we say that the branch name points to the last, or tip , commit of the branch:当我们有一个分支名称,如
main
或feature
,它包含一些 hash ID,我们说分支名称指向分支的最后一个或tip提交:
<-H <--feature
But commit H
— H
here stands in for the real hash ID, whatever it is—has the hash ID of some earlier commit G
.但是这里的提交
H
- H
代表真正的 hash ID,不管它是什么——具有一些早期提交G
的 hash ID。 So we say that H
points to G
:所以我们说
H
指向G
:
<-G <-H <--feature
But commit G
also points backwards to a still-earlier commit:但是提交
G
还指向更早的提交:
... <-F <-G <-H <-- feature
and so on, all the way back to the very first commit ever.依此类推,一直回到有史以来的第一次提交。 This one literally can't point backwards to an earlier commit, so it just doesn't, and that's where Git stops working backwards.
这个实际上不能指向更早的提交,所以它只是没有,这就是 Git 停止向后工作的地方。
So, this is what it means for a commit to be on a branch: we start with the branch name, which automatically determines the last commit on that branch, and then work backwards.所以,这就是提交在分支上的含义:我们从分支名称开始,它会自动确定该分支上的最后一次提交,然后向后工作。 But if that's the case... well, suppose we have something like this, where commit
I
points back to H
, and commit K
also points back to H
:但如果是这样的话......好吧,假设我们有这样的事情,其中提交
I
指向H
,并且提交K
也指向H
:
I--J <-- br1
/
...--G--H
\
K--L <-- br2
Which branch holds commit H
?哪个分支持有提交
H
?
Git's answer is that commit H
is now on both branches at the same time. Git 的回答是提交
H
现在同时在两个分支上。 So are all earlier commits.所有早期的提交也是如此。 Furthermore, even if
H
is the last commit on some third branch:此外,即使
H
是第三个分支上的最后一次提交:
I--J <-- br1
/
...--G--H <-- main
\
K--L <-- br2
this is still the case.现在仍然如此。 Commit
H
is now on all three branches .提交
H
现在在所有三个分支上。
Hence, in Git, the set of branches that contain some commit is dynamic and fluid.因此,在 Git中,包含一些提交的分支集是动态和流动的。 What matters is not the branch names, but the connections from commit to commit.
重要的不是分支名称,而是从提交到提交的连接。 The branch names are useful, but only to get you started .
分支名称很有用,但只是为了让您入门。 Everything else is all about the commits .
其他一切都与提交有关。
1 Because commits are made up of smaller parts, it is possible to work at a lower level. 1由于提交由较小的部分组成,因此可以在较低级别上工作。 But this is roughly analogous to breaking molecules, like salt, into atoms—sodium metal and chlorine—or even subatomic particles like protons, neutrons, and electrons.
但这大致类似于将盐等分子分解成原子——钠金属和氯——甚至是质子、中子和电子等亚原子粒子。 Once you break them up like this, they're not useful any more, not in the way that the salt is anyway.
一旦你像这样分解它们,它们就不再有用了,无论如何都不是盐的方式。 You can't season your food with sodium metal, nor with chlorine, and especially not with neutrons.
你不能用金属钠或氯来调味你的食物,尤其是不能用中子。
2 There are some maintenance commands— git fsck
and git gc
in particular—that simply look over every commit in the repository and figure out which ones connect to which other ones and so on. 2有一些维护命令——特别是
git fsck
和git gc
只需查看存储库中的每个提交,并找出哪些提交连接到其他提交等等。 This is very slow, so it's not the way you use Git in day-to-day operation.这很慢,所以这不是您在日常操作中使用 Git 的方式。 In a bigger repository like the Linux kernel, a
git checkout
or git log
will take up to a few seconds sometimes, but a git fsck
or git gc
could take many minutes. In a bigger repository like the Linux kernel, a
git checkout
or git log
will take up to a few seconds sometimes, but a git fsck
or git gc
could take many minutes. Some of this depends on the speed of your computer and its file systems and so on, but the contrast is pretty clear: finding a commit by hash ID is fast , but finding it any other way is usually excruciatingly slow.其中一些取决于您的计算机及其文件系统的速度等,但对比非常明显:通过 hash ID 查找提交很快,但以任何其他方式查找它通常非常慢。
We mentioned above that each commit has two parts.我们在上面提到每个提交都有两个部分。 These are:
这些是:
the main data, a snapshot .主要数据,快照。 Here, Git saves, for all time, 3 a read-only snapshot of every file's name and content as of the time you, or whoever, made the commit.
在这里,Git 一直保存3每个文件名称和内容的只读快照,该快照是您或任何人进行提交时的时间。 This allows you—or anyone else—to get back all those files as of that snapshot.
这使您(或其他任何人)可以取回该快照的所有这些文件。
the metadata .元数据。 Here, Git saves the name and email address of the person who made the commit.
在这里,Git 保存了提交人的姓名和 email 地址。 Git saves a date-and-time-stamp for when they made the commit.
Git 保存了他们提交时的日期和时间戳。 (Git actually has two name-and-address-and-time fields per commit, here, though most people normally only look at one.) Git lets you add a description—a log message —explaining why you made this commit, if you like.
( Git实际上每次提交都有两个名称和地址和时间字段,尽管大多数人通常只看一个。)喜欢。 And, key for Git itself, this is also where Git stores those earlier-commit hash IDs.
而且,Git 本身的关键,这也是 Git 存储那些早先提交的 hash ID 的地方。 Git keeps a list of such hash IDs.
Git 保留了此类 hash ID 的列表。 Most commits just have that one entry, which tells Git what the parent of the commit is.
大多数提交只有一个条目,它告诉 Git 提交的父级是什么。
It's the parent in the metadata that lets Git show you a commit—which is a snapshot, not a set of changes— as a set of changes.它是元数据中的父项,它让 Git 向您显示一个提交——它是一个快照,而不是一组更改——作为一组更改。 If we have two commits in a row:
如果我们连续两次提交:
... <-F <-G ...
and we take the snapshots out of both F
(the parent) and G
(the child) and compare them, whatever is the same is not changed, and whatever isn't the same... well, comparing those will tell you what changed.我们从
F
(父)和G
(子)中取出快照并比较它们,相同的部分没有改变,而不同的部分......嗯,比较它们会告诉你发生了什么变化. So that's what Git shows: the changes .这就是 Git 显示的内容:更改。 But to get those changes, Git needed two commits, to get the two snapshots.
但要获得这些更改,Git 需要两次提交,以获得两个快照。
3 While no part of any commit can ever change , not all commits have to last forever, so saying for all time is an overstatement. 3虽然任何提交的任何部分都不能改变,但并非所有提交都必须永远持续下去,所以说永远都是夸大其词。 Given a commit's hash ID, if Git can find that commit, that commit is that commit .
给定提交的 hash ID,如果 Git 可以找到该提交,则该提交就是该提交。 It's not any other commit.
这不是任何其他提交。 It must be the commit that had that hash ID the last time you looked.
它必须是您上次查看时具有该 hash ID 的提交。 In other words, the commit is still there, so it's unchanged, and its files are still the same way they were then.
换句话说,提交仍然存在,所以它没有改变,它的文件仍然是它们当时的样子。
You can, however, get Git to delete a commit.但是,您可以让 Git删除提交。 It's not easy: Git is built to add new commits while keeping existing commits, and most of the normal everyday commands you use work this way.
这并不容易:Git 旨在在保留现有提交的同时添加新提交,您使用的大多数日常命令都以这种方式工作。 But you can, with some effort, make some commits hard to find .
但是您可以通过一些努力使一些提交变得难以找到。 Once you do this, and leave them un-find-able (except by maintenance commands) long enough, Git will eventually decide that they must be unwanted trash, and throw them out for real.
一旦你这样做了,并且让它们无法找到(维护命令除外)足够长的时间,Git 最终将决定它们一定是不需要的垃圾,并将它们真正扔掉。 The
git gc
maintenance command in particular does this. git gc
维护命令特别执行此操作。 Once that happens, if you have saved the hash ID somewhere else—written it on a whiteboard for instance—and type it in correctly, Git will say I don't have anything with that ID .一旦发生这种情况,如果您将 hash ID 保存在其他地方(例如,将其写在白板上)并正确输入,Git 会说我没有该 ID 的任何内容。
Because Git is built to add commits, and when two Gits connect and have Git-sex, the receiving Git is usually very willing to add all the sending Git's new commits to itself, new commits spread like viruses.因为 Git 是为添加提交而构建的,并且当两个 Git 连接并具有 Git-sex 时,接收 Git 通常非常愿意将所有发送 Git 的新提交添加到自己,新提交像病毒一样传播。 So just because you added, but then retracted, a commit doesn't mean it didn't get out to some other Git.
因此,仅仅因为您添加但随后撤回,提交并不意味着它没有发送到其他 Git。 It may come back to you later:
稍后可能会回复您:
Don't be afraid to make temporary commits, but do remember that if you let other Gits talk to your Git, they might copy your temporary commits, and present them back to you later—so either be careful about which repositories you let your repository have Git-sex with, or be careful about letting sensitive data get into your temporary commits, or both.不要害怕进行临时提交,但请记住,如果您让其他 Git 与您的 Git 交谈,他们可能会复制您的临时提交,并在稍后将它们呈现给您——所以要么小心您让哪些仓库让您的仓库使用 Git-sex,或者小心让敏感数据进入您的临时提交,或者两者兼而有之。
Note, too, that when you use git push
, you choose which commits your Git sends to some other Git, so git push
is safer for you —you choose which commits, including temporary ones, you send—than if you allow all users everywhere to read your repository (and hence read all your temporary commits). Note, too, that when you use
git push
, you choose which commits your Git sends to some other Git, so git push
is safer for you —you choose which commits, including temporary ones, you send—than if you allow all users everywhere读取您的存储库(并因此读取所有临时提交)。
Receiving Gits, of course, have to be pretty careful.当然,接收 Gits 必须非常小心。 That's why hosting sites like GitHub offer access control (which is not something built directly into Git itself, but rather, an add-on).
这就是为什么像 GitHub 这样的托管网站提供访问控制(这不是 Git 本身直接内置的东西,而是一个附加组件)。
When we have divergent work, such as:当我们有不同的工作时,例如:
I--J <-- br1
/
...--G--H
\
K--L <-- br2
we might want to combine the two divergent lines of work.我们可能希望将两条不同的工作线结合起来。 That way, we can get a commit that adds the feature that someone added in
br1
and the feature that someone added in br2
.这样,我们可以得到一个提交,添加某人在
br1
中添加的特性和某人在br2
中添加的特性。 That's what git merge
is meant for.这就是
git merge
的目的。
Now, git merge
, as a command, does not always make a merge commit .现在,
git merge
作为命令,并不总是进行合并提交。 We need to distinguish carefully between the verb form, to merge , meaning to combine work , and the noun or adjective form, a merge or a merge commit , meaning a commit resulting from doing the work-combining:我们需要仔细区分动词形式to merge ,意思是组合 work ,以及名词或形容词形式a merge或merge commit ,意思是完成工作组合的提交:
The verb form, to merge , is what git merge
usually (or at least often) does.动词形式,即 merge ,是
git merge
通常(或至少经常)所做的。
The noun form, a merge , or its adjective equivalent, a merge commit , is what Git usually (or at least often) makes after doing the to merge work.名词形式, a merge ,或其对应的形容词, a merge commit ,是 Git 通常(或至少经常)在完成合并工作后所做的。
So you can see that these are closely related, but not the same thing.所以你可以看到这些是密切相关的,但不是一回事。 One is a process;
一是过程; the other is a result.
另一个是结果。
We won't go into details about how the process works, but when the result of a merge is a merge commit , that merge commit is just like any other commit, except instead of having a single parent, it has two or more .我们不会go详细介绍该过程是如何工作的,但是当合并的结果是一个合并提交时,该合并提交就像任何其他提交一样,除了它有两个或多个. (Most merge commits have exactly two parents; I'll go into the or more part in a later section.) Remember, all commits have their two parts: snapshot, and list-of-parents.
(大多数合并提交恰好有两个父级;我将在后面的部分中将 go 放入或更多部分。)请记住,所有提交都有两个部分:快照和父级列表。 What's special about a merge commit is that its list has two or more parents .
合并提交的特别之处在于它的列表有两个或多个 parent 。
Now, the first parent of any new commit is simply the commit you started with.现在,任何新提交的第一个父级只是您开始的提交。 You run:
你跑:
git checkout br1
Then you do some stuff to make a new commit, and eventually, you run git commit
.然后你做一些事情来做一个新的提交,最终你运行
git commit
。 Git builds a new commit, with a new and unique hash ID, by: Git 使用新的唯一 hash ID 构建新的提交,通过:
That's probably how you got commit J
, for instance: you ran git checkout br1
, which extracted commit I
.这可能就是您获得提交
J
的方式,例如:您运行了git checkout br1
,它提取了提交I
。 You then made a new commit with git commit
.然后,您使用
git commit
进行了新的提交。 The new commit's parent was commit I
, so that J
pointed back to I
, and now the name br1
selected commit J
instead of selecting commit I
.新提交的父级是提交
I
,因此J
指向I
,现在名称br1
选择了提交J
而不是选择提交I
。
When you use git merge
to make the new commit, however, 5 Git doesn't write out a single -parent commit and advance the branch name.但是,当您使用
git merge
进行新提交时, 5 Git 不会写出单父提交并推进分支名称。 This time, Git writes out a multi -parent commit.这一次,Git 写出一个多父提交。 The first parent in the new commit's list of parents is the same as usual, but at least one additional parent goes into the list.
新提交的父级列表中的第一个父级与往常一样,但至少有一个额外的父级进入列表。
The additional parent, in this case, is the commit you selected when you ran git merge
:在这种情况下,附加父级是您在运行
git merge
时选择的提交:
git checkout br1
git merge br2
This causes Git to use commit L
as the other commit.这会导致 Git 使用提交
L
作为另一个提交。 So, after merging the work on the two branches and coming up with an appropriate snapshot, Git now makes new merge commit M
like this:因此,在合并两个分支上的工作并提出适当的快照后,Git 现在像这样进行新的合并提交
M
:
I--J
/ \₁
...--G--H M <-- br1 (HEAD)
\ /²
K--L <-- br2
The (HEAD)
here signifies that we're "on" branch br1
, so that new commit M
is the new tip of branch br1
.这里的
(HEAD)
表示我们“在”分支br1
,因此新提交M
是分支br1
的新提示。 Commit M
has two parents instead of the usual one: the first parent is commit J
, where branch br1
used to point a moment ago.提交
M
有两个父节点,而不是通常的父节点:第一个父节点是提交J
,其中分支br1
曾经指向刚才。 The second parent is commit L
.第二个父母是提交
L
。 The branch name br2
has not changed, so it still points to commit L
.分支名称
br2
没有改变,所以它仍然指向提交L
。
Because M
points to L
as well as to J
, commits KL
are now on branch br1
.因为
M
指向L
以及J
,所以提交KL
现在在分支br1
上。 This is why your git log
shows them: they exist and are on the branch.这就是您的
git log
显示它们的原因:它们存在并且在分支上。 Git finds them by going to commit M
, then going backwards to both commits J
and L
, and from those two, to both commits I
and K
, and from those two, to commit H
. Git 通过提交
M
找到它们,然后返回到提交J
和L
,然后从这两个提交到提交I
和K
,然后从这两个提交H
。 (Of course, Git has to be careful to visit commit H
exactly once, even though there are now two ways to get there. But that's easy enough for Git to do.) (当然,Git 必须小心访问提交
H
一次,即使现在有两种方法可以到达那里。但这对 Git 来说很容易做到。)
4 The snapshot is made from the copies of files that are in Git's index , not from the files you can see and work with. 4快照是由 Git索引中的文件副本制作的,而不是您可以查看和使用的文件。 This is why Git makes you run
git add
so often.这就是为什么 Git 让你经常运行
git add
。
5 If the merge has a merge conflict, the to-merge process will stop in the middle and make you fix the conflict. 5如果merge有合并冲突,to-merge过程会在中间停止,让你修复冲突。 An eventual
git commit
or git merge --continue
will finish the merge and make a merge commit.最终的
git commit
或git merge --continue
将完成合并并进行合并提交。 To achieve that, before stopping in the middle, git merge
writes out this special in the middle of a conflicted merge state.为了实现这一点,在中间停止之前,
git merge
在冲突的合并 state 的中间写出这个特殊的。 The git commit
command checks for this state and finishes the merge, rather than making an ordinary single-parent commit. git commit
命令检查此 state 并完成合并,而不是进行普通的单亲提交。
Since you're griping, to some extent, about having to make multiple merge commits to merge more than one branch, it's time to mention Git's octopus merge .由于您在某种程度上抱怨必须进行多次合并提交才能合并多个分支,因此是时候提及 Git 的octopus merge了。 Suppose we have a "mainline branch" and two or more features that spring from it, perhaps from a single starting point commit or perhaps from multiple starting points:
假设我们有一个“主线分支”和两个或多个 spring 的功能,可能来自单个起点提交,也可能来自多个起点:
o--o--o <-- feature1
/
...--o--o--o <-- main (HEAD)
\
o--o <-- feature2
We can merge the two feature branches one at a time:我们可以一次合并两个特征分支:
o--o--o <-- feature1
/ \
...--o--o---o--M <-- main (HEAD)
\
o--o <-- feature2
and then:接着:
o--o--o <-- feature1
/ \
...--o--o---o--M--N <-- main (HEAD)
\ /
o-----o <-- feature2
There is nothing wrong with this method.这种方法没有任何问题。 It works fine.
它工作正常。 The mainline branch,
main
here, now has two two-parent merge commits M
and N
.主线分支,这里的
main
,现在有两个双父合并提交M
和N
。 The first parent of N
is M
; N
的第一个父节点是M
; the first parent of M
is the commit directly to its left, on the main line. M
的第一个父级是直接在其左侧的提交,在主线上。 The second parent of N
shows how feature2
got merged and the second parent of M
shows how feature1
got merged. N
的第二个父节点显示了feature2
是如何合并的, M
的第二个父节点显示了feature1
是如何合并的。
Git offers the ability—in some cases, because when doing this kind of merge, there's no good way to do merge conflict resolution, so an octopus merge must be conflict-free—to use a single merge commit to get this result: Git 提供了这样的能力——在某些情况下,因为在进行这种合并时,没有很好的方法来解决合并冲突,所以章鱼合并必须是无冲突的——使用单个合并提交来获得这个结果:
o--o--o <-- feature1
/ \
...--o--o--o---M <-- main (HEAD)
\ /
o--o <-- feature2
Commit M
here has three parents instead of just two.这里的提交
M
有三个父母,而不是只有两个。 The first parent is directly behind it on the left as usual.像往常一样,第一个父母就在它的左边。 The second and third parents are the remaining two branch-tip commits from
feature1
and feature2
.第二个和第三个父母是来自
feature1
和feature2
的剩余两个分支提示提交。
We get this by running:我们通过运行得到这个:
git checkout main
git merge feature1 feature2
The fact that we named two commits makes git merge
use the -s octopus
merge strategy , which tries to merge all these commits (using an octopus style merge base algorithm) and which does the merge only if it can do so without conflicts.我们命名了两个提交的事实使得
git merge
使用-s octopus
合并策略,它尝试合并所有这些提交(使用 octopus 风格的合并基础算法)并且只有在没有冲突的情况下才进行合并。 This means there are some merges you could do with two regular two-parent merges that you cannot do with a three-parent octopus;这意味着您可以使用两个常规的双亲合并进行一些合并,而您无法使用三亲章鱼进行合并; but some people like the octopus merges as they tie all the features in at once, and indicate that there were no conflicts (well, probably).
但是有些人喜欢章鱼合并,因为它们一次将所有功能联系在一起,并表明没有冲突(嗯,可能)。 6
6
Note that an octopus merge still results in putting all the commits on the merged-into branch (in this case main
).请注意,章鱼合并仍然会导致将所有提交放在合并到分支上(在本例中为
main
)。 Git simply follows all parents of the merge, when you run git log
, so that you see all the commits that are part of the branch . Git 只是跟随合并的所有父级,当您运行
git log
时,您可以看到属于分支的所有提交。
6 Because Git is a set of tools, rather than a complete solution, it's possible to construct an octopus merge that doesn't actually use git merge
at all, or that went through two regular merges. 6因为 Git 是一组工具,而不是一个完整的解决方案,所以可以构建一个实际上不使用
git merge
的章鱼合并,或者经过两次常规合并。 But don't do that.但不要那样做。 We won't even look at how you could do that.
我们甚至不会看你如何做到这一点。
The git log
walks through commits, one at a time, moving backwards from commits to their parents. git log
遍历提交,一次一个,从提交向后移动到其父级。 Whenever it encounters a merge commit, it has a choice of which commit(s) to move backwards to.每当遇到合并提交时,它都可以选择向后移动到哪个提交。 But it does not insist on showing you every commit, or even moving to every commit reachable in this way.
但它并不坚持向您展示每个提交,甚至不坚持以这种方式移动到每个可访问的提交。 It just defaults to showing every commit.
它只是默认显示每个提交。
You can limit which commits you see, and you can limit which commits git log
will visit in the first place .你可以限制你看到的提交,你可以限制
git log
首先访问的提交。 If you limit the set of commits visited, you automatically limit the commits seen, so this is pretty powerful.如果你限制访问的提交集,你会自动限制看到的提交,所以这是非常强大的。 We won't look at all the gory details here, but rather only at one very useful and important option:
--first-parent
.我们不会在这里查看所有血淋淋的细节,而只会查看一个非常有用且重要的选项:
--first-parent
。
When we use --first-parent
, we are telling Git: Whenever you reach a merge commit, pretend that this merge commit has only a single parent, namely, its first parent.当我们使用
--first-parent
时,我们是在告诉 Git:每当你到达一个合并提交时,假设这个合并提交只有一个父级,即它的第一个父级。 In other words, ignore the merged-in commits entirely, and don't even walk down those paths.换句话说,完全忽略合并的提交,甚至不要沿着这些路径走。 7 If we have:
7如果我们有:
I--J
/ \₁
...--G--H M--N--O--P <-- main (HEAD)
\ /²
K--L
where some merge occurred at point M
, and we run git log
, we'll see commits P
, O
, N
, M
, J
, L
, K
, I
, H
, and so on (with the ones between M
and H
happening in some order).在
M
点发生了一些合并,我们运行git log
,我们将看到提交P
、 O
、 N
、 M
、 J
、 L
、 K
、 I
、 H
等等( M
和H
之间的那些发生在一些命令)。 8 But if we run: 8但是如果我们运行:
git log --first-parent
the walk will pretend that commit M
has only one parent, J
, and we'll visit commits P
, O
, N
, M
, J
, I
, H
, and so on, in that order. walk 会假装提交
M
只有一个父节点J
,我们将按顺序访问提交P
、 O
、 N
、 M
、 J
、 I
、 H
等等。 We never even look at commits KL
, so we never see them.我们甚至从不查看提交
KL
,因此我们从未见过它们。
7 Note that, just like a fork in a road that rejoins later, if you reverse your path—going down the road from your original destination back to your original starting point—what was a join is now a fork, and what was a fork is now a join. 7请注意,就像在稍后重新连接的道路上的分叉一样,如果您反转您的路径 - 从原来的目的地沿着道路回到原来的起点 - 什么是连接现在是一个分叉,而什么是一个分叉现在是加入。 So, since Git works backwards, merges are actually where things branch, and branch points are where things come together.
因此,由于 Git 向后工作,合并实际上是事物分支的地方,而分支点是事物聚集的地方。 It's really all in how you look at it.
这真的取决于你如何看待它。
8 When a merge offers git log
a fork in the graph walk, the actual order in which the commits come out comes from the sorting options you give. 8当合并提供
git log
一个 fork,提交的实际顺序来自您提供的排序选项。 The default sort is to show the highest commit date first.默认排序是首先显示最高提交日期。 If all the computer clocks were accurate when all the commits were made, this shows the commits in the right order, but sometimes one computer's clock is off, and the commits can get weirdly mixed.
如果在进行所有提交时所有计算机时钟都是准确的,则这会以正确的顺序显示提交,但有时一台计算机的时钟是关闭的,并且提交可能会奇怪地混合。 Consider using
git log --graph
to help view the actual commit graph structure, in difficult cases.在困难的情况下,考虑使用
git log --graph
来帮助查看实际的提交图结构。
As I mentioned at the top of this answer, if you don't want these commits, you must want some other commits.正如我在此答案顶部提到的那样,如果您不想要这些提交,则必须要进行其他一些提交。 When I said these commits I was both speaking in general—Git stores commits, so that's all you get—but also in specific.
当我说这些提交时,我既是泛泛地说——Git 存储提交,这就是你得到的全部——但也是具体的。 If you don't want merge commits, don't make merge commits in the first place.
如果您不想要合并提交,请不要首先进行合并提交。 ("Don't start none, won't be none", as they say.)
(正如他们所说,“不要开始,就不会没有”。)
Now, there are some huge disadvantages to this.现在,这有一些巨大的缺点。 If you don't make merge commits, you can't preserve the actual original work you did.
如果您不进行合并提交,则无法保留您所做的实际原始工作。 You do have that choice though.
不过你确实有这个选择。 When you run
git merge
you can use git merge --squash
, for instance.例如,当您运行
git merge
时,您可以使用git merge --squash
。 This tells Git to go through the merging process , but to make an ordinary, non-merge single parent commit at the end.这通过合并过程告诉 Git 到 go ,但要在最后进行普通的非合并单亲提交。 (It also turns on
--no-commit
, for no good reason. 9 ) (它也打开
--no-commit
,没有充分的理由。 9 )
If you do use this method, remember to delete the branch names that find the commits from before the merge action since those commits are now redundant with the (single) squash-merge that does them.如果您确实使用此方法,请记住删除从合并操作之前找到提交的分支名称,因为这些提交现在与执行它们的(单个)squash-merge 是多余的。 If you allow those commits to come back into view later, they are likely to cause trouble.
如果您允许这些提交稍后重新出现,它们可能会造成麻烦。 This is in many ways the same problem as that sort of viral effect of letting temporary or incorrect commits escape to some other Git repository: Git is built to add commits, not to discard them.
这在许多方面与让临时或不正确的提交逃逸到其他一些 Git 存储库的病毒效应相同的问题:Git 的构建是为了添加提交,而不是丢弃它们。 But by doing a squash-merge, which does not leave a merge trace, you set a trap for yourself in the future, unless those now-unwanted commits really disappear forever.
但是通过做一个不会留下合并痕迹的 squash-merge,你在未来为自己设置了一个陷阱,除非那些现在不需要的提交真的永远消失了。
If you have multiple merges to do, and each will have some conflicts to resolve, you can do them as normal (non-squash) merges or squash merges.如果您有多个合并要做,并且每个合并都会有一些冲突需要解决,您可以将它们作为正常(非壁球)合并或壁球合并进行。 The result will be multiple commits: either multiple merge commits, or multiple ordinary single-parent commits.
结果将是多个提交:多个合并提交,或多个普通的单父提交。 You can, after doing either of these, then use
git reset --soft
to make the new merge-or-not-merge commits hard to find , and then use a plain git commit
to make a new, single, ordinary commit that has the same snapshot as the final merge.您可以在完成其中任何一项之后,然后使用
git reset --soft
使新的合并或不合并提交难以找到,然后使用普通的git commit
进行新的单一普通提交与最终合并相同的快照。 As with git merge --squash
, you should in general now consider the merged branches "dead" and you should get rid of those commits and pretend they never existed and hope they never come back to haunt you.与
git merge --squash
,您现在通常应该认为合并的分支“已死”,您应该摆脱这些提交并假装它们从未存在并希望它们永远不会回来困扰您。
This is not a wrong thing to do, but it requires understanding of what you're doing.这不是一件错误的事情,但它需要了解你在做什么。 Do it only if you understand the consequences.
只有在您了解后果的情况下才这样做。
9 The implied -n
is almost certainly just a leftover from the original shell script implementation, carefully preserved for all time in Git's behavior. 9隐含的
-n
几乎可以肯定只是原始 shell 脚本实现的遗留物,在 Git 的行为中一直小心保存。 It's annoying since if you want this behavior, you can use git merge -n --squash
.这很烦人,因为如果你想要这种行为,你可以使用
git merge -n --squash
。 Right now that's redundant, though.不过,现在这是多余的。
To squash everything in one commit: call git reset --soft
followed by git commit
:要在一次提交中压缩所有内容:调用
git reset --soft
后跟git commit
:
# from Branch_2 :
git reset --soft master
git commit
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.