La Carla ja ha arreglat l'esborrat de tasques gràcies al bisect. Però mentre era a app.js s'ha trobat amb això:
Ningú de l'equip recorda haver escrit aquesta funció. No està documentada, no té comentaris i té l'aspecte d'un pedaç defensiu contra alguna cosa molt concreta. La temptació d'esborrar-la és enorme: són cinc línies que "no fan res".
Aquesta temptació és exactament l'escenari que descriu la tanca de Chesterton: no treguis una tanca del mig del camp fins que sàpigues per què la van posar. I en un repositori Git sempre es pot saber, perquè cada línia de cada fitxer prové d'un commit concret, amb el seu autor, la seva data i el seu missatge.
L'eina que fa aquesta consulta és git blame, i en aquesta lliçó l'esprémerem: no només per veure qui va tocar la línia per última vegada, sinó per travessar el soroll —reformatatges, codi mogut, canvis d'espaiat— i arribar al commit que de debò explica el perquè.
Contingut
- Què és exactament
git blame - Llegir la sortida
- Acotar:
-Lper línies i per funció - Veure més enllà del soroll:
-w,-Mi-C - Viatjar en el temps:
<sha>^i--since git log -L: l'evolució completa d'un rang de línies- Ignorar commits de format:
--ignore-revi.git-blame-ignore-revs - Resolent el misteri de
normalitzaText - Blame a la pràctica: atribució, no culpa
- Què és exactament
git blame
git blamegit blame <fitxer> respon a una pregunta molt concreta:
Per a cada línia del fitxer tal com està ara, quin va ser l'últim commit que la va modificar?
Convé subratllar tres coses d'aquesta definició, perquè els tres malentesos més habituals en surten:
- És "l'últim que la va tocar", no "el que la va escriure". Si l'Ana va escriure la línia al gener i el Bruno li va canviar una coma al juny,
blameassenyala el Bruno. Els apartats 4 i 7 són precisament les eines per travessar aquest soroll. - Treballa sobre l'estat actual del fitxer. Les línies esborrades no hi apareixen: si t'interessen, l'eina és
git log -L(apartat 6) o la cerca «pickaxe»-Sde la lliçó 02-06. - No calcula res de nou. Tota la informació ja és al model de dades de la lliçó 01-04:
blamerecorre l'historial del fitxer comparant versions i atribueix cada línia. És una consulta, no una metadada desada.
- Llegir la sortida
9f3c7a1e (Ana Ferrer 2026-04-12 09:31:44 +0200 1) const llista = document.querySelector('#llista-tasques');
9f3c7a1e (Ana Ferrer 2026-04-12 09:31:44 +0200 2) const formulari = document.querySelector('#formulari-tasca');
4e8b2c9d (Bruno Salas 2026-05-03 17:22:08 +0200 3) const camp = document.querySelector('#nova-tasca');
^7d2a1f8 (Ana Ferrer 2026-03-28 11:05:12 +0200 4)
2c6e9a4f (Carla Vidal 2026-05-19 14:47:31 +0200 5) let tasques = carregaTasques();
00000000 (No Confirmat 2026-08-01 10:12:03 +0200 6) let filtreActiu = 'totes';Les cinc columnes, d'esquerra a dreta:
| Columna | Contingut | Detall |
|---|---|---|
| 1 | Hash abreujat del commit | És el commit que va tocar aquella línia per última vegada |
| 2 | Autor | L'autor, no el committer (la distinció de la lliçó 01-06) |
| 3 | Data i hora | Data d'autoria, amb zona horària |
| 4 | Número de línia | Al fitxer actual |
| 5 | Contingut | La línia tal qual |
I dues marques especials que cal saber llegir:
^7d2a1f8, amb accent circumflex: la línia ve del commit més antic del rang analitzat. Sol ser el commit inicial (boundary commit). Vol dir "això és tan vell com el mateix historial que estic mirant".00000000/ "No Confirmat" (Not Committed Yeten anglès): la línia està modificada a l'arbre de treball i encara no s'ha confirmat. És teva, ara mateix.
Opcions de format que estalvien molt de temps:
git blame -s app.js # només hash i línia: compacte, sense autor ni data
git blame -e app.js # mostra el correu en lloc del nom
git blame --date=short app.js # dates com 2026-04-12, sense hora ni zona
git blame -l app.js # hashos complets de 40 caràcters
git blame -c app.js # format compatible amb git annotateI la que de debò es fa servir cada dia, perquè combina el just:
Un detall pràctic: la sortida de blame és llarga i sol passar pel paginador. Per saltar directament a la zona que t'interessa, a less n'hi ha prou amb /normalitzaText + Enter. Però és molt millor acotar d'entrada, que és el que ve ara.
- Acotar:
-L per línies i per funció
-L per línies i per funcióGairebé mai vols el fitxer sencer. -L limita l'anàlisi a un rang:
8d4f2a7c (Bruno Salas 2026-06-22 11:14:38 +0200 78) llista.addEventListener('click', (e) => {
8d4f2a7c (Bruno Salas 2026-06-22 11:14:38 +0200 79) if (e.target.closest('.esborrar')) {
8d4f2a7c (Bruno Salas 2026-06-22 11:14:38 +0200 80) esborraTasca(e.target.closest('.esborrar').dataset.id);Maneres d'expressar el rang, totes vàlides:
| Sintaxi | Significat |
|---|---|
-L 78,92 |
De la línia 78 a la 92 |
-L 78,+15 |
Quinze línies a partir de la 78 |
-L 78,-5 |
De la 73 a la 78 |
-L 78 |
De la 78 al final del fitxer |
-L ,20 |
Del principi a la 20 |
-L :normalitzaText:app.js |
El bloc d'aquella funció, sense comptar línies |
-L '/^function esborraTasca/',+30 |
Des de la línia que casa amb l'expressió regular |
La forma -L :<nom>:<fitxer> és la més còmoda i la menys coneguda. Git localitza el bloc de la funció fent servir les mateixes regles de funcname que empra git diff per a les capçaleres dels fragments (lliçó 02-05):
b7e2c4a9 (Carla Vidal 2026-02-14 16:03:55 +0100 43) function normalitzaText(text) {
b7e2c4a9 (Carla Vidal 2026-02-14 16:03:55 +0100 44) return text
3f1a8d6c (Ana Ferrer 2026-05-30 10:18:22 +0200 45) .replace(/ /g, ' ')
3f1a8d6c (Ana Ferrer 2026-05-30 10:18:22 +0200 46) .replace(/[-]/g, '')
b7e2c4a9 (Carla Vidal 2026-02-14 16:03:55 +0100 47) .trim();
b7e2c4a9 (Carla Vidal 2026-02-14 16:03:55 +0100 48) }Primera pista del misteri: la funció la va crear la Carla al febrer, i l'Ana hi va afegir els dos replace al maig. Dos commits, dos motius possiblement diferents.
El reconeixement de funcions depèn del llenguatge. Git porta patrons integrats per a molts (
c,java,python,php,rust...). Per a JavaScript, el patró integratjavascripts'activa declarant-lo al.gitattributesamb*.js diff=javascript. Aquest fitxer, i tot el que permet configurar, és el tema de la lliçó 08-04; aquí n'hi ha prou de saber que si-L :funcio:no encerta, aquesta és la raó.
- Veure més enllà del soroll:
-w, -M i -C
-w, -M i -CAquí hi ha el veritable valor de blame, i el que separa l'ús ingenu de l'ús expert. L'atribució "en brut" menteix constantment, perquè:
- Algú va reindentar el fitxer → totes les línies són seves.
- Algú va moure una funció de lloc dins del mateix fitxer → totes les línies són seves.
- Algú va extreure un mòdul a un fitxer nou → totes les línies són seves.
En els tres casos, el contingut no va canviar, i blame està assenyalant el missatger. Aquestes tres opcions ho corregeixen:
-w: ignorar els canvis d'espaiat
Ignora les diferències que són només d'espais en blanc en comparar versions. Si el Bruno va passar el fitxer pel formatador i va canviar la indentació de 4 a 2 espais, -w mira a través d'aquell commit i continua atribuint la línia a qui va escriure el codi real.
És pràcticament gratis i gairebé mai molesta. Posa-la sempre.
-M: seguir codi mogut dins del mateix fitxer
Detecta blocs de línies que es van moure o copiar dins del mateix fitxer i atribueix la línia al seu origen, no al commit que la va moure. És el cas de "he reordenat les funcions per agrupar les d'emmagatzematge".
-C: seguir codi mogut entre fitxers
Detecta línies que vénen d'un altre fitxer modificat al mateix commit. És el cas d'extreure codi a un mòdul nou.
-C té nivells, segons quantes vegades es repeteixi, i aquesta és la taula que cal tenir a mà:
| Opció | Què busca | Cost |
|---|---|---|
-M |
Moviments i còpies dins del mateix fitxer | Baix |
-C |
A més, codi vingut d'altres fitxers modificats al mateix commit | Mitjà |
-C -C |
A més, codi vingut de qualsevol fitxer del commit que va crear el fitxer | Alt |
-C -C -C |
A més, codi vingut de qualsevol fitxer de qualsevol commit | Molt alt |
I es pot afinar el llindar de detecció amb un número: -M20 exigeix almenys 20 caràcters alfanumèrics per considerar que un bloc s'ha mogut (per defecte en són 40 per a -M).
A la pràctica, la invocació que resol el 95 % de les investigacions serioses és:
Fixa't que es pot combinar tot: ignorar espais, seguir moviments entre fitxers amb nivell mitjà-alt, i acotat a una funció. És més lent, però en un fitxer d'uns centenars de línies ni es nota.
Comparació pràctica de l'efecte, sobre les mateixes línies:
c9a2e7f4 (Bruno Salas 2026-07-11 09:02:17 +0200 43) function normalitzaText(text) {
c9a2e7f4 (Bruno Salas 2026-07-11 09:02:17 +0200 44) return text
c9a2e7f4 (Bruno Salas 2026-07-11 09:02:17 +0200 45) .replace(/ /g, ' ')b7e2c4a9 (Carla Vidal 2026-02-14 16:03:55 +0100 43) function normalitzaText(text) {
b7e2c4a9 (Carla Vidal 2026-02-14 16:03:55 +0100 44) return text
3f1a8d6c (Ana Ferrer 2026-05-30 10:18:22 +0200 45) .replace(/ /g, ' ')El primer resultat assenyala el Bruno i un commit de juliol: és el commit en què el Bruno va moure la funció d'index.html a app.js. Informativament, és inútil. El segon assenyala els commits que van escriure el contingut, que és el que buscàvem.
- Viatjar en el temps:
<sha>^ i --since
<sha>^ i --sinceblame accepta un punt de partida diferent d'HEAD:
git blame v1.0.0 -- app.js # el fitxer tal com era a l'etiqueta
git blame 8d4f2a7 -- app.js # tal com era en aquell commitD'aquí surt la tècnica més útil de tot l'apartat: saltar per damunt d'un commit per veure què hi havia abans.
Quan blame et diu que la línia 45 la va tocar 3f1a8d6 i aquell commit resulta ser un reformatatge, vols saber qui la va tocar abans. La resposta és fer blame del pare d'aquell commit:
3f1a8d6^ és "el pare de 3f1a8d6" (lliçó 02-06). En fer blame del fitxer en aquell punt, el commit 3f1a8d6 deixa d'existir per a l'anàlisi i apareix l'anterior. Repetint el procés es pot anar retrocedint capa a capa:
git blame 3f1a8d6^ -- app.js | sed -n '45p'
# → 1c8f4b2 (Carla Vidal 2026-03-02 ...)
git blame 1c8f4b2^ -- app.js | sed -n '45p'
# → b7e2c4a9 (Carla Vidal 2026-02-14 ...)És tediós a mà, i per això existeix git log -L (apartat 6), que fa aquest recorregut d'una sola vegada. Però convé conèixer la tècnica, perquè funciona sempre i en qualsevol versió de Git.
Altres maneres d'acotar el rang temporal:
Amb --since, les línies l'últim canvi de les quals és anterior al límit apareixen marcades amb ^ com a línies de frontera: "això ja hi era abans del període que t'interessa". És útil per respondre a "què s'ha tocat d'aquest fitxer aquest trimestre?" sense soroll de fa tres anys.
git log -L: l'evolució completa d'un rang de línies
git log -L: l'evolució completa d'un rang de líniesblame dona una foto: l'estat actual i qui va tocar cada línia per última vegada. git log -L dona la pel·lícula: tots els commits que han afectat un rang de línies, amb el seu diff.
commit 3f1a8d6c9e2b4a7f1d5c8e3b6a9f2d4c7e1b5a8f Author: Ana Ferrer <[email protected]> Date: Fri May 30 10:18:22 2026 +0200 Elimina els caràcters invisibles en normalitzar el text de les tasques En enganxar text des del gestor de correu, les tasques arribaven amb espais durs (U+00A0) i marques d'amplada zero. El resultat era que dues tasques visualment idèntiques no es detectaven com a duplicades i que el filtre per text no trobava res. diff --git a/app.js b/app.js --- a/app.js +++ b/app.js @@ -43,5 +43,7 @@ function normalitzaText(text) { return text + .replace(/ /g, ' ') + .replace(/[-]/g, '') .trim(); } commit b7e2c4a9f1d6c3e8b5a2f7d9c4e1b8a6f3d5c2e9 Author: Carla Vidal <[email protected]> Date: Sat Feb 14 16:03:55 2026 +0100 Normalitza el text de les tasques abans de desar-les Els espais sobrants al principi i al final provocaven tasques duplicades aparents. diff --git a/app.js b/app.js --- a/app.js +++ b/app.js @@ -41,0 +43,5 @@ +function normalitzaText(text) { + return text + .trim(); +}
Misteri resolt en una sola ordre. Els dos replace no són arbitraris: protegeixen dels caràcters invisibles que arriben en enganxar text des del correu. Esborrar aquesta funció hauria reintroduït una fallada real i difícil de reproduir.
Les formes de -L són les mateixes que a blame:
git log -L 43,48:app.js # rang de línies
git log -L :normalitzaText:app.js # una funció
git log -L '/^function esborraTasca/',+20:app.jsCombinacions molt útils:
git log -L :normalitzaText:app.js --oneline # només la llista de commits, sense diffs
git log -L :normalitzaText:app.js -n 3 # els tres més recents
git log -L :desaTasques:app.js -L :carregaTasques:app.js # dos rangs alhoraLa comparació entre les dues eines, que és la que cal tenir clara:
git blame |
git log -L |
|
|---|---|---|
| Què mostra | Estat actual: qui va tocar cada línia per última vegada | Història completa: tots els canvis del rang |
| Línies esborrades | No hi apareixen | Sí que hi apareixen, als diffs |
| Sortida | Una línia per línia de codi | Un commit amb diff per cada canvi |
| Pregunta que respon | "Qui va escriure això?" | "Com hem arribat fins a això?" |
| Punt de partida típic | Estàs llegint el fitxer i alguna cosa et grinyola | Ja saps quina zona t'interessa |
| Cost | Ràpid | Més lent (recorre l'historial del fitxer) |
El flux natural és encadenar-les: blame per localitzar la zona i el commit, log -L per entendre l'evolució, i git show <sha> per llegir el commit complet amb el seu missatge.
- Ignorar commits de format:
--ignore-rev i .git-blame-ignore-revs
--ignore-rev i .git-blame-ignore-revsAquest apartat resol el problema que més enverina el blame en projectes reals.
L'equip de gestor-tasques decideix adoptar Prettier. L'Ana l'executa sobre tot el projecte i confirma:
[main a3f9c2e] Aplica el format de Prettier a tot el projecte 3 files changed, 412 insertions(+), 398 deletions(-)
Un sol commit, cap canvi de comportament... i a partir d'ara git blame app.js atribueix a l'Ana gairebé totes les línies del fitxer. Tota la informació històrica ha quedat sepultada sota un commit cosmètic.
-w ajuda si el canvi va ser només d'espaiat, però Prettier també reordena, parteix línies llargues i canvia cometes: -w no n'hi ha prou.
--ignore-rev
Git analitza el fitxer com si aquell commit no existís a efectes d'atribució. Les línies que va canviar s'atribueixen al commit anterior que les va tocar de debò. Si una línia no es pot reassignar netament, Git la marca amb un asterisc * per avisar que l'atribució és aproximada.
.git-blame-ignore-revs
Repetir --ignore-rev a mà no escala. La solució és un fitxer versionat amb la llista de commits a ignorar:
# .git-blame-ignore-revs # # Commits purament cosmètics que no han d'aparèixer a git blame. # Requereix: git config blame.ignoreRevsFile .git-blame-ignore-revs # # Aplica el format de Prettier a tot el projecte (2026-07-25) a3f9c2e7b1d4c8f2a6e9b3d5c7f1a4e8b2d6c9f3 # Migra a cometes simples a tot el JavaScript (2026-06-02) 7d1c4a8f3e6b9d2c5a8f1e4b7d3c6a9f2e5b8d1c # Reindenta estils.css a 2 espais (2026-05-14) 2e9b5d8c1f4a7e3b6d9c2f5a8e1b4d7c3f6a9e2b
Dues regles de format que cal respectar:
- Hashos complets de 40 caràcters. Els abreujats no valen; Git els rebutja amb un error.
- Comentaris amb
#. Documenta què era cada commit: d'aquí a un any ningú ho recordarà.
Per aconseguir el hash complet:
Ara s'activa a la configuració (lliçó 01-05):
I a partir d'aquell moment, tots els git blame del repositori l'apliquen sols:
b7e2c4a9 (Carla Vidal 2026-02-14 16:03:55 +0100 43) function normalitzaText(text) {
3f1a8d6c (Ana Ferrer 2026-05-30 10:18:22 +0200 45) .replace(/ /g, ' ')La història real ha tornat. I com que el fitxer està versionat, tot l'equip se'n beneficia tan bon punt executi aquell git config una vegada —el mateix patró de "fitxer versionat + un git config local" que vam veure amb core.hooksPath a la lliçó 06-01.
Altres opcions relacionades:
git blame --ignore-revs-file altra-llista.txt app.js # usar un altre fitxer puntualment
git blame --ignore-rev HEAD app.js # ignorar l'últim commit
git config blame.markIgnoredLines true # marcar amb '?' les línies afectades
git config blame.markUnblamableLines true # marcar amb '*' les no reassignablesCom fer-ho bé des del principi: quan vagis a aplicar un reformatatge massiu, fes-lo en un commit propi i aïllat que no toqui res més, i afegeix-lo al
.git-blame-ignore-revsal commit següent. Un reformatatge barrejat amb canvis funcionals és impossible d'ignorar netament i contamina l'historial per sempre. Aquesta disciplina és part del que veurem a la lliçó 08-02.Nota pràctica: les plataformes d'allotjament (GitHub, GitLab) també llegeixen
.git-blame-ignore-revsi l'apliquen a la seva vista de blame, així que el fitxer serveix tant en local com al web.
- Resolent el misteri de
normalitzaText
normalitzaTextRecapitulant la investigació completa de la Carla, que és el procediment estàndard davant de qualsevol "això per a què hi és?":
# 1. Qui va tocar aquestes línies per última vegada? Ignorant soroll.
git blame -w -C --date=short -L :normalitzaText:app.js app.jsb7e2c4a9 (Carla Vidal 2026-02-14 43) function normalitzaText(text) {
b7e2c4a9 (Carla Vidal 2026-02-14 44) return text
3f1a8d6c (Ana Ferrer 2026-05-30 45) .replace(/ /g, ' ')
3f1a8d6c (Ana Ferrer 2026-05-30 46) .replace(/[-]/g, '')
b7e2c4a9 (Carla Vidal 2026-02-14 47) .trim();
b7e2c4a9 (Carla Vidal 2026-02-14 48) }commit 3f1a8d6c9e2b4a7f1d5c8e3b6a9f2d4c7e1b5a8f Author: Ana Ferrer <[email protected]> Date: Fri May 30 10:18:22 2026 +0200 Elimina els caràcters invisibles en normalitzar el text de les tasques En enganxar text des del gestor de correu, les tasques arribaven amb espais durs (U+00A0) i marques d'amplada zero. [...] app.js | 2 ++
# 4. Qui més fa servir aquesta funció? (pickaxe, lliçó 02-06)
git log -S "normalitzaText" --oneline
grep -n "normalitzaText" *.jsb7e2c4a9 Normalitza el text de les tasques abans de desar-les 3f1a8d6c Elimina els caràcters invisibles en normalitzar el text de les tasques c9a2e7f4 Mou les funcions auxiliars d'index.html a app.js
Quatre ordres, dos minuts, i una resposta definitiva: la funció no es toca, i ara la Carla sap a més que hauria de portar un comentari explicatiu. L'hi afegeix:
/**
* Normalitza el text d'una tasca abans de desar-la.
*
* Elimina espais durs (U+00A0) i marques d'amplada zero (U+200B-U+200D,
* U+FEFF), que arriben en enganxar text des del gestor de correu i provoquen
* que dues tasques visualment idèntiques es tractin com a diferents.
* Vegeu el commit 3f1a8d6.
*/
function normalitzaText(text) {
return text
.replace(/ /g, ' ')
.replace(/[-]/g, '')
.trim();
}Fixa't en el detall: el comentari referencia el commit. És la manera de connectar el codi amb l'explicació completa sense repetir-la, i estalvia a la persona següent tota aquesta investigació.
- Blame a la pràctica: atribució, no culpa
El nom de l'ordre és un accident històric desafortunat. En anglès, to blame és "culpar", i això ha creat una associació desagradable: la idea que git blame serveix per assenyalar el responsable d'una nyap.
No és per a això. Git té fins i tot un àlies amb millor nom, heretat d'altres sistemes:
I "anotar" descriu molt millor el que fa: enriquir cada línia amb la seva procedència.
Els usos legítims, tots ells constructius:
- Entendre el perquè. El cas d'aquesta lliçó. El missatge del commit conté el context que el codi no pot expressar.
- Trobar a qui preguntar. No per retreure res, sinó perquè qui va escriure alguna cosa fa tres mesos té context que tu no tens.
- Datar un canvi. "Aquesta línia és anterior o posterior a la migració?"
- Avaluar el risc de tocar alguna cosa. Codi de fa tres anys que ningú ha tocat i del qual ningú es recorda: cal procedir amb cura.
- Documentar cap enrere. Com acaba de fer la Carla.
I això porta a la conclusió de fons de la lliçó, que convé enunciar sense embuts:
git blameval exactament el que valen els teus missatges de commit.
Si el commit que apareix diu "Elimina els caràcters invisibles en normalitzar el text de les tasques" amb tres línies explicant el perquè, blame t'ha resolt el problema. Si diu "fix", "canvis" o "asdf", blame t'ha dit qui i quan, però no per què, que era l'única cosa que necessitaves.
Aquesta és la connexió directa amb la lliçó 08-01: Escrivint Bons Missatges de Confirmació. Allà veurem com s'escriu un missatge que expliqui la intenció i no la mecànica. I l'argument definitiu per fer-ho bé és aquest: no escrius el missatge per a avui, l'escrius per al git blame que algú farà d'aquí a dos anys —i aquest algú probablement seràs tu.
De la mateixa manera, els commits atòmics (lliçó 08-02) fan que el blame assenyali un canvi comprensible en lloc d'un commit de 900 línies titulat "feina del divendres".
Errors Habituals i Consells
Error 1: creure que blame diu qui va escriure la línia. Diu qui la va tocar per última vegada. Sense -w, -M, -C i --ignore-rev, la resposta sol ser "l'últim que va passar el formatador".
Error 2: fer blame del fitxer sencer. Centenars de línies de sortida per investigar-ne cinc. Fes servir sempre -L, i sobretot -L :funcio:fitxer.
Error 3: aturar-se al primer commit que apareix. Si és un moviment o un reformatatge, continua estirant del fil amb git blame <sha>^ -- <fitxer> o passa directament a git log -L.
Error 4: buscar amb blame una línia que ja no existeix. blame només veu l'estat actual. Per a codi esborrat, git log -L, git log -S "text" (pickaxe) o git log -p -- <fitxer>.
Error 5: hashos abreujats a .git-blame-ignore-revs. Git exigeix els 40 caràcters i falla amb un error poc clar si no els poses. Obtén-los amb git rev-parse.
Error 6: barrejar reformatatge i canvis funcionals en un commit. Aquell commit ja no es pot ignorar netament i contamina el blame del fitxer per sempre. El reformatatge, sempre en el seu propi commit.
Error 7: fer servir blame per repartir responsabilitats. A més de ser tòxic, és tècnicament poc fiable: l'atribució depèn de les opcions que facis servir.
Consell 1: crea un àlies. El veuràs a la lliçó 06-04, però avança'l ja:
Consell 2: encadena les tres eines. blame per localitzar, log -L per a l'evolució, show per al commit complet. És el procediment estàndard i no falla.
Consell 3: quan resolguis un misteri, documenta'l al codi. Un comentari amb la referència al commit estalvia la investigació als següents.
Consell 4: blame a l'editor. Pràcticament tots els editors moderns tenen integració (GitLens al VS Code, :Git blame al Vim amb fugitive, l'anotació integrada d'IntelliJ). Ensenyen el commit de la línia del cursor sense sortir del fitxer. Configura'ls amb -w si et deixen.
Consell 5: combina'l amb bisect. Quan bisect (lliçó 06-02) et dona el commit culpable, blame sobre les línies que va canviar et diu què hi havia abans i per què estava així.
Exercicis
Exercici 1: blame bàsic i acotat
Crea un repositori amb un fitxer app.js que evolucioni en cinc commits de tres autors diferents (pots canviar d'autor amb git commit --author="Nom <correu>"). Després:
- Executa
git blame app.jsi comprova que cada línia s'atribueix al commit correcte. - Acota amb
-La un rang de tres línies. - Fes servir
-L :nomFuncio:app.jsper acotar a una funció. - Prova
-s,-ei--date=shorti compara les sortides.
Exercici 2: l'efecte d'un reformatatge i com ignorar-lo
Sobre el repositori anterior:
- Fes un commit que reindenti tot el fitxer (de 2 a 4 espais) sense canviar res més.
- Executa
git blamei comprova que ara totes les línies són d'aquell commit. - Comprova que
-wrecupera l'atribució original. - Fes un altre commit que canviï les cometes dobles per simples a tot el fitxer, i comprova que
-wja no n'hi ha prou. - Crea un
.git-blame-ignore-revsamb aquell commit, configurablame.ignoreRevsFilei comprova que l'atribució torna.
Exercici 3: la història completa d'una funció
Sobre el mateix repositori:
- Fes servir
git log -L :nomFuncio:app.jsi descriu l'evolució de la funció. - Afegeix una línia a la funció, confirma-la, i esborra-la en un altre commit.
- Comprova que
git blameno mostra rastre d'aquella línia, perògit log -Lsí. - Localitza aquella mateixa línia esborrada fent servir la cerca «pickaxe»
git log -Sde la lliçó 02-06.
Solucions
Solució 1:
mkdir /tmp/practica-blame && cd /tmp/practica-blame
git init -b main
cat > app.js <<'FI'
function normalitzaText(text) {
return text.trim();
}
FI
git add . && git commit -q -m "Afegeix la normalitzacio basica de text" \
--author="Carla Vidal <[email protected]>"
cat > app.js <<'FI'
function normalitzaText(text) {
return text.trim();
}
function desaTasques(tasques) {
localStorage.setItem("tasques", JSON.stringify(tasques));
}
FI
git commit -qam "Afegeix el desament de tasques a localStorage" \
--author="Ana Ferrer <[email protected]>"
cat > app.js <<'FI'
function normalitzaText(text) {
return text
.replace(/ /g, " ")
.trim();
}
function desaTasques(tasques) {
localStorage.setItem("tasques", JSON.stringify(tasques));
}
FI
git commit -qam "Converteix els espais durs en normalitzar" \
--author="Bruno Salas <[email protected]>"5f2a8c1 (Carla Vidal 2026-08-01 1) function normalitzaText(text) {
9d3e7b4 (Bruno Salas 2026-08-01 2) return text
9d3e7b4 (Bruno Salas 2026-08-01 3) .replace(/ /g, " ")
9d3e7b4 (Bruno Salas 2026-08-01 4) .trim();
5f2a8c1 (Carla Vidal 2026-08-01 5) }
5f2a8c1 (Carla Vidal 2026-08-01 6)
2c7f4a9 (Ana Ferrer 2026-08-01 7) function desaTasques(tasques) {Fixa't en un detall instructiu: la línia 2 s'atribueix al Bruno encara que el return text conceptualment el va escriure la Carla. És perquè el Bruno va modificar aquella línia en partir l'expressió en diverses. blame opera sobre línies, no sobre intencions.
git blame -L 1,3 app.js
git blame -L :normalitzaText:app.js
git blame -s app.js # només hash i línia
git blame -e app.js # amb el correuSolució 2:
# 1. Reformatatge d'indentació
sed -i 's/^ / /' app.js
sed -i 's/^ \./ ./' app.js
git commit -qam "Reindenta app.js a 4 espais" \
--author="Ana Ferrer <[email protected]>"
# 2. Ara tot és de l'Ana
git blame --date=short app.js4a8c2e7 (Ana Ferrer 2026-08-01 2) return text 4a8c2e7 (Ana Ferrer 2026-08-01 3) .replace(/ /g, " ") 4a8c2e7 (Ana Ferrer 2026-08-01 4) .trim();
9d3e7b4 (Bruno Salas 2026-08-01 2) return text 9d3e7b4 (Bruno Salas 2026-08-01 3) .replace(/ /g, " ")
# 4. Canvi de cometes: -w ja no n'hi ha prou
sed -i 's/"/'"'"'/g' app.js
git commit -qam "Migra a cometes simples a tot el JavaScript" \
--author="Ana Ferrer <[email protected]>"
git blame -w --date=short app.js1b6d9f3 (Ana Ferrer 2026-08-01 3) .replace(/ /g, ' ')
1b6d9f3 (Ana Ferrer 2026-08-01 8) localStorage.setItem('tasques', ...El canvi no és d'espaiat, així que -w no el travessa.
# 5. El fitxer de commits ignorats
git rev-parse HEAD > /tmp/hash.txt
{
echo "# Commits cosmetics ignorats a git blame"
echo "#"
echo "# Migra a cometes simples a tot el JavaScript"
cat /tmp/hash.txt
} > .git-blame-ignore-revs
git add .git-blame-ignore-revs
git commit -qm "Afegeix la llista de commits ignorats a blame"
git config blame.ignoreRevsFile .git-blame-ignore-revs
git blame -w --date=short app.js9d3e7b4 (Bruno Salas 2026-08-01 3) .replace(/ /g, ' ')
2c7f4a9 (Ana Ferrer 2026-08-01 8) localStorage.setItem('tasques', ...L'atribució real ha tornat, i ara és automàtica per a tot el repositori.
Solució 3:
1b6d9f3 Migra a cometes simples a tot el JavaScript 4a8c2e7 Reindenta app.js a 4 espais 9d3e7b4 Converteix els espais durs en normalitzar 5f2a8c1 Afegeix la normalitzacio basica de text
Quatre commits: dos de cosmètics i dos de contingut real. La pel·lícula completa, inclòs el que blame amb --ignore-revs amaga deliberadament.
# 2. Afegir i esborrar una línia
sed -i "s| .trim();| .replace(/\\\\s+/g, ' ')\n .trim();|" app.js
git commit -qam "Comprimeix els espais multiples en normalitzar"
sed -i "/replace(\/\\\\s+\/g/d" app.js
git commit -qam "Reverteix la compressio d'espais: trencava les tabulacions"# 3. blame no la veu; log -L sí
git blame app.js | grep "s+" || echo "(blame no mostra la línia esborrada)"
git log -L :normalitzaText:app.js --oneline | head -38e2c5f1 Reverteix la compressio d'espais: trencava les tabulacions 3d9a7b4 Comprimeix els espais multiples en normalitzar 1b6d9f3 Migra a cometes simples a tot el JavaScript
8e2c5f1 Reverteix la compressio d'espais: trencava les tabulacions 3d9a7b4 Comprimeix els espais multiples en normalitzar
-S compta aparicions de la cadena i mostra els commits on aquest número canvia: el que la va introduir i el que la va treure. És l'eina per al que ja no hi és.
Conclusió
git blame converteix l'historial en una anotació línia a línia del codi que tens al davant. L'essencial:
- Respon a "quin commit va tocar aquesta línia per última vegada?", sobre l'estat actual del fitxer. No diu qui la va escriure originalment, i no veu les línies esborrades.
- La sortida té cinc columnes —hash, autor, data, número de línia i contingut—;
^marca el commit frontera i00000000els canvis sense confirmar. -Lacota, i-L :funcio:fitxerés la manera més còmoda de fer-ho sense comptar línies.-w,-Mi-Csón el que separa una atribució útil d'una d'inútil: ignoren l'espaiat i segueixen el codi mogut o copiat, dins i entre fitxers, amb nivells creixents (-C,-C -C,-C -C -C).git blame <sha>^ -- <fitxer>salta per damunt d'un commit per veure què hi havia abans: la tècnica manual per estirar del fil.git log -Ldona la pel·lícula completa onblamedona la foto: tots els commits que van afectar un rang, amb els seus diffs, incloses les línies esborrades.--ignore-revi.git-blame-ignore-revs(ambblame.ignoreRevsFile) neutralitzen els reformatatges massius i tornen la història real. Perquè funcioni, el reformatatge ha d'estar en el seu propi commit aïllat.- El procediment estàndard davant de qualsevol "això per a què hi és?":
blame→log -L→show, igrep/log -Sper veure qui més en depèn. - I el més important: és atribució, no culpa, i val el que valguin els teus missatges de commit (lliçó 08-01).
La Carla i l'equip ja tenen un arsenal de consulta considerable: log amb els seus filtres i la seva cerca «pickaxe», bisect, blame, log -L, show. El problema és que les invocacions útils són cada cop més llargues —git blame -w -C --date=short -L :funcio:app.js app.js no s'escriu a mà dues vegades— i que hi ha vistes de l'historial que encara no sabem demanar: el graf complet de branques, només les fusions, qui ha contribuït quant, o un format d'una línia amb colors que es llegeixi d'un cop d'ull.
Toca pujar de nivell amb git log i, sobretot, deixar d'escriure tot això a mà. És la lliçó 06-04: Git Log i Àlies.
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ó
