gitWeb – branching cz.2

W dzisiejszym wpisie chciałbym omówić kolejne funkcjonalności modułu odpowiedzialnego za branching. W poprzednim wpisie opisałem następujące kwestie:

  • Lista branchy
  • Tworzenie nowego brancha

Dzisiaj skupię się na:

  • Checkout – przełączanie się pomiędzy branchami
  • Pobranie drzewa comitów dla aktualnego brancha
  • Opisanie na drzewie commitów, dostępnych branchy

Checkout

Git

Podstawowa funkcjonalność gita, bez której nie da się działać pracując w środowisku z wieloma branchami. Pozwala na przełączenie się pomiędzy dwoma branchami. W konsoli operacja dostępna pod komendą:

Najważniejsze kwestie jakie należy wziąć pod uwagę korzystając z tej komendy to:

  • Wszystkie zmiany, które posiadamy na aktualnym branchu przeniosą się na nowy branch. Oczywiście, jeżeli wystąpi konflikt to zostaniemy o tym poinformowani.
  • Przełączenie się na nowy branch powoduje, że HEAD repozytorium ustawi się na Tip danego brancha. Innymi słowy, zostanie pobrany stan repozytorium z najnowszego commita na nowym branchu.

Tip w branchu, jest to najnowszy commit zrobiony na danym branchu.

VueJs

Po stronie widoku za wiele się nie dzieje. Wywołane kolejne akcje, które na końcu wywołują API. Jedyną rzeczą, na którą chciałem zwrócić uwagę jest główny plik actions.js w sekcji store.

Generalnie chciałem zmodularyzować Vuex w obrębie głównych komponentów Vue. A więc, miałem moduł Vuex branchArea.js, który odpowiadał komponentowi branch.vue, itd.

Aby była możliwa komunikacja między tymi modułami, wydzieliłem nadrzędne miejsce, w którym akcje będą je spajać.

W tym konkretnym przypadku akcja checkout znajduje się w pliku nadrzędnym actions.js, a sama akcja prezentuje się następująco:

Wywołana sekwencja kolejnych akcji. W pierwszej kolejności wywoływany jest CHECKOUT_BRANCH na wskazanym przez użytkownika branchu. W momencie gdy operacja zakończy się sukcesem wywoływana jest akcja GET_COMMIT_TREE_FROM_HEAD, która pobiera listę commitów, zaczynając od HEAD „nowego” brancha. Na samym końcu wywoływane jest builder odpowiedzialny za kompozycję grafu.

Api

Aby przełączyć branch należy odwołać się do:

Sam kontroler wygląda tak samo jak w poprzednich wpisach, a więc przejdę od razu do właściwej implementacji przy pomocy libgit2sharp:

I pojawia się… Magiczne Commands. Z jakiegoś powodu autorzy biblioteki postanowili, że wydzielą część funkcjonalności do statycznych metod. Oczywiście, to jest OS projekt i rozumiem dlaczego wszystko co miało by trafić do commands, jeszcze tam nie trafiło. Ja mam z tym jeden problem, mianowicie dość ciężko do statycznych metod pisze się testy, trzeba je oklejać jakimiś wrapperami, żeby móc mockować takie wywołania.

Poza tym kawałek kodu dość standardowy, parę sprawdzeń warunków brzegowych, zwrócenie NullObject w momencie gdy branch nie istnieje i translacja do obiektu DTO.

Pobranie drzewa commitów dla aktualnego brancha

Tu sprawa wygląda bardzo prosto. Komenda:

pobiera zawszę listę commitów zaczynając od HEAD aktualnego brancha, na którym się znajdujemy.

Pobieranie listy commitów oraz budowanie z nich branchy opisałem we wpisie dotyczącym komendy git log –graph, serdecznie do niego zapraszam.

Opisanie na drzewie commitów branchy

Na powyższym gifie w prawej stronie widać skromnie wyglądające labele BRANCH. Reprezentują one „status” aktualnego brancha. A więc pokazują się tam wszystkie branche przypisane do commita „widocznego” z poziomu obecnego brancha.

Zadanie do zrealizowania może brzmieć następująco:

Pokaż wszystkie branche których najnowszy commit znajduje się na grafie aktualnego brancha.

