Les cinc lliçons anteriors resolien problemes amb nom: he perdut commits, la meva branca ha divergit, el repositori és corrupte. Aquesta s'ocupa de la categoria que no té nom i que, a la pràctica, és la més freqüent:
Git està fent alguna cosa que no entenc, i no sé ni per on començar a mirar.
Un fitxer que apareix com a modificat i ningú no l'ha tocat. Un .gitignore que ignora el que no ha d'ignorar. Un push que triga quaranta segons en un projecte petit. Un hook que no s'executa. Una configuració que diu una cosa i un comportament que en diu una altra. Un git log que amaga commits que existeixen.
No són avaries: són desajustos entre el que et penses que hi ha i el que hi ha de debò. I per a això Git té una caixa d'eines que amb prou feines hem fregat: variables de traça que mostren el que passa per sota, ordres que diuen d'on surt cada ajust, i la capa de lampisteria (plumbing) que consulta la base de dades sense interpretacions.
Més important que les eines és el mètode, perquè sense ell les eines produeixen soroll. Tancarem amb un mètode de diagnòstic en quatre passos i amb una investigació real sobre gestor-tasques que combina bisect i blame per anar del símptoma al commit, i del commit a la línia i el seu perquè.
Contingut
- El mètode de diagnòstic, en quatre passos
- Variables de traça: veure el que Git fa per sota
- Depurar la configuració efectiva
- Depurar rutes i patrons:
check-ignore,check-attr,ls-files - Lampisteria per inspeccionar el graf
- Acotar amb
git diff --statigit log - Una investigació real: de
bisectablame - Un catàleg de misteris i les seves ordres
- El mètode de diagnòstic, en quatre passos
Abans de les eines, el procediment. És el que separa una investigació de vint minuts d'una tarda perduda.
flowchart TD
A["1. REPRODUIR<br/>L'ordre mínima que provoca el símptoma"]
B["2. AÏLLAR<br/>Treure variables: repo net, sense config,<br/>sense hooks, una altra màquina"]
C["3. CONSULTAR L'ESTAT REAL<br/>No el recordat. Lampisteria i traces"]
D["4. COMPROVAR LA HIPÒTESI<br/>Una predicció concreta i falsable"]
A --> B --> C --> D
D -->|"No es compleix"| C
D -->|"Es compleix"| E["Arreglar la causa,<br/>no el símptoma"]
Pas 1: reproduir
Redueix el símptoma a l'ordre mínima que el provoca.
Si no ho pots reproduir a voluntat, no pots verificar que ho has arreglat. I moltes vegades reduir el cas ja revela la causa: «només falla amb aquesta branca», «només amb aquest fitxer», «només des del portàtil de la Carla».
Pas 2: aïllar
Treu variables una a una:
# Sense configuració global ni de sistema
GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=/dev/null git status
# Sense hooks (lliçó 06-01)
git -c core.hooksPath=/dev/null commit -m "prova"
git commit --no-verify -m "prova"
# Sense àlies ni configuració del repositori
git -c include.path= <ordre>
# En un repositori acabat de clonar, a /tmp
git clone <url> /tmp/prova-neta && cd /tmp/prova-netaSi el problema desapareix sense la configuració global, ja saps on mirar. Si persisteix en un clon net, el problema és al repositori o al servidor, no a la teva màquina.
Pas 3: consultar l'estat real, no el recordat
Aquest és el pas que més vegades es passa per alt i el que més vegades resol.
| No preguntis... | Pregunta... |
|---|---|
| «Jo em penso que aquell fitxer està ignorat» | git check-ignore -v <fitxer> |
«Tinc user.email ben configurat» |
git config --list --show-origin --show-scope |
| «Aquell fitxer és a l'índex» | git ls-files --stage <fitxer> |
| «Aquella branca apunta a aquell commit» | git rev-parse <branca> |
| «El fitxer és text normal» | git ls-files --eol <fitxer> |
| «El remot és en aquell commit» | git ls-remote origin <branca> |
| «Aquell hook s'està executant» | GIT_TRACE=1 git commit ... |
La memòria i la documentació menteixen; les ordres no.
Pas 4: comprovar la hipòtesi
Formula una predicció concreta i falsable abans de tocar res:
«Si la causa és que
.gitattributesmarca*.csscom atext eol=crlf, aleshoresgit check-attr eol estils.cssha de dircrlf, igit ls-files --eol estils.cssha de mostrarw/crlf.»
Executa, comprova, i només llavors arregla. Si la predicció falla, la hipòtesi era dolenta: torna al pas 3. Canviar coses a l'atzar fins que «funciona» deixa el problema latent i t'impedeix saber què el causava.
- Variables de traça: veure el que Git fa per sota
Git té un sistema de traces que s'activa amb variables d'entorn. Són la manera més directa de veure què està passant realment.
| Variable | Què mostra | Quan fer-la servir |
|---|---|---|
GIT_TRACE=1 |
Cada subordre que Git executa, amb els seus arguments | Àlies estranys, hooks, ordres que en criden d'altres |
GIT_TRACE_SETUP=1 |
Com Git localitza el repositori: .git, arrel, prefix |
«No és un repositori», worktrees, submòduls |
GIT_TRACE_PERFORMANCE=1 |
Temps per etapa | Ordres lentes (08-06) |
GIT_TRACE_PACKET=1 |
Cada paquet del protocol amb el servidor | fetch/push que fallen o van lents |
GIT_TRACE_PACK_ACCESS=1 |
Accessos a packfiles | Rendiment de la base d'objectes |
GIT_CURL_VERBOSE=1 |
Diàleg HTTP complet | Problemes amb remots HTTPS |
GIT_SSH_COMMAND="ssh -v" |
Diàleg SSH complet | Permission denied (publickey) |
GIT_TRACE2_PERF=1 |
Traces modernes, estructurades | Anàlisi fina |
GIT_TRACE_SHALLOW=1 |
Lògica de clons superficials | --depth (10-04) |
Totes accepten una ruta absoluta en lloc d'1, per escriure a fitxer:
GIT_TRACE: què està executant Git realment
10:14:22.104 git.c:463 trace: built-in: git status 10:14:22.108 run-command.c:657 trace: run_command: 'gpg' '--status-fd=2' '-bsau' '[email protected]'
Allà es veu, per exemple, si Git està cridant gpg (signatura de commits, lliçó 08-05), un hook, o un credential helper.
És especialment útil amb àlies que fan coses estranyes (lliçó 06-04):
10:15:03.221 git.c:750 trace: alias expansion: lg => 'log' '--graph' '--abbrev-commit' '--date=relative' 10:15:03.222 git.c:463 trace: built-in: git log --graph --abbrev-commit --date=relative
I amb hooks que no s'executen:
Si no apareix res, el hook no s'està llançant. Causes habituals: no és executable (chmod +x), core.hooksPath apunta a un altre lloc, o el nom del fitxer té extensió.
GIT_TRACE_SETUP: on es pensa Git que és
10:16:41.003 trace.c:318 setup: git_dir: /home/ana/projectes/gestor-tasques/.git 10:16:41.003 trace.c:319 setup: git_common_dir: /home/ana/projectes/gestor-tasques/.git 10:16:41.003 trace.c:320 setup: worktree: /home/ana/projectes/gestor-tasques 10:16:41.003 trace.c:321 setup: cwd: /home/ana/projectes/gestor-tasques/src 10:16:41.003 trace.c:322 setup: prefix: src/
Resol una família sencera de misteris:
- «No és un repositori Git» estant dins d'un: et diu quin
.gittroba (o que no en troba cap). - Treballes en un repositori que no és el que et penses: típic amb submòduls (lliçó 06-05) i worktrees (06-06), on
git_dirigit_common_dirdifereixen. - Els patrons de
.gitignoreno funcionen com esperes: elprefixexplica des d'on s'interpreten les rutes.
GIT_SSH_COMMAND i GIT_CURL_VERBOSE: problemes de xarxa
Reprenent l'apartat 5.8 de la lliçó 09-01, aquí hi ha la versió completa.
debug1: Reading configuration data /home/ana/.ssh/config debug1: /home/ana/.ssh/config line 4: Applying options for git.exemple.cat debug1: Connecting to git.exemple.cat [192.0.2.10] port 22. debug1: Offering public key: /home/ana/.ssh/id_rsa RSA SHA256:xxxx agent debug1: Authentications that can continue: publickey debug1: Offering public key: /home/ana/.ssh/id_ed25519 ED25519 SHA256:yyyy agent debug1: Server accepts key: /home/ana/.ssh/id_ed25519 ED25519 SHA256:yyyy agent debug1: Authentication succeeded (publickey).
Cada línia és diagnòstic:
| Línia | Què et diu |
|---|---|
Reading configuration data ~/.ssh/config |
Quin fitxer de configuració s'aplica |
Applying options for <host> |
Quin bloc Host ha coincidit |
Connecting to <ip> port 22 |
On va realment (és l'amfitrió correcte?) |
Offering public key: <ruta> |
Quines claus ofereix i en quin ordre |
Server accepts key |
Quina ha acceptat |
Authentications that can continue: publickey |
Ha rebutjat l'anterior i continua provant |
El cas clàssic: tens diverses claus, SSH ofereix l'equivocada primer i el servidor talla després de N intents. La solució és un bloc a ~/.ssh/config:
IdentitiesOnly yes és la clau: força a oferir només aquella.
Per a HTTPS:
* Connected to git.exemple.cat (192.0.2.10) port 443 > GET /equip/gestor-tasques.git/info/refs?service=git-upload-pack HTTP/2 > User-Agent: git/2.45.0 < HTTP/2 401 < www-authenticate: Basic realm="Git" * Issue another request to this URL > Authorization: Basic YW5hOnh4eA== < HTTP/2 200
Diagnostica servidors intermediaris corporatius, certificats, redireccions i credencials. Un 401 persistent apunta al credential helper (lliçó 04-03):
git config --get credential.helper
git credential-cache exit # buidar credencials a la memòria cau
printf 'protocol=https\nhost=git.exemple.cat\n\n' | git credential fillCompte:
GIT_CURL_VERBOSEpot mostrar capçaleresAuthorization. No enganxis la seva sortida en un tiquet públic sense revisar-la (lliçó 08-05).
GIT_TRACE_PACKET: el protocol, paquet a paquet
El nivell més profund. Mostra el diàleg exacte amb el servidor.
packet: git< version 2 packet: git< agent=git/2.45.0 packet: git< ls-refs=unborn packet: git< fetch=shallow wait-for-done packet: git< object-format=sha1 packet: git> command=ls-refs packet: git> peel packet: git> ref-prefix refs/heads/ packet: git< b52c9d1e4a7f3c8b6d2e9a5f1c7b3d8e4a6f2c9b refs/heads/main packet: git< 7d3a8f4a2c6e9b1d5f3a8c6e2b9d4f7a1c5e8b3d refs/heads/GT-241
Per a què serveix de debò:
- Veure quines referències anuncia el servidor i quina versió del protocol es negocia.
- Diagnosticar un
fetchlent: si el servidor anuncia 40.000 referències, aquí hi ha el problema (lliçó 08-06, higiene de referències). - Entendre un
pushrebutjat per un hook de servidor (lliçó 07-06): el missatge del hook viatja en aquests paquets. - Comprovar si es fa servir el protocol v2, molt més eficient:
Traces per a totes les ordres, temporalment
# A la sessió actual
export GIT_TRACE=1
export GIT_TRACE_SETUP=1
# ...reproduir el problema...
unset GIT_TRACE GIT_TRACE_SETUPI un guió per capturar-ho tot de cop quan cal enviar un informe:
#!/usr/bin/env bash
# capturar-traces.sh <ordre git...>
LOG=/tmp/git-traces-$(date +%s).log
GIT_TRACE="$LOG" \
GIT_TRACE_SETUP="$LOG" \
GIT_TRACE_PERFORMANCE="$LOG" \
GIT_TRACE_PACKET="$LOG" \
"$@"
echo "Traça a: $LOG"
echo "REVISA el fitxer abans de compartir-lo: pot contenir credencials."
- Depurar la configuració efectiva
Reprenent la lliçó 01-05. Git llegeix la configuració de diversos llocs i l'últim guanya. Quan alguna cosa es comporta de manera inesperada, la configuració és sospitosa número u.
system /etc/gitconfig core.autocrlf=false global /home/ana/.gitconfig user.name=Ana Ferrer global /home/ana/.gitconfig [email protected] global /home/ana/.gitconfig pull.rebase=true local .git/config remote.origin.url=git.exemple.cat:equip/gestor-tasques.git local .git/config [email protected] local .git/config core.autocrlf=input
Dues columnes noves respecte del --list de sempre:
--show-scope: en quin nivell és (system,global,local,worktree,command).--show-origin: el fitxer exacte, inclosos els que vénen perinclude.path.
A l'exemple, user.email apareix dues vegades: el local guanya. Aquí hi ha l'explicació del «he configurat bé el correu i els commits surten amb un altre» (lliçó 09-01, apartat 5.3).
El valor efectiu d'un ajust
# Quin valor guanya
git config --get user.email
# TOTS els valors definits, en ordre de precedència (l'últim guanya)
git config --get-all user.email
# Amb el seu origen
git config --get-all --show-origin user.email
# Tot el que comenci per un prefix
git config --get-regexp '^remote\.'
git config --get-regexp '^alias\.'
git config --get-regexp '^advice\.'Aquest últim mereix atenció: si algú va copiar una configuració que silencia els advice.*, estàs perdent els suggeriments que resolen la meitat dels problemes del mòdul (lliçó 09-01, apartat 6).
Precedència i anul·lacions
| Nivell | Fitxer | Prioritat |
|---|---|---|
system |
/etc/gitconfig |
Més baixa |
global |
~/.gitconfig o ~/.config/git/config |
Mitjana |
local |
.git/config |
Alta |
worktree |
.git/config.worktree |
Més alta (si extensions.worktreeConfig) |
-c a la línia d'ordres |
— | Màxima |
Variables GIT_* |
Entorn | Depèn de l'ajust |
# Provar un valor sense canviar res, només per a aquesta ordre
git -c core.autocrlf=false status
git -c diff.noprefix=false diff
# Veure què passa SENSE configuració d'usuari
GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=/dev/null git statusL'última és la prova definitiva de «és cosa de la meva configuració?».
Configuració condicional, la font de sorpreses
# ~/.gitconfig
[includeIf "gitdir:~/projectes/feina/"]
path = ~/.gitconfig-feina
[includeIf "gitdir:~/projectes/personal/"]
path = ~/.gitconfig-personalÉs una funcionalitat excel·lent per separar identitats, però produeix el desconcert d'«aquí funciona i allà no». --show-origin ho desembolica en un segon, perquè anomena el fitxer inclòs.
I ull amb la barra final: gitdir:~/projectes/feina/ (amb /) coincideix amb el directori i els seus descendents; sense ella, el comportament canvia.
- Depurar rutes i patrons:
check-ignore, check-attr, ls-files
check-ignore, check-attr, ls-filesAquí viuen els misteris més freqüents del dia a dia.
git check-ignore -v: per què s'ignora (o no) un fitxer
Reprenent la lliçó 08-03:
Lectura: fitxer de regles : línia : patró que decideix, i el fitxer afectat. No hi ha cap ambigüitat possible.
# Diversos fitxers alhora
git check-ignore -v app.js dades/bolcat.sql node_modules/x/y.js
# Tot el que s'està ignorant al projecte
git status --ignored --short
# I el cas desconcertant: per què NO s'ignora?
git check-ignore -v --no-index config-local.json
echo $?Si check-ignore no torna res i el codi de sortida és 1, cap regla no l'ignora. I si el fitxer apareix igualment a git status tot i estar ignorat, la causa és gairebé sempre la mateixa:
És a l'índex. .gitignore només afecta els fitxers sense seguiment; un que ja té seguiment es continua vigilant encara que el patró coincideixi. La solució és la de la lliçó 08-03:
Altres causes menys evidents, que check-ignore -v revela en anomenar el fitxer de regles:
| Fitxer que pot estar decidint | On viu |
|---|---|
.gitignore del repositori |
En qualsevol directori del projecte |
.git/info/exclude |
Local, no versionat |
core.excludesFile |
Global, típicament ~/.gitignore_global |
Un .gitignore d'un directori pare |
Un de més específic guanya |
Una regla de negació !patró |
Reactiva alguna cosa ignorada abans |
I el parany clàssic dels directoris:
Si un directori està ignorat, Git ni tan sols hi entra, així que una regla de negació per a un fitxer de dins no funciona:
git check-attr: quins atributs s'apliquen
Reprenent la lliçó 08-04:
# Un atribut concret
git check-attr eol text diff -- estils.css index.html app.js
# D'on surt cada regla
git check-attr -a --source=HEAD estils.css
# Per a tots els fitxers seguits
git ls-files | git check-attr --stdin -a | grep -v unspecifiedAixò explica de cop una família de misteris:
| Símptoma | Ordre | Causa habitual |
|---|---|---|
| El diff d'un fitxer surt com a binari | git check-attr diff -- <f> |
-diff o binary a .gitattributes |
| Un fitxer no es fusiona mai | git check-attr merge -- <f> |
merge=binary o merge=ours |
| Els finals de línia canvien sols | git check-attr text eol -- <f> |
text, eol=crlf, o core.autocrlf |
| Un fitxer es transforma en confirmar | git check-attr filter -- <f> |
Un filter (LFS, clean/smudge) |
git blame dona resultats estranys |
— | .git-blame-ignore-revs (06-03) |
git ls-files: què hi ha a l'índex de debò
git status interpreta; git ls-files mostra. És la finestra directa a l'índex.
| Camp | Significat |
|---|---|
100644 |
Fitxer normal. 100755 = executable. 120000 = enllaç simbòlic. 160000 = submòdul |
| Hash | El blob que hi ha a l'índex |
0 |
L'etapa. 0 = normal. 1/2/3 = conflicte (lliçó 03-05) |
Aquest últim camp és or durant un conflicte:
Tres etapes: base comuna, la nostra i la seva. Exactament el que vam veure a la lliçó 03-05, ara visible en cru.
Els modes que resolen misteris:
# Per què Git diu que aquest script ha canviat si només li he donat permisos?
git ls-files --stage build.shÍndex 100644, disc executable: aquest és el canvi. Es corregeix amb:
I el mode --eol, que resol el misteri dels finals de línia:
| Columna | Significat |
|---|---|
i/ |
Com està a l'índex (el que es desa al repositori) |
w/ |
Com està al directori de treball (al disc) |
attr/ |
Quin atribut se li aplica |
La primera línia és el cas sa de la Carla a Windows: LF al repositori, CRLF al seu disc (lliçó 08-04). La tercera és un problema: i/crlf significa que s'han desat CRLF dins del repositori, que és justament el que no es vol.
Altres modes útils:
git ls-files --others # sense seguiment
git ls-files --others --exclude-standard # sense seguiment i no ignorats
git ls-files --ignored --exclude-standard # ignorats
git ls-files --deleted # esborrats del disc però encara a l'índex
git ls-files --modified # modificats
git ls-files --unmerged # en conflicte--others --exclude-standard és exactament la llista de «fitxers nous» que mostra git status, sense la resta de la sortida. I --deleted explica el «he esborrat el fitxer i Git continua queixant-se».
- Lampisteria per inspeccionar el graf
Quan la pregunta és sobre l'estructura del repositori, la resposta és a la capa de lampisteria que vam veure a la lliçó 01-04.
git rev-parse: traduir qualsevol cosa a un hash
git rev-parse HEAD
git rev-parse main
git rev-parse HEAD~3
git rev-parse GT-241@{2}
git rev-parse v1.5.0^{commit} # l'etiqueta anotada apunta a un tag; això dona el commitI els seus modes informatius, que resolen preguntes d'entorn:
git rev-parse --show-toplevel # arrel del projecte
git rev-parse --git-dir # on és el .git
git rev-parse --git-common-dir # el .git compartit (worktrees, 06-06)
git rev-parse --abbrev-ref HEAD # nom de la branca actual
git rev-parse --is-inside-work-tree # true/false
git rev-parse --is-bare-repository # true/false
git rev-parse --symbolic-full-name @{u} # l'upstream complet/home/ana/projectes/gestor-tasques /home/ana/projectes/gestor-tasques/.git GT-241 true refs/remotes/origin/GT-241
Són la base de qualsevol guió que hagi de treballar amb repositoris de manera robusta.
git rev-list: comptar i llistar commits
# Quants commits hi ha
git rev-list --count HEAD
# Quants per davant i per darrere (lliçó 09-03)
git rev-list --left-right --count main...origin/main
# Commits que van tocar un fitxer
git rev-list HEAD -- app.js | head
# Tots els objectes assolibles (lliçó 08-06)
git rev-list --objects --all | wc -l
# Els commits arrel (n'hi ha més d'un? → històries no relacionades)
git rev-list --max-parents=0 HEAD
# Només fusions, o només el que no són fusions
git rev-list --merges HEAD | head
git rev-list --no-merges HEAD | headAquest --max-parents=0 és el diagnòstic exacte de les unrelated histories de la lliçó 09-03: si torna dos hashos, hi ha dues arrels.
git cat-file --batch-check: inspecció massiva
# Comprovar si existeix (sense bolcar contingut)
git cat-file -e 4f8a2e6 && echo "existeix" || echo "no existeix"
# Un informe de tots els objectes per tipus
git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype)' \
| sort | uniq -cÉs la mateixa tècnica de l'anàlisi de mides de la lliçó 08-06, aplicada aquí a entendre la composició del repositori.
git verify-pack: què hi ha dins d'un packfile
b52c9d1e4a7f3c8b6d2e9a5f1c7b3d8e4a6f2c9b commit 241 168 12 7d3a8f4a2c6e9b1d5f3a8c6e2b9d4f7a1c5e8b3d tree 118 94 180 4f8a2e6c9b1d5e3a7f2c8b6d9e4a1f5c3b7d2e9a blob 8241 2104 274 6f2b9d4a8c1e5f3b7d9a2c6e4f8b1d3a5c7e9f2b blob 412 87 2378 1 4f8a2e6c9b1d5e3a
Columnes: hash, tipus, mida real, mida comprimida, posició al pack i —a l'última línia— profunditat de delta i objecte base. És la vista de baix nivell del que explicava la lliçó 08-06: l'última entrada es desa com a diferència d'una altra.
git for-each-ref: totes les referències amb les seves dades
git for-each-ref --sort=-committerdate --format='%(refname:short) %(objectname:short) %(committerdate:relative) %(authorname)' refs/headsGT-241 9c4e7b2 fa 2 hores Ana Ferrer GT-238 7d3a8f4 fa 3 dies Bruno Salas main b52c9d1 fa 4 dies Carla Vidal
# Branques sense upstream: existeixen només en aquesta màquina (lliçó 09-05)
git for-each-ref --format='%(refname:short) %(upstream)' refs/heads | awk '$2==""{print $1}'
# Referències que apunten a objectes inexistents (lliçó 09-05)
git for-each-ref --format='%(refname) %(objectname)' | while read -r r s; do
git cat-file -e "$s" 2>/dev/null || echo "TRENCADA: $r"
donegit ls-remote: què hi ha al servidor, sense baixar res
Consulta el servidor directament, sense fetch i sense tocar el teu repositori. Resol preguntes com «existeix aquella branca al servidor?» o «el meu origin/main està al dia?»:
[ "$(git ls-remote origin main | cut -f1)" = "$(git rev-parse origin/main)" ] \
&& echo "al dia" || echo "la meva còpia d'origin/main està desactualitzada"
- Acotar amb
git diff --stat i git log
git diff --stat i git logAbans d'una investigació fina, convé acotar. Aquestes són les eines de gra gruixut.
app.js | 84 ++++++++++++++++----- estils.css | 12 ++-- index.html | 6 +- README.md | 31 ++++++++ 4 files changed, 108 insertions(+), 25 deletions(-)
# Només els noms, per veure l'abast d'un cop d'ull
git diff --name-only v1.4.0 v1.5.0
# Amb el tipus de canvi: Afegit, Modificat, Esborrat, Reanomenat
git diff --name-status v1.4.0 v1.5.0
# Resum de reanomenaments i canvis de mode
git diff --summary v1.4.0 v1.5.0
# Comparar només l'efecte d'una branca (lliçó 07-02)
git diff --stat main...GT-241I git log en mode investigació (lliçó 06-04):
# Commits que van tocar una funció concreta
git log -L :calculaPendents:app.js
# Commits que van afegir o treure una cadena (pickaxe, lliçó 02-06)
git log -S "localStorage" --oneline
# Commits el diff dels quals coincideix amb una expressió regular
git log -G "gestor\.tasques\.v[0-9]" --oneline
# Qui i quan, en un rang de dates
git log --since="2026-07-01" --until="2026-07-31" --format='%h %an %ad %s' --date=short
# Només el que va entrar per la primera línia de pares (lliçó 08-02)
git log --first-parent --oneline main-L :funció:fitxer és especialment potent i poc coneguda: segueix una funció concreta al llarg de l'historial, fins i tot quan es mou dins del fitxer.
- Una investigació real: de
bisect a blame
bisect a blameHo ajuntarem tot en un cas complet sobre gestor-tasques.
El símptoma. La Carla informa: «El comptador de tasques pendents mostra un número de més quan hi ha tasques ocultes pel filtre. A la versió 1.4 funcionava.»
Pas 1: reproduir
El mínim que provoca la fallada, i escrit com una prova automatitzable:
cat > /tmp/prova-comptador.sh <<'EOF'
#!/usr/bin/env bash
# Surt 0 si el comptador és correcte, 1 si no.
node -e '
const fs = require("fs");
const src = fs.readFileSync("app.js", "utf8");
const ctx = { localStorage: { getItem: () => null, setItem: () => {} }, document: null };
// ... arrencada mínima del programa ...
const tasques = [
{ text: "a", completada: false, oculta: false },
{ text: "b", completada: true, oculta: false },
{ text: "c", completada: false, oculta: true }
];
const esperat = 2;
const obtingut = calculaPendents(tasques);
process.exit(obtingut === esperat ? 0 : 1);
' 2>/dev/null
EOF
chmod +x /tmp/prova-comptador.shUna prova automatitzable és el que converteix bisect de tediós en instantani.
Pas 2: acotar el rang
34 commits entre les dues versions. Revisar-los a mà són hores; bisect són cinc passos.
Pas 3: git bisect run
Reprenent la lliçó 06-02:
git bisect start
git bisect bad v1.5.0
git bisect good v1.4.0
git bisect run /tmp/prova-comptador.shBisecting: 16 revisions left to test after this (roughly 4 steps) [7d3a8f4a2c6e9b1d5f3a8c6e2b9d4f7a1c5e8b3d] GT-238 extreu la creació de l'element running /tmp/prova-comptador.sh Bisecting: 8 revisions left to test after this (roughly 3 steps) ... 4f8a2e6c9b1d5e3a7f2c8b6d9e4a1f5c3b7d2e9a is the first bad commit commit 4f8a2e6c9b1d5e3a7f2c8b6d9e4a1f5c3b7d2e9a Author: Bruno Salas <[email protected]> Date: Wed Jul 15 11:23:04 2026 +0200 GT-231 afegeix el filtre de tasques ocultes app.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-)
Del símptoma al commit en cinc passos automàtics. Ara sabem quan es va trencar.
Pas 4: entendre el commit
function calculaPendents(tasques) {
- return tasques.filter(t => !t.completada).length;
+ return tasques.filter(t => !t.completada || t.oculta).length;
}Aquí està: el || t.oculta compta també les ocultes. Però saber quina línia falla no és saber per què hi és, i arreglar-la sense entendre-ho pot trencar el que aquell commit venia a resoldre.
Pas 5: git blame per al perquè
Reprenent la lliçó 06-03:
4f8a2e6c (Bruno Salas 2026-07-15 11:23:04 +0200 12) function calculaPendents(tasques) {
4f8a2e6c (Bruno Salas 2026-07-15 11:23:04 +0200 13) return tasques.filter(t => !t.completada || t.oculta).length;
4f8a2e6c (Bruno Salas 2026-07-15 11:23:04 +0200 14) }
b52c9d1e (Ana Ferrer 2026-06-28 09:14:22 +0200 15)
b52c9d1e (Ana Ferrer 2026-06-28 09:14:22 +0200 16) function renderitzaTasques() {# La història completa d'aquella funció, encara que s'hagi mogut
git log -L :calculaPendents:app.js --format='%h %an %ad %s' --date=shortcommit 4f8a2e6 Bruno Salas 2026-07-15 GT-231 afegeix el filtre de tasques ocultes commit 8a1f6c3 Ana Ferrer 2026-05-02 GT-198 extreu el càlcul a la seva pròpia funció commit 2f8c6e1 Ana Ferrer 2026-04-11 GT-142 mostra el comptador de pendents
GT-231 afegeix el filtre de tasques ocultes Les tasques arxivades deixen d'aparèixer al llistat. S'afegeix la marca `oculta` i es filtra a renderitzaTasques(). El comptador ha de continuar incloent-les perquè l'especificació de GT-231 diu que «pendents» compta totes les tasques no completades, siguin visibles o no.
Aquí hi ha la troballa, i és el que canvia tot el desenllaç. El comportament no és un error: és una decisió deliberada i documentada de GT-231. El que hi ha és una contradicció entre dues especificacions: GT-231 diu que comptin totes les no completades; la Carla espera que compti només les visibles.
Si haguéssim «arreglat» la línia així que la vam veure, hauríem trencat GT-231 i el cicle hauria tornat a començar d'aquí a dues setmanes.
# Qui més en depèn? (pickaxe, lliçó 02-06)
git log -S "calculaPendents" --oneline
git grep -n "calculaPendents" -- '*.js'La conclusió de la investigació no és un pedaç, sinó una pregunta per a l'equip: què significa «pendents»? I la resposta, sigui quina sigui, es documenta al commit que la implementi.
El recorregut, en una taula
| Pas | Pregunta | Eina | Lliçó |
|---|---|---|---|
| 1 | Quin és el símptoma exacte? | Un guió de prova reproduïble | — |
| 2 | En quin rang buscar? | git diff --stat, git rev-list --count |
02-05 |
| 3 | Quin commit el va introduir? | git bisect run |
06-02 |
| 4 | Què va canviar aquell commit? | git show |
02-06 |
| 5 | Per què hi ha aquella línia? | git blame, git log -L, %B |
06-03, 08-01 |
| 6 | Qui en depèn? | git log -S, git grep |
02-06 |
bisect et porta del símptoma al commit. blame i el missatge del commit et porten del commit a la intenció. El primer sense el segon produeix pedaços que trenquen una altra cosa. I aquí es veu, retrospectivament, per què la lliçó 08-01 insistia tant a explicar el perquè als missatges: aquell paràgraf del Bruno ha estalviat un error.
- Un catàleg de misteris i les seves ordres
La taula de consulta ràpida del «Git fa alguna cosa estranya».
| Misteri | Ordre de diagnòstic | Causa habitual |
|---|---|---|
| Un fitxer surt modificat i no l'he tocat | git ls-files --eol <f>, git check-attr -a <f> |
Finals de línia, o filter (08-04) |
Un fitxer ignorat apareix a status |
git ls-files --error-unmatch <f> |
Ja tenia seguiment (08-03) |
Un fitxer no s'ignora encara que és al .gitignore |
git check-ignore -v <f> |
Regla en un altre fitxer, o negació mal posada |
| Un fitxer s'ignora i no hauria | git check-ignore -v <f> |
Un .gitignore d'un directori pare, o el global |
| Un script perd el permís d'execució | git ls-files --stage <f> |
Mode 100644 a l'índex; update-index --chmod=+x |
| Un hook no s'executa | GIT_TRACE=1 git commit, ls -l .git/hooks/ |
No és executable, o core.hooksPath (06-01) |
| L'autor dels commits no és el que espero | git config --list --show-origin | grep user |
Un user.email local trepitjant el global |
| «No és un repositori Git» estant dins | GIT_TRACE_SETUP=1 git status |
Worktree, submòdul, o .git perdut |
push/fetch fallen per autenticació |
GIT_SSH_COMMAND="ssh -v" git fetch |
Clau equivocada oferta primer (04-03) |
push/fetch van molt lents |
GIT_TRACE_PACKET=1, git ls-remote | wc -l |
Milers de referències (08-06) |
git status triga segons |
GIT_TRACE_PERFORMANCE=1 git status |
Recorregut de fitxers sense seguiment (08-06) |
git log no mostra un commit que existeix |
git log --all, git reflog, git cat-file -t |
És en una altra branca, o és inassolible (09-04) |
| Un merge diu «Already up to date» sense ser-ho | git merge-base <a> <b>, git log --graph --all |
Fusió revertida (05-06) |
| Dues branques no es poden fusionar | git rev-list --max-parents=0 HEAD |
Històries no relacionades (09-03) |
| Un submòdul apareix sempre com a modificat | git diff --submodule, git ls-files --stage |
Commit del submòdul diferent (06-05) |
| El diff surt com a binari | git check-attr diff -- <f> |
-diff o binary a .gitattributes |
| Un àlies fa alguna cosa inesperada | GIT_TRACE=1 git <àlies> |
Expansió de l'àlies (06-04) |
| Git es comporta diferent en dues carpetes | git config --list --show-origin --show-scope |
includeIf condicional |
| El remot és en un commit que no esperava | git ls-remote origin <branca> |
Algú ha publicat, o ha forçat (09-03) |
I un guió de diagnòstic general, per quan no saps ni per on començar:
#!/usr/bin/env bash
# diagnostic-git.sh — foto completa de l'estat real del repositori
set -u
echo "=== ENTORN ==="
git --version
git rev-parse --show-toplevel
git rev-parse --git-dir
echo "branca: $(git rev-parse --abbrev-ref HEAD)"
echo "upstream: $(git rev-parse --symbolic-full-name '@{u}' 2>/dev/null || echo 'cap')"
echo; echo "=== ESTAT ==="
git status -sb | head -20
echo; echo "=== DIVERGÈNCIA ==="
git rev-list --left-right --count HEAD...@{u} 2>/dev/null || echo "sense upstream"
echo; echo "=== CONFIGURACIÓ CLAU ==="
git config --list --show-scope 2>/dev/null \
| grep -E '(user\.|core\.(autocrlf|eol|hooksPath|excludesFile|fsmonitor)|pull\.|push\.|merge\.|diff\.)'
echo; echo "=== HOOKS ACTIUS ==="
RUTA=$(git config --get core.hooksPath || echo "$(git rev-parse --git-dir)/hooks")
find "$RUTA" -maxdepth 1 -type f -perm -u+x ! -name '*.sample' 2>/dev/null
echo; echo "=== REFERÈNCIES ==="
echo "branques locals: $(git branch | wc -l) | remotes: $(git branch -r | wc -l) | etiquetes: $(git tag | wc -l)"
echo "sense publicar: $(git for-each-ref --format='%(refname:short) %(upstream)' refs/heads | awk '$2==""{print $1}' | tr '\n' ' ')"
echo; echo "=== INTEGRITAT ==="
git fsck --connectivity-only --no-progress 2>&1 | grep -vE '^(dangling|notice|Checking)' || echo "sense errors"
echo; echo "=== ÚLTIMS MOVIMENTS ==="
git reflog --date=relative -8Desa'l. El dia que et faci falta, t'estalviarà quinze minuts d'ordres soltes.
Errors Habituals i Consells
Error 1: canviar coses a l'atzar fins que funcioni. Deixa el problema latent i no n'aprens res. Formula una hipòtesi falsable i comprova-la.
Error 2: refiar-se de la memòria en lloc de consultar l'estat real. «Jo em penso que aquell fitxer està ignorat» és diferent de git check-ignore -v.
Error 3: no aïllar. Abans d'investigar a fons, comprova si el problema persisteix sense configuració global, sense hooks i en un clon net. Aquest descart costa un minut.
Error 4: activar GIT_TRACE_PACKET d'entrada. És el nivell més profund i produeix molt soroll. Comença per GIT_TRACE i GIT_TRACE_SETUP.
Error 5: enganxar la sortida de GIT_CURL_VERBOSE en un tiquet públic. Pot contenir capçaleres Authorization (08-05). Revisa-la abans.
Error 6: fer servir git config --list sense --show-origin --show-scope. Sense aquestes opcions no saps quin valor guanya ni d'on surt, que és justament el que investigues.
Error 7: arreglar la línia que bisect assenyala sense llegir el missatge del commit. Pots trencar la funcionalitat que aquell commit venia a implementar, com hauria passat a l'apartat 7.
Error 8: intentar bisect sense una prova automatitzable. Amb git bisect run i un guió, trenta commits són cinc passos automàtics; a mà, mitja hora d'avorriment i errors.
Error 9: confondre git status amb la realitat. status interpreta; ls-files --stage, check-attr i rev-parse mostren.
Consell 1: el mètode abans que les eines. Reproduir, aïllar, consultar l'estat real, comprovar la hipòtesi.
Consell 2: git config --list --show-origin --show-scope com a reflex. Resol una fracció sorprenent dels «Git fa alguna cosa estranya».
Consell 3: git check-ignore -v i git check-attr -a. Dues ordres que donen la resposta exacta a dues de les preguntes més freqüents del dia a dia.
Consell 4: git ls-files --eol en qualsevol problema de finals de línia. La taula i/ i w/ ho diu tot d'un cop d'ull.
Consell 5: desa el guió de diagnòstic de l'apartat 8. És la foto completa en trenta segons.
Consell 6: escriu la prova abans de bisecar. L'esforç es recupera al primer bisect run, i la prova es queda al projecte.
Exercicis
Exercici 1: el misteri del fitxer sempre modificat
- Crea un repositori amb
app.js,estils.cssiindex.html, i confirma'ls. - Afegeix un
.gitattributesamb*.css text eol=crlfi confirma'l. - Executa
git statusi observa el resultat. Després executagit ls-files --eoligit check-attr -a estils.css. - Explica exactament què està passant fent servir les columnes
i/iw/. - Resol-ho amb
git add --renormalize .(lliçó 08-04) i comprova ambls-files --eolque les columnes quadren. - Repeteix l'exercici amb un fitxer al qual
.gitattributesmarqui-diffi comprova què canvia agit diff.
Exercici 2: configuració, ignorats i traces
- Crea un repositori i configura
user.emaildiferent al nivell local i al global. - Fes servir
git config --list --show-origin --show-scopeper determinar quin guanya, i confirma-ho fent un commit i mirant%ae. - Crea un
.gitignoreamb*.log, un.git/info/excludeamb!important.logi uncore.excludesFileglobal ambtemporal*. Crea els tres fitxers corresponents i fes servirgit check-ignore -vamb cadascun per determinar quina regla decideix. - Crea un hook
pre-commitque imprimeixi alguna cosa, fes-lo no executable, i intenta confirmar. Fes servirGIT_TRACE=1per demostrar que no es llança. - Fes-lo executable i repeteix, comprovant a la traça que ara sí que apareix.
- Executa
GIT_TRACE_SETUP=1 git statusdes d'un subdirectori i explica cada línia de la sortida.
Exercici 3: la investigació completa
- Crea un repositori amb un
app.jsque contingui una funciócalculaPendentscorrecta, i fes vint commits que toquin altres parts del projecte. - En un commit intermedi, introdueix la fallada de l'apartat 7 (afegir
|| t.oculta) amb un missatge de commit que expliqui el perquè. - Fes deu commits més a sobre.
- Escriu un guió de prova que surti amb
0si la funció és correcta i amb1si no. - Troba el commit culpable amb
git bisect runi anota quants passos ha necessitat. - Fes servir
git show,git blame -Ligit log -L :calculaPendents:app.jsper reconstruir la història completa d'aquella funció. - Llegeix el missatge complet del commit culpable amb
git log -1 --format=%Bi explica per què la resposta correcta no és «canviar la línia».
Solucions
Solució 1:
rm -rf /tmp/p9-06 && mkdir /tmp/p9-06 && cd /tmp/p9-06 && git init -q -b main
git config user.name "Carla Vidal"; git config user.email "[email protected]"
printf 'console.log(1);\n' > app.js
printf 'body { margin: 0; }\n' > estils.css
printf '<h1>Gestor</h1>\n' > index.html
git add . && git commit -q -m "chore: estructura inicial"
# 2. El .gitattributes
printf '*.css text eol=crlf\n' > .gitattributes
git add .gitattributes && git commit -q -m "chore: força CRLF als CSS"Sorprenentment, res. L'atribut s'aplica en escriure al disc, i el fitxer al disc encara té LF. Força'n l'actualització:
Aquí hi ha el misteri típic: un fitxer modificat que ningú no ha tocat.
i/lf w/crlf attr/text eol=crlf estils.css i/lf w/lf attr/ app.js i/lf w/lf attr/ index.html i/lf w/lf attr/ .gitattributes
Lectura de les columnes:
i/lf: a l'índex hi ha LF, que és el que es va desar al commit original.w/crlf: al disc hi ha CRLF, perquè l'atributeol=crlfel converteix en extreure'l.- Git compara índex i disc byte a byte, veu una diferència a cada final de línia, i ho marca com a modificat.
No és un error: és l'atribut funcionant, sobre un fitxer que es va desar abans que l'atribut existís.
# 5. La solució
git add --renormalize .
git status --short
git commit -q -m "chore: renormalitza els finals de línia"
git ls-files --eol estils.cssNet. --renormalize reescriu l'índex aplicant els atributs actuals: ara l'índex desa LF amb coneixement de l'atribut, i la comparació quadra. És exactament el procediment de la lliçó 08-04.
# 6. Amb -diff
printf '*.css text eol=crlf\nindex.html -diff\n' > .gitattributes
git add .gitattributes && git commit -q -m "chore: index.html sense diff"
printf '<h1>Gestor de tasques</h1>\n' > index.html
git diff index.html
git check-attr diff -- index.htmldiff --git a/index.html b/index.html index 8a1f6c3..4f8a2e6 100644 Binary files a/index.html and b/index.html differ
-diff fa que Git tracti el fitxer com a binari a efectes de diff. És útil per a minificats i generats, i desconcertant si no saps que hi és: git check-attr diff ho resol en un segon.
Solució 2:
rm -rf /tmp/p9-06b && mkdir /tmp/p9-06b && cd /tmp/p9-06b && git init -q -b main
git config --global user.email "[email protected]" 2>/dev/null
git config user.name "Ana Ferrer"
git config user.email "[email protected]" # local, diferent
# 2. Qui guanya
git config --list --show-origin --show-scope | grep user.emailglobal /home/ana/.gitconfig [email protected] local .git/config [email protected]
git config --get user.email
echo "x" > f.txt && git add . && git commit -q -m "prova"
git log -1 --format='%ae'El local guanya, perquè és més a prop del repositori. És exactament el mecanisme de l'apartat 3, i la causa habitual del «he configurat bé el correu».
# 3. Les tres capes d'ignorats
printf '*.log\n' > .gitignore
printf '!important.log\n' > .git/info/exclude
printf 'temporal*\n' > /tmp/gitignore-global
git config core.excludesFile /tmp/gitignore-global
touch sortida.log important.log temporal-1.txt normal.txt
for f in sortida.log important.log temporal-1.txt normal.txt; do
printf '%-18s ' "$f"
git check-ignore -v "$f" || echo "(no ignorat)"
donesortida.log .gitignore:1:*.log sortida.log important.log .git/info/exclude:1:!important.log important.log temporal-1.txt /tmp/gitignore-global:1:temporal* temporal-1.txt normal.txt (no ignorat)
Cadascun decidit per un fitxer diferent, i check-ignore -v l'anomena amb el seu número de línia. Fixa't en important.log: la regla que coincideix és la negació, i per això el fitxer no està ignorat:
Quan check-ignore -v torna un patró que comença per !, significa «l'ha rescatat aquesta regla».
# 4. El hook que no s'executa
cat > .git/hooks/pre-commit <<'EOF'
#!/usr/bin/env bash
echo "HOOK EXECUTAT"
EOF
# sense chmod +x
echo "y" >> f.txt
GIT_TRACE=1 git commit -q -am "prova hook" 2>&1 | grep -ci "pre-commit"
git log -1 --format=%sZero mencions al hook a la traça, i el commit s'ha fet. El hook no s'ha llançat.
# 5. Amb permís d'execució
chmod +x .git/hooks/pre-commit
echo "z" >> f.txt
GIT_TRACE=1 git commit -am "prova hook 2" 2>&1 | grep -i "pre-commit"Aquí està: run_command el llança i el echo apareix. GIT_TRACE=1 és el diagnòstic definitiu per a «el meu hook no s'executa» (lliçó 06-01).
# 6. GIT_TRACE_SETUP des d'un subdirectori
mkdir -p src/components && cd src/components
GIT_TRACE_SETUP=1 git status 2>&1 | head -613:04:22.101 trace.c:318 setup: git_dir: /tmp/p9-06b/.git 13:04:22.101 trace.c:319 setup: git_common_dir: /tmp/p9-06b/.git 13:04:22.101 trace.c:320 setup: worktree: /tmp/p9-06b 13:04:22.101 trace.c:321 setup: cwd: /tmp/p9-06b/src/components 13:04:22.101 trace.c:322 setup: prefix: src/components/
| Línia | Què diu |
|---|---|
git_dir |
On és el .git que s'està fent servir |
git_common_dir |
El .git compartit: diferent en un worktree (06-06) |
worktree |
L'arrel del projecte |
cwd |
Des d'on has llançat l'ordre |
prefix |
La ruta relativa que Git anteposa als arguments |
Aquest prefix explica per què git add . des d'un subdirectori només afegeix aquell subdirectori, i per què els patrons s'interpreten com s'interpreten.
Solució 3:
rm -rf /tmp/p9-06c && mkdir /tmp/p9-06c && cd /tmp/p9-06c && git init -q -b main
git config user.name "Ana Ferrer"; git config user.email "[email protected]"
cat > app.js <<'EOF'
function calculaPendents(tasques) {
return tasques.filter(t => !t.completada).length;
}
module.exports = { calculaPendents };
EOF
git add . && git commit -q -m "GT-142 mostra el comptador de pendents"
# 1. Vint commits de farciment
for i in $(seq 1 20); do echo "// canvi $i" >> altres.js; git add .; git commit -q -m "chore: canvi $i"; done# 2. El commit culpable, amb un missatge que explica el perquè
sed -i 's/!t.completada)/!t.completada || t.oculta)/' app.js
git commit -q -am "GT-231 afegeix el filtre de tasques ocultes
Les tasques arxivades deixen d'aparèixer al llistat. S'afegeix la
marca \`oculta\` i es filtra a renderitzaTasques().
El comptador ha de continuar incloent-les perquè l'especificació de
GT-231 diu que \"pendents\" compta totes les tasques no completades,
siguin visibles o no."
CULPABLE=$(git rev-parse --short HEAD)
# 3. Deu commits més
for i in $(seq 21 30); do echo "// canvi $i" >> altres.js; git add .; git commit -q -m "chore: canvi $i"; done
git rev-list --count HEAD# 4. La prova
cat > /tmp/prova-comptador.sh <<'EOF'
#!/usr/bin/env bash
node -e '
const { calculaPendents } = require(process.cwd() + "/app.js");
const tasques = [
{ completada: false, oculta: false },
{ completada: true, oculta: false },
{ completada: false, oculta: true }
];
process.exit(calculaPendents(tasques) === 2 ? 0 : 1);
' 2>/dev/null
EOF
chmod +x /tmp/prova-comptador.sh
/tmp/prova-comptador.sh; echo "estat actual: $?"La prova falla a HEAD: reproduït.
# 5. Bisect
git bisect start
git bisect bad HEAD
git bisect good HEAD~31
git bisect run /tmp/prova-comptador.sh 2>&1 | tail -12Bisecting: 15 revisions left to test after this (roughly 4 steps)
running '/tmp/prova-comptador.sh'
Bisecting: 7 revisions left to test after this (roughly 3 steps)
running '/tmp/prova-comptador.sh'
Bisecting: 3 revisions left to test after this (roughly 2 steps)
running '/tmp/prova-comptador.sh'
Bisecting: 1 revision left to test after this (roughly 1 step)
running '/tmp/prova-comptador.sh'
4f8a2e6c9b1d5e3a7f2c8b6d9e4a1f5c3b7d2e9a is the first bad commit
GT-231 afegeix el filtre de tasques ocultes
bisect found first bad commitCinc passos automàtics per a 31 commits. El logaritme en base 2 de 31 és una mica menys de 5: exactament el que predeia la lliçó 06-02.
function calculaPendents(tasques) {
- return tasques.filter(t => !t.completada).length;
+ return tasques.filter(t => !t.completada || t.oculta).length;
}4f8a2e6c (Ana Ferrer 2026-07-31 13:12:04 +0200 1) function calculaPendents(tasques) {
4f8a2e6c (Ana Ferrer 2026-07-31 13:12:04 +0200 2) return tasques.filter(t => !t.completada || t.oculta).length;
8a1f6c3d (Ana Ferrer 2026-07-31 13:11:58 +0200 3) }git log -L :calculaPendents:app.js --format='%h %ad %s' --date=short | grep -E '^commit|^[0-9a-f]{7} '4f8a2e6 2026-07-31 GT-231 afegeix el filtre de tasques ocultes 8a1f6c3 2026-07-31 GT-142 mostra el comptador de pendents
Dos commits en tota la vida d'aquella funció. -L :funció:fitxer la segueix encara que es mogui de lloc dins del fitxer, cosa que blame -L 1,3 no faria.
GT-231 afegeix el filtre de tasques ocultes Les tasques arxivades deixen d'aparèixer al llistat. S'afegeix la marca `oculta` i es filtra a renderitzaTasques(). El comptador ha de continuar incloent-les perquè l'especificació de GT-231 diu que «pendents» compta totes les tasques no completades, siguin visibles o no.
Per què la resposta correcta no és «canviar la línia»:
El comportament és intencionat i està justificat per escrit. No hi ha cap error de programació: hi ha dues especificacions que es contradiuen. GT-231 diu que el comptador inclogui les ocultes; el que la Carla espera és el contrari.
Si es treu el || t.oculta sense més:
- Es trenca
GT-231, que algú va demanar i algú va validar. - Ningú no sabrà per què, perquè el commit que ho desfaci probablement dirà «corregeix el comptador».
- D'aquí a dues setmanes, qui va demanar
GT-231obrirà un tiquet idèntic en sentit contrari, i el cicle començarà una altra vegada.
La sortida correcta és portar la contradicció a qui la pugui resoldre, i que la decisió —sigui quina sigui— quedi escrita al missatge del commit que la implementi (lliçó 08-01), amb una referència als dos tiquets.
El missatge del Bruno ha estalviat un error. Aquell paràgraf de tres línies és la diferència entre una correcció i un bucle.
Conclusió
Aquesta lliçó anava sobre entendre per què Git està fent el que fa.
- El mètode va abans que les eines: reproduir amb l'ordre mínima, aïllar traient variables (configuració global, hooks, un clon net), consultar l'estat real i no el recordat, i comprovar una hipòtesi concreta abans de canviar res.
- Les variables de traça ensenyen el que passa per sota:
GIT_TRACEper a les subordres, àlies i hooks;GIT_TRACE_SETUPper saber quin repositori es pensa Git que fa servir;GIT_SSH_COMMAND="ssh -v"iGIT_CURL_VERBOSEper a autenticació i xarxa;GIT_TRACE_PACKETper al protocol. Comença per les suaus i revisa la sortida abans de compartir-la. git config --list --show-origin --show-scoperesol una fracció sorprenent dels misteris, perquè diu quin valor guanya i de quin fitxer surt, inclosos elsincludeIfcondicionals.git check-ignore -vigit check-attr -aresponen amb precisió les dues preguntes més freqüents del dia a dia: per què s'ignora (o no) un fitxer, i quins atributs se li apliquen.git ls-filesés la finestra a l'índex:--stageper a modes i etapes de conflicte,--eolper als finals de línia amb les seves columnesi/iw/,--others --exclude-standardper al que és realment nou.- La lampisteria contesta sobre el graf sense interpretacions:
rev-parseper traduir a hashos i conèixer l'entorn,rev-listper comptar i llistar,cat-file --batch-checkper a inspecció massiva,verify-packper a l'interior d'un packfile,for-each-refper a totes les referències ils-remoteper al servidor sense baixar res. - I la investigació completa:
bisectporta del símptoma al commit;blamei el missatge del commit porten del commit a la intenció. Saltar-se el segon pas produeix pedaços que trenquen una altra cosa.
El mòdul, en una idea
El mòdul 8 va acabar anunciant desastres. Aquest els ha resolt tots, i la lliçó de fons és una de sola:
A Git, gairebé res no es perd de debò. El que es perd és la calma.
Els commits són objectes immutables que sobreviuen encara que cap referència no els apunti (09-01). reset mou una branca, no destrueix història (09-02). Una divergència és una decisió pendent, no una avaria (09-03). El reflog recorda per on has passat, i amb ell torna el reset --hard, la branca esborrada, el rebase fallit i el desament temporal eliminat (09-04). Un repositori danyat és un problema de logística, perquè el clon de qualsevol company és una còpia gairebé completa (09-05). I quan res d'això no encaixa, hi ha traces, lampisteria i un mètode (09-06).
El que és veritablement fràgil és el que mai no va arribar a ser un objecte: el canvi sense confirmar, el fitxer sense seguiment. Per això el consell més rendible de tot el mòdul no és una ordre, sinó un hàbit: confirma aviat, marca amb git branch abans del que és arriscat, i publica les teves branques.
El que ve
L'Ana, el Bruno, la Carla i el Diego dominen Git a gestor-tasques. Saben construir l'historial, manipular-lo amb criteri, col·laborar amb un procés, mantenir bons hàbits i sortir dels embolics.
Però gestor-tasques són quatre fitxers i un equip de quatre persones. El món real és més gran i més estrany.
Hi ha projectes amb cinquanta mil fitxers i vint anys d'història, on un git status sense optimitzar triga mig minut. Hi ha repositoris que guarden vídeos, models 3D i fitxers de disseny de centenars de megabytes, i que necessiten un sistema diferent per emmagatzemar-los. Hi ha organitzacions on Git no el fa servir una persona en un terminal, sinó cent canonades de desplegament que clonen, etiqueten i publiquen sense intervenció humana. Hi ha integracions amb editors, amb sistemes de tiquets, amb plataformes de revisió i amb eines d'anàlisi que canvien per complet l'experiència diària. I hi ha un Git que continua evolucionant: SHA-256, referències empaquetades en un format nou, clons parcials, índexs dispersos.
Al mòdul 10: Git al Món Real veurem estudis de cas de com fan servir Git projectes i organitzacions reals, la integració amb altres eines del dia a dia, Git LFS per a fitxers grans, com escalar Git en repositoris enormes i monorepos, el paper de Git a DevOps com a peça central del lliurament continu, i cap a on va Git en els pròxims anys.
Comencem mirant com ho fan els altres, a la lliçó 10-01: Estudis de Cas.
Dominant Git: De Principiant a Avançat
Mòdul 1: Introducció a Git
- Què és Git?
- Instal·lant Git
- Terminologia Bàsica de Git
- El Model de Dades de Git
- Configurant Git
- Configuració Inicial
Mòdul 2: Operacions Bàsiques de Git
- Creant un Repositori
- Clonant un Repositori
- Flux de Treball Bàsic de Git
- Preparant i Confirmant Canvis
- Inspeccionant Canvis amb git diff
- Visualitzant l'Historial de Confirmacions
Mòdul 3: Branques i Fusió
- Entenent les Branques
- Creant i Canviant Branques
- Fusionant Branques
- Estratègies de Fusió
- Resolent Conflictes de Fusió
- Gestió de Branques
Mòdul 4: Treballant amb Repositoris Remots
- Entenent els Repositoris Remots
- Afegint un Repositori Remot
- Autenticació amb Repositoris Remots
- Obtenint i Baixant Canvis
- Enviant Canvis
- Rastrejant Branques
Mòdul 5: Operacions Avançades de Git
- Rebase
- Rebase Interactiu
- Cherry-Picking de Confirmacions
- Desant Canvis Temporals
- Etiquetant Confirmacions
- Revertint Confirmacions
Mòdul 6: Eines i Tècniques de Git
- Usant Git Hooks
- Git Bisect
- Git Blame
- Git Log i Àlies
- Submòduls de Git
- Múltiples Còpies de Treball amb git worktree
Mòdul 7: Estratègies de Col·laboració i Flux de Treball
- Forks i Pull Requests
- Revisions de Codi amb Git
- Flux de Treball Git Flow
- GitHub Flow
- Trunk Based Development
- Integració Contínua amb Git
Mòdul 8: Bones Pràctiques i Consells de Git
- Escrivint Bons Missatges de Confirmació
- Mantenint un Historial Net
- Ignorant Fitxers amb .gitignore
- Atributs de Fitxer amb .gitattributes
- Bones Pràctiques de Seguretat
- Consells de Rendiment
Mòdul 9: Resolució de Problemes i Depuració
- Problemes Habituals de Git
- Desfent Canvis
- Resolent Divergències amb el Remot
- Recuperant Confirmacions Perdudes
- Tractant amb Repositoris Corruptes
- Tècniques Avançades de Depuració
