407 votes

Supprimer complètement le fichier de tous les historiques de validation du référentiel Git

J'ai accidentellement commis un fichier indésirable ( filename.orig en résolvant une fusion) dans mon dépôt il y a plusieurs mois, sans que je m'en aperçoive jusqu'à présent. Je souhaite supprimer complètement le fichier de l'historique du référentiel. Est-il possible de réécrire l'historique des modifications de telle sorte que filename.orig n'a jamais été ajouté au référentiel en premier lieu?

297voto

Charles Bailey Points 244082

S'il vous plaît ne pas utiliser cette recette si votre situation n'est pas celle décrite dans la question. Cette recette est pour la fixation d'un mauvais fusion, et la relecture de votre bon s'engage sur une installation fixe de fusion.

Bien qu' filter-branch à faire ce que vous voulez, il est tout à fait une commande complexe et je serais probablement choisir de le faire avec git rebase. C'est probablement une question de préférence personnelle. filter-branch pouvez le faire en un seul, un peu plus complexe de commande, tandis que l' rebase solution est d'effectuer l'équivalent d'opérations logiques, une étape à la fois.

Essayez la recette suivante:

# create and check out a temporary branch at the location of the bad merge
git checkout -b tmpfix <sha1-of-merge>

# remove the incorrectly added file
git rm somefile.orig

# commit the amended merge
git commit --amend

# go back to the master branch
git checkout master

# replant the master branch onto the corrected merge
git rebase tmpfix

# delete the temporary branch
git branch -d tmpfix

(Notez que vous n'avez pas réellement besoin d'un temporaire de la direction générale, vous pouvez le faire avec un " détaché de la TÊTE, mais vous devez prendre note de l'engagement de l'id généré par l' git commit --amend étape de fournir à l' git rebase commande plutôt que d'utiliser le temporaire nom de la branche.)

209voto

Cupcake Points 22154

Intro: Vous Avez 5 Solutions Disponibles

Le posteur d'origine états:

Par accident, j'ai commis un fichier indésirable...à mon référentiel plusieurs commits il y a...je veux supprimer complètement le fichier à partir du référentiel de l'histoire. Est-il possible de réécrire l'historique des modifications tels que filename.orig n'a jamais été ajouté au référentiel en premier lieu?

Il existe de nombreuses façons de supprimer l'historique d'un fichier complètement de git:

  1. Modifiant le commet.
  2. Réinitialise dur (plus éventuellement un rebase).
  3. Non-rebase interactif.
  4. Interactive rebases.
  5. Le filtrage des branches.

Dans le cas de l'affiche originale, modifiant la validation n'est pas vraiment une option par lui-même, depuis qu'il a fait plusieurs autres commet par la suite, mais pour l'amour de l'exhaustivité, je vais aussi expliquer comment le faire, pour quelqu'un d'autre qui justs veut modifier leur précédente livraison.

Notez que toutes ces solutions impliquent modifier/ré-écriture de l'histoire/commits d'une manière d'une autre, afin que toute personne avec d'anciennes copies de la commet aurez à faire un travail supplémentaire pour re-synchroniser leur histoire avec la nouvelle histoire.

Solution 1: La Modification S'Engage

Si vous avez accidentellement fait un changement (comme l'ajout d'un fichier) dans votre précédente s'engager, et vous ne voulez pas l'histoire de ce changement existe plus, alors vous pouvez simplement modifier la précédente livraison de supprimer le fichier de:

git rm <file>
git commit --amend --no-edit

Solution 2: Hard Reset (Plus Éventuellement un Rebase)

Comme solution n ° 1, si vous voulez juste pour se débarrasser de votre précédente livraison, alors vous ont également la possibilité de simplement faire un hard reset à son parent:

git reset --hard HEAD^

Cette commande de réinitialisation matérielle de votre succursale de la précédente 1stparent commettre.

Toutefois, si, à l'instar de l'affiche originale, vous avez fait plusieurs commits après la validation que vous souhaitez annuler la modification, vous pouvez toujours utiliser réinitialise dur à de le modifier, mais cela implique également l'utilisation d'un rebase. Voici les étapes que vous pouvez utiliser pour modifier un s'engager 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: Non-Rebase interactif

Cela fonctionnera si vous voulez simplement supprimer une validation de l'histoire:

# 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: Interactive Rebases

Cette solution vous permettra d'accomplir les mêmes choses que les solutions #2 et #3, c'est à dire modifier ou de les supprimer s'engage plus loin dans l'histoire de votre immédiatement précédente s'engager, pour quelle solution vous choisissez d'utiliser est une sorte de jusqu'à vous. Interactive rebases ne sont pas bien adaptés à la relocalisation des centaines de commits, pour des raisons de performances, donc je voudrais l'utiliser non-interactive rebases ou le filtre de direction solution (voir ci-dessous) dans ces sortes de situations.

Pour commencer le rebase interactif, utilisez la commande suivante:

git rebase --interactive <commit-to-amend-or-remove>~

# Or `-i` instead of the longer `--interactive`
git rebase -i <commit-to-amend-or-remove>~

Ce sera la cause de git pour un retour de la validation de l'histoire à la maison mère de la s'engager à ce que vous voulez modifier ou supprimer. Il va ensuite vous présenter une liste de la rembobinée s'engage dans l'ordre inverse de ce que l'éditeur de git est à 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 l'enlever, il suffit de supprimer sa ligne dans la liste. Sinon, remplacer "ramasser" avec "edit" sur la 1st 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 l'engager entièrement, alors que tout ce que vous devez faire (autres que de vérification, voir l'étape finale pour cette solution). Si, d'autre part, vous avez voulu modifier le valider, puis git réappliquez de la validation, puis une pause le rebase.

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 valider, puis continuer la rebase:

git rm <file>
git commit --amend --no-edit
git rebase --continue

C'est tout. Comme une étape finale, si vous avez modifié le commettre ou supprimé complètement, c'est toujours une bonne idée de vérifier qu'aucune autre des changements inattendus ont été apportées à votre succursale en comparaison avec son état avant le rebase:

git diff master@{1}

Solution 5: Filtrage Des Branches

Enfin, cette solution est préférable si vous souhaitez effacer toutes les traces de un fichier de l'existence de l'histoire, et aucune des autres solutions sont tout à fait à de la tâche.

git filter-branch --index-filter \
'git rm --cached --ignore-unmatch <file>'

Qui va supprimer <file> de tous les commits, à partir de la racine de la validation. Si au lieu de cela, vous voulez juste de réécrire le commettre éventail HEAD~5..HEAD, alors vous pouvez passer que comme un argument supplémentaire pour 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 l' filter-branch est terminée, il est généralement une bonne idée de vérifier qu'il n'y a pas d'autres changements inattendus par comparaison de votre branche avec ses avant l'opération de filtrage:

git diff master@{1}

Filter-Branch Alternative: BFG Repo Cleaner

J'ai entendu dire que le BFG Repo Cleaner outil s'exécute plus rapidement que git filter-branch, de sorte que vous pourriez vouloir vérifier que d'une option trop. Il est même mentionné officiellement dans le filtre-direction de la documentation comme une alternative viable:

git-filter-branch vous permet de faire des complexes shell-script réécrit de votre historique de Git, mais vous n'avez probablement pas besoin de cette flexibilité s' vous êtes tout simplement en supprimant les données inutiles comme les fichiers volumineux ou des mots de passe. Pour ces opérations, vous pouvez envisager de La BFG Repo-Nettoyant, une JVM à base de alternative à git-filter-branch, généralement au moins 10-50x plus rapide pour ces cas d'utilisation, et avec des caractéristiques très différentes:

  • Une nouvelle version d'un fichier est nettoyé exactement une fois. Le BFG, contrairement à git-filter-branch, ne vous donne pas la possibilité de gérer un fichier différemment, en fonction de l'endroit où il a été commis au sein de votre l'histoire. Cette contrainte donne le rendement de base bénéfice de La BFG, et est bien adapté pour le travail de nettoyage des données incorrectes - vous n'avez pas de soins les mauvaises données, vous voulez juste il disparu.

  • Par défaut, Le BFG tire pleinement parti de multi-core machines, le nettoyage s'engager fichier-arbres en parallèle. git-filter-branch nettoie s'engage de manière séquentielle (c'est à dire dans un seul thread), mais il est possible d'écrire des filtres qui incluent leur propre parallellism, dans le les scripts exécutés les uns contre les commettre.

  • Les options de commande sont beaucoup plus restrictive que git-filtre de direction, et dédiées à la les tâches de suppression des données inutiles - e.g: --strip-blobs-bigger-than 1M.

Des Ressources Supplémentaires

  1. Pro Git § 6.4 Git Outils De Réécriture De L'Histoire.
  2. git-filter-branch(1) Page de Manuel.
  3. git-commit(1) Page de Manuel.
  4. git-reset(1) Page de Manuel.
  5. git-git rebase(1) Page de Manuel.
  6. Le BFG Repo Cleaner (voir aussi cette réponse du créateur lui-même).

118voto

Schwern Points 33677

Si vous n'avez pas commis quelque chose car, git rm le fichier et git commit --amend.

Si vous avez

git filter-branch \
--index-filter 'git rm --cached --ignore-unmatch filename.orig' merge-point..HEAD

allez à travers chaque changement de merge-point de HEAD, de supprimer le nom de fichier.orig et de réécrire le changement. À l'aide de --ignore-unmatch signifie que la commande ne manqueront pas si pour quelque raison le nom de fichier.orig est manquant à partir d'un changement. C'est la manière recommandée à partir de la section Exemples dans le git-filter-branch page de man.

49voto

Darren Points 181

C'est la meilleure façon:
http://github.com/guides/completely-remove-a-file-from-all-revisions

Juste être sûr de sauvegarde de la copie des fichiers.

MODIFIER

Le modifier par Néon ai malheureusement rejeté lors de l'examen.
Voir Neons post ci-dessous, il peut contenir des informations utiles!


E. g. pour supprimer tous *.gz fichiers accidentellement commis dans le dépôt git:

$ du -sh .git ==> e.g. 100M
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch *.gz' HEAD
$ git push origin master --force
$ rm -rf .git/refs/original/
$ git reflog expire --expire=now --all
$ git gc --prune=now
$ git gc --aggressive --prune=now

Cela n'a toujours pas de travail pour moi? (Je suis actuellement à la version git 1.7.6.1)

$ du -sh .git ==> e.g. 100M

Je ne sais pas pourquoi, car je n'avais qu'UNE branche master. De toute façon, j'ai finalement obtenu mon repo git vraiment nettoyé en le poussant dans un nouveau vide et nu dépôt git, par exemple

$ git init --bare /path/to/newcleanrepo.git
$ git push /path/to/newcleanrepo.git master
$ du -sh /path/to/newcleanrepo.git ==> e.g. 5M 

(oui!)

Puis-je cloner que dans un nouveau répertoire et déplacé au-dessus .git dossier dans celui-ci. par exemple

$ mv .git ../large_dot_git
$ git clone /path/to/newcleanrepo.git ../tmpdir
$ mv ../tmpdir/.git .
$ du -sh .git ==> e.g. 5M 

(ouais! enfin nettoyé!)

Après avoir vérifié que tout est bien, alors vous pouvez supprimer l' ../large_dot_git et ../tmpdir annuaires (peut-être que dans quelques semaines ou mois à partir de maintenant, juste au cas où...)

27voto

Roberto Tyley Points 4352

La réécriture historique de Git demandes de changement de tous les concernés s'engager id, et donc tout le monde qui travaille sur le projet aura besoin de supprimer leurs anciennes copies de l'opération, et de faire une nouvelle clone après avoir nettoyé l'histoire. Plus les gens inconvénients, le plus vous avez besoin d'une bonne raison de le faire - votre superflu de fichier n'est pas vraiment à l'origine du problème, mais seulement si vous travaillez sur le projet, vous pouvez ainsi nettoyer l'historique de Git si vous voulez!

Pour le rendre aussi facile que possible, je vous recommande d'utiliser le BFG Repo plus propre, plus simple, plus rapide, alternative à git-filter-branch spécialement conçu pour la suppression de fichiers à partir de Git histoire. Une façon dont il rend votre vie plus facile ici, c'est qu'il gère toutes les refs par défaut (tous les tags, branches, etc) mais c'est aussi 10 - 50x plus rapide.

Vous devez suivre attentivement les étapes suivantes: http://rtyley.github.com/bfg-repo-cleaner/#usage - mais le cœur bits est juste ceci: télécharger le BFG pot (nécessite Java 6 ou au-dessus) et exécutez cette commande:

$ java -jar bfg.jar --delete-files filename.orig my-repo.git

L'ensemble de votre historique du dépôt seront analysés, et un fichier nommé filename.orig (qui n'est pas dans votre dernier commit) seront supprimés. C'est beaucoup plus facile que d'utiliser git-filter-branch à faire la même chose!

La divulgation complète: je suis l'auteur de la BFG Repo-Cleaner.

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