1/52
System kontroli wersji – git

Wyczerpujący przewodnik po poleceniach CLI

Opanuj terminal i stań się profesjonalistą w zarządzaniu kodem.

  • Dlaczego git jest standardem w branży IT?
  • Głębokie zrozumienie mechanizmów śledzenia zmian.
  • Praktyczne przykłady użycia 23 najważniejszych poleceń.

Git stał się standardem w branży programistycznej głównie dzięki swojej wydajności i elastyczności w zarządzaniu nawet bardzo dużymi projektami. Jego rozproszona architektura pozwala każdemu programiście pracować w pełni offline, co znacznąco przyspiesza codzienną pracę. Opanowanie podstawowych 23 komend umożliwia sprawne poruszanie się po historii projektu i współpracę z innymi członkami zespołu. System śledzenia zmian w git pozwala precyzyjnie określić kto, kiedy i dlaczego wprowadził konkretną modyfikację.

W przeciwieństwie do starszych systemów CVS czy SVN, git nie wymaga stałego połączenia z centralnym serwerem do wykonywania typowych operacji. Każda kopia repozytorium zawiera pełną historię projektu, co eliminuje pojedynczy punkt awarii. Dzięki mechanizmowi gałęzi (branchy) i możliwości łatwego łączenia zmian, git doskonale sprawdza się w zespołach liczących od kilku do kilkuset programistów. Znajomość gita jest dziś praktycznie obowiązkowym wymaganiem na każdym stanowisku developerskim.

2/52
Czym jest program git?
  • Rozproszony system kontroli wersji: Oprogramowanie służące do śledzenia historii zmian w plikach tekstowych (głównie kodzie źródłowym).
  • Autor: Stworzony w 2005 roku przez Linusa Torvaldsa na potrzeby jądra Linux.
  • Cechy kluczowe: Szybkość działania, prostota projektu, silne wsparcie dla nieliniowego rozwoju (tysiące równoległych gałęzi).
  • Lokalne działanie: Prawie każda operacja jest wykonywana na Twoim komputerze, bez potrzeby łączenia się z serwerem.

Git to nie tylko narzędzie, ale cały system zarządzania wersją zaprojektowany z myślą o wydajności i niezawodności. Jego rozproszona natura oznacza, że każda kopia repozytorium zawiera pełną historię projektu, co eliminuje ryzyko związane z awarią centralnego serwera. Dzięki lokalnym operacjom większość czynności, takich jak przeglądanie historii czy zatwierdzanie zmian, działa błyskawicznie bez potrzeby połączenia sieciowego.

Praktyczną konsekwencją tego projektu jest możliwość wygodnej pracy offline, na przykład podczas podróży lub w środowiskach o ograniczonym dostępie do internetu. Model rozproszony ułatwia również eksperymentowanie – można tworzyć dowolną liczbę gałęzi lokalnie i synchronizować je dopiero po osiągnięciu stabilnego stanu. Współpraca w zespole staje się elastyczniejsza, ponieważ każdy programista może pracować we własnym tempie, a zmiany scalać w dogodnym momencie.

3/52
Po co nam git?
  • Bezpieczeństwo: Możliwość powrotu do dowolnej wersji projektu z przeszłości.
  • Współpraca: Pozwala wielu programistom pracować nad tym samym plikiem jednocześnie bez nadpisywania swojej pracy.
  • Eksperymentowanie: Tworzenie gałęzi pozwala na testowanie nowych funkcji w izolacji od stabilnej wersji kodu.
  • Dokumentacja zmian: Każdy zapisany stan (commit) zawiera informację o tym, co dokładnie zostało zmienione i dlaczego.

Bezpieczeństwo kodu to jeden z najważniejszych powodów używania gita – każda zmiana zostaje trwale zapisana i można do niej wrócić w dowolnym momencie. Nawet przypadkowe usunięcie pliku czy całego katalogu nie stanowi problemu, ponieważ git przechowuje wszystkie poprzednie wersje w swoim lokalnym repozytorium. Mechanizm gałęzi umożliwia eksperymentowanie z nowymi funkcjonalnościami bez ryzyka naruszenia stabilnej wersji projektu. W razie niepowodzenia wystarczy usunąć gałąź, a główna linia kodu pozostaje nietknięta.

Współpraca w zespole staje się znacznie prostsza dzięki precyzyjnemu śledzeniu autorstwa każdej zmiany. W historii projektu dokładnie widać kto dodał daną funkcję, kto poprawił błąd i dlaczego podjął taką decyzję. Komunikaty commitów pełnią funkcję szczegółowej dokumentacji technicznej całego procesu rozwoju aplikacji. Dodatkowo możliwość pracy równoległej na różnych gałęziach eliminuje blokowanie się członków zespołu na tych samych plikach.

4/52
Architektura: model rozproszony
  • W przeciwieństwie do systemów centralnych (jak SVN), git nie polega na jednym serwerze.
  • Każdy programista posiada na swoim dysku pełną historię projektu.
  • Repozytorium na GitHubie to po prostu kolejna kopia (klon), która służy jako punkt synchronizacji.
  • Awaria głównego serwera nie powoduje utraty historii ani przestoju w pracy.

W modelu rozproszonym gita każdy developer posiada pełnoprawną kopię repozytorium, a nie tylko migawkę bieżącego stanu plików. Oznacza to, że operacje takie jak git log, git diff czy git commit są wykonywane lokalnie, bez żadnego zapytania sieciowego. W przeciwieństwie do systemów scentralizowanych, awaria serwera zdalnego nie blokuje codziennej pracy – cała historia jest bezpiecznie przechowywana na dysku każdego członka zespołu.

W praktyce model ten usprawnia pracę rozproszonych zespołów programistycznych. Deweloperzy mogą zatwierdzać zmiany, tworzyć gałęzie i przeglądać historię bez stałego połączenia z serwerem. Synchronizacja następuje wtedy, gdy jest to wygodne – za pomocą komend git push i git pull. Wiele popularnych platform, takich jak GitHub, GitLab czy Bitbucket, buduje właśnie na tym fundamencie, dodając warstwę współpracy w postaci Pull Requestów i Code Review.

5/52
Kluczowe pojęcie: repozytorium
  • Repozytorium (repo) to katalog projektu, który jest śledzony przez git.
  • Głównym elementem jest ukryty folder .git, w którym przechowywana jest cała baza danych zmian.
  • Użytkownik pracuje w tzw. "obszarze roboczym" (working tree), widząc aktualne wersje plików.
  • Nigdy nie należy ręcznie modyfikować zawartości folderu .git.

Repozytorium w git to po prostu zwykły katalog projektu, w którym znajduje się ukryty podkatalog o nazwie .git. Ten mały folder stanowi całą bazę danych gita – przechowuje wszystkie commity, gałęzie, tagi i konfigurację projektu. Bez folderu .git zwykły katalog nie jest rozpoznawany przez gita i nie podlega żADnemu śledzeniu zmian. Dlatego przy kopiowaniu projektu należy uważać, czy chcemy skopiować tylko pliki źródłowe, czy całe repozytorium wraz z historią.

Baza danych w katalogu .git używa kilku typów obiektów do efektywnego przechowywania informacji o projekcie. Obiekty blob przechowują zawartość plików, drzewa (tree) reprezentują strukturę katalogów, a commity łączą wszystko w spójną historię. Git dodatkowo kompresuje starsze obiekty w pliki pack, aby oszczędzać miejsce na dysku. Dzięki temu nawet bardzo duże projekty z długą historią zajmują relatywnie mało miejsca w porównaniu do przechowywania wielu kopii plików.

6/52
Kluczowe pojęcie: commit jako migawka
  • Commit nie jest listą zmian (diff), lecz migawką (snapshot) wszystkich plików w danym momencie.
  • Jeśli plik nie zmienił się w danym commicie, git po prostu przechowuje odniesienie do poprzedniej wersji.
  • Każdy commit posiada unikalny identyfikator – hash SHA-1 (np. ac73b9...).
  • Commity układają się w strukturę drzewa, tworząc historię projektu.

Każdy commit w gicie jest pełną migawką wszystkich plików w repozytorium w danym momencie, a nie tylko zestawem zmian względem poprzedniej wersji. Dzięki takiemu podejściu przełączanie się między commitami, gałęziami czy przywracanie starych wersji jest niezwykle szybkie, ponieważ git nie musi odtwarzać stanu przez sekwencyjne aplikowanie różnic. Co więcej, każdy commit identyfikowany jest unikalnym hashem SHA-1, co gwarantuje integralność danych i odporność na przypadkowe modyfikacje.

Z praktycznego punktu widzenia oznacza to, że operacje takie jak git checkout, git revert czy git bisect działają natychmiastowo i niezawodnie. Deweloperzy mogą bez obaw eksperymentować – nawet po serii nieudanych zmian wystarczy przejść do dowolnego wcześniejszego commitu, by uzyskać dokładnie ten sam stan plików. Warto pamiętać, że choć przechowywanie migawek mogłoby sugerować ogromne rozmiary repozytoriów, git optymalizuje przechowywanie poprzez deduplikację i kompresję obiektów.

7/52
Kluczowe pojęcie: trzy stany pliku
  • Modified (Zmodyfikowany): Plik został zmieniony, ale zmiana nie została jeszcze zapisana w bazie.
  • Staged (Oczekujący / Indeks): Zmodyfikowany plik został oznaczony do włączenia do następnego commita.
  • Committed (Zatwierdzony): Dane są bezpiecznie przechowywane w lokalnym repozytorium.

Git definiuje trzy główne stany, w jakich może znajdować się plik w repozytorium: zmodyfikowany (modified), przeznaczony do zatwierdzenia (staged) oraz zatwierdzony (committed). Stan zmodyfikowany oznacza, że plik został zmieniony, ale zmiany nie zostały jeszcze oznaczone do zapisania w historii. Przeznaczenie pliku do zatwierdzenia (staging) pozwala precyzyjnie wybrać, które zmiany trafią do następnego commita. Zatwierdzenie (commit) trwale zapisuje migawkę pliku w bazie danych gita.

Strefa przejściowa, zwana staging area lub indeksem, to unikalna cecha gita odróżniająca go od innych systemów kontroli wersji. Pozwala ona na selektywne dodawanie fragmentów zmian z jednego pliku do różnych commitów, co jest niezwykle przydatne przy porządkowaniu historii. Typowy przepływ pracy to edycja plików w katalogu roboczym, dodanie wybranych zmian do strefy przejściowej za pomocą git add, a następnie utworzenie commita z git commit. Git sam zajmuje się synchronizacją stanów między tymi trzema obszarami.

8/52
Rozpoczynanie pracy

Tworzenie i kopiowanie repozytoriów

Rozpoczynając pracę z gitem, pierwszym wyborem jest decyzja między założeniem nowego repozytorium a sklonowaniem już istniejącego. Komenda git init tworzy świeże repozytorium od podstaw w bieżącym katalogu, natomiast git clone pobiera całą historię projektu wraz ze wszystkimi gałęziami z istniejącego zdalnego źródła. Ta sekcja poprowadzi cię przez oba scenariusze, wyjaśniając kiedy i jak je stosować w codziennej pracy.

