Jak mogę używać git rebase bez wymuszonego wypychania?

94

Próbując osiągnąć nirwanę git, spędzam dzień na uczeniu się, jak wykorzystać rebase w sytuacjach, w których obecnie się łączę.

Podczas przechodzenia przez coś, co uważam za przepływ git 101 (który przeliteruję poniżej), muszę push --forcecofać zmiany do źródła.

Nie jestem jedyny - wiem, że to teren zakryty (patrz 1 , 2 , 3 , 4 , 5 ) i rozumiem techniczne powody, dla których siła jest konieczna. Mój problem jest następujący - jest wiele (wiele) wpisów na blogu wychwalających rebase i tego, jak zmieniło to ich życie (zobacz 1 , 2 , 3 , 4, aby wymienić kilka), ale żaden z nich nie wspomina, że push --forcejest to część ich przepływ. Jednak prawie każda odpowiedź na istniejące pytania dotyczące przepełnienia stosu brzmi: „tak, jeśli zamierzasz zmienić bazę, musisz użyć push --force”.

Biorąc pod uwagę liczbę i religijność zwolenników rebase, muszę wierzyć, że stosowanie metody „push --force” nie jest nieodłączną częścią przepływu rebase i że jeśli ktoś często musi zmuszać ich do popychania, robi coś złego .

push --forceto zła rzecz .

Więc oto mój flow. W jaki sposób mógłbym osiągnąć takie same wyniki bez użycia siły?

Prosty przykład

Dwie gałęzie:

  • v1.0 - gałąź wydania, zawiera tylko poprawki
  • master - wszystko do następnego dużego wydania.

Mam kilka zatwierdzeń poprawek i kilka zmian na następne wydanie.

premerge

Chciałbym włączyć poprawki do mojego mastera, aby nie zostały utracone w następnym wydaniu. Przed oświeceniem po prostu:

git checkout master
git merge v1.0

Ale teraz próbuję

git checkout master
git rebase v1.0

Więc teraz jestem tutaj:

wprowadź opis obrazu tutaj

Czas na:

git push

Nie ma kości.

Roy Truelove
źródło

Odpowiedzi:

43

Rebasing jest świetnym narzędziem, ale działa najlepiej, gdy używasz go do szybkiego łączenia gałęzi tematów z wzorcem. Na przykład możesz zmienić bazę gałęzi add-new-widget względem master:

git checkout add-new-widget
git rebase -i master

przed wykonaniem szybkiego łączenia gałęzi w master. Na przykład:

git checkout master
git merge --ff-only add-new-widget

Zaletą tego jest to, że twoja historia nie będzie miała wielu skomplikowanych zatwierdzeń scalania lub konfliktów scalania, ponieważ wszystkie twoje zmiany zostaną przeniesione na czubek mastera przed scaleniem. Dodatkową korzyścią jest zmiana bazy, ale nie musisz jej używać, git push --forceponieważ nie przebijasz historii w gałęzi głównej.

Z pewnością nie jest to jedyny przypadek użycia rebase lub jedyny przepływ pracy, ale jest to jedno z bardziej rozsądnych zastosowań, jakie widziałem. YMMV.

Todd A. Jacobs
źródło
4
Dzięki CG, myślę, że kluczem jest „użyj go do szybkiego łączenia do przodu”. Nie dotyczy mojego przypadku powyżej, w którym mam dwie aktywne gałęzie - gałąź deweloperską i gałąź wydania, ale wydaje się, że ma zastosowanie bardzo dobrze do tymczasowych gałęzi tematycznych, które są potrzebne tylko przez ograniczony czas, a następnie można je usunięte po scaleniu. Dzięki jeszcze raz.
Roy Truelove
1
Rozumiem to, ale pierwotne pytanie pozostaje. Myślę, że właściwą odpowiedzią jest ta udzielona przez @Fabien Quatravaux
IsmailS
5
Cóż, nadal musiałbyś naciskać na siłę gałąź 1.0, nie? Przynajmniej tak mi się zawsze udaje. Metoda Fabiensa zapobiegłaby temu.
joerx
23

