rama principal y 'origen / maestro' han divergido, ¿cómo 'desvincular' ramas '?

965

De alguna manera mi maestro y mi rama de origen / maestro han divergido.
En realidad no quiero que diverjan.

¿Cómo puedo ver estas diferencias y 'fusionarlas'?

Franco
fuente
2
¿Qué quieres decir con divergir? ¿Rebasas a tu maestro después de empujarlo?
Hasen
13
Recibo un mensaje que dice "Su rama y 'origen / maestro' han divergido, # y tienen 1 y 1 confirmaciones diferentes cada una, respectivamente".
Frank
He actualizado mi respuesta para reflejar ese mensaje de advertencia "divergente".
VonC
La respuesta aceptada a otra pregunta también puede ser útil para resolver ciertos casos en los que esto podría entrar en juego (por ejemplo, está tratando de mover a su maestro, pero ya se ha presionado): stackoverflow.com/questions/2862590/…
lindes
8
La explicación en este blog me ayudó infinitamente más que cualquier respuesta a continuación: sebgoo.blogspot.com/2012/02/…

Respuestas:

1014

Puede revisar las diferencias con un:

git log HEAD..origin/master

antes de extraerlo (buscar + fusionar) (consulte también "¿Cómo se consigue que git siempre extraiga de una rama específica?" )


Cuando tienes un mensaje como:

"Su rama y 'origin / master' han divergido, # y tienen 1 y 1 commit (s) diferentes cada uno, respectivamente".

, verifique si necesita actualizarorigin . Si originestá actualizado, se han enviado algunas confirmaciones origindesde otro repositorio mientras usted realizaba sus propias confirmaciones localmente.

... o ---- o ---- A ---- B  origin/master (upstream work)
                   \
                    C  master (your work)

Usted basó el compromiso C en el compromiso A porque ese era el último trabajo que había obtenido de la cadena ascendente en ese momento.

Sin embargo, antes de intentar regresar al origen, otra persona presionó la confirmación B.
El historial de desarrollo ha divergido en caminos separados.

Luego puede fusionar o rebase. Ver Pro Git: Git Branching - Rebasing para más detalles.

Unir

Use el comando git merge:

$ git merge origin/master

Esto le dice a Git que integre los cambios origin/masteren su trabajo y cree una confirmación de fusión.
El gráfico de la historia ahora se ve así:

... o ---- o ---- A ---- B  origin/master (upstream work)
                   \      \
                    C ---- M  master (your work)

La nueva combinación, commit M, tiene dos padres, cada uno representando una ruta de desarrollo que condujo al contenido almacenado en ese commit.

Tenga en cuenta que el historial detrás de M ahora no es lineal.

Rebase

Use el comando git rebase:

$ git rebase origin/master

Esto le dice a Git que reproduzca el compromiso C (su trabajo) como si lo hubiera basado en el compromiso B en lugar de A. Los
usuarios de CVS y Subversion vuelven a redactar sus cambios locales en la parte superior del trabajo ascendente cuando se actualizan antes de confirmar.
Git solo agrega una separación explícita entre los pasos commit y rebase.

El gráfico de la historia ahora se ve así:

... o ---- o ---- A ---- B  origin/master (upstream work)
                          \
                           C'  master (your work)

Commit C 'es un nuevo commit creado por el comando git rebase.
Es diferente de C de dos maneras:

  1. Tiene una historia diferente: B en lugar de A.
  2. Su contenido explica los cambios tanto en B como en C; es lo mismo que M del ejemplo de fusión.

Tenga en cuenta que la historia detrás de C 'sigue siendo lineal.
Hemos elegido (por ahora) permitir solo la historia lineal en cmake.org/cmake.git.
Este enfoque conserva el flujo de trabajo basado en CVS utilizado anteriormente y puede facilitar la transición.
Un intento de insertar C 'en nuestro repositorio funcionará (suponiendo que tenga permisos y que nadie haya presionado mientras estaba haciendo un rebase).

