Read Anthropic’s case study about Graphite Reviewer

Understanding git fast forward merges

Greg Foster
Greg Foster
Graphite software engineer


Note

This guide explains this concept in vanilla Git. For Graphite documentation, see our CLI docs.


Understanding git fast forward merges

One common type of merge operation is the "fast forward" merge. This guide will explain what a fast forward merge is, how it works, and when it's applicable, including detailed examples and explanations of relevant Git terminology.

Join 20,000+ developers at top companies
Stop wrestling with Git commands
The Graphite CLI takes all the pain out of Git, allowing you to ship faster and stop googling Git commands.
main
diff1
diff2

A fast forward in Git occurs during a merge operation when the base branch that's being merged into has no new commits since the feature branch (the branch being merged) was created or last updated. Instead of creating a new "merge commit," Git simply moves the pointer forward, hence the term "fast forward."

Fast-forward merges occur when there is a linear path from the base branch to the target branch, allowing the head of the base branch to be simply moved forward to point to the target branch. The target branch's pointer is moved forward to the head of the source branch if no divergent work has been done on the target branch. This effectively brings the history of the target branch in line with the source branch.

  1. Simpler history: Fast-forward merges keep the repository history linear and clean, which can simplify the understanding and navigation of the history.
  2. Less overhead: No extra merge commits are created, which can be preferable in small projects or projects with fewer parallel developments.
  1. Loss of context: The major downside is the loss of context regarding the feature's development process. Without a dedicated merge commit, it's harder to see the boundary between different features or to identify when a feature was merged.
  2. Complicated reverts: Reverting a specific feature becomes more difficult as there's no single merge commit to revert. You would need to individually revert all commits pertaining to that feature.

While Git is an incredibly useful tool, it has many shortcomings, particularly with rebasing, and managing stacked pull requests.

The Graphite CLI simplifies git, handles rebasing automatically, and allows you to create, submit, and stack pull requests right from the command line.

Under the hood, the CLI runs Git to create branches, commits, and metadata, which means you can still use Git in your scripts, tooling, or whenever you feel like it. Read more about installing the Graphite CLI in our docs.

screenshot of the Graphite CLI

Using the --no-ff flag forces a merge commit even when a fast-forward merge would be possible. This approach is preferred in the git-flow mode introduced by Vincent Driessen, for merging feature, release, and hotfix branches back to develop or main branches.

  1. Preserves history: It keeps a clear record of project changes, including the context of feature development and integration.
  2. Easier to manage features: Features can be collaboratively developed and then cleanly merged as a unit, which also simplifies potential reverts to a single merge commit.
  3. Facilitates code review and collaboration: The merge commits provide a natural place for code review and discussions about specific features or fixes.
  1. Complex history: This method introduces additional merge commits that can clutter the project history, potentially making it harder to follow.
  2. More merge conflicts: There is a slightly increased likelihood of encountering merge conflicts that need to be resolved manually, as changes from the base branch are not automatically incorporated into the feature branch during its development.

Let's go through some common scenarios and commands to understand this better.

Assume you have two branches: main and feature. The feature branch was created from main and has new commits, but main has not changed since feature was created.

  1. Check out the main branch:

    Terminal
    git checkout main
  2. Merge the feature branch:

    Terminal
    git merge feature

If a fast forward is possible, you'll see output like:

Terminal
Updating 4a5b6c7..9d8e7f8
Fast-forward
file.txt | 2 ++
1 file changed, 2 insertions(+)

This output shows that the main branch pointer has moved to the point of the feature branch.

Join 20,000+ developers at top companies
The best engineers use Graphite to simplify Git
Engineers at Vercel, Snowflake & The Browser Company are shipping faster and staying unblocked with Graphite.
main
diff1
diff2

Sometimes, you might want to preserve the exact history of changes, including the fact that a feature branch was created and merged. For this, you can disable the fast forward merge:

Terminal
git merge --no-ff feature

This forces Git to create a new merge commit even if a fast forward merge is possible, thus preserving the history of a separate branch and merge.

When you pull changes from a remote repository, you can explicitly ask for a fast forward merge:

Terminal
git pull --ff-only

This command updates your current branch only if the pull can be resolved as a fast forward.

If you want to fast forward your local branch to catch up with changes in another branch (like main), you can use:

Terminal
git checkout feature
git merge main

This will fast forward the feature branch if no divergent changes have been made on feature.

Before attempting a fast forward merge, you might want to check if it's possible:

Terminal
git merge-base --is-ancestor main feature

This command returns 0 (true) if main can be fast forwarded to feature, and 1 (false) otherwise.

The decision between using fast-forward and non-fast-forward merges largely depends on the project's requirements for history visibility and management. For larger, collaborative projects where tracking the development and integration of features separately is crucial, the non-fast-forward approach recommended by the git-flow model is beneficial. It provides explicit markers in the project history that denote the start and end of feature integrations, facilitating easier troubleshooting and version management.

Conversely, for smaller projects or those with a more streamlined development process, fast-forward merges might be preferred for their simplicity and cleaner history. In any case, it's important to choose a strategy that aligns with the team's workflow and project requirements.

Fore more detail on fast forward merging see the official Git documentation.

Graphite
Git stacked on GitHub

Stacked pull requests are easier to read, easier to write, and easier to manage.
Teams that stack ship better software, faster.

Or install our CLI.
Product Screenshot 1
Product Screenshot 2