Jak usunąć starą historię z repozytorium git?

208

Obawiam się, że nie mogłem znaleźć czegoś takiego jak ten konkretny scenariusz.

Mam repozytorium git z dużą historią: ponad 500 oddziałów, ponad 500 tagów, od połowy 2007 roku. Zawiera ~ 19.500 zmian. Chcielibyśmy usunąć całą historię przed 1 stycznia 2010 r., Aby była mniejsza i łatwiejsza w obsłudze (trzymalibyśmy pełną kopię historii w repozytorium archiwum).

Wiem, że zatwierdzenie, które chcę stać się katalogiem głównym nowego repozytorium. Nie mogę jednak wymyślić właściwego git mojo, aby obciąć repozytorium i zacząć od tego zatwierdzenia. Zgaduję jakiś wariant

git filter-branch

konieczne byłyby przeszczepy; może być również konieczne potraktowanie każdego z ponad 200 oddziałów, które chcemy zachować osobno, a następnie załatanie repo z powrotem razem (coś, co robię wiem, jak to zrobić).

Czy ktoś kiedykolwiek zrobił coś takiego? Mam git 1.7.2.3, jeśli to ma znaczenie.

ebneter
źródło

Odpowiedzi:

118

Po prostu utwórz przeszczep nadrzędnego nowego zatwierdzenia głównego do braku nadrzędnego (lub do pustego zatwierdzenia, np. Rzeczywistego zatwierdzenia głównego repozytorium). Na przykładecho "<NEW-ROOT-SHA1>" > .git/info/grafts

Po utworzeniu przeszczepu od razu zaczyna działać; powinieneś być w stanie spojrzeć git logi zobaczyć, jak zniknęły niechciane stare zobowiązania:

$ 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

Jeśli wszystko wygląda zgodnie z przeznaczeniem, możesz po prostu zrobić proste git filter-branch -- --all aby uczynić go trwałym.

UWAGA: po wykonaniu kroku rozgałęzienia filtru wszystkie identyfikatory zatwierdzeń zostaną zmienione, więc każdy korzystający ze starego repozytorium nigdy nie może połączyć się z kimkolwiek korzystającym z nowego repozytorium.

apenwarr
źródło
6
Musiałem zrobić, git filter-branch --tag-name-filter cat -- --allaby zaktualizować tagi. Ale mam też starsze tagi wskazujące na starą historię, którą chcę usunąć. Jak mogę pozbyć się wszystkich starych tagów? Jeśli ich nie usunę, starsza historia nie zniknie i nadal mogę to zobaczyć gitk --all.
Craig McQueen,
9
„Po prostu stwórz przeszczep rodzica nowego użytkownika root, aby zobowiązać się do braku rodzica” wymaga dopracowania. Próbowałem tego i nie udało mi się ustalić składni „no parent”. Strona podręcznika twierdzi, że nadrzędny identyfikator zatwierdzenia jest wymagany; użycie wszystkich zer daje mi tylko błąd.
Marius Gedminas,
6
Na wypadek, gdyby ktokolwiek zastanawiał się, jak to dokładnie działa, jest to dość proste:echo "<NEW-ROOT-HASH>" > .git/info/grafts
friederbluemle
3
Zgadzam się, wyjaśnienie, czym jest przeszczep, byłoby bardziej niż przydatne
Charles Martin
4
Cytat z połączonej strony wiki na temat przeszczepów. „Począwszy od wersji Git 1.6.5, dodano bardziej elastyczną funkcję git replace, która pozwala zamienić dowolny obiekt na dowolny inny obiekt i śledzi skojarzenia za pomocą referencji, które można przesuwać i przeciągać między repozytoriami”. Ta odpowiedź może być nieaktualna w przypadku bieżących wersji git.
ThorSummoner,
130

Być może jest już za późno na opublikowanie odpowiedzi, ale ponieważ ta strona jest pierwszym wynikiem Google, może być nadal pomocna.

