Além do básico - como GIT funciona e como usá-lo da maneira correta!

Um guia de conhecimento e boas práticas para o usuário profissional de GIT

Itanú Romero
14 min readJun 19, 2022
Photo by RealToughCandy.com: https://www.pexels.com/photo/man-love-people-woman-11035539/

Seja bem vindo(a) ao guia do profissional para o uso de GIT, um material pra você se aprofundar nos estudos, ou mesmo pegar umas dicas de como ser melhor no seu uso diário.

Vou apresentar o conteúdo em 4 passos

  1. Conhecimento básico:
    Entendendo a ferramenta e aprendendo a usar no dia-a-dia de forma simples
  2. Conhecimento avançado:
    Entendendo como a ferramenta funciona por baixo dos panos, em seus detalhes
  3. Como usar da melhor forma:
    Boas práticas e dicas de produtividade para usuários profissionais
  4. Dicionário:
    Comandos importantes pra relembrar quando precisar

Vamos lá!

Início
Photo by Braden Collum on Unsplash

Contextualizando

O Git é uma ferramenta essencial no desenvolvimento de software, ele foi criado para gerenciar versões de código, assim a pessoa desenvolvedora consegue guardar o código antigo sem necessariamente manter vários arquivos, ficando fácil se perder dentro das versões, como:

Em vez disso, ele nos permite manter apenas o arquivo principal, guardando todas as mudanças em versões anteriores deste mesmo arquivo.

Instalação

Para instalar o GIT no seu computador, faça a leitura da documentação do site oficial Instalando o Git.

Lembre-se sempre de usar o site oficial: Git

Manual de aprendizados rápidos

Não tem interesse em aprender a fundo? Siga esses passos rápidos para ver o resumo.
Caso seu interesse seja embasar melhor seu conhecimento e aprender as boas práticas de utilização, siga para o próximo tópico.

Git funciona como um software de controle de versão

Versionamento é o controle de versões, e isso é importante para o desenvolvimento de software pois sempre que editamos um arquivo ou fazemos alguma modificação no nosso código, criamos subjetivamente uma nova “versão” do código, e em muitos casos, substituímos essa nova versão pela antiga. Porém, dessa forma, a versão anterior é perdida.

Antigamente, era feita uma cópia do arquivo ou projeto, quando se criava uma nova versão, pois assim se obtinha o acompanhamento das mudanças, porém por motivos óbvios de praticidade e armazenamento, sem contar com a dificuldade de tratar de versionar softwares criados por equipes inteiras, surgiram os sistemas de versionamento.

Basicamente, ele agrega todas as mudanças registradas desde o início do rastreamento do arquivo, digamos que toda linha que muda em seu código, ele salva essa nova versão do projeto, mas sempre deixando uma forma de retornar às versões antigas do seu código sem manter arquivos físicos de backup.

O git é distribuído

Diferente de outros sistemas de versionamento, o Git permite que existam 2 tipos de repositórios, ou lugar de armazenamento de arquivos e dados, um local e um remoto.

Todas as mudanças são primeiro adicionadas ao repositório local (commit) e depois enviadas ao repositório remoto (push), e também podemos buscar essas informações do repositório remoto (pull).

Vantagens

  • Controle de histórico
  • Trabalho em equipe facilitado
  • Ramificações do projeto
  • Segurança
  • Organização

Mas e o Github?

Quase sempre aparecem juntos, mas é importante destacar que são coisas distintas.

Github funciona como um repositório remoto para o Git, mas não é a única forma de fazê-lo, pois o repositório remoto pode muito bem ser um servidor quanto um serviço como o Github.

Ele também abre opções visuais para a gerência de repositórios Git, além de servir como uma plataforma social para programadores, onde podemos criar repositórios, participar de projetos, etc.

Git Basics

Então, digamos que você já instalou o GIT e já tem uma conta no Github e agora quer criar um repositório, existem duas formas: criando pelo Github ou localmente. Para criar via github, siga os passos dessa documentação: Criar um repositório — GitHub Docs.

