Question

J'ai accidentellement envoyé un fichier indésirable (filename.orig lors de la résolution d'une fusion) dans mon référentiel il y a plusieurs validations, sans que je m'en rende compte jusqu'à présent. Je veux supprimer complètement le fichier de l'historique du référentiel.

Est-il possible de réécrire l'historique des modifications de sorte que <=> n'ait jamais été ajouté au référentiel?

Était-ce utile?

La solution

Veuillez ne pas utiliser cette recette si votre situation n'est pas celle décrite dans la question. Cette recette permet de réparer une mauvaise fusion et de rejouer vos bons sur une fusion fixe.

Bien que filter-branch fasse ce que vous voulez, la commande est assez complexe et je choisirais probablement de le faire avec git rebase. C'est probablement une préférence personnelle. rebase peut le faire en une seule commande légèrement plus complexe, alors que la git commit --amend solution effectue les opérations logiques équivalentes une étape à la fois.

Essayez la recette suivante:

(Notez que vous n'avez pas réellement besoin d'une branche temporaire, vous pouvez le faire avec un 'HEAD détaché', mais vous devez noter l'identifiant de validation généré par l'étape <=> pour fournir à la < => commande plutôt que d'utiliser le nom de la branche temporaire.)

Autres conseils

Intro: 5 solutions sont disponibles

L’affiche originale indique:

  

J'ai accidentellement écrit un fichier indésirable ... dans mon référentiel, plusieurs commits    Il y a ... Je veux supprimer complètement le fichier de l'historique du référentiel.

     

Est-ce que c'est    possible de réécrire l’historique des modifications de sorte que filename.orig n’ait jamais été enregistré.    ajouté au référentiel en premier lieu?

Il existe différentes manières de supprimer complètement l'historique d'un fichier de git:

  1. Modification des validations.
  2. Réinitialisations matérielles (éventuellement plus une base).
  3. Base non interactive.
  4. Rebases interactives.
  5. Filtrage des branches.

Dans le cas de l'affiche originale, la modification du commit n'est pas vraiment une option. par lui-même, car il a fait plusieurs autres commits après, mais pour le bien de la complétude, je vais aussi expliquer comment le faire, pour toute autre personne qui justts souhaite modifier son engagement précédent.

Notez que toutes ces solutions impliquent de modifier / réécrire history / commits. d'une manière d'une autre, de sorte que toute personne avec de vieilles copies des commits devra faire travail supplémentaire pour resynchroniser leur historique avec le nouvel historique.

Solution 1: modification des validations

Si vous avez accidentellement apporté une modification (par exemple en ajoutant un fichier) à votre précédente commettez, et vous ne voulez plus que l'historique de ce changement existe, alors vous pouvez simplement modifier le commit précédent pour supprimer le fichier:

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

Solution 2: Réinitialisation matérielle (éventuellement plus une base)

Comme la solution 1, si vous voulez juste vous débarrasser de votre commit précédent, alors vous Vous pouvez également effectuer une réinitialisation matérielle de son parent:

git reset --hard HEAD^

Cette commande réinitialisera votre branche sur le précédent <1> sup parent commettre.

Cependant , si, comme pour l'affiche d'origine, vous avez effectué plusieurs commits après le commit que vous voulez annuler le changement, vous pouvez toujours utiliser des réinitialisations dures pour modifiez-le, mais cela implique également l'utilisation d'une rebase. Voici les étapes vous pouvez utiliser pour modifier un commit plus loin dans l’historique:

Solution 3: Rebase non interactive

Cela fonctionnera si vous souhaitez simplement supprimer entièrement une validation de l'historique:

Solution 4: Rebases interactives

Cette solution vous permettra de réaliser les mêmes tâches que les solutions n ° 2 et # 3, c’est-à-dire modifier ou supprimer les commits plus loin dans l’histoire que votre le commit précédent, donc quelle solution vous choisissez est en quelque sorte à vous. Les rebases interactifs ne conviennent pas pour rebasonner des centaines de commits, par exemple. raisons de performances, donc je voudrais utiliser des bases non-interactives ou la branche de filtre solution (voir ci-dessous) dans ce genre de situation.

Pour commencer la réinstallation interactive, utilisez les éléments suivants:

Ceci fera que git rembobinera l’historique des commit au parent du commettez que vous voulez modifier ou supprimer. Il vous présentera ensuite une liste des Le rembobinage s’effectue dans l’ordre inverse, quel que soit l’éditeur choisi par git (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)

La validation que vous souhaitez modifier ou supprimer se trouvera en haut de cette liste. Pour le supprimer, supprimez simplement sa ligne dans la liste. Sinon, remplacez & Quot; choisissez & Quot; avec " edit " sur la 1 e ligne, comme suit:

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 que tout ce que vous devez faire (autre que la vérification, voir la dernière étape pour cette solution). Si, par contre, vous vouliez modifier le commit, alors git réappliquera le commit, puis mettra en 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 commit, puis continuer la rebase:

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

C'est ça. Enfin, que vous ayez modifié ou supprimé le commit complètement, c'est toujours une bonne idée de vérifier qu'aucun autre changement inattendu ont été faites à votre branche par den le notifiant avec son état avant la base:

git diff master@{1}

Solution 5: Filtrage des branches

Enfin, cette solution est préférable si vous souhaitez effacer complètement toute trace de l'existence d'un fichier de l'histoire, et aucune des autres solutions sont tout à fait à la hauteur la tâche.

Cela supprimera <file> toutes les validations, à partir de la validation racine. Si vous voulez simplement réécrire la plage de validation HEAD~5..HEAD, vous pouvez alors passer cela comme un argument supplémentaire à filter-branch, comme indiqué dans cette réponse :

Encore une fois, une fois que git filter-branch est terminé, il est généralement bon de vérifier qu’il n’y ait pas d’autres changements inattendus en différenciant votre branche de son état précédent avant l'opération de filtrage:

<*>

Alternative filtre-branche: nettoyeur BFG Repo

J'ai entendu dire que l'outil BFG Repo Cleaner est plus rapide que --strip-blobs-bigger-than 1M, vous pouvez donc vérifier cela comme une option aussi. Elle est même mentionnée officiellement dans la documentation de la branche de filtrage comme solution viable. :

  

git-filter-branch vous permet de faire des réécritures complexes avec scripts shell   de votre historique Git, mais vous n'avez probablement pas & # 8217; t besoin de cette souplesse si   vous & # 8217; supprimez simplement la suppression des données non désirées comme des fichiers volumineux ou des mots de passe.   Pour ces opérations, vous pouvez envisager de BFG.   Repo-Cleaner , basé sur la JVM   alternative à git-filter-branch, généralement au moins 10 à 50 fois plus rapide pour   ces cas d'utilisation, et avec des caractéristiques assez différentes:

     
      
  • Toute version d'un fichier est nettoyée exactement une fois . Le BFG, contrairement à git-filter-branch, ne vous permet pas de gérer   un fichier différemment selon où ou quand il a été commis dans votre   l'histoire. Cette contrainte donne l’avantage de performance principal de   BFG, et convient parfaitement à la tâche de nettoyage des données incorrectes - vous ne & # 8217; t   attention se trouvent les mauvaises données, vous voulez simplement les disparaître .

  •   
  • Par défaut, BFG tire pleinement parti des machines multicœurs en nettoyant les arborescences de fichiers de validation en parallèle. git-filter-branch nettoie   commet séquentiellement (c’est-à-dire d’une manière mono-threadée), bien que soit   possible d'écrire des filtres qui incluent leur propre parallellisme, dans le   scripts exécutés pour chaque commit.

  •   
  • Les options de commande valent beaucoup   plus restrictif que la branche git-filter, et dédié uniquement à la   tâches de suppression des données indésirables, par exemple: <=>.

  •   

Ressources supplémentaires

  1. Pro Git & # 167; 6.4 Outils Git - Historique de réécriture .
  2. Page de manuel de git-filter-branch (1) .
  3. Page de manuel de git-commit (1) .
  4. Page de manuel de git-reset (1) .
  5. Page de manuel de git-rebase (1) .
  6. Le nettoyeur BFG Repo Cleaner (voir aussi Cette réponse du créateur lui-même ).

Si vous n'avez rien commis depuis, il suffit de git rm le fichier et git commit --amend .

Si vous avez

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

passera à travers chaque changement de point de fusion en HEAD , supprimera filename.orig et réécrira le changement. L'utilisation de - ignore-unmatch signifie que la commande n'échouera pas si, pour une raison quelconque, nomfichier.orig est absent d'une modification. C’est la méthode recommandée dans la section Exemples de la page de manuel de git-filter-branch . .

Remarque pour les utilisateurs Windows: le chemin du fichier doit utiliser des barres obliques

.

C’est le meilleur moyen:
http://github.com/guides/completely-remove-a -fichier-de-toutes-les révisions

Veillez simplement à sauvegarder d'abord les copies des fichiers.

MODIFIER

La modification effectuée par Neon a malheureusement été refusée lors de l'examen.
Voir l'article sur Neons ci-dessous, il pourrait contenir des informations utiles!

E.g. supprimer tous les fichiers *. gz validés accidentellement dans le référentiel 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 fonctionné pour moi? (Je suis actuellement à la version 1.7.6.1 de git)

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

Je ne sais pas pourquoi, car je n'avais qu'une branche maîtresse. En tout cas, mon dépôt git a finalement été véritablement nettoyé en insérant un nouveau référentiel git vide et nu, 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!)

