Laboratorium 6: Enumerable, LINQ i Metody Rozszerzające #
Kod startowy #
Zadanie laboratoryjne jest podzielone na dwie części.
- Pierwsza część dotyczy metod rozszerzających,
Enumerableoraz instrukcjiyield.- Druga część obejmuje zapytania LINQ oraz wyrażenia lambda.
Części te są niezależne od siebie i można je realizować w dowolnej kolejności.
Commit Graph #
Klasa Repository reprezentuje uproszczenie repozytorium w systemie kontroli wersji. Repozytorium zawiera następujące właściwości:
Objects: Słownik obiektów, indeksowany ich skrótem (ang. hash).Branches: Słownik gałęzi (wskaźników na obiekt przechowywany w repozytorium), indeksowany nazwą gałęzi.HEAD: Wskaźnik na moment w historii zmian, w którym obecnie znajduje się użytkownik.Authors: Słownik autorów, indeksowany ich identyfikatorem.
Jednym z typów obiektów przechowywanych w systemie kontroli wersji jest commit. W naszym uproszczonym modelu posiada on tablicę ParentHashes, zawierającą skróty jego rodziców. Zauważ, że tak zdefiniowana struktura nie jest drzewem, lecz grafem skierowanym.
Zaimplementuj metody rozszerzające:
TryGetCommit, która posiada parametrhashpraz parametr wyjściowycommit. Zwracatruejeżeli obiekt o danym skrócie znajduje się w repozytorium i reprezentuje obiekt o typieCommit. W przeciwnym przypadku zwracafalse.GetCommitOrThrow, która posiada parametrhash. Jeżeli obiekt o danym skrócie istnieje, to go zwraca, w przeciwnym wypadku rzuca wyjątkiem z wiadomością:$"Commit {hash} not found".
Następnie, zaimplementuj metody rozszerzające:
TraverseBranchByFirstParent, która w sposób leniwy zwraca kolejne obiekty typuCommit, iterując po pierwszych rodzicach obiektu początkowego o skróciestartHash(parametr metody, o domyślnej równy wartości wskaźnikaHEADw repozytorium).TraverseByRevision, która działa w sposób podobny doTraverseBranchByFirstParent, jednak iteruje po kolejnych obiektach typuCommitw sposób zdefiniowany przez wzorzecpattern.
Do implementacji metody
TraverseByRevisionwykorzystaj gotową klasęRevisioni jej statyczną metodęParse. Zapoznaj się z modelem reprezentującymRevision. W naszym uproszczonym modelu wzorzec będzie składał się z podstawyBaseRef, która może być:
- skrótem obiektu (np.
9ea45df2a43d1df035b20e211ce771785e59e12b),- nazwą gałęzi (np.
master),- lub symbolem
HEAD,A także kolekcji modyfikatorów oznaczanych symbolami
~oraz^. Symbole te mają następujące znaczenie:
~N, gdzieNto dodatnia liczba całkowita, powoduje cofnięcie się w historii oNcommitów, idąc po pierwszych rodzicach.^N, gdzieNto dodatnia liczba całkowita, powoduje cofnięcie się w historii o jednego rodzica wstecz, przy czym liczbaNokreśla, który to jest rodzic (indeksowanie od 1).Przykłady:
HEAD~jest wskazaniem na pierwszego rodzica obiektu, na który wskazujeHEAD(to samo coHEAD~1orazHEAD^iHEAD^1).HEAD~2jest wskazaniem na “dziadka” obiektu, na który wskazujeHEAD, idąc po pierwszym rodzicu.HEAD^2jest wskazaniem na drugiego rodzica obiektu, na który wskazujeHEAD.Symbole
~i^można łączyć ze sobą, otrzymując np.HEAD~3^2~^2.Metoda
TraverseByRevisionpowinna działać zgodnie z następującymi krokami:
- Zamień wejściowy ciąg znaków
patternna obiekt klasyRevision.- Próbuj znaleźć identyfikator (skrót) odpowiadający
BaseRefw następującej kolejności:
- jeżeli
BaseRefodpowiada nazwie gałęzi - użyj wartości skrótu, na którą wskazuje ta gałąź,- jeżeli
BaseReftoHEAD- użyj aktualnej wartościHEADrepozytorium,- jeżeli
BaseRefwystępuje jako klucz w obiektach repozytorium - traktuj go jako bezpośrednią wartość skrótu,- w przeciwny razie - zgłoś błąd
Base reference not found.
- Na podstawie uzyskanego skrótu pobierze obiekt z repozytorium i zwróć go jako pierwszy element sekwencji wynikowej.
- Dla każdego z modyfikatorów z kolekcji
Modifierszastosuj metodęApply, iterując i zwracając kolejne commity z repozytorium.- W przypadku błędów pobierania obiektów - zgłoś błąd.
Zaimplementuj interfejs IRevisionModifier dla ParentModifier oraz AncestorModifier, aby uprościć implementację TraverseByRevision. Metoda Apply wykorzystuje repozytorium, aby zwrócić w sposób leniwy kolekcję skrótów obiektów zgodnie z semantyką implementujących ją modyfikatorów.
Błędy w strukturze grafu (brakujący rodzice/przodkowie lub nieznalezione obiekty) zgłoś w postaci wybranego przez siebie wyjątku z przykładowymi wiadomościami:
"Commit '{hash}' not found","Commit {hash} does not have parent #{index}","Commit 'hash' has no parent".
Punktacja #
TryGetCommitiGetCommitOrThrow- 1 pkt.TraverseBranchByFirstParent- 1 pkt.ParentModifieriAncestorModifier- po 0.5 pkt.TraverseByRevision- 1.5 pkt.
Repository Queries #
W tej części zadania będziemy chcieli uzyskać istotne informacje na temat naszego repozytorium:
- Który commit zawierał najwięcej zmienionych linii (dodania + usunięcia).
- Ile plików średnio zmieniają poszczególni autorzy na commit?
- Kto jest autorem największej liczby commit’ów?
- Który commit był pierwszym, który dodawał do repozytorium dany plik?
- Które pliki mają najwięcej współpracujących ze sobą autorów?
Zapytania są niezależne od siebie i można je realizować w dowolnej kolejności.
Szczegóły każdego z zapytań tzn. zwracane informacje i ich kolejność zostały umieszczone w komentarzach XML nad poszczególnymi metodami w klasie
RepositoryQueries. Dodatkowo, odpowiedzi poszczególnych zapytań dla przykładowego repozytorium zostały zawarte w plikuoutput.txt.
Punktacja #
Każde z zapytań jest warte 1.5 pkt. Za wszystkie zapytania można zatem otrzymać maksymalnie 7.5 pkt.
Przykładowe repozytorium #
Historię przykładowego repozytorium przedstawia poniższy diagram:
gitGraph
commit id: "c1"
commit id: "c2"
branch feature/users
checkout feature/users
commit id: "c3"
commit id: "c6"
commit id: "c7"
checkout main
commit id: "c4"
commit id: "c5"
merge feature/users id: "c8"
commit id: "c9"
commit id: "c10"
commit id: "c11"
commit id: "c12"Szczegółowe informacje o zawartości poszczególnych commit’ów znajdują się w pliku SampleRepository.cs.
Przykładowe rozwiązanie #