tipos tag remove qué practices last existen etiquetas crear best git large-files gitlab

qué - git tag remove



Git con archivos grandes (6)

Situación

Tengo dos servidores, producción y desarrollo. En el servidor de producción, hay dos aplicaciones y múltiples (6) bases de datos (MySQL) que necesito distribuir a los desarrolladores para probarlas. Todos los códigos fuente se almacenan en el servidor de GitLab on Development y los desarrolladores solo trabajan con este servidor y no tienen acceso al servidor de producción. Cuando lanzamos una aplicación, el maestro inicia sesión en la producción y extrae una nueva versión de Git. Las bases de datos son grandes (más de 500M cada una y cuentan) y necesito distribuirlas lo más fácilmente posible a los desarrolladores para que las prueben.

Soluciones posibles

  • Después de una secuencia de comandos de copia de seguridad que vuelca bases de datos, cada una a un único archivo, ejecuta una secuencia de comandos que empuja a cada base de datos a su propia rama. Un desarrollador tira de una de estas ramas si quiere actualizar su copia local.

    Este fue encontrado no funciona.

  • Cron en el servidor de producción guarda los registros binarios todos los días y los empuja a la sucursal de esa base de datos. Entonces, en la sucursal, hay archivos con cambios diarios y el desarrollador extrae los archivos que no tiene. El volcado de SQL actual se enviará al desarrollador de otra manera. Y cuando el tamaño del repositorio sea demasiado grande, enviaremos un volcado completo a los desarrolladores y descartaremos todos los datos en el repositorio y comenzaremos desde el principio.

Preguntas

  • ¿Es posible la solución?
  • Si git está presionando / tirando hacia / desde el repositorio, ¿carga / descarga archivos completos, o simplemente cambios en ellos (es decir, agrega nuevas líneas o edita las actuales)?
  • ¿Puede Git gestionar archivos tan grandes? No.
  • ¿Cómo establecer cuántas revisiones se conservan en un repositorio? No importa con la nueva solución.
  • ¿Hay alguna solución mejor? No quiero obligar a los desarrolladores a descargar archivos tan grandes a través de FTP o algo similar.

Actualización 2017:

Microsoft está contribuyendo a Microsoft/GVFS : un sistema de archivos virtual Git que permite a Git manejar " el repositorio más grande del planeta "
(es decir, la base de código de Windows, que es de aproximadamente 3.5 millones de archivos y, cuando se registra en un repositorio de Git, genera un repositorio de aproximadamente 300 GB y produce 1.760 "compilaciones de laboratorio" diarias en 440 sucursales, además de miles de solicitudes de extracción) construcciones de validación)

GVFS virtualiza el sistema de archivos debajo de su git repo para que git y todas las herramientas vean lo que parece ser un repositorio normal, pero GVFS solo descarga objetos a medida que son necesarios.

Algunas partes de GVFS pueden ser aportadas en sentido ascendente (a Git).
Pero mientras tanto, todo el nuevo desarrollo de Windows es ahora (agosto de 2017) en Git .

Actualización de abril de 2015: GitHub propone: Anunciar Git Large File Storage (LFS)

Usando git-lfs (vea git-lfs.github.com ) y un servidor que lo soporte: lfs-test-server , puede almacenar metadatos solo en el git repo, y el archivo grande en otro lugar.

Ver git-lfs/wiki/Tutorial :

git lfs track ''*.bin'' git add .gitattributes "*.bin" git commit -m "Track .bin files"

Respuesta original:

