Skip to content

Instantly share code, notes, and snippets.

@shashanthk
Created May 24, 2025 08:32
Show Gist options
  • Save shashanthk/01017e0242920c5fec19d3f9942480c0 to your computer and use it in GitHub Desktop.
Save shashanthk/01017e0242920c5fec19d3f9942480c0 to your computer and use it in GitHub Desktop.
Git fast-forward config

Let's break down these two Git configuration commands:

  • git config --global pull.ff always
  • git config --global merge.ff false

Both commands use --global, meaning these settings will apply to all your Git repositories on your system, unless overridden by a local repository configuration.


git config --global pull.ff always

This command configures how git pull behaves when integrating changes from a remote repository.

  • pull.ff: This setting controls the "fast-forward" behavior during a git pull.
  • always: When pull.ff is set to always, Git will always attempt a fast-forward merge if possible. If a fast-forward is not possible (meaning your local branch has diverged from the remote branch, i.e., there are commits on your local branch that are not in the remote branch, and vice-versa), Git will abort the pull operation and refuse to proceed.

What is a "fast-forward" merge?

A fast-forward merge happens when the branch you're merging into (your local branch, in the case of git pull) is an ancestor of the branch you're merging from (the remote branch). In simpler terms, if your local branch has no unique commits compared to the remote, Git can simply move your local branch pointer forward to the latest commit of the remote branch. This is the simplest type of "merge" and does not create a new merge commit, keeping your history linear.

Why use pull.ff always?

  • Clean, Linear History: It helps maintain a perfectly linear history, avoiding unnecessary merge commits for simple updates.
  • Prevents Accidental Merges/Rebases: By refusing to pull if a fast-forward isn't possible, it forces you to explicitly decide how to handle divergent histories (e.g., by running git pull --rebase or git pull --no-ff) rather than Git automatically performing a merge or rebase you didn't intend. This gives you more control and prevents "surprise" merge commits.
  • Good for keeping branches up-to-date: If you're constantly fetching from a remote and you don't expect to have local changes that diverge, pull.ff always will ensure your local branch is just a direct copy of the remote.

Caveat: With pull.ff always, if your local branch has diverged (you've made new commits locally and someone else has made new commits on the remote), git pull will fail. You'll then need to manually decide how to integrate the changes, typically by using git pull --rebase (to reapply your local commits on top of the remote's changes) or git pull --no-ff (to create a merge commit).


git config --global merge.ff false

This command configures how git merge (specifically, when you explicitly run git merge <branch-name>) behaves when integrating changes from another branch.

  • merge.ff: This setting controls the "fast-forward" behavior during a git merge.
  • false: When merge.ff is set to false (or no), Git will always create a merge commit, even if a fast-forward merge would be possible. This is equivalent to always using the git merge --no-ff option.

Why use merge.ff false?

  • Preserves Branch History: By always creating a merge commit, you explicitly record when and how a feature branch was integrated into the main branch. This creates a non-linear history that clearly shows the branching and merging points. This can be beneficial for:
    • Auditing and traceability: It's easy to see when a specific feature or set of changes was merged.
    • Reverting features: If you need to undo an entire feature, a merge commit acts as a single point that can be easily reverted.
    • Understanding project flow: The graph history visually represents the development workflow (e.g., feature branches, hotfix branches).
  • Consistency: It enforces a consistent merge strategy across all your merges, regardless of whether a fast-forward is possible.

Contrast with pull.ff always:

It might seem contradictory to have pull.ff always and merge.ff false, but they serve different purposes:

  • pull.ff always is about updating your local branch from a remote. If there's a simple, linear update, it does it cleanly. If there's divergence, it forces you to be explicit about how to handle it. The idea here is that pulling should ideally be a "catch-up" operation.
  • merge.ff false is about integrating a feature branch (or any other branch) into your current branch (e.g., main or develop). By forcing a merge commit, you explicitly mark the integration event, preserving the context of the merged branch's history. This is often preferred in workflows like Git Flow, where feature branches are distinct and their integration should be clearly recorded.

In summary, pull.ff always promotes a clean pull operation by only allowing fast-forwards, while merge.ff false ensures that explicit merges (like merging a feature branch) always create a merge commit to preserve the history of the integrated branch.

@shashanthk
Copy link
Author

After applying this setting, you may sometimes encounter an error like:

fatal: Not possible to fast-forward, aborting when pulling changes from remote branches.

Do not use git rebase arbitrarily to resolve this issue. Instead, use the appropriate options when pulling changes, as shown below:

git pull origin <branch_name> --no-ff

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment