Worktree - nowe drzewo gita
Mechanizm worktree w gicie

Wielu programistów używa gita do kontroli wersji. Stał się on już pewnego rodzaju standardem we współczesnym narzędziowniku dewelopera. Kiedy zabieramy się za nowe zadanie, zaczynamy od stworzenia brancha ze zmianami. Co jednak, gdy musimy przełączać się między zadaniami, a zmiana brancha jest dla nas zbyt mało wygodna? Z pomocą przychodzi worktree.
tl;dr
git worktreepozwala pracować na kilku branchach jednocześnie, każdy w osobnym folderze- Wszystkie foldery korzystają z jednego repozytorium, więc commity z jednego są od razu widoczne w pozostałych
- Nowy worktree tworzymy poleceniem
git worktree add, listę sprawdzamy przezgit worktree list, a usuwamy przezgit worktree remove - Jeden branch może być wybrany tylko w jednym worktree naraz
- Konfiguracja gita i hooki są współdzielone, a prywatne pliki można kopiować automatycznie hookiem
post-checkout - Przydaje się przy długich testach, osobnych buildach i równoległej pracy z agentami AI
Czym jest worktree?
Sytuacja jest prosta. Mamy swój folder z projektem i chcemy zacząć pracę nad nowym zadaniem. Tworzymy więc nowy branch i zaczynamy pracę. Uruchamiamy aplikację i gdy robota jest już rozgrzebana, manager prosi nas o sprawdzenie czegoś w międzyczasie.
Najczęściej w takim przypadku commitujemy zmiany (lub korzystamy z git stash), aby nie stracić swojej pracy, a następnie w tym samym folderze przełączamy się na nowy branch. Nie ma problemu, kiedy kosztuje nas to tylko zmianę brancha, ale jeśli w tym miejscu mamy już skompilowany projekt w odpowiedniej wersji, powrót do poprzedniej wersji może kosztować sporo nerwów.
Może się też zdarzyć, że na projekcie chcemy odpalić jakieś testy, które na bieżąco będziemy poprawiać. Jeśli trwają długo i działają w tle, mogą zablokować naszą pracę – chyba że zrobimy kopię całego folderu.
Nowe zadanie, nowy folder
Sprytniejsi stworzą nowy folder, z którego zaczną wprowadzać nowe zmiany. Jest to wygodne rozwiązanie, ponieważ oddziela od siebie foldery z pracą i umożliwia oddzielenie wygenerowanych przy budowaniu paczek od reszty pracy. Niestety taka kopia to w praktyce osobne repozytorium – commity zrobione w jednym folderze nie są widoczne w drugim bez dodania remote i git fetch, co utrudnia mergowanie pomiędzy branchami.
Tutaj z pomocą przychodzi worktree. Pozwala stworzyć nowy folder z osobnym branchem roboczym, ale zachowuje połączenie w systemie kontroli wersji. Możemy więc porównać swoje zmiany pomiędzy stworzonymi gałęziami, bez łączenia repozytoriów za pomocą dodatkowych remote.
Dodatkowo, jeśli usuniemy taki folder stworzony za pomocą worktree, rzeczy, które zostały zrobione na branchu, zostają w naszej historii (o ile je zacommitujemy).
Wątek AI
Warto też wspomnieć, że funkcjonalność ta jest przydatna do pracy z agentami AI. Możemy zlecić im pracę w pętli na wydzielonej gałęzi, dostarczając niezależną kopię programu. Jeśli potrzebujemy wywołać jakieś testy lub uruchomić program, nie będzie to kolidowało z innym środowiskiem, w którym pracujemy nad czymś innym. Warto tutaj uruchamiać program w kontenerze, żeby nie kolidowały też ze sobą porty urządzenia, ale to temat na inny wpis.
Co ciekawe, agenty, takie jak Claude Code, potrafią same tworzyć własne worktree, aby nie modyfikować naszego kodu. Z tego punktu widzenia nie jest to dla nich nic nowego.
Obsługa gita
Jak już wspomniałem wcześniej, użyjemy git worktree, aby stworzyć kopię naszego projektu w kolejnym folderze z innym branchem.
Nowy worktree
Zakładamy, że tworzymy katalog o nazwie project-feature1 i nowy branch dla niego branch/feature-1.
git worktree add -b branch/feature-1 ../project-feature1Możemy też pracować na wcześniej stworzonym branchu:
git worktree add ../project-feature1 branch/feature-1Lista worktree
Jeśli chcemy sprawdzić, jakie worktree istnieją i na jakich branchach pracują, wystarczy wpisać:
git worktree listUsuwanie worktree
Kiedy już zakończymy pracę nad naszym zadaniem i folder nie jest nam już potrzebny, możemy usunąć worktree.
git worktree remove ../project-feature1Może się także wydarzyć, że przez przypadek (lub z premedytacją) usuniemy folder z naszym featurem. Wtedy z pomocą przychodzi polecenie prune, które czyści metadane gita i dopasowuje je do stanu faktycznego.
git worktree pruneWspółdzielona konfiguracja
Niewątpliwą zaletą git worktree jest współdzielenie konfiguracji gita pomiędzy folderami. Wszystkie ustawienia dotyczące projektu w gicie są zatem widoczne we wszystkich worktree. Dotyczy to np. .git/info/exclude, który przetrzymuje listę plików niepodlegających wersjonowaniu.
Domyślnie te ustawienia są wspólne. Jeśli potrzebujemy ustawień tylko dla jednego worktree, możemy włączyć opcję extensions.worktreeConfig, a następnie zapisywać je poleceniem git config --worktree. Trafiają one wtedy do pliku .git/worktrees/<nazwa>/config.worktree i nie wpływają na pozostałe foldery. Nie dotyczy to jednak info/exclude, który zawsze jest wspólny.
git config extensions.worktreeConfig true
git config --worktree core.sparseCheckout trueMoże się jednak tak zdarzyć, że podczas pracy korzystamy z prywatnej konfiguracji, która nie jest częścią repozytorium (np. mamy własne pliki konfiguracyjne AI). git worktree add tworzy tylko pliki śledzone przez gita, więc takie pliki (np. .env czy lokalne konfiguracje) musimy w takim wypadku sami kopiować do nowo stworzonych folderów.
Można jednak ten proces łatwo zautomatyzować korzystając z hooków gita. W tym przypadku pomocny jest hook o nazwie post-checkout. Wywoływany jest za każdym razem po poprawnym procesie checkout.
#!/usr/bin/env bash
PREV_HEAD=$1
NEW_HEAD=$2
CHECKOUT_TYPE=$3
# Run only on `git worktree add` (null previous HEAD, branch checkout)
if [[ ! "$PREV_HEAD" =~ ^0+$ ]] || [[ "$CHECKOUT_TYPE" != "1" ]]; then
exit 0
fi
# My code after `git worktree add`Jeśli np. używamy konfiguracji dla stow (tak jak tutaj), możemy spróbować czegoś takiego:
#!/usr/bin/env bash
PREV_HEAD=$1
NEW_HEAD=$2
CHECKOUT_TYPE=$3
# Run only on `git worktree add` (null previous HEAD, branch checkout)
if [[ ! "$PREV_HEAD" =~ ^0+$ ]] || [[ "$CHECKOUT_TYPE" != "1" ]]; then
exit 0
fi
WORKTREE_PATH=$(pwd)
cd ~/configuration-folder
stow --target "${WORKTREE_PATH}" configuration_variantPlik zapisujemy w głównym repozytorium jako .git/hooks/post-checkout – hooki są wspólne dla wszystkich worktree. Nie zapomnijmy o dodaniu praw do uruchomienia za pomocą polecenia:
chmod +x .git/hooks/post-checkoutPo co mi to
A teraz kilka sytuacji, w których może nam się przydać git worktree:
- Rozdzielenie zadań, w których mamy tymczasowe raporty niepodlegające wersjonowaniu
- Budowanie aplikacji dla różnych wersji środowiska, żeby nie nadpisywać builda
- Dłuższe testy, nieblokujące głównej gałęzi kodu
- Praca z agentami nad kilkoma zadaniami jednocześnie, szczególnie gdy zlecamy im dłuższą pracę
- Robienie prezentacji kodu, gdy nie chcemy nadpisywać swoich ustawień w trakcie pracy
Podsumowanie
Jak widzimy, git worktree to ciekawe rozwiązanie do rozłączenia pracy nad różnymi zadaniami w kodzie. Pozwala nie tylko stworzyć oddzielne, tymczasowe środowiska pracy, ale wszystkie mają wspólną historię i branche – commit zrobiony w jednym folderze od razu widać w pozostałych. Dzięki temu możliwe jest łatwe przeglądanie historii całego gita z dowolnej z gałęzi.
Sprawdzi się to więc idealnie do pracy z AI oraz z wieloma zadaniami równocześnie. Warto też połączyć to z innymi technologiami, takimi jak konteneryzacja czy tmux do oddzielenia terminala. Myślę, że to jedna z tych funkcjonalności, które przydadzą się w coraz szybciej zmieniającej się pracy programisty.
Bibliografia
- https://git-scm.com/docs/git-worktree - Oficjalna dokumentacja gita z opisem polecenia