Zwijanie historii repozytorium git

85

Mamy projekt git, który ma dość długą historię.

W szczególności na początku projektu było w projekcie sporo binarnych plików zasobów, które zostały teraz usunięte, ponieważ są w rzeczywistości zasobami zewnętrznymi.

Jednak rozmiar naszego repozytorium wynosi> 200 MB (całkowite pobranie wynosi obecnie ~ 20 MB) ze względu na wcześniejsze zatwierdzenie tych plików.

Chcielibyśmy „zwinąć” historię, tak aby repozytorium wyglądało na utworzone z późniejszej wersji niż było. Na przykład

1-----2-----3-----4-----+---+---+
                   \       /
                    +-----+---+---+
  1. Utworzono repozytorium
  2. Dodano duży zestaw plików binarnych
  3. Usunięto duży zestaw plików binarnych
  4. Nowy zamierzony „start” repozytorium

Tak skutecznie chcemy stracić historię projektu przed pewnym momentem. W tym momencie jest tylko jedna gałąź, więc nie ma komplikacji przy próbie radzenia sobie z wieloma punktami startowymi itp. Jednak nie chcemy stracić całej historii i rozpocząć nowego repozytorium z aktualną wersją.

Czy to możliwe, czy też jesteśmy skazani na wieczne posiadanie rozdętego repozytorium?

Gareth
źródło

Odpowiedzi:

89

Możesz usunąć binarny wzdęcie i zachować resztę swojej historii. Git umożliwia zmianę kolejności i „zgniatanie” wcześniejszych zatwierdzeń, dzięki czemu można łączyć tylko te zatwierdzenia, które dodają i usuwają duże pliki binarne. Jeśli wszystkie dodania zostały wykonane w jednym zatwierdzeniu, a usunięcia w innym, będzie to znacznie łatwiejsze niż zajmowanie się każdym plikiem.

$ git log --stat       # list all commits and commit messages 

Wyszukaj w niej zatwierdzenia, które dodają i usuwają twoje pliki binarne i zanotuj ich SHA1, powiedzmy 2bcdefi 3cdef3.

Następnie, aby edytować historię repozytorium, użyj rebase -ipolecenia z opcją interaktywną, zaczynając od rodzica zatwierdzenia, do którego dodałeś pliki binarne. Uruchomi twój $ EDITOR i zobaczysz listę zatwierdzeń zaczynających się od 2bcdef:

$ git rebase -i 2bcdef^    # generate a pick list of all commits starting with 2bcdef
# Rebasing zzzzzz onto yyyyyyy 
# 
# Commands: 
#  pick = use commit 
#  edit = use commit, but stop for amending 
#  squash = use commit, but meld into previous commit 
# 
# If you remove a line here THAT COMMIT WILL BE LOST.
#
pick 2bcdef   Add binary files and other edits
pick xxxxxx   Another change
  .
  .
pick 3cdef3   Remove binary files; link to them as external resources
  .
  .

Wstaw squash 3cdef3jako drugą linię i usuń linię, która mówi pick 3cdef3z listy. Masz teraz listę akcji interaktywnych, rebasektóra połączy zatwierdzenia, które dodają i usuwają twoje pliki binarne w jeden zatwierdzenie, którego różnicą są po prostu inne zmiany w tych zatwierdzeniach. Następnie ponownie zastosuje wszystkie kolejne zatwierdzenia w kolejności, gdy powiesz mu, aby ukończył:

$ git rebase --continue

Zajmie to minutę lub dwie.
Masz teraz repozytorium, w którym nie ma już przychodzących ani wychodzących plików binarnych. Ale nadal będą zajmować miejsce, ponieważ domyślnie Git przechowuje zmiany przez 30 dni, zanim będą mogły zostać zebrane, abyś mógł zmienić zdanie. Jeśli chcesz je teraz usunąć:

$ git reflog expire --expire=1.minute refs/heads/master
      #all deletions up to 1 minute  ago available to be garbage-collected
