web-dev-qa-db-fra.com

Comment puis-je supprimer l'ancien historique d'un référentiel git?

J'ai bien peur de ne rien trouver qui ressemble à ce scénario particulier.

J'ai un dépôt git avec beaucoup d’histoire: plus de 500 branches, plus de 500 balises, remontant à la mi-2007. Il contient environ 19 500 commits. Nous aimerions supprimer tout l'historique avant le 1er janvier 2010 pour le rendre plus petit et plus facile à gérer (nous conserverions une copie complète de l'historique dans un référentiel d'archives).

Je connais le commit que je souhaite avoir pour devenir la racine du nouveau référentiel. Cependant, je ne peux pas trouver le bon mojo de git pour tronquer le dépôt pour commencer avec ce commit. Je devine une variante de

git filter-branch

impliquant des greffes serait nécessaire; il peut également être nécessaire de traiter chacune des 200 branches ou plus que nous souhaitons conserver séparément, puis de rapatrier le rapport de reprise (quelque chose que je fais sais comment faire).

Est-ce que quelqu'un a déjà fait quelque chose comme ça? J'ai git 1.7.2.3 si cela compte.

182
ebneter

Créez simplement un graft du parent de votre nouvelle validation root sur aucun parent (ou sur une validation vide, par exemple la validation réelle de votre référentiel). Par exemple. echo "<NEW-ROOT-SHA1>" > .git/info/grafts

Après avoir créé la greffe, celle-ci prend effet immédiatement. vous devriez être capable de regarder git log et de voir que les anciens commits non désirés sont partis:

$ echo 4a46bc886318679d8b15e05aea40b83ff6c3bd47 > .git/info/grafts
$ git log --decorate | tail --lines=11
commit cb3da2d4d8c3378919844b29e815bfd5fdc0210c
Author: Your Name <[email protected]>
Date:   Fri May 24 14:04:10 2013 +0200

    Another message

commit 4a46bc886318679d8b15e05aea40b83ff6c3bd47 (grafted)
Author: Your Name <[email protected]>
Date:   Thu May 23 22:27:48 2013 +0200

    Some message

Si tout se passe comme prévu, vous pouvez simplement faire un simple git filter-branch -- --all pour le rendre permanent.

BEWARE: après avoir effectué l'étape de la branche de filtrage , tous les identifiants de validation ont été modifiés. repo.

107
apenwarr

Il est peut-être trop tard pour poster une réponse, mais comme cette page est le premier résultat de Google, elle peut toujours être utile.

Si vous voulez libérer de l'espace dans votre dépôt Git, mais que vous ne voulez pas reconstruire tous vos commits (rebase ou greffage) et que vous puissiez toujours pousser/tirer/fusionner des personnes ayant le plein dépôt, vous pouvez utiliser le - clone git clone peu profond (() - profondeur paramètre).

; Clone the original repo into limitedRepo
git clone file:///path_to/originalRepo limitedRepo --depth=10

; Remove the original repo, to free up some space
rm -rf originalRepo
cd limitedRepo
git remote rm Origin

Vous pourrez peut-être supprimer votre référentiel existant en procédant comme suit:

; Shallow to last 5 commits
git rev-parse HEAD~5 > .git/shallow

; Manually remove all other branches, tags and remotes that refers to old commits

; Prune unreachable objects
git fsck --unreachable ; Will show you the list of what will be deleted
git gc --Prune=now     ; Will actually delete your data

Ps: Les anciennes versions de git ne supportaient pas cloner/pousser/tirer de/vers des dépôts peu profonds.

89
Alexandre T.

Cette méthode est facile à comprendre et fonctionne bien. L'argument du script ($1) est une référence (balise, hachage, ...) à la validation à partir de laquelle vous souhaitez conserver votre historique.

#!/bin/bash
git checkout --Orphan temp $1 # create a new branch without parent history
git commit -m "Truncated history" # create a first commit on this branch
git rebase --onto temp $1 master # now rebase the part of master branch that we want to keep onto this branch
git branch -D temp # delete the temp branch

# The following 2 commands are optional - they keep your git repo in good shape.
git Prune --progress # delete all the objects w/o references
git gc --aggressive # aggressively collect garbage; may take a lot of time on large repos

NOTEZ que les anciennes balises resteront toujours présentes; vous devrez peut-être les supprimer manuellement

remarque: Je sais que c'est presque la même chose que @yoyodin, mais il y a quelques commandes et informations supplémentaires importantes ici. J'ai essayé de modifier la réponse, mais comme il s'agit d'un changement substantiel dans la réponse de @ yoyodin, ma modification a été rejetée. Voici les informations!

55
Chris Maes

