web-dev-qa-db-fra.com

Rétablir un fichier unique dans une version précédente dans git

Est-il possible de passer par différents commits sur un fichier. Supposons que j'ai modifié un fichier 5 fois et que je souhaite revenir à la modification 2, après avoir déjà été validé et transféré dans un référentiel.

A ma connaissance, le seul moyen est de conserver de nombreuses branches, est-ce que j'ai bien compris? Si j'ai raison, je vais avoir des centaines de succursales dans quelques jours, alors je ne comprends probablement pas vraiment.

Quelqu'un pourrait-il clarifier cela s'il vous plaît?

554
georgeliquor

Commençons par une description qualitative de ce que nous voulons faire (la réponse de Ben Straub en dit beaucoup). Nous avons effectué un certain nombre de validations, dont cinq ont modifié un fichier donné et nous souhaitons rétablir le fichier dans 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, un message de commit). Donc, nous devons savoir quel commit a la version du fichier que nous voulons. Une fois que nous le saurons, nous devrons effectuer un nouveau commit en rétablissant le fichier dans cet état. (Nous ne pouvons pas nous contenter d’histoire, car nous avons déjà avancé ce contenu et éditer l’histoire en désordre avec tout le monde.)

Commençons donc par trouver le bon commit. Vous pouvez voir les commits qui ont apporté des modifications à un ou plusieurs fichiers donnés très facilement:

git log path/to/file

Si vos messages de validation ne sont pas assez bons et que vous avez besoin de voir ce que le fichier a été fait dans chaque validation, utilisez l'option -p/--patch:

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 démarré gitk via le menu Afficher; 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 git add pour l'ajouter à l'index en vue de sa validation.)

Si votre fichier peut ne pas avoir un historique simple (par exemple, renommer et copier), voir l'excellent commentaire de VonC. Vous pouvez demander à git de rechercher plus attentivement ce type de choses, au détriment de la vitesse. Si vous êtes confiant que l'histoire est simple, vous n'avez pas besoin de vous embêter.

797
Cascabel

Git est très flexible. Vous ne devriez pas avoir besoin de centaines de branches pour faire ce que vous demandez. Si vous souhaitez rétablir l'état jusqu'au deuxième changement (et qu'il s'agit bien d'un changement déjà validé et poussé), utilisez git revert . Quelque chose comme:

_git revert a4r9593432 
_

où a4r9593432 correspond aux caractères de départ du hachage du commit que vous souhaitez annuler.

Si la validation contient des modifications sur de nombreux fichiers, mais que vous souhaitez simplement rétablir l'un de ces fichiers, vous pouvez utiliser git reset (la 2ème ou la 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 ceci est assez complexe et la réinitialisation de Git est dangereuse. Utilisez plutôt _git checkout <hash> <file path>_, comme le suggère Jefromi.

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

_git show a4r9593432:path/to/file.txt
_

Pour toutes les commandes, il existe de nombreuses manières de faire référence à une validation autre que via le hachage de validation (voir Attribution de nom dans le manuel de l'utilisateur Git).

71
Jim Hurne

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

Compte tenu de cela, ce que vous voulez vraiment, c'est un arbre qui contient le dernier contenu de la plupart des fichiers, mais avec le contenu d'un fichier identique à celui qu'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 one-liner qui rétablira un fichier au contenu de 5 commits auparavant, mais la solution lo-fi devrait fonctionner: checkout master~5, copiez le fichier ailleurs, checkout master, recopiez le fichier, puis validez.

7
Ben Straub

Vous pouvez prendre un diff qui annule les modifications souhaitées et le commettre.

Par exemple. Si vous souhaitez annuler les modifications de la plage from..to, procédez comme suit:

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

Extrait de ici: http://git.661346.n2.nabble.com/Revert-a-single-commit-in-a-single-file-td6064050.html

 git revert <commit> 
 git reset 
 git add <path> 
 git commit ... 
 git reset --hard # making sure you didn't have uncommited changes earlier 

Cela a très bien fonctionné pour moi.

4
Felipe