¿Existe una versión "suya" de "git merge -s our"?

876

Al fusionar la rama de tema "B" en "A" usando git merge, obtengo algunos conflictos. Sé que todos los conflictos se pueden resolver utilizando la versión en "B".

Soy consciente de git merge -s ours. Pero lo que quiero es algo comogit merge -s theirs .

¿Por qué no existe? ¿Cómo puedo lograr el mismo resultado después de la fusión conflictiva con los gitcomandos existentes ? (git checkout cada archivo no combinado de B)

ACTUALIZACIÓN: La "solución" de simplemente descartar cualquier cosa de la rama A (el punto de confirmación de fusión a la versión B del árbol) no es lo que estoy buscando.

elmarco
fuente
1
También vea esta respuesta: stackoverflow.com/questions/928646/… - es trivial cambiar el ejemplo para usar la versión B en lugar de A.
Robie Basak
8
Vea el comando SO answer git para hacer una rama como otra para todas las formas actuales posibles de simulacióngit merge -s their .
VonC
44
Entonces, realmente no está buscando una git merge -s theirs(que se puede lograr fácilmente con git merge -s oursuna rama temporal), ya que la nuestra ignora por completo los cambios de la rama de fusión ...
Nicolò Martini
21
@Torek - do el Git proyectos de desarrollo realmente resulta que la ofensiva para proporcionar theirs, además de ours??? Este es un síntoma de uno de los problemas de ingeniería y diseño de alto nivel en Git: inconsistencia.
jww
3
@jww El problema aquí no es que "git merge -s our" sea ofensivo, sino que sea contra-intuitivo. Se puede ver en la pregunta del OP que si se agregara tal característica, la usaría por error cuando lo que realmente quiere hacer es "git merge -s recursive -X suyo". Es común querer fusionar otra rama anulando conflictos con la versión en la otra rama, pero sobrescribir completamente la rama actual con otra rama descartando completamente los cambios actuales es realmente un caso de excepción.
ThomazMoura

Respuestas:

1020

Agregue la -Xopción a theirs. Por ejemplo:

git checkout branchA
git merge -X theirs branchB

Todo se fusionará de la manera deseada.

Lo único que he visto causar problemas es si los archivos se eliminaron de la rama B. Aparecen como conflictos si algo más que git hizo la eliminación.

La solución es fácil. Simplemente ejecute git rmcon el nombre de los archivos que se eliminaron:

git rm {DELETED-FILE-NAME}

Después de eso, el -X theirsdebería funcionar como se esperaba.

Por supuesto, hacer la eliminación real con el git rmcomando evitará que el conflicto ocurra en primer lugar.


Nota : También existe una opción de formulario más largo.

Para usarlo, reemplace:

-X theirs

con:

--strategy-option=theirs
Alan W. Smith
fuente
66
Vea otra respuesta a continuación para una mejor solución (Paul Pladijs) a la pregunta original.
Malcolm
204204
Vale la pena señalar que esto no es lo mismo que "estrategia de fusión theirs". -Xtheirs es la opción de estrategia aplicada a la estrategia recursiva . Esto significa que la estrategia recursiva aún fusionará todo lo que pueda, y solo recurrirá a la lógica "suya" en caso de conflicto. Si bien esto es lo que uno necesita en la mayoría de los casos como el anterior, no es lo mismo que "simplemente tome todo de la rama B tal como está". Realmente se fusiona de todos modos.
Timur el
2
Funciona con otros comandos también. Acabo de hacer un 'git cherry-pick sha1 -X suyo'. ¡Gracias!
Max Hohenegger
48
Esta es una respuesta terrible, porque parece correcta, pero en realidad está mal. "git merge -X suyo" no hace lo que haría "git merge -s suyo". No reemplaza la rama actual con el contenido de la rama combinada. Prefiere los cambios "suyos", pero solo en caso de conflicto. Por lo tanto, la confirmación resultante puede ser bastante diferente de la rama "suya". Esto no es lo que el cartel de la pregunta tenía en mente.
Ivan Krivyakov
77
@ user3338098: sí, tienes razón. Volví a leer la pregunta, y tampoco es tan buena. Desafortunadamente, la causa raíz de la confusión no es la respuesta y ni siquiera la pregunta. Es la elección del diseño de los autores de git dar el mismo nombre 'nuestro' a una estrategia de fusión y a una opción de estrategia de fusión 'recursiva'. La confusión en este caso es prácticamente inevitable.
Ivan Krivyakov
218