W praktyce git clone jest najczęściej używany przy dołączaniu do istniejących projektów – wystarczy jeden adres URL, by otrzymać w pełni funkcjonalną kopię kodu z całą historią zmian. Z kolei git init sprawdza się doskonale przy rozpoczynaniu nowego projektu od zera, pozwalając od samego początku śledzić każdy etap rozwoju. Niezależnie od wybranej ścieżki, pamiętaj o skonfigurowaniu podstawowych danych użytkownika (git config), ponieważ każdy commit jest podpisywany twoim imieniem i adresem email.

9/52
git init – tworzenie repozytorium

Polecenie git init to fundament pracy z gitem. Służy do przekształcenia zwykłego katalogu w projekt śledzony przez system kontroli wersji. Inicjalizacja tworzy niewidoczny folder .git, w którym przechowywana jest cała baza danych, konfiguracja i historia zmian. Możesz użyć tego polecenia nawet w folderze, który już zawiera pliki – git nie usunie ich, lecz pozwoli zacząć je śledzić.

  • Gdzie go używać? W głównym (korzennym) katalogu Twojego projektu.
  • Co się zmienia? Pojawia się folder .git. Od tego momentu możesz używać innych poleceń gita.
  • Wielokrotne użycie: Uruchomienie git init w istniejącym repozytorium jest bezpieczne; służy ono wtedy do zresetowania konfiguracji lub dodania nowych ustawień (np. innej nazwy gałęzi domyślnej).
# Przykład 1: Inicjalizacja w bieżącym folderze roboczym user@dev:~/my-project$ git init Initialized empty git repository in /home/user/my-project/.git/ # Przykład 2: Inicjalizacja z wymuszeniem nazwy głównej gałęzi na 'main' user@dev:~$ git init --initial-branch=main nowy_projekt Initialized empty git repository in /home/user/nowy_projekt/.git/

Polecenie git init tworzy nowe repozytorium poprzez zainicjowanie struktury katalogu .git w bieżącym folderze projektu. Po wykonaniu tej komendy w katalogu pojawia się ukryty podfolder .git, który zawiera wszystkie elementy niezbędne do śledzenia zmian. Na tym etapie repozytorium jest puste – nie ma jeszcze żadnych commitów ani gałęzi, ale wszystkie pliki w katalogu są gotowe do dodania i śledzenia. Warto od razu utworzyć plik .gitignore, aby wykluczyć z śledzenia pliki tymczasowe i binaria.

Zanim git init faktycznie rozpocznie śledzenie plików, konieczne jest dodanie ich do indeksu za pomocą git add i wykonanie pierwszego commita. Standardową praktyką jest utworzenie pierwszego commita z komunikatem "Initial commit" zawierającego podstawowe pliki projektu. Po pierwszym commicie git automatycznie tworzy domyślną gałąź (zwykle nazwaną master lub main), na której będą zapisywane kolejne zmiany. Od tego momentu wszystkie standardowe mechanizmy gita – gałęzie, tagi, mergowanie – są w pełni dostępne.

10/52
git init – zaawansowane użycie
  • Możliwość re-inicjalizacji istniejącego repozytorium w celu naprawy konfiguracji.
  • Opcja --bare do tworzenia repozytoriów serwerowych (bez obszaru roboczego).
# Przykład 3: Tworzenie repozytorium typu "bare" (serwerowego) user@server:~$ git init --bare projekt.git Initialized empty git repository in /home/user/projekt.git/

Podstawowe użycie git init tworzy repozytorium w bieżącym katalogu, ale narzędzie oferuje znacznie więcej możliwości. Flaga --bare tworzy repozytorium bez katalogu roboczego, przeznaczone wyłącznie do przechowywania historii i współdzielenia zmian – standardowe rozwiązanie na serwerach. Opcja --initial-branch pozwala określić domyślną nazwę gałęzi podczas inicjalizacji, co jest szczególnie przydatne w zespołach, które przyjęły konwencję inną niż tradycyjny master.

Flaga --template umożliwia skopiowanie niestandardowych plików konfiguracyjnych lub hooków do nowo tworzonego repozytorium, co ułatwia narzucenie jednolitych standardów w całej organizacji. Git init można bezpiecznie wykonać wielokrotnie w tym samym katalogu – nie zniszczy to istniejących danych ani konfiguracji, a jedynie odświeży ewentualnie brakujące pliki wewnętrzne. Zaawansowani użytkownicy często łączą te opcje, tworząc skrypty automatyzujące proces inicjalizacji nowych repozytoriów zgodnie z polityką firmy.

11/52
git clone – klonowanie projektu

Polecenie git clone służy do pobierania kompletnej kopii istniejącego repozytorium, najczęściej z platformy takiej jak GitHub czy gitLab. W przeciwieństwie do zwykłego pobrania plików (ZIP), klon zawiera całą historię zmian od pierwszego dnia istnienia projektu. Automatycznie tworzy również powiązanie (remote) o nazwie origin, co ułatwia późniejszą synchronizację kodu.

  • Pełna historia: Masz dostęp do każdego commita i każdej gałęzi autora projektu.
  • Zdalne źródło origin: git wie, skąd pobrano projekt i gdzie wysyłać ewentualne poprawki.
  • Protokoły: Możesz klonować przez HTTPS (wygodne, nie wymaga kluczy) lub SSH (bezpieczniejsze, wymaga konfiguracji klucza publicznego).
# Przykład 1: Klasyczne klonowanie przez HTTPS user@dev:~$ git clone https://github.com/user/project.git Cloning into 'project'... remote: Counting objects: 100% (450/450), done. Receiving objects: 100% (450/450), 1.2 MiB | 5 MB/s, done. # Przykład 2: Klonowanie do folderu o innej nazwie user@dev:~$ git clone https://github.com/user/project.git moje_testy

Polecenie git clone pozwala na stworzenie lokalnej kopii zdalnego repozytorium wraz z całą jego historią. Podczas klonowania Git pobiera wszystkie gałęzie oraz obiekty, dzięki czemu od razu mamy dostęp do pełnego projektu. Domyślnie zdalne repozytorium jest automatycznie dodawane jako origin, co ułatwia późniejsze wysyłanie i pobieranie zmian.

Połączenie z serwerem może odbywać się poprzez protokół HTTPS lub SSH. HTTPS jest prostszy w konfiguracji i wymaga jedynie podania hasła lub tokena dostępu. SSH wymaga wygenerowania pary kluczy, ale po skonfigurowaniu pozwala na bezpieczne uwierzytelnianie bez konieczności każdorazowego wpisywania hasła. Wybór protokołu zależy głównie od polityki bezpieczeństwa oraz preferencji zespołu.

12/52
git clone – opcje klonowania
  • Klonowanie z ograniczoną historią (shallow clone) oszczędza miejsce i czas.
  • Klonowanie rekurencyjne pobiera również submoduły.
# Przykład 3: Pobranie tylko ostatniego commita (--depth 1) user@dev:~$ git clone --depth 1 https://github.com/torvalds/linux.git # Przykład 4: Klonowanie z submodułami user@dev:~$ git clone --recurse-submodules adres_repozytorium

Opcja --depth umożliwia wykonanie płytkiego klonowania, które pobiera tylko określoną liczbę ostatnich commitów. Jest to przydatne gdy interesuje nas wyłącznie bieżący stan projektu, a dostęp do pełnej historii nie jest potrzebny. Płytkie klonowanie znacznąco skraca czas pobierania oraz zmniejsza zużycie miejsca na dysku.

Flaga --recurse-submodules automatycznie inicjalizuje i aktualizuje wszystkie podobręcze (submodules) podczas klonowania. Dzięki temu unikamy ręcznego wykonywania komend git submodule init i git submodule update. W projektach wykorzystujących zależności w postaci podobręczy jest to standardowa praktyka, która zapewnia spójność i powtarzalność konfiguracji środowiska.

13/52
Praca ze zmianami

Dodawanie, przenoszenie i usuwanie plików

Dodawanie plików do repozytorium to pierwszy krok w procesie śledzenia zmian w projekcie. Polecenie git add pozwala na selektywne umieszczanie plików w strefie przejściowej, co daje programiście pełną kontrolę nad zawartością kolejnego commita. Dzięki temu każda zmiana może być niezależnie przeanalizowana i zatwierdzona, co minimalizuje ryzyko przypadkowego dołączenia niepożądanych modyfikacji.

Przenoszenie plików za pomocą git mv zachowuje ich historię, natomiast usuwanie poprzez git rm usuwa je zarówno z systemu plików, jak i z indeksu gita. W praktyce inżynierskiej ważne jest, aby przed wykonaniem tych operacji upewnić się, że nie stracimy ważnych danych. Dobrą praktyką jest regularne wykonywanie git status w celu sprawdzenia bieżącego stanu katalogu roboczego przed dodaniem lub usunięciem plików.

14/52
git add – przygotowanie do zapisu

Polecenie git add przenosi zmiany z folderu roboczego do obszaru Staging Area (Indeksu). Jest to kluczowy etap, który pozwala precyzyjnie kontrolować, co znajdzie się w następnej "migawce" projektu. Zamiast zapisać wszystko, co zmieniłeś, możesz wybrać tylko te pliki, które są gotowe i tworzą logiczną całość. To jak układanie towarów na taśmie przed zapłaceniem w kasie.

  • Śledzone vs Niesledzone: git add sprawia, że nowe (nieśledzone dotąd) pliki zaczynają być monitorowane przez gita.
  • Precyzja: Pozwala na unikanie commitów "śmieciowych" (np. logów czy plików tymczasowych), jeśli nie masz dobrze ustawionego pliku .gitignore.
  • Potwierdzenie: Dopóki nie użyjesz git add, Twoje zmiany są widoczne tylko dla Ciebie i nie zostaną zapisane w historii.
# Przykład 1: Dodanie konkretnego, gotowego pliku user@dev:~/repo$ git add main.py # Przykład 2: Dodanie absolutnie wszystkich zmian (łącznie z kasowaniem plików) user@dev:~/repo$ git add -A

Polecenie git add przenosi zmiany z katalogu roboczego do strefy przejściowej (staging area), przygotowując je do utrwalenia w commicie. Strefa ta pełni rolę pośredniego bufora, który daje użytkownikowi możliwość selektywnego wyboru plików lub fragmentów kodu. Dzięki temu commity mogą być małe i skupione na jednym zadaniu, co znacznie ułatwia późniejszą analizę historii.

Pliki w repozytorium mogą być śledzone (tracked) lub nieśledzone (untracked). Git add służy właśnie do rozpoczęcia śledzenia nowych plików lub dodania modyfikacji w już śledzonych. Precyzyjne przygotowanie zmian pozwala uniknąć przypadkowego dołączenia niepowiązanych modyfikacji oraz zwiększa czytelność projektu dla współpracowników przeglądających historię commitów.

15/52
git add – interaktywne dodawanie
  • Możemy dodać tylko części pliku (hunks) za pomocą opcji -p.
  • Umożliwia to tworzenie atomowych commitów skupionych na jednej funkcjonalności.
# Przykład 3: Interaktywne wybieranie fragmentów zmian user@dev:~/repo$ git add -p Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]? y # Przykład 4: Dodanie plików o określonym rozszerzeniu user@dev:~/repo$ git add *.css

Flaga -p (lub --patch) w połączeniu z git add umożliwia interaktywne dodawanie tylko wybranych fragmentów zmodyfikowanego pliku. Git dzieli zmiany na logiczne części (hunks) i pyta użytkownika o decyzję dla każdej z nich. Dzięki temu można podzielić jedną dużą modyfikację na kilka osobnych commitów bez konieczności ręcznego kopiowania kodu między plikami.

