Pierwsze kroki z Git: commit, branch i pull request w praktyce

1
319
4.3/5 - (3 votes)

Nawigacja:

Po co w ogóle Git? Krótkie uporządkowanie tematu

Od „projekt_final_v3_poprawiony” do świadomego wersjonowania

Pierwsze spotkanie z kontrolą wersji zwykle zaczyna się od chaosu w katalogach. Pliki w stylu projekt_final_ostateczny_v3_poprawiony.doc, kopiowanie folderów na pendrive, wysyłanie zipów mailem – to klasyczne symptomy braku narzędzia do wersjonowania. Każda większa zmiana to nowy plik, każda poprawka – nowa kopia. Po tygodniu nikt nie pamięta, gdzie jest „ta właściwa” wersja.

Git rozwiązuje ten problem u źródła. Zamiast ręcznie nazywać pliki i katalogi, tworzysz historię zmian w repozytorium. Każdy zestaw zmian trafia do commita. Commit jest migawką (snapshotem) stanu projektu w konkretnym momencie. Do każdego commita masz:

  • unikalny identyfikator,
  • informację, kto i kiedy go zrobił,
  • opis zmiany (wiadomość commita),
  • pełen stan plików po zmianie.

Dzięki temu nie trzeba tworzyć nowych katalogów dla każdej wersji. W jednym repozytorium trzymasz całą historię, możesz do niej wrócić w każdej chwili, porównać dwie wersje pliku, sprawdzić, co zmieniło się wczoraj vs tydzień temu i w razie wpadki cofnąć błędny krok.

Git jako historia projektu, nie tylko backup

Git często jest mylony z backupem. Owszem, stojące za nim serwisy jak GitHub czy GitLab ułatwiają przechowywanie kopii projektu w chmurze, ale najważniejsze jest coś innego – pełna, szczegółowa historia zmian. Backup odpowiada na pytanie: „Czy mam kopię pliku?”. Git odpowiada na pytanie: „Co dokładnie zmieniło się między poniedziałkiem a wtorkiem, kto to zrobił i dlaczego?”.

Git przechowuje historię jako łańcuch commitów. Każdy commit:

  • wie, na jakiej wcześniejszej wersji się opiera,
  • ma czytelną wiadomość opisu,
  • zawiera stan wszystkich śledzonych plików.

To podejście sprawia, że naprawa błędów jest prostsza – bez dramatycznego cofania całych folderów z backupu. Możesz porównać konkretne commity, zobaczyć, która linijka kodu wywołała błąd, i w razie potrzeby przywrócić dokładnie ten fragment.

Lokalne a zdalne repozytorium – gdzie żyje Twój kod

Git jest systemem rozproszonym. To ważne, bo oznacza, że pełne repozytorium masz na swoim komputerze. Historia, branche, commity – wszystko jest lokalnie, bez konieczności ciągłego łączenia się z serwerem. Pracujesz bez internetu, commitujesz, tworzysz branche i porównujesz zmiany.

Dopiero do współpracy dochodzi pojęcie remote – zdalnego repozytorium. Zwykle hostuje je:

  • GitHub,
  • GitLab,
  • Bitbucket,
  • wewnętrzny serwer Git w firmie.

Zdalne repozytorium jest wspólnym punktem odniesienia dla zespołu. Każdy ma lokalną kopię (po git clone), wykonuje commity u siebie, a potem wysyła je na serwer (git push). Inni pobierają zmiany (git pull) i łączą ze swoją pracą. Różnica między lokalnym a zdalnym Git jest kluczowa – commit robisz lokalnie, a dopiero później decydujesz, co poleci do innych.

Minimalny zestaw pojęć przed pierwszym commitem

Żeby sprawnie wejść w „Pierwsze kroki z Git: commit, branch i pull request w praktyce”, przydaje się krótki słownik:

  • Repozytorium (repo) – katalog projektu zarządzany przez Git. Zawiera kod, pliki i ukrytą strukturę .git z historią.
  • Commit – jednostka zmiany. Zbiór modyfikacji zapisanych w historii, z opisem i autorem.
  • Branch (gałąź) – linia rozwoju projektu. Pozwala pracować nad funkcją niezależnie od głównej gałęzi.
  • Remote – zdalne repozytorium, zwykle na GitHubie lub GitLabie. Standardowa nazwa głównego remote to origin.
  • Pull request (Merge request) – prośba o włączenie zmian z jednej gałęzi do innej, z miejscem na dyskusję i review.

Po opanowaniu tych pięciu pojęć znika większość początkowego stresu. Git przestaje być czarną skrzynką i zaczyna działać jak przewidywalne narzędzie do zarządzania historią projektu.

Samodzielnie czy w zespole – gdzie Git świeci najjaśniej

Git świetnie sprawdza się także solo – pozwala wrócić do działającego stanu sprzed eksperymentu, przetestować dwie wersje rozwiązania i nie bać się większych refactoringów. Przy kilku osobach korzyści rosną wykładniczo: każdy ma swoją gałąź, zmiany trafiają przez pull requesty, a główna gałąź main (lub master) jest stabilna.

