Dobre praktyki pracy z Gitem ● Git Flow ● Konfiguracja i aliasy ● GUI vs. terminal ● Treść wiadomości w commicie ● Wiele commitów w trakcie developmentu ● Squash commitów na branchu przed PR ● Przegląd własnych zmian przed commitem ● Nie nadpisywanie historii w głównych branchach (develop i master) ● Tagowanie wersji w odpowiedni sposób
Treść wiadomości w commicie Pamiętajmy, aby pisać dobre, szczegółowe i opisowe treści wiadomości w commitach. Pozwala to na łatwiejszą analizę logów, nawigowanie w historii, wyszukiwanie błędów i wycofywanie zmian. Zamiast komendy: git commit -m, możemy użyć: git commit. Linki: ● https://chris.beams.io/posts/git-commit/ ● https://robots.thoughtbot.com/5-useful-tips-for-a-better-commit-message ● http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html ● https://fatbusinessman.com/2019/my-favourite-git-commit ● https://github.com/torvalds/linux/pull/17 ● https://github.com/pwittchen/craplog
Squash commitów przed PR ● W trakcie developmentu możemy wykonywać wiele commitów do feature brancha ● Po zakończeniu developmentu, przed wystawieniem PR powinniśmy zesquashować wszystkie commity do jednego ● Pozwoli to zachować czystą i w miarę krótką historię oraz na łatwą nawigację po git logu i wycofywanie zmian w razie potrzeby ● Wyjątkiem od tej reguły jest sytuacja, w której wykonaliśmy wiele znaczących zmian w ramach jednego zadania i chcemy je wyraźnie rozdzielić osobnymi commitami
Nie nadpisywanie historii w głównych branchach ● Nie powinniśmy zmieniać historii (git push --force) na głównych branchach (develop i master) ● Nadpisywanie historii głównych branchy może spowodować rozjazd historii u wszystkich developerów i problemy z dalszą pracą ● Nadpisywanie historii głównych branchy może spowodować utratę kodu lub historii jego zmian ● Nadpisywanie historii głównych branchy powinno być zablokowane w konfiguracji repozytorium (np. z poziomu Bitbucketa)
Tagowanie wersji w odpowiedni sposób v1.2.3 major minor patch breaking changes (major releases) new features (keeping backward compatibility) bug fixes (keeping backward compatibility) 0.x.x Beta release (may be unstable) 1.x.x-RC1 Release Candidate (may be unstable) semantyczne wersjonowanie Jeśli to możliwe, powinniśmy automatyzować release do ECR/Artifactory w oparciu o tagi z Gita
Problem #1 - proponowane rozwiązanie git checkout p1 git rebase -i develop git add -A # otwierany jest edytor tekstu w terminalu p commit1 # p = pick s commit2 # s = squash s commit3 # zapis zmian # otwierany jest edytor tekstu w terminalu git commit git checkout develop git pull git checkout p1 git rebase develop
Problem #2 Co robić w sytuacji, jak mamy brancha z brancha, chcemy zesquashować zmiany z pierwszego brancha i połączyć je z drugim, ale tak, żeby się historia nie rozjechała
Problem #3 Sytuacja, gdzie mamy już wypchnięte zesquashowane zmiany, a okazało się, że trzeba cofnąć zmiany z dwóch ostatnich commitow i wprowadzić nowe zmiany.
Problem #4 Mamy developa lokalnie, na którym robimy jakieś zmiany i później chcemy aby te zmiany znalazły się na nowym branchu, którego tworzymy z developa (albo jest już utworzony)
Problem #4 - proponowane rozwiązanie # reprodukcja problemu git checkout develop # wykonujemy zmiany git commit git commit # dalsze kroki git reset --soft HEAD~2 # 2 = liczba commitów # do wycofania # alternatywna komenda względem powyższej git reset --soft origin/develop # powrót do stanu ze zdalnego developa # commity zostają wycofane # zmiany znajdują się w trybie staging # i nie są zacommitowane git checkout -b p4 git commit
Problem #5 W czasie pracy na branchu z taska weszło sporo nowych commitów do developa. Chcemy już wystawić PRkę i robimy git rebase -i develop. Wtedy musimy osobno poprawiać konflikty z każdym commitem, który różni nas od developa. Mniej więcej tak: ● rebase trwa do momentu pierwszego konfliktu ● naprawiamy konflikty, dajemy git rebase --continue ● pojawiają się kolejne konflikty, bardzo często zbliżone do tych, które już poprawiliśmy ● To powoduje, że możemy wielokrotnie poprawiać ten sam kod.
Problem #5 - proponowane rozwiązanie # reprodukcja problemu git checkout develop # wykonujemy zmiany git commit git commit git checkout p5 # wykonujemy zmiany # w tych samych miejscach # co na developie git rebase -i develop # lub: git rebase develop # dalsze kroki # rozwiązanie konfliktów # w poszczególnych plikach # w edytorze lub IntelliJ/PyCharm git add -A git rebase --continue # konflikty powinny być rozwiązywane # w jednym miejscu tylko raz pro-tip: sytuacje tego typu nie powinny występować lub powinny występować rzadko, jeśli zadania będą odpowiednio podzielone lub rozwiązane sekwencyjnie po kolei i dwie osoby (lub więcej) nie będą modyfikować tych samych plików lub funkcji w tym samym czasie. Warto o tym pamiętać podczas planowania pracy i komunikować się na bieżąco w trakcie developmentu.
Problem #6 - proponowane rozwiązanie # reprodukcja problemu git checkout develop # wykonujemy zmiany git commit git commit # w międzyczasie zostały # wykonane zmiany w tych samych # plikach w tym samym miejscu, # w którym my coś zmienialiśmy git pull # = git fetch + git merge # dalsze kroki git reflog # znajdujemy commita # z naszymi zmianami git checkout git checkout -b p6 # lub: git switch -c p6 git checkout develop git reset --hard origin/master # tworzymy nowy PR # z naszymi zmianami # na nowym branchu
Problem #7 - proponowane rozwiązanie Konflikty tego typu mogą wystąpić, gdy nadpisujemy historię za pomocą komend: git commit --amend lub git rebase w połączeniu z git pull Należy uważać, abyśmy nie mieli pomieszanej historii na zdalnych branchach z branchami lokalnymi, to wtedy powinniśmy uniknąć tego typu błędów.
Problem #8 - proponowane rozwiązanie git log --oneline --graph reference: https://stackoverflow.com/questions/2221658/whats-the-difference-between-head-and-head-in-git Ogólne zasady (rules of thumb): ● używamy ~ przez większość czasu do nawigacji wstecz ● używamy ^ na merge commitach, ponieważ mają dwóch lub więcej rodziców Mnemonika: ● tilde ~ liniowe występowanie i przejście wstecz w linii prostej ● caret ^ sugeruje interesujący segment drzewa lub fork tworzący wiele gałęzi
Problem #8 - proponowane rozwiązanie, c.d. G H I J \ / \ / D E F \ | / \ \ | / | \|/ / B C \ / \/ A A = A^0 B = A^ = A^1 = A~1 C = A^2 D = A^^ = A^1^1 = A~2 E = B^2 = A^^2 F = B^3 = A^^3 G = A^^^ = A^1^1^1 = A~3 H = D^2 = B^^2 = A^^^2 = A~2^2 I = F^ = B^3^ = A^^3^ J = F^2 = B^3^2 = A^^3^2 reference: https://stackoverflow.com/questions/2221658/whats-the-difference-between-head-and-head-in-git