El comando git pull proporciona una forma abreviada de buscar desde el origen y modificar el trabajo local en él:

$ git pull --rebase

Esto combina los pasos de recuperación y rebase anteriores en un solo comando.

VonC
fuente
55
Encontré esto mientras buscaba el mismo problema, ¿puede explicar por qué 'git reset --hard HEAD' no solucionó el problema?
Neth
12
@Neth: porque no se trata de modificaciones por etapas (es decir, modificaciones presentes en el índice pero aún no confirmadas), sino de confirmaciones locales (que difieren de las confirmaciones presentes en el control remoto). git reset --hard HEADsolo eliminaría cualquier modificación no comprometida indexada local y no haría nada para conciliar las diferencias entre las confirmaciones locales y remotas . Solo una fusión o un rebase reunirán los dos conjuntos de confirmaciones (la local y la remota).
VonC
44
Wow, gracias por esta increíble respuesta. ¡Habíamos hecho accidentalmente un "git pull" sin "--rebase", y "git rebase origin / master" era la solución!
mrooney
3
¿Qué tal si solo quiero ignorar / volcar mis cambios locales y estar con mi sucursal local donde está el control remoto? En otras palabras, quiero masterseñalar Bsu ejemplo.
CygnusX1
23
@ CygnusX1 que sería git reset --hard origin/mastercomo se menciona en la respuesta a continuación: stackoverflow.com/a/8476004/6309
VonC
726

Tenía esto y estoy desconcertado sobre lo que lo causó, incluso después de leer las respuestas anteriores. Mi solución fue hacer

git reset --hard origin/master

Luego, eso solo restablece mi copia (local) del maestro (que supongo que está jodida) en el punto correcto, como se representa por el origen / maestro (remoto).

ADVERTENCIA : perderá todos los cambios que aún no se hayan presionado origin/master.

skiphoppy
fuente
21
Sí, se parece un poco a la opción de tontos, pero si no hay peligro real y estás aquí para una solución rápida, esto funciona (para mí de todos modos)
PandaWood
77
Esto requiere estar en la rama maestra antes ("git checkout master").
azulado
Esto me sucedió una vez cuando obtuve lo último del origen, luego alguien más hizo un empuje forzado hacia el origen, lo que provocó la reversión del compromiso anterior en el origen. Entonces, mi sucursal local tenía la confirmación revertida, y cuando traté de sacar lo último del origen, decía que tenía que fusionarme. En este caso, simplemente tenía sentido restablecer --hard origin / (branch) porque el problema ya se había solucionado en origen.
Landon Poch
96
Probablemente debería advertir a los usuarios que esto los hará perder todos los cambios que aún no se han introducido en el origen
Pedro Loureiro
66
@PedroLoureiro Los commits son notas realmente perdidas, aún puedes encontrar los commits con git reflogellos o verlos gitk --all. Pero, sin embargo, por supuesto, el restablecimiento completo es algo más que un rebase.
sebkraemer
52
git pull --rebase origin/master 

es un comando único que puede ayudarlo la mayor parte del tiempo.

Editar: Extrae las confirmaciones del origen / maestro y aplica sus cambios al historial de rama recién extraído.

asitmoharna
fuente
89
por favor mencione lo que hace el comando, de lo contrario la gente podría ejecutarlo y terminar jodiendo
Baz1nga
1
Si no hay ningún problema, debe terminar con su maestro que contiene todos los cambios de origen / maestro, además de que todos sus compromisos locales se reproducirán encima. Me parece bien
Philipp Claßen
77
Excepto cuando hay diferencias reales y te deja en un rebase abortado.
incipiente
Esto produce un error: error: Error al fusionar los cambios. El parche falló en los modelos de solicitud y respuesta 0024
IgorGanapolsky
32

