Git: Jak zmienić bazę do konkretnego zatwierdzenia?

154

Chciałbym zmienić bazę do konkretnego zatwierdzenia, a nie do HEAD z innej gałęzi:

A --- B --- C          master
 \
  \-- D                topic

do

A --- B --- C          master
       \
        \-- D          topic

zamiast

A --- B --- C          master
             \
              \-- D    topic

Jak mogę to osiągnąć?

Ondra Žižka
źródło
4
Czy próbowałeś to zrobić git checkout Bprzed biegiem git rebase?
Peter-Paul van Gemerden
Nie, czy to powinno pomóc? Myślę, że liczą się tylko odniesienia do rebasepolecenia.
Ondra Žižka

Odpowiedzi:

98

Możesz uniknąć używania parametru --onto, tworząc tymczasową gałąź na zatwierdzonym zatwierdzeniu, a następnie używając rebase w jego prostej postaci:

git branch temp master^
git checkout topic
git rebase temp
git branch -d temp
Adam Dymitruk
źródło
5
Bardziej podoba mi się to podejście w stylu RISC :) Spróbuję. Dzięki.
Ondra Žižka
10
Zastanawiam się, dlaczego u mnie to nie działa, w nieco innym scenariuszu . Chcę grupa pominąć pep8 i opierać się na mistrza . git rebase temp(kiedy na grupie ) poddaje się z komunikatem „Obecne grupy branżowe są aktualne”.
Alois Mahdal
4
To rozwiązanie nie zadziała w scenariuszu, w którym temat został już ponownie oparty na wzorcu, ale chcesz oprzeć go na przodku do opanowania. W takim przypadku musisz użyć git rebase --onto <target> <from> <to>, aby można było określić zatwierdzenie <from>.
mirzmaster
Jest to zdecydowanie najłatwiejsza opcja, jeśli chcesz bazować na tym samym zatwierdzeniu, na którym jest oparta gałąź.
Ash
1
Wydaje się, że działa, ale GitLab mówi co innego. Gdybym miał 10 zatwierdzeń do tyłu, 5 zatwierdzeń do przodu, spodziewałem się, że będę miał 8 zatwierdzeń do tyłu, a 5 do przodu po otrzymaniu 2 zatwierdzeń. Ale zamiast tego dodaje więcej zatwierdzeń do tych 5.
ROMANIA_engineer
67

Możesz nawet podejść bezpośrednio:

git checkout topic
git rebase <commitB>
r0hitsharma
źródło
6
Dla mnie to nie robi tego, co jest zamierzone. O ile wiem, próbuje oprzeć się na „ostatnim wspólnym przodku” topici commitB.
Dan Lenski
2
@DanLenski, to nie jest sposób, w jaki działa rebase. Cytując dokumenty , It works by going to the common ancestor of the two branches (the one you’re on and the one you’re rebasing onto), getting the diff introduced by each commit of the branch you’re on, saving those diffs to temporary files, resetting the current branch to the same commit as the branch you are rebasing onto, and finally applying each change in turn. spróbowałem ponownie teraz i wydawało się, że działa dobrze.
r0hitsharma
1
Super łatwe i działa! Mam teraz: commitB_from_master-> topicCommit1-> topicCommit2.
Martin Konicek
To mi się nie udało. Wcześniej GitLab powiedział „n zatwierdza do przodu”. A teraz, gdzie jest napisane „m zatwierdza do przodu” m > n.
ROMANIA_engineer
48

Użyj opcji „na”:

git rebase --onto master^ D^ D
Adam Dymitruk
źródło
2
Di D^czy będzie hashem ostatniego i przedostatniego zatwierdzenia „topic”?
Ondra Žižka
39
Składnia jest podobna do git rebase --onto <new-parent> <old-parent>. Zobacz Ustawianie wskaźnika git parent na innego rodzica . W twoim przypadku <new-parent> to B, a <old-parent> to A.
jsz
7
Zawsze używam 3 argumentów: desitnation, start and end of commits to rebase.
Adam Dymitruk
14
To zadziałało dla mnie:git rebase --onto <commit-ID> master
4
Komentarz @ jsz jest poprawny, w przeciwieństwie do komentarza Simona Southa jest odwrotnie: git rebase --onto master <commit-ID-of-old-parent>i dla OP git rebase --onto B A.
hałaśliwy
19

