Encuentre una rama de Git que contenga cambios en un archivo determinado

88

Tengo 57 sucursales locales. Sé que hice un cambio en cierto archivo en uno de ellos, pero no estoy seguro de cuál. ¿Existe algún tipo de comando que pueda ejecutar para encontrar qué ramas contienen cambios en un archivo determinado?

Dustin
fuente

Respuestas:

87

Encuentra todas las ramas que contienen un cambio en NOMBRE DE ARCHIVO (incluso si están antes del punto de rama (no registrado))

FILENAME="<filename>"
git log --all --format=%H $FILENAME | while read f; do git branch --contains $f; done | sort -u

Inspeccione manualmente:

gitk --all --date-order -- $FILENAME

Encuentra todos los cambios en FILENAME no combinados con el maestro:

git for-each-ref --format="%(refname:short)" refs/heads | grep -v master | while read br; do git cherry master $br | while read x h; do if [ "`git log -n 1 --format=%H $h -- $FILENAME`" = "$h" ]; then echo $br; fi; done; done | sort -u
Seth Robertson
fuente
¿Necesito reemplazar algo en este comando además de FILENAME? Devuelve los 57 nombres de sucursales.
Dustin
@Dustin: Si las 57 ramas contienen ese nombre de archivo, entonces las 57 ramas contienen un cambio que incluye ese nombre de archivo. Las ramas comienzan desde la revisión más antigua accesible en esa rama, incluso si esa revisión existía antes de que usted creara la rama (la rama se crea retroactivamente en algunos sentidos). Creo que necesitas definir mejor tu problema. ¿Sabes cuál es el cambio? ¿Podría usar git log -Schange …o git log --grep LOGMESSAGE …(con… representando el resto del comando que mencioné)?
Seth Robertson
2
@Dustin: Otra opción es usar gitk --all -- filenameque le mostrará gráficamente todos los cambios en ese archivo. Si puede identificar el compromiso en cuestión, puede usarlo git branch --containspara ver a qué ramas ha migrado el compromiso. Si desea ver en qué rama se creó originalmente la confirmación en cuestión , busque en Google git-what-branch, pero tenga en cuenta que las fusiones de avance rápido pueden ocultar esa información.
Seth Robertson
@Seth: No pude recordar el mensaje de confirmación o el cambio de código exacto. Solo tengo una idea general. El problema es que aún no se ha fusionado con master, por lo que para usar gitk todavía tendría que verificar cada rama para encontrar lo que estoy buscando. Sería bueno si pudiera decir, "git, muéstrame las ramas que hicieron cambios a FILENAME desde su punto de rama"
Dustin
1
Podría escribir la primera línea de manera más eficientegit log --all --format='--contains %H' "$file" | xargs git branch
kojiro
52

Todo lo que necesitas es

git log --all -- path/to/file/filename

Si desea conocer la rama de inmediato, también puede usar:

git log --all --format=%5 -- path/to/file/filename | xargs -I{} -n 1 echo {} found in && git branch --contains {}

Además, si tuvo algún cambio de nombre, es posible que desee incluir --followel comando de registro de Git.

Adam Dymitruk
fuente
15
Eso mostrará las confirmaciones, pero preguntó en qué rama. Agregue un --sourceallí y estará dorado.
Karl Bielefeldt
Gracias, pero no creo que eso muestre todas las ramas para cada confirmación. Pero ayudaría.
Adam Dymitruk
1
Es cierto, pero en este caso en particular solo estaba buscando uno.
Karl Bielefeldt
Por cierto, la ruta de asp.net debe estar en formato como AppName / Controllers / XController.cs
Ali Karaca
8

Parece que esto sigue siendo un problema sin una solución adecuada. No tengo suficientes créditos para comentar, así que aquí está mi pequeña contribución.

La primera solución de Seth Robertson funcionó un poco para mí, pero solo me dio ramas locales, entre las cuales había muchos falsos positivos, probablemente debido a fusiones de la rama estable.

