As part our rebase-heavy workflow, I am hoping to use a merge on the master branch. In particular I want to merge only when the topic branch has been rebased onto the most recent master commit thereby making any merge a fast-forward one. I can achieve that by using:
git merge --ff-only
Furthermore, I would like to record the integration of the topic branch with an empty commit which is why I would like to use --no-ff which forces that behaviour:
git merge --no-ff
What I really want, though, is a combination of both: merge only when it's trivial but let me record it anyway. Git thinks that
fatal: You cannot combine --no-ff with --ff-only.
which appears self-evident to some.
git merge --edit --no-ff topicbranch
doesn't achieve the behaviour I want either. So how can I merge with the --no-ff option iff the topic branch in question is rebased to the latest master commit?
UPDATE: Seems like Charles Bailey's answer will do the trick. If you desire to have this rolled into a git alias you could issue this here:
git config --global alias.integrate '!test "$(git merge-base HEAD "$1")" = "$(git rev-parse HEAD)" && git merge --no-ff --edit $1 || echo >&2 "Not up-to-date; refusing to merge, rebase first!"'
A bit of a mouthful but works. Note the force of a commit message edit with option --edit
.
You don't want --ff-only
because you do want to make a merge commit which --ff-only
would inhibit.
The check that you want can be made as a separate check before running the merge command. You could package this into a simple shell function.
E.g.
merge_if_ahead () { if test "$(git merge-base HEAD "$1")" = "$(git rev-parse HEAD)"; then git merge --no-ff "$1" else echo >&2 "Not up to date; refusing to merge" return 1 fi }
I've created an alias that works for my simple tests
git config --global alias.ffmerge '!sh -c "git merge --ff-only $1 && git reset --hard HEAD@{1} && git merge --no-ff $1" -'
It consists of 3 commands, one after each other, that will fail as soon as the first one fails. Explaining each:
git merge --ff-only $1
Will fail if the merge cannot continue as a fast-forward merge. ($1 is the branch name passed in as argument)
git reset --hard HEAD@{1}
If the first command succeeds it means we can do a fast-forward merge but as we want to do a merge commit we'll move back to the state of HEAD just before our (successful) merge. HEAD@{1} is a reflog pointer.
git merge --no-ff $1
Now we'll do the real merge with a merge commit even if the merge can go a head as a fast-forward.
A simple call to git ffmerge
will do the entire thing for us, the only step that can fail is the first and that would leave us in an unchanged working copy as before.
As always with git reset --hard
the working copy should be clean.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With