Git (2/3): ramos

Git (2/3): ramos

Na primeira parte aprendemos a fazer o controle de versões de um projeto localmente: as áreas do git, a configuração inicial e o fluxo de commits. Neste segundo capítulo, vamos nos aprofundar nas **branches**: como criá-las, mover-se entre elas, mesclá-las e resolver conflitos. 🎸

Aviso: Este post foi traduzido para o português usando um modelo de tradução automática. Por favor, me avise se encontrar algum erro.

📚 **Esta entrada faz parte da série _Guia de Git_**, dividida em três capítulos que se leem em ordem:

> * Parte 1: Controle de versões em local

* 👉 **Parte 2: Ramos**

* Parte 3: Repositórios remotos, stash e tags

Ramoslink image 39

branches

O uso de branches é muito útil, já que podemos começar a desenvolver uma nova funcionalidade sem ter que modificar o código original

Criar uma branch (git branch <branch name>)link image 40

**Nota**: Antes de tudo, é preciso dizer que, para criar uma branch em um repositório, deve haver pelo menos um commit; se tentar criar uma branch antes de fazer o primeiro commit, receberemos um erro

Para criar uma ramificação utilizamos o comando git branch <nombre de la rama>

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

Listar ramificações (git branch)link image 41

Criámos a nossa primeira branch; podemos verificar todas as branches que temos criadas escrevendo apenas git branch

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

Além de listar, ele nos diz com um asterisco * em qual branch estamos, neste caso na branch master

Renomear branches, adeus à branch master (git branch -m <old name> <new name>)link image 42

Historicamente, no git a rama principal era chamada master, mas isso tem conotações históricas ruins por causa do conceito de master-slave (mestre-escravo), devido ao sofrimento que muitas pessoas sofreram, por isso agora costuma-se nomear a rama principal como main, então para mudar o nome usamos git branch -m master main

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

Listamos os ramos

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

Como vemos, conseguimos mudar o nome do branch principal de master para main

Mudar de branch (git switch <branch>)link image 43

Se quisermos mudar de branch, basta escrever git switch <nome da branch>

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

Vamos ver em que branch estamos com git branch

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

Como vemos, mudamos para a branch new_branch

Se quisermos criar e mudar de branch em um único comando, podemos usar git switch -c <nome do branch>

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

Vamos ver em que ramo estamos

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

Criamos e mudamos de branch com um único comando

Obter a branch em que estamos (git branch --show-current)link image 44

Como vimos até agora com git branch, podemos obter uma lista de todos os branches e também ver em qual estamos atualmente, mas no caso em que tenhamos uma grande quantidade de branches, o que pode acontecer em uma equipe de trabalho com muita gente, é bom obter o branch atual sem obter uma lista de todos; para isso usamos git branch --show-current

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

Lista de branches mais recentes (git branch --sort=-committerdate)link image 45

No caso de ter muitos ramos, talvez nos interesse saber quais são os mais recentes para ver quais foram os últimos criados e onde deve estar o que há de mais recente em desenvolvimento. Para isso usamos git branch --sort=-committerdate

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

Como vemos, ele as ordenou em ordem inversa ao momento em que as criamos

O comando preterido git checkoutlink image 46

Até há algum tempo, o comando para criar branches e alternar entre elas era git checkout, mas esse comando não apenas faz isso, como também restaura o diretório de trabalho. Porém, isso vai contra a filosofia do Linux, por isso foram criados os comandos git branch, git switch e git restore para dividir essa funcionalidade

Mesclando branches (git merge)link image 47

Como dissemos, criar branches é muito útil para desenvolver novas funcionalidades sem afetar o restante da equipe. Mas, quando elas estão prontas, é preciso levá-las para a branch principal; para isso utilizamos o comando git merge <rama>

**Importante**: Temos que estar na rama que vai adotar as alterações, ou seja, se quisermos fundir as alterações realizadas na rama new_branch2 na rama main, primeiro temos que nos assegurar de estar na rama main

Primeiro, verificamos em que ramo estamos

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

Eliminamos arquivo7.py

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

Fazemos um commit com as mudanças

	
< > 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

Se fizermos um ls, vemos que archivo7.py já não está

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

Criamos um novo arquivo e fazemos um 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

Fizemos dois novos commits nesta branch, vamos vê-lo com 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

Mudamos para a ramificação principal

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

Se agora fizermos novamente ls, veremos que archivo7.py sim está presente

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

Fundimos os ramos, trazemos as alterações de new_branch2 para 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

Fazemos um 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

Vemos que ao fazer o merge não é necessário fazer nenhum commit, vejamos com um git log o que aconteceu

	
< > 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

Vemos que o commit da branch new_branch2 foi incorporado a esta branch

Avanço rápidolink image 48

Neste caso, criamos uma nova branch, não tocamos na principal e apenas modificamos a nova, realizando vários commits. Portanto, ao unir a nova com a principal, todos os commits que foram feitos na nova serão vistos. Para realizar esse tipo de merge, escrevemos 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;

