bsky: leandronsp.com Profile picture
nao to mais aqui (isso aqui faz mal demais ñ sei como vcs aguentam), to ali https://t.co/2lwbMg2kjv

Dec 4, 2022, 36 tweets

Entender os fundamentos de Git é necessário para quem não quer se deixar dominar pela ferramenta.

Nesta 🧵 vou tentar explicar as estruturas fundamentais do Git e como estas se relacionam com os comandos que usamos no dia-dia.

Os principais componentes do Git são:

.git/objects
.git/refs
HEAD

Começamos pelo Object database, que é basicamente onde o Git armazena todos os seus objetos.

Mas que tipo de database é este? SQL? NoSQL?

Vamos primeiro persistir um objeto utilizando o comando `git hash-object`, que:

* permite input do STDIN ou arquivo normal
* retorna uma hash SHA-1
* persiste com a opção `-w`

Podemos reparar que Git persistiu nosso objeto em .git/objects como era esperado, utilizando a hash que foi devolvida como "chave" de acesso

Se temos a hash, conseguimos resgatar o valor original do objeto?

Sim, com o comando `git cat-file`.

Temos no Git, então, um database baseado em chave-valor, com suas chaves em formato SHA-1.

O comando `cat-file` permite passar a opção `-t` que devolve o tipo do objeto, que neste caso, é um blob.

Uma vez com os blobs persistidos, como podemos agrupá-los, adicionar metadados e criar snapshots?

Precisamos promover estes blobs para um uma área de "Stage", com o comando `git update-index`

Podemos adicionar quantos blobs quisermos ao índice utilizando o `update-index`.

Mas como agrupá-los para que sejam promovidos? O Git permite criar uma "árvore" com esses blobs, através do comando `write-tree`.

Note que novos objetos foram criados, que tipos são esses objetos?

Estes objetos são do tipo "tree", que servem para agrupar diferentes blobs e inclusive outras trees quando utilizada a opção `prefix`

E se quisermos adicionar metadados a partir das trees, como por exemplo o autor do trabalho, a data e uma mensagem descritiva?

Sim, estamos falando do comando `git commit-tree`, que promove uma tree de modo a que esta possa ter metadados.

Commits também são objetos, e portanto, possuem uma chave SHA-1 para representar cada commit.

É possível também criar commits com referência a outros commits (parents).

Repare que o objeto commit tem referência para a tree e para o parent (outro commit criado a partir dele).

Como percorrer toda a "rede" de commits a partir do último commit, indo pelos parents, trees até chegar nos blobs?

O comando `git log <sha1>` faz exatamente isso, percorrendo todo o grafo e trazendo tudo de acordo com a linha do tempo.

Git é baseado em grafos.

Manipular objetos no Git é como manipular ponteiros em grafos.

Blobs representam arquivos. Trees representam conjuntos de blobs e outras trees. Commits representam metadados com referência a trees e outros commits.

Executar toda hora `git log <sha1>` não é eficiente pois temos de decorar as hashes.

Mas Git traz uma facilidade para isto, que são referências para commits, que ficam em .git/refs.

Através do comando `update-ref`, criamos referências que são basicamente commits "com nomes".

Soa familiar? Estamos falando de branches.

Mas o quê acontece se não passar argumento (sha-1) para o comando `git log`?

Como o Git sabe que a minha branch atual é a "main"?

Acertou, é aqui que entra o tal do HEAD.

Com o comando `git symbolic-ref` podemos mudar o ponteiro do HEAD, que é uma referência simbólica para a branch atual de trabalho.

O HEAD pode ser qualquer branch ou até mesmo um SHA-1 (no modo detached)

Trocar o HEAD é uma simples chamada ao `symbolic-ref`.

Neste exemplo, fico intercalando entre a branch "main" e branch "fix".

Agora que sabemos que tudo em Git é manipulação de ponteiros, vamos associar este conhecimento com os comandos do dia-dia.

Começando pelo `git add`, que basicamente é a junção do `hash-object` com `update-index`.

O `git commit` é basicamente o `write-tree` (uma vez que há objetos no index) seguido do `commit-tree`, que adiciona informações como autor, date e message.

Já o `git checkout` é literalmente o `symbolic-ref`, que muda o ponteiro (branch) do HEAD.

Com algumas opções adicionais que trazem mais versatilidade.

O comando `git reset` permite mudar o ponteiro da branch, tal como o `update-ref`.

Sem a opção `--hard`, todos os arquivos que divergem ficam em stage a espera de novo commit.

E o merge?

Bem, é super tranquilo entender. Supondo que temos uma branch main e outra "fix", onde fix está 1 commit à frente.

O merge basicamente faz move a branch main para o mesmo commit da branch fix (fast-forward).

E quando a main tem algum commit à frente da branch fix? Neste caso, não é possível fazer fast-forward.

Mas o Git é bem inteligente e trata desta forma:

* Tira snapshot do parent em comum com ambas as branches
* Tira snapshot do commit da branch de destino
* Tira snapshot do commit da branch de origem

E, por fim, cria um commit adicional de "merge". Sim, é o "three-way" merge.

Onde entra o cherry-pick? Com cherry-pick de um commit da branch main, o Git:

* move o ponteiro da branch fix
* aplica o commit da branch main como último commit na branch fix

E quando queremos aplicar as mudanças por cima de outra branch?

Entra o rebase.

Neste cenário, com rebase a partir da branch fix, basicamente:

* muda o ponteiro p/ o HEAD da main
* faz cherry-pick dos commits q sobraram
* por último muda ponteiro para o último do cherry-pick

E as branches remote?

Também são referências.

O comando `git fetch` faz download da branch do server e sincroniza com uma branch "upstream" relacionada à tracking branch (local).

Por serem branches, é possível fazer merge a partir das branches upstream com as locais.

Geralmente, por estarem sempre "atrás", merges com branches upstream são feitos no modo fast-forward.

O comando `git pull` facilita a vida, fazendo fetch + merge para nós.

"Okay, fiz meu trabalho e agora quero mandar de volta para o server. Como atualizo a branch upstream?"

Não precisa, o comando `git push`, além de atualizar o server, também sincroniza a branch tracking local com a upstream.

Assim como branches, tags também são referências com "nome".

Mas são imutáveis, ou seja, para mudar a referência de uma tag é preciso apagá-la e criar outra com mesmo nome.

Como o @coproduto destacou, nesta thread foram apresentados os comandos "plumbing", que foram os building blocks do Git, e como se relacionam com os comandos "porcelain", usados no dia-dia.

@coproduto E pra quem tiver interesse, o próprio guia oficial do Git é bem completo, se ler os primeiros capítulos já cobre muita coisa boa para dominar mais o Git.

Com destaque ao capítulo 10 que fala dos "internals" que eu trouxe à thread.

git-scm.com/book/en/v2/Get…

Share this Scrolly Tale with your friends.

A Scrolly Tale is a new way to read Twitter threads with a more visually immersive experience.
Discover more beautiful Scrolly Tales like this.

Keep scrolling