Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Git - mini-szkolenie [PL]

Git - mini-szkolenie [PL]

Prezentacja z wewnętrznego mini-szkolenia z Gita w j. polskim przeprowadzonego dla Digital Fingerprints.

Piotr Wittchen

December 29, 2021
Tweet

More Decks by Piotr Wittchen

Other Decks in Programming

Transcript

  1. Plan szkolenia • Dobre praktyki pracy z Gitem • Problemy

    podczas pracy z Gitem i rozwiązania • Pytania dotyczące pozostałych problemów z Gitem
  2. 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
  3. 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
  4. 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
  5. 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)
  6. 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
  7. Problem #1 zrobienie squasha do jednego commita na swoim branchu,

    a potem rabase do developa już z tylko tym 1 commitem
  8. 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
  9. 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
  10. Problem #2 - proponowane rozwiązanie # reprodukcja sytuacji git checkout

    develop git checkout -b p2.1 # dokonujemy zmian git commit git commit git checkout -b p2.2 # dokonujemy zmian git commit git commit # dalsze kroki git checkout p2.1 git rebase -i develop # wykonujemy squasha do 1 commita git checkout p2.2 git rebase -i develop # wykonujemy squasha do 1 commita git reset --soft HEAD~1 git stash git checkout p2.1 git stash pop git git add -A git commit # opcjonalnie robimy kolejny squash do 1 commita
  11. 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.
  12. Problem #3 - proponowane rozwiązanie # reprodukcja sytuacji git checkout

    develop git checkout -b p3.1 git commit git commit git rebase -i develop # wykonujemy squasha do 1 commita git commit git push # dalsze kroki git reflog # znajdujemy commit przed squashem git checkout <hash> # np. a24c840 git switch -c p3.2 # since git 2.23.0 git reset --hard HEAD~2 # wykonujemy nowe zmiany git commit git push # PR wykonujemy wtedy # z poziomu nowego brancha p3.2 # usuwamy starego brancha git branch -D p3.1 git push origin --delete p3.1
  13. 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)
  14. 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
  15. 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.
  16. 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.
  17. 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 <hash> 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
  18. 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.
  19. 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
  20. 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
  21. Problem #9 - proponowane rozwiązanie # ignorowanie plików lokalnie git

    update-index --assume-unchanged filename # zaprzestanie ignorowania plików lokalnie git update-index --no-assume-unchanged filename # wyświetlanie plików ignorowanych lokalnie git ls-files -v | grep “^[[:lower:]]”
  22. Problem #10 - proponowane rozwiązanie # resetowanie WSZYSTKICH niezacommitowanych lokalnych

    zmian git reset --hard # resetowanie jednego zmienionego/usuniętego niezestagowanego pliku git checkout HEAD -- /path/to/file # przywracanie usuniętego pliku git checkout $(git rev-list -n 1 HEAD -- /path/to/file)^ -- path/to/file