Fusión de Git con sobrescritura forzada

101

Tengo una rama llamada demoque necesito fusionar con la masterrama. Puedo obtener el resultado deseado con los siguientes comandos:

git pull origin demo
git checkout master
git pull origin master
git merge demo
git push origin master

Mi única preocupación es que, si hay algún problema de fusión , quiero indicarle gitque sobrescriba los cambios en la masterrama sin darme un mensaje de fusión. Entonces, básicamente, los cambios en la demorama deberían sobrescribir automáticamente los cambios en la masterrama.

Miré a mi alrededor, hay varias opciones, pero no quiero correr riesgos con la fusión.

OpenStack
fuente
git push -f origin master
MD XF
4
@MDXF: Puede que me equivoque, pero ¿no debería usar la -fopción con el mergecomando y no con el pushcomando?
OpenStack
Puede probar ambos y ver qué funciona para usted
MD XF
Consulte el enlace a continuación para obtener una solución de sobrescritura forzada
Manohar Reddy Poreddy

Respuestas:

103

No está realmente relacionado con esta respuesta, pero me desharía git pull, que solo se ejecuta git fetchseguido 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 -Xopciones se pasan a la estrategia de fusión, y tanto la recursiveestrategia predeterminada como la resolveestrategia alternativa toman -X ourso -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 diffsalida 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 colora la colourlínea 17 y ellos freda la barneylí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 oursy le -X theirsdicen 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 icomo contador de bucle. Si combinamos los dos cambios, el código resultante ya no se compila. La -Xopció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-commital git mergecomando. 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 -Xargumentos.


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/demoal local demo(los usos suyos git pullque, si su Git es muy antiguo, no se actualizarán origin/demopero producirán el mismo resultado final). La segunda es llevar origin/mastera master.

No tengo claro quién está actualizando demoy / o master. Si escribe su propio código en su propia demorama, y otros escriben código y lo empujan a la demorama 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 rebaseen su lugar. Por otro lado, si nunca realiza ninguna de sus propias confirmaciones demo, ni siquiera necesita una demorama. 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 demopara que coincida con el actualizado origin/demosi 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 democompleto:

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 democonfirmaciones de rama, esto no es útil; también puede mantener la combinación existente (pero tal vez agregarla --ff-onlysegú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-onlypuede 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).

torek
fuente
21

Tuve un problema similar, en el que necesitaba reemplazar efectivamente cualquier archivo que tuviera cambios / conflictos con una rama diferente.

La solución que encontré fue usar git merge -s ours branch.

Tenga en cuenta que la opción es -sy no -X. -sdenota el uso de ourscomo una estrategia de fusión de nivel superior, -Xsería aplicar la oursopción a la recursiveestrategia de fusión, que no es lo que yo (o queremos) queremos en este caso.

Pasos, dónde oldbranchestá la rama con la que desea sobrescribir newbranch.

  • git checkout newbranch comprueba la sucursal que quieres conservar
  • git merge -s ours oldbranch se fusiona en la rama anterior, pero conserva todos nuestros archivos.
  • git checkout oldbranch comprueba la rama que quieres sobrescribir
  • get merge newbranch se fusiona en la nueva rama, sobrescribiendo la antigua
Niall Mccormack
fuente
Nota: ninguna de las otras soluciones aquí funcionó para mí, por lo tanto, estoy publicando mi respuesta.
Niall Mccormack
1
No funcionó para mí. Resolvió el conflicto (resolvió los archivos en conflicto) pero el archivo no se fusionó. Y no se puede fusionar ninguno.
c-an
Si alguien se queda atascado donde se le pide "Ingrese un mensaje de confirmación para explicar por qué es necesaria esta combinación": Ingrese su mensaje, luego presione la tecla ESC en su teclado, escriba: wq y presione ENTER para salir del mensaje.
topherPedersen
19

Este enfoque de fusión agregará una confirmación sobre la mastercual se pega en lo que sea feature, sin quejarse de conflictos u otras tonterías.

ingrese la descripción de la imagen aquí

Antes de tocar nada

git stash
git status # if anything shows up here, move it to your desktop

Ahora prepara maestro

git checkout master
git pull # if there is a problem in this step, it is outside the scope of this answer

Consigue featuretodos los vestidos

git checkout feature
git merge --strategy=ours master

Ir a matar

git checkout master
git merge --no-ff feature
William Entriken
fuente
1
git empuja para terminar
Kochchy
git-scm.com/docs/git-merge#Documentation/git-merge.txt-ours. Funcionó cuando las confirmaciones no se fusionaban limpiamente. Pero este enfoque no siempre funcionará, para citar la fuente Changes from the other tree that do not conflict with our side are reflected in the merge result. No funcionó para mí, ya que había fusionado limpiamente confirmaciones en mi rama que se estaba sobrescribiendo. ¿Hay alguna otra manera?
NiharGht
11

Puedes probar la opción "nuestra" en git merge,

git merge branch -X nuestro

Esta opción obliga a que los trozos en conflicto se resuelvan automáticamente de forma limpia favoreciendo nuestra versión. Los cambios del otro árbol que no entran en conflicto con nuestro lado se reflejan en el resultado de la fusión. Para un archivo binario, todo el contenido se toma de nuestro lado.

raíz
fuente
3
Esto sería al revés, ya que el OP dijo que quiere que la demoversión sea preferida a pesar de que se está fusionando master. También lo hay -X theirs, pero no está claro si esto es lo que realmente quiere el OP.
torek
No has leído todo el camino. Su nota describe lo que ourshace cuando se usa en el backend con la -sopción. Cuando se usa ourscon -X: Descarta todo lo que hizo el otro árbol, declarando que nuestro historial contiene todo lo que sucedió en él. fuente
jimasun
2

Cuando intenté usar -X theirsy otros interruptores de comando relacionados, seguí obteniendo un compromiso de fusión. Probablemente no lo entendía correctamente. Una alternativa fácil de entender es simplemente eliminar la rama y luego rastrearla nuevamente.

git branch -D <branch-name>
git branch --track <branch-name> origin/<branch-name>

Esto no es exactamente una "fusión", pero esto es lo que estaba buscando cuando me encontré con esta pregunta. En mi caso, quería extraer cambios de una rama remota que fueron presionados a la fuerza.

Mate
fuente