Como reescrever o histórico

Como reescrever o histórico

Introdução

Este tutorial vai abranger vários métodos de reescrever e alterar o histórico do Git. O Git usa alguns métodos diferentes para gravar alterações. A gente vai discutir os pontos fortes e fracos dos diferentes métodos e dar exemplos de como trabalhar com eles. Este tutorial discute alguns dos motivos mais comuns para substituir snapshots já com o commit e mostra como evitar as armadilhas de fazer isso.

O principal trabalho do Git é garantir que você nunca perca uma alteração na qual fez commit, mas ele também foi desenvolvido para dar a você o controle total sobre seu fluxo de trabalho de desenvolvimento – o que inclui deixar você definir a aparência exata do histórico do seu projeto. No entanto, ele também cria o potencial de perder commits. O Git oferece comandos de regravação de histórico sob o aviso de isenção de que fazer uso deles pode resultar em perda de conteúdo.

O Git tem vários mecanismos para armazenar o histórico e salvar alterações. Esses mecanismos incluem: Commit --amend, git rebase e git reflog. Essas opções dão a você opções robustas de personalização do fluxo de trabalho. No final deste tutorial, você vai estar familiarizado com os comandos que vão permitir reestruturar seus commits do Git e evitar armadilhas que costumam ser encontradas ao reprocessar o histórico.

Como alterar o último commit: git commit --amend

O comando git commit --amend é uma forma conveniente de modificar o commit mais recente. Ele permite combinar alterações na área de staging com o commit anterior em vez de criar um commit inédito. Ele também pode ser usado para editar a mensagem do commit anterior sem alterar a captura instantânea. Mas a correção não apenas altera o commit mais recente, ela o substitui por completo, o que significa que o commit corrigido vai ser uma nova entidade com sua própria referência. Para o Git, ele vai parecer um commit inédito, visualizado com um asterisco (*) no diagrama abaixo. Há alguns cenários comuns para usar o git commit --amend. Veja exemplos de uso nas seções a seguir.

Git commit amend

Altere a mensagem mais recente de commit do Git

git commit --amend

Digamos que você acabou de fazer um commit e errou na mensagem do log de confirmação. Executar este comando quando não existe nada na área de staging permite que você edite a mensagem do commit anterior sem alterar o snapshot.

Commits prematuros acontecem o tempo todo durante o desenvolvimento diário. É fácil esquecer de preparar um arquivo ou formatar a mensagem de commit da forma errada. O sinalizador --amend é uma forma conveniente de corrigir esses erros pequenos.

git commit --amend -m "an updated commit message"

Adicionar a opção -m permite transmitir uma nova mensagem da linha de comando sem a necessidade de abrir um editor.

Alterando arquivos com commit

O exemplo a seguir demonstra um cenário comum no desenvolvimento baseado em Git. Digamos que você editou alguns arquivos e quer confirmar em um único snapshot, mas se esqueceu de adicionar um dos arquivos na primeira vez. Para corrigir o erro, basta fazer o staging do outro arquivo e confirmar com o sinalizador --amend:

# Edit hello.py and main.py git add hello.py git commit
# Veja que você esqueceu de adicionar as alterações do main.py git add main.py
git commit --amend --no-edit

O sinalizador --no-edit vai permitir fazer a correção no commit sem alterar a mensagem de confirmação. O commit resultante vai substituir o commit incompleto e vai parecer que as alterações para hello.py e main.py foram confirmadas em um único snapshot.

Não emende commits públicos

Os commits corrigidos, são na verdade, commits inéditos e o commit anterior não vai mais estar no branch atual. Isso tem as mesmas consequências que redefinir um snapshot público. Evite corrigir um commit no qual outros desenvolvedores basearam seu trabalho. Esta é uma situação que causa confusão para os desenvolvedores e é complicado se recuperar dela.

Recapitulando

Para revisar, o git commit --amend permite que você pegue o commit mais recente e adicione alterações novas na área de staging a ele. Você pode adicionar ou remover alterações da área de staging do Git para aplicar com um commit --amend. Se não houver nenhuma alteração na área de staging, um --amend ainda vai solicitar que você modifique o último log da mensagem do commit. Tenha cuidado ao usar --amend em commits compartilhados com outros membros da equipe. Corrigir um commit compartilhado com outro usuário pode exigir resoluções de conflitos de merge confusas e demoradas.

Como alterar commits mais antigos ou diversos commits

Para modificar commits mais antigos ou vários commits, você pode usar git rebase para combinar uma sequência de commits em um novo commit base. No modo padrão, git rebase permite que você reescreva o histórico — aplicando os commits em seu branch de trabalho atual ao head do branch. Como seus novos commits vão substituir os antigos, é importante não usar git rebase nos commits públicos ou vai parecer que o histórico do seu projeto desapareceu.

Nessas instâncias ou em instâncias semelhantes nas quais é importante preservar um histórico de projeto limpo, adicionar a opção -i a git rebase permite executar o rebase interativo e, aí, você tem a oportunidade de alterar commits individuais no processo, em vez de mover todos os commits. Você pode aprender mais sobre o rebase interativo e os comandos de rebase adicionais na página de git rebase.

Alterando arquivos com commit

