Aprenda a usar o Git com o Bitbucket Cloud

Saiba como desfazer alterações no Git usando o Bitbucket Cloud

Objetivo

Saiba como desfazer alterações na máquina local e em um repositório do Bitbucket Cloud enquanto colabora com outros.

Missão breve

Comandos apresentados neste tutorial: git revert, git reset, git log e git status

 

Tempo Público-alvo: Pré-requisitos

40 minutos

Este tutorial requer conhecimento destes comandos git:

git clone, git commit, git pull e git push
Você instalou o Git
    Você tem uma conta Bitbucket

Todos cometem erros. Nem todo push é perfeito, então este tutorial vai ajudar a usar as funções git mais comuns para desfazer uma alteração ou alterações com segurança.

Este tutorial requer conhecimento destes comandos git:

Se você não conhece esses comandos, conte com a gente para aprender o Git com o Bitbucket Cloud. Volte aqui e saiba como desfazer mudanças. Esses comandos git podem ser usados em ambientes Windows ou Unix. Este tutorial vai usar as utilidades da linha de comando do Unix ao explicar a navegação pelo sistema de arquivos.

Como desfazer mudanças na máquina local

Quando a alteração que você quer desfazer estiver no sistema local e não foi enviada por push a um repositório remoto, há duas formas básicas de desfazer a alteração:

Comando Definição

git revert

Um comando "desfazer", embora não seja uma operação tradicional de desfazer. Em vez de remover o commit, ele descobre como desfazer as alterações no commit e acrescenta um novo commit com o conteúdo inverso. Isso evita que o Git perca o histórico, o que é importante para a integridade do histórico de revisão e para uma colaboração confiável.

git reset

Um comando git versátil para desfazer alterações. O comando git reset tem um conjunto poderoso de opções, mas vamos usar apenas os seguintes modos de redefinição para este tutorial:

  • --soft: Redefine apenas o HEAD do commit que você seleciona. Funciona quase da mesma forma que o git checkout <commit #>, mas não cria um estado de cabeçalho desvinculado.
  • --mixed: Redefine o HEAD do commit que você seleciona no histórico e desfaz as alterações no índice.
  • --hard: Redefine o HEAD do commit que você seleciona no histórico, desfaz as alterações no índice e desfaz as alterações em seu diretório de trabalho. Neste tutorial a gente não vai testar uma redefinição definitiva.

Para uma descrição completa de como o git reset funciona, consulte git-scm.com Ferramentas do Git - A redefinição desmistificada.

 

À medida que você avança no tutorial, vai aprender diversos outros comandos git como parte do aprendizado de como desfazer alterações, então vamos começar.

Bifurque um repositório

Vamos começar criando um repositório exclusivo com todo o código do original. Este processo é chamado "bifurcação de repositório". Bifurcar é um processo git estendido que é ativado quando um repositório compartilhado é hospedado com um serviço de hospedagem de terceiros como o Bitbucket.

  1. Clique ou insira o seguinte URL: https://bitbucket.org/atlassian/tutorial-documentation-tests/commits/all
  2. Clique no símbolo + na barra lateral esquerda e selecione Bifurcar este repositório, analise a caixa de diálogo e clique em Bifurcar repositório.
  3. Você deve ser levado para a visão geral do novo repositório.
  4. Clique no símbolo + e selecione Clonar este repositório.
  5. No seu computador, clone o repositório.
  6. Navegue para o diretório que contém o repositório clonado.

Agora que você tem um repositório cheio de códigos e um histórico no sistema local, já pode desfazer algumas alterações.

Encontre alterações no sistema local

Você vai ter que encontrar e referenciar a alteração que quer desfazer. Isso pode ser feito navegando na interface de usuário do commit no Bitbucket e há alguns utilitários de linha de comando que podem localizar uma alteração específica.

git status

