tipos tag origin log etiquetas create git branch commit branching-and-merging

origin - git push tag



¿Cómo encontrar todas las confirmaciones no combinadas en el master agrupadas por las ramas en las que se crearon? (4)

Tengo que crear una revisión del código de las ramas no fusionadas.

Al buscar soluciones, no vayamos a un problema de contexto de sucursal local ya que esto se ejecutará en un servidor; solo habrá el control remoto de origen , siempre ejecutaré un comando git fetch origin antes que otros comandos, y cuando hablemos de ramas, nos referiremos a origen / nombre de rama .

Si la configuración fuera simple y cada rama que se originó desde el maestro continuara a su manera, podríamos ejecutar:

git rev-list origin/branch-name --not origin/master --no-merges

para cada rama no combinada y agregue los compromisos resultantes a cada revisión por rama.

El problema surge cuando hay fusiones entre 2-3 sucursales y se continúa el trabajo en algunas de ellas. Como dije, para cada sucursal quiero crear revisiones de código programáticas y no quiero incluir un compromiso en varias revisiones.

Principalmente los problemas se reducen al encontrar la rama original para cada confirmación.
O para hacerlo más simple ... encontrar todas las confirmaciones sin combinar agrupadas por la rama en la que probablemente se crearon.

Centrémonos en un ejemplo simple:

* b4 - branch2''s head * | a4 - branch1''s head | * b3 * | merge branch2 into branch1 * |/ | m3 - master''s head | * /| a3 | | | | | * b2 | * | merge master into branch1 * /| | m2 |/ | * merge branch1 into branch2 | * /| a2 | |/ | | | * b1 | | / | |/ | /| |/ | | * a1 * / m1 |/ | * start

Y lo que quiero obtener es:

  • rama1: a1, a2, a3, a4
  • branch2: b1, b2, b3, b4

La mejor solución que encontré hasta ahora es ejecutar:

git show-branch --topo-order --topics origin/master origin/branch1 origin/branch2

y analizar el resultado:

* [master] m3 ! [branch1] a4 ! [branch2] b4 --- + [branch2] b4 + [branch2^] b3 + [branch1] a4 ++ [branch2~2] b2 -- [branch2~3] Merge branch ''branch1'' into branch2 ++ [branch2~4] b1 + [branch1~2] a3 + [branch1~4] a2 ++ [branch1~5] a1 *++ [branch2~5] m1

La interpretación de salida es así:

  1. Las primeras n lineas son las n ramas analizadas.
  2. una línea con ----
  3. una línea para cada confirmación con un signo más (o menos en el caso de confirmaciones de combinación) en el carácter de la n-ésima sangría si esa confirmación está en la rama n-ésima.
  4. La última línea es la base de fusión para todas las ramas analizadas.

Para el punto 3. la resolución del nombre de confirmación comienza con un nombre de rama y, por lo que veo, esta rama corresponde a la rama en la que se crearon las confirmaciones, probablemente al promover la ruta que llega el primer padre.

Como no estoy interesado en unir combinaciones, los ignoraré.

Luego analizaré cada rama de ruta de acceso para obtener su hash con rev-parse.

¿Cómo puedo manejar esta situación?


El repositorio se puede clonar con --mirror que crea un repositorio --mirror que se puede usar como un espejo del repositorio original y se puede actualizar con git remote update --prune después de lo cual se deben eliminar todas las etiquetas para esta característica.

Lo implemento de esta manera:
1. obtener una lista de las ramas no fusionadas en el maestro

git branch --no-merged master

2. para cada rama, obtenga una lista de revisiones en esa rama y no en la rama maestra

git rev-list branch1 --not master --no-merges

Si la lista está vacía, elimine la rama de la lista de ramas
3. Para cada revisión, determine la rama original con

git name-rev --name-only revisionHash1