Una solución posible y probada para fusionar la rama B en nuestra rama extraída A:

# in case branchA is not our current branch
git checkout branchA

# make merge commit but without conflicts!!
# the contents of 'ours' will be discarded later
git merge -s ours branchB    

# make temporary branch to merged commit
git branch branchTEMP         

# get contents of working tree and index to the one of branchB
git reset --hard branchB

# reset to our merged commit but 
# keep contents of working tree and index
git reset --soft branchTEMP

# change the contents of the merged commit
# with the contents of branchB
git commit --amend

# get rid off our temporary branch
git branch -D branchTEMP

# verify that the merge commit contains only contents of branchB
git diff HEAD branchB

Para automatizarlo, puede envolverlo en un script usando branchA y branchB como argumentos.

Esta solución conserva el primer y segundo padre de la confirmación de fusión, tal como cabría esperar git merge -s theirs branchB.

Paul Pladijs
fuente
P: ¿Por qué necesitas branchTEMP? ¿No podrías simplemente git reset --soft branchA?
cdunn2001
44
@ cdunn2001: De alguna manera pensé lo mismo, pero no. Tenga en cuenta que los git reset --hardcambios que commit branchAapunta a.
Tsuyoshi Ito
55
lamento hacer una pregunta estúpida, pero ¿por qué este método es mejor que el de los demás? ¿Cuáles son las ventajas / desventajas
chrispepper1989
99
@ chrispepper1989 -x el suyo solo afecta a los conflictos , si un trozo nuestro puede aplicarse limpiamente, pasará a la fusión resultante. En este caso, como se muestra en el último comando, estamos obteniendo una fusión que es perfectamente idéntica a la rama B, independientemente de si hubiera habido conflictos o no.
UncleZeiv
2
@UncleZeiv gracias por la explicación, por lo que básicamente hacer un "suyo" mantendría los cambios sin conflicto y los archivos resultantes de código int que no es idéntico al código extraído.
chrispepper1989
89

Las versiones anteriores de git le permitían usar la estrategia de fusión "de ellos":

git pull --strategy=theirs remote_branch

Pero esto se ha eliminado desde entonces, como lo explica Junio ​​Hamano (el responsable de mantenimiento de Git) en este mensaje . Como se señaló en el enlace, en su lugar haría esto:

git fetch origin
git reset --hard origin

Sin embargo, tenga en cuenta que esto es diferente a una fusión real. Su solución es probablemente la opción que realmente está buscando.

Pat Notz
fuente
77
Realmente no entiendo la explicación de Junio ​​Hamano en absoluto. ¿Cómo es git reset --hard originuna solución para unir su estilo? Si quisiera fusionar BranchB en BranchA (como en la respuesta de Alan W. Smith), ¿cómo lo haría usando el resetmétodo?
James McMahon
3
@James McMahon: El punto de Junio ​​C Hamano no git reset --hardes una fusión de estilo "suyo". Por supuesto, git reset --hardno crea ninguna confirmación de fusión, ni ninguna confirmación para el caso. Su punto es que no deberíamos usar una combinación para reemplazar lo que sea en HEAD por otra cosa. Sin embargo, no estoy necesariamente de acuerdo.
Tsuyoshi Ito
11
Es una pena que theirsse haya eliminado porque la lógica era incompleta. No permitió esas instancias donde el código es bueno, es solo que el flujo ascendente se mantiene sobre una base filosófica diferente, por lo que en ese sentido es 'malo', por lo que uno quiere mantenerse actualizado con el flujo ascendente, pero al mismo tiempo, conserve los buenos 'arreglos' de código [git y msysgit tienen esto en parte de este 'conflicto' debido a las filosofías de sus diferentes plataformas de destino]
Philip Oakley
1
También me falta el caso de uso con el que estoy atrapado en este momento, donde estoy compartiendo una rama con otra persona y los dos empujamos los cambios (en diferentes archivos) al mismo tiempo. Así que quiero golpear todas las versiones antiguas que tengo localmente y usar las más nuevas. Quiere hacer lo mismo con los archivos que actualicé. No hay nada que "restablecer". Y la solución para pagar cada archivo individual no es práctica cuando son ~ 20 archivos.
szeitlin
2
Realmente no entiendo la filosofía. Simplemente lo utilicé theirsporque había cambiado accidentalmente algunos archivos en dos ramas separadas y quería descartar los cambios de uno sin tener que hacerlo manualmente para cada archivo.
sudo
65