Git status retorna o status do diretório de trabalho (o local do repositório no sistema local) e a área de staging (em que você prepara um conjunto de alterações a serem adicionadas ao histórico do projeto) e vai mostrar todos os arquivos que tenham alterações e se elas tiverem sido adicionadas à área de staging. Vamos executar git status e examinar o estado atual do repositório.

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean

O resultado do git status mostra que tudo está atualizado com o branch principal remoto e não há alterações pendentes aguardando para serem confirmadas. No próximo exemplo, a gente vai fazer umas edições no repositório e o examinar em um estado de alterações pendentes. Isso significa que você tem alterações em arquivos no repositório do sistema local que não foram preparadas para serem adicionadas ao histórico do projeto.

Para demonstrar este próximo exemplo, primeiro abra o arquivo myquote2.html. Faça algumas modificações no conteúdo de myquote2.html, salve e saia do arquivo. Vamos executar o git status mais uma vez para examinar o repositório neste estado.

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
 
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: myquote2.html
 
no changes added to commit (use "git add" and/or "git commit -a")
--

O resultado mostra que o repositório tem modificações pendentes em myquote2.html. Ótimo! Se a modificação que você quer desfazer, como no exemplo acima, não tiver sido adicionada à área de staging ainda, você pode apenas editar o arquivo e seguir adiante. O Git só começa a rastrear uma alteração quando você a adiciona à área de staging e a confirma no histórico do projeto.

Vamos "desfazer" as alterações em myquote2.html. Como este é um exemplo simplificado com alterações mínimas, há dois métodos disponíveis para desfazer.  Se a gente executar git checkout myquote2.html, o repositório vai restaurar myquote2.html para a versão já confirmada. Uma alternativa é executar git reset --hard, que vai reverter todo o repositório para o último commit.

git log

O comando git log permite que você liste, filtre e busque no histórico do projeto por alterações específicas. Enquanto o git status permite que você inspecione o diretório de trabalho e a área de staging, o git log só exibe o histórico confirmado.

O mesmo log do histórico confirmado pode ser encontrado na interface de usuário do Bitbucket acessando a exibição de "commits" de um repositório. A exibição de commits do repositório de demonstração se encontra em: https://bitbucket.org/dans9190/tutorial-documentation-tests/commits/all. Essa exibição vai ter um resultado parecido com o utilitário de linha de comando git log. Ela pode ser usada para encontrar e identificar um commit a ser desfeito.

No exemplo a seguir, você pode ver várias coisas no histórico, mas cada modificação é, na verdade, um commit, então é isso o que a gente precisa encontrar e desfazer.

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

nothing to commit, working tree clean

$ git log

commit 1f08a70e28d84d5034a8076db9103f22ec2e982c
Author: Daniel Stevens <dstevens@atlassian.com>
Date:   Wed Feb 7 17:06:50 2018 +0000

    Initial Bitbucket Pipelines configuration

commit 52f823ca251a132225dd1cc18ad768de8d336e84
Author: Daniel Stevens <dstevens@atlassian.com>
Date:   Fri Sep 30 15:50:58 2016 -0700

    repeated quote to show how a change moves through the process