Komentarz jsz powyżej uratował mi mnóstwo bólu, więc oto oparty na nim przepis krok po kroku, którego używałem do zmiany bazy / przeniesienia dowolnego zatwierdzenia na inne zatwierdzenie:

  1. Znajdź poprzedni punkt rozgałęzienia gałęzi, która ma zostać zmieniona (przeniesiona) - nazwij go starym rodzicem. W powyższym przykładzie jest to A
  2. Znajdź zatwierdzenie, do którego chcesz przenieść gałąź - nazwij ją nowym rodzicem. Na przykładzie to B.
  3. Musisz być na swojej gałęzi (tej, którą się poruszasz):
  4. Zastosuj rebase: git rebase --onto <new parent> <old parent>

W powyższym przykładzie jest to tak proste, jak:

   git checkout topic
   git rebase --onto B A
Nestor Milyaev
źródło
6
To powinna być prawidłowa odpowiedź. Poza tym, że używam git rebase --onto B master, zobacz moją odpowiedź, aby uzyskać dokładniejsze wyjaśnienie.
Zack Morris
Nie działało dobrze dla mnie. Wybrałem 2 kolejne commity (ostatnie od mastera, który był w aktualnej gałęzi i pierwszy od mastera, który nie był w aktualnej gałęzi). Zacząłem od 100 za - 10 do przodu i zamiast 99 za - 10 do przodu, teraz mam 105 za - 13 do przodu.
ROMANIA_engineer
Przykro mi, że to nie zadziałało. Wygląda na to, że twoje gałęzie trochę się rozeszły - sugerowałbym zgniatanie najpierw przed próbą ponownego bazowania gałęzi z tak wieloma różnicami.
Nestor Milyaev
11

Rozwiązanie tematu

Prawidłowym poleceniem odpowiadającym na wysłane pytanie może być dowolne z poniższych (zakładając, że gałąź topicjest już wyewidencjonowana):

git rebase --onto B master
git rebase --onto master~1 master
git rebase --onto B A
git rebase --onto B C
git rebase --onto B

Jeśli topicnie jest wyrejestrowany, po prostu dodajesz topicdo polecenia (z wyjątkiem ostatniego) w następujący sposób:

git rebase --onto B master topic

Alternatywnie, najpierw sprawdź oddział z:

git checkout topic

Zmień wartość dowolnego ciągu zatwierdzeń na zatwierdzenie docelowe

Podstawowa forma polecenia, którego potrzebujemy, zaczerpnięta z dokumentacji, to:

git rebase --onto <Target> [<Upstream> [<Branch>]]

<Branch>jest opcjonalna i wszystko, co robi, to sprawdzenie określonej gałęzi przed wykonaniem reszty polecenia. Jeśli już wyewidencjonowałeś gałąź, którą chcesz przestawić, nie potrzebujesz tego. Zauważ, że musisz określić <Upstream>, aby określić, w <Branch>przeciwnym razie git pomyśli, że określasz <Upstream>.

<Target>jest zatwierdzeniem, do którego dołączymy nasz ciąg zatwierdzeń. Podając nazwę gałęzi, po prostu określasz zatwierdzenie głowy tej gałęzi. <Target>może być dowolnym zatwierdzeniem, które nie będzie zawarte w ciągu przenoszonych zatwierdzeń. Na przykład:

A --- B --- C --- D         master
      \
       \-- X --- Y --- Z    feature

Aby przenieść całą gałąź funkcji, nie można wybrać X, Y, Zlub featuregdy <Target>od tych, wszystkie są rewizje wewnątrz grupy są przenoszone.

<Upstream>jest wyjątkowy, ponieważ może oznaczać dwie różne rzeczy. Jeśli jest to zatwierdzenie, które jest przodkiem wyewidencjonowanej gałęzi, wówczas służy jako punkt odcięcia. W przykładzie I, pod warunkiem, byłoby to wszystko, co jest nie C, Dalbo master. Wszystkie zatwierdzenia po <Upstream>do głowy wyrejestrowanej gałęzi są tymi, które zostaną przeniesione.

Jednakże, jeśli <Upstream>nie jest przodkiem, to git tworzy kopię zapasową łańcucha od określonego zatwierdzenia, aż znajdzie wspólnego przodka z wyewidencjonowaną gałęzią (i przerywa, jeśli nie może go znaleźć). W naszym przypadku, <Upstream>z B, C, Dczy masterbędzie w ogóle wynik popełnić Bsłużąc jako punkt przecięcia. <Upstream>jest sam w sobie poleceniem opcjonalnym i jeśli nie jest określone, to git sprawdza rodzica wyewidencjonowanej gałęzi, co jest odpowiednikiem wprowadzenia master.

