Cerramos el módulo 3 con un problema abierto: todo el trabajo de gestor-tareas vive dentro de un único .git/, el del portátil Ubuntu de Ana. Las ramas de Bruno solo existían en nuestra imaginación, y Carla lleva tres módulos esperando en su Windows 11 a que alguien le diga de dónde descargar el proyecto.

Este módulo cierra ese círculo, y empieza por donde hay que empezar: entendiendo qué es un remoto. Y la respuesta va a decepcionar a quien esperase magia, porque un remoto no es un servidor, ni un servicio, ni una copia sincronizada de tu proyecto. Un remoto es un nombre corto para una URL. Nada más. Todo lo demás —fetch, pull, push, las ramas de seguimiento— se construye encima de esa idea minúscula.

En esta lección no vamos a registrar ningún remoto todavía (eso es la lección siguiente). Vamos a construir el modelo mental correcto: por qué en un sistema distribuido no existe técnicamente un servidor central, qué es un repositorio bare y por qué el del servidor no tiene ficheros con los que trabajar, qué son exactamente esas referencias origin/main que llevan apareciendo desde la lección 02-02, y qué añaden plataformas como GitHub o GitLab sobre el Git que ya conoces.

Contenido

  1. Qué es un remoto: un nombre corto para una URL
  2. Por qué no hay servidor central (técnicamente)
  3. El repositorio de referencia: una convención, no una imposición
  4. Repositorios bare: el servidor no necesita ficheros
  5. Las referencias remotas: .git/refs/remotes/
  6. origin/main es una referencia local de solo lectura
  7. Convenciones de nombre: origin, upstream y los demás
  8. Plataformas de alojamiento: qué añaden sobre Git
  9. El mapa mental completo

  1. Qué es un remoto: un nombre corto para una URL

Empecemos por la definición exacta, porque casi todo el desconcierto posterior nace de no tenerla clara.

Un remoto es una entrada en la configuración de tu repositorio que asocia un nombre corto a una URL (y a unas reglas sobre qué referencias traer de allí).

Eso es literalmente todo. Cuando escribes origin, Git busca en .git/config una sección [remote "origin"], lee la URL que hay dentro y la usa. Es exactamente el mismo mecanismo que un alias de shell o una entrada en /etc/hosts: un nombre cómodo para algo largo e incómodo de teclear.

Compara las dos formas de hacer lo mismo:

# Sin remoto registrado: la URL entera, cada vez
git fetch https://git.ejemplo.es/equipo/gestor-tareas.git main

# Con el remoto registrado: un nombre corto
git fetch origin main

Las dos funcionan. La primera demuestra el punto: Git puede hablar con cualquier repositorio dándole su URL directamente, sin registrar nada. Los remotos existen para no repetir la URL cuarenta veces al día y para poder asociarle configuración adicional.

Lo que un remoto NO es

Es igual de importante saber qué no hay detrás de esa palabra:

Un remoto no es… Por qué la gente lo cree
Una carpeta sincronizada tipo Dropbox Nada se sincroniza solo: los objetos viajan únicamente cuando ejecutas fetch, pull o push
Una conexión permanente Git es completamente asíncrono: trabaja sin red y solo se conecta en esos tres comandos
Una copia idéntica de tu repositorio El remoto tiene sus propias ramas, que pueden ir por delante o por detrás de las tuyas
Un servidor con inteligencia El otro extremo ejecuta Git, igual que tú; no hay lógica de negocio ahí
Algo obligatorio Un repositorio Git es perfectamente funcional sin ningún remoto, como el de Ana durante tres módulos

Ese último punto merece énfasis. Todo lo que has hecho hasta ahora —confirmar, ramificar, fusionar, resolver conflictos, consultar el historial— funciona sin red y sin remotos. Los remotos no añaden capacidades a Git: añaden transporte. Son el mecanismo por el que los objetos de un .git/objects/ acaban en otro .git/objects/.

Las URL que admite un remoto

Un remoto puede apuntar a sitios muy distintos, y no todos están en Internet:

# HTTPS: lo más habitual con plataformas de alojamiento
https://git.ejemplo.es/equipo/gestor-tareas.git

# SSH, forma abreviada tipo scp (la más vista)
[email protected]:equipo/gestor-tareas.git

# SSH, forma de URL completa (equivalente a la anterior)
ssh://[email protected]/equipo/gestor-tareas.git

# Ruta local: una carpeta del propio ordenador o de un disco montado
/home/ana/copias/gestor-tareas.git

# Ruta local en formato file://
file:///home/ana/copias/gestor-tareas.git

