Node:Fusionar repetidamente con la rama principal, Next:, Previous:Algunos principios para trabajar con derivaciones, Up:Salir del limbo (Cómo trabajar con derivaciones y sobrevivir)



Fusionar repetidamente con la rama principal

Supongamos que qsmith necesita hacer desarrollo en una derivación para no desestabilizar la rama principal que comparte con jrandom. El primer paso es crear una rama nueva. Observe como primero qsmith crea una etiqueta normal (no-rama) en ese punto de la rama principal y después crea la derivación:

paste$ pwd
/home/qsmith/myproj
paste$ cvs tag Root-of-Exotic_Greetings
cvs tag: Tagging .
T README.txt
T foo.gif
T hello.c
cvs tag: Tagging a-subdir
T a-subdir/whatever.c
cvs tag: Tagging a-subdir/subsubdir
T a-subdir/subsubdir/fish.c
cvs tag: Tagging b-subdir
T b-subdir/random.c
paste$ cvs tag -b Exotic_Greetings-branch
cvs tag: Tagging .
T README.txt
T foo.gif
T hello.c
cvs tag: Tagging a-subdir
T a-subdir/whatever.c
cvs tag: Tagging a-subdir/subsubdir
T a-subdir/subsubdir/fish.c
cvs tag: Tagging b-subdir
T b-subdir/random.c
paste$

Etiquetar primero la rama principal podría servir para obtener algún día la rama principal en el momento de que la derivación fue creada. Si tuviese que hacer eso debería haber un modo de referirse a esa instantánea de la rama principal sin referirse a la derivación. No puede usar la etiqueta de la derivación ya que lo que obtendría es esa derivación no las revisiones que forman la raiz del tronco. El único modo de hacer esto sería hacer una etiqueta de las revisiones de las que sale la derivación. (Alguna gente que esta regla tan fielmente que consideré listarla como "principio número 4 de ramificación: Crear siempre una etiqueta no-derivación en la posición de la derivación." Sin embargo en algunos sitios no se usa y parece que lo hacen bien por lo que es una cuestión de gusto.) De ahora en adelante me referiré a esta etiqueta no-derivación como etiqueta del punto de derivación.

Observe que me he adherido a una convención de nombres: La etiqueta del punto de derivación empieza con Root-of- (Raiz-de-), y después el nombre, que usará subrayado en vez de guión para separar las palabras. Cuando la derivación es creada su etiqueta acabará con el sufijo -branch (rama) que le indicará con sólo mirar el nombre que es una derivación. (La etiqueta del punto de derivación Root-of-Exotic_Greetings no incluye el sufijo -branch porque no es una derivación.) No tiene que usar esta convención en particular pero desde luego es aconsejable usar alguna.

Por supuesto, he sido extra pedante. En pequeños proyectos donde cada uno sabe quién está haciendo qué y se pueden arreglar fácilmente las confusiones estas convenciones no tienen que ser usadas. El que use la etiqueta del punto de derivación o una estricta convención de nombres para sus etiquetas dependerá de la complejidad del proyecto y su esquema de derivaciones. (No olvide que siempre puede volver atrás más tarde para actualizar viejas etiquetas y usar una nueva convención; obtenga la versión de la vieja etiqueta, añada la nueva etiqueta y borre después la antigua.)

Ahora qsmith puede empezar a trabajar con la derivación:

paste$ cvs update -r Exotic_Greetings-branch
cvs update: Updating .
cvs update: Updating a-subdir
cvs update: Updating a-subdir/subsubdir
cvs update: Updating b-subdir
paste$

Hace algunos cambios a un par de ficheros y los entrega en la derivación:

paste$ emacs README.txt a-subdir/whatever.c b-subdir/random.c
...
paste$ cvs ci -m "print greeting backwards, etc"
cvs commit: Examining .
cvs commit: Examining a-subdir
cvs commit: Examining a-subdir/subsubdir
cvs commit: Examining b-subdir
Checking in README.txt;
/usr/local/newrepos/myproj/README.txt,v  <--  README.txt
new revision: 1.14.2.1; previous revision: 1.14
done
Checking in a-subdir/whatever.c;
/usr/local/newrepos/myproj/a-subdir/whatever.c,v  <--  whatever.c
new revision: 1.3.2.1; previous revision: 1.3
done
Checking in b-subdir/random.c;
/usr/local/newrepos/myproj/b-subdir/random.c,v  <--  random.c
new revision: 1.1.1.1.2.1; previous revision: 1.1.1.1
done
paste$

Mientras tanto jrandom sigue trabajando en el tronco. Ella modifica dos o tres ficheros que qsmith tocó. Para ponerlo más dificil haremos sus cambios creen conflictos con el trabajo de qsmith:

floss$ emacs README.txt whatever.c
 ...
floss$ cvs ci -m "some very stable changes indeed"
cvs commit: Examining .
cvs commit: Examining a-subdir
cvs commit: Examining a-subdir/subsubdir
cvs commit: Examining b-subdir
Checking in README.txt;
/usr/local/newrepos/myproj/README.txt,v  <--  README.txt
new revision: 1.15; previous revision: 1.14
done
Checking in a-subdir/whatever.c;
/usr/local/newrepos/myproj/a-subdir/whatever.c,v  <--  whatever.c
new revision: 1.4; previous revision: 1.3
done
floss$