W otwartych projektach open source Git jest absolutnym standardem. Workflow typu „fork → branch → commit → pull request” umożliwia pracę setkom osób na jednym kodzie bez ciągłego nadpisywania się nawzajem.

Przygotowanie środowiska i pierwsze repozytorium

Instalacja Git i szybki test działania

Na początek trzeba mieć Git w systemie. Na szczęście instalacja jest jednorazowa i szybka.

  • Windows: pobierz instalator z git-scm.com i przejdź kreator z domyślnymi ustawieniami. Zainstaluje się m.in. Git Bash.
  • macOS: najprościej zainstalować Xcode Command Line Tools poleceniem xcode-select --install, albo użyć Homebrew: brew install git.
  • Linux: zależnie od dystrybucji, np. sudo apt install git w Debian/Ubuntu, sudo dnf install git w Fedora.

Po instalacji otwórz terminal (lub Git Bash na Windows) i wpisz:

git --version

Jeśli zobaczysz wersję (np. git version 2.44.0), wszystko jest gotowe. Jeśli nie – terminal nie widzi Gita i trzeba poprawić instalację lub zmienne środowiskowe.

Konfiguracja użytkownika – kto robi commity

Git zapisuje przy każdym commicie autora – imię/nick i adres e-mail. Konfiguracja jest dwupoziomowa:

  • globalna – dotyczy wszystkich repozytoriów użytkownika,
  • lokalna – dotyczy konkretnego repozytorium i może nadpisać globalną.

Na początek wystarczy konfiguracja globalna:

git config --global user.name "Jan Kowalski"
git config --global user.email "jan.kowalski@example.com"

Aktualną konfigurację da się podejrzeć poleceniem:

git config --list

W projektach firmowych adres e-mail zwykle dopasowuje się do konta na GitHubie lub GitLabie. Dzięki temu platforma poprawnie przypisuje commity do konkretnego użytkownika.

Zakładanie katalogu projektu i inicjalizacja repozytorium

Minimalny workflow na start to:

  1. Załóż katalog na projekt.
  2. Przejdź do niego w terminalu.
  3. Zainicjuj repozytorium Git.

Przykład:

mkdir moj-projekt
cd moj-projekt
git init

Polecenie git init tworzy ukryty folder .git. To tam Git zapisuje całą historię, konfigurację i metadane. Od tej chwili katalog jest repozytorium. Usunięcie .git „odgitowuje” folder – pliki zostaną, ale historia zniknie.

Na początku stan repozytorium jest pusty – Git nie śledzi jeszcze żadnych plików. To widać po git status, do którego za chwilę wrócimy.

Różnica między git init a git clone

Dwa polecenia są często mylone: git init i git clone. Pełnią inne role.

PolecenieCelTypowa sytuacja użycia
git initTworzy nowe, puste repozytorium w istniejącym katalogu.Start zupełnie nowego projektu, który jeszcze nie istnieje nigdzie indziej.
git clone <url>Pobiera istniejące repozytorium (kod + historię) ze zdalnego serwera.Dołączenie do istniejącego projektu na GitHubie/GitLabie.

Jeśli ktoś w zespole założył już repozytorium na GitHubie, nie używasz git init, tylko:

git clone https://github.com/organizacja/moj-projekt.git
cd moj-projekt

Dzięki temu od razu dostajesz pełne repozytorium z historią, branchami i ustawionym remote origin.

Dwóch programistów omawia kod na monitorze w biurowym open space
Źródło: Pexels | Autor: Mikhail Nilov

Pierwsze pliki i pierwszy commit – od zmian do historii

Dodanie pierwszego pliku i czytanie git status

Po utworzeniu repozytorium czas dorzucić pierwszy plik. Niech to będzie prosty przykład:

echo "# Mój pierwszy projekt" > README.md

Teraz warto od razu sprawdzić, jak Git widzi zmiany:

git status

Typowy komunikat na tym etapie:

On branch main

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        README.md

nothing added to commit but untracked files present (use "git add" to track)

Kluczowe elementy:

  • On branch main – jesteś na gałęzi main.
  • No commits yet – repozytorium nie ma jeszcze historii.
  • Untracked files – pliki, których Git jeszcze nie śledzi (czyli nie ma ich w żadnym commicie).

Git dzieli pliki na trzy podstawowe kategorie:

  • untracked – nowo utworzone, jeszcze nie dodane do Gita,
  • tracked, modified – znane Gitowi, ale z nierozliczonymi zmianami,
  • staged – przygotowane do commita, siedzą w tzw. staging area.

Staging area – po co jest git add

Zanim zmiany trafią do commita, przechodzą przez obszar zwany staging area (indeks). To filtr, który pozwala zdecydować, które zmiany trafią do najbliższego commita. Nie wszystko trzeba commitować naraz.

Dodanie pojedynczego pliku:

git add README.md

Dodanie wszystkich nowych i zmienionych plików w bieżącym katalogu:

git add .

Po git add README.md, polecenie git status pokaże:

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   README.md

