Eliminación de un binario grande del historial de Git para administrar el tamaño de los repositorios clonados

Servicios de Azure DevOps | Azure DevOps Server | Azure DevOps Server 2022

Git es un repositorio de código fuente distribuido (repositorio) popular que permite a los usuarios trabajar con el repositorio completo mientras están en un estado desconectado. Las ventajas de Git están bien documentadas, pero ¿qué ocurre si necesita "revertir el reloj" en el repositorio principal? Esto no es intuitivo y requiere permisos elevados. Este requisito se espera para algo que afecte a cada usuario del repositorio.

¿Cómo puede revertir el repositorio central de forma segura?

Escenario de problema

Imagina que realiza un commit de un archivo grande, como un vídeo, a tu servidor Git. En un sistema de código fuente tradicional, es conveniente almacenar todo en un solo lugar y, a continuación, extraer lo que necesita. Con Git, todo el repositorio se clona en el equipo local de cada usuario. Con un archivo grande, todos los usuarios del proyecto también deben descargar los archivos grandes.

Con cada archivo grande adicional que se compromete al servidor, el problema solo crece. El repositorio se vuelve demasiado grande para ser eficaz para sus usuarios. Incluso si quita el archivo grande del repositorio local y vuelve a enviarlo, el archivo todavía existe en el historial del repositorio. Como resultado, el archivo todavía se descarga en el equipo local de todos los usuarios como parte del historial.

Captura de pantalla que muestra el cuadro de diálogo de Cambios en Team Explorer con un vídeo grande incluido entre los cambios.

Agregue un archivo grande al repositorio local.

Captura de pantalla que muestra el servidor y los repositorios locales, ambos con una copia de los archivos de vídeo grandes.

Después de realizar un commit en el repositorio local, el servidor también tiene el archivo de gran tamaño.

Congelar el repositorio

Para corregir el problema de un repositorio grande, debe empezar en el origen. En este escenario, el origen es el repositorio del servidor. Pida al equipo que deje de hacer push en el repositorio. Si se producen más inserciones durante este proceso, debe tener en cuentalas para que no pierda ningún dato.

Importante

En los siguientes pasos se quita el vídeo del historial de la rama, pero el archivo permanece en el historial del repositorio al clonar el repositorio desde Azure Repos. Quitar los archivos del historial de la rama impide que se actualicen y, en consecuencia, se cree otra versión del archivo grande en el repositorio.

Obtenga más información sobre cómo administrar archivos grandes en Git. Para obtener una explicación y una solución alternativa para este comportamiento al usar repositorios de Git de Azure Repos, consulte ¿Por qué la clonación de Visual Studio Team Services devuelve objetos antiguos sin referencia?.

Fusión mediante cambio de base y forzar el envío de cambios

Si nadie más del equipo realizó ningún cambio en el repositorio, que normalmente ocurre a través de un push, puede tomar la ruta fácil. Básicamente, hace que el repositorio local tenga el aspecto que desea que tenga (es decir, sin el archivo grande). A continuación, forzará los cambios en el servidor.

Es posible que tenga que clonar o corregir el repositorio local antes de comenzar este trabajo. Este proceso podría dar lugar a cambios o trabajos perdidos, por lo que debe continuar con precaución.

De forma predeterminada, puede modificar los archivos de proyecto locales e insertar cambios en el servidor, pero no puede realizar operaciones de nivel de servidor, como la eliminación o la rebasificación. Para continuar, necesitará forzar la inserción (es preferible) o permisos de administrador en el repositorio. Póngase en contacto con el administrador del proyecto para solicitar estos permisos o pida a alguien que ya lo tenga para ayudarle. Para obtener más información, vea Establecimiento de permisos de repositorios Git.

Captura de pantalla que muestra la consola: git push --force permissions.

A continuación, necesita hacer un rebase del repositorio.

  1. Usa git log para buscar los valores de hash del Algoritmo de Hash Seguro (SHA) de las confirmaciones más recientes. Necesitarás esta información pronto porque necesitas conocer el commit correcto más reciente. Para obtener esa información, abra un terminal de comandos de Git y escriba: lo siguiente.

    git log

    Como alternativa, puede obtener el hash SHA al ver el historial de ramas en Visual Studio Team Explorer.

    Captura de pantalla que muestra la opción Ver Historial de la rama principal.

  2. Abra una terminal de Git.

    Captura de pantalla que muestra la acción Abrir símbolo de sistema en el diálogo de Sincronización.

  3. Busque el número de hash sha de interés.

    Captura de pantalla que muestra el símbolo del sistema para la confirmación de vídeo.

    Necesita el SHA que comienza con 25b4.

    Recuerde que Git usa punteros para determinar dónde se encuentra en el repositorio la rama principal o actual. El estado del repositorio que le interesa se sitúa en algún momento pasado.

  4. Para retroceder en el tiempo y hacer que el estado anterior sea el nuevo estado actual, use el git rebase comando :

    git rebase -i <SHA hash of desired new current branch>

    Captura de pantalla que muestra el uso de rebase para quitar el archivo de vídeo.

    El -i interruptor proporciona seguridad adicional porque abre el historial en un editor. (En este artículo se usa una implementación de Git que abre el editor vi clásico en la línea de comandos de Windows. Es posible que lo recuerde si ha trabajado con un sistema basado en Unix).

  5. En este ejemplo, escriba:

    git rebase -i 25b4

  6. Después de que el editor se abra, quite todas las líneas pick excepto la rama que deseas conservar como la nueva cabecera. Cuando todo parezca correcto, en vi, escriba :w\<enter\> para guardar o entrar !q\<enter\> para salir sin guardar.

    Captura de pantalla que muestra el símbolo del sistema: comando git rebase -i 25b4 pick.

    Cambie las líneas que ya no desee.

    Captura de pantalla que muestra el símbolo del sistema: git rebase -i comando drop 25b4.

  7. Cambie pick a drop como se muestra. A continuación, escriba :w (en vi) para guardar y escriba :q! para iniciar el rebase.

    Vuelva a introducir git log ahora. La rama conflictiva debe estar ausente en el registro. Si es así, está listo para el paso final, que requiere permisos de administrador del proyecto:

    git log

    Captura de pantalla que muestra los repositorios locales y del servidor después de rebase.

    La confirmación del gran vídeo ya ha desaparecido del repositorio local.

  8. Escriba el comando siguiente:

    git push --force

    Captura de pantalla que muestra el símbolo del sistema: git push --force.

    Captura de pantalla que muestra la línea de comandos: git push --force resultado.

    Este comando obliga a tu repositorio a sobrescribir el repositorio en el servidor.

    Use este comando con precaución porque puede perder fácilmente datos en el servidor.

    Captura de pantalla que muestra un empuje forzado manteniendo contenido, sin el archivo de vídeo grande.