El conflicto no es aparente todavía ya que ninguno de los desarrolladores ha intentado hacer la fusión de la derivación con el tronco. Ahora jrandom hace la fusión:

floss$ cvs update -j Exotic_Greetings-branch
cvs update: Updating .
RCS file: /usr/local/newrepos/myproj/README.txt,v
retrieving revision 1.14
retrieving revision 1.14.2.1
Merging differences between 1.14 and 1.14.2.1 into README.txt
rcsmerge: warning: conflicts during merge
cvs update: Updating a-subdir
RCS file: /usr/local/newrepos/myproj/a-subdir/whatever.c,v
retrieving revision 1.3
retrieving revision 1.3.2.1
Merging differences between 1.3 and 1.3.2.1 into whatever.c
rcsmerge: warning: conflicts during merge
cvs update: Updating a-subdir/subsubdir
cvs update: Updating b-subdir
RCS file: /usr/local/newrepos/myproj/b-subdir/random.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.2.1
Merging differences between 1.1.1.1 and 1.1.1.1.2.1 into random.c
floss$ cvs update
cvs update: Updating .
C README.txt
cvs update: Updating a-subdir
C a-subdir/whatever.c
cvs update: Updating a-subdir/subsubdir
cvs update: Updating b-subdir
M b-subdir/random.c
floss$

Dos de los ficheros tienen conflictos. No importa, con su saber hacer jarandom resuelve los conflictos, entrega y etiqueta el tronco indicando una fusión con éxito.

floss$ emacs README.txt a-subdir/whatever.c
 ...
floss$ cvs ci -m "merged from Exotic_Greetings-branch (conflicts resolved)"
cvs commit: Examining .
cvs commit: Examining a-subdir
cvs commit: Examining a-subdir/subsubdir
cvs commit: Examining b-subdir
Checking in README.txt;
/usr/local/newrepos/myproj/README.txt,v  <--  README.txt
new revision: 1.16; previous revision: 1.15
done
Checking in a-subdir/whatever.c;
/usr/local/newrepos/myproj/a-subdir/whatever.c,v  <--  whatever.c
new revision: 1.5; previous revision: 1.4
done
Checking in b-subdir/random.c;
/usr/local/newrepos/myproj/b-subdir/random.c,v  <--  random.c
new revision: 1.2; previous revision: 1.1
done
floss$ cvs tag merged-Exotic_Greetings
cvs tag: Tagging .
T README.txt
T foo.gif
T hello.c
cvs tag: Tagging a-subdir
T a-subdir/whatever.c
cvs tag: Tagging a-subdir/subsubdir
T a-subdir/subsubdir/fish.c
cvs tag: Tagging b-subdir
T b-subdir/random.c
floss$

Mientras, qsmith no necesita esperar que termine la fusión para continuar el desarrollo si hace una etiqueta del conjunto de cambios que jrandom fusionó (más tarde, jrandom necesitará saber el nombre de esta etiqueta; en general las derivaciones dependen de una frecuente y completa comunicación entre los desarrolladores):

paste$ cvs tag Exotic_Greetings-1
cvs tag: Tagging .
T README.txt
T foo.gif
T hello.c
cvs tag: Tagging a-subdir
T a-subdir/whatever.c
cvs tag: Tagging a-subdir/subsubdir
T a-subdir/subsubdir/fish.c
cvs tag: Tagging b-subdir
T b-subdir/random.c
paste$ emacs a-subdir/whatever.c
 ...
paste$ cvs ci -m "print a randomly capitalized greeting"
cvs commit: Examining .
cvs commit: Examining a-subdir
cvs commit: Examining a-subdir/subsubdir
cvs commit: Examining b-subdir
Checking in a-subdir/whatever.c;
/usr/local/newrepos/myproj/a-subdir/whatever.c,v  <--  whatever.c
new revision: 1.3.2.2; previous revision: 1.3.2.1
done
paste$

Y por supuesto cuando qsmith haya hecho sus cambios tendrá que etiquetar:

paste$ cvs -q tag Exotic_Greetings-2
T README.txt
T foo.gif
T hello.c
T a-subdir/whatever.c
T a-subdir/subsubdir/fish.c
T b-subdir/random.c
paste$

Mientras todo esto sucede jrandom hace un cambio en un fichero distinto, uno que qsmith no ha tocado en sus ediciones:

floss$ emacs README.txt
 ...
floss$ cvs ci -m "Mention new Exotic Greeting features" README.txt
Checking in README.txt;
/usr/local/newrepos/myproj/README.txt,v  <--  README.txt
new revision: 1.17; previous revision: 1.16
done
floss$

En este momento qsmith ha entregado un nuevo cambio en su derivación y jrandom ha entregado otro cambio no conflictivo en un fichero distinto del tronco. Observe que sucede cuando jrandom trata de fusionar desde la derivación de nuevo:

