Rétablir une partie d'un commit avec git

144

Je veux revenir sur un commit particulier dans git. Malheureusement, notre organisation utilise toujours CVS comme standard, donc lorsque je me reconnecte à CVS, plusieurs commits git sont regroupés en un seul. Dans ce cas, j'adorerais sélectionner le commit git original, mais c'est impossible.

Existe-t-il une approche similaire à git add --patchcelle qui me permettrait d'éditer sélectivement les différences pour décider quelles parties d'un commit annuler?

sauter
la source
Plus de solutions ici , mais en se concentrant sur la limitation du retour partiel à des fichiers spécifiques.
ntc2

Réponses:

226

Utilisez l' option --no-commit( -n) pour git revert, puis annulez les modifications, puis utilisez 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

Remarque: les fichiers que vous ajoutez à l'aide de git add --patch sont les fichiers que vous souhaitez restaurer, et non les fichiers que vous souhaitez conserver.

mipadi
la source
13
Cela peut valoir la peine d'ajouter la dernière commande requise, pour ceux qui ne sont pas si familiers avec git: après la validation, git reset --hardpour annuler les autres modifications que vous ne vouliez pas annuler.
tremby le
15
git reset --hardest dangereux pour les débutants, car il pourrait perdre les modifications voulues. Au lieu de cela, habituez-vous git status, cela suggère git checkout -- FILE..de revenir en arrière de manière plus sûre.
Tino
Quelle omission béante git; git revertdevrait juste prendre un --patchargument.
Kaz
@Kaz: git revertest utilisé pour annuler les commits entiers. Vous pouvez utiliser git checkout -ppour sélectionner de manière interactive les bits à rétablir.
mipadi
1
Je voudrais également ajouter la (peut-être) évidente, qui consiste d' abord à enregistrer votre travail . Soit d' commitabord, soit stashensuite essayez revert.
Felipe Alvarez
39

J'ai utilisé ce qui suit avec succès.

Commencez par annuler le commit complet (le met dans l'index) mais ne le validez pas.

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

Ensuite, supprimez de manière interactive les bonnes modifications rétablies de l'index

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

Puis commettez la différence inverse des mauvais changements

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

Enfin, les bons changements rétablis (qui n'ont pas été mis en scène par la commande de réinitialisation) sont toujours dans l'arborescence de travail. Ils doivent être nettoyés. Si aucune autre modification non validée n'est laissée dans l'arborescence de travail, cela peut être

git reset --hard
user1338062
la source
5
N'est-ce pas une alternative supérieure à la réponse acceptée (qui utilise reset HEAD .), car elle ne nécessite pas le nettoyage final du répertoire de travail?
Steven Lu
2
Cette réponse est supérieure, car elle reset -pest plus courte que reset HEADsuivie par add -p. Mais cela nécessite toujours le nettoyage, puisque les "bons" morceaux qui ont été réinitialisés sont toujours dans le répertoire de travail après la validation.
Chiel ten Brinke
Cette réponse n'est pas supérieure car la suppression interactive des modifications souhaitées est souvent déroutante et sujette à des erreurs - surtout si l'une d'entre elles nécessite une modification.
Kaz
5

Personnellement, je préfère cette version, qui réutilise le message de validation généré automatiquement et donne à l'utilisateur la possibilité d'éditer et de coller le mot «partiellement» avant de finalement s'engager.

# 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
la source
4

Solution:

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
la source
Cela aiderait les gens si vous disiez en quoi c'est différent de la solution acceptée
CharlesB
1
@Krzysztof Pourquoi le paiement à la fin est-il important et pourquoi cette solution est-elle différente de celle de user1338062?
martin
3

Une autre alternative (si votre version actuelle d'un fichier n'est pas trop éloignée de la version que vous essayez de rétablir) est d'obtenir le hachage du commit juste avant celui que vous souhaitez annuler partiellement (à partir de git log). Alors votre commande devient:

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

Cela modifie les fichiers dans votre arbre de travail mais ne crée aucun commit, donc si vous avez vraiment du mal, vous pouvez simplement recommencer git reset --hard -- file/you/want/to/fix.ext.

Walf
la source
1

Vous pouvez utiliser git-revert -n, puis utiliser add --patch pour sélectionner des morceaux.

William Pursell
la source