Git pull.rebase vs pull.ff: Differences and Best Practices
Problem Statement
When working with Git, many users encounter confusion between the pull.rebase
and pull.ff
configuration options. Both settings affect how git pull
integrates upstream changes into your local branch:
# Configurations in question
git config pull.rebase false # Merge (default)
git config pull.ff true # Allow fast-forward merges
At first glance, these settings appear similar since both can result in fast-forward merges when possible. However, they control fundamentally different aspects of Git's behavior. Using them incorrectly can lead to unexpected merge commits, history pollution, or failed operations.
Understanding the Configuration Options
pull.rebase
: Choose Strategy (Merge vs. Rebase)
Controls whether git pull
uses a merge or rebase strategy:
# Possible values
false # Default - performs merge
true # Rebase local commits on top of fetched changes
merges # Rebase while preserving merge commits
interactive # Interactive rebase
- Setting to
true
will rebase your local commits on top of upstream changes - Setting to
false
enables default merge behavior
pull.ff
: Control Merge Behavior
Only applies when pull.rebase=false
and specifies how merge commits are created:
# Possible values
true # Fast-forward when possible, create merge otherwise (default)
false # Always create merge commit (--no-ff)
only # Allow only fast-forwards, abort otherwise
- Acts as the configuration equivalent for
--ff
,--no-ff
, and--ff-only
CLI flags - If you set
pull.rebase
, this setting is ignored - no merge occurs
Key Interaction
pull.ff
has no effect when pull.rebase
is enabled since rebasing rewrites history rather than creating a merge commit. These settings operate on different workflow stages!
Practical Recommendations
Recommended Global Configuration
# Set default pull behavior to rebase
git config --global pull.rebase true
# For branches where you want to enforce fast-forwardability
git config --global pull.ff only
Workflow Comparison Table
Command | When Fast-Forward Possible | When Not Fast-Forwardable |
---|---|---|
pull.rebase=false + pull.ff=true | Fast-forwards | Creates merge commit |
pull.rebase=false + pull.ff=only | Fast-forwards | Aborts operation |
pull.rebase=true | Rebases local commits | Rebases local commits |
Anti-Pattern
Avoid mixing strategies without understanding the implications:
# This combination is contradictory and confusing
git config pull.rebase true
git config pull.ff false # Ignored since rebase is enabled!
Expert Best Practices
When to Use Each Configuration
pull.rebase=true
(Recommended)- Preferred for feature branches and solo development
- Keeps history linear and clean
- Avoids superfluous merge commits
- Ideal for: Developers working on shared repositories and CI/CD pipelines
pull.ff=only
(Specific use cases)- Enforces branch compatibility before integration
- Prevents unintended merge commits
- Suitable for: Release branches, hotfix branches, and production environments
Advanced Alternative: Manual Fetch Workflow
# Instead of git pull, use:
$ git fetch
$ git log --graph --oneline origin/main your-branch
$ git rebase origin/main # OR
$ git merge origin/main
When to Use Manual Workflow
- Complex integrations: View changes before integrating
- History cleanup: Drop/edit commits before rebasing
- Multiple upstreams: Handle special merge scenarios
- Troubleshooting: Diagnose integration conflicts
Key Takeaways
pull.rebase
defines whether to rebase or mergepull.ff
controls how merges happen (but only if merging)- For clean history:
git config --global pull.rebase true
- For strict branch control:
pull.ff=only
- Always consider separating
git fetch
andgit merge
/git rebase
Understanding these configuration options helps maintain cleaner Git histories and prevents workflow disruptions. Choose the strategy that aligns with your team's collaboration model and quality standards.