Taka technika sprzyja tworzeniu atomowych commitów, które zawierają zmiany dotyczące tylko jednego zagadnienia. Przeglądanie historii projektu staje się wówczas znacznie prostsze, ponieważ każdy wpis w logu opisuje konkretną, zamkniętą całość. Interaktywne dodawanie to jedna z bardziej zaawansowanych, ale niezwykle przydatnych umiejętności w codziennej pracy z Gitem.

16/52
git mv – przenoszenie i zmiana nazw

Choć w systemie plików możesz po prostu przeciągnąć plik lub zmienić jego nazwę, użycie git mv jest znacznie bezpieczniejsze dla historii Twojego kodu. git traktuje to polecenie jako sekwencję: skasuj plik w starej lokalizacji, dodaj w nowej i odnotuj to w poczekalni (staging). Dzięki temu logi historii będą czytelnie pokazywać ciągłość zmian w tym obiekcie, zamiast traktować to jako skasowanie jednego i stworzenie zupełnie nowego pliku.

  • Wygoda: Jedno polecenie zamiast trzech (usuwanie, przenoszenie fizyczne, dodawanie).
  • Bezpieczeństwo: git natychmiast wie, że to ten sam plik, co pomaga przy scalaniu (merge) gałęzi w przyszłości.
  • Staging: Zmiana jest od razu dodawana do poczekalni, nie musisz robić git add.
# Przykład 1: Szybka zmiana nazwy pliku z zachowaniem historii user@dev:~/repo$ git mv stare_intro.md WSTEP.md # Przykład 2: Porządkowanie kodu – przeniesienie do modułu user@dev:~/repo$ git mv utils.js src/lib/utils.js

Polecenie git mv łączy w sobie zmianę nazwy lub przeniesienie pliku z automatycznym dodaniem odpowiednich wpisów do indeksu. Dzięki temu nie trzeba wykonywać osobno git rm dla starej lokalizacji i git add dla nowej. Git sam rejestruje operację jako zmianę nazwy, co pozwala zachować ciągłość historii tego pliku.

Zachowanie historii jest kluczowe przy refaktoryzacji kodu, ponieważ dzięki niemu w logach widzimy pełną ścieżkę zmian niezależnie od aktualnej nazwy pliku. Polecenie działa zarówno na pojedynczych plikach, jak i na całych katalogach. Warto pamiętać, że git mv to wygodny skrót, ale to samo można osiągnąć przez standardowe przeniesienie pliku w systemie, a następnie jawne wykonanie git add i git rm.

17/52
git restore – przywracanie plików

Pomyłki to naturalna część programowania. git restore to Twój "przycisk Undo". Pozwala on na wycofanie zmian, które wprowadziłeś w plikach, przywracając je do stanu z ostatniego zatwierdzonego commita lub do stanu z poczekalni. Jest to bezpieczniejsze i bardziej nowoczesne polecenie niż starsze wersje checkout używane do tego celu.

  • Praca lokalna: Najczęstsze użycie to porzucenie nieudanych eksperymentów w pliku, których jeszcze nie dodałeś do indeksu.
  • Zarządzanie indeksem: Opcja --staged pozwala "wyjąć" plik z poczekalni, jeśli dodałeś go tam przez pomyłkę (tzw. unstage).
  • Ostrzeżenie: Przywracanie zmian w folderze roboczym jest nieodwracalne – stracisz to, co napisałeś od ostatniego zapisu!
# Przykład 1: Porzucenie wszystkich zmian w pliku konfiguracyjnym user@dev:~/repo$ git restore config.json # Przykład 2: Wyciągnięcie pliku z poczekalni (unstage), zmiany zostają na dysku user@dev:~/repo$ git restore --staged index.html

Polecenie git restore służy do cofania zmian w plikach, które nie zostały jeszcze utrwalone w commicie. Domyślnie przywraca ono zawartość pliku z ostatniego zatwierdzenia, czyli odrzuca wszystkie niezapisane modyfikacje w katalogu roboczym. Jest to szybsza i bardziej czytelna alternatywa dla starszego polecenia git checkout.

Opcja --staged pozwala na wycofanie zmian wyłącznie z indeksu, bez naruszania katalogu roboczego. Należy jednak pamiętać, że git restore bez wcześniejszego commita trwale usuwa niezapisane modyfikacje – nie da się ich później odzyskać. Przed użyciem tego polecenia warto upewnić się, że nie tracimy ważnych danych, ewentualnie wcześniej wykonując tymczasowe commity lub kopie zapasowe.

18/52
git restore – zaawansowane przywracanie
  • Możliwość przywrócenia pliku z konkretnego commita (np. sprzed tygodnia).
  • Użyteczne przy pomyłkowym usunięciu ważnego fragmentu kodu.
# Przykład 3: Przywrócenie pliku ze wskazanego commita user@dev:~/repo$ git restore --source=abc1234 src/main.py # Przykład 4: Przywrócenie całego katalogu roboczego user@dev:~/repo$ git restore .

Dodanie parametru --source z referencją do konkretnego commita pozwala przywrócić plik do stanu z dowolnego punktu w historii. Jest to przydatne gdy chcemy cofnąć się do starszej wersji funkcji lub odtworzyć przypadkowo usunięty fragment kodu. Git automatycznie dopasowuje zawartość pliku do wskazanego rewizji, nie modyfikując przy tym samej historii.

Można również przywracać całe katalogi, co jest wygodne po nieudanej operacji scalania lub przypadkowej modyfikacji wielu plików naraz. Wystarczy jako argument podać nazwę katalogu zamiast pojedynczego pliku. Taka opera-cja jest jednak dość inwazyjna, dlatego przed jej wykonaniem warto dokładnie przeanalizować, które zmiany naprawdę chcemy odrzucić.

19/52
git rm – usuwanie z projektu

Wyrzucanie niepotrzebnych plików z projektu musi być odnotowane przez system kontroli wersji. Polecenie git rm usuwa plik fizycznie z dysku oraz – co ważniejsze – z bazy danych gita (indeksu). Dzięki temu przy następnym commicie git będzie wiedział, że plik ma zostać usunięty z historii wersji od tego momentu. Jeśli usuniesz plik zwykłym klawiszem Delete, git zgłosi to jako "zmianę nieprzygotowaną do zapisu", którą i tak będziesz musiał potwierdzić.

  • Tryb dyskowy: Standardowo usuwa plik z repozytorium i z Twojego komputera.
  • Tryb "--cached": Bardzo przydatny, gdy chcesz przestać śledzić plik gitem (np. dodałeś omyłkowo plik z hasłem), ale chcesz go zostawić fizycznie na dysku.
  • Usuwanie folderów: Flaga -r (recursive) pozwala usunąć cały katalog wraz z całą jego zawartością.
# Przykład 1: Całkowite usunięcie przestarzałego pliku dokumentacji user@dev:~/repo$ git rm old_docs.txt # Przykład 2: Usunięcie z git, ale zostawienie pliku na dysku (np. do .gitignore) user@dev:~/repo$ git rm --cached .env.local

Polecenie git rm usuwa plik zarówno z systemu plików, jak i z indeksu Gita, przygotowując operację do następnego commita. Dzięki temu historia repozytorium odnotowuje celowe usunięcie, a nie brakujący plik. Standardowe usunięcie przez system plików wymaga dodatkowo ręcznego wykonania git add, co może prowadzić do nieścisłości.

Opcja --cached pozwala na usunięcie pliku tylko z indeksu, pozostawiając go na dysku – przydaje się gdy chcemy przestać śledzić plik konfiguracyjny lub wygenerowany artefakt. Flaga -r umożliwia rekurencyjne usuwanie całych katalogów, co jest wygodne przy porządkowaniu struktury projektu. Warto zachować ostrożność, ponieważ git rm trwale usuwa pliki z dysku, a próba odzyskania może być czasochłonna.

20/52
Badanie historii i stanu

Status, logi, różnice i debugowanie

Badanie historii repozytorium jest kluczową umiejętnością każdego programisty pracującego z Gitem. Polecenie git log umożliwia przeglądanie pełnej historii commitów z możliwością filtrowania według autora, daty czy zakresu zmian. Zaawansowane opcje, takie jak --oneline czy --graph, pozwalają na szybką wizualizację gałęzi i powiązań między commitami.

Polecenia git status i git diff dostarczają informacji o bieżącym stanie plików w katalogu roboczym. Status pokazuje, które pliki zostały zmodyfikowane, dodane lub usunięte, natomiast diff przedstawia dokładne różnice w kodzie źródłowym. Umiejętność debugowania za pomocą tych narzędzi znacząco przyspiesza identyfikację błędów i zrozumienie wprowadzonych zmian.

21/52
git status – twój nawigator

Nie bez powodu git status to najczęściej wpisywana komenda w terminalu. To Twój kompas, który w każdej sekundzie mówi Ci, w jakim miejscu projektu się znajdujesz. Informuje Cię nie tylko o tym, które pliki zmieniłeś, ale też na jakiej gałęzi pracujesz i czy Twoje lokalne repozytorium jest zsynchronizowane z serwerem (GitHubem). Zanim zrobisz cokolwiek innego, zawsze sprawdź status!

  • Informacje o gałęzi: Dowiesz się, czy jesteś na gałęzi main, czy może na eksperymentalnym feature.
  • Status śledzenia: Jeśli Twoja gałąź jest "ahead" (do przodu), oznacza to, że masz commity, których jeszcze nie wysłałeś na serwer.
  • Wskazówki: git jest pomocny – w statusie często podpowiada dokładnie, jakiej komendy użyć, by cofnąć zmianę lub dodać plik do poczekalni.
# Przykład 1: Typowy status po edycji stylu i dodaniu obrazka user@dev:~/repo$ git status On branch main Changes not staged for commit: (use "git add <file>..." to update what will be committed) modified: style.css Untracked files: (use "git add <file>..." to include in what will be committed) assets/logo.png

Komenda git status jest najczęściej używanym poleceniem w codziennej pracy z Gitem, ponieważ dostarcza kompleksowego podglądu bieżącego stanu repozytorium. Informuje o gałęzi, na której się znajdujesz, pokazuje pliki zmodyfikowane, dodane do śledzenia oraz te nieśledzone. Dodatkowo wyświetla informacje o tym, czy twoja gałąż jest zsynchronizowana z gałęzią zdalną.

Dzięki czytelnym podpowiedziom, takim jak sugestie użycia komend do dodania plików czy utworzenia commita, git status pełni rolę interaktywnego asystenta. Jest to pierwsze polecenie, które powinieneś wykonać przed rozpoczęciem jakichkolwiek operacji, aby upewnić się, że wiesz, w jakim stanie znajduje się projekt. Regularne sprawdzanie statusu zapobiega przypadkowym utraceniom danych i konfliktom.

22/52
git status – skrócona wersja
  • Opcja -s (short) jest bardziej kompaktowa i czytelna przy wielu plikach.
  • Pokazuje kody stanów (np. M - zmodyfikowany, ?? - nieśledzony).
# Przykład 2: Skrócony status user@dev:~/repo$ git status -s M style.css ?? new-image.png A src/header.js # Przykład 3: Status z informacją o gałęzi user@dev:~/repo$ git status -sb

