2012-09-17 17 views
25

Supongamos que acabo de actualizar la rama foo en master, con conflictos. Quiero asegurarme de no dañar accidentalmente el contenido de foo durante la resolución de conflictos introduciendo cambios adicionales o perdiendo cambios (que no sean los adecuados para la resolución de conflictos). He hecho esto a través de:Comparando las diferencias a través de una rebase en Git

diff -u <(git diff `git merge-base master [email protected]{1}` [email protected]{1}) \ 
     <(git diff `git merge-base master foo ` foo ) 

(actualización o la sintaxis equivalente ... para git-diff la que acabo de ser recordado :)

diff -u <(git diff [email protected]{1}) <(git diff master...foo) | mate 

Esto me muestra todos los cambios que se han producido a master..foo considerado como un parche, que es exactamente lo que quiero comprobar para ser mínimo. Sin embargo, la invocación es compleja y el resultado no es completamente sencillo de interpretar.

¿Hay una manera mejor de llevar a cabo esta tarea? ¿Proporcionar la misma información, pero con un mejor método o formato? ¿O debería simplemente tomar lo anterior y finalizarlo en un script?

+0

¿No es esto esencialmente equivalente a 'git diff foo @ {1} foo'? – twalberg

+1

@twalberg No; 'git diff foo @ {1} foo' muestra tanto mis resoluciones de conflictos como los cambios realizados en el dominio sobre los que he vuelto a basar, en lugar de solo las resoluciones de conflictos. –

+0

Ah, claro ... Solo estaba considerando mi propio caso de rebase más común, en el que la base de combinación de la fuente y el destino permanece igual (reescribiendo/aplastando una rama antes de presionar). – twalberg

Respuesta

2

Las diferencias entre el maestro y foo porcentualizada:

git diff master..foo

vs

Las diferencias de pre-rebase foo después se desvía maestro (tenga en cuenta tres puntos):

git diff [email protected]{1}

?

+0

Gracias, usando' ... 'acorta el comando, y lo he usado antes así que Debería haberlo pensado. Sin embargo, todavía me queda la oportunidad de comparar explícitamente esas dos diferencias. ¿Tienes alguna sugerencia para eso? –

2

Dado que es una base de datos y no una fusión, es posible que desee comparar foo consigo mismo, pero antes de la fusión. Si recuerdo correctamente [email protected]{1} obtendrá el padre de la confirmación actual para foo, y tal vez esto no es lo que está buscando.

Creo que se puede hacer algo como lo siguiente (suponiendo que no haya hecho un git gc):

En la rama foo después de que el rebase:

$ git reflog 

Esto le mostrará cómo la cabeza de su rama ha sido movido. Debería ver algunos registros de este tipo (dependiendo si se rebase de manera interactiva o no):

64d3c9e [email protected]{15}: rebase -i (finish): returning to refs/heads/master 
64d3c9e [email protected]{16}: rebase -i (fixup): Fixes external dependencies 
049169e [email protected]{17}: rebase -i (fixup): updating HEAD 
d4c2e69 [email protected]{18}: rebase -i (pick): Fixes external dependencies 
049169e [email protected]{19}: rebase -i (fixup): Imports futures 
e490bed [email protected]{20}: rebase -i (fixup): updating HEAD 
... 
etc 
... 

mirar hacia adelante a la última de comprometerse de foo antes de la fusión. Dependiendo de lo que hayas hecho, puede ser difícil o no. Es por eso que no he proporcionado un script do-it.

Recoja el ID de confirmación que tiene la última confirmación para foo antes de la rebase y luego compare con esa identificación de confirmación. Dicen que es Commit Identificación: XXXX:

git diff XXXX...foo 

Tal vez eso es lo que quiere.

+0

Cuando respondí a twalberg, esto no excluirá los cambios de master en los que volví a basarme, y por lo tanto es muy ruidoso. (Por cierto, cavar a través del reflog de HEAD no es necesario, el reflog de * branch * contendrá solo los estados antes y después, y 'foo @ {1}' es exactamente el commit que se propone buscar manualmente. '@' mira en el reflog, no en el ancestro.) –

+0

@KevinReid Tienes razón, confundí '@' con '~'. No los uso a menudo. Lo siento. – manu

1

Un enfoque ligeramente diferente: Git tiene un buen soporte para mostrar esto en el caso de fusiones. es decir"¿Qué cambió en relación con parent-1 que no puede ser explicado por parent-2?"

Hay un par de maneras que usted puede aprovechar que el apoyo a su rebase:

Opción 1:

  1. Haga una de usar y tirar fusionar primero (en lugar de un rebase). Luego, la pantalla de fusión normal mostrará qué cambió. Comprueba que esto es lo que querías.

  2. Retroceda y vuelva a calcular, y compare la punta rebasada con el resultado de la combinación: deben ser idénticos.

Opción 2 (si cambio de base es más fácil que la fusión):

  1. Haz lo rebase.
  2. Usa un injerto para que parezca una combinación local/temporal.

