Git (2/3): branches

Git (2/3): branches

In the first part we learned how to keep version control of a project locally: the git areas, the initial configuration, and the commit workflow. In this second chapter we dive into **branches**: how to create them, move between them, merge them, and resolve conflicts. 🎸

Disclaimer: This post has been translated to English using a machine translation model. Please, let me know if you find any mistakes.

📚 **This entry is part of the _Git Guide_ series**, divided into three chapters that should be read in order:

> * Part 1: Version control locally

* 👉 **Part 2: Branches**

* Part 3: Remote repositories, stash, and tags

Brancheslink image 20

branches

Using branches is very useful, as we can start developing a new feature without having to modify the original code.

Create a branch (git branch <branch name>)link image 21

**Note**: First of all, it should be said that to create a branch in a repository there must be at least one commit; if you try to create a branch before making the first commit, you will receive an error

To create a branch, we use the command git branch <branch name>

	
< > Input
Python
!cd notebook_git && git branch new_branch
Copied

List branches (git branch)link image 22

We have created our first branch; we can check all the branches we have created by typing just git branch

	
< > Input
Python
!cd notebook_git && git branch
Copied
>_ Output
			
* master
new_branch

In addition to listing, it tells us with an asterisk * which branch we are on, in this case on the master branch

Rename branches, goodbye to the master branch (git branch -m <old name> <new name>)link image 23

Historically in git the main branch has been called master, but this has bad historical connotations because of the master-slave concept, due to the suffering many people experienced, so the main branch is now usually referred to as main, so to rename it we use git branch -m master main

	
< > Input
Python
!cd notebook_git && git branch -m master main
Copied

We list the branches

	
< > Input
Python
!cd notebook_git && git branch
Copied
>_ Output
			
* main
new_branch

As we can see, we have been able to change the name of the main branch from master to main

Switch branches (git switch <branch>)link image 24

If we want to switch branches, we just have to write git switch <branch name>

	
< > Input
Python
!cd notebook_git && git switch new_branch
Copied
>_ Output
			
Cambiado a rama 'new_branch'

Let's see which branch we're on with git branch

	
< > Input
Python
!cd notebook_git && git branch
Copied
>_ Output
			
main
* new_branch

As we can see, we have switched to the new_branch branch

If we want to create and switch to a branch in a single command, we can use git switch -c <branch name>

	
< > Input
Python
!cd notebook_git && git switch -c new_branch2
Copied
>_ Output
			
Cambiado a nueva rama 'new_branch2'

Let's see which branch we're on

	
< > Input
Python
!cd notebook_git && git branch
Copied
>_ Output
			
main
new_branch
* new_branch2

We have created and switched branches with a single command

Get the branch we are on (git branch --show-current)link image 25

As we have seen so far with git branch we can get a list of all the branches and also see which one we are currently on, but in the case where we have a large number of branches, which can happen in a work team with a lot of people, it is good to get the current branch without getting a list of all of them, for that we use git branch --show-current

	
< > Input
Python
!cd notebook_git && git branch --show-current
Copied
>_ Output
			
new_branch2

List of most recent branches (git branch --sort=-committerdate)link image 26

In case you have many branches, you may want to know which ones are the most recent to see which were created last and where the latest development should be. For that, we use git branch --sort=-committerdate

	
< > Input
Python
!cd notebook_git && git branch --sort=-committerdate
Copied
>_ Output
			
* new_branch2
new_branch
main

As we can see, it has ordered them in reverse order from when we created them.

The deprecated git checkout commandlink image 27

Until some time ago, the command to create branches and switch between them was git checkout, but this command not only does this, it also restores the working directory. However, this goes against the Linux philosophy, so the commands git branch, git switch, and git restore were created to split this functionality.

Merging branches (git merge)link image 28

As we have said, creating branches is very useful for developing new features without affecting the rest of the team. But when they are finished, they need to be brought into the main branch; to do this we use the command git merge <branch>

**Important**: We need to be on the branch that will receive the changes; that is, if we want to merge the changes made in the new_branch2 branch into the main branch, first we must make sure we are on the main branch

First, we check which branch we are on

	
< > Input
Python
!cd notebook_git && git branch --show-current
Copied
>_ Output
			
new_branch2

We removed archivo7.py

	
< > Input
Python
!cd notebook_git && git rm archivo7.py
Copied
>_ Output
			
