# Re: [RFC/PATCH] Fast forward strategies allow, never, and only

5 messages from 2008-03-11 to 2008-03-12. Participants: colin@horizon.com, Lars Hjemli, Bruce Stephens, Junio C Hamano.
Thread: https://gitlist.dev/t/12635

## colin@horizon.com, 2008-03-11 09:35

Subject: Re: [RFC/PATCH] Fast forward strategies allow, never, and only
Message-ID: <20080311093553.23191.qmail@science.horizon.com>
URL: https://gitlist.dev/e/20080311093553.23191.qmail%40science.horizon.com

```
> What's lacking is "why this is a good idea".

Seconded.  A long time ago (and I'm too lazy to find a link), Linus
explained why disabling fast-forward merges was almost always a Bad Idea,
and nobody has come up with a good reason why you'd want one since.

But from memory, suppose that you have two developers, each working on
their own branch:

     a--a--a <-- A's head
    /
o--o
    \
     b--b--b <-- B's head

Then suppose that they merge back and forth to get to the same state.
With fast-forward merges, it will go like this:

A merges from B:
     a--a--a
    /       \
o--o         o <-- A's head
    \       /
     b--b--b <-- B's head

Then B merges from A:
     a--a--a
    /       \
o--o         o <-- Both heads
    \       /
     b--b--b


And look, they are in sync and can go on to develop from a common base
version.  Future merges will do nothing.


If, instead, you have every merge generate a commit, then you get:
     a--a--a
    /       \
o--o         o <-- A's head
    \       / \
     b--b--b---o <-- B's head

     a--a--a
    /       \
o--o         o---o <-- A's head
    \       / \ /
     b--b--b---o <-- B's head

     a--a--a
    /       \
o--o         o---o <-- A's head
    \       / \ / \
     b--b--b---o---o <-- B's head

.. and it never ends.  All of the merged commits are identical trees, but
if you insist on creating a new commit object each time, you can generate
an infinite number of bogus commits, and more to the point, A and B will
never actually agree on the current HEAD commit.

With more developers, you can make even more of a mess.

What use does the "--ff=never" option have except to generate this cruft?
Flexibility is useful only as long as it provides the ability to do
something desirable.  There's no point to having a button that should
never be pushed.

```

## Lars Hjemli, 2008-03-11 10:09

Subject: Re: [RFC/PATCH] Fast forward strategies allow, never, and only
Message-ID: <8c5c35580803110309q2474c42q4758d618fca3cea@mail.gmail.com>
URL: https://gitlist.dev/e/8c5c35580803110309q2474c42q4758d618fca3cea%40mail.gmail.com
In-Reply-To: <20080311093553.23191.qmail@science.horizon.com>

```
On Tue, Mar 11, 2008 at 10:35 AM,  <colin@horizon.com> wrote:
> > What's lacking is "why this is a good idea".
>
>  Seconded.  A long time ago (and I'm too lazy to find a link), Linus
>  explained why disabling fast-forward merges was almost always a Bad Idea,
>  and nobody has come up with a good reason why you'd want one since.

The reason for --no-ff was twofold:
* theoretical: when you want to record the integration of a topic branch
* practical: when merging git-svn branches in git, git-svn dcommit
would update the wrong svn 'branch' if the merge was a fast-forward

I originally needed --no-ff due to the 'practical' aspects (I used
git-svn when working with the day-job svn repository), but now that
we've switched to git (Hurray!) I'm still using --no-ff for the
'theoretical' reason: our topic branches tend to be named after
bugtracker tickets, so by recording the merge of such a branch we get
a very explicit note in our git log about when each ticket was
resolved.

YMMV.

--
larsh

```

## Bruce Stephens, 2008-03-11 12:24

Subject: Re: [RFC/PATCH] Fast forward strategies allow, never, and only
Message-ID: <80r6eho3cs.fsf@tiny.isode.net>
URL: https://gitlist.dev/e/80r6eho3cs.fsf%40tiny.isode.net
In-Reply-To: <20080311093553.23191.qmail@science.horizon.com>

```
colin@horizon.com writes:

>> What's lacking is "why this is a good idea".

[...]

> .. and it never ends.  All of the merged commits are identical trees, but
> if you insist on creating a new commit object each time, you can generate
> an infinite number of bogus commits, and more to the point, A and B will
> never actually agree on the current HEAD commit.
>
> With more developers, you can make even more of a mess.
>
> What use does the "--ff=never" option have except to generate this cruft?
> Flexibility is useful only as long as it provides the ability to do
> something desirable.  There's no point to having a button that should
> never be pushed.

IIUC what the new option is about is (optionally) forbidding merges.
So it's orthogonal to the existing --no-ff and --ff merge options.

So you *don't* get that kind of criss-crossing: if you've got a local
commit, the merge fails.  So you have to use rebase.  So it's not
making the history more complex, it's linearizing it.

Now surely you don't always want to do that, but it seems like a very
convenient option that you can generally have on, and switch off when
you intend to do a merge.

```

