Summary: git merge-base --is-ancestor checks if one commitment is the ancestor of another (where commits are considered their own ancestors, which is a particularly strange form of incest, perhaps :-)). Since the branch label can only be redirected using git merge , when the current branch ( HEAD ) indicates a commit that is the ancestor of another commit, we can use this to determine if git merge quickly execute -forward.
It sounds like you wanted this to be posted as an answer, so I converted it to a working git alias that you can add to your global git configuration. The alias is a bit long and complex, and it's probably best to just cut and paste it into your git aliases section:
canff = "!f() { if [ $# -gt 0 ]; then b=\"$1\"; git rev-parse -q --verify \"$b^{commit}\" >/dev/null || { printf \"%s: not a valid commit specifier\n\" \"$b\"; return 1; } else b=$(git rev-parse --symbolic-full-name --abbrev-ref @{u}) || return $?; fi; if git merge-base --is-ancestor HEAD \"$b\"; then echo \"merge with $b can fast-forward\"; else echo \"merge with $b cannot fast-forward\"; fi; }; f"
Here is the same thing that is written as a shell script, in a more readable way and some comments:
#! /bin/sh # # canff - test whether it is possible to fast-forward to # a given commit (which may be a branch name). If given # no arguments, find the upstream of the current (HEAD) branch. # First, define a small function to print the upstream name # of the current branch. If no upstream is set, this prints a # message to stderr and returns with failure (nonzero). upstream_name() { git rev-parse --symbolic-full-name --abbrev-ref @{u} } # Now define a function to detect fast-forward-ability. canff() { local b # branch name or commit ID if [ $# -gt 0 ]; then # at least 1 argument given b="$1" # make sure it is or can be converted to a commit ID. git rev-parse -q --verify "$b^{commit}" >/dev/null || { printf "%s: not a valid commit specifier\n" "$b" return 1 } else # no arguments: find upstream, or bail out b=$(upstream_name) || return $? fi # now test whether git merge --ff-only could succeed on $b if git merge-base --is-ancestor HEAD "$b"; then echo "merge with $b can fast-forward" else echo "merge with $b cannot fast-forward" fi }
The shell script just needs the main section to drive it, which is the f call after the alias. The alias itself simply dug up everything: from canff and upstream_name to one line. The git configuration file rules require that the entire alias be enclosed in double quotes, which in turn requires that all internal double quotes be converted to double-backslash sequences.
(I also pulled out the local b statement, because as an alias, it starts a new shell instance each time, so hygiene of the name variable becomes unimportant.)
(In fact, you can write an alias as multiple lines. Just a prefix for each new line with a backslash. However, this alias is so complex that it looks too ugly, so I ended up leaving only one large line.)