Teraz, gdy git wybrał zatwierdzenia, które będzie wycinał i przesuwał, stosuje je w kolejności <Target>, pomijając te, które zostały już zastosowane do celu.

Ciekawe przykłady i wyniki

Korzystając z tego punktu wyjścia:

A --- B --- C --- D --- E         master
            \
             \-- X --- Y --- Z    feature
  • git rebase --onto D A feature
    Zastosuje zobowiązuje B, C, X, Y, Zaby popełnić Di skończyć omijając Ba Cponieważ już zostały zastosowane.

  • git rebase --onto C X feature
    Zastosuje zatwierdzenia Yi Zzatwierdzenie C, skutecznie usuwając zatwierdzenieX

Isaac Brown
źródło
4

Prostszym rozwiązaniem jest git rebase <SHA1 of B> topic. Działa to niezależnie od tego, gdzie jesteś HEAD.

Możemy potwierdzić to zachowanie z dokumentu git rebase

<upstream>Odgałęzienie upstream do porównania. Może to być dowolne poprawne zatwierdzenie , a nie tylko nazwa istniejącej gałęzi. Domyślnie skonfigurowany nadrzędny dla bieżącej gałęzi.


Możesz się zastanawiać, co się stanie, jeśli wspomnę o SHA1 topicw powyższym poleceniu?

git rebase <SHA1 of B> <SHA1 of topic>

To również zadziała, ale rebase nie Topicwskaże wtedy nowej gałęzi tak utworzonej i HEADbędzie w stanie odłączonym. Z tego miejsca musisz ręcznie usunąć stare Topici utworzyć nowe odniesienie do gałęzi w nowej gałęzi utworzonej przez rebase.

Numer945
źródło
3

Użyłem mieszanki opisanych powyżej rozwiązań:

$ git branch temp <specific sha1>
$ git rebase --onto temp master topic
$ git branch -d temp

O wiele łatwiej było mi to przeczytać i zrozumieć. Przyjęte rozwiązanie doprowadziło mnie do konfliktu scalania (zbyt leniwego, by naprawić go ręcznie):

$ git rebase temp
First, rewinding head to replay your work on top of it...
Applying: <git comment>
Using index info to reconstruct a base tree...
M       pom.xml
.git/rebase-apply/patch:10: trailing whitespace.
    <some code>
.git/rebase-apply/patch:17: trailing whitespace.
        <some other code>
warning: 2 lines add whitespace errors.
Falling back to patching base and 3-way merge...
Auto-merging pom.xml
CONFLICT (content): Merge conflict in pom.xml
error: Failed to merge in the changes.
Patch failed at 0001 <git comment>
The copy of the patch that failed is found in: .git/rebase-apply/patch

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
malat
źródło
1
to samo tutaj, kilka plików miało konflikty, kiedy użyłem 2 najpopularniejszych odpowiedzi (tj. r0hitsharma i Dymitruk)
Oliver
3

Ponieważ zmiana bazy jest tak fundamentalna, oto rozwinięcie odpowiedzi Nestora Milyaeva . Łącząc JSZ użytkownika i Simon Południowej komentarze z odpowiedzią Adama Dymitruk za daje tej komendy, który pracuje na topicoddziale, niezależnie od tego, czy gałązki z masteroddziału popełnić Aalbo C:

git checkout topic
git rebase --onto <commit-B> <pre-rebase-A-or-post-rebase-C-or-base-branch-name>

Zauważ, że ostatni argument jest wymagany (w przeciwnym razie przewija twoją gałąź do zatwierdzenia B).

Przykłady:

# if topic branches from master commit A:
git checkout topic
git rebase --onto <commit-B> <commit-A>
# if topic branches from master commit C:
git checkout topic
git rebase --onto <commit-B> <commit-C>
# regardless of whether topic branches from master commit A or C:
git checkout topic
git rebase --onto <commit-B> master

Więc ostatnie polecenie jest tym, którego zwykle używam.

Zack Morris
źródło
-2

Jest inny sposób, aby to zrobić lub jeśli chcesz wrócić do więcej niż jednego zatwierdzenia.

Oto przykład, aby wrócić do nliczby zatwierdzeń:

git branch topic master~n

Ze względu na to pytanie można to również zrobić:

git branch topic master~1

Polecenie działa idealnie na git version 2.7.4. Nie testowałem tego na żadnej innej wersji.

Talha Ashraf
źródło
Czy błędnie zinterpretowałeś to jako pytanie o rozgałęzianie? To właściwie kwestia zmiany bazy.
NetherGranite