Skip to content

DevOps Day 32: Keeping History Clean with git rebase

Today's task was a deep dive into an advanced but incredibly important Git workflow: updating a feature branch with the latest changes from the main branch using git rebase. The key requirement was to do this without creating an extra "merge commit," which is a common goal for teams that value a clean, linear, and easy-to-read commit history.

This was a fantastic lesson in the power of rewriting history for a good cause. I learned how rebase works by "re-playing" my commits on top of the latest code, and why a "force push" is a necessary and deliberate part of this workflow. This document is my detailed, first-person guide to that entire process.

Table of Contents


The Task

My objective was to update a feature branch with the latest changes from the master branch in a repository on the Storage Server. The specific requirements were: 1. Work within the /usr/src/kodekloudrepos/media Git repository. 2. Update the feature branch with the latest commits from master. 3. The process must not create a merge commit. 4. The updated feature branch must be pushed back to the remote server.


My Step-by-Step Solution

The solution required a precise sequence of commands. Because rebase rewrites history, the order of operations and the final push command were critical. As the repository was in a root-owned directory, all commands required sudo.

Phase 1: Preparation

  1. I connected to the Storage Server (ssh natasha@ststor01) and navigated to the repository (cd /usr/src/kodekloudrepos/media).
  2. This was the most critical first step. I had to be on the branch that I wanted to update. I switched to the feature branch.
    sudo git checkout feature
    

Phase 2: The Rebase and Force Push

  1. With my current branch set to feature, I executed the rebase command, telling it to use master as the new base.

    sudo git rebase master
    
    Git then took all the commits that were unique to my feature branch and re-applied them, one by one, on top of the latest commit from master. The output confirmed the rebase was successful.

  2. My local feature branch now had a different history than the feature branch on the remote server. A normal push would be rejected. I had to use a force push to overwrite the remote branch with my new, clean history.

    sudo git push origin feature --force
    

Phase 3: Verification

The final step was to look at the commit log to see the result of my work. 1. I ran a git log command that shows all branches and their relationships.

sudo git log --oneline --graph --all
2. The output showed a beautiful, clean, straight-line history. The commits from my feature branch now appeared directly after the latest commits from master, with no messy "merge commit" bubble in the graph. This was the definitive proof of success.


Why Did I Do This? (The "What & Why")

- The Problem: When I work on a feature branch for a long time, the master branch continues to evolve as other developers merge their work. My branch becomes "stale" or "out of date." Before I can merge my feature, I must incorporate the latest changes from master to ensure my code is compatible. - git rebase: This is one of two ways to solve the problem. Rebase is a command that rewrites history. It works by: 1. Finding the common ancestor commit between my feature branch and master. 2. Temporarily "saving" all the commits I made on my feature branch after that point. 3. Moving my feature branch's starting point to the latest commit on master. 4. "Re-playing" my saved commits, one by one, on top of this new base. - The Benefit - A Clean, Linear History: The end result is a project history that is very easy to read. It looks like all the feature work was done in a straight line, as if I had started my work on the very latest version of master. This makes it much easier to follow the project's evolution and to use tools like git bisect to find bugs. - Force Push (--force or -f): Because rebase creates new commits (with new hashes) to replace the old ones, my local branch's history is now fundamentally different from the remote branch's history. The remote server will reject a normal push. A force push is required to say, "I know what I'm doing. Overwrite the remote branch with my new local version."


Deep Dive: The Great Debate - rebase vs. merge

This task perfectly illustrates the core difference between the two ways to integrate changes in Git.

[Image of a git rebase vs. git merge diagram]

  • git merge master (from feature branch):

    • What it does: It takes the latest state of master and the latest state of feature and creates a brand-new "merge commit" that combines them.
    • History: It preserves history exactly as it happened. The merge commit has two parents and clearly shows in the log that two separate lines of development were joined together.
    • Pros: It's non-destructive and considered "safer" because it never changes existing commits.
    • Cons: If this happens frequently, the project history can become cluttered with many merge commits, making it hard to read and understand the project's timeline.
  • git rebase master (from feature branch):

    • What it does: It rewrites history by re-playing the feature branch's commits on top of master.
    • History: It creates a clean, linear, straight-line history. It looks like the feature was developed sequentially after the latest work on master.
    • Pros: The history is beautiful and easy to follow.
    • Cons: It's a "destructive" operation because it abandons the old commits. It can be dangerous if used incorrectly on a branch that other people are also working on.

The Golden Rule: It's generally considered safe to rebase your own local feature branches to keep them up-to-date. It is generally considered unsafe to rebase a branch that has been pushed and is being used by multiple people (like the master branch itself).


Common Pitfalls and the Dangers of Rewriting History

- Rebasing the Wrong Branch: The most critical mistake is to be on master and run git rebase feature. This would try to replay all of master's commits on top of the feature branch, which is the opposite of what's intended and can cause a huge mess. You must checkout the branch you want to update first. - Merge Conflicts during Rebase: A rebase applies commits one by one. If any of those commits has a conflict with the new base, the process will stop, and you will have to resolve the conflict for that specific commit before continuing with git rebase --continue. This can be more complex than resolving a single, large merge conflict. - Forgetting to Force Push: A regular git push after a rebase will be rejected. You must use --force (or the safer --force-with-lease). - Team Confusion: If I rebase a branch that my colleague has also checked out, my force push will put their local branch out of sync. They will need to perform some advanced Git commands to fix their local copy. This is why rebasing on shared branches requires careful team coordination.


Exploring the Commands I Used

- sudo git checkout feature: The command to switch my working directory to the branch I intended to modify. - sudo git rebase master: The main command of the task. It takes the commits from the current branch (feature) and re-plays them on top of the master branch. - sudo git push origin feature --force: The command to update the remote server. The --force flag is mandatory because the rebase has rewritten the local branch's history, and I need to overwrite the remote branch to match. - sudo git log --oneline --graph --all: A very powerful command for viewing the commit history. - --oneline: Condenses each commit to a single line. - --graph: Draws an ASCII graph showing the branching and merging history. - --all: Shows the history of all branches, not just the current one.