„Changes to be committed” oznacza, że te zmiany trafią do najbliższego commita. Można to traktować jak koszyk przed kasą – do koszyka wrzucasz tylko to, co chcesz „zaksięgować” w danym kroku.

Pierwszy commit krok po kroku

Gdy staging area zawiera to, co trzeba, pora na commit. Minimalne polecenie wygląda tak:

git commit -m "Dodaj README z opisem projektu"

Co się dzieje przy commicie:

  • Git zapisuje aktualny stan wszystkich plików w staging area jako nową migawkę.
  • Tworzy unikalne ID commita (hash).
  • Zapisuje autora, datę, wiadomość i wskaźnik do poprzedniego commita.
  • Przesuwa wskaźnik gałęzi (np. main) na ten nowy commit.

Po pierwszym commicie git status pokaże czysty stan:

On branch main
nothing to commit, working tree clean

Czysty stan oznacza, że wszystkie śledzone pliki są w tej samej wersji co ostatni commit. Od tego momentu każdy kolejny commit będzie kolejnym krokiem w historii projektu.

Jak pisać sensowne wiadomości commitów

Commit bez dobrego opisu szybko traci wartość. Poprawna wiadomość powinna zwięźle odpowiadać na pytanie: co zostało zrobione. Kilka praktycznych zasad:

  • Stosuj czasownik w trybie rozkazującym: „Dodaj”, „Popraw”, „Usuń”, „Zmień”.
  • Pisz krótko: 50–72 znaki w pierwszej linii w zupełności wystarczy.
  • Podgląd historii – git log, git show, git diff

    Commit działa dopiero wtedy, gdy umiesz z niego korzystać. Trzeba umieć podejrzeć, co i kiedy zostało zmienione.

    Najprostszy podgląd historii:

git log

Standardowy widok bywa rozwlekły. W praktyce częściej przydają się skrócone formaty:

git log --oneline
git log --oneline --graph --decorate --all
  • --oneline – każdy commit w jednym wierszu.
  • --graph – prosty „rysunek” gałęzi z lewej.
  • --decorate – pokazuje nazwy branchy i tagów przy commitach.
  • --all – uwzględnia wszystkie gałęzie, nie tylko bieżącą.

Szczegóły pojedynczego commita (po ID lub skrócie ID z git log):

git show 1a2b3c4d

To polecenie pokazuje autora, datę, wiadomość i pełny diff – co dokładnie zostało zmienione w tym kroku.

Gdy chcesz porównać stan roboczy z ostatnim commitem:

git diff

Żeby porównać dwa konkretne commity:

git diff 1a2b3c4d 5e6f7a8b

W codziennej pracy git diff jest prostym „lupą” na zmiany przed commitem i przed review.

Cofanie się z twarzą – git restore i git reset (lokalnie)

Każdy robi błędne commity i złe zmiany. Klucz w tym, żeby umieć się z nich wycofać, zanim trafią na zdalne repozytorium.

Kilka podstawowych scenariuszy:

  • Wycofanie zmian w pliku w katalogu roboczym (wróć do ostatniego commita):
git restore nazwa_pliku
  • Wyjęcie pliku ze staging area (pozostaw zmiany, ale niech nie będą przygotowane do commita):
git restore --staged nazwa_pliku
  • Całkowite wyczyszczenie staging area (zostaw zmiany w katalogu roboczym):
git reset

Gdy commit jest świeży, tylko na Twoim komputerze, można go też po prostu skasować:

git reset --soft HEAD~1

Ta wersja przenosi zmiany z ostatniego commita z powrotem do staging area. Oznacza to: commit znika z historii, ale kod zostaje gotowy do ponownego zakomitowania z lepszą wiadomością lub z poprawkami.

Ignorowanie zbędnych plików – .gitignore w boju

Po co w ogóle .gitignore

Repozytorium ma trzymać kod i konfiguracje niezbędne do odtworzenia projektu, a nie wszystko, co przypadkiem wygenerował system. Logi, katalogi z zależnościami, pliki tymczasowe IDE – to nie należy do historii.

Przykład z życia: ktoś wrzucił do Gita node_modules. Repozytorium urosło do gigabajtów, klonowanie trwało wieki, a i tak każdy po klonie musiał odpalać npm install. Jeden plik .gitignore rozwiązałby ten problem od początku.

Tworzenie pliku .gitignore

W katalogu głównym repozytorium tworzysz plik tekstowy o nazwie .gitignore:

touch .gitignore

Przykładowa zawartość dla projektu Node.js:

node_modules/
dist/
.env
npm-debug.log
.DS_Store
  • Wpis kończący się / oznacza katalog.
  • Kropka na początku (.env, .DS_Store) to standard dla plików ukrytych.

Wzorce stosowane w .gitignore są elastyczne. Można używać znaków wieloznacznych:

*.log
*.tmp
*.swp

Jak działa .gitignore w praktyce

Kluczowa zasada: .gitignore nie działa wstecz na pliki już śledzone. Jeśli jakiś plik został kiedyś dodany do repozytorium, samo dopisanie go do .gitignore nie usunie go z historii.

