Git Reset e Reflog

GitBeginner
Pratique Agora

Introdução

Bem-vindo, viajante do tempo do Git! Hoje, vamos explorar dois recursos poderosos do Git que lhe darão um controle sem precedentes sobre o histórico do seu repositório: git reset e git reflog. Essas ferramentas são como os controles avançados da sua máquina do tempo Git, permitindo que você se mova entre diferentes estados do seu projeto e até recupere trabalhos "perdidos".

O comando git reset é uma ferramenta versátil que pode ajudá-lo a desfazer alterações, retirar arquivos da área de preparação (unstage) e até reescrever seu histórico de commits. No entanto, com grandes poderes vêm grandes responsabilidades, e o git reset pode ser um pouco intimidador para iniciantes. É aí que entra o git reflog - ele funciona como uma rede de segurança, registrando todas as mudanças feitas nas referências do seu repositório (como as pontas das branches), permitindo que você se recupere até mesmo dos resets mais drásticos.

Neste laboratório, vamos abordar:

  1. Soft Reset: Movendo o HEAD sem alterar o diretório de trabalho ou a área de preparação.
  2. Mixed Reset: Retirando alterações da área de preparação enquanto mantém as modificações no diretório de trabalho.
  3. Hard Reset: Descartando alterações completamente.
  4. Usando o Reflog para se recuperar de operações "destrutivas".
  5. Resets baseados em tempo: Movendo seu repositório para um estado de um momento específico no tempo.

Ao final deste laboratório, você terá uma compreensão sólida de como usar esses recursos poderosos do Git de forma segura e eficaz. Você será capaz de manipular o histórico do seu repositório com confiança, sabendo que sempre poderá encontrar o caminho de volta se necessário.

Vamos mergulhar e começar a dominar o git reset e o reflog!

Configurando seu Espaço de Trabalho

Antes de começarmos a resetar e usar o reflog, vamos configurar um espaço de trabalho com alguns commits para experimentarmos. Criaremos um novo diretório, inicializaremos um repositório Git e adicionaremos alguns arquivos com múltiplos commits.

Abra seu terminal e digite estes comandos:

cd ~/project
mkdir git-reset-lab
cd git-reset-lab
git init

Agora, vamos criar alguns arquivos e fazer uma série de commits, copiando e colando os seguintes comandos no seu terminal:

echo "## Git Reset and Reflog Lab" > README.md
git add README.md
git commit -m "Initial commit"

echo "function add(a, b) { return a + b; }" > math.js
git add math.js
git commit -m "Add addition function"

echo "function subtract(a, b) { return a - b; }" >> math.js
git add math.js
git commit -m "Add subtraction function"

echo "function multiply(a, b) { return a * b; }" >> math.js
git add math.js
git commit -m "Add multiplication function"

Vamos detalhar o que acabamos de fazer:

  1. Criamos um arquivo README e fizemos nosso commit inicial.
  2. Criamos um arquivo JavaScript com uma função de adição e o commitamos.
  3. Adicionamos uma função de subtração ao mesmo arquivo e a commitamos.
  4. Por fim, adicionamos uma função de multiplicação e a commitamos.

Agora temos um repositório com algum histórico para experimentar!

Soft Reset: Movendo o HEAD

O primeiro tipo de reset que exploraremos é o reset "soft". Um soft reset move o HEAD (e a branch atual) para um commit diferente, mas não altera a área de preparação (staging area) nem o diretório de trabalho. Isso é útil quando você deseja "desfazer" alguns commits, mas manter todas as alterações prontas para um novo commit.

Vamos tentar um soft reset:

git reset --soft HEAD~2

Este comando move o HEAD dois commits para trás (para o commit anterior a "Add subtraction function"). O ~2 significa "dois commits antes do HEAD atual". Você pode usar ~N para voltar N commits.

Agora, se você executar git status, verá que as alterações dos commits "Add subtraction function" e "Add multiplication function" estão preparadas (staged) juntas. Os arquivos no seu diretório de trabalho não mudaram. Todo o trabalho desses dois commits está agora pronto para ser enviado como um único e novo commit.

Isso é útil em cenários onde você deseja "esmagar" (squash) seus últimos commits em um só. Você pode dar um soft reset para alguns commits atrás e então fazer um novo commit com todas essas alterações.

Vamos commitar essas alterações novamente com uma nova mensagem:

git commit -m "Add subtraction and multiplication functions"

Lembre-se, embora o soft reset seja geralmente seguro (pois não descarta nenhuma alteração), ele reescreve o histórico. Se você já enviou (push) os commits originais, precisará fazer um push forçado para atualizar a branch remota, o que pode causar problemas para os colaboradores. Sempre se comunique com sua equipe antes de reescrever o histórico compartilhado!

Mixed Reset: Retirando Alterações da Área de Preparação

O próximo tipo de reset que veremos é o reset "mixed". Este é, na verdade, o modo padrão do git reset se você não especificar nenhuma flag. Um mixed reset move o HEAD e atualiza a área de preparação para corresponder a ele, mas não toca no diretório de trabalho.

Vamos fazer algumas alterações e prepará-las:

echo "function divide(a, b) { return a / b; }" >> math.js
git add math.js

Agora, digamos que mudamos de ideia e não queremos preparar essa alteração ainda. Podemos usar um mixed reset:

git reset HEAD