y hacer coincidir las expresiones regulares para ^([^/~/^]*)([/~/^].*)?$ . El primer patrón es el nombre de la rama, el segundo es la ruta relativa a la rama.
Si el nombre de la rama encontrada no es igual a la rama inicial, elimine la revisión de la lista.

Al final obtuve una lista de ramas y para cada una de ellas una lista de confirmaciones.

Después de un poco más de investigación de bash, se puede hacer todo en una línea con:

git rev-list --all --not master --no-merges | xargs -L1 git name-rev | grep -oE ''[0-9a-f]{40}/s[^/~/^]*''

El resultado es una salida en el formulario.

hash branch

que se puede leer, analizar, ordenar, agrupar o lo que sea.


No hay una respuesta correcta a esta pregunta porque no está bien especificada.

El historial de Git es simplemente un gráfico acíclico dirigido (DAG), y generalmente es imposible determinar las relaciones semánticas entre dos nodos arbitrarios en un DAG a menos que los nodos estén lo suficientemente etiquetados. A menos que pueda garantizar que los mensajes de confirmación en su gráfico de ejemplo sigan un patrón confiable y analizable por la máquina, las confirmaciones no están lo suficientemente etiquetadas: es imposible identificar automáticamente las confirmaciones en las que está interesado sin un contexto adicional (por ejemplo, garantiza que los desarrolladores sigan) ciertas mejores prácticas).

Aquí hay un ejemplo de lo que quiero decir. Usted dice que commit a1 está asociado con branch1 , pero esto no se puede determinar con certeza simplemente mirando los nodos de su gráfico de ejemplo. Es posible que alguna vez la historia de tu repositorio de ejemplo se pareciera a esto:

* merge branch1 into branch2 - branch2''s head |/ _|/ / * b1 | | | | _|_/ / | | * a1 * / m1 |/ | * start - master''s head

Tenga en cuenta que branch1 ni siquiera existe todavía en el gráfico anterior. El gráfico anterior podría haber surgido de la siguiente secuencia de eventos:

  1. branch2 se crea al start en el repositorio compartido
  2. el usuario # 1 crea a1 en su sucursal local sucursal2
  3. Mientras tanto, el usuario # 2 crea m1 y b1 en su sucursal local sucursal2
  4. el usuario # 1 empuja su sucursal local de branch2 al repositorio compartido, haciendo que la referencia de branch2 en el repositorio compartido apunte a a1
  5. el usuario # 2 intenta empujar su sucursal local de branch2 al repositorio compartido, pero esto falla con un error de no avance rápido ( branch2 actualmente apunta a a1 y no se puede avanzar a b1 )
  6. el usuario # 2 ejecuta git pull , fusionando a1 en b1
  7. el usuario # 2 ejecuta git commit --amend -m "merge branch1 into branch2" por alguna razón inexplicable
  8. el usuario # 2 empuja, y el historial del repositorio compartido termina pareciéndose al DAG anterior

Algún tiempo después, el usuario # 1 crea una branch1 fuera de a1 y crea un a2 , mientras que el usuario # 2 avanza rápidamente m1 al master , lo que da como resultado el siguiente historial de confirmación:

* merge a1 into b1 - branch2''s head * |/ a2 - branch1''s head | _|/ |/ * b1 | | | | _|_/ / | | * a1 * / m1 - master''s head |/ | * start

Dado que esta secuencia de eventos es técnicamente posible (aunque improbable), ¿cómo puede un humano, por no hablar de Git, decirle qué comillas "pertenecen" a qué rama?

Análisis de mensajes de confirmación de fusión

Si puede garantizar que los usuarios no cambian los mensajes de confirmación de combinación (siempre aceptan el valor predeterminado de Git), y que Git nunca ha cambiado y nunca cambiará el formato de mensaje de confirmación de combinación predeterminado, entonces el mensaje de confirmación de confirmación de combinación se puede usar como una pista que a1 comenzó en branch1 . Tendrá que escribir un script para analizar los mensajes de confirmación; no hay una sola línea de Git simple para hacer esto por usted.