Jeśli chcesz zwolnić trochę miejsca w swoim repozytorium git, ale nie chcesz odbudowywać wszystkich swoich commits (rebase lub graft) i nadal możesz wypychać / wyciągać / scalać osoby, które mają pełne repo, możesz użyć git klon płytki klon ( parametr --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

Możesz być w stanie spłycić swoje istniejące repozytorium, wykonując następujące kroki:

; 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

Jak usunąć wszystkie lokalne tagi git?

Ps: Starsze wersje git nie obsługiwały klonowania / wypychania / ściągania z / do płytkich repozytoriów.

Alexandre T.
źródło
9
+1 To poprawna odpowiedź dla nowszych wersji Git. (Och, proszę wróć do PPCG !)
wizzwizz4,
6
Jak cdprzejść do folderu, który właśnie został usunięty? Mam wrażenie, że brakuje tutaj niektórych informacji. Czy jest też sposób na zastosowanie tych zmian w zdalnym repozytorium?
Trogdor,
4
@Jez To byłaby druga najczęściej głosowana odpowiedź. Ta odpowiedź nie jest dla Ciebie, jeśli chcesz trwale pozbyć się historii. Jest przeznaczony do pracy z wielkimi historiami.
Nikt
4
Aby odpowiedzieć na moje pytanie: git clone file:///Users/me/Projects/myProject myClonedProject --shallow-since=2016-09-02Działa jak urok!
Micros
5
@Jez możesz przekształcić płytkie repo w normalne, uruchamiając git filter-branch -- --all. Spowoduje to zmianę wszystkich skrótów, ale potem będziesz mógł przekazać go do nowego repozytorium
Ed'ka
61

Ta metoda jest łatwa do zrozumienia i działa dobrze. Argumentem script ( $1) jest odwołanie (tag, skrót, ...) do zatwierdzenia, od którego chcesz zachować swoją historię.

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

UWAGA: stare tagi nadal będą obecne; więc może być konieczne ich ręczne usunięcie

Uwaga: Wiem, że jest to prawie taka sama odpowiedź jak @yoyodin, ale są tu ważne dodatkowe polecenia i informacje. Próbowałem edytować odpowiedź, ale ponieważ jest to istotna zmiana w odpowiedzi @ yoyodin, moja edycja została odrzucona, więc oto informacje!

Chris Maes
źródło
Doceniam wyjaśnienia dotyczące poleceń git prunei git gc. Czy istnieje wyjaśnienie dla pozostałych poleceń w skrypcie? W tej chwili nie jest jasne, jakie argumenty są przekazywane i co robi każde polecenie. Dzięki.
user5359531,
2
@ user5359531 dzięki za uwagę, dodałem kilka komentarzy do każdego polecenia. Mam nadzieję że to pomoże.
Chris Maes,
4
Scalaj konflikty w dowolnym miejscu ... niezbyt przydatne
Warpzit
3
@Warpzit Pozbyłem się konfliktów scalania, dodając -pdo rebasepolecenia, jak sugerowano w innej odpowiedzi
leonbloy
1
Dokładnie tak postąpiłem i wszystko, co otrzymałem, to ta sama historia, co poprzednio, z nową gałęzią zaczynającą się od zmiany, którą chciałem przyciąć z tą samą historią jak wcześniej. Żadna historia nie została usunięta.
DrStrangepork
51

Wypróbuj tę metodę Jak obciąć historię git :

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

Tutaj $1jest SHA-1 z popełnić chcesz zachować i skrypt utworzy nowy oddział, który zawiera wszystkie zobowiązuje między $1a masteri cała historia jest starsza spadła. Zauważ, że ten prosty skrypt zakłada, że ​​nie masz istniejącej gałęzi o nazwie temp. Zauważ też, że ten skrypt nie usuwa danych git ze starej historii. Uruchom git gc --prune=all && git repack -a -f -F -dpo sprawdzeniu, czy naprawdę chcesz stracić całą historię. Możesz także potrzebować, rebase --preserve-mergesale ostrzegam, że implementacja git tej funkcji nie jest idealna. Sprawdź wyniki ręcznie, jeśli z nich korzystasz.

yoyodyn
źródło
22
Próbowałem tego, ale rebasekrok po kroku dostałem konflikty scalania . Dziwne - nie spodziewałem się, że w takich okolicznościach możliwe będą konflikty scalania.
Craig McQueen,
2
Użyj, git commit --allow-empty -m "Truncate history"jeśli zatwierdzone przez Ciebie zatwierdzenie nie zawiera żadnych plików.
friederbluemle
2
Jak zepchnąć to z powrotem do zdalnego sterownika? Kiedy to robię, kończę na starej i nowej historii.
rustyx
1
Co to powinna być „temp”? Co powinieneś podać za argument? Czy istnieje przykład, jak powinny wyglądać te polecenia, gdy je uruchomisz? Dzięki.
user5359531
1
Uważam, że 1 USD to suma zatwierdzeń. (Więcej szczegółów znajduje się w linkowanym artykule).
Chris Nolet
34

Jako alternatywę dla przepisywania historii, rozważ użycie git replacejak w tym artykule z książki Pro Git . Omawiany przykład obejmuje zastąpienie zatwierdzenia nadrzędnego w celu symulacji początku drzewa, przy jednoczesnym zachowaniu pełnej historii jako oddzielnej gałęzi do przechowywania.

Jeff Bowman
źródło
Tak, myślę, że prawdopodobnie mógłbyś zrobić z tym, co chcieliśmy, jeśli wybrałbyś również oddzielną gałąź pełnej historii. (Próbowaliśmy zmniejszyć repozytorium.)
ebneter
1
Zniechęciło mnie to, że odpowiedź była poza witryną; ale zawiera link do strony GitScm, a samouczek, do którego prowadzi, jest bardzo dobrze napisany i wydaje się bezpośrednio odpowiadać pytaniu PO.
ThorSummoner,
@ThorSummoner Przepraszamy za to! Rozwiążę tę odpowiedź nieco dokładniej na miejscu
Jeff Bowman,
Niestety nie jest to alternatywa dla przepisywania historii. Na początku artykułu znajduje się mylące zdanie, które prawdopodobnie wywarło takie wrażenie. Czy można to usunąć z tej odpowiedzi? W artykule zobaczysz, że autor przepisuje historię obciętej gałęzi, ale proponuje sposób ponownego przyłączenia starszej gałęzi „historii” git replace. Myślę, że to zostało poprawione w innym pytaniu, w którym opublikowałeś tę odpowiedź.
Mitch
1
Dyskusja na temat git replacekontra git graftjest przeprowadzana na stackoverflow.com/q/6800692/873282
koppor
25

Jeśli chcesz zachować na upstream repozytorium z pełnej historii , ale mniejszych lokalnych kasach, zrobić klon z płytką git clone --depth=1 [repo].

Po wciśnięciu zatwierdzenia możesz to zrobić

  1. git fetch --depth=1przycinać stare zobowiązania. To sprawia, że ​​stare zmiany i ich obiekty są nieosiągalne.
  2. git reflog expire --expire-unreachable=now --all. Aby wygasnąć wszystkie stare commity i ich obiekty
  3. git gc --aggressive --prune=all aby usunąć stare obiekty

Zobacz także Jak usunąć lokalną historię git po zatwierdzeniu? .

Pamiętaj, że nie możesz wypchnąć tego „płytkiego” repozytorium w inne miejsce: „płytka aktualizacja nie jest dozwolona”. Zobacz Zdalne odrzucenie (płytka aktualizacja niedozwolona) po zmianie zdalnego adresu URL Git . Jeśli chcesz to zrobić, musisz trzymać się szczepienia.

koppor
źródło
1
Punkt 1. zrobił dla mnie różnicę. Pozdrawiam
clapas,
21

Musiałem przeczytać kilka odpowiedzi i kilka innych informacji, aby zrozumieć, co robię.

1. Zignoruj ​​wszystko starsze niż określony zatwierdzenie

Plik .git/info/graftsmoże definiować fałszywych rodziców dla zatwierdzenia. Linia z tylko identyfikatorem zatwierdzenia mówi, że zatwierdzenie nie ma rodzica. Jeśli chcielibyśmy powiedzieć, że zależy nam tylko na ostatnich 2000 zatwierdzeniach, możemy wpisać:

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

git rev-parse podaje nam identyfikator zatwierdzenia 2000. rodzica bieżącego zatwierdzenia. Powyższe polecenie zastąpi plik przeszczepów, jeśli jest obecny. Sprawdź, czy jest tam pierwszy.

2. Przepisz historię Git (opcjonalnie)

Jeśli chcesz, aby ten szczepiony fałszywy rodzic był prawdziwym rodzicem, uruchom:

git filter-branch -- --all

Zmieni wszystkie identyfikatory zatwierdzeń. Każda kopia tego repozytorium musi zostać silnie zaktualizowana.

3. Oczyść miejsce na dysku

Nie zrobiłem kroku 2, ponieważ chciałem, aby moja kopia była zgodna z wersją wstępną. Chciałem tylko zaoszczędzić trochę miejsca na dysku. Aby zapomnieć o wszystkich starych zobowiązaniach:

git prune
git gc

Alternatywnie: płytkie kopie

Jeśli masz płytką kopię innego repozytorium i po prostu chcesz zaoszczędzić trochę miejsca na dysku, możesz zaktualizować .git/shallow. Ale uważaj, aby nic nie wskazywało na zatwierdzenie wcześniej. Abyś mógł uruchomić coś takiego:

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

Wpis w płytkiej pracy działa jak przeszczep. Ale uważaj, aby nie używać jednocześnie przeszczepów i płytki. Przynajmniej nie miej tam takich samych wpisów, to się nie powiedzie.

Jeśli nadal masz jakieś stare odwołania (tagi, gałęzie, zdalne głowy), które wskazują na starsze zatwierdzenia, nie zostaną one wyczyszczone i nie zaoszczędzisz więcej miejsca na dysku.

Maikel
źródło
Obsługa <GIT_DIR> / info / grafts jest przestarzała i zostanie usunięta w przyszłej wersji Git.
danny
Proszę rozważyć użycie git replacezamiast tego. Zobacz stackoverflow.com/questions/6800692/…
Joel AZEMAR
3

Kiedy rebase lub przeć do głowy / Master może wystąpił ten błąd

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'

Aby rozwiązać ten problem w panelu git, należy usunąć gałąź główną z „gałęzi chronionych”

wprowadź opis zdjęcia tutaj

możesz uruchomić to polecenie

git push -f origin master

lub

git rebase --onto temp $1 master
HMagdy
źródło
0

Jest tu zbyt wiele odpowiedzi, które nie są aktualne, a niektóre nie w pełni wyjaśniają konsekwencje. Oto, co zadziałało dla mnie, aby skrócić historię za pomocą najnowszego gita 2.26:

Najpierw utwórz atrapę zatwierdzenia. To zatwierdzenie pojawi się jako pierwsze zatwierdzenie w skróconym repozytorium. Potrzebujesz tego, ponieważ to zatwierdzenie pomieści wszystkie pliki podstawowe dla historii, którą przechowujesz. SHA jest identyfikatorem poprzedniego zatwierdzenia, które chcesz zachować (w tym przykładzie 8365366). Ciąg „Initial” pojawi się jako komunikat zatwierdzenia pierwszego zatwierdzenia. Jeśli używasz systemu Windows, wpisz poniżej polecenie z wiersza polecenia Git Bash.

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

Powyższe polecenie wyświetli SHA, na przykład d10f7503bc1ec9d367da15b540887730db862023 .

Teraz wystarczy wpisać:

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

Spowoduje to, że najpierw umieści wszystkie pliki w zatwierdzeniu 8365366jako atrapy d10f750. Następnie odtworzy wszystkie zatwierdzenia po 8365366 ponad d10f750. Wreszciemaster wskaźnik gałęzi zostanie zaktualizowany do ostatniego odtworzenia zatwierdzenia.

Teraz, jeśli chcesz wypchnąć te obcięte repozytorium, po prostu zrób git push -f .

Kilka rzeczy, o których należy pamiętać (dotyczy to zarówno innych metod, jak i tej): Tagi nie są przenoszone. Podczas gdy identyfikatory zatwierdzania i znaczniki czasu są zachowane, GitHub pokaże te zatwierdzenia w nagłówkach typu lumpsum Commits on XY date.

Na szczęście możliwe jest zachowanie okrojonej historii jako „archiwum”, a później można dołączyć przycięte repozytorium z repozytorium archiwum. Aby to zrobić, zobacz ten przewodnik .

Shital Shah
źródło
-3

możesz usunąć katalog, pliki, a także całą historię związaną z katalogiem lub plikiem za pomocą niżej wymienionego jar [pobierz go] i poleceń

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

git clone --bare repo-url cd repo_dir java -jar bfg.jar --delete-foldery nazwa_folderu git reflog wygasają --expire = teraz --all && git gc --prune = teraz - agresywny git push --mirror repo_url

RahulMohan Kolakandy
źródło
-10
  1. usuń dane git, rm .git
  2. git init
  3. dodaj zdalny git
  4. siła pchnięcia
Brad Reid
źródło
6
to zadziała, aby usunąć CAŁĄ historię, ale nie o to, o co prosił: zachowaj historię od stycznia 2010 r.
Chris Maes
1
Chciałem tylko podziękować, ponieważ pomogło mi to w moim scenariuszu, chociaż może to nie być odpowiednia odpowiedź na pytanie
apnerve,