Jak podejść do skomplikowanego scalenia

25

Oto umowa, dołączyłem do nowej firmy i poproszono mnie o zakończenie pracy w oddziale, który nie był dotykany przez prawie rok. W międzyczasie gałąź master rozwijała się w stałym tempie. Idealnie chciałbym scalić wszystkie zmiany z gałęzi master do gałęzi funkcji i kontynuować pracę od tego momentu, ale nie jestem zbyt pewien, jak do tego podejść.

Jak bezpiecznie wykonać to scalanie, zachowując ważne zmiany po obu stronach oddziału?

Vlad Spreys
źródło
Dziękujemy wszystkim za niesamowite opinie. Zamierzam dać git-imerge szansę, a jeśli okaże się, że to bałagan, zastosuję nowe podejście oddziałowe!
Vlad Spreys,
Czy ktoś może wyjaśnić, dlaczego nie możemy git cherry-picktutaj skorzystać ?
Santosh Kumar
1. Modlitwy. 2. rebase. 3. test. 4. połączyć.
AK_
1
Wolę bazowanie w tej sytuacji, ponieważ będzie ono zatwierdzać przez zatwierdzanie. Umożliwi to także zgniecenie nowej funkcji przed udostępnieniem scalonego kodu. YMMV.
Stephen

Odpowiedzi:

27

U podstaw tego, jak połączyć dwa (być może niekompatybilne) fragmenty kodu, jest problem programistyczny , a nie problem kontroli wersji . Polecenie scalania Git może pomóc w tym procesie, ale zależy to od kształtu problemu.

Najbardziej sensowne jest porównanie obu wersji z bazą. To da ci wyobrażenie najlepszej strategii dalszego rozwoju. Twoje podejście może być inne w zależności od charakteru i nakładania się zmian w każdej gałęzi.

Wyobraź sobie idealny scenariusz: odkryłbyś, że główna gałąź i gałąź funkcji modyfikowały tylko wykluczające się wzajemnie części kodu, więc możesz po prostu zatwierdzić wszystkie zmiany i być gotowym do pracy.

Oczywiście prawie na pewno tak nie będzie, ale pytanie brzmi: jak daleko od tego idealnego scenariusza będzie? tzn. jak przenikają się zmiany?

Jak dojrzała była stara gałąź funkcji? Czy był w dobrym stanie, czy nie (lub nieznany)? Ile funkcji zostało ukończonych?

Jeśli odpowiedni kod w głównej gałęzi bardzo się zmienił w ciągu ostatniego roku lub funkcja nie jest w bardzo dojrzałym stanie, mogę rozważyć utworzenie nowego widelca najnowszego i ręczne włączenie starej funkcji. Pozwoli ci to na stopniowe podejście do jego działania.

Jeśli zrobisz niechlujne połączenie dużej ilości kodu, ale to nie zadziała, będzie to bardzo trudne do debugowania. Jeśli główny oddział bardzo się zmienił w ciągu ostatniego roku, konieczne mogą być poważne zmiany w projekcie tej funkcji, aby działała. Nie byłoby właściwe wprowadzanie tych zmian za pomocą „rozwiązywania konfliktów”, ponieważ wymagałoby to wprowadzenia wszystkich zmian naraz i nadziei, że zadziała. Problem ten spotęgowałby możliwość błędów w starej, częściowo ukończonej gałęzi.


źródło
+1 „Mogę rozważyć utworzenie nowego widelca najnowszego i ręczne włączenie starej funkcji ponownie”
mika
1
Pierwsze kluczowe pytanie brzmi: czy buduje się starą gałąź funkcji? Czy to działa Jeśli nie, bardzo trudno będzie przetestować połączenie.
Móż
22

Z mojego ograniczonego doświadczenia w git mogę powiedzieć, że czasami szybsze jest ponowne uruchomienie gałęzi funkcji od nowa, jeśli master zaszedł zbyt daleko od punktu odłączenia.

Scalenie dwóch gałęzi bez znajomości historii kodu (biorąc pod uwagę, że właśnie dołączyłeś do projektu) jest naprawdę trudne, i założę się, że nawet programista, który śledził projekt od samego początku, prawdopodobnie popełniłby błędy podczas scalania.

Ma to oczywiście sens, jeśli gałąź funkcji nie jest ogromna, ale można po prostu pozostawić otwartą starą gałąź funkcji , gałąź ponownie z poziomu głównego i ręcznie ponownie wprowadzić zmiany, które składają się na tę funkcję. Wiem, że jest to najbardziej ręczne podejście, ale pozwala mieć pełną kontrolę w przypadku braku lub przeniesienia kodu.

