Większość poprzednich odpowiedzi jest niebezpiecznie błędna!
Nie rób tego:
git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch
Gdy następnym razem uruchomisz git rebase(lub git pull --rebase) te 3 zatwierdzenia zostaną po cichu odrzucone newbranch! (patrz wyjaśnienie poniżej)
Zamiast tego zrób to:
git reset --keep HEAD~3
git checkout -t -b newbranch
git cherry-pick ..HEAD@{2}
- Najpierw odrzuca 3 ostatnie zatwierdzenia (
--keepjest jak --hard, ale bezpieczniejsze, bo zawiedzie, zamiast odrzucać niezaangażowane zmiany).
- Potem rozwidla się
newbranch.
- Następnie wiśnia wybiera te 3 zmiany z powrotem na
newbranch. Skoro jesteśmy już nie odwołuje się oddział, to robi to za pomocą git za reflog : HEAD@{2}jest popełnić, że HEADodnosi się do 2 operacje temu, czyli zanim 1. wyrejestrowany newbranchi 2. używane git resetaby odrzucić 3 zobowiązuje.
Ostrzeżenie: reflog jest domyślnie włączony, ale jeśli go ręcznie wyłączyłeś (np. Używając „gołego” repozytorium git), nie będziesz mógł odzyskać 3 zatwierdzeń po uruchomieniu git reset --keep HEAD~3.
Alternatywą, która nie polega na logowaniu jest:
# newbranch will omit the 3 most recent commits.
git checkout -b newbranch HEAD~3
git branch --set-upstream-to=oldbranch
# Cherry-picks the extra commits from oldbranch.
git cherry-pick ..oldbranch
# Discards the 3 most recent commits from oldbranch.
git branch --force oldbranch oldbranch~3
(jeśli wolisz, możesz pisać @{-1}- gałąź wcześniej wypisana - zamiast oldbranch).
Wyjaśnienie techniczne
Po co git rebaseodrzucać 3 zatwierdzenia po pierwszym przykładzie? To dlatego, że git rebasebez argumentów włącza --fork-pointdomyślnie opcję, która używa lokalnego reflogu, aby spróbować być odpornym na wymuszanie wypychania gałęzi upstream.
Załóżmy, że rozgałęziłeś początek / wzorzec, gdy zawierał zatwierdzenia M1, M2, M3, a następnie sam dokonałeś trzech zatwierdzeń:
M1--M2--M3 <-- origin/master
\
T1--T2--T3 <-- topic
ale potem ktoś przepisuje historię, zmuszając źródło / master do usunięcia M2:
M1--M3' <-- origin/master
\
M2--M3--T1--T2--T3 <-- topic
Korzystając z lokalnego dziennika, git rebasemożesz zobaczyć, że rozwidliłeś się z wcześniejszej inkarnacji gałęzi origin / master, a zatem, że zatwierdzenia M2 i M3 tak naprawdę nie są częścią twojej gałęzi tematów. Stąd rozsądnie zakłada się, że skoro M2 zostało usunięte z gałęzi upstream, nie będzie już potrzebne w gałęzi tematu, gdy gałąź tematu zostanie ponownie wydana:
M1--M3' <-- origin/master
\
T1'--T2'--T3' <-- topic (rebased)
Takie zachowanie ma sens i ogólnie jest właściwą rzeczą do zrobienia podczas bazowania.
Dlatego przyczyną niepowodzenia następujących poleceń:
git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch
to dlatego, że pozostawiają rejestr w złym stanie. Git widzi, newbranchże rozwidlił gałąź odgałęzienia w wersji, która zawiera 3 zatwierdzenia, a następnie reset --hardprzepisuje historię pobierania w górę, aby usunąć zatwierdzenia, a więc następnym razem, gdy git rebasego uruchomisz , odrzuca je jak każde inne zatwierdzenie, które zostało usunięte z pobierania.
Ale w tym konkretnym przypadku chcemy, aby te 3 zatwierdzenia były traktowane jako część gałęzi tematu. Aby to osiągnąć, musimy oddzielić się od poprzedniej wersji, która nie obejmuje 3 zatwierdzeń. To właśnie robią moje sugerowane rozwiązania, dlatego oba pozostawiają rejestr w prawidłowym stanie.
Aby uzyskać więcej informacji, zobacz definicję --fork-pointw dokumentacji git rebase i git merge-base .