Portem sis mòduls ajornant aquesta lliçó. A la 03-02 vam dir que el reflog explica com Git recorda per on ha passat HEAD. A la 03-06 vam prometre que esborrar una branca amb -D gairebé mai no és irreversible, i vam remetre aquí. A la 05-01 vam dir que un rebase desastrós es pot desfer, i vam remetre aquí. A la 05-04, que un stash drop accidental és recuperable. A la 09-02, que els commits que un reset --hard deixa enrere continuen allà. I a la 09-03, que origin/main@{1} guarda on era el servidor abans de la reescriptura.

Totes aquestes promeses s'aguanten sobre la mateixa peça, i és hora de desenvolupar-la sencera.

El reflog és probablement la funcionalitat de Git amb millor relació entre el que salva i el poc que es coneix. Molta gent porta anys fent servir Git sense haver-lo executat mai, i és exactament l'ordre que converteix «he perdut tres dies de feina» en «he trigat dos minuts a recuperar-ho».

Comencem pel més important: si has arribat aquí en pànic, no has perdut res, sempre que allò que busques arribés a ser un commit. Continua llegint amb calma.

Contingut

  1. Què és el reflog i on viu
  2. Local i temporal: els dos límits que cal conèixer
  3. Llegir git reflog
  4. HEAD@{n} davant de HEAD@{temps}
  5. El patró universal de recuperació
  6. Receptari: reset --hard, branca esborrada, rebase, merge, --amend
  7. Quan el reflog no n'hi ha prou: git fsck --lost-found
  8. Recuperar un desament temporal eliminat
  9. Els límits reals: el que no es pot recuperar
  10. Higiene: no destruir la teva pròpia xarxa de seguretat

  1. Què és el reflog i on viu

El reflog és el registre local de tots els moviments de cada referència.

Cada vegada que una referència —HEAD, una branca, una branca de seguiment remota— canvia de valor, Git escriu una línia en un fitxer de text anotant d'on venia, on va, qui ho ha fet i per què.

Mira-t'ho directament:

cat .git/logs/HEAD | tail -3
4f8a2e6c9b1d5e3a b52c9d1e4a7f3c8b Ana Ferrer <[email protected]> 1785312041 +0200	commit: GT-241 base del filtre
b52c9d1e4a7f3c8b 7d3a8f4a2c6e9b1d Ana Ferrer <[email protected]> 1785312388 +0200	commit: GT-241 calcula el comptador
7d3a8f4a2c6e9b1d 4f8a2e6c9b1d5e3a Ana Ferrer <[email protected]> 1785312551 +0200	reset: moving to HEAD~2

Cada línia té: hash anterior, hash nou, identitat, marca de temps, i descripció de l'operació. És text pla, sense màgia.

L'estructura de directoris:

find .git/logs -type f | head
.git/logs/HEAD
.git/logs/refs/heads/main
.git/logs/refs/heads/GT-241
.git/logs/refs/remotes/origin/main
.git/logs/refs/stash
Fitxer Registra els moviments de
.git/logs/HEAD HEAD: tot el que has fet, inclosos els canvis de branca
.git/logs/refs/heads/<branca> Només aquella branca
.git/logs/refs/remotes/origin/<branca> La branca de seguiment: el que va portar cada fetch (09-03)
.git/logs/refs/stash La pila de desaments temporals (05-04)

I aquí hi ha la clau de tot el mòdul. Recupera la idea de la lliçó 09-01:

Un commit sobreviu mentre alguna cosa hi arribi. Les entrades del reflog compten com a referències.

Per això git gc no elimina un commit que apareix al reflog, encara que cap branca no l'apunti. El reflog no és només un registre: és el que manté vius els objectes orfes. Aquesta és la raó tècnica que la recuperació funcioni.

flowchart LR
    subgraph refs["Referències que mantenen viu un commit"]
        R1["Branques<br/>refs/heads/*"]
        R2["Etiquetes<br/>refs/tags/*"]
        R3["Branques remotes<br/>refs/remotes/*"]
        R4["Desament temporal<br/>refs/stash"]
        R5["REFLOG<br/>.git/logs/*"]
    end
    C["commit b52c9d1"]
    R1 -.-> C
    R5 ==>|"encara que les altres<br/>desapareguin"| C

  1. Local i temporal: els dos límits que cal conèixer

Abans del receptari, les dues limitacions. No entendre-les és el que fa que algú confiï en el reflog quan no ha de fer-ho.

És LOCAL

El reflog no viatja. No es publica amb push, no es baixa amb fetch, i un git clone no el porta.

git clone git.exemple.cat:equip/gestor-tasques.git
cd gestor-tasques
git reflog
2f8c6e1 HEAD@{0}: clone: from git.exemple.cat:equip/gestor-tasques.git

Una sola entrada. Tot l'historial de moviments que l'Ana tenia a la seva màquina es queda a la seva màquina.

Tres conseqüències pràctiques:

  1. El reflog de l'Ana no pot rescatar el desastre del Bruno. Cadascú té el seu.
  2. El reflog no és una còpia de seguretat. Si el disc mor, mor amb ell. Les còpies de seguretat reals són git bundle i els mateixos clons de l'equip (lliçó 09-05).
  3. Un clon acabat de fer no té xarxa de seguretat. El primer reset --hard desafortunat en un clon nou sí que et pot deixar sense res a recuperar, perquè no hi ha entrades anteriors.

I una conseqüència positiva, la de l'apartat 9 de la lliçó 09-03: si un company força i destrueix una branca del servidor, el teu reflog d'origin/branca conserva on era, i amb ell es restaura.

És TEMPORAL

Les entrades caduquen. Dos paràmetres ho governen:

git config --get gc.reflogExpire            # per defecte: 90 dies
git config --get gc.reflogExpireUnreachable # per defecte: 30 dies
Paràmetre S'aplica a Valor per defecte
gc.reflogExpire Entrades el commit de les quals continua sent assolible 90 dies
gc.reflogExpireUnreachable Entrades el commit de les quals ja no és assolible des de cap branca 30 dies

La segona és la que importa: els commits realment orfes tenen 30 dies. Després, un git gc els purga definitivament.

La caducitat no passa sola: l'executa git gc (lliçó 08-06), que Git llança automàticament quan s'acumulen objectes solts. A la pràctica, tens setmanes, no mesos, i encara menys anys.

Pots allargar el termini:

# Un any per a tot, als teus repositoris de treball
git config --global gc.reflogExpire "1 year"
git config --global gc.reflogExpireUnreachable "1 year"

# O per branca, amb un patró
git config gc.refs/heads/main.reflogExpire never

Costa uns quants megabytes i compra molta tranquil·litat.

