Skip to content
This page has been auto-translated and may contain errors.View in English

Branches no Git ​

docs.scrimba.com

Você está no meio da construção do weather-app e quer tentar adicionar uma funcionalidade de previsão do tempo para cinco dias. Pode levar alguns commits até acertar, e enquanto isso não acontece, você não quer código pela metade convivendo com a versão do app que já funciona para todo mundo que o usa. Uma branch é a forma que o Git tem de resolver isso: uma linha de trabalho separada onde o experimento fica guardado, enquanto a main continua funcionando exatamente como antes de você começar.

O que uma branch realmente é ​

Veja isso na prática:

bash
$ git branch forecast
$ git switch forecast
Switched to branch 'forecast'

Dois comandos: o primeiro cria uma nova branch chamada forecast, o segundo te move para ela. A partir daí, todo commit que você fizer cai na forecast, enquanto a main permanece exatamente onde está.

Uma branch é um rótulo móvel que aponta para um commit. Agora mesmo, main e forecast apontam para o mesmo commit, aquele em que você estava quando executou git branch forecast. No momento em que você faz um novo commit estando na forecast, esse rótulo avança para o novo commit. A main permanece exatamente onde estava até você alternar para ela e fazer um commit por conta própria.

Você pode ver os dois rótulos com git branch:

bash
$ git branch
  forecast
* main

O asterisco marca a branch em que você está atualmente. Uma branch é apenas um nome apontando para um commit. Esse é todo o mecanismo. Todo o resto deste capítulo é uma variação sobre mover esses rótulos de um lugar para outro.

Em um projeto real, os nomes das branches carregam informação. Uma convenção comum é usar um prefixo curto descrevendo o tipo de mudança, seguido de uma descrição: feature/five-day-forecast, fix/broken-date-picker, chore/upgrade-eslint. Alguns times adicionam um número de ticket (feature/wa-142-forecast). Qualquer convenção que você escolher, mantenha-a consistente no projeto todo, para que qualquer pessoa entenda para que serve uma branch sem precisar abri-la.

O fluxo de trabalho que esses nomes sustentam é o de branches de feature de vida curta: crie uma branch para uma única tarefa, faça commits nela, tenha a mudança revisada e mesclada, depois apague a branch. Uma branch que vive por semanas se distancia cada vez mais da main, e o merge final fica mais difícil quanto mais tempo esse distanciamento continuar. As branches mais saudáveis terminam em poucos dias.

Uma branch é pouco mais que um arquivo minúsculo guardando o hash de um commit, escondido dentro do controle interno do Git. Criar uma significa escrever essa única linha, e é por isso que git branch termina instantaneamente até mesmo em um repositório com anos de histórico. Essa mesma leveza é o motivo pelo qual convenções de nomenclatura e branches de vida curta importam na prática: apagar uma branch parada também não custa nada ao Git, então nada impede que branches abandonadas se acumulem, exceto a própria disciplina do time.

JunoO que uma branch realmente égit branch forecast cria um novo rótulo apontando para o seu commit atual, e git switch forecast te move para ele. Nada muda nos seus arquivos até você de fato fazer um commit lá. Gosto de imaginar branches como post-its em um commit: baratos de adicionar, baratos de mover, baratos de descolar quando você termina.
JunoO que uma branch realmente é Um nome de branch é um ponteiro, então criar uma não custa nada e alternar entre elas é instantâneo. Dê às branches nomes que descrevam o trabalho, como feature/five-day-forecast, e mantenha-as de vida curta: crie, faça commit, mescle, apague. Branches de vida longa são as que transformam um merge de rotina em uma dor de cabeça de verdade.
JunoO que uma branch realmente é Uma branch é literalmente um arquivo pequeno guardando um hash de commit, e é por isso que criar uma é uma das operações mais baratas do Git. Convenções de nomenclatura e branches de vida curta são a forma que um time tem de manter seu trabalho paralelo legível; o próprio Git não impõe nada disso. Já limpei branches abandonadas de seis meses atrás o suficiente para ter opiniões fortes sobre isso.

