3.1 Conflictos de API
El libro se puede descargar aquí
Antes de explicar los detalles de los que se tienen que cuidar cuando trabajen con
conflictos de API, consideremos lo que los Cambios de API significan al hacer
merges sin conflictos.
3.1.1 Ejemplo 15
Déjanme mostrarles el ancestro común:
Listing 3.1:Ejemplo 15 - ancestro común1#!/usr/bin/python 2 3import sys 4 5colors = {"black": "black mirror", 6 "white": "white noise", 7 "blue": "blue sky"} 8 9def getPhrase(color): 10 color = color.lower() 11 if color not in colors: 12 sys.stderr.write("There is no phrase defined for color %s\n" % color) 13 sys.exit(1) 14 phrase = colors[color] 15 phrase = "%s: %s" % (color, phrase) 16 return phrase 17 18for color in sys.argv[1:]: 19 print(getPhrase(color))
Vamos a imprimir las frases para múltiples colores, no solo uno.
Sobre branchA la API es cambiaba para que el color original se incluya
opcionalmente en la frase:
Listing 3.2:Ejemplo 15 - branchA1#!/usr/bin/python 2 3import sys 4 5colors = {"black": "black mirror", 6 "white": "white noise", 7 "blue": "blue sky"} 8 9def getPhrase(color, includeColor): 10 color = color.lower() 11 if color not in colors: 12 sys.stderr.write("There is no phrase defined for color %s\n" % color) 13 sys.exit(1) 14 phrase = colors[color] 15 if (includeColor): 16 phrase = "%s: %s" % (color, phrase) 17 return phrase 18 19for color in sys.argv[1:]: 20 print(getPhrase(color, True))
En el mundo real, este cambio probablemente no tenga ninún sentido pero está
bien. Tengan paciencia y dejen que siga con el problema para los efectos de
explicación. Miremos la rama branchB:
Listing 3.3:Ejemplo 15 - branchB1#!/usr/bin/python 2 3import sys 4 5colors = {"black": "black mirror", 6 "white": "white noise", 7 "blue": "blue sky"} 8 9def getPhrase(color): 10 color = color.lower() 11 if color not in colors: 12 sys.stderr.write("There is no phrase defined for color %s\n" % color) 13 sys.exit(1) 14 phrase = colors[color] 15 phrase = "%s: %s" % (color, phrase) 16 return phrase 17 18# I love color white 19print(getPhrase("white")) 20for color in sys.argv[1:]: 21 print(getPhrase(color))
Ahora la frase del color blanco siempre se va a imprimir antes que todos los
colores usados como parámetros. Qué sucederá si tratamos de hacer merge?
Qué piensan?
Listing 3.4:Ejemplo 15 - git merge$ git merge example15/branchB --no-edit Auto-merging example.py Merge made by the ’recursive’ strategy. example.py | 2 ++ 1 file changed, 2 insertions(+)
Excelente! Y el resultado final?
Listing 3.5:Ejemplo 15 - resultado final1#!/usr/bin/python 2 3import sys 4 5colors = {"black": "black mirror", 6 "white": "white noise", 7 "blue": "blue sky"} 8 9def getPhrase(color, includeColor): 10 color = color.lower() 11 if color not in colors: 12 sys.stderr.write("There is no phrase defined for color %s\n" % color) 13 sys.exit(1) 14 phrase = colors[color] 15 if (includeColor): 16 phrase = "%s: %s" % (color, phrase) 17 return phrase 18 19# I love color white 20print(getPhrase("white")) 21for color in sys.argv[1:]: 22 print(getPhrase(color, True))
Ustedes pueden ver el problema, cierto? Dejen que les muestra si es que no lo han
visto aún:
Listing 3.6:Ejemplo 15 - tenemos un problema$ ./example.py blue Traceback (most recent call last): File "./example.py", line 20, in <module> print(getPhrase("white")) TypeError: getPhrase() takes exactly 2 arguments (1 given)
Ah, esta línea, agregada en la branchB, no ha sido actualizada a la nueva API
que viene de branchA:
Listing 3.7:Ejemplo 15 - este es el
problema19# I love color white 20print(getPhrase("white"))
Y eso es porque, cuando la API fue cambiada en la rama branchA, las llamadas
preexistentes sobre la rama fueron ajustadas.... pero las llamadas que fueron
agregadas added en la otra rama no van a recibir un ajuste mágicamente al hacer el
merge. Incluso si no hay conflictos, los cambios de API pueden ser engañosos
al hacer merges.
3.1.2 Ejemplo 16
Ahora bien, que pasará cuando hay cambios conflictivos en la API? Es la
misma situación que acabamos de ver, pero en esterides porque se tiene que
considerar las modificaciones de todas las ramas involucradas, no de una
sola.
Miremos otro ejemplo basado en el ejemplo que acabamos de usar. Aquí está el
archivo completo:
Listing 3.8:Ejemplo 16 - todo el archivo1#!/usr/bin/python 2 3import sys 4 5RESET=chr(0x1b) + "[m" 6colors = {"black": {"phrase": "black mirror", "fg": chr(0x1b) + "[0;7m"}, # reverse on color 7 "white": {"phrase": "white noise", "fg": chr(0x1b) + "[1m"}, 8 "blue": {"phrase": "blue sky", "fg": chr(0x1b) + "[1;34m"}} 9 10<<<<<<< HEAD 11def getPhrase(color, showColor): 12||||||| 46e6753 13def getPhrase(color): 14======= 15def getPhrase(color, useColor): 16>>>>>>> example16/branchB 17 """ 18 Get the phrase that corresponds to one color 19 20 Parameters 21 ---------- 22 color: color to get the phrase for. It can be used in any combination of uppercase/lowercase letters 23<<<<<<< HEAD 24 showColor: whether to display the original color name before the phrase or not 25||||||| 46e6753 26======= 27 useColor: we can change the color of the phrase when writing on the output 28>>>>>>> example16/branchB 29 """ 30 color = color.lower() 31 if color not in colors: 32 sys.stderr.write("There is no phrase defined for color %s\n" % color) 33 sys.exit(1) 34<<<<<<< HEAD 35 phrase = colors[color] 36 if showColor: 37 phrase = "%s: %s" % (color, phrase) 38||||||| 46e6753 39 phrase = colors[color] 40 phrase = "%s: %s" % (color, phrase) 41======= 42 phrase = colors[color]["phrase"] 43 phrase = "%s: %s" % (color, phrase) 44 if useColor: 45 phrase = colors[color]["fg"] + phrase + RESET 46>>>>>>> example16/branchB 47 return phrase 48 49for color in sys.argv[1:]: 50 print(getPhrase(color, True))
Cada rama agregó un método a getPhrase()... y el nombre puede prestarse a
confusión, cierto? Son el mismo parámetro (misma intención) con nombres
diferentes? Ese sería otro nivel de dificultad adicional que habría que manejar.
Afortuadamente, tenemos docstrings para la función y en el segundo CB podemos
ver qué hace el parámetro de cada rama. Podemos concluir que no son lo
mismo:
Listing 3.9:Ejemplo 16 - CB#217 """ 18 Get the phrase that corresponds to one color 19 20 Parameters 21 ---------- 22 color: color to get the phrase for. It can be used in any combination of uppercase/lowercase letters 23<<<<<<< HEAD 24 showColor: whether to display the original color name before the phrase or not 25||||||| 46e6753 26======= 27 useColor: we can change the color of the phrase when writing on the output 28>>>>>>> example16/branchB 29 """
El parámetro de UB, showColor, se usa para indicar si el color original va a ser
antepuesto a la frase a ser impresa. El parámetro de LB, useColor, se usa para
cambiar el color del texto en el terminal como un efecto visual. Por lo tanto, como es
habitual, tenemos que mantener la intención de ambas ramas. En el primer CB,
cual parámetro irá primero? Desde mi punto de vista, cualquiera de los dos
estará bien pero podría haber ciertas reglas que seguir.... cada situación es
diferente. Esta será mi resolución para los dos primeros conflictos de un solo
golpe:
Listing 3.10:Ejemplo 16 - solución a los primeros 2 CBs10def getPhrase(color, showColor, colorOnOutput): 11 """ 12 Get the phrase that corresponds to one color 13 14 Parameters 15 ---------- 16 color: color to get the phrase for. It can be used in any combination of uppercase/lowercase letters 17 showColor: whether to display the original color name before the phrase or not 18 colorOnOutput: we can change the color of the phrase when writing on the output 19 """
Cambié el nombre del parámetro que viene de la otra rama para que la
diferencia entre ellos sea más avidente. Eso significa que también tendré
que modificar los lugares donde se use useColor (el nombre original del
prámetro) en la función. Veamos el siguiente CB comenzando en la línea
24 :
Listing 3.11:Ejemplo 16 - CB#324<<<<<<< HEAD 25 phrase = colors[color] 26 if showColor: 27 phrase = "%s: %s" % (color, phrase) 28||||||| 46e6753 29 phrase = colors[color] 30 phrase = "%s: %s" % (color, phrase) 31======= 32 phrase = colors[color]["phrase"] 33 phrase = "%s: %s" % (color, phrase) 34 if useColor: 35 phrase = colors[color]["fg"] + phrase + RESET 36>>>>>>> example16/branchB
dMU
Se agregó un condicional en la línea 26 para controlar si de usará el color original
en la frase.
dML
Se modificó la forma en la que se obtiene la frase asociada al color porque la
estructura de diccionario cambió (línea 32) y se agregó un condicional en la
línea 34 para modificar la frase para que el color del texto en el terminal
cambie.
Resolution
Dado que el LB es más diferente al MB que el UB, trabajaremos desde el
LB.
Listing 3.12:Ejemplo 16 - Paso 1 - LB#331======= 32 phrase = colors[color]["phrase"] 33 phrase = "%s: %s" % (color, phrase) 34 if useColor: 35 phrase = colors[color]["fg"] + phrase + RESET 36>>>>>>> example16/branchB
Luego todo lo que tenemos que hacer es agregar el condicional para incluir el
color original y ajustar indentación:
Listing 3.13:Ejemplo 16 - Paso 2 - LB#331======= 32 phrase = colors[color]["phrase"] 33 if showColor: 34 phrase = "%s: %s" % (color, phrase) 35 if useColor: 36 phrase = colors[color]["fg"] + phrase + RESET 37>>>>>>> example16/branchB
Y listo, cierto? Los pillé!!! Recuerdan que hay que ajustar el nombre del
parámetro Did que viene de la otra rama? Lo cambié de useColor a
colorOnOutput mientras se resolvían los CBs 1 y 2, así que debemos ajustar
eso en este CB:
Listing 3.14:Ejemplo 16 - Paso 3 - LB#331======= 32 phrase = colors[color]["phrase"] 33 if showColor: 34 phrase = "%s: %s" % (color, phrase) 35 if colorOnOutput: 36 phrase = colors[color]["fg"] + phrase + RESET 37>>>>>>> example16/branchB
Y así, el resultado final sería este:
Listing 3.15:Ejemplo 16 - conflictos resueltos1#!/usr/bin/python 2 3import sys 4 5RESET=chr(0x1b) + "[m" 6colors = {"black": {"phrase": "black mirror", "fg": chr(0x1b) + "[0;7m"}, # reverse on color 7 "white": {"phrase": "white noise", "fg": chr(0x1b) + "[1m"}, 8 "blue": {"phrase": "blue sky", "fg": chr(0x1b) + "[1;34m"}} 9 10def getPhrase(color, showColor, colorOnOutput): 11 """ 12 Get the phrase that corresponds to one color 13 14 Parameters 15 ---------- 16 color: color to get the phrase for. It can be used in any combination of uppercase/lowercase letters 17 showColor: whether to display the original color name before the phrase or not 18 colorOnOutput: we can change the color of the phrase when writing on the output 19 """ 20 color = color.lower() 21 if color not in colors: 22 sys.stderr.write("There is no phrase defined for color %s\n" % color) 23 sys.exit(1) 24 phrase = colors[color]["phrase"] 25 if showColor: 26 phrase = "%s: %s" % (color, phrase) 27 if colorOnOutput: 28 phrase = colors[color]["fg"] + phrase + RESET 29 return phrase 30 31for color in sys.argv[1:]: 32 print(getPhrase(color, True))
Y ya estamos listos, cierto? Pillados de nuevo!!! Se fijaron en la llamada de la
línea 32?
Listing 3.16:Ejemplo 16 - llamada que debe ser ajustada31for color in sys.argv[1:]: 32 print(getPhrase(color, True))
Solo tiene 2 parámetros.... pero deberían ser 3, cierto? Por qué pasó esto?
Ah, este es uno de esos casos en los que la inteligencia de git se pone en
nuestra contra. Si git nota que 2 ramas aplican el mismo cambio, simplemente
lo toma de una de las ramas tal cual y no genera conflicto. En este caso
ambas ramas agregaron el segundo paámetro con el valor True en ambas,
así que es el mismo cambio viniendo desde ambas ramas. Pero debemos
ajustar la llamada para que ambos parámetros nuevos sean True y
entonces quedará resuelto. Este es el resultado recontrafinal del conflicto
:
Listing 3.17:Ejemplo 16 - Conflicto resuelto de verdad1#!/usr/bin/python 2 3import sys 4 5RESET=chr(0x1b) + "[m" 6colors = {"black": {"phrase": "black mirror", "fg": chr(0x1b) + "[0;7m"}, # reverse on color 7 "white": {"phrase": "white noise", "fg": chr(0x1b) + "[1m"}, 8 "blue": {"phrase": "blue sky", "fg": chr(0x1b) + "[1;34m"}} 9 10def getPhrase(color, showColor, colorOnOutput): 11 """ 12 Get the phrase that corresponds to one color 13 14 Parameters 15 ---------- 16 color: color to get the phrase for. It can be used in any combination of uppercase/lowercase letters 17 showColor: whether to display the original color name before the phrase or not 18 colorOnOutput: we can change the color of the phrase when writing on the output 19 """ 20 color = color.lower() 21 if color not in colors: 22 sys.stderr.write("There is no phrase defined for color %s\n" % color) 23 sys.exit(1) 24 phrase = colors[color]["phrase"] 25 if showColor: 26 phrase = "%s: %s" % (color, phrase) 27 if colorOnOutput: 28 phrase = colors[color]["fg"] + phrase + RESET 29 return phrase 30 31for color in sys.argv[1:]: 32 print(getPhrase(color, True, True))
Eso fue algo de trabajo, cierto?
3.1.3 Ejercicios
Exercise 7
De el git repo, hagen checkout de la revisión 4284497396 and hagan merge de
cdb5330a9b .
Resuelvan todos los conflictos. La solución está aquí.
Copyright 2020 Edmundo Carmona Antoranz