Jeśli w przeszłości mam zatwierdzenie, które wskazuje na jednego z rodziców, ale chcę zmienić rodzica, na który wskazuje, jak mam to zrobić?
Korzystanie git rebase
. Jest to ogólne polecenie „weź zatwierdzenie (-a) i umieść je / plop na innym nadrzędnym (podstawowym)” poleceniu w Git.
Niektóre rzeczy należy jednak wiedzieć:
Ponieważ zatwierdzenia SHA dotyczą ich rodziców, po zmianie elementu nadrzędnego danego zatwierdzenia zmieni się jego SHA - podobnie jak SHA wszystkich zatwierdzeń, które następują po nim (nowsze od niego) w linii rozwoju.
Jeśli pracujesz z innymi ludźmi i już opublikowałeś omawiane zatwierdzenie publicznie tam, gdzie je pobrali, modyfikacja zatwierdzenia jest prawdopodobnie złym pomysłem. Wynika to z nr 1, a tym samym wynikającego z tego zamieszania w repozytoriach innych użytkowników, gdy próbuje się dowiedzieć, co się stało, ponieważ Twoje SHA nie pasują już do nich dla „tych samych” zobowiązań. (Szczegółowe informacje znajdują się w sekcji „ODZYSKIWANIE Z REBASY UPSTREAM” na połączonej stronie podręcznika).
To powiedziawszy, jeśli obecnie jesteś w oddziale z pewnymi zatwierdzeniami, które chcesz przenieść do nowego rodzica, wyglądałoby to mniej więcej tak:
git rebase --onto <new-parent> <old-parent>
To przeniesie wszystko po <old-parent>
w bieżącej gałęzi, aby usiąść <new-parent>
zamiast niej.
--onto
służy! Dokumentacja zawsze była dla mnie całkowicie niejasna, nawet po przeczytaniu tej odpowiedzi!git rebase --onto <new-parent> <old-parent>
Powiedziała mi wszystko o tym, jak wykorzystaćrebase --onto
to każde inne pytanie i dokument, które czytałem do tej pory, czego nie udało się.rebase this commit on that one
lubgit rebase --on <new-parent> <commit>
. Korzystanie ze starego rodzica tutaj nie ma dla mnie sensu.git rebase <new-parent>
.Jeśli okaże się, że musisz unikać ponownego wprowadzania kolejnych zatwierdzeń (np. Ponieważ zapisywanie historii byłoby niemożliwe do wykonania), możesz użyć git replace (dostępny w Git 1.6.5 i nowszych).
Po ustaleniu powyższej zamiany wszelkie żądania dotyczące obiektu B faktycznie zwrócą obiekt C. Zawartość C jest dokładnie taka sama jak zawartość B, z wyjątkiem pierwszego rodzica (ci sami rodzice (z wyjątkiem pierwszego), to samo drzewo, ten sam komunikat zatwierdzenia).
Zastąpienia są domyślnie aktywne, ale można je wyłączyć, używając
--no-replace-objects
opcji git (przed nazwą polecenia) lub ustawiającGIT_NO_REPLACE_OBJECTS
zmienną środowiskową. Zamienniki można udostępniać, naciskającrefs/replace/*
(oprócz zwykłegorefs/heads/*
).Jeśli nie podoba ci się zatwierdzanie zmian (wykonane przy pomocy sed powyżej), możesz utworzyć zamianę zamienną za pomocą poleceń wyższego poziomu:
Duża różnica polega na tym, że ta sekwencja nie propaguje dodatkowych rodziców, jeśli B jest zatwierdzeniem scalania.
źródło
refs/replace/
hierarchii odwołań. Jeśli musisz to zrobić tylko raz, możesz to zrobićgit push yourremote 'refs/replace/*'
w repozytorium źródłowym igit fetch yourremote 'refs/replace/*:refs/replace/*'
repozytoriach docelowych. Jeśli musisz to zrobić wiele razy, możesz zamiast tego dodać te refspecs doremote.yourremote.push
zmiennej config (w źródłowym repozytorium) iremote.yourremote.fetch
zmiennej config (w docelowych repozytoriach).git replace --grafts
. Nie jestem pewien, kiedy to zostało dodane, ale jego jedynym celem jest utworzenie zamiennika, który jest taki sam jak zatwierdzenie B, ale z określonymi rodzicami.Zauważ, że zmiana zatwierdzenia w Git wymaga zmiany wszystkich zatwierdzeń następujących po nim. Odradza to, jeśli opublikowałeś tę część historii, a ktoś mógł oprzeć swoją pracę na historii sprzed zmiany.
Alternatywnym rozwiązaniem
git rebase
wymienionym w odpowiedzi Amber jest użycie mechanizmu przeszczepów (patrz definicja przeszczepów Git w Słowniku Git i dokumentacja.git/info/grafts
pliku w dokumentacji układu repozytorium Git ) w celu zmiany elementu nadrzędnego zatwierdzenia, sprawdź, czy działało poprawnie w niektórych przeglądarkach historii (gitk
,git log --graph
, itp.), a następnie użyjgit filter-branch
(zgodnie z opisem w sekcji „Przykłady” strony podręcznika), aby uczynić go trwałym (a następnie usuń przeszczep i opcjonalnie usuń oryginalnegit filter-branch
referencje, których kopie zapasowe utworzono w kopii zapasowej , lub ponownie skonfiguruj repozytorium):UWAGA !!! Rozwiązanie to różni się od rozwiązania rebase że
git rebase
byłoby rebase / przeszczepie zmiany , natomiast rozwiązanie oparte na przeszczepy po prostu reparent commity jak jest , nie biorąc pod uwagę różnice między starym i nowym rodzica rodzica!źródło
git replace
zastąpił przeszczepy git (zakładając, że masz git 1.6.5 lub nowszy).git-replace
+git-filter-branch
? W moim teściefilter-branch
zdawałem się szanować zastępstwo, wypuszczając całe drzewo.git filter-branch
przepisuje historię, szanuje przeszczepy i zamienniki (ale w przerobionej historii zastąpienia będą dyskusyjne); push spowoduje zmianę non-faf-forward.git replace
tworzy w miejscu zbywalne zamienniki; push będzie przewijać do przodu, ale będziesz musiał pchnąć,refs/replace
aby przenieść zamienniki, aby poprawić historię. HTHAby wyjaśnić powyższe odpowiedzi i bezwstydnie podłączyć własny skrypt:
Zależy to od tego, czy chcesz „rebase” czy „reparent”. Rebase , jak sugeruje Amber , porusza dyferencjału . Reparent , jak sugeruje Jakuba i Chris , porusza migawek całego drzewa. Jeśli chcesz się zregenerować, sugeruję używanie
git reparent
zamiast wykonywania pracy ręcznie.Porównanie
Załóżmy, że masz zdjęcie po lewej stronie i chcesz, aby wyglądało jak zdjęcie po prawej:
Zarówno ponowne bazowanie, jak i ponowne rodzenie utworzy ten sam obraz, ale definicja
C'
różni się. Dziękigit rebase --onto A B
,C'
nie będzie zawierać żadnych zmian wprowadzonychB
. Dziękigit reparent -p A
,C'
będzie identycznyC
(poza tym, żeB
nie będzie w historii).źródło
reparent
skryptem; działa pięknie; wysoce zalecane . Poleciłbym także utworzenie nowejbranch
etykiety i docheckout
tej gałęzi przed wywołaniem (ponieważ etykieta gałęzi się przeniesie).Zdecydowanie odpowiedź @ Jakuba pomogła mi kilka godzin temu, kiedy próbowałem dokładnie tego samego co OP.
Jest to jednak
git replace --graft
teraz łatwiejsze rozwiązanie dotyczące przeszczepów. Poważnym problemem związanym z tym rozwiązaniem było to, że gałąź filtrów spowodowała, że straciłem każdą gałąź, która nie została scalona z gałęzią HEAD. Następniegit filter-repo
wykonał pracę idealnie i bezbłędnie.Fore więcej informacji: zapoznaj się z sekcją „Przeszczepianie historii” w dokumentacji
źródło