Dada una rama, me gustaría ver una lista de confirmaciones que existen solo en esa rama. En esta pregunta , discutimos formas de ver qué confirmaciones están en una rama pero no en una o más ramas especificadas.
Esto es ligeramente diferente. Me gustaría ver lo que cometa, está en una, pero no en las otras ramas.
El caso de uso se encuentra en una estrategia de ramificación en la que algunas ramas solo deben fusionarse y nunca comprometerse directamente. Esto se usaría para verificar si se han realizado confirmaciones directamente en una rama de "solo combinación".
EDITAR: A continuación se muestran los pasos para configurar un repositorio git ficticio para probar:
git init
echo foo1 >> foo.txt
git add foo.txt
git commit -am "initial valid commit"
git checkout -b merge-only
echo bar >> bar.txt
git add bar.txt
git commit -am "bad commit directly on merge-only"
git checkout master
echo foo2 >> foo.txt
git commit -am "2nd valid commit on master"
git checkout merge-only
git merge master
Solo debería aparecer la confirmación con el mensaje "confirmación incorrecta directamente en solo combinación", que se realizó directamente en la rama de solo combinación.

git log ^branch1 ^branch2 merge-only-branchsintaxis?git log ^branch1 ^branch2 merge-only-branchrequiere enumerar cada rama. Eso se puede evitar con un uso inteligente de bash / grep (vea mi respuesta a continuación), pero espero que git tenga algún soporte incorporado para esto. Tiene razón en que supone que todas las ramas de fusión son remotas (solo las locales son tan buenas como inexistentes para otros desarrolladores). El uso--no-mergesomite todas las confirmaciones que se fusionaron y luego se eliminó la rama de fusión de origen original, por lo que se supone que las ramas de fusión de se mantienen hasta que se fusionan con una rama que no es de fusión solo (es decir, maestra).Respuestas:
Acabamos de encontrar esta elegante solución
En su ejemplo, por supuesto, la confirmación inicial todavía aparece.
esta respuesta no responde exactamente a la pregunta, porque la confirmación inicial todavía aparece. Por otro lado, muchas personas que vienen aquí parecen encontrar la respuesta que buscan.
fuente
initial valid commit, que es parte de ambasmerge-onlymasterramas y . Sin embargo, si uno pasa por el esfuerzo de poner el nombre de la rama actual al final seguido de nombres de rama con prefijo ^ de los que se sabe que se originó la rama actual, resuelve la mitad de los problemas (excluyendo las cosas que se fusionaron). Ej .:git log --first-parent --no-merges merge-only ^mastergit log --first-parent --no-merges | grep <branch_name>Cortesía de mi querido amigo Redmumba :
... dónde
origin/merge-onlyestá el nombre de la rama de solo combinación remota. Si trabaja en un repositorio de git solo local, sustitúyalorefs/remotes/originporrefs/headsy sustituya el nombre de la rama remotaorigin/merge-onlycon el nombre de la rama localmerge-only, es decir:fuente
git for-each-refpara enumerar todos los nombres de referencia en el origen ygrep -vpara omitir la rama de solo combinación.git logtoma una--notopción, a la que pasamos nuestra lista de todas las referencias (excepto la rama de solo fusión). Si tiene una respuesta más elegante al problema, escuchémosla./*de losgit for-each-refscomandos se basa en no coincidir con algún archivo existente y no tenerfailglobonullglobestablecer (las opciones de bash , otras shells varían). Debe citar / escapar de los asteriscos o simplemente dejar el final/*(losgit for-each-refpatrones pueden coincidir "desde el principio hasta una barra oblicua"). Tal vez usegrep -Fv refs/remotes/origin/foo(refs/heads/foo) para ser más estricto sobre qué referencias se eliminan.git log --no-merges B1 --not B2donde B1 es la rama que le interesa y B2 es la rama con la que desea comparar B1. Tanto B1 como B2 pueden ser sucursales locales o remotas, por lo que puede especificargit log --no-merges master --not origin/master, o incluso especificar dos sucursales remotas.Esto le mostrará todas las confirmaciones realizadas en su rama.
fuente
origin/branchNameapuntará al jefe de la rama remota YHEADapuntará al commitid de la última confirmación local en esa rama. Así que esto no funcionará cuando haya usado git push.La respuesta de @Prakash funciona. Solo por claridad ...
enumera las confirmaciones en la rama de características pero no en la rama ascendente (normalmente su maestro).
fuente
Quizás esto podría ayudar:
fuente
Prueba esto:
Básicamente,
git rev-list --all ^branchobtiene todas las revisiones que no están en la rama y luego todas las revisiones en el repositorio y restan la lista anterior, que son solo las revisiones en la rama.Después de los comentarios de @ Brian:
De la documentación de git rev-list:
List commits that are reachable by following the parent links from the given commit(s)Entonces un comando como
git rev-list Adonde A es una confirmación enumerará las confirmaciones que son accesibles desde A, incluida A.Con eso en mente, algo como
git rev-list --all ^Aenumerará las confirmaciones no accesibles desde A
Entonces
git rev-list --all ^branchenumerará todas las confirmaciones no accesibles desde la punta de la rama. Lo que eliminará todas las confirmaciones en la rama, o en otras palabras, las confirmaciones que están solo en otras ramas.Ahora vamos a
git rev-list --all --not $(git rev-list --all ^branch)Esto sera como
git rev-list --all --not {commits only in other branches}Así que queremos enumerar los
allque no son accesibles desdeall commits only in other branchesQue es el conjunto de confirmaciones que solo están en la rama. Tomemos un ejemplo simple:
Aquí el objetivo es obtener D y E, las confirmaciones no en ninguna otra rama.
git rev-list --all ^branchdar solo BAhora,
git rev-list --all --not Bes a lo que llegamos. Lo que también esgit rev-list -all ^B: queremos que no se pueda acceder a todas las confirmaciones desde B. En nuestro caso, son D y E. Que es lo que queremos.Espero que esto explique cómo funciona correctamente el comando.
Editar después del comentario:
Después de los pasos anteriores, si lo hace
git rev-list --all --not $(git rev-list --all ^merge-only), obtendrá el compromiso que estaba buscando: el"bad commit directly on merge-only".Pero una vez que realice el último paso en sus pasos,
git merge masterel comando no dará el resultado esperado. Porque a partir de ahora no hay confirmación que no esté en merge-only ya que la confirmación adicional en master también se ha fusionado para merge-only. Entoncesgit rev-list --all ^branchda un resultado vacío y, porgit rev-list -all --not $(git rev-list --all ^branch)lo tanto , dará todas las confirmaciones solo en combinación.fuente
xargs -L 1 -t git branch -a --containsmuestra muchos falsos positivos (confirmaciones que, de hecho, están en otras ramas). Intenté con y sin--no-merges. ¡Gracias por responder!git rev-list --all ^branchle dará todas las confirmaciones que no están incluidasbranch. Usted está restando después de que en la lista que están enbranch; pero, por definición, todas las confirmaciones que no están incluidas nobranchestán incluidasbranch, por lo que no resta nada. Lo que jimmyorr busca son confirmaciones que estén enbranchpero nomaster, ni en ninguna otra rama. No desea restar las confirmaciones que no están incluidasbranch; desea restar las confirmaciones que están en cualquier otra rama.branch, pero también lo hacegit rev-list branch. Estás escribiendogit rev-list branchde una manera más complicada (y más lenta). No funciona responder a la pregunta, que es cómo encontrar todas las confirmacionesbranchque no están en ninguna otra rama .Otra variación de las respuestas aceptadas, para usar con
mastergit log origin/master --not $(git branch -a | grep -Fv master)Filtra todas las confirmaciones que ocurren en cualquier rama que no sea la maestra.
fuente
Esta no es exactamente una respuesta real, pero necesito acceso al formato y mucho espacio. Intentaré describir la teoría detrás de lo que considero las dos mejores respuestas: la aceptada y la (al menos actualmente) mejor clasificada . Pero, de hecho, responden a diferentes preguntas .
Las confirmaciones en Git suelen estar "en" más de una rama a la vez. De hecho, de eso se trata la pregunta. Dado:
donde las letras mayúsculas representan los ID de hash de Git reales, a menudo buscamos solo confirmaciones
Ho solo confirmacionesI-Jen nuestragit logsalida. Las confirmaciones hastaGestán en ambas ramas, por lo que nos gustaría excluirlas.(Tenga en cuenta que en los gráficos dibujados como este, las confirmaciones más nuevas están hacia la derecha. Los nombres seleccionan la confirmación única más a la derecha en esa línea. Cada una de esas confirmaciones tiene una confirmación principal, que es la confirmación a su izquierda: el padre de
HesG, y el padre deJesI. El padre deIes deGnuevo. El padre deGesF, yFtiene un padre que simplemente no se muestra aquí: es parte de la...sección).Para este caso particularmente simple, podemos usar:
para ver
I-J, o:para ver
Hsolo. El nombre del lado derecho, después de los dos puntos, le dice a Git: sí, estas confirmaciones . El nombre del lado izquierdo, antes de los dos puntos, le dice a Git: no, no estas confirmaciones . Git comienza por el final , en la confirmaciónHo confirmación,Jy funciona al revés . Para (mucho) más sobre esto, consulte Think Like (a) Git .De la forma en que está redactada la pregunta original, el deseo es encontrar confirmaciones que sean accesibles desde un nombre en particular, pero no desde cualquier otro nombre en la misma categoría general. Es decir, si tenemos una gráfica más compleja:
podríamos elegir uno de estos nombres, como
name4oname3, y preguntar: ¿ qué confirmaciones se pueden encontrar con ese nombre, pero no con los otros nombres? Si elegimosname3la respuesta es comprometerseL. Si elegimosname4, la respuesta es ningún compromiso: el compromiso de que losname4nombres es compromisoNpero compromisoNse puede encontrar comenzando enname5y trabajando hacia atrás.La respuesta aceptada funciona con nombres de seguimiento remoto, en lugar de nombres de sucursales, y le permite designar uno, el que está escrito,
origin/merge-onlycomo el nombre seleccionado y mirar todos los demás nombres en ese espacio de nombres. También evita mostrar fusiones: si seleccionamosname1como el "nombre interesante" y decimos mostrarme confirmaciones que son accesibles desdename1pero no cualquier otro nombre , veremos la confirmación de fusiónMasí como la confirmación regularI.La respuesta más popular es bastante diferente. Se trata de atravesar el gráfico de confirmaciones sin seguir ambos tramos de una fusión y sin mostrar ninguna de las confirmaciones que son fusiones. Si comenzamos con
name1, por ejemplo, no mostraremosM(es una fusión), pero asumiendo que el primer padre de la fusiónMes un compromisoI, ni siquiera veremos los compromisosJyK. Vamos a terminar mostrando cometerI, y también se comprometeH,G,F, y así sucesivamente, ninguno de estos son confirmaciones de mezcla y todos son accesibles por a partir deMy trabajando hacia atrás, visitando sólo el primero de los padres de cada combinación de comprometerse.La respuesta más popular es bastante adecuada, por ejemplo, para ver
mastercuándomasterse pretende que sea una rama de solo combinación. Si todo el "trabajo real" se realizó en las ramas laterales que posteriormente se fusionaronmaster, tendremos un patrón como este:donde todos los nombres sin letra
oconfirmaciones sin son confirmaciones ordinarias (sin fusión)MyNson confirmaciones de fusión. ComprometerseIes la inicial cometer: el primer cometer jamás se ha hecho, y el único que debería estar en la maestra que no es una fusión cometió. Si elgit log --first-parent --no-merges masterprograma muestra algún compromiso diferente aI, tenemos una situación como esta:donde queremos ver la confirmación
*que se realizó directamentemaster, no fusionando alguna rama de características.En resumen, la respuesta popular es excelente para mirar
mastercuándomasterse debe combinar solo, pero no es tan buena para otras situaciones. La respuesta aceptada funciona para estas otras situaciones.¿Son nombres de seguimiento remoto como
origin/masternombres de sucursales ?Algunas partes de Git dicen que no lo son:
dice
on branch master, pero:dice
HEAD detached at origin/master. Prefiero estar de acuerdo congit checkout/git switch:origin/masterno es un nombre de rama porque no se puede "acceder" a él.La respuesta aceptada usa nombres de seguimiento remoto
origin/*como "nombres de sucursales":La línea media, que invoca
git for-each-ref, itera sobre los nombres de seguimiento remoto para el control remoto nombradoorigin.La razón por la que esta es una buena solución al problema original es que aquí estamos interesados en los nombres de las ramas de otra persona , en lugar de los nombres de nuestras ramas. Pero eso significa que hemos definido rama como algo diferente a nuestros nombres de rama . Eso está bien: solo tenga en cuenta que está haciendo esto, cuando lo haga.
git logatraviesa alguna (s) parte (s) del gráfico de confirmaciónLo que realmente estamos buscando aquí es una serie de lo que he llamado daglets: mira ¿Qué queremos decir exactamente con "rama"? Es decir, estamos buscando fragmentos dentro de algún subconjunto del gráfico de confirmación general .
Siempre que hagamos que Git mire un nombre de rama como
master, un nombre de etiqueta comov2.1, o un nombre de seguimiento remoto comoorigin/master, tendemos a querer que Git nos diga sobre esa confirmación. y cada confirmación a la que podemos llegar desde esa confirmación: comenzando allí y trabajando al revés.En matemáticas, esto se conoce como caminar por un gráfico . El gráfico de confirmación de Git es un gráfico acíclico dirigido o DAG , y este tipo de gráfico es especialmente adecuado para caminar. Al caminar por un gráfico de este tipo, se visitará cada vértice del gráfico al que se puede acceder a través del camino que se está utilizando. Los vértices en el gráfico de Git son las confirmaciones, y los bordes son arcos (enlaces unidireccionales) que van de cada hijo a cada padre. (Aquí es donde Think Like (a) Git entra . La naturaleza unidireccional de los arcos significa que Git debe funcionar al revés, de hijo a padre).
Los dos comandos principales de Git para recorrer gráficos son
git logygit rev-list. Estos comandos son extremadamente similares, de hecho, en su mayoría están construidos a partir de los mismos archivos fuente, pero su salida es diferente:git logproduce una salida para que los humanos la lean, mientras quegit rev-listproduce una salida para que la lean otros programas Git. 1 Ambos comandos realizan este tipo de recorrido gráfico.El recorrido del gráfico que hacen es específicamente: dado un conjunto de confirmaciones de punto de partida (tal vez solo una confirmación, tal vez un montón de ID de hash, tal vez un montón de nombres que se resuelven en ID de hash), recorre el gráfico, visitando las confirmaciones . Directivas particulares, como
--noto un prefijo^, o--ancestry-path, o--first-parent, modifican el recorrido del gráfico de alguna manera.A medida que recorren el gráfico, visitan cada confirmación. Pero solo imprimen algunos subconjuntos seleccionados de las confirmaciones caminadas. Directivas como
--no-mergeso--before <date>indican el código que recorre el gráfico que se compromete a imprimir .Para hacer esta visita, una confirmación a la vez, estos dos comandos usan una cola de prioridad . Ejecutas
git logogit rev-listy le das algunas confirmaciones de punto de partida. Ponen esas confirmaciones en la cola de prioridad. Por ejemplo, un simple:convierte el nombre
masteren un ID de hash sin procesar y coloca ese ID de hash en la cola. O:convierte ambos nombres en ID de hash y, asumiendo que se trata de dos ID de hash diferentes, coloca ambos en la cola.
La prioridad de las confirmaciones en esta cola está determinada por aún más argumentos. Por ejemplo, el argumento
--author-date-orderle dice agit logogit rev-listque use la marca de tiempo del autor , en lugar de la marca de tiempo del confirmador. El valor predeterminado es usar la marca de tiempo del confirmador y elegir el compromiso más reciente por fecha: el que tiene la fecha numérica más alta. Entoncesmaster develop, suponiendo que se resuelvan en dos confirmaciones diferentes, Git mostrará la que vino más tarde primero, porque estará al principio de la cola.En cualquier caso, el código de revisión para caminar ahora se ejecuta en un bucle:
--no-merges: no imprime nada si es una confirmación de fusión;--before: no imprime nada si su fecha no es anterior a la hora designada. Si no se suprime la impresión, imprima la confirmación: paragit log, muestre su registro; paragit rev-list, imprime su ID de hash.--first-parentsuprime todos menos el primer padre de cada combinación.(Ambos
git logygit rev-listpueden simplificar el historial con o sin reescritura de los padres en este punto también, pero lo omitiremos aquí).Para una cadena simple, como comenzar en
HEADy trabajar hacia atrás cuando no hay combinaciones de confirmaciones, la cola siempre tiene una confirmación en la parte superior del ciclo. Hay una confirmación, así que la sacamos, la imprimimos y ponemos su padre (único) en la cola y volvemos a dar la vuelta, y seguimos la cadena hacia atrás hasta que llegamos a la primera confirmación, o el usuario se cansa de lagit logsalida y se cierra. el programa. En este caso, ninguna de las opciones de pedido importa: solo hay una confirmación para mostrar.Cuando hay fusiones y seguimos a ambos padres, ambas "patas" de la fusión, o cuando otorgas
git logogit rev-listmás de una confirmación inicial, las opciones de clasificación son importantes.Por último, considere el efecto de un especificador de confirmación
--noto^delante de él. Estos tienen varias formas de escribirlos:o:
o:
todos significan lo mismo. El
--notes como el prefijo^excepto que se aplica a más de un nombre:significa no branch1, no branch2, sí branch3; pero:
significa que no es branch1, no branch2, no branch3, y tienes que usar un segundo
--notpara apagarlo:lo cual es un poco incómodo. Las dos directivas "no" se combinan a través de XOR, por lo que si realmente lo desea, puede escribir:
para significar no branch1, no branch2, sí branch3 , si quieres ofuscar .
Todos estos funcionan al afectar el recorrido del gráfico. Como
git logogit rev-listpaseos el gráfico, se asegura de no poner en la cola de prioridad ningún commit que es accesible desde cualquiera de los negados referencias. (De hecho, también afectan la configuración inicial: las confirmaciones negadas no pueden ir a la cola de prioridad directamente desde la línea de comando, por lo quegit log master ^masterno muestra nada, por ejemplo).Toda la elegante sintaxis descrita en la documentación de gitrevisions hace uso de esto, y puede exponerlo con una simple llamada a
git rev-parse. Por ejemplo:La sintaxis de tres puntos significa que las confirmaciones se pueden alcanzar desde el lado izquierdo o derecho, pero excluyendo las confirmaciones accesibles desde ambos . En este caso , se puede acceder a la
origin/masterconfirmaciónb34789c0b,, desdeorigin/pu(fc307aa37...), por lo que elorigin/masterhash aparece dos veces, una vez con una negación, pero de hecho Git logra la sintaxis de tres puntos colocando dos referencias positivas (las dos ID de hash no negadas) y uno negativo, representado por el^prefijo.Similarmente:
La
^@sintaxis significa todos los padres de la confirmación dada , y élmaster^mismo, el primer padre de la confirmación seleccionado por nombre de rama,masteres una confirmación de fusión, por lo que tiene dos padres. Estos son los dos padres. Y:El
^!sufijo significa el compromiso en sí, pero ninguno de sus padres . En este caso,master^es0b07eecf6.... Ya vimos a ambos padres con el^@sufijo; aquí están de nuevo, pero esta vez, negadas.1 Muchos programas de Git se ejecutan literalmente
git rev-listcon varias opciones y leen su salida para saber qué confirmaciones y / u otros objetos de Git usar.2 Debido a que el gráfico es acíclico , es posible garantizar que ninguno haya sido visitado ya, si agregamos la restricción nunca mostrar un padre antes de mostrar todos sus hijos a la prioridad.
--date-order,--author-date-ordery--topo-orderagregue esta restricción. El orden de clasificación predeterminado, que no tiene nombre, no lo tiene. Si las marcas de tiempo de las confirmaciones son confusas, si, por ejemplo, algunas confirmaciones fueron realizadas "en el futuro" por una computadora cuyo reloj estaba apagado, esto podría en algunos casos conducir a resultados de apariencia extraña.Si llegaste tan lejos, ahora sabes mucho sobre
git logResumen:
git logse trata de mostrar algunas confirmaciones seleccionadas mientras se recorre parte o la totalidad de alguna parte del gráfico.--no-mergesargumento, que se encuentra tanto en las respuestas aceptadas como en las mejor clasificadas actualmente, suprime la visualización de algunas confirmaciones que se caminan.--first-parentargumento, de la respuesta actualmente mejor clasificada, suprime caminar algunas partes del gráfico, durante el recorrido del gráfico en sí.--notprefijo a los argumentos de la línea de comandos, como se usa en la respuesta aceptada, suprime la visita a algunas partes del gráfico, desde el principio.Obtenemos las respuestas que nos gustan, a dos preguntas diferentes, utilizando estas funciones.
fuente