Git Reset vs Revert vs Rebase

Neste artigo, você aprenderá sobre diferentes maneiras de brincar com commits no Git.

Como desenvolvedor, você passaria por essas situações várias vezes em que gostaria de reverter para um de seus commits anteriores, mas não tinha certeza de como fazer isso. E mesmo que você conheça os comandos Git como reset, revert, rebase, você não está ciente das diferenças entre eles. Então, vamos começar e entender o que são git reset, revert e rebase.

Git Redefinir

Git reset é um comando complexo e é usado para desfazer as alterações.

Você pode pensar no git reset como um recurso de reversão. Com git reset, você pode pular entre vários commits. Existem três modos de executar um comando git reset: –soft, –mixed e –hard. Por padrão, o comando git reset usa o modo misto. Em um fluxo de trabalho git reset, três mecanismos internos de gerenciamento do git entram em cena: HEAD, área de preparação (índice) e o diretório de trabalho.

O diretório de trabalho é o local onde você está trabalhando atualmente, é o local onde seus arquivos estão presentes. Usando um comando git status, você pode ver quais arquivos/pastas estão presentes no diretório de trabalho.

Staging Area (Index) é onde o git rastreia e salva todas as alterações nos arquivos. As alterações salvas são refletidas no diretório .git. Você usa git add “filename” para adicionar o arquivo à área de preparação. E como antes, ao executar git status, você verá quais arquivos estão presentes na área de teste.

A ramificação atual no Git é chamada de HEAD. Ele aponta para o último commit, que aconteceu na ramificação de checkout atual. É tratado como um ponteiro para qualquer referência. Depois de fazer o checkout em outra ramificação, o HEAD também se move para a nova ramificação.

Deixe-me explicar como o git reset funciona nos modos rígido, suave e misto. O modo difícil é usado para ir para o commit apontado, o diretório de trabalho é preenchido com arquivos desse commit e a área de preparação é redefinida. Na reinicialização suave, apenas o ponteiro é alterado para o commit especificado. Os arquivos de todos os commits permanecem no diretório de trabalho e na área de preparação antes da redefinição. No modo misto (padrão), o ponteiro e a área de preparação são redefinidos.

Git Redefinir Hard

O objetivo do git hard reset é mover o HEAD para o commit especificado. Ele removerá todos os commits ocorridos após o commit especificado. Este comando mudará o histórico de commits e apontará para o commit especificado.

Neste exemplo, vou adicionar três novos arquivos, confirmá-los e executar uma reinicialização total.

Como você pode ver no comando abaixo, agora não há nada para confirmar.

$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.

(use "git push" to publish your local commits)

nothing to commit, working tree clean

Agora, criarei 3 arquivos e adicionarei algum conteúdo a eles.

$ vi file1.txt
$ vi file2.txt
$ vi file3.txt

Adicione esses arquivos ao repositório existente.

$ git add file*

Quando você executar novamente o comando status, ele refletirá os novos arquivos que acabei de criar.

$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.

(use "git push" to publish your local commits)

Changes to be committed:

(use "git restore --staged <file>..." to unstage)

new file:
file1.txt

new file:
file2.txt

new file:
file3.txt

Antes de confirmar, deixe-me mostrar a você, atualmente tenho um registro de 3 confirmações no Git.

$ git log --oneline
0db602e (HEAD -> master) one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Agora, vou me comprometer com o repositório.

$ git commit -m 'added 3 files'
[master d69950b] added 3 files
3 files changed, 3 insertions(+)
create mode 100644 file1.txt
create mode 100644 file2.txt
create mode 100644 file3.txt

Se eu fizer ls-files, você verá que os novos arquivos foram adicionados.

$ git ls-files
demo
dummyfile
newfile
file1.txt
file2.txt
file3.txt

Quando executo o comando log no git, tenho 4 commits e o HEAD aponta para o commit mais recente.

$ git log --oneline
d69950b (HEAD -> master) added 3 files
0db602e one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Se eu excluir o arquivo1.txt manualmente e fizer um git status, ele mostrará a mensagem de que as alterações não estão preparadas para confirmação.

