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 (
--keep
jest 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 HEAD
odnosi się do 2 operacje temu, czyli zanim 1. wyrejestrowany newbranch
i 2. używane git reset
aby 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 rebase
odrzucać 3 zatwierdzenia po pierwszym przykładzie? To dlatego, że git rebase
bez argumentów włącza --fork-point
domyś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 rebase
moż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 --hard
przepisuje historię pobierania w górę, aby usunąć zatwierdzenia, a więc następnym razem, gdy git rebase
go 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-point
w dokumentacji git rebase i git merge-base .