Powiedzmy że przerabiam kurs, a postępy commituję i wrzucam z gałęzi master na Github. Nachodzi mnie jednak zamysł aby móc coś podłubać w kodzie, który już mam – zboczyć z kursu. Rodzą się jednak komplikacje, bo kurs jest kursem i chcę mieć czysty materiał, z ładnie zachowanym porządkiem przerabianych rzeczy. Generalnie raczej zasada jest taka, że na serwer wysyła się zawsze działającą wersję, więc jak coś mi się uda zrobić z sensem, to wtedy powinienem dołączyć do głównego projektu i wypchnąć na serwer. Zaś robić parę wersji tego samego projektu trzymając po różnych katalogach i przeładowywać do IDE… bez sensu jak już i tak korzystam z Git-a.
Taki mniej więcej może być przykładowy scenariusz, że nawet jeżeli jesteś dev-wannabe, uczysz się sam, to dodatkowy branch się przyda. Daje on opcję eksperymentowania z kodem na bocznej gałęzi, przy jednoczesnym programowaniu na master i wypychaniu tylko z tego brancha – feel free!
Nowa gałąź
Utworzenie nowej gałęzi i przełączenie się na nią:
git branch branch-name git checkout branch-name
lub utworzenie i przełączenie w jednej linijce:
git checkout -b branch-name
W tym momencie na nowym branchu znajduje się to samo co na branchu, na którym byłem kiedy go tworzyłem. Jeżeli więc, nie tworzyłem wcześniej żadnych gałęzi, to mam kopię z master i jest to punkt wyjścia do dalszej pracy, niezależnej od master.
Po jakimś czasie przychodzi moment aby połaczyć – zmergować, moją boczną gałąź z gałęzią master. Narobiłem tam jednak ileś commitów o różnych, mniej lub bardziej szczęśliwych nazwach i nie chcę tak od razu łączyć jej z master, bo one wszystkie będą zbędnie widoczne i kiepsko pasujące do historii głównej gałęzi. Chcę mieć przykładowo tylko jeden commit na bocznej gałęzi, odpowiednio go nazwać i żeby on był widoczny w historii na master.
Rebase, czyli robienie zgniotka
Aby zgnieść określoną ilość rewizji, na bocznej gałęzi od master, w jeden wynikowy commit:
git rebase -i HEAD~3
Powinno się pojawić interaktywne okno wyboru w domyślnym edytorze tekstu, na Debianie mam w Nano. Liczba po HEAD~ oznacza ilość commitów od najnowszego jakie mają być zgniecione w jeden.
Następnie w linijkach z rewizjami Feature 3 i Feature 2 zamiast pick wpisuję squash lub fixup, bądź ich literkowy skrót.
Zapisuję – ctrl+o, zatwierdzam – enter, wychodzę – ctrl+x i można sprawdzić logi:
git log
Wyświetli się pełna lista rewizji wraz z ich log message, a z trójki zaznaczonych do zgniecenia commitów powinien zostać jeden wynikowy – Feature 1. Każda rewizja ma swój log message, jeżeli wybrałem opcję fixup, to w jego logach zostało pominięte info o zgnieceniu, a jeżeli squash, to będzie widać jakie commity ma w brzuszku. Wygodniejszą alternatywą dla git log jest zapewniające jedno-liniowy wynik dla każdej rewizji:
git log --oneline
Jeżeli nie podoba się nazwa rewizji lub nie będzie pasować do historii na master to:
git commit --amend -m "Other-name"
Merge, czyli scalenie master z jej odgałęzią
Przełączam się na master, sprawdzam czy aby na pewno tam się znajduję i merguję ją z odgałęzieniem od niej, na którym był rebase:
git checkout master git branch git merge branch-name
Btw. Jeżeli były by jeszcze jakieś dodatkowe boczne gałęzie od tej bocznej do master, to proces wygląda podobnie, po prostu najpierw trzeba połączyć tą boczną z jeszcze bardziej boczną, a dopiero później boczną z master.
Achtung konflikt!
W najprostszym przypadku, Git ma dwa pliki z dwóch branchy, które pragnie połączyć w jeden i umieścić na master. Wyszedł jednak konflikt bo na tych samych liniach obu plików ma różną treść i nie wie co wybrać. Git słusznie nie nadpisze pliku o tej samej nazwie z głównej gałęzi, treścią z odgałęzi, bo nie wiadomo co jest prawidłowe. Generalnie nie ma problemu jak na odgałęzieniu tworzę nowe pliki ale jeżeli coś zmieniam w istniejących, to trzeba się liczyć z tego typu sytuacją.
Git w takim wypadku poinformuje podczas mergowania o jaki (wiadomo, może być ich więcej) plik chodzi i wklei do niego oznaczenie co jest na HEAD (aktualny branch – master), a co przyszło z odgałęzi i odseparowuje: “=======“.
<<<<<<< HEAD int x = 5; ======= int x = 10; >>>>>>> branch-name
Trzeba edytować plik, usunąć znaczniki gita, wybrać prawidłową treść, następnie dodać plik do poczekalni:
git add .
Mergować jeszcze raz nie trzeba, więc tylko zatwierdzić nową rewizję:
git commit -m "Commit-name"
Ewentualnie wypchnąć ją na serwer:
git push
Ewentualnie dla porządku usunąć zmergowane z master odgałęzienie:
git branch -D branch-name