Puis-je faire un retour partiel dans GIT

156

Est-il possible de restaurer un seul fichier ou certaines modifications dans un fichier en commit multi-fichiers?

Histoire complète J'ai commis un tas de fichiers. Un certain nombre de commits plus tard, quelqu'un qui restera sans nom (JACK !!!) a copié un fichier dans son référentiel et a validé plusieurs fichiers, écrasant certaines des modifications que j'ai faites. Je veux restaurer le fichier qui a été bloqué ou mieux encore, entrer et annuler deux modifications dans ce fichier. Cela devra être un commit de retour séparé car il a été tiré et poussé.

Embrayage
la source
Brian m'a mis sur la bonne voie. J'ai fini par utiliser git add --patch puis utiliser la fonction d'édition dans patch pour obtenir ce que je voulais.
Embrayage le
2
Je suis assez en retard au match, mais je pense avoir trouvé la «bonne» réponse .
ntc2
@ ntc2 Curieusement, la réponse acceptée a fonctionné beaucoup mieux pour moi que celle que vous avez liée - elle a créé des conflits de fusion que je dois résoudre, au lieu de simplement échouer à appliquer les correctifs.
Iiridayn
@liridayn Avez-vous lu toute ma réponse? Dans la section «variations», je prétends que l'ajout --3wayentraîne des conflits de fusion au lieu de l'échec de l'application des correctifs.
ntc2

Réponses:

204

Vous pouvez annuler le commit sans en créer un nouveau en ajoutant l'option '--no-commit'. Cela laisse tous les fichiers rétablis dans la zone intermédiaire. À partir de là, j'effectuerais une réinitialisation logicielle et ajouterais les modifications que je voulais vraiment. Pour un exemple de workflow:

git revert <sha-of-bad-commit> --no-commit
git reset   // This gets them out of the staging area
<edit bad file to look like it should, if necessary>
git add <bad-file>
git checkout . // This wipes all the undesired reverts still hanging around in the working copy
git commit
bobDevil
la source
42
Après la réinitialisation logicielle, vous pouvez également git add -pdémarrer une session interactive qui vous permet d'ajouter sélectivement des morceaux de fichiers au lieu de fichiers entiers. (Il y a aussi git reset -pà désinstaller sélectivement les changements. Bon à savoir, mais probablement pas ce que vous voulez dans ce scénario.)
Stéphan Kochen
1
C'était exactement ce que je recherchais: revenir en copie de travail, sélectionner les mecs de manière interactive, valider. Vous méritez un gros homme salé pour ça: *
das_weezul
Une autre solution inspirée par celle - ci serait revert, reset, stash -p(Stash « annule que je ne veux pas faire actualy »), addle reste,commit
Cyril Chapon
82

Vous pouvez appliquer de manière interactive l'ancienne version d'un fichier à l'aide de la checkoutcommande.

Par exemple, si vous savez COMMIToù le code à rajouter a été supprimé, vous pouvez exécuter la commande suivante:

git checkout -p COMMIT^ -- FILE_TO_REVERT

Git vous demandera de rajouter les morceaux manquants dans la version actuelle du fichier. Vous pouvez utiliser epour créer un correctif du changement avant de le réappliquer.

cmcginty
la source
Très utile si vous souhaitez restaurer une partie du fichier uniquement! Fondamentalement, pour obtenir des morceaux de la tête,git checkout -p path/to/file
falstaff
40

Vous pouvez simplement extraire manuellement l'ancien et bon contenu des fichiers que vous souhaitez rétablir git checkout. Par exemple, si vous souhaitez revenir my-important-fileà la version qu'il était dans la version abc123, vous pouvez faire

git checkout abc123 -- my-important-file

Vous avez maintenant l'ancien contenu de my-important-fileback, et pouvez même les éditer si vous le souhaitez, et vous engager comme d'habitude pour faire un commit qui annulera les modifications qu'il a apportées. S'il n'y a que quelques parties de son commit que vous souhaitez annuler, utilisez git add -ppour sélectionner seulement quelques morceaux du patch que vous validez.

Brian Campbell
la source
19
Ce n'est pas un revert, vous reprenez simplement une ancienne version du fichier. Un correct revertsupprimera les mauvaises modifications de commit dans le fichier, même s'il a été modifié plusieurs fois depuis ce mauvais commit, et vous ne voulez pas perdre ces modifications.
Totor
2
Totor, voyez ma réponse. Vous pouvez utiliser '-p' pour enregistrer les modifications. La réponse est très proche de celle-ci en fait.
cmcginty
1
Ou vous pouvez combiner cela en git checkout -p.
Léo Lam
31

J'ai trouvé un moyen de le faire sur la liste de diffusion Git:

git show <commit> -- <path> | git apply --reverse

Source: http://git.661346.n2.nabble.com/Revert-a-single-commit-in-a-single-file-td6064050.html#a6064406

Variations

Cette commande échoue (ne provoquant aucun changement) si le correctif ne s'applique pas proprement, mais avec --3wayvous, des conflits que vous pouvez ensuite résoudre manuellement (dans ce cas, la réponse de Casey pourrait être plus pratique):

git show <commit> -- <path> | git apply --reverse --3way

Vous pouvez également l'utiliser pour annuler partiellement plusieurs commits, par exemple:

git log -S<string> --patch | git apply --reverse

pour restaurer les fichiers avec des modifications correspondant <string>à n'importe quel commit. C'est exactement ce dont j'avais besoin dans mon cas d'utilisation (quelques commits séparés ont introduit des modifications similaires dans différents fichiers, ainsi que des modifications d'autres fichiers de manière indépendante que je ne voulais pas rétablir).

Si vous avez diff.noprefix=truedéfini dans votre ~/.gitconfigalors vous devez ajouter -p0à la git applycommande, par exemple

git show <commit> -- <path> | git apply -p0 --reverse
ntc2
la source
Cette réponse est bien meilleure que celle acceptée: "mieux encore, entrez et annulez deux changements dans ce fichier".
réécrit le
Je voulais restaurer seulement une partie d'un commit, alors j'ai fait: (1) git show ... > foo.txt (2) éditer foo.txtpour supprimer les diffs que je ne voulais pas revenir (3) git apply --reverse foo.txt pour rétablir les diffs toujours répertoriés foo.txt.
cxw
L'ajout de --binary --full-index à git show part pourrait également être une variante utile
Laurynas Biveinis