Chapters ▾ 2nd Edition

2.4 Fundamentos do Git - Desfazendo Coisas

Desfazendo Coisas

Em qualquer estágio, você pode querer desfazer algo. Aqui, revisaremos algumas ferramentas básicas para desfazer alterações que você fez. Tenha cuidado, pois você nem sempre pode desfazer alguns desses desfazimentos. Esta é uma das poucas áreas no Git onde você pode perder algum trabalho se fizer de forma incorreta.

Um dos desfazimentos comuns ocorre quando você faz o commit muito cedo e possivelmente se esquece de adicionar alguns arquivos, ou você bagunça sua mensagem de commit. Se você quiser refazer esse commit, faça as alterações adicionais que esqueceu, prepare-as e faça o commit novamente usando a opção --amend:

$ git commit --amend

Este comando pega sua área de preparação e a utiliza para o commit. Se você não fez nenhuma alteração desde o seu último commit (por exemplo, você executa este comando imediatamente após o commit anterior), então o seu snapshot ficará exatamente o mesmo e tudo o que você alterará será a sua mensagem de commit.

O mesmo editor de mensagem de commit é iniciado, mas já contém a mensagem do seu commit anterior. Você pode editar a mensagem da mesma forma de sempre, mas ela substitui (overwrites) seu commit anterior.

Como exemplo, se você fizer o commit e, em seguida, perceber que se esqueceu de preparar as alterações em um arquivo que queria adicionar a este commit, você pode fazer algo assim:

$ git commit -m 'Initial commit'
$ git add forgotten_file
$ git commit --amend

Você acaba com um único commit — o segundo commit substitui os resultados do primeiro.

Note

É importante entender que quando você está alterando (amending) seu último commit, você não está tanto corrigindo-o, mas substituindo-o inteiramente por um novo e melhorado commit que empurra o antigo commit para fora do caminho e coloca o novo commit em seu lugar. Efetivamente, é como se o commit anterior nunca tivesse acontecido, e ele não aparecerá no histórico do seu repositório.

O valor óbvio de alterar (amending) commits é fazer pequenas melhorias em seu último commit, sem sobrecarregar o histórico do seu repositório com mensagens de commit na forma, “Ops, esqueci de adicionar um arquivo” ou “Droga, corrigindo erro de digitação no último commit”.

Note

Altere apenas commits que ainda são locais e não foram enviados (pushed) para algum lugar. Alterar commits enviados anteriormente e forçar o push do branch causará problemas para seus colaboradores. Para saber mais sobre o que acontece quando você faz isso e como se recuperar se estiver do lado do destinatário, leia Os Perigos do Rebase (Perils of Rebasing).

Retirando a Preparação de um Arquivo Preparado (Unstaging)

As próximas duas seções demonstram como trabalhar com a sua área de preparação e com as alterações do diretório de trabalho. A parte boa é que o comando que você usa para determinar o estado dessas duas áreas também o lembra de como desfazer alterações nelas. Por exemplo, digamos que você alterou dois arquivos e quer fazer o commit deles como duas alterações separadas, mas você acidentalmente digitou git add * e preparou ambos. Como você pode retirar a preparação de (unstage) um dos dois? O comando git status o lembra:

$ git add *
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README
    modified:   CONTRIBUTING.md

Logo abaixo do texto “Changes to be committed”, ele diz para usar git reset HEAD <file>…​ para retirar a preparação (unstage). Então, vamos usar esse conselho para retirar a preparação do arquivo CONTRIBUTING.md:

$ git reset HEAD CONTRIBUTING.md
Unstaged changes after reset:
M	CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README

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

    modified:   CONTRIBUTING.md

O comando é um pouco estranho, mas funciona. O arquivo CONTRIBUTING.md está modificado, mas mais uma vez não preparado (unstaged).

Note

É verdade que git reset pode ser um comando perigoso, especialmente se você fornecer a flag --hard. No entanto, no cenário descrito acima, o arquivo no seu diretório de trabalho não é tocado, então é relativamente seguro.

Por enquanto, essa invocação mágica é tudo o que você precisa saber sobre o comando git reset. Entraremos em muito mais detalhes sobre o que o reset faz e como dominá-lo para fazer coisas realmente interessantes em Reset Desmistificado.

Desfazendo a Modificação de um Arquivo Modificado

E se você perceber que não deseja manter as alterações feitas no arquivo CONTRIBUTING.md? Como você pode desfazer as modificações facilmente — revertê-lo para a aparência que tinha da última vez em que fez o commit (ou clonou inicialmente, ou como quer que tenha chegado ao seu diretório de trabalho)? Felizmente, o git status também diz como fazer isso. Na saída do último exemplo, a área não preparada se parece com isto:

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

    modified:   CONTRIBUTING.md

Ele diz explicitamente como descartar as alterações feitas. Vamos fazer o que ele diz:

$ git checkout -- CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README

Você pode ver que as alterações foram revertidas.

Important

É importante entender que git checkout -- <file> é um comando perigoso. Quaisquer alterações locais feitas nesse arquivo são perdidas — o Git simplesmente substituiu aquele arquivo pela última versão preparada ou commitada. Nunca use esse comando a menos que você saiba com certeza absoluta que não deseja essas alterações locais não salvas.

Se você gostaria de manter as alterações feitas nesse arquivo, mas ainda precisa tirá-lo do caminho por enquanto, abordaremos as opções de stashing e branching no Ramificação (Branching) no Git; essas são geralmente maneiras melhores de prosseguir.

Lembre-se, qualquer coisa que seja commitada no Git quase sempre pode ser recuperada. Até mesmo commits que estavam em branches que foram excluídos ou commits que foram sobrescritos com um commit --amend podem ser recuperados (consulte Recuperação de Dados para recuperação de dados). No entanto, qualquer coisa que você perca e que nunca tenha sido commitada provavelmente nunca mais será vista.

Desfazendo coisas com git restore

O Git versão 2.23.0 introduziu um novo comando: git restore. É basicamente uma alternativa ao git reset que acabamos de abordar. A partir do Git versão 2.23.0, o Git usará git restore em vez de git reset para muitas operações de desfazer.

Vamos refazer nossos passos e desfazer as coisas com git restore em vez de git reset.

Retirando a Preparação de um Arquivo Preparado com git restore

As próximas duas seções demonstram como trabalhar com a sua área de preparação e as alterações do diretório de trabalho com git restore. A parte boa é que o comando que você usa para determinar o estado dessas duas áreas também o lembra de como desfazer alterações nelas. Por exemplo, digamos que você alterou dois arquivos e quer fazer o commit deles como duas alterações separadas, mas você acidentalmente digitou git add * e preparou ambos. Como você pode retirar a preparação de (unstage) um dos dois? O comando git status o lembra:

$ git add *
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   CONTRIBUTING.md
	renamed:    README.md -> README

Logo abaixo do texto “Changes to be committed”, ele diz para usar git restore --staged <file>…​ para retirar a preparação (unstage). Então, vamos usar esse conselho para retirar a preparação do arquivo CONTRIBUTING.md:

$ git restore --staged CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	renamed:    README.md -> README

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:   CONTRIBUTING.md

O arquivo CONTRIBUTING.md está modificado, mas mais uma vez não preparado (unstaged).

Desfazendo a Modificação de um Arquivo Modificado com git restore

E se você perceber que não deseja manter as alterações feitas no arquivo CONTRIBUTING.md? Como você pode desfazer as modificações facilmente — revertê-lo para a aparência que tinha da última vez em que fez o commit (ou clonou inicialmente, ou como quer que tenha chegado ao seu diretório de trabalho)? Felizmente, o git status também diz como fazer isso. Na saída do último exemplo, a área não preparada se parece com isto:

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:   CONTRIBUTING.md

Ele diz explicitamente como descartar as alterações feitas. Vamos fazer o que ele diz:

$ git restore CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	renamed:    README.md -> README
Important

É importante entender que git restore <file> é um comando perigoso. Quaisquer alterações locais feitas nesse arquivo são perdidas — o Git simplesmente substituiu aquele arquivo pela última versão preparada ou commitada. Nunca use esse comando a menos que você saiba com certeza absoluta que não deseja essas alterações locais não salvas.