## Bruce Stephens, 2008-03-11 12:33

Subject: Re: [RFC/PATCH] Fast forward strategies allow, never, and only
Message-ID: <80lk4po2xp.fsf@tiny.isode.net>
URL: https://gitlist.dev/e/80lk4po2xp.fsf%40tiny.isode.net
In-Reply-To: <80r6eho3cs.fsf@tiny.isode.net>

```
Bruce Stephens <bruce.stephens@isode.com> writes:

> colin@horizon.com writes:

[...]

> IIUC what the new option is about is (optionally) forbidding merges.
> So it's orthogonal to the existing --no-ff and --ff merge options.

I'm wrong.  My apologies.

[...]

```

## Junio C Hamano, 2008-03-12 01:57

Subject: Re: [RFC/PATCH] Fast forward strategies allow, never, and only
Message-ID: <7v1w6gbt6n.fsf@gitster.siamese.dyndns.org>
URL: https://gitlist.dev/e/7v1w6gbt6n.fsf%40gitster.siamese.dyndns.org
In-Reply-To: <20080311093553.23191.qmail@science.horizon.com>

```
colin@horizon.com writes:

>      a--a--a
>     /       \
> o--o         o---o <-- A's head
>     \       / \ /
>      b--b--b---o <-- B's head
>
>      a--a--a
>     /       \
> o--o         o---o <-- A's head
>     \       / \ / \
>      b--b--b---o---o <-- B's head
>
> .. and it never ends.  All of the merged commits are identical trees, but
> if you insist on creating a new commit object each time, you can generate
> an infinite number of bogus commits, and more to the point, A and B will
> never actually agree on the current HEAD commit.
>
> With more developers, you can make even more of a mess.
>
> What use does the "--ff=never" option have except to generate this cruft?

Judicious use of non-fast-forward has a justification that is not too
unreasonable.  That is, when you want to treat one lineage of history as
"more special than others".

If your workflow is always to branch from the special branch ("master")
when working on even a miniscule topic and merge that back to "master", if
you happen to have worked only on a single topic and the "master" was
never advanced during the time you worked on that topic, merging the topic
back to "master" will result in a fast-forward.  When you look back that
history, you won't be able to tell where the topic started and ended by
following the ancestry chain of the "master" branch.

Using "never fast forward" policy on such a special branch will be a way
to make sure that all commits on the first-parent ancestry of that special
branch will be merges from something else, and by computing $it^1..$it^2
for a merge commit $it on the special branch, which merges the topic fully
into it, you can tell what commits the topic consisted of.

When you have repeated merges from a topic to that special branch, this
computation needs to be a bit more than just $it^1..$it^2 of the last
merge commit that merges the topic into "master".  E.g. you would have two
"should have been fast forward but artificially made into a real merge for
the purpose of peeing in the snow" like this:

           o---o---o---o---o "topic"
          /     \           \
      ---o-------*-----------* "master"
 
By following the first-parent ancestry of "master", you can tell that the
first two changes on "topic" were accepted earlier and then three fixups
on top were incorporated much later, which is not something you can do if
you allowed fast-forward merge into "master".  Computing this history is
somewhat expensive but it is doable.  You have to follow the commit
ancestry of "topic", and for each commit you find, you would need to see
which commit on the first-parent ancestry of "master" can reach it
(e.g. the three topmost ones on "topic" can be reachable only by the last
merge on "master", while the remaining two can be reached by the previous
merge on "master").

In other words, if there is a globally special "master" history where
everybody meets, forcing an artificial merge can have value.  However, for
this to work, you can never commit anything directly on such a special
"master" branch, because directly committing on "master" is equivalent to
fork a small topic branch that has a single commit on it, and immediately
merging it back with a fast-forward merge to "master".  So an artificial
merge can have value but that value can be had only with a disciplined
workflow.

Last night I pulled a topic from Shawn which was a series of updates to
the bash completion script.  It was based on the tip of 'master' and
resulted in a fast forward.  In git.git circle, it happens that my
"master" history is not special at all.  I have "trivially correct fixups"
directly committed on "master" all the time, and fast-forwarding to the
tip of bash completion updates Shawn collected for me was exactly that,
with only different committer.  So even though I act as the top-level
integrator for git.git history, there was no reason to do non-fast-forward
merge at that point.  My tree is not that special.

On the other hand, I probably _could_ use non-ff to manage "next", which
will fork off of the tip of "master" after every major release.  In order
to treat the first topic that will be merged into "next" just like other
later topics, it should be merged without fast-forward.  The latter topics
will never fast-forward (because topics fork off of "master" or "maint"
and never from "next" itself) but the very first one can (because "master"
and "next" will be at the same at that point), and allowing fast-forward
would mean the first topic after a major release is treated differently
from others.  This is possible only because there is a fairly strict
discipline of not committing anything directly on top of "next" and not
forking off of it.

```
