In this video I’ll be covering everything
you need to know about merge and rebase, and when and why you’d want
to use one over the other We've got animations, we've got live coding,
we've got common merge & rebase scenarios, essentially we've got your
definitive guide right here . Let's start first with merging
in this merge vs rebase showdown. Say, you're working on a project, and
you split off from mainline to work on a feature branch. Maybe a coworker also
pushes changes to mainline in the meantime. Now once that feature is complete and tested I’m
ready to merge it back to my mainline branch. To do this, I’ll hop over into my terminal and
make sure I have my main branch selected. Then I’ll run "git merge feature_branch" to tell
git to bring that branch over onto my currently selected branch which is main. Git will pop
open my default terminal text editor - by default that is vim - and allow me to add a
descriptive message to that merge commit. I can either press “I” to enter insert mode
and type my message. But I’ll leave it as it is for now. Then I’m going to type "esq : wq",
and enter to save the default merge message. The result is what's called a 3-way merge, because
not only are we merging our branch with mainline, we're taking into account any changes
that were introduced in the meantime. A 3-way merge results in a merge commit that
weaves together the tips of both branches: here and here, and takes into
account their common ancestor, here, to resolve any conflicts between the two
branch tips. 1, 2, 3, that's a 3-way merge. This example is probably the one you’re most
familiar with if you’ve used merge before, but just like rebase, at it’s core merge’s job is to
integrate changes from one branch to another, and that doesn’t always have to be one directional. If
I rewind here, let’s say I’d like to incorporate the changes introduced but that new commit into
my feature branch without actually merging it back onto mainline. This would be useful if you
wanted to make sure your work was compatible with the latest changes, but I still had more
work to do on my feature before moving it over. This time instead of checking out my main branch,
I’ll go ahead and check out my feature branch. Next I’ll run “git merge main”. Git will pop
open that default terminal text editor, and I can optionally provide a merge commit message. I’ll
run "esq : wq", just like before, hit enter, and the result will be that same 3-way merge commit but placed on the tip of my feature branch instead of the tip of main. With that, my feature branch has integrated
the latest changes, but still remains functionally separate from that shared mainline branch. A
quick note about this workflow, every time new changes are pushed and integrated, another merge
commit will be created. Plus once you’re ready to merge that feature, another merge commit will be
created on main, on top of all those other ones. Now this looks a little messy, but one thing to point out with merge
is we’re always flowing forward in time. We’re taking into account what happened in
the past and adding to it, but we’re never changing history. This non-destructive nature
is a benefit of merge, but is it always a positive? Hold onto that thought because I
have a feeling rebase can help with that. But what if no changes were introduced to that
shared main branch as we were developing our feature branch? Well instead of a 3-way merge,
we’d get what’s called a fast-forward merge. Fast-forward merges happen when the feature branch
is already anchored off the latest changes. You still have to take into account the tips of both
branches (here and here), but since the branch is built off of the latest commit there is sequential
history from the tip of the main branch all the way to the tip of the feature branch, and no merge
conflicts are possible. When I run the merge, git simply will move that HEAD pointer from the
tip of mainline, up to the tip of your feature branch resulting in the feature branch being
essentially absorbed into mainline. The original branch fades away, but the commits stay where
they are, and we now have a nice linear history. Let me demonstrate this. I’ll open up my terminal
and run "git merge feature_branch". You'll notice Git will tell me it performed a fast-forward merge in the output
of the command. Plus I don’t actually have to specify a commit message, because there’s no
merge commit. A fast-forward merge is a lot cleaner and more straightforward than a 3-way
merge, but we do lose some historical detail since there is actually no merge commit and our
branch was absorbed. If you’d prefer to keep that feature branch around for documentation
purposes, just pass the “—no-ff” flag to the command like this, and git will construct a
merge commit regardless of if it’s able to be fast-forwarded or not. But keep fast-forwarding
in mind, because this will be important later on. Now that we know about merges,
and I’ve teased it enough, how does rebase compare and
why use it in the first place? Well first I'd like to point out that
these animations and live coding examples are looking nice right?! What you're seeing
is a sneak peak of the enhanced animations of my upcoming LearnGit.io service,
if you want to join the waitlist at waitlist.learngit.io for monthly updates,
and when you sign up, you'll get access to all the updates I’ve done in the past, and
trust me, there's some cool stuff in there. But anyways let’s get back to rebase, well
let’s actually go back to our original example. Let’s say we want to take advantage of rebase in this
situation instead of merge. We can use rebase to incorporate our collaborator's changes into
our feature branch. To do this, I’ll hop over into my terminal, but this time I’ll check out my
feature branch (“git checkout feature_branch”). I’ll then run “git rebase main”. When this
command executes, Git will start by simply setting aside those feature_branch
commits in a temporary holding area. After that, Git’s gonna sequentially copy
each commit one-by-one and replay it on top of the latest commit from main. The original
commits are then deleted. The result is that I’ve actually re-anchored my feature branch
off of the latest changes from main and in the process integrated my collaborators
changes into my work, since my work’s now based off those changes. You’ll notice something
important about this picture too. Our history is now linear and there is a single path from our
anchor point all the way up to the tip of our feature branch - meaning a fast-forward merge
is now on the table. No complex merge commits, just clean linear project history when I’m ready
to ship my feature. This type of rebase - when used in conjunction with fast-forward merging
- is the most common rebase workflow. It allows changes to be integrated incrementally instead
of all at once, and enables developers to ensure compatibility with the latest changes but in
the safety of your own branch. If I were to illustrate a merge-only workflow and compare that
side-by-side with a rebase & merge workflow, you can see just how much cleaner a rebased history
is. This is the primary benefit of using it. But before you get ahead of yourself, rebase
isn’t all sunshine and rainbows. Rebase avoids merge commits, but it isn't magic - you
can still have conflicts. As you can see, if your feature branch changes are incompatible
with the branch you rebasing, the operation will fail and you’ll need to fix any conflicts. If
you’re going to get a conflict when you merge, you’re going to get that same conflict when you
rebase. But I’m going to cover conflicts in an upcoming video, and I’ll link that video here
and at the end of this video when it goes live. But besides conflicts, there is one other critical thing to know about rebase before you
go out and start using it. You see, git considers all commits immutable which means
they cannot be changed once they are created. It also means that during rebase, when Git grabs
those commits, sets them aside and replays them, the commits that end up on our newly re-anchored
branch are actually copies of their originals. You can see that if I run “git log” before and after
the rebase, the commit hashes are different. This signales to Git that these commits are brand new
and have not been seen before in project history. At this point, you might be asking yourself,
well so what if they're copies? The commits contain the same changes as the originals,
so I mean who cares if they're copies or not? Well that’s kind of a good question and the
answer is that sometimes it doesn’t matter, but sometimes it really does matter. If the branch you’re rebasing is local only
and not shared by other contributors, sure, go ahead and rebase there’s not a ton of risk
there. But what if that branch is shared? In that case, you might want to rethink using
rebase and I’ll give you an example of why. So say that feature branch is shared with another
collaborator. And I’m just going to annotate the commits in this example for clarity. Now let’s say
a third collaborator pushed a change to main. You both pull that change down. Say your collaborator
continues work on that feature branch and adds some commits, but you run rebase to re-anchor and
integrate that latest change onto your feature branch first. Okay so now there is a problem.
You’ve abandoned the original commits and created brand new ones, but your coworker hasn’t. Both
of your feature branches, even though they are technically the same branch, have diverged and
contain a completely different set of commits as far as Git is concerned. Integrating your work
together when it comes time will be challenging. This is a simplistic example, but these
common scenarios become unwieldy when branches are shared due to rebase's deletion and
rewriting of history. It’s also the reason why it’s not best practice to use rebase
to move branches onto main. In fact, many repositories just prevent that
behavior in the first place for that reason But at the end of the day I think the official
documentation does the best job of summing it up: "Do not rebase commits that exist outside your repository and that people
may have based work on.” If you follow that guideline, you’ll be fine. Now that you know the inner workings of both
rebase and merge, you’ve probably noticed merge and rebase are not really competitors,
they’re teammates. Integrating both into your Git tool belt while keeping an eye out for
the pitfalls is what efficient programming is all about. If I were to summarize this
video in just one sentence it would be: use rebase to re-anchor your feature
branch to keep up with the latest changes, then use merge to move those changes back
onto shared branches. It’s that simple. Now Git experts among you may have noticed
that I simplified some of the examples and diagrams in this video for the sake of
clarity. But if you’d like a deep dive into anything I left out just leave
a comment. Also subscribe. Peace.