Strategie łączenia 1 roku rozwoju w Visual Studio

32

Mam klienta, który nalegał, aby cały 2016 rok trzymać się z dala od głównych oddziałów. Miały 3-4 inne zespoły pracujące nad aplikacją na różnych stanowiskach. Wprowadzono wiele dużych zmian (zmiana sposobu wstrzykiwania zależności, czyszczenie kodu za pomocą ReSharper itp.). Teraz padło na mnie, aby połączyć main z naszą nową gałęzią deweloperów, aby przygotować się do przyspieszenia naszych zmian w łańcuchu.

Podczas mojego początkowego łączenia, TFS zgłosił ~ 6500 plików z rozwiązaniem konfliktu. Niektóre z nich będą łatwe, ale niektóre będą znacznie trudniejsze (szczególnie niektóre javascript, kontrolery interfejsu API i usługi obsługujące te kontrolery).

Czy mogę zastosować takie podejście, które ułatwi mi to?

Aby to wyjaśnić, wielokrotnie wyrażałem wiele obaw związanych z tym podejściem. Klient był i jest świadomy trudności z tym związanych. Ponieważ wybrali brak personelu QA (1 tester na 4 programistów, brak testów automatycznych, małe testy regresji), nalegali, abyśmy utrzymywali naszą gałąź odizolowaną od zmian w głównej gałęzi pod pretekstem, że zmniejszy to potrzebę naszego tester, aby wiedzieć o zmianach wprowadzanych gdzie indziej.

Jednym z większych problemów tutaj jest aktualizacja do wersji kątowej i niektórych programów innych firm - niestety nie znaleźliśmy dobrego sposobu na zbudowanie tego rozwiązania, dopóki wszystkie elementy nie zostaną przywrócone na miejsce.

użytkownik258451
źródło
63
Nie. Masz dwie oddzielne gałęzie, które były aktywnie rozwijane przez rok. Scalenie będzie do bani.
17 z 26
2
Jaki jest zakres zmian, które wprowadził Twój zespół? Bardziej wydajne może być zidentyfikowanie tych zmian, a następnie ręczne ich ponowne zastosowanie w bieżącej głównej bazie kodu.
kdgregory
2
Czy to cały 2015 czy 2016? Jeśli jego rok 2015 to 2 lata, co oznacza, że ​​będzie podwójnie ssać.
David mówi Przywróć Monikę
1
Dlaczego klientowi zależy, czy praca, którą dla niego wykonujesz, znajduje się w osobnej gałęzi w systemie kontroli wersji?
Ixrec
17
Cokolwiek zrobisz, upewnij się, że rozliczasz się co godzinę.
Sean McSomething

Odpowiedzi:

37

Byłby prosty sposób, który oddzieliłby twój nowy rozwój od głównej gałęzi, nie wprowadzając cię w tę niefortunną sytuację: każda zmiana z pnia powinna być codziennie łączona z gałęzią programistów . (Czy twój klient był tak krótkowzroczny, że nie mógł przewidzieć, że pewnego dnia twój oddział będzie musiał zostać ponownie przeniesiony do głównej linii?)

W każdym razie najlepszym podejściem jest IMHO, próbujące powtórzyć to, co powinno się wydarzyć z pierwszej ręki:

  • zidentyfikować semantykę zmian w linii głównej dla dnia 1 po utworzeniu gałęzi. Zastosuj je do bieżącej bazy kodu tak dobrze, jak to możliwe. Jeśli była to „zmiana lokalna”, powinna być prosta, jeśli była to „refaktoryzacja przekrojowa”, taka jak zmiana nazwy powszechnie używanej klasy, zastosuj ją w semantycznie równoważny sposób do bieżącej bazy kodu. Mam nadzieję, że w tym roku nie wprowadzono sprzecznych przekrojowych zmian w bazie kodu w „twojej” gałęzi, w przeciwnym razie może to stać się prawdziwą łamigłówką
  • przetestować wynik (czy wspominałem, że do tego zadania potrzebujesz dobrego zestawu testów)? Napraw wszystkie błędy wykryte podczas testu
  • teraz powtórz ten proces dla zmian w linii głównej dla dnia 2, następnie dnia 3 i tak dalej.

Może to działać, gdy zespoły ściśle przestrzegają klasycznych zasad kontroli wersji („zatwierdzaj tylko możliwe do kompilacji, przetestowane stany” i „melduj się wcześnie i często”).