Ensuite, je clone cela dans un nouveau répertoire et je déplace son dossier .git 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 va bien, vous pouvez supprimer les répertoires ../ large_dot_git et ../ tmpdir (peut-être dans quelques semaines ou un mois à partir de maintenant). juste au cas où ...)

La réécriture de l’historique Git exige la modification de tous les identifiants de validation affectés. Ainsi, tous ceux qui travaillent sur le projet doivent supprimer leurs anciennes copies du référentiel et créer un nouveau clone une fois que vous avez nettoyé l’historique. Plus les gens sont gênés, plus vous avez besoin d’une bonne raison - votre fichier superflu ne pose pas vraiment de problème, mais si vous travaillez sur le projet, vous pouvez tout aussi bien nettoyer l'historique Git si vous voulez!

Pour simplifier au maximum les choses, nous vous recommandons d’utiliser le BFG Repo-Cleaner , une alternative plus simple et plus rapide à git-filter-branch spécialement conçue pour supprimer des fichiers de l’historique Git. Une façon de vous faciliter la vie ici est qu’il gère réellement toutes les références par défaut (toutes les balises, branches, etc.), mais également 10 à 50 fois plus rapidement.

Vous devez suivre attentivement les étapes suivantes: http://rtyley.github.com / bfg-repo-cleaner / # usage - mais le cœur du problème n’est que ceci: téléchargez le Jarre BFG (requiert Java 6 ou supérieur) et exécutez la commande suivante:

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