Debe autenticarse en el servidor para que esta acción funcione.

Si usa Azure Repos, es posible que tenga que configurar una credencial alternativa que no use caracteres especiales. Un ejemplo es el símbolo (@) en una dirección de correo electrónico. Para realizar esta tarea, siga las instrucciones de Autenticación con Azure Repos.

Ahora la rama se ha ido permanentemente del servidor. Los clones y sincronizaciones posteriores por los miembros del equipo del proyecto no descargan los archivos grandes que estaba intentando quitar. Los usuarios necesitan bajar del servidor para asegurarse de que están sincronizados con el nuevo estado del repositorio en el servidor.

Si los usuarios tienen confirmaciones más recientes

Si otros usuarios han enviado commits al repositorio del servidor, debe considerar otro aspecto. Quiere quitar la rama que contiene los archivos grandes, pero no quiere perder los cambios realizados por el equipo. Para solucionar esta situación, al abrir el editor como parte del rebase, examine detenidamente los commits. Asegúrese de que las confirmaciones que desea conservar aparezcan en las pick líneas. Elimine los que desea quitar, como dónde se agregó un archivo grande.

Después de rebasar, los demás usuarios del equipo también deben volver a basarse para que todos tengan una copia coherente del repositorio del servidor. Este trabajo es tedioso para todo el mundo y quieres evitarlo. Si necesita quitar un cambio, debe coordinarse con el equipo. Para más información sobre la rebasificación, consulte Bifurcación de Git: Rebasing.

La clave es asegurarse de que conoce las confirmaciones que desea y las confirmaciones que no desea. Revise el git log o el historial en su entorno de desarrollo integrado (como Visual Studio). Tome una nota meticulosa de los hashes SHA que se deben mantener y los que se deben quitar.

En escenarios en los que el archivo grande es antiguo y había ramas y fusiones posteriores, podría quitar el archivo mediante el git filter-branch conmutador. Para obtener más información, consulte Eliminación de datos confidenciales de un repositorio.

Consideraciones sobre procedimientos recomendados

Asegúrese de que los archivos grandes permanezcan fuera del repositorio principal para evitar trabajo adicional a los miembros del equipo. Estos son algunos procedimientos recomendados comunes para que el equipo tenga en cuenta.

Cosas que hacer

  • Confirmar los cambios con frecuencia. Siempre puede corregirlos después con un squash o un rebase.
  • Utilice ramificaciones para aislar sus cambios. Las ramas son baratas y privadas, y la fusión es sencilla. También puede realizar una copia de seguridad de los cambios en una rama insertandolos en el servidor.
  • Use una convención de nomenclatura al publicar ramas de temas. Asigne el nombre a la rama users/<alias>/<branchname>. Este nombre ayuda a agrupar las ramas y facilita que otros usuarios identifiquen al propietario.
  • Recuerde insertar los cambios. Use Commit != Checkin y (Commit + Push) == Checkin.
  • Considere la posibilidad de usar .gitignore para archivos binarios grandes para que no se agreguen al repositorio. Para obtener más información, consulte Omitir archivos.
  • Considere la posibilidad de usar el control de versiones de NuGet o Team Foundation Server para almacenar los archivos binarios grandes.

Cosas que se deben evitar

  • No fusione mediante cambio de base después de enviar cambios. Rebasear commits ya enviados en Git puede ser perjudicial porque obliga a todos los demás en el repositorio a rebasear sus cambios locales. Es posible que los miembros del equipo no estén satisfechos si necesitan realizar esta tarea. Rebasear los commits enviados en tu propia rama personal, incluso si están enviados, no es un problema a menos que otras personas extraen esos commits.
  • No suba archivos binarios a su repositorio. Git no comprime archivos binarios de la manera en que lo hace Team Foundation Version Control. Dado que todos los repos tienen todo el historial, incluir archivos binarios significa un sobrecrecimiento permanente.

Resumen

A veces, los elementos no deseados, como los archivos grandes, se agregan a un repositorio y se deben quitar para mantener el repositorio limpio y ligero. Para realizar esta tarea, asegúrese de que su repositorio local esté en orden. Use el git rebase comando y, a continuación, use el git push --force comando para sobrescribir el repositorio del servidor con el repositorio local.