Prosty workflow:

  1. Dodaj wzorzec do .gitignore.
  2. git add .gitignore
  3. git commit -m "Dodaj podstawowe reguły .gitignore"

Od tej pory nowe pliki pasujące do wzorca nie będą się pojawiały w git status jako untracked.

Jeśli jakiś plik został wcześniej dodany i chcesz go „odgitować”, ale zostawić fizycznie na dysku:

git rm --cached nazwa_pliku
git commit -m "Przestań śledzić nazwa_pliku, dodaj do .gitignore"

Przy dużych projektach zwykle stosuje się jeden wspólny .gitignore na górze repo plus opcjonalne lokalne ignorowania dla IDE w katalogach typu .idea/ czy .vscode/.

Gotowe szablony .gitignore

Nie ma sensu wymyślać reguł od zera. GitHub utrzymuje repozytorium z gotowymi szablonami .gitignore dla różnych języków i frameworków:

https://github.com/github/gitignore

Przykład: dla projektów w Pythonie można wziąć plik Python.gitignore i dostosować do swoich potrzeb. To oszczędza czas i redukuje ryzyko przypadkowego wrzucenia np. plików z wirtualnego środowiska (venv/).

Dwóch programistów przy laptopach omawia kod w biurze
Źródło: Pexels | Autor: olia danilevich

Branch w praktyce – po co, kiedy i jak tworzyć gałęzie

Czym jest branch w Git w praktycznym sensie

Branch to po prostu ruchomy wskaźnik na konkretny commit w historii. Gdy tworzysz nową gałąź, nie kopiujesz całego projektu. Dodajesz tylko nowy „znacznik” w tym samym drzewie commitów.

Dzięki temu możesz pracować nad eksperymentem lub nową funkcją, nie dotykając stabilnej gałęzi main. Łączenie tej pracy z powrotem do głównej linii to później kwestia jednego merge’a.

Tworzenie i przełączanie się między branchami

Tworzenie nowej gałęzi w oparciu o bieżący stan:

git branch feature/logowanie

Przełączenie się na nową gałąź:

git switch feature/logowanie

Skrót 2 w 1 – utwórz i przełącz:

git switch -c feature/logowanie

Lista dostępnych branchy lokalnie:

git branch

Znak gwiazdki przy nazwie wskazuje aktywną gałąź. To na niej będą lądować nowe commity.

Typowe rodzaje gałęzi w projektach

Prosty, praktyczny podział:

  • main/master – stabilna bazowa gałąź produkcyjna.
  • feature/… – nowe funkcje, większe zmiany w kodzie.
  • bugfix/… – poprawki błędów.
  • hotfix/… – pilne poprawki na produkcję.

Nazwy gałęzi powinny jasno mówić, nad czym jest praca, np. feature/rejestracja-uzytkownika zamiast nowa-funkcja. Po kilku tygodniach to robi dużą różnicę.

Branch a katalog – czym to się różni

Częsty błąd początkujących: osobne katalogi na różne wersje projektu zamiast branchy. W Git nie trzeba dublować kodu na dysku. Wszystkie wersje siedzą w jednym repozytorium, a branch określa, jaki zestaw plików masz aktualnie w katalogu roboczym.

Jeśli w trakcie przełączania brancha Git zgłasza konflikt, to zwykle znaczy, że masz niezacommitowane zmiany. Wtedy:

  • albo commitujesz je na bieżącej gałęzi,
  • albo odkładasz na bok przez git stash,
  • albo świadomie odrzucasz (git restore).

Porządkowanie gałęzi – usuwanie po zakończeniu pracy

Po zmergowaniu gałęzi i wdrożeniu zmian nie ma sensu trzymać starych branchy w nieskończoność.

Usunięcie lokalnej gałęzi po tym, jak jej zmiany trafiły do main:

git branch -d feature/logowanie

Jeśli Git krzyczy, że gałąź nie jest zmergowana, a jesteś pewny, że chcesz ją usunąć, używasz wersji wymuszającej:

git branch -D feature/logowanie

Merge i rebase – łączenie pracy bez czarów

Podstawowy merge – łączenie gałęzi

Gdy funkcja na gałęzi feature/… jest gotowa, trzeba ją wprowadzić do stabilnej linii, zwykle main. Najpierw przełączasz się na gałąź, do której chcesz wciągnąć zmiany:

git switch main
git merge feature/logowanie

Jeśli odgałęziłeś się od aktualnego main i nikt w międzyczasie tam nie commitował, Git zrobi tzw. fast-forward – po prostu przesunie wskaźnik main na ostatni commit z gałęzi feature/logowanie.

Gdy w obu gałęziach powstały nowe commity, Git utworzy commit scalający (merge commit). Historia w git log --graph zacznie mieć rozgałęzienia, ale wszystko będzie nadal spójne.

Konflikty merge – co się dzieje i jak to ogarnąć

Konflikt pojawia się, gdy te same fragmenty pliku zostały zmienione w dwóch gałęziach i Git nie jest w stanie sam zdecydować, którą wersję wybrać.

Przykład komunikatu:

Auto-merging app.js
CONFLICT (content): Merge conflict in app.js
Automatic merge failed; fix conflicts and then commit the result.

