portable - Git Rebase Conflict: ¿Quién es HEAD?
meld portable windows (2)
Definiciones
En esta sección vamos a ver las deficiones que nos piden en respuesta:
¿Quién es HEAD?
HEAD
: el compromiso actual tu repo está activado . La mayoría de las veces, HEAD apunta al último compromiso en su sucursal, pero ese no es el caso. HEAD
realmente solo significa "a qué apunta mi repo actualmente".
En el caso de que se HEAD
referencia a la HEAD
confirmación no es la punta de ninguna rama, esto se denomina " detached head
".
¿Es la CABEZA de la rama del desarrollo?
En el momento en que se realiza una fusión o rebase, el HEAD
pasa inmediatamente a la confirmación creada o refactorizada y, por lo tanto, apunta a la rama de desarrollo .
En git bash
podemos ver la situación de HEAD
, listando commit:
# Normal commit list
git log
# List of commit in a single line
git log --oneline
# All commits graphically-linear (Recommended as alias)
git log --all --graph --decorate --oneline
Práctica
En esta sección veremos el _how_ trabaja algunas acciones realizadas por el usuario.
Cuando el usuario procede a:
# Command to change from the branch to the current one to experimentalbranch
git checkout experimentalbranch
# Command that traverses the typical workflow to synchronize its local repository with the main branch of the central repository (remoterepo)
git fetch remoterepo
# git fetch origin
# git fetch origin branch:branch
# With the command git rebase, you can take all the changes confirmed in one branch (remoterepo), and reapply them over another developmentbranch
git rebase remoterepo/developmentbranch
En este momento, llego a los conflictos. Sin embargo, no estoy familiarizado con ninguno de estos cambios (estoy reajustando semanas en cambios, porque no fusionaron mis cambios inmediatamente). Además, es mi primera vez haciendo rebase. Estoy más acostumbrado a fusionarme.
La unión de ramas se realiza de dos maneras:
git merge
git rebase.
Nota :
Para los ejemplos usaremos el siguiente árbol :
* a122f6d (HEAD -> remoterepo) Commit END
* 9667bfb Commit MASTER
| * b9bcaf0 (origin/experimentalbranch, experimentalbranch) Commit 3
| * 110b2fb Commit 2
| * e597c60 Commit 1
|/
* 0e834f4 (origin/remoterepo) First commit
git merge
La forma más conocida es git merge
, que realiza una fusión de tres bandas entre las dos últimas instantáneas de cada rama y el ancestro común de ambas, creando una nueva confirmación con cambios mixtos.
Por ejemplo :
git checkout remoterepo
git merge experimentalbranch
Nos produciría:
* 003e576 (HEAD -> remoterepo) Merge branch ''experimentalbranch'' in remoterepo
|/
| * b9bcaf0 (origin/experimentalbranch, experimentalbranch) Commit 3
| * 110b2fb Commit 2
| * e597c60 Commit 1
* | a122f6d Commit END
* | 9667bfb Commit MASTER
|/
* 0e834f4 (origin/remoterepo) First commit
git rebase
git rebase
básicamente lo que hace es recopilar uno por uno los cambios confirmados en una rama y volver a aplicarlos en otra .
El uso de rebase puede ayudarnos a evitar conflictos siempre que se aplique a confirmaciones locales que no se hayan cargado en ningún repositorio remoto . Si no es cuidadoso con este último y un compañero usa los cambios afectados, asegúrese de que tendrá problemas, ya que estos tipos de conflictos suelen ser difíciles de reparar .
Por ejemplo :
git checkout remoterepo
git rebase experimentalbranch
* f8a74be (HEAD -> remoterepo) Commit END
* 4293e9d Commit MASTER
* b9bcaf0 (origin/experimentalbranch, experimentalbranch) Commit 3
* 110b2fb Commit 2
* e597c60 Commit 1
* 0e834f4 (origin/remoterepo) First commit
¿Qué es el origen?
origin
: el nombre predeterminado que git le da a su repositorio remoto principal . Su caja tiene su propio repo, y lo más probable es que se empuja hacia algún repo remoto al que usted y todos sus compañeros de trabajo presionan. Ese repo remoto casi siempre se llama origen, pero no tiene que serlo.
Tengo este proyecto donde el repositorio remoto tiene la rama de desarrollo principal y tengo una bifurcación que contiene la rama experimental. Debo rebase
cambios de la rama de desarrollo a mi rama experimental antes de empujar a mi bifurcación. Así que va como:
git checkout experimentalbranch
git fetch remoterepo
git rebase remoterepo/developmentbranch
En este momento, llego a los conflictos. Sin embargo, no estoy familiarizado con ninguno de estos cambios (estoy reajustando semanas en cambios, porque no fusionaron mis cambios inmediatamente). Además, es mi primera vez haciendo rebase
. Estoy más acostumbrado a merge
.
En meld, por lo general es como <<LOCAL||REMOTE>>
para merge
, lo que suena muy intuitivo. Pero en rebase
, es <<HEAD||COMMIT MESSAGE>>
. ¿Quién es HEAD
? ¿Es la HEAD
de la rama del desarrollo? ¿Es el último código en la rama de desarrollo o en otro lugar?
TL; DR (agregado en mayo de 2018)
Todo esto es fundamentalmente al menos un poco confuso porque Git permite que su funcionamiento interno se muestre a través de usted.
Tenga en cuenta que los casos que nos preocupan aquí ocurren cuando ejecuta:
git checkout somebranch; git rebase origin/their-branch
o similar. La reorganización se detuvo temporalmente para obligarlo a resolver un conflicto de fusión, después de lo cual se supone que debe git add
el conflicto resuelto y ejecutar git rebase --continue
. (Si usa alguna herramienta de combinación con git mergetool
, o una interfaz GUI elegante, esa interfaz puede hacer todo o parte de esto por usted de otra manera, pero debajo, es git add
los archivos resueltos y ejecutar git rebase --continue
. )
Al comienzo, el compromiso HEAD
es su rama, de modo que si utiliza git checkout --ours
o git checkout --theirs
, --ours
significa suya - la confirmación final de origin/their-branch
mientras que --theirs
significa Tuyo , el primer commit que estás rebasando. Este es el tipo normal de confusión de Git (vea ¿Cuál es el significado preciso de "nuestro" y "de ellos" en git? ) Y no es lo que llevó a la pregunta original.
Más tarde, sin embargo, el HEAD
commit es en realidad una especie de mezcla . Es el resultado de copiar algunos de sus compromisos sobre su último compromiso . Ahora está teniendo un conflicto entre su propia serie de compromisos parcialmente construida y sus propios compromisos originales . La fuente de este conflicto suele ser algo que "ellos" hicieron (algo que cambió en el camino en el origin/their-branch
). Todavía tienes que resolver este conflicto. Cuando lo haga, puede ver que el mismo conflicto se repite en las confirmaciones posteriores.
Nuevamente, HEAD
o local
o --ours
es un compromiso que se ha creado mediante la combinación de sus cambios y sus cambios , y el otro compromiso ( remote
o >>>>>>>
o - --theirs
) es su propio compromiso, que se vuelve a generar. está intentando copiar encima de HEAD
.
Más
Al fusionar (incluido el reajuste de bases, que es un caso especial de "fusión" repetida internamente), hay dos "cabezas" (dos puntas de rama específicas) involucradas. Llamemos a estos your-branch
y origin/their-branch
:
G - H -------- <-- HEAD=your-branch
/ /
... - E - F M <-- desired merge commit [requires manual merge]
/ /
I - J - K - L <-- origin/their-branch
Este punto es comúnmente confuso (y no es sorprendente), aunque cuando se lo etiqueta así, es lo suficientemente claro.
Sin embargo, para empeorar las cosas, git usa --ours
y --theirs
para referirse a los dos compromisos de la cabeza durante una fusión, siendo "nuestro" el que estaba en (cometer H
) cuando ejecutó git merge
, y "suyo" siendo, bueno, de ellos (cometer L
). Pero cuando estás haciendo un rebase, las dos cabezas se invierten, de modo que "nuestro" es el jefe al que estás rebasando, es decir, su código actualizado, y "suya" es el compromiso al que estás rebasando actualmente, Es decir, tu propio código.
Esto se debe a que rebase utiliza en realidad una serie de operaciones de selección selectiva. Comienzas con casi la misma imagen:
G - H <-- HEAD=your-branch
/
... - E - F
/
I - J - K - L <-- origin/their-branch
Lo que git debe hacer aquí es copiar el efecto de los compromisos G
y H
, es decir, git cherry-pick
commit G
, y luego hacerlo de nuevo con commit H
Pero para hacer eso, git tiene que cambiar para cometer L
primero, internamente (usando el modo "HEAD desasociado"):
G - H <-- your-branch
/
... - E - F
/
I - J - K - L <-- HEAD, origin/their-branch
Ahora puede iniciar la operación de rebase comparando los árboles para los compromisos F
y G
(para ver lo que cambió), luego comparando F
vs L
(para ver si parte de su trabajo ya está en L
) y tomando cualquier cambio que no esté en L
y añadirlo. Esta es una operación de "fusión", internamente.
G - H <-- your-branch
/
... - E - F G'' <-- HEAD
/ /
I - J - K - L <-- origin/their-branch
Si la fusión no va bien, HEAD
aún se deja en el compromiso L
(porque el compromiso G''
todavía no existe). Por lo tanto, sí, HEAD
es el jefe de su rama de desarrollo; al menos, es ahora.
Sin embargo, una vez que existe la copia de G
, HEAD
mueve a G''
y git intenta copiar los cambios de H
, de la misma manera (diff G
vs H
, luego diff F
vs G''
, y fusiona los resultados):
G - H <-- your-branch
/
... - E - F G'' - H'' <-- HEAD
/ /
I - J - K - L <-- origin/their-branch
Nuevamente, si la combinación falla y necesita ayuda, te quedas con HEAD
apuntando a G''
lugar de H''
ya que H''
aún no existe.
Una vez que se fusionan, todos los éxitos y las confirmaciones G''
y H''
existen, git elimina la etiqueta que your-branch
comete de la confirmación H
, y hace que apunte a la confirmación H''
lugar:
G - H
/
... - E - F G'' - H'' <-- HEAD=your-branch
/ /
I - J - K - L <-- origin/their-branch
y ahora estás rediseñado y HEAD
es una vez más lo que esperas. Pero durante la rebase, HEAD
es su punta de rama (cometer L
) o uno de los nuevos compromisos copiados y anexados más allá de su punta de rama; y --ours
significa que la rama se está cultivando al final de L
mientras que --theirs
significa que el commit se está copiando desde ( G
o H
arriba).
(Esto es básicamente git exponiendo el mecanismo en bruto de cómo hace lo que hace, lo que sucede bastante en git).