Cómo Gardenscapes estuvo a punto de frustrarse una vez

Descargo de responsabilidad: esta historia sucedió hace varios años. Pero parece que todavía no ha perdido su relevancia.





... Desarrollamos Gardenscapes. Todavía tenía rastros de los viejos Gardenscapes debajo de Windows. Ni siquiera era Match-3, sino un objeto oculto. Y nadie ni siquiera podía imaginar las alturas que alcanzaría el juego.



Y luego un buen día ...



Cómo todo empezó



Al acceder al repositorio, vimos el siguiente mensaje:



“Este repositorio ha sido deshabilitado. El personal de GitHub ha desactivado el acceso a este repositorio debido al uso excesivo de recursos, en violación de nuestros Términos de servicio. Comuníquese con el soporte para restaurar el acceso a este repositorio. Lea aquí para obtener más información sobre cómo reducir el tamaño de su repositorio ".



Como habrás adivinado, usamos github para alojar repositorios de git. Y así, de repente y sin declarar la guerra, github bloqueó nuestro repositorio por exceder el tamaño máximo permitido. La cifra exacta no se dio en su sitio web. En el momento del bloqueo, la carpeta .git tenía un tamaño de aproximadamente 25 GB. (Nota 2020: los límites ahora son más altos y el sitio de github establece explícitamente que el tamaño del repositorio no debe exceder los 100 GB).



¿Cómo logramos hacer un repositorio tan grande? La razón es clara: almacenamos archivos binarios en él. Está escrito en todas partes que no se recomienda hacer esto, pero es mucho más fácil para nosotros. Queremos que el juego se lance desde el repositorio inmediatamente, sin esfuerzo adicional. Por lo tanto, asignamos gráficos y otros recursos del juego al repositorio.



Pero esto no es tan malo. Una lección importante que aprendimos de toda esta historia: nunca le cuentes a nadie sobre Fight Club, no puedes enviar binarios al repositorio con archivos que cambian con frecuencia. E hicimos esto: comprometimos el archivo ejecutable y los atlas de texturas. Ahora nos hemos vuelto mucho más inteligentes y tenemos Teamcity, que puede compilar un binario y crear atlas, además de scripts especiales que descargan todo esto para el usuario. Pero esa es una historia completamente diferente... Y para archivos muy grandes, usamos Git LFS, Google Drive y otros beneficios de la civilización.



Lucha por la historia



Entonces, nada funciona para nadie. Le dijimos al equipo que tendrían que trabajar localmente por un día, pero no esforzarse mucho, de lo contrario resolverían los conflictos más tarde (todos estaban muy molestos y se fueron inmediatamente a tomar el té). Y empezaron a pensar qué hacer. Está claro que se necesita un nuevo repositorio, pero ¿qué confirmar allí? Una forma sencilla es el estado actual de todas las ramas. Pero no nos gustó mucho, porque el historial de cambios se perderá, el comando git blame favorito de todos se romperá y todo dará un salto mortal. Por lo tanto, decidimos hacer esto: borrar el historial de archivos binarios y mantener el historial de archivos de texto.





Paso 1. Eliminar el historial de binarios



Teníamos una copia local completa del repositorio. Lo primero que encontramos fue la excelente utilidad BFG Repo-Cleaner . Es muy simple y muy rápido al mismo tiempo, y el nombre es bueno.



Un escenario de ejecución de ejemplo:



java -jar bfg.jar bfg --delete-files *.{pvrtc,webp,png,jpeg,fla,swl,swf,pbi,bin,mask,ods,ogv,ogg,ttf,mp4} path_to_repository


Los parámetros contienen todas las extensiones de los archivos binarios que se nos ocurrieron. De todas las confirmaciones del mundo, se eliminará la información sobre archivos con estas extensiones. La utilidad es inteligente y al borrar el historial de un archivo deja su versión más reciente. Además, esta última versión se incluirá en la confirmación más reciente de la rama. También queríamos eliminar el historial de archivos exe y dll, pero la utilidad dio un error. Aparentemente, por alguna razón, el procesamiento en forma de * .exe está prohibido. Además, si especifica explícitamente un archivo, por ejemplo, gardenscapes.exe, entonces todo funciona. (Nota 2020: es posible que el error ya se haya solucionado).



Paso 2. Comprimir el repositorio



Después del primer paso, el tamaño del repositorio sigue siendo grande. La razón de esto es la forma en que funciona git. Eliminamos solo los enlaces a los archivos, pero los archivos permanecieron.



Para eliminar físicamente los archivos, debe ejecutar el comando git gc, a saber:



git reflog expire --expire=now --all


 y que:



git gc --prune=now --aggressive


