El mòdul anterior va acabar amb una llista de desastres anunciats: el reset --hard sobre tres dies de feina, el commit a la branca equivocada, el pull sobre una branca divergida, la branca esborrada que sí que importava, el .git/index corrupte. Aquest mòdul és el manual per quan passen.
I passen sempre en el pitjor moment. Són les set de la tarda d'un divendres, la Carla ha d'entregar GT-241 abans del tancament, executa una ordre que no acaba d'entendre i el terminal respon una cosa que no havia vist mai. El primer que fa la majoria de la gent en aquell moment —provar ordres a l'atzar tretes d'una resposta de fòrum— és exactament el que converteix un problema petit en un de gros.
Aquesta lliçó és dues coses. Primer, un protocol de calma: què fer abans de tocar res. I després, el catàleg d'urgències: una taula gran de símptoma → causa probable → on es resol, que funciona com a índex de la resta del mòdul. Els casos trivials es resolen aquí mateix; els que tenen substància es remeten a la seva lliçó.
Comencem per la idea que sosté tota la resta, perquè és la que permet treballar amb calma.
Contingut
- La idea que ho sosté tot: a Git gairebé res no es perd
- El protocol de calma: quatre ordres abans de tocar res
- La còpia de seguretat de deu segons
- El catàleg d'urgències
- Els casos trivials, resolts aquí
- Com llegir un missatge d'error de Git
- Què NO fer mai en estat de pànic
- La idea que ho sosté tot: a Git gairebé res no es perd
Recupera el model de dades de la lliçó 01-04. Un commit és un objecte immutable a .git/objects, identificat pel hash del seu contingut. Una branca és un fitxer de 41 bytes que conté un hash (lliçó 03-01). I la relació entre totes dues coses és la clau:
Els commits no pertanyen a les branques. Les branques simplement apunten a commits.
D'aquí es dedueix el que fa habitable aquest mòdul: quan esborres una branca, no esborres cap commit. Quan fas reset --hard, no esborres cap commit. Quan un rebase surt malament, els commits originals continuen allà. L'única cosa que ha canviat és que cap referència no els apunta: s'han tornat inassolibles.
flowchart LR
subgraph abans["Abans del reset --hard HEAD~2"]
A1["A"] --> B1["B"] --> C1["C"] --> D1["D"]
R1(["main"]) -.-> D1
end
subgraph despres["Després"]
A2["A"] --> B2["B"] --> C2["C (inassolible)"] --> D2["D (inassolible)"]
R2(["main"]) -.-> B2
end
abans ==> despres
C i D continuen existint, byte a byte, a .git/objects. Ningú no els ha tocat. Simplement main ja no hi arriba, i per això git log no els mostra. Recuperar-los és qüestió de tornar a apuntar-hi alguna cosa, i per a això existeix el reflog, que és el tema de la lliçó 09-04.
Els objectes inassolibles sí que acaben desapareixent, però no immediatament: git gc (lliçó 08-06) els elimina quan fa més de dues setmanes que no tenen referències, i les entrades del reflog caduquen als 90 dies (30 per al que és inassolible). A la pràctica, tens setmanes per recuperar qualsevol commit.
L'excepció, i és la important
Tot l'anterior es recolza en una condició: que la feina hagi arribat a ser un commit. El que mai no es va confirmar no és a la base de dades d'objectes i, per tant, no hi ha res a recuperar.
| Estat de la feina | És a .git/objects? |
Recuperable? |
|---|---|---|
| Confirmada (encara que sense publicar) | Sí | Sí, sempre, mentre gc no ho purgui |
Preparada amb git add (sense confirmar) |
Sí, com a blob solt | Sí, amb git fsck (09-04) |
A git stash |
Sí, com a commits ocults | Sí, fins i tot després de stash drop (09-04) |
| Modificada al directori de treball | No | No. Només el teu editor et pot salvar |
| Fitxer sense seguiment | No | No |
Aquesta taula és el mapa mental de tot el mòdul. Fixa't en el detall de la segona fila: git add ja desa el contingut a la base de dades. Un fitxer que vas preparar i després vas destruir amb git checkout és recuperable, encara que no el confirmessis mai.
D'aquí surt el consell més rendible del mòdul, i no és una ordre:
Confirma aviat i sovint. Un commit lleig amb el missatge "wip" et protegeix de tot el que ve a les cinc lliçons següents. Ja ho netejaràs amb
rebase -i(lliçó 05-02) abans de publicar.
- El protocol de calma: quatre ordres abans de tocar res
Quan alguna cosa va malament, la temptació és actuar. Resisteix-la. Les quatre ordres següents no modifiquen res, triguen tres segons i, en la majoria dels casos, el diagnòstic ja està fet quan acabes de llegir-les.
Pas 1: git status
És l'ordre més infravalorada de Git. No només diu quins fitxers han canviat: diu en quin estat es troba el repositori i, molt sovint, et suggereix literalment la sortida.
interactive rebase in progress; onto 7d3a8f4 Last command done (2 commands done): pick e91d4a8 Extreu la creació de l'element de tasca squash b52c9d1 Corregeix el comptador Next commands to do (3 remaining commands): pick 4f8a2e6 Afegeix el filtre de pendents pick 9c4e7b2 Documenta el filtre al README (use "git rebase --edit-todo" to view and edit) You are currently rebasing branch 'GT-241' onto '7d3a8f4'. (fix conflicts and then run "git rebase --continue") (use "git rebase --skip" to skip this patch) (use "git rebase --abort" to check out the original branch)
Aquí hi ha tot: quina operació està a mitges, per on va, i les tres maneres de sortir-ne. Molta gent en pànic no executa git status precisament quan més ho necessita.
Els estats que pot anunciar:
El que diu git status |
Què significa | Sortida immediata |
|---|---|---|
HEAD detached at 4f8a2e6 |
Ets fora de qualsevol branca (03-02) | git switch - o git switch -c branca-nova |
You have unmerged paths |
Conflicte de fusió a mitges (03-05) | Resoldre'l, o git merge --abort |
interactive rebase in progress |
Rebase interactiu a mitges (05-02) | git rebase --continue / --abort |
You are currently cherry-picking |
Cherry-pick a mitges (05-03) | git cherry-pick --continue / --abort |
You are currently reverting |
Revert a mitges (05-06) | git revert --continue / --abort |
You are currently bisecting |
Bisect en marxa (06-02) | git bisect reset |
Your branch and 'origin/x' have diverged |
Divergència amb el remot | Lliçó 09-03 |
nothing to commit, working tree clean |
No hi ha res sense desar | El que s'hagi perdut, si de cas, és en commits |
Pas 2: git log --oneline --graph -20
La forma de l'historial visible des d'on ets ara. Serveix per a dues coses: confirmar que ets on et penses que ets, i veure si el commit que busques continua sent assolible.
* 9c4e7b2 (HEAD -> GT-241) Afegeix el filtre de pendents * b52c9d1 Extreu la creació de l'element de tasca * 4f8a2e6 (origin/main, main) Fusiona GT-238 |\ | * 7d3a8f4 Corregeix el comptador de tasques |/ * e91d4a8 Documenta la instal·lació al README
I una variant que convé tenir a mà quan sospites que et falta alguna cosa:
# TOT el que és assolible des de qualsevol referència, incloses les remotes
git log --oneline --graph --decorate --all -30Si el commit que busques apareix a --all però no al log normal, no s'ha perdut: és en una altra branca. Aquest és el cas més freqüent de tots, i no requereix recuperar res.
Pas 3: git reflog -20
El registre de per on ha passat HEAD. És la caixa negra del repositori i contesta la pregunta "què dimonis acabo de fer?".
9c4e7b2 HEAD@{0}: reset: moving to HEAD~2
b52c9d1 HEAD@{1}: commit: Afegeix el filtre de pendents
4f8a2e6 HEAD@{2}: commit: Extreu la creació de l'element de tasca
7d3a8f4 HEAD@{3}: checkout: moving from main to GT-241
e91d4a8 HEAD@{4}: pull: Fast-forwardEs llegeix de baix a dalt en ordre cronològic. L'entrada HEAD@{0} és sempre l'última cosa que ha passat, i aquí diu clarament que hi va haver un reset que va deixar enrere dos commits. Els hashos d'aquells commits continuen escrits allà, i això és exactament el que fa possible recuperar-los.
El reflog és el tema complet de la lliçó 09-04. Aquí n'hi ha prou d'executar-lo i llegir-lo: en el 80 % dels ensurts, la resposta és en aquestes vint línies.
Pas 4: git fsck --no-progress, només si l'anterior no quadra
Comprova la integritat de la base d'objectes. No l'executis per rutina: és lent en repositoris grans i la seva sortida espanta més del que hauria (els objectes dangling són normals i inofensius). Reserva aquest pas per quan Git es negui a fer coses bàsiques o doni errors sobre objectes. La seva interpretació completa és a la lliçó 09-05.
El protocol, en un àlies
Val la pena tenir-lo com a àlies (lliçó 06-04):
git config --global alias.panic '!f() {
echo "=== STATUS ==="; git status;
echo; echo "=== LOG ==="; git log --oneline --graph --decorate -20;
echo; echo "=== REFLOG ==="; git reflog -20;
}; f'Tres segons, zero modificacions, i gairebé sempre el diagnòstic complet.
- La còpia de seguretat de deu segons
Si després del protocol de calma la situació continua sense ser clara, fes una còpia abans d'intentar res. És la diferència entre "tinc un problema" i "tinc un problema i a més he empitjorat les coses".
A Windows (PowerShell):
Tres detalls importants:
cp -a(o-Rpreservant enllaços) copia el directori sencer,.gitinclòs. Això és el que fa que sigui una còpia real i no un clon parcial.- No facis servir
git cloneper a això. Un clon no s'emporta el reflog, ni els desaments temporals, ni les branques no publicades, ni els canvis sense confirmar. Precisament el que necessites conservar. - Fes-ho abans d'executar qualsevol ordre que comenci per
git reset,git clean,git rebaseogit push --force.
Quan el problema estigui resolt, esborra la còpia. Quan no ho estigui, agrairàs tenir-la.
Per a còpies de seguretat de debò, amb format i no amb cp, hi ha git bundle, que es veu a la lliçó 09-05.
- El catàleg d'urgències
Aquesta és la taula central de la lliçó. Busca't a la columna de l'esquerra.
| Símptoma | Causa probable | On es resol |
|---|---|---|
| He fet canvis a la branca equivocada (sense confirmar encara) | git switch conserva els canvis sense confirmar, però se't va oblidar canviar de branca abans de començar |
Aquí, apartat 5.1 |
| He confirmat a la branca equivocada | El mateix, però ja has fet commit |
Aquí, apartat 5.2 |
| L'últim commit té l'autor equivocat | user.email mal configurat en aquest repositori (01-05) |
Aquí, apartat 5.3 |
| Diversos commits tenen l'autor equivocat | Configuració mal posada des de fa temps | 09-02 (rebase -i) o filter-repo (08-05) |
| M'he oblidat un fitxer a l'últim commit | Un git add incomplet |
Aquí, apartat 5.4 |
fatal: Unable to create '.git/index.lock': File exists |
Un procés Git va morir sense netejar, o hi ha un altre Git executant-se | Aquí, apartat 5.5 |
detected dubious ownership in repository |
El directori pertany a un altre usuari (típic a WSL, Docker, discos externs) | Aquí, apartat 5.6 |
! [rejected] main -> main (non-fast-forward) |
El remot té commits que tu no tens | 09-03 |
Your branch and 'origin/main' have diverged |
Tots dos costats han avançat des de l'ancestre comú | 09-03 |
hint: You have divergent branches and need to specify how to reconcile them |
pull sense política configurada |
09-03 |
fatal: refusing to merge unrelated histories |
Dos historials sense ancestre comú | 09-03 |
He fet push --force i he esborrat la feina d'un company |
Reescriptura d'historial publicat (regla d'or, 05-01) | 09-03 i 09-04 |
You are in 'detached HEAD' state |
HEAD apunta a un commit i no a una branca (03-02) |
Aquí, apartat 5.7 |
He fet git reset --hard i he perdut commits |
La branca s'ha mogut; els commits continuen allà | 09-02 (concepte) i 09-04 (rescat) |
He fet git reset --hard i he perdut canvis sense confirmar |
Mai no van arribar a la base de dades | 09-02. Males notícies: gairebé mai no és recuperable |
He esborrat una branca amb -D i la necessitava |
La referència s'ha esborrat; els commits no | 09-04 |
| Un rebase ha sortit malament i no sé com estava abans | Reescriptura local | 09-04 (ORIG_HEAD i reflog) |
He fet stash drop d'una cosa que necessitava |
La referència del desament temporal s'ha esborrat | 09-04 |
Un fitxer que vaig esborrar reapareix a cada pull |
Continua a l'historial del remot: algú el torna a portar | 09-02 i 09-03 |
Un fitxer és al .gitignore i Git el continua veient |
Ja tenia seguiment: .gitignore només afecta el que no se segueix (08-03) |
09-06 (check-ignore -v) |
Permission denied (publickey) |
Clau SSH absent, no carregada o no registrada (04-03) | Aquí, apartat 5.8, i 09-06 per traçar-ho |
| Estic a mitges d'un conflicte i no sé com sortir-ne | Fusió, rebase o cherry-pick interromput | 03-05; sortida ràpida a l'apartat 5.9 |
| Tot el fitxer apareix com a modificat i només vaig canviar una línia | Finals de línia CRLF/LF (08-04) | 09-06 (ls-files --eol, check-attr) |
error: object file ... is empty o fatal: bad object |
Corrupció real de la base d'objectes | 09-05 |
Git es nega a fer res i git status falla |
.git/index corrupte, o HEAD trencat |
09-05 |
git status triga uns quants segons |
No és una fallada: és rendiment (08-06) | 08-06 |
| No entenc per què Git fa el que fa | Configuració inesperada, atributs, hooks | 09-06 |
Un consell d'ús: abans de buscar a internet, busca en aquesta taula. Les respostes de fòrum solen estar descontextualitzades i moltes comencen per git reset --hard, que és exactament el que no has d'executar sense entendre-ho.
- Els casos trivials, resolts aquí
Aquests no necessiten lliçó pròpia. Són problemes d'un minut, sempre que sàpigues quina és l'ordre.
5.1. Canvis sense confirmar a la branca equivocada
L'Ana porta mitja hora tocant app.js i descobreix que és a main, no a GT-241.
No has perdut res. Els canvis sense confirmar no pertanyen a cap branca: viuen al directori de treball i a l'índex. Canviar de branca se'ls emporta amb tu.
git status # confirma que només hi ha modificacions, sense commits nous
git switch -c GT-241 # crea la branca aquí i s'emporta els canvis
git statusEls canvis són intactes, ara a la branca correcta, i main no s'ha mogut.
Si la branca ja existeix:
Git s'emporta els canvis sense més, excepte si algun dels fitxers modificats difereix entre les dues branques. En aquest cas avisa:
error: Your local changes to the following files would be overwritten by checkout: app.js Please commit your changes or stash them before you switch branches.
La sortida és el stash de la lliçó 05-04:
5.2. Ja has confirmat a la branca equivocada
El Bruno ha fet dos commits de GT-247 directament sobre main, i main encara no s'ha publicat amb ells.
No has perdut res. Els commits existeixen; l'única cosa mal posada és a què apunta cada branca. La recepta és: marcar aquí, i tornar la branca al seu lloc.
b52c9d1 (HEAD -> main) GT-247 mostra el nombre de tasques pendents 4f8a2e6 GT-247 afegeix el comptador al DOM e91d4a8 (origin/main) Documenta la instal·lació al README
# 2. Crear la branca correcta AQUÍ (sense canviar-s'hi: només posa un marcador)
git branch GT-247
# 3. Tornar main a on era el remot
git reset --hard origin/main
# 4. Anar-se'n a la branca bona
git switch GT-247
git log --oneline -3b52c9d1 (HEAD -> GT-247) GT-247 mostra el nombre de tasques pendents 4f8a2e6 GT-247 afegeix el comptador al DOM e91d4a8 (origin/main, main) Documenta la instal·lació al README
L'ordre importa: crea primer la branca, i només després mou main. Així els commits mai no deixen de ser assolibles i el reset --hard és completament segur. (Si t'equivoques d'ordre, tampoc no passa res: per a això hi ha el reflog de la 09-04. Però fes-ho bé.)
Si els commits ja estaven publicats a main, la resposta no és aquesta sinó git revert (lliçó 05-06), perquè estaries reescrivint historial públic.
Variant: si són alguns commits solts i no els últims, l'eina és git cherry-pick (lliçó 05-03).
5.3. El commit té l'autor equivocat
La Carla ha configurat el portàtil nou i ha confirmat com a [email protected].
Per a l'últim commit, sense publicar:
# Primer, arregla la causa, o tornarà a passar
git config user.name "Carla Vidal"
git config user.email "[email protected]"
# I ara el commit
git commit --amend --author="Carla Vidal <[email protected]>" --no-editCarla Vidal <[email protected]> | Carla Vidal <[email protected]>
Un matís que confon molta gent: Git desa dues identitats per commit.
| Camp | Qui és | Quan canvia |
|---|---|---|
Author (%an/%ae) |
Qui va escriure el canvi | Es conserva en rebase i cherry-pick |
Committer (%cn/%ce) |
Qui va crear aquest objecte commit | S'actualitza a cada reescriptura |
--author només toca el primer. Per al segon, Git fa servir user.email en el moment de crear el commit, així que n'hi ha prou d'haver corregit la configuració abans de l'--amend. Si necessites forçar-ho:
GIT_COMMITTER_NAME="Carla Vidal" GIT_COMMITTER_EMAIL="[email protected]" \
git commit --amend --no-editI la prevenció, que val més que la cura (lliçó 01-05):
# Que Git es negui a confirmar si no hi ha identitat explícita al repositori
git config --global user.useConfigOnly trueAmb això, un repositori sense user.email propi dona error en lloc d'inventar-se una identitat a partir del nom de la màquina. És la configuració que evita aquest problema per sempre.
Si són diversos commits, cal reescriure: git rebase -i amb exec (lliçó 05-02) per a uns quants, o git filter-repo --mailmap (lliçó 08-05) per a un historial sencer. I sempre amb la regla d'or al davant: només si no està publicat.
5.4. M'he oblidat un fitxer a l'últim commit
--no-edit conserva el missatge tal com és. El commit anterior se substitueix per un de nou amb el mateix missatge i el fitxer inclòs.
El hash canvia. Si ja l'havies publicat, això reescriu historial: no ho facis, i afegeix-hi un commit nou al seu lloc. És exactament la situació de la lliçó 08-02, on la solució elegant és git commit --fixup + rebase --autosquash abans de publicar.
Comprovació prèvia útil, per no incloure-hi res de més:
git status --short # què hi ha preparat ara mateix?
git diff --cached --stat # què entraria exactament a l'amend?5.5. .git/index.lock: File exists
fatal: Unable to create '/home/ana/gestor-tasques/.git/index.lock': File exists. Another git process seems to be running in this repository, e.g. an editor opened by 'git commit'. Please make sure all processes are terminated then try again.
Què és. Git crea .git/index.lock abans de modificar l'índex i l'esborra en acabar. És un forrellat perquè dos processos no escriguin alhora. Si el procés va morir —el vas tancar amb Ctrl+C, es va penjar l'editor, l'IDE el va matar—, el fitxer es queda orfe.
El procediment correcte, en aquest ordre:
# 1. Hi ha realment un altre Git executant-se? (molt freqüent: l'IDE)
ps aux | grep -i "[g]it"
# 2. Si no n'hi ha cap, mirar el forrellat: si és d'una estona enrere, és orfe
ls -l .git/index.lock
# 3. Esborrar-lo
rm -f .git/index.lock
# 4. Comprovar que tot està bé
git statusLa causa més habitual amb diferència és un IDE (VS Code, IntelliJ, Eclipse) executant git status en segon pla just quan tu llances una ordre. Si et passa sovint, no és un problema de Git: és una cursa entre dos clients.
No esborris
.git/index.locksense comprovar el pas 1. Si de debò hi ha un altre procés escrivint, treure-li el forrellat pot corrompre l'índex —que és justament el que tracta la lliçó 09-05—. Amb la comprovació feta, esborrar-lo és completament inofensiu.
Hi ha altres forrellats amb la mateixa lògica i el mateix tractament: .git/HEAD.lock, .git/refs/heads/main.lock, .git/shallow.lock.
5.6. detected dubious ownership in repository
fatal: detected dubious ownership in repository at '/home/ana/gestor-tasques' To add an exception for this directory, call: git config --global --add safe.directory /home/ana/gestor-tasques
Què és. Des de Git 2.35, Git es nega a operar en un repositori el propietari del qual, al sistema de fitxers, no ets tu. És una mesura de seguretat, no una fallada: un repositori aliè pot contenir hooks (lliçó 06-01) o configuració que s'executen en llançar ordres.
Els escenaris típics: WSL accedint a un disc de Windows, un contenidor Docker amb un volum muntat, un disc extern formatat en un altre sistema, o un repositori clonat amb sudo.
# Veure de qui és realment
ls -ld .
# L'excepció, per a un repositori concret
git config --global --add safe.directory /home/ana/gestor-tasques
# Per a tot un arbre (Git 2.36+)
git config --global --add safe.directory '/mnt/projectes/*'I el que no has de fer, encara que ho trobis a internet:
Abans d'afegir l'excepció, planteja't si la propietat és correcta. Si el repositori és teu i apareix com a de root, probablement el vas clonar amb sudo i la solució real és:
5.7. "You are in 'detached HEAD' state"
Note: switching to '4f8a2e6'. You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by switching back to a branch.
No és un error. És Git informant que HEAD apunta directament a un commit i no a una branca (lliçó 03-02). S'hi arriba amb git checkout <sha>, amb git switch --detach, en inspeccionar una etiqueta, durant un bisect o enmig d'un rebase.
Dos casos:
A. No has fet commits estant-hi. Torna i prou:
B. Has fet commits estant-hi. Aquest és el cas que cal saber, perquè aquests commits no són a cap branca i desapareixeran de la vista tan bon punt et moguis:
git log --oneline -3 # anota el hash del teu últim commit
git switch -c rescat-demo # crea una branca AQUÍ mateixI si ja t'havies mogut i els has perdut de vista: no passa res, el reflog els té (lliçó 09-04).
5.8. Permission denied (publickey)
[email protected]: Permission denied (publickey). fatal: Could not read from remote repository.
Diagnòstic en tres ordres, de menys a més (lliçó 04-03):
# 1. El servidor em reconeix?
ssh -T [email protected]Hi ana-ferrer! You've successfully authenticated, but git.exemple.cat does not provide shell access.
Si això funciona i el push no, el problema és la URL del remot, no la clau:
Si no funciona:
I si continua fallant, la traça detallada, que és terreny de la lliçó 09-06:
Causes freqüents en ordre de probabilitat: la clau pública no està registrada al servidor; l'agent no s'està executant (típic després de reiniciar); permisos incorrectes a ~/.ssh (ha de ser 700, i les claus privades 600); o hi ha diverses claus i SSH ofereix la que no toca (s'arregla amb un bloc Host a ~/.ssh/config).
5.9. Estic a mitges d'un conflicte i vull sortir-ne
La mecànica de resolució és completa a la lliçó 03-05. L'única cosa que cal aquí és la sortida d'emergència, perquè en pànic no es recorda:
git status # diu quina operació està en marxa
git merge --abort # si és una fusió
git rebase --abort # si és un rebase
git cherry-pick --abort # si és un cherry-pick
git revert --abort # si és un revert--abort torna el repositori exactament a l'estat previ a l'operació. És segur i no perd res del que hi havia abans de començar (sí que perd la feina de resolució que haguessis fet durant el conflicte).
Si --abort falla perquè tenies canvis sense confirmar barrejats, el reflog i ORIG_HEAD són la sortida, i això és la lliçó 09-04.
- Com llegir un missatge d'error de Git
Git té fama de missatges críptics i en part és merescuda, però han millorat moltíssim. Val la pena saber-los llegir, perquè gairebé sempre contenen la solució.
L'anatomia típica:
error: Your local changes to the following files would be overwritten by merge: app.js Please commit your changes or stash them before you merge. Aborting
| Part | Què aporta |
|---|---|
fatal: |
Git no ha fet res. L'estat és el mateix d'abans |
error: |
Alguna cosa ha fallat; pot haver-hi canvis parcials. Llegeix la resta |
warning: |
Ha funcionat, però hi ha alguna cosa que hauries de saber |
hint: |
El suggeriment de Git. Llegeix-lo: sol ser literalment la solució |
Aborting |
Confirmació explícita que no s'ha tocat res |
Tres regles pràctiques:
fatal:és tranquil·litzador. Significa que Git s'ha negat a actuar. Res no s'ha trencat.- Les línies
hint:són la documentació al lloc exacte. Es poden silenciar ambadvice.*, i molta gent ho fa sense adonar-se'n en copiar configuracions alienes. Comprova-ho ambgit config --get-regexp '^advice\.'. - El missatge anomena els fitxers implicats. Gairebé sempre acota el problema a l'instant.
I un ajust que ajuda a llegir, si treballes en anglès forçat per defecte:
git config --global advice.detachedHead true # que torni a avisar si el vas silenciar
LANG=en_US.UTF-8 git status # forçar l'idioma per buscar l'errorBuscar el missatge en anglès dona moltíssims més resultats útils que buscar-lo traduït.
- Què NO fer mai en estat de pànic
Tanca la lliçó la llista negra. Tot el que segueix ha convertit problemes petits en desastres reals.
1. Copiar i enganxar ordres d'internet sense entendre-les. Especialment si comencen per git reset --hard, git push --force, git clean -fdx o rm -rf .git. La resposta que et serveix a tu depèn d'un context que el fòrum no coneix.
2. git push --force. Mai en pànic, i gairebé mai fora d'ell. Si ha de ser, --force-with-lease (lliçons 04-05 i 09-03), i només sobre una branca que sigui teva.
3. git clean -fdx "per deixar-ho net". Esborra de debò, i sense xarxa de seguretat: els fitxers sense seguiment no són a la base de dades. Executa sempre git clean -n abans (lliçó 09-02).
4. Esborrar .git. És el repositori sencer: tot l'historial, totes les branques, el reflog. No és "la configuració".
5. Tornar a clonar a sobre del directori amb problemes. De vegades reclonar és la resposta correcta (lliçó 09-05), però primer es copia i es rescata el que és local: branques sense publicar, desaments temporals, canvis en curs.
6. Encadenar ordres sense mirar el resultat de cadascuna. Després de cada pas: git status i git log --oneline -5.
7. Executar git gc --prune=now o git reflog expire --expire=now. És l'única cosa d'aquesta llista que destrueix la xarxa de seguretat de tot el mòdul (lliçó 08-06). Si hi ha alguna cosa a recuperar, aquestes ordres ho fan impossible.
8. Callar. Si has forçat alguna cosa sobre una branca compartida, o has esborrat alguna cosa del servidor, avisa l'equip en aquell moment. Cinc minuts després és un avís; cinc hores després és un trencaclosques per a tothom.
Errors Habituals i Consells
Error 1: actuar abans de diagnosticar. El protocol de calma —status, log --graph, reflog— costa tres segons i resol la majoria dels casos sense tocar res.
Error 2: no llegir el que diu git status. És l'ordre que més informació dona i la primera que s'oblida en pànic. Sovint conté literalment l'ordre que necessites.
Error 3: creure que un reset --hard o un branch -D han destruït commits. No ho han fet. Els commits continuen a .git/objects i el reflog sap on són (09-04).
Error 4: confiar que tot és recuperable, inclòs el que no s'ha confirmat. No ho és. El que mai no va ser un commit (ni un add, ni un stash) no és enlloc.
Error 5: fer servir git clone com a còpia de seguretat d'emergència. No s'emporta el reflog, ni els desaments temporals, ni les branques locals, ni el que no s'ha confirmat. Fes servir cp -a del directori complet.
Error 6: esborrar .git/index.lock sense comprovar si hi ha un altre procés. Amb la comprovació és inofensiu; sense ella pot corrompre l'índex.
Error 7: silenciar els hint: de Git. Són la millor documentació contextual que existeix, escrita just quan la necessites.
Error 8: safe.directory '*'. Desactiva una protecció real de seguretat a tot arreu. Afegeix l'excepció concreta, o arregla la propietat del directori.
Consell 1: confirma aviat i sovint. Un commit "wip" cada mitja hora fa que les cinc lliçons següents gairebé mai no et calguin. L'historial es neteja després amb rebase -i.
Consell 2: defineix l'àlies git panic. El dia que el necessitis no recordaràs les tres ordres.
Consell 3: cp -a abans de res seriós. Deu segons que compren tranquil·litat per experimentar.
Consell 4: user.useConfigOnly true a la configuració global. Elimina per sempre el commit amb l'autor equivocat.
Consell 5: busca els missatges d'error en anglès. Deu vegades més resultats útils.
Consell 6: quan passi alguna cosa rara amb una branca compartida, avisa immediatament. El cost social d'avisar tard és molt més gran que el d'avisar.
Exercicis
Exercici 1: el commit a la branca equivocada
- Crea un repositori amb
app.jsi tres commits amain. Simula el remot amb un clon--barelocal i publicamain. - Sense canviar de branca, fes dos commits més que haurien d'haver anat a
GT-247. - Aplica el procediment de l'apartat 5.2 i deixa
maincom al remot iGT-247amb els dos commits. - Demostra amb
git log --oneline --all --graphque cap commit no s'ha perdut. - Comprova al reflog quins moviments han quedat registrats.
Exercici 2: identitat i fitxers oblidats
- En un repositori nou, configura deliberadament malament
user.email(ningu@localhost) i fes un commit que inclogui nomésindex.html, oblidantestils.css. - Comprova l'autor amb
git log -1 --format='%an <%ae>'. - Corregeix la configuració, afegeix el fitxer oblidat i arregla l'autor en un sol
--amend. - Comprova que el hash del commit ha canviat i explica per què això el faria inacceptable si ja estigués publicat.
- Activa
user.useConfigOnly truei comprova què passa en intentar confirmar en un repositori sense identitat configurada.
Exercici 3: el protocol de calma sobre un desastre simulat
- Crea un repositori amb sis commits i una branca
GT-241amb dos commits propis. - Estant a
GT-241, executagit reset --hard HEAD~2. - Sense recuperar res encara, executa els quatre passos del protocol de calma i escriu què et diu cadascun.
- Identifica al reflog el hash exacte del commit que era la punta de
GT-241abans delreset. - Crea una branca
rescatsobre aquest hash i comprova que la feina és intacta. - Repeteix l'experiment, però aquesta vegada amb un fitxer modificat i sense confirmar en el moment del
reset --hard. Explica la diferència.
Solucions
Solució 1:
mkdir -p /tmp/p9-01 && cd /tmp/p9-01
git init -b main gestor-tasques
cd gestor-tasques
git config user.name "Ana Ferrer"; git config user.email "[email protected]"
echo "console.log('gestor-tasques');" > app.js
git add . && git commit -q -m "chore: commit inicial"
echo "// llistat" >> app.js && git commit -q -am "feat: llistat de tasques"
echo "// comptador" >> app.js && git commit -q -am "feat: comptador"
# El "servidor"
git init -q --bare /tmp/p9-01/remot.git
git remote add origin /tmp/p9-01/remot.git
git push -q -u origin main# 2. Dos commits que anaven a GT-247, però a main
echo "// filtre 1" >> app.js && git commit -q -am "GT-247 base del filtre"
echo "// filtre 2" >> app.js && git commit -q -am "GT-247 filtre de pendents"
git log --oneline -37d3a8f4 (HEAD -> main) GT-247 filtre de pendents b52c9d1 GT-247 base del filtre 4f8a2e6 (origin/main) feat: comptador
# 3. El procediment: marcar aquí, tornar main
git branch GT-247
git reset --hard origin/main
git switch GT-247* 7d3a8f4 (HEAD -> GT-247) GT-247 filtre de pendents * b52c9d1 GT-247 base del filtre * 4f8a2e6 (origin/main, main) feat: comptador * e91d4a8 feat: llistat de tasques * 9c4e7b2 chore: commit inicial
main és on el remot, GT-247 té els seus dos commits, i els cinc commits continuen existint. El reset --hard va ser segur perquè git branch GT-247 ja els mantenia assolibles.
7d3a8f4 HEAD@{0}: checkout: moving from main to GT-247
4f8a2e6 HEAD@{1}: reset: moving to origin/main
7d3a8f4 HEAD@{2}: commit: GT-247 filtre de pendents
b52c9d1 HEAD@{3}: commit: GT-247 base del filtre
4f8a2e6 HEAD@{4}: commit: feat: comptadorFixa't en HEAD@{1}: el reflog registra el reset i el hash des del qual es va sortir. Encara que haguessis oblidat el pas 2, 7d3a8f4 és allà escrit.
Solució 2:
mkdir /tmp/p9-01-b && cd /tmp/p9-01-b && git init -q -b main
git config user.name "Carla Vidal"
git config user.email "ningu@localhost" # malament a propòsit
echo "<h1>Gestor de tasques</h1>" > index.html
echo "body { margin: 0; }" > estils.css
git add index.html # ens oblidem estils.css
git commit -q -m "feat: estructura inicial de la pàgina"# 3. Tot en un sol amend
git config user.email "[email protected]"
git add estils.css
git commit --amend --author="Carla Vidal <[email protected]>" --no-edit
git log -1 --format='%h autor: %an <%ae> | committer: %cn <%ce>'
git show --stat --oneline HEAD3f8a1d6 autor: Carla Vidal <[email protected]> | committer: Carla Vidal <[email protected]>
3f8a1d6 feat: estructura inicial de la pàgina estils.css | 1 + index.html | 1 + 2 files changed, 2 insertions(+)
El committer també s'ha corregit perquè l'--amend va crear l'objecte després d'arreglar user.email.
3f8a1d6 HEAD@{0}: commit (amend): feat: estructura inicial de la pàgina
8f4c2a9 HEAD@{1}: commit (initial): feat: estructura inicial de la pàgina8f4c2a9 → 3f8a1d6. Un commit és immutable (lliçó 01-04): --amend no el modifica, en crea un de nou i mou la branca. Si l'original estigués publicat, qui el tingués baixat el continuaria tenint, la branca hauria divergit i el push seria rebutjat: exactament l'escenari de la lliçó 09-03.
# 5. useConfigOnly
git config --global user.useConfigOnly true
mkdir /tmp/p9-01-c && cd /tmp/p9-01-c && git init -q -b main
echo x > f.txt && git add . && git commit -m "prova"Git es nega en lloc d'inventar-se usuari@nom-de-la-maquina. El problema de l'apartat 5.3 deixa de ser possible.
Solució 3:
mkdir /tmp/p9-01-d && cd /tmp/p9-01-d && git init -q -b main
git config user.name "Ana Ferrer"; git config user.email "[email protected]"
for i in 1 2 3 4 5 6; do echo "linia $i" >> app.js; git add . ; git commit -q -m "commit $i"; done
git switch -q -c GT-241
echo "// filtre" >> app.js && git commit -q -am "GT-241 base del filtre"
echo "// filtre ui" >> estils.css && git add . && git commit -q -m "GT-241 estils del filtre"
git log --oneline -3b52c9d1 (HEAD -> GT-241) GT-241 estils del filtre 4f8a2e6 GT-241 base del filtre 7d3a8f4 (main) commit 6
Lectura: no hi ha cap operació a mitges i no hi ha res sense desar. Això és bona notícia: tot el que hi havia estava confirmat i, per tant, és a la base de dades.
Lectura: des d'aquí, els dos commits de
GT-241ja no són assolibles.git logno els veu. Això és el que provoca el pànic, i és només un problema de referències.
7d3a8f4 HEAD@{0}: reset: moving to HEAD~2
b52c9d1 HEAD@{1}: commit: GT-241 estils del filtre
4f8a2e6 HEAD@{2}: commit: GT-241 base del filtre
7d3a8f4 HEAD@{3}: checkout: moving from main to GT-241Lectura: allà hi ha tot.
HEAD@{1}és la punta que hi havia abans del reset.
Lectura: Git confirma que el commit existeix i que ningú no l'apunta. Dangling aquí és exactament el que esperem (lliçó 09-05).
# 4 i 5. El rescat
git branch rescat b52c9d1
git log --oneline rescat -3
git diff GT-241 rescat --statb52c9d1 (rescat) GT-241 estils del filtre 4f8a2e6 GT-241 base del filtre 7d3a8f4 (HEAD -> GT-241, main) commit 6
Feina intacta. Crear una branca és més segur que fer reset, perquè no mou res del que ja està bé: només afegeix un punter. És el patró que es generalitza a la lliçó 09-04.
# 6. La diferència: canvis SENSE confirmar
git switch -q GT-241
echo "// feina de tres hores sense confirmar" >> app.js
git status --shortAquesta línia no és enlloc. No hi ha cap entrada de reflog que la registri, no hi ha cap objecte penjant que la contingui, git fsck no troba res. Mai no va arribar a la base de dades d'objectes.
I la variant que canvia el resultat completament:
echo "// feina de tres hores" >> app.js
git add app.js # <-- l'única diferència
git reset --hard HEAD
git fsck --lost-foundAmb un simple git add, el contingut és recuperable. Aquesta és la frontera exacta entre el que Git pot salvar i el que no, i per això el consell de confirmar (o si més no preparar) sovint no és una mania d'estil: és la xarxa de seguretat.
Conclusió
Aquesta lliçó és l'índex del mòdul i, sobretot, el canvi d'actitud que fa que la resta funcioni.
- A Git gairebé res no es perd de debò. Els commits són objectes immutables que sobreviuen encara que cap referència no els apunti; esborrar una branca o moure un punter amb
resetno destrueix res. El que és veritablement fràgil és el que mai no va arribar a la base de dades: els canvis sense confirmar i els fitxers sense seguiment. git addja compta. El contingut preparat es desa com a blob i es pot rescatar. Confirmar és millor, però preparar ja és una xarxa.- El protocol de calma —
git status,git log --oneline --graph -20,git reflog -20i, només si cal,git fsck— no modifica res, triga tres segons i resol la majoria dels ensurts abans de tocar una sola ordre destructiva. cp -adel directori complet és la còpia de seguretat d'emergència. Uncloneno serveix: no s'emporta el reflog, ni els desaments temporals, ni el que és local.- Els missatges de Git són millors del que la seva fama suggereix.
fatal:significa que no s'ha fet res; les línieshint:solen contenir literalment la solució. - Els casos trivials es resolen en una ordre:
git switch -cper als canvis a la branca equivocada,git branch+reset --hard origin/xper als commits mal col·locats,commit --amend --authorper a la identitat,--amend --no-editper al fitxer oblidat, esborrarindex.lockdesprés de comprovar els processos, isafe.directoryper a la propietat dubtosa. - I la llista negra: res de
--force,clean -fdx,rm -rf .gitnigc --prune=nowen estat de pànic.
La resta del mòdul desenvolupa els casos que no caben en una fila de taula. Comencem pel més freqüent i pel que tanca una promesa pendent des de la lliçó 05-06: el mecanisme de git reset, els seus tres modes, què toca exactament cadascun, i com triar l'eina correcta segons si el que vols desfer està sense confirmar, confirmat sense publicar o ja publicat.
Continua a la lliçó 09-02: Desfent Canvis.
Dominant Git: De Principiant a Avançat
Mòdul 1: Introducció a Git
- Què és Git?
- Instal·lant Git
- Terminologia Bàsica de Git
- El Model de Dades de Git
- Configurant Git
- Configuració Inicial
Mòdul 2: Operacions Bàsiques de Git
- Creant un Repositori
- Clonant un Repositori
- Flux de Treball Bàsic de Git
- Preparant i Confirmant Canvis
- Inspeccionant Canvis amb git diff
- Visualitzant l'Historial de Confirmacions
Mòdul 3: Branques i Fusió
- Entenent les Branques
- Creant i Canviant Branques
- Fusionant Branques
- Estratègies de Fusió
- Resolent Conflictes de Fusió
- Gestió de Branques
Mòdul 4: Treballant amb Repositoris Remots
- Entenent els Repositoris Remots
- Afegint un Repositori Remot
- Autenticació amb Repositoris Remots
- Obtenint i Baixant Canvis
- Enviant Canvis
- Rastrejant Branques
Mòdul 5: Operacions Avançades de Git
- Rebase
- Rebase Interactiu
- Cherry-Picking de Confirmacions
- Desant Canvis Temporals
- Etiquetant Confirmacions
- Revertint Confirmacions
Mòdul 6: Eines i Tècniques de Git
- Usant Git Hooks
- Git Bisect
- Git Blame
- Git Log i Àlies
- Submòduls de Git
- Múltiples Còpies de Treball amb git worktree
Mòdul 7: Estratègies de Col·laboració i Flux de Treball
- Forks i Pull Requests
- Revisions de Codi amb Git
- Flux de Treball Git Flow
- GitHub Flow
- Trunk Based Development
- Integració Contínua amb Git
Mòdul 8: Bones Pràctiques i Consells de Git
- Escrivint Bons Missatges de Confirmació
- Mantenint un Historial Net
- Ignorant Fitxers amb .gitignore
- Atributs de Fitxer amb .gitattributes
- Bones Pràctiques de Seguretat
- Consells de Rendiment
Mòdul 9: Resolució de Problemes i Depuració
- Problemes Habituals de Git
- Desfent Canvis
- Resolent Divergències amb el Remot
- Recuperant Confirmacions Perdudes
- Tractant amb Repositoris Corruptes
- Tècniques Avançades de Depuració