Criando e alternando entre branches ​

git branch forecast sozinho cria o rótulo sem te mover para ele. Na maioria das vezes você quer as duas coisas de uma vez, então git switch tem um atalho:

bash
$ git switch -c forecast
Switched to a new branch 'forecast'

-c significa "create" (criar). git switch -c cria e te move para uma branch ao mesmo tempo. Esse único comando é o que você vai usar praticamente toda vez que começar um novo trabalho.

Alternar entre branches muda qual snapshot dos seus arquivos aparece. Experimente: a linha echo abaixo é uma forma rápida de criar um novo arquivo, src/forecast.js, com uma única linha dentro, e todo o resto são comandos que você já conhece:

bash
$ git switch forecast
$ echo "// lógica da previsão de cinco dias" > src/forecast.js
$ git add src/forecast.js
$ git commit -m "Start forecast module"
$ ls src
forecast.js  index.js
$ git switch main
Switched to branch 'main'
$ ls src
index.js

O arquivo não desapareceu. Ele existe na branch forecast, e o snapshot da branch main nunca o incluiu. Volte para forecast e ele reaparece exatamente como você o deixou.

Você vai encontrar um verbo mais antigo para isso em códigos e tutoriais já existentes: git checkout forecast faz o mesmo trabalho que git switch forecast. checkout é mais antigo e faz mais de uma coisa: dependendo do que você passar para ele, pode alternar branches ou restaurar um arquivo para uma versão anterior, e o nome do comando por si só não te diz qual das duas coisas vai acontecer. switch e restore dividem essas duas tarefas em comandos separados, para que o nome já deixe claro de antemão: switch te move entre branches, restore devolve um arquivo ao estado que tinha em um determinado commit. Prefira os verbos mais novos; reconheça o checkout quando encontrá-lo por aí.

checkout decide o que fazer com base no que você passa a ele: um nome de branch alterna branches, um caminho de arquivo restaura esse arquivo, e se um nome corresponde a ambos, o Git precisa adivinhar qual dos dois você quis dizer. Essa ambiguidade é exatamente o motivo pelo qual o comando foi dividido em dois. switch só muda branches e restore só muda arquivos, então um caminho digitado errado não consegue mais te fazer alternar de branch por acidente. Espere ver checkout em scripts e tutoriais antigos por muitos anos ainda; escreva switch e restore em qualquer coisa nova.

JunoCriando e alternando entre branchesgit switch -c forecast cria uma branch e te move para ela em um único passo, que é o que você vai usar praticamente toda vez. Alternar entre branches troca qual versão dos seus arquivos você vê, então um arquivo adicionado em uma branch fica ausente em outra até você voltar. Nada se perde, ele fica guardado na outra branch.
JunoCriando e alternando entre branchesgit switch -c nome cria e move em um único passo. Você vai encontrar git checkout nome fazendo o mesmo trabalho em tutoriais e códigos mais antigos; é a mesma ideia por trás de um comando mais antigo e mais sobrecarregado. Use switch no dia a dia e leia checkout sem hesitar quando aparecer.
JunoCriando e alternando entre branchescheckout cuida tanto de alternar branches quanto de restaurar arquivos, dependendo dos argumentos que recebe, que é exatamente a ambiguidade que switch e restore vieram separar para eliminar. Espere ler checkout em scripts e tutoriais antigos por muitos anos ainda; ele continua funcionando, mas não é mais o comando a se escrever por padrão.

HEAD e HEAD desanexado ​