I el contrari, que mai no has d'executar tret que sàpigues exactament què fas (lliçó 08-06):

# DESTRUEIX la xarxa de seguretat de tot aquest mòdul
git reflog expire --expire=now --all
git gc --prune=now

  1. Llegir git reflog

git reflog
7d3a8f4 HEAD@{0}: reset: moving to HEAD~2
9c4e7b2 HEAD@{1}: commit: GT-241 estils de l'indicador
3e7f1a8 HEAD@{2}: commit: GT-241 calcula el comptador
8a1f6c3 HEAD@{3}: rebase (finish): returning to refs/heads/GT-241
8a1f6c3 HEAD@{4}: rebase (pick): GT-241 base del filtre
2f8c6e1 HEAD@{5}: rebase (start): checkout origin/main
b52c9d1 HEAD@{6}: checkout: moving from main to GT-241
7d3a8f4 HEAD@{7}: pull: Fast-forward
4f8a2e6 HEAD@{8}: commit: chore: configuració d'eslint

Com es llegeix:

  • HEAD@{0} és sempre l'última cosa que ha passat. L'ordre va del present cap al passat.
  • El hash de cada línia és on va quedar HEAD DESPRÉS d'aquella operació. Aquest detall és crucial i produeix l'error més freqüent: si vols l'estat anterior a l'operació de HEAD@{0}, necessites el hash de HEAD@{1}.
  • La descripció diu quina ordre ho ha provocat.

El vocabulari de les descripcions:

Descripció Què l'ha produïda
commit: git commit
commit (amend): git commit --amend
commit (initial): El primer commit del repositori
checkout: moving from X to Y git switch o git checkout
reset: moving to X git reset
pull: / merge X: Una integració
rebase (start/pick/finish): Les fases d'un rebase
cherry-pick: git cherry-pick
revert: git revert
branch: Created from X git branch o git switch -c
clone: from X El clon inicial
fetch: forced-update Algú ha reescrit la branca remota (09-03)

El reflog d'una branca concreta

git reflog show GT-241
9c4e7b2 GT-241@{0}: commit: GT-241 estils de l'indicador
3e7f1a8 GT-241@{1}: commit: GT-241 calcula el comptador
8a1f6c3 GT-241@{2}: rebase (finish): refs/heads/GT-241 onto 2f8c6e1
b52c9d1 GT-241@{3}: branch: Created from HEAD

Molt més net que el de HEAD quan saps quina branca t'interessa, perquè no inclou els canvis de branca.

Formats útils

# Amb dates relatives: imprescindible per orientar-se
git reflog --date=relative -10

# Amb data i hora exactes
git reflog --date=iso -10

# Amb el missatge del commit a més de l'operació
git reflog --format='%h  %gd  %gs  |  %s' -10

# Només el d'una branca, amb gràfic
git log --walk-reflogs --oneline GT-241
7d3a8f4 HEAD@{fa 3 minuts}: reset: moving to HEAD~2
9c4e7b2 HEAD@{fa 41 minuts}: commit: GT-241 estils de l'indicador
3e7f1a8 HEAD@{fa 2 hores}: commit: GT-241 calcula el comptador

--date=relative és el que més ajuda en una recuperació: «ho tenia just abans de dinar» es tradueix directament a una entrada.

I git log -g (o --walk-reflogs), que recorre el reflog mostrant cada entrada com un commit complet:

git log -g --oneline --stat -5

Útil quan no reconeixes un commit pel seu missatge i necessites veure quins fitxers tocava.

  1. HEAD@{n} davant de HEAD@{temps}

Dues sintaxis que s'assemblen i signifiquen coses diferents.

Sintaxi Què és Exemple
HEAD@{2} L'entrada número 2 del reflog (comptant des de 0) Dues operacions enrere
HEAD@{2.hours.ago} On era HEAD fa dues hores Segons el rellotge
HEAD~2 Dos commits enrere al graf Recorre pares, no reflog
HEAD^2 El segon pare (només en fusions) L'altra branca de la fusió
main@{5} Cinquena entrada del reflog de main
main@{yesterday} On era main ahir
origin/main@{1} On era la branca remota abans de l'últim fetch 09-03
@{-1} La branca anterior on has estat El que fa servir git switch -

La distinció crítica, i mereix un exemple:

git log --oneline HEAD~3 -1        # tres commits enrere a la història
git log --oneline HEAD@{3} -1      # on era HEAD fa tres operacions

HEAD~3 recorre el graf: segueix la cadena de pares. HEAD@{3} recorre la teva història personal: pot ser en una altra branca, en un commit orfe, en qualsevol lloc on hagis estat.

Quan has fet un reset, HEAD~1 et porta al pare del commit actual (que ja no és el que buscaves) i HEAD@{1} et porta on eres abans del reset (que sí que ho és).

Les formes temporals accepten expressions molt naturals:

git log --oneline main@{yesterday} -3
git log --oneline main@{"1 week ago"} -3
git log --oneline main@{2026-07-28.09:00:00} -3
git diff main@{1.day.ago} main

I l'avís que dona Git quan et passes de termini:

warning: log for 'main' only goes back to Tue, 14 Jul 2026 10:12:04 +0200

No és un error: t'està dient que ha fet servir l'entrada més antiga disponible. Si la data que has demanat és anterior, el resultat no és el que et penses.

  1. El patró universal de recuperació

Totes les receptes de l'apartat següent són el mateix procediment. Aprèn-te'l una vegada.

flowchart TD
    A["1. git reflog (o git reflog show branca)"] --> B["2. Localitzar l'entrada:<br/>fer servir la descripció i --date=relative"]
    B --> C["3. VERIFICAR el candidat:<br/>git show hash --stat<br/>git log --oneline hash -5"]
    C --> D{"És el que buscava?"}
    D -->|No| B
    D -->|Sí| E["4. git branch rescat hash"]
    E --> F["5. Comprovar-ho a la branca nova"]
    F --> G["6. Integrar: merge, cherry-pick,<br/>o reset --hard si n'estàs segur"]

El pas 4 és el que cal interioritzar, i és la diferència entre una recuperació tranquil·la i un segon ensurt:

# BÉ: crea un punter nou. No mou res del que ja tens
git branch rescat 9c4e7b2

# PITJOR: mou la teva branca actual, i si t'equivoques de hash perds de vista el d'ara
git reset --hard 9c4e7b2

git branch és purament additiu. Si el hash era l'equivocat, esborres la branca i en proves un altre. No s'ha mogut res. Amb reset --hard cada intent fallit afegeix una capa més de confusió.

I el pas 3, verificar, evita l'error de recuperar el commit equivocat:

git show 9c4e7b2 --stat          # quins fitxers tocava?
git log --oneline 9c4e7b2 -5     # quina història té al darrere?
git diff HEAD 9c4e7b2 --stat     # en què es diferencia del que tinc ara?

I per inspeccionar sense comprometre't a res:

git switch --detach 9c4e7b2      # mirar el projecte en aquell estat
# ...obrir fitxers, executar l'aplicació...
git switch -                     # tornar

  1. Receptari: reset --hard, branca esborrada, rebase, merge, --amend

6.1. Desfer un git reset --hard

La situació. L'Ana volia treure un commit i va escriure HEAD~3.

No has perdut res (tret del que estigués sense confirmar, lliçó 09-02).

git reflog -5
4f8a2e6 HEAD@{0}: reset: moving to HEAD~3
9c4e7b2 HEAD@{1}: commit: GT-241 estils de l'indicador
3e7f1a8 HEAD@{2}: commit: GT-241 calcula el comptador
8a1f6c3 HEAD@{3}: commit: GT-241 base del filtre
2f8c6e1 HEAD@{4}: checkout: moving from main to GT-241

HEAD@{1} (9c4e7b2) és la punta que hi havia just abans del reset.

git show 9c4e7b2 --stat          # verificar
git branch rescat-GT-241 9c4e7b2
git log --oneline rescat-GT-241 -4
9c4e7b2 (rescat-GT-241) GT-241 estils de l'indicador
3e7f1a8 GT-241 calcula el comptador
8a1f6c3 GT-241 base del filtre
2f8c6e1 chore: configuració d'eslint

Recuperat. Ara, si vols que GT-241 torni a ser això:

git switch GT-241
git reset --hard rescat-GT-241
git branch -d rescat-GT-241

La drecera, si acabes de fer-ho i no has fet res més (lliçó 09-02):

git reset --hard ORIG_HEAD

O directament amb la sintaxi del reflog:

git reset --hard HEAD@{1}

6.2. Recuperar una branca esborrada amb -D

Això tanca la promesa de la lliçó 03-06.

git branch -D GT-247
Deleted branch GT-247 (was b52c9d1).

Primer regal: Git imprimeix el hash en esborrar. Si el tens al buffer del terminal, la recuperació és immediata:

git branch GT-247 b52c9d1

Si has tancat el terminal, hi ha dos camins.

A. El reflog de HEAD (funciona si vas estar en aquella branca):

git reflog --date=relative | grep -i "GT-247"
b52c9d1 HEAD@{fa 3 hores}: checkout: moving from GT-247 to main
b52c9d1 HEAD@{fa 3 hores}: commit: GT-247 valida el formulari

B. Buscar per missatge entre tots els commits orfes (funciona sempre):

git log -g --oneline --all | grep -i "GT-247"

O directament sobre la base d'objectes:

git fsck --lost-found --no-progress | grep commit

I el detall important que s'oblida: el reflog de la branca mateixa s'esborra amb ella.

ls .git/logs/refs/heads/
main

.git/logs/refs/heads/GT-247 ja no existeix. Per això cal buscar al reflog de HEAD, que sí que sobreviu.

# La recuperació
git branch GT-247 b52c9d1
git log --oneline GT-247 -3

-D gairebé mai no és irreversible. Els commits continuen a .git/objects, assolibles des del reflog de HEAD, durant almenys 30 dies. La promesa de la lliçó 03-06 queda complerta.

I l'excepció, per ser honestos: si la branca es va crear, se li van fer commits i es va esborrar sense que HEAD hi passés mai (per exemple, amb git branch X <hash> i després git branch -D X), el reflog de HEAD no la registra. En aquest cas, git fsck de l'apartat 7.

6.3. Rescatar un rebase que ha sortit malament

Això tanca la promesa de la lliçó 05-01.

El Bruno ha fet un rebase -i de deu commits, ha aixafat els que no havia d'aixafar i ha acabat amb --continue.

git reflog -12
4b8e2c9 HEAD@{0}: rebase (finish): returning to refs/heads/GT-241
4b8e2c9 HEAD@{1}: rebase (squash): GT-241 el filtre complet
8f2d6a1 HEAD@{2}: rebase (squash): GT-241 base del filtre
6e2b9c7 HEAD@{3}: rebase (pick): GT-241 base del filtre
2f8c6e1 HEAD@{4}: rebase (start): checkout origin/main
9c4e7b2 HEAD@{5}: checkout: moving from main to GT-241

L'entrada clau és rebase (start): l'anterior a ella és l'estat exacte previ al rebase. Aquí, HEAD@{5}9c4e7b2.

git branch abans-del-rebase 9c4e7b2
git log --oneline abans-del-rebase -10

Els deu commits originals, intactes.

Una drecera que gairebé ningú no coneix: durant un rebase, Git desa la posició inicial a ORIG_HEAD, i també en una referència especial:

git rev-parse ORIG_HEAD
cat .git/rebase-merge/orig-head 2>/dev/null    # només mentre el rebase està en marxa

I si el rebase encara no ha acabat, la sortida és molt més simple:

git rebase --abort

Torna tot exactament a l'estat previ. Fes-lo servir sempre que puguis: és més net que recuperar després.

Per comparar el resultat amb l'original i decidir què fer, git range-diff (lliçó 07-02):

git range-diff abans-del-rebase...GT-241

6.4. Desfer un merge que no havies d'haver fet

git merge funcionalitat/experimental
Merge made by the 'ort' strategy.
 14 files changed, 892 insertions(+), 31 deletions(-)

Si acabes de fer-lo i no hi ha res a sobre:

git reset --hard ORIG_HEAD

merge sempre escriu ORIG_HEAD abans de fusionar. És la sortida canònica.

Si ja has fet més coses a sobre:

git reflog -6
c7e2a91 HEAD@{0}: commit: GT-241 ajusta el comptador
2b9e6c1 HEAD@{1}: merge funcionalitat/experimental: Merge made by the 'ort' strategy.
7d3a8f4 HEAD@{2}: commit: GT-241 estils de l'indicador

HEAD@{2} és l'estat previ a la fusió. Però compte: si hi tornes, perds també el commit c7e2a91 que has fet després. El correcte és rescatar-lo a part:

git branch abans-del-merge 7d3a8f4
git switch abans-del-merge
git cherry-pick c7e2a91          # portar només el commit bo (lliçó 05-03)

I si el merge ja està publicat, res de reflog: la resposta és git revert -m 1 (lliçó 05-06).

Un cas freqüent i desconcertant: git merge --abort no funciona perquè la fusió va acabar bé; només era mala idea. --abort serveix mentre hi ha conflictes sense resoldre, no després.

6.5. Recuperar un commit sobreescrit per --amend

