Como desfazer uma operação git cherry-pick

GitBeginner
Pratique Agora

Introdução

O recurso cherry-pick do Git permite aplicar commits específicos de um branch para outro. Embora poderoso, às vezes pode ser necessário reverter uma operação cherry-pick devido a erros ou conflitos. Neste laboratório, você aprenderá como realizar um cherry-pick e, em seguida, usar vários métodos para desfazê-lo quando necessário. Ao final, você terá experiência prática com operações cherry-pick e as habilidades para se recuperar de problemas comuns.

Configurando um Repositório de Teste

Nesta etapa, criaremos um repositório Git de teste para praticar operações cherry-pick. Isso fornecerá um ambiente seguro para experimentar vários comandos Git.

Criando um Novo Repositório Git

Vamos começar criando um novo diretório para nosso repositório de teste e inicializando-o como um repositório Git:

mkdir -p ~/project/cherry-pick-lab
cd ~/project/cherry-pick-lab
git init

Você deve ver uma saída semelhante a:

Initialized empty Git repository in /home/labex/project/cherry-pick-lab/.git/

Configurando a Configuração do Usuário Git

Antes de podermos fazer commits, precisamos configurar um nome de usuário e e-mail para o Git:

git config --local user.name "LabEx User"
git config --local user.email "labex@example.com"

Criando Commits Iniciais no Branch Principal (Main)

Vamos criar alguns commits iniciais no branch principal:

## Create and commit the first file
echo "## Cherry Pick Lab" > README.md
git add README.md
git commit -m "Initial commit with README"

## Create and commit a second file
echo "console.log('Hello, world!');" > app.js
git add app.js
git commit -m "Add main application file"

Visualizando o Histórico de Commits

Vamos verificar nosso histórico de commits para garantir que tudo esteja configurado corretamente:

git log --oneline

Você deve ver uma saída semelhante a:

abcd123 (HEAD -> main) Add main application file
efgh456 Initial commit with README

Os hashes de commit reais serão diferentes no seu sistema. Anote o hash do commit para "Add main application file", pois o usaremos mais tarde.

Criando um Branch de Funcionalidade (Feature Branch)

Agora, vamos criar um branch de funcionalidade onde faremos algumas alterações adicionais:

git checkout -b feature-branch

Você deve ver uma saída semelhante a:

Switched to a new branch 'feature-branch'

Agora vamos adicionar alguns commits a este branch de funcionalidade:

## Create and commit a new feature file
echo "function newFeature() { return 'awesome'; }" > feature.js
git add feature.js
git commit -m "Add new feature function"

## Modify the README file
echo -e "## Cherry Pick Lab\n\nThis repo demonstrates git cherry-pick operations." > README.md
git add README.md
git commit -m "Update README with project description"

Agora temos um branch principal com dois commits e um branch de funcionalidade com dois commits adicionais. Na próxima etapa, usaremos cherry-pick para aplicar um dos commits do branch de funcionalidade ao branch principal.

Realizando uma Operação Cherry-pick

Nesta etapa, aprenderemos como usar o comando cherry-pick para aplicar um commit específico de um branch para outro. Isso é útil quando você deseja incorporar seletivamente as alterações de um branch para outro.

Entendendo Cherry-pick

Cherry-picking no Git permite que você escolha um commit específico de um branch e o aplique a outro branch. Ao contrário do merge ou rebase, que normalmente aplicam vários commits, o cherry-picking aplica apenas um commit por vez.

Voltando para o Branch Principal

Primeiro, vamos voltar para o branch principal onde queremos aplicar um commit do branch de funcionalidade:

git checkout main

Você deve ver a saída confirmando a troca:

Switched to branch 'main'

Visualizando os Commits do Branch de Funcionalidade

Antes de fazer o cherry-pick, vamos examinar os commits no branch de funcionalidade que podemos querer aplicar ao main:

git log feature-branch --oneline

Isso mostrará todos os commits no branch de funcionalidade, incluindo aqueles compartilhados com o branch principal. Você verá uma saída semelhante a:

1234abc Update README with project description
5678def Add new feature function
abcd123 Add main application file
efgh456 Initial commit with README

Anote o hash do commit para "Add new feature function" (neste exemplo, é 5678def). Usaremos este hash na próxima etapa.