$ git status
On branch master
Your branch is ahead of 'origin/master' by 3 commits.

(use "git push" to publish your local commits)

Changes not staged for commit:

(use "git add/rm <file>..." to update what will be committed)

(use "git restore <file>..." to discard changes in working directory)

deleted:
file1.txt

no changes added to commit (use "git add" and/or "git commit -a")

Agora, executarei o comando hard reset.

$ git reset --hard
HEAD is now at d69950b added 3 files

Se eu verificar novamente o status, descobrirei que não há nada para confirmar e que o arquivo excluído voltou ao repositório. O rollback aconteceu porque depois de deletar o arquivo, eu não fiz o commit, então depois de um hard reset, ele voltou ao estado anterior.

$ git status
On branch master
Your branch is ahead of 'origin/master' by 3 commits.

(use "git push" to publish your local commits)

nothing to commit, working tree clean

Se eu verificar o log do git, é assim que ficará.

$ git log
commit d69950b7ea406a97499e07f9b28082db9db0b387 (HEAD -> master)
Author: mrgeek <[email protected]>
Date:
Mon May 17 19:53:31 2020 +0530

added 3 files

commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14
Author: mrgeek <[email protected]>
Date:
Mon May 17 01:04:13 2020 +0530

one more commit

commit 59c86c96a82589bad5ecba7668ad38aa684ab323
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:54:53 2020 +0530

new commit

commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD)
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:16:33 2020 +0530

test

O objetivo do hard reset é apontar para o commit especificado e atualizar o diretório de trabalho e a área de preparação. Deixe-me mostrar mais um exemplo. Atualmente, a visualização dos meus commits está assim:

  O que é um servidor sem cabeça?

Aqui, executarei o comando com HEAD^, o que significa que quero redefinir para o commit anterior (um commit de volta).

$ git reset --hard HEAD^
HEAD is now at 0db602e one more commit

Você pode ver que o ponteiro da cabeça mudou para 0db602e de d69950b.

$ git log --oneline
0db602e (HEAD -> master) one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Se você verificar o log, o commit de d69950b desapareceu e o cabeçalho agora aponta para 0db602e SHA.

$ git log
commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 (HEAD -> master)
Author: mrgeek <[email protected]>
Date:
Mon May 17 01:04:13 2020 +0530

one more commit

commit 59c86c96a82589bad5ecba7668ad38aa684ab323
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:54:53 2020 +0530

new commit

commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD)
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:16:33 2020 +0530

Test

Se você executar ls-files, poderá ver que file1.txt, file2.txt e files3.txt não estão mais no repositório porque esse commit e seu arquivo foram removidos após o hard reset.

$ git ls-files
demo
dummyfile
newfile

Git Soft Reset

Da mesma forma, agora mostrarei um exemplo de reinicialização suave. Considere, eu adicionei os 3 arquivos novamente como mencionado acima e os confirmei. O git log aparecerá como mostrado abaixo. Você pode ver que ‘soft reset’ é meu último commit, e HEAD também está apontando para isso.

$ git log --oneline
aa40085 (HEAD -> master) soft reset
0db602e one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Os detalhes do commit no log podem ser vistos usando o comando abaixo.

$ git log
commit aa400858aab3927e79116941c715749780a59fc9 (HEAD -> master)
Author: mrgeek <[email protected]>
Date:
Mon May 17 21:01:36 2020 +0530

soft reset

commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14
Author: mrgeek <[email protected]>
Date:
Mon May 17 01:04:13 2020 +0530

one more commit

commit 59c86c96a82589bad5ecba7668ad38aa684ab323
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:54:53 2020 +0530

new commit

commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD)
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:16:33 2020 +0530

test

Agora, usando o soft reset, quero mudar para um dos commits mais antigos com SHA 0db602e085a4d59cfa9393abac41ff5fd7afcb14

Para fazer isso, executarei o comando abaixo. Você precisa passar mais de 6 caracteres iniciais do SHA, não é necessário o SHA completo.