Plik z konfliktem będzie zawierał znaczniki:

<<<<<<< HEAD
wersja z gałęzi, na której jesteś (np. main)
=======
wersja z gałęzi scalanej (feature/logowanie)
>>>>>>> feature/logowanie

Kroki rozwiązania:

  1. Otwórz plik w edytorze.
  2. Znajdź znaczniki konfliktu <<<<<<<, =======, >>>>>>>.
  3. Ręcznie wybierz lub połącz obie wersje.
  4. Usuń znaczniki.
  5. git add app.js
  6. git commit – commit scalający z rozwiązanymi konfliktami.

Większość IDE i narzędzi (VS Code, IntelliJ, meld, KDiff3) ma wbudowane widoki do wygodnego rozwiązywania konfliktów z trzema panelami.

Rebase – przepisanie historii zamiast merge commita

Rebase nie łączy historii przez dodatkowy commit, tylko „przestawia” Twoje commity tak, jakby zostały zrobione później, na aktualnym stanie innej gałęzi.

Scenariusz:

  1. Jesteś na feature/logowanie.
  2. W międzyczasie ktoś zrobił nowe commity na main.

Chcesz, żeby Twoja gałąź była oparta na aktualnym main:

git fetch origin
git switch feature/logowanie
git rebase origin/main

Git weźmie Twoje commity z gałęzi feature/logowanie, tymczasowo je zdejmie, zaktualizuje bazę do origin/main, a potem „ponownie odtworzy” Twoje zmiany na nowej podstawie. Efekt: czysta, liniowa historia, jakbyś zaczął pracę dopiero po ostatnich zmianach w main.

Merge vs rebase – kiedy co wybrać

Prosty praktyczny podział:

  • merge – gdy chcesz zachować prawdziwą historię, kto był kiedy odgałęziony od kogo; typowy przy scalaniu branchy w repo współdzielonym.
  • rebase – gdy czytelność i liniowość historii jest ważniejsza niż oryginalne rozgałęzienia; typowo na lokalnych branchach przed wypchnięciem na serwer.

Jedna kluczowa zasada: nie rób rebase na gałęziach, które inni już pobrali i na nich pracują. Przepisywanie historii współdzielonej gałęzi powoduje zamieszanie (inne hashe commitów, konieczność git pull --rebase z ręcznym ogarnięciem).

Lokalnie kontra zdalnie – praca z GitHubem i GitLabem

Dodanie zdalnego repozytorium do istniejącego projektu

Załóżmy, że masz już lokalne repo z commitami i chcesz je wypchnąć na GitHuba lub GitLaba. Najpierw tworzysz puste repo na serwerze (bez inicjalizacji README, żeby uniknąć konfliktów). Potem w katalogu projektu:

git remote add origin git@github.com:uzytkownik/nazwa-repo.git

Sprawdzenie, czy zdalne repo zostało podpięte:

git remote -v

Pierwsze wypchnięcie aktualnej gałęzi, np. main:

git push -u origin main

Opcja -u ustawia tzw. upstream – od teraz wystarczy git push i git pull bez podawania origin main.

Klucze SSH vs HTTPS – jak się łączyć

Dostęp do GitHuba/GitLaba można skonfigurować na dwa popularne sposoby:

  • HTTPS – adres w stylu https://github.com/uzytkownik/repo.git. Każdy push/pull uwierzytelniasz hasłem lub tokenem (osobistym), często też managerem poświadczeń.
  • SSH – adres w stylu git@github.com:uzytkownik/repo.git. Raz dopinasz klucz publiczny do konta i potem pracujesz bez podawania hasła.

W codziennej pracy wygodniejszy bywa SSH. Minimalny zestaw kroków:

ssh-keygen -t ed25519 -C "twoj_email@example.com"
# domyślna ścieżka, bez hasła lub z, zależnie od preferencji
cat ~/.ssh/id_ed25519.pub

Zawartość pliku id_ed25519.pub wklejasz w ustawieniach kluczy SSH na GitHubie lub GitLabie. Potem wystarczy:

ssh -T git@github.com

Jeśli zobaczysz komunikat w stylu „Hi <user>! You’ve successfully authenticated…”, konfiguracja działa.

Klasyczny workflow: clone, pull, push

Gdy repo istnieje już na serwerze, najczęstszy scenariusz to sklonowanie go na lokalny komputer:

git clone git@github.com:uzytkownik/nazwa-repo.git
cd nazwa-repo

Pobieranie nowych zmian z serwera i łączenie z lokalną gałęzią:

git pull

Domyślnie git pull robi fetch + merge. W wielu zespołach standardem jest:

git config --global pull.rebase true

Dzięki temu przy git pull Git zrobi fetch + rebase, co trzyma historię lokalnych commitów w linii prostej.

Wysyłanie swoich commitów na serwer:

git push

Jeśli Git zgłosi, że zdalna gałąź ma nowsze commity, których nie masz, najpierw robisz git pull (ew. z rebase) i dopiero potem pchasz dalej.

Śledzone gałęzie i branch na serwerze

