¿Cómo puedo ver de qué rama se bifurcó otra rama?
Mi repositorio git tiene tres ramas, devel
, stable
y customers/acme_patches
. Hace mucho tiempo, stable
se bifurcó de devel
, y todos la solución de bugs tiene lugar en stable
. De vez en cuando, stable
se fusiona de nuevo en devel
. customers/acme_patches
es una rama con algunos parches específicos del cliente. La rama no se fusionó con devel
y stable
.
Un poco de arte ASCII para ilustrar el escenario:
o---o---o customers/acme_patches? / o---o---1---o---o---o stable / \ \ o---o---o---2---o---o---o---o devel \ o---o---o customers/acme_patches?
Ahora me pregunto:
¿De qué rama se bifurcó customers/acme_patches
- devel
o stable
? Sólo sé que fue bifurcado de uno de ellos en el pasado, pero no se cual. Por ejemplo, podría haber sido commit 1
o 2
en el diagrama anterior.
He estado jugando con git log --oneline --graph
y gitk
pero desde que customers/acme_patches
se bifurcó hace unos cientos de confirmaciones, es difícil seguir las líneas que se dibujan.
Hay tal vez un comando rápido (un pequeño script está bien, también) que de alguna manera puede seguir las confirmaciones en customers/acme_patches
hacia atrás para encontrar la primera confirmación con dos hijos (el punto de bifurcación) y entonces determina si esa confirmación se hizo en stable
o en devel
?
En el mejor de los casos, podría simplemente ejecutar algo como (excuse the prompt, I'm on Windows):
C:\src> git fork-origin customers/acme_patches
stable
4 answers
Bueno, probablemente no hay una solución perfecta para esta respuesta. Quiero decir que no hay fork-origin
equivalente en git (que yo sepa).
Debido a que la rama stable
se fusiona en devel
, su acme_patches
(de 1) está en ambas ramas devel
y stable
.
Lo que podrías hacer es:
git branch --contains $(git merge-base customers/acme_patches devel stable)
Si tienes estable y no devel, o devel y no estable, entonces sabes de dónde viene.
Por ejemplo, en el caso 2, tendrías
$ git branch --contains $(git merge-base customers/acme_patches devel stable)
customers/acme_patches
devel
Mientras que en el caso 1 usted tendría
$ git branch --contains $(git merge-base customers/acme_patches devel stable)
customers/acme_patches
devel
stable
Como ahora está en ambas ramas (debido a la fusión de stable a dev)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-26 11:27:24
Con git 1.9/2.0 (Q1 2014), puedes usar git merge-base --fork-point
para pedir el mejor ancestro común según Git.
Puedes ver esa nueva opción:
- detallado en " ¿Cómo puedo recuperar/resincronizar después de que alguien empuje un rebase o un reset a una rama publicada?".
- usado en " ¿Cómo tratar con un repositorio público que ya ha sido rebasado?".
Y desde commit ad8261dde John Keeping (johnkeeping
), git rebase
puede usar la misma nueva opción --fork-point
, que puede ser útil si necesita rebase una rama como customers/acme_patches
en devel
.
(No estoy diciendo que esto tendría sentido en su escenario específico)
Nota: Git 2.16 (Q1 2018) aclara y mejora la documentación para "merge-base --fork-point
", ya que estaba claro qué calculaba, pero no por qué/para qué.
See commit 6d1700b (09 Nov 2017) by Junio C Hamano (gitster
).
(Merged by Junio C Hamano -- gitster
-- in commit 022dd4a , 27 Nov 2017)
merge-base --fork-point
doc: aclarar los modos de ejemplo y de fallaLa historia ilustrada utilizada para explicar el modo
--fork-point
nombrado tres commits de keypoint B3, B2 y B1 de la más antigua a la más reciente, que era difícil de leer.
Reetiquetado a B0, B1, B2.
También ilustre la historia después de que se hizo la rebase usando la facilidad--fork-point
.El texto ya menciona el uso de reflog, pero la descripción no es clear qué beneficio estamos tratando de obtener usando reflog.
Aclarar que es encontrar el cometa que estaban en la punta de la rama de seguimiento remoto .
Esto a su vez requiere que los usuarios conozcan las ramificaciones de los supuestos subyacentes, a saber, la expiración de las entradas de reflog hará que sea imposible determinar qué confirmaciones estaban en la punta de las ramas de seguimiento remoto y fallamos en caso de duda (en lugar de dando un resultado aleatorio e incorrecto sin siquiera advertencia).
Otra limitación es que no será útil si no se bifurcó desde la punta de una rama de seguimiento remoto, sino desde el centro.
Descríbelos.
Así que la documentación ahora dice:
Después de trabajar en la rama
topic
creada congit checkout -b topic origin/master
, el historial de la rama de seguimiento remotoorigin/master
puede haber sido rebobinado y reconstruido, lo que lleva a un historia de esto forma:
o---B2
/
---o---o---B1--o---o---o---B (origin/master)
\
B0
\
D0---D1---D (topic)
Donde
origin/master
solía apuntar a las confirmaciones B0, B1, B2 y ahora puntos en B, y sutopic
rama se inició en la parte superior de la misma de nuevo cuandoorigin/master
estaba en B0, y construiste tres commits, D0, D1, y D, encima.
Imagine que ahora quiere rebase el trabajo que hizo en eltopic
en la parte superior de la actualizaciónorigin/master
.En tal caso,
git merge-base origin/master topic
devolvería el padre de B0 en la imagen de arriba, peroB0^..D
es no el rango de commits que querrías reproducir encima de B (incluye B0, que no es lo que escribiste; es un commit que el otro lado descartó cuando movió su punta de B0 a B1).
git merge-base --fork-point origin/master topic
está diseñado para ayudar en tal caso.
No solo toma en cuenta B, sino también B0, B1 y B2 (es decir, consejos antiguos de las ramas de seguimiento remoto que el reflog de su repositorio conoce) para ver en qué commit se construyó su rama temática y encuentra B0, lo que le permite reproducir solo las confirmaciones en tu tema, excluyendo las confirmaciones del otro lado más tarde descartar.Por lo tanto
$ fork_point=$(git merge-base --fork-point origin/master topic)
Encontrará B0, y
$ git rebase --onto origin/master $fork_point topic
Repetirá D0, D1 y D encima de B para crear una nueva historia de esto forma:
o---B2
/
---o---o---B1--o---o---o---B (origin/master)
\ \
B0 D0'--D1'--D' (topic - updated)
\
D0---D1---D (topic - old)
Una advertencia es que las entradas de reflog más antiguas en su repositorio pueden ser expirado por
git gc
.
Si B0 ya no aparece en el reflog de la rama de seguimiento remotoorigin/master
, el modo--fork-point
obviamente no puede find it and fails, evitando dar un resultado aleatorio e inútil(como el padre de B0, como el mismo comando sin la opción--fork-point
da).Además, la rama de seguimiento remoto utiliza el modo
--fork-point
con debe ser el que su tema bifurcado de su punta.
Si se bifurcó desde una confirmación más antigua que la punta, este modo no encontraría el punto de bifurcación (imagine en el historial de ejemplo anterior que B0 no existía,origin/master
comenzó en B1, se trasladó a B2 y luego B, y se bifurcó su tema enorigin/master^
cuandoorigin/master
era B1; la forma de el historial sería el mismo que el anterior, sin B0, y el padre de B1 es lo quegit merge-base origin/master topic
encuentra correctamente, pero el modo--fork-point
no lo hará, porque no es uno de los commits que solían estar en la punta deorigin/master
).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-29 22:09:44
Bien, git merge-base customers/acme_patches stable
debería mostrar el ancestro común de esas dos ramas.
Puedes probar, por ejemplo, gitk --left-right customers/acme_patches...stable
(¡nota tres puntos!). Esto mostrará todas las confirmaciones que están en esas ramas y no en la base de fusión. Usando --left-right
marcará cada commit con una flecha izquierda o derecha según la rama en la que se encuentren - una flecha izquierda si están en customers/acme_patches y una flecha derecha si están en stable.
Posiblemente también añadir --date-order
que he encontrado a veces ayuda a tener sentido de salida.
(Puede usar esta sintaxis con git log --graph
en lugar de gitk
, pero en mi humilde opinión este es un caso en el que la visualización del gráfico visual es una gran mejora).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-26 11:02:31
No estoy seguro si cubre todos los casos, pero aquí están las funciones que se me ocurrieron:
git_branch_contains() {
local b=$1
local c=$2
IFS_=$IFS
IFS=$'\n'
local branches=($(git branch --contains "$c" | sed -E 's/^(\*| ) //'))
IFS=$IFS_
for b2 in "${branches[@]:+${branches[@]}}"; do
if [ "$b2" = "$b" ]; then
return 0
fi
done
return 1
}
git_upstream_branch() {
local b=$1
local c1=$(git merge-base --fork-point master "$b") || local c1=
local c2=$(git merge-base --fork-point dev "$b") || local c2=
if ! [ "$c1" ]; then
echo dev
return
fi
if ! [ "$c2" ]; then
echo master
return
fi
local fp
if git merge-base --is-ancestor "$c1" "$c2"; then
fp=$c2
else
fp=$c1
fi
if git_branch_contains master "$fp" && ! git_branch_contains dev "$fp"; then
echo master
else
echo dev
fi
}
Y aquí está el script para probarlos (git-upstream-branch-test.sh
):
#!/usr/bin/env bash
set -eu
. git-upstream-branch.sh
git_commit() {
if ! [ "${commit_i:-}" ]; then
commit_i=0
fi
(( commit_i++ )) || true
echo "$commit_i" > "$commit_i"
git add "$commit_i"
git commit -qm "c$commit_i"
}
git_merge() {
if ! [ "${merge_i:-}" ]; then
merge_i=0
fi
(( merge_i++ )) || true
git merge -m "$merge_i" $1
}
A_TOPOLOGY=${1:-}
mkdir git-upstream-branch-test-repo
cd git-upstream-branch-test-repo
git init -q
if [ "$A_TOPOLOGY" = 10 ]; then
git_commit
git_commit
git checkout -qb dev
git_commit
git_commit
git checkout -q master
git_commit
git_commit
c=$(git rev-parse HEAD)
git_commit
git_commit
git checkout -q dev
git checkout -qb t1
git_commit
git_commit
git checkout -q dev
git_commit
git_commit
git rebase --onto "$c" dev t1
elif [ "$A_TOPOLOGY" = 11 ]; then
git_commit
git_commit
git checkout -qb dev
git_commit
git_commit
git checkout -q master
git_commit
git_commit
git checkout -q dev
c=$(git rev-parse HEAD)
git_commit
git_commit
git checkout -q master
git checkout -qb t1
git_commit
git_commit
git checkout -q master
git_commit
git_commit
git rebase --onto "$c" master t1
else
git_commit
git_commit
git checkout -qb dev
git_commit
git_commit
git checkout -q master
git_commit
git_commit
if [ "$A_TOPOLOGY" = 4 ] || [ "$A_TOPOLOGY" = 5 ] || [ "$A_TOPOLOGY" = 6 ]; then
git_merge dev
git_commit
git_commit
git checkout -q dev
git_commit
git_commit
git checkout -q master
elif [ "$A_TOPOLOGY" = 7 ] || [ "$A_TOPOLOGY" = 8 ] || [ "$A_TOPOLOGY" = 9 ]; then
git checkout -q dev
git_merge master
git_commit
git_commit
git checkout -q master
git_commit
git_commit
fi
git checkout -qb t1
git_commit
git_commit
git checkout -q master
git_commit
git_commit
if [ "$A_TOPOLOGY" = 2 ] || [ "$A_TOPOLOGY" = 5 ] || [ "$A_TOPOLOGY" = 8 ]; then
git_merge dev
elif [ "$A_TOPOLOGY" = 3 ] || [ "$A_TOPOLOGY" = 6 ] || [ "$A_TOPOLOGY" = 9 ]; then
git checkout -q dev
git_merge master
fi
fi
git --no-pager log --oneline --graph --decorate --all
git_upstream_branch t1
Úsalo así,
$ rm -rf git-upstream-branch-test-repo && ./git-upstream-branch-test.sh NUMBER
Donde el NÚMERO es un número del 1 al 11 para especificar qué caso (topología) probar.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-25 14:41:44