Vamos primeiro verificar se estamos na branch principal

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

Criamos uma nova branch

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

Mudamos para ela

	
< > 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

Vamos ver o 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

Certo, estamos em um branch criado a partir do principal com todo o seu histórico de logs, fazemos dois novos 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

Fazemos um novo log para ver que, nesta nova branch, foram criados

	
< > 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

Como vemos, os dois últimos commits são os que criamos e podemos verificar que esses commits não estão na branch principal (para isso, especifico que seja feito sobre a branch main)

	
< > 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

Agora vamos para a branch main para fazer o merge

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

Por fim, fazemos o merge do tipo fast forward

	
< > 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

O merge foi feito, vamos ver o que aconteceu com o log na branch main

	
< > 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

Como se vê, foram mantidos os dois commits feitos na rama branch_fast_forward

Sem fast-forward ou merge verdadeirolink image 49

Neste caso, criamos uma nova branch, não tocamos na principal e modificamos a nova, realizando vários commits. Em seguida, fazemos um commit na principal. Portanto, ao unir a nova na principal, será visto um único commit na nova. Para fazer esse tipo de merge, escrevemos 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;

Vamos primeiro verificar se estamos na ramificação principal

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

Criamos uma nova branch

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

Trocamos para ela

	
< > 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

Certo, estamos em uma branch criada a partir da principal com todo o seu histórico de logs, fazemos dois novos 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

Fazemos um novo log para ver o que nesta nova ramificação foi criado

	
< > 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

Como vemos, os dois últimos commits são os que criamos e podemos verificar que esses commits não estão na branch principal (para isso especifico que seja feito sobre a branch main)

	
< > 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

Agora vamos para a branch main

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

Criamos um novo 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

Por último, fazemos o merge do tipo não fast forward

	
< > 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

Como vemos, abre o editor para introduzir uma mensagem de commit e uma mensagem por defeito. Aceitamos a mensagem e vemos o que aconteceu

	
< > 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

Como vemos, foi criado um novo commit com as alterações da nova branch na branch principal

Esmagamentolink image 50

Nesse tipo de fusão, todos os commits de uma nova branch são reunidos em um único commit na branch principal; para isso, escrevemos git merge <rama> --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;

Vamos primeiro verificar se estamos na branch principal

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

Criamos um novo branch

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

Mudamos para ela

	
< > 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

Vamos ver o 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

Ok, estamos em uma ramificação criada a partir da principal com todo o seu histórico de logs, fazemos três novos 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

Fazemos um novo log para ver que nesta nova branch foram criados

	
< > 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

Como vemos, os três últimos commits são os que criamos e podemos comprovar que esses commits não estão na branch principal (para isso especifico que o faça sobre a branch main)

	
< > 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

Agora vamos para a ramificação main para fazer o merge

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

Fazemos o merge do tipo squash

	
< > 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

A mesclagem foi feita, vamos ver o que aconteceu com o log na branch main

	
< > 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

As alterações feitas no branch não aparecem, vamos fazer um 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

Vemos que temos que fazer o commit com a fusão. Isso é porque o Git não sabe que mensagem colocar e deixa para nós fazermos isso, de modo que o fazemos

	
< > 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

Vamos fazer novamente um 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

Conflitos ao mesclarlink image 51

Quando ramificações são mescladas, pode acontecer de um mesmo arquivo ter sido modificado em várias ramificações. Isso pode ser normal em projetos em que várias pessoas estão desenvolvendo, então vamos ver como resolver isso.

Criamos um conflitolink image 52

Verificamos que estamos na branch main

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

Criamos um novo branch a partir desta

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

Adicionamos uma linha ao arquivo archivo1.py, lembramos que estamos na branch main

	
< > 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')

arquivo1.py está modificado, fazemos um 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(+)

Agora vamos para a branch rama_con_conflicto e adicionamos uma nova linha a 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')

Como vemos, arquivo1.py não é igual na rama main e na rama rama_con_conflicto. Fazemos um commit com a modificação de arquivo1.py na rama rama_con_conflicto

	
< > 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(+)

Voltamos para a branch main

	
< > 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

Fazemos um merge da rama rama_con_conflicto

	
< > 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.

Ao fazer o merge, ele já nos avisa que há um conflito em archivo1.py e que não foi possível fazer o merge. Fazemos um 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")

Ele nos diz a mesma coisa, mas nos dá mais informações; nos diz que podemos abortar a fusão com git merge --abort. Mas, em vez disso, vamos resolvê-lo

Resolver um conflitolink image 53

No nosso caso, sabemos onde está o problema, mas, caso não soubéssemos, por meio de git diff podemos encontrar o problema

	
< > 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 está nos dizendo que o problema está na última linha. A versão do arquivo da branch HEAD (na qual estamos atualmente main) tem print('rama main') na última linha, enquanto a versão da branch rama_con_conflicto tem print('rama rama_con_conflicto') na última linha. Portanto, é preciso abrir o arquivo com qualquer editor e resolver isso.

