I've been using Git for about 15 years, and I
thought I knew what a merge conflict was. Then I
tried to make this video explaining merge conflicts, and I
realized I have no idea what I'm doing. Which is
great because merge conflicts have always been confusing for me,
and now I understand why. So in this video we're
going to use the new merge editor experience in Visual
Studio code, and I'm going to show you why you
might not fully understand what a merge conflict is, and
together we're going to become masters of the merge.
You've seen this before. You've got the current change, which
is your commit, and you've got the incoming change, which
is the other person's commit, and that's the merge conflict,
right? Well, yes, but that's actually only part of the
bigger picture. There's actually a third commit in the mix
here that's called the base, and it is the common
ancestor of both your change and the change that you
have a conflict with. Now, this made zero sense to
me the first time I heard it, so.
Let's look at a little demonstration. Let's say you have
a main branch. Each time you change the main branch,
you make a commit.
Now let's say at this point you decide to make
a feature branch to do some other work. That branch
is created from the main branch at this commit, right
here, commit #2.
While you are working on your feature branch, let's assume
that other people make changes to the main branch with
commits 3 and four.
And some of those changes are on the same lines
that you changed in your feature branch. The common ancestor
or base between your branch and the current commit on
the main branch is commit #2. Now, why does this
matter? When you try and merge your feature branch back
into main, Git compares your commit with the base. That's
how it knows that you've changed something. It also compares
the current
commit on main, which is the incoming change with the
base, and it sees that your feature branch and the
current commit both changed the same line and that's how
you end up with a merge conflict. OK, let's see
how this plays out in real life using Visual Studio
Code. By default, VS Code displays a merge conflict as two
sets of changes in the same file. You can use
the buttons to accept your change except the incoming change.
Or just take both changes and resolve it yourself. In
this experience, you don't actually see the base that Git
is using for the comparison. It hides all that from
you, so all you see are your changes and the
incoming changes. This experience works very well when you know
exactly what you want. For instance, in this case we
can see that in one change they're single quotes, and
in the other change there's backticks. We can easily discern
that we want either single quotes or back ticks, and
accept that change. In this second
change, it's not that simple. There's a lot of changes
here, and they look very similar, and it's very hard
to reason about which change we want to keep, which
change we don't want to keep, and which change we
want to combine. And maybe we don't want any of
these changes, but we don't know, because we can't actually
see the base. We only have part of the picture.
We only know what the changes are, not what the
actual state was that we changed from. And This is
why merge conflicts.
Are so frustrating. So VS Code has introduced a brand new
merge editor experience where you can actually see the base.
You can toggle into the new merge editor experience by
clicking the resolve in merge Editor button. When you click
this button, you'll see that you now have 3 views
here your changes which are also called current. The changes
that you're in conflict with also called incoming and a
preview panel down at the bottom with the new merge
editor.
You use the top two panes to resolve conflicts, building
up a result that you can preview and edit before you
accept the merge. The result pane here is what actually
gets staged on your disk after the merge is completed.
OK, but what about the base? The base can be
toggled on and off from the ellipses menu. Here you can
either display it at the top or in the middle
of the current and incoming changes. If you put your
mouse on the right hand side you can see how.
The current changes differ from the base. In this case
I added a log statement, I changed the numbering on
the parameters, I change the log statement at the bottom
and I deleted one log statement.
And if we put the cursor on the incoming changes,
we can see how those differ from the base. The
params are the same here, but a log statement was
added, one was changed and one was deleted down below
here. Now if this is too busy for you, you
could also just compare the incoming and base by selecting
compare with base and you'll see just your changes in
the base. The base appears on the left and your
changes appear on the right. You can do the same
thing with the incoming changes.
So what do we actually want here? Well, this is
a contrived example, but if we look at this first
conflict, let's say that yes, we do want this log
statement.
And although the way we're setting the parameters here is
ultimately the same outcome, we like the way we're doing
it in the current change. So in this case it's
pretty simple, we just select accept current. But then let's
say we decide actually we do want this extra log
statement from the incoming change. In that case we can
revert this and say remove current in the preview. And
since we want both log statements, we can click accept
combination on either the left or the right when we
do this.
VS Code will smartly combine the changes for us.
If you don't see the accept combination, that's because VS Code
cannot automatically combine your changes.
And this preview area here is editable. So let's say
we accept the combination and then we decide that we
do not want this extra logging statement. Well, we can
just remove it right here in the preview. Now notice
the second conflict here. In this case, we might decide
that the way that the base is doing this is
actually what we want.
Neither my changes or the incoming changes are better than
what's in the base.
Because our preview shows the base by default, we can
just ignore both the current and incoming changes, which just
gives us what's in the base already.
When we do that, our conflicts remaining counter goes to
0, so we know that we've handled all of the
conflicts. In the case that you want to just accept
all of the changes from either the left or the
right, you can click the ellipses at the top and select
accept all changes from left or accept all changes from
right. When you've handled all the conflicts, click the complete
merge button to stage the file.
And if you want to go back to the inline
experience, it's in the original file, so you can just
close this and reopen that file.
Note that if you save as you go, or if
you have autosave on, you'll need to undo that in
order to get back to the original merge state, which
is the command or control.
And as a final tip, if you just want to
cancel a merge all together at any point, you can
choose abort merge from the command palette. I hope this
video has given you greater insight into what a merge
conflict actually is, because if you can understand the concept
of the base, you'll find that merge conflicts are much
less frustrating and they make a whole lot more sense
in the new merge editor experience in Visual Studio code.
By the way, this interface where you have the current
base and incoming changes.
It's also called a 3 way merge. And now you
know what that is and you can brag about it
at parties if that's the kind of thing that you
talk about at the parties that you go to. Hey,
if you like this video, like this video, and subscribe
and check out some of these other videos, you'll love
them, guaranteed.