Opcja -s (skrócona) generuje zwięzły, dwukolumnowy widok, w którym pierwsza kolumna odnosi się do obszaru stagingu, a druga do katalogu roboczego. Kody takie jak M oznaczają modyfikację, ?? plik nieśledzony, a A nowo dodany plik w staging area. Taki format jest szczególnie przydatny w skryptach i narzędziach automatyzujących, gdzie liczy się szybkość parsowania.

Możliwe jest również łączenie kodów statusu, na przykład MM oznacza plik zmodyfikowany zarówno w staging area, jak i w katalogu roboczym, ale z różnymi zmianami. Dla zaawansowanych użytkowników istnieje opcja --porcelain, która zapewnia jeszcze bardziej stabilny format wyjścia, idealny do integracji z innymi narzędziami. Skrócony widok znacznąco przyspiesza pracę, gdy mamy do czynienia z dużą liczbą plików.

23/52
git log – kronika projektu

Polecenie git log to wejście do archiwum Twojego sukcesu (i czasem błędów). Pozwala ono prześledzić każdy krok milowy projektu, pokazując listę wszystkich zatwierdzonych commitów. To tutaj sprawdzisz, kto pracował nad daną funkcjonalnością, kiedy wprowadzono zmianę i jaki był jej cel. Dobrze opisane commity sprawiają, że log czyta się jak historię rozwoju aplikacji.

  • Identyfikatory: Każdy wpis ma unikalny hash (SHA-1), który jest "adresem" tego konkretnego stanu projektu.
  • Autorstwo: Widzisz imię, nazwisko i e-mail osoby, która stworzyła dany commit.
  • Kontekst czasowy: Dokładna data i godzina zapisu zmiany pozwalają na analizę postępów prac.
# Przykład 1: Pełna informacja o ostatnich zmianach user@dev:~/repo$ git log commit d6f7a8... (HEAD -> main) Author: Anna Programistka <ania@code.pl> Date: Mon Mar 23 10:15:22 2026 +0100 Refaktoryzacja bazy danych – optymalizacja zapytań SQL

Polecenie git log prezentuje historię commitów w odwrotnej kolejności chronologicznej, pokazując najnowsze zmiany na górze. Każdy wpis zawiera unikalny identyfikator SHA-1, który jest 40-znakowym skrótem kryptograficznym, jednoznacznie identyfikującym dany commit. Informacje o autorze wraz z adresem e-mail oraz znacznik czasu pozwalają dokładnie określić, kto i kiedy wprowadził daną zmianę.

Identyfikatory SHA-1 są fundamentalne dla integralności Gita – każda zmiana w treści commita skutkuje zupełnie innym hashem, co uniemożliwia manipulację historią bez wykrycia. Domyślny widok git log pokazuje pełne hashe, ale w praktyce często wystarczy kilka pierwszych znaków. Historia commitów to nie tylko lista zmian, ale także pełny kontekst decyzji projektowych podjętych w trakcie rozwoju.

24/52
git log – formatowanie i grafy
  • --oneline – jeden commit na linię.
  • --graph – wizualizacja rozgałęzień.
  • --grep – szukanie w opisach commitów.
# Przykład 2: Czytelny graf historii user@dev:~/repo$ git log --oneline --graph --all # Przykład 3: Commity konkretnego autora user@dev:~/repo$ git log --author="Jan"

Opcja --oneline skraca każdy wpis do pojedynczej linii zawierającej skrócony hash i tytuł commita, co jest idealne do szybkiego przeglądania długiej historii. Flaga --graph rysuje graficzną reprezentację gałęzi i merge'y, używając znaków ASCII do wizualizacji rozgałęzień. Dzięki temu można łatwo prześledzić, w którym momencie powstały gałęzie poboczne i jak zostały scalone z gałęzią główną.

Opcja --grep umożliwia filtrowanie commitów na podstawie wzorca w treści wiadomości commita, co jest nieocenione przy poszukiwaniu konkretnych typów zmian. Można łączyć wiele kryteriów filtrowania, na przykład --author z --since, aby znaleźć commity konkretnego autora z określonego okresu. Zaawansowane formatowanie pozwala również na wyświetlanie tylko statystyk zmian lub różnic między commitami.

25/52
git diff – lupa na zmiany

Samo wiedzieć, że plik się zmienił, to za mało. git diff pokazuje dokładnie, które linie zostały dodane (kolor zielony, znak +), a które usunięte (kolor czerwony, znak -). To Twoje główne narzędzie do weryfikacji kodu przed wysłaniem go do kolegów z zespołu. Pozwala uniknąć zostawienia w kodzie niepotrzebnych print()ów czy komentarzy testowych.

  • Unstaged Diff: Domyślnie pokazuje to, co napisałeś, ale czego jeszcze nie dodałeś do poczekalni (staging area).
  • Różnica w indeksie: Opcja --staged pokazuje to, co już dodałeś i co wejdzie do następnego commita. Zawsze używaj tego przed git commit!
  • Porównywanie wszystkiego: Możesz porównać dwie dowolne gałęzie, by zobaczyć, co różni Twoją wersję od wersji głównej.
# Przykład 1: Sprawdzenie co dokładnie dopisałem w pliku script.py user@dev:~/repo$ git diff script.py # Przykład 2: Porównanie zmian gotowych do zatwierdzenia user@dev:~/repo$ git diff --staged

Komenda git diff bez argumentów pokazuje różnice między katalogiem roboczym a staging area, czyli zmiany, które nie zostały jeszcze dodane do poczekalni. Linie dodane są oznaczane znakiem + i wyświetlane na zielono, a linie usunięte znakiem - i wyświetlane na czerwono. Domyślny kontekst trzech linii wokół każdej zmiany pomaga zrozumieć otoczenie modyfikacji.

Aby zobaczyć różnice między staging area a ostatnim commitem, używamy git diff --staged lub --cached. Porównywanie dwóch dowolnych commitów jest możliwe przez podanie ich identyfikatorów SHA jako argumentów. Działanie diffa można dostosować za pomocą opcji takich jak --ignore-space-change, która pomija zmiany w białych znakach.

26/52
git diff – statystyki
  • Możliwość wyświetlenia tylko podsumowania zmian (liczba linii dodanych/usuniętych).
  • Opcja --word-diff pozwala na analizę zmian wewnątrz linii.
# Przykład 4: Statystyka zmian dla pliku user@dev:~/repo$ git diff --stat main.py # Przykład 5: Porównanie gałęzi user@dev:~/repo$ git diff main..feature-X

Opcja --stat generuje podsumowanie statystyk zamiast pełnego diffa, pokazując listę zmienionych plików oraz liczbę dodanych i usuniętych linii. Flaga --word-diff wyświetla zmiany na poziomie poszczególnych słów, a nie całych linii, co jest szczególnie przydatne przy przeglądaniu zmian w dokumentacji. Porównywanie gałęzi za pomocą git diff branch1..branch2 pokazuje wszystkie różnice między ich ostatnimi commitami.

Możliwe jest również generowanie statystyk tylko dla wybranego zakresu linii w pliku, co pozwala skupić się na konkretnym fragmencie kodu. Parametr --diff-filter umożliwia filtrowanie wyników według typu zmiany, na przykład tylko dodane pliki (A) lub tylko usunięte (D). Statystyki diffa są niezwykle pomocne podczas code review, pozwalając szybko ocenić rozmiar i zakres proponowanych zmian.

27/52
git show – inspekcja szczegółów

Podczas gdy log pokazuje listę, git show pozwala zajrzeć do środka konkretnego elementu historii. Najczęściej używa się go do szybkiego sprawdzenia, co dokładnie zawierał dany commit – pokazuje on opis zmiany oraz pełny diff (różnice w kodzie). To idealne narzędzie, gdy kolega z zespołu prosi Cię o przejrzenie jego ostatniego zapisu.

  • Elastyczność: Możesz zobaczyć treść pliku z przeszłości bez przełączania całej gałęzi (używając składni hash:plik).
  • Skupienie: Pozwala na skupienie się na jednym, konkretnym punkcie w czasie bez przeszukiwania całego logu.
  • Metadane: Wyświetla również ukryte informacje o tagach czy sygnaturach bezpieczeństwa.
# Przykład 1: Podejrzenie ostatniej wprowadzonej zmiany (HEAD) user@dev:~/repo$ git show # Przykład 2: Wyświetlenie pliku README.md w wersji sprzed 3 commitów user@dev:~/repo$ git show HEAD~3:README.md

Polecenie git show wyświetla szczegółowe informacje o dowolnym obiekcie Gita, w szczególności o commitach, pokazując ich metadane oraz pełną treść diffa. Po podaniu identyfikatora SHA można zobaczyć dokładnie, co zostało zmienione w danym commicie, wraz z kontekstem i autorem. Opcja --name-only ogranicza wyjście do listy zmienionych plików, co jest przydatne przy szybkiej inspekcji zakresu zmian.

Możliwe jest również wyświetlenie zawartości pojedynczego pliku z konkretnego commita, co pozwala zajrzeć do historii pliku bez przełączania gałęzi. W przypadku tagów, git show wyświetla nie tylko sam tag, ale także powiązany z nim commit oraz wiadomość adnotacji. Ta komenda jest podstawowym narzędziem do szczegółowej analizy poszczególnych punktów w historii projektu.

28/52
git show – inne zastosowania
  • Wyświetlanie metadanych tagów.
  • Szybkie sprawdzenie diffa dla konkretnego identyfikatora SHA.
# Przykład 3: Szczegóły taga v1.0 user@dev:~/repo$ git show v1.0 # Przykład 4: Tylko opis commita user@dev:~/repo$ git show -s --format=fuller abc1d2

Jeśli tag został utworzony jako adnotowany (z użyciem -a), git show wyświetli także metadane taga, takie jak jego autor, datę utworzenia oraz dołączoną wiadomość. Inspekcja konkretnego SHA pozwala sprawdzić nie tylko commit, ale także drzewo i bloby, które go tworzą, co daje pełen wgląd w strukturę repozytorium. Przy pracy z tagami wersji produkcyjnych, git show pozwala szybko zweryfikować, jakie dokładnie zmiany zostały włączone do danej wersji.

Możliwe jest także użycie git show HEAD~n, aby zobaczyć n-ty commit wstecz od bieżącego, bez konieczności znajdowania jego hasha. Sprawdzanie konfliktów scalania za pomocą git show -m pokazuje różnice względem każdego z rodziców commita scalającego. To niezwykle wszechstronne narzędzie do eksploracji historii i zrozumienia ewolucji kodu.

29/52
git grep – wyszukiwarka kodowa

Programiści spędzają więcej czasu na czytaniu kodu niż na jego pisaniu. git grep to potężna wyszukiwarka, która działa błyskawicznie, ponieważ przeszukuje tylko pliki śledzone przez gita, ignorując zbędne foldery systemowe czy pliki binarne. Jest o wiele szybsza niż standardowe wyszukiwanie w wielu edytorach tekstu i pozwala na używanie zaawansowanych wzorców (RegEx).

  • Kontekst: Możesz szukać fraz w historiach gałęzi, których aktualnie nie masz nawet otwartych w edytorze.
  • Efektywność: Wyniki pokazują nie tylko plik, ale i numer linii, co pozwala natychmiast przejść do edycji.
  • Szybkość: Nawet w projektach o tysiącach plików wyniki pojawiają się w ułamku sekundy.
# Przykład 1: Znalezienie gdzie zdefiniowano funkcję "calculate_total" user@dev:~/repo$ git grep -n "def calculate_total" # Przykład 2: Szukanie frazy "FIXME" w całej historii projektu user@dev:~/repo$ git grep "FIXME"

