Git MERGE vs REBASE: The Definitive Guide

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
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.
Info
Channel: The Modern Coder
Views: 18,874
Rating: undefined out of 5
Keywords: git, github, git rebase, git rebase tutorial, version control, learn git, gitrebase, git tutorial, rebase, merge vs rebase, fast-forward merge, git merge
Id: zOnwgxiC0OA
Channel Id: undefined
Length: 9min 39sec (579 seconds)
Published: Wed Aug 30 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.