Me encontré en esta situación cuando intenté cambiar la base de una rama que estaba rastreando una rama remota, y estaba tratando de cambiar la base en master. En este escenario, si intentas cambiar la base, lo más probable es que encuentres tu rama divergente y puede crear un desastre que no es para git nubees.

Digamos que está en la rama my_remote_tracking_branch, que fue ramificada desde master

$ git status

# En la rama my_remote_tracking_branch

nada que comprometer (directorio de trabajo limpio)

Y ahora estás tratando de rebase del maestro como:

git rebase master

¡DETÉNGASE AHORA y ahorrese algunos problemas! En su lugar, use merge como:

git merge master

Sí, terminarás con compromisos adicionales en tu rama. Pero a menos que esté preparado para ramas "no divergentes", este será un flujo de trabajo mucho más fluido que el rebase. Vea este blog para una explicación mucho más detallada.

Por otro lado, si su sucursal es solo una sucursal local (es decir, aún no se ha enviado a ningún control remoto), definitivamente debe hacer un rebase (y su sucursal no divergerá en este caso).

Ahora, si está leyendo esto porque ya se encuentra en un escenario "divergente" debido a dicho rebase, puede volver al último commit desde el origen (es decir, en un estado no divergente) usando:

git reset --hard origin / my_remote_tracking_branch

paneer_tikka
fuente
55
Una regla general es usarla rebasesi la rama que está cambiando no ha sido publicada (y utilizada por otras personas). De lo contrario, use merge. Si modifica las ramas ya publicadas (y usadas), debe coordinar una conspiración para reescribir el historial en cada desarrollador que haya usado su rama.
Mikko Rantalainen
1
Lamentablemente no leí este mensaje antes de hacer el git rebase master...
Vitaly Isaev
Si hago git rebase master mientras estoy en la rama 'foobar', técnicamente foobar está separado del origen / foobar hasta que hago un git push -f, ¿verdad?
relipse
1
git reset --hard origin/my_remote_tracking_branches lo que realmente funcionó
roothahn
24

En mi caso, esto es lo que hice para causar el mensaje divergente : lo hice git pushpero luego lo hice git commit --amendpara agregar algo al mensaje de confirmación. Luego también hice otra confirmación.

Entonces, en mi caso, eso simplemente significaba que origen / maestro estaba desactualizado. Como sabía que nadie más estaba tocando origen / maestro, la solución fue trivial: git push -f (donde -fsignifica fuerza)

Darren Cook
fuente
8
+1 para git push -fsobrescribir los cambios previamente confirmados y enviados al origen. También estoy seguro de que nadie más tocó el repositorio.
zacharydl
44
Comando muy arriesgado. Escriba una breve información sobre el factor de riesgo del comando.
J4cK
1
@Trickster: ya había descrito el riesgo: "como sabía, nadie más estaba tocando origen / maestro". Creo que, en ese caso, este no es un comando arriesgado.
Darren Cook
1
Si alguien se compromete con el maestro y luego una persona ejecuta el comando git push -f, entonces es un comando de alto riesgo
J4cK
11

