Wie entferne ich den alten Verlauf aus einem Git-Repository?

208

Ich fürchte, ich konnte so etwas wie dieses spezielle Szenario nicht finden.

Ich habe ein Git-Repository mit viel Geschichte: mehr als 500 Filialen, mehr als 500 Tags, die bis Mitte 2007 zurückreichen. Es enthält ~ 19.500 Commits. Wir möchten den gesamten Verlauf vor dem 1. Januar 2010 entfernen, um ihn kleiner und einfacher zu handhaben (wir würden eine vollständige Kopie des Verlaufs in einem Archiv-Repository aufbewahren).

Ich kenne das Commit, das ich zum Stamm des neuen Repositorys werden möchte. Ich kann jedoch nicht das richtige Git-Mojo herausfinden, um das Repo abzuschneiden und mit diesem Commit zu beginnen. Ich vermute eine Variante von

git filter-branch

die Einbeziehung von Transplantaten wäre notwendig; Es kann auch notwendig sein, jeden der über 200 Zweige, die wir behalten möchten, separat zu behandeln und dann das Repo wieder zusammenzufügen (etwas, das ich zu tun weiß).

Hat jemand jemals so etwas getan? Ich habe Git 1.7.2.3, wenn das wichtig ist.

ebneter
quelle

Antworten:

118

Erstellen Sie einfach ein Transplantat des übergeordneten Elements Ihres neuen Root-Commits für kein übergeordnetes Element (oder für ein leeres Commit, z. B. das echte Root-Commit Ihres Repositorys). Z.Becho "<NEW-ROOT-SHA1>" > .git/info/grafts

Nachdem das Transplantat erstellt wurde, wird es sofort wirksam. Sie sollten in der Lage sein zu git logsehen, dass die unerwünschten alten Commits verschwunden sind:

$ 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

Wenn alles wie beabsichtigt aussieht, können Sie es einfach machen git filter-branch -- --all, um es dauerhaft zu machen.

ACHTUNG: Nach dem Filter-Verzweigungsschritt haben sich alle Commit-IDs geändert, sodass jeder, der das alte Repo verwendet, niemals mit jemandem zusammengeführt werden darf, der das neue Repo verwendet.

Apenwarr
quelle
6
Ich musste tun git filter-branch --tag-name-filter cat -- --all, um Tags zu aktualisieren. Ich habe aber auch ältere Tags, die auf den alten Verlauf verweisen, den ich löschen möchte. Wie kann ich all diese alten Tags loswerden? Wenn ich sie nicht lösche, verschwindet der ältere Verlauf nicht und ich kann ihn immer noch mit sehen gitk --all.
Craig McQueen
9
"Erstellen Sie einfach ein Transplantat des Elternteils Ihres neuen Root-Commits für kein Elternteil" muss noch näher erläutert werden. Ich habe das versucht und die Syntax für "kein Elternteil" nicht herausgefunden. Die manuelle Seite behauptet, dass eine übergeordnete Festschreibungs-ID erforderlich ist. Die Verwendung aller Nullen gibt mir nur einen Fehler.
Marius Gedminas
6
Falls sich jemand gefragt hat, wie genau es funktioniert, ist es ziemlich einfach:echo "<NEW-ROOT-HASH>" > .git/info/grafts
friederbluemle
3
Ich stimme zu, zu erklären, was ein Transplantat ist, wäre mehr als nützlich
Charles Martin
4
Zitiert von der verlinkten Wiki-Seite über Transplantate. "Ab Git 1.6.5 wurde das flexiblere Git-Ersetzen hinzugefügt, mit dem Sie jedes Objekt durch ein anderes Objekt ersetzen und die Assoziationen über Refs verfolgen können, die zwischen Repos verschoben und gezogen werden können." Daher ist diese Antwort für aktuelle Versionen von git möglicherweise veraltet.
ThorSummoner
129

Vielleicht ist es zu spät, um eine Antwort zu veröffentlichen, aber da diese Seite das erste Ergebnis von Google ist, kann sie dennoch hilfreich sein.

Wenn Sie etwas Speicherplatz in Ihrem Git-Repo freigeben möchten, aber nicht alle Ihre Commits (Rebase oder Graft) neu erstellen möchten und dennoch in der Lage sind, von Personen mit dem vollständigen Repo zu pushen / ziehen / zusammenzuführen, können Sie das Git verwenden Klon flacher Klon ( Parameter --depth ).

; 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

Sie können Ihr vorhandenes Repo möglicherweise flacher machen, indem Sie die folgenden Schritte ausführen:

; 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

Wie entferne ich alle lokalen Git-Tags?

Ps: Ältere Versionen von git haben das Klonen / Drücken / Ziehen von / zu flachen Repos nicht unterstützt.

