Rebasing a zatwierdzenie scalające Git

183

Weźmy następujący przypadek:

Mam trochę pracy w gałęzi tematycznej i teraz jestem gotowy do powrotu do mastera:

* eb3b733 3     [master] [origin/master]
| * b62cae6 2   [topic]
|/  
* 38abeae 1

Wykonuję scalanie z mastera, rozwiązuję konflikty i mam:

*   8101fe3 Merge branch 'topic'  [master]
|\  
| * b62cae6 2                     [topic]
* | eb3b733 3                     [origin/master]
|/  
* 38abeae 1

Teraz scalanie zajęło mi trochę czasu, więc ponownie pobieram i zauważam, że zdalna gałąź główna ma nowe zmiany:

*   8101fe3 Merge branch 'topic'  [master]
|\  
| * b62cae6 2                     [topic]
| | * e7affba 4                   [origin/master]
| |/  
|/|   
* | eb3b733 3
|/  
* 38abeae 1

Jeśli spróbuję `` git rebase origin / master '' z poziomu głównego, jestem zmuszony ponownie rozwiązać wszystkie konflikty, a także stracę zatwierdzenie scalania:

* d4de423 2       [master]
* e7affba 4       [origin/master]
* eb3b733 3
| * b62cae6 2     [topic]
|/  
* 38abeae 1

Czy istnieje czysty sposób na zmianę bazy zatwierdzenia scalenia, aby otrzymać historię taką jak ta, którą pokazuję poniżej?

*   51984c7 Merge branch 'topic'  [master]
|\  
| * b62cae6 2                     [topic]
* | e7affba 4                     [origin/master]
* | eb3b733 3
|/  
* 38abeae 1
jipumarino
źródło
74
TL; DR:git rebase --preserve-merges origin/master
Ilia K.
6
Jeśli chodzi o konieczność ponownego rozwiązywania konfliktów, warto rzucić okiem na git rerere .
Parker Coates,
git config --global pull.rebase preserve aby zawsze zachować fuzję, zobowiązuje się podczas rebase
galath
4
Ostrzeżenie: począwszy od Git 2.18 (Q2 2018, 5 lat później), git --rebase-mergesostatecznie zastąpi stary git --preserve-merges. Zobacz, co dokładnie robi Git „ rebase --preserve-merges” (i dlaczego?)
VonC
1
--preserve-mergesjest przestarzałe. Użyjgit rebase --rebase-merges origin/master
Arjun Sreedharan,

Odpowiedzi:

127

Istnieją dwie opcje.

Jednym z nich jest wykonanie interaktywnej rebase i edytowanie zatwierdzenia scalania, ponowne ręczne scalenie i kontynuowanie rebase.

Innym jest użycie --rebase-mergesopcji on git rebase, która jest opisana w następujący sposób z podręcznika: „Domyślnie rebase po prostu usuwa polecenia scalające z listy zadań do wykonania i umieszcza zatwierdzone zmiany w pojedynczej, liniowej gałęzi. Z --rebase- scalenia, rebase spróbuje zamiast tego zachować strukturę rozgałęzień w zatwierdzeniach, które mają zostać zmienione, poprzez odtworzenie zatwierdzeń scalających. Wszelkie rozwiązane konflikty scalania lub ręczne zmiany w tych zatwierdzeniach scalających będą musiały zostać rozwiązane / ponownie zastosowane ręcznie. "

siride
źródło
16
Wypróbowałem opcję -p i rzeczywiście pozostawia historię zmian tak, jak chciałem, ale zmusza mnie do ponownego rozwiązania konfliktów, nawet w plikach, które nie były edytowane w origin / master. Zgodnie z twoją pierwszą sugestią, jaka byłaby dokładna sekwencja poleceń?
jipumarino
2
@jipumarino: git rebase -i (powiedz mu, aby edytować zatwierdzenie merge), kiedy dojdzie do zatwierdzenia merge, git reset --hard HEAD ^, git merge, napraw konflikty, git commit, git rebase --continue. Możesz również spojrzeć na git rerere, który ma pomóc w tego typu rzeczach (ale nigdy nie korzystałem, więc nie mogę zaoferować żadnej porady ani pomocy).
pan
2
Dzięki. Włączyłem rerere i próbowałem z rebase -p i działa tak, jak powinno.
jipumarino
3
Oto doskonały wpis na blogu opisujący dokładnie tę sytuację: Rebasing Merge Commits in Git
kynan Kwietnia
1
rere nie jest rozwiązaniem, ponieważ za pierwszym razem nadal musisz ręcznie rozstrzygać połączenia.
Flimm,
29

Ok, to stare pytanie i już zaakceptowałem odpowiedź @siride, ale ta odpowiedź w moim przypadku nie była wystarczająca, ponieważ --preserve-mergeszmusza cię do rozwiązania wszystkich konfliktów po raz drugi. Moje rozwiązanie oparte na pomyśle, @Tobi Bale z dokładnymi poleceniami krok po kroku

Więc zaczniemy od takiego stanu na podstawie przykładu w pytaniu:

*   8101fe3 Merge branch 'topic'  [HEAD -> master]
|\  
| * b62cae6 2                     [topic]
| |
| | * f5a7ca8 5                   [origin/master]
| | * e7affba 4
| |/  
|/|   
* | eb3b733 3
|/  
* 38abeae 1