Nowy lokalny branch, którego jeszcze nie ma na serwerze, trzeba „zarejestrować”:

git switch -c feature/export-csv
git push -u origin feature/export-csv

Znowu -u ustawia powiązanie lokalnej gałęzi z jej odpowiednikiem na serwerze. Git będzie wiedział, skąd pobierać i dokąd wysyłać zmiany.

Lista wszystkich zdalnych gałęzi:

git branch -r

Jeśli chcesz ściągnąć zdalną gałąź, której nie masz jeszcze lokalnie:

git fetch origin
git switch -c feature/raporty origin/feature/raporty

Przycinanie zdalnych referencji po usuniętych branchach

Na dłuższą metę w repo pojawia się „śmietnik” – gałęzie usunięte na serwerze nadal wiszą u ciebie jako referencje zdalne. Można to łatwo posprzątać:

git fetch --prune

Git wyrzuci odniesienia do zdalnych gałęzi, które już nie istnieją na serwerze.

Pull request / merge request – po co w ogóle to klikać

Na GitHubie tworzy się pull requesty (PR), na GitLabie merge requesty (MR). Mechanika jest podobna: chcesz wciągnąć zmiany z jednej gałęzi do innej (zwykle z feature/… do main).

Podstawowe cele:

  • dyskusja o zmianach przed scaleniem,
  • code review – druga para oczu na kod,
  • spięcie z CI – testy, linty, skrypty builda przed wdrożeniem.

Minimalny workflow:

  1. Tworzysz gałąź funkcjonalną lokalnie.
  2. Robisz na niej commity.
  3. Wypychasz gałąź na zdalne repo.
  4. W UI GitHuba/GitLaba zakładasz PR/MR do wybranej gałęzi bazowej.

Przygotowanie gałęzi pod pull request

Dobrze przygotowana gałąź to mniej konfliktów i mniej komentarzy w stylu „rozbij to proszę na mniejsze kawałki”.

  • Commity logicznie podzielone (np. osobno backend, osobno poprawki testów, osobno refaktor nazewnictwa).
  • Historia zaktualizowana do stanu docelowej gałęzi (merge lub rebase na aktualny main przed otwarciem PR).
  • Testy lokalne przechodzą; aplikacja się uruchamia.

Przykładowa sekwencja przed wypchnięciem:

git fetch origin
git switch feature/logowanie
git rebase origin/main        # lub git merge origin/main
# rozwiązujesz ewentualne konflikty
git push -u origin feature/logowanie

Tworzenie pull requestu krok po kroku

Po wypchnięciu gałęzi GitHub zwykle pokaże baner typu „Compare & pull request”. Można też wejść na kartę „Pull requests” i kliknąć „New pull request”.

Kluczowe elementy PR:

  • base branch – np. main (tam trafią zmiany),
  • compare branch – twoja gałąź funkcjonalna,
  • sensowny tytuł (co faktycznie robi ta zmiana),
  • opis: kontekst, co zmieniono, jak testować, potencjalne ryzyka.

Na GitLabie analogicznie – po wypchnięciu nowej gałęzi panel zwykle proponuje utworzenie merge requestu.

Code review – jak odpowiadać na uwagi

Po otwarciu PR/MR pozostali członkowie zespołu mogą dodawać komentarze do linii kodu, zgłaszać sugestie, prosić o poprawki. Typowy cykl:

  1. Pojawiają się komentarze w PR.
  2. Wprowadzasz poprawki w tym samym branchu lokalnie.
  3. Dodajesz i commitujesz zmiany.
  4. git push – PR automatycznie się aktualizuje.

Nie ma potrzeby zakładania nowego PR po każdej serii poprawek. Do jednej funkcji trzymasz jeden branch i jeden PR, dopóki nie zostanie zmergowany.

Scalanie pull requestu – strategie merge

GitHub i GitLab oferują różne tryby scalania:

  • Merge commit – domyślny. Tworzy commit scalający, zachowuje pełne rozgałęzienia historii.
  • Squash and merge – łączy wszystkie commity z gałęzi w jeden commit na main. Historia jest czystsza, ale tracisz oryginalne podziały.
  • Rebase and merge (w zależności od konfiguracji) – odtwarza commity z gałęzi funkcjonalnej na szczycie gałęzi bazowej i włącza je bez dodatkowego merge commita.

W małych projektach squash bywa dobrym kompromisem: w PR możesz mieć wiele drobnych commitów, a do main wpada jeden logiczny pakiet.

Aktualizacja pull requestu po zmianach na main

Częsty przypadek: twój PR „stoi”, bo w międzyczasie main poszedł do przodu. PR pokazuje konflikty. Najprostsza ścieżka:

git fetch origin
git switch feature/logowanie
git rebase origin/main     # lub git merge origin/main, jeśli zespół woli merge
# rozwiązujesz konflikty, commitujesz
git push --force-with-lease

Opcja --force-with-lease jest bezpieczniejsza niż „gołe” --force – Git nie pozwoli przepisać historii, jeśli na zdalnej gałęzi pojawiły się nieznane ci commity.

Usuwanie zdalnych gałęzi po zmergowaniu PR