Komenda git grep przeszukuje jedynie pliki śledzone przez Gita, co odróżnia ją od zwykłego grepa, który przeszukuje wszystkie pliki w systemie plików. Wspiera pełne wyrażenia regularne, co pozwala na tworzenie zaawansowanych wzorców wyszukiwania, takich jak wyszukanie definicji funkcji czy wywołań metod. Opcja -n dodaje numery linii do wyników, a -c zlicza dopasowania w każdym pliku.

Flaga --show-function wyświetla nazwę funkcji zawierającej dopasowanie, co jest nieocenione przy analizie kodu źródłowego. Parametr -C (context) pozwala wyświetlić określoną liczbę linii kontekstu wokół każdego dopasowania, ułatwiając zrozumienie otoczenia znalezionego fragmentu. Dzięki integracji z repozytorium git grep automatycznie uwzględnia pliki ignorowane przez .gitignore, co czyni wyniki bardziej trafnymi.

30/52
git bisect – detektywistyczne śledztwo

Pojawił się błąd, ale nie wiesz, kiedy? git bisect to narzędzie, które automatyzuje proces szukania winnego commita za pomocą wyszukiwania binarnego. Zamiast sprawdzać każdy commit po kolei (co przy setkach zmian jest niemożliwe), git prosi Cię o sprawdzenie tylko kilku punktów. Ty mówisz "tutaj działa" lub "tutaj nie działa", a git błyskawicznie zawęża krąg podejrzanych, aż wskaże dokładnie tę jedną zmianę, która popsuła aplikację.

  • Oszczędność czasu: Znalezienie błędu w historii 1000 commitów wymaga sprawdzenia tylko około 10 wersji.
  • Automatyzacja: Możesz napisać skrypt testowy, a git sam przeskanuje historię i znajdzie błąd bez Twojej ingerencji.
  • Metodologia: Dzielisz historię na "dobrą" (przeszłość) i "złą" (teraźniejszość).
# Przykład 1: Rozpoczęcie śledztwa (obecna wersja jest popsuta) user@dev:~/repo$ git bisect start user@dev:~/repo$ git bisect bad user@dev:~/repo$ git bisect good v2.1 # Wersja 2.1 była OK

Polecenie git bisect implementuje algorytm wyszukiwania binarnego, który w zaledwie kilku krokach pozwala zlokalizować commit wprowadzający błąd, nawet w repozytorium z tysiącami commitów. Proces rozpoczyna się od wskazania commita uznawanego za dobry oraz commita znanego jako zły, a następnie Git automatycznie przełącza się między kolejnymi kandydatami. Po każdym przełączeniu testujesz, czy błąd występuje, i oznaczasz commit jako dobry (git bisect good) lub zły (git bisect bad).

W ciągu zaledwie kilkunastu iteracji Git zawęża zakres podejrzanych commitów do jednego, który prawdopodobnie wprowadził problem. Opcja bisect run pozwala zautomatyzować cały proces, wykonując skrypt testowy dla każdego kandydata i automatycznie go oznaczając. Jest to niezwykle efektywne narzędzie do debugowania, szczególnie w dużych projektach, gdzie ręczne przeglądanie setek commitów byłoby niepraktyczne.

31/52
git bisect – kończenie procesu
  • Po każdym kroku testujemy kod i informujemy gita czy jest good czy bad.
  • Na końcu otrzymujemy wskazanie konkretnego, winnego commita.
# Przykład 2: Oznaczanie kroku i reset user@dev:~/repo$ git bisect good ae12345... is the first bad commit user@dev:~/repo$ git bisect reset # Powrót do punktu wyjścia

Po zakończeniu procesu bisect nie zapomnij wywołać komendy git bisect reset, która przywraca repozytorium do pierwotnego stanu sprzed rozpoczęcia poszukiwań. Bez tego kroku pozostaniesz w trybie bisect, a HEAD będzie wskazywać na ostatni oznaczony commit zamiast na właściwą gałąź. To częsty błąd początkujących, którzy po znalezieniu winnego commitu zapominają o resecie i później dziwią się nieoczekiwanemu zachowaniu repozytorium.

Do pełnej automatyzacji procesu możesz użyć git bisect run z skryptem, który zwraca kod wyjścia 0 dla commitów dobrych i 1 dla złych. W praktyce świetnie sprawdza się to w potokach CI/CD, gdzie skrypt kompiluje projekt i uruchamia testy, a bisect sam znajduje commit, który wprowadził regresję. Dzięki temu zamiast ręcznego testowania dziesiątek rewizji wystarczy jedno polecenie, a cały proces odbywa się automatycznie i znacznie szybciej.

32/52
Rozwijanie i modelowanie historii

Gałęzie, commity, tagi i łączenie zmian

Rozwijanie historii repozytorium poprzez gałęzie to jedna z najpotężniejszych funkcjonalności Gita. Gałęzie pozwalają na równoległą pracę nad różnymi funkcjonalnościami bez zakłócania stabilności głównej linii kodu. Każda gałąź jest lekkim wskaźnikiem na konkretny commit, co sprawia, że tworzenie i przełączanie między gałęziami jest niemal natychmiastowe.

Tagowanie commitów umożliwia oznaczanie ważnych punktów w historii projektu, takich jak wersje wydaniowe czy kamienie milowe. Łączenie zmian poprzez merge lub rebase scala prace z różnych gałęzi w jednolitą historię. Rebase dodatkowo pozwala na utrzymanie liniowej i czytelnej historii, co jest szczególnie cenione w projektach open source i komercyjnych.

33/52
git branch – równoległe światy

Możliwość tworzenia gałęzi (branching) to jedna z najpotężniejszych cech gita. Wyobraź sobie, że Twój projekt to drzewo. Główny pień (main) to stabilna wersja kodu. Gałęzie pozwalają Ci "odbić" w bok, by dopisać nową funkcję lub naprawić błąd, nie ryzykując popsucia głównej wersji. Możesz mieć dziesiątki takich gałęzi jednocześnie i swobodnie się między nimi przełączać. W Gicie gałąź to technicznie tylko lekki wskaźnik na konkretny commit.

  • Izolacja: Twoje eksperymenty na jednej gałęzi nie wpływają na pracę innych osób na innych gałęziach.
  • Organizacja: Pozwala na utrzymywanie porządku – jedna gałąź to zazwyczaj jedno zadanie do wykonania.
  • Lekkość: Tworzenie gałęzi w Gicie jest natychmiastowe i nie zużywa dodatkowego miejsca na dysku.
# Przykład 1: Sprawdzenie jakie gałęzie istnieją w projekcie user@dev:~/repo$ git branch * main feature/login-page # Przykład 2: Stworzenie nowej gałęzi bez przełączania się na nią user@dev:~/repo$ git branch fix/bug-123

Gałęzie w Git są niezwykle lekkie, ponieważ każda z nich to jedynie ruchomy wskaźnik na konkretny commit – nie ma tu kopiowania plików ani tworzenia osobnych katalogów. Komenda git branch nazwa-gałęzi tworzy nowy wskaźnik w miejscu, w którym aktualnie się znajdujesz, natomiast git branch -l wyświetla wszystkie lokalne gałęzie. Przełączanie między gałęziami odbywa się za pomocą git switch lub git checkout, co zmienia zawartość katalogu roboczego na stan z wybranego commita.

To właśnie ta lekkość sprawia, że w Git zachęca się do tworzenia wielu gałęzi – jedna gałąź na funkcję, jedna na bugfixa, osobna na eksperymenty. W przeciwieństwie do innych systemów kontroli wersji, gdzie gałęzie są ciężkimi kopiami, w Git przełączanie między nimi jest błyskawiczne. Dzięki temu równoległa praca nad wieloma niezależnymi funkcjonalnościami staje się naturalnym i bezpiecznym procesem, który nie spowalnia codziennej pracy.

34/52
git branch – usuwanie i zdalne

Nie wszystkie gałęzie są potrzebne wiecznie. Gdy skończysz pracę nad nową funkcją i połączysz ją z głównym kodem, warto usunąć boczną gałąź, by zachować porządek. git pozwala na bezpieczne usuwanie (tylko jeśli zmiany zostały zapisane gdzieś indziej) oraz na usuwanie siłowe, jeśli uznasz, że praca na danej gałęzi była ślepym zaułkiem.

  • Sprzątanie: Regularne usuwanie starych gałęzi ułatwia nawigację po projekcie.
  • Zdalne gałęzie: Możesz również zobaczyć listę gałęzi Twoich kolegów, które znajdują się na serwerze (GitHub).
  • Wymuszanie: Flaga -D (duże D) to polecenie "usuń i nie pytaj o zdanie", przydatne przy porzucaniu nieudanych pomysłów.
# Przykład 3: Bezpieczne usunięcie ukończonej gałęzi user@dev:~/repo$ git branch -d feature/done # Przykład 4: Lista wszystkich gałęzi (lokalnych i tych na serwerze) user@dev:~/repo$ git branch -a

Usuwanie gałęzi lokalnej wykonuje się poleceniem git branch -d nazwa, które działa tylko wtedy, gdy gałąź została już scalona – w przeciwnym razie Git ostrzeże przed utratą zmian. Gdy chcesz wymusić usunięcie niescalonej gałęzi, użyj flagi -D (wielka litera), ale rób to świadomie, bo zmiany zostaną bezpowrotnie utracone. To zabezpieczenie chroni przed przypadkowym skasowaniem pracy, która nie została jeszcze włączona do głównej gałęzi.

Zdalne gałęzie możesz przeglądać za pomocą git branch -r, a wszystkie naraz (lokalne i zdalne) dzięki git branch -a. Gałęzie zdalne są tylko do odczytu i reprezentują stan gałęzi w repozytorium zdalnym z momentu ostatniego pobrania danych. Aby zsynchronizować się ze zdalnym repozytorium, użyj git fetch – wtedy dane o zdalnych gałęziach zostaną odświeżone i będziesz widzieć aktualny stan pracy innych członków zespołu.

35/52
git commit – kamień milowy

Polecenie git commit to najważniejszy moment w pracy z gitem. To stworzenie "migawek" (snapshots) Twojego projektu. Każdy commit jest trwałym zapisem stanu plików, który możesz w dowolnej chwili przywrócić. To jak zapisanie stanu gry przed trudnym bossem. Bardzo ważne jest, aby każdy commit miał jasny, zrozumiały opis (wiadomość), który wyjaśni Tobie i innym, dlaczego ta zmiana została wprowadzona.

  • Atomowe commity: Dobrą praktyką jest robienie małych, czystych zapisów dotyczących jednej rzeczy.
  • Snapshot, nie Diff: git przechowuje pełny stan plików (optymalnie), a nie tylko różnice, co czyni go niezwykle szybkim.
  • Lokalność: Commit dzieje się tylko na Twoim komputerze. Dopóki nie zrobisz push, nikt inny go nie zobaczy.
# Przykład 1: Klasyczny commit z krótką wiadomością user@dev:~/repo$ git commit -m "Add basic authentication logic" # Przykład 2: Szybki zapis zmian w już śledzonych plikach (pomija git add) user@dev:~/repo$ git commit -am "Update README with contact info"

