A la lliçó 02-06 vam aprendre a moure'ns per l'historial: git log amb els seus formats, els seus filtres per autor i data, i la cerca amb -S i -G. Allò era suficient per a un projecte d'una sola branca i uns quants commits.
Ara gestor-tasques té vuit mesos, quatre branques actives, fusions, etiquetes de versió i tres persones treballant-hi. I les preguntes que es fa l'equip han canviat de naturalesa:
- Quina forma té l'historial? Què s'ha fusionat a
mainaquest mes? - Què hi ha a la branca de la Carla que no sigui a
main, i a l'inrevés? - Qui ha aportat què, i quant?
- Quines són les fites —etiquetes i fusions— sense el soroll dels commits intermedis?
I hi ha un segon problema, més prosaic però igual de real. La invocació de la lliçó anterior era git blame -w -C --date=short -L :normalitzaText:app.js app.js. Ningú escriu això dues vegades. La segona meitat de la lliçó va d'això: convertir les ordres llargues que fas servir cada dia en paraules curtes, amb els àlies de Git.
Contingut
--graph: veure la forma de l'historial--first-parent: la línia principal, sense soroll--simplify-by-decoration: només les fites--mergesi--no-merges- Rangs de tres punts i
--left-right --pretty=format:avançat: colors i alineaciógit shortlog: qui ha aportat què- Component consultes amb el que hem après
- Àlies: què són i on viuen
- Àlies amb
!: shell i funcions - Un joc d'àlies per al dia a dia
- Riscos i bones pràctiques amb els àlies
--graph: veure la forma de l'historial
--graph: veure la forma de l'historialgit log per defecte dona una llista plana ordenada per data, que en un historial amb branques és enganyosa: no es veu què venia d'on. --graph dibuixa el DAG (lliçó 03-01) amb caràcters ASCII a l'esquerra.
* c8f2a1e (HEAD -> main, origin/main) Fusiona funcionalitat/filtre-estat |\ | * 4e8b2c9 (funcionalitat/filtre-estat) Afegeix el filtre per estat de la tasca | * 2c6e9a4 Extreu els estils del llistat a estils.css |/ * 8d4f2a7 Delega els esdeveniments del llistat al contenidor | * 7a1f5c3 (correccio/esborrat-svg) Usa closest() en detectar el boto d'esborrar |/ * 3f1a8d6 Elimina els caracters invisibles en normalitzar el text de les tasques * b7e2c4a (tag: v1.0.0) Normalitza el text de les tasques abans de desar-les
Els tres modificadors són inseparables a la pràctica:
| Opció | Què aporta |
|---|---|
--graph |
Les línies i asteriscos que dibuixen la topologia |
--oneline |
Un commit per línia: sense això, el graf és il·legible |
--decorate |
Els (HEAD -> main, tag: v1.0.0): on apunten les referències |
--all |
Totes les referències, no només la branca actual |
Com llegir el dibuix:
- Cada
*és un commit. La columna en què es troba indica el seu "carril". |\marca un commit de fusió: hi entren dues línies.|/marca el punt en què dos carrils s'uneixen cap enrere: l'ancestre comú.- La branca
correccio/esborrat-svgapareix penjant de3f1a8d6sense haver-se fusionat: són commits que existeixen només en aquella branca.
Un detall sobre --decorate: des de Git 2.13 està actiu per defecte quan la sortida va a un terminal (--decorate=auto), així que sovint no cal escriure'l. Es pot fixar amb:
I variants de --all que eviten el soroll de desenes de branques remotes antigues:
git log --graph --oneline --branches # només branques locals
git log --graph --oneline --branches --tags # locals + etiquetes
git log --graph --oneline --remotes=origin # només les d'origin
git log --graph --oneline main funcionalitat/filtre-estat # només aquestes dues
--first-parent: la línia principal, sense soroll
--first-parent: la línia principal, sense sorollJa va aparèixer a la lliçó 06-02 amb bisect, i a la 05-06 en triar -m 1. La idea és la mateixa: en un commit de fusió, el primer pare és la branca en què eres (normalment main) i el segon és la que vas portar.
c8f2a1e Fusiona funcionalitat/filtre-estat 8d4f2a7 Delega els esdeveniments del llistat al contenidor 3f1a8d6 Elimina els caracters invisibles en normalitzar el text de les tasques b7e2c4a Normalitza el text de les tasques abans de desar-les
Compara-ho amb el --graph de l'apartat anterior: han desaparegut 4e8b2c9 i 2c6e9a4, que eren els commits dins de la branca fusionada. El que queda és la història de main a nivell d'integracions: cada línia és o un commit directe o "aquí va entrar una funcionalitat sencera".
És la vista correcta per a:
- Redactar les notes d'una versió.
- Respondre a "què ha entrat a
mainaquesta setmana?". - Comptar quantes funcionalitats es van integrar en un període.
# El que ha entrat a main des de l'última etiqueta
git log --oneline --first-parent v1.0.0..main
# Quantes integracions en l'últim mes
git log --oneline --first-parent --since=1.month main | wc -l
--simplify-by-decoration: només les fites
--simplify-by-decoration: només les fitesAquesta opció és poc coneguda i molt útil. Mostra únicament els commits que tenen una referència apuntant-los (una branca, una etiqueta o HEAD), més els mínims necessaris per mantenir el graf connectat.
* c8f2a1e (HEAD -> main, origin/main) Fusiona funcionalitat/filtre-estat |\ | * 4e8b2c9 (funcionalitat/filtre-estat) Afegeix el filtre per estat de la tasca * | 7a1f5c3 (correccio/esborrat-svg) Usa closest() en detectar el boto d'esborrar |/ * b7e2c4a (tag: v1.0.0) Normalitza el text de les tasques abans de desar-les * 1a5c9f3 (tag: v0.9.0) Prepara la versio de proves
Quatre-cents commits reduïts a cinc línies: l'esquelet del projecte. És el primer que convé executar en arribar a un repositori desconegut, perquè en deu segons et dona el mapa: quines versions hi ha, quines branques són vives i on van néixer.
Combinacions útils:
# Només l'evolució de les versions
git log --graph --oneline --simplify-by-decoration --tags
# Amb dates, per veure el ritme de publicació
git log --simplify-by-decoration --tags --date=short \
--pretty=format:'%C(yellow)%d%C(reset) %ad %s'
--merges i --no-merges
--merges i --no-mergesDos filtres complementaris i directes:
git log --oneline --merges # NOMÉS commits de fusió
git log --oneline --no-merges # tots MENYS els de fusió| Filtre | Què mostra | Per a què serveix |
|---|---|---|
--merges |
Només commits amb dos o més pares | Veure l'historial d'integracions |
--no-merges |
Només commits de feina real | Estadístiques, notes de versió, revisions |
--min-parents=N |
Commits amb almenys N pares | --min-parents=3 troba octopus merges |
--max-parents=1 |
Equivalent a --no-merges |
Igual, amb una altra sintaxi |
--max-parents=0 |
Els commits arrel (sense pares) | Detectar historials empeltats |
--no-merges és especialment important per a les estadístiques: si comptes commits per autor sense aquesta opció, qui més fusiona sembla el més productiu.
# Les fusions d'aquest mes, amb qui les va fer
git log --merges --since=1.month --pretty=format:'%h %an %s'
# La feina real des de l'última versió, per a les notes de publicació
git log --no-merges --oneline v1.0.0..HEAD
- Rangs de tres punts i
--left-right
--left-rightDe la lliçó 02-06 recordem A..B: "els commits assolibles des de B però no des d'A". És asimètric i respon a "què li falta a A de B?".
El triple punt A...B és la diferència simètrica: els commits que són en un o altre, però no en tots dos. Respon a "en què s'han separat aquestes dues branques?".
gitGraph commit id: "base" commit id: "M1" branch funcionalitat/filtre-estat commit id: "F1" commit id: "F2" checkout main commit id: "M2" commit id: "M3"
git log --oneline main..funcionalitat/filtre-estat # F1, F2
git log --oneline funcionalitat/filtre-estat..main # M2, M3
git log --oneline main...funcionalitat/filtre-estat # F1, F2, M2, M3Amb A...B tot sol no se sap quin commit és de quin costat. Per a això hi ha --left-right:
> 6f2b9d4 Afegeix el filtre per estat de la tasca > a1e5c93 Extreu els estils del llistat a estils.css < e91d4a8 Corregeix el comptador de tasques pendents < 7d3a8f4 Actualitza el README amb les instruccions d'arrencada
| Marca | Significat |
|---|---|
< |
El commit és al costat esquerre del rang (aquí, main) |
> |
El commit és al costat dret (aquí, la branca) |
És la vista perfecta abans de fusionar o rebasar: d'un cop d'ull veus quant s'ha mogut cada costat. I amb el remot:
# Com estic respecte de la meva branca de seguiment? (lliçó 04-06)
git log --oneline --left-right --graph HEAD...@{u}
# Només el recompte, que sovint és l'únic que vols
git rev-list --left-right --count main...funcionalitat/filtre-estatDos commits per davant a main, dos a la branca. Aquest rev-list --count és, per cert, el que hi ha darrere del [ahead 2, behind 2] de git status i git branch -vv.
Un afegit útil: --cherry-mark marca amb = els commits que són equivalents a un de l'altre costat (mateix canvi, hash diferent) —el que vèiem amb git cherry a la lliçó 05-03:
--pretty=format: avançat: colors i alineació
--pretty=format: avançat: colors i alineacióA la lliçó 02-06 vam veure els marcadors bàsics de --pretty=format:. Ara anem als dos grups que converteixen una sortida llegible en una sortida excel·lent: colors i alineació.
Colors
| Marcador | Efecte |
|---|---|
%C(rojo), %C(green), %C(yellow), %C(blue), %C(magenta), %C(cyan) |
Color de text |
%C(bold blue) |
Color amb atribut (bold, dim, ul, reverse) |
%C(reset) |
Torna al color per defecte |
%C(auto) |
Fa servir el color que Git faria servir per defecte per a aquell camp |
%C(auto,yellow) |
Groc, però només si el color és actiu |
%C(always,red) |
Vermell sempre, fins i tot en redirigir a un fitxer |
%C(auto) és el que cal fer servir per defecte, i hi ha una raó concreta: aplicat a %d (les decoracions), pinta cada referència amb el seu color canònic —HEAD en cian, les branques locals en verd, les remotes en vermell, les etiquetes en groc— exactament com fa git log --decorate sense format personalitzat. Escriure això a mà és impossible.
A més, %C(auto) respecta la configuració color.ui: si rediriges la sortida a un fitxer o a grep, els codis d'escapament no s'emeten i el fitxer queda net.
Alineació
Quan el format té diverses columnes, sense alineació queda un desastre visual. Els marcadors d'emplenament ho resolen:
| Marcador | Efecte |
|---|---|
%<(N) |
Els camps següents ocupen com a mínim N caràcters, alineats a l'esquerra |
%<(N,trunc) |
Igual, però trunca amb .. si se'n passa |
%<(N,ltrunc) |
Trunca per l'esquerra (..final) |
%<(N,mtrunc) |
Trunca pel mig (ini..fi) |
%>(N) |
Alineat a la dreta |
%><(N) |
Centrat |
%<|(N) |
Omple fins a la columna N (absoluta, no relativa) |
El format complet que fa servir l'equip:
git log --pretty=format:'%C(auto)%h %C(blue)%<(16,trunc)%an%C(reset) %C(green)%<(12)%ar%C(reset) %C(auto)%d%C(reset) %s'c8f2a1e Ana Ferrer fa 2 dies (HEAD -> main, origin/main) Fusiona funcionalitat/filtre-estat 4e8b2c9 Bruno Salas fa 3 dies (funcionalitat/filtre-estat) Afegeix el filtre per estat 2c6e9a4 Carla Vidal fa 4 dies Extreu els estils del llistat a estils.css 8d4f2a7 Bruno Salas fa 6 set. Delega els esdeveniments del llistat al contenidor b7e2c4a Carla Vidal fa 5 mesos (tag: v1.0.0) Normalitza el text de les tasques
Desglossament peça a peça:
%C(auto)%h→ hash abreujat, amb el color estàndard de Git per als hashos.%C(blue)%<(16,trunc)%an%C(reset)→ autor en blau, exactament 16 columnes, truncat si no hi cap.%C(green)%<(12)%ar%C(reset)→ data relativa ("fa 2 dies") en verd, 12 columnes.%C(auto)%d%C(reset)→ les decoracions, cadascuna amb el seu color canònic.%s→ l'assumpte, que ocupa la resta.
El resultat és tabular, llegible i compacte. A l'apartat 11 el convertirem en un àlies.
Un recordatori dels marcadors més usats, per no haver de tornar a la lliçó 02-06:
| Marcador | Contingut |
|---|---|
%H / %h |
Hash complet / abreujat |
%an / %ae |
Nom / correu de l'autor |
%cn / %ce |
Nom / correu del committer |
%ad / %cd |
Data d'autoria / de commit |
%ar / %cr |
Les mateixes, en format relatiu |
%s / %b |
Assumpte / cos del missatge |
%d / %D |
Decoracions amb parèntesis / sense |
%p / %P |
Hashos abreujats / complets dels pares |
%G? |
Estat de la signatura GPG (G bona, N sense signar...) |
%n / %% |
Salt de línia / signe de percentatge literal |
I per no repetir el format cada vegada, es pot desar amb nom a la configuració:
git config --global pretty.taula '%C(auto)%h %C(blue)%<(16,trunc)%an%C(reset) %C(green)%<(12)%ar%C(reset) %C(auto)%d%C(reset) %s'
git log --pretty=taula
git shortlog: qui ha aportat què
git shortlog: qui ha aportat quègit shortlog agrupa els commits per autor. Sense arguments, llista els missatges agrupats; amb -s (summary) i -n (numbered), dona el recompte ordenat:
Opcions útils:
| Opció | Efecte |
|---|---|
-s |
Només el recompte, sense els missatges |
-n |
Ordena per nombre de commits, no alfabèticament |
-e |
Mostra el correu al costat del nom |
--no-merges |
Exclou les fusions: gairebé sempre és el que vols |
-c |
Agrupa per committer en lloc de per autor |
--since / --until |
Acota el període |
# Aportacions reals dels últims tres mesos
git shortlog -sn --no-merges --since=3.months
# Qui ha tocat app.js
git shortlog -sn --no-merges -- app.js
# Notes de versió agrupades per persona
git shortlog --no-merges v1.0.0..HEADAna Ferrer (12):
Elimina els caracters invisibles en normalitzar el text de les tasques
Afegeix el comptador de tasques pendents
...
Bruno Salas (9):
Delega els esdeveniments del llistat al contenidor
...Aquesta última sortida és, literalment, un esborrany de notes de versió.
Dos advertiments importants, perquè shortlog es malinterpreta amb facilitat:
- El nombre de commits no mesura la productivitat. Un commit pot ser una coma o una funcionalitat sencera. Com a mètrica de rendiment és pitjor que inútil: és perversa, perquè premia trossejar artificialment.
- Una mateixa persona pot aparèixer diverses vegades si ha fet servir noms o correus diferents (portàtil personal, servidor, plataforma web). S'unifica amb un fitxer
.mailmapa l'arrel del repositori:
# .mailmap Ana Ferrer <[email protected]> <[email protected]> Bruno Salas <[email protected]> <[email protected]> Carla Vidal <[email protected]>
Git l'aplica automàticament a shortlog, log i blame.
- Component consultes amb el que hem après
Tot l'anterior es combina, i aquí hi ha la potència real. Alguns exemples que resolen preguntes concretes del dia a dia de gestor-tasques:
# Què ha entrat a main des de v1.0.0, sense soroll de fusions,
# amb qui i quan?
git log --no-merges --first-parent v1.0.0..main \
--pretty=format:'%C(auto)%h %C(blue)%<(14,trunc)%an%C(reset) %ad %s' \
--date=short# Quins commits van tocar app.js i esmenten localStorage al diff?
# (pickaxe -S de la lliçó 02-06 + format)
git log -S "localStorage" --oneline -- app.js# El commit culpable que va trobar bisect, en el seu context de graf
git log --graph --oneline --all --ancestry-path 8d4f2a7..main--ancestry-path restringeix el rang als commits que són en el camí entre els dos extrems, que és exactament el que vols per respondre "com va arribar aquell commit fins a main?".
# L'evolució d'una funció concreta (log -L de la lliçó 06-03)
# limitada als últims tres mesos
git log -L :normalitzaText:app.js --since=3.months --oneline# Quantes línies ha canviat cada persona? (amb totes les reserves
# de l'apartat 7 sobre les mètriques)
git log --no-merges --numstat --pretty=format:'%an' | \
awk 'NF==1 {autor=$0} NF==3 {mes[autor]+=$1; menys[autor]+=$2}
END {for (a in mes) printf "%-20s +%-8d -%d\n", a, mes[a], menys[a]}'I una comparació de les tres vistes de l'historial que hem vist, per tenir-les juntes:
| Vull veure... | Ordre |
|---|---|
| La forma completa del projecte | git log --graph --oneline --all |
La història de main a nivell d'integracions |
git log --oneline --first-parent main |
| Només les fites (etiquetes i branques) | git log --graph --oneline --simplify-by-decoration --all |
| La feina real, sense fusions | git log --oneline --no-merges |
| En què s'han separat dues branques | git log --oneline --left-right main...altra |
| Qui ha aportat quant | git shortlog -sn --no-merges |
Arribats aquí, el problema és evident: cap d'aquestes ordres no s'escriu a mà dues vegades.
- Àlies: què són i on viuen
Un àlies de Git és un nom curt per a una ordre llarga. Es defineix amb git config:
git config --global alias.st status
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commitA partir d'aquí, git st és git status. Git resol l'àlies abans d'executar i li passa els arguments extra:
On viuen. Al .gitconfig corresponent al nivell triat (lliçó 01-05), sota la secció [alias]:
# ~/.gitconfig
[user]
name = Carla Vidal
email = [email protected]
[alias]
st = status
co = checkout
br = branch
ci = commitEs poden editar a mà al fitxer —que és més còmode per als àlies llargs— o amb git config. I consultar-los:
git config --get-regexp '^alias\.' # llistar-los tots
git config --get alias.lg # veure'n un de concret
git config --global --unset alias.co # esborrar-ne unEls tres nivells funcionen igual que per a la resta de la configuració: --system, --global i local. Els àlies personals van a --global; un àlies específic d'un projecte, al local. I recorda que la configuració no es clona: els àlies són teus, no del repositori.
Dues regles que eviten sorpreses:
- Un àlies no pot sobreescriure una ordre existent. Si defineixes
alias.status, Git ignorarà l'àlies i executarà l'ordre real. És una protecció deliberada. - Els àlies es poden encadenar, però amb compte: un àlies que n'invoca un altre funciona, i un àlies recursiu penja Git.
- Àlies amb
!: shell i funcions
!: shell i funcionsUn àlies normal només pot començar per una subordre de Git. Per a tota la resta —encadenar ordres, fer servir canonades, invocar programes externs, col·locar els arguments en un lloc diferent del final— s'anteposa !, que li diu a Git: "això és una ordre de shell, executa-la tal com és".
git config --global alias.neteja '!git branch --merged main | grep -v main | xargs -r git branch -d'Tres coses fonamentals sobre els àlies amb !:
- S'executen des de l'ARREL del repositori
Aquest és el risc principal i la font número u de sorpreses. Git fa cd a l'arrel de l'arbre de treball abans d'executar l'àlies. Així que si ets a ~/projectes/gestor-tasques/components/ i executes un àlies amb !ls, veuràs el contingut de ~/projectes/gestor-tasques/, no el del directori en què ets.
Git dona una variable per pal·liar-ho:
GIT_PREFIX conté la ruta relativa des de l'arrel fins al directori en què eres en invocar l'àlies (buida si ja eres a l'arrel). Qualsevol àlies amb ! que treballi amb rutes relatives ha de tenir-ho en compte:
- Els arguments s'afegeixen al final... llevat que facis servir una funció
Git enganxa els arguments que escriguis al final de l'ordre de l'àlies. Això és un problema quan els necessites al mig:
# MALAMENT: 'git cerca hola' executa 'git log --oneline | grep hola'
git config --global alias.cerca '!git log --oneline | grep'En aquest cas funciona per casualitat. Però si l'argument va al mig, no:
# MALAMENT: 'git des main' executa 'git log --oneline ..HEAD main'
git config --global alias.des '!git log --oneline ..HEAD'La solució canònica és definir una funció de shell i cridar-la:
El patró '!f() { ...; }; f' es llegeix així: defineix una funció anomenada f, i tot seguit invoca-la. Els arguments que Git enganxa al final es converteixen en els arguments d'f. És l'idioma estàndard i el veuràs a tots els .gitconfig del món.
Fes servir "$@" per passar tots els arguments i "$1", "$2" per a posicions concretes. I posa-hi cometes sempre: sense cometes, un nom de branca amb caràcters estranys trenca l'àlies.
- S'executen amb
sh, no amb bash
sh, no amb bashGit fa servir /bin/sh. En molts sistemes això és dash, no bash, i les extensions de bash ([[ ]], arrays, mapfile) no funcionen. Escriu àlies en shell POSIX o invoca explícitament l'intèrpret:
Per a qualsevol cosa que passi de tres línies, la resposta correcta no és un àlies sinó un script al PATH. Git té una convenció preciosa per a això: qualsevol executable anomenat git-<alguna-cosa> que sigui al PATH es pot invocar com git <alguna-cosa>:
# ~/bin/git-resum (amb chmod +x)
#!/usr/bin/env bash
set -euo pipefail
echo "=== Branca actual ==="
git branch --show-current
echo
echo "=== Últims 10 commits ==="
git log --oneline -10
echo
echo "=== Estat ==="
git status --shortSense configurar res. És la via correcta per a la lògica complexa, i de passada l'script es pot versionar i compartir.
- Un joc d'àlies per al dia a dia
Aquests són els que de debò es guanyen el lloc. Començant pel clàssic absolut:
git config --global alias.lg "log --graph --abbrev-commit --decorate --all --pretty=format:'%C(auto)%h%C(reset) -%C(auto)%d%C(reset) %s %C(green)(%ar)%C(reset) %C(blue)<%an>%C(reset)'"* c8f2a1e - (HEAD -> main, origin/main) Fusiona funcionalitat/filtre-estat (fa 2 dies) <Ana Ferrer> |\ | * 4e8b2c9 - (funcionalitat/filtre-estat) Afegeix el filtre per estat (fa 3 dies) <Bruno Salas> | * 2c6e9a4 - Extreu els estils del llistat a estils.css (fa 4 dies) <Carla Vidal> |/ * 8d4f2a7 - Delega els esdeveniments del llistat al contenidor (fa 6 set.) <Bruno Salas> * b7e2c4a - (tag: v1.0.0) Normalitza el text de les tasques (fa 5 mesos) <Carla Vidal>
El lg és probablement l'àlies més copiat de la història de Git, i amb raó: substitueix una ordre de 180 caràcters que ningú recorda.
La taula completa del joc recomanat:
| Àlies | Definició | Per a què |
|---|---|---|
st |
status --short --branch |
Estat compacte amb la branca i el seguiment |
lg |
El de dalt | El graf complet, acolorit |
lgd |
log --graph --oneline --decorate |
Igual però només la branca actual |
ultim |
log -1 HEAD --stat |
L'últim commit amb els seus fitxers |
linia |
log --oneline -20 |
Els últims 20, sense més |
fites |
log --graph --oneline --simplify-by-decoration --all |
L'esquelet del projecte |
integrat |
log --oneline --first-parent |
La línia principal de main |
qui |
shortlog -sn --no-merges |
Aportacions per persona |
culpa |
blame -w -C --date=short |
El blame de la lliçó 06-03, ben configurat |
historia |
!f() { git log -L :"$1":"$2"; }; f |
git historia normalitzaText app.js |
pendent |
!git log --oneline @{u}..HEAD |
El que tinc sense enviar |
entrant |
!git fetch -q && git log --oneline HEAD..@{u} |
El que hi ha al remot sense portar |
divergencia |
!f() { git log --oneline --left-right "$1"...HEAD; }; f |
En què m'he separat d'una branca |
wip |
!git add -A && git commit -m 'WIP: feina en curs' --no-verify |
Desament ràpid abans de canviar de context |
desfes |
reset HEAD~1 --mixed |
Desfà l'últim commit conservant els canvis |
esmena |
commit --amend --no-edit |
Afegeix el que és preparat a l'últim commit |
neteja |
!git branch --merged main | grep -vE '^\*|main' | xargs -r git branch -d |
Esborra les branques ja fusionades |
branques |
branch -vv --sort=-committerdate |
Branques per activitat recent, amb seguiment |
desa |
stash list --pretty=format:'%C(yellow)%gd%C(reset) %s' |
La pila de stash, llegible |
etiquetes |
tag -l --sort=-v:refname --format='%(refname:short) %(creatordate:short) %(subject)' |
Etiquetes per versió descendent |
alias |
!git config --get-regexp '^alias\.' | sed 's/^alias\.//' |
Recordar quins àlies tinc |
Aquest últim és més útil del que sembla: al cap de sis mesos ningú recorda tots els seus àlies.
Per instal·lar-los de cop, és més còmode editar el fitxer que llançar vint git config:
[alias]
st = status --short --branch
lg = log --graph --abbrev-commit --decorate --all --pretty=format:'%C(auto)%h%C(reset) -%C(auto)%d%C(reset) %s %C(green)(%ar)%C(reset) %C(blue)<%an>%C(reset)'
fites = log --graph --oneline --simplify-by-decoration --all
integrat = log --oneline --first-parent
qui = shortlog -sn --no-merges
culpa = blame -w -C --date=short
historia = "!f() { git log -L :\"$1\":\"$2\"; }; f"
pendent = "!git log --oneline @{u}..HEAD"
divergencia = "!f() { git log --oneline --left-right \"$1\"...HEAD; }; f"
desfes = reset HEAD~1 --mixed
esmena = commit --amend --no-edit
branques = branch -vv --sort=-committerdate
alias = "!git config --get-regexp '^alias\\.' | sed 's/^alias\\.//'"Fixa't en l'escapament: al fitxer, els valors amb cometes o barres invertides van entre cometes dobles i amb les barres duplicades. És la part més molesta dels àlies amb !, i la raó per la qual convé provar-los just després d'escriure'ls.
I això connecta amb els mòduls anteriors: els àlies no són només per a log. pendent fa servir @{u} de la lliçó 04-06; esmena és el --amend de la 02-04; neteja és la gestió de branques de la 03-06; culpa és el blame de la 06-03. Un àlies és la manera de fixar en un gest el que has après a fer bé, per no haver de recordar les set opcions cada vegada.
- Riscos i bones pràctiques amb els àlies
Risc 1: el directori d'execució. Ja vist: els àlies amb ! s'executen des de l'arrel del repositori. Fes servir GIT_PREFIX si l'àlies treballa amb rutes.
Risc 2: àlies destructius amb noms curts. Un git p que faci push --force és un accident esperant a passar. Regla: com més destructiu, més llarg el nom. I en el cas de push --force, fes servir sempre --force-with-lease (lliçó 04-05).
Risc 3: dependència de la teva màquina. Els teus àlies no són al portàtil del company a qui demanes ajuda, ni al servidor. Convé saber l'ordre real que hi ha al darrere de cadascun.
Risc 4: sh no és bash. Les extensions de bash fallen silenciosament o amb errors estranys.
Risc 5: àlies que amaguen efectes. Un git sync que faci fetch + rebase + push va bé fins al dia que hi ha conflictes i no entens en quin punt s'ha quedat el procés.
Bones pràctiques:
- Que el nom digui què fa.
pendentés millor quep. - Versiona el teu
.gitconfig. Molts guarden els seus dotfiles en un repositori propi: els àlies són configuració valuosa i costa refer-los. - Afegeix àlies a poc a poc. Quan notis que escrius el mateix per tercera vegada, allà tens un candidat. Copiar una llista de quaranta àlies d'internet garanteix no fer-ne servir cap.
- Prova l'àlies tot just crear-lo, especialment els que porten
!i cometes. - Per a lògica complexa,
git-<nom>alPATH, no un àlies de tres línies.
Errors Habituals i Consells
Error 1: --graph sense --oneline. El graf amb missatges complets és il·legible. Van sempre junts.
Error 2: oblidar --all i creure que la branca no existeix. git log --graph només mostra la branca actual. Sense --all, no veus les altres.
Error 3: confondre .. amb .... A..B és asimètric ("el que li falta a A"); A...B és simètric ("en què s'han separat"). Amb --left-right l'ambigüitat desapareix.
Error 4: comptar commits amb shortlog sense --no-merges. Qui més integra sembla qui més produeix.
Error 5: fer servir el recompte de commits com a mètrica de rendiment. És una mala mètrica i crea incentius perversos.
Error 6: àlies amb ! que assumeix el directori actual. S'executa des de l'arrel. GIT_PREFIX.
Error 7: arguments al mig d'un àlies sense fer servir una funció. Git els enganxa al final. El patró '!f() { ...; }; f' és la solució.
Error 8: escapament mal fet al .gitconfig. Cometes dobles al voltant del valor i barres invertides duplicades. Prova l'àlies immediatament.
Consell 1: %C(auto) per defecte als teus formats. Acoloreix les decoracions correctament i respecta la configuració i les redireccions.
Consell 2: desa formats amb nom. git config --global pretty.taula '...' i després git log --pretty=taula.
Consell 3: --simplify-by-decoration en arribar a un repositori nou. Deu segons per al mapa complet.
Consell 4: git rev-list --count A...B per al recompte pur. Quan només vols saber quant, no què.
Consell 5: un .mailmap tan bon punt algú aparegui duplicat. És un fitxer de tres línies que arregla shortlog, log i blame alhora.
Exercicis
Exercici 1: llegir la forma d'un historial
Crea un repositori amb aquesta topologia: quatre commits a main, una branca funcionalitat/a amb dos commits fusionada amb --no-ff, una branca funcionalitat/b amb dos commits sense fusionar, i una etiqueta v1.0 al segon commit de main. Després:
- Mostra el graf complet amb
--graph --oneline --decorate --all. - Mostra només la línia principal amb
--first-parent. - Mostra només les fites amb
--simplify-by-decoration --all. - Mostra només les fusions, i després només els commits que no ho són.
- Explica quins commits desapareixen en cada vista i per què.
Exercici 2: comparar dues branques
Sobre el mateix repositori:
- Afegeix dos commits més a
maini dos més afuncionalitat/b. - Fes servir
main..funcionalitat/bifuncionalitat/b..main, i explica la diferència. - Fes servir
main...funcionalitat/bamb--left-righti identifica cada costat. - Obtén el recompte amb
git rev-list --left-right --count. - Crea un format
--prettyamb colors i alineació que mostri hash, autor (14 columnes truncades), data curta i assumpte.
Exercici 3: construir els teus àlies
- Crea els àlies
lg,qui,pendenticulpade la taula de l'apartat 11. - Crea un àlies
historiaque accepti dos arguments (funció i fitxer) i executigit log -L. Prova'l. - Crea un àlies que demostri el problema de
GIT_PREFIX: un que executilssense més, i un altre que ho faci bé. Executa'ls des d'un subdirectori i compara. - Llista tots els teus àlies amb un àlies.
Solucions
Solució 1:
mkdir /tmp/practica-log && cd /tmp/practica-log
git init -b main
for i in 1 2; do echo "main $i" >> main.txt; git add .; git commit -q -m "Commit main $i"; done
git tag v1.0
git switch -qc funcionalitat/a
echo "a1" > a.txt && git add . && git commit -q -m "Funcionalitat A part 1"
echo "a2" >> a.txt && git commit -qam "Funcionalitat A part 2"
git switch -q main
echo "main 3" >> main.txt && git commit -qam "Commit main 3"
git merge -q --no-ff funcionalitat/a -m "Fusiona funcionalitat/a"
echo "main 4" >> main.txt && git commit -qam "Commit main 4"
git switch -qc funcionalitat/b v1.0
echo "b1" > b.txt && git add . && git commit -q -m "Funcionalitat B part 1"
echo "b2" >> b.txt && git commit -qam "Funcionalitat B part 2"
git switch -q main* 9c4e7b2 (HEAD -> main) Commit main 4 * 5f1d8a3 Fusiona funcionalitat/a |\ | * 2a8c6f9 (funcionalitat/a) Funcionalitat A part 2 | * 7d3e1b5 Funcionalitat A part 1 * | 4b9f2c8 Commit main 3 |/ | * 8e5a3d7 (funcionalitat/b) Funcionalitat B part 2 | * 1c7b4f9 Funcionalitat B part 1 |/ * 3a6d9e2 (tag: v1.0) Commit main 2 * 6f2c8b1 Commit main 1
9c4e7b2 Commit main 4 5f1d8a3 Fusiona funcionalitat/a 4b9f2c8 Commit main 3 3a6d9e2 Commit main 2 6f2c8b1 Commit main 1
Han desaparegut 2a8c6f9 i 7d3e1b5: són els commits dins de funcionalitat/a, als quals només s'arriba pel segon pare de la fusió.
* 9c4e7b2 (HEAD -> main) Commit main 4 | * 8e5a3d7 (funcionalitat/b) Funcionalitat B part 2 |/ | * 2a8c6f9 (funcionalitat/a) Funcionalitat A part 2 |/ * 3a6d9e2 (tag: v1.0) Commit main 2
Només els commits amb referència i el mínim per connectar-los: l'esquelet.
9c4e7b2 Commit main 4 2a8c6f9 Funcionalitat A part 2 7d3e1b5 Funcionalitat A part 1 4b9f2c8 Commit main 3 3a6d9e2 Commit main 2 6f2c8b1 Commit main 1
Solució 2:
echo "main 5" >> main.txt && git commit -qam "Commit main 5"
echo "main 6" >> main.txt && git commit -qam "Commit main 6"
git switch -q funcionalitat/b
echo "b3" >> b.txt && git commit -qam "Funcionalitat B part 3"
echo "b4" >> b.txt && git commit -qam "Funcionalitat B part 4"
git switch -q maind4a8f2c Funcionalitat B part 4 b1e6c9f Funcionalitat B part 3 8e5a3d7 Funcionalitat B part 2 1c7b4f9 Funcionalitat B part 1
El que té la branca i no té main: el que entraria en fusionar.
7f3c2e8 Commit main 6 2d9b5a1 Commit main 5 9c4e7b2 Commit main 4 5f1d8a3 Fusiona funcionalitat/a 2a8c6f9 Funcionalitat A part 2 7d3e1b5 Funcionalitat A part 1 4b9f2c8 Commit main 3
El que li falta a la branca de main: el que portaria en rebasar o fusionar main dins seu.
< 7f3c2e8 Commit main 6 < 2d9b5a1 Commit main 5 < 9c4e7b2 Commit main 4 < 5f1d8a3 Fusiona funcionalitat/a < 2a8c6f9 Funcionalitat A part 2 < 7d3e1b5 Funcionalitat A part 1 < 4b9f2c8 Commit main 3 > d4a8f2c Funcionalitat B part 4 > b1e6c9f Funcionalitat B part 3 > 8e5a3d7 Funcionalitat B part 2 > 1c7b4f9 Funcionalitat B part 1
git log --pretty=format:'%C(auto)%h %C(blue)%<(14,trunc)%an%C(reset) %C(green)%ad%C(reset) %s' --date=short -8Solució 3:
git config --global alias.lg "log --graph --abbrev-commit --decorate --all --pretty=format:'%C(auto)%h%C(reset) -%C(auto)%d%C(reset) %s %C(green)(%ar)%C(reset) %C(blue)<%an>%C(reset)'"
git config --global alias.qui "shortlog -sn --no-merges"
git config --global alias.pendent "!git log --oneline @{u}..HEAD"
git config --global alias.culpa "blame -w -C --date=short"
git config --global alias.historia '!f() { git log -L :"$1":"$2"; }; f'
git config --global alias.alias "!git config --get-regexp '^alias\\.' | sed 's/^alias\\.//'"L'àlies amb arguments:
cat > funcions.js <<'FI'
function saluda(nom) {
return "Hola " + nom;
}
FI
git add . && git commit -q -m "Afegeix la funcio de salutacio"
sed -i 's/"Hola "/"Hola! "/' funcions.js
git commit -qam "Afegeix l'exclamacio a la salutacio"
git historia saluda funcions.jsEl problema de GIT_PREFIX:
mkdir -p components && echo "x" > components/boto.js
git add . && git commit -q -m "Afegeix el component boto"
git config --global alias.mal '!ls'
git config --global alias.be '!f() { cd "${GIT_PREFIX:-.}" && ls; }; f'
cd components
git malHa llistat l'arrel del repositori, no el directori en què som.
Amb GIT_PREFIX, l'àlies respecta el directori des del qual es va invocar.
alias !git config --get-regexp '^alias\.' | sed 's/^alias\.//'
be !f() { cd "${GIT_PREFIX:-.}" && ls; }; f
culpa blame -w -C --date=short
historia !f() { git log -L :"$1":"$2"; }; f
lg log --graph --abbrev-commit --decorate --all --pretty=format:...
mal !ls
pendent !git log --oneline @{u}..HEAD
qui shortlog -sn --no-mergesConclusió
Aquesta lliçó ha convertit git log d'un llistat en una eina de consulta, i ha eliminat la fricció de fer-la servir. L'essencial:
--graph --oneline --decorate --allés la vista canònica de la topologia. Els quatre van junts.--first-parentdona la història demaina nivell d'integracions, sense l'interior de les branques fusionades. És el mateix "primer pare" del-m 1dereverti delbisect --first-parent.--simplify-by-decorationredueix l'historial a les seves fites: el primer que cal executar en un repositori desconegut.--merges/--no-mergesseparen integracions de feina real;--no-mergesés gairebé obligatori en qualsevol estadística.A...Bamb--left-rightrespon a "en què s'han separat aquestes dues branques?", amb<i>marcant cada costat.git rev-list --left-right --countdona només el recompte.--pretty=format:amb%C(auto)i els marcadors d'alineació%<(N,trunc)produeix sortides tabulars i acolorides; desa-les amb nom apretty.<nom>.git shortlog -sn --no-mergesresumeix les aportacions, amb.mailmapper unificar identitats — i amb l'advertiment que comptar commits no mesura res útil.- Els àlies viuen a
[alias]del.gitconfigdel nivell que triïs. Els simples són un nom per a una subordre; els que comencen per!són shell, amb tres cauteles: s'executen des de l'arrel (fes servirGIT_PREFIX), els arguments s'enganxen al final (fes servir el patró'!f() { ...; }; f') i corren sotash, nobash. - Per a lògica llarga, un executable
git-<nom>alPATHés millor que un àlies. - I la idea de fons: un àlies fixa en un gest el que has après a fer bé.
culpa,pendent,fitesonetejasón les lliçons anteriors condensades en una paraula.
L'equip ja sap interrogar el seu historial amb comoditat. Però gestor-tasques és a punt de deixar de ser un sol repositori: els botons, els diàlegs i els camps de formulari que l'Ana ha anat extraient s'han convertit en una biblioteca interna, components-ui, que viu a git.exemple.cat/equip/components-ui.git i que altres dos projectes de l'empresa també volen fer servir.
La pregunta és com es compon un projecte a partir de diversos repositoris sense copiar i enganxar codi, i sense perdre la traçabilitat de quina versió de la biblioteca fa servir cada versió de l'aplicació. La resposta nativa de Git són els submòduls, i és la lliçó 06-05: Submòduls de Git.
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ó