Zauważ, że mamy 2 commits ahead master, więc wybór cherry nie zadziała.

  1. Przede wszystkim stwórzmy poprawną historię, której chcemy:

    git checkout -b correct-history # create new branch to save master for future
    git rebase --strategy=ours --preserve-merges origin/master
    

    Używamy, --preserve-mergesaby zapisać nasze zobowiązanie do łączenia w historii. Używamy --strategy=oursignorować wszelkie konflikty scalania, bo nie dbam o to, co będzie w treść tej seryjnej commit, musimy tylko teraz miłą historię.

    Historia będzie wyglądać tak (pomijając mistrza):

    *   51984c7 Merge branch 'topic'  [HEAD -> correct-history]
    |\  
    | * b62cae6 2                     [topic]
    * | f5a7ca8 5                     [origin/master]
    * | e7affba 4
    * | eb3b733 3
    |/  
    * 38abeae 1
    
  2. Uzyskajmy teraz poprawny indeks.

    git checkout master # return to our master branch
    git merge origin/master # merge origin/master on top of our master
    

    W tym miejscu mogą pojawić się dodatkowe konflikty przy scalaniu, ale byłyby to tylko konflikty z plików zmienionych między 8101fe3a f5a7ca8, ale nie obejmują już rozwiązanych konfliktów ztopic

    Historia będzie wyglądać następująco (pomijając poprawną historię):

    *   94f1484 Merge branch 'origin/master'  [HEAD -> master]
    |\  
    * | f5a7ca8 5                   [origin/master]
    * | e7affba 4
    | *   8101fe3 Merge branch 'topic'
    | |\  
    | | * b62cae6 2                     [topic]
    |/ /
    * / eb3b733 3
    |/  
    * 38abeae 1
    
  3. Ostatnim etapem jest połączenie naszej gałęzi z poprawną historią i gałęzią z odpowiednim indeksem

    git reset --soft correct-history
    git commit --amend
    

    Używamy reset --softdo resetowania naszej gałęzi (i historii) do poprawnej historii, ale pozostawiamy indeks i drzewo robocze bez zmian. Następnie używamy commit --amenddo przepisania naszego zatwierdzenia merge, które miało nieprawidłowy indeks, z naszym dobrym indeksem z master.

    W końcu będziemy mieli taki stan (zwróć uwagę na inny identyfikator top commit):

    *   13e6d03 Merge branch 'topic'  [HEAD -> master]
    |\  
    | * b62cae6 2                     [topic]
    * | f5a7ca8 5                     [origin/master]
    * | e7affba 4
    * | eb3b733 3
    |/  
    * 38abeae 1
    
Ivan Naydonov
źródło
To jest niesamowite i bardzo pomogło! Ale nie udało mi się załatwić sprawy z commit --amend: czy możesz dodać więcej informacji na ten temat? Co dokładnie dzieje się po uruchomieniu tego - zauważyłem, że zmieniono SHA w commitach - ale dlaczego? A co się stanie, jeśli tego nie uruchomisz?
ZenJ
1
@ZenJ git commit --amenddodaje zmiany do ostatniego zatwierdzenia (HEAD, w tym przypadku zatwierdzenia scalającego). Ponieważ zmienia się zawartość zatwierdzenia, skrót jest aktualizowany.
Dries Staelens
1
Dla osób, które nie mają włączonej opcji „rerere” przed naprawieniem konfliktów, to rozwiązanie jest świetne, ponieważ pozwala uniknąć konieczności ponownego naprawiania konfliktów. Dzięki!
Shackleford
6

Biorąc pod uwagę, że właśnie straciłem dzień, próbując to rozgryźć i faktycznie znalazłem rozwiązanie z pomocą współpracownika, pomyślałem, że powinienem się przyłączyć.

Mamy dużą bazę kodu i mamy do czynienia z 2 gałęziami, które są jednocześnie mocno modyfikowane. Istnieje gałąź główna i gałąź drugorzędna, jeśli tak jest.

Podczas scalania gałęzi dodatkowej z gałęzią główną, praca jest kontynuowana w gałęzi głównej i zanim skończę, nie mogę przesłać zmian, ponieważ są one niezgodne.

Dlatego muszę „zmienić bazę” mojego „scalenia”.

Tak to w końcu zrobiliśmy:

1) zanotuj SHA. np .: c4a924d458ea0629c0d694f1b9e9576a3ecf506b

git log -1

2) Utwórz odpowiednią historię, ale to przerwie scalanie.

git rebase -s ours --preserve-merges origin/master

3) zanotuj SHA. np .: 29dd8101d78

git log -1

4) Teraz zresetuj do miejsca, w którym byłeś wcześniej

git reset c4a924d458ea0629c0d694f1b9e9576a3ecf506b --hard

5) Teraz połącz aktualny master z gałęzią roboczą

git merge origin/master
git mergetool
git commit -m"correct files

6) Teraz, gdy masz już właściwe pliki, ale złą historię, uzyskaj odpowiednią historię na szczycie swojej zmiany za pomocą:

git reset 29dd8101d78 --soft

7) A następnie - popraw wyniki w oryginalnym zatwierdzeniu scalania

git commit --amend

Voila!

Claude Peloquin
źródło
1

Wygląda na to, że chcesz usunąć swoje pierwsze scalenie. Możesz postępować zgodnie z następującą procedurą:

git checkout master      # Let's make sure we are on master branch
git reset --hard master~ # Let's get back to master before the merge
git pull                 # or git merge remote/master
git merge topic

To dałoby ci to, czego chcesz.

Antoine Pelisse
źródło
4
Przy włączonym rerere wydaje się, że daje to ten sam wynik, co rozwiązanie rebase -p podane powyżej przez siride.
jipumarino
0
  • Od momentu połączenia
  • Wybierz nową zmianę, która powinna być łatwa
  • skopiuj swoje rzeczy
  • powtórz scalenie i rozwiąż konflikty, po prostu kopiując pliki z lokalnej kopii;)
Tobi B
źródło
1
Ta odpowiedź wygląda dobrze, ale byłaby bardziej przydatna, gdybyś podał rzeczywiste polecenia git, na wypadek, gdyby użytkownik był nowy w git
Louise Davies