Każdy commit w Git to nie tylko zmiana plików, ale pełna migawka całego repozytorium (snapshot) z unikalnym identyfikatorem SHA-1. Komenda git commit -m "treść wiadomości" tworzy nowy commit z podanym komunikatem, jednak w profesjonalnych projektach zachęca się do pominięcia flagi -m, aby otworzyć edytor i napisać dłuższy opis. Struktura commita obejmuje hash, autora, datę, komunikat oraz wskaźnik do drzewa plików – to wszystko razem tworzy niezmienny wpis w historii.

Dobry komunikat commita powinien w pierwszej linii (do 50 znaków) streszczać zmianę, a po pustej linii zawierać szczegółowy opis dlaczego zmiana została wprowadzona. Przestrzeganie tej konwencji ułatwia późniejsze przeglądanie historii, generowanie dziennika zmian i współpracę w zespole. Pamiętaj, że commit to kamień milowy – każdy z nich powinien reprezentować logicznie spójną, działającą wersję kodu, a nie przypadkowy zbiór modyfikacji z całego dnia pracy.

36/52
git commit – poprawki

Zrobiłeś commit, ale zapomniałeś dodać jednego pliku? Albo zauważyłeś literówkę w opisie zmiany? Nie musisz robić kolejnego, "śmieciowego" commita. Opcja --amend pozwala na edycję Twojego ostatniego zapisu, jakby błąd nigdy nie powstał. Pamiętaj tylko, że zmienia to historię – nie używaj tego na commitach, które już wysłałeś na serwer i które widzą inni!

# Przykład 3: Poprawienie opisu ostatniego commita user@dev:~/repo$ git commit --amend -m "Better description for last change" # Przykład 4: Dołączenie zapomnianego pliku do ostatniego commita user@dev:~/repo$ git add forgotten_file.txt user@dev:~/repo$ git commit --amend --no-edit

Komenda git commit --amend pozwala zmodyfikować ostatni commit bez tworzenia nowego wpisu w historii – możesz poprawić literówkę w komunikacie, dodać zapomniany plik lub zmienić jego treść. Gdy dodasz zmiany do staging area, a następnie wykonasz git commit --amend, Git zastąpi poprzedni commit nowym, zawierającym zarówno stare, jak i nowe zmiany. Należy jednak pamiętać, że zmienia to hash commita, więc nie używaj tej komendy do commitów, które zostały już opublikowane i są współdzielone z innymi.

W praktyce --amend to Twoje koło ratunkowe dla drobnych pomyłek: zapomniałeś dodać pliku do ostatniego commita? Wystarczy git add plik && git commit --amend --no-edit. Chcesz poprawić komunikat? Użyj git commit --amend -m "nowy komunikat". Pamiętaj jednak, że to narzędzie służy tylko do ostatniego commita – jeśli chcesz zmienić starsze wpisy, musisz sięgnąć po git rebase -i. Stosuj amend z rozwagą, zwłaszcza gdy pracujesz w zespole na współdzielonym repozytorium.

37/52
git merge – łączenie pracy

Praca na gałęziach nabiera sensu w momencie ich łączenia. git merge bierze historię z jednej gałęzi i wciela ją do Twojej aktualnej gałęzi pracy. Najczęściej robimy to, gdy skończymy pisać nową funkcję na bocznej gałęzi i chcemy ją dodać do głównej wersji projektu. git stara się robić to automatycznie, dbając o to, by nic nie zginęło.

  • Przesunięcie do przodu: Jeśli główna gałąź nie zmieniła się od czasu Twojego "odbicia", git po prostu przesunie wskaźnik do przodu.
  • Scalanie trójstronne: Jeśli w obu gałęziach zaszły zmiany, git utworzy specjalny "commit scalenia" (merge commit).
  • Zastosowanie: Idealne do bezpiecznego integrowania ukończonych funkcji.
# Przykład 1: Scalanie funkcji do gałęzi głównej user@dev:~/repo$ git switch main user@dev:~/repo$ git merge feature/news-letter

Scalanie gałęzi to fundamentalna operacja w Git, która pozwala łączyć równoległy rozwój projektu w jedną spójną historię. Git automatycznie wykrywa wspólnego przodka (merge base) i na jego podstawie próbuje scalić zmiany bez udziału użytkownika. Gdy jedna z gałęzi nie ma nowych commitów od punktu rozwidlenia, Git wykonuje fast-forward – przesuwa wskaźnik bez tworzenia nowego commita. W przeciwnym razie powstaje merge commit, który ma dwóch rodziców i reprezentuje moment połączenia gałęzi.

W praktyce warto regularnie włączać zmiany z gałęzi głównej do swojej gałęzi roboczej, aby minimalizować ryzyko dużych konfliktów. Merge commit zachowuje pełną informację o tym, które zmiany i z której gałęzi pochodzą, co ułatwia późniejsze audyty. Zespół powinien ustalić strategię scalania – niektórzy preferują zawsze tworzenie merge commita (--no-ff), inni wolą rebase dla czystszej historii. Niezależnie od wyboru, kluczowe jest, aby gałąź główna zawsze pozostawała w stanie produkcyjnym po każdym scaleniu.

38/52
git merge – gdy pojawią się konflikty

Konflikty to sytuacja, w której dwie osoby zmieniły tę samą linię w tym samym pliku. git nie wie, która wersja jest ważniejsza i prosi o Twoją interwencję. To nie jest błąd – to mechanizm bezpieczeństwa, który zapobiega nadpisywaniu pracy kolegów bez Twojej wiedzy.

  • Ręczna edycja: Musisz otworzyć plik, usunąć znaczniki konfliktu (<<<<<<<, =======, >>>>>>>) i zostawić poprawny kod.
  • Zakończenie: Po naprawieniu plików musisz je dodać (add) i zatwierdzić (commit).
  • Ratunek: Jeśli czujesz, że sytuacja Cię przerosła, zawsze możesz anulować scalanie komendą --abort.
# Przykład 3: Anulowanie scalenia i powrót do stanu sprzed merge user@dev:~/repo$ git merge --abort # Przykład 4: Sprawdzenie które pliki wymagają ręcznej naprawy user@dev:~/repo$ git diff --name-only --diff-filter=U

Konflikt scalania pojawia się, gdy Git nie jest w stanie samodzielnie połączyć zmian modyfikujących tę samą linię w tym samym pliku. W takiej sytuacji Git przerywa operację merge i oznacza pliki jako skonfliktowane, wstrzykując do nich znaczniki konfliktów. Rozwiązanie konfliktu wymaga ręcznej edycji pliku – należy usunąć znaczniki i pozostawić docelową wersję kodu, a następnie dodać plik i sfinalizować commita.

Narzędzia zewnętrzne, takie jak git mergetool, mogą znacząco przyspieszyć rozwiązywanie konfliktów, prezentując trzy wersje pliku obok siebie. Warto pamiętać, że konflikty nie są oznaką błędu – to naturalna konsekwencja równoległej pracy wielu programistów. Kluczową umiejętnością jest komunikacja z zespołem przy rozwiązywaniu złożonych konfliktów, aby nie nadpisać cudzej pracy. Przed ostatecznym zatwierdzeniem scalenia zawsze należy przetestować aplikację, ponieważ rozwiązanie konfliktu może wprowadzić logiczny błąd.

39/52
git rebase – przepisanie historii

Rebase to alternatywa dla merge. Zamiast tworzyć commit scalenia, git "odcina" Twoją gałąź i przyczepia ją na samym czubku innej gałęzi. Efektem jest idealnie liniowa historia, bez zbędnych rozgałęzień. Wygląda to tak, jakbyś od razu zaczął pisać kod na najnowszej wersji projektu. To narzędzie dla estetów, którzy dbają o czystość logów.

  • Przenoszenie bazy: Zmieniasz "bazę", na której opierają się Twoje zmiany.
  • Czystość: Unikasz dziesiątek commitów typu "Merge branch 'main'".
  • Złota zasada: Nigdy nie używaj rebase na gałęziach publicznych (np. main), z których korzystają inni, bo zburzysz ich historię!
# Przykład 1: Przeniesienie zmian feature na aktualny stan develop user@dev:~/repo$ git switch feature user@dev:~/repo$ git rebase develop

Rebase różni się od merga tym, że zamiast tworzyć węzeł scalający, przepisuje historię, nakładając commity z bieżącej gałęzi na wierzch gałęzi docelowej. Każdy przenoszony commit otrzymuje nowy identyfikator SHA-1, nawet jeśli treść zmian pozostaje taka sama – zmienia się bowiem jego rodzic. Efektem jest idealnie liniowa historia, pozbawiona rozwidleń i merge commitów, co ułatwia jej przeglądanie i analizę.

Rebase jest szczególnie przydatny w gałęziach lokalnych przed wysłaniem zmian do repozytorium zdalnego – pozwala utrzymać czystą historię projektu. Nigdy nie należy jednak rebase'ować commitów, które zostały już wypchnięte i są współdzielone z innymi członkami zespołu, ponieważ przepisanie wspólnej historii powoduje chaos. Alternatywnym podejściem jest użycie git pull --rebase, które łączy pobranie zmian z rebase'em w jednym kroku. Złota zasada: rebase to potężne narzędzie dla lokalnej historii, merge – dla współdzielonej.

40/52
git rebase – porządkowanie commitów

Najpotężniejszą formą jest tryb interaktywny (-i). Pozwala on na "edytowanie" przeszłości przed wysłaniem jej na serwer. Możesz łączyć małe commity w jeden duży (squash), zmieniać ich kolejność, a nawet usuwać te, które uznasz za zbędne. To Twoje narzędzie do robienia porządku przed oddaniem pracy do recenzji (Code Review).

  • Zgniatanie: Łączy wiele commitów w jeden (np. series poprawek typu "typo fix").
  • Rewording: Pozwala zmienić opis starego commita, jeśli zrobiłeś w nim błąd językowy.
  • Fixup: Podobne do squash, ale automatycznie odrzuca wiadomość łączonego commita.
# Przykład 2: Interaktywne porządkowanie 5 ostatnich wpisów user@dev:~/repo$ git rebase -i HEAD~5

Tryb interaktywny rebase'a (git rebase -i) otwiera edytor z listą commitów do przetworzenia, gdzie każdemu można przypisać konkretną akcję. Najczęściej używane operacje to squash (scalenie commita z poprzednim) oraz reword (zmiana treści komunikatu). Pozwala to przekształcić serię chaotycznych commitów roboczych w logicznie uporządkowaną historię z czytelnymi opisami zmian.

W praktyce przed wysłaniem pull requesta warto wykonać interaktywny rebase, aby scalić drobne poprawki i tymczasowe commity w większe, znaczące jednostki pracy. Należy jednak pamiętać, że zmiana kolejności commitów może wywołać konflikty, jeśli późniejszy commit modyfikuje kod wprowadzony przez wcześniejszy. Dobrą praktyką jest wykonywanie rebase -i tylko na gałęziach lokalnych i upewnienie się, że stan końcowy przechodzi testy. Umiejętność porządkowania historii odróżnia doświadczonych użytkowników Gita od początkujących.

41/52
git reset – cofanie w czasie

git reset to jedno z najbardziej wszechstronnych, ale i groźnych poleceń. Pozwala na cofnięcie wskaźnika Twojej gałęzi do dowolnego momentu w przeszłości. Istnieją trzy główne sposoby jego działania, które różnią się tym, co dzieje się z Twoimi zmianami po "wycofaniu" commita.

  • --soft: Cofa commit, ale zostawia wszystkie zmiany w poczekalni (staging area). Idealne, gdy chcesz coś dopisać do ostatniego zapisu.
  • --mixed (domyślny): Cofa commit i wyjmuje zmiany z poczekalni, ale zostawia je na Twoim dysku. Możesz je teraz edytować od nowa.
  • Bezpieczeństwo: Resetowanie jest bezpieczne, dopóki nie użyjesz najbardziej drastycznej opcji...