Po 365 powtórzeniach (lub 250, jeśli masz szczęście i możesz powiązać pracę na weekendowe zmiany), będziesz prawie gotowy (prawie, ponieważ musisz dodać liczbę zmian, które będą miały miejsce w głównej linii podczas okresu integracji ). Ostatnim krokiem będzie ponowne scalenie zaktualizowanej gałęzi programisty w pień (abyś nie stracił historii pnia). To powinno być łatwe, ponieważ technicznie powinno to być jedynie zastąpienie dotkniętych plików.

I tak, mówię poważnie, prawdopodobnie nie ma na to skrótu. Może się okazać, że „porcje dzienne” mogą być czasami zbyt małe, ale nie spodziewałbym się tego, chyba bardziej prawdopodobne, że porcje dzienne mogą okazać się zbyt duże. Mam nadzieję, że twój klient naprawdę dobrze za to płaci i że jest to dla niego tak drogie, że wyciągnie wnioski z porażki.

Powinienem dodać, że możesz tego spróbować także z przełączonymi stronami - reintegrując zmiany z gałęzi w małych porcjach do linii głównej. Może to być prostsze, gdy w gałęzi dewelopera wprowadzono znacznie mniej zmian niż w linii głównej lub większość zmian nastąpiła w nowych plikach źródłowych, które obecnie nie są częścią linii głównej. Można to postrzegać jako „przenoszenie” funkcji z produktu A (gałąź programistów) do nieco innego produktu B (aktualny stan pnia). Ale jeśli większość przekrojowych refaktoryzacji została przeprowadzona w linii głównej i mają one wpływ na twój nowy kod (kolizje scalania 6500 wydają się być na to dowodem), może być łatwiej, tak jak to opisałem na początku.

