Minimalny realny Git do programowania opartego na trunkingu
Mniej znaczy więcej, jeśli chodzi o potężne — i zbyt skomplikowane — narzędzie, takie jak Git
Eli jest współzałożycielem i współzałożycielem trunk.io . Zajmował kierownicze stanowiska inżynieryjne w firmach Microsoft, Uber i Google (które przejęło start-up, którego był współzałożycielem). Jego głównym celem jest pomaganie zespołom w szybszym tworzeniu lepszego oprogramowania.

Programowanie w środowisku współpracy jest skomplikowaną operacją, która obejmuje śledzenie i koordynację pracy wielu programistów. Wielu z nas używa Git do kontroli wersji, aby mieć kontrolę nad wszystkimi tymi zmianami. Ale według własnych słów Linusa Git to „menedżer informacji z piekła rodem”. Oferuje tak wiele opcji i dziwnych stanów, że możesz łatwo znaleźć się w złym miejscu do punktu, w którym musisz ponownie sklonować od zera — nawet jeśli nie zrobiłeś nic złego.
Wielu z nas wie, że jeden programista, który przestudiował specyfikację Git refspec i leżące u jej podstaw drzewo Merkle, na którym jest oparta, ale bycie znawcą Git prawdopodobnie nie jest w twoim opisie stanowiska. Mogę doskonale prowadzić samochód, nie rozumiejąc, jak właściwie działa skrzynia biegów, a ostatecznie Git jest tylko środkiem do celu.
Aby w pełni wykorzystać Git, musisz używać go jak najmniej , jeśli chodzi o programowanie oparte na trunkingu. Ogranicz używane polecenia, odpowiednio aktualizuj gałąź funkcji i ustandaryzuj użycie jako zespół. Indywidualnie możesz cieszyć się swobodą robienia wszystkiego, na co masz ochotę. Ale jako zespół cenę za wolność płaci się w tarciach.
Ogranicz swoje działania Git
Najłatwiejszą praktyką do wdrożenia w celu uzyskania maksymalnej wydajności Git jest trzymanie się podzbioru poleceń. Git może robić wiele rzeczy, ale zdecydowana większość jego mocy istnieje tylko po to, by pomóc Ci wydostać się z okropnych sytuacji. Liczba poleceń, na które pozwala Git, a liczba poleceń, które powinieneś kiedykolwiek wywołać, jest zupełnie inna.
Zostałem kiedyś poproszony przez CTO o poprowadzenie zajęć z zaawansowanego git dla jego zespołu inżynierów. Moja odpowiedź była prosta:
do czasu, gdy potrzebujesz zaawansowanego git — koła już zsunęły się z samochodu. zamiast tego skupmy się na poprawnym używaniu core git.
Oto 10 poleceń git, których używam, aby wykonać swoją pracę, jednocześnie minimalizując liczbę lądowań w piekle Git:
Zarządzanie oddziałem
git checkout -t -b {branch-name}
Utwórz gałąź, ustaw upstream na bieżącą gałąź i przełącz się na nią. Możesz to również zrobić za pomocą polecenia `git branch`, ale wymaga to wielu poleceń. Ponieważ zawsze chcę przełączyć się do gałęzi podczas jej tworzenia, to polecenie jest bardziej zwięzłe. Utwórz i wyewidencjonuj oddział za jednym zamachem. Zwykle nazywam wszystkie moje gałęzie eli/{work-being-done}
.
git checkout {branch-name}
Przełącz się na oddział.
git branch -vv
Zobacz swoje obecne oddziały i ich upstreamy.
git branch -D {branch-name}
Usuń lokalną gałąź, zwykle w celu wyczyszczenia nieaktualnych/połączonych z głównymi gałęziami.
Pchanie i ciągnięcie
git fetch origin main:main
Przeciągnij pilota main
do swojego lokalnego main
, nawet jeśli nie jesteś aktualnie w main
oddziale! To jest ; bardzo przydatne, gdy próbujesz połączyć najnowsze main
w branży PR.
git push origin
Wciśnij mój kod do pilota; prawdopodobnie uruchomię wiele maszyn w CI, aby sprawdzić moją pracę.
git pull
Zaktualizuj mój lokalny oddział za pomocą pilota o tej samej nazwie.
Zobowiązanie się
git add -A .
Dodaj wszystko, nad czym pracuję (nowe i edytowane pliki).
git commit -am "work"
Zobacz tutaj, aby zapoznać się z moimi przemyśleniami na temat lokalnych komunikatów dotyczących zatwierdzeń
git merge main
Dużo więcej na ten temat poniżej. Jestem pragmatykiem, a nie rebaserem i cieszę się, że mogę zabłocić mój lokalny oddział wydarzeniami z main
.
Zadbaj o odpowiednią aktualność gałęzi funkcji
W moim ostatnim artykule omówiłem, jak zarządzać kodem lądowania na main
. Ale często podczas tworzenia, a na pewno przed połączeniem, musisz być na bieżąco z najnowszymi zmianami, main
ponieważ:
- Potrzebujesz jakiejś funkcji, która wylądowała,
main
aby wykonać swoją funkcję. - Coś się
main
zmieniło i jeśli tego nie odbierzesz, złamiesz to, próbując wylądować swój kod. - Chcesz po prostu zachować świeżość, ponieważ jesteś inżynierem i kochasz błyszczące, nowe rzeczy.
Drzwi 1: git merge
To jest moja strategia fuzji. Jest prosty, niezawodny i bez zbędnych dodatków. Podstawowy git merge
bierze wszystkie zmiany, które zaszły w main
i łączy je razem jako zatwierdzenia wraz ze zmianami. Możesz czytać do znudzenia o wadach tego bezproblemowego rozwiązania, ale szczerze tylko wzruszam ramionami i mówię „nie obchodzi mnie to”. Jeśli zgnieciesz swoje gałęzie pracy main
, wtedy cały ten nonsens dotyczący zanieczyszczenia git-log stanie się właśnie tym.
Szczerze mówiąc, nie ma znaczenia, jak/dlaczego/co pojawiło się w mojej gałęzi pracy. Liczy się — czy to się buduje, czy działa i czy kod robi to, co chcę, żeby robił? Reszta jest akademicka.
W praktyce, a jestem bardzo praktycznym inżynierem, git merge
pozwala na czyste radzenie sobie z konfliktami scalania i powrót do budowania. Wszystkie konflikty scalania rozwiązujesz raz — i możesz zaczynać. To najlepsza opcja dla początkujących, zapracowanych zaawansowanych użytkowników i tych z was, którzy nie marzą o zostaniu guru Git.
Drzwi 2: git rebase
To zdecydowanie najgorsza opcja. Okay wiem; jest praktycznie całe pokolenie, które widzi rebase vs scalanie czegoś takiego jak tabulatory vs spacje. Ale w „prawdziwych” projektach, w których jeden lub wiele zespołów codziennie łączy się w repozytorium, rebase wymaga rozwiązania n konfliktów scalania zamiast jednego z rebase scalania lub squasha (więcej na ten temat poniżej).
Dodatkowo każda zmiana bazowania powoduje przepisanie historii Git, a to oznacza, że jeśli twoja gałąź ma zdalny odpowiednik, musisz git push --force
podbić jej historię nową historią zmiany bazy. Teoretycznie jest to w porządku, ale może przypadkowo usunąć poprzednią pracę w sposób, który jest niezwykle trudny do odzyskania. Jest to po prostu o wiele bardziej niebezpieczne niż łączenie i szczerze mówiąc przyprawia mnie o ból głowy na samą myśl o tym.
Na potrzeby tego artykułu próbowałem zmienić bazę, aby przypomnieć sobie, jak bardzo nie lubię tego przepływu pracy. Zobacz, jakie proste polecenie rebase wypluwa do mojego terminala:

Z tym terminalem jest bardzo źle. Dostaję notatki o skrótach zatwierdzeń — nie chcę ich widzieć. Dostaję cztery linie żółtych podpowiedzi, które przypominają mi, jak to wszystko działa. A teraz jestem w jakimś przepływie pracy, aby pobrać kod z main
mojego oddziału. Drzewo decyzyjne jest głupio skomplikowane; git rebase --skip
- co to jest?! Dlaczego pominięcie zatwierdzenia miałoby być w porządku? Wiesz co? Nie mów mi, bo mam pracę do wykonania.
Drzwi 3: git squash rebase
Zdecydowanie nie chcesz tego, co jest za drzwiami nr 2, ale niektórzy ludzie są zagorzałymi rebaserami. Mój współzałożyciel, współzałożyciel, David Apirian, pokazał mi swoje tajne wejście za kulisy do rebase i jest to trochę niesamowite. Nazwiemy to rebase squasha .
Powtórzyłem, jak ważna jest prostota Gita, ale dzięki takiemu podejściu możesz mieć swój rebase i też go zjeść. Otrzymujesz magiczne nawarstwianie rebase bez szaleństwa standardowego przepływu rebase. Dzięki rebazie do squasha możesz:
- Zobacz różnicę swoich lokalnych zatwierdzeń przed przekazaniem do GitHub.
- Łatwo cofnij swoje ostatnie zatwierdzenie.
- Unikaj zanieczyszczania widoku żądania ściągnięcia w GitHub za pomocą mnóstwa małych lokalnych zatwierdzeń.
Krok 1 — zmiażdż wszystkie lokalne zobowiązania:
git reset --soft $(git merge-base HEAD main) &&
git commit -am "" --allow-empty-message
git rebase main
Możesz połączyć to wszystko w jedno polecenie uber, aby pobrać najnowszą wersję główną, zmiażdżyć wszystkie swoje zatwierdzenia i oprzeć się na najnowszej głównej:
git reset --soft $(git merge-base HEAD main) &&
git commit -am "" --allow-empty-message &&
git fetch origin main:main &&
git rebase main
[alias]
smartcommit = !git add -A . && git commit -am "" --allow-empty-message
squash = !git reset --soft $(git merge-base HEAD main) && git commit -am "" --allow-empty-message
squashrebase = !git squash && git fetch origin main:main && git rebase main
smart = !git smartcommit && git squashrebase
Jednym z fajniejszych aspektów ciągłego zgniatania lokalnych zobowiązań jest to, że na szczycie git log
znajduje się cała praca, którą wykonałeś w oddziale. Twoja lokalna praca teraz bardziej przypomina to, jak będzie wyglądał PR połączony z squashem, kiedy wyląduje w main
.
W dowolnym momencie możesz wyskoczyć z ostatniego zatwierdzenia HEAD w lokalnym oddziale i zobaczyć całą pracę, którą wykonałeś main
. Jest to podobne do szczegółowego widoku tego, co zmieniło się w GitHub, ale pozwala pozostać w terminalu i uniknąć mylącego przełączania między dwoma różnymi interfejsami użytkownika.
Muszę przyznać, że kiedy David po raz pierwszy pokazał mi ten przepływ pracy, byłem pod wrażeniem. W większości zabił rebase
smoka, a możliwość lokalnego przeglądania wszystkich plików, które zmieniłeś w swoim oddziale, jest super fajna.
Podczas gdy rebasing squasha pomaga utrzymać lepszy stan płynności, przeważająca większość programistów lepiej po prostu scala. Nieprawidłowe użycie rebase lub squash rebase powoduje tarcie, które zabija produktywność.
Pułapki pomocy w gałęzi GitHub
GitHub oferuje to ustawienie ☝️, aby zasugerować main
automatyczne połączenie z Twoją gałęzią, jeśli jest nieaktualna. To ustawienie często przynosi efekt przeciwny do zamierzonego. Chcę, aby mój lokalny oddział był jedynym źródłem prawdy dla mojego PR i nikt inny (w tym GitHub) nie powinien na to naciskać. Praca z GitHubem w moim osobistym oddziale powinna być ulicą jednokierunkową. Koduję lokalnie i naciskam.
Jeśli skonfigurujesz GitHub, aby zaczął przesyłać aktualizacje do gałęzi roboczej, prosisz o ruch w złym kierunku. I to jest przepis na ból.
Ten problem pojawia się również wtedy, gdy recenzenci kodu pozostawiają niewiele sugestii dotyczących zatwierdzenia w żądaniu ściągnięcia, a ty beztrosko je stosujesz. Jeśli następnie wykonasz jakąkolwiek pracę w lokalnym oddziale przed wyciągnięciem tych zmian… otworzyłeś mały worek bólu.
Często odkrywam, że przypadkowo to sobie zrobiłem, kiedy próbuję przesłać moje aktualizacje z powrotem do pilota, a git mówi mi, że nie jestem zsynchronizowany. W tym momencie mam dwie opcje:
git push --force
i zdmuchnij wszystko na pilocie, a nie w lokalnym oddziale. Ogólnie rzecz biorąc, wszystko, co ma słowo siła, jest destrukcyjne i zniechęca.git merge origin/{my-work-branch}
aby scalić zdalne zmiany w lokalnym widoku oddziału.
To ustawienie GitHub ️☝️ wymaga, aby PR-y były aktualne main
przed scaleniem. To generalnie „rozwiązuje” możliwość zepsucia main
; jednak działa tylko z repozytoriami o bardzo niskiej prędkości. Gdy garstka programistów spróbuje połączyć wiele PR dziennie, kończy się to wyścigiem o fuzję main
— lub w inny sposób stale przeprowadzają scalanie/rebase main
w swojej gałęzi, aby znów być „na bieżąco” i po prostu ścigają się ze współpracownikami być pierwszym, który się połączy. Bolesny. Zazwyczaj, jeśli potrzebujesz tej funkcji, ale nie jesteś programistą solo, prawdopodobnie zamiast tego potrzebujesz kolejki scalającej — która jest skalowalnym sposobem na osiągnięcie nieuszkodzonego głównego. To jest coś, co uznaliśmy za wystarczająco ważne, aby stworzyć produkt z Trunk Merge .
Zminimalizuj Git, aby uzyskać maksymalną wartość
Utrzymując korzystanie z Git tak prosto, jak to możliwe, dajesz programistom mniej liny, na której mogą się zawiesić, i więcej czasu na wykonanie pracy programistycznej, która naprawdę ma znaczenie. Spraw, aby Git odgrywał drugoplanową rolę, a nie był głównym bohaterem. Aby utrzymać rozsądne i oparte na współpracy środowisko programistyczne, pamiętaj o:
- Wybierz najmniejszą powierzchnię Gita do użycia i trzymaj się jej.
- Zawsze zarządzaj zmianami nadrzędnymi w ten sam sposób.
- Ustandaryzuj praktyki w całym zespole, aby Twoi współpracownicy również nie wypadli z torów.