En la primera parte aprendimos a llevar el control de versiones de un proyecto en local: las áreas de git, la configuración inicial y el flujo de commits. En este segundo capítulo nos sumergimos en las **ramas**: cómo crearlas, moverte entre ellas, fusionarlas y resolver conflictos. 🎸
📚 **Esta entrada es parte de la serie _Guía de Git_**, dividida en tres capítulos que se leen en orden:
> * Parte 1: Control de versiones en local
* 👉 **Parte 2: Ramas**
* Parte 3: Repositorios remotos, stash y tags
Ramas
El uso de ramas es muy útil, ya que podemos empezar a desarrollar una nueva característica sin tener que modificar el código original
Crear una rama (git branch <branch name>)
**Nota**: Antes de nada hay que decir que para crear una rama en un repositorio debe haber al menos un commit, si se intenta crear una rama antes de hacer el primer commit recibiremos un error
Para crear una rama utilizamos el comando git branch <nombre de la rama>
InputPython!cd notebook_git && git branch new_branchCopied
Listar ramas (git branch)
Hemos creado nuestra primera rama, podemos comprobar todas las ramas que tenemos creadas escribiendo solo git branch
InputPython!cd notebook_git && git branchCopied
* masternew_branch
Además de listar nos dice con un asterisco * en qué rama estamos, en este caso en la rama master
Renombrar ramas, adiós a la rama master (git branch -m <old name> <new name>)
Históricamente en git se ha llamado master a la rama principal, pero esto tiene unas connotaciones históricas malas por el concepto de master-slave (maestro-esclavo), debido al sufrimiento que recibieron muchas personas, por lo que ahora se suele catalogar la rama principal como main, así que para cambiarle el nombre usamos git branch -m master main
InputPython!cd notebook_git && git branch -m master mainCopied
Listamos las ramas
InputPython!cd notebook_git && git branchCopied
* mainnew_branch
Como vemos, hemos podido cambiar el nombre de la rama principal de master a main
Cambiar de rama (git switch <branch>)
Si queremos cambiar de rama solo hay que escribir git switch <nombre de la rama>
InputPython!cd notebook_git && git switch new_branchCopied
Cambiado a rama 'new_branch'
Vamos a ver en qué rama estamos con git branch
InputPython!cd notebook_git && git branchCopied
main* new_branch
Como vemos, hemos cambiado de rama a new_branch
Si queremos crear y cambiar de rama en un solo comando podemos usar git switch -c <nombre de la rama>
InputPython!cd notebook_git && git switch -c new_branch2Copied
Cambiado a nueva rama 'new_branch2'
Vamos a ver en qué rama estamos
InputPython!cd notebook_git && git branchCopied
mainnew_branch* new_branch2
Hemos creado y cambiado de rama con un solo comando
Obtener la rama en la que estamos (git branch --show-current)
Como hemos visto hasta ahora con git branch podemos obtener una lista de todas las ramas y además ver en la que estamos actualmente, pero en el caso en el que tengamos una gran cantidad de ramas, cosa que se puede dar en un equipo de trabajo con mucha gente, está bien obtener la rama y no obtener una lista de todas, para eso usamos git branch --show-current
InputPython!cd notebook_git && git branch --show-currentCopied
new_branch2
Lista de ramas más recientes (git branch --sort=-committerdate)
En caso de tener muchas ramas, a lo mejor nos interesa saber cuáles son las más recientes para ver cuáles han sido las últimas creadas y dónde debería estar lo último en desarrollo, para eso usamos git branch --sort=-committerdate
InputPython!cd notebook_git && git branch --sort=-committerdateCopied
* new_branch2new_branchmain
Como vemos, las ha ordenado en orden inverso a cuando las hemos creado
El comando deprecado git checkout
Hasta hace un tiempo el comando para crear ramas y cambiar entre ellas era git checkout, pero este comando no solo hace esto, sino que también restaura el directorio de trabajo. Pero esto va en contra de la filosofía de Linux, por lo que se crearon los comandos git branch, git switch y git restore para dividir esta funcionalidad
Fusionando ramas (git merge)
Como hemos dicho, crear ramas es muy útil para desarrollar nuevas características sin afectar al resto del equipo. Pero cuando estas están terminadas hay que llevarlas a la rama principal, para ello utilizamos el comando git merge <rama>
**Importante**: Tenemos que estar en la rama que va a adoptar los cambios, es decir, si queremos fusionar los cambios realizados en la rama
new_branch2en la ramamain, primero tenemos que asegurarnos de estar en la ramamain
Primero comprobamos en qué rama estamos
InputPython!cd notebook_git && git branch --show-currentCopied
new_branch2
Eliminamos archivo7.py
InputPython!cd notebook_git && git rm archivo7.pyCopied
rm 'archivo7.py'
Hacemos un commit con los cambios
InputPython!cd notebook_git && git commit -am "Eliminado archivo7.py"Copied
[new_branch2 5168f78] Eliminado archivo7.py1 file changed, 1 deletion(-)delete mode 100644 archivo7.py
Si hacemos un ls vemos que archivo7.py ya no está
InputPython!cd notebook_git && ls | grep archivo7Copied
Creamos un nuevo archivo y hacemos un commit
InputPython!cd notebook_git && touch archivo8.py && git add archivo8.py && git commit -m "Commit con el archivo 8"Copied
[new_branch2 564ccfb] Commit con el archivo 81 file changed, 0 insertions(+), 0 deletions(-)create mode 100644 archivo8.py
Hemos hecho dos commits nuevos en esta rama, veámoslo con git log
InputPython!cd notebook_git && git log --graph --oneline --decorateCopied
* 564ccfb (HEAD -> new_branch2) Commit con el archivo 8* 5168f78 Eliminado archivo7.py* 4bb9d75 (new_branch, main) Commit con el archivo 7* ea615a9 Eliminado archivo5.py* e3153a5 Commit con los archivos 4 y 5* 0b09cfa Añadido .gitignore* 04ebd1f Commit con los archivos 1 y 2* c4930d7 Tercer commit, hola.py* 6e99e73 Segundo commit, hola.py* 1c95e4f Primer commit, hola.py
Cambiamos a la rama principal
InputPython!cd notebook_git && git switch mainCopied
Cambiado a rama 'main'
Si ahora hacemos otra vez ls veremos que archivo7.py sí está
InputPython!cd notebook_git && ls | grep archivo7Copied
archivo7.py
Fusionamos las ramas, traemos los cambios de new_branch2 a main
InputPython!cd notebook_git && git merge new_branch2Copied
Actualizando 4bb9d75..564ccfbFast-forwardarchivo7.py | 1 -archivo8.py | 02 files changed, 1 deletion(-)delete mode 100644 archivo7.pycreate mode 100644 archivo8.py
Hacemos un git status
InputPython!cd notebook_git && git statusCopied
En la rama mainnada para hacer commit, el árbol de trabajo está limpio
Vemos que al hacer el merge no es necesario hacer ningún commit, veamos con un git log qué ha pasado
InputPython!cd notebook_git && git log --graph --oneline --decorateCopied
* 564ccfb (HEAD -> main, new_branch2) Commit con el archivo 8* 5168f78 Eliminado archivo7.py* 4bb9d75 (new_branch) Commit con el archivo 7* ea615a9 Eliminado archivo5.py* e3153a5 Commit con los archivos 4 y 5* 0b09cfa Añadido .gitignore* 04ebd1f Commit con los archivos 1 y 2* c4930d7 Tercer commit, hola.py* 6e99e73 Segundo commit, hola.py* 1c95e4f Primer commit, hola.py
Vemos que el commit de la rama new_branch2 se ha incorporado a esta rama
Fast-forward
Este caso creamos una nueva rama, la principal no la tocamos y solo modificamos la nueva, realizando varios commits. Por lo que al unir la nueva en la principal se verán todos los commits que se habían hecho en la nueva. Para realizar este tipo de merge escribimos git merge --ff-only <rama>
InputPython### Este código es para crear el gráfico de las ramas, no es necesario para el cursoimport graphviz# Crear el gráfico con la dirección de las flechas de izquierda a derechaorin = graphviz.Digraph('G', graph_attr={'rankdir': 'LR'})fast_foward = graphviz.Digraph('G', graph_attr={'rankdir': 'LR'})# Agregar nodos (pelotas) A, B, C y D en la rama principalorin.node('A', shape='circle', label='A', color='blue')orin.node('B', shape='circle', label='B', color='blue')orin.node('C', shape='circle', label='C', color='blue')orin.node('D', shape='circle', label='D', color='blue')orin.node('E', shape='circle', label='', color='transparent')fast_foward.node('A', shape='circle', label='A', color='blue')fast_foward.node('B', shape='circle', label='B', color='blue')fast_foward.node('C', shape='circle', label='C', color='blue')fast_foward.node('D', shape='circle', label='D', color='blue')# Agregar nodos (pelotas) X e Y en la rama secundariaorin.node('X', shape='circle', label='X', color='green')orin.node('Y', shape='circle', label='Y', color='green')fast_foward.node('X', shape='circle', label='X', color='magenta')fast_foward.node('Y', shape='circle', label='Y', color='magenta')# Agregar flechas entre los nodos en la rama principalorin.edges(['AB', 'BC', 'CD'])orin.edge('D', 'E', color='transparent') # Hacer la flecha de C a D transparentefast_foward.edges(['AB', 'BC', 'CD', 'DX', 'XY'])# Agregar flechas entre los nodos en la rama secundariaorin.edges(['DX', 'XY'])# Mostrar el diagrama de flujo en la celda de código de Jupyter Notebookdisplay(orin)display(fast_foward)Copied
<graphviz.graphs.Digraph at 0x7f58f80c09a0>
<graphviz.graphs.Digraph at 0x7f58f9203fa0>
Vamos primero a comprobar que estamos en la rama principal
InputPython!cd notebook_git && git branch --show-currentCopied
main
Creamos una nueva rama
InputPython!cd notebook_git && git branch branch_fast_forwardCopied
Cambiamos a ella
InputPython!cd notebook_git && git switch branch_fast_forwardCopied
Cambiado a rama 'branch_fast_forward'
InputPython!cd notebook_git && git branch --show-currentCopied
branch_fast_forward
Vamos a ver el log
InputPython!cd notebook_git && git log --graph --oneline --decorate --allCopied
* 564ccfb (HEAD -> branch_fast_forward, new_branch2, main) Commit con el archivo 8* 5168f78 Eliminado archivo7.py* 4bb9d75 (new_branch) Commit con el archivo 7* ea615a9 Eliminado archivo5.py* e3153a5 Commit con los archivos 4 y 5* 0b09cfa Añadido .gitignore* 04ebd1f Commit con los archivos 1 y 2* c4930d7 Tercer commit, hola.py* 6e99e73 Segundo commit, hola.py* 1c95e4f Primer commit, hola.py
Vale, estamos en una rama creada a partir de la principal con todo su historial de logs, hacemos dos nuevos commits
InputPython!cd notebook_git && git rm archivo4.py && git commit -am "Eliminado archivo4.py"Copied
rm 'archivo4.py'[branch_fast_forward 4484e70] Eliminado archivo4.py1 file changed, 1 deletion(-)delete mode 100644 archivo4.py
InputPython!cd notebook_git && git rm hola.py && git commit -am "Eliminado hola.py"Copied
rm 'hola.py'[branch_fast_forward 94149fc] Eliminado hola.py1 file changed, 3 deletions(-)delete mode 100644 hola.py
Hacemos un nuevo log para ver que en esta nueva rama se han creado
InputPython!cd notebook_git && git log --graph --oneline --decorateCopied
* 94149fc (HEAD -> branch_fast_forward) Eliminado hola.py* 4484e70 Eliminado archivo4.py* 564ccfb (new_branch2, main) Commit con el archivo 8* 5168f78 Eliminado archivo7.py* 4bb9d75 (new_branch) Commit con el archivo 7* ea615a9 Eliminado archivo5.py* e3153a5 Commit con los archivos 4 y 5* 0b09cfa Añadido .gitignore* 04ebd1f Commit con los archivos 1 y 2* c4930d7 Tercer commit, hola.py* 6e99e73 Segundo commit, hola.py* 1c95e4f Primer commit, hola.py
Como vemos, los dos últimos commits son los que hemos creado y podemos comprobar que esos commits no están en la rama principal (para eso especifico que lo haga sobre la rama main)
InputPython!cd notebook_git && git log main --graph --oneline --decorateCopied
* 564ccfb (new_branch2, main) Commit con el archivo 8* 5168f78 Eliminado archivo7.py* 4bb9d75 (new_branch) Commit con el archivo 7* ea615a9 Eliminado archivo5.py* e3153a5 Commit con los archivos 4 y 5* 0b09cfa Añadido .gitignore* 04ebd1f Commit con los archivos 1 y 2* c4930d7 Tercer commit, hola.py* 6e99e73 Segundo commit, hola.py* 1c95e4f Primer commit, hola.py
Ahora nos vamos a la rama main para hacer el merge
InputPython!cd notebook_git && git switch mainCopied
Cambiado a rama 'main'
Por último, hacemos el merge de tipo fast forward
InputPython!cd notebook_git && git merge --ff-only branch_fast_forwardCopied
Actualizando 564ccfb..94149fcFast-forwardarchivo4.py | 1 -hola.py | 3 ---2 files changed, 4 deletions(-)delete mode 100644 archivo4.pydelete mode 100644 hola.py
Se ha hecho el merge, veamos qué ha pasado con el log en la rama main
InputPython!cd notebook_git && git log --graph --oneline --decorateCopied
* 94149fc (HEAD -> main, branch_fast_forward) Eliminado hola.py* 4484e70 Eliminado archivo4.py* 564ccfb (new_branch2) Commit con el archivo 8* 5168f78 Eliminado archivo7.py* 4bb9d75 (new_branch) Commit con el archivo 7* ea615a9 Eliminado archivo5.py* e3153a5 Commit con los archivos 4 y 5* 0b09cfa Añadido .gitignore* 04ebd1f Commit con los archivos 1 y 2* c4930d7 Tercer commit, hola.py* 6e99e73 Segundo commit, hola.py* 1c95e4f Primer commit, hola.py
Como se ve, se han mantenido los dos commits hechos en la rama branch_fast_forward
No fast-forward o true merge
En este caso creamos una nueva rama, la principal no la tocamos y modificamos la nueva, realizando varios commits. A continuación realizamos un commit en la principal. Por lo que al unir la nueva en la principal se verá un único commit en la nueva. Para hacer este tipo de merge escribimos git merge <rama> --no-ff
InputPython### Este código es para crear el gráfico de las ramas, no es necesario para el cursoimport graphviz# Crear el gráfico con la dirección de las flechas de izquierda a derechaorin = graphviz.Digraph('G', graph_attr={'rankdir': 'LR'})not_fast_forward = graphviz.Digraph('G', graph_attr={'rankdir': 'LR'})# Agregar nodos (pelotas) A, B, C y D en la rama principalorin.node('A', shape='circle', label='A', color='blue')orin.node('B', shape='circle', label='B', color='blue')orin.node('C', shape='circle', label='C', color='blue')orin.node('D', shape='circle', label='D', color='blue')orin.node('E', shape='circle', label='', color='transparent')not_fast_forward.node('A', shape='circle', label='A', color='blue')not_fast_forward.node('B', shape='circle', label='B', color='blue')not_fast_forward.node('C', shape='circle', label='C', color='blue')not_fast_forward.node('D', shape='circle', label='D', color='blue')not_fast_forward.node('E', shape='circle', label='E', color='blue')# Agregar nodos (pelotas) X e Y en la rama secundariaorin.node('X', shape='circle', label='X', color='green')orin.node('Y', shape='circle', label='Y', color='green')not_fast_forward.node('X', shape='circle', label='X', color='green')not_fast_forward.node('Y', shape='circle', label='Y', color='green')# Agregar nodo (pelota) M en la rama principalnot_fast_forward.node('M', shape='circle', label='M', color='magenta')# Agregar flechas entre los nodos en la rama principalorin.edges(['AB', 'BC', 'CD'])orin.edge('D', 'E', color='transparent') # Hacer la flecha de C a D transparentenot_fast_forward.edges(['AB', 'BC', 'CD', 'DE'])# Agregar flechas entre los nodos en la rama secundariaorin.edges(['DX', 'XY'])not_fast_forward.edges(['DX', 'XY', 'YM'])# Agregar flechas de la rama principal al nodo Mnot_fast_forward.edge('E', 'M')# Mostrar el diagrama de flujo en la celda de código de Jupyter Notebookdisplay(orin)display(not_fast_forward)Copied
<graphviz.graphs.Digraph at 0x7f58f80c2fb0>
<graphviz.graphs.Digraph at 0x7f58f80c2230>
Vamos primero a comprobar que estamos en la rama principal
InputPython!cd notebook_git && git branch --show-currentCopied
main
Creamos una nueva rama
InputPython!cd notebook_git && git branch branch_no_fast_forwardCopied
Cambiamos a ella
InputPython!cd notebook_git && git switch branch_no_fast_forwardCopied
Cambiado a rama 'branch_no_fast_forward'
InputPython!cd notebook_git && git branch --show-currentCopied
branch_no_fast_forward
Vale, estamos en una rama creada a partir de la principal con todo su historial de logs, hacemos dos nuevos commits
InputPython!cd notebook_git && touch file1 && git add file1 && git commit -m "file1"Copied
[branch_no_fast_forward e4e23c9] file11 file changed, 0 insertions(+), 0 deletions(-)create mode 100644 file1
InputPython!cd notebook_git && touch file2 && git add file2 && git commit -m "file2"Copied
[branch_no_fast_forward 8df3429] file21 file changed, 0 insertions(+), 0 deletions(-)create mode 100644 file2
Hacemos un nuevo log para ver qué en esta nueva rama se han creado
InputPython!cd notebook_git && git log --graph --oneline --decorateCopied
* 8df3429 (HEAD -> branch_no_fast_forward) file2* e4e23c9 file1* 94149fc (main, branch_fast_forward) Eliminado hola.py* 4484e70 Eliminado archivo4.py* 564ccfb (new_branch2) Commit con el archivo 8* 5168f78 Eliminado archivo7.py* 4bb9d75 (new_branch) Commit con el archivo 7* ea615a9 Eliminado archivo5.py* e3153a5 Commit con los archivos 4 y 5* 0b09cfa Añadido .gitignore* 04ebd1f Commit con los archivos 1 y 2* c4930d7 Tercer commit, hola.py* 6e99e73 Segundo commit, hola.py* 1c95e4f Primer commit, hola.py
Como vemos, los dos últimos commits son los que hemos creado y podemos comprobar que esos commits no están en la rama principal (para eso especifico que lo haga sobre la rama main)
InputPython!cd notebook_git && git log main --graph --oneline --decorateCopied
* 94149fc (main, branch_fast_forward) Eliminado hola.py* 4484e70 Eliminado archivo4.py* 564ccfb (new_branch2) Commit con el archivo 8* 5168f78 Eliminado archivo7.py* 4bb9d75 (new_branch) Commit con el archivo 7* ea615a9 Eliminado archivo5.py* e3153a5 Commit con los archivos 4 y 5* 0b09cfa Añadido .gitignore* 04ebd1f Commit con los archivos 1 y 2* c4930d7 Tercer commit, hola.py* 6e99e73 Segundo commit, hola.py* 1c95e4f Primer commit, hola.py
Ahora nos vamos a la rama main
InputPython!cd notebook_git && git switch mainCopied
Cambiado a rama 'main'
Creamos un nuevo commit
InputPython!cd notebook_git && touch file3 && git add file3 && git commit -m "file3"Copied
[main 8bdf4d8] file31 file changed, 0 insertions(+), 0 deletions(-)create mode 100644 file3
Por último, hacemos el merge de tipo no fast forward
InputPython!cd notebook_git && git merge branch_no_fast_forward --no-ffCopied
ommit. comenzando con '#' serán ignoradas, y un mensaje vacío abortasaria esta># especialmente si esto fusiona un upstream actualizado en una rama de tópico. /home/wallabot/Documentos/web/portafolio/posts/notebook_git/.git/MERGE_MSG [ línea 1/7 (14%), col 1/48 (2%), car 0/301 (0%) ] [ Párrafo justificado ]...llabot/Documentos/web/portafolio/posts/notebook_git/.git/MERGE_MSG Modificado
Como vemos, nos abre el editor para introducir un mensaje de commit y un mensaje por defecto. Aceptamos el mensaje y vemos qué ha pasado
InputPython!cd notebook_git && git statusCopied
En la rama mainnada para hacer commit, el árbol de trabajo está limpio
InputPython!cd notebook_git && git log --graph --oneline --decorateCopied
* 274529c (HEAD -> main) Merge branch 'branch_no_fast_forward' into main|\| * 8df3429 (branch_no_fast_forward) file2| * e4e23c9 file1* | 8bdf4d8 file3|/* 94149fc (branch_fast_forward) Eliminado hola.py* 4484e70 Eliminado archivo4.py* 564ccfb (new_branch2) Commit con el archivo 8* 5168f78 Eliminado archivo7.py* 4bb9d75 (new_branch) Commit con el archivo 7* ea615a9 Eliminado archivo5.py* e3153a5 Commit con los archivos 4 y 5* 0b09cfa Añadido .gitignore* 04ebd1f Commit con los archivos 1 y 2* c4930d7 Tercer commit, hola.py* 6e99e73 Segundo commit, hola.py* 1c95e4f Primer commit, hola.py
InputPython!cd notebook_git && lsCopied
api_keys.py archivo1.py archivo2.py archivo8.py file1 file2 file3
Como vemos, se ha creado un nuevo commit con los cambios de la nueva rama en la rama principal
Squash
En este tipo de fusión, todos los commits de una nueva rama se juntan en uno solo en la rama principal, para esto escribimos git merge <rama> --squash
InputPython### Este código es para crear el gráfico de las ramas, no es necesario para el cursoimport graphviz# Crear el gráfico con la dirección de las flechas de izquierda a derechaorin = graphviz.Digraph('G', graph_attr={'rankdir': 'LR'})squash = graphviz.Digraph('G', graph_attr={'rankdir': 'LR'})# Agregar nodos (pelotas) A, B, C y D en la rama principalorin.node('A', shape='circle', label='A', color='blue')orin.node('B', shape='circle', label='B', color='blue')orin.node('C', shape='circle', label='C', color='blue')orin.node('D', shape='circle', label='D', color='blue')orin.node('E', shape='circle', label='', color='transparent')squash.node('A', shape='circle', label='A', color='blue')squash.node('B', shape='circle', label='B', color='blue')squash.node('C', shape='circle', label='C', color='blue')squash.node('D', shape='circle', label='D', color='blue')# Agregar nodos (pelotas) X e Y en la rama secundariaorin.node('X', shape='circle', label='X', color='green')orin.node('Y', shape='circle', label='Y', color='green')# Agregar nodo (pelota) M en la rama principalsquash.node('M', shape='circle', label='M', color='magenta')# Agregar flechas entre los nodos en la rama principalorin.edges(['AB', 'BC', 'CD'])orin.edge('D', 'E', color='transparent') # Hacer la flecha de C a D transparentesquash.edges(['AB', 'BC', 'CD', 'DM'])# Agregar flechas entre los nodos en la rama secundariaorin.edges(['DX', 'XY'])# Mostrar el diagrama de flujo en la celda de código de Jupyter Notebookdisplay(orin)display(squash)Copied
<graphviz.graphs.Digraph at 0x7f58f80e6470>
<graphviz.graphs.Digraph at 0x7f58f80e6f80>
Vamos primero a comprobar que estamos en la rama principal
InputPython!cd notebook_git && git branch --show-currentCopied
main
Creamos una nueva rama
InputPython!cd notebook_git && git branch branch_squashCopied
Cambiamos a ella
InputPython!cd notebook_git && git switch branch_squashCopied
Cambiado a rama 'branch_squash'
InputPython!cd notebook_git && git branch --show-currentCopied
branch_squash
Vamos a ver el log
InputPython!cd notebook_git && git log --graph --oneline --decorate --allCopied
* 274529c (HEAD -> branch_squash, main) Merge branch 'branch_no_fast_forward' into main|\| * 8df3429 (branch_no_fast_forward) file2| * e4e23c9 file1* | 8bdf4d8 file3|/* 94149fc (branch_fast_forward) Eliminado hola.py* 4484e70 Eliminado archivo4.py* 564ccfb (new_branch2) Commit con el archivo 8* 5168f78 Eliminado archivo7.py* 4bb9d75 (new_branch) Commit con el archivo 7* ea615a9 Eliminado archivo5.py* e3153a5 Commit con los archivos 4 y 5* 0b09cfa Añadido .gitignore* 04ebd1f Commit con los archivos 1 y 2* c4930d7 Tercer commit, hola.py* 6e99e73 Segundo commit, hola.py* 1c95e4f Primer commit, hola.py
Vale, estamos en una rama creada a partir de la principal con todo su historial de logs, hacemos tres nuevos commits
InputPython!cd notebook_git && git rm file1 && git commit -am "Eliminado file1"Copied
rm 'file1'[branch_squash 767b632] Eliminado file11 file changed, 0 insertions(+), 0 deletions(-)delete mode 100644 file1
InputPython!cd notebook_git && git rm file2 && git commit -am "Eliminado file2"Copied
rm 'file2'[branch_squash a47f771] Eliminado file21 file changed, 0 insertions(+), 0 deletions(-)delete mode 100644 file2
InputPython!cd notebook_git && git rm file3 && git commit -am "Eliminado file3"Copied
rm 'file3'[branch_squash 85f8c9f] Eliminado file31 file changed, 0 insertions(+), 0 deletions(-)delete mode 100644 file3
Hacemos un nuevo log para ver que en esta nueva rama se han creado
InputPython!cd notebook_git && git log --graph --oneline --decorateCopied
* 85f8c9f (HEAD -> branch_squash) Eliminado file3* a47f771 Eliminado file2* 767b632 Eliminado file1* 274529c (main) Merge branch 'branch_no_fast_forward' into main|\| * 8df3429 (branch_no_fast_forward) file2| * e4e23c9 file1* | 8bdf4d8 file3|/* 94149fc (branch_fast_forward) Eliminado hola.py* 4484e70 Eliminado archivo4.py* 564ccfb (new_branch2) Commit con el archivo 8* 5168f78 Eliminado archivo7.py* 4bb9d75 (new_branch) Commit con el archivo 7* ea615a9 Eliminado archivo5.py* e3153a5 Commit con los archivos 4 y 5* 0b09cfa Añadido .gitignore* 04ebd1f Commit con los archivos 1 y 2* c4930d7 Tercer commit, hola.py* 6e99e73 Segundo commit, hola.py* 1c95e4f Primer commit, hola.py
Como vemos, los tres últimos commits son los que hemos creado y podemos comprobar que esos commits no están en la rama principal (para eso especifico que lo haga sobre la rama main)
InputPython!cd notebook_git && git log main --graph --oneline --decorateCopied
* 274529c (main) Merge branch 'branch_no_fast_forward' into main|\| * 8df3429 (branch_no_fast_forward) file2| * e4e23c9 file1* | 8bdf4d8 file3|/* 94149fc (branch_fast_forward) Eliminado hola.py* 4484e70 Eliminado archivo4.py* 564ccfb (new_branch2) Commit con el archivo 8* 5168f78 Eliminado archivo7.py* 4bb9d75 (new_branch) Commit con el archivo 7* ea615a9 Eliminado archivo5.py* e3153a5 Commit con los archivos 4 y 5* 0b09cfa Añadido .gitignore* 04ebd1f Commit con los archivos 1 y 2* c4930d7 Tercer commit, hola.py* 6e99e73 Segundo commit, hola.py* 1c95e4f Primer commit, hola.py
Ahora nos vamos a la rama main para hacer el merge
InputPython!cd notebook_git && git switch mainCopied
Cambiado a rama 'main'
Hacemos el merge de tipo squash
InputPython!cd notebook_git && git merge branch_squash --squashCopied
Actualizando 274529c..85f8c9fFast-forwardCommit de aplastamiento -- no actualizando HEADfile1 | 0file2 | 0file3 | 03 files changed, 0 insertions(+), 0 deletions(-)delete mode 100644 file1delete mode 100644 file2delete mode 100644 file3
Se ha hecho el merge, veamos qué ha pasado con el log en la rama main
InputPython!cd notebook_git && git log --graph --oneline --decorateCopied
* 274529c (HEAD -> main) Merge branch 'branch_no_fast_forward' into main|\| * 8df3429 (branch_no_fast_forward) file2| * e4e23c9 file1* | 8bdf4d8 file3|/* 94149fc (branch_fast_forward) Eliminado hola.py* 4484e70 Eliminado archivo4.py* 564ccfb (new_branch2) Commit con el archivo 8* 5168f78 Eliminado archivo7.py* 4bb9d75 (new_branch) Commit con el archivo 7* ea615a9 Eliminado archivo5.py* e3153a5 Commit con los archivos 4 y 5* 0b09cfa Añadido .gitignore* 04ebd1f Commit con los archivos 1 y 2* c4930d7 Tercer commit, hola.py* 6e99e73 Segundo commit, hola.py* 1c95e4f Primer commit, hola.py
No aparecen los cambios realizados en la rama, hagamos un git status
InputPython!cd notebook_git && git statusCopied
En la rama mainCambios a ser confirmados:(usa "git restore --staged <archivo>..." para sacar del área de stage)borrados: file1borrados: file2borrados: file3
Vemos que tenemos que hacer el commit con la fusión. Esto es porque Git no sabe qué mensaje ponerle y nos deja a nosotros que lo hagamos, de modo que lo hacemos
InputPython!cd notebook_git && git commit -m "Merge squash de los commits de la rama branch_squash"Copied
[main 52acb97] Merge squash de los commits de la rama branch_squash3 files changed, 0 insertions(+), 0 deletions(-)delete mode 100644 file1delete mode 100644 file2delete mode 100644 file3
Volvemos a hacer un git log
InputPython!cd notebook_git && git log --graph --oneline --decorateCopied
* 52acb97 (HEAD -> main) Merge squash de los commits de la rama branch_squash* 274529c Merge branch 'branch_no_fast_forward' into main|\| * 8df3429 (branch_no_fast_forward) file2| * e4e23c9 file1* | 8bdf4d8 file3|/* 94149fc (branch_fast_forward) Eliminado hola.py* 4484e70 Eliminado archivo4.py* 564ccfb (new_branch2) Commit con el archivo 8* 5168f78 Eliminado archivo7.py* 4bb9d75 (new_branch) Commit con el archivo 7* ea615a9 Eliminado archivo5.py* e3153a5 Commit con los archivos 4 y 5* 0b09cfa Añadido .gitignore* 04ebd1f Commit con los archivos 1 y 2* c4930d7 Tercer commit, hola.py* 6e99e73 Segundo commit, hola.py* 1c95e4f Primer commit, hola.py
Conflictos a la hora de fusionar
Cuando se fusionan ramas puede pasar que un mismo archivo haya sido modificado en varias ramas. Esto puede ser normal en proyectos donde varias personas están desarrollando, así que vamos a ver cómo solucionarlo
Creamos un conflicto
Comprobamos que estamos en la rama main
InputPython!cd notebook_git && git branch --show-currentCopied
main
Creamos una nueva rama a partir de esta
InputPython!cd notebook_git && git branch rama_con_conflictoCopied
Añadimos una línea al archivo archivo1.py, recordamos que estamos en la rama main
InputPython!cd notebook_git && echo "print('rama main')" >> archivo1.pyCopied
InputPython!cd notebook_git && cat archivo1.pyCopied
print('Este es el archivo 1')print('rama main')
archivo1.py está modificado, hacemos un commit
InputPython!cd notebook_git && git add archivo1.py && git commit -m "archivo1.py en rama main"Copied
[main 53f909b] archivo1.py en rama main1 file changed, 1 insertion(+)
Ahora nos vamos a la rama rama_con_conflicto y añadimos una nueva línea a archivo1.py
InputPython!cd notebook_git && git switch rama_con_conflictoCopied
Cambiado a rama 'rama_con_conflicto'
InputPython!cd notebook_git && git branch --show-currentCopied
rama_con_conflicto
InputPython!cd notebook_git && echo "print('rama rama_con_conflicto')" >> archivo1.pyCopied
InputPython!cd notebook_git && cat archivo1.pyCopied
print('Este es el archivo 1')print('rama rama_con_conflicto')
Como vemos, archivo1.py no es igual en la rama main y en la rama rama_con_conflicto. Hacemos un commit con la modificación de archivo1.py en la rama rama_con_conflicto
InputPython!cd notebook_git && git add archivo1.py && git commit -m "archivo1.py en rama rama_con_conflicto"Copied
[rama_con_conflicto 32851c3] archivo1.py en rama rama_con_conflicto1 file changed, 1 insertion(+)
Volvemos a la rama main
InputPython!cd notebook_git && git switch mainCopied
Cambiado a rama 'main'
InputPython!cd notebook_git && git branch --show-currentCopied
main
Hacemos un merge de la rama rama_con_conflicto
InputPython!cd notebook_git && git merge rama_con_conflictoCopied
Auto-fusionando archivo1.pyCONFLICTO (contenido): Conflicto de fusión en archivo1.pyFusión automática falló; arregle los conflictos y luego realice un commit con el resultado.
Al hacer el merge ya nos avisa que hay un conflicto en archivo1.py y que no se ha podido hacer el merge. Hacemos un git status
InputPython!cd notebook_git && git statusCopied
En la rama mainTienes rutas no fusionadas.(arregla los conflictos y ejecuta "git commit"(usa "git merge --abort" para abortar la fusion)Rutas no fusionadas:(usa "git add <archivo>..." para marcar una resolución)modificados por ambos: archivo1.pysin cambios agregados al commit (usa "git add" y/o "git commit -a")
Nos dice lo mismo, pero nos da más información, nos dice que podemos abortar la fusión con git merge --abort. Pero en lugar de eso vamos a solucionarlo
Solucionar un conflicto
En nuestro caso sabemos dónde está el problema, pero en caso de no saberlo, mediante git diff podemos encontrar el problema
InputPython!cd notebook_git && git diff archivo1.pyCopied
diff --cc archivo1.pyindex 8b4bf58,b5c003c..0000000--- a/archivo1.py+++ b/archivo1.py@@@ -1,2 -1,2 +1,6 @@@print('Este es el archivo 1')++<<<<<<< HEAD+print('rama main')++=======+ print('rama rama_con_conflicto')++>>>>>>> rama_con_conflicto
git diff nos está diciendo que el problema está en la última línea. La versión del archivo de la rama HEAD (en la que estamos actualmente main) tiene print('rama main') en la última línea, mientras que la versión de la rama rama_con_conflicto tiene print('rama rama_con_conflicto') en la última línea. De modo que hay que abrir el fichero con cualquier editor y solucionar esto.
Tras editar el fichero con mi editor de código he quitado todas las líneas que sobran y se ha quedado así
InputPython!cd notebook_git && cat archivo1.pyCopied
print('Este es el archivo 1')print('rama main')
Ahora hacemos un git status para ver qué tenemos que hacer
InputPython!cd notebook_git && git statusCopied
En la rama mainTienes rutas no fusionadas.(arregla los conflictos y ejecuta "git commit"(usa "git merge --abort" para abortar la fusion)Rutas no fusionadas:(usa "git add <archivo>..." para marcar una resolución)modificados por ambos: archivo1.pysin cambios agregados al commit (usa "git add" y/o "git commit -a")
Nos dice que tenemos el archivo archivo1.py con modificaciones, por lo que lo añadimos al área de staged y luego hacemos un commit
InputPython!cd notebook_git && git add archivo1.py && git commit -m "archivo1.py con el merge resuelto"Copied
[main 679bb49] archivo1.py con el merge resuelto
Volvemos a hacer un git status a ver si se ha solucionado
InputPython!cd notebook_git && git statusCopied
En la rama mainnada para hacer commit, el árbol de trabajo está limpio
Parece que se ha resuelto, hacemos un git log para comprobarlo
InputPython!cd notebook_git && git log --graph --oneline --decorateCopied
* 679bb49 (HEAD -> main) archivo1.py con el merge resuelto|\| * 32851c3 (rama_con_conflicto) archivo1.py en rama rama_con_conflicto* | 53f909b archivo1.py en rama main|/* 52acb97 Merge squash de los commits de la rama branch_squash* 274529c Merge branch 'branch_no_fast_forward' into main|\| * 8df3429 (branch_no_fast_forward) file2| * e4e23c9 file1* | 8bdf4d8 file3|/* 94149fc (branch_fast_forward) Eliminado hola.py* 4484e70 Eliminado archivo4.py* 564ccfb (new_branch2) Commit con el archivo 8* 5168f78 Eliminado archivo7.py* 4bb9d75 (new_branch) Commit con el archivo 7* ea615a9 Eliminado archivo5.py* e3153a5 Commit con los archivos 4 y 5* 0b09cfa Añadido .gitignore* 04ebd1f Commit con los archivos 1 y 2* c4930d7 Tercer commit, hola.py* 6e99e73 Segundo commit, hola.py* 1c95e4f Primer commit, hola.py
Eliminar ramas
La idea de una rama es abrirla para desarrollar una nueva funcionalidad o característica de manera que no afecte al resto del equipo de desarrollo. Por lo tanto, cuando esto está conseguido, se fusiona con la rama principal. Así que después de la fusión ya no tiene sentido mantener la rama, por lo que hay que eliminarla, pero se hará de una manera diferente si la rama se ha fusionado o no con la principal
Eliminar ramas que se han fusionado (git branch -d <rama>)
Para eliminar una rama sobre la que ya se ha hecho la fusión con la principal solo hay que hacer git branch -d <rama> o git branch --delete <rama>. Vamos a eliminar la última rama que hemos creado y fusionado
InputPython!cd notebook_git && git branch -d rama_con_conflictoCopied
Eliminada la rama rama_con_conflicto (era 32851c3).
Si ahora listamos todas las ramas, podemos ver que ya no tendremos rama_con_conflicto
InputPython!cd notebook_git && git branchCopied
branch_fast_forwardbranch_no_fast_forwardbranch_squash* mainnew_branchnew_branch2
Eliminar ramas que no se han fusionado con la rama principal (git branch -D <rama>)
Si intentamos hacer lo mismo que antes con una rama que nunca se ha fusionado con otra, obtendremos un error
Vamos a crear una nueva rama, vamos a hacer un commit y no la vamos a fusionar
InputPython!cd notebook_git && git branch branch_sin_fusionCopied
InputPython!cd notebook_git && git switch branch_sin_fusionCopied
Cambiado a rama 'branch_sin_fusion'
InputPython!cd notebook_git && touch file4 && git add file4 && git commit -m "file4"Copied
[branch_sin_fusion 9506b0a] file41 file changed, 0 insertions(+), 0 deletions(-)create mode 100644 file4
InputPython!cd notebook_git && git switch mainCopied
Cambiado a rama 'main'
Con todo lo que hemos aprendido hasta ahora, podemos ver que lo que hemos hecho ha sido crear una nueva rama, cambiar a ella, crear un nuevo archivo, hacer un commit y volver a la rama main. Ahora vamos a intentar eliminar esta nueva rama
InputPython!cd notebook_git && git branch -d branch_sin_fusionCopied
error: La rama 'branch_sin_fusion' no ha sido fusionada completamente.Si estás seguro de querer borrarla, ejecuta 'git branch -D branch_sin_fusion'.
Como vemos, nos dice que branch_sin_fusion no ha sido fusionada, por lo que no se ha eliminado, y que si estamos seguros de eliminarla tenemos que hacer git branch -D branch_sin_fusion, de modo que lo hacemos para eliminarla
InputPython!cd notebook_git && git branch -D branch_sin_fusionCopied
Eliminada la rama branch_sin_fusion (era 9506b0a).
Ahora sí se ha eliminado
---
➡️ **Continúa en la Parte 3: repositorios remotos, stash y tags**, donde llevarás tu código a la nube y le sacarás partido a las herramientas avanzadas de Git.