Doktor Brown
źródło
9
Jeśli kontynuujesz rozwój pnia podczas ponownej integracji, sugeruję najpierw rozgałęzić końcówkę pnia i rozwinąć ją. W przeciwnym razie skutecznie łączysz jednocześnie przeszłość i przyszłość. Ale oczywiście trzymam się Doktora Browna w przypadku wszelkich nieciągłości czasoprzestrzennych.
radarbob
1
@radarbob: to, co sugerujesz, ma sens tylko wtedy, gdy OP integruje się z dewelopera do pnia, ale nie wtedy, gdy decyduje się połączyć ze pnia do dewelopera, jak to opisałem wcześniej. Jeśli OP przenosi zmiany z pnia do jego oddziału z dnia na dzień (począwszy od zmian 365 dni w przeszłości), nie będzie miało znaczenia, czy rozwój w pniu będzie kontynuowany. Będzie musiał tylko kontynuować tę taktykę, aż dotrze do teraźniejszości (zakładając, że może zintegrować i przetestować zmiany tych 3-4 drużyn w ciągu jednego dnia w mniej niż jeden dzień).
Doc Brown
Cytat: „Powinienem dodać, że możesz tego spróbować również z przełączonymi stronami - reintegrując zmiany z gałęzi w codziennych pakietach do głównej linii. ” Mój paskudny zmysł podpala bezpiecznik. Wyczuwam potencjalną kaskadę „zniekształceń rezonansowych harmonicznych” z integrującymi się zmianami zmian.
radarbob
To dobra rada. Zobacz edycję, aby rozwiązać kilka kwestii tutaj.
user258451
1
@ user258451: więc masz różne zespoły kontroli jakości dla pnia i nowego oddziału programistów, którzy nie chcieli ze sobą rozmawiać? Great Scott: - ((
Doc Brown
14

Na tym etapie scalania powiedziałbym, że automatyczne łączenie może tylko skomplikować proces. Miałem podobne problemy z oddziałami, które były rozbieżne przez ponad rok, a najbardziej skuteczną metodą, jaką mam, jest:

  • Zrób kopię oryginalnego stanu nie połączonego
  • Różnica między nie połączonymi a najnowszymi
  • Rozbij wszelkie wspólne elementy
    • Na przykład wykonaj wszystkie zmiany nazw funkcji, a następnie zmiany parametrów itp.
    • Zignoruj ​​białe spacje na diff, jeśli standardy się zmieniły, w przeciwnym razie zmarnujesz dużo czasu na zliczanie spacji
  • Najpierw skoncentruj się na podstawowej funkcjonalności

W końcu ostrzeżenia kompilatora i różnice będą twoimi najlepszymi przyjaciółmi, używaj niezmergowanego różnicowania, aby zobaczyć dokładnie, co się różni i po prostu kontynuuj. Mogą istnieć różne narzędzia, których możesz użyć, ale to od ciebie zależy, które z nich będzie najlepsze.

Kluczem do sukcesu jest kontynuacja.

Edytować:

Słowo ostrzeżenia, takie podejście będzie oznaczać, że historia kontroli wersji stanie się „zepsuta”, ponieważ stracisz dowody scalenia między gałęziami, a także historię nie połączonej gałęzi.

Dzięki komentarzom „Jacka Aidleya” i „17 z 26”

Erdrik Ironrose
źródło
1
Głównym problemem związanym z tym podejściem jest to, że niszczy zapis zmian pozostawionych w systemie kontroli wersji.
Jack Aidley,
Zasadniczo, dokonując tych samych zmian po raz drugi podczas scalania, nadal będziesz mieć dziennik zmian, będzie to po prostu kolejność wykonywana podczas scalania, a nie kolejność, w jakiej zostały wykonane podczas programowania. Nie idealny, ale lepszy niż nic. Ponadto nadal miałbyś pierwotny stan nie połączony, gdyby był on utrzymywany w kontroli wersji.
Erdrik Ironrose
Miałbyś dziennik zmian, ale nie miałbyś historycznych dowodów na to, że zmiany zostały scalone z oddziału do oddziału. To byłaby wielka sprawa w TFS, który oferuje tylko niezmergowane zestawy zmian do wyboru podczas łączenia z gałęzi do gałęzi.
17 z 26
@ 17of26 Chociaż zgadzam się, że możesz przywrócić jakiś zapis zmian, 17 z 26 ma rację, że ten zapis nie będzie kompletny ani dokładny. Może być tak, że takie podejście jest wystarczająco łatwiejsze, aby utrata rekordu była akceptowalnym kompromisem, biorąc pod uwagę złą sytuację, w której się teraz znajdują. Myślę jednak, że ważną wadą jest rozpoznanie bez względu na to, co zdecydują.
Jack Aidley
1
@Jack Aidley Zdecydowanie zgadzam się, że to dość problem, więc dodałem trochę odpowiedzi, aby się zastanowić. Mam nadzieję, że to w porządku?
Erdrik Ironrose
8

Kilka lat temu mieliśmy klienta o takich samych wymaganiach dotyczących oddzielenia oddziałów. Tak zrobiliśmy.

Nigdy nie połączyliśmy ich oddziału. Mieli tam swoją unikalną wersję. Pobraliśmy je dodatkowo za zmiany, ponieważ w zasadzie mieliśmy dwa główne pnie zamiast 1 głównego pnia i gałęzi.

Próbowaliśmy połączyć się z powrotem do bagażnika, ale po 2 tygodniach postanowiliśmy porzucić ten wysiłek, ponieważ płonęło wiele godzin bez żadnych wymiernych korzyści.

Więc nie łącz go z powrotem. W przyszłości scalanie krytycznych poprawek do tego oddziału klienta w razie potrzeby, a wszelkie ulepszenia byłyby jednorazowo naliczane specjalnie dla tego klienta.

Jon Raynor
źródło
Ta strategia jest możliwa tylko wtedy, gdy różne kierunki rozwoju są skierowane do różnych klientów. Lub, jeśli przypadki zastosowania, w które zaangażowany jest produkt, pozwalają na użycie dwóch różnych linii produktów równolegle, w sposób niezintegrowany. Według mnie OP opisuje sytuację, w której nowa linia rozwoju jest skierowana do tego samego klienta, co ten, który korzysta z łącza, i nie jest jasne, czy równoległe użycie dwóch linii produktów może mieć sens w jego przypadku.
Doc Brown
1

To nie będzie zabawne, ale to, jak bolesne będzie, zależy od charakteru zmian i ich izolacji.

Sugeruję, abyś spróbował zjednoczyć gałęzie poprzez refaktoryzację w jak największym stopniu, zanim wykonasz scalenie.

Narzędzie scalania jest głupie, ponieważ patrzy tylko na różnice tekstowe i nie rozumie kodu w żaden sposób. Jeśli główna gałąź zmieniła nazwę klasy używanej w aplikacji, a gałąź funkcji używa starej nazwy w nowym kodzie, narzędzie do scalania nie zrozumie, że nazwa klasy powinna również zostać zmieniona w nowym kodzie. Ale jeśli zrobisz refaktoryzację w gałęzi B, aby zmienić nazwę klasy jak w gałęzi A, będzie działać zarówno w starym, jak i nowym kodzie, a scalanie przebiegnie bezproblemowo.

Po drugie, powinieneś sprawdzić, jak zlokalizowane są zmiany w gałęzi programistycznej. Jeśli zmiany w gałęzi funkcji są zlokalizowane w kilku obszarach, nie musisz konwertować niezmienionego kodu, możesz po prostu skopiować i zastąpić z gałęzi głównej.

W obszarach kodu, w których nastąpiły nietrywialne zmiany w obu gałęziach, musisz dokładnie sprawdzić kod i zdecydować, jak przepisać.

JacquesB
źródło