Nesse caso, teremos que clonar o repositório remoto no nosso ambiente local com

git clone git@github.com:NomeUsuário/nome-repo.git

Localmente, ou via Git em linhas de comando, pode ser feito com os seguintes passos, depois que o terminal foi aberto.

Navegue até a pasta

cd minha/pasta

Inicialize o repositório

git init

Verifique o status do git

git status

Crie arquivos, e logo adicione eles ao rastreamento do git com

git add nomeDoArquivo.txt

Verifique o status do git, que deverá conter agora o arquivo adicionado

git status

E adicione esse arquivo com as mudanças atuais no repositório local

git commit -m “the file nameDoArquivo.txt has been created and added to the repository”

Agora devemos criar o repositório remotamente de acordo com a documentação oficial Criar um repositório — GitHub Docs.

E adicionar o ponto remoto com

git remote add <nome_identificador> <url_do_repositório>

Ou, formatado

git remote add origin https://github.com/usuario/repo.git

Agora, basta enviar as nossas mudanças, fazendo o envio dos nossos commits, que serão implementados no repositório remoto, usando

git push <nome_identificador>

Ou, formatado

git push origin

E pra finalizar, quando houverem novas mudanças no repositório remoto é sempre bom trazer essas mudanças para o seu repositório local, assim tendo sempre a versão mais atualizada do código

git pull origin

Entendendo como ele funciona a fundo

Vamos entender como ele funciona passo a passo!

Meio
Photo by Andrea Leopardi on Unsplash

Encontrando e aplicando as diferenças

Basicamente, ele funciona como um comparador de arquivos, porém para a melhora de performance, utiliza o algoritmo Longest Common Subsequence que permite que ele consiga acessar cada linha do seu código e verificar se houve uma mudança ou não, porém não dependendo apenas da linha em si mas do contexto que foi apresentado.

Quando isso é executado, temos como resultado um patch, que representa a maior subsequência comum entre os dois arquivos, juntamente com metadados, como qual linha, arquivo, etc. E o processo de criação desse patch pode ser mais conhecido como commit, que possui o patch com as modificações e um cabeçalho que ajuda a identificá-lo.

O patch é formado por Chunks ou Hunks, que são acumulados de código.

Quando é necessário enviar as modificações de um repositório a outro, são enviados esses patches no lugar dos arquivos reais, e eles podem ser comumente visualizados pelas ferramentas do Github, como no exemplo abaixo:

Dessa forma não é necessário enviar todos os arquivos completos, porém o outro colaborador deve possuir alguma versão do repositório para que esse patch possa ser aplicado.

Para melhoria de performance, também é aplicado o conceito de Tarball, que une todos os arquivos em uma coisa só, e criando uma compressão sem perda de dados.

Como exemplo, podemos dizer que enviamos uma Tarball para alguém quando ele precisa copiar esse repositório, e enviamos patches para atualizar as versões em seu local de trabalho. E normalmente esses patches são zipados com o nome de deltas, por isso quando efetuar um git pull, buscando as novas atualizações do repositório é comum ver o seguinte retorno:

Ramificações

Uma branch, ou galho, pode ser considerada uma duplicata do diretório do projeto, onde serão feitas as alterações, e após gerar os patches contendo as atualizações, pode-se retornar à branch principal e aplicar os patches com as modificações já testadas. Servem para a modificação de arquivos e projetos sem a perda da consistência da ramificação principal, e também abrindo para diversos ciclos de desenvolvimento rodarem em paralelo, pois podemos ter uma branch para cada modificação e elas serão aplicadas na branch principal apenas quando estiverem prontas. Esse processo de aplicação de patches criados de uma branch a outra é chamado de merge.

Existem ferramentas para ajudar no merge dessas branches, como as Pull requests (PR), que facilitam a entrada de modificações dentro de uma branch principal, com maior controle e segurança, já que é possível bloquear essa PR se ela não for aprovada por outros mantenedores do código ou não passarem nos testes.

