Git básico III: sincronizar con el remoto
Tercera entrega de la serie Git básico. Las anteriores: crear y clonar repos y el ciclo de cambios. Tiempo de lectura estimado: 5 minutos.
Hasta aquí todo lo que hemos hecho vive en tu máquina. Los commits están en tu repo local y nadie más los ve. Para que el trabajo sea útil al resto del equipo (o a ti mismo desde otra máquina) hace falta sincronizar con un servidor remoto. Los tres comandos de hoy: git push, git pull y git log. Los dos primeros mueven commits entre tu máquina y el remoto; el tercero te deja leer qué ha pasado.
git push: subir tus commits al remoto
git push envía los commits que tienes en local (y que aún no están en el remoto) hacia el servidor. La forma más común:
git push
Eso funciona si tu rama local ya tiene configurado qué rama remota trackea. Cuando empujas una rama por primera vez, git no sabe dónde va y hay que decírselo:
git push -u origin mi-rama
-u significa "upstream": establece el tracking, de modo que a partir de ahí puedes usar git push a secas. origin es el nombre por defecto del remoto (el que se configuró al hacer clone). mi-rama es la rama que quieres enviar.
Errores típicos:
- "Updates were rejected": alguien subió commits a la misma rama después de que tú hicieras tu último pull. Solución:
git pull, resolver conflictos si los hay, volver a intentar. - "failed to push some refs": misma causa, normalmente. No intentes "arreglarlo" con
--forcesin saber lo que haces. El force push sobre una rama compartida borra commits de otros.
Sobre --force: existe pero no es para repos compartidos. Si has hecho rebase o amend de commits que ya habías empujado a tu rama, --force-with-lease es más seguro que --force: aborta si ha habido cambios nuevos en el remoto que no tienes en local. Es el mínimo civilizado cuando necesitas sobrescribir.
git push --force-with-lease # "force, pero solo si nadie más ha tocado"
git pull: bajar lo que han subido otros
git pull trae al repo local los commits que están en el remoto y no tienes tú. Funcionalmente son dos operaciones encadenadas: git fetch (descarga los commits al repo, pero no los aplica) y git merge (los integra en tu rama actual).
git pull # fetch + merge en la rama actual
git pull --rebase # fetch + rebase en vez de merge
La diferencia entre merge y rebase importa: con merge, si había commits divergentes, git crea un "merge commit" que une las dos líneas. Con rebase, tus commits se reaplican encima de lo que haya en el remoto, quedando un historial lineal.
Yo prefiero pull --rebase casi siempre, porque odio los merge commits vacíos de "Merge branch 'main' of origin/main" que aparecen cuando dos personas hacen pull al mismo tiempo. Se puede hacer que sea el comportamiento por defecto:
git config --global pull.rebase true
Cuándo pull --rebase te va a doler: si tienes commits propios sin pushear y los de otros tocan lo mismo, te toca resolver conflictos durante el rebase, commit a commit. Es manejable, pero si no te sientes cómodo con rebase, usa pull a secas.
Error clásico: hacer git pull con cambios sin committear que están tocando ficheros que el pull va a modificar. Git se niega y te pide que hagas stash, commit o descartes. Normalmente lo resuelves con git stash, git pull, git stash pop (lo veremos en el post sobre stash).
git log: leer la historia
git log te muestra el historial de commits: quién, cuándo, qué. Sin argumentos, es verboso y poco útil. Con los flags adecuados se convierte en el comando que más vas a usar para investigar.
git log --oneline # una línea por commit
git log --oneline --graph # con el grafo de ramas al margen
git log --oneline --graph --all # todas las ramas, no solo la actual
La combinación --oneline --graph --all --decorate (que suele meterse en un alias) te da un árbol compacto con hash corto, mensaje y referencias (ramas y tags). Es la vista por defecto que deberías mirar cuando un repo te parece "que está raro".
Filtros que se usan mucho:
git log --author="Alicia" # commits de una persona
git log --since="2 weeks ago" # últimos 14 días
git log --grep="bug" # commits cuyo mensaje contiene "bug"
git log -- src/handler.go # commits que tocan un fichero
git log -p -- src/handler.go # igual pero con el diff de cada commit
El -- antes del nombre de fichero es importante: le dice a git "lo que viene ahora son rutas, no ramas ni refs". En ficheros con nombres normales se puede omitir, pero si tu rama se llama igual que un fichero, hay que ponerlo.
Mi combinación favorita para buscar cuándo se introdujo una línea concreta:
git log -S "funcionSospechosa" --oneline -- src/
-S (pickaxe) busca commits donde el texto aparece o desaparece en el diff. Perfecto para "¿cuándo se añadió esta llamada?" o "¿cuándo la borraron?".
Para un log tipo "página de gitk en terminal":
git log --graph --pretty=format:'%C(yellow)%h%Creset -%C(red)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
Es largo, pero cabe en un alias y luego se usa con dos caracteres. El post de git aliases ya publicado cubre este en detalle.
El ciclo con remoto en vivo
Un día típico:
git pull --rebase # traigo lo nuevo del equipo
# ... edito código ...
git status # veo qué he tocado
git add -p # añado selectivamente
git commit -m "Arreglar validación de email"
git log --oneline -5 # reviso mis últimos commits
git push # subo mi trabajo
Con estos tres comandos (más los seis anteriores de la serie) tienes cubierto el 80% del trabajo diario con git en solitario o en equipos pequeños.
Errores típicos con el remoto
Hacer force push sin --force-with-lease. Si trabajas con más gente, una línea sin --force-with-lease puede borrar commits ajenos silenciosamente. Nunca fuerces un push sin saber si alguien ha tocado esa rama.
Confundir origin con upstream. En proyectos forkeados la convención es: origin apunta a tu fork, upstream al repo original. git push va a origin; para sincronizar con upstream se hace git fetch upstream.
Pullear con trabajo sin guardar. Si no estás seguro, primero git status. Si hay cosas sin committear, stash o commit antes de pullear. El mensaje de git en ese caso es claro: léelo.
Hasta aquí el nivel básico
Con los nueve comandos de esta serie de tres posts (init, clone, status, add, commit, diff, pull, push, log) puedes trabajar en git de forma competente durante el 80% de los días. El 20% restante es donde las cosas se ponen interesantes: ramas, deshacer, resolver follones. De eso trata la serie intermedia, que empieza en la próxima entrega con git branch, git switch y git merge. Y cuando quieras profundizar más, te espera la serie avanzada.