Depois de editar o arquivo com meu editor de código, removi todas as linhas extras e ele ficou assim

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

Agora fazemos um git status para ver o que temos que fazer

	
< > 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")

Diz-nos que temos o arquivo archivo1.py com modificações, por isso o adicionamos à área de staged e depois fazemos um 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

Voltamos a fazer um git status para ver se foi resolvido

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

Parece que foi resolvido, fazemos um git log para verificá-lo

	
< > 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

Eliminar ramoslink image 54

A ideia de um branch é criá-lo para desenvolver uma nova funcionalidade ou característica de forma que não afete o restante da equipe de desenvolvimento. Portanto, quando isso é alcançado, ele é mesclado com o branch principal. Assim, depois da mesclagem, não faz mais sentido manter o branch, então ele precisa ser excluído, mas isso será feito de maneira diferente se o branch tiver sido mesclado ou não com o principal.

Excluir ramos que foram mesclados (git branch -d <ramo>)link image 55

Para eliminar um ramo sobre o qual já foi feita a fusão com o principal, basta fazer git branch -d <ramo> ou git branch --delete <ramo>. Vamos eliminar o último ramo que criamos e fundimos

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

Se agora listarmos todos os ramos, podemos ver que já não teremos 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

Remover branches que não foram mescladas com a branch principal (git branch -D <branch>)link image 56

Se tentarmos fazer o mesmo que antes com uma ramificação que nunca foi mesclada com outra, obteremos um erro

Vamos criar uma nova branch, vamos fazer um commit e não vamos mesclá-la

	
< > 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'

Com tudo o que aprendemos até agora, podemos ver que o que fizemos foi criar uma nova ramificação, mudar para ela, criar um novo arquivo, fazer um commit e voltar para a ramificação main. Agora vamos tentar eliminar esta nova ramificação

	
< > 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'.

Como vemos, ele nos diz que branch_sin_fusion não foi mesclada, por isso não foi eliminada, e que, se temos certeza de que queremos eliminá-la, precisamos fazer git branch -D branch_sin_fusion, então fazemos isso para eliminá-la

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

Agora sim foi eliminado

---

➡️ **Continua na Parte 3: repositorios remotos, stash y tags**, onde levarás o teu código para a nuvem e aproveitarás as ferramentas avançadas do Git.

Continuar lendo

Últimos posts -->

Você viu esses projetos?

Gymnasia

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

Aplicativo móvel de treino pessoal com assistente de IA, biblioteca de exercícios, acompanhamento de rotinas, dieta e medidas corporais

Horeca chatbot

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

Chatbot conversacional para cozinheiros de hotéis e restaurantes. Um cozinheiro, gerente de cozinha ou serviço de quarto de um hotel ou restaurante pode falar com o chatbot para obter informações sobre receitas e menus. Mas também implementa agentes, com os quais pode editar ou criar novas receitas ou menus

Naviground

Naviground Naviground
Ver todos os projetos -->
>_ Disponível para projetos

Tem um projeto com IA?

Vamos conversar.

maximofn@gmail.com

Especialista em Machine Learning e Inteligência Artificial. Desenvolvo soluções com IA generativa, agentes inteligentes e modelos personalizados.

Quer assistir alguma palestra?

Últimas palestras -->

Quer melhorar com essas dicas?

Últimos tips -->

Use isso localmente

Os espaços do Hugging Face nos permitem executar modelos com demos muito simples, mas e se a demo quebrar? Ou se o usuário a deletar? Por isso, criei contêineres docker com alguns espaços interessantes, para poder usá-los localmente, aconteça o que acontecer. Na verdade, se você clicar em qualquer botão de visualização de projeto, ele pode levá-lo a um espaço que não funciona.

Flow edit

Flow edit Flow edit

Edite imagens com este modelo de Flow. Baseado em SD3 ou FLUX, você pode editar qualquer imagem e gerar novas

FLUX.1-RealismLora

FLUX.1-RealismLora FLUX.1-RealismLora
Ver todos os contêineres -->
>_ Disponível para projetos

Tem um projeto com IA?

Vamos conversar.

maximofn@gmail.com

Especialista em Machine Learning e Inteligência Artificial. Desenvolvo soluções com IA generativa, agentes inteligentes e modelos personalizados.

Você quer treinar seu modelo com esses datasets?

short-jokes-dataset

HuggingFace

Dataset com piadas em inglês

Uso: Fine-tuning de modelos de geração de texto humorístico

231K linhas 2 colunas 45 MB
Ver no HuggingFace →

opus100

HuggingFace

Dataset com traduções de inglês para espanhol

Uso: Treinamento de modelos de tradução inglês-espanhol

1M linhas 2 colunas 210 MB
Ver no HuggingFace →

netflix_titles

HuggingFace

Dataset com filmes e séries da Netflix

Uso: Análise de catálogo Netflix e sistemas de recomendação

8.8K linhas 12 colunas 3.5 MB
Ver no HuggingFace →
Ver mais datasets -->