845 votes

Comment puis-je rétablir une version antérieure d'un seul fichier ?

Existe-t-il un moyen de parcourir les différents commits d'un fichier ? Disons que j'ai modifié un fichier 5 fois et que je veux revenir à la modification 2, après avoir déjà commis et poussé vers un dépôt.

D'après ce que j'ai compris, le seul moyen est de conserver de nombreuses branches, ai-je bien compris ? Si j'ai raison, je vais avoir des centaines de branches dans quelques jours, donc je ne comprends probablement pas vraiment.

Quelqu'un pourrait-il clarifier ce point ?

1 votes

Voulez-vous revenir au fichier avec la modification 2 de façon permanente, ou voulez-vous temporairement retirer la modification 2 pour voir à quoi ressemblait le fichier à ce moment-là ?

0 votes

J'utilise toujours SVN quotidiennement et git seulement un peu, donc je ne peux pas vous aider avec votre question - mais j'ai pensé que vous pourriez être intéressé par cette ressource - le livre "Pro Git" est disponible en ligne ici -. progit.org/book Je possède et j'ai lu le livre - il est excellent, dommage que je n'aie pas l'occasion d'utiliser git en ce moment ; il semble que vous ayez juste besoin d'obtenir une version historique de votre fichier et ensuite de réengager l'ancienne version, pas de branche ?

0 votes

@Jim Hurne : Oui, je veux rétablir le fichier de façon permanente. @house9 : Merci, je vais jeter un coup d'oeil à ce livre. La partie historique semble bonne, comment est-elle faite ?

1145voto

Jefromi Points 127932

Commençons par une description qualitative de ce que nous voulons faire (une grande partie de ceci est dit dans la réponse de Ben Straub). Nous avons fait un certain nombre de commits, dont cinq ont modifié un fichier donné, et nous voulons revenir à l'une des versions précédentes. Tout d'abord, git ne conserve pas les numéros de version des fichiers individuels. Il ne fait que suivre le contenu - un commit est essentiellement un instantané de l'arbre de travail, accompagné de quelques métadonnées (par exemple, le message du commit). Donc, nous devons savoir quel commit a la version du fichier que nous voulons. Une fois que nous le savons, nous devons faire un nouveau commit pour remettre le fichier dans cet état. (Nous ne pouvons pas simplement jouer avec l'historique, car nous avons déjà poussé ce contenu, et l'édition de l'historique perturbe tout le monde).

Commençons donc par trouver le bon engagement. Vous pouvez voir les commits qui ont fait des modifications au(x) fichier(s) donné(s) très facilement :

git log path/to/file

Si vos messages de commit ne sont pas suffisants, et que vous avez besoin de voir ce qui a été fait au fichier à chaque commit, utilisez la fonction -p/--patch option :

git log -p path/to/file

Ou, si vous préférez la vue graphique de gitk

gitk path/to/file

Vous pouvez également le faire une fois que vous avez lancé gitk par le biais du menu de vue ; une des options pour une vue est une liste de chemins à inclure.

Dans tous les cas, vous pourrez trouver le SHA1 (hash) du commit avec la version du fichier que vous voulez. Maintenant, tout ce que vous avez à faire est ceci :

# get the version of the file from the given commit
git checkout <commit> path/to/file
# and commit this modification
git commit

