You are currently browsing the category archive for the ‘sw.SCM’ category.

Desde hace dos semanas atrás he querido escribir sobre como resuelvo conflictos; pero, por alguna razón cósmica, se me olvidaba. La idea es simple: divide y vencerás. Veremos como resolver, commit por commit, los conflictos provenientes de un merge en lugar de tratar de resolver el lote completo. No obstante, me reservo una nota importante para el final del artículo. 🙂

(Estimado lectores, mi consejo es que recreen estos pasos.)

1.   Creando el proyecto base

Trabajaremos sobre un repositorio hipotético con un único fichero index.js:

export function sayHello() {
  console.log('Hello world');
}
export function darkOperation(a, b) {
  return a + b;
}

Son dos funciones simples. Una nos «Hello world» y la otra es una función del más creativo de los programadores.

Nuestro log es hasta el momento:

* 18c6bb7 (HEAD -> master) Initial commit

(Si siguen paso a paso el ejemplo, no olviden que el hash no será el mismo en su caso.)

2.   Que pase el desgraciado

Nuestro programador desgraciado decide crear la rama desgraciado. En ella deja el siguiente cambio:

export function sayHello() {
  console.log('Hola world');
  return true;
}
export function times(x, y) {
  return x * y;
}

Además, nos deja de recuerdo su genialidad para crear commits.

* 584061d (HEAD -> desgraciado) Changes
* 18c6bb7 (master) Initial commit

3.   Nosotros

Suponiendo que nos dicen que tenemos que traducir la palabra «mundo» y que cambiemos de nombre a una de las variables de la función desconocida en nuestra rama nosotros, obtendríamos

export function sayHello() {
  console.log('Hello mundo');
  return true;
}
export function darkOperation(a, w) {
  return a + w;
}

Con el log

* 4039ab8 (HEAD -> nosotros) Change argument name
* 88c3c47 i18n mundo
* 18c6bb7 (master) Initial commit

4.   La desgracia

Si la rama del desgraciado es integrada antes en master, obtendremos el siguiente estado

*   9e950b4 (HEAD -> master) Merge branch 'desgraciado'
|\
| * 584061d (desgraciado) Changes
|/
* 18c6bb7 Initial commit

Luego, si desde nuestra rama nosotros hacemos un merge, nos quedamos con el código

export function sayHello() {
<<<< HEAD
  console.log('Hello mundo');
||||||| merged common ancestors
  console.log('Hello world');
=======
  console.log('Hola world');
  return true;
>>> master
}
<<<< HEAD
export function darkOperation(a, w) {
  return a + w;
||||||| merged common ancestors
export function darkOperation(a, b) {
  return a + b;
=======
export function times(x, y) {
  return x * y;
>>>> master
}

Y acá empieza nuestra pesadilla…

5.   El capo

Pero, si en lugar de un desgraciado hubiese sido un capo quien hiciera esos mismos cambios de manera inteligente, tendríamos un rama capo que luciría como sigue

* b1c09f3 (HEAD -> capo) Fix unkwon function
* b319b3a Add chiche
* 36626c0 Change argument names
* 4dff9c0 i18n Hola
* 18c6bb7 (master) Initial commit

El código resultante es el mismo que lo que hizo el desgraciado.

export function sayHello() {
  console.log('Hola world');
  return true;
}
export function times(x, y) {
  return x * y;
}

La diferencia es que tenemos commits atómicos. Y esto será vital para poder resolver los conflictos.

No olvidemos que esto es muy positivo como equipo tanto como desarrolladores independientes. Nos dice que respetamos a las personas en nuestra profesión.

6.   Nosotros nuevamente

Cuando mezclemos los cambios del capo en master, obtendremos

*   6e5241f (HEAD -> master) Merge branch 'capo'
|\
| * b1c09f3 (capo) Fix unkwon function
| * b319b3a Add chiche
| * 36626c0 Change argument names
| * 4dff9c0 i18n Hola
|/
* 18c6bb7 Initial commit

Ahora, si hacemos un merge en nuestra rama nosotros, tendremos los mismos conflictos. Pero…

7.   Más sencillo, imposible

Lo que podemos hacer para evitar resolver los conflictos como un único lote es empezar creando una rama solved a partir de master, donde dejaremos solucionados los conflictos. En la rama solved veríamos algo como

*   6e5241f (HEAD -> solved, master) Merge branch 'capo'
|\
| * b1c09f3 (capo) Fix unkwon function
| * b319b3a Add chiche
| * 36626c0 Change argument names
| * 4dff9c0 i18n Hola
|/
* 18c6bb7 Initial commit

En ella, obtendremos los conflictos, pero no mendiante un merge, sino mediante un rebase. Es decir, en la rama solved en lugar de hacer un git merge nosotros, haremos un git rebase nosotros.

Esto nos mostrará el mensaje de conflictos, pero en el código veremos sólo el primer conflicto

export function sayHello() {
<<<< HEAD
  console.log('Hello mundo');
||||||| merged common ancestors
  console.log('Hello world');
=======
  console.log('Hola world');
>>>> i18n Hola
}
export function darkOperation(a, w) {
  return a + w;
}

Escribimos el código para luego corregirlo

export function sayHello() {
  console.log('Hola mundo');
}
export function darkOperation(a, w) {
  return a + w;
}

Lo añadimos al stage para decirle a git que lo hemos corregido con git add index.js. Luego, ejecutamos git rebase –-continue para aplicar el siguiente commit obteniendo

export function sayHello() {
  console.log('Hola mundo');
}
<<<< HEAD
export function darkOperation(a, w) {
  return a + w;
||||||| merged common ancestors
export function darkOperation(a, b) {
  return a + b;
=======
export function darkOperation(x, y) {
  return x + y;
>>>> Change argument names
}

Repetimos este proceso hasta acabar con un mensaje como «No rebase in progress?». Esto nos indicará que hemos terminado.

8.   El paso final

Finalmente, sólo nos queda ir a nuestra rama nosotros y desde ella hacer un merge desde master. Obtendremos los mismo conflictos; sin embargo, tenemos el conflicto solucionado en la rama solved. Traemos esa solución desde esa rama con git checkout solved -– index.js. Luego damos por resuelto el conflicto y obtenemos

*   ee2c35a (HEAD -> nosotros) Merge branch 'master' into 'nosotros'
|\
| *   6e5241f (master) Merge branch 'capo'
| |\
| | * b1c09f3 (capo) Fix unkwon function
| | * b319b3a Add chiche
| | * 36626c0 Change argument names
| | * 4dff9c0 i18n Hola
| |/
* | 4039ab8 Change argument name
* | 88c3c47 i18n mundo
|/
* 18c6bb7 Initial commit

Desde este momento, podemos eliminar la rama solved.

9.   Comentarios finales

Lo que no debemos olvidar es que la clave para todo esto es que cada miembro del equipo piense en el equipo. Los commit atómicos nos ayudan en este caso a poder reproducir cada conflicto y resolverlo en su versión más simple, es decir, en el commit en el que se originó.

Hay otras herramientas como habilitar la opción rerere de git para poder recordar la manera en que resolvemos estos conflictos y así reducir incluso más este proceso. Nuevamente, es parte del estilo de cada desarrollador. Saludos a todos y nos vemos. 😉

Anuncios

Categorías

Anuncios