# Protocolo git:// (sin autenticación, solo lectura, prácticamente en desuso)
git://git.ejemplo.es/equipo/gestor-tareas.git

Los protocolos ya los viste en la lección 02-02 al hablar de git clone; son exactamente los mismos, porque clone no es más que "crear un repositorio + registrar un remoto + traérselo todo".

Un detalle que conviene interiorizar: una ruta local es una URL perfectamente válida. Puedes tener un remoto que apunte a una carpeta de tu propio disco, y todo el módulo funciona igual. Esto es magnífico para practicar, y lo usaremos en los ejercicios: no necesitas cuenta en ningún sitio ni conexión a Internet para aprender remotos.

  1. Por qué no hay servidor central (técnicamente)

En la lección 01-01 vimos la diferencia entre control de versiones centralizado y distribuido. Ahora toca la consecuencia práctica de aquello, que es más radical de lo que suele parecer.

En Subversion o CVS, el servidor es estructuralmente distinto del cliente: contiene el historial, decide qué números de revisión se asignan y es imprescindible para casi cualquier operación. Sin servidor, no hay control de versiones.

En Git no existe esa distinción. Todos los repositorios son iguales. El repositorio que hay en git.ejemplo.es ejecuta el mismo Git, guarda los mismos objetos con el mismo formato y no tiene ningún privilegio especial sobre el de Ana. Si el servidor ardiera esta tarde, cualquiera de los tres podría montar uno nuevo con su propia copia y el equipo seguiría trabajando en cinco minutos.

flowchart TB
    subgraph ANA["Portátil de Ana · Ubuntu"]
        A1["Repositorio completo<br/>.git/objects + .git/refs<br/>+ directorio de trabajo"]
    end
    subgraph BRUNO["MacBook de Bruno · macOS"]
        B1["Repositorio completo<br/>.git/objects + .git/refs<br/>+ directorio de trabajo"]
    end
    subgraph CARLA["Portátil de Carla · Windows 11"]
        C1["Repositorio completo<br/>.git/objects + .git/refs<br/>+ directorio de trabajo"]
    end
    subgraph SRV["git.ejemplo.es · repositorio bare"]
        S1["Repositorio completo<br/>objects + refs<br/><b>sin</b> directorio de trabajo"]
    end

    A1 <-->|push / fetch| S1
    B1 <-->|push / fetch| S1
    C1 <-->|push / fetch| S1
    A1 <-.->|técnicamente posible:<br/>intercambio directo| B1

Fíjate en la línea discontinua entre Ana y Bruno. No es un adorno: es Git funcionando exactamente como fue diseñado. Bruno puede registrar el portátil de Ana como remoto y traerse sus commits directamente, sin que el servidor intervenga. Linus Torvalds diseñó Git para el kernel de Linux, donde cientos de personas se intercambian parches y ramas sin un repositorio central obligatorio.

Entonces, ¿por qué todo el mundo usa un servidor?

Porque las razones para tener un repositorio de referencia no son técnicas, sino organizativas:

  • Disponibilidad. El portátil de Ana está apagado por las noches, en el avión y cuando se va de vacaciones. Un servidor está encendido siempre.
  • Punto de acuerdo. Con un repositorio de referencia, la pregunta "¿cuál es la versión buena de main?" tiene una respuesta única y aburrida. Sin él, hay tres respuestas y tres opiniones.
  • Copia de seguridad implícita. Si el disco de Bruno muere, su trabajo enviado sigue existiendo.
  • Control de acceso. Alguien tiene que decidir quién puede escribir en main. Eso requiere un sitio donde aplicar esas reglas.
  • Automatización. Las pruebas automáticas, los despliegues y los avisos necesitan un punto fijo al que engancharse (módulo 7).
  • Direccionalidad de red. Ana no puede conectarse al portátil de Carla: está detrás del router de su casa, con IP cambiante y sin puertos abiertos. Un servidor con dirección estable resuelve el problema de que todos puedan llegar a algún sitio común.

Esta última razón es la más subestimada y probablemente la más decisiva en la práctica.

  1. El repositorio de referencia: una convención, no una imposición

De todo lo anterior sale una idea que conviene fijar bien:

El repositorio "oficial" de un proyecto lo es porque el equipo ha acordado que lo sea, no porque Git lo distinga de ninguna manera.

Si mañana el equipo decide que el repositorio bueno pasa a estar en otro servidor, basta con que los tres cambien una línea de configuración —la URL de su remoto origin— y ese otro será el oficial. Git no se entera del cambio y no le importa. Es una migración de cinco segundos por persona, algo impensable en un sistema centralizado.