# Przykład 1: Cofnięcie commita w celu dopisania zmian (Soft) user@dev:~/repo$ git reset --soft HEAD~1

Git reset to narzędzie do przesuwania wskaźnika HEAD, które działa na trzech poziomach określonych przez drzewa Gita: HEAD (ostatni commit), index (staging area) i working directory (katalog roboczy). Tryb --soft cofa tylko HEAD, pozostawiając zmiany w indexie i katalogu roboczym – idealne do ponownego zacommitowania plików. Tryb --mixed (domyślny) dodatkowo resetuje index, ale zachowuje zmiany w plikach, co często służy do wycofania plików ze stagingu.

Tryb --hard jest najpotężniejszy i najbardziej ryzykowny – przywraca wszystkie trzy drzewa do stanu wskazanego commita, bezpowrotnie usuwając niezacommitowane zmiany. Zrozumienie trzech drzew Gita (HEAD, index, working directory) jest kluczowe do świadomego używania reset. W codziennej pracy --mixed przydaje się do rozpakowania commita, a --soft do połączenia kilku commitów w jeden. Wartościową sztuczką jest git reset --soft HEAD~1, która cofa ostatni commit, ale zostawia wszystkie zmiany gotowe do ponownego zacommitowania.

42/52
git reset – tryb destrukcyjny

Opcja --hard to polecenie, które nie bierze jeńców. Przywraca projekt do wskazanego stanu i bezpowrotnie usuwa wszystko, co napisałeś po drodze. Używamy go tylko wtedy, gdy zupełnie pogubiliśmy się w kodzie i chcemy zacząć od nowa z punktu, o którym wiemy, że był dobry.

  • Ryzyko: Stracisz wszystkie niezapisane commitem zmiany na dysku.
  • Ostatnia deska ratunku: Często używane do "wyrównania" gałęzi lokalnej ze stanem na serwerze, gdy lokalna historia za bardzo nam się pomieszała.
# Przykład 2: Całkowite porzucenie zmian i powrót do stabilnej wersji z serwera user@dev:~/repo$ git reset --hard origin/main

Tryb --hard trwale usuwa niezapisane zmiany z katalogu roboczego i indexu, dlatego przed jego użyciem warto upewnić się, że wszystkie ważne modyfikacje zostały zacomitowane lub zachowane w inny sposób. Git przechowuje referencję ORIG_HEAD, która wskazuje poprzednią pozycję HEAD przed wykonaniem resetu, co daje pewną możliwość odzyskania stanu. Jednak jeśli zmiany nie były nigdy zacomitowane, git reset --hard usuwa je bezpowrotnie – nawet ORIG_HEAD nie pomoże.

W sytuacjach, gdy niebezpieczne zmiany zostały już wypchnięte do repozytorium zdalnego, należy użyć git revert zamiast resetu, ponieważ revert tworzy nowy commit cofający zmiany bez modyfikowania istniejącej historii. Bezpieczną alternatywą dla --hard jest git stash, który tymczasowo odkłada zmiany na później. Zawsze warto uruchomić git status i git diff przed wykonaniem resetu --hard, aby świadomie ocenić, co zostanie utracone. Doświadczeni programiści traktują tryb --hard z szacunkiem i używają go tylko wtedy, gdy są w stu procentach pewni, że chcą odrzucić wszystkie bieżące zmiany.

43/52
git switch – nawigacja między wersjami

Polecenie git switch to nowszy, czytelniejszy sposób na przemieszczanie się między różnymi "światami" (gałęziami) projektu. Zostało wprowadzone, aby rozdzielić funkcje starego polecenia checkout na dwie prostsze komendy: switch (do gałęzi) i restore (do plików). Dzięki temu praca z projektem stała się bardziej intuicyjna.

  • Ochrona pracy: git nie pozwoli Ci przełączyć gałęzi, jeśli masz niezapisane zmiany, które mogłyby zostać nadpisane.
  • Tworzenie: Opcja -c pozwala jednym ruchem stworzyć nową gałąź i natychmiast do niej wejść.
  • Czystość: To polecenie zajmuje się tylko gałęziami – nie musisz się martwić, że omyłkowo przywrócisz treść jakiegoś pliku.
# Przykład 1: Przejście na gałąź develop, aby zobaczyć postęp prac u innych user@dev:~/repo$ git switch develop # Przykład 2: Stworzenie nowej gałęzi dla pilnej poprawki i wejście na nią user@dev:~/repo$ git switch -c hotfix/api-v2-crash

Polecenie git switch zostało wprowadzone w Gicie 2.23 jako bardziej intuicyjna alternatywa dla git checkout, która rozwiewa wątpliwości dotyczące przełączania gałęzi. W przeciwieństwie do checkout, switch skupia się wyłącznie na nawigacji między gałęziami i nie obsługuje operacji przywracania plików. Flaga -c pozwala w jednym kroku utworzyć nową gałąź i natychmiast się na nią przełączyć, co znacznie przyspiesza codzienną pracę.

W praktyce git switch zastępuje około 80% przypadków użycia git checkout, co czyni go podstawowym narzędziem do poruszania się między gałęziami. W trybie detached HEAD warto od razu utworzyć nową gałąź poleceniem git switch -c new-branch, aby nie zgubić potencjalnych zmian. Dobrą praktyką jest używanie switch zamiast checkout w codziennej pracy, ponieważ jego interfejs jest bardziej przewidywalny i mniej podatny na błędy. Pamiętaj, że switch działa tylko na gałęziach i commitach – do przywracania plików służy odrębne polecenie git restore.

44/52
git switch – szybki powrót

Bardzo często pracujesz na zmianę na dwóch gałęziach (np. main i feature). git posiada skrót, który pozwala przeskakiwać między nimi bez wpisywania ich nazw. To oszczędza czas i pozwala na błyskawiczne porównywanie wyników pracy.

# Przykład 3: Powrót do poprzednio aktywnej gałęzi (jak przycisk "w tył" w radiu) user@dev:~/repo$ git switch -

Specjalna składnia git switch - pozwala błyskawicznie wrócić do poprzednio odwiedzonej gałęzi, co jest niezwykle pomocne przy częstym przełączaniu między dwoma gałęziami roboczymi. Mechanizm ten działa podobnie do skrótu cd - w powłoce systemowej, zapamiętując ostatnią lokalizację przed wykonaniem przełączenia. Kluczowa różnica między switch a restore polega na tym, że switch operuje na całym drzewie roboczym, podczas gdy restore dotyczy pojedynczych plików.

Szybki powrót za pomocą git switch - jest szczególnie przydatny podczas code review, gdy na przemian przeglądamy kod na gałęzi głównej i gałęzi funkcyjnej. W trybie detached HEAD warto utworzyć tymczasową gałąź, jeśli planujemy dłużej pracować nad konkretnym commitem. Należy pamiętać, że switch nie przenosi niezcommitowanych zmian między gałęziami – w razie konfliktu Git poprosi o ich przechowanie poleceniem git stash. Zachowanie to chroni przed przypadkowym wymieszaniem niezakończonej pracy z kodem na innej gałęzi.

45/52
git tag – kamienie milowe i wersje

Tagi to "zakładki" w historii Twojego projektu. Podczas gdy gałęzie są dynamiczne i zmieniają się z każdym commitem, tagi są nieruchome. Używamy ich do oznaczania konkretnych, ważnych momentów, takich jak wydanie nowej wersji stabilnej aplikacji. Kiedy powiesz "Mamy wersję 1.0", tworzysz taga v1.0, aby zawsze móc wrócić dokładnie do tego kodu, który dostali użytkownicy.

  • Lekki: Zwykły wskaźnik na commit (jak gałąź, która się nie rusza).
  • Opisany (Zalecane): Zawiera opis, datę i nazwisko autora taga. Jest trwalszy i lepiej opisany.
  • Dystrybucja: Tagi nie są wysyłane automatycznie przy push – musisz to zrobić celowo, aby serwer o nich wiedział.
# Przykład 1: Oznaczenie wersji produkcyjnej z profesjonalnym opisem user@dev:~/repo$ git tag -a v1.0.0 -m "Official release for customer X" # Przykład 2: Wypchnięcie wszystkich nowo stworzonych tagów na serwer user@dev:~/repo$ git push --tags

Tagi w Gicie peŁnią funkcję trwałych etykiet dla konkretnych commitów, najczęściej używanych do oznaczania wersji wydaniowych oprogramowania. Rozróż niamy tagi lekkie (lightweight), które są jedynie wskaźnikami do commita, oraz tagi adnotowane (annotated), które przechowują dodatkowe metadane – autora, datę, wiadomość i podpis GPG. Polecenie git tag bez argumentów wyświetla listę wszystkich tagów, a dodanie flagi -l "v1.*" pozwala filtrować tagi według wzorca.

Domyślnie operacja git push nie przesyła tagów do zdalnego repozytorium – należy to zrobić jawnie za pomocą git push origin v1.0 lub git push --tags dla wszystkich tagów. Tagi adnotowane są zalecane w projektach komercyjnych i open-source, ponieważ dostarczają pełnego kontekstu dla wydania, w tym daty i opisu zmian. W praktyce warto ustalić konwencję nazewnictwa tagów, np. semantic versioning (vMAJOR.MINOR.PATCH), aby łatwo identyfikować kolejne wersje.

46/52
git fetch (wypełnianie braków) – optymalizacja wielkich projektów

W dzisiejszych czasach repozytoria mogą ważyć wiele gigabajtów. Technika wypełniania braków jest realizowana przez zaawansowane opcje polecenia fetch (np. --refetch). Pozwala ona zaoszczędzić miejsce, pobierając na dysk tylko strukturę plików, a treść (obiekty blob) ściąga dopiero wtedy, gdy są naprawdę potrzebne. Wypełnianie braków służy do "dociągnięcia" tych brakujących fragmentów historii, gdy np. musisz wrócić do starej wersji projektu w celach diagnostycznych.

  • Oszczędność czasu: Nie musisz czekać godziny na pobranie 10GB danych, których nigdy nie otworzysz.
  • Elastyczność: Pobierasz tylko to, co aktualnie edytujesz.
  • Wymagania: Działa najlepiej z nowoczesnymi serwerami git (np. GitHub), które obsługują filtry bloba.
# Przykład 1: Pobranie brakujących danych dla pełnej historii user@dev:~/repo$ git fetch --refetch # Przykład 2: Selektywne pobranie głębszej historii dla analizy błędów user@dev:~/repo$ git fetch --deepen=50

Flaga --deepen w poleceniu git fetch pozwala pogłębić płytkie repozytorium (shallow clone) o określoną liczbę commitów, co jest przydatne, gdy potrzebujemy starszej historii niż początkowo pobrana. Opcja --refetch wymusza całkowite ponowne pobranie wszystkich obiektów z repozytorium zdalnego, ignorując lokalny cache – przydaje się przy uszkodzeniu lokalnych danych. Odświeżanie zdalnych gałęzi śledzących odbywa się automatycznie podczas git fetch, aktualizując referencje w przestrzeni refs/remotes.

