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

O loop de commit

docs.scrimba.com

Digamos que você está trabalhando no weather-app há vinte minutos. Você corrigiu um bug real em src/index.js, a conversão de temperatura estava errada, e enquanto estava lá também começou a reformatar styles.css, mas essa mudança está meio terminada e não está pronta para ninguém ver. Você quer salvar a correção do bug como seu próprio ponto de controle agora, sem arrastar o estilo inacabado junto com ele.

É exatamente para isso que serve o loop de commit. Se você ainda não configurou um repositório, Seu primeiro repositório mostra como usar git init e clonar primeiro. A partir daqui, este capítulo assume que você tem um aberto.

Os três estágios: diretório de trabalho, área de staging, commit

Toda mudança que você faz no Git passa por três estágios antes de se tornar histórico permanente. Seu diretório de trabalho é o projeto como ele está no disco agora, os arquivos que você está editando ativamente. A área de staging é um espaço de espera onde você coloca exatamente as mudanças que quer salvar em seguida. Um commit é o snapshot salvo em si, uma vez que você o escreve, mais a mensagem que o descreve.

Imagine fazer as malas para uma mudança. Sua casa inteira é o diretório de trabalho: tudo o que você possui, em qualquer estado que esteja. As caixas que você embalou e fechou na porta da frente são a área de staging: apenas o que você deliberadamente escolheu levar. O caminhão de mudança saindo com essas caixas é o commit: um registro do exatamente o que saiu, naquele momento, com um rótulo na parte externa dizendo o que está dentro.

Aqui está esse modelo mental aparecendo em um terminal real. Depois de editar ambos os arquivos em weather-app, git status lê o estado atual sem mudar nada:

bash
$ git status
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   src/index.js
        modified:   styles.css

no changes added to commit (use "git add" to commit)

Ambos os arquivos aparecem como "não staged" porque editar um arquivo apenas muda seu diretório de trabalho. Nada se move para a área de staging até você dizer ao Git para colocar lá com git add, que é a próxima seção.

É aqui que toda a razão de ser da área de staging fica clara. A área de staging permite que você escolha exatamente o que entra no seu commit, independentemente do que mais ainda está editado no seu diretório de trabalho. Sem ela, salvar um ponto de controle forçaria você a pegar toda mudança no disco de uma vez, pronta ou não.

Com ela, você pode fazer staging da correção de bug terminada em src/index.js, deixar o styles.css meio-terminado de fora, e fazer commit apenas da parte que está realmente pronta. Esse detalhe, de que staging e seu diretório de trabalho podem conter coisas diferentes ao mesmo tempo, confunde quase todo mundo na primeira semana. Uma vez que clica, o resto do fluxo de trabalho diário do Git faz muito mais sentido.

No trabalho do dia a dia, faça git status uma rotina antes de cada add e cada commit, independentemente de algo parecer errado. Não custa nada, nunca muda seu projeto, e evita o erro comum de fazer commit de mais do que você quis. Desenvolva o hábito de verificar o status, fazer staging, depois verificar o status novamente antes de fazer commit: leva segundos e significa que a mensagem no seu commit sempre corresponde ao que realmente entrou nele.

A área de staging é um arquivo real, chamado de index, situado em .git/index dentro do seu repositório. Toda vez que você executa git add, Git atualiza esse arquivo para registrar qual versão de qual arquivo está staged. Quando você executa git commit, Git constrói o novo commit puramente a partir do que o index diz, sem verificar seu diretório de trabalho novamente. Esse é todo o mecanismo por trás da divisão que você viu há um momento em git status: dois arquivos editados no disco, mas o index não rastreando nenhum deles ainda, então o commit que seguiria atualmente conteria nada.