Algo semelhante são os Forks, porém essa ação é como uma cópia de um repositório completo com o objetivo de ser mergeado no futuro, muito usado por projetos open-source.

O sistema de merge foi um grande diferencial do GIT, pois realiza essa ação de uma melhor forma. Deixando com que todos os usuários tenham acesso de commit em uma branch separada, onde a branch principal pode ser protegida de commits e pushes sem um pull request.

Logo, um processo comum para realizar uma alteração em um repositório é:

  1. Trazer o repositório para sua máquina com
    git clone
  2. Após fazer alterações, marque-as e prepare-as para realizar o patch com
    git add
  3. Empacote as modificações e aplique metadados como resumo, identificação, criador, etc. Com
    git commit

A identificação de um commit é um hash para evitar o corrompimento de patches, pois se apenas 1 caractere estiver diferente do esperado, o hash é diferente e pode ser encontrado o arquivo ou patch corrompido de forma rápida.

Cada commit criado é ligado ao anterior, e uma branch é um desvio nesse grafo para outras ramificações.

Diretório .git

As mudanças reais estarão contidas neste repositório. Após o git init para inicialização desse repositório e o git add & git commit para adicionar todos os arquivos dentro do diretório, tudo estará guardado no .git/, logo, contanto que este diretório não seja apagado, todas as suas alterações estarão salvas.

Dentro dele, ficarão objetos que representam seus arquivos, que podem ser vistos mais a fundo na documentação oficial do GIT. Basicamente o .git é um banco de dados das modificações e de seus arquivos dentro desse repositório.

Objetos

Como já dito anteriormente, o diretório .git pode ser considerado um banco de dados, e ele basicamente possui estes objetos:

  1. Blob
    Texto puro, é um arquivo que corresponde a um arquivo original no repositório, quando realizamos o comando git add o sistema cria um blob para esse arquivo/diretório adicionado.
  2. Tree
    É a representação de um diretório, responsável por guardar referências para outros diretórios e arquivos, aponta para diversos blobs
  3. Commit
    Aponta para um tree, e referente às mudanças de um tree para o outro (nos seus blobs) é capaz de mostrar as mudanças, além de adicionar metadados.

Com esses dados, alguém pode se perguntar o que são as branchs, na realidade, elas não existem. Elas são referências que apontam para objetos e conjuntos de objetos, como os commits, esses ponteiros são armazenados em .git/refs/heads/<nome>, e podem ser atualizadas.

Já as tags, são como as branchs, um nome apontando para um commit, possui metadados como mensagem e data e podem representar um release de uma versão do sistema.

Comandos de baixo nível

Também conhecidos como Plumbing ou encanamento, são comandos que juntos compõem os comandos mais conhecidos, chamados de Porcelain. Isso melhora a usabilidade, porém entender como esses comandos de baixo nível funcionam, nos faz entender melhor como usar o GIT.

Escolha inteligente com three-way merge

Para evitar conflitos de merge, algo que pode ser deveras trabalhoso, o GIT compara versões conflitantes com o último commit antes da criação da nova branch, logo, as 2 versões conflitantes são comparadas com a versão original que estava antes das modificações feitas pelas duas versões. Assim, com essa base na versão original, pode-se escolher a modificação correta automaticamente, dependendo de qual realmente foi a última versão modificada. Assim que ocorre o merge, é criado um commit para o merge que se torna a versão base a partir desse momento.

Diferenças entre repositórios criados e repositórios clonados

A diferença é próxima a 0, na verdade tudo que diferencia é que quando clonamos um repositório, ele já vem com as configurações de repositório remoto criadas, já em um repositório criado com git init, devemos adicionar o remote com

git remote add <nome_identificador> <url_do_repositório>

Logo, Github é apenas uma forma de utilizar o Git com um repositório remoto, pois podemos apontar o repositório remoto para qualquer local, até mesmo na sua própria máquina.

Finalmente
Photo by Joshua Hoehne on Unsplash

