Saltar al contenido
sudoquest@root
Git

Remotos

Trabajar con otros a través de un servidor: clonar, subir y traer, el push rechazado, las ramas que siguen a otras y mirar antes de juntar.

35 min de lectura

Hasta ahora, todo pasaba en un solo repositorio, el tuyo. Pero Git está pensado para trabajar con otros: cada uno tiene su copia entera del repositorio (con toda la historia) y las copias se ponen al día unas con otras. Lo normal es que haya un repositorio en un servidor que todos usan de punto de encuentro: a ese, desde tu copia, lo llamas un remoto.

En este tema trabajas en Viajes Brisa, una agencia con dos oficinas, Madrid (la tuya) y Lisboa (donde está Rui). La web de la agencia vive en Git, en el servidor srv-git, y cada oficina tiene su copia. Como siempre, cada sección tiene un ejercicio con una terminal.

Clonar: tu copia del repositorio

Para empezar a trabajar en un repositorio que ya existe, lo clonas: Git trae toda la historia y te deja la última versión en una carpeta. La dirección del repositorio de la agencia es git@srv-git:agencia/web.git (se entra por ssh, como el usuario git, al repositorio agencia/web.git).

tecnico@pc-madrid:~$ git clone git@srv-git:agencia/web.git
Cloning into 'web'...
remote: Enumerating objects: 6, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (5/5), done.
remote: Total 6 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (6/6), done.

Las líneas que empiezan por remote: las escribe el servidor mientras prepara lo que manda (los objetos: commits, árboles y archivos). La carpeta se llama como el repositorio, web, sin el .git. Dentro, Git ya ha apuntado de dónde viene, con el nombre origin:

tecnico@pc-madrid:~/web$ git remote -v
origin	git@srv-git:agencia/web.git (fetch)
origin	git@srv-git:agencia/web.git (push)

Y hay ramas nuevas: las de seguimiento, como origin/main, que son la foto de cómo estaba main en srv-git la última vez que hablaste con él. No las tocas tú: las mueve Git al subir y al traer. git branch -a las enseña todas:

tecnico@pc-madrid:~/web$ git branch -a
* main
remotes/origin/HEAD -> origin/main
remotes/origin/main

Tu main sigue a origin/main: por eso git status dice ahora cómo vas respecto a ella.

tecnico@pc-madrid:~/web$ git status
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean

Ejercicio

Tu copia de la web

La web de la agencia está en srv-git: git@srv-git:agencia/web.git. Haz tu copia en ~/web y mira de dónde viene.

Preparando la máquina…

Subir lo tuyo: git push

Trabajas como siempre: cambias, git add, git commit. El commit está solo en tu copia, y git status te lo recuerda:

tecnico@pc-madrid:~/web$ git status
On branch main
Your branch is ahead of 'origin/main' by 1 commit.
(use "git push" to publish your local commits)

nothing to commit, working tree clean

git push lo sube a srv-git: manda los objetos que allí faltan y mueve allí main a tu commit. La última línea dice qué rama se movió y de dónde a dónde:

tecnico@pc-madrid:~/web$ git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 2 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 377 bytes | 377.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To srv-git:agencia/web.git
 72f1dfc..378749a  main -> main

Ahora origin/main también está en tu commit, y git status vuelve a decir up to date. Si no había nada nuevo que subir, git push dice Everything up-to-date.

Ejercicio

La oferta de Oporto

El viaje a Oporto baja de 199 € a 179 €. En tu copia (~/web), cámbialo en ofertas.txt, guárdalo en un commit y súbelo a srv-git, para que Lisboa lo tenga.

Preparando la máquina…

Traer lo de los demás: git pull

Rui, desde Lisboa, sube sus cambios a srv-git. Para tenerlos en tu copia, git pull: trae lo nuevo del servidor (mueve origin/main) y lo junta con tu main. Si tú no tenías nada nuevo, juntar es solo avanzar (Fast-forward, como en el tema 3):

tecnico@pc-madrid:~/web$ git pull
remote: Enumerating objects: 6, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 4 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (4/4), 425 bytes | 425.00 KiB/s, done.
From srv-git:agencia/web
 72f1dfc..8e87f8c  main       -> origin/main