Esta es la secuencia de comandos recomendada por el autor de la utilidad. Aquí gc realmente lleva mucho tiempo. Además, con la configuración predeterminada del repositorio, el cliente git no tiene suficiente memoria para completar la operación y necesita bailar con una pandereta. (Nota 2020: en ese momento teníamos una versión de git de 32 bits. Lo más probable es que estos problemas ya no estén en la versión de 64 bits).



Paso 3. Escribir confirmaciones en el nuevo repositorio



Esta resultó ser la parte más interesante de la búsqueda. 



Para comprender lo que sigue, debe comprender cómo funciona git. Puedes leer más sobre git en muchos lugares, incluido nuestro blog:



  1. Git: Consejos para principiantes - Parte 1
  2. Git: Consejos para principiantes - Parte 2
  3. Git: Consejos para principiantes - Parte 3


Entonces, tenemos muchísimas confirmaciones localmente, estas confirmaciones son correctas, es decir, sin el historial de binarios. Parecería que basta con ejecutar git push y todo funcionará solo. ¡Pero no!



Si solo ejecuta el comando git push -u master, luego git comienza alegremente el proceso de carga de datos al servidor, pero falla con un error de aproximadamente 2 GB. Esto significa que no podrá cargar tantas confirmaciones de una sola vez. Nos comeremos el elefante en partes. Pensamos que 2000 confirmaciones probablemente cabrían en 2GB. El volumen total de nuestro repositorio era entonces de aproximadamente 20.000 confirmaciones, distribuidas entre 4 ramas: master-v101-v102-v103. (Nota 2020: ¡eh, joven! Desde entonces, todo se ha vuelto mucho más serio. Ya hay más de 100,000 confirmaciones en este repositorio, y hay varias docenas de ramas de lanzamiento. Al mismo tiempo, todavía encajamos en los límites de Github) En



primer lugar, consideramos el número de confirmaciones en las ramas cuando comando de ayuda:



git rev-list --count <branch-name>


Por ejemplo, hay aproximadamente 10,000 confirmaciones en la rama maestra. Ahora podemos usar la sintaxis extendida para el comando git push, a saber:



git push -u origin HEAD~8000:refs/origin/master


HEAD ~ 8000: refs / origin / master es el denominado refspec. El lado izquierdo dice que necesita tomar confirmaciones hasta una confirmación que esté a 8,000 de HEAD, es decir, aproximadamente 2,000 confirmaciones. Y el lado derecho es que debe enviarlos a la rama maestra remota. Aquí se necesita la ruta completa a la rama refs / origin / master.



Después de eso, todavía no hay una rama maestra y, por ejemplo, git fetch no podrá descargarla. Esto no es sorprendente, después de todo, el compromiso que apunta a su HEAD aún no existe. Sin embargo, al repetir el comando git push HEAD ~ 8000: refs / origin / master , vimos la respuesta de que estas confirmaciones ya están en el servidor y, por lo tanto, el trabajo está hecho.



A continuación, pensamos que el proceso está claro y el resto del trabajo se puede asignar al guión. La última confirmación será muy grande, ya que contendrá todos los binarios. Por lo tanto, por si acaso, las últimas 10 confirmaciones se completan por separado. El guión resultó así:



git push origin HEAD~6000:refs/origin/master
git push origin HEAD~5000:refs/origin/master
git push origin HEAD~4000:refs/origin/master
git push origin HEAD~3000:refs/origin/master
git push origin HEAD~2000:refs/origin/master
git push origin HEAD~1000:refs/origin/master
git push origin HEAD~10:refs/origin/master
git push origin master
 
git checkout v101
 
git push -u origin HEAD~1000:refs/origin/v101
git push origin HEAD~10:refs/origin/v101
git push origin v101
 
git checkout v102
…  ..


Es decir, escribimos constantemente todas nuestras ramas en el servidor, 2,000 confirmaciones por inserción y las últimas 10 confirmaciones por separado.



Toda esta historia tomó mucho tiempo, y el reloj se mostró más cerca de las 12 de la noche. Así que dejamos que el guión funcionara durante la noche, dijimos las oraciones adecuadas a Cthulhu (Nota 2020: todavía era relativamente popular en ese momento) y nos fuimos a casa. 



El final. Final feliz



Por la mañana, después de abrir el repositorio en el sitio de github, nos aseguramos de que el script funcionara correctamente y de que todas las confirmaciones y ramas estuvieran en su lugar.



Como resultado: el tamaño del repositorio (carpeta .git) se ha reducido de 25 GB a 7,5 GB. Al mismo tiempo, se conserva todo el historial de confirmación importante, todo excepto los binarios. Los diseñadores del juego bebieron más té de lo habitual. Los programadores tuvieron una experiencia inolvidable. Y empezaron a pensar urgentemente en cómo hacerlo para que no fuera necesario comprometer el archivo ejecutable en el repositorio, pero sería conveniente trabajar con él.



All Articles