Durante um rebase, a edição ou o comando e vai pausar a reprodução do rebase nesse commit e permitir que você faça alterações adicionais com git commit --amend O Git vai interromper a reprodução e apresentar uma mensagem:

Stopped at 5d025d1... formatting
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue

Várias mensagens

Cada commit regular do Git vai ter uma mensagem de log explicando o que aconteceu no commit. Essas mensagens dão informações valiosas do histórico do projeto. Durante um rebase, você pode executar alguns comandos nos commits para modificar as mensagens de confirmação.

  • Reword ou "r" vai parar a reprodução do rebase e deixar você reescrever a mensagem de commit individual.
  • Squash ou "s" durante a reprodução do rebase vai fazer com que os commits marcados como s sejam interrompidos e você poderá consolidar as mensagens de confirmação separadas em uma mensagem única. Saiba mais sobre isso na seção de squash de commits abaixo.
  • Fixup ou "f" tem o mesmo efeito de combinação que o squash, com a diferença que os commits de correção não vão interromper a reprodução do rebase para abrir um editor para combinar mensagens de confirmação. Os commits marcados como "f" vão ter suas mensagens descartadas em favor da mensagem de confirmação anterior.

Realize squash nos commits para um histórico limpo

O comando s "squash" é onde a gente vê a verdadeira utilidade do rebase. O squash permite especificar quais commits você quer consolidar nos commits anteriores. É isso que possibilita um "histórico limpo". Durante a reprodução do rebase, o Git vai executar o comando de rebase especificado para cada commit. No caso dos commits de squash, o Git vai abrir seu editor de texto configurado e solicitar a combinação de mensagens de confirmação especificadas. Todo esse processo pode ser visualizado assim:

Tutorial do Git: exemplo de git rebase -i

Observe que os commits modificados com um comando de rebase têm um ID diferente dos commits originais. Os commits marcados com pick vão ter um novo ID se os commits anteriores tiverem sido reescritos.

Soluções de hospedagem modernas do Git, como Bitbucket, oferecem os recursos "auto squashing" no merge. Esses recursos vão fazer o rebase na hora e consolidar os commits de branches quando você utilizar a interface do usuário das soluções hospedadas. Para obter mais informações, veja "Squash commits when merging a Git branch with Bitbucket."

Recapitulando

O rebase do Git dá a você o poder de modificar seu histórico e o rebase interativo permite fazer isso sem deixar um rastro de "bagunça". Isso dá a liberdade de cometer e corrigir erros e refinar seu trabalho, enquanto ainda mantém um histórico de projeto limpo e linear.

A rede de segurança: git reflog

Os logs de referência, ou "reflogs", são um mecanismo usado pelo Git para registrar atualizações aplicadas às pontas dos branches e a outras referências de commits. O reflog permite voltar aos commits mesmo que eles não sejam mencionados por nenhum branch ou tag. Depois de revisar o histórico, o reflog contém informações sobre o antigo estado dos branches e permite voltar àquele estado, se necessário. Toda vez que a ponta do branch é atualizada por algum motivo (pela alternância de branches, por baixar novas alterações, reescrever o histórico ou apenas ao adicionar commits novos), uma nova entrada vai ser adicionada a reflog. Nesta seção, aprenda mais sobre o comando git reflog e explore alguns de seus usos comuns.

Uso

git reflog

Exibe o reflog do repositório local.

git reflog --relative-date

Mostra o reflog com informações de datas relativas (por exemplo, 2 semanas atrás).

Exemplo

Para entender o git reflog, veja um exemplo.

0a2e358 HEAD@{0}: reset: moving to HEAD~2
0254ea7 HEAD@{1}: checkout: moving from 2.2 to master
c10f740 HEAD@{2}: checkout: moving from master to 2.2

O reflog acima mostra uma verificação do master para o branch 2.2 e volta. Daqui, há uma redefinição difícil para um commit mais antigo. A atividade mais recente é representada na parte superior denominada HEAD@{0}.

Se, por acidente, acontecer de você mover de volta, o reflog vai conter o commit master apontado para (0254ea7) antes de você, também por acidente, descartar dois commits.

git reset --hard 0254ea7

Usando a redefinição do Git, é possível alterar o master de volta para o commit que era antes. Isso proporciona uma rede de segurança no caso do histórico ser alterado por acidente.

É importante saber que o reflog apenas proporciona essa rede de segurança se as alterações tiverem sido confirmadas em seu repositório local e se ele rastrear movimentos da ponta do branch dos repositórios. Além disso, as entradas de reflog têm uma data de vencimento. O prazo de vencimento padrão para as entradas de reflog é de 90 dias.

Para obter informações adicionais, consulte a página do git reflog

Resumo

Neste artigo, você viu vários métodos para alterar o histórico do Git e desfazer alterações do Git e, em mais detalhes, o processo de rebase do Git. Alguns dos principais argumentos são:

  • Há várias maneiras de reescrever o histórico com o Git.
  • Use git commit --amend para alterar a mensagem de log mais recente.
  • Use git commit --amend para fazer modificações no commit mais recente.
  • Use git rebase para combinar commits e modificar o histórico de um branch.
  • git rebase -i oferece um controle muito mais refinado das modificações do histórico do que um rebase padrão do Git.

Saiba mais sobre esses comandos em suas próprias páginas: