Close

Redefinição, verificação e reversão


Os comandos git reset, git checkout e git revert são algumas das ferramentas mais úteis em sua caixa de ferramentas do Git. Todos eles permitem que você desfaça algum tipo de alteração em seu repositório, e os dois primeiros comandos podem ser usados para manipular commits ou arquivos individuais.

Por serem muito parecidos, é muito fácil misturar qual comando deve ser usado em qualquer cenário de desenvolvimento. Neste artigo, a gente vai comparar as configurações mais comuns de git reset, git checkout e git revert. Espero que você saia com a confiança necessária para navegar em seu repositório usando qualquer um desses comandos.

As três árvores do Git

É útil pensar em cada comando em termos de seu efeito nos três mecanismos de gerenciamento de estado de um repositório do Git: o diretório de trabalho, o instantâneo de staging e o histórico de commits. Esses componentes às vezes são conhecidos como "As três árvores" do Git. As três árvores são exploradas em profundidade na página git reset. Tenha esses mecanismos em mente ao ler este artigo.

Um checkout é uma operação que move o ponteiro de referência HEAD para um commit especificado. Para demonstrar melhor esse comportamento, considere o exemplo a seguir:

Mova o ponteiro de referência HEAD para um commit especificado
bancos de dados
Material relacionado

Como mover um Repositório do Git completo

Logotipo do Bitbucket
VER SOLUÇÃO

Aprenda a usar o Git com o Bitbucket Cloud

Este exemplo demonstra uma sequência de commits no branch main. A referência HEAD e a referência do branch main apontam no momento para o commit d. Agora vamos executar o git checkout b

Sequência de confirmações na ramificação principal

Esta é uma atualização para a árvore do "histórico de commits". O comando git checkout pode ser usado em um escopo de nível de commit ou de arquivo. Um checkout no nível do arquivo vai alterar o conteúdo do arquivo para o commit específico.

Uma reversão é uma operação que recebe um commit especificado e cria um novo commit que inverte o commit especificado. O git revert só pode ser executado em um escopo de nível de commit e não tem funcionalidade no nível do arquivo.

Uma redefinição é uma operação que recebe um commit especificado e redefine as "três árvores" para corresponder ao estado do repositório nesse commit especificado. Uma redefinição pode ser invocada em três modos diferentes, que correspondem às três árvores.

O checkout e a redefinição costumam ser usados para realizar ações de desfazer locais ou privadas. Eles modificam o histórico de um repositório que pode causar conflitos ao enviar para repositórios compartilhados remotos. A reversão é considerada uma operação segura para a ação de desfazer pública, pois cria um novo histórico que pode ser compartilhado remotamente e não substitui o histórico do qual os membros da equipe remota podem depender.

Referência para Git reset, revert e checkout


A tabela abaixo resume os casos de uso mais comuns para todos esses comandos. Mantenha essa referência à mão, pois sem dúvida você vai precisar usar pelo menos algumas delas durante sua carreira no Git.

Comando

Escopo

Casos de uso comuns

git reset

Escopo

Nível de commit

Casos de uso comuns

Descartar confirmações em uma ramificação privada ou descartar alterações sem confirmação

git reset

Escopo

Nível do arquivo

Casos de uso comuns

Desfazer o staging de um arquivo

git checkout

Escopo

Nível de commit

Casos de uso comuns

Alternar entre os branches ou inspecionar instantâneos antigos

git checkout

Escopo

Nível do arquivo

Casos de uso comuns

Descartar alterações no diretório de trabalho

git revert

Escopo

Nível de commit

Casos de uso comuns

Desfazer commits em um branch público

git revert

Escopo

Nível do arquivo

Casos de uso comuns

(N/A)

Operações de nível de commit


Os parâmetros que você passa para git reset e git checkout determinam o escopo. Quando você não inclui um caminho de arquivo como parâmetro, eles operam em commits inteiros. É o que a gente vai explorar nesta seção. Observe que o git revert não tem equivalente no nível do arquivo.

Redefinir um commit específico

No nível do commit, a redefinição é uma maneira de mover a ponta de um branch para um commit diferente, o que pode ser usado para remover commits do branch atual. Por exemplo, o comando a seguir move o branch de hotfix para trás por dois commits.

git checkout hotfix git reset HEAD~2

Os dois commits que estavam no final do hotfix agora são commits órfãos, pendentes. Ou seja: eles vão ser excluídos na próxima vez que o Git realizar uma coleta de lixo. Em outras palavras, você está dizendo que quer jogar fora esses commits. Essa situação pode ser vista da seguinte forma:

Redefinir o branch de hotfix para HEAD-2

Esse uso do git reset é uma maneira simples de desfazer alterações que não foram compartilhadas com mais ninguém. É o seu comando quando você começa a trabalhar em uma função e se pega pensando: “Caramba, o que estou fazendo? Eu deveria começar de novo.”

Além de mover o branch atual, você também pode usar o git reset para alterar o instantâneo de staging e/ou o diretório de trabalho passando uma das seguintes marcações:

  • --soft — O instantâneo de staging e o diretório de trabalho não são alterados de forma alguma.
  • --mixed — O instantâneo de staging é atualizado para corresponder ao commit especificado, mas o diretório de trabalho não é afetado. Essa é a opção padrão.
  • --hard — O instantâneo de staging e o diretório de trabalho são atualizados para corresponder ao commit especificado.

É mais fácil pensar nesses modos como definições do escopo de uma operação git reset. Para ver mais informações, visite a página git reset.

Fazer checkout de commits antigos

O comando git checkout é usado para atualizar o estado do repositório para um ponto específico no histórico do projeto. Quando transmitido com um nome de branch, ele permite alternar entre branches.