Esa flexibilidad tiene una cara incómoda: Git no protege nada por sí mismo. No hay un concepto nativo de "esta rama es sagrada" ni de "solo Ana puede escribir aquí". Cualquiera con permiso de escritura puede, en principio, reescribir main en el servidor. Las protecciones que usan los equipos —ramas protegidas, revisiones obligatorias, comprobaciones que deben pasar antes de integrar— las añaden las plataformas de alojamiento encima de Git, no Git.

Lo veremos con detalle en el módulo 7, pero interioriza ya la separación: Git mueve objetos; la plataforma pone las reglas.

  1. Repositorios bare: el servidor no necesita ficheros

En la lección 02-01 dejamos una idea a medias: git init --bare crea un repositorio sin directorio de trabajo, y dijimos que se entendería al hablar de remotos. Ha llegado el momento.

Qué es exactamente

Un repositorio normal tiene esta forma:

gestor-tareas/
├── .git/            ← el repositorio (objetos, referencias, configuración)
│   ├── objects/
│   ├── refs/
│   ├── HEAD
│   └── config
├── index.html       ← el directorio de trabajo
├── estilos.css
├── app.js
└── README.md

Un repositorio bare tiene esta otra:

gestor-tareas.git/
├── objects/         ← lo que en el otro estaba dentro de .git/
├── refs/
├── HEAD
├── config
├── description
└── hooks/

Es el contenido de .git/ promovido a la raíz, y ni un solo fichero del proyecto. Por eso se llama bare, "desnudo": no tiene copia de trabajo con la que vestirse.

Por convención, los directorios de repositorios bare terminan en .gitgestor-tareas.git— para que se vea de un vistazo qué son. De ahí viene, precisamente, el .git final de las URL que llevas viendo todo el curso.

Por qué el repositorio del servidor tiene que ser bare

Imagina por un momento que no lo fuera: que en el servidor hubiera un repositorio normal, con su index.html y su app.js en el disco, y con HEAD apuntando a main.

Ana ejecuta git push y envía tres commits nuevos a main. ¿Qué debería pasar en el servidor?

  • Si Git actualizara la rama main y los ficheros del directorio de trabajo, estaría modificando ficheros de una máquina en la que a lo mejor hay alguien editándolos en ese preciso momento. Podría destruir trabajo sin avisar.
  • Si actualizara la rama pero no los ficheros, el repositorio del servidor quedaría en un estado incoherente: git status allí diría que hay decenas de ficheros modificados y borrados, cuando en realidad nadie ha tocado nada.

Ninguna de las dos opciones es aceptable, así que Git elige una tercera: negarse. Si intentas enviar a la rama que está activa en un repositorio no bare, obtienes esto:

! [remote rejected] main -> main (branch is currently checked out)
error: failed to push some refs to '/home/ana/copias/gestor-tareas'

Con un repositorio bare el problema desaparece de raíz: como no hay directorio de trabajo, no hay nada que pueda quedar incoherente. Recibir un push se reduce a añadir objetos y mover un puntero, que es una operación limpia y segura.

Repositorio normal Repositorio bare
Directorio de trabajo No
Se crea con git init / git clone git init --bare / git clone --bare
Estructura proyecto/.git/… proyecto.git/… (todo en la raíz)
Sirve para Trabajar: editar, confirmar Compartir: recibir y servir
Admite git push a su rama activa No Sí (no tiene rama "activa" en ese sentido)
Se puede editar código en él No hay ficheros que editar
git status Informativo Error: no hay copia de trabajo

Lo que hay al otro lado de GitHub, GitLab o Gitea es siempre un repositorio bare. Cuando clonas, la plataforma sirve los objetos desde uno; cuando envías, los recibe en uno.

Crear uno para practicar

Esto te va a servir en todos los ejercicios del módulo, así que compruébalo ahora:

mkdir -p /tmp/servidor
git init --bare /tmp/servidor/gestor-tareas.git
Initialized empty Git repository in /tmp/servidor/gestor-tareas.git/
ls /tmp/servidor/gestor-tareas.git/
HEAD  branches  config  description  hooks  info  objects  refs

Exactamente lo que esperábamos: el contenido de un .git/, sin ficheros de proyecto. Y su configuración lo declara:

git -C /tmp/servidor/gestor-tareas.git config core.bare
true

Ese git -C <ruta> es un truco cómodo: ejecuta el comando como si estuvieras dentro de ese directorio, sin tener que ir y volver.

Este directorio es un "servidor" perfectamente válido. Puedes enviarle cambios y clonar de él. La única diferencia con GitHub es que está en tu disco y no tiene interfaz web.

  1. Las referencias remotas: .git/refs/remotes/

