Post

Fixing Push and Pull Errors - Behind Tips, Reconciling Divergent Branches

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:

  1. Merge (default behavior for most beginners):
    1
    
    git pull --no-rebase
    

    This creates a merge commit, preserving the history of both sets of changes.

  2. Rebase (for a cleaner, linear history):
    1
    
    git pull --rebase
    

    This reapplies your local changes on top of the remote branch.

  3. 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:

  1. Merge (recommended for beginners):
    1
    
    git config pull.rebase false
    
  2. Rebase (for advanced users):
    1
    
    git config pull.rebase true
    
  3. 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:

  1. Merge:
    1
    
    git config --global pull.rebase false
    
  2. Rebase:
    1
    
    git config --global pull.rebase true
    
  3. 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:

  1. For the current repository:
    1
    2
    
    git config --unset pull.rebase
    git config --unset pull.ff
    
  2. 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.

  1. The remote branch (main) has two new commits made by your colleague:
    1
    
    A -- B -- C  (Remote main)
    
  2. Your local branch (main) has one new commit that you made:
    1
    
    A -- B -- D  (Local main)
    
  3. 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 that C (remote changes) and D (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 after C.
  • 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:

  1. Temporarily Remove Your Local Commits:
    • Git saves your local commits aside.
  2. Update to Match the Remote main:
    • Your branch is updated with the latest changes from the remote main branch.
  3. Reapply Your Local Commits:
    • Git reapplies your local commits on top of the updated remote main.

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.
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

FeatureMergeRebaseFast-Forward Only
HistoryPreserves branching historyLinear historyNo history rewrite, only works if linear
ComplexityEasy to understandRequires understanding of history rewriteSimple, but aborts if branches diverge
ConflictsResolve conflicts once during mergeResolve conflicts for each commitAvoids conflicts by aborting on divergence
Use CaseCollaborative projects with branchingClean history in solo or team workflowsSynchronizing with no local changes

This post is licensed under CC BY 4.0 by the author.