Tworzenie repozytorium GitHub z tylko podzbiorem historii lokalnego repozytorium

Tło: zbliżam się do open sourcing osobisty kod badawczy nad którym pracuję od ponad dwóch lat. Zaczęło życie jako repozytorium SVN, ale przeniosłem się do Git około rok temu i chciałbym udostępnić kod na Githubie. Jednak na przestrzeni lat zgromadziła wiele cruft i wolałbym, aby Wersja publiczna rozpoczęła swoje życie w obecnym stanie. Jednak nadal chciałbym przyczynić się do tego i włączyć innych ludzi potencjalny wkład.

Pytanie: czy istnieje sposób na "rozwidlenie" repozytorium Gita tak, że żadna historia nie zostanie zachowana na forku (który żyje na Githubie), ale moje lokalne repozytorium nadal ma kompletną historię i mogę ściągnąć/wypchnąć do Githuba?

Nie mam doświadczenia w administrowaniu dużymi repozytoriami, więc szczegóły są bardzo mile widziane.

Author: Peter Mortensen, 2011-04-20

3 answers

Możesz stworzyć nową, świeżą historię całkiem łatwo w Git. Powiedzmy, że chcesz, aby Twoja master gałąź była tą, którą będziesz przesyłać do Githuba, a twoja pełna historia ma być przechowywana w old-master. Możesz po prostu przenieść swoją master gałąź do old-master, a następnie uruchomić nową gałąź bez historii za pomocą git checkout --orphan:

git branch -m master old-master
git checkout --orphan master
git commit -m "Import clean version of my code"

Teraz masz nową master gałąź bez historii, którą możesz wcisnąć do Githuba. Ale, jak mówisz, chciałbyś być w stanie zobaczyć całą starą historię w lokalnym repozytorium; i prawdopodobnie chciałby, aby nie został rozłączony.

Możesz to zrobić za pomocą git replace. Replacement ref jest sposobem określenia alternatywnego commita za każdym razem, gdy Git patrzy na dany commit. Możesz więc powiedzieć Gitowi, aby spojrzał na ostatni commit Twojej starej gałęzi, zamiast na pierwszy commit Twojej nowej gałęzi, patrząc na historię. Aby to zrobić, musisz wprowadzić odłączoną historię ze starego repozytorium.

git replace master old-master

Teraz ty masz nową gałąź, w której możesz zobaczyć całą swoją historię, ale rzeczywiste obiekty commitów są odłączone od starej historii, więc możesz wypchnąć nowe commity na GitHub bez pojawiania się starych commitów. Wypchnij swoją master gałąź do Githuba, a tylko nowe commity trafią do Githuba. Ale spójrz na historię w gitk lub git log, a zobaczysz pełną historię.

git push github master:master
gitk --all

Gotchas

Jeśli kiedykolwiek oprzesz nowe gałęzie na starych commitach, będziesz trzeba uważać, aby historia była oddzielona; w przeciwnym razie nowe commity na tych gałęziach będą miały stare commity w swojej historii, więc wyciągniesz całą historię, jeśli wypchniesz ją na GitHub. Dopóki zachowasz wszystkie nowe commity oparte na nowym master, nic Ci nie będzie.

Jeśli kiedykolwiek uruchomisz git push --tags github, to popchnie wszystkie Twoje tagi, w tym Stare, co spowoduje, że cała Twoja stara historia zostanie pociągnięta razem z nią. Możesz sobie z tym poradzić przez usuwanie wszystkich starych tagów (git tag -d $(git tag -l)), lub nigdy nie używając git push --tags, ale tylko ręcznie, lub używając dwóch repozytoriów, jak opisano poniżej.

Podstawowy problem leżący u podstaw obu tych zmian polega na tym, że jeśli kiedykolwiek wypchniesz dowolny ref, który łączy się ze starą historią (inną niż poprzez zastąpione commity), wypchniesz całą starą historię. Prawdopodobnie najlepszym sposobem na uniknięcie tego jest użycie dwóch repozytoriów, jednego zawierającego tylko nowe commity, a drugiego zawiera zarówno starą, jak i nową historię, w celu sprawdzenia pełnej historii. Wykonujesz całą swoją pracę, zatwierdzanie, popychanie i wyciąganie z Githuba, w repozytorium tylko z nowymi commitami; w ten sposób nie możesz przypadkowo wypchnąć starych commitów.

Następnie pobierasz wszystkie nowe commity do repozytorium, które ma pełną historię, kiedy tylko chcesz spojrzeć na całość. Możesz pobrać z GitHub lub innego lokalnego repozytorium, cokolwiek będzie wygodniejsze. Będzie to twoje archiwum, ale aby uniknąć przypadkowego opublikowania Twojej starej historii, nigdy nie naciskasz z niego na GitHub. Oto jak możesz go skonfigurować:

~$ mkdir newrepo
~$ cd newrepo
newrepo$ git init
newrepo$ git pull ~/oldrepo master
# Now newrepo has just the new history; we can set up oldrepo to pull from it
newrepo$ cd ~/oldrepo
oldrepo$ git remote add newrepo ~/newrepo
oldrepo$ git remote update
oldrepo$ git branch --set-upstream master newrepo/master
# ... do work in newrepo, commit, push to GitHub, etc.
# Now if we want to look at the full history in oldrepo:
oldrepo$ git pull

Jeśli używasz Gita starszego niż 1.7.2

Nie masz git checkout --orphan, więc musisz to zrobić ręcznie, tworząc nowe repozytorium z bieżącej wersji istniejącego repozytorium, a następnie pobierając starą historię rozłączenia. Można to zrobić z, dla przykład:

oldrepo$ mkdir ~/newrepo
oldrepo$ cp $(git ls-files) ~/newrepo
oldrepo$ cd ~/newrepo
newrepo$ git init
newrepo$ git add .
newrepo$ git commit -m "Import clean version of my code"
newrepo$ git fetch ~/oldrepo master:old-master

Jeśli używasz Gita starszego niż 1.6.5

git replace i replace refs zostały dodane w 1.6.5, więc będziesz musiał użyć starszego, nieco mniej elastycznego mechanizmu znanego jako grafts , który pozwala na określenie alternatywnych rodziców dla danego commita. Zamiast polecenia git replace Uruchom:

echo $(git rev-parse master) $(git rev-parse old-master) >> .git/info/grafts

To sprawi, że będzie wyglądało lokalnie, jakby master commit miał old-master jako swój rodzic, więc zobaczysz o jeden commit więcej niż w przypadku git replace.

 70
Author: Brian Campbell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-04-16 11:52:21

ODPOWIEDŹ Briana wydaje się być kompletna i kompetentna, ale trochę złożona.

Łatwym rozwiązaniem byłoby utrzymanie dwóch repozytoriów.

Prywatne repozytorium GitHub, nad którym pracujesz. Robisz całą pełną historię wypycha do tego repozytorium.

Drugim repozytorium jestpubliczne repozytorium GitHub, do którego publikujesz tylko wtedy, gdy chcesz "udostępnić" nową wersję publicznie. Publikujesz do niego za pomocą prostej łatki diff +, a następnie zatwierdzasz / align = "left" /

 2
Author: Guy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-04-16 11:55:11

Bardzo prosty i ciekawy sposób na zrobienie tego jest jak poniżej -

Załóżmy, że masz commity C1 do C10 w REPO-a, gdzie C1 jest początkowym commitem, a C10 ostatnim nagłówkiem. I chcesz utworzyć nowy REPO-B taki, że ma commity C4 do C8 (podzbiór).

Uwaga: użycie tej metody zmieniłoby Shas commitów( na przykład C4' na C8' w tym przypadku), ale zmiany, które posiada każdy commit, pozostaną takie same, a twój pierwszy commit rozpocznie się od wszystkich zmian wasze wcześniejsze commity do tego momentu razem wzięte.

Co mam zrobić?


Recursively copy everything over on your local machine

cp -R REPO-A REPO-B

Opcjonalnie usuń wszystkie piloty z REPO-B, ponieważ najprawdopodobniej chcesz użyć tego jako oddzielnego repozytorium.

cd REPO-B
git remote -v
git remote remove REMOTE_NAME

Wymuś przesunięcie wskaźnika gałęzi na późniejszy koniec podzbioru. Dla testerów od C4 do C8 oznaczałoby to C8. Ale najprawdopodobniej potrzebowałbyś podzbiorów do głowicy (np. od C4 do C10 lub C6 do C10) w takim przypadku nie jest wymagany poniższy krok.

git checkout -b temp
git branch -f master C8
git checkout master
git branch -D temp

Wprowadź commit SHA wcześniejszego końca podzbioru w katalogu file .git/info/grafts. W tym przypadku jest to SHA commit C4.

git rev-parse --verify C4 >> .git/info/grafts

Wykonaj filtrowanie gałęzi Git bez żadnych argumentów:

git filter-branch

Lub to, co nie działa:

git filter-branch --all

Możesz teraz wcisnąć to do oddzielnego / nowego pilota, jeśli chcesz:

git remote add origin NEWREMOTE
git push -u origin master

Jak to działa?


Ten link mówi ci, jak to właściwie naprawdę działa - http://git.661346.n2.nabble.com/how-to-delete-the-entire-history-before-a-certain-commit-td5000540.html

Możesz przeczytać o graftach na stronie podręcznika Git-filter-branch(1), w gitrepository-layout(5) Opis układu repozytorium Git, a w gitglossary( 7), Słowniczek Gita.

W skrócie, każda linia w .git / info / grafts składa się z identyfikatora SHA-1 obiektu, po której następuje oddzielona spacją lista efektywnych (szczepionych) rodziców. Więc do cięcia historii, np. po zatwierdzeniu a3eb250f996bf5e, trzeba wstawić linia zawierająca tylko ten SHA-1 w .plik git / info / grafts, np.:

$ git rev-parse --verify a3eb250f996bf5e>>.git / info / grafts

 1
Author: Sumeet Pareek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-04-16 11:59:45