Tot el que hem vist al mòdul donava per suposada una cosa: que la base de dades d'objectes estava sana. El reflog trobava el hash, git branch creava el punter, git show llegia el commit. Els objectes eren on havien de ser i el seu contingut era el correcte.

Aquesta lliçó s'ocupa de quan no ho està.

error: object file .git/objects/4f/8a2e6c9b1d5e3a7f2c8b6d9e4a1f5c3b7d2e9a is empty
fatal: loose object 4f8a2e6c9b1d5e3a7f2c8b6d9e4a1f5c3b7d2e9a (stored in .git/objects/4f/8a2e6...) is corrupt

És el missatge que més espanta de tot el curs, i cal començar per dues coses.

La primera és que gairebé mai no és corrupció de debò. La majoria dels missatges alarmants tenen causes mundanes: un disc ple, un procés interromput, un antivirus que va bloquejar un fitxer. Abans de «reparar» res, cal descartar això.

La segona és la que fa habitable aquesta lliçó: en un sistema distribuït, cada clon és una còpia de seguretat gairebé completa. El repositori del Bruno té els mateixos objectes que el teu. El servidor també. Aquest és, gairebé sempre, el camí de recuperació més ràpid, i moltes vegades el correcte és directament tornar a clonar en lloc d'intentar arreglar res.

Veurem com distingir la corrupció real d'una altra cosa, com llegir git fsck sense espantar-se, com reparar els casos freqüents ordenats de menor a major gravetat, i —el més important— quan deixar d'intentar-ho.

Contingut

  1. Quan sospitar corrupció i quan és una altra cosa
  2. Les causes reals de la corrupció
  3. git fsck com a eina de diagnòstic
  4. Reparacions, de menor a major gravetat
  5. La xarxa de seguretat del model distribuït
  6. Reconstruir un repositori conservant la teva feina
  7. Prevenció: manteniment i còpies de seguretat de debò
  8. Quan deixar de reparar i tornar a clonar

  1. Quan sospitar corrupció i quan és una altra cosa

Abans de res, la taula de descart. Molts errors que semblen corrupció no ho són.

Missatge / símptoma És corrupció? Causa real i solució
fatal: Unable to create '.git/index.lock': File exists No Forrellat orfe (09-01, apartat 5.5)
error: insufficient permission for adding an object No Permisos: es va fer servir sudo alguna vegada. chown -R
fatal: detected dubious ownership No Propietat del directori (09-01, apartat 5.6)
error: cannot open .git/objects/...: No space left on device No encara, però ho serà Disc ple. Allibera espai ABANS de continuar
fatal: bad object HEAD Potser HEAD trencat (apartat 4.5) o repositori buit
warning: ignoring dangling symref No Referència simbòlica a una cosa inexistent. Inofensiu
dangling commit / blob / tree a fsck No Completament normal (09-04)
error: object file ... is empty Fitxer d'objecte de 0 bytes. Apartat 4.4
fatal: loose object ... is corrupt El contingut no coincideix amb el seu hash. Apartat 4.4
error: refs/heads/x does not point to a valid object! Referència trencada. Apartat 4.3
missing blob / broken link from a fsck Falta un objecte necessari. Apartat 3
fatal: index file smaller than expected Sí, però lleu Índex corrupte (apartat 4.1): és una memòria cau
error: bad signature 0x00000000 Sí, lleu El mateix: índex corrupte
fatal: packed object ... cannot be read , greu Packfile danyat. Apartat 4.4
Tot va lent però funciona No Rendiment (08-06)

El descart previ, en tres ordres

Abans de tocar res de .git:

# 1. Hi ha espai al disc? És la causa número u d'escriptures a mitges
df -h .
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda2       234G  234G     0 100% /home

Si veus Use% 100%, aquest és el problema. Allibera espai abans de continuar; qualsevol reparació escriurà objectes i fallarà igualment.

# 2. És meu, el repositori? Tinc permisos?
ls -ld .git .git/objects
find .git -not -user "$(id -un)" | head
# 3. On viu el repositori?
df -T . | tail -1
/dev/sda2 ext4 ...

Si en lloc d'ext4/apfs/ntfs local hi veus nfs, cifs, fuse.sshfs o una ruta dins de Dropbox, OneDrive, iCloud o Google Drive, ja tens l'explicació, i és l'apartat 2.

  1. Les causes reals de la corrupció

Git és notablement robust. Els objectes són immutables, s'escriuen un cop, i cadascun porta la seva pròpia suma de verificació: el nom de l'objecte és el hash del seu contingut, així que qualsevol alteració es detecta en llegir-lo (lliçó 01-04). La corrupció, quan passa, gairebé sempre ve de fora.

Causa Com passa Freqüència
Repositori en una carpeta sincronitzada amb el núvol Dropbox/OneDrive/iCloud/Drive sincronitzen fitxers de .git en ple commit, o resolen «conflictes» duplicant fitxers La més comuna de llarg
Disc ple Git escriu un objecte a mitges i no el pot acabar Molt comuna
Tall de llum o tancament forçat durant un commit/gc Escriptura interrompuda Comuna en portàtils
.git en un volum de xarxa (NFS, SMB, sshfs) Bloquejos i mtime poc fiables, escriptures reordenades Comuna en entorns corporatius
Antivirus Bloqueja o posa en quarantena fitxers de .git/objects Comuna a Windows
Matar processos de Git a la brava kill -9 durant gc o repack Ocasional
Disc amb sectors defectuosos Maquinari que falla Rara, però greu
Editar fitxers de .git a mà «Ara arreglaré això amb un editor de text» Autoinfligida

El cas del núvol, que mereix èmfasi

No posis un repositori Git dins d'una carpeta sincronitzada pel núvol.

És la causa clàssica, i el raonament és senzill: Git escriu molts fitxers petits en un ordre concret (objecte, després índex, després referència). Un sincronitzador els puja segons el seu propi criteri i velocitat. Si treballes des de dues màquines, el sincronitzador pot combinar un .git/index d'una amb les referències de l'altra, o crear fitxers del tipus main (conflicted copy 2026-07-28).lock.

El resultat és un repositori en un estat que Git mai no hauria pogut produir.

I el més irònic: no cal en absolut. Git ja és un sistema de sincronització distribuït. Per tenir el projecte en dues màquines, la resposta és un remot, no Dropbox.

# Si et trobes en aquesta situació: treu el repositori de la carpeta sincronitzada
mv ~/Dropbox/gestor-tasques ~/projectes/gestor-tasques
cd ~/projectes/gestor-tasques
git fsck --full          # comprovar els danys

Si de debò necessites que un repositori hi visqui, l'única manera segura és que sigui un repositori nu (--bare) usat com a remot, al qual mai no s'escriu des de dos llocs alhora. Tot i així, git bundle (apartat 7) és millor idea.

La comprovació d'antivirus a Windows

Si la Carla treballa a Windows 11 i pateix errors intermitents:

Excloure de la protecció en temps real:
  C:\Users\carla\projectes\           (o com a mínim les carpetes .git)

I a Git for Windows, un ajust que redueix problemes amb antivirus i amb sistemes de fitxers lents:

git config --global core.fscache true

  1. git fsck com a eina de diagnòstic

git fsck (file system check) recorre la base d'objectes, verifica que el contingut de cada objecte coincideix amb el seu hash, i comprova que totes les referències entre objectes es resolen.

git fsck --full --no-progress

Opcions útils:

Opció Què fa
--full Comprova també els objectes dins dels packfiles (per defecte només els solts)
--no-progress Sense barra de progrés: millor per redirigir a un fitxer
--lost-found Crea enllaços a .git/lost-found/ (09-04)
--unreachable Llista també el que és inassolible comptant el reflog
--connectivity-only Ràpid: només comprova enllaços, no verifica hashos
--strict Més sever: avisa de coses que Git tolera
--dangling / --no-dangling Mostrar o amagar els penjants

Llegir la sortida: el que és normal davant del que és greu

Aquesta és la taula que evita el pànic innecessari.

Línia de fsck Gravetat Què significa Què fer
dangling commit <sha> Cap Commit al qual no arriba cap referència Res. És normal després de reset, rebase, --amend
dangling blob <sha> Cap Contingut d'un git add sense confirmar Res (o recuperar-lo, 09-04)
dangling tree <sha> Cap Directori sense commit que el faci servir Res
unreachable <tipus> <sha> Cap Igual, comptant el reflog Res
notice: HEAD points to an unborn branch Cap Repositori acabat de crear sense commits Res
warning: ... has zero-padded file modes Molt baixa Repositori antic o importat Res, o fsck.zeroPaddedFilemode ignore
warning: ... missingSpaceBeforeDate Molt baixa Metadades mal formades d'una importació Res
error: <sha>: object corrupt or missing ALTA L'objecte no es pot llegir Apartat 4.4
missing blob <sha> ALTA Un arbre referencia un blob que no existeix Apartat 4.4
missing tree <sha> ALTA Un commit referencia un arbre inexistent Apartat 4.4
broken link from <sha> to <sha> ALTA Un objecte apunta a un altre que falta Apartat 4.4
error: refs/heads/x does not point to a valid object! ALTA Referència trencada Apartat 4.3
dangling commit en quantitats enormes Mitjana Pot indicar un gc interromput git gc

La regla d'or per llegir fsck:

# Amagar el soroll normal i quedar-se NOMÉS amb el que importa
git fsck --full --no-progress 2>&1 | grep -v "^dangling" | grep -v "^notice"

Si això no torna res, el teu repositori està sa. Punt. Tot el que hagis vist era soroll.

# I per separar per gravetat
git fsck --full --no-progress 2>&1 | grep -E "^(error|missing|broken)"

Un exemple de sortida sana

git fsck --full --no-progress
Checking object directories: 100% (256/256), done.
Checking objects: 100% (18432/18432), done.
dangling commit 9c4e7b2e4a7f3c8b6d2e9a5f1c7b3d8e4a6f2c9b
dangling blob 6f2b9d4a8c1e5f3b7d9a2c6e4f8b1d3a5c7e9f2b
dangling commit 3e7f1a8b6d2e9a5f1c7b3d8e4a6f2c9b7d3a8f4a

Tres línies alarmants en aparença, zero problemes. Són les restes del reset --hard de la setmana passada i d'un git add que no es va arribar a confirmar. Un repositori actiu sempre té això.

Un exemple de sortida greu

Checking object directories: 100% (256/256), done.
error: refs/heads/GT-241 does not point to a valid object!
error: object file .git/objects/4f/8a2e6c9b1d5e3a7f2c8b6d9e4a1f5c3b7d2e9a is empty
fatal: loose object 4f8a2e6c9b1d5e3a7f2c8b6d9e4a1f5c3b7d2e9a is corrupt
missing blob 8a1f6c3d5e2b9c7f4a6d1e8b3c5f7a9d2e4b6c8a
broken link from  tree 2f8c6e1a4b7d9c3e5f2a8b6d1c9e4f7a3b5d2c8e
              to  blob 8a1f6c3d5e2b9c7f4a6d1e8b3c5f7a9d2e4b6c8a

Aquí sí que hi ha feina. Una referència trencada i un objecte illegible, amb l'arbre que el necessita identificat. És l'apartat 4.

Comprovar un objecte concret

git cat-file -t 4f8a2e6      # tipus
git cat-file -s 4f8a2e6      # mida
git cat-file -p 4f8a2e6      # contingut
fatal: Not a valid object name 4f8a2e6

Aquest missatge, per a un hash que saps que existia, és la confirmació que l'objecte ha desaparegut.

  1. Reparacions, de menor a major gravetat

4.1. .git/index corrupte (el cas més freqüent i més lleu)

error: bad index file sha1 signature
fatal: index file corrupt

o

fatal: index file smaller than expected

No has perdut res. I la raó és conceptualment important:

L'índex és una memòria cau derivada. No conté informació única: és una foto de què hi hauria d'haver al pròxim commit, reconstruïble enterament a partir de HEAD i del directori de treball.

Per això la reparació és de les més agraïdes de Git:

# 1. Treure l'índex trencat
rm -f .git/index

# 2. Reconstruir-lo des de HEAD
git reset

# 3. Comprovar
git status
git status --short
 M app.js
 M estils.css
?? esborrany.txt

L'única cosa que es perd és què estava preparat amb git add: després de la reconstrucció, totes les teves modificacions apareixen com a no preparades. Els canvis en si són intactes, perquè viuen als fitxers del disc, no a l'índex.

Un matís important: fes servir git reset (--mixed), mai git reset --hard. El primer reconstrueix l'índex i deixa el directori de treball en pau; el segon s'enduria per davant totes les teves modificacions (lliçó 09-02).

Si a més tenies un conflicte de fusió a mitges, la informació de les etapes de l'índex (:1:, :2:, :3: de la lliçó 03-05) sí que es perd, i cal refer la fusió:

git merge --abort 2>/dev/null || true
rm -f .git/index
git reset
git merge <branca>        # tornar a començar

4.2. Forrellats orfes

Ja vist a la lliçó 09-01, aquí en la seva versió completa. Els forrellats són fitxers .lock que Git crea abans d'escriure i esborra en acabar:

find .git -name "*.lock"
.git/index.lock
.git/refs/heads/GT-241.lock
.git/config.lock

El procediment:

# 1. SEMPRE primer: hi ha algun Git executant-se?
ps aux | grep "[g]it "

# 2. De quan són?
ls -l $(find .git -name "*.lock")

# 3. Si no hi ha processos i són antics, esborrar-los
find .git -name "*.lock" -delete

# 4. Comprovar
git status