No está del todo claro cuál es el resultado deseado, por lo que existe cierta confusión sobre la forma "correcta" de hacerlo en las respuestas y sus comentarios. Intento dar una visión general y ver las siguientes tres opciones:

Intente fusionar y usar B para conflictos

Esta no es la "versión de ellos para git merge -s ours" sino la "versión de ellos para git merge -X ours" (que es la abreviatura de git merge -s recursive -X ours):

git checkout branchA
# also uses -s recursive implicitly
git merge -X theirs branchB

Esto es lo que hace, por ejemplo, la respuesta de Alan W. Smith .

Use contenido de B solamente

Esto crea una confirmación de fusión para ambas ramas, pero descarta todos los cambios branchAy solo mantiene el contenido branchB.

# Get the content you want to keep.
# If you want to keep branchB at the current commit, you can add --detached,
# else it will be advanced to the merge commit in the next step.
git checkout branchB

# Do the merge an keep current (our) content from branchB we just checked out.
git merge -s ours branchA

# Set branchA to current commit and check it out.
git checkout -B branchA

Tenga en cuenta que la fusión confirma que el primer padre ahora es de branchBy solo el segundo es de branchA. Esto es lo que hace, por ejemplo, la respuesta de Gandalf458 .

Use contenido de B solamente y mantenga el orden principal correcto

Esta es la verdadera "versión para ellos git merge -s ours". Tiene el mismo contenido que en la opción anterior (es decir, solo el de branchB), pero el orden de los padres es correcto, es decir, el primer padre proviene branchAy el segundo de branchB.

git checkout branchA

# Do a merge commit. The content of this commit does not matter,
# so use a strategy that never fails.
# Note: This advances branchA.
git merge -s ours branchB

# Change working tree and index to desired content.
# --detach ensures branchB will not move when doing the reset in the next step.
git checkout --detach branchB

# Move HEAD to branchA without changing contents of working tree and index.
git reset --soft branchA

# 'attach' HEAD to branchA.
# This ensures branchA will move when doing 'commit --amend'.
git checkout branchA

# Change content of merge commit to current index (i.e. content of branchB).
git commit --amend -C HEAD

Esto es lo que hace la respuesta de Paul Pladijs (sin requerir una rama temporal).

siegi
fuente
Una versión más limpia de la opción 3:git checkout branchA; git merge -s ours --no-commit branchB; git read-tree -um @ branchB; git commit
2016
@jthill: Si bien su versión es más concisa, también utiliza el comando de bajo nivel ("plomería") al read-treeque el usuario promedio de git podría no estar acostumbrado (al menos no lo estoy ;-)). Las soluciones en la respuesta usan solo los comandos de alto nivel ("porcelana") que la mayoría de los usuarios de git deberían conocer. ¡Los prefiero pero gracias por tu versión, sin embargo!
siegi
¿Puedes explicar el penúltimo paso (checkout branchA)? Parece que no debería estar allí.
Patrick
@Patrick, este paso es obligatorio. Si lo omite, obtendrá la misma confirmación pero no tendría una rama que apunte a esta confirmación. Entonces, tan pronto como cambie a otro commit / branch, el commit que hizo con el último comando ( …commit --amend…) se "perderá". Estoy planeando agregar algunas explicaciones gráficas a esta respuesta tan pronto como tenga tiempo de hacerlo ... ;-)
siegi
62

Usé la respuesta de Paul Pladijs desde ahora. Descubrí que puedes hacer una fusión "normal", se producen conflictos, así que

git checkout --theirs <file>

para resolver el conflicto utilizando la revisión de la otra rama. Si hace esto para cada archivo, tiene el mismo comportamiento que esperaría de

