O Guia para o Git que eu nunca tive

 

Entendendo o Git

Médicos têm estetoscópios. 
Mecânicos têm chaves inglesas. 
Nós, desenvolvedores, temos o Git.

Você já notou que o Git é tão integral ao trabalho com código que as pessoas quase nunca o incluem em sua pilha tecnológica ou em seu currículo? A suposição é que você já o conhece, ou pelo menos o suficiente para se virar, mas será mesmo?

O Git é um Sistema de Controle de Versão (VCS Version Control System). A tecnologia onipresente que nos permite armazenar, alterar e colaborar em código com outros.

Como desenvolvedores, nossa rotina diária gira em torno de ler, escrever e revisar código. O Git é, sem dúvida, uma das ferramentas mais importantes que usamos. Dominar os recursos e funcionalidades que o Git oferece é um dos melhores investimentos que você pode fazer em si mesmo como desenvolvedor.

Vamos estabelecer as bases

O Git às vezes te faz se sentir como Peter Griffin?

Se você não aprender o Git da maneira certa, corre o risco de ficar constantemente coçando a cabeça, empacado nos mesmos problemas, ou lamentando o dia em que ver outro conflito de merge aparecer no seu terminal. Vamos garantir que isso não aconteça definindo alguns conceitos fundamentais do Git.

Branches (ramos)

Em um repositório Git, você encontrará uma linha principal de desenvolvimento, tipicamente chamada de “main” ou “master” (descontinuada) a partir da qual várias branches divergem. Essas branches representam fluxos simultâneos (paralelos) de trabalho, permitindo que os desenvolvedores abordem múltiplas funcionalidades ou correções simultaneamente dentro do mesmo projeto.

Branches

Commits
Os commits do Git servem como pacotes de código atualizado, capturando um instantâneo do código do projeto em um ponto específico no tempo. Cada commit registra as mudanças feitas desde que o último commit foi gravado, construindo juntos uma história abrangente da jornada de desenvolvimento do projeto.

Ao referenciar commits, você geralmente usará seu hash criptográfico identificado de forma única.

Exemplo:

git show abc123def456789`

Isso mostra informações detalhadas sobre o commit com aquele hash.

Tags
As tags do Git servem como marcos dentro da história do Git, marcando geralmente marcos significativos no desenvolvimento de um projeto, como lançamentos, versões ou commits destacados. Essas tags são inestimáveis para marcar pontos específicos no tempo, representando frequentemente os pontos de partida ou grandes conquistas na jornada de um projeto.

HEAD
O commit mais recente na branch atualmente verificada é indicado pelo HEAD, servindo como um ponteiro para qualquer referência dentro do repositório. Quando você está em uma ramificação específica, o HEAD aponta para o commit mais recente nessa branch. Às vezes, em vez de apontar para a ponta de uma branch, o HEAD pode apontar diretamente para um commit específico (estado de HEAD desanexado).

Stages
Entender os Git stages é crucial para navegar no seu fluxo de trabalho do Git. Eles representam as transições lógicas onde ocorrem mudanças nos seus arquivos antes de serem comprometidos com o repositório. Vamos mergulhar no conceito de estágios do Git:

Diretório de trabalho (working dir)

O working dir (diretório de trabalho) é onde você edita, modifica e cria arquivos para o seu projeto. Representando o estado atual dos seus arquivos na sua máquina local.

Staging area
A staging area é como uma área de espera ou uma zona pré-commit onde você prepara suas mudanças antes de comprometê-las com o repositório.

Comando útil aqui: git add e ou git rm podem ser usados para adicionar ou desfazer mudanças

Repositório local ️ 
O repositório local é onde o Git armazena permanentemente as mudanças comprometidas. Ele permite que você revise a história do seu projeto, reverta para estados anteriores e colabore com outros no mesmo código.

Repositório remoto 
O repositório remoto é um local centralizado, geralmente hospedado em um servidor (como GitHub, GitLab ou Bitbucket), onde você pode compartilhar e colaborar com outros no seu projeto.

Você pode usar comandos como git push e git pull para empurrar/puxar suas mudanças comprometidas do seu repositório local para o repositório remoto.

Introdução ao Git
Bem, você tem que começar em algum lugar, e no Git isso é o seu espaço de trabalho. Você pode fazer um fork ou clonar um repositório existente e ter uma cópia desse espaço de trabalho, ou se você está começando completamente do zero em uma nova pasta local na sua máquina, você tem que transformá-la em um repositório git com git init. O próximo passo, crucialmente não a ser negligenciado, é configurar suas credenciais.

Configuração de credenciais
Ao executar operações de push e pull para um repositório remoto, você não quer ter que digitar seu nome de usuário e senha toda vez, evite isso simplesmente executando o seguinte comando:

git config - global credential.helper store

Na primeira vez que você interagir com o repositório remoto, o Git solicitará que você insira seu nome de usuário e senha. E depois disso, você não será solicitado novamente.

É importante notar que as credenciais são armazenadas em um formato de texto simples dentro de um arquivo .git-credentials.

Para verificar as credenciais configuradas, você pode usar o seguinte comando:

git config - global credential.helper

Trabalhando com branches
Ao trabalhar localmente, é crucial saber em qual branch você está atualmente. Esses comandos são úteis:

# Mostrará as mudanças no repositório local
git branch
# Ou criar um ramo diretamente com
git branch nome-do-ramo-de-recurso

Para transitar entre branch, use:

git switch

Além de transicionar entre eles, você também pode usar:

git checkout
# Um atalho para mudar para um ramo que ainda precisa ser criado com a flag -b
git checkout -b nome-do-ramo-de-recurso

Para verificar o estado do repositório, use:

git status

Uma ótima maneira de sempre ter uma visão clara do seu ramo atual é vê-lo diretamente no terminal. Muitos complementos de terminal podem ajudar com isso. Aqui está um.

Git Bash para Windows

Trabalhando com commits
Ao trabalhar com commits, utilize git commit -m para registrar mudanças, git amend para modificar o commit mais recente, e faça o seu melhor para aderir às convenções de mensagem de commit.

# Certifique-se de adicionar uma mensagem a cada commit
git commit -m "mensagem significativa"

Se você tiver mudanças no seu último commit, você não precisa criar outro commit completamente, você pode usar a flag — amend para modificar o commit mais recente com suas mudanças preparadas

# faça suas mudanças
git add .
git commit - amend
# Isso abrirá seu editor de texto padrão para modificar a mensagem do commit, se necessário.
git push origin sua Branch - force

⚠️ Exercite cautela ao utilizar — force, pois tem o potencial de sobrescrever o histórico do ramo alvo. Sua aplicação no ramo principal/master deve ser geralmente evitada.

Como regra geral, é melhor commitar mais vezes do que não, para evitar perder progresso ou redefinir acidentalmente as mudanças não preparadas. Pode-se reescrever a história depois, agrupando vários commits ou fazendo um rebase interativo.

Use git log para mostrar uma lista cronológica de commits, começando pelo commit mais recente e voltando no tempo

Manipulando a História
Manipular a história envolve alguns comandos poderosos. Rebase reescreve a história do commit, Squashing combina vários commits em um, e Cherry-picking seleciona commits específicos.

Rebasing e merging
Faz sentido comparar rebasing com merging, já que seu objetivo é o mesmo, mas eles o alcançam de maneiras diferentes. A diferença crucial é que o rebasing reescreve a história do projeto. Uma escolha desejada para projetos que valorizam uma história de projeto clara e facilmente compreensível. Por outro lado, o merging mantém ambas as histórias de ramos, gerando um novo commit de merge.

Durante um rebase, a história do commit do ramo de recursos é reestruturada à medida que é movida para o HEAD do ramo principal

O fluxo de trabalho aqui é bastante direto.

Certifique-se de estar no ramo que você deseja rebasear e busque as últimas mudanças do repositório remoto:

git checkout seu ramo
git fetch

Agora escolha o ramo em que você deseja rebasear e execute este comando:

git rebase ramo_upstream

Após o rebase, você pode precisar forçar o envio de suas mudanças se o ramo já tiver sido enviado para um repositório remoto:

git push origin seu_ramo --force

Squashing
O Squashing do Git é usado para condensar vários commits em um único commit coeso.

O conceito é fácil de entender e especialmente útil se o método de unificar o código usado for rebasing, já que a história será alterada, é importante estar ciente dos efeitos na história do projeto. Houve vezes em que lutei para realizar um squash, especialmente usando rebase interativo, felizmente temos algumas ferramentas para nos ajudar. Este é o meu método preferido de squashing, que envolve mover o ponteiro HEAD para trás X número de commits enquanto mantém as mudanças preparadas.

# Mude para o número após HEAD~ dependendo dos commits que você deseja squashar
git reset - soft HEAD~X
git commit -m "Sua mensagem de commit squashada"
git push origin seu_ramo - force

Cherry-picking
Cherry-picking é útil para incorporar seletivamente mudanças de um ramo para outro, especialmente quando a fusão de branches inteiras não é desejável ou viável. No entanto, é importante usar o cherry-picking com moderação, pois pode levar a commits duplicados e históricos divergentes se aplicado erroneamente.

Para realizar isso primeiro você tem que identificar o hash do commit do commit que você gostaria de escolher, você pode fazer isso com git log. Uma vez que você tenha identificado o hash do commit, você pode executar:

git checkout ramo_alvo
git cherry-pick <hash-do-commit>
# Faça isso várias vezes se múltiplos commits forem desejados
git push origin ramo alvo

Comandos Avançados do Git

Assinando commits
Assinar commits é uma maneira de verificar a autenticidade e integridade de seus commits no Git. Isso permite que você assine criptograficamente seus commits usando sua chave GPG (GNU Privacy Guard), assegurando ao Git que você é de fato o autor do commit.

Você pode fazer isso criando uma chave GPG e configurando o Git para usar a chave ao commitar. Aqui estão os passos:

# Gerar uma chave GPG
gpg - gen-key
# Configurar o Git para Usar Sua Chave GPG
git config - global user.signingkey <sua-id-de-chave-gpg>
# Adicionar a chave pública à sua conta do GitHub
# Assinar seus commits com a flag -S
git commit -S -m "Sua mensagem de commit"
# Visualizar commits assinados
git log - show-signature

Git reflog
Um tópico que não exploramos é referências do Git, que são ponteiros para vários objetos dentro do repositório, principalmente commits, mas também tags e ramos. Eles servem como pontos nomeados na história do Git, permitindo que os usuários naveguem pelo cronograma do repositório e acessem snapshots específicos do projeto. Saber como navegar pelas referências do Git pode ser muito útil e elas podem usar git reflog para fazer exatamente isso. Aqui estão alguns dos benefícios:

  • Recuperar commits ou ramos perdidos
  • Depuração e resolução de problemas
  • Desfazer erros

Rebase interativo
Rebase interativo é um recurso poderoso do Git que permite reescrever a história do commit de forma interativa. Ele permite modificar, reordenar, combinar ou deletar commits antes de aplicá-los a um ramo.

Para usar você tem que se familiarizar com as possíveis ações, como são:

  • Escolher (“p”)
  • Reword (“r”)
  • Editar (“e”)
  • Squash (“s”)
  • Drop (“d”)

Colaborando com o Git

Origin vs Upstream
O origin é o repositório remoto padrão associado ao seu repositório Git local quando você o clona. Se você fizer um fork de um repositório, então esse fork se torna seu repositório “origin” por padrão.

Upstream, por outro lado, refere-se ao repositório original do qual seu repositório foi bifurcado.

Para manter seu repositório bifurcado atualizado com as últimas mudanças do projeto original, você busca mudanças do repositório “upstream” e as mescla ou rebaseia em seu repositório local.

Para ver os repositórios remotos associados ao seu repositório Git local, execute:

git remote -v

Conflitos
Não entre em pânico, ao tentar mesclar ou rebasear uma branch e conflitos forem detectados, isso apenas significa que existem mudanças conflitantes entre versões diferentes do mesmo arquivo ou arquivos em seu repositório e eles podem ser facilmente resolvidos (na maioria das vezes).

Git merge
Eles são tipicamente indicados dentro dos arquivos afetados, onde o Git insere marcadores de conflito <<<<<<<, ======= e >>>>>>> para destacar as seções conflitantes. Decida quais mudanças manter, modificar ou remover, garantindo que o código resultante faça sentido e retenha a funcionalidade pretendida.

Após resolver manualmente os conflitos nos arquivos conflitantes, remova os marcadores de conflito <<<<<<<, ======= e >>>>>>> e ajuste o código conforme necessário.

Salve as mudanças nos arquivos conflitantes uma vez que você esteja satisfeito com a resolução.

Fluxos de trabalho populares do Git

Vários fluxos de trabalho do Git existem, no entanto, é importante notar que não há um fluxo de trabalho Git universalmente “melhor”. Em vez disso, cada abordagem tem seu próprio conjunto de prós e contras. Vamos explorar esses diferentes fluxos de trabalho para entender suas forças e fraquezas.

Feature Branch Workflow
Cada nova funcionalidade ou correção de bug é desenvolvida em sua própria branch e depois mesclada de volta a branch principal assim que concluída.

  • Vantagem: Isolamento de mudanças e redução de conflitos.
  • Desvantagem: Pode se tornar complexo e requer gestão diligente de ramos.

Gitflow Workflow

Gitflow define um modelo de ramificação estrito com branchs predefinidas para diferentes tipos de tarefas de desenvolvimento.

Inclui ramos de longa duração como principal, desenvolver, branchs de recursos, branchs de lançamento e branchs de correção de bugs.

  • Vantagem: Adequado para projetos com lançamentos programados e manutenção de longo prazo.
  • Desvantagem: Pode ser excessivamente complexo para equipes menores

Forking Workflow
Neste fluxo de trabalho, cada desenvolvedor clona o repositório principal, mas em vez de enviar mudanças diretamente para ele, eles enviam mudanças para seu próprio fork do repositório. Os desenvolvedores então criam pull requests para propor mudanças ao repositório principal, permitindo revisão de código e colaboração antes da mesclagem.

  • Vantagem: Encoraja a colaboração de colaboradores externos sem conceder acesso direto de escrita ao repositório principal.
  • Desvantagem: Manter a sincronização entre forks e o repositório principal pode ser desafiador.

Pull Request Workflow
Semelhante ao Fluxo de Trabalho de Forking, mas em vez de fazer um fork, os desenvolvedores criam ramos de recursos diretamente no repositório principal.

  • Vantagem: Facilita a revisão de código, colaboração e compartilhamento de conhecimento entre os membros da equipe.
  • Desvantagem: A dependência de revisores de código humanos pode introduzir atrasos no processo de desenvolvimento.

Trunk-Based Workflow
Se você está em uma equipe focada em iteração rápida e entrega contínua, você pode usar o desenvolvimento baseado em tronco no qual os desenvolvedores trabalham diretamente no ramo principal, comprometendo mudanças pequenas e frequentes.

  • Vantagem: Promove iteração rápida, integração contínua e um foco em entregar mudanças pequenas e frequentes para produção.
  • Desvantagem: Requer pipelines robustos de teste automatizado e implantação para garantir a estabilidade do ramo principal, pode não ser adequado para projetos com cronogramas de lançamento rigorosos ou desenvolvimento de funcionalidades complexas.

O que diabos é um fork?
Forking é altamente recomendado para colaborar em projetos de código aberto, já que você tem controle completo sobre sua própria cópia do repositório. Você pode fazer mudanças, experimentar com novas funcionalidades ou corrigir bugs sem afetar o projeto original.

O que me levou muito tempo para descobrir foi que, embora os repositórios bifurcados comecem como entidades separadas, eles mantêm uma conexão com o repositório original. Essa conexão permite que você acompanhe as mudanças no projeto original e sincronize seu fork com as atualizações feitas por outros.

É por isso que mesmo quando você envia para seu repositório de origem. Suas mudanças também aparecerão no remoto.

Git Cheatsheet

# Clone a Repository
git clone <repository_url>

# Stage Changes for Commit
git add <file(s)>

# Commit Changes
git commit -m "Commit message"

# Push Changes to the Remote Repository
git push

# Force Push Changes (use with caution)
git push --force

# Reset Working Directory to Last Commit
git reset --hard

# Create a New Branch
git branch <branch_name>

# Switch to a Different Branch
git checkout <branch_name>

# Merge Changes from Another Branch
git merge <branch_name>

# Rebase Changes onto Another Branch (use with caution)
git rebase <base_branch>

# View Status of Working Directory
git status

# View Commit History
git log

# Undo Last Commit (use with caution)
git reset --soft HEAD^

# Discard Changes in Working Directory
git restore <file(s)>

# Retrieve Lost Commit References
git reflog

# Interactive Rebase to Rearrange Commits
git rebase --interactive HEAD~3

Comentários

Postagens mais visitadas