Usando corretamente

Agora que já conversamos um pouco sobre o motivo pelo qual o Git existe e como ele funciona por debaixo dos planos, vamos aprender algumas boas práticas em sua utilização!

Digamos que o GIT é como uma máquina do tempo, cada commit grava o estado de seu código na linha do tempo e quando você quiser pode voltar para esses momentos.

Os estágios do GIT

Para entender melhor o próximo tópico, é importante saber que o GIT divide o trabalho em alguns estágios.

  • Working Tree
    Diretório normal de trabalho com todos os arquivos, aqui estão todos os arquivos que não são ignorados pelo GIT.
    Arquivos e pastas ignoradas não participam nem deste estágio nem dos outros.
  • Stage
    Local onde ficam adicionadas as modificações aguardando pelo commit, apenas as modificações neste bloco serão colocadas no commit.
  • Commit
    Dados que estavam no stage agora são empacotados juntamente com a mensagem de commit e outros metadados.

Melhorando seus commits

Leve as mensagens de commit a sério

Seja específico, se essa vai ser a sua dica para saber quando viajar no tempo e encontrar essa mudança específica no seu código, precisamos saber exatamente o que foi modificado sem perder tempo olhando diff por diff, então evite colocar coisas como “refatorando” ou “hotfix”.

Fazendo a mensagem perfeita

Busque sempre criar um commit sem a flag -m, pois assim é possível adicionar mais detalhes.

No arquivo de commit adicione o subject sendo o resumo representando esse grupo de diferenças de forma concisa e um body com uma explicação detalhada, que pode conter:

  • Contexto
  • Motivo
  • O que está diferente
  • Cuidados, dicas e particularidades

Outras dicas, por Ruan Brandão.

Errei a mensagem de commit, e agora?

Imagine que você foi escrever a mensagem de commit e saiu algum erro de digitação ou no seu inglês… A maioria das pessoas aceitaria o erro e seguiria em frente rezando pra ninguém notar, mas como estamos falando de boas práticas no GIT, vamos ver uma forma simples de ajustar a mensagem do seu último commit, basta fazer (leia “ — ” como “--”, pois o Medium modifica automaticamente):

git commit -m “a sua mensagem corrigida” amend

Esqueceu de adicionar uma mudança em seu último commit?

Pode ser que após terminar de realizar modificações e commitar, apareça um novo arquivo ou modificação necessária que faça enorme sentido colocar dentro deste último commit.

Neste caso, é possível utilizar um comando muito abrangente do git chamado reset que é capaz de diversas ações, inclusive a remoção de commits como é o nosso caso.

Para esse momento é importante destacar que o seu último commit é destacado para o GIT como a constante HEAD, e digamos que ele é a cabeça de uma lista ligada de commits, quando queremos acessar os últimos 3 commits podemos fazê-lo com HEAD~3.

As flags que podem ser utilizadas dependem da maneira que você deseja resetar esses commits, sendo elas

  • — hard, a qual apaga todas as modificações realizadas nos commits selecionados
  • — soft, que remove os commits mas não apaga as modificações.

Então nesse caso, se temos 2 commits que deveriam estar sendo apresentados como um só, pois são do mesmo contexto e fazem mais sentido juntos, podemos fazer:

git reset — soft HEAD~2

E após isso podemos commitar as mudanças que agora estão disponíveis no nível de staged, que é onde ficam as mudanças após serem adicionadas pelo git add.

Outra forma de fazer essa ação, que pode ser considerada como mais correta, e até um pouco mais avançada:

git rebase -i HEAD~2

Esse comando realiza um rebase interativo nos últimos 2 commits, e abrirá uma tela que mostra esses 2 commits, e nela podemos escolher qual será o commit principal (pick), qual iremos renomear (rename), qual podemos “amassar” e torná-lo parte do último commit escolhido (squash), entre outras opções que serão mostradas no arquivo que será mostrado em tela. Agora é só escolher qual a mensagem e commit que deseja manter e quais renomear ou amassar.