Updating 72f1dfc..8e87f8c
Fast-forward
destinos/sintra.html | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 destinos/sintra.html

La parte de From es lo que trajo (origin/main avanzó); lo de debajo, el merge con tu main. En git log verás el commit de Rui, con su nombre como autor.

Ejercicio

Lo nuevo de Lisboa

Rui, desde Lisboa, ha subido a srv-git la página de Sintra. Trae lo nuevo a tu copia (~/web) y mira quién lo hizo.

Preparando la máquina…

Cuando srv-git tiene algo que tú no: el push rechazado

Tú hiciste un commit (la página de Toledo) y, mientras, Rui subió el suyo (la de Sintra). Si ahora intentas subir, srv-git lo rechaza: aceptarlo borraría el commit de Rui de main.

tecnico@pc-madrid:~/web$ git push
To srv-git:agencia/web.git
! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'srv-git:agencia/web.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

Lo que pide es traer lo de Rui y juntarlo con lo tuyo antes de subir. Pero git pull a secas tampoco lo hace todavía: como las dos copias han hecho commits (han divergido), Git quiere que le digas cómo juntarlas:

tecnico@pc-madrid:~/web$ git pull
From srv-git:agencia/web
 72f1dfc..8e87f8c  main       -> origin/main
hint: You have divergent branches and need to specify how to reconcile them.
hint: You can do so by running one of the following commands sometime before
hint: your next pull:
hint: 
hint:   git config pull.rebase false  # merge (the default strategy)
hint:   git config pull.rebase true   # rebase
hint:   git config pull.ff only       # fast-forward only
...
fatal: Need to specify how to reconcile divergent branches.

Hay dos maneras, las del tema 4:

  • git pull --no-rebase: un merge, con su commit de merge (Git abre el editor con el mensaje Merge branch 'main' of srv-git:agencia/web; basta con guardarlo).
  • git pull --rebase: rehace tus commits encima de los de Rui, en línea, sin merge.

Para no tener que decirlo cada vez, se elige una con git config pull.rebase false (merge) o true (rebase). Con el merge, la historia queda así:

tecnico@pc-madrid:~/web$ git log --oneline --graph
*   c8848a6 (HEAD -> main) Merge branch 'main' of srv-git:agencia/web
|\  
| * 8e87f8c (origin/main, origin/HEAD) Página de Sintra
* | 3751407 Página de Toledo
|/  
* 72f1dfc La web de la agencia

Ya tienes lo de los dos, así que git push sube sin problema. Si lo tuyo y lo de Rui tocaban las mismas líneas, el pull se queda a medias con un conflicto, y se resuelve como en el tema 4.

Ejercicio

Dos oficinas a la vez

Tienes en tu copia un commit sin subir (la página de Toledo), pero Rui subió antes la de Sintra. Sube lo tuyo a srv-git sin perder lo de Rui.

Preparando la máquina…

Una rama nueva en srv-git: git push -u

Las ramas que creas son tuyas hasta que las subes. Si haces una rama oporto y pruebas git push, Git no sabe adónde mandarla:

tecnico@pc-madrid:~/web$ git push
fatal: The current branch oporto has no upstream branch.
To push the current branch and set the remote as upstream, use

  git push --set-upstream origin oporto

git push -u origin oporto (la -u es --set-upstream) la sube a origin con el mismo nombre y, además, hace que tu oporto siga a la de allí: desde entonces, en esa rama bastan git push y git pull, y git status dice cómo vas.

tecnico@pc-madrid:~/web$ git push -u origin oporto
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Delta compression using up to 2 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (4/4), 404 bytes | 404.00 KiB/s, done.
Total 4 (delta 0), reused 0 (delta 0), pack-reused 0
To srv-git:agencia/web.git
* [new branch]      oporto -> oporto
Branch 'oporto' set up to track remote branch 'oporto' from 'origin'.

git branch -vv enseña qué rama sigue cada una (entre corchetes) y si vas por delante (ahead) o por detrás (behind):