La segunda solución de Adam Dymitruk no funcionó para mí en absoluto. Para empezar, ¿qué es --format =% 5? No es reconocido por git, no pude encontrar nada al respecto y no pude hacer que funcione con otras opciones de formato.

Pero su primera solución combinada con la opción --source y con un simple grep resultó útil:

git log --all --source -- <filename> | grep -o "refs/.*" | sort -u

Esto me proporciona una serie de etiquetas y ramas remotas y una rama local, donde hice los últimos cambios en el archivo. No estoy seguro de cuán completo es esto.

ACTUALIZAR según la solicitud de @nealmcb, ordenando las ramas por cambio más reciente:

Primero, puede cambiar el grep a "refs / heads /.*", lo que le dará solo las ramas locales. Si solo hay unas pocas ramas, puede examinar la última confirmación de cada una de esta manera:

git log -1 <branch> -- <filename>

Si hay más ramas y realmente desea automatizar esto, puede combinar los dos comandos usando xargs, formato de registro de git y otro tipo en esta línea:

git log --all --source -- <filename> | grep -o "refs/heads/.*" | sort -u | xargs -I '{}' git log -1 --format=%aI%x20%S '{}' -- <filename> | sort -r

Esto dará como resultado una salida como esta:

2020-05-07T15:10:59+02:00 refs/heads/branch1
2020-05-05T16:11:52+02:00 refs/heads/branch3
2020-03-27T11:45:48+00:00 refs/heads/branch2
Simplón
fuente
Genial, gracias. Ahora, ¿qué tal ordenar por cambio más reciente?
nealmcb
1

Sé que esta pregunta es antigua, pero seguí volviendo a ella antes de desarrollar mi propia solución. Siento que es más elegante y gracias al uso de la base de fusión se filtran las ramas no deseadas.

#!/bin/bash

file=$1
base=${2:-master}

b=$(tput bold) # Pretty print
n=$(tput sgr0)

echo "Searching for branches with changes to $file related to the $base branch"

# We look through all the local branches using plumbing
for branch in $(git for-each-ref --format='%(refname:short)' refs/heads/); do
  # We're establishing a shared ancestor between base and branch, to only find forward changes.  
  merge_base=$(git merge-base $base $branch)
  # Check if there are any changes in a given path.
  changes=$(git diff $merge_base..$branch --stat -- $file)

  if [[ ! -z $changes ]]; then
    echo "Branch: ${b}$branch${n} | Merge Base: $merge_base"
    # Show change statistics pretty formatted
    git diff $merge_base..$branch --stat -- $file
  fi
done

Si lo coloca en PATH como git-find-changes(con permisos ejecutables), puede llamarlo congit find-changes /path

Salida de ejemplo para git find-changes app/models/

Branch: update_callbacks | Merge base: db07d23b5d9600d88ba0864aca8fe79aad14e55b
 app/models/api/callback_config.rb | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)
Branch: repackaging | Merge base: 76578b9b7ee373fbe541a9e39cf93cf5ff150c73
 app/models/order.rb                 | 38 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)
Marcin Raczkowski
fuente
Guión muy interesante. Voto a favor. ¿Es mostrar cambios o buscar cambios?
VonC
@VonC gracias, uso corregido, debería encontrarse en todas partes
Marcin Raczkowski
0

El siguiente es un método de fuerza bruta poco elegante, pero espero que funcione. Asegúrese de haber escondido primero los cambios no confirmados, ya que cambiará la rama en la que se encuentra actualmente.

for branch in $(git for-each-ref --format="%(refname:short)" refs/heads); do
    git checkout $branch && git grep SOMETHING
done
blanqueamiento
fuente
1
Porque si conoces el cambio, puedes usarlogit log -S
Seth Robertson
¿Qué caparazón estás asumiendo? ¿Intento?
Peter Mortensen
Sin bashismos, eso debería funcionar en POSIX. Sin for-each-refembargo, no estoy seguro de que aún respete esa shortbandera. Dicho esto, ignore mi respuesta; las otras respuestas son mejores.
whiteinge