No está realmente relacionado con esta respuesta, pero me desharía git pull
, que solo se ejecuta git fetch
seguido de git merge
. Estás haciendo tres fusiones, lo que hará que tu Git ejecute tres operaciones de recuperación, cuando una recuperación es todo lo que necesitas. Por lo tanto:
git fetch origin # update all our origin/* remote-tracking branches
git checkout demo # if needed -- your example assumes you're on it
git merge origin/demo # if needed -- see below
git checkout master
git merge origin/master
git merge -X theirs demo # but see below
git push origin master # again, see below
Controlando la fusión más complicada
La parte más interesante aquí es git merge -X theirs
. Como señaló root545 , las -X
opciones se pasan a la estrategia de fusión, y tanto la recursive
estrategia predeterminada como la resolve
estrategia alternativa toman -X ours
o -X theirs
(una u otra, pero no ambas). Sin embargo, para comprender lo que hacen, debe saber cómo Git encuentra y trata los conflictos de fusión .
Un conflicto de fusión puede ocurrir dentro de algún archivo 1 cuando la versión base difiere tanto de la versión actual (también llamada local, HEAD o --ours
) como de la otra versión (también llamada remota o --theirs
) de ese mismo archivo. Es decir, la fusión ha identificado tres revisiones (tres confirmaciones): base, nuestra y de ellos. La versión "base" es de la base de fusión entre nuestro compromiso y su compromiso, como se encuentra en el gráfico de compromiso (para obtener más información sobre esto, consulte otras publicaciones de StackOverflow). Luego, Git encontró dos conjuntos de cambios: "lo que hicimos" y "lo que hicieron". Estos cambios se encuentran (en general) en una línea por línea, puramente textualbase. Git no tiene una comprensión real del contenido de los archivos; es simplemente comparar cada línea de texto.
Estos cambios son lo que ve en la git diff
salida y, como siempre, también tienen contexto . Es posible que las cosas que cambiamos estén en líneas diferentes de las que cambiaron, por lo que los cambios parecen no colisionar, pero el contexto también ha cambiado (por ejemplo, debido a que nuestro cambio está cerca de la parte superior o inferior del archivo, por lo que el archivo se agota en nuestra versión, pero en la de ellos, también han agregado más texto en la parte superior o inferior).
Si los cambios ocurren en diferentes líneas, por ejemplo, cambiamos color
a la colour
línea 17 y ellos fred
a la barney
línea 71, entonces no hay conflicto: Git simplemente toma ambos cambios. Si los cambios ocurren en las mismas líneas, pero son cambios idénticos , Git toma una copia del cambio. Solo si los cambios están en la misma línea, pero son cambios diferentes, o ese caso especial de contexto de interferencia, se obtiene un conflicto de modificación / modificación.
Las opciones -X ours
y le -X theirs
dicen a Git cómo resolver este conflicto, eligiendo solo uno de los dos cambios: el nuestro o el de ellos. Como dijiste que estás fusionando demo
(el de ellos) con master
(el nuestro) y quieres los cambios de demo
, querrás -X theirs
.
Aplicar a ciegas -X
, sin embargo, es peligroso. ¡El hecho de que nuestros cambios no entren en conflicto línea por línea no significa que nuestros cambios en realidad no entren en conflicto! Un ejemplo clásico ocurre en lenguajes con declaraciones de variables. La versión base puede declarar una variable no utilizada:
int i;
En nuestra versión, eliminamos la variable no utilizada para que desaparezca la advertencia del compilador, y en su versión, agregan un bucle algunas líneas más tarde, utilizando i
como contador de bucle. Si combinamos los dos cambios, el código resultante ya no se compila. La -X
opción no es de ayuda aquí ya que los cambios están en diferentes líneas .
Si tiene un conjunto de pruebas automatizado, lo más importante que debe hacer es ejecutar las pruebas después de la fusión. Puede hacer esto después de comprometerse y arreglar las cosas más tarde si es necesario; o puede hacerlo antes de confirmar, agregando --no-commit
al git merge
comando. Dejaremos los detalles de todo esto para otras publicaciones.
1 También puede tener conflictos con respecto a las operaciones de "todo el archivo", por ejemplo, tal vez arreglemos la ortografía de una palabra en un archivo (para que tengamos un cambio), y eliminen todo el archivo (para que tengan un Eliminar). Git no resolverá estos conflictos por sí solo, independientemente de los -X
argumentos.
Hacer menos fusiones y / o fusiones más inteligentes y / o usar rebase
Hay tres fusiones en nuestras dos secuencias de comandos. El primero es traerlo origin/demo
al local demo
(los usos suyos git pull
que, si su Git es muy antiguo, no se actualizarán origin/demo
pero producirán el mismo resultado final). La segunda es llevar origin/master
a master
.
No tengo claro quién está actualizando demo
y / o master
. Si escribe su propio código en su propia demo
rama, y otros escriben código y lo empujan a la demo
rama origin
, entonces esta fusión del primer paso puede tener conflictos o producir una fusión real. La mayoría de las veces, es mejor usar rebase, en lugar de fusionar, para combinar el trabajo (es cierto que esto es una cuestión de gustos y opiniones). Si es así, es posible que desee utilizar git rebase
en su lugar. Por otro lado, si nunca realiza ninguna de sus propias confirmaciones demo
, ni siquiera necesita una demo
rama. Alternativamente, si desea automatizar mucho de esto, pero puede verificar cuidadosamente cuando hay confirmaciones que tanto usted como otros hicieron, es posible que desee usargit merge --ff-only origin/demo
: esto avanzará rápidamente demo
para que coincida con el actualizado origin/demo
si es posible, y simplemente fallará por completo si no (en ese momento puede inspeccionar los dos conjuntos de cambios y elegir una combinación real o una rebase según corresponda).
Esta misma lógica se aplica a master
, a pesar de que está haciendo la combinación de master
, por lo que definitivamente necesita una master
. Sin embargo, es incluso más probable que desee que la fusión falle si no se puede realizar como una no fusión de avance rápido, por lo que probablemente también debería ser así git merge --ff-only origin/master
.
Digamos que nunca haces tus propios compromisos demo
. En este caso, podemos deshacernos del nombre por demo
completo:
git fetch origin # update origin/*
git checkout master
git merge --ff-only origin/master || die "cannot fast-forward our master"
git merge -X theirs origin/demo || die "complex merge conflict"
git push origin master
Si está haciendo sus propias demo
confirmaciones de rama, esto no es útil; también puede mantener la combinación existente (pero tal vez agregarla --ff-only
según el comportamiento que desee), o cambiarla para hacer una rebase. Tenga en cuenta que los tres métodos pueden fallar: la combinación puede fallar con un conflicto, la combinación con --ff-only
puede no poder avanzar rápidamente y la rebase puede fallar con un conflicto (la rebase funciona, en esencia, mediante la selección de confirmaciones, que usa la combinación maquinaria y, por lo tanto, puede tener un conflicto de fusión).
git push -f origin master