Po scaleniu PR/MR większość serwisów proponuje usunięcie gałęzi funkcjonalnej. Można to zrobić również z linii komend:

git push origin --delete feature/logowanie

Lokalnie:

git branch -d feature/logowanie

Jeżeli gałąź nie była zmergowana, a mimo to chcesz ją skasować, używasz:

git branch -D feature/logowanie

Typowe problemy przy współpracy ze zdalnym repo

Kilka scenariuszy, które szybko się powtarzają:

  • „non-fast-forward” przy pushu – ktoś już wypchnął nowe commity na zdalną gałąź. Rozwiązanie: git pull --rebase, ewentualnie merge, potem ponownie git push.
  • „divergent branches” przy pullu – lokalna gałąź i zdalna rozeszły się (masz lokalne commity, których nie ma na serwerze, i odwrotnie). Zależnie od konfiguracji Git zapyta, czy chcesz merge, czy rebase. Dobrze jest mieć to ustawione globalnie, żeby uniknąć niespodzianek.
  • „permission denied (publickey)” przy SSH – brak poprawnie dodanego klucza na koncie lub używasz innego klucza niż ten, który GitHub/GitLab zna. Sprawdzasz ~/.ssh/config, testujesz ssh -T git@github.com.

Wspólne repo, wiele komputerów – jak utrzymać porządek

Jeśli pracujesz na dwóch maszynach (np. stacjonarka + laptop), nie trzeba kopiować całego katalogu projektu. Wystarczy:

  1. Na pierwszym komputerze: regularnie commitujesz i pushujesz na zdalne repo.
  2. Na drugim: robisz git clone raz, a potem tylko git pull i dalszą pracę w tych samych branchach.

Zmiana kontekstu staje się wtedy banalna. Ważne tylko, żeby nie trzymać długotrwale niezacommitowanych zmian na jednym komputerze – lepiej je zakomitować (nawet w roboczym branchu) i przepchnąć na serwer.

Integracja z CI/CD – automatyczne testy i build

GitHub Actions, GitLab CI/CD i podobne narzędzia potrafią odpalać pipeline’y przy każdym pushu albo przy PR/MR. Typowy schemat:

  • commit i git push na gałąź,
  • serwer odpala testy jednostkowe, linty, czasem builda,
  • status pipeline’u pojawia się w PR/MR (zielono/czerwono).

W praktyce oznacza to, że wiele błędów technicznych (np. niedziałający build, sypiące się testy) wychwytywane jest zanim ktokolwiek przejrzy kod. Merge jest wtedy blokowany do czasu, aż pipeline przejdzie.

Praca w forku – gdy nie masz uprawnień do repo

Jeśli nie możesz tworzyć branchy bezpośrednio w głównym repo (np. projekt open source), korzystasz z forka:

  1. Na GitHubie klikasz „Fork” na stronie repo.
  2. Pracujesz we własnej kopii: git clone git@github.com:twoje-konto/nazwa-repo.git.
  3. Tworzysz gałąź funkcjonalną w forku, robisz commity, wypychasz.
  4. Tworzysz pull request z forka do oryginalnego repo (upstream).

Aby śledzić zmiany z upstreamu:

git remote add upstream git@github.com:oryginalny-owner/nazwa-repo.git
git fetch upstream
git switch main
git merge upstream/main   # lub rebase

Dzięki temu utrzymujesz swojego forka w miarę aktualnego względem projektu źródłowego.

Najczęściej zadawane pytania (FAQ)

Po co używać Gita, skoro mogę robić kopie plików typu „projekt_final_v3”?

Ręczne kopie plików szybko prowadzą do chaosu: nikt nie wie, która wersja jest aktualna, trudno porównać zmiany i cofnąć tylko jedną pomyłkę. Git przechowuje całą historię projektu w jednym repozytorium i zapisuje konkretne zestawy zmian jako commity.

Każdy commit ma autora, datę, opis i pełny stan śledzonych plików. Dzięki temu możesz sprawdzić, kto co zmienił, porównać dwie wersje, cofnąć się o kilka kroków albo wyłuskać tylko ten fragment, który wprowadził błąd. Znika potrzeba tworzenia folderów „backup_ostatni_ostatni_naprawdę”.

Czym różni się commit od zwykłego zapisu pliku?

Zapis pliku działa tylko lokalnie w edytorze – nadpisujesz aktualną wersję i tyle. Commit to świadome utrwalenie zestawu zmian w historii repozytorium. Git widzi, co dokładnie się zmieniło między poprzednim a nowym stanem i przypisuje temu unikalny identyfikator.

W praktyce wygląda to tak: edytujesz pliki, dodajesz je do indeksu (git add), a potem robisz commit z konkretną wiadomością (git commit -m „Opis zmiany”). Od tej chwili możesz do tego punktu wrócić, porównać go z innymi commitami, a także bezpiecznie eksperymentować dalej.

Do czego służy branch (gałąź) w Git i kiedy ją tworzyć?

