Phase 1: Linux & Networking Fundamentals

Merge vs Rebase Workflows

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 friends are building a super cool, giant LEGO castle together. Each of you might work on different parts at the same time – maybe one friend builds the main tower, another makes the drawbridge, and you're busy with the secret dungeon. Eventually, you'll need to put all those amazing pieces together to make one big, complete castle. In programming, when people work on different parts of a project, we also need ways to combine their work into the main project. There are two main ways we do this, like two different strategies for adding your LEGO section to the big castle.

The first way is like using git merge. Let's say your friend finishes their awesome tower. When they merge their tower into the main castle, it’s like they take their completed tower section and snap it directly onto the existing main castle structure. To show exactly when their tower joined the big castle, they add a special 'connection block'. This clearly marks: 'Here’s where the tower part, built separately, joined the main castle!' You can always look back and see the full story of how the tower was built on its own, and then when it officially joined the main project. It's like having a detailed map of all the building stages.

The other way is called git rebase. This is a bit different. If your friend used git rebase to add their tower, they wouldn't just add a connection block. Instead, they would carefully take apart all the blocks they used for their tower. Then, they would rebuild their tower directly on top of the very latest part of the main castle, as if they had only started building their tower after all the main castle parts were already finished. This makes the castle's history look super neat and tidy, like one long, continuous line of building, without any separate paths joining. It looks like your friend always built their tower perfectly in sync with the newest parts of the main castle.

So, why have two ways? Using git merge is generally simpler and safer because it always keeps the full, exact history of how everything happened, like that special connection block. It’s great for showing distinct pieces coming together. git rebase, on the other hand, is like tidying up your own work before you put it into the big castle. It makes the final building sequence look very clean and straightforward, as if you always built your part perfectly on top of the latest version of the main castle. This means you can choose the best way to combine your work, either by clearly showing every step of how things converged or by making the project’s history look like one smooth, continuous build.

When working with Git, you'll often need to bring changes from one branch into another. The two primary ways to do this are git merge and git rebase. While both achieve a similar outcome – integrating changes – they do so in fundamentally different ways that impact your project's history. Understanding these differences is crucial for maintaining a clean, understandable repository, especially as a DevOps Engineer managing infrastructure code.

git merge is generally the simpler and safer option. When you merge, Git takes the content of the source branch and combines it with the target branch, creating a brand-new commit called a "merge commit." This merge commit has two parent commits, effectively preserving the exact history of when and how the branches diverged and then combined. It's like recording a timestamp of when two separate storylines converged. This non-destructive approach means you're always adding to the history, never rewriting it.

git rebase, on the other hand, rewrites history. Instead of creating a merge commit, it takes the commits from your feature branch and reapplies them one by one onto the tip of the target branch. The result is a linear history, making it look as if you started your work from the very latest point of the target branch. This can make the commit history much cleaner and easier to follow, as there are no distracting merge commits. However, because it rewrites history, rebase should never be used on branches that have already been pushed to a shared remote repository and other team members might be working from. Doing so can cause significant headaches for your team.

Key Takeaways

  • Merge preserves history by creating a new merge commit.
  • Rebase rewrites history by moving/reapplying commits, creating a linear timeline.
  • Use merge for shared branches; it's non-destructive and generally safer.
  • Use rebase for local, private branches to maintain a clean, linear history before pushing.
  • Never rebase commits that have already been pushed to a shared remote repository.

Code Example

bash
git checkout feature/my-new-feature
git fetch origin main # Ensure your local 'main' tracking branch is up-to-date
git rebase origin/main # Reapply your feature branch commits on top of the latest 'main'

# After rebase, your branch will appear as if you started work 
# from the very latest 'main' branch, with a clean linear history.
# You can then resolve any conflicts and force push (if allowed/necessary 
# for your private branch) or merge into main.

How this code works

This code sequence illustrates how to update a feature branch to integrate the latest changes from the main branch using a rebase workflow, aiming for a clean, linear project history. First, git checkout feature/my-new-feature ensures the developer is working on their specific feature branch. The next step, git fetch origin main, is crucial: it updates the local repository's knowledge of the remote main branch's state without altering any local branches. This ensures the subsequent rebase operation uses the absolute latest version of main as its base, rather than an potentially outdated local copy.

Following the fetch, git rebase origin/main performs the core action. This command takes all the commits made on the current feature branch and reapplies them one by one onto the tip of the updated origin/main branch. This effectively rewrites the feature branch's history, making it appear as if the feature work began directly from the very latest point of main. The result is a straightforward, linear commit history, which simplifies understanding the project's evolution and can make future merges smoother, as long as any conflicts introduced during reapplication are resolved.