git push –force y cómo solucionarlo

¿Te has encontrado alguna vez en una situación en la que un comando git erróneo ha causado estragos en el repo de tu proyecto? La gente comete errores, y a veces esos errores pueden costar horas de tu equipo. En este tutorial, te mostraremos cómo recuperarte de un desafortunado git push --force rápidamente.

No nos confiemos. Tarde o temprano, esto va a suceder. Mientras trabajas con varias remotas en el mismo repositorio git, eventualmente git push --force en master (u otra rama importante con la que nunca se debe jugar).

Eso puede ocurrir, por ejemplo, al desplegar con Deis o Heroku que utilizan remotas git separadas para construir y desplegar una aplicación. Después de un largo día de trabajo, es increíblemente fácil ejecutar git push --force en lugar del habitual git push --force deis master.

¡Oops! En un abrir y cerrar de ojos, sus compañeros de equipo han perdido todo su último trabajo. Es hora de enfrentarse a su rabia.

Sin embargo, como nos dice una excelente guía, ¡NO TE DEJES PÁNICO! Lo bueno es que usas git, y eso significa que todo se puede arreglar.

En el mejor de los casos: alguien más que está trabajando en el mismo código sacó una versión reciente del master justo antes de que tú lo rompieras. Entonces todo lo que tienes que hacer es entrar en el chat de tu equipo y pedirle a esa persona que fuerce el push de sus cambios recientes.

Si tienes suerte, su repositorio local tendrá el historial completo de commits, tu error se sobrescribirá con código fresco, y no se perderá nada. Pero, ¿y si no tienes esa suerte? Entonces, ¡sigue leyendo!

Caso 1: Fuiste la última persona en empujar a master antes del error

¡Buenas noticias! Tienes todo lo que necesitas para deshacer tu error ante tus propios ojos. Simplemente no cierres ni borres tu terminal. Primero, entra en el chat de tu equipo y confiesa tus pecados. Pídele a la gente que no se meta con el repo durante el próximo minuto o así mientras arreglas las cosas.

En la salida del comando git push --force en tu shell busca una línea que se parezca a esta:

 + deadbeef...f00f00ba master -> master (forced update)

El primer grupo de símbolos (que parece el prefijo SHA de un commit) es la clave del rescate. deadbeef es tu último commit bueno a la rama master justo antes de infligir el daño.

Así que todo lo que necesitas es… forzar el empuje (¡luchando fuego con fuego!) de este commit de vuelta a la rama master, encima de uno malo.

$ git push --force origin deadbeef:master

¡Felicidades! Has salvado el día. Ahora es el momento de aprender de tus errores.

Caso 2: el master fue cambiado por otra persona antes de que tú lo estropearas

Así que, justo antes de que hicieras git push --force alguien había cerrado un montón de pull requests, y el master ahora no se parece en nada a tu copia local. Ya no puedes hacer git push --force sha1:master porque no tienes commits recientes en local (y no puedes conseguirlos con git fetch porque ya no pertenecen a ninguna rama). Aun así, mantén la calma y pide a tus compañeros que se mantengan fuera del remoto durante un tiempo.

Nos beneficiaremos del hecho de que GitHub no elimina inmediatamente los commits inalcanzables. Por supuesto, tampoco puedes recuperarlos, pero hay una solución.

Ten en cuenta que sólo funcionará si estás «vigilando» el repositorio, de forma que todo lo que ocurra en él aparezca en un feed que se muestra en la portada de tu GitHub. Abre ese feed y busca algo como esto:

an hour agoUsername pushed to master at org/repo - deadbeef Implement foo - deadf00d Fix bar

Ahora puedes:

  • Componer una URL https://github.com/org/repo/tree/deadbeef, donde deadbeef es un hash del último commit bueno de la rama dañada;
  • Abrir el conmutador de ramas/etiquetas en la interfaz web de GitHub;

Conmutador de ramas/etiquetas de GitHub

Conmutador de ramas/etiquetas de GitHub

  • Crea un nombre para una nueva rama temporal (e.g., master-before-force-push);
  • Haga clic en «Crear rama».

Ahora puede recuperar todos los commits que faltan:

$ git fetchFrom github.com:org/repo * master-before-force-push -> origin/master-before-force-push

Su problema se reduce ahora al descrito en un ejemplo anterior:

$ git push --force origin origin/master-before-force-push:master

Si todavía necesitas que tu trabajo esté en el master, sólo tienes que hacer un rebase encima:

$ git rebase origin/master

Cómo evitar ese desastre en el futuro

  1. GitHub y GitLab tienen una característica llamada «ramas protegidas.» Marca master, develop, stable, o cualquier otra rama crucial como protegida y nadie podrá forzar el push en ellas. Siempre es posible «desproteger» temporalmente una rama si realmente necesita sobrescribir el historial.

    Lea los documentos de GitHub para más detalles.

  2. En lugar de la opción --force, utilice --force-with-lease. Detendrá la operación de empuje si alguien ha empujado a la misma rama mientras usted estaba trabajando en ella (y no ha sacado ningún cambio).

    Vea este artículo de Atlassian.

  3. Crea alias para los comandos que utilizan git push --force para evitar acciones destructivas por accidente:

    # ~/.gitconfig deploy = "!git push --force deis \"$(git rev-parse --abbrev-ref HEAD):master\""

    Lee el capítulo de ProGit sobre alias.

  4. ¡Nunca hagas tus experimentos en la rama principal de un repositorio! git checkout -b experiments es tu mejor amigo.

Consejo Pro: git push tiene muchas opciones. --force y --all trabajan juntos especialmente bien. Usted puede utilizar este combo al volver al proyecto después de varios meses de inactividad. Pruébalo si te sientes muy aventurero!

Leave a Reply