Powyższa metoda robi dwie rzeczy. W pierwszej kolejności pobrana jest lista commitów dla aktualnego brancha. Następnie pobrany jest Tip każdego dostępnego brancha. Mając te dwie informacje możemy w prosty sposób zmapować te dwie kolekcje.

Podsumowanie

Komponent zbudowany… ale nie kompletny. Brakuje w nim jeszcze wielu, wielu rzeczy. Między innymi zmiana nazwy, usuwanie, „śledzenie” branchy zdalnych. Operacja Checkout wiąże się również z konfliktami i mergowaniem. Jak widać sporo pracy jeszcze przede mną.

W tych dwóch wpisach opisałem budowę jednego z wielu komponentów, które będą budować moją aplikację. Oprócz samego opisywania kodu, chciałem również wrzucić troszkę teorii dotyczącej samego gita. Chciałbym w dalszym ciągu pisać w takiej konwencji. Dzięki temu mam zamiar wprowadzić trochę urozmaicenia i ciekawej wiedzy we wpisy.

W tym momencie kończę kolejny spory moduł. Jeszcze przed świętami chciałbym opisać część dotyczącą tworzenia commitów i podglądu zmian w plikach.

gitWeb – branching cz.1

W dzisiejszym wpisie opiszę jak zrealizowałem branchowanie w mojej aplikacji.

Na powyższym  gifie widzimy wszystkie funkcjonalności, które udało mi się do tej pory zrealizować. A są to:

  • Lista branchy
  • Stworzenie nowego brancha
  • Przełączenie się na inny branch
  • Pobranie drzewa comitów dla aktualnego brancha
  • Opisanie na drzewie commitów branchy

W tym wpisie opiszę kompletną ścieżkę implementacji od frontendu do wywołania biblioteki libgit2sharp. Podzielę to na 3 sekcje:

  • komenda jaką należy wykonać w konsoli gita
  • moduł VueJs
  • API wraz z implementacją libgit2sharp.

Jako, że każdy z elementów jest obszerny dzisiaj skupie się jedynie na opisie listy branchy i sposobie w jaki udało mi się zaimplementować dodanie nowego brancha.

Lista branchy

Za pomocą tej komendy w konsoli pokażą się wszystkie branche w repozytorium. Zarówno te lokalne jak i „śledzące” zdalne. Dodatkowo dostajemy informację na którym branchu obecnie się znajdujemy.

Komponent VueJs

Lista zaimplementowana została w komponencie o nazwie branch.vue. Nie chcę wklejać tu całego komponentu bo nie będzie to miało sensu. Wkleję jedynie najważniejsze kawałki odpowiedzialne za pobranie listy branych i ich wyświetlenie.

Pobranie danych odbywa się w następujący sposób:

Funkcja beforeMount wywoływana jest przed umieszczeniem komponentu na widoku. Jest to mój punkt wejściowy do aplikacji. Zostanie on nim dopóki nie wymyślę lepszego punktu wejściowego. Funkja jest bardzo prosta wywołuje ona akcję Vuex o nazwie GET_ALL_BRANCHES.

Jak widać akcja jest również prosta. Wywołany zostaje serwis, który odpytuje  WebApi. W przypadku poprawnego pobrania danych przekazywane są one do mutacji SET_ALL_BRANCHES. Ustawia ona stan aplikacji zgodnie z tym, jak powinno się to robić korzystając z Vuex. Tylko mutacje powinny zmieniać stan aplikacji.

Sam serwis do komunikacji wygląda następująco:

 

Jak widać wszystko jest dość przejrzyste i proste. Chciałbym aby cała aplikacja tak wyglądała 😉

API

Po stronie backendu funkcjonalności dotyczące branchy wystawione są za pomocą API w kontrolerze BranchController.cs.

Jeżeli chodzi o samo API wybrałem REST ze względu na to, że jest teraz super fancy i modny. A tak na serio to dzięki tej aplikacji chciałbym poznać to podejście. Jego wady, zalety, ograniczenia.

No więc lista branchy dostępna jest pod:

a sam kontroler jest zaimplementowany maksymalnie prosto:

Jak widać nic szczególnego, wstrzyknięty jedynie odpowiedni provider odpowiedzialny za obsługę branchów. Sam provider wygląda tak:

Ot i pierwsze spotkanie z libgit2Sharp. Jeszcze nie opisywałem tej biblioteki. Do providera wstrzykiwany jest obiekt typu IRepository. Jest to interfejs pochodzący właśnie z tej biblioteki. Stanowi on swoiste serce całego frameworka. Reprezentuje on repozytorium gita. Dzięki temu obiektowi mamy dostęp do prawie wszystkich (opiszę w kolejnych wpisach) operacji i zasobów, które możemy wywołać na repozytorium.

W tym konkretnym  przypadku pobranie listy branchy sprowadza się do zwykłej projekcji w Linq na kolekcji Branches. Klasa Branch jest moją klasą. Chciałem ograniczyć ilość zależności w aplikacji do minimum. Dlatego postanowiłem, że nie będę korzystał z dostępnych w bibliotece typów tylko stworze własne obiekty DTO. Dzięki temu podejściu zależność do libgit2Sharp mam wyłącznie w projekcie gitWeb.Core. Kontrolery nic nie wiedzą jaki provider do gita czy innego typu repozytorium jest wykorzystywany. Otwiera to furtkę do implementacji innych systemów kontroli wersji jak np Mercurial czy SVN bez konieczności większych zmian w aplikacji.

Nowy branch

Podstawowa komendą do stworzenia nowego brancha jest:

Oczywiście jest całe mnóstwo opcji, które można wykorzystać do jego stworzenia, ale w obecnej fazie wykorzystuję tą podstawową.

Dodatkowo, tworzone branche nie odwołują się do żadnego zdalnego brancha. Tą funkcjonalność dorobię w ramach Push i Pull.

Komponent VueJs

Do komponentu branch.vue musiałem dodać mini formularz który umożliwi mi wpisanie nazwy nowego brancha.

 

Po stronie komponentu do obsługi tworzenia nowego brancha mamy 2 funkcję:

clearCreationForm – prosta funkcja do czyszczenia stanu.

createBranch – tak jak w przypadku listy branchy, komponenty delegują całą logikę dotyczącą komunikacją z API do Vuex. A więc w tym przypadku wywołujemy akację CREATE_NEW_BRANCH. Przyjmuje ona jako parametr nazwę nowego brancha. Jeżeli operacja tworzenia przebiegnie pomyślnie, czyszczona jest forma z danymi.

Sama akcja w Vuex jest już bardziej skomplikowana:

Więc co tu się dzieje? Tak jak wspomniałem wcześniej, na początku wywoływana jest akcja CREATE_NEW_BRANCH. Akcja ta wywołuje serwis który z kolei wywołuje metodę API do stworzenia brancha. Jeżeli wszystko pójdzie zgodnie z planem to wywoływana jest akcja CHECKOUT_BRANCH.

W tej fazie projektu nowy branch jest automatycznie checkoutowany. I tak samo, jeżeli wszystko będzie w porządku to wywoływana jest (opisana wcześniej) akcja GET_ALL_BRANCHES.

W tym wpisie nie będę opisywał procesu checkoutowania., poświecę na niego osobny wpis.

Serwis odpowiedzialny za odwołanie się do API został zaimplementowany następująco:

API

Stworzyć nowy branch możemy odwołując się do poniższego zasobu:

Szybkie spojrzenie na kontroler oraz branchProvider:

W podstawowej formie żeby stworzyć branch należy jedynie wywołać metodę CreateBranch na obiekcie repozytorium oraz przekazać nazwę jako parametr.

Podsumowanie

Dzisiejszym wpisem rozpocząłem serię dotyczącą implementacji poszczególnych elementów projektu. Już powoli zaczyna coś działać! Strasznie mnie to nakręca do dalszego działania.

Widać wyraźnie, że biblioteka libgit2sharp jest naprawdę prosta w użyciu. Samo opisujące się metody i prosty interfejs sprawia, że bardzo dobrze się z niego korzysta. Dodatkowo community (github, stackOverflow) jest dość potężne, więc nie ma też problemu z rozwiązywaniem ewentualnych problemów czy błędów w samej bibliotece.

W kolejnym wpisie opisze kolejne części branchingu – a więc checkout brancha, zmianę nazwy oraz usunięcie.