Najlepszym scenariuszem byłoby sparowanie programowania ze starszym w tym przypadku, pomagając lepiej poznać kod.
Może nawet okazać się szybszy, jeśli weźmiesz pod uwagę konflikty scalania i czas testowania!

W pewnym sensie założyłem, że przynajmniej próba scalenia jest oczywiście najlepszą rzeczą do zrobienia. Jeśli to się nie powiedzie lub okaże się zbyt trudne, spróbuj zbierania wiśni, jeśli pójdzie nie tak, idź ręcznie.

GavinoGrifoni
źródło
2
Zdecydowanie niewłaściwe podejście.
Andy,
12
nie, jest to prawidłowe podejście - jeśli masz starożytną gałąź i nie znasz kodu, próba scalenia nie będzie bardzo udana i będzie bardzo ryzykowna. Określenie zmian, które zostały pierwotnie wprowadzone, sprawdzenie, czy są one w ogóle istotne w nowym kodzie, a następnie zastosowanie ich do sensowności jest sposobem na to. Jest to podejście ręczne, ale w tych okolicznościach jest to jedyne bezpieczne rozwiązanie. Oczywiście nadal najpierw spróbuję scalenia, aby zobaczyć, co się stanie, i sprawdzę dziennik, aby zobaczyć, ile zmian zaszło również w gałęzi - może to być trywialne.
gbjbaanb
10
@DavidPacker: Nie sądzę, że GavianoGrifoni sugeruje, aby wyrzucić całą pracę za burtę. Sugeruje ręczne przenoszenie zmian ze starej gałęzi do bieżącej linii rozwoju, krok po kroku. To odrzuci starą historię , nie więcej.
Doc Brown
3
@DavidPacker 1st: oddział jest rok nieaktualny, 2nd: facet, którego zadaniem było dokończenie go, w ogóle nie zna kodu. Biorąc pod uwagę te 2 czynniki, ręczne ponowne zastosowanie jest jedynym realistycznym sposobem podejścia do zadania. Nikt nie sugeruje prostego kopiowania i wklejenia wersji końcówki starej gałęzi.
gbjbaanb
5
@DavidPacker: Konflikty scalania mogą stać się złe - jeśli musisz rozwiązać 500 z nich na raz, zanim ponownie wprowadzisz program w stan umożliwiający kompilację i testowanie. Tego rodzaju sytuacja oczekuje tutaj OP. Jeśli uważasz, że możliwe jest efektywne użycie git, aby uniknąć takiej sytuacji „wszystko albo nic”, dlaczego nie zmodyfikujesz swojej odpowiedzi i nie powiesz OP, w jaki sposób można to osiągnąć?
Doc Brown
16

git-imerge został zaprojektowany właśnie do tego celu. Jest to narzędzie git, które zapewnia metodę przyrostowego łączenia. Łącząc się stopniowo, wystarczy poradzić sobie z kolizjami między dwiema wersjami, nigdy więcej. Ponadto znacznie większa liczba połączeń może być wykonywana automatycznie, ponieważ poszczególne zestawy zmian są mniejsze.

Joe
źródło
6

Próba połączenia głowy głównej z jednoroczną gałęzią może być frustracją i pogłębieniem zagłębienia na biurku za pomocą czoła.

Linia główna nie dotarła do miejsca, w którym jest za jednym razem w ciągu miesięcy. To także miało zmiany i wydania. Próba aktualizacji tego wszystkiego w jednym monolitycznym połączeniu może być przytłaczająca.

Zamiast tego zacznij od scalenia od pierwszego scalenia funkcji z powrotem do linii głównej po podziale nieaktualnej gałęzi. Spraw, by scalenie działało. Następnie następna funkcja scalania. I tak dalej. Wiele z tych połączeń funkcji połączy się bez konfliktu. Nadal ważne jest, aby upewnić się, że bieżąca funkcjonalność przestarzałej gałęzi pozostaje zgodna z kierunkiem, w którym poszła linia główna.

Możesz rozgałęzić się na czele nieaktualnej gałęzi w celu scalenia innych zmian. Chodzi o to, aby upewnić się, że zatwierdzenia i historia, gdy ktoś na nie spojrzy, jest jasne i przekazuje rolę i zasady każdej gałęzi. Nieaktualna gałąź była gałąź funkcji. Ten, nad którym pracujesz, to gałąź akumulacji i pojednania.