(La commande checkout lit d'abord le fichier dans l'index, puis le copie dans l'arbre de travail, il n'est donc pas nécessaire d'utiliser la commande git add pour l'ajouter à l'index en vue de son engagement).

Si votre fichier n'a pas un historique simple (par exemple, les renommages et les copies), consultez l'excellent commentaire de VonC. git peuvent être orientés pour rechercher plus soigneusement ces éléments, au détriment de la vitesse. Si vous êtes sûr que l'histoire est simple, vous n'avez pas besoin de vous inquiéter.

18 votes

Ne devriez-vous pas utiliser aussi -C o --find-copies-harder pour détecter les renommages et des copies plus --follow pour continuer à lister l'historique d'un fichier au-delà des renommages ? Étant donné l'accent mis dans votre réponse sur contenu du fichier il serait bon de la compléter en mentionnant sa conséquence naturelle : la inférence (voir stackoverflow.com/questions/612580/ )

2 votes

Jusqu'au bout ! Merci !

3 votes

Une explication fantastique. Non seulement vous avez répondu parfaitement à la question, mais vous avez également approfondi ma compréhension. Je n'avais jamais vu gitk avant l'un ou l'autre - étonnant !

123voto

Jim Hurne Points 2584

Git est très flexible. Vous ne devriez pas avoir besoin de centaines de branches pour faire ce que vous demandez. Si vous voulez revenir à la deuxième modification (et qu'il s'agit bien d'une modification qui a déjà été validée et poussée), utilisez la commande git revert . Quelque chose comme :

git revert a4r9593432 

où a4r9593432 est le caractère de départ du hachage de la livraison que vous voulez annuler.

Si le commit contient des changements dans de nombreux fichiers, mais que vous voulez seulement revenir en arrière sur un seul de ces fichiers, vous pouvez utiliser git reset (la 2ème ou 3ème forme) :

git reset a4r9593432 -- path/to/file.txt
# the reverted state is added to the staging area, ready for commit
git diff --cached path/to/file.txt        # view the changes
git commit
git checkout HEAD path/to/file.txt        # make the working tree match HEAD           

Mais c'est assez complexe, et le git reset est dangereux. Utilisez git checkout <hash> <file path> à la place, comme le suggère Jefromi.

Si vous voulez juste voir à quoi ressemblait le fichier dans le commit x, vous pouvez utiliser git show :

git show a4r9593432:path/to/file.txt

Pour toutes les commandes, il y a de nombreuses façons de se référer à un commit autrement que par le hash de commit (cf. Nommer les commits dans le manuel de l'utilisateur de Git).

6 votes

Est-ce que le git revert faites marche arrière un mais pas "revenir à l'état antérieur au deuxième changement" ?

1 votes

@Alex - Oui, vous avez raison. Bien vu ! Seuls les changements introduits dans le commit donné sont annulés. C'est douloureux, mais vous pourriez utiliser plusieurs appels à get revert pour revenir en arrière sur tous les commits jusqu'à un certain point dans le temps. Ou vous pouvez faire quelque chose comme ce que vous suggérez dans votre réponse avec git diff y git apply .

15 votes

Aussi, git revert n'est pas pour un fichier spécifique, mais pour l'ensemble du référentiel.

13voto

Yawar Points 2393

Disons que vos 5 engagements sont A , B , C , D , E -- A étant le premier et E le plus récent. Vous voulez revenir en arrière file.txt à ce qu'il était après l'engagement A . Vous n'avez pas besoin d'apprendre ou de vous souvenir des différentes versions ou options des commandes git-reset et git-checkout. Exécuter :

git show A:file.txt >file.txt

# If you want to commit the older version:
git add file.txt
git commit

La commande git-show montre le contenu de, ou des informations sur, tout objet dans un dépôt Git. Lorsqu'on lui donne une référence de commit comme A (ou master^ ou HEAD~5 ou ...), suivi de deux points, suivi d'un nom de fichier, il affichera le contenu de ce fichier tel qu'il était après ce commit. Ensuite, il suffit de rediriger la sortie vers le fichier.

11voto

Ben Straub Points 3224

Git ne pense pas en termes de versions de fichiers. Une version dans git est un instantané de l'arbre entier.

Dans ces conditions, ce que vous voulez vraiment est un arbre qui a le dernier contenu de la plupart des fichiers, mais avec le contenu d'un fichier identique à celui d'il y a 5 commits. Cela prendra la forme d'un nouveau commit par-dessus les anciens, et la dernière version de l'arbre aura ce que vous voulez.

Je ne sais pas s'il existe un outil permettant de rétablir le contenu d'un seul fichier à celui d'il y a 5 commits, mais la solution la plus simple devrait fonctionner : checkout master~5 copier le fichier ailleurs, vérifier master copier le fichier en arrière, puis valider.

7 votes

La ligne unique que vous demandez est un checkout sur un seul fichier au lieu de l'arbre de travail entier ; voir ma réponse.

0 votes

Wow. J'ai l'impression que je devrais rédiger ma réponse. Bien joué !

8voto

Alex Budovski Points 8932

Vous pouvez prendre un diff qui annule les changements que vous voulez et le livrer.

Par exemple, si vous voulez annuler les changements dans la plage from..to faites ce qui suit

git diff to..from > foo.diff  # get a reverse diff
patch < foo.diff
git commit -a -m "Undid changes from..to".

3 votes

Quelques problèmes. Si vous voulez créer un patch, il doit être le patch pour seulement le fichier en question ( git diff to..from path/to/file ). Pour appliquer un correctif, vous devez utiliser git apply au lieu de patch. Et il n'y a pas besoin d'utiliser un patch du tout dans ce cas ; voir ma réponse.

0 votes

C'est toujours utile, car vous pouvez modifier le diff (c'est-à-dire ne rétablir qu'une partie des changements).

3 votes

@Jefromi Il y a absolument une raison d'utiliser un patch, parce que votre réponse n'est PAS équivalente à revert : elle effacera toutes les modifications depuis le commit incriminé, et non pas seulement les modifications de ce seul commit comme git revert ferait. Un patch spécifique au fichier et au commit est un changement beaucoup plus précis et sémantiquement correct.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X