Intro : Vous disposez de 5 solutions
L'affiche originale déclare :
J'ai accidentellement commis un fichier non désiré ... à mon référentiel il y a plusieurs commits il y a plusieurs commits... Je veux supprimer complètement le fichier de l'historique du dépôt.
Est-il possible de réécrire l'historique des changements de telle sorte que filename.orig
n'a jamais été ajouté au référentiel en premier lieu ?
Il existe de nombreuses manières différentes de supprimer complètement l'historique d'un fichier de git :
- Modifier les engagements.
- Remise à zéro (plus éventuellement un rebasement).
- Rebasement non interactif.
- Rebases interactives.
- Filtrage des branches.
Dans le cas de l'affiche originale, modifier le commit n'est pas vraiment une option en soi, puisqu'il a fait plusieurs commits supplémentaires par la suite, mais dans l'intérêt de la d'être complet, je vais aussi expliquer comment le faire, pour tous ceux qui veulent juste veut modifier son commit précédent.
Notez que toutes ces solutions impliquent modification/réécriture histoire/commits d'une manière ou d'une autre, donc toute personne ayant des anciennes copies des commits devra faire travail supplémentaire pour re-synchroniser leur historique avec le nouvel historique.
Solution 1 : Modifier les engagements
Si vous avez accidentellement fait une modification (comme l'ajout d'un fichier) dans votre précédent précédente, et que vous ne voulez plus que l'historique de cette modification existe, alors vous pouvez simplement modifier le commit précédent pour en retirer le fichier :
git rm <file>
git commit --amend --no-edit
Solution 2 : Réinitialisation matérielle (éventuellement accompagnée d'un rebasement)
Comme pour la solution n°1, si vous voulez simplement vous débarrasser de votre précédent commit, alors vous avez également la possibilité de faire une réinitialisation matérielle de son pare-feu. avez aussi la possibilité de faire une réinitialisation de son parent :
git reset --hard HEAD^
Cette commande va réinitialiser votre branche à la valeur 1 précédente. st parent commettre.
Cependant si, comme l'auteur de l'article original, vous avez fait plusieurs commits après le commit sur lequel vous voulez annuler la modification, vous pouvez toujours utiliser les hard resets. le commit sur lequel vous voulez annuler le changement, vous pouvez toujours utiliser les hard resets pour le modifier, mais cela implique également l'utilisation d'une rebase. Voici les étapes que vous pouvez utiliser pour modifier un commit plus loin dans l'histoire :
# Create a new branch at the commit you want to amend
git checkout -b temp <commit>
# Amend the commit
git rm <file>
git commit --amend --no-edit
# Rebase your previous branch onto this new commit, starting from the old-commit
git rebase --preserve-merges --onto temp <old-commit> master
# Verify your changes
git diff master@{1}
Solution 3 : Rebasement non interactif
Cela fonctionnera si vous voulez juste supprimer entièrement un commit de l'historique :
# Create a new branch at the parent-commit of the commit that you want to remove
git branch temp <parent-commit>
# Rebase onto the parent-commit, starting from the commit-to-remove
git rebase --preserve-merges --onto temp <commit-to-remove> master
# Or use `-p` insteda of the longer `--preserve-merges`
git rebase -p --onto temp <commit-to-remove> master
# Verify your changes
git diff master@{1}
Solution 4 : rebasements interactifs
Cette solution vous permettra d'accomplir les mêmes choses que les solutions #2 et #3, c'est-à-dire modifier ou supprimer des commits plus anciens que le commit précédent. La solution que vous choisissez d'utiliser dépend donc de vous. Les rebasements interactifs ne sont pas bien adaptés pour rebaser des centaines de commits, pour des raisons de performance. pour des raisons de performance, j'utiliserais donc des rebasements non interactifs ou la solution (voir ci-dessous) dans ce genre de situation.
Pour commencer le rebasement interactif, utilisez ce qui suit :
git rebase --interactive <commit-to-amend-or-remove>~
# Or `-i` instead of the longer `--interactive`
git rebase -i <commit-to-amend-or-remove>~
Ceci fera que git remontera l'historique des livraisons jusqu'au parent de la livraison que vous voulez modifier ou supprimer. que vous voulez modifier ou supprimer. Il vous présentera alors une liste des des commits remontés dans l'ordre inverse dans l'éditeur que git est configuré pour utiliser (c'est Vim par défaut) :
pick 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`
pick 7668f34 Modify Bash config to use Homebrew recommended PATH
pick 475593a Add global .gitignore file for OS X
pick 1b7f496 Add alias for Dr Java to Bash config (OS X)
Le commit que vous voulez modifier ou supprimer sera en haut de cette liste. Pour le supprimer, il suffit de supprimer sa ligne dans la liste. Sinon, remplacez "pick" par "modifier" sur la ligne 1 st ligne, comme ceci :
edit 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`
Ensuite, entrez git rebase --continue
. Si vous avez choisi de supprimer entièrement le commit, alors c'est tout ce que vous avez à faire (autre que la vérification, voir l'étape finale pour cette solution). Si, d'un autre côté, vous voulez modifier le commit, alors git réappliquera le commit et mettra ensuite en pause le rebasement.
Stopped at 00ddaacab0a85d9989217dd9fe9e1b317ed069ac... Add symlinks
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
À ce stade, vous pouvez supprimer le fichier et modifier le commit, puis poursuivre la procédure de rebasement :
git rm <file>
git commit --amend --no-edit
git rebase --continue
C'est ça. Comme étape finale, que vous ayez modifié le commit ou que vous l'ayez supprimé complètement, c'est toujours une bonne idée de vérifier qu'aucun autre changement inattendu n'a été faite à votre branche en la comparant avec son état avant le rebasement :
git diff master@{1}
Solution 5 : filtrer les branches
Enfin, cette solution est la meilleure si vous voulez effacer complètement toute trace de l'existence d'un fichier dans l'historique, et qu'aucune autre solution n'est à la hauteur. d'existence d'un fichier dans l'historique, et qu'aucune des autres solutions n'est à la hauteur de la à la hauteur de la tâche.
git filter-branch --index-filter \
'git rm --cached --ignore-unmatch <file>'
Cela supprimera <file>
de tous les commits, en commençant par le commit racine. Si au lieu de cela, vous voulez juste réécrire la plage de commit HEAD~5..HEAD
alors vous pouvez le passer comme un argument supplémentaire à filter-branch
comme indiqué dans cette réponse :
git filter-branch --index-filter \
'git rm --cached --ignore-unmatch <file>' HEAD~5..HEAD
Encore une fois, après le filter-branch
est terminée, c'est généralement une bonne idée de vérifier qu'il n'y a pas d'autres changements inattendus en comparant votre branche avec son état précédent avant l'opération de filtrage :
git diff master@{1}
Alternative filtre-branche : Nettoyeur BFG Repo
J'ai entendu dire que le Nettoyeur BFG Repo fonctionne plus rapidement que l'outil git filter-branch
Vous pouvez donc envisager cette option. Il est même mentionné officiellement dans le documentation sur les filtres et les branches comme une alternative viable :
git-filter-branch vous permet de faire des réécritures complexes de votre historique Git. de votre historique Git, mais vous n'avez probablement pas besoin de cette flexibilité si vous êtes simplement suppression des données indésirables comme les fichiers volumineux ou les mots de passe. Pour ces opérations, vous pouvez envisager Le BFG Repo-Cleaner une alternative à git-filter-branch, basée sur la JVM alternative à git-filter-branch, typiquement au moins 10-50x plus rapide pour ces cas d'utilisation, et avec des caractéristiques assez différentes :
-
Toute version particulière d'un fichier est nettoyée exactement une fois . Le BFG, contrairement à git-filter-branch, ne vous donne pas la possibilité de traiter un fichier différemment en fonction de l'endroit ou du moment où il a été livré au sein de votre organisation. un fichier différemment en fonction de l'endroit ou du moment où il a été livré dans votre historique. Cette contrainte donne l'avantage principal de performance de The BFG, et est bien adaptée à la tâche de nettoyage des mauvaises données. soins où les mauvaises données sont, vous voulez juste ça disparu .
-
Par défaut, le BFG tire pleinement parti des machines multi-cœurs, nettoyant les arbres de fichiers en parallèle. git-filter-branch nettoie les commits séquentiellement (c'est-à-dire d'une manière monofilaire). les commits séquentiellement (c'est-à-dire d'une manière monofilière), bien qu'il est possible d'écrire des filtres qui incluent leur propre parallélisme, dans les scripts exécutés contre chaque commit.
-
Le site options de la commande sont beaucoup plus restrictives que git-filter branch, et dédiées uniquement à la tâches de suppression des données non désirées - par ex : --strip-blobs-bigger-than 1M
.
Ressources supplémentaires
-
Pro Git § 6.4 Les outils Git - Réécrire l'histoire .
-
Page de manuel de git-filter-branch(1) .
-
Page de manuel de git-commit(1) .
-
git-reset(1) Page de manuel .
-
Page de manuel de git-rebase(1) .
-
Le nettoyeur BFG Repo (voir aussi cette réponse du créateur lui-même ).
1 votes
En rapport : Comment supprimer/supprimer un gros fichier de l'historique des livraisons dans un dépôt Git ? .
0 votes
aide.github.com/articles/