La Carla ha fet git commit --amend i s'ha adonat que ha perdut part del missatge original —o pitjor, que el commit anterior tenia canvis que el nou no té.

No has perdut res. --amend no modifica el commit: en crea un de nou i mou la branca (lliçó 09-01). L'original continua a la base de dades.

git reflog -3
3f8a1d6 HEAD@{0}: commit (amend): GT-241 calcula el comptador sobre la llista completa
8f4c2a9 HEAD@{1}: commit: GT-241 calcula el comptador
b52c9d1 HEAD@{2}: commit: GT-241 base del filtre

HEAD@{1} (8f4c2a9) és el commit original, abans de l'--amend.

# Veure el missatge original complet
git show 8f4c2a9 --stat
git log -1 --format=%B 8f4c2a9

# Comparar els dos
git diff 8f4c2a9 3f8a1d6

I les maneres de recuperar, segons el que necessitis:

# A. Tornar del tot a l'original
git reset --hard 8f4c2a9

# B. Recuperar només el missatge
git commit --amend -m "$(git log -1 --format=%B 8f4c2a9)"

# C. Recuperar un fitxer concret que s'ha perdut a l'amend
git restore --source=8f4c2a9 --staged --worktree app.js

# D. Veure què s'ha perdut exactament
git diff 3f8a1d6 8f4c2a9

6.6. Recuperar després d'un checkout a una altra branca amb commits en detached HEAD

Reprenent l'apartat 5.7 de la lliçó 09-01: has fet commits en HEAD desacoblat i te n'has anat.

git reflog -5
7d3a8f4 HEAD@{0}: checkout: moving from 9c4e7b2 to main
9c4e7b2 HEAD@{1}: commit: prova de l'algorisme alternatiu
3e7f1a8 HEAD@{2}: commit: esbós del filtre per data
4f8a2e6 HEAD@{3}: checkout: moving from main to 4f8a2e6

La línia checkout: moving from 9c4e7b2 to main et dona el hash d'on vas sortir.

git branch experiment-dates 9c4e7b2

La taula de referència ràpida

Desastre On mirar Ordre de rescat
reset --hard de més git reflog -5, entrada anterior al reset: git branch rescat HEAD@{1}
Branca esborrada amb -D El hash que ha imprès -D, o git reflog | grep <branca> git branch <branca> <hash>
Rebase desastrós Entrada anterior a rebase (start) git branch abans 9c4e7b2 / git rebase --abort si continua en marxa
Merge no desitjat ORIG_HEAD, o entrada anterior al merge git reset --hard ORIG_HEAD
--amend que se n'ha endut alguna cosa Entrada commit: anterior a commit (amend): git restore --source=<hash> <fitxer>
Commits en HEAD desacoblat checkout: moving from <hash> to <branca> git branch <nom> <hash>
Branca remota reescrita per un altre git reflog show origin/<branca> git branch antiga origin/<branca>@{1}
Desament temporal eliminat git fsck --unreachable | grep commit Apartat 8
No apareix res al reflog git fsck --lost-found Apartat 7

  1. Quan el reflog no n'hi ha prou: git fsck --lost-found

El reflog registra els moviments de referències. Hi ha objectes que arriben a la base de dades sense que cap referència es mogui, i per a aquests cal l'altra eina.

git fsck --lost-found --no-progress
Checking object directories: 100% (256/256), done.
dangling commit 9c4e7b2e4a7f3c8b6d2e9a5f1c7b3d8e4a6f2c9b
dangling commit 3e7f1a8b6d2e9a5f1c7b3d8e4a6f2c9b7d3a8f4a
dangling blob 6f2b9d4a8c1e5f3b7d9a2c6e4f8b1d3a5c7e9f2b
dangling tree 8a1f6c3d5e2b9c7f4a6d1e8b3c5f7a9d2e4b6c8a

I a més crea un directori amb enllaços:

ls .git/lost-found/commit/
ls .git/lost-found/other/

Què és cada cosa

Tipus Què és Per què apareix
dangling commit Un commit al qual no arriba cap referència reset, branca esborrada, rebase, --amend, desament temporal eliminat
dangling blob Contingut d'un fitxer sense arbre que el referenciï Un git add el commit del qual mai no es va arribar a fer
dangling tree Un directori sense commit que el faci servir Un commit a mitges, o l'arbre d'un desament temporal
unreachable X Igual, però comptant el reflog Amb --unreachable també es veuen els del reflog

dangling és normal i inofensiu. No és un símptoma de corrupció. És el que s'espera en qualsevol repositori actiu. La distinció entre això i els errors de debò (missing, broken link) és la lliçó 09-05.

Com inspeccionar els candidats

Un fsck pot tornar desenes de línies. La feina és identificar quin és el teu.

# Els commits penjants, amb data, autor i missatge, ordenats per data
git fsck --lost-found --no-progress 2>/dev/null \
  | awk '/dangling commit/ {print $3}' \
  | xargs -I{} git log -1 --format='%ci  %h  %an  %s' {} \
  | sort -r
2026-07-30 18:22:41 +0200  9c4e7b2  Ana Ferrer  GT-241 estils de l'indicador
2026-07-30 17:51:03 +0200  3e7f1a8  Ana Ferrer  GT-241 calcula el comptador
2026-07-28 11:04:17 +0200  8a1f6c3  Bruno Salas  WIP a GT-238

Això converteix una llista de hashos en informació utilitzable. Desa-ho com a àlies:

git config --global alias.perduts '!git fsck --lost-found --no-progress 2>/dev/null | awk "/dangling commit/ {print \$3}" | xargs -I{} git log -1 --format="%ci  %h  %an  %s" {} | sort -r'

Inspecció individual, amb les eines de lampisteria de la lliçó 01-04:

# El contingut de l'objecte commit tal qual
git cat-file -p 9c4e7b2
tree 8a1f6c3d5e2b9c7f4a6d1e8b3c5f7a9d2e4b6c8a
parent 2f8c6e1a4b7d9c3e5f2a8b6d1c9e4f7a3b5d2c8e
author Ana Ferrer <[email protected]> 1785312041 +0200
committer Ana Ferrer <[email protected]> 1785312041 +0200

GT-241 estils de l'indicador de pendents
# El diff complet
git show 9c4e7b2

# Només els fitxers
git show --stat 9c4e7b2

# Què contenia un blob penjant
git cat-file -p 6f2b9d4 | head -20

# De quin tipus és un objecte qualsevol
git cat-file -t 8a1f6c3

Els blobs penjants: l'add que mai no es va confirmar

