Revertendo parte de um commit com git

144

Eu quero reverter um commit específico no git. Infelizmente, nossa organização ainda usa o CVS como padrão; portanto, quando eu me comprometo com o CVS, vários commits git são agrupados em um. Nesse caso, eu adoraria destacar o commit git original, mas isso é impossível.

Existe uma abordagem semelhante à git add --patchque me permita editar seletivamente diffs para decidir quais partes de um commit serão revertidas?

skiphoppy
fonte
Mais soluções aqui , mas com foco na limitação da reversão parcial para arquivos específicos.
Ntc2

Respostas:

226

Use a opção --no-commit( -n) para git revert, em seguida, desestabilize as alterações e use git add --patch:

$ git revert -n $bad_commit    # Revert the commit, but don't commit the changes
$ git reset HEAD .             # Unstage the changes
$ git add --patch .            # Add whatever changes you want
$ git commit                   # Commit those changes

Nota: Os arquivos adicionados usando o git add --patch são os que você deseja reverter, e não os que deseja manter.

mipadi
fonte
13
Pode valer a pena adicionar o comando final necessário, para aqueles que não estão familiarizados com o git: após confirmar, git reset --hardpara descartar as outras alterações que você não deseja reverter.
tremby 26/09/12
15
git reset --hardé perigoso para iniciantes, pois pode perder as edições desejadas. Em vez de se acostumar git status, isso sugere git checkout -- FILE..reverter as coisas com mais segurança.
Tino
Que omissão escancarada git; git revertdeve apenas ter uma --patchdiscussão.
Kaz
@ Kaz: git reverté usado para reverter confirmações inteiras. Você pode usar git checkout -ppara selecionar interativamente os bits a serem revertidos.
Mipadi 31/03/19
1
Gostaria também de acrescentar o (talvez) óbvio, que primeiro salva seu trabalho . Ou commitprimeiro, ou stash, então tente revert.
Felipe Alvarez
39

Eu usei o seguinte com sucesso.

Primeiro, reverta a confirmação completa (coloca-a no índice), mas não a confirme.

git revert -n <sha1>  # -n is short for --no-commit

Em seguida, remova interativamente as mudanças GOOD revertidas do índice

git reset -p          # -p is short for --patch  

Em seguida, confirme diff reverso das mudanças ruins

git commit -m "Partially revert <sha1>..."

Finalmente, as alterações GOOD revertidas (que não foram testadas pelo comando reset) ainda estão na árvore de trabalho. Eles precisam ser limpos. Se nenhuma outra alteração não confirmada for deixada na árvore de trabalho, isso poderá ser feito por

git reset --hard
user1338062
fonte
5
Essa não é uma alternativa superior à resposta aceita (que usa reset HEAD .), porque não requer a limpeza final do diretório de trabalho?
Steven Lu
2
Esta resposta é superior, porque reset -pé mais curta que a reset HEADseguida por add -p. Mas ainda requer a limpeza, pois os "bons" blocos que foram redefinidos ainda estão no diretório de trabalho após a confirmação.
Chiel ten Brinke
Essa resposta não é superior porque a remoção interativa das alterações desejadas geralmente é confusa e propensa a erros - especialmente se alguma delas exigir uma edição.
Kaz
5

Pessoalmente, prefiro esta versão, que reutiliza a mensagem de confirmação gerada automaticamente e oferece ao usuário a oportunidade de editar e colar a palavra "Parcialmente" antes de finalmente confirmar.

# generate a revert commit
# note the hash printed to console on success
git revert --no-edit <hash to revert>

# undo that commit, but not its changes to the working tree
# (reset index to commit-before-last; that is, one graph entry up from HEAD)
git reset HEAD~1

# interactively add reversions
git add -p

# commit with pre-filled message
git commit -c <hash from revert commit, printed to console after first command>

# reset the rest of the current directory's working tree to match git
# this will reapply the excluded parts of the reversion to the working tree
# you may need to change the paths to be checked out
# be careful not to accidentally overwrite unsaved work
git checkout -- .
jeffcook2150
fonte
4

Solução:

git revert --no-commit <commit hash>
git reset -p        # every time choose 'y' if you want keep the change, otherwise choose 'n'
git commit -m "Revert ..."
git checkout -- .   # Don't forget to use it.
Krzysztof Kaczmarski
fonte
Ajudaria as pessoas se você dissesse que é diferente da solução aceita
CharlesB
1
@Krzysztof Por que o checkout no final é importante e por que essa solução é diferente da do user1338062?
martin
3

Outra alternativa (se a versão atual de um arquivo não estiver muito longe da versão que você está tentando reverter) é obter o hash do commit imediatamente antes do que você deseja reverter parcialmente (de git log). Então seu comando se torna:

$ git checkout -p <hash_preceding_commit_to_revert> -- file/you/want/to/fix.ext

Isso altera os arquivos na sua árvore de trabalho, mas não cria confirmações; portanto, se você realmente encher tudo, pode começar novamente git reset --hard -- file/you/want/to/fix.ext.

Walf
fonte
1

Você pode usar git-revert -n e, em seguida, use add --patch para selecionar pedaços.

William Pursell
fonte