Determine si una fusión se resolverá mediante un avance rápido

Quiero saber si una fusión particular se resolverá mediante "avance rápido" o no antes de ejecutar el command de fusión.

Sé que puedo solicitar específicamente que la fusión no se resuelva mediante "avance rápido" (usando la --no-ff ). O que puedo intentar resolver la fusión solo por avance rápido (usando la opción --ff ).

Pero a veces quiero saber si una fusión en particular va a resolverse mediante un avance rápido antes de ejecutarlo. Me doy count de que, en teoría, puedo resolverlo explorando el tree de la historia. Y también me doy count de que podría ejecutar la fusión y ver qué sucede, pero esto se vuelve problemático si luego decido que preferiría que la fusión se resuelva de la otra manera, ya que tengo que deshacer la fusión (al restablecer las tags de la twig en el ref-log) y hacerlo de nuevo.

NOTA: La pregunta --dry-run ( ¿Hay una opción git-merge -dry-run? ) Es mucho más sobre ver qué conflictos de fusión pueden existir en una combinación, y no sobre fusiones que pueden resolverse a través de un avance rápido. .

Resumen: git merge-base --is-ancestor testing si un commit es un ancestro de otro (donde se considera que los commits son sus propios ancestros, lo cual es una forma de incesto particularmente extraña, quizás :-)). Dado que una label de bifurcación solo se puede reenviar rápidamente mediante la git merge cuando la bifurcación actual ( HEAD ) apunta a una confirmación que es un ancestro de la otra confirmación, podemos usar esto para determinar si git merge podría hacer una operación de avance rápido.

Parece que quería que esto se publicara como respuesta, por lo que lo convertí en un git alias funcional, que puede poner en su configuration global de git. El alias es un poco largo y complicado, y probablemente sería mejor cortarlo y pegarlo en la sección de alias de configuration de git:

 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" 

Aquí está lo mismo escrito como un script de shell, de una manera más legible, y algunos comentarios:

 #! /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 } 

El script de shell solo necesita una sección principal para conducirlo, que es la llamada a f después del alias. El alias en sí mismo simplemente canff todo de canff y upstream_name en una sola línea. Las reglas del file de configuration de Git requieren que el alias completo se comience con comillas dobles, lo que a su vez requiere que todas las comillas dobles internas se conviertan en secuencias de comillas invertidas y comillas dobles.

(También saqué el enunciado local b , ya que como alias, esto dispara una nueva instancia del shell cada vez, por lo que la higiene del nombre variable deja de ser importante.)

(De hecho, es posible escribir el alias como líneas múltiples. Simplemente prefija cada línea nueva con barra invertida. Sin embargo, este alias es tan complejo que se ve feo de esa manera también, así que terminé dejándolo solo en una línea grande).

Puede probar si git merge-base <branch1> <branch2> es igual a git rev-parse <branch1> . Si es igual, a ff merge o ya está actualizado cuando ejecuta git merge <branch1> <branch2> . Si no, una fusión no-ff.

 function isff(){ a=$(git merge-base $1 $2) b=$(git rev-parse $1) c=$(git rev-parse $2) if [[ "$b" == "$c" ]] || [[ "$a" == "$c" ]];then echo merge dry run: already up-to-date return fi if [ "$a" == "$b" ];then echo merge dry run: a fast forward merge else echo merge dry run: a non fast forward merge fi } 

isff master topic

Hay una pregunta similar, pero no exactamente igual, que se hace aquí: ¿Hay una opción git-merge –dry-run?

La primera respuesta parece que podría ser lo que estás buscando.

Específicamente, una merge con la bandera --no-commit y usando --abort cuando ya ha visto suficiente y quiere regresar y hacer la fusión real.

Al ver la pregunta a la que se refirió @ Ashwin-nair, encontré lo que creo que es una respuesta.

por

 git checkout MY_BRANCH git merge OTHER_BRANCH 

Si el resultado de

 git branch --contains MY_BRANCH 

contiene OTHER_BRANCH , luego una fusión con OTHER_BRANCH se puede resolver mediante el avance rápido.