@CodeGnome ma rację. Nie należy ponownie bazować na gałęzi v1.0, ale na gałęzi v1.0 na master, to zrobi różnicę.

git checkout -b integrate_patches v1.0
git rebase master
git checkout master
git merge integrate_patches

Utwórz nową gałąź wskazującą na wersję 1.0, przenieś tę nową gałąź na górę nadrzędną, a następnie zintegruj nową wersję poprawek wersji 1.0 z gałęzią główną. Otrzymasz coś takiego:

o [master] [integrate_patches] Another patch on v1.0
o A patch on v1.0
o Another change for the next major release
o Working on the next major release
|  o [v1.0] Another path on v1.0
|  o A patch on v1.0
| /
o Time for the release

Ten sposób korzystania z rebase jest zalecany w oficjalnej dokumentacji git .

Myślę, że masz rację git push --force: powinieneś go używać tylko wtedy, gdy popełniłeś błąd i popchnąłeś coś, czego nie chciałeś.

Fabien Quatravaux
źródło
Myślę, że to najlepsza odpowiedź na konkretny problem w PO. Tworzysz tymczasową gałąź scalającą i ponownie bazujesz na niej, a następnie scalasz do mastera i wypychasz do początku. Tymczasowej gałęzi nie trzeba wypychać do początku. Jedyną dodatkową radą, jaką dałbym, jest posiadanie gałęzi deweloperskiej lub qa, w której można zakwalifikować kod scalony / zmieniony. Po kwalifikacji ten kod byłby następnie scalany tylko z ff do master. Pozwala to na łatwe poprawki, jeśli proces kwalifikacji trwa zbyt długo. Jest to w zasadzie proces „przepływu git”.
Javid Jamae
Dzięki Fabien, świetna odpowiedź. Dla tych z nas, którzy chcą tylko zintegrować zmiany masterz samą gałęzią funkcji i nie mają nic przeciwko scaleniu samej gałęzi funkcji, można to zrobić za pomocą:git checkout my-branch; git rebase master; git checkout master; git merge my-branch
Siavas
17

Musisz wymusić push, jeśli zmienisz bazę i już opublikowałeś swoje zmiany, prawda?

Używam rebase całą masę, ale albo publikuję na czymś prywatnym, gdzie wymuszenie nie ma znaczenia (np. Mój własny klon na GitHub, jako część żądania ściągnięcia), albo rebase przed pierwszym push.

To jest serce przepływu pracy, w którym używasz rebase, ale nie wymuszaj zbyt dużego nacisku: nie publikuj rzeczy, dopóki nie będą gotowe, nie zmieniaj bazy po naciśnięciu.

Daniel Pittman
źródło
Dzięki Dan. Czy mógłbyś mi powiedzieć, jak to osiągnąć? Czy to nie jest scenariusz, w którym ma zastosowanie rebase?
Roy Truelove
2
Jeśli wyodrębnisz całą swoją pracę do gałęzi tematycznych, ustawia to dobry scenariusz zmiany bazy. Jesteś rebasenowy pociągnął zmiany do swojego tematu oddział, ale kiedy już zakończone zmian tego oddziału, ci mergez tyłu oddziału do głównej gałęzi rozwojowej.
redhotvengeance
1
Problem polega na tym, że opublikowałeś gałąź - musisz więc wymusić wypychanie do repozytorium. Musisz zrezygnować z jednego z dwóch: publikowania w sposób, w jaki to robisz, lub zmiany bazy. Przepraszam.
Daniel Pittman
Wygląda na to, że zmiana bazy nie działa w tym scenariuszu. Wersja 1.0 nie jest gałęzią tematyczną, jest gałęzią wydania, więc nigdy nie umrze i musi zostać opublikowana.
Roy Truelove
5