Aquí está la pieza central de la lección, y probablemente el concepto que más malentendidos genera en todo Git.

En el módulo 3 aprendiste que una rama es un fichero de 41 bytes en .git/refs/heads/ con un hash dentro. Pues bien: existe un segundo directorio hermano, .git/refs/remotes/, con ficheros exactamente iguales.

Miremos el repositorio que Bruno clonó en la lección 02-02:

find .git/refs -type f
.git/refs/heads/main
.git/refs/remotes/origin/main
.git/refs/remotes/origin/HEAD
cat .git/refs/heads/main
cat .git/refs/remotes/origin/main
c5d9b1e8a3f2b7d4c6e9a1f5b8d2c4e7a9f3b6d1
c5d9b1e8a3f2b7d4c6e9a1f5b8d2c4e7a9f3b6d1

Dos ficheros con el mismo hash dentro. El primero es la rama main de Bruno; el segundo es la referencia remota origin/main.

La estructura completa de referencias queda así:

Directorio Contiene Ejemplo de nombre
.git/refs/heads/ Tus ramas locales main, funcionalidad/exportar-csv
.git/refs/remotes/<remoto>/ Referencias remotas origin/main, origin/documentacion/notas
.git/refs/tags/ Etiquetas (lección 05-05) v1.2.0

Y, como viste en la lección 03-01, muchas de estas referencias pueden acabar comprimidas en .git/packed-refs en lugar de existir como ficheros sueltos. Se consultan igual:

git rev-parse origin/main
c5d9b1e8a3f2b7d4c6e9a1f5b8d2c4e7a9f3b6d1

git rev-parse resuelve cualquier nombre de referencia a su hash, esté donde esté guardada. Es la forma robusta de consultarlas y la que debes usar en scripts.

  1. origin/main es una referencia local de solo lectura

Ahora la afirmación importante, la que hay que entender para que el resto del módulo tenga sentido:

origin/main no está en el servidor. Está en tu disco. Es una nota que Git se dejó a sí mismo diciendo "la última vez que hablé con origin, su rama main estaba en este commit".

Piénsalo como una fotografía con fecha, no como una ventana en directo. Y de ahí se derivan tres consecuencias que conviene tener grabadas:

Primera: origin/main puede estar desactualizado, y normalmente lo está. Si Bruno envió tres commits hace diez minutos y tú no has ejecutado ningún fetch, tu origin/main sigue señalando el commit de ayer. Git no tiene forma de saberlo: no hay ninguna conexión abierta ni ningún aviso. Solo se entera cuando le pides que hable con el servidor.

Segunda: solo se actualiza en momentos concretos. Las referencias remotas cambian cuando ejecutas git fetch, git pull (que hace un fetch por dentro), git clone o un git push con éxito. En cualquier otro momento están congeladas.

Tercera: no puedes trabajar directamente sobre ella. Es de solo lectura desde el punto de vista de tu trabajo diario:

git switch origin/main
Note: switching to 'origin/main'.

You are in 'detached HEAD' state...

Git no te cambia a una rama origin/main, porque no es una rama tuya: te deja en HEAD desacoplado sobre ese commit, exactamente el estado que estudiaste en la lección 03-02. Y si confirmas ahí, tus commits quedarán colgando sin ninguna rama que los sostenga.

Del mismo modo, no puedes confirmar sobre ella ni fusionar dentro de ella. Lo que sí puedes —y harás constantemente— es usarla como referencia, igual que cualquier otro nombre de commit:

# ¿Qué tengo yo que el servidor no tenía la última vez que miré?
git log --oneline origin/main..main

# ¿Qué tenía el servidor que yo no tengo?
git log --oneline main..origin/main

# ¿Qué diferencias de contenido hay?
git diff origin/main main

# Crear una rama a partir de ella
git switch -c correccion/urgente origin/main

# Fusionar su contenido en mi rama actual
git merge origin/main

Toda la sintaxis de rangos y referencias del módulo 2 (.., ~, ^) funciona con ellas sin ninguna diferencia, porque son referencias normales y corrientes: solo cambia el directorio donde viven y quién las actualiza.

El diagrama que hay que recordar

flowchart LR
    subgraph LOCAL["Repositorio local de Bruno"]
        M["main<br/>(rama local)<br/>refs/heads/main"]
        OM["origin/main<br/>(referencia remota)<br/>refs/remotes/origin/main"]
    end
    subgraph SERVER["git.ejemplo.es (bare)"]
        SM["main<br/>(la rama de verdad)<br/>refs/heads/main"]
    end

    M -->|"git merge origin/main<br/>(integración local)"| OM
    OM -->|"git fetch<br/>(actualiza la foto)"| SM
    M -->|"git push<br/>(envía objetos y mueve la rama)"| SM

