Pytałem wcześniej, jak zmiażdżyć pierwsze dwa commity w repozytorium git.
Chociaż rozwiązania są dość interesujące i nie tak zawrotne w głowie, jak niektóre inne rzeczy w git, wciąż są trochę przysłowiową torbą cierpienia, jeśli trzeba powtarzać procedurę wiele razy podczas opracowywania projektu.
Wolę więc przejść przez ból tylko raz, a potem móc wiecznie korzystać ze standardowej interaktywnej bazy.
To, co chcę zrobić, to mieć puste początkowe zatwierdzenie, które istnieje wyłącznie w celu bycia pierwszym. Bez kodu, bez niczego. Zajmuje tylko miejsce, dzięki czemu może być bazą dla bazy.
Moje pytanie brzmi zatem: mając istniejące repozytorium, jak przejść do wstawiania nowego, pustego zatwierdzenia przed pierwszym i przesuwania wszystkich do przodu?
Odpowiedzi:
Aby to osiągnąć, należy wykonać 2 kroki:
newroot
Dla wygody umieścimy nowy pusty zatwierdzenie w tymczasowej gałęzi .1. Utwórz nowy pusty zatwierdzenie
Można to zrobić na wiele sposobów.
Używając tylko hydrauliki
Najczystszym podejściem jest użycie hydrauliki Gita do bezpośredniego utworzenia zatwierdzenia, co pozwala uniknąć dotykania kopii roboczej lub indeksu lub gałęzi, która jest pobierana itp.
Utwórz obiekt drzewa dla pustego katalogu:
Zawiń wokół niego zatwierdzenie:
Utwórz odniesienie do niego:
Możesz oczywiście przestawić całą procedurę na jednowarstwową, jeśli dobrze znasz swoją powłokę.
Bez kanalizacji
Za pomocą zwykłych poleceń porcelany nie można utworzyć pustego zatwierdzenia bez sprawdzenia
newroot
gałęzi i wielokrotnego aktualizowania indeksu i kopii roboczej, bez wyraźnego powodu. Ale niektórym może to być łatwiejsze do zrozumienia:Zauważ, że w bardzo starych wersjach Gita, które nie mają
--orphan
przełącznika nacheckout
, musisz zastąpić pierwszy wiersz tym:2. Przepisz historię, aby zacząć od tego pustego zatwierdzenia
Masz tutaj dwie opcje: bazowanie lub czyste przepisywanie historii.
Rebasing
Ma to zaletę prostoty. Jednak zaktualizuje także nazwę i datę osoby zatwierdzającej przy każdym ostatnim zatwierdzeniu w oddziale.
Ponadto, w przypadku niektórych historii przypadków, może nawet zawieść z powodu konfliktów scalania - pomimo tego, że bazujesz na zatwierdzeniu, które nie zawiera niczego.
Przepisywanie historii
Czystsze podejście polega na przepisaniu gałęzi. W przeciwieństwie do
git rebase
, musisz sprawdzić, które zatwierdzenie rozpoczyna się w twoim oddziale:Oczywiście przepisywanie odbywa się na drugim etapie; to pierwszy krok, który wymaga wyjaśnienia. Co
git replace
mówi Git, że ilekroć widzi odniesienie do obiektu, który chcesz zastąpić, Git powinien zamiast tego spojrzeć na zastąpienie tego obiektu.Za pomocą
--graft
przełącznika mówisz mu coś nieco innego niż zwykle. Mówisz, że nie masz jeszcze obiektu zastępującego, ale chcesz zastąpić<currentroot>
obiekt zatwierdzenia jego dokładną kopią, z wyjątkiem tego, że nadrzędne zatwierdzenia zastąpienia powinny być tymi wymienionymi przez Ciebie (tj.newroot
Zatwierdzeniem ). Następniegit replace
idzie do przodu i tworzy to zatwierdzenie dla ciebie, a następnie deklaruje to jako zamiennik oryginalnego zatwierdzenia.Teraz, jeśli to zrobisz
git log
, zobaczysz, że rzeczy już wyglądają tak, jak chcesz: gałąź zaczyna sięnewroot
.Należy jednak pamiętać, że
git replace
tak naprawdę nie modyfikuje historii - ani nie rozprzestrzenia się poza repozytorium. Po prostu dodaje lokalne przekierowanie do twojego repozytorium z jednego obiektu do drugiego. Oznacza to, że nikt inny nie widzi efektu tej zamiany - tylko ty.Dlatego ten
filter-branch
krok jest konieczny. Pogit replace
utworzeniu dokładnej kopii ze skorygowanymi zatwierdzeniami nadrzędnymi dla zatwierdzenia głównego;git filter-branch
następnie powtarza ten proces dla wszystkich następujących zatwierdzeń. Właśnie tam historia jest przepisywana, abyś mógł ją udostępnić.źródło
--onto newroot
opcja jest zbędna; możesz się bez niego obejść, ponieważ przekazany argumentnewroot
jest taki sam, jak argument poprzedzający -newroot
.git-checkout
miał tę zmianę. Najpierw zaktualizowałem to, aby wspomniało o tym podejściu, dzięki za wskaźnik.git rebase --merge -s recursive -X theirs --onto newroot --root master
aby automatycznie rozwiązać wszystkie konflikty (zobacz tę odpowiedź). @AlexanderKuzinScalenie odpowiedzi Arystotelesa Pagaltzisa i Uwe Kleine-Königa oraz komentarza Richarda Bronosky'ego.
(po prostu umieścić wszystko w jednym miejscu)
źródło
git rebase newroot master
powodu błędu musiałem zmienić polecenie rebase na .Podoba mi się odpowiedź Arystotelesa. Okazało się jednak, że dla dużego repozytorium (> 5000 zatwierdzeń) odgałęzienie filtru działa lepiej niż rebase z kilku powodów 1) jest szybsze 2) nie wymaga interwencji człowieka, gdy występuje konflikt scalenia. 3) może przepisać tagi - zachowując je. Zauważ, że odgałęzienie filtru działa, ponieważ nie ma wątpliwości co do zawartości każdego zatwierdzenia - jest dokładnie takie samo jak przed tym „rebase”.
Moje kroki to:
Zauważ, że opcje „--tag-name-filter cat” oznaczają, że tagi zostaną przepisane, aby wskazywały na nowo utworzone zatwierdzenia.
źródło
Z powodzeniem wykorzystałem fragmenty odpowiedzi Arystotelesa i Kenta:
Spowoduje to również przepisanie wszystkich gałęzi (nie tylko
master
) oprócz znaczników.źródło
refs/original/
i usuwa każdy numer referencyjny. Odwołania, które usuwa, powinny już być przywoływane przez inną gałąź, więc tak naprawdę nie znikają, tylko zostająrefs/original/
usunięte.timedatectl set-time '2017-01-01 00:00:00'
daćnewroot
starą sygnaturę czasową.git rebase --root --onto $emptyrootcommit
powinien zrobić to łatwo
źródło
$emptyrootcommit
jest z pewnością zmienną powłoki, która z pewnością się rozszerza?Myślę, że użycie
git replace
igit filter-branch
jest lepszym rozwiązaniem niż użyciegit rebase
:Ideą tego jest:
git filter-branch
Oto skrypt dla 2 pierwszych kroków:
Możesz uruchomić ten skrypt bez ryzyka (nawet jeśli wykonanie kopii zapasowej przed wykonaniem czynności, której nigdy wcześniej nie robiłeś, jest dobrym pomysłem;)), a jeśli wynik nie jest zgodny z oczekiwaniami, po prostu usuń pliki utworzone w folderze
.git/refs/replace
i spróbuj ponownie; )Po sprawdzeniu, że stan repozytorium jest zgodny z oczekiwaniami, uruchom następujące polecenie, aby zaktualizować historię wszystkich oddziałów :
Teraz musisz zobaczyć 2 historie, starą i nową (
filter-branch
więcej informacji na ten temat znajdziesz w pomocy ). Możesz porównać 2 i ponownie sprawdzić, czy wszystko jest w porządku. Jeśli jesteś zadowolony, usuń niepotrzebne pliki:Możesz wrócić do swojego
master
oddziału i usunąć oddział tymczasowy:Teraz wszystko powinno być zrobione;)
źródło
Byłem podekscytowany i napisałem „idempotentną” wersję tego ładnego skryptu ... zawsze wstawi ten sam pusty zatwierdzenie, a jeśli uruchomisz go dwa razy, nie zmieni to za każdym razem skrótów zatwierdzenia. Oto moje zdanie na temat git-insert-empty-root :
Czy to warte dodatkowej złożoności? może nie, ale użyję tego.
POWINIEN także pozwolić na wykonanie tej operacji na kilku sklonowanych kopiach repozytorium i uzyskać takie same wyniki, aby były nadal kompatybilne ... testowanie ... tak, działa, ale trzeba również usunąć i dodać ponownie piloty, np .:
źródło
Aby dodać puste zatwierdzenie na początku repozytorium, jeśli zapomniałeś utworzyć puste zatwierdzenie natychmiast po „git init”:
źródło
Oto co wymyśliłem:
źródło
Oto mój
bash
skrypt oparty na odpowiedzi Kenta z ulepszeniami:master
;git checkout --orphan
działa tylko z gałęzią, a nie stanem odłączonym, więc jest sprawdzany wystarczająco długo, aby nowy root zatwierdził, a następnie usunięty;filter-branch
(Kent zostawił tam symbol zastępczy do ręcznej zamiany);filter-branch
operacja przepisuje tylko lokalne oddziały, nie zbyt pilotyźródło
Aby przełączyć zatwierdzenie główne:
Najpierw utwórz zatwierdzenie, które chcesz jako pierwsze.
Po drugie, zmień kolejność zatwierdzeń, używając:
git rebase -i --root
Edytor będzie pojawiał się z zatwierdzeniami aż do zatwierdzenia głównego, na przykład:
wybierz 1234 starą wiadomość główną
wybierz 0294 Zatwierdź pośrodku
wybierz 5678 zatwierdzenie, które chcesz umieścić w katalogu głównym
Następnie możesz umieścić zatwierdzenie, które chcesz, umieszczając je w pierwszym wierszu. W przykładzie:
wybierz 5678 zatwierdzenie, które chcesz umieścić w katalogu głównym
wybierz 1234 starą wiadomość główną
wybierz 0294 Zatwierdź pośrodku
Wyjdź z edytora, aby zmienić kolejność zatwierdzeń.
PS: Aby zmienić edytor, którego używa git, uruchom:
git config --global core.editor name_of_the_editor_program_you_want_to_use
źródło
Łącząc najnowsze i najlepsze. Bez skutków ubocznych, bez konfliktów, utrzymanie tagów.
Pamiętaj, że w GitHub stracisz dane uruchamiania CI, a PR może zostać pomieszany, chyba że inne gałęzie również zostaną naprawione.
źródło
Po odpowiedzi Arystoteles Pagaltzis i inni, ale używając prostszych poleceń
Pamiętaj, że Twoje repo nie powinno zawierać żadnych lokalnych modyfikacji oczekujących na zatwierdzenie.
Uwaga
git checkout --orphan
będzie działać w nowych wersjach git.Uwaga: przez większość czasu
git status
przydatne wskazówki.źródło
Uruchom nowe repozytorium.
Ustaw datę z powrotem na żądaną datę początkową.
Rób wszystko, co chcesz, aby to zrobić, dostosowując czas systemowy do odzwierciedlenia tego, kiedy tego chciałeś. W razie potrzeby wyciągaj pliki z istniejącego repozytorium, aby uniknąć niepotrzebnego pisania.
Kiedy dotrzesz do dzisiaj, zamień repozytoria i gotowe.
Jeśli jesteś po prostu szalony (ugruntowany), ale dość inteligentny (prawdopodobnie, ponieważ musisz mieć pewną liczbę bystrych pomysłów, aby wymyślić takie szalone pomysły), napisz ten proces.
Ułatwi to również, gdy zdecydujesz, że przeszłość wydarzy się w inny sposób za tydzień.
źródło
Wiem, że ten post jest stary, ale ta strona jest pierwsza, gdy Googling „wstawia commit git”.
Po co komplikować proste rzeczy?
Masz ABC i chcesz ABZC.
git rebase -i trunk
(lub cokolwiek przed B)git add ..
git commit
(git commit --amend
który będzie edytować B, a nie tworzyć Z)[Możesz tutaj zrobić tyle,
git commit
ile chcesz, aby wstawić więcej zatwierdzeń. Oczywiście możesz mieć problemy z krokiem 5, ale rozwiązywanie konfliktu z git to umiejętność, którą powinieneś mieć. Jeśli nie, ćwicz!]git rebase --continue
Proste, prawda?
Jeśli rozumiesz
git rebase
, dodanie zatwierdzenia „root” nie powinno stanowić problemu.Baw się dobrze z git!
źródło
git rebase
nie może tego zrobić.