Wiele z tego będzie łatwiejsze, jeśli stare gałęzie funkcji lub wydania nadal istnieją i są łatwo dostępne (niektóre miejsca mają zasady czyszczenia nazw gałęzi starszych niż niektóre daty, aby lista gałęzi nie była przytłaczająca ).

Ważną rzeczą w tym wszystkim jest upewnienie się, że testujesz i naprawiasz po udanym scaleniu każdej części historii głównej na swoim miejscu. Nawet jeśli coś może się łączyć bez konfliktów, oznacza to po prostu, że kod nie powodował konfliktu. Jeśli sposób dostępu do przestarzałej funkcji był przestarzały lub usunięty, po udanym scaleniu mogą być konieczne poprawki.

Nawiasem mówiąc, działa to również w przypadku innych systemów kontroli wersji. Czasami musiałem scalić określoną grupę zatwierdzeń SVN do gałęzi (wybieranie Cherry) dla jednej funkcji, naprawić gałąź do pracy z tą funkcją, a następnie scalić następną grupę zatwierdzeń SVN zamiast robić tylko hurtową SVN łączyć.

Chociaż można tutaj zrobić git cherry pick i pozwala to na wprowadzenie określonych zatwierdzeń, ma to pewne wady, które mogą skomplikować proces. Wybieranie wiśniowe nie pokaże informacji o pobranym zatwierdzeniu (możesz dołączyć je do wiadomości zatwierdzenia). To utrudnia śledzenie zmian w historii.

Co więcej, oznacza to, że nie zamierzasz skutecznie odtwarzać mistrza na nieaktualnej gałęzi - wybierasz możliwie niekompletne funkcje - i te funkcje mogą być odtwarzane poza kolejnością.

Kluczowym powodem, dla którego należy połączyć historyczne zobowiązania do opanowania starej gałęzi, jest możliwość zachowania, nazwijmy to „przyszłą historią” starej gałęzi w stanie, o którym można myśleć. Możesz wyraźnie zobaczyć połączenia z historii w przestarzałą gałąź i poprawki reintegrujące funkcjonalność. Funkcje są dodawane w tej samej kolejności, w jakiej były opanowane. A kiedy skończysz i w końcu wykonasz scalenie od głowy mistrza do przestarzałej gałęzi, wiesz, że wszystko zostało scalone i nie brakuje żadnych zobowiązań.


źródło
+1 To interesujące potencjalne alternatywne rozwiązanie, które wykorzystuje scalanie w celu wprowadzenia dużych zmian. Widzę jednak wadę: załóżmy, że masz główne wersje ABCDE i chcesz włączyć gałąź funkcji A1 do E. Może być dużo marnowanego wysiłku, łącząc kod w BC i D. Na przykład, co jeśli D do E był duża zmiana projektu, która spowodowała, że ​​przyrostowe zmiany B, C i D nie były istotne? Zależy to również od tego, jak dojrzała była ta funkcja. Przydatne potencjalne podejście, ale jego stosowność należy rozważyć przed rozpoczęciem.
1

Krok 1. Poznaj kod, przeanalizuj jego architekturę i zmiany, które zostały wprowadzone w obu gałęziach od czasu ostatniego wspólnego przodka.

Krok 2. Jeśli funkcja wydaje się zasadniczo niezależna i dotyczy głównie różnych obszarów kodu, scalania, rozwiązywania konfliktów, testowania, naprawiania itp. To jest szczęśliwa ścieżka, którą możesz zrobić. W przeciwnym razie przejdź do kroku 3

Krok 3. Przeanalizuj obszary konfliktu, dokładnie zrozum funkcjonalny wpływ i przyczyny. Łatwo mogą pojawić się konflikty wymagań biznesowych. Porozmawiaj z licencjatami, innymi programistami, jeśli to konieczne. Poczuj złożoność związaną z usuwaniem zakłóceń.

Krok 4. W świetle powyższego, zdecyduj, czy chcesz scalić / wybrać / nawet wyciąć-wkleić tylko te części, które nie powodują konfliktu i ponownie pisać sprzeczne elementy, LUB czy ponownie napisać całą operację od zera .

Brad Thomas
źródło
0

1. Przejdź do gałęzi, która jest używana jako główna gałąź programisty / wydania.

To gałąź, która zawiera najnowsze zmiany w systemie. Może być master, core, dev, to zależy od firmy. W twoim przypadku jest to prawdopodobnie masterbezpośrednio.

git checkout master
git pull

Pociągnij, aby upewnić się, że masz najnowszą wersję głównej gałęzi rozwoju.

