Korzystam z Git już od kilku miesięcy przy projekcie z innym deweloperem. Mam kilkuletnie doświadczenie z SVN , więc chyba przynoszę ze sobą dużo bagażu.
Słyszałem, że Git doskonale nadaje się do rozgałęziania i łączenia, a jak dotąd nie widzę tego. Jasne, rozgałęzienie jest bardzo proste, ale kiedy próbuję się połączyć, wszystko idzie do piekła. Teraz jestem przyzwyczajony do tego z SVN, ale wydaje mi się, że właśnie wymieniłem jeden sub-par system wersjonowania na inny.
Mój partner mówi mi, że moje problemy wynikają z chęci połączenia się nie chcąc, i że w wielu sytuacjach powinienem używać bazy zamiast scalania. Oto przykładowy schemat pracy:
clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature
git checkout master
git merge my_new_feature
Zasadniczo utwórz gałąź funkcji, ZAWSZE dokonuj zmiany bazy od głównej do gałęzi i łącz ją z gałęzi z powrotem do głównej. Należy pamiętać, że oddział zawsze pozostaje lokalny.
Oto przepływ pracy, z którym zacząłem
clone remote repository
create my_new_feature branch on remote repository
git checkout -b --track my_new_feature origin/my_new_feature
..work, commit, push to origin/my_new_feature
git merge master (to get some changes that my partner added)
..work, commit, push to origin/my_new_feature
git merge master
..finish my_new_feature, push to origin/my_new_feature
git checkout master
git merge my_new_feature
delete remote branch
delete local branch
Są dwie zasadnicze różnice (tak myślę): zawsze używam scalania zamiast zmiany bazy i wypycham swoją gałąź funkcji (a moja gałąź funkcji zatwierdza) do zdalnego repozytorium.
Moje rozumowanie dla zdalnej gałęzi jest takie, że chcę, aby moja praca była tworzona podczas tworzenia kopii zapasowej. Kopie zapasowe naszego repozytorium są automatycznie tworzone i można je przywrócić, jeśli coś pójdzie nie tak. Mój laptop nie jest lub nie jest tak dokładnie. Dlatego nienawidzę mieć kodu na moim laptopie, który nie jest dublowany gdzie indziej.
Moje rozumowanie dla scalania zamiast rebase jest takie, że scalanie wydaje się standardowe, a rebase wydaje się być funkcją zaawansowaną. Mam przeczucie, że to, co próbuję zrobić, nie jest zaawansowaną konfiguracją, więc zmiana bazy powinna być niepotrzebna. Przeczytałem nawet nową książkę Pragmatic Programming na Git, która obszernie omawia scalanie i prawie nie wspomina o rebase.
W każdym razie śledziłem przebieg pracy w niedawnym oddziale, a kiedy próbowałem połączyć go z powrotem w master, wszystko poszło do piekła. Było mnóstwo konfliktów z rzeczami, które nie powinny mieć znaczenia. Konflikty nie miały dla mnie sensu. Zajęło mi dzień, aby wszystko załatwić, a ostatecznie kulminacją było zmuszenie do zdalnego mistrza, ponieważ mój lokalny mistrz rozwiązał wszystkie konflikty, ale zdalny nadal nie był szczęśliwy.
Jaki jest „prawidłowy” przepływ pracy dla czegoś takiego? Git ma sprawić, że rozgałęzianie i łączenie będzie super łatwe, a ja po prostu tego nie widzę.
Aktualizacja 2011-04-15
To wydaje się być bardzo popularnym pytaniem, więc pomyślałem, że zaktualizuję swoje dwuletnie doświadczenie, odkąd po raz pierwszy zadałem.
Okazuje się, że oryginalny przepływ pracy jest poprawny, przynajmniej w naszym przypadku. Innymi słowy, robimy to i działa:
clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git checkout master
git merge my_new_feature
W rzeczywistości nasz przepływ pracy jest nieco inny, ponieważ mamy tendencję do wykonywania połączeń squashowych zamiast połączeń surowych. ( Uwaga: jest to kontrowersyjne, patrz poniżej. ) To pozwala nam przekształcić całą gałąź funkcji w pojedyncze zatwierdzenie na master. Następnie usuwamy naszą gałąź funkcji. To pozwala nam logicznie konstruować nasze commity na master, nawet jeśli są trochę niechlujne na naszych oddziałach. Oto co robimy:
clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git checkout master
git merge --squash my_new_feature
git commit -m "added my_new_feature"
git branch -D my_new_feature
Kontrowersje związane ze scalaniem squash - jak zauważyło kilku komentatorów, scalanie squash wyrzuci całą historię w gałęzi funkcji. Jak sama nazwa wskazuje, zgniata wszystkie zatwierdzenia w jeden. W przypadku małych funkcji ma to sens, ponieważ skraca je w jednym pakiecie. W przypadku większych funkcji prawdopodobnie nie jest to świetny pomysł, zwłaszcza jeśli twoje indywidualne zatwierdzenia są już atomowe. To naprawdę sprowadza się do osobistych preferencji.
Żądania ściągania Github i Bitbucket (inni?) - jeśli zastanawiasz się, w jaki sposób scalanie / rebase odnosi się do żądań ściągania , zalecam wykonanie wszystkich powyższych kroków, dopóki nie będziesz gotowy, aby ponownie połączyć się z mistrzem. Zamiast ręcznie łączyć się z gitem, akceptujesz PR. Zauważ, że nie spowoduje to scalenia squasha (przynajmniej domyślnie), ale non-squash, non-fast forward jest przyjętą konwencją scalania w społeczności Pull Request (o ile mi wiadomo). W szczególności działa to tak:
clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git push # May need to force push
...submit PR, wait for a review, make any changes requested for the PR
git rebase master
git push # Will probably need to force push (-f), due to previous rebases from master
...accept the PR, most likely also deleting the feature branch in the process
git checkout master
git branch -d my_new_feature
git remote prune origin
Pokochałem Gita i nigdy nie chcę wracać do SVN. Jeśli walczysz, po prostu trzymaj się go, a ostatecznie zobaczysz światło na końcu tunelu.
rebase
zrozumieniu