Adicione apenas o necessário para seu commit

Ao começar no git é normal aprender apenas o git add . e sempre usar ele para colocar seus arquivos na fila de staged para serem adicionados a um novo commit, porém essa não é a melhor prática, não leva muito tempo e experiência para passar por uma situação na qual se modifica um arquivo sem a intenção e ele acaba surgindo no seu commit e atrapalhando seu progresso.

Outro motivo para evitar a adição de todos os arquivos modificados e monitorados para seu staged é evitar modificações fora de seu próprio contexto, como modificar uma funcionalidade do sistema ao mesmo tempo que modifica a documentação de outra parte do sistema, essas modificações não participam do mesmo contexto e logo não devem ser colocadas juntas.

Apenas arquivos

  • Para selecionar apenas os arquivos necessários
    git add nomeDoArquivo.txt
  • Para remover arquivos selecionados
    git reset — nomeDoArquivo.txt
  • Para selecionar, remover e parar de monitorar arquivos em massa:
    git add -i

Essa flag de interatividade abre uma tela interativa onde podemos adicionar arquivo por arquivo através de uma interface dinâmica, muito melhor que bater a cabeça adicionando arquivo por arquivo.

Também podemos usar seletores de arquivos ou de pastas, por exemplo:

  • Se queremos todos os arquivos de uma pasta
    git add pasta/
  • Se queremos todos os arquivos JavaScript modificados
    git add *.js

Caso tenha apenas um arquivo e queira fazer uma adição e commit rápido, mesmo não sendo a melhor prática, pode te ajudar a ser mais produtivo. A flag -a adiciona todos os arquivos monitorados para staged.

git commit -am “mensagem”

Modificações de diferentes contextos em um mesmo arquivo

A forma mais detalhada de adicionar mudanças no seu commit é escolher hunks ou pedaços de código modificado dentro de um único arquivo e adicioná-los separadamente.

Digamos que você modificou 2 funções em seu arquivo e as duas são de contextos diferentes ou você queira commitar apenas uma delas pois a outra ainda não está pronta ou por outro motivo. Em vez de apagar as modificações da função que não estará no commit, podemos usar o comando a seguir, onde “-p” é uma referência a “patch”.

git add -p

Nisso teremos uma interface que mostrará as diferenças de cada uma das modificações que podem ser adicionadas, e ela perguntará se nós queremos realizar o stage desses hunks, nesse momento escolheremos a opção “s” que significa split ou dividir, que fará com que possamos escolher pedaço por pedaço de código.

Então a interface mostrará pedaço por pedaço e decidimos se ele ficará dentro do commit ou não, usando “y” para “sim, adicione’ e “n” para “não, passe para o próximo” ou “q” de “quit” para sair.

Agora, para confirmar o que está sendo levado para o commit podemos usar o comando git diff — staged.

Resetou um commit com um arquivo que não podia perder?

Se ao realizar um git reset — hard HEAD foi apagado um arquivo que era necessário, fique tranquilo que é possível recuperá-lo, basta que tu tenha o SHA1 (ou identificador) do commit apagado, e agora podemos migrar para uma nova branch retornando com as modificações:

git checkout -b nomeDaBranch SHA1doCommit

Agora ao realizar um git log podemos ver que o commit apagado agora aponta como head da branch teste.

Devemos voltar à branch principal agora:

git checkout nomeDaBranchPrincipal

E agora puxar esse commit de volta para a branch principal:

git rebase nomeDaBranch

Pronto. Agora é só deletar a branch criada:

git branch -d nomeDaBranch

Gestão de branches

É recomendável que seu time se reúna para desenvolver um guia de boas práticas para a gestão das branches, criando padrões e evitando conflitos e excesso de branchs inúteis no seu repositório. Isso vai ajudar a aumentar a produtividade e também manter rotinas de CI e CD. Veja mais neste vídeo.

--

--

Itanú Romero

Software Engineer — Problem solver — Technology enthusiastic