2. Przejdź do kasy i wyciągnij gałąź zawierającą pracę, którą chcesz zakończyć.

Ciągniesz, aby upewnić się, że rzeczywiście masz najnowszą zawartość oddziału. Sprawdzając to bezpośrednio, bez tworzenia go najpierw lokalnie, upewniasz się, że nie masz w nim nowej zawartości master(lub odpowiednio głównej gałęzi programistów).

git checkout <name of the obsolete branch>
git pull origin <name of the obsolete branch>

3. Połącz główną gałąź programistyczną z przestarzałą gałęzią.

Przed uruchomieniem następującego polecenia upewnij się, wpisując git branchlub git statusże jesteś w nieaktualnej gałęzi.

git merge master

git mergeKomenda spróbuje połączyć zawartości z określonej branży, w tym przypadku master, do oddziału jesteś obecnie.

Nacisk na spróbuje . Mogą występować konflikty scalania, które będą musiały zostać rozwiązane przez Ciebie i tylko Ciebie.

4. Napraw konflikty scalania, zatwierdź i wciśnij poprawkę konfliktu

Po naprawieniu konfliktu scalania we wszystkich plikach, na których się znajduje, wykonaj etap, zatwierdź i pchnij rozwiązanie konfliktu do origin.

git add .
git commit -m "fixed the merge conflict from the past year to update the branch"
git push

Zasadniczo można wywoływać git add .wszystkie pliki w celu zatwierdzenia. Podczas rozwiązywania konfliktów scalania chcesz zaktualizować wszystkie niezbędne pliki.

Uwaga dodatkowa

Rozwiązanie konfliktu scalania może być żmudnym zadaniem. Zwłaszcza jeśli jesteś nowy w firmie. Być może jeszcze nie masz odpowiedniej wiedzy, aby samodzielnie rozwiązać wszystkie konflikty scalania.

Poświęć trochę czasu, aby dokładnie sprawdzić wszystkie konflikty, które miały miejsce, i odpowiednio je naprawić, przed kontynuowaniem pracy.


Może się zdarzyć, że zaczniesz pracować nad rocznym oddziałem, scalisz z nim obecny stan rozwoju i nie będzie żadnych konfliktów scalania.

Dzieje się tak, gdy choć system bardzo się zmienił w ciągu roku, nikt nie dotknął plików, które zostały faktycznie zmienione w rocznym oddziale.

Andy
źródło
6
# 4 jest potencjalnie problemem. Jeśli w ciągu ostatniego roku wprowadzono wiele zmian, mogą być wymagane poważne zmiany starej funkcji. Wykonanie tego przez scalenie wymaga wprowadzenia poważnych zmian w kodzie za jednym razem i mam nadzieję, że zadziała, co nie jest dobrą praktyką programistyczną. A kto wie, jaki był stan niedokończonej funkcji? Możesz skończyć z ogromnym bałaganem niedziałającego kodu, a kto wie, czy było to spowodowane problemami z oryginałem, czy wprowadzonymi zmianami?
David, tak długo, jak działa to standardowe podejście, jest w porządku, a PO powinien najpierw spróbować. Ale zdecydowanie istnieje ryzyko, że w opisywanej sytuacji wystąpi zbyt wiele konfliktów scalania, aby poradzić sobie z nimi w ten sposób „wszystko albo nic”.
Doc Brown
@ dan1111 To, że istnieją konflikty scalania, jest całkowicie w porządku, a tak naprawdę przejście przez nie jest właściwą drogą. Ponieważ gałąź pozostała nietknięta przez rok, możesz być całkiem pewien, że nie jest to nic ważnego i nie wpłynie to w dużej mierze na system. Nawet jeśli oddział jest opóźniony o rok, możesz dostać zaledwie 2, aby nie łączyć konfliktów.
Andy,
4
Założenie, że ta gałąź jest nieistotna, jest nieuzasadnione. Mogła to być fundamentalna zmiana w projekcie, która została porzucona i teraz jest ponownie podnoszona. To może być cokolwiek. Masz rację, że może to być prosta sprawa, a konfliktów może być niewiele lub wcale - w takim przypadku Twoja odpowiedź byłaby poprawna. Ale to nie jedyna możliwość.
@ dan1111 Jeśli ktoś nie dotknął miejsca w oddzielnym oddziale przez rok, nie będzie tak bardzo zastanawiać się nad zmianami systemowymi. Wynika to z mojego własnego doświadczenia z przestarzałymi (powyżej 6 miesięcy) oddziałami.
Andy,