├── 0_style.md ├── 1_ro.md ├── 1_ro_exerc.md ├── 2_branch.md ├── 2_branch_exerc.md ├── 3_pick_stash.md ├── 3_pick_stash_exerc.md ├── 4_rebase.md ├── 4_rebase_exerc.md ├── 5_hunk.md ├── 5_hunk_exerc.md ├── 6_extra.md ├── 6_extra_exerc.md └── README.md /0_style.md: -------------------------------------------------------------------------------- 1 | Uma mensagem de commit satisfaz as regras de estilo propostas quando: 2 | 3 | - Separa o assunto do corpo c/ uma linha em branco 4 | - Limita a linha do assunto em 50 caracteres 5 | - Deixa o 1o caractere do assunto em maiúsculo 6 | - Não termina o assunto com um ponto 7 | - Usa 3a pessoa (presente indicativo) no assunto 8 | - Faz o corpo ter no máximo 72 caracteres/linha 9 | - Usa o corpo para explicar o que/porque/como 10 | 11 | Além disso, um tal commit: 12 | 13 | - Utiliza o imperativo no inglês 14 | - Separa partes dos nomes das branches c/ - ou / 15 | - Não quebra linhas no corpo para conteúdo gerado 16 | - Prefere explicações ao invés de links externos 17 | 18 | - PS: Pode iniciar com um separador de contexto 19 | 20 | O contextos pode ser uma parte do projeto 21 | (e.g. um comando do CLI, uma funcionalidade de uma API) 22 | ou uma atividade realizada 23 | (limpeza, refatoração, estilo, bugfix, etc.) 24 | 25 | *Dica*: 26 | Para conjugar o verbo, 27 | pense que está implícito o texto "Este commit" 28 | no início da mensagem do commit, 29 | assim o texto "Apaga o código redundante" 30 | seria lido como "Este commit apaga o código redundante". 31 | -------------------------------------------------------------------------------- /1_ro.md: -------------------------------------------------------------------------------- 1 | # Investigação de um repositório (somente leitura) 2 | 3 | - Metadados 4 | - Mensagem de commit (título e corpo) 5 | - 2 UNIX Timestamps (autoria e commit) inteiros (precisão: segundos) 6 | - Nome/e-mail do autor 7 | - Nome/e-mail do committer 8 | - Parents (commits "pais/mães") 9 | - Anotações 10 | - Conteúdo do arquivos 11 | - Deltas? Não! 12 | - SHA1 13 | - refs 14 | - HEAD 15 | - branches 16 | - tags 17 | - `^` / `~` 18 | - *Detached* HEAD 19 | - `.git/HEAD` 20 | - remote-tracking 21 | 22 | ``` 23 | gitk 24 | --all 25 | ``` 26 | 27 | ``` 28 | git log 29 | -n 30 | --all 31 | --oneline 32 | --decorate 33 | --graph 34 | --format 35 | %h 36 | %p 37 | --follow 38 | --full-history 39 | --grep 40 | ``` 41 | 42 | ``` 43 | git blame 44 | ``` 45 | 46 | ``` 47 | git show 48 | --stat 49 | --numstat 50 | ``` 51 | 52 | ``` 53 | git diff 54 | --cached (ou --staged) 55 | --name-only 56 | --check 57 | -b 58 | ``` 59 | 60 | ``` 61 | git grep 62 | -P 63 | -A 64 | -B 65 | ``` 66 | 67 | ``` 68 | git bisect 69 | start 70 | old 71 | new 72 | reset 73 | ``` 74 | -------------------------------------------------------------------------------- /1_ro_exerc.md: -------------------------------------------------------------------------------- 1 | # Exercício 2 | 3 | O py.test é um pacote em Python 4 | voltado à realização de testes automatizados 5 | que possui uma arquitetura baseada em introspecção e metaprogramação, 6 | além de muitos plugins. 7 | Alguns desses plugins são incluídos com o próprio py.test, 8 | tais como: 9 | 10 | - `monkeypatch`: para possibilitar mocks isoladamente entre testes; 11 | - `recwarn`: para auxiliar no registro/captura de warnings; 12 | - `tmpdir`: criação de diretórios/arquivos temporários; 13 | - `doctest`: integração com o doctest, módulo built-in 14 | voltado ao uso de exemplos de documentação como testes. 15 | 16 | Para realizar o exercício, selecione qualquer um desses 4 plugins. 17 | O código do py.test está em , 18 | e o código do plugin está no módulo homônimo 19 | no diretório/package `_pytest` do repositório. 20 | 21 | Questões sobre o py.test e o plugin escolhido 22 | (`monkeypatch`, `recwarn`, `tmpdir` ou `doctest`): 23 | 24 | 1. Quando o plugin foi commitado no projeto? 25 | Nessa ocasião, quantos arquivos foram afetados? 26 | 27 | 2. Quem são os desenvolvedores envolvidos com a versão atual do plugin? 28 | Na história do plugin, há mais alguém envolvido? 29 | 30 | 3. Avaliando o repositório inteiro do py.test, 31 | qual é o commit com o maior assunto em sua mensagem? 32 | E com o menor assunto? 33 | Se você pudesse trocar essas mensagens, o que colocaria no lugar? 34 | 35 | 4. O status de desenvolvimento exibido no PyPI para o projeto 36 | é obtido a partir de um *classifier* no setup.py. 37 | Quais commits fizeram o py.test mudar esse status? 38 | Qual foi a primeira versão estável do py.test com esse status? 39 | 40 | 5. Desde a versão 3 do py.test, quantos foram os releases? 41 | Em quais desses releases houve mudança no plugin selecionado? 42 | 43 | 6. Um DAG (*Directed Acyclic Graph*) pode ter mais de uma raiz? 44 | Quantas o repositório do py.test possui? 45 | 46 | 7. Esse repositório já esteve em SVN (Subversion), 47 | já esteve em hg (Mercurial), 48 | e em algum instante foi convertido para git. 49 | Já esteve no BitBucket , 50 | e hoje está no GitHub. 51 | O hábito de fazer referência a alguma *issue* ou *pull request* 52 | usando números com prefixo `#` nas mensagens dos commits 53 | trouxe problemas com o auto-incremento do *issue tracking system* 54 | nessa mudanças, uma dificuldade para se referir à *issue* correta. 55 | Qual a solução adotada pelo projeto para manter 56 | a informação acessível mesmo com uma eventual mudança no *remote* 57 | e no *issue tracking system*? 58 | 59 | Desafios: 60 | 61 | 8. Quantos merges há no projeto? 62 | Há algum merge com mais de 2 *parents*? 63 | 64 | 9. Quando aconteceram as transições SVN/hg/git e BitBucket/GitHub? 65 | 66 | Perguntas abertas: 67 | 68 | 10. Qual é a documentação mais importante do projeto? 69 | E a mais abraangente? 70 | 71 | 11. O que você pode dizer sobre os whitespaces 72 | e sobre estilo da mensagem de commit? 73 | -------------------------------------------------------------------------------- /2_branch.md: -------------------------------------------------------------------------------- 1 | # Branching 2 | 3 | ``` 4 | git branch 5 | -d 6 | -D 7 | -Dr 8 | --all 9 | 10 | git checkout 11 | (arquivo) 12 | -b 13 | -f 14 | --orphan 15 | 16 | git tag 17 | -d 18 | -f 19 | ``` 20 | 21 | ## Merge (mescla) 22 | 23 | - Opções de merge: 24 | - *Fast forward*: padrão, equivalente ao rebase, 25 | aplicável somente quando não houve branching; 26 | - Novo commit: ao invés de mudar a história, 27 | cria um commit com 2 *parents*. 28 | 29 | - Polêmica: 30 | 31 | 32 | O merge permite múltiplos *parents*, passados como argumentos. 33 | Algumas opções do `git merge`: 34 | 35 | - `--no-ff`: Desabilita o fast forward; 36 | - `--ff-only`: Não realiza o merge se isso for criar um novo commit; 37 | - `--allow-unrelated-histories`: Unifica árvores separadas 38 | (criadas com `git checkout --orphan`). 39 | 40 | 41 | ## Exemplo 42 | 43 | ``` 44 | cria_commit() { 45 | printf "%s\n" '#!/usr/bin/env python' \ 46 | "print('$1')" \ 47 | > "$1".py 48 | chmod +x "$1".py 49 | git add "$1".py 50 | git commit -m "Add $1.py" 51 | } 52 | 53 | git init 54 | cria_commit first 55 | cria_commit x 56 | git checkout -b alt master^ 57 | cria_commit y 58 | git checkout -b alt2 master^ 59 | cria_commit z 60 | git checkout -b alt3 master^ 61 | cria_commit w 62 | git checkout master 63 | git merge alt alt2 alt3 -m "Merge branches, x+y+z+w octupus" 64 | git checkout --orphan new 65 | git rm -f *.py 66 | cria_commit new 67 | git checkout master 68 | git merge new --allow-unrelated-histories --no-edit 69 | git branch -D alt alt2 alt3 new 70 | ``` 71 | -------------------------------------------------------------------------------- /2_branch_exerc.md: -------------------------------------------------------------------------------- 1 | # Exercício 2 | 3 | O objetivo é criar um novo repositório do zero 4 | com a seguinte estrutura: 5 | 6 | ``` 7 | @---L---M b1 8 | / 9 | / D---G---H b2 10 | / / 11 | C---F---I---K b3 12 | / 13 | A---B---E---J master 14 | ``` 15 | 16 | As letras de A a N representam commits, 17 | e o commit à esquerda é o parent do commit à direita 18 | sempre que há linhas unindo os mesmos. 19 | 20 | As branches indicadas `b1`, `b2`, `b3` e `master` 21 | apontam para o commit à direita de suas linhas. 22 | 23 | Todos os commits inserem um único arquivo 24 | com a própria letra do commit em minúsculo, 25 | e.g. `a.py` para o A, `d.py` para o D, 26 | exceto o commit @, que possui dois arquivos: `x.py` e `y.py`. 27 | 28 | Cada arquivo `.py`, se chamado, deve ter uma mensagem diferente. 29 | Guarde o resultado deste exercício, 30 | ele será utilizado em breve. 31 | 32 | 33 | ## Exercício extra 34 | 35 | O objetivo agora é 36 | criar uma nova branch `merge-mastered` sem modificar as demais, 37 | com novos commits N, O, P, Q, R e END que façam o grafo ficar da forma: 38 | 39 | ``` 40 | O---------- --------R 41 | \ / / \ 42 | N-----------P-------Q \ 43 | / / \ 44 | @---L---M \ 45 | / \ 46 | / D---G---H-------\ 47 | / / \\ 48 | C---F---I---K---------\\\ 49 | / \\\ 50 | A---B---E---J-----------------END merge-mastered 51 | ``` 52 | -------------------------------------------------------------------------------- /3_pick_stash.md: -------------------------------------------------------------------------------- 1 | # Deltas 2 | 3 | ## Aplicando mudanças 4 | 5 | ``` 6 | git cherry-pick 7 | -m 8 | ``` 9 | 10 | ## Colocando alterações em uma pilha "temporária" 11 | 12 | ``` 13 | git stash 14 | pop 15 | drop 16 | list 17 | ``` 18 | 19 | ``` 20 | refs 21 | stash 22 | stash@{1} 23 | ... 24 | ``` 25 | 26 | ## Recuperação de commits perdidos 27 | 28 | `git reflog` 29 | -------------------------------------------------------------------------------- /3_pick_stash_exerc.md: -------------------------------------------------------------------------------- 1 | # Exercício 2 | 3 | Há 14 commits no `master` do repositório 4 | , 5 | 6 | Em uma cópia local, os commits devem ser apagados com `git reset`, 7 | e a branch `origin/master` deve ser removida. 8 | 9 | O objetivo do exercício é restaurar o conteúdo dos commits, 10 | garantindo que: 11 | 12 | - O README nunca tenha sido criado 13 | - Um único commit inseriu as duas tabelas em `static/`, 14 | sem nenhuma alteração posterior 15 | - A diff entre o resultado e o `master` removido seja vazio 16 | -------------------------------------------------------------------------------- /4_rebase.md: -------------------------------------------------------------------------------- 1 | # Rebase 2 | 3 | ## Rebase interativo 4 | 5 | - Principal comando para manipulação de histórico 6 | - Resulta em uma única sequência de commits "lista ligada linear" 7 | - Casos de uso: 8 | - Trocar a ordem de commits 9 | - Remover commit na história recente 10 | - Editar commit na história recente 11 | (generalização do git commit --amend) 12 | - Inserir commit na história recente 13 | - Dividir commit (split) 14 | - "Linearizar" merges 15 | 16 | "História recente" significa a partir do último merge. 17 | Aplicar o rebase a conteúdo anterior a esse commit de merge 18 | irá linearizar a história, 19 | fazendo todos os commits terem um único *parent* 20 | 21 | ``` 22 | git rebase 23 | -i 24 | pick 25 | edit 26 | reword 27 | squash 28 | --root 29 | --continue 30 | --abort 31 | ``` 32 | 33 | 34 | ## Rebase não-interativo 35 | 36 | `git rebase --onto` 37 | -------------------------------------------------------------------------------- /4_rebase_exerc.md: -------------------------------------------------------------------------------- 1 | # Exercício 2 | 3 | Seja um projeto em git utilizando as seguintes branches: 4 | 5 | ``` 6 | @---L---M b1 7 | / 8 | / D---G---H b2 9 | / / 10 | C---F---I---K b3 11 | / 12 | A---B---E---J master 13 | ``` 14 | 15 | 1. Descreva o que você faria para obter uma branch `new-master` 16 | na qual os commits de A a M estariam em ordem alfabética, 17 | com o commit @ entre os commits K e L. 18 | Supondo que nenhum dos commits altera os mesmos trechos do código, 19 | quais commits teriam alterações, e quais seriam as alterações? 20 | 21 | 2. Admitindo que se deseja 22 | manter a história da branch `master` inalterada, 23 | descreva diferentes formas como o conteúdo das demais branches 24 | poderia ser inserido à branch `master`. 25 | 26 | 3. Suponha que o commit @ esteja fazendo duas coisas diferentes, 27 | uma no arquivos x.py e outra no y.py. 28 | Como você faria para dividir esse commit em 2, 29 | cada um contendo apenas as alterações de um dos arquivos? 30 | 31 | 4. Descreva como uma tal árvore poderia ser transformada em: 32 | 33 | ``` 34 | @L'---M'-----Z fourth-master 35 | / / 36 | C'---F'---IK'---DGH' 37 | / 38 | A---B---E---J 39 | ``` 40 | -------------------------------------------------------------------------------- /5_hunk.md: -------------------------------------------------------------------------------- 1 | # Hunk patching e resolução de conflitos 2 | 3 | *Hunks* são os blocos de alteração no código. 4 | Se você alterou uma linha no início do arquivo, 5 | três linhas adjacentes do final do arquivo e inseriu um outro arquivo, 6 | então você têm 3 hunks de mudanças. 7 | 8 | 9 | ## Patching 10 | 11 | Com o `-p` (de *patch*), é possível modificar seletivamente os hunks 12 | tanto no workspace como na área de staging: 13 | 14 | - `git add -p`: hunks de *workspace* p/ *staging*; 15 | - `git reset -p`: hunks de *commit* p/ *staging*; 16 | - `git checkout -p`: hunks de *commit* p/ *staging* e *workspace*. 17 | 18 | Opções de *patch*: 19 | 20 | - `y`: aplica o hunk (*yes*); 21 | - `n`: ignora o hunk (*no*); 22 | - `s`: tenta dividir em mais de um hunk (*split*); 23 | - `a`: aplica todos os hunks deste arquivo (*all*); 24 | - `d`: ignora todos os hunks deste arquivo (*delete*); 25 | - `q`: ignora todos os demais hunks (*quit*); 26 | - `e`: edita o hunk (*edit*). 27 | 28 | 29 | ## Conflitos 30 | 31 | Os comandos `cherry-pick`, `rebase` e `merge` 32 | podem resultar em conflito quando os diferentes caminhos 33 | possuem hunks que modificam as mesmas linhas de código, ou adjacentes. 34 | 35 | A resolução do conflito é sempre manual. Após resolver o conflito, 36 | utiliza-se o `--continue` do respectivo comando para retomar o mesmo, 37 | ou então o `--abort` para cancelar o processo em andamento, 38 | assim como foi visto com o `rebase` interativo. 39 | 40 | O conflito é marcado por regiões com blocos 41 | delimitados por `<<<<<<`, `======` e `>>>>>>`, 42 | os quais separam o hunk de cada uma das versões (HEAD e MERGE_HEAD) 43 | 44 | Podemos usar o `git diff` 45 | para visualizar a origem de cada parte depois das alterações 46 | (*3-way diff*). Ou então o próprio `git show`: 47 | 48 | - `git show :1:arquivo # Ancestral comum` 49 | - `git show :2:arquivo # HEAD` 50 | - `git show :3:arquivo # MERGE_HEAD` 51 | -------------------------------------------------------------------------------- /5_hunk_exerc.md: -------------------------------------------------------------------------------- 1 | # Exercício 2 | 3 | O repositório 4 | possui o gerador de certificados PDF da Python Sudeste 2018. 5 | 6 | O objetivo do exercício é separar os imports, 7 | dividindo os commits em que eles aparecem (split), 8 | para que a linha do import e a linha que modifica o `requirements.txt` 9 | sempre venham em um commit anterior ao do conteúdo 10 | que usa os objetos importados. 11 | -------------------------------------------------------------------------------- /6_extra.md: -------------------------------------------------------------------------------- 1 | # Extra! 2 | 3 | Há inúmeros outros comandos no git, tais como, 4 | `git submodule`, `git am`, `git format-patch`, `git svn`, etc., 5 | além de possibilidades de casos de uso. 6 | Falar de git por si só poderia resultar em um curso de um ano inteiro! 7 | 8 | Aqui consta algumas outras ações que podem ser realizadas com o git 9 | que têm a ver com o que foi exposto neste tutorial. 10 | 11 | 12 | ## Git fetch VS Merge hell 13 | 14 | Até agora, o único comando do `git` que envolveu algum servidor 15 | foi o `git clone`. 16 | Os outros comandos que se comunicam com o servidor são: 17 | 18 | - `git fetch`: atualiza todas as branches *remote-tracking* 19 | - `git push`: atualiza uma branch ou tag no servidor 20 | - `git pull`: fetch + merge, basicamente 21 | 22 | Usar o `git fetch` ao invés do `git pull` traz uma vantagem: 23 | o controle sobre suas branches locais. 24 | Você sempre pode fazer um `git merge --ff-only` depois, 25 | talvez você queira usar o `git diff` antes. 26 | 27 | Em cenários com merges desencontrados de código indo e voltando 28 | (e.g. funcionalidades sendo habilitadas/desabilitadas), 29 | o `git pull` pode ser um dos elementos que inicia o *merge hell*: 30 | situação na qual o mesmo conteúdo é aplicado diversas vezes, 31 | há commits desnecessários (do ponto de vista do fast forward), 32 | além de commits "sujando" o blame, 33 | e um visual de *Guitar Hero* (ou *Frets on Fire!*) no grafo. 34 | Merges são úteis, mas às vezes precisamos remover conteúdo 35 | ao invés de reverter. 36 | 37 | Um simples `git commit --amend` pode ajudar a evitar o vai-e-volta, 38 | a política de nunca sobreescrever (`git push -f`) o `master` 39 | não se aplica àquela branch em que apenas você está trabalhando. 40 | 41 | 42 | ## Garbage collector 43 | 44 | Se quisermos evitar que alguém recupere conteúdo de um histórico, 45 | por exemplo por ter credenciais/senhas/tokens removidos, 46 | apenas fazer o rebase do trecho pode não ser o suficiente. 47 | Se alguém souber o hash (ou ver pelo `git reflog`), 48 | poderá recuperar o commit que não faz parte da história 49 | que leva aos branches/tags e demais refs salvos. 50 | 51 | Para isso, podemos usar o `git gc`. 52 | Podemos verificar os hashes dos commits que não são referenciados 53 | com o `git fsck --unreachable`. 54 | 55 | 56 | ## Filter-branch 57 | 58 | Para modificar o e-mail de todos os commits de uma história: 59 | 60 | ``` 61 | git filter-branch 62 | --commit-filter 'GIT_AUTHOR_EMAIL=novo@email.br \ 63 | GIT_COMMITTER_EMAIL=novo@email.br \ 64 | git commit-tree "$@"' 65 | ``` 66 | 67 | Esse `git commit-tree` tem um comportamento diferente do `git commit`, 68 | por exemplo no que diz respeito ao timestamp de commit. 69 | 70 | Normalmente ninguém quer mudar o repositório inteiro, mas somente os 71 | commits que já estão atribuídos a uma pessoa em específico. 72 | 73 | ``` 74 | git filter-branch 75 | --commit-filter 'if [ "$GIT_AUTHOR_NAME" = "Someone" ] ; then \ 76 | export GIT_AUTHOR_EMAIL=novo@email.br ; \ 77 | fi ; \ 78 | if [ "$GIT_COMMITTER_NAME" = "Someone" ] ; then \ 79 | export GIT_COMMITTER_EMAIL=novo@email.br ; \ 80 | fi ; \ 81 | git commit-tree "$@"' 82 | ``` 83 | 84 | Esse é um exemplo clássico do `filter-branch`, 85 | mas não é algo nem simples, nem comum. 86 | 87 | 88 | ## Git subtree 89 | 90 | Podemos ter dois DAGs se unindo em um merge 91 | se estávamos fazendo tudo em um único repositório do git, 92 | com a separação iniciada pelo `git checkout --orphan`. 93 | 94 | Mas e se quiséssemos fazer um merge de repositórios diferentes? 95 | O comando que faz isso existe: é o `git subtree`. 96 | Foi utilizado no repositório do , 97 | unindo os commits de repositórios novos de diferentes Coding Dojos. 98 | 99 | Supondo que os diretórios "irmãos" `a` e `b` são repositórios `git`, 100 | ambos com apenas a branch `master`, 101 | e que se deseja unificá-los em `a`, 102 | com o conteúdo de `b` inserido no diretório `other`. 103 | O comando fica: 104 | 105 | `git subtree add --prefix other ../a master` 106 | -------------------------------------------------------------------------------- /6_extra_exerc.md: -------------------------------------------------------------------------------- 1 | # Exercício 2 | 3 | 1. No repositório do brutoliquido, normalize o nome do único autor, 4 | sem modificar nenhum timestamp. 5 | O que acontece ao tentar um `git pull` depois dessa mudança? 6 | 7 | 2. Os repositórios 8 | e 9 | são de duas apresentações de tutorial de Python no WTA 10 | (Workshop de Tecnologia Adaptativa). 11 | Seria possível criar um repositório `wta` contendo dois diretórios, 12 | um para cada edição do evento? Como? 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tutorial de git para quem já sabe commitar 2 | 3 | Por *Danilo J. S. Bellini* 4 | 5 | Este tutorial admite que o participante conheça 6 | a diferença entre *workspace*, área de *staging* e *commits*, 7 | e já tenha utilizado alguns recursos do git, incluindo: 8 | 9 | - `git init` 10 | - `git clone` 11 | - `git commit` 12 | - `git add` 13 | - `git reset` 14 | - `git push` 15 | - `git pull` 16 | - `git log` 17 | - `git mv` 18 | - `git rm` 19 | - `git remote` 20 | - `git config` 21 | 22 | E que já tenha sido apresentado 23 | a algum guia de estilo para mensagens de commit. 24 | O blog post 25 | 26 | condensa e justifica 7 regras de estilo de outras fontes 27 | (o *This has all been said before* possui uma referência por palavra), 28 | regras estas traduzidas/adaptadas para o português neste tutorial, 29 | e incrementadas para abranger outros cenários. 30 | 31 | Caso você não tenha o domínio sobre os fundamentos acima expostos, 32 | recomendo a leitura do capítulo *Git Basics* do livro *Pro Git*, 33 | gratuitamente disponível em 34 | em sua versão web (HTML, PDF). 35 | O link para a primeira edição em português é 36 | 37 | e o capítulo citado teve o título traduzido como *Git Essencial*. 38 | 39 | Há ainda sites que ensinam o básico de git de maneira interativa, 40 | como o . 41 | O ensina a organização em branches 42 | de forma interativa e bastante visual, 43 | algo que pode auxiliar a compreensão deste material 44 | (caso o leitor saiba inglês). 45 | Outra opção seria o jogo que mostra as 46 | estruturas internas de repositórios git em tempo real 47 | 48 | O conteúdo deste tutorial está organizado da seguinte forma: 49 | 50 | 0. [Estilo](0_style.md) 51 | 1. [Visualização](1_ro.md) 52 | 2. [Branching](2_branch.md) 53 | 3. [Commit deltas (cherry-pick / stash)](3_pick_stash.md) 54 | 4. [Rebase](4_rebase.md) 55 | 5. [Hunk patching e resolução de conflitos](5_hunk.md) 56 | 6. [Exemplos de outros assuntos](6_extra.md) 57 | --------------------------------------------------------------------------------