En mi caso, impulsé los cambios origin/mastery luego me di cuenta de que no debería haberlo hecho :-( Esto fue complicado por el hecho de que los cambios locales estaban en un subárbol. Así que volví al último buen commit antes del "malo" local cambios (usando SourceTree) y luego recibí el "mensaje de divergencia".

Después de arreglar mi desorden localmente (los detalles no son importantes aquí) quería "retroceder en el tiempo" la origin/masterrama remota para que estuviera sincronizada con el local masternuevamente. La solución en mi caso fue:

git push origin master -f

Tenga en cuenta el -finterruptor (fuerza). Esto eliminó los "cambios incorrectos" que se habían introducido origin/masterpor error y ahora las ramas locales y remotas están sincronizadas.

Tenga en cuenta que esta es una operación potencialmente destructiva, así que realicela solo si está 100% seguro de que "retroceder" el maestro remoto a tiempo está bien.

Laryx Decidua
fuente
Siempre útil pero seguramente no responde la pregunta.
Thibault D.
1
@ThibaultD. incluso si no fuera así, esto es exactamente lo que estaba buscando.
Neil Chowdhury
Me estoy poniendo You are not allowed to force push code to a protected branch on this project.. Estoy tratando de empujar a mi tenedor.
BanAnanas
Tuve que quitar la protección en gitlab repo stackoverflow.com/questions/32246503/…
BanAnanas
5

Sé que hay muchas respuestas aquí, pero creo que git reset --soft HEAD~1merece algo de atención, porque le permite mantener los cambios en el último compromiso local (no presionado) mientras resuelve el estado divergente. Creo que esta es una solución más versátil que pull rebase, ya que el commit local puede revisarse e incluso trasladarse a otra sucursal.

La clave está usando --soft, en lugar de la dura --hard. Si hay más de 1 commit, una variación de HEAD~xdebería funcionar. Así que aquí están todos los pasos que resolvieron mi situación (tenía 1 confirmación local y 8 confirmaciones en el control remoto):

1) git reset --soft HEAD~1 deshacer el compromiso local. Para los siguientes pasos, he usado la interfaz en SourceTree, pero creo que los siguientes comandos también deberían funcionar:

2) git stash para guardar los cambios de 1). Ahora todos los cambios son seguros y ya no hay divergencia.

3) git pull para obtener los cambios remotos.

4) git stash pop o git stash applypara aplicar los últimos cambios escondidos, seguidos de una nueva confirmación, si así lo desea. Este paso es opcional, junto con 2) , cuando se desea eliminar los cambios en la confirmación local. Además, cuando desee comprometerse con otra rama, este paso debe hacerse después de cambiar a la deseada.

Bianca Daniciuc
fuente
En realidad, en estos días, el pull --rebaseescondite automáticamente de todos modos. stackoverflow.com/a/30209750/6309
VonC
4

Para ver las diferencias:

git difftool --dir-diff master origin/master

Esto mostrará los cambios o diferencias entre las dos ramas. En araxis (Mi favorito) lo muestra en un estilo de carpeta diff. Mostrando cada uno de los archivos modificados. Luego puedo hacer clic en un archivo para ver los detalles de los cambios en el archivo.

C Johnson
fuente
1
Uso interesante de git-dir: +1
VonC
3

En mi caso, esto fue causado por no comprometer mi resolución de conflictos.

El problema fue causado al ejecutar el git pullcomando. Los cambios en el origen llevaron a conflictos con mi repositorio local, que resolví. Sin embargo, no los cometí. La solución en este punto es confirmar los cambios ( git commitel archivo resuelto)

Si también ha modificado algunos archivos desde que resolvió el conflicto, el git statuscomando mostrará las modificaciones locales como modificaciones locales no preparadas y combinará la resolución como modificaciones locales preparadas. Esto se puede resolver adecuadamente al confirmar los cambios de la fusión primero git commit, luego agregando y confirmando los cambios no organizados como de costumbre (por ejemplo, por git commit -a).

Mohammad Reza Esmaeilzadeh
fuente
0

Tenía el mismo mensaje cuando intentaba editar el último mensaje de confirmación, de confirmación ya empujada, usando: git commit --amend -m "New message" Cuando presioné los cambios usando git push --force-with-lease repo_name branch_name no hubo problemas.

LoveForDroid
fuente
0

Encontré este problema cuando creé una rama basada en la rama A por

git checkout -b a

y luego configuro el flujo ascendente de la rama a la rama de origen B por

git branch -u origin/B

Entonces recibí el mensaje de error anterior.

Una forma de resolver este problema para mí fue,

  • Eliminar la rama a
  • Crear una nueva rama b por
git checkout -b b origin/B
YanQing
fuente