Branch to osobna linia rozwoju projektu. Dzięki gałęziom możesz pracować nad nową funkcją, poprawką lub eksperymentem bez dotykania stabilnej wersji w main/master. Wszystkie commity z danej gałęzi są odseparowane, dopóki ich świadomie nie połączysz.

Nową gałąź tworzysz najczęściej, gdy:

  • dodajesz nową funkcjonalność,
  • naprawiasz konkretny błąd,
  • testujesz alternatywne rozwiązanie.

Typowy schemat: przełączasz się na main, pobierasz aktualne zmiany, robisz nową gałąź, pracujesz i dopiero gotowy efekt zgłaszasz do połączenia przez pull request.

Co to jest pull request i jak się ma do commita i brancha?

Pull request (na GitHubie) lub merge request (na GitLabie) to prośba o włączenie zmian z jednej gałęzi do innej, najczęściej z feature brancha do main. W pull requeście widać listę commitów, różnice w plikach i komentarze zespołu.

Typowy ciąg zdarzeń:

  • tworzysz branch z main,
  • robisz w nim commity,
  • wypychasz gałąź na zdalne repozytorium (git push),
  • zakładasz pull request, gdzie inni mogą zrobić code review, dodać uwagi i finalnie zatwierdzić merge.

Commit to najmniejsza jednostka zmiany, branch grupuje je tematycznie, a pull request spina całość procesem przeglądu i integracji.

Jaka jest różnica między lokalnym a zdalnym repozytorium Git?

Lokalne repozytorium żyje na Twoim komputerze. Masz tam całą historię, branche i możesz robić commity, nawet bez internetu. Zdalne repozytorium (remote), np. na GitHubie, jest wspólnym punktem odniesienia dla zespołu i miejscem wymiany zmian.

Podstawowa zasada:

  • lokalnie: tworzysz repo (git init lub git clone), pracujesz, robisz commity,
  • zdalnie: wysyłasz swoje commity (git push) i pobierasz cudze (git pull).

Commit zawsze jest operacją lokalną. Dopiero push decyduje, które zmiany zobaczą inni.

Kiedy użyć git init, a kiedy git clone?

git init uruchamiasz, gdy zaczynasz zupełnie nowy projekt w katalogu, który nie ma jeszcze repozytorium. Polecenie tworzy ukryty folder .git i od tego momentu Git zaczyna śledzić historię zmian w tym katalogu.

git clone służy do pobierania istniejącego projektu ze zdalnego serwera (np. GitHub). Otrzymujesz wtedy pełną historię, gotowe branche i skonfigurowany remote origin. Zasada jest prosta: nowy projekt – git init; dołączanie do już istniejącego – git clone <url>.

Czy Git ma sens, jeśli pracuję sam nad małym projektem?

Tak. Nawet w jednoosobowym projekcie Git daje realne korzyści: możesz bez strachu eksperymentować w osobnej gałęzi, wrócić do działającej wersji sprzed refaktoryzacji i sprawdzić, kiedy dodałeś fragment, który teraz sprawia problemy.

Typowy scenariusz solo: robisz większą zmianę, commity co kilka logicznych kroków, a jeśli po godzinie widzisz, że pomysł nie wypalił, po prostu wracasz do poprzedniego commita zamiast ręcznie odkręcać pliki. Przy kilku takich akcjach Git zaczyna oszczędzać godziny frustracji.

Bibliografia

  • Pro Git, Second Edition. Apress (2014) – Kompleksowe omówienie Gita: commity, branche, workflow zespołowy
  • Git Reference Manual. Free Software Foundation – Oficjalna dokumentacja poleceń git, w tym commit, branch, clone, init
  • Git – About Version Control. Git – Wprowadzenie do kontroli wersji i motywacja użycia Gita zamiast kopii plików
  • GitHub Docs – About pull requests. GitHub – Definicja pull request, proces review i integracji zmian w repozytorium
  • GitLab Documentation – Merge requests. GitLab – Opis merge requestów jako narzędzia do przeglądu i scalania zmian
  • Atlassian Git Tutorials – Getting Git Right. Atlassian – Praktyczne tutoriale: commit, branch, clone, pull, push, workflow zespołowy
  • Version Control with Git, Second Edition. O’Reilly Media (2012) – Książka o praktycznym użyciu Gita, w tym pracy lokalnej i zdalnej

1 KOMENTARZ

  1. Artykuł „Pierwsze kroki z Git: commit, branch i pull request w praktyce” jest niesamowicie pomocny dla osób, które dopiero zaczynają swoją przygodę z systemem kontroli wersji Git. Autorka w przystępny sposób wyjaśnia, jak wykonać podstawowe operacje, takie jak commit, tworzenie branchy czy pull requesty, co sprawia, że nawet początkujący użytkownicy będą mogli łatwo zacząć pracę z Git. Jednakże brakuje mi trochę bardziej zaawansowanych informacji na temat rozwiązywania konfliktów czy pracy z gitignore. Ogólnie jednak artykuł jest świetnym wprowadzeniem do tematu i polecam go wszystkim, którzy chcą nauczyć się podstaw Git-a.

Komentowanie treści jest możliwe wyłącznie dla zalogowanych osób.