Tres cosas distintas con nombres parecidos: tu main, tu origin/main y el main del servidor. La lección 04-06 está dedicada íntegramente a esa distinción, porque es la fuente de la mayoría de las confusiones. Por ahora quédate con esto: las dos primeras están en tu disco; la tercera no.

Cómo verlas

# Solo las referencias remotas
git branch -r
  origin/HEAD -> origin/main
  origin/main
# Todas: locales y remotas
git branch -a
* main
  remotes/origin/HEAD -> origin/main
  remotes/origin/main

Ese origin/HEAD es un extra que crea git clone: recuerda cuál es la rama por defecto del servidor. Es lo que permite escribir git log origin como atajo de git log origin/main, y lo que hace que un clon te sitúe en main y no en cualquier otra rama.

  1. Convenciones de nombre: origin, upstream y los demás

El nombre de un remoto es libre. Puede ser origin, servidor, pepito o x. Pero hay convenciones muy asentadas que conviene respetar, porque cualquier persona que se incorpore al proyecto —o cualquier tutorial que leas— las da por supuestas:

Nombre Significado convencional Cuándo aparece
origin El repositorio del que clonaste; el de referencia del equipo Lo pone git clone automáticamente
upstream El repositorio original del que salió tu copia personal Al trabajar con forks (módulo 7)
fork Tu copia personal, cuando origin es el proyecto original Convención alternativa a la anterior
staging, produccion Repositorios de despliegue En flujos de despliegue por push (módulo 10)
backup, espejo Copia secundaria del repositorio Redundancia

Sobre upstream conviene decir dos palabras ahora para evitar una confusión clásica, porque la palabra se usa en Git con dos significados completamente distintos:

  1. upstream como nombre de remoto: el proyecto original del que hiciste un fork. Es una simple convención de nombre, sin nada especial. Se desarrolla en la lección 07-01.
  2. upstream como rama de seguimiento: la referencia remota con la que está emparejada una rama local tuya. Esto sí es un concepto de Git con configuración propia, y es el tema de la lección 04-06.

Son cosas distintas que comparten palabra. Cuando leas "la rama no tiene upstream", se refiere al segundo sentido.

Recomendación práctica: llama origin al repositorio de referencia de tu equipo y no te compliques. La convención está tan asentada que desviarse de ella solo genera preguntas.

  1. Plataformas de alojamiento: qué añaden sobre Git

Llegados aquí conviene ser tajante para deshacer un malentendido muy extendido: GitHub no es Git. GitHub es una empresa que aloja repositorios Git y les añade servicios alrededor. Git funcionaba antes de que GitHub existiera y funcionaría igual si desapareciera mañana.

Todo lo que has hecho en tres módulos, y todo lo que harás en este, funciona contra un repositorio bare en un servidor con SSH, sin ninguna plataforma de por medio. Las plataformas aportan lo que Git deliberadamente no hace.

Plataforma Modelo Rasgos característicos
GitHub Servicio comercial; opción autoalojada (Enterprise Server) El mayor ecosistema; Actions para automatización; enorme comunidad de proyectos abiertos
GitLab Servicio comercial; edición comunitaria autoalojable y gratuita Plataforma DevOps completa: CI/CD, registro de contenedores, gestión de incidencias, despliegue
Gitea / Forgejo Software libre autoalojado Muy ligero (un solo binario); ideal para servidores pequeños o redes internas
Bitbucket Servicio comercial (Atlassian) Integración estrecha con Jira y el resto de herramientas de Atlassian
Codeberg Servicio sin ánimo de lucro sobre Forgejo Alojamiento libre para proyectos de código abierto
SourceHut Servicio comercial minimalista Flujo basado en correo y parches, al estilo del kernel de Linux

Qué añade una plataforma exactamente

  • Alojamiento con disponibilidad y copias de seguridad: la máquina encendida que ningún portátil puede ser.
  • Control de acceso: quién lee, quién escribe, quién administra; y ramas protegidas, que impiden reescribir main o borrarla.
  • Propuestas de cambio (pull requests en GitHub, merge requests en GitLab): la conversación, la revisión y la aprobación de un conjunto de commits antes de integrarlo. Es una capa social sobre el git merge que ya conoces. Módulo 7.
  • Gestión de incidencias, hitos y tableros de trabajo.
  • Integración continua: ejecutar pruebas y despliegues automáticamente en cada envío. Módulos 7 y 10.
  • Interfaz web: navegar el código, leer el historial, comparar ramas y ver los diffs sin clonar nada.
  • Documentación: README.md renderizado, wikis, páginas web del proyecto.
  • Búsqueda y descubrimiento: encontrar código y proyectos, que es lo que convirtió a GitHub en lo que es.