Essayez cette méthode Comment tronquer l'historique git :

#!/bin/bash
git checkout --Orphan temp $1
git commit -m "Truncated history"
git rebase --onto temp $1 master
git branch -D temp

Ici $1 est SHA-1 du commit que vous voulez conserver et le script créera une nouvelle branche contenant tous les commits entre $1 et master et tout l'historique plus ancien sera supprimé. Notez que ce script simple suppose que vous n’avez pas de branche existante appelée temp. Notez également que ce script n'efface pas les données git de l'ancien historique. Exécutez git gc --Prune=all && git repack -a -f -F -d après avoir vérifié que vous souhaitiez réellement perdre tout l'historique. Vous pouvez aussi avoir besoin de rebase --preserve-merges mais soyez averti que l'implémentation de cette fonctionnalité par git n'est pas parfaite. Inspectez les résultats manuellement si vous l'utilisez.

48
yoyodyn

Au lieu de réécrire l’historique, envisagez d’utiliser git replace comme dans cet article de Pro Git livre . L'exemple présenté implique le remplacement d'un commit parent pour simuler le début d'une arborescence, tout en conservant l'historique complet en tant que branche distincte pour la sauvegarde.

32
Jeff Bowman

Si vous voulez conserver le dépôt en amont avec historique complet , mais les extractions locales plus petites font un clone peu profond avec git clone --depth=1 [repo].

Après avoir poussé un commit, vous pouvez faire

  1. git fetch --depth=1 Elaguer les anciens commits. Cela rend les anciens commits et leurs objets inaccessibles.
  2. git reflog expire --expire-unreachable=now --all. Pour expirer tous les anciens commits et leurs objets
  3. git gc --aggressive --Prune=all pour supprimer les anciens objets

Voir aussi Comment supprimer l'historique git local après un commit? .

Notez que vous ne pouvez pas transférer ce référentiel "peu profond" vers un autre emplacement: "mise à jour peu profonde non autorisée". Voir Rejet à distance (mise à jour peu profonde non autorisée) après avoir modifié l'URL distante de Git . Si vous voulez cela, vous devez vous en tenir à la greffe.

21
koppor

J'avais besoin de lire plusieurs réponses et quelques autres informations pour comprendre ce que je faisais.

1. Ignorer tout ce qui est plus ancien qu'un certain commit

Le fichier .git/info/grafts peut définir de faux parents pour un commit. Une ligne avec juste un identifiant de commit indique que le commit n'a pas de parent. Si nous voulions dire que nous nous soucions seulement des 2000 derniers commits, nous pouvons taper:

git rev-parse HEAD~2000 > .git/info/grafts

git rev-parse nous donne l'identifiant de commit du 2000ème parent du commit en cours. La commande ci-dessus écrasera le fichier de greffe s'il est présent. Vérifiez si c'est là en premier.

2. Réécrire l'historique Git (facultatif)

Si vous voulez que ce faux parent greffé devienne réel, lancez:

git filter-branch -- --all

Cela changera tous les identifiants de commit. Chaque copie de ce référentiel doit être mise à jour avec force.

. Nettoyer l'espace disque

Je n'ai pas fait l'étape 2, car je voulais que ma copie reste compatible avec l'amont. Je voulais juste économiser de l'espace disque. Pour oublier tous les anciens commits:

git Prune
git gc

Alternative: copies superficielles

Si vous avez une copie superficielle d'un autre référentiel et souhaitez simplement économiser de l'espace disque, vous pouvez mettre à jour .git/shallow. Mais faites attention à ce que rien ne pointe vers un commit avant. Pour que vous puissiez exécuter quelque chose comme ceci:

git fetch --Prune
git rev-parse HEAD~2000 > .git/shallow
git Prune
git gc

L'entrée dans les bas-fonds fonctionne comme une greffe. Mais veillez à ne pas utiliser de greffes et peu profondes en même temps. Au moins, n’avez pas les mêmes entrées, cela échouera.

Si vous avez encore d'anciennes références (balises, branches, têtes distantes) pointant vers des commits plus anciens, elles ne seront pas nettoyées et vous ne économiserez pas davantage d'espace disque.

15
Maikel

Quand rebase ou Poussez vers tête/maître cette erreur peut se produire

remote: GitLab: You are not allowed to access some of the refs!
To git@giturl:main/xyz.git
 ! [remote rejected] master -> master (pre-receive hook declined)
error: failed to Push some refs to 'git@giturl:main/xyz.git'

Pour résoudre ce problème, git dashboard doit supprimer la branche principale de "branches protégées" .

enter image description here

alors vous pouvez exécuter cette commande

git Push -f Origin master

ou

git rebase --onto temp $1 master
2
HMagdy