tecnico@pc-madrid:~/web$ git branch -vv
  main   72f1dfc [origin/main] La web de la agencia
* oporto 55061b2 [origin/oporto] Página de Oporto

Al revés también funciona: si Rui sube una rama verano y tú haces git switch verano (después de traerla), Git crea tu verano siguiendo a origin/verano.

Ejercicio

Oporto, para revisar

Prepara la página de Oporto (destinos/oporto.html) en una rama nueva, oporto, y súbela a srv-git para que Rui la revise. Que tu rama siga a la de srv-git (así, después, bastan git push y git pull); main no se toca.

Preparando la máquina…

Mirar antes de juntar: git fetch

git pull trae y junta de una vez. A veces conviene ver antes qué viene. git fetch hace solo la primera mitad: trae lo nuevo y mueve origin/main, sin tocar tu main ni tus archivos.

tecnico@pc-madrid:~/web$ git fetch
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 374 bytes | 374.00 KiB/s, done.
From srv-git:agencia/web
 72f1dfc..d9ced03  main       -> origin/main

Ahora git status sabe que vas por detrás. Y puedes mirar qué trae: git log main..origin/main enseña los commits que están en origin/main y no en tu main, y git diff main origin/main, qué cambian:

tecnico@pc-madrid:~/web$ git log --oneline main..origin/main
d9ced03 (origin/main, origin/HEAD) Lisboa, más barata
tecnico@pc-madrid:~/web$ git diff main origin/main
diff --git a/ofertas.txt b/ofertas.txt
index ad37cf9..255fe0a 100644
--- a/ofertas.txt
+++ b/ofertas.txt
@@ -1,5 +1,5 @@
Ofertas de la semana

-Lisboa: 149 €
+Lisboa: 139 €
Oporto: 199 €
Sevilla: 129 €

Si te parece bien, lo juntas con git merge origin/main (que es lo que habría hecho git pull):

tecnico@pc-madrid:~/web$ git merge origin/main
Updating 72f1dfc..d9ced03
Fast-forward
ofertas.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

Ejercicio

Antes de juntar, mirar

Rui avisa de que ha subido un cambio en las ofertas. Antes de juntarlo con tu copia (~/web), tráelo sin juntar, mira qué cambia y, después, júntalo.

Preparando la máquina…

Más cosas de los remotos

  • git remote add nombre dirección añade otro remoto (al clonar ya tienes origin); git remote rename le cambia el nombre, git remote remove lo quita y git remote show origin cuenta lo que sabe de él (sus ramas y cuáles siguen las tuyas).
  • Un repositorio que empezó con git init (y no con git clone) no tiene ningún remoto, y git push dice No configured push destination. Se le pone con git remote add origin dirección, y git push -u origin main lo sube a un repositorio vacío del servidor (en srv-git los crea el administrador) y deja tu main siguiendo a la de allí.
  • git push origin --delete verano borra la rama verano de srv-git (no hace falta tenerla en tu copia; git branch -d solo borra la tuya).
  • Cuando alguien borra una rama en srv-git, tu origin/verano se queda hasta que haces git fetch --prune. Si tu verano la seguía, git branch -vv la marca [origin/verano: gone]: la de allí ya no está.
  • git ls-remote origin pregunta al servidor qué ramas tiene ahora mismo, sin traer nada.

Resumen

Orden Qué hace
git clone dirección tu copia de un repositorio, con su historia y el remoto origin
git remote -v los remotos de tu copia y sus direcciones
git push sube tus commits de la rama actual a la que sigue
git pull trae lo nuevo y lo junta con tu rama (--no-rebase: merge; --rebase: en línea)
git fetch solo trae: mueve origin/main, sin tocar tu rama
git log main..origin/main los commits que vienen y aún no tienes
git merge origin/main junta lo traído con tu rama
git push -u origin rama sube una rama nueva y hace que la tuya la siga
git branch -vv / -a qué rama sigue cada una / también las de seguimiento
git push origin --delete rama borra una rama del servidor
git fetch --prune trae y quita las de seguimiento cuya rama ya no está