Isso retira nossas alterações da área de preparação (unstage), mas as mantém no diretório de trabalho. Se você executar git status agora, verá que math.js está modificado, mas não preparado.

O mixed reset é útil quando você preparou algumas alterações, mas decidiu que ainda não está pronto para commitá-las. Talvez você queira revisar as alterações novamente ou fazer mais modificações antes de prepará-las.

Lembre-se, ao contrário do soft reset, o mixed reset altera a área de preparação. No entanto, ainda é seguro no sentido de que não descarta nada do seu trabalho - tudo continua no seu diretório de trabalho.

Hard Reset: Descartando Alterações

O terceiro e mais drástico tipo de reset é o reset "hard". Um hard reset move o HEAD, atualiza a área de preparação E atualiza o diretório de trabalho para corresponder. Isso significa que ele descarta todas as alterações desde o commit para o qual você está resetando.

Vamos tentar um hard reset:

git add math.js
git commit -m "Add division function"
git status
git reset --hard HEAD~1

Isso prepara e commita nossa função de divisão, e então realiza um hard reset para o commit anterior, efetivamente "desfazendo" nosso último commit e descartando as alterações.

Se você olhar para o arquivo math.js agora, verá que a função de divisão sumiu. É como se nunca a tivéssemos escrito.

O hard reset é poderoso, mas perigoso. É útil quando você deseja descartar completamente algum trabalho e começar do zero a partir de um commit anterior. No entanto, seja muito cuidadoso com ele, pois pode descartar alterações permanentemente.

Sempre verifique duas vezes se você está resetando para o commit correto antes de fazer um hard reset. Se não tiver certeza, é mais seguro usar um soft ou mixed reset, ou criar uma nova branch antes de experimentar.

Usando o Reflog para Recuperar Commits Perdidos

E se você perceber que não pretendia descartar aquela função de divisão? É aqui que o git reflog vem ao resgate. O reflog é um registro de todos os lugares onde o HEAD esteve no seu repositório local. É como um super-histórico que registra até comandos de reescrita de histórico, como o reset.

Vamos olhar o reflog:

git reflog

Você deve ver uma lista de todas as ações recentes que realizou, incluindo seus resets. Cada entrada possui um identificador HEAD@{n}.

Para recuperar seu commit perdido, você pode resetar para o estado anterior ao seu hard reset:

git reset --hard HEAD@{1}

Isso reseta para o estado do HEAD antes da sua última ação (que foi o hard reset).

Verifique o math.js agora, e você verá que sua função de divisão está de volta!

cat math.js
function add(a, b) { return a + b; }
function subtract(a, b) { return a - b; }
function multiply(a, b) { return a * b; }
function divide(a, b) { return a / b; }

O reflog é uma rede de segurança poderosa, permitindo que você se recupere de quase qualquer acidente no Git. No entanto, lembre-se de que ele é local para sua máquina e temporário (as entradas são normalmente mantidas por 30 a 90 dias). Ele não substitui backups regulares ou o envio do seu trabalho para um repositório remoto.

Resets Baseados em Tempo

O Git também permite que você resete seu repositório para o estado em que ele estava em um momento específico no tempo. Isso pode ser útil se você se lembrar aproximadamente de quando seu repositório estava no estado ao qual deseja retornar.

Vamos tentar um reset baseado em tempo:

git reset --hard master@{"1 hour ago"}

Isso reseta seu repositório para o estado de 1 hora atrás. Você pode usar vários descritores de tempo como "yesterday" (ontem), "2 days ago" (2 dias atrás), "3 minutes ago" (3 minutos atrás), etc.

Tenha cuidado com resets baseados em tempo, pois eles podem ser menos precisos do que resetar para um commit específico. Sempre verifique o estado do seu repositório após um reset baseado em tempo para garantir que você chegou onde pretendia.

Lembre-se, você sempre pode usar o reflog para desfazer um reset baseado em tempo se ele não der o resultado esperado.

Resumo

Parabéns, senhor do tempo do Git! Você acaba de dominar alguns dos comandos mais poderosos e potencialmente perigosos do Git. Vamos recapitular os principais conceitos que abordamos:

  1. Soft Reset: Move o HEAD sem alterar a área de preparação ou o diretório de trabalho. Útil para agrupar (squash) commits.
  2. Mixed Reset: Move o HEAD e atualiza a área de preparação, mas não toca no diretório de trabalho. Ótimo para retirar alterações da área de preparação.
  3. Hard Reset: Move o HEAD, atualiza a área de preparação e o diretório de trabalho. Poderoso, mas potencialmente destrutivo.
  4. Reflog: Uma rede de segurança que registra todas as mudanças no HEAD, permitindo que você se recupere de quase qualquer erro no Git.
  5. Resets Baseados em Tempo: Permitem que você resete seu repositório para o estado em que estava em um momento específico.

Lembre-se, com grandes poderes vêm grandes responsabilidades. Embora esses comandos ofereçam um controle incrível sobre o histórico do seu repositório, eles também podem ser perigosos se usados sem cuidado. Sempre verifique antes de realizar um reset, especialmente um hard reset, e lembre-se de que o reflog é seu amigo se algo der errado.

À medida que você continua sua jornada no Git, pratique esses comandos em um ambiente seguro até se sentir confortável com eles. Eles são ferramentas poderosas que podem melhorar muito seu fluxo de trabalho no Git quando usados corretamente.

Bom reset, e que seu histórico do Git seja sempre limpo e significativo!