In bitbucket, for merging PRs we have multiple choises as you can seem from below: Merge Options
and I don't think I completely understand the differences between the last 2 options.
At the moment I think for the rebase + ff we would end up with a linear history without a merge commit, while with rebase + no ff we would have a linear history BUT with a merge commit?
--no-ff
tells git that you want to create a merge commit even when a ff
is possible (IOW, you are currently standing on a commit that is an ancestor [not necessarily a direct parent] of the commit you are trying to merge... in that case git avoids creating a new merge commit and it just does a ff
to that commit instead).
So, imagine this scenario:
* <- (A)
*
*
* <- (HEAD)
*
*
If you run git merge A
there, git will just do:
* <- (HEAD, A)
*
*
*
*
*
You can see how git moved HEAD
to the exact same commit where A
is.... so a fast-forward
, or ff
for short.
If you had used --no-ff
for that merge instead, git would do this:
* <- (HEAD)
|\
| * <- (A)
| *
| *
|/
* # My comment: This is the commit where HEAD was before merging
*
*
And you can see how instead of just moving HEAD
to A
, it created a new merge commit having HEAD@{1}
(aka, after the merge is finished, the commit where HEAD
was before) and A
.
--ff-only
tells git that you want to do the merge only if a ff
is possible. Considering what I said there about what a ff
means, then this option implies that do not want to proceed if the commit that you are on is not an ancestor of the commit you are asking to merge.
So, if you start from here:
* <- (A)
*
*
* <- (HEAD)
*
*
And run git merge --ff-only A
, git will be happy to run it:
* <- (HEAD, A)
*
*
*
*
*
However, if you are like this:
* <- (HEAD)
*
*
| * <- (A)
| *
| *
|/
*
*
*
And you run git merge --ff-only A
, git will complain about it and won't do anything, cause a ff
is not possible.
This would run fine, though: git merge A
. And it would create this:
* <- (HEAD)
|\
* |
* |
* |
| * <- (A)
| *
| *
|/
*
*
*
Now, the rebase part. If you have a branch that has diverged
(and as I explained a --ff-ony
would not work), if you rebase it, that means that you straighten it. Say:
* <- (main)
*
*
| * <- (A)
| *
| *
|/
*
*
*
Here a --ff-only
is impossible. But if you rebase:
* <- (A)
*
*
* <- (main)
*
*
*
*
(I don't think there is a need to explain that those commits after main
are not exactly the same 3 commits that where in main...A
before the rebase took place so not going into that much detail).
Now, if you tried to merge A
into main
at this point, git would do a ff
because now it is possible to do it... and if you would like to see a real merge commit instead of a ff
at that point, then you also provide --no-ff
.