$ git fsck --unreachable      # lists all the blobs(files) that will be garbage-collected
$ git prune
$ git gc                      

Teraz usunąłeś wzdęcie, ale zachowałeś resztę swojej historii.

Paweł
źródło
7
Musisz tylko pamiętać, że jeśli inni już wyciągnęli z tego repozytorium, przepisanie historii zmyli ich przyciąganie. Podręcznik git-rebase wyjaśnia, jak odzyskać te inne repozytoria. kernel.org/pub/software/scm/git/docs/git-rebase.html
Otto,
to świetna odpowiedź na konkretny problem użytkownika, ale nie na rzeczywiste pytanie! Odpowiedź Davitenio jest świetną odpowiedzią na rzeczywiste pytanie.
Sam Watkins,
27

Możesz użyć git filter-branchz graftami, aby zatwierdzić numer 4 jako nowe zatwierdzenie root twojej gałęzi. Po prostu utwórz plik .git/info/graftszawierający tylko jedną linię zawierającą SHA1 zatwierdzenia numer 4.

Jeśli teraz zrobisz git loglub gitkzobaczysz, że te polecenia wyświetlą zatwierdzenie numer 4 jako katalog główny twojej gałęzi. Ale w twoim repozytorium nic się nie zmieni. Możesz usunąć, .git/info/graftsa wyjście git loglub gitkbędzie jak poprzednio. Aby zatwierdzić numer 4 jako nowy root, będziesz musiał uruchomić git filter-branchbez argumentów.

davitenio
źródło
Jest to o wiele lepsze niż rebase, ponieważ nie ma problemów z zachowaniem zatwierdzeń scalających i nie powoduje zmiany sygnatur czasowych. Łatwiejsze i szybsze niż wszystkie metody rebase.
mmrobins
Właściwie, czy istnieje sposób na fizyczne usunięcie wszystkich zatwierdzeń, które nie są już częścią tej gałęzi? git gc --prune=0wydaje się, że ich nie czyści.
Verhogen
1
@verhogen git gc --prune=nowfizycznie czyści wszystkie zatwierdzenia, do których nie ma już odniesienia. Jeśli to nie zadziała, możesz mieć zdalną gałąź śledzenia, która nadal odwołuje się do starego katalogu głównego. Wyświetl listę za pomocą git branch -r, a następnie usuń zdalną gałąź, na przykład za pomocą, git branch -rd origin/mastera następnie uruchom git gc --prune=nowponownie.
kayahr
20

Dzięki wpisowi JesperE, który przyjrzałem się git-filter-branch- to może być to, czego chcesz. Wygląda na to, że możesz zachować również swoje wcześniejsze zatwierdzenia, z wyjątkiem tego, że zostaną zmodyfikowane po usunięciu dużych plików. Ze strony podręcznika git-filter-branch :

Załóżmy, że chcesz usunąć plik (zawierający informacje poufne lub naruszenie praw autorskich) ze wszystkich zatwierdzeń:

git filter-branch --tree-filter 'rm nazwa_pliku' HEAD

Koniecznie przeczytaj tę stronę podręcznika ... oczywiście chciałbyś to zrobić na wolnym klonie swojego repozytorium, aby upewnić się, że działa zgodnie z oczekiwaniami.

Pat Notz
źródło
2
Sprawdź link na githubie ... ma kilka potężnych opcji z poleceniem git-filter-branch: help.github.com/articles/remove-sensitive-data
ricosrealm
5

Czy git-fast-exportto, czego szukasz?

NAME
   git-fast-export - Git data exporter

SYNOPSIS
   git-fast-export [options] | git-fast-import

DESCRIPTION
   This program dumps the given revisions in a form suitable to be piped into git-fast-
   import(1).

   You can use it as a human readable bundle replacement (see git-bundle(1)), or as a kind
   of an interactive git-filter-branch(1).
JesperE
źródło