Fazendo Cherry-pick de um Commit

Agora, vamos fazer o cherry-pick do commit "Add new feature function" do branch de funcionalidade para nosso branch principal:

git cherry-pick [COMMIT_HASH]

Substitua [COMMIT_HASH] pelo hash real que você anotou anteriormente. Por exemplo:

git cherry-pick 5678def

Se o cherry-pick for bem-sucedido, você verá uma saída semelhante a:

[main 98765ab] Add new feature function
 1 file changed, 1 insertion(+)
 create mode 100644 feature.js

Verificando o Cherry-pick

Vamos confirmar se o cherry-pick funcionou como esperado:

git log --oneline

Você deve agora ver o commit cherry-picked no histórico do seu branch principal:

98765ab (HEAD -> main) Add new feature function
abcd123 Add main application file
efgh456 Initial commit with README

Você também pode verificar se o arquivo existe:

ls -la

Você deve ver feature.js listado na saída:

total 16
drwxr-xr-x 3 labex labex 4096 Jan 1 00:00 .
drwxr-xr-x 3 labex labex 4096 Jan 1 00:00 ..
drwxr-xr-x 8 labex labex 4096 Jan 1 00:00 .git
-rw-r--r-- 1 labex labex   29 Jan 1 00:00 app.js
-rw-r--r-- 1 labex labex   42 Jan 1 00:00 feature.js
-rw-r--r-- 1 labex labex   16 Jan 1 00:00 README.md

A operação cherry-pick aplicou com sucesso o commit do branch de funcionalidade ao branch principal. Na próxima etapa, aprenderemos como desfazer este cherry-pick, se necessário.

Desfazendo um Cherry-pick com Git Reset

Agora que fizemos o cherry-pick de um commit com sucesso, vamos aprender como desfazer essa operação. Nesta etapa, usaremos o comando git reset, que é a maneira mais direta de desfazer um cherry-pick recente.

Entendendo Git Reset

O comando git reset move o ponteiro do branch atual para um commit especificado, efetivamente "desfazendo" quaisquer commits que vieram após esse ponto. Existem três modos principais de git reset:

  • --soft: Move o ponteiro do branch, mas deixa as alterações preparadas (staged)
  • --mixed (padrão): Move o ponteiro do branch e desfaz as alterações preparadas (unstages)
  • --hard: Move o ponteiro do branch e descarta todas as alterações

Para desfazer um cherry-pick, usaremos a opção --hard para remover completamente o commit cherry-picked e suas alterações.

Verificando o Status Atual

Primeiro, vamos verificar nosso status atual para confirmar que estamos no branch principal com o commit cherry-picked:

git log --oneline -n 3

Você deve ver uma saída semelhante a:

98765ab (HEAD -> main) Add new feature function
abcd123 Add main application file
efgh456 Initial commit with README

Desfazendo o Cherry-pick com Git Reset

Para desfazer a operação cherry-pick, usaremos git reset com a opção --hard para mover o ponteiro do branch de volta um commit:

git reset --hard HEAD~1

Este comando diz ao Git para redefinir o branch para o commit anterior ao HEAD atual (a parte ~1 significa "um commit antes").

Você deve ver uma saída semelhante a:

HEAD is now at abcd123 Add main application file

Verificando o Reset

Vamos verificar se o cherry-pick foi desfeito:

git log --oneline

Você deve ver que o commit cherry-picked não está mais no histórico:

abcd123 (HEAD -> main) Add main application file
efgh456 Initial commit with README

Vamos também verificar se o arquivo feature.js foi removido:

ls -la

A saída não deve incluir feature.js:

total 12
drwxr-xr-x 3 labex labex 4096 Jan 1 00:00 .
drwxr-xr-x 3 labex labex 4096 Jan 1 00:00 ..
drwxr-xr-x 8 labex labex 4096 Jan 1 00:00 .git
-rw-r--r-- 1 labex labex   29 Jan 1 00:00 app.js
-rw-r--r-- 1 labex labex   16 Jan 1 00:00 README.md

Parabéns! Você desfez com sucesso uma operação cherry-pick usando git reset. Este método é limpo e simples, mas ele reescreve o histórico, por isso só deve ser usado para alterações locais que não foram enviadas para um repositório compartilhado.

