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

  1. El punt de partida: un repositori sense remots
  2. git remote add: registrar el primer
  3. git remote -v: quins remots tinc
  4. git remote show: la radiografia completa
  5. Com queda a .git/config
  6. El refspec, explicat amb calma
  7. Reanomenar, eliminar i canviar l'URL
  8. set-url --push: llegir d'un lloc i escriure en un altre
  9. Treballar amb diversos remots
  10. init davant de clone: dues maneres de tenir un remot

  1. 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:

cd ~/projectes/gestor-tasques
git log --oneline -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
git branch
  correccio/focus-despres-esborrar
  documentacio/actualitzar-notes
* main

I la pregunta del dia:

git remote
(sense sortida)

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:

cat .git/config
[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true

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:

mkdir -p /tmp/servidor
git init --bare /tmp/servidor/gestor-tasques.git
Initialized empty Git repository in /tmp/servidor/gestor-tasques.git/

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.

  1. git remote add: registrar el primer

La sintaxi és tan simple com el concepte:

git remote add <nom> <url>

L'Ana l'executa:

git remote add origin https://git.exemple.cat/equip/gestor-tasques.git
(sense sortida)

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 remote add inexistent https://aquest.servidor.no.existeix/res.git
(sense sortida)

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:

git remote remove inexistent

Regles del nom

  • Ha de ser únic dins del repositori: si ja existeix origin, git remote add origin … falla amb error: 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

git remote add -f origin https://git.exemple.cat/equip/gestor-tasques.git

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.

  1. git remote -v: quins remots tinc

Les dues ordres de consulta bàsiques:

git remote
origin

Només els noms. I la versió útil:

git remote -v
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.

  1. git remote show: la radiografia completa

Aquesta ordre és d'una altra categoria, i convé entendre per què:

git remote show origin

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:

git remote show origin
* 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 darrer fetch.
  • stale: tu tens la referència però ja no existeix al servidor. Algú l'ha esborrada allà. Es neteja amb git remote prune, que veurem a la lliçó 04-04.

Si només vols la informació sense sortir a la xarxa:

git remote show -n origin

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.

  1. Com queda a .git/config

Tornem al fitxer, que és on de debò s'entén el que ha passat:

cat .git/config
[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:

git config remote.origin.url
https://git.exemple.cat/equip/gestor-tasques.git
git config remote.origin.fetch
+refs/heads/*:refs/remotes/origin/*

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.

  1. El refspec, explicat amb calma

fetch = +refs/heads/*:refs/remotes/origin/*

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:

git config remote.origin.fetch '+refs/heads/main:refs/remotes/origin/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í».

  1. Reanomenar, eliminar i canviar l'URL

Tres operacions de manteniment, totes locals, totes instantànies i cap de perillosa.

Reanomenar

git remote rename <vell> <nou>
git remote rename origin central
git remote -v
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/* a refs/remotes/central/*. El teu origin/main passa a dir-se central/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è main continuï apuntant al remot correcte.
git config --get-regexp '^(remote|branch)\.'
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:

git remote rename central origin

Eliminar

git remote remove <nom>        # forma recomanada
git remote rm <nom>            # àlies equivalent

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 -v
origin	[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
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

  1. set-url --push: llegir d'un lloc i escriure en un altre

Una 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 -v
origin	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 push a alguna cosa invàlida perquè cap enviament accidental no prosperi.
# Convertir un remot en estrictament de només lectura
git remote set-url --push origin NO_ENVIAR
git push origin main
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:

git remote set-url --delete --push origin NO_ENVIAR

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 -v

Amb 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.

  1. 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 -v
origin		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:

git fetch --all
git branch -r
  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/pwa

Per 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-nova

Sense 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:

# Portar de tots els remots d'una vegada
git fetch --all
Fetching origin
Fetching personal
Fetching preproduccio

  1. init davant de clone: dues maneres de tenir un remot

Tanquem 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 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 main

Aquest 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:

git remote -v
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ò:

Username for 'https://git.exemple.cat':

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:

git ls-remote origin

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:

  1. Crea un repositori normal amb dos o tres commits i una branca addicional.
  2. Comprova que no té cap remot.
  3. Crea un repositori bare que farà de servidor.
  4. Registra'l com a origin.
  5. Mostra el contingut de .git/config abans i després, assenyalant exactament quines línies hi ha afegit l'ordre.
  6. Verifica amb git ls-remote que 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:

  1. +refs/heads/*:refs/remotes/origin/*
  2. +refs/heads/main:refs/remotes/origin/main
  3. refs/heads/main:refs/remotes/origin/main (sense el +)
  4. +refs/heads/release/*:refs/remotes/origin/release/*
  5. +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:

  1. Un bare equip.git i un altre bare personal.git.
  2. Un repositori de treball amb tots dos registrats com a origin i personal.
  3. Envia un commit diferent a cadascun, de manera que les seves històries divergeixin.
  4. Respon: quins commits té personal que no tingui origin? I a l'inrevés?
  5. Configura el repositori perquè llegeixi d'origin però enviï a personal, 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
# 2. Sense remots
git remote -v
(sense sortida)
# 5a. La configuració abans
cat .git/config
[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true
# 3. El servidor
git init --bare /tmp/ex1/servidor.git

# 4. Registrar-lo
git remote add origin /tmp/ex1/servidor.git
# 5b. La configuració després
cat .git/config
[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ó.

# 6. Comprovar que respon
git ls-remote origin
(sense sortida, i codi de sortida 0)
echo $?
0

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:

git ls-remote /tmp/ex1/no-existeix.git
fatal: '/tmp/ex1/no-existeix.git' does not appear to be a git repository

Solució 2:

  1. +refs/heads/*:refs/remotes/origin/* — «Porta totes les branques del remot i desa-les sota refs/remotes/origin/, forçant l'actualització encara que no sigui un avanç net.» És el comportament per defecte de qualsevol clon.

  2. +refs/heads/main:refs/remotes/origin/main — «Porta només la branca main i desa-la com a origin/main.» La resta de branques del servidor s'ignoren completament; ni tan sols apareixeran a git branch -r. Equival a --single-branch.

  3. refs/heads/main:refs/remotes/origin/main — El mateix, però sense el +: si al servidor es reescrivís la història de main, el fetch fallaria 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.

  4. +refs/heads/release/*:refs/remotes/origin/release/* — «Porta només les branques el nom de les quals comenci per release/ i conserva-les amb la mateixa estructura sota origin/release/.» Útil en repositoris enormes on només t'interessen les branques de publicació.

  5. +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 -r
  origin/release/1.0
  origin/release/1.1

origin/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 -v
origin	/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 --all
# 4. Comparar els dos remots
git log --oneline origin/main..personal/main
9c4e2b7 Prova una idea solta
git log --oneline personal/main..origin/main
5a1d8f3 Afegeix l'esquelet de la lògica de tasques

Cadascun té un commit que l'altre no té: han divergit des del commit inicial comú. Es pot veure de cop:

git rev-list --left-right --count origin/main...personal/main
1	1

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 -v
origin	/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
5a1d8f3 Afegeix l'esquelet de la lògica de tasques
# …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 main
e7b3f2a Comprovació de destinació d'enviament
# I el repositori de l'equip NO l'ha rebut
git --git-dir=/tmp/ex3/equip.git log --oneline -1 main
5a1d8f3 Afegeix l'esquelet de la lògica de tasques

Confirmat: 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 amb fetch, push o git ls-remote.
  • git remote -v llista 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ó de pull i push.
  • Tot viu a .git/config, en una secció [remote "origin"] amb dues claus: url i fetch.
  • El refspec +refs/heads/*:refs/remotes/origin/* diu: «porta totes les branques del remot i desa-les sota refs/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 un fetch et 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 --push permet 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.
  • init davant de clone: el primer exigeix registrar el remot a mà; el segon l'hereta. Tots dos arriben al mateix lloc, perquè clone és init + 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

Mòdul 2: Operacions Bàsiques de Git

Mòdul 3: Branques i Fusió

Mòdul 4: Treballant amb Repositoris Remots

Mòdul 5: Operacions Avançades de Git

Mòdul 6: Eines i Tècniques de Git

Mòdul 7: Estratègies de Col·laboració i Flux de Treball

Mòdul 8: Bones Pràctiques i Consells de Git

Mòdul 9: Resolució de Problemes i Depuració

Mòdul 10: Git al Món Real

© Copyright 2026. Tots els drets reservats