A la lliçó anterior va quedar clar què és un remot: un nom curt per a un URL. Ara toca crear-lo. I toca fer-ho amb el cas que portem tres mòduls ajornant: l'Ana publicarà gestor-tasques.
Fins avui, el projecte ha viscut íntegrament al seu portàtil Ubuntu. Té un historial respectable —quatre commits de base, dues fusions, un squash, un conflicte resolt— i main és a c2a8f1e. A la lliçó 02-02 vam veure en Bruno clonar el projecte i vam comprovar que el clon deixava registrat un remot anomenat origin sense que ningú l'hi demanés. Aquella lliçó explicava la meitat del camí: com s'hereta un remot. Aquesta n'explica l'altra meitat: com se'n crea un des de zero, que és el que l'Ana necessita ara perquè el seu repositori va néixer amb git init i no ha parlat amb ningú en tota la seva vida.
A més, obrirem .git/config i esmicolarem la línia fetch = +refs/heads/*:refs/remotes/origin/*. Es diu refspec, apareix a tots els repositoris del món i gairebé ningú sap què diu. Entendre-la és el que converteix fetch i push d'ordres màgiques en ordres predictibles.
Contingut
- El punt de partida: un repositori sense remots
git remote add: registrar el primergit remote -v: quins remots tincgit remote show: la radiografia completa- Com queda a
.git/config - El refspec, explicat amb calma
- Reanomenar, eliminar i canviar l'URL
set-url --push: llegir d'un lloc i escriure en un altre- Treballar amb diversos remots
initdavant declone: dues maneres de tenir un remot
- El punt de partida: un repositori sense remots
L'Ana és al seu projecte, tal com el vam deixar al final del mòdul 3:
c2a8f1e (HEAD -> main) Fusiona el missatge de llista buida 6d3f8b2 Mostra un missatge quan el llistat és buit 3b9e7d1 Afegeix l'exportació del llistat de tasques a CSV
I la pregunta del dia:
Silenci. No hi ha cap remot configurat, que és exactament el que cal esperar en un repositori creat amb git init. Ho podem confirmar mirant el disc:
Quatre línies de configuració bàsica i cap secció [remote …]. Comparat amb el .git/config d'en Bruno que vam veure a la lliçó 02-02, aquí falten tant la secció del remot com la de seguiment de la branca.
Abans de continuar, algú ha d'haver creat el repositori de destinació. En un cas real, l'Ana entraria a la interfície web de GitHub, GitLab o el Gitea de l'empresa i premeria «Nou repositori», i n'obtindria un URL. Per practicar sense dependre de res, muntem l'equivalent exacte amb el que vam aprendre a la lliçó anterior:
Important: el repositori de destinació està buit. Ni un commit, ni una branca. Això és el normal i el desitjable: publicar un projecte consisteix a abocar la teva història en un repositori acabat de crear.
Al relat farem servir l'URL real de l'equip, https://git.exemple.cat/equip/gestor-tasques.git; a les ordres que puguis reproduir, la ruta local. Són intercanviables: per a Git, totes dues són URL vàlids.
git remote add: registrar el primer
git remote add: registrar el primerLa sintaxi és tan simple com el concepte:
L'Ana l'executa:
Silenci absolut, que a Git significa èxit. I amb raó: no ha passat res més que escriure tres línies en un fitxer de text. No s'ha connectat enlloc, no ha comprovat que l'URL existeixi, no ha descarregat res i no ha demanat credencials.
Comprovem-ho de la manera més contundent possible:
Git ho accepta encantat. git remote add és una operació purament local i fora de línia. Només quan executis fetch o push contra aquest nom descobrirà Git que l'URL no porta enlloc. Aquest detall explica molts desconcerts: la gent es pensa que «el remot està bé perquè git remote add no ha donat error», quan aquesta ordre no dona mai error per un URL dolent.
Netegem l'experiment:
Regles del nom
- Ha de ser únic dins del repositori: si ja existeix
origin,git remote add origin …falla amberror: remote origin already exists. - Admet lletres, xifres, guions i guions baixos. Evita espais, barres i accents.
- No hi ha cap paraula reservada.
originés convenció pura, com vam veure a 04-01.
L'opció -f
Amb -f (fetch), Git registra el remot i tot seguit executa un git fetch contra ell. És una drecera còmoda quan afegeixes un remot que ja té contingut i te'l vols endur immediatament. En el cas de l'Ana no aporta res, perquè el repositori de destinació està buit.
git remote -v: quins remots tinc
git remote -v: quins remots tincLes dues ordres de consulta bàsiques:
Només els noms. I la versió útil:
origin https://git.exemple.cat/equip/gestor-tasques.git (fetch) origin https://git.exemple.cat/equip/gestor-tasques.git (push)
Per què apareix dues vegades el mateix remot? Perquè Git desa per separat l'URL de lectura (fetch) i el d'escriptura (push). Normalment són idèntics i per això la línia es repeteix, però poden ser diferents: ho veurem a l'apartat 8, i allà la sortida de -v deixa de ser redundant i passa a ser informativa.
git remote -v hauria de ser el teu primer reflex en arribar a un repositori aliè, juntament amb git status i git log --oneline -5. En tres segons et diu amb qui parla aquell repositori.
git remote show: la radiografia completa
git remote show: la radiografia completaAquesta ordre és d'una altra categoria, i convé entendre per què:
Aquesta sí que es connecta a la xarxa. A diferència de git remote -v, que només llegeix el teu .git/config, git remote show consulta el servidor per preguntar-li quines branques té ara mateix. Si no hi ha connexió o les credencials fallen, aquesta ordre falla.
Com que el repositori de l'Ana encara està buit, la sortida és escarransida. Vegem en canvi la que obté en Bruno més endavant, quan ja hi ha contingut, perquè és la que ensenya alguna cosa:
* remote origin
Fetch URL: https://git.exemple.cat/equip/gestor-tasques.git
Push URL: https://git.exemple.cat/equip/gestor-tasques.git
HEAD branch: main
Remote branches:
correccio/focus-despres-esborrar tracked
documentacio/actualitzar-notes tracked
main tracked
experiment/pwa new (next fetch will store in remotes/origin)
funcionalitat/antiga stale (use 'git remote prune' to remove)
Local branches configured for 'git pull':
main merges with remote main
Local refs configured for 'git push':
main pushes to main (up to date)Val la pena llegir-la línia per línia, perquè conté cinc informacions diferents:
| Secció | Què significa |
|---|---|
| Fetch URL / Push URL | Els dos URL configurats. Si difereixen, aquí es veu |
| HEAD branch | La branca per defecte del servidor. És el que determina en quina branca et deixa un git clone |
| Remote branches | Les branques que existeixen ara mateix al servidor, amb el seu estat |
| Local branches configured for 'git pull' | Quina branca local s'integra amb quina de remota (lliçó 04-06) |
| Local refs configured for 'git push' | Quina branca local va on en enviar, i si està al dia |
Els tres estats possibles d'una branca remota mereixen explicació:
tracked: existeix al servidor i tu en tens la referència local. Situació normal.new: existeix al servidor però tu encara no la tens. Algú l'ha creada des del teu darrerfetch.stale: tu tens la referència però ja no existeix al servidor. Algú l'ha esborrada allà. Es neteja ambgit remote prune, que veurem a la lliçó 04-04.
Si només vols la informació sense sortir a la xarxa:
Amb -n (no query), Git mostra únicament el que sap per la seva configuració local. És ràpid i funciona sense connexió, però la llista de branques remotes serà la de la teva darrera sincronització, no la real.
- Com queda a
.git/config
.git/configTornem al fitxer, que és on de debò s'entén el que ha passat:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[remote "origin"]
url = https://git.exemple.cat/equip/gestor-tasques.git
fetch = +refs/heads/*:refs/remotes/origin/*Això és tot el que ha fet git remote add. Una secció nova amb dues claus. La pots editar a mà amb qualsevol editor i funcionarà igual; les ordres git remote no són més que una interfície còmoda per escriure en aquest fitxer.
Les mateixes claus es poden consultar i modificar amb git config, perquè són configuració normal i corrent:
Recorda de la lliçó 01-05 que aquestes claus són al nivell local (.git/config), el més específic dels tres. Un remot pertany a un repositori concret i no tindria cap sentit configurar-lo a --global.
I ara, aquesta segona línia.
- El refspec, explicat amb calma
Aquesta línia apareix a tots els repositoris Git del planeta i gairebé ningú la sabria explicar. És una llàstima, perquè és senzilla i perquè entendre-la il·lumina tot el mòdul. Es diu refspec: una especificació de com es tradueixen les referències de l'altre repositori a referències del teu.
L'estructura
Un refspec té tres parts:
+refs/heads/*:refs/remotes/origin/* │└─────────┬┘ └────────────┬─────┘ │ ORIGEN DESTINACIÓ │ (allà, al seu (aquí, al meu │ repositori repositori) │ remot) │ └── modificador opcional
| Part | Valor | Significat |
|---|---|---|
+ |
Modificador | Permet actualitzacions no fast-forward: escriu el valor nou encara que no sigui un avanç net |
refs/heads/* |
Origen | Totes les branques del repositori remot |
: |
Separador | «es tradueix a» |
refs/remotes/origin/* |
Destinació | Es desen aquí, sota l'espai de noms del remot |
Llegida en català pla:
«Agafa totes les branques del remot i desa-les al meu disc sota
refs/remotes/origin/, sobreescrivint el que hi hagi.»
Exemples concrets de la traducció que fa l'asterisc:
| Branca al servidor | Es desa al teu disc com | Nom curt que fas servir |
|---|---|---|
refs/heads/main |
refs/remotes/origin/main |
origin/main |
refs/heads/documentacio/actualitzar-notes |
refs/remotes/origin/documentacio/actualitzar-notes |
origin/documentacio/actualitzar-notes |
refs/heads/correccio/focus-despres-esborrar |
refs/remotes/origin/correccio/focus-despres-esborrar |
origin/correccio/focus-despres-esborrar |
Aquí hi ha l'origen de la barra d'origin/main, aquella pregunta que va quedar oberta al final del mòdul 3. No és un separador arbitrari ni una sintaxi especial: és la ruta real del fitxer dins de .git/refs/. origin/main es diu així perquè és a refs/remotes/origin/main.
flowchart LR
subgraph REMOT["Servidor: refs/heads/"]
R1["main"]
R2["documentacio/<br/>actualitzar-notes"]
R3["correccio/<br/>focus-despres-esborrar"]
end
subgraph LOCAL["El teu disc: refs/remotes/origin/"]
L1["origin/main"]
L2["origin/documentacio/<br/>actualitzar-notes"]
L3["origin/correccio/<br/>focus-despres-esborrar"]
end
R1 -->|refspec| L1
R2 -->|refspec| L2
R3 -->|refspec| L3
Per què existeix aquesta traducció
Podria semblar un rodeig innecessari. No seria més simple que les branques del servidor es copiessin tal qual a les teves branques locals?
No, i per una raó fonamental: serien les mateixes referències i es trepitjarien. Si refs/heads/main del servidor es copiés damunt del teu refs/heads/main, cada fetch destruiria la teva feina local no enviada. L'espai de noms separat és el que permet que convisquin tres coses alhora:
- El teu
main, que tu controles i on confirmes. - La teva foto del seu
main(origin/main), que Git actualitza. - El seu
main, que és en una altra màquina.
I és també el que fa possible que Git et digui «vas 2 commits per davant»: té les dues referències per separat i les pot comparar.
El + del principi
El modificador + significa «actualitza aquesta referència encara que el canvi no sigui un fast-forward».
Recorda del mòdul 3 que un fast-forward és un avanç net: el valor nou és descendent de l'anterior. Sense el +, Git es negaria a actualitzar origin/main si al servidor algú hagués reescrit la història (amb un push --force, per exemple), perquè el commit nou no seria descendent del que tu tenies registrat.
Per a les referències remotes volem el +: la seva feina és reflectir fidelment l'estat del servidor, sigui quin sigui. Si el servidor ha canviat de manera estranya, la teva foto ha de mostrar aquest estat estrany; ja decidiràs tu què fer-ne, de les teves branques locals. La protecció contra reescriptures s'aplica en l'altre sentit, en enviar, i és el tema de la lliçó 04-05.
Refspecs a mida
El refspec per defecte porta totes les branques, però es pot afinar. Si un repositori té centenars de branques i només t'interessa main:
Sense comodí: només aquella branca. És, de fet, el que fa per dins git clone --single-branch, esmentat a la lliçó 02-02.
Es poden acumular diverses línies per portar diversos grups:
git config --add remote.origin.fetch '+refs/heads/main:refs/remotes/origin/main'
git config --add remote.origin.fetch '+refs/heads/release/*:refs/remotes/origin/release/*'[remote "origin"]
url = https://git.exemple.cat/equip/gestor-tasques.git
fetch = +refs/heads/main:refs/remotes/origin/main
fetch = +refs/heads/release/*:refs/remotes/origin/release/*Ara git fetch origin portarà main i totes les branques de publicació, i ignorarà la resta. En un repositori corporatiu amb set-centes branques obertes, això és la diferència entre un fetch de dos segons i un de trenta.
El mateix concepte reapareix en l'enviament. Quan a la lliçó 04-05 escriguis git push origin main:main, estaràs donant un refspec a mà, amb la mateixa sintaxi origen:destinació. La coherència és total: els dos costats de : signifiquen sempre el mateix, «d'aquí» i «cap aquí».
- Reanomenar, eliminar i canviar l'URL
Tres operacions de manteniment, totes locals, totes instantànies i cap de perillosa.
Reanomenar
central https://git.exemple.cat/equip/gestor-tasques.git (fetch) central https://git.exemple.cat/equip/gestor-tasques.git (push)
I això no és només un canvi d'etiqueta. Reanomenar un remot obliga Git a reescriure tot el que depenia del nom:
- Les referències remotes es mouen de
refs/remotes/origin/*arefs/remotes/central/*. El teuorigin/mainpassa a dir-secentral/main. - El refspec s'actualitza per apuntar a l'espai de noms nou.
- La configuració de seguiment de les branques (
branch.main.remote) es reescriu perquèmaincontinuï apuntant al remot correcte.
remote.central.url https://git.exemple.cat/equip/gestor-tasques.git remote.central.fetch +refs/heads/*:refs/remotes/central/* branch.main.remote central branch.main.merge refs/heads/main
Tot coherent. Git ha fet la feina completa. Tornem al nom convencional:
Eliminar
Eliminar un remot esborra la seva secció de .git/config, totes les seves referències remotes (refs/remotes/<nom>/*) i la configuració de seguiment de les branques que el feien servir.
El que no esborra: ni un sol commit. Els objectes continuen a .git/objects/. Si elimines un remot per error, n'hi ha prou de tornar-lo a afegir i fer fetch; l'únic que hi perds és temps.
Canviar l'URL
Aquest és el cas més freqüent a la vida real: l'empresa migra de servidor, o passes d'HTTPS a SSH després de configurar les teves claus.
git remote set-url origin [email protected]:equip/gestor-tasques.git
git remote -vorigin [email protected]:equip/gestor-tasques.git (fetch) origin [email protected]:equip/gestor-tasques.git (push)
Mateix repositori, mateix historial, mateixes referències: només canvia el camí per arribar-hi. Res més no en resulta afectat, perquè la identitat dels commits és al seu hash, no a l'URL des del qual van arribar.
Aquí es fa tangible aquella idea de la lliçó 04-01: el repositori «oficial» ho és per convenció. Migrar un equip sencer a un altre servidor és un set-url per persona.
| Ordre | Què toca | Requereix xarxa |
|---|---|---|
git remote add |
Crea la secció a .git/config |
No |
git remote -v |
Només llegeix .git/config |
No |
git remote show <n> |
Llegeix la config i pregunta al servidor | Sí |
git remote rename |
Config + referències remotes + seguiment | No |
git remote remove |
Esborra config + referències remotes + seguiment | No |
git remote set-url |
Només l'URL | No |
git remote prune <n> |
Esborra referències de branques ja inexistents | Sí |
set-url --push: llegir d'un lloc i escriure en un altre
set-url --push: llegir d'un lloc i escriure en un altreUna volta de rosca que explica per què git remote -v distingeix (fetch) de (push):
git remote set-url --push origin [email protected]:equip/gestor-tasques.git
git remote -vorigin https://git.exemple.cat/equip/gestor-tasques.git (fetch) origin [email protected]:equip/gestor-tasques.git (push)
Ara l'Ana llegeix per HTTPS i escriu per SSH. Els dos accessos van al mateix repositori, però per camins diferents.
Per a què serveix això a la pràctica?
- Llegir sense credencials, escriure amb elles. Un repositori públic es clona per HTTPS sense autenticar-se; per enviar cal identitat, i SSH la resol sense teclejar res (lliçó 04-03).
- Tallafocs corporatius. En algunes xarxes només està obert el 443 d'HTTPS per sortir, però l'enviament passa per un altre camí.
- Miralls de només lectura. Llegir d'una rèplica ràpida i propera, escriure sempre a l'original.
- Impedir enviaments per error. Existeix un truc conegut: apuntar l'URL de
pusha alguna cosa invàlida perquè cap enviament accidental no prosperi.
fatal: 'NO_ENVIAR' does not appear to be a git repository fatal: Could not read from remote repository.
Una bona assegurança quan clones un projecte aliè només per llegir-lo.
Per desfer qualsevol d'aquests ajustos:
I les opcions relacionades, per completar el quadre:
# Afegir un URL addicional (envia als dos: útil per a miralls)
git remote set-url --add --push origin [email protected]:equip/gestor-tasques.git
# Veure tots els URL de tots els remots, incloses les múltiples
git remote -vAmb dos URL d'enviament, un sol git push envia als dos llocs. És la manera més simple de mantenir un mirall sincronitzat sense eines externes.
- Treballar amb diversos remots
Un repositori pot tenir tants remots com vulguis, i no és cap raresa: és el normal tan bon punt un projecte creix una mica.
Suposem que en Bruno manté a més una còpia personal del projecte i que l'equip desplega enviant a un servidor de preproducció:
git remote add personal [email protected]:bruno/gestor-tasques.git
git remote add preproduccio [email protected]:apps/gestor-tasques.git
git remote -vorigin https://git.exemple.cat/equip/gestor-tasques.git (fetch) origin https://git.exemple.cat/equip/gestor-tasques.git (push) personal [email protected]:bruno/gestor-tasques.git (fetch) personal [email protected]:bruno/gestor-tasques.git (push) preproduccio [email protected]:apps/gestor-tasques.git (fetch) preproduccio [email protected]:apps/gestor-tasques.git (push)
Cada remot té el seu propi espai de referències, completament separat:
origin/HEAD -> origin/main origin/main origin/documentacio/actualitzar-notes personal/main personal/experiment/pwa preproduccio/main
Ara origin/main, personal/main i preproduccio/main són tres referències diferents que poden apuntar a tres commits diferents. I les pots comparar com qualsevol altra referència:
# Què té la meva còpia personal que no sigui al repositori de l'equip?
git log --oneline origin/main..personal/main
# Va preproducció per darrere de l'equip?
git log --oneline preproduccio/main..origin/main
# Portar una branca de la meva còpia personal
git switch -c experiment/pwa personal/experiment/pwaPer a què serveix tenir-ne diversos
| Situació | Configuració típica |
|---|---|
| Contribuir a un projecte aliè | origin = la teva còpia personal; upstream = el projecte original (mòdul 7) |
Desplegar per push |
origin = codi; produccio = servidor de desplegament (mòdul 10) |
| Mirall o còpia de seguretat | origin = principal; mirall = rèplica |
| Migració de servidor | origin = el vell; nou = la destinació, mentre dura la transició |
| Intercanvi directe entre companys | origin = servidor; bruno = el seu portàtil, per a feina puntual a quatre mans |
Aquest darrer cas il·lustra el que dèiem a 04-01 sobre la naturalesa distribuïda de Git. Si l'Ana i en Bruno estan asseguts a la mateixa sala treballant junts:
# L'Ana registra el repositori d'en Bruno, accessible per SSH a la xarxa local
git remote add bruno [email protected]:/home/bruno/projectes/gestor-tasques
git fetch bruno
git log --oneline bruno/funcionalitat/idea-novaSense passar pel servidor, sense publicar res. És Git fent exactament allò per a què es va dissenyar.
I una ordre útil quan n'hi ha diversos:
init davant de clone: dues maneres de tenir un remot
init davant de clone: dues maneres de tenir un remotTanquem amb la comparació que dona sentit a tota la lliçó, perquè és la diferència entre el camí de l'Ana i el d'en Bruno.
Repositori amb git init (Ana) |
Repositori amb git clone (Bruno) |
|
|---|---|---|
| Remot en començar | Cap | origin, automàtic |
Cal executar remote add |
Sí | No |
| Referències remotes en començar | Cap | Totes les del servidor |
| Branca local de partida | La que creïs tu | La branca per defecte del servidor |
| Seguiment configurat | No: cal establir-lo | Sí, automàtic |
| Primer enviament | git push -u origin main |
git push a seques |
| Història de partida | La teva | La del servidor |
flowchart TB
Q{"El projecte ja<br/>existeix en un servidor?"}
Q -->|"No: jo el començo"| I["git init<br/>treballar, confirmar<br/><b>git remote add origin URL</b><br/>git push -u origin main"]
Q -->|"Sí: m'hi incorporo"| C["git clone URL<br/><b>origin ja registrat</b><br/>seguiment ja configurat<br/>a treballar"]
I --> F["Repositori amb remot<br/>i seguiment"]
C --> F
Els dos camins arriben al mateix lloc. clone no és més que init + remote add + fetch + switch, empaquetat en una sola ordre, tal com vam desglossar a la lliçó 02-02. Ara tens les peces soltes i les pots muntar a mà:
# Un clon «manual», pas a pas, equivalent a git clone
mkdir gestor-tasques && cd gestor-tasques
git init
git remote add origin https://git.exemple.cat/equip/gestor-tasques.git
git fetch origin
git switch mainAquest darrer git switch main mereix una nota: encara que no existeixi cap branca local anomenada main, Git veu que existeix origin/main, dedueix el que vols i crea la branca local amb el seu seguiment ja configurat. Es diu DWIM (Do What I Mean) i és el tema de la lliçó 04-06.
L'Ana ja està a punt
El repositori de l'Ana ja té el seu remot:
origin https://git.exemple.cat/equip/gestor-tasques.git (fetch) origin https://git.exemple.cat/equip/gestor-tasques.git (push)
Però encara no ha enviat res. I si ho intentés ara mateix contra un servidor real per HTTPS, es trobaria amb això:
Una pregunta que no sap respondre, perquè la seva contrasenya de la interfície web probablement no valgui. Abans d'enviar cal resoldre l'autenticació, i aquest és exactament el tema de la lliçó següent.
Errors Habituals i Consells
Error 1: creure que git remote add valida l'URL. No ho fa: només escriu en un fitxer. Pots registrar https://aixo.no.existeix/res.git sense cap avís. La primera comprovació real arriba amb git fetch, git push o git ls-remote.
Error 2: error: remote origin already exists. Passa en intentar afegir un remot que ja hi és, molt típic en repositoris clonats on origin ja ve posat. Si el que vols és canviar-ne l'adreça, l'ordre és git remote set-url origin <url>, no add.
Error 3: confondre git remote -v amb git remote show. El primer llegeix la teva configuració i funciona sense connexió; el segon pregunta al servidor i pot fallar per xarxa o credencials. Per diagnosticar «quin URL tinc configurat?» fes servir -v; per a «què hi ha ara mateix al servidor?», show.
Error 4: no entendre el refspec i, per tant, no entendre per què origin/main no és una branca. La línia +refs/heads/*:refs/remotes/origin/* diu literalment on es desen les referències del remot: en un espai de noms separat del de les teves branques. Sense aquesta separació, cada fetch et masegaria la feina.
Error 5: provar de fer git push a un remot no bare. Si has muntat tu el servidor amb git init en comptes de git init --bare, l'enviament serà rebutjat amb branch is currently checked out. Recorda la lliçó 04-01: els repositoris que reben han de ser bare.
Error 6: deixar remots morts acumulats. Després d'una migració de servidor, molta gent afegeix el nou i deixa el vell. Mesos després, git fetch --all triga una eternitat esperant el temps d'espera d'un servidor apagat. Neteja amb git remote remove.
Consell 1: comprova un remot acabat d'afegir amb git ls-remote. És la manera més barata de verificar que l'URL, la xarxa i les credencials funcionen, sense descarregar objectes:
Si retorna la llista de referències del servidor, tot està en ordre. Si falla, tens l'error concret abans d'haver-hi invertit res.
Consell 2: fes servir sempre origin per al repositori principal. La convenció està tan assentada que qualsevol documentació, script o company la dona per descomptada. Reserva la creativitat per als remots secundaris.
Consell 3: guarda git remote -v a la teva rutina d'arribada a un repositori. Aquesta ordre i git log --oneline --graph -10 et donen en cinc segons el mapa complet d'on ets.
Exercicis
Exercici 1: publicar un projecte existent
Simula el cas de l'Ana de principi a fi, en local:
- Crea un repositori normal amb dos o tres commits i una branca addicional.
- Comprova que no té cap remot.
- Crea un repositori bare que farà de servidor.
- Registra'l com a
origin. - Mostra el contingut de
.git/configabans i després, assenyalant exactament quines línies hi ha afegit l'ordre. - Verifica amb
git ls-remoteque el remot respon, i explica què retorna estant el servidor buit.
Exercici 2: desxifrar refspecs
Tradueix al català cadascun d'aquests refspecs i digues què faria un git fetch amb cadascun:
+refs/heads/*:refs/remotes/origin/*+refs/heads/main:refs/remotes/origin/mainrefs/heads/main:refs/remotes/origin/main(sense el+)+refs/heads/release/*:refs/remotes/origin/release/*+refs/tags/*:refs/tags/*
Després, configura el número 4 en un repositori de prova i demostra amb ordres que un fetch ja no porta les branques que no comencen per release/.
Exercici 3: dos remots i una comparació
Munta aquesta situació i resol-la amb ordres:
- Un bare
equip.giti un altre barepersonal.git. - Un repositori de treball amb tots dos registrats com a
originipersonal. - Envia un commit diferent a cadascun, de manera que les seves històries divergeixin.
- Respon: quins commits té
personalque no tinguiorigin? I a l'inrevés? - Configura el repositori perquè llegeixi d'
originperò enviï apersonal, i demostra que funciona.
Solucions
Solució 1:
mkdir -p /tmp/ex1 && cd /tmp/ex1
# 1. Repositori amb història
git init -b main projecte
cd projecte
echo "<h1>Gestor de tasques</h1>" > index.html
git add . && git commit -m "Estructura inicial del gestor de tasques"
echo "body { font-family: sans-serif; }" > estils.css
git add . && git commit -m "Afegeix els estils base del llistat"
git switch -c documentacio/actualitzar-notes
echo "# Gestor de tasques" > README.md
git add . && git commit -m "Afegeix el README del projecte"
git switch main# 3. El servidor
git init --bare /tmp/ex1/servidor.git
# 4. Registrar-lo
git remote add origin /tmp/ex1/servidor.git[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[remote "origin"]
url = /tmp/ex1/servidor.git
fetch = +refs/heads/*:refs/remotes/origin/*Les dues línies noves són la secció [remote "origin"] amb el seu url i el seu fetch. Res més. Ni referències, ni objectes, ni connexió.
L'absència de sortida és la resposta correcta: el servidor ha contestat i la seva llista de referències és buida, perquè el repositori bare encara no té ni un commit. L'important és que el codi de sortida sigui 0: significa que Git hi ha arribat i hi ha pogut parlar. Compara-ho amb un URL dolent:
Solució 2:
-
+refs/heads/*:refs/remotes/origin/*— «Porta totes les branques del remot i desa-les sotarefs/remotes/origin/, forçant l'actualització encara que no sigui un avanç net.» És el comportament per defecte de qualsevol clon. -
+refs/heads/main:refs/remotes/origin/main— «Porta només la brancamaini desa-la com aorigin/main.» La resta de branques del servidor s'ignoren completament; ni tan sols apareixeran agit branch -r. Equival a--single-branch. -
refs/heads/main:refs/remotes/origin/main— El mateix, però sense el+: si al servidor es reescrivís la història demain, elfetchfallaria en lloc d'actualitzar la referència, perquè el valor nou no seria descendent de l'anterior. Poques vegades es vol això en una referència remota, la feina de la qual és reflectir el servidor tal qual. -
+refs/heads/release/*:refs/remotes/origin/release/*— «Porta només les branques el nom de les quals comenci perrelease/i conserva-les amb la mateixa estructura sotaorigin/release/.» Útil en repositoris enormes on només t'interessen les branques de publicació. -
+refs/tags/*:refs/tags/*— «Porta totes les etiquetes del remot i desa-les com a etiquetes locals, amb el mateix nom.» Fixa't que aquí no hi ha traducció d'espai de noms: origen i destinació són iguals. Les etiquetes no se separen per remot com les branques, i per això una etiqueta portada és indistingible d'una de creada per tu. Les etiquetes són el tema de la lliçó 05-05.
Comprovació del cas 4:
cd /tmp/ex1/projecte
# Preparar el servidor amb diverses branques
git push origin main
git switch -c release/1.0 && git commit --allow-empty -m "Versió 1.0" && git push origin release/1.0
git switch -c release/1.1 && git commit --allow-empty -m "Versió 1.1" && git push origin release/1.1
git switch main
git push origin documentacio/actualitzar-notes
# Un clon nou amb refspec restringit
cd /tmp/ex1
git clone servidor.git restringit
cd restringit
git config remote.origin.fetch '+refs/heads/release/*:refs/remotes/origin/release/*'
# Esborrar les referències que ja tenia, per partir de zero
git branch -r | grep -v HEAD | sed 's/^ *//' | xargs -r -n1 git branch -rd
git fetch origin
git branch -rorigin/main i origin/documentacio/actualitzar-notes no apareixen. El refspec restringit les ha deixat fora: Git ni tan sols s'ha molestat a portar-les.
Solució 3:
mkdir -p /tmp/ex3 && cd /tmp/ex3
# 1. Els dos servidors
git init --bare equip.git
git init --bare personal.git
# 2. El repositori de treball amb els dos remots
git init -b main feina
cd feina
echo "<h1>Gestor de tasques</h1>" > index.html
git add . && git commit -m "Estructura inicial del gestor de tasques"
git remote add origin /tmp/ex3/equip.git
git remote add personal /tmp/ex3/personal.git
git remote -vorigin /tmp/ex3/equip.git (fetch) origin /tmp/ex3/equip.git (push) personal /tmp/ex3/personal.git (fetch) personal /tmp/ex3/personal.git (push)
# 3. Base comuna i després divergència
git push origin main
git push personal main
echo "console.log('equip');" > app.js
git add . && git commit -m "Afegeix l'esquelet de la lògica de tasques"
git push origin main
git reset --hard HEAD~1
echo "console.log('personal');" > experiment.js
git add . && git commit -m "Prova una idea solta"
git push personal main
git fetch --allCadascun té un commit que l'altre no té: han divergit des del commit inicial comú. Es pot veure de cop:
Un commit exclusiu a cada costat. Aquesta sintaxi de tres punts i la seva lectura són el tema de la lliçó 04-06.
# 5. Llegir d'origin, enviar a personal
git remote set-url --push origin /tmp/ex3/personal.git
git remote -vorigin /tmp/ex3/equip.git (fetch) origin /tmp/ex3/personal.git (push) personal /tmp/ex3/personal.git (fetch) personal /tmp/ex3/personal.git (push)
# Demostració: un fetch d'origin porta el de l'equip…
git fetch origin
git log --oneline -1 origin/main# …però un push a origin escriu a personal
git commit --allow-empty -m "Comprovació de destinació d'enviament"
git push origin main
git --git-dir=/tmp/ex3/personal.git log --oneline -1 mainConfirmat: origin llegeix d'equip.git i escriu a personal.git. És el mecanisme que fan servir els qui clonen un projecte aliè de només lectura i envien els seus canvis a la seva pròpia còpia.
Conclusió
En aquesta lliçó l'Ana ha fet el pas que portava tres mòduls pendent: connectar el seu repositori amb el món. El que hem après:
git remote add <nom> <url>registra un remot, i és una operació purament local: escriu tres línies a.git/config, no valida l'URL i no toca la xarxa. La primera comprovació real arriba ambfetch,pushogit ls-remote.git remote -vllista els remots amb els seus URL de lectura i escriptura, sense connexió.git remote show <nom>sí que consulta el servidor i en dona la radiografia completa: branca per defecte, branques remotes amb el seu estat (tracked,new,stale) i la configuració depullipush.- Tot viu a
.git/config, en una secció[remote "origin"]amb dues claus:urlifetch. - El refspec
+refs/heads/*:refs/remotes/origin/*diu: «porta totes les branques del remot i desa-les sotarefs/remotes/origin/, forçant l'actualització». El+permet actualitzacions no fast-forward; els:separen origen i destinació. D'aquí en surt la barra d'origin/main: és la ruta real del fitxer. I aquesta separació d'espais de noms és el que impedeix que unfetchet masegui les branques locals. - Reanomenar, eliminar i canviar l'URL són operacions locals i inofensives. Reanomenar mou a més les referències remotes i reescriu la configuració de seguiment; eliminar no esborra cap commit.
set-url --pushpermet llegir d'un lloc i escriure en un altre, o bloquejar els enviaments apuntant a un URL invàlid.- Diversos remots conviuen sense problema, cadascun amb el seu espai de referències, i es comparen entre si com qualsevol altre nom de commit.
initdavant declone: el primer exigeix registrar el remot a mà; el segon l'hereta. Tots dos arriben al mateix lloc, perquècloneésinit+remote add+fetch+switch.
El que ve
El remot de l'Ana està configurat, però si intentés enviar ara mateix ensopegaria amb una pregunta incòmoda: Username for 'https://git.exemple.cat':. Registrar un URL no dona permís per escriure-hi.
A la lliçó 04-03: Autenticació amb Repositoris Remots resolem aquest mur. Veurem per què el clone d'un repositori públic no demana res i el push sí, què és un testimoni personal d'accés i per què va substituir la contrasenya, com generar i fer servir una clau SSH amb ssh-keygen -t ed25519, i com cada sistema operatiu desa les credencials per no haver-les de teclejar quaranta vegades al dia. És especialment rellevant per a la Carla, que s'incorporarà des de Windows 11 i necessita deixar això resolt abans d'escriure la seva primera línia de codi.
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ó