Alexandre T.
quelle
9
+1 Dies ist die richtige Antwort für neuere Versionen von Git. (Oh, und bitte komm zurück zu PPCG !)
wizzwizz4
6
Wie können Sie cdzu einem Ordner gelangen, der gerade gelöscht wurde? Ich habe das Gefühl, dass hier einige Informationen fehlen. Gibt es auch eine Möglichkeit, diese Änderungen auf das Remote-Repo anzuwenden?
Trogdor
4
@Jez Das wäre die andere Top-Antwort. Diese Antwort ist nichts für Sie, wenn Sie die Geschichte dauerhaft loswerden möchten. Es ist für die Arbeit mit großen Geschichten.
Niemand
4
Um meine eigene Frage zu beantworten: git clone file:///Users/me/Projects/myProject myClonedProject --shallow-since=2016-09-02Funktioniert wie ein Zauber!
Micros
5
@Jez du kannst dein flaches Repo durch Laufen in ein normales umwandeln git filter-branch -- --all. Dies wird alle Hashes darin ändern, aber danach können Sie es auf ein neues Repo verschieben
Ed'ka
60

Diese Methode ist leicht zu verstehen und funktioniert gut. Das Argument für script ( $1) ist ein Verweis (Tag, Hash, ...) auf das Commit, ab dem Sie Ihren Verlauf beibehalten möchten.

#!/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

HINWEIS : Alte Tags bleiben weiterhin vorhanden. Daher müssen Sie sie möglicherweise manuell entfernen

Bemerkung: Ich weiß, dass dies fast die gleiche Antwort wie @yoyodin ist, aber hier gibt es einige wichtige zusätzliche Befehle und Informationen. Ich habe versucht, die Antwort zu bearbeiten, aber da es sich um eine wesentliche Änderung der Antwort von @ yoyodin handelt, wurde meine Bearbeitung abgelehnt. Hier sind die Informationen!

Chris Maes
quelle
Ich schätze die Erklärungen für die Befehle git pruneund git gc. Gibt es eine Erklärung für den Rest der Befehle im Skript? Derzeit ist nicht klar, welche Argumente an ihn übergeben werden und was die einzelnen Befehle tun. Vielen Dank.
user5359531
2
@ user5359531 danke für deine Bemerkung, ich habe für jeden Befehl einige weitere Kommentare hinzugefügt. Hoffe das hilft.
Chris Maes
4
Überall Konflikte zusammenführen ... nicht sehr nützlich
Warpzit
3
@ Warpzit Ich habe Zusammenführungskonflikte durch Hinzufügen -pzum rebaseBefehl
beseitigt
1
Ich folgte genau dem und alles, was ich bekam, war die gleiche Geschichte wie zuvor mit einem neuen Zweig, beginnend mit dem Commit, auf das ich mit der gleichen Geschichte wie zuvor zurückschneiden wollte. Es wurde keine Historie entfernt.
DrStrangepork
51

Versuchen Sie diese Methode So schneiden Sie den Git-Verlauf ab :

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

Hier $1ist SHA-1 des Commits, das Sie behalten möchten, und das Skript erstellt einen neuen Zweig, der alle Commits zwischen $1und enthält, masterund der gesamte ältere Verlauf wird gelöscht. Beachten Sie, dass dieses einfache Skript davon ausgeht, dass kein Zweig aufgerufen wurde temp. Beachten Sie auch, dass dieses Skript die Git-Daten für den alten Verlauf nicht löscht. Führen git gc --prune=all && git repack -a -f -F -dSie aus, nachdem Sie überprüft haben, dass Sie wirklich den gesamten Verlauf verlieren möchten. Möglicherweise müssen Sie rebase --preserve-mergesjedoch gewarnt werden, dass die Git-Implementierung dieser Funktion nicht perfekt ist. Überprüfen Sie die Ergebnisse manuell, wenn Sie diese verwenden.

Yoyodyn
quelle
22
Ich habe es versucht, aber im rebaseSchritt wurden Zusammenführungskonflikte festgestellt. Seltsam - ich hatte nicht erwartet, dass Zusammenführungskonflikte unter diesen Umständen möglich sein könnten.
Craig McQueen
2
Verwenden git commit --allow-empty -m "Truncate history"Sie diese Option, wenn das ausgecheckte Commit keine Dateien enthält.
friederbluemle
2
Wie schiebe ich dies zurück zum Remote-Master? Wenn ich das mache, habe ich sowohl alte als auch neue Geschichte.
Rustyx
1
Was soll "Temp" sein? Was solltest du als Argument dafür anführen? Gibt es ein Beispiel dafür, wie diese Befehle aussehen sollen, wenn Sie sie tatsächlich ausführen? Vielen Dank.
user5359531
1
Ich glaube, 1 $ ist der Commit-Hash. (Weitere Details finden Sie im verlinkten Artikel).
Chris Nolet
34

