Gestionar ramas (branch) en Git

Una de las cualidades principales de los sistemas de control de versiones es la gestión del código en ramas permitiendo modificaciones del código en paralelo. En concreto vamos a abordar la gestión de ramas o, como se las llama generalmente, braches en Git. Empecemos!

Dividiendonos en ramas (branch)

Ya hemos visto cómo commitar (Realizar un commit) nuestros cambios al repositorio para que queden registrados en el timeline. Ahora supongamos que tenemos un código medianamente decente que podamos liberar para su uso en producción, lo lógico sería que los futuros commits que vayamos a realizar no los realicemos directamente sobre ese código estable ya podría resultar perjudicial para la estabilidad de nuestro proyecto. Por ello, podemos crear ramas en las que seguir haciendo que nuestro proyecto crezca sin comprometer a las partes estables ya desarrolladas.

Por defecto, Git, llama a la rama principal master, está rama tiende a usarse sólo para albergar código estable, por otro lado se suele establecer una rama extra, llamada develop en la que se va implementando el código hasta que tengamos un software estable.

Empecemos con un pequeño ejemplo para crear una nueva rama en Git:

# Pedimos a Git que cree una nueva rama
git branch develop  
# Listamos las ramas que tiene nuestro repositorio
git branch  
  develop
* master
# Y por último nos cambiamos a la rama que acabamos de crear
git checkout develop  
Switched to branch 'develop'  

Con esto ya tedríamos una nueva rama creada con el comando git branch (develop), y también estaríamos situados en ella usando el comando git checkout, esto quiere decir, que si ahora ejecutásemos alguna modificación seguida de un commit, estos cambios se registrarían sobre dicha rama. Vamos a probarlo, editamos nuestro archivo index.html:

Hola Mundo  
Una rama en el mundo  
Adios Mundo  

Y ahora commitamos los cambios efectuados:

# Preparamos los archivos a commitar
git add index.html  
# Commitamos los cambios sobre nuestra nueva rama
git commit -m "Mi primer commit sobre develop"  

Una vez efectuado el commit y registrados los cambios en la rama, vamos a comprobar que en nuestra rama principal, sobre la que realizamos los primeros commits, no se han reflejado los cambios que acabamos de commitar. Lo primero que debemos hacer es cambiar a nuestra primera rama:

# Cambiamos a master
git checkout master  

Si ahora nos dirigimos a nuestro archivo index.html y hemos hecho todos los pasos anteriores correctamente, veremos lo siguiente:

Hola Mundo  
Adios Mundo  

El archivo está tal y como lo dejamos (en master).

Juntando lo bueno con lo mejor

Ahora que ya podemos llevar nuestro código de desarrollo por un lado (rama develop), y nuestro código estable por otro (rama master), nos queda saber cómo podemos implementar un código entre ramas en Git para que ambas reflejen los cambios, de esta manera podremos coger las modificaciones efectuadas en develop cuando por fin tengamos un código estable e implementarlos en master para tener una nueva versión con las nuevas funcionalidades y/o correcciones disponible. En esta ocasión entra en juego el comando git merge, que se utiliza para unir 2 ramas en Git. A continuación tenéis una demostración:

# No colocamos sobre la rama a la quequeremos añadir código procedente de otra rama
git checkout master  
# La rama que queremos unir a nuestra rama actual
git merge develop  
Updating 6851219..f0486d0  
Fast-forward  
 index.html | 1 +
 1 file changed, 1 insertion(+)

Por lo que si ahora consultamos nuestro archivo index.html desde nuestra rama master tendrá el mismo código que la rama develop.

Para ayudaros a comprender que es lo que hemos hecho hasta ahora podéis comprobar el siguiente gráfico:
ejemplo Git merge

Ramas conflictivas

Puede suceder que cuando queramos unir 2 ramas en Git estas entren en conflicto, esto sucede cuando ambas ramas tienen commits diferentes. Vamos a simular dicho escenario en nuestro repositorio Git, primero editamos el archivo index.html de nuestra rama master:

<p>Comenzamos de cero</p>

Y ahora commitamos nuestros cambios en master y cambiamos a nuestra rama develop:

git add index.html  
git commit -m "Reseteando master"  
git checkout develop  

Ahora que nos encontramos en develop vamos a editar también el archivo index.html para que entre en conflicto con master:

Hola Mundo  
Una rama en el mundo  
Aprendiendo Git  
Adios Mundo  

Y commitamos los cambios efectuados sobre develop:

git add index.html  
git commit -m "Añadido código a index.html"  

Hemos hecho modificaciones muy diferentes sobre ambas ramas para que al intentar unirlas (realizar un merge) entren en conflicto, ya que si se trata de modificaciones leves y fáciles de resolver Git las resolverá por nosotros, por poneros un ejemplo, si en vez de haber sustituido todo el contenido del archivo index.html de la rama master hubiéramos incluido simplemente código en las primera linea, y por otro lado, al archivo index.html de la rama develop le hubiéramos añadido código a la última linea, en el momento de haber realizado un merge no surgirían conflictos, ya que Git incluiría ambas modificaciones, tanto el código añadido al comienzo como el que se incluyó en el final de línea.

Volviendo a nuestra situación actual tendríamos un conflicto si intentásemos unir ambas ramas. Creo que la mejor manera de representar esta situación es mediante un gráfico:
ejemplo Git ramas conflictivas

Cuando se da la situación descrita en la imagen entre 2 ramas que queremos unir surgen diferencias que hay que solventar. Vamos a ver que pasa si le decimos a Git que las una:

git checkout master  
git merge develop  
Auto-merging index.html  
CONFLICT (content): Merge conflict in index.html  
Automatic merge failed; fix conflicts and then commit the result.  

Git no cancela el merge si no que edita el archivo resultante (index.html) para que nosotros solventemos los conflictos. Vamos a echarle un ojo a index.html.

Cuando Git detecta un conflicto durante un merge lo refleja entre estas marcas >>>>>> e indica a quién pertenece cada código, en este caso a HEAD que es la referencia para master y a develop, y separa ambos códigos con =======. Para resolver el conflicto nosotros simplemente tendremos que editar este archivo quitando las marcas que ha dejado Git y poniendo el código que deseemos commitar, una vez hecha la modificación, commitamos la edición para dar fin a nuestro merge.

Editamos nuestro index.html:

Hola Mundo  
Comenzamos de cero  

Y commitamos nuestros cambios para finalizar el merge:

git add index.html  
git commit -m "Merge branch 'develop'"  

Y así quedaría la imagen de nuestro repositorio:
ejemplo Git conflictos resueltos

Hasta aquí la teoría básica sobre el uso de ramas en Git. No dudéis en comentar.