Lo importante para este módulo: ninguno de esos servicios cambia cómo funciona fetch, push o merge. Aprender Git bien te sirve en las seis plataformas por igual, y cambiar de una a otra es cambiar una URL.

  1. El mapa mental completo

Antes de pasar a la práctica, unifiquemos todo lo visto en un solo esquema del recorrido que hace un commit desde el teclado de Ana hasta el disco de Carla:

flowchart TB
    subgraph A["Ana · Ubuntu"]
        A1["Directorio<br/>de trabajo"] -->|"git add"| A2["Índice"]
        A2 -->|"git commit"| A3["Repositorio local<br/>.git/objects"]
    end

    A3 -->|"git push origin main"| S["Servidor bare<br/>git.ejemplo.es"]

    subgraph C["Carla · Windows 11"]
        C3["Repositorio local<br/>.git/objects<br/>+ refs/remotes/origin/main"] -->|"git merge origin/main"| C1["Directorio<br/>de trabajo"]
    end

    S -->|"git fetch origin"| C3

Presta atención a la asimetría de la parte inferior. El git fetch de Carla solo llega hasta su repositorio local: actualiza origin/main y descarga objetos, pero no toca ni un fichero de su disco de trabajo. Hace falta un segundo paso —una integración, normalmente un merge— para que el trabajo de Ana aparezca en sus ficheros. Esa separación en dos pasos es el corazón de la lección 04-04, y es lo que distingue fetch de pull.

Y observa también lo que no aparece en el diagrama: nada sucede solo. Cada flecha es un comando que alguien ejecuta.

Errores Comunes y Consejos

Error 1: creer que origin es una palabra reservada de Git. No lo es: es el nombre que git clone pone por defecto al remoto, y podría ser cualquier otro. En git fetch origin, la palabra origin es un nombre configurado en tu .git/config, no una parte de la sintaxis del comando.

Error 2: pensar que origin/main consulta el servidor. Es un fichero de tu disco con un hash dentro. Si no has hecho fetch, puede llevar días desactualizado. Cuando alguien te diga "pues en el servidor ya está", tu primer reflejo debe ser git fetch, no discutir.

Error 3: creer que el servidor "tiene" tu repositorio. Tu repositorio es tuyo y está completo en tu disco. El servidor tiene otro repositorio, que casualmente comparte historia con el tuyo. Por eso puedes trabajar sin red durante una semana entera.

Error 4: intentar enviar a un repositorio no bare. Si montas tu propio servidor con git init en lugar de git init --bare, el primer push fallará con branch is currently checked out. Los repositorios que reciben envíos deben crearse con --bare.

Error 5: confundir la plataforma con Git. "GitHub no me deja hacer force push a main" no es una limitación de Git, sino una regla de rama protegida configurada en la plataforma. Distinguir qué capa te está hablando ahorra muchísimo tiempo depurando.

Consejo 1: monta un bare local para practicar. git init --bare /tmp/servidor/proyecto.git te da un servidor real, sin cuentas, sin contraseñas y sin Internet. Todo el módulo se puede hacer así, y es la mejor forma de aprender sin miedo a romper nada.

Consejo 2: acostúmbrate a git remote -v como primer comando al llegar a un repositorio desconocido. Junto con git status y git log --oneline -5, es lo que te dice dónde estás y con quién hablas.

Consejo 3: piensa siempre en "tres cosas distintas". main, origin/main y el main del servidor. En cuanto un problema de remotos te desconcierte, pregúntate de cuál de las tres estás hablando. Nueve de cada diez veces, ahí está la respuesta.

Ejercicios

Ejercicio 1: anatomía de un repositorio bare

Crea un repositorio bare y uno normal en /tmp y responde con comandos:

  1. ¿Qué diferencias hay en la estructura de directorios de cada uno?
  2. ¿Qué dice core.bare en la configuración de cada uno?
  3. ¿Qué ocurre si ejecutas git status dentro del bare?
  4. ¿Cuánto ocupa cada uno recién creado y por qué la diferencia es mínima?

Ejercicio 2: referencias remotas al desnudo

Partiendo de un repositorio bare con contenido, clónalo dos veces simulando a Bruno y a Carla. Después:

  1. Localiza en el disco el fichero de la referencia origin/main de Carla y muestra su contenido.
  2. Comprueba que ese hash coincide con el de su rama local main.
  3. Haz que Bruno envíe un commit nuevo al bare.
  4. Sin ejecutar fetch, comprueba que el origin/main de Carla sigue apuntando al commit antiguo. Explica por qué.
  5. Comprueba con git rev-parse cuál es el estado real de la rama en el bare.