git merge <branch> -s theirs

De todos modos, ¡el esfuerzo es más de lo que sería con la estrategia de fusión! (Esto fue probado con git versión 1.8.0)

Musicmatze
fuente
1
sería genial si se pudiera recuperar la lista de archivos. Por ejemplo, git ls-files --modified | xargs git addquería hacer esto para agregar en ambos lados fusionar: /
andho
En realidad, git devuelve los archivos agregados en ambos lados, git ls-files --modifiedasí que supongo que esta también es una solución viable.
andho
1
Gracias por agregar esto también. Más a menudo, entonces no es necesario en una base de archivo por archivo. Además, el estado de git muestra "Rutas no fusionadas" en la parte inferior. Para obtener la lista de rutas no resueltas, utilizo este alias:git config --global alias.unresolved '!git status --short|egrep "^([DAU])\1"'
Melvyn
¿Cuál es el significado de -Xy cuál es la diferencia entre -Xy -s? No puedo encontrar ningún documento.
Lei Yang
21

Resolví mi problema usando

git checkout -m old
git checkout -b new B
git merge -s ours old
elmarco
fuente
una rama "B" de la rama "vieja"
elmarco
13

Al fusionar la rama de tema "B" en "A" usando git merge, obtengo algunos conflictos. Sé que todos los conflictos se pueden resolver utilizando la versión en "B".

Soy consciente de git merge -s our. Pero lo que quiero es algo así como git merge> -s suyo.

Supongo que creó una rama de master y ahora desea volver a fusionarse en master, anulando cualquiera de las cosas antiguas en master. Eso es exactamente lo que quería hacer cuando me encontré con esta publicación.

Haz exactamente lo que quieres hacer, excepto combinar primero una rama con la otra. Acabo de hacer esto, y funcionó muy bien.

git checkout Branch
git merge master -s ours

Luego, finalice el proceso de pago y combine su rama en él (ahora se realizará sin problemas):

git checkout master
git merge Branch
Gandalf458
fuente
1
Gracias. Esta debería ser la respuesta aceptada, con mucho, la más simple y la más correcta. Si su rama está detrás / entra en conflicto con el maestro, entonces es el trabajo de la rama fusionarse y hacer que todo funcione antes de fusionar todo con el maestro.
StackHola
Esto funcionó de maravilla, gracias.
Kodie Grantham
12

Si estás en la rama A, haz:

git merge -s recursive -X theirs B

Probado en git versión 1.7.8

rafalmag
fuente
8

Para hacer realmente una fusión que solo toma información de la rama que está fusionando, puede hacer

git merge --strategy=ours ref-to-be-merged

git diff --binary ref-to-be-merged | git apply --reverse --index

git commit --amend

No habrá conflictos en ningún escenario que conozca, no tiene que hacer ramificaciones adicionales, y actúa como una confirmación de fusión normal.

Sin embargo, esto no juega bien con los submódulos.

Thoutbeckers
fuente
¿Qué hace el -R aquí?
P. Myer Nore
De esta forma, pierde el historial de archivos "movidos y modificados". Estoy en lo cierto?
elysch
1
El -R es --reverse, actualicé la respuesta con eso para que sea más autodocumentado.
Elijah Lynn el
Esto no parece funcionar si los archivos que está intentando fusionar se eliminan en la rama entrante.
resolviendoJ
7

¿Por qué no existe?

Si bien menciono en " comando git para hacer una rama como otra " cómo simular git merge -s theirs, tenga en cuenta que Git 2.15 (Q4 2017) ahora es más claro:

La documentación para ' -X<option>' para fusiones se escribió de manera engañosa para sugerir que " -s theirs" existe, lo cual no es el caso.

Ver commit c25d98b (25 de septiembre de 2017) por Junio ​​C Hamano ( gitster) .
(Fusionada por Junio ​​C Hamano - gitster- en commit 4da3e23 , 28 sep 2017)

estrategias de fusión: evite implicar que " -s theirs" existe

