Revertir parte de un commit con git

144

Quiero revertir un commit particular en git. Desafortunadamente, nuestra organización todavía usa CVS como estándar, por lo que cuando me comprometo con CVS, varios git commits se agrupan en uno. En este caso, me encantaría destacar el git commit original, pero eso es imposible.

¿Existe un enfoque similar al git add --patchque me permitiría editar selectivamente las diferencias para decidir qué partes de un compromiso revertir?

skiphoppy
fuente
Más soluciones aquí , pero centrándose en limitar la reversión parcial a archivos específicos.
ntc2

Respuestas:

226

Use la opción --no-commit( -n) para git revert, luego desestabilizar los cambios, luego 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: Los archivos que agrega usando git add --patch son los archivos que desea revertir, no los archivos que desea conservar.

mipadi
fuente
13
Puede valer la pena agregar el comando final requerido, para aquellos que no están tan familiarizados con git: después de comprometerse, git reset --harddescartar los otros cambios que no desea revertir.
temblorosa
15
git reset --hardes peligroso para los novatos, ya que podría perder las ediciones deseadas. En lugar de acostumbrarse git status, esto sugiere git checkout -- FILE..revertir las cosas de manera más segura.
Tino
¡Qué omisión tan grande git! git revertDebería tomar una --patchdiscusión.
Kaz
@Kaz: git revertse utiliza para revertir confirmaciones completas. Puede usar git checkout -ppara seleccionar interactivamente bits para revertir.
mipadi
1
También me gustaría agregar lo (quizás) obvio, que primero es guardar su trabajo . Ya sea commiten primer lugar, o stash, a continuación, tratar revert.
Felipe Alvarez
39

He usado lo siguiente con éxito.

Primero revierta el compromiso completo (lo pone en el índice) pero no lo confirme.

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

Luego, elimine interactivamente los BUENOS cambios revertidos del índice

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

Luego comete la diferencia inversa de los cambios incorrectos

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

Finalmente, los BUENOS cambios revertidos (que no han sido organizados por el comando reset) todavía están en el árbol de trabajo. Necesitan ser limpiados. Si no quedan otros cambios no confirmados en el árbol de trabajo, esto puede hacerse

git reset --hard
usuario1338062
fuente
55
¿No es esta una alternativa superior a la respuesta aceptada (que usa reset HEAD .), porque no requiere la limpieza final del directorio de trabajo?
Steven Lu
2
Esta respuesta es superior, porque reset -pes más corta que la reset HEADseguida add -p. Pero aún requiere la limpieza, ya que los "buenos" trozos que se han restablecido todavía están en el directorio de trabajo después de la confirmación.
Chiel ten Brinke
Esta respuesta no es superior porque la eliminación interactiva de los cambios que desea a menudo es confusa y propensa a errores, especialmente si alguno de ellos requiere una edición.
Kaz
5

Personalmente, prefiero esta versión, que reutiliza el mensaje de confirmación generado automáticamente y le da al usuario la oportunidad de editar y pegar la palabra "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
fuente
4

Solución:

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
fuente
Ayudaría a las
1
@Krzysztof ¿Por qué es importante el proceso de pago al final y por qué esta solución es diferente de la del usuario1338062?
Martin
3

Otra alternativa (si su versión actual de un archivo no está muy lejos de la versión que está intentando revertir) es obtener el hash de la confirmación inmediatamente antes de la que desea revertir parcialmente (desde git log). Entonces su comando se convierte en:

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

Esto cambia los archivos en su árbol de trabajo pero no crea confirmaciones, por lo que si realmente rellena puede comenzar de nuevo git reset --hard -- file/you/want/to/fix.ext.

Walf
fuente
1

Puede usar git-revert -n, y luego usar add --patch para seleccionar trozos.

William Pursell
fuente