Als Alternative zum Umschreiben des Verlaufs sollten Sie die Verwendung git replacewie in diesem Artikel aus dem Pro Git- Buch in Betracht ziehen . In dem besprochenen Beispiel wird ein übergeordnetes Commit ersetzt, um den Beginn eines Baums zu simulieren, während der gesamte Verlauf als separater Zweig zur sicheren Aufbewahrung beibehalten wird.

Jeff Bowman
quelle
Ja, ich denke, Sie könnten wahrscheinlich das tun, was wir wollten, wenn Sie auch den separaten Zweig für die vollständige Geschichte auf den Kopf stellen würden. (Wir haben versucht, das Repository zu verkleinern.)
ebneter
1
Ich war entmutigt, weil die Antwort nicht vor Ort war. Es verlinkt jedoch auf die GitScm-Site, und das Tutorial, auf das es verweist, ist sehr gut geschrieben und scheint direkt auf den Punkt der OP-Frage zu kommen.
ThorSummoner
@ThorSummoner Entschuldigung! Ich werde die Antwort vor Ort etwas ausführlicher entwickeln
Jeff Bowman
Leider ist dies keine Alternative zum Umschreiben der Geschichte. Es gibt einen verwirrenden Satz am Anfang des Artikels, der wahrscheinlich diesen Eindruck erweckte. Könnte das aus dieser Antwort entfernt werden? Sie werden in dem Artikel sehen, dass der Autor den Verlauf des abgeschnittenen Zweigs neu schreibt, aber eine Möglichkeit vorschlägt, den alten Zweig "Verlauf" mithilfe von erneut anzuhängen git replace. Ich glaube, dies wurde bei einer anderen Frage korrigiert, bei der Sie diese Antwort gepostet haben.
Mitch
1
Eine Diskussion über git replaceversus git graftwird unter stackoverflow.com/q/6800692/873282
koppor
25

Wenn Sie das Upstream- Repository mit dem vollständigen Verlauf , aber lokalen kleineren Kassen behalten möchten , führen Sie einen flachen Klon mit durch .git clone --depth=1 [repo]

Nachdem Sie ein Commit gedrückt haben, können Sie dies tun

  1. git fetch --depth=1die alten Verpflichtungen zu beschneiden. Dies macht die alten Commits und ihre Objekte unerreichbar.
  2. git reflog expire --expire-unreachable=now --all. Alle alten Commits und ihre Objekte ablaufen lassen
  3. git gc --aggressive --prune=all um die alten Objekte zu entfernen

Siehe auch So entfernen Sie den lokalen Git-Verlauf nach einem Commit. .

Beachten Sie, dass Sie dieses "flache" Repository nicht an eine andere Stelle verschieben können: "flache Aktualisierung nicht zulässig". Siehe Remote abgelehnt (flaches Update nicht erlaubt), nachdem die Git-Remote-URL geändert wurde . Wenn du das willst, musst du beim Pfropfen bleiben.

koppor
quelle
1
Punkt Nummer 1. machte den Unterschied für mich. Prost
Clapas
21

Ich musste mehrere Antworten und andere Informationen lesen, um zu verstehen, was ich tat.

1. Ignorieren Sie alles, was älter als ein bestimmtes Commit ist

Die Datei .git/info/graftskann gefälschte Eltern für ein Commit definieren. Eine Zeile mit nur einer Commit-ID besagt, dass das Commit kein übergeordnetes Element hat. Wenn wir sagen wollten, dass uns nur die letzten 2000 Commits wichtig sind, können wir Folgendes eingeben:

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

git rev-parse gibt uns die Commit-ID des 2000. übergeordneten Elements des aktuellen Commits. Der obige Befehl überschreibt die Transplantatdatei, falls vorhanden. Überprüfen Sie, ob es zuerst da ist.

2. Schreiben Sie den Git-Verlauf neu (optional)

Wenn Sie dieses gepfropfte falsche Elternteil zu einem echten machen möchten, führen Sie Folgendes aus:

git filter-branch -- --all

Es werden alle Commit-IDs geändert. Jede Kopie dieses Repositorys muss unbedingt aktualisiert werden.

3. Bereinigen Sie den Speicherplatz

Ich habe Schritt 2 nicht ausgeführt, weil ich wollte, dass meine Kopie mit dem Upstream kompatibel bleibt. Ich wollte nur etwas Speicherplatz sparen. Um alle alten Verpflichtungen zu vergessen:

git prune
git gc

Alternative: flache Kopien