Ejercicio 3: explicarlo con tus palabras

Carla acaba de incorporarse y te escribe: "Entonces, cuando hago git clone, ¿me descargo una carpeta compartida? ¿Si cambio algo se entera el resto?"

Redacta una respuesta de entre 150 y 250 palabras que aclare: qué es un remoto, por qué su repositorio es completo e independiente, qué es origin/main y cuándo se sincroniza algo. Sin usar la palabra "mágico" y sin dar por sabido nada que no se haya visto en el curso.

Soluciones

Solución 1:

mkdir -p /tmp/comparativa && cd /tmp/comparativa

git init normal
git init --bare desnudo.git
Initialized empty Git repository in /tmp/comparativa/normal/.git/
Initialized empty Git repository in /tmp/comparativa/desnudo.git/

Ya en el mensaje se ve la diferencia: uno inicializa normal/.git/ y el otro desnudo.git/ directamente.

# 1. Estructura
ls -A /tmp/comparativa/normal
ls -A /tmp/comparativa/desnudo.git
.git

HEAD  branches  config  description  hooks  info  objects  refs

El repositorio normal solo contiene .git; el bare tiene ese mismo contenido en su raíz.

# 2. La marca en la configuración
git -C /tmp/comparativa/normal config core.bare
git -C /tmp/comparativa/desnudo.git config core.bare
false
true
# 3. git status en el bare
git -C /tmp/comparativa/desnudo.git status
fatal: this operation must be run in a work tree

El mensaje es literal: status compara el directorio de trabajo con el índice, y aquí no hay directorio de trabajo que comparar.

# 4. Tamaño
du -sh /tmp/comparativa/normal /tmp/comparativa/desnudo.git
44K	/tmp/comparativa/normal
44K	/tmp/comparativa/desnudo.git

Prácticamente idénticos: en ambos casos lo que ocupa es la estructura vacía de directorios y los ficheros de plantilla de hooks/. La diferencia real aparecería con contenido, y aun así sería pequeña: el bare se ahorra solo la copia desplegada de los ficheros, no los objetos.

Solución 2:

# Preparar el "servidor" con algo de contenido
mkdir -p /tmp/practica && cd /tmp/practica
git init --bare servidor.git

# Un repositorio de partida que lo alimente
git clone servidor.git inicial
cd inicial
echo "<h1>Gestor de tareas</h1>" > index.html
git add . && git commit -m "Estructura inicial del gestor de tareas"
git push origin main
cd ..

# Los dos clones
git clone servidor.git bruno
git clone servidor.git carla
# 1. El fichero de la referencia remota de Carla
find /tmp/practica/carla/.git/refs -type f
cat /tmp/practica/carla/.git/refs/remotes/origin/main
/tmp/practica/carla/.git/refs/heads/main
/tmp/practica/carla/.git/refs/remotes/origin/main
/tmp/practica/carla/.git/refs/remotes/origin/HEAD
4f8c2a1e9b3d7f6a2c8e4b1d9f3a7c5e2b8d6f4a

(El hash será distinto en tu máquina; lo importante es la estructura.)

# 2. Coincide con su rama local
git -C /tmp/practica/carla rev-parse main origin/main
4f8c2a1e9b3d7f6a2c8e4b1d9f3a7c5e2b8d6f4a
4f8c2a1e9b3d7f6a2c8e4b1d9f3a7c5e2b8d6f4a

Recién clonado, las dos referencias señalan el mismo commit.

# 3. Bruno trabaja y envía
cd /tmp/practica/bruno
echo "body { font-family: sans-serif; }" > estilos.css
git add . && git commit -m "Añadir estilos base del listado"
git push origin main
# 4. Carla, sin hacer fetch
git -C /tmp/practica/carla rev-parse origin/main
4f8c2a1e9b3d7f6a2c8e4b1d9f3a7c5e2b8d6f4a

El mismo hash de antes. Y la explicación es exactamente la idea central de la lección: origin/main es un fichero en el disco de Carla que nadie ha tocado. Bruno ha escrito en el bare, no en el portátil de Carla. Git no tiene ningún mecanismo de aviso: la referencia solo se actualizará cuando Carla ejecute fetch o pull.

# 5. El estado real del servidor
git -C /tmp/practica/servidor.git rev-parse main
7a2e5c8b4f1d3a9e6c2b8f4d1a7e3c9b5f2d8a6e

Un hash distinto: el commit de Bruno. La referencia de Carla está desactualizada, y lo estará hasta que hable con el servidor.