Zrozumienie referencji zdalnych (refs/remotes/origin/*) jest kluczowe – to one przechowują stan gałęzi zdalnych w momencie ostatniego pobrania, a nie rzeczywisty stan na serwerze. W przypadku płytkich klonów warto rozważyć użycie git fetch --unshallow, które przekształca shallow clone w pełne repozytorium z całą historią. Dobrą praktyką jest regularne wykonywanie git fetch --prune, które usuwa zdalne gałęzie śledzące, które zostały już usunięte na serwerze.

47/52
Współpraca

Synchronizacja ze światem zewnętrznym

Współpraca zespołowa w Git opiera się na synchronizacji lokalnych repozytoriów z centralnym repozytorium zdalnym. Polecenie git push wysyła lokalne zmiany na serwer, natomiast git pull pobiera zmiany innych członków zespołu i scala je z lokalną gałęzią roboczą. Regularna synchronizacja zapobiega powstawaniu konfliktów i zapewnia aktualność kodu u wszystkich programistów.

Pull requesty (lub merge requesty) stanowią standardowy mechanizm przeglądu kodu przed włączeniem go do głównej gałęzi projektu. Proces code review pozwala na wykrycie błędów, wymianę wiedzy między członkami zespołu oraz utrzymanie wysokiej jakości kodu. Git wspiera ten proces poprzez mechanizmy takie jak GitHub Flow, który definiuje sprawdzony wzorzec współpracy dla współczesnych zespołów programistycznych.

48/52
git fetch – pobieranie informacji

Praca w zespole wymaga wiedzy o tym, co robią inni. git fetch to polecenie, które "dzwoni" do serwera (np. GitHuba) i pyta: "Co się zmieniło?". Pobiera ono wszystkie nowe informacje, gałęzie i commity kolegów, ale nie zmienia niczego w Twoich plikach roboczych. To bezpieczny sposób na podejrzenie cudzej pracy bez ryzyka poplątania własnego kodu.

  • Bezpieczeństwo: Twoje pliki na dysku pozostaną nienaruszone.
  • Aktualizacja mapy: git uaktualnia tzw. "gałęzie zdalne" (np. origin/main), abyś widział, gdzie na osi czasu są inni.
  • Przegląd: Po fetch możesz użyć git diff origin/main, by zobaczyć różnice przed ich połączeniem.
# Przykład 1: Sprawdzenie co nowego na serwerze bez integracji user@dev:~/repo$ git fetch origin # Przykład 2: Pobranie konkretnej gałęzi, by przygotować się do jej sprawdzenia user@dev:~/repo$ git fetch origin feature-api

Podstawowa funkcja git fetch polega na pobraniu zmian z repozytorium zdalnego bez automatycznego scalania ich z lokalną gałęzią roboczą, co daje pełną kontrolę nad procesem integracji. W przeciwieństwie do git pull, fetch jedynie aktualizuje zdalne gałęzie śledzące (origin/main, origin/feature), pozostawiając lokalną gałąź roboczą nietkniętą. Dzięki temu możemy w bezpieczny sposób podejrzeć, co zmieniło się na serwerze, zanim zdecydujemy się włączyć te zmiany do własnego kodu.

Możliwość bezpiecznej inspekcji cudzej pracy przed integracją jest jedną z najważniejszych zalet fetch – możemy przejść na gałąź śledzącą, przejrzeć zmiany i zdecydować, czy i jak je scalić. Fetch jest również bezpieczniejszy niż pull w sytuacjach, gdy pracujemy nad niestabilną gałęzią lub gdy nasze lokalne zmiany nie są jeszcze gotowe do wysłania. W codziennej pracy dobrym nawykiem jest wykonanie git fetch przed rozpoczęciem nowego zadania, aby mieć świadomość najnowszych zmian na serwerze.

49/52
git pull – pobieranie i scalanie

Polecenie git pull to w rzeczywistości dwie komendy w jednej: fetch (pobierz dane) oraz merge (połącz je z moim kodem). To najszybszy sposób na zaktualizowanie swojej pracy o najnowsze poprawki z serwera. Jest to codzienna rutyna każdego programisty pracującego w zespole – zazwyczaj zaczynamy i kończymy dzień tym poleceniem.

  • Konwojowanie zmian: Automatycznie dociąga brakujące commity i próbuje je wkomponować w Twoją aktualną gałąź.
  • Rebase Pull: Możesz użyć opcji --rebase, aby Twoje lokalne commity zawsze były "na górze" historii, co zapobiega powstawaniu zbędnych merge-commitów.
  • Konflikty: Ponieważ pull zawiera w sobie merge, może on wywołać konflikty, które będziesz musiał rozwiązać ręcznie.
# Przykład 1: Standardowa aktualizacja Twojej gałęzi roboczej user@dev:~/repo$ git pull # Przykład 2: Profesjonalna aktualizacja z zachowaniem czystej historii user@dev:~/repo$ git pull --rebase origin main

Polecenie git pull łączy w sobie dwie operacje: git fetch (pobranie zmian z serwera) oraz git merge (scalenie ich z lokalną gałęzią), co przyspiesza codzienną pracę, ale niesie ryzyko nieoczekiwanych konfliktów. Domyślna strategia pull to merge, która tworzy dodatkowy commit scalający w historii – może to prowadzić do zaśmiecenia historii w projektach z wieloma współpracownikami. Alternatywą jest git pull --rebase, który nakłada lokalne zmiany na wierzch pobranych commitów, utrzymując liniową i czystą historię projektu.

Pull z rebase jest szczególnie zalecany w gałęziach funkcyjnych, gdzie ważna jest czytelna historia bez zbędnych commitów scalających. W przypadku konfliktów podczas rebase, Git zatrzymuje proces i pozwala rozwiązać konflikty commit po commicie, co daje większą kontrolę niż w przypadku merge. Aby uniknąć przypadkowego merge, warto ustawić domyślną strategię pull na rebase za pomocą git config pull.rebase true. Pamiętaj jednak, że rebase nie powinien być stosowany na gałęziach współdzielonych z innymi członkami zespołu – w takich przypadkach bezpieczniejszy jest standardowy merge.

50/52
git push – wysyłanie efektów pracy

Kiedy Twoja funkcja jest już gotowa, przetestowana i zatwierdzona (commit), czas pochwalić się nią światu. Polecenie git push wysyła Twoje lokalne commity na zdalny serwer. Dopiero od tego momentu Twoi współpracownicy mogą pobrać Twoje zmiany i z nich korzystać. To moment, w którym Twoja praca staje się częścią wspólnego projektu.

  • Wymagania: Musisz mieć najnowszą wersję kodu z serwera (zrobić pull), zanim git pozwoli Ci wypchnąć własne zmiany.
  • Upstream: Opcja -u pozwala zapamiętać, do której gałęzi na serwerze ma trafiać kod z Twojej gałęzi lokalnej.
  • Bezpieczeństwo: git nie pozwoli Ci nadpisać cudzej pracy, chyba że użyjesz bardzo niebezpiecznej opcji "force" (nie zalecane!).
# Przykład 1: Wysyłanie ukończonej funkcji na serwer (gałąź main) user@dev:~/repo$ git push origin main # Przykład 2: Pierwsze wysłanie nowej gałęzi z ustawieniem powiązania user@dev:~/repo$ git push -u origin feature/new-ui

Komenda git push to moment, w którym lokalna praca trafia do zdalnego repozytorium, stając się dostępna dla reszty zespołu. Bez niej wszystkie zmiany pozostają wyłącznie na naszym komputerze, co uniemożliwia współpracę. Flaga -u (--set-upstream) jest szczególnie ważna przy pierwszym pushowaniu nowej gałęzi – zapamiętuje połączenie między lokalną a zdalną gałęzią, co upraszcza kolejne wywołania.

Gdy remote zawiera nowsze zmiany, push zostanie odrzucony – to mechanizm ochronny zapobiegający nadpisaniu cudzej pracy. W takiej sytuacji należy najpierw pobrać zmiany komendą git pull i rozwiązać ewentualne konflikty. Opcja --force-with-lease jest bezpieczniejsza od --force, ponieważ sprawdza, czy nikt nie dodał nowych commitów od momentu ostatniej synchronizacji. Stosuj ją tylko na własnych gałęziach, nigdy na wspólnych.

51/52
Dobre praktyki pracy z git

Oto złote zasady, które sprawią, że praca z gitem będzie przyjemnością, a nie walką:

  • Rób atomowe commity: Jeden commit powinien dotyczyć jednej, konkretnej zmiany (np. naprawa jednego błędu).
  • Pisz sensowne wiadomości: Opisuj "dlaczego" coś zmieniłeś, a nie tylko "co". Inni będą Ci wdzięczni za rok.
  • Pracuj na gałęziach: Nigdy nie programuj bezpośrednio na main. Stwórz gałąź, przetestuj, a potem scalaj.
  • Często synchronizuj: Rób pull regularnie, aby uniknąć gigantycznych konfliktów na koniec tygodnia.
  • Sprawdzaj przed zapisem: Zawsze używaj git status i git diff przed zrobieniem commita.

Regularne commitowanie z czytelnymi komunikatami to fundament dobrej współpracy – każdy commit powinien opisywać jedno logiczne uzasadnienie zmiany. Gałęzie funkcyjne (feature branches) izolują pracę nad nowymi funkcjonalnościami, dzięki czemu gałąź główna pozostaje stabilna. Plik .gitignore oszczędza czas i nerwy, automatycznie pomijając pliki tymczasowe, zależności i konfiguracje IDE.

Przeglądanie własnych zmian przed commitowaniem (git diff --cached) pomaga wychwycić literówki, zbędne pliki czy pozostawione znaczniki debugowania. Nigdy nie używaj --force na wspólnych gałęziach, takich jak main czy develop – może to bezpowrotnie usunąć czyjąś pracę. Warto też ustalić z zespołem konwencję nazewnictwa gałęzi i commitów, np. według Conventional Commits.

52/52
Podsumowanie i gratulacje

Gratulacje! Poznałeś 23 najważniejsze polecenia programu git w teorii i praktyce.

Opanowanie tych komend pozwoli Ci na profesjonalną pracę w każdym zespole programistycznym na świecie.

Pamiętaj: git to nie zestaw komend do nauki na pamięć, to sposób myślenia o historii Twojego projektu.

Dziękuję za uwagę! Czy masz jakieś pytania?

Zapraszam do wspólnego testowania przykładów w terminalu.

Gratulacje – opanowałeś podstawowe komendy i mechanizmy Gita, co daje Ci solidne fundamenty do codziennej pracy z kodem źródłowym. Najważniejsze koncepcje: repozytorium, commit, gałąź, merge, push i pull – są uniwersalne i przenoszą się na dowolny hosting (GitHub, GitLab, Bitbucket). Kluczem do biegłości jest regularna praktyka i stopniowe poznawanie bardziej zaawansowanych narzędzi.

Dalsze kroki: zapoznaj się z rebase interactive, cherry-pick, bisect oraz submodułami – to naturalna ścieżka rozwoju. Świetne darmowe zasoby to dokumentacja Gita, interaktywny kurs Learn Git Branching oraz książka Pro Git Scotta Chacona. Pamiętaj, że nawet zaawansowani użytkownicy wciąż się uczą – Git to narzędzie, które rośnie wraz z Twoimi potrzebami. Powodzenia w dalszej przygodzie z kontrolą wersji!