$ git reset --soft 0db602e085a4

Agora, quando executo o git log, posso ver que o HEAD foi redefinido para o commit que especifiquei.

$ git log
commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 (HEAD -> master)
Author: mrgeek <[email protected]>
Date:
Mon May 17 01:04:13 2020 +0530

one more commit

commit 59c86c96a82589bad5ecba7668ad38aa684ab323
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:54:53 2020 +0530

new commit

commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD)
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:16:33 2020 +0530

test

Mas a diferença aqui é que os arquivos do commit (aa400858aab3927e79116941c715749780a59fc9) onde adicionei 3 arquivos ainda estão no meu diretório de trabalho. Eles não foram excluídos. É por isso que você deve usar um soft reset em vez de um hard reset. Não há risco de perder os arquivos no modo suave.

$ git ls-files
demo
dummyfile
file1.txt
file2.txt
file3.txt
newfile

Git Reverter

No Git, o comando revert é usado para realizar uma operação de reversão, ou seja, para reverter algumas alterações. É semelhante ao comando reset, mas a única diferença aqui é que você executa um novo commit para voltar a um determinado commit. Resumindo, é justo dizer que o comando git revert é um commit.

  Como fazer referência cruzada de células entre planilhas do Microsoft Excel

O comando Git revert não exclui nenhum dado durante a execução da operação de reversão.

Digamos que estou adicionando 3 arquivos e executando uma operação git commit para o exemplo de reversão.

$ git commit -m 'add 3 files again'
[master 812335d] add 3 files again
3 files changed, 3 insertions(+)
create mode 100644 file1.txt
create mode 100644 file2.txt
create mode 100644 file3.txt

O log mostrará o novo commit.

$ git log --oneline
812335d (HEAD -> master) add 3 files again
0db602e one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Agora eu gostaria de reverter para um dos meus últimos commits, digamos – “59c86c9 new commit”. Eu executaria o comando abaixo.

$ git revert 59c86c9

Isso abrirá um arquivo, você encontrará os detalhes do commit para o qual está tentando reverter e poderá dar um nome ao seu novo commit aqui, salvar e fechar o arquivo.

Revert "new commit"

This reverts commit 59c86c96a82589bad5ecba7668ad38aa684ab323.

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
# Your branch is ahead of 'origin/master' by 4 commits.
# (use "git push" to publish your local commits)
#
# Changes to be committed:
# modified: dummyfile

Depois de salvar e fechar o arquivo, esta é a saída que você obterá.

$ git revert 59c86c9
[master af72b7a] Revert "new commit"
1 file changed, 1 insertion(+), 1 deletion(-)

Agora, para fazer as alterações necessárias, ao contrário do reset, o revert realizou mais um novo commit. Se você verificar o log novamente, encontrará um novo commit devido à operação de reversão.

$ git log --oneline
af72b7a (HEAD -> master) Revert "new commit"
812335d add 3 files again
0db602e one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Git log terá todo o histórico de commits. Se você deseja remover os commits do histórico, reverter não é uma boa escolha, mas se você deseja manter as alterações de commit no histórico, reverter é o comando adequado em vez de redefinir.

Git RebaseName

No Git, rebase é a maneira de mover ou combinar commits de um branch sobre outro branch. Como desenvolvedor, eu não criaria meus recursos na ramificação master em um cenário do mundo real. Eu trabalharia em minha própria ramificação (uma ‘ramificação de recursos’) e, quando tiver alguns commits em minha ramificação de recursos com o recurso adicionado, gostaria de movê-la para a ramificação principal.

Às vezes, o rebase pode ser um pouco confuso de entender porque é muito semelhante a uma mesclagem. O objetivo de mesclar e rebasear ambos é pegar os commits do meu branch de recursos e colocá-los em um branch master ou qualquer outro branch. Considere, eu tenho um gráfico que se parece com isso:

Suponha que você esteja trabalhando em equipe com outros desenvolvedores. Nesse caso, você pode imaginar que isso pode ficar realmente complexo, onde você tem vários outros desenvolvedores trabalhando em diferentes ramificações de recursos e eles mesclam várias alterações. Torna-se confuso rastrear.

  Como fazer referência cruzada de células entre planilhas do Microsoft Excel

Então, é aqui que o rebase vai ajudar. Desta vez, em vez de fazer um git merge, farei um rebase, onde quero pegar meus dois commits de feature branch e movê-los para o branch master. Um rebase pegará todos os meus commits do branch feature e os moverá para cima dos commits do branch master. Portanto, nos bastidores, o git está duplicando os commits do branch de recurso no branch master.

Essa abordagem fornecerá um gráfico de linha reta limpo com todos os commits em uma linha.

Isso facilita o rastreamento de quais commits foram para onde. Você pode imaginar que se você estiver em uma equipe com muitos desenvolvedores, todos os commits ainda estarão seguidos. Portanto, é realmente fácil de seguir, mesmo que você tenha muitas pessoas trabalhando no mesmo projeto ao mesmo tempo.

Deixe-me mostrar isso de forma prática.

É assim que meu branch master se parece atualmente. Tem 4 confirmações.

$ git log --oneline
812335d (HEAD -> master) add 3 files again
0db602e one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Vou executar o comando abaixo para criar e mudar para uma nova ramificação chamada feature, e esta ramificação será criada a partir do 2º commit, ou seja, 59c86c9

(master)
$ git checkout -b feature 59c86c9
Switched to a new branch 'feature'

Se você checar o log no branch feature, ele tem apenas 2 commits vindos do master (mainline).

(feature)
$ git log --oneline
59c86c9 (HEAD -> feature) new commit
e2f44fc (origin/master, origin/HEAD) test

Vou criar o recurso 1 e enviá-lo para a ramificação do recurso.

(feature)
$ vi feature1.txt

(feature)
$ git add .
The file will have its original line endings in your working directory

(feature)
$ git commit -m 'feature 1'
[feature c639e1b] feature 1
1 file changed, 1 insertion(+)
create mode 100644 feature1.txt

Vou criar mais um recurso, ou seja, o recurso 2, no ramo de recursos e confirmá-lo.

(feature)
$ vi feature2.txt

(feature)
$ git add .
The file will have its original line endings in your working directory

(feature)
$ git commit -m 'feature 2'
[feature 0f4db49] feature 2
1 file changed, 1 insertion(+)
create mode 100644 feature2.txt

Agora, se você verificar o log do branch do recurso, ele tem dois novos commits, que executei acima.

(feature)
$ git log --oneline
0f4db49 (HEAD -> feature) feature 2
c639e1b feature 1
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Agora quero adicionar esses dois novos recursos à ramificação master. Para isso, usarei o comando rebase. A partir do branch feature, farei o rebase contra o branch master. O que isso fará é ancorar novamente minha ramificação de recursos em relação às alterações mais recentes.

(feature)
$ git rebase master
Successfully rebased and updated refs/heads/feature.

Agora vou prosseguir e verificar o branch master.

(feature)
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 3 commits.

(use "git push" to publish your local commits)

E, finalmente, rebase o branch master contra o meu branch de recurso. Isso pegará esses dois novos commits no meu branch de recurso e os repetirá no topo do meu branch master.

(master)
$ git rebase feature
Successfully rebased and updated refs/heads/master.

Agora, se eu verificar o log no branch master, posso ver que os dois commits do meu branch features foram adicionados ao meu branch master com sucesso.

(master)
$ git log --oneline
766c996 (HEAD -> master, feature) feature 2
c036a11 feature 1
812335d add 3 files again
0db602e one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Isso foi tudo sobre redefinir, reverter e rebase comandos no Git.

Conclusão

Isso foi tudo sobre redefinir, reverter e rebase comandos no Git. Espero que este guia passo a passo tenha sido útil. Agora, você sabe como brincar com seus commits de acordo com a necessidade usando os comandos mencionados no artigo.