Encuentra commit de fusión que incluye un commit específico

183

Imagine la siguiente historia:

       c---e---g--- feature
      /         \
-a---b---d---f---h--- master

¿Cómo puedo encontrar cuando commit "c" se ha fusionado en master (es decir, find merge commit "h")?

Guillaume Morin
fuente

Respuestas:

158

Su ejemplo muestra que la rama featureaún está disponible.

En ese caso hes el último resultado de:

git log master ^feature --ancestry-path

Si la rama featureya no está disponible, puede mostrar las confirmaciones de fusión en la línea del historial entre cy master:

git log <SHA-1_for_c>..master --ancestry-path --merges

Esto sin embargo, también le mostrará todas las fusiones que sucedieron después h, y entre ey gsobre feature.


Comparando el resultado de los siguientes comandos:

git rev-list <SHA-1_for_c>..master --ancestry-path

git rev-list <SHA-1_for_c>..master --first-parent

le dará el SHA-1 hcomo la última fila en común.

Si lo tiene disponible, puede usarlo comm -1 -2en estos resultados. Si está en msysgit, puede usar el siguiente código perl para comparar:

perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/'  file1 file2

(código perl de http://www.cyberciti.biz/faq/command-to-display-lines-common-in-files/ , que lo tomó de "alguien en el grupo de noticias comp.unix.shell").

Vea la sustitución del proceso si desea que sea una línea.

Gauthier
fuente
44
Incluso si tiene comunicación, no puede usarla porque la salida del comando "git rev-list" no está ordenada lexicográficamente. Por supuesto, podría ordenar la salida de cada comando antes de buscar líneas comunes, pero la confirmación deseada no sería necesariamente la última. Así que creo que es necesario algo como el comando perl (aunque oscuro).
mhagger
12
Acabo de escribir un script git-when-merged que implementa esta sugerencia (con bastantes otras características). Ver github.com/mhagger/git-when-merged
mhagger
2
Supongamos que en algún momento masterse fusionó featuree inmediatamente featurese fusionó mastercomo un avance rápido (la punta de los featurereemplazos master). ¿Causaría eso --first-parentdevolver al padre equivocado?
Kelvin
44
Lo intenté comm -1 -2pero no funcionó. commsolo funciona en líneas ordenadas. (El perl one-liner funciona, aunque no pude leerlo)
Domon
2
Esto es un poco tarde, pero se agrega para que las personas puedan encontrarlo útil, ya que git de forma nativa no proporciona esto. Hay algunos casos de esquinas que creo que esta respuesta no maneja (vea mi respuesta a continuación stackoverflow.com/a/43716029/58678 que maneja la mayoría de los casos de esquinas). Estos son los casos de esquina: git find-merge h master(no devuelve nada pero debería devolver h), git find-merge d master(devuelve f pero debería devolver d), git find-merge c feature(devuelve e pero debería devolver g).
FELIZ
133

Agregue esto a su ~/.gitconfig:

[alias]
    find-merge = "!sh -c 'commit=$0 && branch=${1:-HEAD} && (git rev-list $commit..$branch --ancestry-path | cat -n; git rev-list $commit..$branch --first-parent | cat -n) | sort -k2 -s | uniq -f1 -d | sort -n | tail -1 | cut -f2'"
    show-merge = "!sh -c 'merge=$(git find-merge $0 $1) && [ -n \"$merge\" ] && git show $merge'"

Entonces puedes usar los alias de esta manera:

# current branch
git find-merge <SHA-1>
# specify master
git find-merge <SHA-1> master

Para ver el mensaje de confirmación de fusión y otros detalles, úselo git show-mergecon los mismos argumentos.

(Basado en la respuesta de Gauthier . Gracias a Rosen Matev y javabrett por corregir un problema con sort).

robinst
fuente
66
¡Hermoso! Esta es la mejor solución directa.
N Jones
1
Tenga en cuenta que sort -k2 | uniq -f1 -d | sort -n | tail -1 | cut -f2no está encontrando correctamente la última fila en común. Aquí hay un ejemplo donde falla.
Rosen Matev el
1
@RosenMatev Se encuentra 16db9fef5c581ab0c56137d04ef08ef1bf82b0b7aquí cuando lo ejecuto en su pasta, ¿no es eso lo que se espera? ¿En qué sistema operativo estás?
robinst
1
@robinst, el tuyo es correcto. Tengo "(GNU coreutils) 8.4" y para mí lo encuentra29c40c3a3b33196d4e79793bd8e503a03753bad1
Rosen Matev
2
@powlo: Nada, es solo un comando en lugar de dos por conveniencia.
robinst
28

git-get-merge localizará y mostrará la confirmación de fusión que está buscando:

pip install git-get-merge
git get-merge <SHA-1>

El comando sigue a los hijos del commit dado hasta que se encuentre una fusión en otra rama (presumiblemente master).

Jian
fuente
22

Es decir, para resumir la publicación de Gauthier:

perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/' <(git rev-list --ancestry-path <SHA-1_for_c>..master) <(git rev-list --first-parent <SHA-1_for_c>..master) | tail -n 1

EDITAR: debido a que utiliza la sustitución de proceso " <()", no es compatible con POSIX y es posible que no funcione con su shell. Funciona con basho zshsin embargo.

Totor
fuente
44
No suelo copiar / pegar código de Internet, ¡pero cuando lo hago funciona perfectamente! Gracias Totor por permitirme no pensar.
Ben
2
@ TheodoreR.Smith desafortunadamente, la sintaxis <()no es compatible con POSIX. Necesita usar bash, zsho una sustitución de proceso de soporte de shell . Edité mi respuesta en consecuencia.
Totor
No funciono para mi. Creo que mi rama de características primero se fusionó con otra rama antes de fusionarse con master. Eso podría explicar por qué sus comandos resultan en "Fusionar rama 'release_branch'" en lugar de "Fusionar rama 'feature_branch'".
Tim Kuipers
14

Necesitaba hacer esto, y de alguna manera lo encontré git-when-merged(que en realidad hace referencia a esta pregunta SO, pero Michael Haggerty nunca agregó una referencia a su muy lindo script Python aquí). Entonces ahora tengo.

Alex Dupuy
fuente
De hecho, llegué a esta pregunta desde su página.
Flavio
12

Sobre la base de la gran respuesta de Gauthier, no necesitamos usar commpara comparar las listas. Como estamos buscando el último resultado en el --ancestry-pathque también está --first-parent, simplemente podemos buscar el último en la salida del primero:

git rev-list <SHA>..master --ancestry-path | grep -f <(git rev-list <SHA>..master --first-parent) | tail -1

O para algo ágil y reutilizable, aquí hay una función para resaltar .bashrc:

function git-find-merge() {
  git rev-list $1..master --ancestry-path | grep -f <(git rev-list $1..master --first-parent) | tail -1
}
maldad
fuente
Esto funcionó para mí. commno funcionaba cuando las entradas no estaban ordenadas.
FELIZ
4

Para la multitud de Ruby, hay git-wherede . Muy fácil.

$ gem install git-whence
$ git whence 1234567
234557 Merge pull request #203 from branch/pathway
acero
fuente
4

Yo uso el script bash que coloco en el camino ~/bin/git-find-merge. Se basa en la respuesta de Gauthier y la respuesta de evilstreak con algunos retoques para manejar casos de esquina. commse lanza cuando las entradas no están ordenadas. grep -ffunciona perfectamente.

Casos de esquina:

  • Si commit es un commit de fusión en la ruta del primer padre de la rama, entonces devuelve commit.
  • Si commit es un commit NO FUSIBLE en la ruta del primer padre de la rama, luego devuelva la rama. Es una combinación ff o commit solo está en la rama y no hay una buena manera de descubrir el commit correcto.
  • Si commit y branch son iguales, entonces devuelve commit.

~/bin/git-find-merge guión:

#!/bin/bash

commit=$1
if [ -z $commit ]; then
    echo 1>&2 "fatal: commit is required"
    exit 1
fi
commit=$(git rev-parse $commit)
branch=${2-@}

# if branch points to commit (both are same), then return commit
if [ $commit == $(git rev-parse $branch) ]; then
    git log -1 $commit
    exit
fi

# if commit is a merge commit on first-parent path of branch,
# then return commit
# if commit is a NON-merge commit on first-parent path of branch,
# then return branch as it's either a ff merge or commit is only on branch
# and there is not a good way to figure out the right commit
if [[ $(git log --first-parent --pretty='%P' $commit..$branch | \
    cut -d' ' -f1 | \
    grep $commit | wc -l) -eq 1 ]]; then
    if [ $(git show -s --format="%P" $commit | wc -w) -gt 1 ]; then
        # if commit is a merge commit
        git log -1 $commit
    else
        # if commit is a NON-merge commit
        echo 1>&2 ""
        echo 1>&2 "error: returning the branch commit (ff merge or commit only on branch)"
        echo 1>&2 ""
        git log -1 $branch
    fi
    exit
fi

# 1st common commit from bottom of first-parent and ancestry-path
merge=$(grep -f \
    <(git rev-list --first-parent  $commit..$branch) \
    <(git rev-list --ancestry-path $commit..$branch) \
        | tail -1)
if [ ! -z $merge ]; then
    git log -1 $merge
    exit
fi

# merge commit not found
echo 1>&2 "fatal: no merge commit found"
exit 1

Lo que me permite hacer esto:

(master)
$ git find-merge <commit>    # to find when commit merged to current branch
$ git find-merge <branch>    # to find when branch merged to current branch
$ git find-merge <commit> pu # to find when commit merged to pu branch

Este script también está disponible en mi github .

FELICIDAD
fuente
2
git log --topo-order

Luego busque la primera fusión antes de la confirmación.

romano
fuente
1

Mi versión rubí de la idea de @ robinst funciona dos veces más rápido (lo cual es importante cuando busco un commit muy antiguo).

find-commit.rb

commit = ARGV[0]
master = ARGV[1] || 'origin/master'

unless commit
  puts "Usage: find-commit.rb commit [master-branch]"
  puts "Will show commit that merged <commit> into <master-branch>"
  exit 1
end

parents = `git rev-list #{commit}..#{master} --reverse --first-parent --merges`.split("\n")
ancestry = `git rev-list #{commit}..#{master} --reverse --ancestry-path --merges`.split("\n")
merge = (parents & ancestry)[0]

if merge
  system "git show #{merge}"
else
  puts "#{master} doesn't include #{commit}"
  exit 2
end

Puedes usarlo así:

ruby find-commit.rb SHA master
Kaplan Ilya
fuente
0

Puedes probar algo como esto. La idea es iterar a través de todas las confirmaciones de fusión y ver si la confirmación "c" es accesible desde una de ellas:

$ git log --merges --format='%h' master | while read mergecommit; do
  if git log --format='%h' $mergecommit|grep -q $c; then
    echo $mergecommit;
    break
  fi
done
Holygeek
fuente
Esto es lo mismo que:git rev-list <SHA-1_for_c>..master --ancestry-path --merges
Eugen Konkov