L’ensemble de votre historique de référentiel sera analysé, ainsi que tout fichier nommé nomfichier.orig (il ne figure pas dans votre dernière commit ) sera supprimée. C’est beaucoup plus facile que d’utiliser git-filter-branch pour faire la même chose!

Divulgation complète: je suis l'auteur du BFG Repo-Cleaner.

You should probably clone your repository first.

Remove your file from all branches history:
git filter-branch --tree-filter 'rm -f filename.orig' -- --all

Remove your file just from the current branch:
git filter-branch --tree-filter 'rm -f filename.orig' -- --HEAD    

Lastly you should run to remove empty commits:
git filter-branch -f --prune-empty -- --all

Juste pour ajouter que la solution de Charles Bailey, je viens d'utiliser un git rebase -i pour supprimer les fichiers indésirables d'un commit précédent et cela a fonctionné comme un charme. Les étapes:

# Pick your commit with 'e'
$ git rebase -i

# Perform as many removes as necessary
$ git rm project/code/file.txt

# amend the commit
$ git commit --amend

# continue with rebase
$ git rebase --continue

La méthode la plus simple que j'ai trouvée a été suggérée par leontalbot (en tant que commentaire), qui est un publication publiée par Anoopjohn . Je pense que sa valeur son propre espace comme une réponse:

(je l'ai converti en script bash)

#!/bin/bash
if [[ $1 == "" ]]; then
    echo "Usage: <*> FILE_OR_DIR [remote]";
    echo "FILE_OR_DIR: the file or directory you want to remove from history"
    echo "if 'remote' argument is set, it will also push to remote repository."
    exit;
fi
FOLDERNAME_OR_FILENAME=$1;

#The important part starts here: ------------------------

git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch $FOLDERNAME_OR_FILENAME" -- --all
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

if [[ $2 == "remote" ]]; then
    git push --all --force
fi
echo "Done."

Tous les crédits vont à Annopjohn et à leontalbot pour le signaler.

REMARQUE

Sachez que le script n'inclut pas les validations. Veillez donc à ne pas commettre d'erreur et à disposer d'une copie de sauvegarde en cas de problème. Cela a fonctionné pour moi, mais cela peut ne pas fonctionner dans votre situation. UTILISEZ-LE AVEC PRÉCAUTION (suivez le lien si vous voulez savoir ce qui se passe).

Certainement, branche de filtre git est la voie à suivre.

Malheureusement, cela ne suffira pas pour supprimer complètement filename.orig de votre référentiel, car il peut toujours être référencé par des balises, des entrées de reflog, des télécommandes, etc.

Je recommande également de supprimer toutes ces références, puis d'appeler le ramasse-miettes. Vous pouvez utiliser le script git forget-blob à partir de ce site Web pour faire tout cela en une seule étape.

git oublier-blob nom_fichier.orig

S'il s'agit du dernier commit que vous souhaitez nettoyer, j'ai essayé avec la version 2.14.3 de Git (Apple Git-98):

touch empty
git init
git add empty
git commit -m init

# 92K   .git
du -hs .git

dd if=/dev/random of=./random bs=1m count=5
git add random
git commit -m mistake

# 5.1M  .git
du -hs .git

git reset --hard HEAD^
git reflog expire --expire=now --all
git gc --prune=now

# 92K   .git
du -hs .git

C’est ce que branche de filtre git a été conçu pour.

Vous pouvez également utiliser:

git réinitialiser le fichier / chemin HEAD

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top