JunoAs três áreas do Git Seu diretório de trabalho são seus arquivos como estão agora. A área de staging é onde você coloca exatamente as mudanças que quer salvar em seguida, e um commit é o snapshot salvo uma vez que você o escreve. Editar um arquivo nunca o faz staging por si só, você escolhe o que entra com git add, e é isso que permite que você salve uma mudança terminada enquanto deixa uma meio-feita de fora.
JunoAs três áreas do Git Diretório de trabalho, área de staging, commit: edições vivem na primeira, git add move exatamente o que você escolhe para a segunda, e git commit salva a segunda como um snapshot permanente. Execute git status antes de fazer staging e novamente antes de fazer commit, é grátis e mantém seus commits correspondendo ao que você quis salvar.
JunoAs três áreas do Git A área de staging é um arquivo real, o index em .git/index, e git commit constrói seu snapshot a partir do index, nunca diretamente do seu diretório de trabalho. É por isso que dois arquivos editados podem aparecer como unstaged: o index não foi informado sobre nenhum deles ainda. Entender o index dessa forma torna todo comando de staging menos como mágica e mais como ler e escrever um arquivo.

Fazendo staging de mudanças: git add

git add move uma mudança do seu diretório de trabalho para a área de staging. Aponte-o para o arquivo cuja mudança você quer no próximo commit:

bash
$ git add src/index.js
$ git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   src/index.js

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   styles.css

src/index.js se moveu para "Changes to be committed", a correção de bug está staged e pronta. styles.css ainda está listado sob "not staged", exatamente onde você quer enquanto o reformatting está inacabado. Executar git add styles.css também o faria staging; executar git add . faz staging de cada arquivo alterado na pasta atual de uma vez, o que é conveniente uma vez que você confia que tudo no disco está realmente pronto para ir.

Às vezes, um único arquivo tem tanto uma mudança que você quer salvar quanto uma que não, e dividi-los em dois arquivos não é prático. git add -p <file> (abreviação de --patch) percorre as mudanças do arquivo em pequenos pedaços, chamados hunks, e pede que você decida sobre cada um:

bash
$ git add -p src/index.js
@@ -12,7 +12,7 @@ function celsiusToFahrenheit(celsius) {
-  return celsius * (9 / 5 + 32)
+  return (celsius * 9) / 5 + 32
Stage this hunk [y,n,q,a,d,e,?]?

Responda y para fazer staging desse hunk, n para deixá-lo para depois, e q para parar. Isso lhe dá controle de tamanho de commit mesmo dentro de um único arquivo, o que vale a pena usar sempre que "metade deste arquivo está pronta" descreve sua situação.

git add faz duas coisas de uma vez, e conhecer ambas explica um problema que você eventualmente encontrará. Primeiro, ele escreve o conteúdo atual do arquivo em um novo blob, um objeto que Git armazena permanentemente, identificado por um hash da mesma forma que commits são, que contém o conteúdo exato de um arquivo em um momento. Segundo, ele atualiza o index para apontar para esse blob para esse arquivo. Nada sobre add olha para o arquivo novamente depois.

É por isso que, se você git add src/index.js e depois continua editando o mesmo arquivo antes de fazer commit, git status o mostra como staged e modified ao mesmo tempo: o index ainda aponta para o blob de quando você executou add, enquanto seu diretório de trabalho seguiu em frente. git commit só vê o blob staged, então um segundo git add é o que traz as edições mais recentes antes de você salvar.

JunoFazendo staging de mudançasgit add <file> move as mudanças atuais desse arquivo para a área de staging, pronto para o próximo commit. Arquivos que você ainda não adicionou ficam de fora, então você pode terminar uma coisa e deixar outra meio-editada. git add . faz staging de cada arquivo alterado de uma vez, prático uma vez que tudo está realmente pronto.
JunoFazendo staging de mudançasgit add <file> faz staging de um arquivo inteiro, e git add -p <file> permite que você o faça hunk por hunk quando apenas parte do arquivo está pronta. Essa segunda é a ferramenta para usar no momento em que "metade deste arquivo está pronta" é verdade.
JunoFazendo staging de mudançasgit add escreve o conteúdo atual do seu arquivo em um blob e aponta o index para ele, ele não monitora o arquivo depois. Edite o arquivo novamente antes de fazer commit e status o mostra como staged e modified, porque o index ainda contém o blob mais antigo. Re-execute add para trazer a edição mais recente. Essa confundiu-me por dez minutos confusos na primeira vez que encontrei.

Salvando um snapshot: git commit

Uma vez que a área de staging contém o que você quer, git commit o salva como um snapshot permanente, com a flag -m (abreviação de "message") fornecendo a descrição que viaja com ele:

bash
$ git commit -m "Fix temperature conversion in Celsius-to-Fahrenheit formula"
[main 4f2a1c9] Fix temperature conversion in Celsius-to-Fahrenheit formula
 1 file changed, 1 insertion(+), 1 deletion(-)

Apenas src/index.js entrou, porque é tudo o que você fez staging. styles.css ainda está editado no seu diretório de trabalho, intocado, esperando por sempre que o reformatting estiver terminado. O id curto, 4f2a1c9, nomeia esse commit exato, e o capítulo History cobre a leitura da lista completa de commits de um projeto como este.

Escreva mensagens de commit no modo imperativo, como uma instrução. "Fix temperature conversion" corresponde à linguagem que o próprio Git usa ("este commit irá...") e lê de forma limpa em ferramentas que listam commits um por linha. Para qualquer coisa mais envolvida do que uma correção de uma linha, adicione um corpo que explique por que a mudança era necessária. O diff já mostra o que mudou, então salve o corpo para o raciocínio por trás disso:

bash
$ git commit -m "Fix temperature conversion in Celsius-to-Fahrenheit formula

The formula was applying operator precedence in the wrong order,
which rounded warm temperatures down by a degree in the UI."

Se você notar um erro de digitação ou um detalhe perdido logo após fazer commit, git commit --amend substitui seu commit mais recente por um novo em vez de adicionar outro em cima:

bash
$ git commit --amend -m "Fix temperature conversion in Celsius-to-Fahrenheit formula"

Apenas corrija um commit que você não compartilhou em lugar nenhum ainda. Uma vez que você o empurrou (enviou para uma cópia compartilhada do projeto), emendar reescreve histórico que outras pessoas podem já ter, e Undoing things cobre as formas seguras de corrigir um commit após esse ponto.

Um commit em si é um pequeno objeto com três partes: um ponteiro para uma tree, o autor e a mensagem, e um ponteiro para seu commit pai. A tree é um snapshot de toda estrutura de diretórios do seu projeto naquele momento: para cada arquivo e pasta, ela registra um nome e um ponteiro para um blob (conteúdo de um arquivo) ou outra tree (uma subpasta). Executar git add escreve blobs e atualiza o index um arquivo por vez; git commit é o momento em que Git transforma o index atual em uma tree terminada e a envolve em um objeto commit.

Dois arquivos com conteúdo idêntico apontam para exatamente o mesmo blob, mesmo sentado em pastas diferentes, então um commit nunca armazena esses bytes duas vezes. Essa estrutura também torna comparar dois commits eficiente: Git percorre suas duas trees lado a lado e só abre os blobs cujos ponteiros realmente diferem, pulando cada arquivo que permaneceu igual.

JunoSalvando um commitgit commit -m "message" salva o que está atualmente staged como um snapshot permanente, com a mensagem que você fornece. Qualquer coisa que você não fez staging fica de fora e permanece editável. Mantenha a mensagem curta e clara sobre o que o commit faz, você mesmo futuro a lerá de volta mais vezes do que espera.
JunoSalvando um commit Escreva mensagens de commit como uma instrução, "Fix" em vez de "Fixed", e adicione um corpo sempre que o diff sozinho deixaria alguém adivinhando por que você fez a mudança. git commit --amend substitui seu último commit em vez de empilhar um novo, que é perfeito para um erro de digitação de mensagem, contanto que você não tenha empurrado esse commit em lugar nenhum ainda.
JunoSalvando um commit Um commit aponta para uma tree, e uma tree mapeia nomes para blobs e trees adicionais, um snapshot aninhado completo do projeto construído a partir de tudo o que o index continha no tempo de commit. add escreve os blobs e commit transforma o index em uma tree terminada, e conteúdo idêntico em qualquer lugar do projeto compartilha um blob em vez de ser armazenado duas vezes. Imagine essa forma e duas coisas param de parecer mágica: por que nada fica duplicado, e por que comparar dois commits significa caminhar duas trees lado a lado em vez de reler cada arquivo.