Desfazendo um Cherry-pick com Git Revert

Na etapa anterior, usamos git reset para desfazer um cherry-pick. No entanto, git reset reescreve o histórico, o que pode ser problemático se você já enviou suas alterações para um repositório compartilhado. Nesta etapa, aprenderemos como usar git revert para desfazer com segurança um cherry-pick sem reescrever o histórico.

Entendendo Git Revert

O comando git revert cria um novo commit que desfaz as alterações introduzidas por um commit anterior. Ao contrário de git reset, que remove commits do histórico, git revert adiciona um novo commit que neutraliza as alterações, preservando o histórico de commits.

Realizando um Cherry-pick Novamente

Primeiro, vamos fazer o cherry-pick do commit novamente para que tenhamos algo para reverter:

## Get the commit hash from the feature branch
FEATURE_HASH=$(git log feature-branch --oneline | grep "new feature" | cut -d ' ' -f 1)

## Cherry-pick the commit
git cherry-pick $FEATURE_HASH

Você deve ver uma saída semelhante a:

[main 98765ab] Add new feature function
 1 file changed, 1 insertion(+)
 create mode 100644 feature.js

Verificando o Status Atual

Vamos confirmar se o cherry-pick foi bem-sucedido:

git log --oneline -n 3
ls -la

Você deve ver o commit cherry-picked no histórico e o arquivo feature.js na listagem do diretório.

Revertendo o Cherry-pick

Agora, vamos usar git revert para desfazer o cherry-pick, preservando o histórico:

git revert HEAD --no-edit

A flag --no-edit diz ao Git para usar a mensagem de commit padrão sem abrir um editor.

Você deve ver uma saída semelhante a:

[main abc9876] Revert "Add new feature function"
 1 file changed, 1 deletion(-)
 delete mode 100644 feature.js

Verificando o Revert

Vamos verificar o histórico de commits:

git log --oneline -n 4

Você deve ver tanto o commit cherry-picked quanto o commit de revert:

abc9876 (HEAD -> main) Revert "Add new feature function"
98765ab Add new feature function
abcd123 Add main application file
efgh456 Initial commit with README

Agora, vamos verificar se o arquivo feature.js foi removido:

ls -la

A saída não deve incluir feature.js:

total 12
drwxr-xr-x 3 labex labex 4096 Jan 1 00:00 .
drwxr-xr-x 3 labex labex 4096 Jan 1 00:00 ..
drwxr-xr-x 8 labex labex 4096 Jan 1 00:00 .git
-rw-r--r-- 1 labex labex   29 Jan 1 00:00 app.js
-rw-r--r-- 1 labex labex   16 Jan 1 00:00 README.md

Embora tanto git reset quanto git revert alcancem o mesmo resultado final (removendo as alterações introduzidas pelo cherry-pick), eles o fazem de maneiras diferentes:

  • git reset remove o commit do histórico, o que pode causar problemas se o commit foi compartilhado com outras pessoas.
  • git revert adiciona um novo commit que desfaz as alterações, preservando o histórico de commits, o que é mais seguro para repositórios compartilhados.

A escolha entre esses métodos depende da sua situação específica:

  • Use git reset para alterações locais que não foram compartilhadas
  • Use git revert para alterações que foram enviadas para um repositório compartilhado

Lidando com Conflitos de Cherry-pick

Às vezes, quando você faz o cherry-pick de um commit, o Git pode encontrar conflitos se as alterações no commit entrarem em conflito com as alterações no seu branch atual. Nesta etapa, aprenderemos como lidar com conflitos de cherry-pick e como abortar uma operação de cherry-pick.

Criando um Cenário com Potenciais Conflitos

Primeiro, vamos criar um cenário que resultará em um conflito de cherry-pick:

## Switch to main branch and modify README.md
git checkout main
echo -e "## Cherry Pick Lab\n\nThis is the main branch README." > README.md
git commit -am "Update README in main branch"

## Switch to feature branch and make a conflicting change to README.md
git checkout feature-branch
echo -e "## Cherry Pick Lab\n\nThis README has been updated in the feature branch." > README.md
git commit -am "Update README in feature branch"

Tentando um Cherry-pick com Conflitos

Agora, vamos voltar para o branch principal e tentar fazer o cherry-pick do commit do branch de recurso:

git checkout main
CONFLICT_HASH=$(git log feature-branch --oneline | grep "Update README in feature" | cut -d ' ' -f 1)
git cherry-pick $CONFLICT_HASH

Como ambos os branches modificaram as mesmas linhas em README.md, você deve ver um conflito:

error: could not apply a1b2c3d... Update README in feature branch
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'

Visualizando o Conflito

Vamos examinar o conflito:

git status

Você deve ver uma saída indicando um conflito em README.md:

On branch main
You are currently cherry-picking commit a1b2c3d.
  (fix conflicts and run "git cherry-pick --continue")
  (use "git cherry-pick --abort" to cancel the cherry-pick operation)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
  both modified:   README.md

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

Vamos olhar o conteúdo do arquivo em conflito:

cat README.md

Você deve ver algo como:

## Cherry Pick Lab

<<<<<<< HEAD
This is the main branch README.
=======
This README has been updated in the feature branch.
>>>>>>> a1b2c3d... Update README in feature branch

Resolvendo o Conflito

Para resolver o conflito, precisamos editar o arquivo e decidir quais alterações manter. Vamos modificar README.md para incluir ambas as alterações:

echo -e "## Cherry Pick Lab\n\nThis is the main branch README.\n\nThis README has also been updated with content from the feature branch." > README.md

Agora, vamos marcar o conflito como resolvido e continuar o cherry-pick:

git add README.md
git cherry-pick --continue

O Git abrirá um editor com uma mensagem de commit padrão. Salve e feche o editor para concluir o cherry-pick.

Abortando um Cherry-pick

Às vezes, você pode decidir que não quer resolver os conflitos e prefere cancelar a operação de cherry-pick. Vamos criar outro conflito e, em seguida, abortar o cherry-pick:

## Create another conflicting commit in feature branch
git checkout feature-branch
echo "// This will conflict with app.js in main" > app.js
git commit -am "Modify app.js in feature branch"

## Try to cherry-pick this commit to main
git checkout main
ANOTHER_CONFLICT=$(git log feature-branch --oneline | grep "Modify app.js" | cut -d ' ' -f 1)
git cherry-pick $ANOTHER_CONFLICT

Você deve ver outro conflito. Desta vez, vamos abortar o cherry-pick:

git cherry-pick --abort

Você deve ver que a operação de cherry-pick foi cancelada e seu diretório de trabalho foi restaurado ao seu estado anterior:

git status

Output:

On branch main
nothing to commit, working tree clean

Lidar com conflitos durante operações de cherry-pick é uma habilidade essencial para usuários do Git. Você tem três opções quando encontra um conflito:

  1. Resolva o conflito manualmente, depois use git add e git cherry-pick --continue
  2. Ignore o commit em conflito com git cherry-pick --skip
  3. Aborte toda a operação de cherry-pick com git cherry-pick --abort

A melhor abordagem depende da situação específica e dos requisitos do seu projeto.

Resumo

Neste laboratório, você ganhou experiência prática com o recurso cherry-pick do Git e aprendeu várias maneiras de desfazer operações de cherry-pick:

  1. Você criou um repositório de teste com vários branches e commits para praticar
  2. Você realizou uma operação de cherry-pick para aplicar um commit específico de um branch para outro
  3. Você aprendeu como desfazer um cherry-pick usando git reset, que é adequado para alterações locais
  4. Você explorou como desfazer com segurança um cherry-pick usando git revert, que preserva o histórico
  5. Você praticou o tratamento de conflitos de cherry-pick e aprendeu como abortar uma operação de cherry-pick

Essas habilidades são inestimáveis ​​ao trabalhar com Git em projetos do mundo real. O cherry-picking permite que você aplique seletivamente alterações entre branches, enquanto saber como desfazer cherry-picks ajuda você a se recuperar de erros e manter um histórico do Git limpo.

Lembre-se de que diferentes métodos de desfazer servem a propósitos diferentes:

  • Use git reset para alterações locais que não foram compartilhadas
  • Use git revert para alterações que foram enviadas para um repositório compartilhado
  • Use git cherry-pick --abort para cancelar uma operação de cherry-pick em andamento

Ao entender essas opções, você pode escolher o método mais apropriado para sua situação específica.