Przenieś najnowsze commity do nowej gałęzi za pomocą Git
Chciałbym przenieść kilka ostatnich commitów, które wprowadziłem do mastera do nowej gałęzi i przenieść master z powrotem do niej zanim te commity zostały wprowadzone. Niestety, mój Git-fu nie jest jeszcze wystarczająco silny, jakaś pomoc?
Tzn. jak Mogę przejść z tego
master A - B - C - D - E
Do tego?
newbranch C - D - E
/
master A - B
11 answers
Przeniesienie do nowego oddziału
WARNING: ta metoda działa, ponieważ tworzysz nową gałąź za pomocą pierwszego polecenia: git branch newbranch
. Jeśli chcesz przenieść commity do istniejącej gałęzi, musisz scalić zmiany do istniejącej gałęzi przed wykonaniem git reset --hard HEAD~3
(Zobacz przeniesienie do istniejącego oddziału poniżej). jeśli najpierw nie połączysz swoich zmian, zostaną one utracone.
O ile nie ma innych okoliczności, może to łatwo zrobić, rozgałęziając się i cofając.
# Note: Any changes not committed will be lost.
git branch newbranch # Create a new branch, saving the desired commits
git reset --hard HEAD~3 # Move master back by 3 commits (GONE from master)
git checkout newbranch # Go to the new branch that still has the desired commits
Ale upewnij się, ile commitów ma wrócić. Alternatywnie, możesz zamiast HEAD~3
, po prostu podać hash commita (lub referencji jak origin / master ), do którego chcesz "powrócić" w gałęzi master (/current), np:
git reset --hard a1b2c3d4
*1 będziesz tylko "tracić" commity z gałęzi master, ale nie martw się, będziesz miał te commity w New Branch!
WARNING: z Gitem w wersji 2.0 i nowszej, jeśli później git rebase
nowa gałąź po oryginalnej (master
) gałęzi, możesz potrzebować jawnej opcji --no-fork-point
podczas rebase, aby uniknąć utraty przeniesionych zmian. Posiadanie branch.autosetuprebase always
zestawu sprawia, że jest to bardziej prawdopodobne. Zobacz odpowiedź Johna Mellora Po szczegóły.
Przeniesienie do istniejącego oddziału
Jeśli chcesz przenieść swoje commity do istniejącej gałęzi , będzie to wyglądało tak:
git checkout existingbranch
git merge master
git checkout master
git reset --hard HEAD~3 # Go back 3 commits. You *will* lose uncommitted work.
git checkout existingbranch
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
2018-07-23 08:07:38
Dla tych, którzy zastanawiają się, dlaczego to działa (jak byłem na początku):
Chcesz wrócić do C i przenieść D i E do nowej gałęzi. Oto jak to wygląda na początku:A-B-C-D-E (HEAD)
↑
master
Po git branch newBranch
:
newBranch
↓
A-B-C-D-E (HEAD)
↑
master
Po git reset --hard HEAD~2
:
newBranch
↓
A-B-C-D-E (HEAD)
↑
master
Ponieważ gałąź jest tylko wskaźnikiem, master wskazuje na ostatni commit. Kiedy tworzysz newBranch , po prostu tworzysz nowy wskaźnik do ostatniego commita. Następnie za pomocą git reset
przesunąłeś wskaźnik master do tyłu o dwa commity. Ale ponieważ nie przeniosłeś newBranch , nadal wskazuje na commit, który pierwotnie wykonał.
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
2015-12-31 00:29:20
Ogólnie...
Metoda wystawiona przez sykorę jest najlepszą opcją w tym przypadku. Ale czasami nie jest to najłatwiejsze i nie jest to ogólna metoda. Dla metody ogólnej użyj git cherry-pick :
Aby osiągnąć to, czego chce OP, jest to proces dwuetapowy:
Krok 1-Uwaga, który commit z master chcesz na newbranch
Wykonaj
git checkout master
git log
Zwróć uwagę na skróty (powiedzmy 3) commitów, które chcesz wprowadzić newbranch
. Tutaj użyję:
C commit: 9aa1233
D commit: 453ac3d
E commit: 612ecb3
Uwaga: możesz użyć pierwszych siedmiu znaków lub cały hash commit
Krok 2-umieść je na newbranch
git checkout newbranch
git cherry-pick 612ecb3
git cherry-pick 453ac3d
git cherry-pick 9aa1233
Lub (w Git 1.7.2+, użyj zakresów)
git checkout newbranch
git cherry-pick 612ecb3~1..9aa1233
git cherry-pick stosuje te trzy commity do newbranch.
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
2018-02-28 18:13:52
Jeszcze jeden sposób, aby to zrobić, używając tylko 2 komend. Utrzymuje również obecne działające drzewo w stanie nienaruszonym.
git checkout -b newbranch # switch to a new branch
git branch -f master HEAD~3 # make master point to some older commit
Stara wersja - zanim dowiedziałem się o git branch -f
git checkout -b newbranch # switch to a new branch
git push . +HEAD~3:master # make master point to some older commit
Bycie zdolnym do bycia zdolnym do bycia zdolnym do bycia zdolnym do bycia zdolnym do bycia zdolnym do bycia zdolnym do bycia zdolnym do bycia zdolnym do bycia zdolnym do bycia zdolnym do bycia zdolnym do bycia zdolnym.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
2018-06-21 14:17:32
Większość poprzednich odpowiedzi jest niebezpiecznie błędna!
Nie rób tego:
git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch
Przy następnym uruchomieniu git rebase
(lub git pull --rebase
) Te 3 commity zostaną po cichu odrzucone z 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 najnowsze commity (
--keep
jest jak--hard
, ale bezpieczniejsze, ponieważ nie powiedzie się, a nie wyrzuci niezarejestrowane zmiany). - potem rozwidla się
newbranch
.
/ Align = "left" / te 3 commity wracają do
newbranch
. Ponieważ nie są już referowane przez gałąź, robi to używając reflog Gita: HEAD@{2}
to commit, który HEAD
odnosił się do 2 operacji temu, tzn. przed 1. sprawdzone newbranch
i 2. używane git reset
do odrzucenia 3 commitów.
Ostrzeżenie: reflog jest domyślnie włączony, ale jeśli ręcznie go wyłączyłeś (np. używając "gołego" repozytorium git), nie będziesz w stanie odzyskać 3 commitów po uruchomieniu git reset --keep HEAD~3
.
Alternatywą, która nie opiera się na reflogu 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 napisać @{-1}
- wcześniej sprawdzoną gałąź-zamiast oldbranch
).
Wyjaśnienie techniczne
Dlaczego git rebase
miałby odrzucić 3 commity po pierwszym przykładzie? To dlatego, że git rebase
bez argumentów domyślnie włącza opcję --fork-point
, która używa lokalnego reflog, aby spróbować być solidna w stosunku do gałęzi upstream siłą.
Załóżmy, że rozgałęziłeś origin / master, gdy zawierało commity M1, M2, M3, a następnie wykonałeś trzy commity:
M1--M2--M3 <-- origin/master
\
T1--T2--T3 <-- topic
Ale potem ktoś przepisuje historię przez wymuszanie origin / master aby usunąć M2:
M1--M3' <-- origin/master
\
M2--M3--T1--T2--T3 <-- topic
Używając lokalnego refloga, git rebase
możesz zobaczyć, że oddzieliłeś się od wcześniejszej inkarnacji gałęzi origin/master, a więc commity M2 i M3 nie są tak naprawdę częścią twojej gałęzi tematycznej. Stąd rozsądnie zakłada się, że skoro m2 było po usunięciu z gałęzi upstream, nie chcesz już jej w swojej gałęzi tematycznej ani po rebasowaniu gałęzi tematycznej:
M1--M3' <-- origin/master
\
T1'--T2'--T3' <-- topic (rebased)
To zachowanie ma sens i jest generalnie słuszne podczas rebasingu.
Więc powód, dla którego następujące polecenia nie działają:
git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch
To dlatego, że zostawiają reflog w złym stanie. Git widzi newbranch
jako rozwidlenie gałęzi upstream przy rewizji, która zawiera 3 commity, a następnie reset --hard
przepisuje na nowo gałąź upstream Historia, aby usunąć commity, więc następnym razem, gdy uruchomisz git rebase
, odrzuca je tak, jak każdy inny commit, który został usunięty z programu upstream.
Ale w tym konkretnym przypadku chcemy, aby te 3 commity były traktowane jako część gałęzi tematycznej. Aby to osiągnąć, musimy oddzielić się od wcześniejszych wersji, która nie zawiera 3 commitów. To właśnie robią moje proponowane rozwiązania, stąd oba pozostawiają reflog we właściwym stanie.
Aby uzyskać więcej informacji, zobacz definicja --fork-point
w Git rebasei Git merge-base docs.
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
2016-09-16 01:34:57
To nie" porusza " ich w sensie technicznym, ale ma ten sam efekt:
A--B--C (branch-foo)
\ ^-- I wanted them here!
\
D--E--F--G (branch-bar)
^--^--^-- Opps wrong branch!
While on branch-bar:
$ git reset --hard D # remember the SHAs for E, F, G (or E and G for a range)
A--B--C (branch-foo)
\
\
D-(E--F--G) detached
^-- (branch-bar)
Switch to branch-foo
$ git cherry-pick E..G
A--B--C--E'--F'--G' (branch-foo)
\ E--F--G detached (This can be ignored)
\ /
D--H--I (branch-bar)
Now you won't need to worry about the detached branch because it is basically
like they are in the trash can waiting for the day it gets garbage collected.
Eventually some time in the far future it will look like:
A--B--C--E'--F'--G'--L--M--N--... (branch-foo)
\
\
D--H--I--J--K--.... (branch-bar)
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
2014-08-17 10:23:48
Aby zrobić to bez przepisywania historii (tzn. jeśli już wypchnąłeś commity):
git checkout master
git revert <commitID(s)>
git checkout -b new-branch
git cherry-pick <commitID(s)>
Obie gałęzie mogą zostać popchnięte bez użycia siły!
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
2016-01-21 16:10:36
Znacznie prostsze rozwiązanie przy użyciu git stash
Jeśli:
- Twoim głównym celem jest powrót
master
i - chcesz zachować zmiany, ale nie dbasz szczególnie o poszczególne commity i
- jeszcze nie nacisnąłeś i
- chcesz, aby to było łatwe i nie skomplikowane z gałęziami temp i innymi bólami głowy
Następnie poniższe jest o wiele prostsze (począwszy od gałęzi master
, która ma trzy błędne "commit"): {]}
git reset HEAD~3
git stash
git checkout newbranch
git stash pop
Co to robi, po numerze linii
- wycofuje ostatnie trzy commity (i ich Wiadomości) do
master
, ale pozostawia wszystkie pliki robocze nienaruszone - usuwa wszystkie zmiany pliku roboczego, czyniąc drzewo robocze
master
równym stanowi HEAD~3 - przełącza się na istniejącą gałąź
newbranch
- stosuje Ukryte zmiany do katalogu roboczego i czyści skrytkę
Możesz teraz używać git add
i git commit
jak zwykle would. Wszystkie nowe commity zostaną dodane do newbranch
.
Czego to nie robi
- Nie zostawia przypadkowych tymczasowych gałęzi zaśmiecających twoje drzewo]}
- Nie zachowuje błędnych commitów i komunikatów commitów, {[49] } więc musisz dodać nowy commit do tego nowego commita
Bramki
OP stwierdził, że celem jest "przywrócenie master' a do czasów, zanim te commity zostały wprowadzone " bez utraty zmian, a to rozwiązanie robi to.
Robię to co najmniej raz w tygodniu, gdy przypadkowo robię nowe commity do master
zamiast develop
. Zwykle mam tylko jeden commit do rollback, w którym to przypadku użycie git reset HEAD^
w linii 1 jest prostszym sposobem na wycofanie tylko jednego commita.
Nie rób tego, jeśli pchnąłeś zmiany mistrza pod prąd
Ktoś inny mógł usunąć te zmiany. Jeśli przepisujesz tylko lokalnego mistrza, nie ma to wpływu, gdy jest on popychany pod prąd, ale popychanie przepisanej historii do kolaborantów może powodować bóle głowy.
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
2018-09-26 23:36:43
Miałem taką sytuację:
Branch one: A B C D E F J L M
\ (Merge)
Branch two: G I K N
Wykonałem:
git branch newbranch
git reset --hard HEAD~8
git checkout newbranch
Spodziewałem się, że commit I będzie głową, ale commit L jest teraz...
Aby mieć pewność, że wylądujesz we właściwym miejscu w historii, łatwiej będzie pracować z Hashem commita
git branch newbranch
git reset --hard #########
git checkout newbranch
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
2013-09-20 10:17:35
1) Utwórz nową gałąź, która przeniesie wszystkie zmiany do new_branch.
git checkout -b new_branch
2) potem wróć do starej gałęzi.
git checkout master
3) Do git rebase
git rebase -i <short-hash-of-B-commit>
4) następnie otwarty edytor zawiera Ostatnie 3 Informacje o zatwierdzeniu.
...
pick <C's hash> C
pick <D's hash> D
pick <E's hash> E
...
5) Zmień pick
na drop
we wszystkich tych 3 commitach. Następnie zapisz i zamknij Edytor.
...
drop <C's hash> C
drop <D's hash> D
drop <E's hash> E
...
6) teraz Ostatnie 3 commity są usuwane z bieżącej gałęzi (master
). Teraz mocno wciśnij gałąź ze znakiem +
przed gałąź nazwisko.
git push origin +master
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
2018-06-01 05:40:11
Możesz to zrobić to tylko 3 proste kroki, które użyłem.
1) Utwórz nową gałąź, w której chcesz zatwierdzić najnowszą aktualizację.
git branch <branch name>
2) Znajdź najnowszy identyfikator zatwierdzenia dla nowej gałęzi.
git log
3) Skopiuj ID zmian zauważ, że ostatnia lista zmian znajduje się na górze. więc możesz znaleźć swoje zobowiązanie. można to również znaleźć za pośrednictwem wiadomości.
git cherry-pick d34bcef232f6c...
Możesz również podać kilka rang commit dowód osobisty.
git cherry-pick d34bcef...86d2aec
Teraz możesz wcisnąć swój kod
git push
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
2018-05-25 14:14:27