commit 4801b87c2147dce83f1bf31acfcffa6cb1d7e0a5
Merge: 1a6a403 3b29606
Author: Dan Stevens [Atlassian] <dstevens@atlassian.com>
Date:   Fri Jul 29 18:45:34 2016 +0000

    Merged in changes (pull request #6)

    Changes

Vamos ver um pouco mais de perto um dos commits na lista.

commit 52f823ca251a132225dd1cc18ad768de8d336e84
Author: Daniel Stevens <dstevens@atlassian.com>
Date:   Fri Sep 30 15:50:58 2016 -0700
 
    repeated quote to show how a change moves through the process

O que você pode ver é que cada mensagem de commit tem quatro elementos:

Elemento Descrição

Hash de commit

Uma sequência alfanumérica (codificada em SHA-1) que identifica essa alteração específica

Autor

A pessoa que confirmou a mudança

Data

A data em que a alteração foi confirmada no projeto

Mensagem de commit

Uma sequência de texto que descreve as alterações.

Dica de prática recomendada: escreva mensagens de commit descritivas e curtas para ajudar a criar um repositório de trabalho mais harmonioso para todos.


 

 

Localize um commit específico

O mais provável é que a alteração que você quer desfazer vai estar em algum lugar mais para o fim do histórico do projeto, o que pode ser um tanto cansativo. Então, vamos aprender algumas operações básicas usando git log para encontrar uma alteração específica.

  1. Vá até a janela de terminal e navegue pela parte superior do repositório local usando o comando cd (mudar de diretório).
$ cd ~/repos/tutorial-documentation-tests/

Digite o comando git log --oneline. Adicionar --oneline vai exibir cada commit em uma linha só que permite visualizar mais históricos no terminal.

Pressione a tecla q para sair do log de commits e voltar ao prompt de comando quando quiser.

Você deve ver algo como o exemplo a seguir:

$ git log --oneline
1f08a70 (HEAD -> master, origin/master, origin/HEAD) Initial Bitbucket Pipelines configuration
52f823c repeated quote to show how a change moves through the process
4801b87 Merged in changes (pull request #6)
1a6a403 myquote edited online with Bitbucket
3b29606 (origin/changes) myquote2.html edited online with Bitbucket
8b236d9 myquote edited online with Bitbucket
235b9a7 testing prs
c5826da more changes
...
  1. Pressione a tecla q para voltar ao seu prompt de comando.
  2. Localize o commit com o hash c5826da e mais alterações na lista que o comando git log produziu. Alguém não escreveu uma mensagem de commit descritiva, então a gente vai ter que descobrir se as alterações necessárias estão aqui.
  3. Destaque e copie o hash de commit c5826da do resultado do git log na janela de terminal.
  4. Digite git show e cole ou transcreva o hash de commit que você copiou e pressione enter. Você deve ver algo assim:
$git show c5826daeb6ee3fd89e63ce35fc9f3594fe243605
commit c5826daeb6ee3fd89e63ce35fc9f3594fe243605
Author: Daniel Stevens <dstevens@atlassian.com>
Date:   Tue Sep 8 13:50:23 2015 -0700

    more changes

diff --git a/README.md b/README.md
index bdaee88..6bb2629 100644
--- a/README.md
+++ b/README.md
@@ -11,12 +11,7 @@ This README would normally document whatever steps are necessary to get your app
 ### How do I get set up? ###

 * Summary of set up
-* Configuration
-* Dependencies
-* Database configuration
-* How to run tests
-* Deployment instructions
-* more stuff and things
:

O prompt na parte inferior vai continuar sendo preenchido até que mostre toda a alteração. Pressione q para sair do prompt de comando.

Filtre o git log para encontrar um commit específico

Você pode filtrar e ajustar o resultado do git log com as adições a seguir:

Este filtro Faz isso Este comando de exemplo Pode resultar em
-<n>

Limita o número de confirmações mostradas

git log -10

Os 10 commits mais recentes no histórico

--after

--before

Limita as confirmações mostradas no período de tempo correlacionado

Você também pode usar --after "yyyy-mm-dd" --before "yyyy-mm-dd"

git log --after 2017-07-04

Todos os commits após 4 de julho de 2017

--author="name"

Lista todos os commits que correspondem com o nome do autor

git log --author="Alana"

Todos os commits feitos por qualquer autor com Alana no campo de nome

--grep="message string"

Retorna qualquer commit com uma mensagem que corresponda à sequência digitada

git log --grep="HOT-"

Todos os commits que têm HOT- como sequência de texto nas mensagens

 

Esta foi uma visualização muito breve do comando git log. Se você gostar de trabalhar no comando, talvez queira conferir o tutorial do git log avançado.

Desfaça uma alteração com git reset

Para começar, vamos apenas desfazer o commit mais recente no histórico. Neste caso, digamos que você acabou de ativar os pipelines da solução de integração e implementação contínuas do Bitbucket, mas percebeu que o script não está bem certo.

  1. Digite git log --oneline na janela de terminal.
  2. Copie o hash de commit do segundo commit no diretório log: 52f823c e depois pressione q para sair do log.
  3. Digite git reset --soft 52f823c na janela de terminal. O comando deve ser executado em plano de fundo se for bem-sucedido.  Pronto! Você desfez a primeira alteração. Agora vamos ver o resultado desta ação.
  4. Digite git status na janela de terminal e você vai ver que o commit foi desfeito e agora é uma alteração não confirmada. Você deve ver algo assim:
$ git status
On branch master
Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
  (use "git pull" to update your local branch)
 
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
 
    new file:   bitbucket-pipelines.yml
  1. Digite git log --oneline na janela de terminal. Você deve ver algo assim:
$ git log --oneline
52f823c repeated quote to show how a change moves through the process
4801b87 Merged in changes (pull request #6)
1a6a403 myquote edited online with Bitbucket
3b29606 (origin/changes) myquote2.html edited online with Bitbucket
8b236d9 myquote edited online with Bitbucket
235b9a7 testing prs
c5826da more changes
43a87f4 remivng
d5c4c62 a few small changes
23a7476 Merged in new-feature2 (pull request #3)
5cc4e1e add a commit message
cbbb5d6 trying a thing
438f956 adding section for permissions and cleaning up some formatting
23251c1 updated snipptes.xml organization into resources. other files misc changes
3f630f8 Adding file to track changes
...
  1. Você pode ver que o novo HEAD da ramificação é o commit 52f823c, o que é bem o objetivo.
  2. Pressione q para sair do log. Deixe o terminal aberto, porque, agora que você aprendeu a fazer uma redefinição simples, a gente vai tentar algo um pouco mais complexo.

Desfaça várias alterações com git reset

Digamos que você percebeu que a solicitação pull nº 6 (4801b87) precisou ser revisada e você quer manter um histórico limpo. Você deve redefinir HEAD para o commit 1a6a403. Desta vez, você vai usar o comando git reset.

  1. Digite git log --online
  2. Copie o hash de commit 1a6a403 (myquote editado on-line com o Bitbucket) que é o commit bem abaixo da solicitação pull nº 6 com as alterações que a gente quer desfazer.
  3. Digite git reset 1a6a403 na janela de terminal. O resultado deve ser algo assim:
$ git reset 1a6a403
Unstaged changes after reset:
M README.md
M myquote2.html

Você pode ver que as alterações estão em um estado não confirmado. Isso significa que a gente removeu várias alterações, tanto do histórico do projeto como da área de staging.

  1. Digite git status na janela de terminal. O resultado deve ser algo assim:

$ git status
On branch master
Your branch is behind 'origin/master' by 6 commits, and can be fast-forwarded.
  (use "git pull" to update your local branch)
 
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:   README.md
    modified:   myquote2.html
 
Untracked files:
  (use "git add <file>..." to include in what will be committed)
 
    bitbucket-pipelines.yml
 
no changes added to commit (use "git add" and/or "git commit -a")

Agora, você pode ver que a primeira alteração que a gente desfez (o arquivo bitbucket-pipelines.yml) não tem nenhum rastreamento pelo git. Isso acontece porque invocando git reset remove a alteração do cabeçalho da ramificação e do rastreamento da área de índice de git. O processo interno é um pouco mais complexo do que o conteúdo que a gente pode explicar aqui. Leia mais em git reset.

  1. Digite git log --oneline na janela de terminal.
1a6a403 myquote edited online with Bitbucket
8b236d9 myquote edited online with Bitbucket
43a87f4 remivng
d5c4c62 a few small changes
23a7476 Merged in new-feature2 (pull request #3)
5cc4e1e add a commit message
cbbb5d6 trying a thing
438f956 adding section for permissions and cleaning up some formatting
23251c1 updated snipptes.xml organization into resources. other files misc changes
3f630f8 Adding file to track changes
e52470d README.md edited online with Bitbucket
e2fad94 README.md edited online with Bitbucket
592f84f Merge branch 'master' into new-feature2 Merge branch  especially if it merges an updated upstream into a topic branch.
7d0bab8 added a line
879f965 adding to the quote file
8994332 Merged in HOT-235 (pull request #2)
b4a0b43 removed sarcastic remarks because they violate policy.
b5f5199 myquote2.html created online with Bitbucket
b851618 adding my first file
5b43509 writing and using tests

Agora, o resultado do log exibe que o histórico de commits também foi modificado e começa no commit 1a6a403. Para fins de demonstração e mais exemplos, digamos que a gente quer desfazer a redefinição que acabou de fazer. Depois de mais considerações, talvez a gente queira manter o conteúdo da solicitação pull nº 6. 

Como fazer push de redefinições no Bitbucket

Git resets são um dos poucos métodos de "desfazer" que o Git oferece. Redefinições são, no geral, consideradas uma opção "insegura" para desfazer alterações. Elas são boas para trabalhar no local em códigos isolados, mas são arriscadas quando compartilhadas com membros da equipe.

Para compartilhar uma ramificação redefinida com uma equipe remota, um "push forçado" deve ser executado. Um "push forçado" é iniciado executando git push -f. Um envio por push forçado destrói qualquer histórico na ramificação que foi criada após o ponto do push.

A seguir, um exemplo desse cenário "inseguro":

  • O desenvolvedor A está trabalhando em uma ramificação no desenvolvimento de um novo recurso.
  • O desenvolvedor B trabalha na mesma ramificação desenvolvendo um recurso separado.
  • O desenvolvedor B decide redefinir a ramificação para um estado anterior ao início do trabalho dos desenvolvedores A e B.
  • O desenvolvedor B, então, envia a ramificação de redefinição forçada para o repositório remoto.
  • O desenvolvedor A faz o pull da ramificação para receber atualizações. Durante esse pull, o desenvolvedor A recebe a atualização forçada. Isso redefine a ramificação local do desenvolvedor A para um momento anterior a qualquer trabalho de recurso e perde os commits.

Desfaça um git reset

Até agora, a gente passou os hashes SHA do git commit para o git reset. O resultado de git log não tem os commits redefinidos. Como ter esses commits de volta? O Git nunca exclui os commits por completo, a menos que tenha removido algum ponteiro deles. Além disso, o git armazena um log separado de todos os movimentos de referência chamado "reflog". A gente pode examinar o reflog executando git reflog.

1a6a403 HEAD@{0}: reset: moving to 1a6a403
1f08a70 HEAD@{1}: reset: moving to origin/master
1f08a70 HEAD@{2}: clone: from git@bitbucket.org:dans9190/tutorial-documentation-tests.git

O resultado de git reflog deve ser parecido com o que aparece acima. Você pode ver um histórico de ações no repositório. A linha superior é uma referência à redefinição que a gente fez para redefinir a solicitação pull nº 6. Vamos redefinir para restaurar a solicitação pull nº 6. A segunda coluna desse resultado de reflog indica um ponteiro de referência para uma ação de modificação feita no repositório. Aqui, HEAD@{0} é uma referência ao comando de redefinição que a gente já executou. É melhor não responder a esse comando de redefinição, então a gente vai restaurar o repositório para HEAD@{1}.

$ git reset --hard HEAD@{1}
HEAD is now at 1f08a70 Initial Bitbucket Pipelines configuration

Vamos examinar o histórico de commits do repositório com git log --oneline:

$git log --online
1f08a70 Initial Bitbucket Pipelines configuration
52f823c repeated quote to show how a change moves through the process
4801b87 Merged in changes (pull request #6)
1a6a403 myquote edited online with Bitbucket
3b29606 myquote2.html edited online with Bitbucket
8b236d9 myquote edited online with Bitbucket
235b9a7 testing prs
c5826da more changes
43a87f4 remivng
d5c4c62 a few small changes
23a7476 Merged in new-feature2 (pull request #3)
5cc4e1e add a commit message
cbbb5d6 trying a thing
438f956 adding section for permissions and cleaning up some formatting
23251c1 updated snipptes.xml organization into resources. other files misc changes
3f630f8 Adding file to track changes
e52470d README.md edited online with Bitbucket
e2fad94 README.md edited online with Bitbucket
592f84f Merge branch 'master' into new-feature2 Merge branch  especially if it merges an updated upstream into a topic branch.
7d0bab8 added a line
:

Aqui, é possível ver que o histórico de commits do repositório foi restaurado para a versão anterior que a gente estava experimentando. Dá para ver que o commit 4801b87 foi restaurado mesmo que parecesse perdido na primeira operação de redefinição. O git reflog é uma ferramenta avançada para desfazer alterações no repositório. Saiba mais sobre o uso aprofundado na página do git reflog.

git revert

O conjunto de exemplos anterior fez uma viagem e tanto no tempo, desfazendo operações usando git reset e git reflog. O Git contém outro utilitário de "desfazer" que é considerado, com frequência, "mais seguro" do que redefinir. A reversão cria commits novos que contêm uma inversão das alterações dos commits especificados. Esses commits de reversão podem, assim, ser enviados por push com segurança a repositórios remotos para serem compartilhados com outros desenvolvedores.

Na seção a seguir, você vai ver o uso de git revert. A gente vai continuar com o exemplo da seção anterior. Para começar, a gente vai examinar o log e encontrar um commit para reverter.

$ git log --online
1f08a70 Initial Bitbucket Pipelines configuration
52f823c repeated quote to show how a change moves through the process
4801b87 Merged in changes (pull request #6)
1a6a403 myquote edited online with Bitbucket
1f08a70 Initial Bitbucket Pipelines configuration
52f823c repeated quote to show how a change moves through the process
4801b87 Merged in changes (pull request #6)
1a6a403 myquote edited online with Bitbucket
3b29606 myquote2.html edited online with Bitbucket
8b236d9 myquote edited online with Bitbucket
235b9a7 testing prs
c5826da more changes
43a87f4 remivng
d5c4c62 a few small changes
23a7476 Merged in new-feature2 (pull request #3)
5cc4e1e add a commit message
cbbb5d6 trying a thing
438f956 adding section for permissions and cleaning up some formatting
23251c1 updated snipptes.xml organization into resources. other files misc changes
3f630f8 Adding file to track changes
e52470d README.md edited online with Bitbucket
e2fad94 README.md edited online with Bitbucket
592f84f Merge branch 'master' into new-feature2 Merge branch  especially if it merges an updated upstream into a topic branch.
7d0bab8 added a line
:

Para este exemplo, vamos usar o commit 1f08a70 mais recente como o commit em que a gente vai operar. Para este contexto, digamos que a gente quer desfazer as edições feitas nesse commit. Execute:

$ git revert 1f08a70

Isso vai dar a partida no fluxo de trabalho git merge. O Git vai criar um commit novo com conteúdo que é uma reversão do commit especificado para a reversão. O Git, assim, vai abrir um editor de texto configurado para preparar para uma nova mensagem de commit. As reversões são consideradas a opção mais segura de desfazer devido a esse fluxo de trabalho de commit. A criação de commits de reversão deixam um rastro visível no histórico de commits de quando uma operação de desfazer foi executada.

Você aprendeu a desfazer alterações!

Parabéns! Você terminou. Volte para este tutorial quando quiser ou veja a seção Como fazer alterações para se aprofundar mais. Continue fazendo um bom trabalho no Bitbucket!