Branches no Git: intuição e realidade
(jvns.ca)- Muitas pessoas acham que o funcionamento das branches do Git não é intuitivo.
- Explica a diferença entre o modelo intuitivo mais comum sobre branches no Git e como elas são realmente representadas internamente no Git.
- Mostra que o modelo intuitivo e a forma como o Git realmente funciona estão, na prática, muito intimamente relacionados.
- Discute as limitações do modelo intuitivo e por que ele pode causar problemas.
Modelo intuitivo de branch
- Muitas pessoas pensam em uma branch por analogia a um 'galho de macieira'.
- No Git, uma branch não tem o conceito de 'pai', o que é diferente de pensar que ela se ramificou a partir de
main.
No Git, uma branch é o histórico inteiro
- No Git, uma branch não é apenas os commits ramificados, mas inclui todo o histórico anterior de commits.
- Mostra, por meio de um repositório de exemplo, que tanto
mainquantomybranchtêm 4 commits.
Branches são armazenadas como IDs de commit
- Internamente no Git, uma branch é armazenada como um pequeno arquivo de texto que contém um ID de commit.
- O commit mais recente de cada branch é registrado nesse arquivo.
- Como não existe relação de pai e filho entre branches, o Git não conhece a relação entre elas.
A intuição das pessoas normalmente não está tão errada assim
- Dizer que a intuição das pessoas sobre Git está 'errada' é um tanto tolo.
- Mesmo um modelo 'errado' pode ser útil na prática.
Rebase usa o conceito 'intuitivo' de branch
- O rebase reaplica em
mainapenas os commits da branch 'intuitiva'. - O resultado do rebase corresponde ao modelo intuitivo.
Merge também usa o conceito 'intuitivo' de branch
- O merge não copia commits, mas precisa de um commit base compartilhado.
- A merge base encontra o commit em que a branch se separou, com base no modelo intuitivo.
Pull requests do GitHub também usam a ideia intuitiva
- Ao criar no GitHub um pull request para fazer merge de
mybranchemmain, ele mostra apenas os commits da branch intuitiva.
A intuição é boa, mas tem limites
- A definição intuitiva de branch combina bem com o trabalho real no Git, mas o Git não consegue reconhecer uma branch ramificada de
maincomo algo diferente.
Trunk e branches derivadas
- As pessoas percebem
mainemybranchde forma diferente, e isso afeta a maneira como usam o Git. - O Git não distingue se uma branch é uma 'ramificação' de outra branch.
O Git pode fazer rebase 'ao contrário'
- Como o Git não informa se uma branch é uma 'ramificação' de outra, o usuário precisa saber qual branch rebasing em qual momento.
- Tanto
git rebase mainquanto o rebase inversogit rebase mybranchsão possíveis. O mesmo vale para merge.
A ausência de hierarquia entre branches no Git é um pouco estranha
- Dizer que a branch
mainnão é especial vem do fato de que o Git não reconhece relações entre branches. - Existem relações entre as branches, mas o Git não sabe de nada.
A UI de branches do Git também é estranha
- Quando você quer ver apenas os commits 'ramificados', a forma de usar
git logegit diffé diferente.
No GitHub, a branch padrão é especial
- O GitHub tem uma 'branch padrão', e ela exerce um papel especial.
Opinião do GN⁺
O ponto mais importante deste texto é entender a diferença entre a compreensão intuitiva que as pessoas têm sobre branches no Git e a forma como o Git realmente funciona. Este texto deve ajudar engenheiros de software iniciantes a entender melhor o conceito de branches no Git e a usá-las de maneira mais eficaz. É interessante e útil observar como o modelo intuitivo de branch corresponde ao trabalho real e como o Git não trata as relações entre branches.
1 comentários
Comentários do Hacker News
git reset --hardegit stashpara manipular mudanças e ponteiros de branch. Para desfazer um merge errado, usogit reset --hard <último commit antes do merge>; para aplicar pequenas alterações de um branch local no branch principal, usogit stash, depois faço checkout do branch principal e aplico comgit stash apply.mainé especial, mas ferramentas como o GitLab oferecem recursos de branch protegido para reduzir erros. A ideia de branches "pai" e "filho" pode ser realmente interessante, e seria preciso oferecer suporte a vários branches "pai" para branches de suporte de longo prazo.git addegit commit. Ele ajuda a visualizar os branches durante a leitura.git merge my-branchfaz merge demy-branchno branch atual, egit rebase my-branchfaz rebase do branch atual sobremy-branch.head) tivesse uma "cauda" que apontasse para o commit-base onde aquele branch começou. Como branches são rebaseados com frequência, às vezes é preciso pensar sobre onde eles começaram. Seria mais conveniente se o Git informasse que o commit-base pertence aomain.git range-diff. Essa ferramenta compara dois intervalos, comomain..previousemain..current.