Si las fusiones son siempre intencionales

Alternativamente, si sus desarrolladores siguen las mejores prácticas (cada combinación es intencional y está destinada a incluir una rama con un nombre diferente, lo que da como resultado un repositorio sin esas combinaciones de combinaciones estúpidas creadas por git pull ), y no está interesado en las confirmaciones de un completó la rama secundaria, luego las confirmaciones en las que está interesado están en la ruta del primer padre. Si sabe qué rama es la matriz de la rama que está analizando, puede hacer lo siguiente:

git rev-list --first-parent --no-merges parent-branch-ref..branch-ref

Este comando enumera los identificadores SHA1 para las confirmaciones que son accesibles desde branch-ref excluyendo las confirmaciones accesibles desde parent-branch-ref y las confirmaciones que se fusionaron desde las sucursales secundarias.

En su gráfico de ejemplo anterior, suponiendo que el orden principal esté determinado por sus anotaciones y no por el orden de las líneas que van a un compromiso de fusión, git rev-list --first-parent --no-merges master..branch1 imprimiría el SHA1 los identificadores para las confirmaciones a4, a3, a2 y a1 (en ese orden; use --reverse si desea el orden opuesto), y git rev-list --first-parent --no-merges master..branch2 imprimiría el Identificadores SHA1 para confirmaciones b4, b3, b2 y b1 (de nuevo, en ese orden).

Si las sucursales tienen relaciones claras entre padres e hijos

Si sus desarrolladores no siguen las mejores prácticas y sus sucursales están llenas de esas combinaciones estúpidas creadas por git pull (o una operación equivalente), pero tiene claras relaciones de rama padre / hijo, entonces escribir un script para realizar el siguiente algoritmo puede funcionar. tú:

  1. Encuentre todas las confirmaciones accesibles desde la rama de interés, excluyendo todas las confirmaciones de su sucursal principal, la sucursal principal de éste, la sucursal principal de su matriz, etc., y guarde los resultados. Por ejemplo:

    git rev-list master..branch1 >commit-list

  2. Haga lo mismo para todas las ramas de hijos, nietos, etc. de la rama de interés. Por ejemplo, suponiendo que branch2 se considera un hijo de branch1 :

    git rev-list ^master ^branch1 branch2 >commits-to-filter-out

  3. Filtre los resultados del paso # 2 de los resultados del paso # 1. Por ejemplo:

    grep -Fv -f commits-to-filter-out commit-list

El problema con este enfoque es que una vez que una rama secundaria se fusiona con su principal, esas confirmaciones se consideran parte de la principal, incluso si el desarrollo en la rama secundaria continúa. Aunque esto tiene sentido semánticamente, no produce el resultado que usted dice que quiere.

Algunas mejores prácticas

Estas son algunas de las mejores prácticas para hacer que este problema en particular sea más fácil de resolver en el futuro. La mayoría, si no todas, se pueden aplicar mediante un uso inteligente de los enlaces en el repositorio compartido.

  1. Sólo una tarea por rama. Múltiples tareas están prohibidas.
  2. NUNCA permita que el desarrollo continúe en una rama secundaria una vez que se haya fusionado con su matriz. La fusión implica que una tarea está hecha, final de la historia. Respuestas a preguntas anticipadas:
    • P: ¿Qué sucede si descubro un error en la rama secundaria? A: Comience una nueva rama fuera del padre. NO continúe el desarrollo en la rama infantil.
    • P: ¿Qué pasa si la nueva característica aún no está hecha? A: ¿Entonces por qué fusionaste la rama? Tal vez fusionaste una subtarea completa; si es así, las subtareas restantes deben ir en sus propias ramas fuera de la rama principal. NO continúe el desarrollo en la rama infantil.
  3. Prohibir el uso de git pull
  4. Una rama secundaria no debe fusionarse con su padre a menos que todas sus ramas secundarias se hayan fusionado en ella.
  5. Si la rama no tiene ninguna rama secundaria, considere volver a establecer la base en la rama principal antes de fusionar con --no-ff . Si tiene sucursales de niños, todavía puede cambiar de base, pero por favor, mantenga las fusiones no --no-ff de las ramas de niños (esto es más complicado de lo que debería ser).
  6. Combine la rama principal con frecuencia para que los conflictos de combinación sean más fáciles de resolver.
  7. Evite fusionar una rama de abuelo directamente en su rama de nieto; primero fúndase con el niño, luego fíjelo con el nieto.

