4.7 Tipikus Git hibák junioroknál – és hogyan kerüld el
Áttekintő
Mindenki követ el hibákat Git-tel. A kérdés nem az, hogy elköveted-e, hanem az, hogy tudod-e megoldani. Ez az alfejezet a leggyakoribb Git-baleseteket mutatja be, amelyeket szinte minden junior fejlesztő átél az első hónapokban – és ami fontosabb: megmutatja, hogyan javítsd ki őket pánik nélkül.
A jó hír: a Git ritka kivételtől eltekintve visszafordítható. A rossz hír: van az a néhány kivétel, ami komoly bajt okoz, ha nem vigyázol (force push mainre, szenzitív adat a history-ban). Ezeket a kivételeket fogjuk alaposan körbejárni.
Amit tanulni fogsz:
- Hogyan vonj vissza commitot biztonságosan –
revertvs.resetvs.amend - Mi történik, ha véletlenül mainre commitolsz
- Miért veszélyes a
git push --forceés mikor szabad mégis használni - Hogyan kezeld, ha jelszó vagy API kulcs bekerült a history-ba
- A
.gitignorehelyes használata és tipikus hibái
Részletes leírás
1. Accidental commit mainre – főágra commitoltál véletlenül
Ez az egyik leggyakoribb junior hiba: elfelejtesz branch-et váltani, és egyenesen a main-re (vagy master-re) commitolsz.
Hogyan ismered fel:
git log --oneline -5
# bf3a1c2 (HEAD -> main) WIP: authentication draft ← ez nem kellett volna ide
# a7f9e01 Merge pull request #42 from feature/login
Megoldás – ha még nem pusholtak mások:
Ha a commit csak a te lokális főágadon van, egyszerűen létre tudsz hozni belőle egy új branch-et, majd visszavonni a main-en:
# 1. Hozz létre új branch-et a jelenlegi állapotból
git branch feature/authentication
# 2. Vond vissza a commitot main-en (de ne töröld a változtatásokat!)
git reset HEAD~1
# 3. Ellenőrzés
git log --oneline -3 # a commit eltűnt a mainről
git status # a változtatások staged állapotban maradnak
A git reset HEAD~1 visszaállítja a HEAD-et egy committal korábbra, de a változtatásokat megtartja a working directorydban (ez az alapértelmezett --mixed mód).
Ha már pusholták mások is:
Ha a commit már a remote main-en van és mások is lehúzták, NE használj reset-et. Helyette revert:
git revert bf3a1c2
A revert egy új commitot hoz létre, ami visszavonja a változtatásokat – nem módosítja a history-t, így mások munkáját nem zavarja meg.
2. Commit szenzitív adattal – jelszó, API kulcs a kódban
Ez a legkomolyabb hiba, amit lehet elkövetni Git-tel. Ha egy API kulcs, jelszó, vagy titkos token bekerült a commitba, az adat kompromittálódott – még akkor is, ha utána törlöd.
Miért?
- A Git history megőrzi az összes korábbi állapotot
- Ha pusholsz GitHubra, bárki láthatja a history-t
- Automatizált botok folyamatosan pásztázzák a GitHub-ot ilyen adatok után
- Még privát repo esetén is: ha a repot klónozzák, az adat átkerül
Azonnali teendők, ha megtörtént:
- Azonnal érvénytelenítsd az érintett kulcsot/jelszót – ne várd meg, amíg kiveszed a history-ból
- Generálj új kulcsot
- Csak ezután foglalkozz a Git history-val
A history megtisztítása:
Ha még nem pusholtak mások, és csak az utolsó commitban van a probléma:
# Módosítsd az utolsó commitot (amend)
# 1. Vedd ki a szenzitív fájlt a staged területről
git rm --cached .env
# 2. Add hozzá a .gitignore-hoz
echo ".env" >> .gitignore
git add .gitignore
# 3. Módosítsd az utolsó commitot
git commit --amend
Ha már több commit régen van benne, vagy már pusholva van, a git filter-branch vagy a BFG Repo Cleaner eszközre lesz szükség – ez haladó téma, és ilyenkor kérj senior segítséget.
Megelőzés – ez a lényeg:
# .gitignore – ezek mindig legyenek benne
.env
.env.local
.env.production
*.key
*.pem
config/secrets.yml
A .env.example fájlba kerüljenek a szükséges változók üresen – ez mutatja, milyen változókra van szükség, de nem tartalmaz valódi értékeket:
# .env.example (ezt IGEN commitolod)
DATABASE_URL=
SECRET_KEY=
API_KEY=
3. Force push – és mikor okoz katasztrófát
A git push --force felülírja a remote history-t a te lokális history-dddal. Ez hatékony eszköz, de csapatban használva komoly bajt okozhat.
Mi történik force push esetén:
Remote main: A → B → C → D
Te lokálisan: A → B → C' (reset után)
git push --force
Remote main: A → B → C' ← D (D eltűnt!)
Ha valaki D-t alapul véve dolgozott, a változtatásai "elvesznek" (pontosabban: a history-ból kiesnek).
Mikor NEM szabad force push-t használni:
- Megosztott branch-ekre (main, develop, staging)
- Ha mások is lehúzták már az adott branch-et
- Production ágakra soha, semmilyen körülmények között
Mikor szabad (óvatosan) force push-t használni:
- Saját feature branch-edre, amit senki más nem használ
- Ilyenkor a biztonságosabb verzió:
git push --force-with-lease
# Biztonságosabb: csak akkor push-ol, ha senki más nem módosított
git push --force-with-lease origin feature/my-branch
A --force-with-lease ellenőrzi, hogy a remote branch megegyezik-e azzal, amit te utoljára lehúztál. Ha valaki közben változtatott, a push megtagadja.
4. Elveszett változtatások – és hogyan találd meg őket
Úgy érzed, elveszett valami? Valószínűleg nem.
A git reflog a mentőöv:
A Git minden HEAD-módosítást naplóz a reflog-ban – még akkor is, ha azok más parancsokkal "eltűnnek":
git reflog
# 3c5a819 HEAD@{0}: reset: moving to HEAD~2
# bf3a1c2 HEAD@{1}: commit: Add payment integration
# a7f9e01 HEAD@{2}: commit: Fix validation bug
Ha véletlenül reset-eltél valamit, a régi commit hash-e megvan a reflog-ban:
# Visszaállítás a "elveszett" commitra
git checkout bf3a1c2
# vagy
git reset --hard bf3a1c2
Fontos: a reflog lokális, és kb. 90 napig tartja meg az adatokat. Ha pusholva sem volt és töröltük lokálisan is, és 90 nap telt el – akkor valóban elveszett.
5. git reset – a három mód különbsége
A reset az egyik legfélreértettebb Git parancs. Három módja van, és fontos, melyiket mikor használod:
| Parancs | Commit history | Staged (index) | Working directory |
|---|---|---|---|
git reset --soft HEAD~1 | Visszalép | Változatlan (staged marad) | Változatlan |
git reset HEAD~1 (vagy --mixed) | Visszalép | Törli a stage-et | Változatlan |
git reset --hard HEAD~1 | Visszalép | Törli a stage-et | Törli a változtatásokat |
Ökölszabály:
--soft: újra akarom commitolni ugyanazt, csak a commit üzenetét változtatom--mixed(alapértelmezett): újra akarom nézni és stage-elni a változtatásokat--hard: tényleg el akarom dobni mindent – visszafordíthatatlan a lokális változtatásokra nézve
6. git revert vs. git reset – melyiket mikor
Egyszerű szabály:
git revert – ha a commit már megosztott (pusholva van, mások is látják):
git revert abc1234
# Új commitot hoz létre, ami visszavonja az abc1234-et
# Biztonságos megosztott branch-eken
git reset – ha a commit csak lokálisan van, még nem pusholták:
git reset HEAD~1
# Visszavon lokálisan, módosítja a history-t
# NE használd pusholva lévő commitokon
7. .gitignore tipikus hibái
Hiba: utólag adtad hozzá, de a fájl már tracked állapotban van
Ha egy fájl már be van commitolva, a .gitignore-ba való felvétel nem elég – a Git tovább követi:
# Kiveszi a tracking-ből, de a fájlt nem törli
git rm --cached filename.log
# Vagy könyvtárra:
git rm -r --cached node_modules/
# Ezután commitolj
git add .gitignore
git commit -m "chore: remove tracked files that should be ignored"
Hiba: globális .gitignore hiánya
Az IDE által generált fájlok (.idea/, .vscode/, *.DS_Store) nem valók a projekt .gitignore-jába – mások más IDE-t használnak. Ezek a globális .gitignore-ba valók:
# Globális gitignore beállítása
git config --global core.excludesfile ~/.gitignore_global
# ~/.gitignore_global tartalma például:
.DS_Store
.idea/
*.swp
Thumbs.db
Életszerű példák
1. példa: A hétfő reggeli pánik
Szituáció: Péter hétfőn reggel megnyitja a terminált, git status-t futtat, és látja, hogy a múlt pénteken mainre commitolt két félkész commitot. Közben a kolléga is lehúzta a mainet.
Mi a teendő:
# 1. Ellenőrzés – hol vagyunk, mi a helyzet
git log --oneline -5
git log --oneline origin/main -5
# 2. Ha a kollégánál is ott vannak a commitok, revert-et alkalmazunk
git revert HEAD~1 # a legutóbbi commit visszafordítása
git revert HEAD~1 # az eggyel korábbi (most már ez a legutóbbi)
# 3. Push
git push origin main
A revert után a Péter commitjai ott maradnak a history-ban (látható, hogy mi történt), de az általuk hozott változtatások visszavonódnak. A kollégánál nem törik el semmi.
2. példa: „Ó nem, belecommitoltam az API kulcsot"
Szituáció: Anna push előtt veszi észre, hogy az utolsó commitjában ott van a config.js fájl az éles Stripe API kulccsal.
# 1. Azonnal: invalidálja a kulcsot a Stripe dashboard-on
# (ez a legfontosabb lépés, ezt ELŐSZÖR)
# 2. Elveszi a fájlt a commitból, de megtartja lokálisan
git rm --cached config.js
# 3. .gitignore-ba felveszi
echo "config.js" >> .gitignore
git add .gitignore
# 4. Módosítja az utolsó commitot
git commit --amend --no-edit
# (--no-edit: nem kell újra megadni a commit üzenetet)
# 5. Ezután pusholja az új kulccsal konfigurált változatot
Mivel a push még nem történt meg, az amend megoldja a helyzetet.
3. példa: A feature branch elveszett reset után
Szituáció: Béla dühösen git reset --hard-ot futtatott, majd rájött, hogy az utolsó 3 commitja fontos munkát tartalmazott.
# reflog megmutatja, hol volt a HEAD
git reflog
# 5d3b2a1 HEAD@{0}: reset: moving to HEAD~3
# c8f1e09 HEAD@{1}: commit: Add export functionality
# b2a4d7f HEAD@{2}: commit: Fix calculation bug
# a9e3c01 HEAD@{3}: commit: Refactor data processing
# Az elveszett munkát tartalmazza a HEAD@{1} állapot (c8f1e09)
# Visszaállítás:
git reset --hard c8f1e09
# Vagy ha csak megtekinteni szeretnénk:
git checkout c8f1e09
Tesztfeladatok
1. Mi a különbség a git reset --soft HEAD~1 és a git reset --hard HEAD~1 parancsok között?
a) Nincs különbség, mindkettő visszavonja a commitot
b) A --soft megtartja a változtatásokat staged állapotban, a --hard visszafordíthatatlanul törli a lokális változtatásokat is
c) A --soft csak a commit üzenetet módosítja
d) A --hard pusholja is a változtatásokat
(Helyes: b)
2. Kollégád már lehúzta azt a commitot, amit véletlenül a main-re tettél. Mi a helyes megközelítés a visszavonáshoz?
a) git reset --hard HEAD~1, majd git push --force
b) git revert <commit-hash>, majd git push
c) A commitot egyszerűen töröljük a GitHub UI-n
d) Nem lehet visszavonni, ha mások már lehúzták
(Helyes: b)
3. API kulcsot commitoltál, és már pusholva is van. Mi az ELSŐ teendő?
a) git push --force az érintett commit törlésével
b) BFG Repo Cleaner futtatása a history megtisztítására
c) Az érintett API kulcs azonnali érvénytelenítése a szolgáltatónál
d) A repository törlése és újra létrehozása
(Helyes: c – a kulcs kompromittálódott, az a legfontosabb lépés)
4. Mikor biztonságos a git push --force használata?
a) Sosem
b) Csak a main branch-en
c) Saját feature branch-eden, amelyet senki más nem használ
d) Bármikor, ha gyorsabb megoldást kínál
(Helyes: c)
5. .gitignore-ba felvettél egy fájlt, de a Git továbbra is követi a változtatásait. Mi a valószínű ok?
a) A .gitignore szintaxisa hibás
b) A fájl már korábban be lett commitolva, ezért tracked állapotban van
c) A .gitignore csak az új klónozásoknál működik
d) Git 2.0 felett már nem működik a .gitignore
(Helyes: b)
6. Mi a git reflog parancscélja?
a) A remote repository log-jait mutatja
b) A HEAD korábbi állapotait naplózza, beleértve a "elveszett" commitokat is
c) Csak a merge commitokat listázza
d) A .git könyvtár fájljainak listáját jeleníti meg
(Helyes: b)
7. Mi a git push --force-with-lease előnye a sima --force-szal szemben?
a) Gyorsabban fut
b) Tömöríti a history-t
c) Csak akkor hajtja végre a force push-t, ha senki más nem módosította a remote branch-et a legutóbbi lehúzásod óta
d) Automatikusan backup-olja a remote állapotot
(Helyes: c)
8. Melyik parancsra van szükség ahhoz, hogy egy már tracked fájlt eltávolíts a Git tracking alól (de ne törölj a fájlrendszerről)?
a) git ignore <fájlnév>
b) git rm <fájlnév>
c) git rm --cached <fájlnév>
d) git untrack <fájlnév>
(Helyes: c)
9. Szituáció: Éppen reset --hard-ot futtatottál és rájöttél, hogy kellett volna az a commit. Milyen paranccsal találod meg a "elveszett" commit hash-ét?
a) git log --all
b) git stash list
c) git reflog
d) git status --verbose
(Helyes: c)
10. Melyik állítás igaz a .gitignore és a globális gitignore kapcsolatáról?
a) A globális gitignore felülírja a projekt .gitignore-ját
b) IDE-specifikus fájlok (pl. .idea/, .DS_Store) inkább a globális gitignore-ba valók, hogy ne terheljük a projekt .gitignore-ját mások IDE-beállításaival
c) Globális gitignore nem létezik, csak projekt szintű
d) A projekt .gitignore nem működik, ha van globális is beállítva
(Helyes: b)