Phase 5: Advanced & Professional Skills

Rebasing vs. merging & when to use each

Beginner ~3 min read
Think of it this way A friendly analogy. Read this if the technical version feels dense. Show Hide

Imagine you and your friend are building a super cool LEGO castle together. It’s a huge project, so you decide to split the work. You want to build a tall wizard's tower, and your friend wants to build a fancy drawbridge. To make sure you don't bump into each other or mix up your bricks, you each build your part on separate tables, like your own little mini-projects away from the main castle. Every time you add a few bricks and connect them perfectly, it’s like saving your progress – in coding, we call these 'commits'.

Eventually, you'll both want to put your finished tower and drawbridge onto the main castle base. One way to do this is called merging. This is like building a special 'connector piece' that brings your tower and your friend's drawbridge together onto the main castle. All the bricks you used for your tower stay exactly as you built them, and the same for your friend's drawbridge. You can always look back at the castle and see that "this tower came from you, and this drawbridge came from your friend, and here’s the special connector piece that joined them." Merging is super clear and safe, especially when you're adding finished, important parts to the main castle that everyone relies on, because it keeps a perfect record of how everything came together.

Now, let's imagine that while you were building your tower, someone else added a whole new wall to the main castle on the big table. Your friend, still working on their drawbridge, had started building it from an older version of the main castle, before that new wall was even there. If your friend wants their drawbridge to look like it was built directly onto that new wall, they could use rebasing. Instead of just making a connector piece, your friend carefully takes apart their drawbridge (but remembers exactly how it goes!). Then, they re-build their drawbridge directly on top of the brand new wall that was just added to the main castle.

From then on, it looks like your friend built their drawbridge directly onto the latest castle from the very beginning. The castle's history becomes very neat and straight, like a single, smooth path, without any extra connector pieces. Rebasing is useful when you’re working on your own part for a while and you want to make sure your work is perfectly up-to-date with the main castle before you eventually connect it. So, when you build websites or apps, sometimes you'll want to clearly record every step and every join with merging, and other times you'll want your own work to look like one continuous story, neatly stacked on top of the latest progress using rebasing.

When working on a frontend project, you'll often create separate branches for new features or bug fixes. Eventually, you'll need to integrate this work back into your main codebase. Git offers two primary ways to do this: merging and rebasing. Merging is like taking two divergent paths and bringing them back together by creating a new "merge commit." This merge commit explicitly records that two branches were combined, preserving the exact history of both. It's a non-destructive operation because it doesn't change existing commits, making it a safe and transparent way to integrate work, often preferred for bringing completed features into stable, shared branches like main or develop.

Rebasing, on the other hand, is about moving or "replaying" your branch's commits onto a new base commit. Instead of creating a merge commit, rebasing rewrites the history of your feature branch, making it appear as if you started your work from the latest point of the target branch. This results in a cleaner, more linear project history without extra merge commits. Imagine you're building a feature on top of an older version of main, and main has moved forward. Rebasing your feature branch onto the latest main updates your branch to that point, making it look like your changes were made directly after the very latest main commit.

So, when do you use each? Merge is generally preferred for integrating finished features into public, shared branches (like main or develop). It preserves the complete historical context, including when and where branches diverged. Use rebase when you want to keep your local feature branch up-to-date with the main branch before you eventually merge it, or when you want to clean up your own commits within your local feature branch before pushing them for review. Rebasing keeps your personal branch tidy and helps ensure your changes apply smoothly to the latest codebase. Crucially, never rebase a branch that has already been pushed to a shared remote repository and other developers are working on it, as this rewrites shared history and can cause significant problems for your team.

Key Takeaways

  • Merging combines branch histories, creating a new "merge commit."
  • Rebasing rewrites history by moving your commits to a new base, creating a linear history.
  • Use merge when integrating completed features into shared, public branches (main).
  • Use rebase to keep your local feature branch updated with main or to clean up your own commits before pushing.
  • Never rebase public/shared branches that others are working on!

Code Example

bash
# Example 1: Merging a feature branch into main
git checkout main              # Switch to the main branch
git pull origin main           # Get the latest changes from remote
git merge feature-branch-name  # Merge your feature branch into main
git push origin main           # Push the updated main branch

# Example 2: Rebasing a feature branch onto main (to update it)
git checkout feature-branch-name # Switch to your feature branch
git pull origin feature-branch-name # Get latest changes for your feature branch
git rebase main                # Rebase your feature branch onto main
git push origin feature-branch-name --force-with-lease # Push rewritten history (use with caution!)

How this code works

This code illustrates two distinct strategies for incorporating changes from a feature-branch-name into the main branch. The first set of commands demonstrates git merge, which combines development histories while preserving all original commits. It starts by using git checkout main and git pull origin main to ensure the local main branch is fully up-to-date. Then, git merge feature-branch-name integrates the feature branch's changes, typically creating a new "merge commit" on main. This merge commit explicitly shows when the feature branch was integrated, maintaining a clear record of parallel development. Finally, git push origin main sends these updated changes to the remote repository.

The second approach uses git rebase to integrate changes, aiming for a linear, cleaner project history. It begins by using git checkout feature-branch-name and git pull origin feature-branch-name to get the latest version of the feature branch. The core step, git rebase main, takes all commits from the feature-branch-name and reapplies them on top of the latest main branch commits. This effectively rewrites the feature branch's history, making it appear as if its development started from the current main. Because history is rewritten, a simple push won't work. The command git push origin feature-branch-name --force-with-lease is then required. This is a crucial safety measure; it forces the push to update the remote branch but checks that the remote hasn't been updated by someone else in the meantime, preventing accidental overwrites of their work.