¿Hay alguna manera de averiguar de qué rama proviene un commit dado su valor hash SHA-1 ?
Puntos de bonificación si puedes decirme cómo lograr esto usando Ruby Grit.
¿Hay alguna manera de averiguar de qué rama proviene un commit dado su valor hash SHA-1 ?
Puntos de bonificación si puedes decirme cómo lograr esto usando Ruby Grit.
Respuestas:
Si bien Dav tiene razón en que la información no se almacena directamente, eso no significa que nunca pueda averiguarlo. Aquí hay algunas cosas que puedes hacer.
git branch -a --contains <commit>
Esto le dirá todas las ramas que tienen el compromiso dado en su historia. Obviamente, esto es menos útil si la confirmación ya se ha fusionado.
Si está trabajando en el repositorio en el que se realizó la confirmación, puede buscar en los reflogs la línea de esa confirmación. Los registros de más de 90 días son eliminados por git-gc, por lo que si la confirmación es demasiado antigua, no la encontrará. Dicho esto, puedes hacer esto:
git reflog show --all | grep a871742
para encontrar cometer a871742. Tenga en cuenta que DEBE utilizar los primeros 7 dígitos abreviados de la confirmación. La salida debería ser algo como esto:
a871742 refs/heads/completion@{0}: commit (amend): mpc-completion: total rewrite
indicando que la confirmación se realizó en la rama "finalización". El resultado predeterminado muestra hashes de confirmación abreviados, así que asegúrese de no buscar el hash completo o no encontrará nada.
git reflog show
en realidad es solo un alias para git log -g --abbrev-commit --pretty=oneline
, así que si quieres jugar con el formato de salida para hacer diferentes cosas disponibles para grep, ¡ese es tu punto de partida!
Si no está trabajando en el repositorio donde se realizó la confirmación, lo mejor que puede hacer en este caso es examinar los reflogs y encontrar cuándo la confirmación se introdujo por primera vez en su repositorio; con suerte, buscaste la rama a la que estaba comprometido. Esto es un poco más complejo, porque no puedes recorrer el árbol de confirmaciones y los registros a la vez. Desea analizar el resultado de reflog, examinando cada hash para ver si contiene el commit deseado o no.
Esto depende del flujo de trabajo, pero con buenos flujos de trabajo, se realizan confirmaciones en las ramas de desarrollo que luego se fusionan. Puede hacer esto:
git log --merges <commit>..
para ver los commits de fusión que tienen el commit dado como antepasado. (Si la confirmación solo se fusionó una vez, la primera debería ser la combinación que está buscando; de lo contrario, tendrá que examinar algunas, supongo). El mensaje de confirmación de combinación debe contener el nombre de la rama que se fusionó.
Si desea poder contar con esto, puede usar la --no-ff
opción git merge
para forzar la creación de confirmación de fusión incluso en el caso de avance rápido. (Sin embargo, no se ponga demasiado ansioso. Eso podría volverse confuso si se usa en exceso). La respuesta de VonC a una pregunta relacionada elabora útilmente este tema.
git describe
suficiente, ya que las etiquetas (anotadas) se pueden ver como más significativas que las ramas.
--no-ff
opción para asegurarse de que siempre haya una confirmación de fusión, de modo que siempre pueda rastrear la ruta de una confirmación dada a medida que se fusiona con el maestro.
-a
bandera al primer comando, por ejemplogit branch -a --contains <commit>
-r
.
merge --no-ff
para registrar de manera confiable el nombre de la sucursal cuando se fusione. Pero, de lo contrario, piense en los nombres de las ramas como etiquetas cortas temporales y confirme las descripciones como permanentes. "¿Con qué nombre corto nos referimos a esto durante el desarrollo?" realmente no debería ser una pregunta tan importante como "¿qué hace este compromiso?"
Este simple comando funciona como un encanto:
git name-rev <SHA>
Por ejemplo (donde test-branch es el nombre de la rama):
git name-rev 651ad3a
251ad3a remotes/origin/test-branch
Incluso esto funciona para escenarios complejos, como:
origin/branchA/
/branchB
/commit<SHA1>
/commit<SHA2>
Aquí git name-rev commit<SHA2>
vuelve branchB .
git name-rev --name-only <SHA>
más útil para obtener solo el nombre de la sucursal. Mi pregunta ... ¿Puede devolver más de una rama en cualquier circunstancia?
Actualización de diciembre de 2013:
git-what-branch
(El script de Perl, ver más abajo) ya no parece mantenerse.git-when-merged
es una alternativa escrita en Python que me funciona muy bien.
Se basa en " Buscar confirmación de fusión que incluye una confirmación específica ".
git when-merged [OPTIONS] COMMIT [BRANCH...]
Encuentre cuándo una confirmación se fusionó en una o más ramas.
Encuentre la confirmación de fusión que se introdujoCOMMIT
en las RAMAS especificadas.Específicamente, busque el commit más antiguo en el historial del primer padre
BRANCH
que contiene elCOMMIT
como ancestro.
Respuesta original de septiembre de 2010:
Sebastien Douche simplemente twitteó (16 minutos antes de esta respuesta SO):
git-what-branch : descubre en qué rama se encuentra un commit o cómo llegó a una rama con nombre
Este es un script de Perl de Seth Robertson que parece muy interesante:
SINOPSIS
git-what-branch [--allref] [--all] [--topo-order | --date-order ]
[--quiet] [--reference-branch=branchname] [--reference=reference]
<commit-hash/tag>...
VISIÓN GENERAL
Indíquenos (de manera predeterminada) la ruta causal más temprana de los commits y las fusiones para hacer que el commit solicitado llegue a una rama con nombre. Si se realizó una confirmación directamente en una rama con nombre, obviamente esa es la ruta más temprana.
Por ruta causal más temprana, nos referimos a la ruta que se fusionó en una rama con nombre la primera, por tiempo de confirmación (a menos que
--topo-order
se especifique).ACTUACIÓN
Si muchas ramas (por ejemplo, cientos) contienen la confirmación, el sistema puede tardar mucho tiempo (para una confirmación particular en el árbol de Linux, tardó 8 segundos en explorar una rama, pero había más de 200 ramas candidatas) para rastrear el camino a cada compromiso
La selección de un particular--reference-branch --reference tag
para examinar será cientos de veces más rápida (si tiene cientos de ramas candidatas).EJEMPLOS
# git-what-branch --all 1f9c381fa3e0b9b9042e310c69df87eaf9b46ea4
1f9c381fa3e0b9b9042e310c69df87eaf9b46ea4 first merged onto master using the following minimal temporal path:
v2.6.12-rc3-450-g1f9c381 merged up at v2.6.12-rc3-590-gbfd4bda (Thu May 5 08:59:37 2005)
v2.6.12-rc3-590-gbfd4bda merged up at v2.6.12-rc3-461-g84e48b6 (Tue May 3 18:27:24 2005)
v2.6.12-rc3-461-g84e48b6 is on master
v2.6.12-rc3-461-g84e48b6 is on v2.6.12-n
[...]
Este programa no tiene en cuenta los efectos de elegir el compromiso de interés, solo fusiona las operaciones.
git-what-branch
Parece que ya no se mantiene. git-when-merged es una alternativa escrita en Python que funciona muy bien para mí.
Por ejemplo, para encontrar que c0118fa
commit vino de redesign_interactions
:
* ccfd449 (HEAD -> develop) Require to return undef if no digits found
* 93dd5ff Merge pull request #4 from KES777/clean_api
|\
| * 39d82d1 Fix tc0118faests for debugging debugger internals
| * ed67179 Move &push_frame out of core
| * 2fd84b5 Do not lose info about call point
| * 3ab09a2 Improve debugger output: Show info about emitted events
| * a435005 Merge branch 'redesign_interactions' into clean_api
| |\
| | * a06cc29 Code comments
| | * d5d6266 Remove copy/paste code
| | * c0118fa Allow command to choose how continue interaction
| | * 19cb534 Emit &interact event
Deberías correr:
git log c0118fa..HEAD --ancestry-path --merges
Y desplácese hacia abajo para encontrar la última confirmación de fusión . Cual es:
commit a435005445a6752dfe788b8d994e155b3cd9778f
Merge: 0953cac a06cc29
Author: Eugen Konkov
Date: Sat Oct 1 00:54:18 2016 +0300
Merge branch 'redesign_interactions' into clean_api
Actualizar
O solo un comando:
git log c0118fa..HEAD --ancestry-path --merges --oneline --color | tail -n 1
git merge -m"Any String Here"
, oscurecerá la información de la sucursal de origen y destino.
Merge: f6b70fa d58bdcb
. Puede nombrar su compromiso de fusión como lo desee. No tendré materia
git branch --contains <ref>
es el comando de "porcelana" más obvio para hacer esto. Si desea hacer algo similar con solo comandos de "plomería":
COMMIT=$(git rev-parse <ref>) # expands hash if needed
for BRANCH in $(git for-each-ref --format "%(refname)" refs/heads); do
if $(git rev-list $BRANCH | fgrep -q $COMMIT); then
echo $BRANCH
fi
done
(crosspost de esta respuesta SO )
khichar.anil cubrió la mayor parte de esto en su respuesta.
Solo estoy agregando la bandera que eliminará las etiquetas de la lista de nombres de revisión. Esto nos da:
git name-rev --name-only --exclude=tags/* $SHA
La opción de un hombre pobre es utilizar la herramienta tig
1 sobre HEAD
, buscar la confirmación, y luego seguir visualmente la línea desde que cometen una copia de seguridad hasta que una combinación de confirmación se ve. El mensaje de fusión predeterminado debe especificar en qué rama se fusiona y dónde :)
1 Tig es una interfaz de modo de texto basada en ncurses para Git. Funciona principalmente como un navegador de repositorio Git, pero también puede ayudar a organizar los cambios para confirmar a nivel de fragmento y actuar como un localizador para la salida de varios comandos Git.
Como experimento, hice un enlace posterior a la confirmación que almacena información sobre la rama actualmente desprotegida en los metadatos de confirmación. También modifiqué ligeramente gitk para mostrar esa información.
Puede verificarlo aquí: https://github.com/pajp/branch-info-commits
Trato con el mismo problema ( canalización de múltiples ramas de Jenkins ): tener solo información de confirmación e intentar encontrar un nombre de sucursal de donde proviene originalmente esta confirmación. Debe funcionar para sucursales remotas, las copias locales no están disponibles.
Esto es con lo que trabajo:
git rev-parse HEAD | xargs git name-rev
Opcionalmente, puede quitar la salida:
git rev-parse HEAD | xargs git name-rev | cut -d' ' -f2 | sed 's/remotes\/origin\///g'
Creo que alguien debería enfrentar el mismo problema que no puede encontrar la rama, aunque en realidad existe en una rama.
Será mejor que hagas todo primero:
git pull --all
Luego haga la búsqueda de sucursal:
git name-rev <SHA>
o:
git branch --contains <SHA>
Si el OP está tratando de determinar el historial que atravesó una rama cuando se creó una confirmación particular ("averigüe de qué rama proviene una confirmación dado su valor hash SHA-1"), entonces sin el registro no hay ningún registros en la base de datos de objetos Git que muestra qué rama nombrada estaba vinculada a qué historial de confirmación.
(Publiqué esto como respuesta en respuesta a un comentario).
Esperemos que este script ilustre mi punto:
rm -rf /tmp/r1 /tmp/r2; mkdir /tmp/r1; cd /tmp/r1
git init; git config user.name n; git config user.email e@x.io
git commit -m"empty" --allow-empty; git branch -m b1; git branch b2
git checkout b1; touch f1; git add f1; git commit -m"Add f1"
git checkout b2; touch f2; git add f2; git commit -m"Add f2"
git merge -m"merge branches" b1; git checkout b1; git merge b2
git clone /tmp/r1 /tmp/r2; cd /tmp/r2; git fetch origin b2:b2
set -x;
cd /tmp/r1; git log --oneline --graph --decorate; git reflog b1; git reflog b2;
cd /tmp/r2; git log --oneline --graph --decorate; git reflog b1; git reflog b2;
El resultado muestra la falta de alguna forma de saber si el commit con 'Add f1' vino de la rama b1 o b2 del clon remoto / tmp / r2.
(Últimas líneas de la salida aquí)
+ cd /tmp/r1
+ git log --oneline --graph --decorate
* f0c707d (HEAD, b2, b1) merge branches
|\
| * 086c9ce Add f1
* | 80c10e5 Add f2
|/
* 18feb84 empty
+ git reflog b1
f0c707d b1@{0}: merge b2: Fast-forward
086c9ce b1@{1}: commit: Add f1
18feb84 b1@{2}: Branch: renamed refs/heads/master to refs/heads/b1
18feb84 b1@{3}: commit (initial): empty
+ git reflog b2
f0c707d b2@{0}: merge b1: Merge made by the 'recursive' strategy.
80c10e5 b2@{1}: commit: Add f2
18feb84 b2@{2}: branch: Created from b1
+ cd /tmp/r2
+ git log --oneline --graph --decorate
* f0c707d (HEAD, origin/b2, origin/b1, origin/HEAD, b2, b1) merge branches
|\
| * 086c9ce Add f1
* | 80c10e5 Add f2
|/
* 18feb84 empty
+ git reflog b1
f0c707d b1@{0}: clone: from /tmp/r1
+ git reflog b2
f0c707d b2@{0}: fetch origin b2:b2: storing head
git log 80c10e5..HEAD --ancestry-path --merges --oneline --color | tail -n 1
y los git log 086c9ce..HEAD --ancestry-path --merges --oneline --color | tail -n 1
comandos para ambos casos?
$ git log HEAD^1..HEAD --ancestry-path --merges --oneline --color | tail -n 1
que produce376142d merge branches
y $ git log HEAD^2..HEAD --ancestry-path --merges --oneline --color | tail -n 1
qué rendimiento 376142d merge branches
: que muestra el resumen de confirmación de fusión, que (como estaba afirmando) se puede sobrescribir cuando se crea la fusión, posiblemente ofuscando el historial de ramificación de la fusión.
Use lo siguiente si le interesan los estados de salida de shell:
branch-current
- el nombre de la sucursal actualbranch-names
- nombres de sucursal limpios (uno por línea)branch-name
- Asegúrese de que solo se devuelva una rama de branch-names
Ambos branch-name
y branch-names
aceptan un commit como argumento, y por defecto HEAD
si no se da ninguno.
branch-current = "symbolic-ref --short HEAD" # https://stackoverflow.com/a/19585361/5353461
branch-names = !"[ -z \"$1\" ] && git branch-current 2>/dev/null || git branch --format='%(refname:short)' --contains \"${1:-HEAD}\" #" # https://stackoverflow.com/a/19585361/5353461
branch-name = !"br=$(git branch-names \"$1\") && case \"$br\" in *$'\\n'*) printf \"Multiple branches:\\n%s\" \"$br\">&2; exit 1;; esac; echo \"$br\" #"
% git branch-name eae13ea
master
% echo $?
0
0
.% git branch-name 4bc6188
Multiple branches:
attempt-extract
master%
% echo $?
1
1
.Debido al estado de salida, estos se pueden construir de forma segura. Por ejemplo, para obtener el control remoto utilizado para buscar:
remote-fetch = !"branch=$(git branch-name \"$1\") && git config branch.\"$branch\".remote || echo origin #"
Para encontrar la sucursal local:
grep -lR YOUR_COMMIT .git/refs/heads | sed 's/.git\/refs\/heads\///g'
Para encontrar la rama remota:
grep -lR $commit .git/refs/remotes | sed 's/.git\/refs\/remotes\///g'
Además de buscar en todo el árbol hasta encontrar un hash que coincida, no.