Un cas especial que mereix cura: .git/config.lock. Si Git va morir escrivint la configuració, el fitxer .git/config pot haver quedat truncat. Comprova-ho abans d'esborrar el forrellat:

git config --list --local
cat .git/config

Si està trencat, la configuració local és el més fàcil de refer a mà de tot el repositori.

4.3. Referències trencades

error: refs/heads/GT-241 does not point to a valid object!

Què és. El fitxer refs/heads/GT-241 conté un hash que no correspon a cap objecte existent. Recorda que una branca són 41 bytes de text (lliçó 03-01):

cat .git/refs/heads/GT-241
git cat-file -t "$(cat .git/refs/heads/GT-241)"
b52c9d1e4a7f3c8b6d2e9a5f1c7b3d8e4a6f2c9b
fatal: Not a valid object name b52c9d1e4a7f3c8b6d2e9a5f1c7b3d8e4a6f2c9b

Confirmat: la referència apunta al buit.

Reparació A: el reflog de la branca. És la millor opció, perquè conserva història:

git reflog show GT-241
b52c9d1 GT-241@{0}: commit: GT-241 estils de l'indicador
7d3a8f4 GT-241@{1}: commit: GT-241 calcula el comptador
4f8a2e6 GT-241@{2}: branch: Created from main

Prova les entrades de dalt a baix fins a trobar-ne una de vàlida:

git cat-file -t 7d3a8f4      # commit → aquesta sí que existeix
git update-ref refs/heads/GT-241 7d3a8f4
git log --oneline GT-241 -3

Has perdut l'últim commit, però recuperes la branca. Molt millor que res.

Reparació B: packed-refs. Les referències també viuen consolidades en un fitxer (lliçó 08-06):

grep GT-241 .git/packed-refs
7d3a8f4a2c6e9b1d5f3a8c6e2b9d4f7a1c5e8b3d refs/heads/GT-241

De vegades la versió empaquetada és vàlida encara que la solta estigui trencada. Esborrant la solta, Git fa servir l'empaquetada:

rm .git/refs/heads/GT-241
git log --oneline GT-241 -3

Reparació C: el remot.

git fetch origin
git update-ref refs/heads/GT-241 origin/GT-241

Reparació D: si la branca no importa, esborrar-la.

git update-ref -d refs/heads/GT-241

I la neteja massiva, quan hi ha moltes referències trencades:

# Veure totes les referències i la seva validesa
git for-each-ref --format='%(refname) %(objectname)' | while read -r ref sha; do
  git cat-file -e "$sha" 2>/dev/null || echo "TRENCADA: $ref -> $sha"
done

4.4. Objectes solts illegibles o absents

Aquest és el cas seriós.

error: object file .git/objects/4f/8a2e6c9b1d5e3a7f2c8b6d9e4a1f5c3b7d2e9a is empty
fatal: loose object 4f8a2e6... is corrupt

Primer pas, el diagnòstic exacte:

ls -l .git/objects/4f/8a2e6c9b1d5e3a7f2c8b6d9e4a1f5c3b7d2e9a
-r--r--r-- 1 ana ana 0 jul 28 16:04 .git/objects/4f/8a2e6c9b...

Zero bytes: escriptura interrompuda. És el patró típic de disc ple o tall de llum.

# Què se suposa que era? (sovint es pot deduir del context de fsck)
git fsck --full --no-progress 2>&1 | grep -A2 "4f8a2e6"
broken link from  tree 2f8c6e1a4b7d9c3e5f2a8b6d1c9e4f7a3b5d2c8e
              to  blob 4f8a2e6c9b1d5e3a7f2c8b6d9e4a1f5c3b7d2e9a

La reparació que gairebé sempre funciona: portar-lo d'un altre clon.

Aquesta és la idea central de la lliçó i l'apartat 5 la desenvolupa. Els objectes de Git són idèntics a tot arreu: el mateix contingut produeix el mateix hash i el mateix fitxer comprimit. Si el Bruno té aquell commit, el seu objecte serveix exactament igual.

# 1. Treure l'objecte trencat (de zero bytes: no hi ha res a perdre!)
rm .git/objects/4f/8a2e6c9b1d5e3a7f2c8b6d9e4a1f5c3b7d2e9a

# 2. Portar-lo del remot: la manera senzilla
git fetch origin --force '+refs/heads/*:refs/remotes/origin/*'

# 3. O copiar-lo directament d'un altre clon
cp /ruta/al/clon/del/bruno/.git/objects/4f/8a2e6c9b1d5e3a... \
   .git/objects/4f/

# 4. Comprovar
git fsck --full --no-progress 2>&1 | grep -E "^(error|missing|broken)"

Si l'altre clon el té empaquetat en lloc de solt, cal extreure'l:

# Al clon sa:
cd /ruta/al/clon/del/bruno
git cat-file -p 4f8a2e6 > /tmp/objecte-recuperat

# Al repositori trencat:
cd ~/projectes/gestor-tasques
git hash-object -w -t blob /tmp/objecte-recuperat
4f8a2e6c9b1d5e3a7f2c8b6d9e4a1f5c3b7d2e9a

Si el hash resultant coincideix amb el que faltava, la reparació és exacta. I no pot no coincidir: el hash es calcula del contingut, així que si en surt un altre, el contingut no era el mateix (lliçó 01-04).

Per a un objecte de tipus tree o commit, el mateix procediment amb -t tree / -t commit i el contingut exacte que dona cat-file -p al clon sa.

Si l'objecte era un packfile danyat:

fatal: packed object 4f8a2e6... (stored in .git/objects/pack/pack-a1b2c3.pack) is corrupt

Un packfile trencat és pitjor, perquè conté milers d'objectes i les cadenes de delta depenen les unes de les altres (lliçó 08-06). Es pot intentar extreure el que és salvable:

# Moure el pack trencat fora i veure què sobreviu
mkdir -p /tmp/packs-trencats
mv .git/objects/pack/pack-a1b2c3.* /tmp/packs-trencats/
git fsck --full --no-progress

# Intentar recuperar objectes individuals del pack trencat
git verify-pack -v /tmp/packs-trencats/pack-a1b2c3.idx 2>/dev/null | head

Però siguem clars: amb un packfile danyat, la resposta correcta gairebé sempre és reclonar (apartat 8). Extreure objectes d'un pack trencat és un exercici d'arqueologia que rarament compensa quan hi ha un clon sa a un git clone de distància.

4.5. HEAD trencat

fatal: bad object HEAD
fatal: not a git repository (or any of the parent directories)

HEAD és un fitxer de text d'una línia (lliçó 03-01):

cat .git/HEAD

El que hauria de contenir:

ref: refs/heads/main

El que pot haver passat:

Contingut de .git/HEAD Problema Reparació
ref: refs/heads/branca-esborrada La branca no existeix git symbolic-ref HEAD refs/heads/main
Buit o brossa binària Escriptura interrompuda Reescriure'l
Un hash solt (sense ref:) HEAD desacoblat: no és un error git switch main
Un hash d'un objecte inexistent Corrupció real Apuntar a una branca vàlida
# Veure quines branques hi ha realment
ls .git/refs/heads/
cat .git/packed-refs 2>/dev/null | grep refs/heads

