Contents
Git is a standard version control tool. You should definitely use it even for small personal projects. And when it comes to any teamwork, it’s mandatory.
Unfortunately, with default Git configuration we will not always see our work history true. Here we will investigate what is Git fast-forward merge mode behavior, how does it affect repository history, and why one should think about disabling it.
- Speed up everyday work with handy Git aliases
- Simpler Git branch first push with no more errors
- Git fast-forward merge – why you should turn it off
Usual work on branches
In a standard, multi-person work on a single project it’s normal every task is done on a separate branch. Usually, teams and companies follow some version of the Git flow. It:
- prevents people from rewriting each other changes,
- minimizes code conflicts (and personal ones too 😉),
- provides a stable branch with an always-working version of a product,
- and is generally a good idea.
Also, the most common (and simplest) way for merging branches is to do, well, merges (as opposed to rebasing, another possible strategy).
To simulate this let’s create a simple repository with two commits in history.
# create new project
mkdir great-project
cd great-project
git init
# add first commit
echo "Hello" > README.md
git add README.md
git commit -m 'Initial commit'
# add second commit
echo "Hello World" > README.md
git add README.md
git commit -m 'Changed "Hello" to "Hello World"'
Here is how our repository looks right now. It contains two commits on the master
branch.

Now we will start branch with our new feature implementation. We will call this awesome-feature
# create new branch
git checkout -b awesome-feature
# create first commit on branch
echo "lorem" > test.txt
git add test.txt
git commit -m 'Added "lorem" to test.txt'
# create second commit on branch
echo "ipsum" >> test.txt
git add test.txt
git commit -m 'Added "ipsum" to test.txt'
Right now we have two branches, master
awesome-feature
master
awesome-feature

How Git simplifies history
Now it’s time to merge our feature branch master
master
awesome-feature
git checkout master
git merge awesome-feature
If we look at the Git history graph right now, it doesn’t tell us in any way that two of the last four commits were done on a separate branch. Git flattened history, deciding that since no other commits were made master
master

This is problematic out of at least two reasons. Firstly, we don’t see that some of the commits were done in the scope of a common branch, and how was it named. We lose the
Preventing Git fast-forward merges
We can prevent Git from doing fast-forward when we merge --no-ff
(“no fast-forward”) flag. Let’s recreate the same situation in the repository, this time with a branch next-feature
# create new branch
git checkout -b next-feature
# create first commit on branch
echo "dolor" > new.txt
git add new.txt
git commit -m 'Added "dolor" to new.txt'
# create second commit on branch
echo "sit" >> new.txt
git add new.txt
git commit -m 'Added "sit" to new.txt'

When we now do a merge master
git checkout master
git merge --no-ff --no-edit next-feature
This time our Git history looks different. When we did a merge, Git created a merge commit. We can clearly see, even after the merge, that those two commits were done on a separate branch.

Moreover, in the merge commit default message (that we didn’t edit, thanks --no-edit
Disabling fast-forward permanently
To prevent Git fast-forward mode permanently we can disable fast-forward globally. Then we don’t have to remember to use --no-ff
flag for every merge operation.
One important thing to know is that when we pull new changes from the remote repository, Git in fact does a merge operation with the remote branch. So to prevent it from creating a new commit every time we pull changes, which is totally redundant, we have to set two Git configuration parameters.
git config --global merge.ff false
git config --global pull.ff true
This will add configuration parameters ~/.gitconfig
[merge]
ff = false
[pull]
ff = true
Fast-forward on GitHub and GitLab
Worth noticing is that when we merge branches from pull requests on GitHub or GitLab, they are also done in no fast-forward mode. This ensures we have true repository history preserved.
Project-wide merge policy
To have Git history truthful and consistent it requires that all team members are following the same policy and use the same configuration. Various GUI for Git handle this differently, but usually can be configured to follow the same strategy as we discussed. A little bit of team effort at first helps to keep the repository clean, which often benefits on later stages.
Do Catholics use Git Amen command?
Feel free to create it with an alias: https://betterdev.blog/handy-git-aliases/
Use tags to mark update points, if you want to keep track of them (I do, too). At the end of the day, every update is really “on top” of all of the previous updates. A simpler git history that actually shows how one builds upon the previous is easier to understand.
really nice article, helps a lot!