Con respecto a las limitaciones de git con archivos grandes , puedes considerar bup (presentado en detalles en GitMinutes # 24 )

El diseño de bup destaca los tres problemas que limitan un repo de git:

  • archivos enormes (el archivo xdelta para packfile solo está en la memoria, lo cual no es bueno con archivos grandes)
  • gran cantidad de archivos , lo que significa, un archivo por blob, y slow git gc para generar un packfile a la vez.
  • enormes archivos de paquete , con un índice de archivo de paquete ineficaz para recuperar datos del (enorme) archivo de paquete.

Manejo de archivos enormes y xdelta

La razón principal por la que git no puede manejar archivos grandes es que los ejecuta a través de xdelta , lo que generalmente significa que intenta cargar todo el contenido de un archivo en la memoria a la vez .
Si no hiciera esto, tendría que almacenar todo el contenido de cada revisión de cada archivo, incluso si solo cambiaba algunos bytes de ese archivo.
Ese sería un uso terriblemente ineficiente del espacio en disco , y git es bien conocido por su formato de repositorio increíblemente eficiente.

Desafortunadamente, xdelta funciona muy bien para archivos pequeños y se vuelve increíblemente lento y tiene mucha memoria para archivos grandes .
Para el propósito principal de git, es decir. administrar su código fuente, esto no es un problema.

Lo que hace bup en lugar de xdelta es lo que llamamos " hashsplitting ".
Queríamos una forma de propósito general para hacer una copia de respaldo eficiente de cualquier archivo grande que pudiera cambiar en pequeñas formas, sin almacenar todo el archivo cada vez. Leemos el archivo un byte a la vez, calculando una suma de comprobación progresiva de los últimos 128 bytes.

Parece que a rollsum va bastante bien en su trabajo. Puede encontrarlo en bupsplit.c .
Básicamente, convierte los últimos 128 bytes leídos en un entero de 32 bits. Lo que hacemos entonces es tomar los 13 bits más bajos del rollo, y si son todos 1, consideramos que es el final de un pedazo.
Esto ocurre en promedio una vez cada 2^13 = 8192 bytes , por lo que el tamaño promedio del fragmento es 8192 bytes.
Estamos dividiendo esos archivos en fragmentos según la suma de comprobación progresiva.
Luego almacenamos cada fragmento por separado (indexado por su sha1sum) como un blob git.

Con hashsplitting, no importa la cantidad de datos que agregue, modifique o elimine en el medio del archivo, todos los fragmentos antes y después del fragmento afectado son absolutamente iguales.
Lo único que importa para el algoritmo hashsplitting es la secuencia de "separación" de 32 bytes, y un único cambio solo puede afectar, a lo sumo, una secuencia separadora o los bytes entre dos secuencias separadoras.
Al igual que la magia, el algoritmo de fragmentación hashsplit dividirá su archivo de la misma manera todas las veces, incluso sin saber cómo lo había fragmentado previamente.

El siguiente problema es menos obvio: después de almacenar su serie de fragmentos como git blobs, ¿cómo almacena su secuencia? Cada blob tiene un identificador sha1 de 20 bytes, lo que significa que la lista simple de blobs va a ser 20/8192 = 0.25% de la longitud del archivo.
Para un archivo de 200 GB, son 488 megas de datos de secuencia.

Extendemos un poco más el algoritmo hashsplit usando lo que llamamos "fanout". En lugar de verificar solo los últimos 13 bits de la suma de comprobación, usamos bits de suma de comprobación adicionales para producir divisiones adicionales.
Lo que terminas con es un árbol real de blobs, que los objetos ''tree'' de git son ideales para representar.

Manejando una gran cantidad de archivos y git gc

git está diseñado para manejar repositorios de tamaño razonable que cambian con relativa poca frecuencia . Puede pensar que cambia su código fuente "con frecuencia" y que git maneja cambios mucho más frecuentes que, por ejemplo, svn puede manejar.
Pero ese no es el mismo tipo de "frecuentemente" de lo que estamos hablando.

El asesino n. ° 1 es la forma en que agrega nuevos objetos al repositorio: crea un archivo por blob. Luego, luego ejecuta ''git gc'' y combina esos archivos en un solo archivo (usando la compresión xdelta altamente eficiente e ignorando los archivos que ya no son relevantes).

'' git gc '' es lento , pero para los repositorios de código fuente, el almacenamiento súper eficiente resultante (y el acceso realmente rápido asociado a los archivos almacenados) lo vale.

bup no hace eso. Simplemente escribe paquetes de archivos directamente.
Afortunadamente, estos paquetes de archivos aún están formateados con git, por lo que git puede acceder a ellos una vez que estén escritos.

Manejo de enormes repositorios (lo que significa un gran número de grandes paquetes de archivos)

Git no está diseñado para manejar repositorios supergrandes .
La mayoría de los repositorios de git son lo suficientemente pequeños como para fusionarlos en un solo archivo de paquete, lo que '' git gc '' suele hacer eventualmente.

La parte problemática de los grandes paquetes no son los archivos packet mismos: git está diseñado para esperar que el tamaño total de todos los paquetes sea mayor que la memoria disponible, y una vez que puede manejar eso, puede manejar virtualmente cualquier cantidad de datos con la misma eficiencia.
El problema son los archivos de índices de archivos de paquete ( .idx ) .

cada paquete ( *.pack ) en git tiene un idx asociado ( *.idx ) que es una lista ordenada de hash de objetos git y compensaciones de archivos.
Si está buscando un objeto particular basado en su sha1, abra el idx, busque binario para encontrar el hash correcto, luego tome el desplazamiento del archivo asociado, busque ese desplazamiento en el archivo de paquete y lea el contenido del objeto.

El rendimiento de la búsqueda binaria se basa en O(log n) con el número de hashes en el paquete, con un primer paso optimizado (puede leer sobre él en otro lugar) que de alguna manera lo mejora en O(log(n)-7) .
Desafortunadamente, esto se rompe un poco cuando tienes muchos paquetes .

Para mejorar el rendimiento de este tipo de operación, bup introduce midx (pronunciado "midix" y abreviatura de "multi-idx").
Como su nombre lo indica, indexan múltiples paquetes a la vez.


Los archivos grandes que se cargan en algún momento crean problemas y errores. Esto sucede generalmente. Principalmente, git admite menos de 50 MB de archivos para cargar. Para cargar archivos de más de 50MB en el repositorio de git, el usuario debería necesitar instalar otro asistente que colabore para cargar archivos grandes (.mp4, .mp3, .psd), etc.

hay algunos comandos git básicos que conoces antes de subir un archivo grande en git. esta es la configuración para cargar en github. necesita instalar gitlfs.exe

intall it desde lfsinstall.exe



entonces deberías usar comandos básicos de git junto con algunos diferentes

git lfs install git init git lfs track ".mp4" git lfs track ".mp3" git lfs track ".psd" git add . git add .gitattributes git config lfs.https://github.com/something/repo.git/info/lfs.locksverify false git commit -m "Add design file" git push origin master` ones

puede encontrar que lo encuentra lfs.https://github.com/something/repo.git/info/lfs.locksverify false como las instrucciones durante el comando push si push sin usarlo


Puede ver soluciones como git-annex , que trata de administrar archivos (grandes) con git, sin verificar el contenido del archivo en git (!)
(Febrero de 2015: un servicio de hosting como GitLab lo integra de forma nativa :
Consulte " ¿Admite GitLab archivos grandes a través de git-annex o de lo contrario? ")

git no administra archivos grandes, como explica en su respuesta .

Eso no significa que git no podrá hacerlo mejor un día sin embargo.
Del episodio 9 de GitMinutes ( mayo de 2013, ver también más abajo) , From Peff (Jeff King) , en 36''10 '''':

(transcripción)

Hay un reino de repositorios grandes donde la gente está interesada en almacenar, ya sabes, 20, 30 o 40 GB, en algún momento incluso repositorios del tamaño de una TB, y sí proviene de tener muchos archivos, pero viene mucho de esto de tener archivos realmente grandes y archivos binarios realmente grandes que no se manejan tan bien con los demás.

Eso es una especie de problema abierto. Hay un par de soluciones: git-annex es probablemente la más madura de todas, donde básicamente no ponen el activo en git, ponen el activo grande en un servidor de activos, y ponen un puntero en git.

Me gustaría hacer algo así, donde el activo está conceptualmente en git, es decir, el SHA1 de ese objeto es parte del SHA1 que entra en el árbol, que va dentro del ID de confirmación y todas esas cosas.
Entonces, desde la perspectiva de git, es parte del repositorio, pero en un nivel inferior, en el nivel de almacenamiento de objetos, en un nivel por debajo del gráfico de historia conceptual , donde ya tenemos múltiples maneras de almacenar un objeto: tenemos objetos sueltos , He empaquetado objetos , me gustaría tener tal vez una nueva forma de almacenar un objeto, es decir, "no lo tenemos aquí, pero está disponible por un servidor de activos", o algo así.

( Thomas Ferris Nicolaisen ) Oh genial ...

El problema con cosas como git-annex es: una vez que las utilizas, estás ... encerrado en las decisiones que tomaste en ese momento para siempre. Ya sabes, si decides oh 200 MB es grande, y vamos a almacenar en un servidor de activos, y luego, más tarde decides, aah debería haber sido 300 MB , buena suerte: eso está codificado en tu historia para siempre.
Y así, al decir conceptualmente , en el nivel de git, este objeto está en el repositorio de git, no en algún puntero a él, no en un puntero a un servidor de activos, el objeto real está allí, y luego cuidando esos detalles a un bajo nivel. nivel, en el nivel de almacenamiento, eso lo libera para tomar muchas decisiones diferentes, e incluso puede cambiar su decisión más tarde sobre cómo realmente desea almacenar las cosas en el disco.

No es un proyecto de alta prioridad por ahora ...

3 años después, en abril de 2016, Git Minutes 40 incluye una entrevista de Michael Haggerty de GitHub alrededor de 31 ''(Gracias Christian Couder por la entrevista ).

Está especializado en back-end de referencia desde hace bastante tiempo .
Él está citando el trabajo de David Turner en back-end como el más interesante en este momento. (Vea la rama actual de David " pluggable-backends " de su horquilla git / git )

(transcripción)

Christian Couder (CD): ¿El objetivo es tener git refs almacenados en una base de datos, por ejemplo? Michael Haggerty (MH): Sí, lo veo como dos aspectos interesantes: el primero es simplemente tener la capacidad de conectar diferentes referencias de entrada de fuente. Las referencias de entrada se almacenan en el sistema de archivos, como una combinación de referencias sueltas y referencias empaquetadas .
La referencia suelta es un archivo por referencia, y la referencia empaquetada es un archivo grande que contiene una lista de muchas referencias.

Entonces ese es un buen sistema, especialmente para un uso local; ya que no tiene ningún problema real de rendimiento para las personas normales, pero tiene algún problema, como que no puede almacenar reflogs de referencias una vez que se han eliminado las referencias, ya que puede haber conflictos con las referencias más recientes que se han creado con similares nombres. También hay un problema donde los nombres de referencia se almacenan en el sistema de archivos para que pueda tener referencias que se llaman similares pero con mayúsculas diferentes.
Entonces, esas son cosas que podrían arreglarse teniendo un sistema de back-end de referencia diferente en general.
Y el otro aspecto de la serie de parches de David Turner es un cambio para almacenar referencias en una base de datos llamada lmdb , esta es una base de datos realmente rápida basada en memoria que tiene algunas ventajas de rendimiento sobre el back-end del archivo.

[sigue otras consideraciones acerca de tener un embalaje más rápido y un anuncio de parche de referencia]


Realmente, de verdad, no quieres que los archivos binarios sean revisados ​​en tu repositorio de Git.

Cada actualización que agregue se agregará acumulativamente al tamaño total de su repositorio, lo que significa que, en el futuro, su repositorio de Git tardará más y más en clonar y usar más y más espacio en disco, porque Git almacena todo el historial de la sucursal localmente. lo que significa que cuando alguien visita la sucursal, no solo tiene que descargar la última versión de la base de datos; también tendrán que descargar todas las versiones anteriores.

Si necesita proporcionar archivos binarios grandes, cárguelos en algún servidor por separado y luego ingrese un archivo de texto con una URL donde el desarrollador puede descargar el archivo binario grande. FTP es en realidad una de las mejores opciones, ya que está específicamente diseñado para transferir archivos binarios, aunque HTTP es probablemente aún más sencillo.


Tener un almacenamiento auxiliar de los archivos a los que se hace referencia desde su código guardado de git es a donde va la mayoría de la gente. git-annex se ve muy completo, pero muchas tiendas simplemente usan un repositorio FTP o HTTP (o S3) para los archivos grandes, como volcados de SQL. Mi sugerencia sería vincular el código en el git repo a los nombres de los archivos en el almacenamiento auxiliar rellenando algunos de los metadatos, específicamente una suma de verificación (probablemente SHA), en el hash, así como una fecha.

  • De modo que cada archivo auxiliar obtiene un nombre base, una fecha y una suma SHA (para algunas versiones n).
  • Si tiene rotación de archivos wild, usar solo SHA representa una pequeña pero real amenaza de colisión hash, de ahí la inclusión de una fecha (hora de época o fecha ISO).
  • Coloque el nombre de archivo resultante en el código, de modo que el fragmento auxiliar esté incluido, muy específicamente, por referencia.
  • Estructura los nombres de tal manera que un pequeño script se pueda escribir fácilmente para convertir todos los nombres de archivos auxiliares, de modo que la lista de cualquier confirmación sea trivial. Esto también permite que los antiguos se retiren en algún momento, y se pueden integrar con el sistema de implementación para extraer los nuevos archivos auxiliares a producción sin golpear los antiguos (aún), antes de activar el código del repositorio git.

Aterrizar enormes archivos en git (o la mayoría de repositorios) tiene un impacto desagradable en el rendimiento de git después de un tiempo: un git clone realmente no debería tomar veinte minutos, por ejemplo. Considerando que el uso de los archivos por referencia significa que algunos desarrolladores nunca tendrán que descargar los fragmentos grandes en absoluto (un fuerte contraste con el git clone ), ya que las probabilidades son que la mayoría solo son relevantes para el código desplegado en producción. Su kilometraje puede variar, por supuesto.


rsync podría ser una buena opción para actualizar de manera eficiente las copias de las bases de datos de los desarrolladores.

Utiliza un algoritmo delta para actualizar incrementalmente los archivos. De esta forma solo transfiere los bloques del archivo que han cambiado o que son nuevos. Por supuesto, todavía necesitarán descargar el archivo completo primero, pero las actualizaciones posteriores serán más rápidas.

Básicamente, obtienes una actualización incremental similar a la de un git fetch sin la copia inicial en expansión que el clon git daría. La pérdida no es tener la historia, pero parece que no la necesitas.

rsync es una parte estándar de la mayoría de las distribuciones de Linux, si lo necesita en Windows, hay un puerto empaquetado disponible: http://itefix.no/cwrsync/

Para enviar las bases de datos a un desarrollador, puede usar un comando similar a:

rsync -avz path/to/database(s) HOST:/folder

O los desarrolladores podrían extraer las bases de datos que necesitan con:

rsync -avz DATABASE_HOST:/path/to/database(s) path/where/developer/wants/it