tortoise - ¿Por qué se invierte el significado de "nuestro" y "de ellos" con git-svn?
migrate svn to git with branches (1)
Yo uso git-svn y noté que cuando tengo que arreglar un conflicto de fusión después de realizar una git svn rebase
, el sentido de la --ours
y --theirs
opciones para, por ejemplo, git checkout
se invierte. Es decir, si hay un conflicto y quiero conservar la versión que proviene del servidor SVN y deshacerme de los cambios que hice localmente, tengo que usar los ours
, cuando esperaría que fueran los theirs
.
¿Porqué es eso?
Ejemplo:
mkdir test
cd test
svnadmin create svnrepo
svn co file://$PWD/svnrepo svnwc
cd svnwc
echo foo > test.txt
svn add test.txt
svn ci -m ''svn commit 1''
cd ..
git svn clone file://$PWD/svnrepo gitwc
cd svnwc
echo bar > test.txt
svn ci -m ''svn commit 2''
cd ..
cd gitwc
echo baz > test.txt
git commit -a -m ''git commit 1''
git svn rebase
git checkout --ours test.txt
cat test.txt
# shows "bar" but I expect "baz"
git checkout --theirs test.txt
cat test.txt
# shows "baz" but I expect "bar"
Eso parece consistente con lo que hace una rebase.
git svn rebase
obtendrá revisiones del padre SVN del HEAD actual y rebases el trabajo actual (no confirmado a SVN) en su contra.git rebase
menciona:
Tenga en cuenta que una fusión de rebase funciona al repetir cada confirmación desde la rama de trabajo en la parte superior de la rama<upstream>
.
Debido a esto, cuando ocurre un conflicto de fusión:- el lado reportado como el nuestro es la serie hasta ahora rebasada, comenzando con
<upstream>
, - y la suya es la rama de trabajo .
En otras palabras, los lados se intercambian .
- el lado reportado como el nuestro es la serie hasta ahora rebasada, comenzando con
git rebase reproduce cada confirmación desde la rama de trabajo en la parte superior de la rama
<upstream>
.
Si reconcilia ambas definiciones:
- los commits provenientes de SVN son aquellos sobre los cuales se reproducen las confirmaciones locales de Git. Son parte de la "serie rebasada hasta ahora", y se mencionan como "nuestro" (en su caso, el archivo
test.txt
con contenido debar
) - la rama de trabajo (que contiene Git commits unknown to SVN, en su caso, el archivo
test.txt
con contenidobaz
) es "their", y cada una de esas confirmaciones locales de Git se están reproduciendo.
En otras palabras, SVN o no:
- la rama "
<upstream>
" (en la parte superior de la cual se reproduce cualquier cosa, y que es parte de las confirmaciones rebasadas hasta ahora) es " nuestra ". - lo que se está reproduciendo (la rama de trabajo) es "de ellos ".
Buena punta mnemotécnica de CommaToast :
lo que sea que HEAD señale es "nuestro"
(y lo primero que hace un git rebase upstream
es realizar la git rebase upstream
la rama upstream
encima de la cual desea volver a establecer la base: HEAD se refiere a la cadena upstream
: la ours
ahora).
La confusión probablemente proviene del rol de la rama de trabajo en una git merge
clásica.
Cuando te estás fusionando:
- la "rama de trabajo" es la que contiene lo que está "hasta ahora fusionada", y se considera como "nuestro",
- mientras que el otro compromiso representa lo que está siendo reproducido, pero no se reproduce, en la parte superior de la rama de trabajo, y considerado como "su".
Como menciona la página de git rebase
, una combinación durante una rebase significa que el lado se intercambia.
Otra forma de decir lo mismo es considerar que:
- lo que tenemos en la rama de salida es '' nuestro '',
- lo que teníamos (y está siendo fusionado o repetido) es '' suyo ''.
En una fusión :
x--x--x--x--x(*) <- current branch B (''*''=HEAD)
/
/
/--y--y--y <- other branch to merge
, no cambiamos la rama actual ''B'', entonces lo que tenemos es aún en lo que estábamos trabajando (y nos fusionamos de otra rama)
x--x--x--x--x---------o(*) MERGE, still on branch B
/ ^ /
/ ours /
/ /
--y--y--y--/
^
their
¡Pero en una rebase , cambiamos de lado porque lo primero que hace una rebase es verificar la rama ascendente! (para reproducir los commits actuales en la parte superior)
x--x--x--x--x(*) <- current branch B
/
/
/--y--y--y <- upstream branch
Un git rebase upstream
cambiará primero HEAD
of B a HEAD
de la rama upstream (de ahí el cambio de ''ours'' y ''theirs'' en comparación con la rama de trabajo "actual" anterior).
x--x--x--x--x <- former "current" branch, new "theirs"
/
/
/--y--y--y(*) <- upstream branch with B reset on it,
new "ours", to replay x''s on it
, y luego la rebase reproducirá ''sus'' confirmaciones en la nueva ''nuestra'' rama B:
x--x..x..x..x <- old "theirs" commits, now "ghosts", available through reflogs
/
/
/--y--y--y--x''--x''--x''(*) <- branch B with HEAD updated ("ours")
^
|
upstream branch
El único paso adicional con git svn rebase
es que git svn rebase
se realiza una "búsqueda" de svn en la rama remota de Git que representa las confirmaciones de SVN.
Tienes inicialmente:
x--x--x--x--x(*) <- current branch B, "ours" for now.
/
/
/--y--y--y <- SVN tracking branch, "theirs for now"
, primero actualiza la rama de seguimiento de SVN con nuevos commits provenientes de SVN
x--x--x--x--x(*) <- current branch B, still "ours", not for long
/
/
/--y--y--y--y''--y'' <- SVN tracking branch updated
, luego cambia la rama actual al lado SVN (que se convierte en "nuestro")
x--x--x--x--x <- for "B", now "their" during the rebase
/
/
/--y--y--y--y''--y''(*) <- SVN tracking branch updated, and branch B:
now "ours" (this is "what we now have")
, antes de reproducir los commits en los que estabas trabajando (pero que ahora son "suyos" durante esa rebase)
x--x..x..x..x <- old "theirs" commits, now "ghosts", available through reflogs
/
/
/--y--y--y--y''--y''--x''--x''--x''(*) <- branch B with HEAD updated ("ours")
^
|
upstream SVN tracking branch