O Git acompanha onde você está atualmente por meio de um marcador chamado HEAD. Normalmente, HEAD aponta para a sua branch atual, e essa branch aponta para um commit, então HEAD se move automaticamente sempre que você alterna de branch ou faz um commit. Se você fizer checkout de um commit específico pelo hash, ou de uma tag (um rótulo fixo preso a um único commit, geralmente marcando um lançamento), em vez de um nome de branch, o HEAD se conecta diretamente a esse commit em vez de a um rótulo de branch. Esse estado é chamado de HEAD desanexado.

Você pode navegar livremente por ali, e pode até fazer commits, mas nada aponta para esses novos commits pelo nome. Se você alternar para uma branch sem antes criar uma, eles ficam difíceis de encontrar de novo.

bash
$ git switch a1b2c3d
HEAD is now at a1b2c3d Add password strength meter

Essa mensagem, "HEAD is now at", é o Git te dizendo que você saiu do território das branches e conectou o HEAD diretamente a um único commit.

No dia a dia, raramente você cai nessa situação por acidente. Isso costuma acontecer quando você faz checkout de uma tag antiga ou de um commit específico para ver como o código se comportava naquele ponto, ou quando uma ferramenta de build faz checkout de um commit exato para trabalhar a partir dele. Se você só quer olhar, está tudo bem: navegue pelos arquivos, rode o app, depois volte para uma branch quando terminar. Se você fizer uma mudança que vale a pena manter enquanto estiver desanexado, crie uma branch antes de sair de lá: git switch -c fix/old-bug a partir desse estado desanexado dá ao commit um rótulo de verdade, para que ele sobreviva à troca.

Um commit deixado para trás por um HEAD desanexado não é apagado no instante em que você sai de lá. Ele se torna inalcançável: continua armazenado, mas nenhuma branch, tag ou HEAD aponta mais para ele. O Git também não limpa esses commits imediatamente. Ele mantém um reflog, um registro de todos os lugares por onde o HEAD já passou, e um commit inalcançável costuma sobreviver por semanas antes de o Git realmente apagá-lo de vez.

Isso te dá um tempo real para recuperar o commit, embora não dure para sempre. Criar uma branch antes de sair de lá mantém o trabalho desanexado a salvo. Resgatar um commit do reflog funciona hoje, mas a entrada expira eventualmente, e um hash de commit sai da sua cabeça muito antes disso acontecer, então faça da branch um hábito e guarde o reflog para aquela rara vez em que você esquecer, coberta em Desfazendo coisas.

JunoHEAD e HEAD desanexado HEAD marca o commit em que você está atualmente, e normalmente ele acompanha a branch em que você está. Se você fizer checkout de um commit específico em vez de uma branch, você acaba com um HEAD desanexado, e qualquer novo commit que você fizer ali não tem nenhum nome de branch apontando para ele. Se isso acontecer e você quiser manter o trabalho, crie uma branch na hora, antes de ir para qualquer outro lugar.
JunoHEAD e HEAD desanexado Você provavelmente vai encontrar um HEAD desanexado ao fazer checkout de uma tag ou de um commit antigo para dar uma olhada, e isso é inofensivo enquanto você só estiver lendo. No momento em que você fizer um commit que vale a pena manter, execute git switch -c ali mesmo para dar a ele uma branch antes de ir para outro lugar.
JunoHEAD e HEAD desanexado Normalmente HEAD aponta para uma branch, que aponta para um commit. Desanexado, HEAD aponta direto para o commit, então qualquer coisa que você adicionar ali não tem nenhum nome de branch te segurando quando você sair. Raramente se perde de vez, já que o reflog mantém um rastro, mas não conte com lembrar o hash do commit três semanas depois. Crie a branch antes de sair, sempre.

Para onde isso leva ​

Você já criou branches, alternou entre elas e viu como a main permanece intocada enquanto você experimenta em outro lugar. A próxima pergunta é como o trabalho feito em uma branch volta para a main, que é exatamente o que Merge e conflitos aborda. Se você quiser relembrar como ler o histórico de commits antes de seguir adiante, Histórico percorre git log, git show e git diff.