floss$ cvs -q update -j Exotic_Greetings-branch
RCS file: /usr/local/newrepos/myproj/README.txt,v
retrieving revision 1.14
retrieving revision 1.14.2.1
Merging differences between 1.14 and 1.14.2.1 into README.txt
rcsmerge: warning: conflicts during merge
RCS file: /usr/local/newrepos/myproj/a-subdir/whatever.c,v
retrieving revision 1.3
retrieving revision 1.3.2.2
Merging differences between 1.3 and 1.3.2.2 into whatever.c
rcsmerge: warning: conflicts during merge
RCS file: /usr/local/newrepos/myproj/b-subdir/random.c,v
retrieving revision 1.1
retrieving revision 1.1.1.1.2.1
Merging differences between 1.1 and 1.1.1.1.2.1 into random.c
floss$ cvs -q update
C README.txt
C a-subdir/whatever.c
floss$

¡Hay conflictos! ¿Esperaba esto?

El problema radica en el significado de fusionar. En Una introduccion a CVS expliqué que cuando usted ejecuta

floss$ cvs update -j BRANCH

en una copia de trabajo, CVS fusiona en la copia de trabajo las diferencias entre la raiz BRANCH y su estado actual. El problema con este comportamiento es que, en esta situación, la mayoría de esos cambios ya habían sido incorporados al tronco la primera vez que jrandom hizo una fusión. Cuando CVS intentó fusionarlos de nuevo (sobre ellos mismos que es como estaban) se produce naturalmente un conflicto.

Lo que jrandom realmente quería hacer era fusionar en su copia de trabajo los cambios entre la más reciente fusión del tronco con su estado actual. Usted puede hacer esto usando dos -j indicadores para actualizar, como debería recordar en Una introduccion a CVS, siempre que sepa que revisión corresponde con cada indicador. Afortunadamente qsmith hizó una etiqueta exactamente en el último punto de fusión (¡hurra por planificar con antelación!), por lo que esto no será problema. Primero veamos como jrandom puede devolver su copia de trabajo un estado limpio, desde el que puede rehacer la fusión:

floss$ rm README.txt a-subdir/whatever.c
floss$ cvs -q update
cvs update: warning: README.txt was lost
U README.txt
cvs update: warning: a-subdir/whatever.c was lost
U a-subdir/whatever.c
floss$

Ahora ella puede hacer la fusión, usando la etiqueta colocada convenientemente por qsmith.

floss$ cvs -q update -j Exotic_Greetings-1 -j Exotic_Greetings-branch
RCS file: /usr/local/newrepos/myproj/a-subdir/whatever.c,v
retrieving revision 1.3.2.1
retrieving revision 1.3.2.2
Merging differences between 1.3.2.1 and 1.3.2.2 into whatever.c
floss$ cvs -q update
M a-subdir/whatever.c
floss$

Mucho mejor. Los cambios de qsmith han sido incorporados a whatever.c; jrandom puede hacer una entrega y etiquetado:

floss$ cvs -q ci -m "merged again from Exotic_Greetings (1)"
Checking in a-subdir/whatever.c;
/usr/local/newrepos/myproj/a-subdir/whatever.c,v  <--  whatever.c
new revision: 1.6; previous revision: 1.5
done
floss$ cvs -q tag merged-Exotic_Greetings-1
T README.txt
T foo.gif
T hello.c
T a-subdir/whatever.c
T a-subdir/subsubdir/fish.c
T b-subdir/random.c
floss$

Incluso si qsmith hubiese olvidado etiquetar en el punto de fusión, las esperanzas no estaría perdidas. Si jrandom supiese aproximadamente cuando hizo qsmith su primera entrega ella podría tratar de filtrar por la fecha:

floss$ cvs update -j Exotic_Greetings-branch:3pm -j Exotic_Greetings_branch

Aunque útil como último recurso, filtrar por fecha no es tan bueno porque selecciona los cambios basandose en los recuerdos de la gnete en vez de en designaciones que dependan del desarrollador. Si el primer conjunto de cambios fusionados de qsmith hubiera ocurrido en varias entregas en vez de sólo una jrandom pudiera equivocadamente elegir una fecha u hora que tomara algunos de los cambios, pero no todos.

No es necesario que cada punto etiquetado en los cambios de qsmith sea enviado al repositorio un una simple entrega. Ocurrió así casualmente en el ejemplo. En la vida real, qsmith pudo haber hecho varias entregas entre cada etiquetado. Él puede trabajar de forma aislada en su derivación tanto como quiera. La razón de las etiquetas es registar sucesivos puntos en la derivación donde considere que los cambios deban ser fusionados con la rama principal. Siempre que jrandom fusione usando dos indicadores -j y sea cuidadoso al usar las etiquetas de ramificación de qsmith en el orden apropiado y una sóla vez por cada un la rama principal padecer el problema de la doble fusión.

Podrían ocurrir conflictos, pero éstos serían de la inevitable clase que requiere resolución humana; situaciones en las que tanto el tronco como la derivación realizan cambios en la misma área de código.