rm 'archivo7.py'

We make a commit with the changes

	
< > Input
Python
!cd notebook_git && git commit -am "Eliminado archivo7.py"
Copied
>_ Output
			
[new_branch2 5168f78] Eliminado archivo7.py
1 file changed, 1 deletion(-)
delete mode 100644 archivo7.py

If we do an ls, we see that archivo7.py is no longer there

	
< > Input
Python
!cd notebook_git && ls | grep archivo7
Copied

We create a new file and make a commit

	
< > Input
Python
!cd notebook_git && touch archivo8.py && git add archivo8.py && git commit -m "Commit con el archivo 8"
Copied
>_ Output
			
[new_branch2 564ccfb] Commit con el archivo 8
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 archivo8.py

We have made two new commits on this branch, let’s see it with git log

	
< > Input
Python
!cd notebook_git && git log --graph --oneline --decorate
Copied
>_ Output
			
* 564ccfb (HEAD -&gt; 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

We switched to the main branch

	
< > Input
Python
!cd notebook_git && git switch main
Copied
>_ Output
			
Cambiado a rama 'main'

If we now run ls again, we will see that archivo7.py is there.

	
< > Input
Python
!cd notebook_git && ls | grep archivo7
Copied
>_ Output
			
archivo7.py

We merge the branches, bringing the changes from new_branch2 into main

	
< > Input
Python
!cd notebook_git && git merge new_branch2
Copied
>_ Output
			
Actualizando 4bb9d75..564ccfb
Fast-forward
archivo7.py | 1 -
archivo8.py | 0
2 files changed, 1 deletion(-)
delete mode 100644 archivo7.py
create mode 100644 archivo8.py

We do a git status

	
< > Input
Python
!cd notebook_git && git status
Copied
>_ Output
			
En la rama main
nada para hacer commit, el árbol de trabajo está limpio

We see that when doing the merge it is not necessary to make any commit; let’s see with a git log what happened

	
< > Input
Python
!cd notebook_git && git log --graph --oneline --decorate
Copied
>_ Output
			
* 564ccfb (HEAD -&gt; 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

We see that the commit from the new_branch2 branch has been incorporated into this branch

Fast-forwardlink image 29

In this case, we create a new branch, we do not touch the main one, and we only modify the new one, making several commits. So when merging the new branch into the main one, all the commits that were made on the new branch will be visible. To perform this type of merge, we write git merge --ff-only <rama>

	
< > Input
Python
### Este código es para crear el gráfico de las ramas, no es necesario para el curso
import graphviz
# Crear el gráfico con la dirección de las flechas de izquierda a derecha
orin = 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 principal
orin.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 secundaria
orin.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 principal
orin.edges(['AB', 'BC', 'CD'])
orin.edge('D', 'E', color='transparent') # Hacer la flecha de C a D transparente
fast_foward.edges(['AB', 'BC', 'CD', 'DX', 'XY'])
# Agregar flechas entre los nodos en la rama secundaria
orin.edges(['DX', 'XY'])
# Mostrar el diagrama de flujo en la celda de código de Jupyter Notebook
display(orin)
display(fast_foward)
Copied
>_ Output
			
&lt;graphviz.graphs.Digraph at 0x7f58f80c09a0&gt;
>_ Output
			
&lt;graphviz.graphs.Digraph at 0x7f58f9203fa0&gt;

Let's first check that we are on the main branch

	
< > Input
Python
!cd notebook_git && git branch --show-current
Copied
>_ Output
			
main

We create a new branch

	
< > Input
Python
!cd notebook_git && git branch branch_fast_forward
Copied

We switched to her

	
< > Input
Python
!cd notebook_git && git switch branch_fast_forward
Copied
>_ Output
			
Cambiado a rama 'branch_fast_forward'
	
< > Input
Python
!cd notebook_git && git branch --show-current
Copied
>_ Output
			
branch_fast_forward

Let's see the log

	
< > Input
Python
!cd notebook_git && git log --graph --oneline --decorate --all
Copied
>_ Output
			
* 564ccfb (HEAD -&gt; 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

Okay, we are on a branch created from the main one with all its log history, we make two new commits

	
< > Input
Python
!cd notebook_git && git rm archivo4.py && git commit -am "Eliminado archivo4.py"
Copied
>_ Output
			
rm 'archivo4.py'
[branch_fast_forward 4484e70] Eliminado archivo4.py
1 file changed, 1 deletion(-)
delete mode 100644 archivo4.py
	
< > Input
Python
!cd notebook_git && git rm hola.py && git commit -am "Eliminado hola.py"
Copied
>_ Output
			
rm 'hola.py'
[branch_fast_forward 94149fc] Eliminado hola.py
1 file changed, 3 deletions(-)
delete mode 100644 hola.py

We create a new log to see that in this new branch they have been created

	
< > Input
Python
!cd notebook_git && git log --graph --oneline --decorate
Copied
>_ Output
			
* 94149fc (HEAD -&gt; 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

As we can see, the last two commits are the ones we created, and we can verify that those commits are not on the main branch (to do that, I specify that it should be done on the main branch)

	
< > Input
Python
!cd notebook_git && git log main --graph --oneline --decorate
Copied
>_ Output
			
* 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

Now we switch to the main branch to do the merge

	
< > Input
Python
!cd notebook_git && git switch main
Copied
>_ Output
			
Cambiado a rama 'main'

Finally, we perform the fast-forward merge

	
< > Input
Python
!cd notebook_git && git merge --ff-only branch_fast_forward
Copied
>_ Output
			
Actualizando 564ccfb..94149fc
Fast-forward
archivo4.py | 1 -
hola.py | 3 ---
2 files changed, 4 deletions(-)
delete mode 100644 archivo4.py
delete mode 100644 hola.py

The merge has been done, let's see what happened to the log on the main branch

	
< > Input
Python
!cd notebook_git && git log --graph --oneline --decorate
Copied
>_ Output
			
* 94149fc (HEAD -&gt; 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

As can be seen, the two commits made on the branch_fast_forward branch have been retained.

No fast-forward or true mergelink image 30

In this case we create a new branch, we do not touch the main one, and we modify the new one, making several commits. Next, we make a commit on the main branch. Therefore, when merging the new one into the main one, it will appear as a single commit in the new branch. To do this type of merge, we write git merge <rama> --no-ff

	
< > Input
Python
### Este código es para crear el gráfico de las ramas, no es necesario para el curso
import graphviz
# Crear el gráfico con la dirección de las flechas de izquierda a derecha
orin = 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 principal
orin.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 secundaria
orin.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 principal
not_fast_forward.node('M', shape='circle', label='M', color='magenta')
# Agregar flechas entre los nodos en la rama principal
orin.edges(['AB', 'BC', 'CD'])
orin.edge('D', 'E', color='transparent') # Hacer la flecha de C a D transparente
not_fast_forward.edges(['AB', 'BC', 'CD', 'DE'])
# Agregar flechas entre los nodos en la rama secundaria
orin.edges(['DX', 'XY'])
not_fast_forward.edges(['DX', 'XY', 'YM'])
# Agregar flechas de la rama principal al nodo M
not_fast_forward.edge('E', 'M')
# Mostrar el diagrama de flujo en la celda de código de Jupyter Notebook
display(orin)
display(not_fast_forward)
Copied
>_ Output
			
&lt;graphviz.graphs.Digraph at 0x7f58f80c2fb0&gt;
>_ Output
			
&lt;graphviz.graphs.Digraph at 0x7f58f80c2230&gt;

Let's first check that we are on the main branch

	
< > Input
Python
!cd notebook_git && git branch --show-current
Copied
>_ Output
			
main

We create a new branch

	
< > Input
Python
!cd notebook_git && git branch branch_no_fast_forward
Copied

We switched to her

	
< > Input
Python
!cd notebook_git && git switch branch_no_fast_forward
Copied
>_ Output
			
Cambiado a rama 'branch_no_fast_forward'
	
< > Input
Python
!cd notebook_git && git branch --show-current
Copied
>_ Output
			
branch_no_fast_forward

Okay, we’re on a branch created from the main one, with all its log history, and we make two new commits

	
< > Input
Python
!cd notebook_git && touch file1 && git add file1 && git commit -m "file1"
Copied
>_ Output
			
[branch_no_fast_forward e4e23c9] file1
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 file1
	
< > Input
Python
!cd notebook_git && touch file2 && git add file2 && git commit -m "file2"
Copied
>_ Output
			
[branch_no_fast_forward 8df3429] file2
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 file2

We make a new log to see what has been created in this new branch

	
< > Input
Python
!cd notebook_git && git log --graph --oneline --decorate
Copied
>_ Output
			
* 8df3429 (HEAD -&gt; 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

As we can see, the last two commits are the ones we created, and we can verify that those commits are not on the main branch (for that, I specify that it should be done on the main branch)

	
< > Input
Python
!cd notebook_git && git log main --graph --oneline --decorate
Copied
>_ Output
			
* 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

Now we are going to the main branch

	
< > Input
Python
!cd notebook_git && git switch main
Copied
>_ Output
			
Cambiado a rama 'main'

We create a new commit

	
< > Input
Python
!cd notebook_git && touch file3 && git add file3 && git commit -m "file3"
Copied
>_ Output
			
[main 8bdf4d8] file3
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 file3

Finally, we perform the non-fast-forward merge

	
< > Input
Python
!cd notebook_git && git merge branch_no_fast_forward --no-ff
Copied
>_ Output
			
ommit. comenzando con '#' serán ignoradas, y un mensaje vacío abortasaria esta&gt;# 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

As we can see, it opens the editor for us to enter a commit message and a default message. We accept the message and see what has happened

	
< > Input
Python
!cd notebook_git && git status
Copied
>_ Output
			
En la rama main
nada para hacer commit, el árbol de trabajo está limpio
	
< > Input
Python
!cd notebook_git && git log --graph --oneline --decorate
Copied
>_ Output
			
* 274529c (HEAD -&gt; 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
	
< > Input
Python
!cd notebook_git && ls
Copied
>_ Output
			
api_keys.py archivo1.py archivo2.py archivo8.py file1 file2 file3

As we can see, a new commit has been created with the changes from the new branch on the main branch

Squashlink image 31

In this type of merge, all the commits from a new branch are combined into a single one on the main branch; to do this, we write git merge <branch> --squash

	
< > Input
Python
### Este código es para crear el gráfico de las ramas, no es necesario para el curso
import graphviz
# Crear el gráfico con la dirección de las flechas de izquierda a derecha
orin = 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 principal
orin.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 secundaria
orin.node('X', shape='circle', label='X', color='green')
orin.node('Y', shape='circle', label='Y', color='green')
# Agregar nodo (pelota) M en la rama principal
squash.node('M', shape='circle', label='M', color='magenta')
# Agregar flechas entre los nodos en la rama principal
orin.edges(['AB', 'BC', 'CD'])
orin.edge('D', 'E', color='transparent') # Hacer la flecha de C a D transparente
squash.edges(['AB', 'BC', 'CD', 'DM'])
# Agregar flechas entre los nodos en la rama secundaria
orin.edges(['DX', 'XY'])
# Mostrar el diagrama de flujo en la celda de código de Jupyter Notebook
display(orin)
display(squash)
Copied
>_ Output
			
&lt;graphviz.graphs.Digraph at 0x7f58f80e6470&gt;
>_ Output
			
&lt;graphviz.graphs.Digraph at 0x7f58f80e6f80&gt;

Let's first check that we are on the main branch

	
< > Input
Python
!cd notebook_git && git branch --show-current
Copied
>_ Output
			
main

We create a new branch

	
< > Input
Python
!cd notebook_git && git branch branch_squash
Copied

We switched to her

	
< > Input
Python
!cd notebook_git && git switch branch_squash
Copied
>_ Output
			
Cambiado a rama 'branch_squash'
	
< > Input
Python
!cd notebook_git && git branch --show-current
Copied
>_ Output
			
branch_squash

Let's look at the log

	
< > Input
Python
!cd notebook_git && git log --graph --oneline --decorate --all
Copied
>_ Output
			
* 274529c (HEAD -&gt; 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

Okay, we’re on a branch created from the main one with all its commit history, and we make three new commits

	
< > Input
Python
!cd notebook_git && git rm file1 && git commit -am "Eliminado file1"
Copied
>_ Output
			
rm 'file1'
[branch_squash 767b632] Eliminado file1
1 file changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 file1
	
< > Input
Python
!cd notebook_git && git rm file2 && git commit -am "Eliminado file2"
Copied
>_ Output
			
rm 'file2'
[branch_squash a47f771] Eliminado file2
1 file changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 file2
	
< > Input
Python
!cd notebook_git && git rm file3 && git commit -am "Eliminado file3"
Copied
>_ Output
			
rm 'file3'
[branch_squash 85f8c9f] Eliminado file3
1 file changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 file3

We create a new log to see that in this new branch they have been created

	
< > Input
Python
!cd notebook_git && git log --graph --oneline --decorate
Copied
>_ Output
			
* 85f8c9f (HEAD -&gt; 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

As we can see, the last three commits are the ones we created, and we can verify that those commits are not on the main branch (for that, I specify that it should be done on the main branch)

	
< > Input
Python
!cd notebook_git && git log main --graph --oneline --decorate
Copied
>_ Output
			
* 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

Now we go to the main branch to do the merge

	
< > Input
Python
!cd notebook_git && git switch main
Copied
>_ Output
			
Cambiado a rama 'main'

We do a squash merge

	
< > Input
Python
!cd notebook_git && git merge branch_squash --squash
Copied
>_ Output
			
Actualizando 274529c..85f8c9f
Fast-forward
Commit de aplastamiento -- no actualizando HEAD
file1 | 0
file2 | 0
file3 | 0
3 files changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 file1
delete mode 100644 file2
delete mode 100644 file3

The merge has been done, let's see what happened with the log in the main branch

	
< > Input
Python
!cd notebook_git && git log --graph --oneline --decorate
Copied
>_ Output
			
* 274529c (HEAD -&gt; 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

The changes made in the branch do not appear; let's run a git status

	
< > Input
Python
!cd notebook_git && git status
Copied
>_ Output
			
En la rama main
Cambios a ser confirmados:
(usa "git restore --staged &lt;archivo&gt;..." para sacar del área de stage)
borrados: file1
borrados: file2
borrados: file3

We see that we have to make the commit with the merge. This is because Git doesn’t know what message to put on it and leaves it up to us to do it, so we do it

	
< > Input
Python
!cd notebook_git && git commit -m "Merge squash de los commits de la rama branch_squash"
Copied
>_ Output
			
[main 52acb97] Merge squash de los commits de la rama branch_squash
3 files changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 file1
delete mode 100644 file2
delete mode 100644 file3

We go back to doing a git log

	
< > Input
Python
!cd notebook_git && git log --graph --oneline --decorate
Copied
>_ Output
			
* 52acb97 (HEAD -&gt; 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

Merge conflictslink image 32

When branches are merged, it may happen that the same file has been modified in multiple branches. This can be normal in projects where several people are developing, so let’s see how to resolve it.

We create a conflictlink image 33

We verify that we are on the main branch

	
< > Input
Python
!cd notebook_git && git branch --show-current
Copied
>_ Output
			
main

We create a new branch from this one

	
< > Input
Python
!cd notebook_git && git branch rama_con_conflicto
Copied

We add a line to the archivo1.py file, and we remember that we are on the main branch

	
< > Input
Python
!cd notebook_git && echo "print('rama main')" &gt;&gt; archivo1.py
Copied
	
< > Input
Python
!cd notebook_git && cat archivo1.py
Copied
>_ Output
			
print('Este es el archivo 1')
print('rama main')

archivo1.py is modified, we make a commit

	
< > Input
Python
!cd notebook_git && git add archivo1.py && git commit -m "archivo1.py en rama main"
Copied
>_ Output
			
[main 53f909b] archivo1.py en rama main
1 file changed, 1 insertion(+)

Now we go to the rama_con_conflicto branch and add a new line to archivo1.py

	
< > Input
Python
!cd notebook_git && git switch rama_con_conflicto
Copied
>_ Output
			
Cambiado a rama 'rama_con_conflicto'
	
< > Input
Python
!cd notebook_git && git branch --show-current
Copied
>_ Output
			
rama_con_conflicto
	
< > Input
Python
!cd notebook_git && echo "print('rama rama_con_conflicto')" &gt;&gt; archivo1.py
Copied
	
< > Input
Python
!cd notebook_git && cat archivo1.py
Copied
>_ Output
			
print('Este es el archivo 1')
print('rama rama_con_conflicto')

As we can see, archivo1.py is not the same in the main branch and in the rama_con_conflicto branch. We make a commit with the modification of archivo1.py in the rama_con_conflicto branch

	
< > Input
Python
!cd notebook_git && git add archivo1.py && git commit -m "archivo1.py en rama rama_con_conflicto"
Copied
>_ Output
			
[rama_con_conflicto 32851c3] archivo1.py en rama rama_con_conflicto
1 file changed, 1 insertion(+)

We return to the main branch

	
< > Input
Python
!cd notebook_git && git switch main
Copied
>_ Output
			
Cambiado a rama 'main'
	
< > Input
Python
!cd notebook_git && git branch --show-current
Copied
>_ Output
			
main

We merge the rama_con_conflicto branch

	
< > Input
Python
!cd notebook_git && git merge rama_con_conflicto
Copied
>_ Output
			
Auto-fusionando archivo1.py
CONFLICTO (contenido): Conflicto de fusión en archivo1.py
Fusión automática falló; arregle los conflictos y luego realice un commit con el resultado.

When we do the merge, it already warns us that there is a conflict in archivo1.py and that the merge could not be completed. We run a git status

	
< > Input
Python
!cd notebook_git && git status
Copied
>_ Output
			
En la rama main
Tienes rutas no fusionadas.
(arregla los conflictos y ejecuta "git commit"
(usa "git merge --abort" para abortar la fusion)
Rutas no fusionadas:
(usa "git add &lt;archivo&gt;..." para marcar una resolución)
modificados por ambos: archivo1.py
sin cambios agregados al commit (usa "git add" y/o "git commit -a")

It tells us the same thing, but gives us more information; it tells us that we can abort the merge with git merge --abort. But instead, we're going to fix it.

Resolve a conflictlink image 34

In our case, we know where the problem is, but if we don’t know it, we can find the problem using git diff

	
< > Input
Python
!cd notebook_git && git diff archivo1.py
Copied
>_ Output
			
diff --cc archivo1.py
index 8b4bf58,b5c003c..0000000
--- a/archivo1.py
+++ b/archivo1.py
@@@ -1,2 -1,2 +1,6 @@@
print('Este es el archivo 1')
++&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD
+print('rama main')
++=======
+ print('rama rama_con_conflicto')
++&gt;&gt;&gt;&gt;&gt;&gt;&gt; rama_con_conflicto

git diff is telling us that the problem is in the last line. The version of the file in the HEAD branch (the one we are currently on, main) has print('rama main') on the last line, while the version in the rama_con_conflicto branch has print('rama rama_con_conflicto') on the last line. So we need to open the file with any editor and fix this.

After editing the file with my code editor, I removed all the extra lines and it ended up like this

	
< > Input
Python
!cd notebook_git && cat archivo1.py
Copied
>_ Output
			
print('Este es el archivo 1')
print('rama main')

Now we run a git status to see what we need to do

	
< > Input
Python
!cd notebook_git && git status
Copied
>_ Output
			
En la rama main
Tienes rutas no fusionadas.
(arregla los conflictos y ejecuta "git commit"
(usa "git merge --abort" para abortar la fusion)
Rutas no fusionadas:
(usa "git add &lt;archivo&gt;..." para marcar una resolución)
modificados por ambos: archivo1.py
sin cambios agregados al commit (usa "git add" y/o "git commit -a")

It tells us that we have the file archivo1.py with changes, so we add it to the staged area and then make a commit

	
< > Input
Python
!cd notebook_git && git add archivo1.py && git commit -m "archivo1.py con el merge resuelto"
Copied
>_ Output
			
[main 679bb49] archivo1.py con el merge resuelto

We run git status again to see if it has been resolved

	
< > Input
Python
!cd notebook_git && git status
Copied
>_ Output
			
En la rama main
nada para hacer commit, el árbol de trabajo está limpio

It seems to have been resolved; let's run a git log to check it.

	
< > Input
Python
!cd notebook_git && git log --graph --oneline --decorate
Copied
>_ Output
			
* 679bb49 (HEAD -&gt; 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

Delete brancheslink image 35

The idea of a branch is to create it to develop a new functionality or feature in a way that does not affect the rest of the development team. Therefore, once this is achieved, it is merged into the main branch. So after the merge, it no longer makes sense to keep the branch, so it must be deleted, but this will be done differently depending on whether the branch has been merged into the main branch or not.

Remove branches that have been merged (git branch -d <branch>)link image 36

To delete a branch that has already been merged into the main one, you only need to run git branch -d <branch> or git branch --delete <branch>. Let's delete the last branch we created and merged.

	
< > Input
Python
!cd notebook_git && git branch -d rama_con_conflicto
Copied
>_ Output
			
Eliminada la rama rama_con_conflicto (era 32851c3).

If we now list all the branches, we can see that we will no longer have rama_con_conflicto

	
< > Input
Python
!cd notebook_git && git branch
Copied
>_ Output
			
branch_fast_forward
branch_no_fast_forward
branch_squash
* main
new_branch
new_branch2

Delete branches that have not been merged into the main branch (git branch -D <branch>)link image 37

If we try to do the same as before with a branch that has never been merged with another, we will get an error

Let's create a new branch, make a commit, and we're not going to merge it.

	
< > Input
Python
!cd notebook_git && git branch branch_sin_fusion
Copied
	
< > Input
Python
!cd notebook_git && git switch branch_sin_fusion
Copied
>_ Output
			
Cambiado a rama 'branch_sin_fusion'
	
< > Input
Python
!cd notebook_git && touch file4 && git add file4 && git commit -m "file4"
Copied
>_ Output
			
[branch_sin_fusion 9506b0a] file4
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 file4
	
< > Input
Python
!cd notebook_git && git switch main
Copied
>_ Output
			
Cambiado a rama 'main'

With everything we have learned so far, we can see that what we have done has been to create a new branch, switch to it, create a new file, make a commit, and return to the main branch. Now we are going to try to delete this new branch.

	
< > Input
Python
!cd notebook_git && git branch -d branch_sin_fusion
Copied
>_ Output
			
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'.

As we can see, it tells us that branch_sin_fusion has not been merged, so it has not been deleted, and that if we are sure we want to delete it we have to run git branch -D branch_sin_fusion, so we do it to delete it

	
< > Input
Python
!cd notebook_git && git branch -D branch_sin_fusion
Copied
>_ Output
			
Eliminada la rama branch_sin_fusion (era 9506b0a).

It has now been deleted.

---

➡️ **Continue in Part 3: remote repositories, stash, and tags**, where you'll take your code to the cloud and make use of Git's advanced tools.

Continue reading

Last posts -->

Have you seen these projects?

Gymnasia

Gymnasia Gymnasia
React Native
Expo
TypeScript
FastAPI
Next.js
OpenAI
Anthropic

Mobile personal training app with AI assistant, exercise library, workout tracking, diet and body measurements

Horeca chatbot

Horeca chatbot Horeca chatbot
Python
LangChain
PostgreSQL
PGVector
React
Kubernetes
Docker
GitHub Actions

Chatbot conversational for cooks of hotels and restaurants. A cook, kitchen manager or room service of a hotel or restaurant can talk to the chatbot to get information about recipes and menus. But it also implements agents, with which it can edit or create new recipes or menus

View all projects -->
>_ Available for projects

Do you have an AI project?

Let's talk.

maximofn@gmail.com

Machine Learning and AI specialist. I develop solutions with generative AI, intelligent agents and custom models.

Do you want to watch any talk?

Last talks -->

Do you want to improve with these tips?

Last tips -->

Use this locally

Hugging Face spaces allow us to run models with very simple demos, but what if the demo breaks? Or if the user deletes it? That's why I've created docker containers with some interesting spaces, to be able to use them locally, whatever happens. In fact, if you click on any project view button, it may take you to a space that doesn't work.

Flow edit

Flow edit Flow edit

FLUX.1-RealismLora

FLUX.1-RealismLora FLUX.1-RealismLora
View all containers -->
>_ Available for projects

Do you have an AI project?

Let's talk.

maximofn@gmail.com

Machine Learning and AI specialist. I develop solutions with generative AI, intelligent agents and custom models.

Do you want to train your model with these datasets?

short-jokes-dataset

HuggingFace

Dataset with jokes in English

Use: Fine-tuning text generation models for humor

231K rows 2 columns 45 MB
View on HuggingFace →

opus100

HuggingFace

Dataset with translations from English to Spanish

Use: Training English-Spanish translation models

1M rows 2 columns 210 MB
View on HuggingFace →

netflix_titles

HuggingFace

Dataset with Netflix movies and series

Use: Netflix catalog analysis and recommendation systems

8.8K rows 12 columns 3.5 MB
View on HuggingFace →
View more datasets -->