Para ello, crear un .git/info/injertos con una sola línea:

TIP UPSTREAM OLDTIP 

Dónde TIP es el ID de cometer su sucursal porcentualizada y los otros son los dos padres deseados (es decir, las dos cosas que te hubieras fusionado, si hubieras estado fusionándote). Luego examínelo como si fuera una fusión real.

No estoy seguro acerca de la automatización; Tiendo a hacer esto con gitk abierto, porque hace que la comparación de las revisiones correctas sea fácil (usando el menú contextual).

Si realmente quiere comparar dos diffs, entonces puede que quiera ver interdiff (1), pero no estoy seguro de qué tan bien hará frente a los archivos base que son diferentes.

+0

Con respecto a la opción 1, en los casos en que deseo hacer esta verificación, es muy probable que una combinación tenga * conflictos * diferentes a una rebase, y en cualquier caso, estoy buscando verificar la rebase que realmente sucedió. En cuanto a la opción 2, la probé y descubrí que la vista está saturada con los cambios introducidos en el maestro, que es exactamente lo que quiero ocultar. No tengo 'interdiff', pero su página man dice" Los diffs deben ser ambos relativos a los mismos archivos. " –

+0

Tengo una respuesta similar para esto que funciona para una serie y es básicamente" simplemente use commit-tree para generar la fusión post-rebase suponiendo que el resultado de la fusión debe ser el que sea el árbol de la rebase finalizada Esto simplemente nos permite esencialmente fusionar desde la parte anterior a v1 y forzar el resultado para tener el árbol de v2. Esto puede ser pasó a git-show y producirá el adorable formato combinado, sin tener que depender de injertos, y por lo tanto puede ser automatizado. – Jake

2

git-cherry busca confirmaciones en una rama que no están en otra. Tendrá que utilizar como:

git cherry -v OLDTIP TIP UPSTREAM 

es decir, buscar confirmaciones que estaban en OLDTIP pero que no son en UPSTREAM..TIP. Mira la firma del parche para ver si se incluye un parche, por lo que si se agregó un parche, se descartó o se modificó durante la rebase, aparecerá en la lista. Las cosas que se aplicaron sin cambios no aparecerán.

Se puede lograr un efecto similar regresando a OLDTIP y haciendo git rebase -i TIP, ya que utiliza la misma lógica para completar la lista de confirmaciones.

+0

Esto no parece ayudar con el examen de los cambios dentro del archivo. Estoy interesado en hunk- cambios de nivel perdidos o dañados debido a resoluciones de conflicto incorrectas, no commits completos. –

+0

La idea aquí es reducir el número de confirmaciones que necesita para comparar (idealmente a cero). No es útil si h Hay un pequeño número de confirmaciones grandes y conflictivas, pero podría ayudar con una gran cantidad de confirmaciones pequeñas. –

+0

Ah, supongo, que con este estándar tengo un pequeño número de compromisos grandes, porque la siguiente pregunta que me vino a la mente es "Una vez que tengo el compromiso de comparar, ¿cómo propones que los compare? aparte de realizar el diff-diff con el que comencé? " –

3

Lo que realmente queremos mostrar es la diferencia combinada de conflicto que se genera como si hubiésemos hecho una combinación para obtener de (a) a (b) donde (a) estaba basado previamente (en sentido ascendente) y (b) ahora se basa en (upstream-new).

No queremos solo una diferencia directa.

En su lugar, básicamente podemos hacer la fusión, pero forzar al árbol resultante a ser $ b^{tree}, que ya sabemos que es el punto "final" correcto de lo que queremos.

Más o menos, vamos a suponer que tenemos

newcommit -> the new version of the series 
oldcommit -> the old version of the series 
upstream -> the (new) version of the base of the series 

podemos generar la fusión a través de

git commit-tree newcommit^{tree} -p oldcommit -p upstream -m "message" 

y luego mostrar el resultado de "mostrar git" y esto generará un diff combinado formato que muestra todos los bits necesarios como una resolución de conflicto que ignora automáticamente los cambios que no fueron parte de la resolución de conflictos.

Esto funciona incluso para simplemente ammending un cambio así, y se puede hacer un poco más de trabajo para asegurarse de que la fusión generada cometer tiene autor exacta y marcas temporales para que sea consistente a lo largo de varias llamadas (ya que somos almacenamiento una referencia suelta en la base de datos de objetos).

Desafortunadamente no he podido averiguar cómo hacer que "git diff" difiera de la misma forma sin generar un árbol todavía. No estoy seguro de qué argumentos tendríamos que pasar para llegar a ese punto.

+0

¡Idea ingeniosa! Tendré que ver qué tan útiles son los resultados la próxima vez que tenga este problema. –

+0

Solo tuve la oportunidad de probar esto. Encontré una advertencia que debe tenerse en cuenta: 'upstream' _deleted_ un archivo que tenía cambios (para dividirlo en varios otros archivos), y la diferencia producida por esta técnica no menciona ese archivo en absoluto, por lo que no se mostraría la pérdida de esos cambios. –