What are the 'pull.rebase false' and 'pull.ff true' differences?

Learn what are the 'pull.rebase false' and 'pull.ff true' differences? with practical examples, diagrams, and best practices. Covers git development techniques with visual explanations.

Understanding Git's 'pull.rebase false' vs. 'pull.ff true'

A visual representation of Git branches merging and rebasing, showing diverging lines coming together in different patterns. One side shows a clean linear history (rebase), the other shows a merge commit (no-rebase).

Explore the fundamental differences between Git's pull.rebase false and pull.ff true configurations, and learn how they impact your repository history and collaboration workflows.

When working with Git, understanding how git pull integrates changes from a remote repository into your local branch is crucial for maintaining a clean and understandable project history. The behavior of git pull is primarily controlled by two key configurations: pull.rebase and pull.ff. While they might seem similar at first glance, they dictate vastly different strategies for incorporating upstream changes. This article will demystify these settings, explain their implications, and help you choose the right strategy for your workflow.

The Core of 'git pull'

Before diving into the configurations, let's briefly recap what git pull does. Essentially, git pull is a convenience command that combines two other Git commands: git fetch followed by git merge or git rebase.

  • git fetch: Downloads commits, files, and refs from a remote repository into your local repository, but it doesn't modify your local working branch.
  • git merge: Integrates changes from one branch into another, typically creating a new merge commit if there are divergent histories.
  • git rebase: Reapplies commits from one branch onto another, effectively rewriting history to create a linear sequence of commits.

Understanding 'pull.rebase false' (Default: Merge)

When pull.rebase is set to false (which is the default behavior in many Git versions, or when not explicitly set), git pull performs a git fetch followed by a git merge. This means that if your local branch has diverged from the remote branch (i.e., you have local commits that are not on the remote, and the remote has commits you don't have), Git will create a new merge commit to combine the two histories. This merge commit explicitly records the point at which the two histories were joined.

This approach preserves the exact history of your commits, showing exactly when and where merges occurred. It's often preferred in collaborative environments where maintaining an accurate, immutable history of all merges is important, even if it results in a less linear commit graph.

git config --global pull.rebase false
git pull # Equivalent to: git fetch && git merge origin/main

Configuring and executing git pull with pull.rebase false

A Git commit history diagram showing a merge operation. The main branch has commits A-B-C. A feature branch diverges from B with commits D-E. When pulling with pull.rebase false, a new merge commit F is created, connecting E and C, resulting in history A-B-D-E-F. The diagram clearly shows the merge commit.

Visualizing git pull with pull.rebase false (merge strategy)

Understanding 'pull.ff true' (Fast-Forward Only)

The pull.ff configuration specifically controls the behavior of git merge when pull.rebase is false. When pull.ff is set to true (which is the default for git merge when possible), Git attempts a 'fast-forward' merge. A fast-forward merge occurs when there is no divergent history – meaning your local branch is an ancestor of the remote branch (you have no unique local commits, or your local commits are already included in the remote). In this scenario, Git simply moves your branch pointer forward to the latest commit on the remote, without creating a new merge commit.

If a fast-forward merge is not possible (i.e., your local branch has diverged from the remote), and pull.ff is true, Git will fall back to a regular merge commit (the same behavior as pull.rebase false). So, pull.ff true essentially means "fast-forward if possible, otherwise merge."

It's important to note that pull.ff only comes into play when pull.rebase is false. If pull.rebase is true, then pull.ff is ignored because the rebase strategy takes precedence.

git config --global pull.ff true
git pull # If fast-forward is possible, it will fast-forward. Otherwise, it will merge.

Configuring and executing git pull with pull.ff true

A Git commit history diagram showing a fast-forward merge. The main branch has commits A-B-C. The local branch is at B. When pulling with pull.ff true, the local branch pointer simply moves to C, resulting in history A-B-C. No new merge commit is created.

Visualizing git pull with pull.ff true (fast-forward strategy)

Key Differences and When to Use Each

The fundamental difference lies in how divergent histories are handled:

  • pull.rebase false (Merge Strategy): Always creates a merge commit when histories diverge. This preserves the exact history, showing all branches and merges. The commit graph might look more complex but accurately reflects development paths.
  • pull.ff true (Fast-Forward if possible, else Merge): Prioritizes a clean, linear history by fast-forwarding if no local commits exist. If local commits exist and histories diverge, it falls back to a merge commit, similar to pull.rebase false.

Here's a comparison table to summarize:

A comparison table showing the behavior of pull.rebase false and pull.ff true. Columns are 'Configuration', 'Divergent History', 'Linear History', 'Commit Graph', 'History Rewriting'. Rows detail how each setting handles these aspects.

Comparison of pull.rebase false and pull.ff true

Choosing Your Strategy

The choice between these configurations depends on your team's workflow and preferences for commit history:

  • Use pull.rebase false (or default merge behavior) if:

    • You want an explicit record of every merge event in your history.
    • You prefer not to rewrite history, even locally.
    • Your team values a complete, immutable historical record over a perfectly linear one.
    • You are new to Git and want to avoid the complexities of rebasing.
  • Use pull.rebase true (for a linear history) if:

    • You prefer a clean, linear project history without extra merge commits.
    • You want your local commits to appear as if they were made after the remote changes.
    • You are comfortable with history rewriting (which only affects your local branch until you push).
    • Note: This article focuses on pull.rebase false vs pull.ff true. For a deep dive into pull.rebase true, see our article on Git rebase strategies.
  • pull.ff true is the default and generally a good balance: It keeps history linear when possible (fast-forward) and creates a merge commit when necessary to avoid rewriting history. It's a sensible default for most scenarios where you want to avoid rebase but still get a clean history when possible.

By understanding these configurations, you can make informed decisions about how git pull integrates changes, leading to a more predictable and manageable Git history for your projects.