Si todos tus desarrolladores siguen estas reglas, entonces un simple:

git rev-list --first-parent --no-merges parent-branch..child-branch

es todo lo que necesita para ver los compromisos que se hicieron en esa rama menos los compromisos realizados en sus ramas secundarias.


Si capto su espacio problemático, creo que puede usar --sha1-name

git show-branch --topo-order --topics --sha1-name origin / master origin / branch1 origin / branch2

para enumerar lo que le interesa, luego ejecute los confirmaciones a través de git-what-branch

git-what-branch : descubra en qué rama se encuentra un commit, o cómo llegó a una rama con nombre. Este es un git-what-branch de Seth Robertson

y formatear el informe para satisfacer sus necesidades?


Yo sugeriría hacerlo de la manera en que lo describiste. Pero trabajaría en la salida de git log --format="%H:%P:%s" ^origin/master origin/branch1 origin/branch2 , para que puedas hacer un mejor git log --format="%H:%P:%s" ^origin/master origin/branch1 origin/branch2 árboles.

  1. Construya una estructura de árbol adecuada a partir de la salida, marcando los padres y los niños.
  2. Comience a caminar desde las cabezas (obtenga sus SHA de git rev-parse ). Marca cada compromiso con los nombres de la cabeza de donde vienes y su distancia.
    • Para los pasos no primeros (la otra parte de la fusión), agregaría 100 a la distancia.
    • Si cumple con un compromiso de fusión, verifique qué dice sobre qué rama se fusionó en cuál. Utilice esta información cuando siga los dos enlaces principales: Si el nombre analizado de la rama a la que va a ir no coincide con su HEAD actual, agregue 10000 a la distancia.
    • Para ambos padres: ahora sabes su nombre. Agregue todos sus hijos a los que son padres primarios a un dict: commit -> known-name .
  3. Tome su dictamen de compromisos conocidos y comience a caminar por el árbol (hacia los niños, no hacia los padres). Resta 10000 de la distancia desde la rama fusionada. Mientras realiza este recorrido, no vaya a los compromisos para los que no es el primer padre y se detenga tan pronto como llegue a un punto de bifurcación (un compromiso que tiene dos hijos). Detente también si golpeas una de tus cabezas de rama.

Ahora, para cada una de sus confirmaciones, tendrá una lista de valores de distancia (que podrían ser negativos) a sus cabezas de rama. Para cada confirmación, la rama con la menor distancia es aquella en la que es más probable que se haya creado la confirmación.

Si tiene tiempo, es posible que desee recorrer todo el historial y luego restar el historial del maestro, lo que podría dar resultados ligeramente mejores si sus sucursales se han fusionado en el maestro anteriormente.

No se pudo resistir: hizo un script en Python que hace lo que describí. Pero con un cambio: con cada paso normal, la distancia no aumenta, sino que disminuye. Esto tiene el efecto de que se prefieren las ramas que vivieron más tiempo después de un punto de fusión, lo que personalmente me gusta más. Aquí está: https://gist.github.com/Chronial/5275577

Uso: simplemente ejecute git-annotate-log.py ^origin/master origin/branch1 origin/branch2 verifique la calidad de los resultados (generará un árbol de registro git con anotaciones).