# Comprobación final: tras el fetch, se pone al día
git -C /tmp/practica/carla fetch origin
git -C /tmp/practica/carla rev-parse origin/main
7a2e5c8b4f1d3a9e6c2b8f4d1a7e3c9b5f2d8a6e

Y fíjate en un detalle que anticipa la lección 04-04: main de Carla sigue en el commit antiguo y sus ficheros no han cambiado. El fetch solo ha actualizado la foto.

Solución 3:

Hola, Carla. No, no es una carpeta compartida: es bastante mejor que eso.

Cuando clonas, no te descargas un enlace a algo remoto, sino una copia completa e independiente del proyecto, con todo el historial, todas las ramas y toda la base de datos de objetos. A partir de ese momento puedes confirmar, crear ramas, fusionarlas y consultar el historial de hace dos años sin conexión y sin permiso de nadie. Tu repositorio es tan válido como el del servidor.

Lo que sí guarda el clon es de dónde vino: registra la URL bajo el nombre corto origin. Eso es un remoto, ni más ni menos: un nombre para una URL.

Sobre si el resto se entera: no, y esto es importante. Nada se sincroniza automáticamente. Tus commits están solo en tu disco hasta que ejecutas git push, y los de los demás no llegan hasta que ejecutas git fetch o git pull. Verás referencias que se llaman origin/main: no son el servidor, son una nota que Git guarda en tu propio disco con el aspecto que tenía el servidor la última vez que hablaste con él. Si llevas dos días sin hacer fetch, esa nota tiene dos días de antigüedad.

En resumen: trabajas en local, y decides tú cuándo enviar y cuándo recibir.

Conclusión

Esta lección ha construido el modelo mental que sostiene todo el módulo. Lo esencial:

  • Un remoto es un nombre corto para una URL guardado en .git/config. No es una copia sincronizada, ni una conexión abierta, ni nada obligatorio. Git puede hablar con un repositorio dándole la URL directamente; el remoto solo evita repetirla.
  • Técnicamente no hay servidor central. Todos los repositorios Git son iguales y completos. El repositorio "de referencia" lo es por acuerdo del equipo, y las razones para tener uno son organizativas: disponibilidad, punto de acuerdo, copia de seguridad, control de acceso, automatización y direccionalidad de red.
  • Un repositorio bare es un repositorio sin directorio de trabajo: el contenido de .git/ en la raíz. Es la forma correcta de montar un repositorio que recibe envíos, porque así no puede quedar en un estado incoherente. Lo que hay detrás de GitHub o GitLab es siempre un bare.
  • Las referencias remotas viven en .git/refs/remotes/ y son ficheros idénticos a los de las ramas locales. origin/main es una referencia local de solo lectura que recuerda dónde estaba el remoto la última vez que se habló con él. Se actualiza solo con fetch, pull, clone o push.
  • main, origin/main y el main del servidor son tres cosas distintas. Las dos primeras están en tu disco.
  • origin y upstream son convenciones de nombre, no palabras reservadas. Y ojo: upstream significa además otra cosa —la rama de seguimiento— que veremos en la lección 04-06.
  • Las plataformas de alojamiento añaden servicios sobre Git (control de acceso, propuestas de cambio, integración continua, interfaz web), pero no cambian cómo funciona Git. Aprender Git te sirve en todas por igual.

Lo que viene

Ya sabes qué es un remoto. En la lección 04-02: Agregando un Repositorio Remoto pasamos a la práctica: Ana publica por fin gestor-tareas. Registraremos el remoto con git remote add, inspeccionaremos lo que aparece con git remote -v y git remote show, aprenderemos a renombrarlo, eliminarlo y cambiarle la URL, y —sobre todo— abriremos .git/config para desmenuzar con calma esa línea fetch = +refs/heads/*:refs/remotes/origin/* que llevamos dos módulos viendo de refilón sin explicar. Es el refspec, la pieza que casi nadie entiende y que, una vez comprendida, hace que fetch y push dejen de parecer arbitrarios.

Dominando Git: De Principiante a Avanzado

Módulo 1: Introducción a Git

Módulo 2: Operaciones Básicas de Git

Módulo 3: Ramas y Fusión

Módulo 4: Trabajando con Repositorios Remotos

Módulo 5: Operaciones Avanzadas de Git

Módulo 6: Herramientas y Técnicas de Git

Módulo 7: Estrategias de Colaboración y Flujo de Trabajo

Módulo 8: Mejores Prácticas y Consejos de Git

Módulo 9: Solución de Problemas y Depuración

Módulo 10: Git en el Mundo Real

© Copyright 2026. Todos los derechos reservados