Myślę, że istnieje dobry przypadek użycia tego wzorca rebase-następnie-wymuszenie, który nie jest wynikiem błędnego naciśnięcia: samodzielnej pracy nad gałęzią funkcji z wielu lokalizacji (komputerów). Robię to często, ponieważ czasami pracuję w biurze na moim komputerze stacjonarnym, a czasami z domu / klienta na laptopie. Muszę od czasu do czasu zmienić bazę, aby nadążyć za główną gałęzią i / lub aby scalenia były czystsze, ale muszę też naciskać na siłę, gdy zostawiam jedną maszynę, aby pracować na innej (gdzie po prostu ciągnę). Działa jak urok, o ile tylko ja pracuję w branży.

8 czterdzieści
źródło
2
Mam ten sam przepływ pracy (pracuję z wielu komputerów / lokalizacji). Zatem załóżmy, że pracujesz nad gałęzią tematyczną zwaną „mitopią”. Tak długo, jak zawsze przebudowujesz lokalną gałąź jednorazowego użytku (która jest tylko gałęzią mitopii) na „nadrzędną”, a następnie scalasz ją z powrotem w mitopię, wtedy nigdy nie będziesz musiał zmuszać do pchania. OP ma nieco inny scenariusz, więc w takim przypadku może być konieczne użycie siły. Uważam jednak, że OP zmienia bazę w niewłaściwy sposób - gdyby zrobił to tak, jak opisałem, nie byłoby konieczne użycie siły.
bwv549
3

Oto, czego używam (zakładając, że nazwa twojego oddziału to foobar ):

git checkout master              # switch to master
git rebase   foobar              # rebase with branch
git merge -s ours origin/master  # do a basic merge -- but this should be empty
git push origin master           # aaand this should work
wonny
źródło
2
Mistrz zbuntowania wygląda dziwnie.
Emil Vikström
2
gitnie jest pozbawiony osobowości
pachnący
1
git merge -s ours origin/<branch>to naprawiło to dla nas
Max
0

tl; dr łączenie ze współdzielonymi gałęziami, ponowne bazowanie na poszczególnych gałęziach. --force-with-leasejest bezpieczniejszą alternatywą dla siły i powinien pomóc ci osiągnąć wspomnianą git nirwanę bez destrukcyjnej natury siły.

Ogólna praktyczna zasada, którą widziałem, sprawdza się w przepływach pracy różnych zespołów, polega na używaniu jej mergedla współdzielonych gałęzi (tj., Master lub develop) i używaniu rebasepodczas pracy nad własną gałęzią funkcji. Oto typowy cykl życia gałęzi funkcji

git checkout master
git checkout -b new-feature
git commit -am "commit new work"
git push -u origin new-feature
# have code reviewed, run CI, etc.,
# meanwhile master has new commits
git checkout master
git pull
git rebase -i master # -i for interactive rebase allows you to squash intermediate commits
git push --force-with-lease
git merge master

Prosta angielska wersja tego, co tutaj zrobiliśmy:

  1. Utworzono nową gałąź z mastera
  2. Zakończono pracę na gałęzi i przekazano do zdalnego
  3. rebased od mistrza
  4. Przenieś pracę na zdalną za pomocą force-with-lease
  5. scal w master z bardzo czystym logiem git, zmniejszając bałagan związany z wielokrotnymi połączeniami z naszej współdzielonej gałęzi, aby nasza gałąź została „dogoniona” z najnowszą wersją główną (dzielona gałąź)

Czwarty krok jest NAPRAWDĘ ważny i jest jednym z głównych powodów, dla których zacząłem opowiadać się za używaniem rebase. force-with-leasesprawdza pilota, aby zobaczyć, czy zostały dodane nowe zatwierdzenia. Jeśli git pushokaże się, że jest destrukcyjny, nie będzie naciskał!

Mam nadzieję, że to da komuś większą pewność siebie podczas korzystania z rebase.

lucasnad27
źródło