Git push rechazado: por qué pasa y cómo solucionarlo
Un git push rechazado tiene cuatro causas distintas. Aprende a leer el mensaje de error y aplicar el comando correcto para cada una.
Has commiteado tu trabajo, escribes git push con toda la confianza del mundo y la terminal te devuelve ! [rejected] en rojo. No has roto nada. Git acaba de frenarte a propósito. Un git push rechazado es uno de los primeros muros reales cuando dejas de trabajar solo y empiezas a compartir un repo con más gente, y detrás de ese mensaje rojo hay cuatro causas muy distintas. Aquí explico cómo leer el error para saber cuál es la tuya y qué comando ejecutar en cada caso.
¿Qué significa que Git rechace un push?
Git rechaza un push cuando aceptarlo pondría en riesgo historia que ya vive en el remoto. Es la regla de seguridad más importante del sistema: te protege de pisar el trabajo de otra persona. Cuando actualizas una rama que apuntaba al commit A para que apunte al commit B, Git solo lo permite sin quejarse si B desciende de A. A eso se le llama fast-forward, y es la única actualización que Git da por segura de forma automática.[1]
Si tu push no cumple esa condición, o si el servidor tiene reglas encima (permisos, protección de ramas), lo bloquea antes de tocar nada. La terminal te lo cuenta, pero el mensaje cambia según el motivo. Aprender a distinguirlos es lo que separa “le doy a --force y a ver qué pasa” de resolverlo en treinta segundos. Si vienes de trabajar en solitario y quieres el panorama completo de colaborar en un repo, tienes la guía de trabajo en equipo con Git y GitHub como base.
Causa 1: la rama remota tiene commits que tú no tienes
Este es el clásico, el rechazo non-fast-forward. Aparece cuando alguien subió commits a la misma rama después de tu último git pull, así que tu historia local se ha quedado atrás. El mensaje es inconfundible:
$ git push
To github.com:equipo/proyecto.git
! [rejected] main -> main (fetch first)
error: failed to push some refs to 'github.com:equipo/proyecto.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.
Git etiqueta este rechazo como (fetch first) cuando todavía no has traído a tu copia local los commits del remoto, que es el caso más habitual. Verás (non-fast-forward) en su lugar cuando ya los tenías y aun así tu rama diverge, por ejemplo tras reescribir con rebase o amend un commit que ya estaba publicado. Los dos son la misma familia de rechazo y se arreglan igual.
El propio Git te da la pista: integra primero el trabajo remoto y luego sube el tuyo. La forma más limpia es traer esos commits y reaplicar los tuyos encima con rebase:
# Trae los commits del remoto y reaplica los tuyos encima, sin merge de ruido
git pull --rebase origin main
# Si no hay conflictos, tu rama vuelve a ir por delante y ya puedes subir
git push
Si prefieres verlo en dos pasos separados, git fetch origin seguido de git merge origin/main hace lo mismo, con la diferencia de que genera un commit de merge en lugar de reescribir tu historia. Cuál te conviene depende del proyecto, y lo explico a fondo en el detalle del error non-fast-forward.
Lo que nunca debes hacer aquí es reaccionar con git push --force para saltarte el aviso. Ese rechazo significa que hay trabajo de un compañero en el remoto, y forzar lo borraría del historial. Existe una forma más prudente de forzar cuando de verdad hace falta, --force-with-lease, pero no es la reacción por defecto a un non-fast-forward. La reviso en la FAQ.
Causa 2: la rama está protegida
Si el push llega al servidor pero rebota con protected branch, la rama tiene una regla de protección que impide subir commits directos. Es habitual en main de cualquier equipo serio: los cambios entran por Pull Request y no de otra forma.[2] El mensaje viene de GitHub:
remote: error: GH006: Protected branch update failed for refs/heads/main.
remote: error: Changes must be made through a pull request.
To github.com:equipo/proyecto.git
! [remote rejected] main -> main (protected branch hook declined)
Fíjate en [remote rejected] en vez de [rejected]: el servidor te ha aceptado la conexión y luego ha dicho que no. La solución aquí no pasa por integrar nada. Cambia tu forma de trabajar: mueve tu commit a una rama propia y ábrela como Pull Request:
# Crea una rama a partir de donde estás (git switch es estable, no experimental)
git switch -c fix/validacion-login
git push -u origin fix/validacion-login
# Ahora abres el Pull Request en GitHub y el equipo lo revisa antes de entrar a main
Esta protección aplica a todo el mundo por igual, incluido el dueño del repo si está configurada así. Aquí el problema es la política del proyecto, y los permisos no tienen nada que ver.
Causa 3: no tienes permiso de escritura (el 403)
Cuando ves un 403, el problema es de acceso: tu cuenta no puede escribir en ese repositorio. Es algo distinto de la protección de ramas, aunque sea fácil confundirlos. El mensaje señala directamente a tu usuario:
remote: Permission to equipo/proyecto.git denied to tu-usuario.
fatal: unable to access 'https://github.com/equipo/proyecto.git/': The requested URL returned error: 403
Esto le pasa casi siempre a quien clona un proyecto open source e intenta pushear sin ser colaborador. Tienes dos salidas. Si es tu equipo y deberías tener acceso, pide al administrador que te añada como colaborador con permiso de escritura. Si es un proyecto ajeno al que quieres contribuir, haz un fork: una copia del repo bajo tu propia cuenta, donde sí mandas tú. Pusheas a tu fork y desde ahí abres un Pull Request al original.
La diferencia con la causa 2 importa. El 403 dice que no puedes escribir en ese repo. La protección de rama dice algo distinto: que ahí solo se entra por Pull Request. Confundirlas te lleva a pedir permisos que no arreglan nada o a pelearte con una política que no vas a cambiar.
Causa 4: no hay upstream configurado
Este caso es Git preguntándote a dónde quieres subir, sin que haya un rechazo real detrás. Ocurre la primera vez que pusheas una rama nueva que aún no tiene rama remota asociada:
fatal: The current branch feature-login has no upstream branch.
To push the current branch and set the remote as upstream, use
git push --set-upstream origin feature-login
To have this happen automatically for branches without a tracking
upstream, see 'push.autoSetupRemote' in 'git help config'.
Aquí solo falta decirle a Git el destino, sin que haya nada que integrar ni ningún permiso pendiente. El propio mensaje te da el comando, y -u es la forma corta de --set-upstream:
# Sube la rama y la deja enlazada al remoto para futuros push/pull sin argumentos
git push -u origin feature-login
A partir de ahí, esa rama recuerda su destino y un git push a secas ya sabe adónde ir.
Cómo identificar tu caso leyendo el mensaje
La causa está escrita en la primera línea del error. Esta tabla mapea cada mensaje a su arreglo directo:
| Mensaje de error | Causa | Arreglo |
|---|---|---|
! [rejected] ... (fetch first) o (non-fast-forward) | La rama remota tiene commits que tú no tienes | git pull --rebase origin <rama> y luego git push |
protected branch hook declined / GH006: Protected branch update failed | Rama protegida en GitHub | Crea una rama nueva y abre un Pull Request |
remote: Permission to ... denied / error: 403 | No tienes permiso de escritura en el repo | Haz un fork o pide acceso de colaborador |
fatal: The current branch <rama> has no upstream branch | Falta configurar el upstream | git push -u origin <rama> |
Con leer las palabras fetch first o non-fast-forward, protected, 403 o upstream ya sabes en cuál de los cuatro casos estás. El resto es aplicar el comando de su fila.
Antes de seguir, practica ese diagnóstico: en el siguiente ejercicio te toca leer un push rechazado y elegir el arreglo correcto sin ejecutar nada a lo bruto.
Cargando ejercicio...
Errores comunes al resolver un push rechazado
Forzar el push ante un non-fast-forward
git push --force sobre un rechazo non-fast-forward es la forma más rápida de borrar el trabajo de un compañero. El rechazo existe precisamente porque hay commits en el remoto que tú no tienes; forzar los reemplaza por tu versión y esos commits desaparecen del historial compartido. Integra primero con git pull --rebase y fuerza solo cuando sepas exactamente por qué, con --force-with-lease en vez de --force.
Confundir el 403 con la rama protegida
Son problemas distintos con soluciones opuestas. El 403 se arregla consiguiendo permiso (colaborador o fork). La rama protegida se resuelve trabajando por Pull Request, porque ni el dueño puede saltársela añadiendo permisos. Si intentas pedir acceso de escritura para esquivar una protección de main, estás resolviendo el problema equivocado.
Hacer git pull sin —rebase en cada sincronización
Un git pull a secas hace merge, y cada vez que sincronizas genera un commit de merge automático. En una rama con varias personas, el historial se llena de “Merge branch ‘main’” que no aportan nada y ensucian el git log. Con --rebase reaplicas tus commits encima de los remotos y el historial queda lineal.
Pushear a main en un repo con Pull Requests obligatorios
Trabajar directamente sobre main en un proyecto que exige Pull Request te garantiza un rechazo en cuanto intentes subir. Crea tu rama antes de empezar a commitear, no después de que Git te frene.
Checklist para desatascar un push rechazado
- He leído la primera línea del error e identificado la palabra clave (
non-fast-forward,protected,403oupstream) - Ante un non-fast-forward, integro con
git pull --rebaseantes de volver a pushear - No uso
git push --forcecomo reacción automática a un rechazo - Distingo un 403 (permiso) de una rama protegida (política de Pull Request)
- Trabajo en una rama propia cuando
mainestá protegida y abro un Pull Request - La primera vez que subo una rama nueva uso
git push -u origin <rama>
Si estás dando estos primeros pasos en Git en equipo y quieres una base ordenada en lugar de ir apagando fuegos error por error, en el curso Git de cero a profesional trabajamos estos flujos de forma guiada. Te dejo el correo por si quieres que te avise de cuándo abre y de las próximas guías del tema:
Un concepto nuevo cada semana
Fuentes
- Documentación oficial de git push (git-scm.com): semántica de fast-forward, comportamiento de
--forcey--force-with-lease, y la opción-u/--set-upstream. - Reglas de protección de ramas (GitHub Docs): configuración que obliga a integrar cambios mediante Pull Request en una rama protegida.
Preguntas Frecuentes
¿Debo usar —force o —force-with-lease en un git push rechazado?
Usa --force-with-lease siempre que tengas que forzar:
git push --force-with-lease
La diferencia es que --force sobrescribe el remoto pase lo que pase, mientras que --force-with-lease aborta el push si el remoto ha cambiado respecto a lo que tú tenías registrado, así no pisas commits que otra persona subió mientras trabajabas. No es infalible: si acabas de hacer un git fetch que actualizó tu referencia local, la protección deja de avisarte. Y en ningún caso es la respuesta correcta a un rechazo normal por trabajo remoto pendiente, donde lo que toca es integrar con git pull --rebase.
¿Por qué me rechaza el push si soy el dueño del repositorio?
Porque la protección de ramas aplica a todo el mundo, incluido el dueño, cuando está configurada así. Es intencionado: evita que nadie, ni siquiera tú con prisa un viernes, meta cambios en main sin pasar por revisión. Si necesitas subir directo de forma puntual, tendrías que desactivar o ajustar la regla en la configuración del repo, aunque en un proyecto con equipo casi nunca es buena idea.
¿git pull o git fetch + git merge para resolver el non-fast-forward?
Las dos opciones traen el trabajo remoto, pero te dan control distinto. git pull combina la descarga y la integración en un solo paso, y con --rebase mantiene el historial lineal. git fetch seguido de git merge separa las dos fases, así puedes inspeccionar qué llegó del remoto antes de mezclarlo. Si quieres entender bien cuándo usar cada uno, lo desarrollo en la diferencia entre git pull y git fetch.
¿Qué es un fork y cuándo lo necesito?
Un fork es tu copia del repositorio, bajo tu cuenta de GitHub y con permiso de escritura. Lo necesitas cuando quieres contribuir a un proyecto en el que no eres colaborador, típico del open source: te da un 403 al pushear al original, así que pusheas a tu fork y desde ahí abres un Pull Request al repo de origen. Si es un repo de tu propio equipo, normalmente no hace falta fork, basta con que te añadan como colaborador con permiso de escritura.