# Reparar apuntant a una branca existent (forma correcta, no editar a mà)
git symbolic-ref HEAD refs/heads/main

# Comprovar
git status
git log --oneline -3

Si no queda cap branca vàlida, el reflog de HEAD continua sent text pla i llegible encara que Git no funcioni:

tail -5 .git/logs/HEAD
... 7d3a8f4a2c6e9b1d ... commit: GT-241 calcula el comptador
git update-ref refs/heads/rescat 7d3a8f4
git symbolic-ref HEAD refs/heads/rescat

4.6. .git/config corrupte

fatal: bad config line 12 in file .git/config

És el fitxer més fàcil de reparar, perquè el seu contingut és trivial de refer:

cat .git/config
[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
[remote "origin"]
	url = git.exemple.cat:equip/gestor-tasques.git
	fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
	remote = origin
	merge = refs/heads/main

Si està truncat, edita'l (aquí sí, amb un editor de text: config és text pla per disseny i Git no el verifica amb hashos) o reconstrueix l'essencial:

git remote add origin git.exemple.cat:equip/gestor-tasques.git
git branch --set-upstream-to=origin/main main
git config user.name "Ana Ferrer"
git config user.email "[email protected]"

Taula resum de reparacions

Problema Gravetat Reparació Es perd alguna cosa?
.git/index corrupte Baixa rm .git/index && git reset Només què estava preparat
Forrellat .lock orfe Baixa Comprovar processos i esborrar Res
.git/config trencat Baixa Editar o refer amb git config Configuració local
Referència trencada Mitjana Reflog, packed-refs, o el remot Potser els últims commits d'aquella branca
HEAD trencat Mitjana git symbolic-ref HEAD refs/heads/main Res
Objecte solt illegible Alta Portar-lo d'un altre clon Res, si algú el té
Packfile danyat Molt alta Reclonar Només el que fos exclusivament local
Corrupció estesa Molt alta Reclonar El mateix

  1. La xarxa de seguretat del model distribuït

Aquí hi ha la idea que converteix aquesta lliçó en una cosa manejable, i que tanca el cercle del mòdul 1.

En un sistema centralitzat, el servidor és un punt únic de fallada: si es corromp, s'ha corromput el projecte. A Git, cada clon conté tot l'historial. L'Ana, el Bruno, la Carla, el Diego i el servidor tenen cinc còpies completes de la base d'objectes.

flowchart TD
    S["git.exemple.cat<br/>història completa"]
    A["Ana (Ubuntu)<br/>història completa<br/>+ GT-241 sense publicar"]
    B["Bruno (macOS)<br/>història completa"]
    C["Carla (Windows)<br/>història completa"]
    D["Diego (fork)<br/>història completa"]
    S <--> A
    S <--> B
    S <--> C
    S <--> D

Conseqüència pràctica: quan el teu repositori es corromp, la pregunta correcta no és «com el reparo?» sinó «què tinc jo que no tingui ningú més?».

# 1. Quines branques tinc sense publicar?
git for-each-ref --format='%(refname:short)  upstream=[%(upstream:short)]' refs/heads
main       upstream=[origin/main]
GT-241     upstream=[]
GT-238     upstream=[origin/GT-238]

GT-241 no té upstream: existeix només en aquesta màquina. És l'única cosa que cal salvar.

# 2. Quins commits tinc per davant del remot?
git log --oneline origin/main..main
git log --oneline --branches --not --remotes

# 3. Hi ha desaments temporals?
git stash list

# 4. Hi ha canvis sense confirmar?
git status --short

Amb aquestes quatre respostes ja saps exactament quant val la reparació. Si la resposta és «res, tot està publicat», reclonar triga dos minuts i és la solució perfecta.

I si el problema és del servidor, la direcció s'inverteix: qualsevol clon de l'equip el pot reconstruir.

# Reconstruir el remot des d'un clon sa
cd ~/projectes/gestor-tasques
git push --all origin
git push --tags origin

# O crear un mirall complet des de zero
git clone --mirror ~/projectes/gestor-tasques /tmp/gestor-tasques-restaurat.git

--mirror copia totes les referències tal qual: branques, etiquetes, notes. És la manera correcta de duplicar un repositori nu.

  1. Reconstruir un repositori conservant la teva feina

El procediment complet, quan has decidit reclonar però tens coses locals a salvar.

Pas 1: la còpia de seguretat, sempre

cd ~/projectes
cp -a gestor-tasques gestor-tasques-TRENCAT-$(date +%Y%m%d-%H%M)

Encara que vagis a reclonar. Costa deu segons i és l'única cosa que impedeix que un error durant el rescat sigui definitiu.

Pas 2: inventari del que només tens tu

cd ~/projectes/gestor-tasques

# Branques sense publicar
git for-each-ref --format='%(refname:short) %(upstream)' refs/heads | awk '$2==""{print $1}'

# Commits locals per davant del remot, de totes les branques
git log --oneline --branches --not --remotes

# Desaments temporals
git stash list

# Canvis sense confirmar
git status --short

# Etiquetes locals sense publicar
git tag --no-merged origin/main 2>/dev/null

Pas 3: extreure el que és local a un bundle

git bundle empaqueta commits en un sol fitxer que funciona com un remot. És l'eina correcta per a això, i funciona fins i tot si el repositori està parcialment danyat (mentre els objectes implicats estiguin sans).

# Tot el que tinc i no és al remot
git bundle create /tmp/rescat.bundle --branches --not --remotes

# O només una branca concreta
git bundle create /tmp/GT-241.bundle GT-241

# Comprovar que el bundle és vàlid
git bundle verify /tmp/rescat.bundle
The bundle contains these 2 refs:
7d3a8f4a2c6e9b1d5f3a8c6e2b9d4f7a1c5e8b3d refs/heads/GT-241
b52c9d1e4a7f3c8b6d2e9a5f1c7b3d8e4a6f2c9b refs/heads/GT-238
The bundle requires these 1 ref:
4f8a2e6c9b1d5e3a7f2c8b6d9e4a1f5c3b7d2e9a refs/heads/main
/tmp/rescat.bundle is okay

Si a més tens canvis sense confirmar, treu-los a part:

git diff > /tmp/canvis-sense-confirmar.patch
git diff --cached > /tmp/canvis-preparats.patch
# I els fitxers sense seguiment, a mà:
cp -a esborrany.txt notes.md /tmp/rescat-fitxers/

Si git bundle falla perquè el repositori està massa danyat, la via bruta també serveix:

# Copiar els fitxers del projecte (sense .git) i tractar-los com a canvis nous
mkdir /tmp/rescat-feina
rsync -a --exclude='.git' ~/projectes/gestor-tasques/ /tmp/rescat-feina/

Es perd l'historial de les branques locals, però es conserva el contingut, que sol ser el que importava.

Pas 4: clonar de nou

cd ~/projectes
mv gestor-tasques gestor-tasques-trencat
git clone git.exemple.cat:equip/gestor-tasques.git
cd gestor-tasques

Pas 5: reincorporar el que s'ha rescatat

# Portar el bundle com si fos un remot
git fetch /tmp/rescat.bundle 'refs/heads/*:refs/heads/*'
git branch
  GT-238
  GT-241
* main

Les branques locals han tornat, amb tots els seus commits.

# Els canvis sense confirmar
git apply /tmp/canvis-sense-confirmar.patch

# Els que estaven preparats
git apply --cached /tmp/canvis-preparats.patch

# Els fitxers sense seguiment
cp -a /tmp/rescat-fitxers/* .

Pas 6: verificar i netejar

git fsck --full --no-progress 2>&1 | grep -E "^(error|missing|broken)"
git log --oneline --all --graph -15
git status

Si no hi ha errors i tota la teva feina hi és:

rm -rf ~/projectes/gestor-tasques-trencat
rm /tmp/rescat.bundle /tmp/*.patch

No esborris la còpia fins que ho hagis verificat. És l'única regla que importa d'aquest apartat.

El procediment en una taula

Pas Ordre Per a què
1 cp -a <repo> <repo>-TRENCAT-<data> Xarxa de seguretat
2 git for-each-ref + git log --branches --not --remotes Saber què és exclusivament teu
3 git bundle create /tmp/rescat.bundle --branches --not --remotes Empaquetar el que és local
4 git clone <url> Repositori net
5 git fetch /tmp/rescat.bundle 'refs/heads/*:refs/heads/*' Tornar el que és local
6 git fsck --full + comprovar Verificar abans d'esborrar res

  1. Prevenció: manteniment i còpies de seguretat de debò

git maintenance i git fsck periòdics

Reprenent la lliçó 08-06:

# Manteniment programat en segon pla
git maintenance start

A més de millorar el rendiment, manté la base d'objectes ordenada i redueix el nombre de fitxers solts exposats a escriptures interrompudes.

I una comprovació periòdica, que en un repositori normal triga segons:

# Ràpida: només enllaços, no verifica hashos
git fsck --connectivity-only --no-progress

# Completa: verifica cada objecte contra el seu hash. Mensual està bé
git fsck --full --no-progress 2>&1 | grep -vE "^(dangling|notice)"

I una configuració que fa que Git verifiqui els objectes en rebre'ls, detectant la corrupció en el moment en lloc de mesos després:

git config --global transfer.fsckObjects true
git config --global fetch.fsckObjects true
git config --global receive.fsckObjects true

Costa una mica de temps a cada fetch i a canvi impedeix que un objecte mal format entri al teu repositori. Al servidor, receive.fsckObjects és directament obligatori.

git bundle: la còpia de seguretat de debò

Un bundle és un únic fitxer amb tot el que li demanis, que funciona com un remot. És portàtil, verificable i no depèn de cap servei.

# Còpia completa del repositori
git bundle create ~/copies/gestor-tasques-$(date +%Y%m%d).bundle --all

# Comprovar
git bundle verify ~/copies/gestor-tasques-20260731.bundle

# Restaurar: es clona com si fos una URL
git clone ~/copies/gestor-tasques-20260731.bundle gestor-tasques-restaurat

I còpies incrementals, si el repositori és gran:

# Només el posterior a una etiqueta
git bundle create ~/copies/des-de-v1.5.bundle v1.5.0..main

Un guió de còpia setmanal:

#!/usr/bin/env bash
# copia-git.sh — còpia de seguretat dels repositoris de treball
DESTI=~/copies/git
mkdir -p "$DESTI"
for repo in ~/projectes/*/; do
  [ -d "$repo/.git" ] || continue
  nom=$(basename "$repo")
  echo "=== $nom ==="
  git -C "$repo" bundle create "$DESTI/$nom-$(date +%Y%m%d).bundle" --all \
    && git -C "$repo" bundle verify "$DESTI/$nom-$(date +%Y%m%d).bundle" > /dev/null \
    && echo "  OK" || echo "  HA FALLAT"
done
# Conservar només les 8 còpies més recents de cada repositori
find "$DESTI" -name "*.bundle" -mtime +56 -delete

Per què un bundle és millor que copiar .git amb cp:

cp -a .git git bundle
Coherència si hi ha un Git escrivint Pot copiar un estat a mitges Coherent: el genera Git
Verificable No : git bundle verify
Mida Tot, inclosos els objectes orfes Només el que és assolible, comprimit
Restauració Copiar de tornada git clone <fitxer>
Portàtil (correu, USB) Milers de fitxers Un fitxer

La llista de prevenció

1. Mai un repositori en una carpeta sincronitzada pel núvol. La causa número u. Fes servir un remot.

2. Mai .git en un volum de xarxa si ho pots evitar. Clona en local i publica.

3. Exclou les teves carpetes de projectes de l'antivirus a Windows.

4. Vigila l'espai al disc. Un df -h de tant en tant.

5. git maintenance start als repositoris de treball.

6. transfer.fsckObjects true per detectar la corrupció quan entra.

7. Publica les teves branques. Una branca publicada és a dues màquines. És la millor còpia de seguretat i la més barata.

8. git bundle setmanal del que només tens tu.

9. No matis processos de Git amb kill -9, especialment durant gc o repack.

10. No editis fitxers de .git a mà (tret de config, que és text per disseny). Fes servir git update-ref, git symbolic-ref, git config.

  1. Quan deixar de reparar i tornar a clonar

La decisió més important de la lliçó, i la que més temps estalvia.

Reclonar és la resposta correcta quan:

  • El repositori està completament publicat (res local sense enviar). Aleshores reclonar no té cap cost.
  • Hi ha un packfile danyat. Extreure objectes d'un pack trencat rarament compensa.
  • git fsck torna més d'un grapat d'errors.
  • La corrupció reapareix després de reparar-la: hi ha una causa de fons (disc, núvol, antivirus) que no has resolt.
  • Portes més de trenta minuts intentant reparar-lo. El cost d'un git clone és de minuts; el d'una tarda d'arqueologia, no.

Val la pena intentar la reparació quan:

  • El problema és l'índex, un forrellat, HEAD o config. Són minuts i no es perd res.
  • És una referència trencada i el reflog o packed-refs la resolen.
  • Falta un objecte i saps d'on portar-lo.
  • Tens feina local important sense publicar i necessites extreure-la abans de reclonar (apartat 6).

En forma d'arbre:

flowchart TD
    Q1{"Tinc feina local<br/>sense publicar?"}
    Q1 -->|No| R1["RECLONAR.<br/>És la resposta correcta<br/>i triga dos minuts"]
    Q1 -->|Sí| Q2{"El dany és índex, forrellat,<br/>HEAD o una referència?"}
    Q2 -->|Sí| R2["Reparar: apartat 4.<br/>Minuts, sense pèrdua"]
    Q2 -->|No: objectes o packfiles| Q3{"Puc extreure el que és local<br/>amb git bundle?"}
    Q3 -->|Sí| R3["Bundle + reclonar +<br/>reincorporar (apartat 6)"]
    Q3 -->|No| R4["rsync del contingut sense .git,<br/>reclonar, i reaplicar com a canvis"]

I la frase que resumeix la lliçó:

Un repositori Git danyat no és un problema de dades: és un problema de logística. Les dades gairebé sempre són en un altre lloc. La teva feina consisteix a identificar què és exclusivament teu, salvar-ho, i portar la resta d'on estigui sa.

Errors Habituals i Consells

Error 1: espantar-se amb els dangling de git fsck. Són completament normals en qualsevol repositori actiu. Filtra amb grep -v "^dangling" i mira si queda alguna cosa.

Error 2: donar per corrupte el que és un disc ple. df -h és la primera ordre. Reparar amb el disc al 100 % no pot funcionar.

Error 3: tenir el repositori a Dropbox, OneDrive o iCloud. És la causa número u de corrupció real, i no cal en absolut: Git ja sincronitza.

Error 4: git reset --hard per reparar un índex corrupte. rm .git/index && git reset reconstrueix sense tocar els teus fitxers; amb --hard els perdries tots.

Error 5: editar fitxers de .git amb un editor de text. Tret de config, fes servir les ordres: git update-ref, git symbolic-ref, git config.

Error 6: reparar sense haver fet una còpia. cp -a costa deu segons i evita que un error durant el rescat sigui definitiu.

Error 7: passar hores reparant un repositori que està íntegrament publicat. Comprova primer què tens que no tingui ningú més. Si la resposta és «res», reclona.

Error 8: fer servir cp -a .git com a còpia de seguretat «oficial». Val per a una emergència, però pot copiar un estat a mitges. git bundle és coherent i verificable.

Error 9: esborrar el repositori trencat abans de verificar el nou. Verifica amb git fsck i comprova que tota la teva feina hi és; després esborra.

Consell 1: aprèn el filtre de fsck. git fsck --full --no-progress 2>&1 | grep -vE "^(dangling|notice)". Si no torna res, estàs sa.

Consell 2: git bundle create ... --all setmanal. Un fitxer, verificable, restaurable amb git clone.

Consell 3: transfer.fsckObjects true a la configuració global. Detecta la corrupció quan entra, no mesos després.

Consell 4: publica les teves branques. És la còpia de seguretat més barata i la que més vegades salva.

Consell 5: l'índex és una memòria cau. Perdre'l no té importància; recordar-ho evita molt pànic innecessari.

Consell 6: posa't un límit de temps. Trenta minuts de reparació i, si continua trencat, reclona. El clon en triga dos.

Exercicis

Exercici 1: llegir git fsck sense espantar-se

  1. Crea un repositori amb deu commits, una branca amb tres commits propis i un git add sense confirmar.
  2. Provoca objectes penjants: un reset --hard HEAD~3, un commit --amend i un branch -D.
  3. Executa git fsck --full i compta quantes línies torna.
  4. Aplica el filtre del consell 1 i comprova que no queda res. Explica per què el repositori està sa malgrat les línies anteriors.
  5. Identifica, entre els penjants, quin correspon al git add sense confirmar i recupera'l amb git cat-file -p.

Exercici 2: trencar i reparar

Sobre un repositori de proves (mai sobre un de real):

  1. Corromp l'índex: printf 'brossa' > .git/index. Executa git status i anota l'error. Repara'l i explica per què no es perd res.
  2. Trenca una referència: echo "0000000000000000000000000000000000000000" > .git/refs/heads/GT-241. Executa git fsck i repara-la amb el reflog.
  3. Trenca HEAD: echo "ref: refs/heads/inexistent" > .git/HEAD. Diagnostica i repara amb git symbolic-ref.
  4. Buida un objecte solt: localitza'n un amb find .git/objects -type f i trunca'l amb : > <fitxer>. Executa git fsck --full i observa l'error.
  5. Repara el punt 4 des d'un clon sa que hauràs fet abans de trencar res.
  6. Després de cada reparació, executa git fsck --full filtrat i confirma que està net.

Exercici 3: rescat complet amb bundle

  1. Munta un remot local amb git init --bare i clona'l.
  2. Al clon, publica dos commits a main i crea dues branques locals sense publicar amb tres commits cadascuna. Afegeix un desament temporal i algun canvi sense confirmar.
  3. Fes l'inventari de l'apartat 5: quines branques no tenen upstream, quins commits no són al remot.
  4. Crea un bundle amb tot el que és local i verifica'l.
  5. Esborra el clon sencer (simulant corrupció irreparable) i torna a clonar.
  6. Reincorpora les dues branques des del bundle i comprova que els sis commits hi són.
  7. Explica què s'ha perdut i què no, i com ho hauries evitat publicant les branques.

Solucions

Solució 1:

rm -rf /tmp/p9-05 && mkdir /tmp/p9-05 && cd /tmp/p9-05 && git init -q -b main
git config user.name "Ana Ferrer"; git config user.email "[email protected]"
for i in $(seq 1 10); do echo "linia $i" >> app.js; git add .; git commit -q -m "commit $i"; done

git switch -q -c GT-241
for i in 1 2 3; do echo "filtre $i" >> filtre.js; git add .; git commit -q -m "GT-241 part $i"; done
echo "PREPARAT SENSE CONFIRMAR" > estils.css && git add estils.css
# 2. Els desastres
git reset -q --hard HEAD~3
git commit -q --amend -m "commit 10 (missatge canviat)" 2>/dev/null || true
git switch -q main
git branch -D GT-241
Deleted branch GT-241 (was 8f4c2a9).
# 3. La sortida completa
git fsck --full --no-progress 2>&1 | wc -l
git fsck --full --no-progress 2>&1 | head
9
Checking object directories: 100% (256/256), done.
Checking objects: 100% (43/43), done.
dangling commit 9c4e7b2e4a7f3c8b6d2e9a5f1c7b3d8e4a6f2c9b
dangling blob 6f2b9d4a8c1e5f3b7d9a2c6e4f8b1d3a5c7e9f2b
dangling commit 3e7f1a8b6d2e9a5f1c7b3d8e4a6f2c9b7d3a8f4a
dangling tree 8a1f6c3d5e2b9c7f4a6d1e8b3c5f7a9d2e4b6c8a
dangling commit 8f4c2a9b6d2e9a5f1c7b3d8e4a6f2c9b7d3a8f4a

Nou línies d'aspecte preocupant.

# 4. El filtre
git fsck --full --no-progress 2>&1 | grep -vE "^(dangling|notice|Checking)"
(sense sortida)

Zero errors. El repositori està perfectament sa.

La raó: dangling significa «aquest objecte existeix i és llegible, però cap referència no hi arriba». És exactament el resultat esperat d'un reset, un --amend i un branch -D: els commits continuen allà (per això són recuperables, lliçó 09-04) però ja no pengen de cap branca. És el contrari d'un problema: és la prova que la xarxa de seguretat funciona.

Els errors de debò comencen per error:, missing o broken link, i aquí no n'hi ha cap.

# 5. El blob de l'add sense confirmar
for b in $(git fsck --lost-found --no-progress 2>/dev/null | awk '/dangling blob/ {print $3}'); do
  echo "--- $b ---"; git cat-file -p "$b" | head -2
done
--- 6f2b9d4a8c1e5f3b7d9a2c6e4f8b1d3a5c7e9f2b ---
PREPARAT SENSE CONFIRMAR
git cat-file -p 6f2b9d4 > estils.css && cat estils.css
PREPARAT SENSE CONFIRMAR

Solució 2:

rm -rf /tmp/p9-05b && mkdir /tmp/p9-05b && cd /tmp/p9-05b && git init -q -b main
git config user.name "Bruno Salas"; git config user.email "[email protected]"
for i in 1 2 3 4 5; do echo "l $i" >> app.js; git add .; git commit -q -m "c$i"; done
git switch -q -c GT-241
echo "filtre" > filtre.js && git add . && git commit -q -m "GT-241 filtre"
git switch -q main

# El clon sa, ABANS de trencar res (pas 5)
git clone -q /tmp/p9-05b /tmp/p9-05b-sa
# 1. Índex corrupte
echo "FEINA SENSE CONFIRMAR" >> app.js
printf 'brossa' > .git/index
git status
error: bad index file sha1 signature
fatal: index file corrupt
rm -f .git/index
git reset
git status --short
tail -1 app.js
 M app.js
FEINA SENSE CONFIRMAR

Res perdut. La modificació continuava al fitxer del disc; l'índex només era la foto de què entraria al pròxim commit, i es reconstrueix des de HEAD més el directori de treball. És una memòria cau.

# 2. Referència trencada
git rev-parse GT-241 > /tmp/hash-bo.txt
echo "0000000000000000000000000000000000000000" > .git/refs/heads/GT-241
git fsck --full --no-progress 2>&1 | grep -E "^(error|missing|broken)"
error: refs/heads/GT-241 does not point to a valid object!
git log --oneline GT-241 -1
fatal: bad object GT-241
git reflog show GT-241
b52c9d1 GT-241@{0}: commit: GT-241 filtre
7d3a8f4 GT-241@{1}: branch: Created from HEAD
git cat-file -t b52c9d1        # comprovar que l'objecte existeix
git update-ref refs/heads/GT-241 b52c9d1
git log --oneline GT-241 -2
commit
b52c9d1 (GT-241) GT-241 filtre
7d3a8f4 (HEAD -> main) c5

Reparada, i amb el seu commit. El reflog conservava el hash correcte perquè el fitxer de reflog és independent del fitxer de referència.

# 3. HEAD trencat
echo "ref: refs/heads/inexistent" > .git/HEAD
git status
On branch inexistent

No commits yet

Git no falla, però es pensa que és en una branca que no existeix: git log no mostra res i un commit crearia una branca nova.

ls .git/refs/heads/
git symbolic-ref HEAD refs/heads/main
git status -sb | head -1
git log --oneline -1
GT-241  main
## main
7d3a8f4 c5
# 4. Objecte solt buidat
git gc -q --prune=now 2>/dev/null    # empaquetem per deixar pocs solts
echo "contingut nou" > nou.txt && git add . && git commit -q -m "c6"
OBJ=$(find .git/objects -type f -path "*/??/*" | head -1)
echo "Objecte triat: $OBJ"
cp "$OBJ" /tmp/objecte-original     # per si de cas
: > "$OBJ"
ls -l "$OBJ"
git fsck --full --no-progress 2>&1 | grep -vE "^(dangling|Checking)"
-rw-r--r-- 1 bruno bruno 0 jul 31 12:44 .git/objects/6f/2b9d4a8c1e5f3b...
error: object file .git/objects/6f/2b9d4a8c1e5f3b7d9a2c6e4f8b1d3a5c7e9f2b is empty
error: unable to mmap .git/objects/6f/2b9d4...: No such device
fatal: loose object 6f2b9d4a8c1e5f3b7d9a2c6e4f8b1d3a5c7e9f2b is corrupt
git log --oneline -1
fatal: loose object 6f2b9d4... is corrupt

Git es nega a operar: no pot llegir un objecte que necessita.

# 5. Reparar des del clon sa
SHA=$(basename $(dirname "$OBJ"))$(basename "$OBJ")
echo "Falta: $SHA"
rm "$OBJ"

# El té, el clon sa?
git -C /tmp/p9-05b-sa cat-file -t "$SHA"
Falta: 6f2b9d4a8c1e5f3b7d9a2c6e4f8b1d3a5c7e9f2b
blob
# Extreure'l i tornar-lo a escriure
git -C /tmp/p9-05b-sa cat-file -p "$SHA" > /tmp/contingut
NOU=$(git hash-object -w -t blob /tmp/contingut)
echo "Escrit: $NOU"
[ "$NOU" = "$SHA" ] && echo "COINCIDEIX: reparació exacta"
Escrit: 6f2b9d4a8c1e5f3b7d9a2c6e4f8b1d3a5c7e9f2b
COINCIDEIX: reparació exacta

El hash coincideix, així que la reparació és demostrablement exacta. No pot ser d'altra manera: el nom de l'objecte és el hash del seu contingut (lliçó 01-04), de manera que si el hash surt igual, el contingut és idèntic byte a byte. Aquesta propietat és la que fa que reparar un repositori Git des d'un altre clon sigui segur i verificable.

# 6. Verificació final
git fsck --full --no-progress 2>&1 | grep -vE "^(dangling|notice|Checking)"
git log --oneline --all -3
(sense sortida)
2f8c6e1 (HEAD -> main) c6
7d3a8f4 c5
b52c9d1 (GT-241) GT-241 filtre

Solució 3:

rm -rf /tmp/p9-05c && mkdir /tmp/p9-05c && cd /tmp/p9-05c
git init -q --bare servidor.git
git clone -q servidor.git feina && cd feina
git config user.name "Carla Vidal"; git config user.email "[email protected]"

# 2. El que és publicat
echo "// gestor-tasques" > app.js && git add . && git commit -q -m "chore: inici"
echo "// llistat" >> app.js && git commit -q -am "feat: llistat"
git push -q -u origin main

# Dues branques locals SENSE publicar
for r in GT-241 GT-247; do
  git switch -q -c "$r" main
  for i in 1 2 3; do echo "$r part $i" >> "$r.js"; git add .; git commit -q -m "$r part $i"; done
done
git switch -q main

# Un desament temporal i canvis sense confirmar
echo "experiment" >> app.js && git stash push -q -m "experiment a mitges"
echo "feina en curs" >> app.js
echo "esborrany" > esborrany.txt
# 3. Inventari
echo "--- Branques sense upstream ---"
git for-each-ref --format='%(refname:short) %(upstream)' refs/heads | awk '$2==""{print "  "$1}'
echo "--- Commits que no són a cap remot ---"
git log --oneline --branches --not --remotes
echo "--- Desaments temporals ---"
git stash list
echo "--- Sense confirmar ---"
git status --short
--- Branques sense upstream ---
  GT-241
  GT-247
--- Commits que no són a cap remot ---
2f8c6e1 GT-247 part 3
7a4d9b3 GT-247 part 2
5c1e8f2 GT-247 part 1
9c4e7b2 GT-241 part 3
3e7f1a8 GT-241 part 2
8a1f6c3 GT-241 part 1
--- Desaments temporals ---
stash@{0}: On main: experiment a mitges
--- Sense confirmar ---
 M app.js
?? esborrany.txt

Sis commits, dues branques, un desament temporal i dos canvis existeixen únicament en aquesta màquina. Aquest és exactament el valor de la reparació.

# 4. El bundle
git bundle create /tmp/rescat.bundle --branches --not --remotes
git bundle verify /tmp/rescat.bundle
The bundle contains these 2 refs:
9c4e7b2... refs/heads/GT-241
2f8c6e1... refs/heads/GT-247
The bundle requires these 1 ref:
b52c9d1...
/tmp/rescat.bundle is okay
# El desament temporal i el que és sense confirmar, a part
git stash show -p stash@{0} > /tmp/stash.patch
git diff > /tmp/sense-confirmar.patch
mkdir -p /tmp/fitxers-nous && cp esborrany.txt /tmp/fitxers-nous/
# 5. La catàstrofe
cd /tmp/p9-05c && rm -rf feina
git clone -q servidor.git feina && cd feina
git log --oneline --all
b52c9d1 (HEAD -> main, origin/main) feat: llistat
1e6f2c8 chore: inici

Només el que és publicat.

# 6. Reincorporar
git fetch -q /tmp/rescat.bundle 'refs/heads/*:refs/heads/*'
git branch
git log --oneline GT-241 -3
git log --oneline GT-247 -3
  GT-241
  GT-247
* main
9c4e7b2 (GT-241) GT-241 part 3
3e7f1a8 GT-241 part 2
8a1f6c3 GT-241 part 1
2f8c6e1 (GT-247) GT-247 part 3
7a4d9b3 GT-247 part 2
5c1e8f2 GT-247 part 1

Els sis commits i les dues branques, amb els seus hashos originals.

git apply /tmp/sense-confirmar.patch
cp /tmp/fitxers-nous/esborrany.txt .
git stash apply --index 2>/dev/null || git apply /tmp/stash.patch
git status --short
 M app.js
?? esborrany.txt
git fsck --full --no-progress 2>&1 | grep -vE "^(dangling|notice|Checking)"
(sense sortida)
# 7. Què s'ha perdut
git stash list
git reflog | wc -l
(sense sortida)
3

El que s'ha perdut:

  • La pila de desaments temporals com a tal. El contingut s'ha reaplicat des del pedaç, però l'entrada stash@{0} no existeix. (Un bundle amb --all hauria inclòs refs/stash.)
  • Tot el reflog, que és local i no viatja (lliçó 09-04). El clon nou té tres entrades.
  • Les branques de seguiment antigues i la configuració local del repositori.

El que no s'ha perdut: ni un sol commit, ni un sol canvi.

Com s'hauria evitat: publicant les branques.

git push -q -u origin GT-241 GT-247

Amb les dues branques publicades, l'inventari del pas 3 hauria sortit buit, i tota aquesta lliçó s'hauria reduït a rm -rf feina && git clone. Dos segons de push davant de vint minuts de rescat.

Conclusió

La corrupció d'un repositori Git espanta molt més del que costa.

  • La majoria dels missatges alarmants no són corrupció: un forrellat orfe, un disc ple, un problema de permisos o de propietat. Descarta-ho primer amb df -h i ls -ld .git.
  • Les causes reals vénen gairebé sempre de fora de Git: un repositori en una carpeta sincronitzada amb el núvol (la número u), un disc ple, un tall de llum, un volum de xarxa, un antivirus. Els objectes de Git són immutables i porten la seva pròpia verificació; Git rarament es trenca sol.
  • git fsck es llegeix filtrant el soroll. Els objectes dangling són completament normals —són les restes de cada reset, rebase i --amend— i la seva presència és la prova que la xarxa de seguretat de la lliçó 09-04 funciona. El que és greu comença per error:, missing o broken link.
  • Les reparacions, de menor a major: l'índex és una memòria cau derivada i es reconstrueix amb rm .git/index && git reset sense perdre res; els forrellats s'esborren després de comprovar processos; una referència trencada es repara amb el reflog, packed-refs o el remot; HEAD amb git symbolic-ref; un objecte illegible portant-lo d'un altre clon, amb la garantia que si el hash coincideix, la reparació és exacta.
  • El model distribuït és la xarxa de seguretat. Cada clon de l'equip és una còpia gairebé completa. La pregunta correcta davant d'un repositori danyat no és «com el reparo?» sinó «què tinc jo que no tingui ningú més?», i es respon amb git for-each-ref i git log --branches --not --remotes.
  • El rescat es fa amb git bundle: empaquetar el que és local en un fitxer, reclonar, i reincorporar-ho amb git fetch <bundle>. I sempre amb un cp -a previ i una verificació posterior abans d'esborrar res.
  • Prevenció: res de repositoris al núvol ni en volums de xarxa, git maintenance start, transfer.fsckObjects true, git bundle periòdic i —el que més rendeix— publicar les branques.
  • I el criteri: si tot està publicat, reclona. Triga dos minuts i és la resposta correcta. Reserva la reparació per a l'índex, els forrellats, HEAD i les referències, o per extreure feina local abans de reclonar.

Amb això, el mòdul ha cobert els quatre problemes que anunciava el tancament del mòdul 8: el reset --hard desafortunat, l'embolic amb el remot, la branca esborrada i el repositori que es nega a funcionar.

Queda el que és transversal. Perquè moltes vegades el problema no és que Git estigui trencat ni que hagis perdut alguna cosa, sinó que Git està fent alguna cosa que no entens: un fitxer que s'ignora quan no hauria de ser així, una configuració que ve d'un lloc que no sabies que existia, un push que falla per raons opaques, un canvi de comportament que ningú no recorda haver introduït. Per a això hi ha una caixa d'eines pròpia —traces, lampisteria, check-ignore, check-attr, ls-files— i, sobretot, un mètode.

Continua a la lliçó 09-06: Tècniques Avançades de Depuració.

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