La descripción de -Xours opción fusión tiene una nota entre paréntesis que le dice a los lectores que es muy diferente de -s ours, lo cual es correcto, pero la descripción de lo -Xtheirsque sigue dice descuidadamente "esto es lo contrario de ours", dando una falsa impresión de que los lectores también necesitan ser advertido de que es muy diferente de lo -s theirsque en realidad ni siquiera existe.

-Xtheirses una opción de estrategia aplicada a la estrategia recursiva. Esto significa que la estrategia recursiva aun fusionará todo lo que pueda, y solo " theirs" volverá a la lógica en caso de conflictos.

Ese debate sobre la pertinencia o no de una theirsestrategia de fusión se trajo recientemente en este hilo de septiembre de 2017 .
Reconoce hilos más antiguos (2008)

En resumen, la discusión previa se puede resumir en "no queremos" -s theirs, ya que fomenta el flujo de trabajo incorrecto ".

Menciona el alias:

mtheirs = !sh -c 'git merge -s ours --no-commit $1 && git read-tree -m -u $1' -

Yaroslav Halchenko intenta abogar una vez más por esa estrategia, pero Junio ​​C. Hamano agrega :

La razón por la cual la nuestra y la de ellos no es simétrica es porque usted es usted y no ellos: el control y la propiedad de nuestra historia y su historia no es simétrica.

Una vez que decida que su historial es la línea principal, preferiría tratar su línea de desarrollo como una rama lateral y hacer una fusión en esa dirección, es decir, el primer padre de la fusión resultante es un compromiso en su historial y el segundo padre es el último malo de tu historia. Entonces terminarías usando "checkout their-history && merge -s ours your-history " para mantener sensible la primera paternidad.

Y en ese punto, el uso de " -s ours" ya no es una solución por falta de " -s theirs".
Es una parte adecuada de la semántica deseada, es decir, desde el punto de vista de la línea de historia canónica sobreviviente, desea preservar lo que hizo, anulando lo que hizo la otra línea de historia. .

Junio ​​agrega, como comentó Mike Beaton :

git merge -s ours <their-ref>efectivamente dice 'marcar confirmaciones hechas <their-ref>en su rama como confirmaciones para ser ignoradas permanentemente';
y esto es importante porque, si posteriormente se fusiona desde estados posteriores de su rama, sus cambios posteriores se introducirán sin que se introduzcan los cambios ignorados .

VonC
fuente
1
Esta es una respuesta útil, pero no menciona un punto clave que acabo de entender de la respuesta de Junio ​​C. Hamano: git merge -s ours <their-ref>efectivamente dice 'marca confirmaciones hechas a <their-ref> en su rama como confirmaciones para ignorar permanentemente '; y esto es importante porque, si posteriormente se fusionan de los estados posteriores de su rama, sus cambios posteriores serán traídos sin los cambios ignorados nunca ser llevado en.
MikeBeaton
1
@ MikeBeaton Gracias. He incluido tu comentario en la respuesta para mayor visibilidad.
VonC
5

Ver la respuesta ampliamente citada de Junio ​​Hamano : si va a descartar el contenido comprometido, simplemente descarte los commits o, en cualquier caso, manténgalo fuera de la historia principal. ¿Por qué molestar a todos en el futuro leer mensajes de commit de commits que no tienen nada que ofrecer?

Pero a veces hay requisitos administrativos, o quizás alguna otra razón. Para aquellas situaciones en las que realmente tiene que registrar confirmaciones que no aportan nada, desea:

(edit: wow, ¿me las arreglé para equivocarme antes? Esto funciona).

git update-ref HEAD $(
        git commit-tree -m 'completely superseding with branchB content' \
                        -p HEAD -p branchB    branchB:
)
git reset --hard
jthill
fuente
3

Este utiliza un comando de árbol de lectura git plumbing, pero hace un flujo de trabajo general más corto.

git checkout <base-branch>

git merge --no-commit -s ours <their-branch>
git read-tree -u --reset <their-branch>
git commit

# Check your work!
git diff <their-branch>
Michael R
fuente
0

Esto fusionará su newBranch en baseBranch existente

