Fixing Push and Pull Errors - Behind Tips, Reconciling Divergent Branches
Error Breakdown
When You Run git push
You might see something like this:
1
2
3
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. If you want to integrate the remote changes,
hint: use 'git pull' before pushing again.
Why This Happens:
- Your remote branch has new changes.
- Git is asking you to pull those changes before you push.
When You Run git pull
You might see:
1
2
3
4
5
6
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:
git config pull.rebase false # merge
git config pull.rebase true # rebase
git config pull.ff only # fast-forward only
fatal: Need to specify how to reconcile divergent branches.
Why This Happens:
- There are changes both locally and remotely, and Git needs you to choose how to combine them. Git doesn’t know how to reconcile the differences. It needs you to choose a strategy to handle the conflicting updates.
Solutions
Solution 1: Temporary Fix (For This Pull Only)
If you’re in a hurry, you can choose a strategy just for the current pull:
- Merge (default behavior for most beginners):
1
git pull --no-rebase
This creates a merge commit, preserving the history of both sets of changes.
- Rebase (for a cleaner, linear history):
1
git pull --rebase
This reapplies your local changes on top of the remote branch.
- Fast-Forward Only (for minimal changes):
1
git pull --ff-only
This works only if you have no local changes.
Solution 2: Set a Default Strategy for the Current Repository
To avoid specifying a strategy every time, you can configure a default for the current repository:
- Merge (recommended for beginners):
1
git config pull.rebase false
- Rebase (for advanced users):
1
git config pull.rebase true
- Fast-Forward Only:
1
git config pull.ff only
Solution 3: Set a Default Strategy Globally
If you work with multiple repositories and want to set a global default:
- Merge:
1
git config --global pull.rebase false
- Rebase:
1
git config --global pull.rebase true
- Fast-Forward Only:
1
git config --global pull.ff only
Cleaning Up Existing Configurations
If you’ve set configurations before and want to reset them:
- For the current repository:
1 2
git config --unset pull.rebase git config --unset pull.ff
- Globally:
1 2
git config --global --unset pull.rebase git config --global --unset pull.ff
To confirm the configuration is removed:
- For the current repository:
1
git config --get pull.rebase
- Globally:
1
git config --global --get pull.rebase
Differences between merge, rebase, and fast-forward only
Let’s use a simple scenario to explain the differences between merge, rebase, and fast-forward only in Git.
Scenario Overview
You and your colleague are working on a shared project with a Git repository.
- The remote branch (
main
) has two new commits made by your colleague:1
A -- B -- C (Remote main)
- Your local branch (
main
) has one new commit that you made:1
A -- B -- D (Local main)
- Now, you want to
git pull
to integrate the remote changes (C
) into your local branch (D
).
1. Merge
What Happens:
- Git creates a merge commit that combines your local and remote changes.
- The history shows that two branches were merged.
Result:
1
2
3
4
5
C
/
A -- B -- E (Merge Commit)
\
D
- Commit
E
is the merge commit, showing thatC
(remote changes) andD
(local changes) were merged.
Characteristics:
- Preserves full history of both branches (branch structure).
- Easy to see where changes diverged and were combined.
- A merge commit is created even if no conflicts exist.
When to Use:
- When you want to maintain the complete branching history.
2. Rebase
What Happens:
- Git re-applies your local changes (
D
) on top of the remote branch (C
). - The branch history is rewritten to look like your changes were made after the remote changes.
Result:
1
A -- B -- C -- D (Rebased Local main)
- Your commit (
D
) is moved to appear after the remote commit (C
).
Characteristics:
- Creates a linear history without merge commits.
- The original commit
D
is replaced by a new version applied afterC
. - Can be confusing if conflicts arise because the history has changed.
When to Use:
- When you want a clean, linear history for easier readability.
- Often used in collaborative projects to keep the main branch clean.
What Rebase Does with git pull
(Config: pull.rebase=true
):
When you set pull.rebase=true
, Git will use rebase instead of merge when you run git pull
. Here’s what happens:
- Temporarily Remove Your Local Commits:
- Git saves your local commits aside.
- Update to Match the Remote
main
:- Your branch is updated with the latest changes from the remote
main
branch.
- Your branch is updated with the latest changes from the remote
- Reapply Your Local Commits:
- Git reapplies your local commits on top of the updated remote
main
.
- Git reapplies your local commits on top of the updated remote
What Happens After the Rebase?
- Push to Remote:
- After rebasing, you need to use
git push --force-with-lease
to push your changes to the remote branch. This is required because rebasing rewrites commit history, making your branch diverge from the remote.
- After rebasing, you need to use
1
git push --force-with-lease
--force-with-lease
ensures you don’t overwrite remote changes made by others.Key Point: Rebasing rewrites your commit history, so when you push to
main
, you must force-push to overwrite the remote branch. Use--force-with-lease
to avoid accidentally overwriting someone else’s changes.
3. Fast-Forward Only
What Happens:
- Git simply moves the branch pointer forward to the latest commit if your local branch has no additional commits.
- If there are local changes (like in our scenario), Git will abort the pull.
Result (if no local changes):
1
A -- B -- C (Local main matches Remote main)
Result (in our scenario):
- Git refuses to pull because your local branch (
D
) has diverged from the remote branch (C
).
Characteristics:
- Only works when the local branch is strictly behind the remote branch (no local commits).
- No merge commit or rebase occurs.
When to Use:
- When you want to ensure the local branch matches the remote branch exactly, without any additional changes.
Comparison
Feature | Merge | Rebase | Fast-Forward Only |
---|---|---|---|
History | Preserves branching history | Linear history | No history rewrite, only works if linear |
Complexity | Easy to understand | Requires understanding of history rewrite | Simple, but aborts if branches diverge |
Conflicts | Resolve conflicts once during merge | Resolve conflicts for each commit | Avoids conflicts by aborting on divergence |
Use Case | Collaborative projects with branching | Clean history in solo or team workflows | Synchronizing with no local changes |
Comments powered by Disqus.