git checkout hotfix

Internamente, tudo o que o comando acima faz é mover o HEAD para um branch diferente e atualizar o diretório de trabalho para corresponder a essa mudança. Como essa operação tem o potencial de substituir as alterações locais, o Git força você a fazer o commit ou o stash de quaisquer alterações no diretório de trabalho que vão ser perdidas durante a operação de checkout. Ao contrário do git reset, o git checkout não move nenhum branch.

Mover o HEAD de principal para hotfix

Você também pode fazer o check out de commits arbitrários passando a referência do commit em vez de um branch. É exatamente a mesma coisa que o check out de um branch: a referência HEAD é movida para o commit especificado. Por exemplo, o comando a seguir vai verificar o avô do commit atual:

git checkout HEAD~2
Mover `HEAD` para um commit arbitrário

Ele é útil para fazer a inspeção rápida uma versão antiga do seu projeto. No entanto, como não há referência de branch para o HEAD atual, você fica em um estado de HEAD desanexado. Essa situação pode ser perigosa se você começar a adicionar novos commits porque não vai ter como voltar para eles depois que você mudar para outro branch. Por esse motivo, você deve sempre criar um novo branch antes de adicionar commits a um HEAD desanexado.

Desfazer commits públicos com o comando de reverter

Reverter desfaz um commit criando um novo commit. Essa é uma maneira segura de desfazer alterações, pois não há chance de reescrever o histórico de commits. Por exemplo, o comando a seguir vai identificar as alterações contidas no penúltimo commit, criar um novo commit desfazendo essas alterações e anexar o novo commit no projeto existente.

git checkout hotfix git revert HEAD~2

Essa situação pode ser vista da seguinte forma:

Reverter o penúltimo commit

Compare esse comportamento com o do git reset, que altera o histórico de commits existente. Por esse motivo, o git revert deve ser usado para desfazer alterações em um branch público, e o git reset deve ser reservado para desfazer alterações em um branch privado.

Você também pode pensar no git revert como uma ferramenta para desfazer alterações com commit, enquanto o git reset HEAD é para desfazer alterações sem commit.

Como o git checkout, o git revert tem o potencial de sobrescrever arquivos no diretório de trabalho, então ele vai solicitar que você faça o commit ou o stash das alterações que seriam perdidas durante a operação de reversão.

Operações em nível de arquivo


Os comandos git reset e git checkout também aceitam um caminho de arquivo opcional como parâmetro. Assim, o comportamento deles tem uma alteração drástica. Em vez de operar em instantâneos inteiros, eles são forçados a limitar suas operações a um único arquivo.

Git Reset de um arquivo específico

Quando invocado com um caminho de arquivo, o git reset atualiza o instantâneo de staging para corresponder à versão do commit especificado. Por exemplo, esse comando vai buscar a versão do foo.py no penúltimo commit e o preparar para o próximo commit:

git reset HEAD~2 foo.py

Assim como na versão de nível de commit do git reset, essa opção costuma ser mais usada com HEAD em vez de um commit arbitrário. Executar git reset HEAD foo.py vai desfazer o staging de foo.py. As alterações que ele contém ainda vão estar presentes no diretório de trabalho.

Mover um arquivo do histórico de commits para o instantâneo de staging

As marcações --soft, --mixed e --hard não têm nenhum efeito na versão em nível de arquivo do git reset, pois o instantâneo de staging sempre é atualizado, e o diretório de trabalho nunca é atualizado.

Git Checkout de um arquivo

Fazer checkout de um arquivo é semelhante a usar o git reset com um caminho de arquivo, exceto pelo fato de que ele atualiza o diretório de trabalho em vez do ambiente de staging. Ao contrário da versão em nível de commit desse comando, essa ação não move a referência HEAD, o que significa que você não vai mudar de branch.

Mover um arquivo do histórico de commits para o diretório de trabalho

Por exemplo, o comando a seguir faz com que o foo.py no diretório de trabalho corresponda ao do penúltimo commit:

git checkout HEAD~2 foo.py

Assim como a invocação no nível do commit do git checkout, essa opção pode ser usada para inspecionar versões antigas de um projeto, mas o escopo é limitado ao arquivo especificado.

Se você fizer o staging e o commit do arquivo com checkout, o efeito vai ser o de “reverter” para a versão antiga desse arquivo. Observe que assim você remove todas as alterações subsequentes no arquivo, enquanto o comando git revert desfaz apenas as alterações introduzidas pelo commit especificado.

Como o git reset, ele costuma ser usado com HEAD como referência de commit. Por exemplo, git checkout HEAD foo.py tem o efeito de descartar alterações que não passaram por staging para foo.py. Esse é um comportamento semelhante ao de git reset HEAD --hard, mas opera apenas no arquivo especificado.

Resumo


Agora você deve ter todas as ferramentas necessárias para desfazer alterações em um repositório do Git. Os comandos git reset, git checkout e git revert podem ser confusos, mas quando você pensa sobre seus efeitos no diretório de trabalho, no instantâneo de staging e no histórico de commits, deve ser mais fácil discernir qual comando se encaixa na tarefa de desenvolvimento em questão.


Compartilhar este artigo
Próximo tópico

Leitura recomendada

Marque esses recursos para aprender sobre os tipos de equipes de DevOps ou para obter atualizações contínuas sobre DevOps na Atlassian.

Pessoas colaborando usando uma parede cheia de ferramentas

Blog do Bitbucket

Ilustração do DevOps

Caminho de aprendizagem de DevOps

Demonstrações de funções no Demo Den com parceiros da Atlassian

Como o Bitbucket Cloud funciona com o Atlassian Open DevOps

Inscreva-se para receber a newsletter de DevOps

Thank you for signing up