Aquest és el cas que salva més feina del que la gent espera. Si vas preparar un fitxer amb git add i després el vas perdre, el contingut és a la base de dades (lliçó 09-01, taula de l'apartat 1).

# Buscar entre els blobs penjants el que contingui una cadena que recordis
for b in $(git fsck --lost-found --no-progress 2>/dev/null | awk '/dangling blob/ {print $3}'); do
  if git cat-file -p "$b" 2>/dev/null | grep -q "calculaPendents"; then
    echo "=== $b ==="
    git cat-file -p "$b" | head -5
  fi
done
=== 6f2b9d4a8c1e5f3b7d9a2c6e4f8b1d3a5c7e9f2b ===
function calculaPendents(tasques) {
  return tasques.filter(t => !t.completada).length;
}
# Recuperar-lo
git cat-file -p 6f2b9d4 > app.js

Buscar per una cadena que recordis és molt més eficaç que revisar els blobs un a un.

  1. Recuperar un desament temporal eliminat

Això tanca la promesa de la lliçó 05-04.

git stash drop
Dropped refs/stash@{0} (b52c9d1e4a7f3c8b6d2e9a5f1c7b3d8e4a6f2c9b)

Primer regal, un altre cop: drop imprimeix el hash. Amb ell:

git stash apply b52c9d1

I si va ser un git stash clear, que esborra tota la pila sense imprimir res, hi ha dos camins.

A. El reflog del desament temporal, si encara existeix:

git reflog show stash
cat .git/logs/refs/stash

stash clear esborra la referència, però el fitxer de log a vegades sobreviu fins al següent gc. Val la pena mirar-lo primer.

B. Buscar els commits del desament temporal entre els inassolibles:

git fsck --unreachable --no-progress | grep commit | awk '{print $3}' \
  | xargs -I{} git log -1 --format='%h %ci %s' {} \
  | grep -i "WIP on\|On .*:"
b52c9d1 2026-07-30 16:12:44 +0200 WIP on GT-241: 7d3a8f4 GT-241 base del filtre
8f4c2a9 2026-07-29 09:31:20 +0200 On main: proves del comptador

Els desaments temporals es reconeixen pel seu missatge: WIP on <branca>: (els automàtics) o On <branca>: <missatge> (els que has creat amb -m).

Un desament temporal és internament un commit de fusió amb dos o tres pares, i val la pena veure-ho perquè explica com funciona:

git show --format='%h  pares: %p  %s' -s b52c9d1
b52c9d1  pares: 7d3a8f4 4f8a2e6 9c4e7b2  WIP on GT-241: 7d3a8f4 GT-241 base del filtre
Pare Què conté
1r (7d3a8f4) El HEAD en el moment del desament
2n (4f8a2e6) L'índex (el que estava preparat)
3r (9c4e7b2) Els fitxers sense seguiment, si vas fer servir -u

I la recuperació:

# Aplicar-lo directament
git stash apply b52c9d1

# O tornar-lo a la pila per tractar-lo amb normalitat
git stash store -m "recuperat: GT-241 a mitges" b52c9d1
git stash list
stash@{0}: recuperat: GT-241 a mitges
# Veure què contenia sense aplicar-lo
git show b52c9d1                  # els canvis del directori de treball
git show b52c9d1^2                # el que estava preparat
git show b52c9d1^3                # els fitxers sense seguiment (si n'hi ha)

  1. Els límits reals: el que no es pot recuperar

Toca ser honestos. Hi ha dues fronteres que cap tècnica d'aquesta lliçó no travessa.

Frontera 1: el que mai no va entrar a la base de dades

Ja ho vam veure a la lliçó 09-01, i és la limitació fonamental:

Situació Recuperable?
Un commit, encara que la branca s'esborrés
Un git add sense confirmar : hi ha blob
Un desament temporal, fins i tot eliminat : són commits
Un fitxer modificat i destruït amb git restore No
Un fitxer destruït per reset --hard sense add previ No
Un fitxer sense seguiment esborrat per git clean No

Quan ets en un dels tres últims casos, Git ja no et pot ajudar. El que queda és fora de Git:

1. L'historial local del teu editor. És la via que més vegades funciona i la que menys es prova.

  • VS Code: paleta d'ordres → «Local History: Find Entry to Restore». Desa versions de cada fitxer que has editat, independentment de Git.
  • IntelliJ / WebStorm / Eclipse: menú contextual del fitxer → Local History → Show History. Desa setmanes.
  • Vim: si tens set undofile, l'historial de desfer persisteix entre sessions (:earlier 1h).

2. Còpies de seguretat del sistema: Time Machine a macOS, instantànies de Btrfs/ZFS, versions anteriors a Windows, còpies al núvol.

3. Buscar a /tmp i als fitxers d'intercanvi de l'editor (.swp de Vim, ~ d'altres).

Frontera 2: el que ja ha passat per gc

Si es va executar git gc --prune=now després que el commit quedés orfe, l'objecte ha desaparegut físicament. Si a més es va executar git reflog expire --expire=now --all, no en queda ni registre.

Comprova si encara hi és abans de rendir-te:

git cat-file -t 9c4e7b2
fatal: Not a valid object name 9c4e7b2

Aquest missatge significa que l'objecte ja no existeix. Però abans de donar-lo per perdut, mira fora de la teva màquina:

# El té el servidor?
git fetch origin '+refs/*:refs/remotes/copia/*'
git log --all --oneline | grep -i "GT-241"

# El té un company? (el seu clon és una còpia gairebé completa)
git remote add bruno /ruta/o/url/al/clon/del/bruno
git fetch bruno
git log --oneline bruno/GT-241

Aquesta és la xarxa de seguretat del model distribuït, i és el tema central de la lliçó 09-05.

I la prevenció, que és el que de debò resol això

# 1. Confirma aviat i sovint. Un "wip" cada mitja hora ho canvia tot.
git commit -am "wip"

# 2. Abans de qualsevol operació arriscada, un marcador. Costa dos segons.
git branch copia-$(date +%H%M)

# 3. Abans de netejar, un desament temporal amb els fitxers sense seguiment inclosos
git stash push -u -m "per si de cas"

# 4. Allarga el reflog als teus repositoris de treball
git config --global gc.reflogExpire "1 year"
git config --global gc.reflogExpireUnreachable "1 year"

# 5. Publica les teves branques, encara que estiguin a mitges
git push -u origin GT-241

La cinquena és la més infravalorada. Una branca publicada és a dues màquines. Cap accident local no hi pot fer res.

  1. Higiene: no destruir la teva pròpia xarxa de seguretat

Tanca la lliçó el que no s'ha de fer.

1. git reflog expire --expire=now --all. Buida el registre. Tot el que és orfe queda desprotegit.

2. git gc --prune=now. Elimina immediatament els objectes inassolibles. Té el seu ús legítim (després d'un filter-repo de la lliçó 08-05, on esborrar és justament l'objectiu), i cap en un dia normal.

3. Confiar en el reflog com a còpia de seguretat. És local, temporal i mor amb el disc. No és una còpia de seguretat.

4. Recuperar amb reset --hard en lloc de git branch. Cada intent fallit afegeix confusió. branch no mou res.

5. Treballar mesos sense publicar una branca. Un clon nou té el reflog buit; una branca sense publicar existeix en un sol disc.

I una comprovació de salut de trenta segons, per saber si la teva xarxa de seguretat és on et penses:

echo "Entrades al reflog de HEAD: $(git reflog | wc -l)"
echo "Entrada més antiga: $(git reflog --date=iso | tail -1)"
echo "reflogExpire: $(git config --get gc.reflogExpire || echo '90 dies (per defecte)')"
echo "reflogExpireUnreachable: $(git config --get gc.reflogExpireUnreachable || echo '30 dies (per defecte)')"
echo "Objectes penjants: $(git fsck --lost-found --no-progress 2>/dev/null | grep -c dangling)"
echo "Branques sense publicar: $(git for-each-ref --format='%(refname:short) %(upstream)' refs/heads | awk '$2==""{print $1}' | tr '\n' ' ')"

Errors Habituals i Consells

Error 1: no provar git reflog. És la primera ordre davant de qualsevol «he perdut commits», i la majoria de la gent no l'executa mai.

Error 2: confondre HEAD@{1} amb HEAD~1. El primer és on eres fa una operació; el segon és el commit pare. Després d'un reset, són coses completament diferents.

Error 3: agafar el hash de l'entrada equivocada. El hash d'una línia és on va quedar HEAD després d'aquella operació. Per a l'estat previ a HEAD@{0}, necessites HEAD@{1}.

Error 4: recuperar amb reset --hard en lloc de git branch. Si el hash era l'equivocat, amb branch esborres la branca i en proves una altra; amb reset cada intent complica més l'estat.

Error 5: creure que el reflog viatja en un clone. No viatja. El clon nou té una sola entrada i cap xarxa de seguretat.

Error 6: confiar en el reflog per sempre. 90 dies per al que és assolible, 30 per al que és orfe, i gc els aplica. A la pràctica, setmanes.

Error 7: espantar-se amb els dangling de git fsck. Són normals en qualsevol repositori actiu. El greu és missing i broken link (lliçó 09-05).

Error 8: donar per perdut un desament temporal. stash drop imprimeix el hash, stash clear deixa els commits orfes, i tots dos es recuperen amb git fsck --unreachable buscant WIP on.

Error 9: rendir-se sense mirar fora de Git. L'historial local de l'editor recupera el que Git no pot, i gairebé ningú no ho prova.

Consell 1: el patró és sempre el mateix. Reflog → verificar amb git showgit branch rescat <hash>.

Consell 2: git reflog --date=relative. «Ho tenia abans de dinar» es tradueix directament a una entrada.

Consell 3: defineix l'àlies git perduts. El dia que el necessitis no recordaràs la canonada de fsck i awk.

Consell 4: git rebase --abort mentre puguis. Molt més net que recuperar després.

Consell 5: allarga el reflog a un any. Dues línies de configuració global i uns quants megabytes.

Consell 6: publica les teves branques. Una branca en dues màquines és immune als accidents d'una.

Exercicis

Exercici 1: el receptari complet

Sobre un repositori de proves amb app.js, estils.css i index.html:

  1. Crea sis commits a main i una branca GT-241 amb tres commits propis.
  2. Provoca quatre desastres seguits, sense recuperar entre l'un i l'altre: a. git reset --hard HEAD~2 a GT-241. b. git switch main && git branch -D GT-241. c. Un git commit --amend a main que esborri part del missatge. d. Un git merge d'una branca experimental que no volies.
  3. Executa git reflog --date=relative i anota, per a cada desastre, quina entrada faries servir.
  4. Recupera les quatre coses fent servir sempre git branch, mai reset.
  5. Verifica cada recuperació amb git show --stat i git diff.

Exercici 2: els límits de la recuperació

  1. En un repositori nou, crea tres situacions: un commit, un fitxer preparat amb git add, i un fitxer modificat només al disc.
  2. Executa git reset --hard HEAD~1.
  3. Recupera el commit amb el reflog i el fitxer preparat amb git fsck --lost-found + git cat-file -p.
  4. Documenta per què el tercer és irrecuperable, comprovant-ho amb git fsck i git reflog.
  5. Crea un desament temporal amb -u, elimina'l amb git stash clear i recupera'l amb git fsck --unreachable.
  6. Comprova amb git show --format='%p' -s que el desament té tres pares i inspecciona'n cadascun.

Exercici 3: la caducitat i la higiene

  1. Crea un repositori amb deu commits i provoca cinc commits orfes amb reset.
  2. Comprova que apareixen a git fsck --lost-found.
  3. Executa git reflog expire --expire=now --all i torna a mirar git reflog i git fsck. Explica la diferència entre les dues sortides.
  4. Executa git gc --prune=now i comprova amb git cat-file -t <hash> que els objectes han desaparegut de debò.
  5. Repeteix l'experiment configurant abans gc.reflogExpireUnreachable "1 year" i explica què canvia.
  6. Escriu la comprovació de salut de l'apartat 10 en un guió i interpreta'n la sortida en un repositori real teu.

Solucions

Solució 1:

rm -rf /tmp/p9-04 && mkdir /tmp/p9-04 && cd /tmp/p9-04 && 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 "chore: commit $i"; done

git switch -q -c GT-241
echo "// filtre" > filtre.js && git add . && git commit -q -m "GT-241 base del filtre"
echo "// comptador" >> filtre.js && git commit -q -am "GT-241 calcula el comptador"
echo ".pendent{}" > estils.css && git add . && git commit -q -m "GT-241 estils de l'indicador"
git rev-parse --short HEAD
9c4e7b2
# 2a. reset --hard
git reset -q --hard HEAD~2

# 2b. esborrar la branca
git switch -q main
git branch -D GT-241
Deleted branch GT-241 (was 8a1f6c3).
# 2c. amend destructiu
git commit -q --amend -m "chore"

# 2d. merge no desitjat
git switch -q -c experimental HEAD~1
echo "experiment" > experiment.txt && git add . && git commit -q -m "experiment que no val"
git switch -q main
git merge -q --no-ff experimental -m "Merge experimental"
git log --oneline -3
c7e2a91 (HEAD -> main) Merge experimental
2b9e6c1 (experimental) experiment que no val
3f8a1d6 chore
# 3. El reflog complet
git reflog --date=relative -12
c7e2a91 HEAD@{fa 4 segons}: merge experimental: Merge made by the 'ort' strategy.
3f8a1d6 HEAD@{fa 12 segons}: checkout: moving from experimental to main
2b9e6c1 HEAD@{fa 18 segons}: commit: experiment que no val
7d3a8f4 HEAD@{fa 25 segons}: checkout: moving from main to experimental
3f8a1d6 HEAD@{fa 33 segons}: commit (amend): chore
8f4c2a9 HEAD@{fa 40 segons}: checkout: moving from GT-241 to main
6e2b9c7 HEAD@{fa 48 segons}: reset: moving to HEAD~2
9c4e7b2 HEAD@{fa 55 segons}: commit: GT-241 estils de l'indicador
Desastre Entrada a fer servir Hash
a. reset --hard L'anterior al reset: 9c4e7b2
b. branch -D El hash imprès, o el checkout: moving from GT-241 8a1f6c3 (el que va imprimir -D)
c. --amend La commit: anterior a commit (amend): 8f4c2a9
d. merge L'anterior al merge, o ORIG_HEAD 3f8a1d6

Fixa't en el matís de (b): després del reset --hard, GT-241 apuntava a 6e2b9c7, i això és el que va esborrar -D (8a1f6c3 a l'exemple del missatge). Però el que l'Ana vol és l'estat previ al reset, 9c4e7b2. El reflog els conserva tots dos.

# 4. Recuperar, sempre amb branch
git branch rescat-GT-241-completa 9c4e7b2
git branch rescat-amend 8f4c2a9
git branch rescat-abans-merge 3f8a1d6

git branch
  experimental
* main
  rescat-GT-241-completa
  rescat-abans-merge
  rescat-amend

Cap d'aquestes operacions no ha mogut main. Tot el que hi havia continua on era, i ara a més hi ha tres punts d'accés al que s'ha recuperat.

# 5. Verificar
git log --oneline rescat-GT-241-completa -4
9c4e7b2 (rescat-GT-241-completa) GT-241 estils de l'indicador
8a1f6c3 GT-241 calcula el comptador
6e2b9c7 GT-241 base del filtre
7d3a8f4 chore: commit 6

Els tres commits, inclosos els dos que el reset --hard havia deixat enrere.

git log -1 --format=%B rescat-amend
chore: commit 6

El missatge original, abans que l'--amend el deixés en chore.

git show --stat rescat-abans-merge -1
git diff rescat-abans-merge main --stat
 experiment.txt | 1 +
 1 file changed, 1 insertion(+)

L'única cosa que va aportar el merge no desitjat. Per desfer-lo:

git reset --hard rescat-abans-merge
git log --oneline -1
3f8a1d6 (HEAD -> main, rescat-abans-merge) chore

I per restaurar GT-241 de debò:

git branch -m rescat-GT-241-completa GT-241
git branch -d rescat-amend rescat-abans-merge

Solució 2:

rm -rf /tmp/p9-04b && mkdir /tmp/p9-04b && cd /tmp/p9-04b && git init -q -b main
echo "base" > app.js && git add . && git commit -q -m "c1"

# 1. Les tres situacions
echo "COMMIT" >> app.js && git commit -q -am "c2: confirmat"
echo "PREPARAT" > estils.css && git add estils.css
echo "NOMES AL DISC" >> app.js

# 2. El desastre
git reset -q --hard HEAD~1
# 3. Recuperar el commit
git reflog -3
3f8a1d6 HEAD@{0}: reset: moving to HEAD~1
8f4c2a9 HEAD@{1}: commit: c2: confirmat
3f8a1d6 HEAD@{2}: commit (initial): c1
git branch rescat-commit 8f4c2a9
git show --stat rescat-commit
 app.js | 1 +
# El fitxer preparat
git fsck --lost-found --no-progress
dangling blob 6f2b9d4a8c1e5f3b7d9a2c6e4f8b1d3a5c7e9f2b
git cat-file -p 6f2b9d4
git cat-file -p 6f2b9d4 > estils.css
PREPARAT
# 4. El tercer
git fsck --lost-found --no-progress | wc -l
git reflog | grep -c "NOMES AL DISC"
grep -rl "NOMES AL DISC" .git/ 2>/dev/null | wc -l
1
0
0

Un sol objecte penjant (el de l'add), cap entrada de reflog, i ni un byte a tot .git. La línia NOMES AL DISC mai no es va convertir en objecte: no se'n va calcular el SHA, no es va comprimir, no es va escriure. Git no pot tornar el que mai no va rebre. Les úniques vies serien l'historial local de l'editor o una còpia del sistema de fitxers.

# 5. El desament temporal
echo "feina a mitges" >> app.js
echo "fitxer nou" > esborrany.txt
git stash push -u -q -m "GT-241 a mitges"
git stash list
git stash clear
git stash list
stash@{0}: On main: GT-241 a mitges
(sense sortida)
git fsck --unreachable --no-progress | grep commit | awk '{print $3}' \
  | xargs -I{} git log -1 --format='%h %ci %s' {} | grep -i "on main"
b52c9d1 2026-07-31 12:14:02 +0200 On main: GT-241 a mitges
git stash store -m "recuperat" b52c9d1
git stash list
git stash pop
git status --short
stash@{0}: recuperat
 M app.js
?? esborrany.txt

Recuperat íntegre, inclòs el fitxer sense seguiment.

# 6. L'estructura interna del desament temporal
git show --format='%h  pares: %p' -s b52c9d1
b52c9d1  pares: 3f8a1d6 8a1f6c3 9c4e7b2
git cat-file -t b52c9d1
git show --stat b52c9d1^2      # l'índex
git show --stat b52c9d1^3      # els que no tenen seguiment
commit
(buit: no hi havia res preparat)
 esborrany.txt | 1 +

Tres pares exactament com deia l'apartat 8: HEAD, l'índex i els fitxers sense seguiment. Un desament temporal és un commit, i per això sobreviu a stash clear.

Solució 3:

rm -rf /tmp/p9-04c && mkdir /tmp/p9-04c && cd /tmp/p9-04c && git init -q -b main
for i in $(seq 1 10); do echo "linia $i" >> app.js; git add .; git commit -q -m "commit $i"; done
git rev-parse --short HEAD
git reset -q --hard HEAD~5
9c4e7b2
# 2. Els orfes
git fsck --lost-found --no-progress | grep -c "dangling commit"
git reflog | head -3
5
3f8a1d6 HEAD@{0}: reset: moving to HEAD~5
9c4e7b2 HEAD@{1}: commit: commit 10
8f4c2a9 HEAD@{2}: commit: commit 9
# 3. Buidar el reflog
git reflog expire --expire=now --all
git reflog | wc -l
git fsck --lost-found --no-progress | grep -c "dangling commit"
0
5

La diferència clau: el reflog és buit, però els objectes continuen allà. reflog expire esborra el registre, no els objectes. Encara es poden recuperar amb fsck... però ja no saps quina era la punta ni en quin ordre estaven, perquè aquella informació vivia al reflog. Recuperable, sí; còmode, no.

git cat-file -t 9c4e7b2
git log --oneline 9c4e7b2 -3        # el commit i la seva història continuen accessibles
commit
# 4. Ara sí, la purga
git gc --prune=now -q
git fsck --lost-found --no-progress | grep -c "dangling commit"
git cat-file -t 9c4e7b2
0
fatal: Not a valid object name 9c4e7b2

Ara sí que ha desaparegut de debò. L'objecte ja no existeix a .git/objects. Cap tècnica d'aquesta lliçó no el recupera; només un clon aliè o una còpia de seguretat.

# 5. Amb reflog llarg
rm -rf /tmp/p9-04d && mkdir /tmp/p9-04d && cd /tmp/p9-04d && git init -q -b main
git config gc.reflogExpire "1 year"
git config gc.reflogExpireUnreachable "1 year"
for i in $(seq 1 10); do echo "l $i" >> app.js; git add .; git commit -q -m "c$i"; done
git rev-parse --short HEAD > /tmp/punta.txt
git reset -q --hard HEAD~5
git gc --prune=now -q
git reflog | wc -l
git cat-file -t "$(cat /tmp/punta.txt)"
6
commit

El gc --prune=now no ha pogut purgar res, perquè les entrades del reflog continuen vigents i el reflog compta com a referència. Aquest és el mecanisme exacte que manté vius els objectes orfes, i per això allargar la caducitat protegeix de debò.

# 6. Comprovació de salut
cat > /tmp/salut-git.sh <<'EOF'
#!/usr/bin/env bash
echo "Entrades al reflog de HEAD:  $(git reflog | wc -l)"
echo "Entrada més antiga:          $(git reflog --date=iso | tail -1)"
echo "reflogExpire:                $(git config --get gc.reflogExpire || echo '90 dies (defecte)')"
echo "reflogExpireUnreachable:     $(git config --get gc.reflogExpireUnreachable || echo '30 dies (defecte)')"
echo "Objectes penjants:           $(git fsck --lost-found --no-progress 2>/dev/null | grep -c dangling)"
echo "Branques sense publicar:     $(git for-each-ref --format='%(refname:short) %(upstream)' refs/heads | awk '$2==""{print $1}' | tr '\n' ' ')"
EOF
chmod +x /tmp/salut-git.sh && /tmp/salut-git.sh
Entrades al reflog de HEAD:  11
Entrada més antiga:          3f8a1d6 HEAD@{2026-07-31 11:02:14 +0200}: commit (initial): c1
reflogExpire:                1 year
reflogExpireUnreachable:     1 year
Objectes penjants:           5
Branques sense publicar:     main

L'última línia és la que dona més informació en un repositori real: cada branca sense upstream existeix en un sol disc.

Conclusió

El reflog és la raó per la qual gairebé res no es perd a Git, i saber-ho canvia per complet la relació amb les ordres destructives.

  • El reflog registra cada moviment de cada referència, en text pla sota .git/logs/. I no és només un registre: les seves entrades compten com a referències, i per això mantenen vius els commits als quals no arriba cap branca.
  • És local: no viatja en un clone, no es publica, i mor amb el disc. No és una còpia de seguretat. I és temporal: 90 dies per al que és assolible, 30 per al que és orfe, que gc aplica.
  • HEAD@{n} no és HEAD~n. El primer recorre la teva història personal de moviments; el segon recorre el graf. Després d'un reset, són coses diferents, i confondre'ls és l'error més comú.
  • El patró de recuperació és sempre el mateix: git reflog → verificar amb git show --statgit branch rescat <hash>. Crear una branca és additiu i no mou res; reset --hard complica cada intent fallit.
  • El receptari cobreix tot el que aquest curs havia anat prometent: desfer un reset --hard (entrada anterior al reset:), recuperar una branca esborrada amb -D (el hash que imprimeix, o el reflog de HEAD, perquè el de la branca s'esborra amb ella), rescatar un rebase (entrada anterior a rebase (start), o --abort si continua en marxa), desfer un merge (ORIG_HEAD) i recuperar el que se'n va endur un --amend (l'entrada commit: anterior).
  • Quan el reflog no n'hi ha prou, git fsck --lost-found: els objectes dangling són normals, i entre ells hi ha els commits orfes i els blobs d'un git add que mai no es va confirmar. git cat-file -p i git show els inspeccionen.
  • Un desament temporal és un commit amb dos o tres pares (HEAD, índex, sense seguiment), i per això sobreviu a drop i a clear: es busca per WIP on entre els inassolibles i es torna amb git stash store.
  • Els límits són reals: el que mai no va ser objecte (modificacions sense add, fitxers esborrats per clean) i el que ja ha passat per gc --prune. Aquí només salven l'historial local de l'editor, una còpia del sistema o el clon d'un company.
  • I la prevenció que fa innecessària tota la lliçó: confirmar sovint, posar un git branch abans del que és arriscat, stash push -u abans de netejar, allargar el reflog a un any i publicar les branques.

Fins aquí, tot s'aguantava sobre una premissa: que la base de dades d'objectes estigués sana. Els objectes existien, els hashos quadraven i Git els podia llegir.

I quan no? Quan git status respon error: object file .git/objects/4f/8a2e6... is empty, quan una referència apunta a un objecte que no existeix, quan l'índex és corrupte i Git es nega a fer absolutament res. Això ja no és un problema de referències mal posades: és un problema d'integritat, i té el seu propi diagnòstic, les seves pròpies reparacions i —el més important— un criteri clar per saber quan cal deixar d'intentar arreglar-ho i tornar a clonar.

Continua a la lliçó 09-05: Tractant amb Repositoris Corruptes.

Dominant Git: De Principiant a Avançat

Mòdul 1: Introducció a Git

Mòdul 2: Operacions Bàsiques de Git

Mòdul 3: Branques i Fusió

Mòdul 4: Treballant amb Repositoris Remots

Mòdul 5: Operacions Avançades de Git

Mòdul 6: Eines i Tècniques de Git

Mòdul 7: Estratègies de Col·laboració i Flux de Treball

Mòdul 8: Bones Pràctiques i Consells de Git

Mòdul 9: Resolució de Problemes i Depuració

Mòdul 10: Git al Món Real

© Copyright 2026. Tots els drets reservats