Wenn Sie eine flache Kopie eines anderen Repositorys haben und nur Speicherplatz sparen möchten, können Sie ein Update durchführen .git/shallow. Aber seien Sie vorsichtig, dass nichts auf ein Commit von früher hinweist. Sie könnten also so etwas ausführen:

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

Der Eintrag in flach funktioniert wie ein Transplantat. Achten Sie jedoch darauf, keine Transplantate zu verwenden und gleichzeitig flach zu sein. Zumindest nicht die gleichen Einträge, es wird fehlschlagen.

Wenn Sie noch einige alte Referenzen (Tags, Zweige, Remote-Köpfe) haben, die auf ältere Commits verweisen, werden diese nicht bereinigt und Sie sparen nicht mehr Speicherplatz.

Maikel
quelle
Die Unterstützung für <GIT_DIR> / info / transplants ist veraltet und wird in einer zukünftigen Git-Version entfernt.
Danny
Bitte verwenden Sie git replacestattdessen. Siehe stackoverflow.com/questions/6800692/…
Joel AZEMAR
3

Beim Rebase oder Push to Head / Master kann dieser Fehler auftreten

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'

Um dieses Problem im Git-Dashboard zu beheben, sollte der Hauptzweig aus "Geschützte Zweige" entfernt werden.

Geben Sie hier die Bildbeschreibung ein

Dann können Sie diesen Befehl ausführen

git push -f origin master

oder

git rebase --onto temp $1 master
HMagdy
quelle
0

Hier gibt es zu viele Antworten, die nicht aktuell sind, und einige erklären die Konsequenzen nicht vollständig. Folgendes hat für mich funktioniert, um den Verlauf mit dem neuesten Git 2.26 zu verkürzen:

Erstellen Sie zuerst ein Dummy-Commit. Dieses Commit wird als erstes Commit in Ihrem abgeschnittenen Repo angezeigt. Sie benötigen dies, da dieses Commit alle Basisdateien für den von Ihnen gespeicherten Verlauf enthält. Die SHA ist die ID des vorherigen Commits des Commits, das Sie behalten möchten (in diesem Beispiel 8365366). Die Zeichenfolge 'Initial' wird als Commit-Nachricht des ersten Commits angezeigt. Wenn Sie Windows verwenden, geben Sie den folgenden Befehl an der Eingabeaufforderung von Git Bash ein.

# 8365366 is id of parent commit after which you want to preserve history
echo 'Initial' | git commit-tree 8365366^{tree}

Der obige Befehl gibt beispielsweise SHA aus d10f7503bc1ec9d367da15b540887730db862023.

Geben Sie jetzt einfach Folgendes ein:

# d10f750 is commit ID from previous command
git rebase --onto d10f750 8365366

Dadurch werden zuerst alle Dateien ab dem Festschreiben 8365366in das Dummy-Festschreiben eingefügt d10f750. Dann werden alle Commits nach 8365366 über dem Ende wiedergegeben d10f750. Schließlich wird der masterVerzweigungszeiger auf das zuletzt wiedergegebene Commit aktualisiert.

Wenn Sie diese abgeschnittenen Repos verschieben möchten, tun Sie dies einfach git push -f.

Einige Dinge zu beachten (dies gilt sowohl für andere als auch für diese Methoden): Tags werden nicht übertragen. Während Commit-IDs und Zeitstempel erhalten bleiben, wird GitHub diese Commits in einer pauschalen Überschrift wie anzeigen Commits on XY date.

Glücklicherweise ist es möglich, den abgeschnittenen Verlauf als "Archiv" beizubehalten. Später können Sie das zugeschnittene Repo mit dem Archiv-Repo verbinden. Informationen hierzu finden Sie in dieser Anleitung .

Shital Shah
quelle
-3

Sie können das Verzeichnis, die Dateien und auch den gesamten Verlauf des Verzeichnisses oder der Datei löschen, indem Sie das unten angegebene jar [download it] und die Befehle verwenden

bfg.jar-Datei: https://rtyley.github.io/bfg-repo-cleaner/

git clone --bare repo-url cd repo_dir java -jar bfg.jar --delete-folders ordnername git reflog expire --expire = now --all && git gc --prune = now --aggressive git push --mirror repo_url

RahulMohan Kolakandy
quelle
-10
  1. git Daten entfernen, rm .git
  2. git init
  3. Fügen Sie eine Git-Fernbedienung hinzu
  4. Kraft drücken
Brad Reid
quelle
6
das wird funktionieren, um ALLE Geschichte zu entfernen, aber nicht für das, was er gefragt hat: Geschichte seit Januar 2010 behalten
Chris Maes
1
Ich wollte mich nur
bedanken,