Understanding Git Divergent Branches: A Real-World Example¶
The Scenario¶
While working on a feature branch, I encountered a common Git problem that many developers face. Let me share my experience and how to resolve it.
What I Was Doing¶
I was working on a feature branch called feature/25.10.25 in my project. Here's what happened step by step:
- First, I successfully pushed some changes to GitHub
- Then, I made a local commit with the message "changes" (commit ID:
396de08) - I tried to push again, but Git rejected my push!
The First Error: Push Rejected¶
Here's what Git told me when I tried to push:
$ git push
To https://github.com/dwdas9/home.git
! [rejected] feature/25.10.25 -> feature/25.10.25 (fetch first)
error: failed to push some refs to 'https://github.com/dwdas9/home.git'
hint: Updates were rejected because the remote contains work that you do not
hint: have locally. This is usually caused by another repository pushing to
hint: the same ref. If you want to integrate the remote changes, use
hint: 'git pull' before pushing again.
Git's message was clear: "use git pull before pushing again." So that's exactly what I did.
The Second Error: Git Asks Me to Choose¶
Following Git's advice, I ran git pull, but instead of solving the problem, I got another error:
$ git pull
hint: You have divergent branches and need to specify how to reconcile them.
hint: You can do so by running one of the following commands sometime before
hint: your next pull:
hint:
hint: git config pull.rebase false # merge
hint: git config pull.rebase true # rebase
hint: git config pull.ff only # fast-forward only
hint:
hint: You can replace "git config" with "git config --global" to set a default
hint: preference for all repositories. You can also pass --rebase, --no-rebase,
hint: or --ff-only on the command line to override the configured default per
hint: invocation.
fatal: Need to specify how to reconcile divergent branches.
This is the critical moment! Git is essentially saying:
"I can see your branches have diverged. I can help you reconcile them, but YOU need to tell me HOW. Do you want to merge? Rebase? Or only fast-forward?"
This is where many developers get confused. Git is giving us three options, but which one should we choose? That's what this article is about!
What Actually Happened¶
The error message gives us a crucial clue: "the remote contains work that you do not have locally."
This means: - While I was working on my local branch and making commits - Someone else (or I from another machine, or maybe a CI/CD system) pushed different commits to the same branch on GitHub - Now my local version and the remote version have diverged - they've gone in different directions from a common starting point
Think of it like two people editing the same document simultaneously but in different ways. Eventually, you need to reconcile the changes.
Understanding the Problem Visually¶
Let me show you what "divergent branches" actually means:
What Happened? 🤔
• You both started with A → B → C
• Someone else pushed E and F to remote 🔴
• You created commit D locally 🟡
• Now: DIVERGED! Two different versions exist!
Breaking Down the Divergence¶
Looking at the diagram above:
- Commits A, B, C: These are the commits that both my local branch and the remote branch shared originally
- Commit C: This is where our histories diverged (the "common ancestor")
- Commits E and F (in red): Someone pushed these to the remote while I was working
- Commit D (in yellow): This is my local commit that hasn't been pushed yet
The problem? Git doesn't know which version is "correct" - both have valid new work!
Why This Happens¶
There are several common scenarios that lead to divergent branches:
1. Multiple Developers Working on the Same Branch¶
- You and a teammate are both working on
feature/25.10.25 - Your teammate pushes their changes first
- When you try to push, your branch has diverged
2. Working from Multiple Machines¶
- You commit and push from your work computer
- Later, you commit from your laptop (forgetting to pull first)
- The branches diverge
3. CI/CD Automation¶
- Your CI/CD pipeline makes automated commits (version bumps, generated files, etc.)
- You're working locally at the same time
- When you try to push, there's a conflict
4. Force Push by Someone Else¶
- Someone force-pushed to the branch, rewriting history
- Your local branch is now based on old commits
The Three Solutions¶
When Git detects divergent branches and you try to pull, it stops and asks you to choose how to reconcile them. This is exactly what happened when I ran git pull - Git showed me three options and said "fatal: Need to specify how to reconcile divergent branches."
Let's explore each option Git offered:
Solution 1: Merge Strategy¶
Merge Strategy 🔀
• Combines both histories
• Creates new merge commit M 🔵
• Keeps all commits intact
git pull
git push
• Preserves complete history
• Safest option
• Easy to understand
• Extra merge commit
• History can get messy
• Graph looks complex
When to Use Merge¶
- Best for: Main branches, shared team branches
- Use when: You want to preserve the complete history of both branches
- Team setting: When multiple people are collaborating and you want transparency
How It Works¶
The merge strategy creates a new "merge commit" that has two parents - your local changes and the remote changes. This preserves both lines of development in the history.
Example scenario: You're working on the main branch with your team. You want to see exactly when and how different features were integrated.
Solution 2: Rebase Strategy ⭐¶
When to Use Rebase¶
- Best for: Feature branches, personal branches
- Use when: You want a clean, linear history
- Team setting: When you're the primary developer on a feature branch
How It Works¶
Rebase "replays" your commits on top of the latest remote commits. It's like saying "pretend I made my changes AFTER the remote changes were made." Your commit gets a new ID because it's technically a new commit in a new position.
Example scenario: You're working on a feature branch alone. You want the Git history to look like you made your changes in a nice, orderly sequence.
Important: This is my recommended approach for the situation in the screenshot!
Solution 3: Fast-Forward Only¶
When to Use Fast-Forward Only¶
- Best for: When you want to be extra cautious
- Use when: You only want to pull when there's no divergence
- Team setting: When you want to ensure you never accidentally merge or rebase
Why It Fails¶
Fast-forward only works when your local branch is simply "behind" the remote - meaning the remote has new commits, but you haven't made any new commits locally. In our case, we HAVE made new commits (commit D), so this option fails.
Example scenario: You cloned a repo, haven't made any changes, and just want to update to the latest version. Fast-forward works perfectly here.