git checkout <baseBranch> // this will checkout baseBranch
git merge -s ours <newBranch> // this will simple merge newBranch in baseBranch
git rm -rf . // this will remove all non references files from baseBranch (deleted in newBranch)
git checkout newBranch -- . //this will replace all conflicted files in baseBranch
Pawan Maheshwari
fuente
Sugeriría agregar git commit --amendal final de su ejemplo, o los usuarios pueden ver la confirmación de fusión git logy asumir que la operación se ha completado.
Michael R
0

Creo que lo que realmente quieres es:

git checkout -B mergeBranch branchB
git merge -s ours branchA
git checkout branchA
git merge mergeBranch
git branch -D mergeBranch

Esto parece torpe, pero debería funcionar. Lo único que realmente no me gusta de esta solución es que el historial de git será confuso ... Pero al menos el historial se conservará por completo y no necesitará hacer algo especial para los archivos eliminados.

briemers
fuente
0

El equivalente (que mantiene el orden principal) a 'git merge -s theirs branchB'

Antes de fusionar:ingrese la descripción de la imagen aquí

!!! ¡Asegúrate de estar limpio!

Haz la fusión:

git commit-tree -m "take theirs" -p HEAD -p branchB 'branchB^{tree}'
git reset --hard 36daf519952 # is the output of the prev command

Lo que hicimos ? Creamos un nuevo commit en el que dos padres son nuestros y los suyos y el contenido del commit es branchB - suyo

Después de fusionar:ingrese la descripción de la imagen aquí

Más precisamente:

git commit-tree -m "take theirs" -p HEAD -p 'SOURCE^{commit}' 'SOURCE^{tree}'
Boaz Nahum
fuente
0

Esta respuesta fue dada por Paul Pladijs. Solo tomé sus órdenes e hice un alias git por conveniencia.

Edite su .gitconfig y agregue lo siguiente:

[alias]
    mergetheirs = "!git merge -s ours \"$1\" && git branch temp_THEIRS && git reset --hard \"$1\" && git reset --soft temp_THEIRS && git commit --amend && git branch -D temp_THEIRS"

Entonces puedes "git merge -s suyo" ejecutando:

git checkout B (optional, just making sure we're on branch B)
git mergetheirs A
Brain2000
fuente
-1

Hace poco necesitaba hacer esto para dos repositorios separados que comparten un historial común. Empecé con:

  • Org/repository1 master
  • Org/repository2 master

Quería repository2 masterque se aplicaran repository1 mastertodos los cambios, aceptando todos los cambios que haría el repositorio2. En términos de git, esta debería ser una estrategia llamada -s theirsPERO no existe. Tenga cuidado porque -X theirsse llama como si fuera lo que quiere, pero NO lo mismo (incluso lo dice en la página del manual).

La forma en que resolví esto fue ir repository2y hacer una nueva sucursal repo1-merge. En esa rama, corrígit pull [email protected]:Org/repository1 -s ours y se fusiona bien sin problemas. Luego lo empujo al control remoto.

Luego vuelvo repository1y hago una nueva sucursal repo2-merge. En esa rama, ejecuto git pull [email protected]:Org/repository2 repo1-mergeque se completará con problemas.

Finalmente, necesitaría emitir una solicitud de fusión repository1para convertirlo en el nuevo maestro, o simplemente mantenerlo como una rama.

Trann
fuente
-2

Una forma simple e intuitiva (en mi opinión) de dos pasos de hacerlo es

git checkout branchB .
git commit -m "Picked up the content from branchB"

seguido por

git merge -s ours branchB

(que marca las dos ramas como fusionadas)

La única desventaja es que no elimina los archivos que se han eliminado en la rama B de su rama actual. Una simple diferencia entre las dos ramas luego mostrará si hay alguno de estos archivos.

Este enfoque también deja en claro en el registro de revisión posterior lo que se hizo y lo que se pretendía.

Rubén
fuente
esto puede ser correcto para la pregunta original, sin embargo, el OP actualizó la pregunta en 2008 de tal manera que esta respuesta es incorrecta.
user3338098
1
@ user3338098 Sí, una lástima que dejaron el título engañoso y justo abofetearon una actualización no tan visible en la parte final del cuerpo pregunta .... porque esta respuesta hace responder correctamente a la pregunta del título.
RomainValeri
Estoy completamente de acuerdo con @RomainValeri
AppleCiderGuy