tipos tag practices etiquetas delete create best git egit large-files jgit git-filter-branch

etiquetas - git tags best practices



¿Es posible adelgazar un repositorio.git sin reescribir el historial? (4)

Tenemos varios repositorios de git que han crecido a un tamaño inmanejable debido a la inclusión histórica de archivos de prueba binarios y archivos java .jar .

Estamos a punto de pasar por el ejercicio de git filter-branch en estos repositorios, re-clonándolos en cualquier lugar que se usen (de docenas a cientos de implementaciones cada uno, dependiendo del repositorio) y teniendo en cuenta los problemas con la reescritura de la historia, me preguntaba. Si pudiera haber alguna otra solución.

Idealmente, me gustaría externalizar los archivos de problemas sin volver a escribir el historial de cada repositorio. En teoría, esto debería ser posible porque está verificando los mismos archivos, con los mismos tamaños y los mismos hashes, y los obtiene de un lugar diferente (un almacén de objetos local en lugar del remoto). Lamentablemente, ninguna de las soluciones potenciales que he encontrado hasta ahora parece permitirme hacer esto.

Comenzando con git-annex , lo más cerca que pude encontrar de una solución a mi problema fue cómo anexar retroactivamente un archivo que ya está en un repositorio de git , pero al igual que con solo eliminar los archivos grandes, esto requiere que se vuelva a escribir el historial para convertir el git add original git add en un git annex add .

A partir de ahí, comencé a ver otros proyectos enumerados en lo que no es git-annex , así que examiné git-bigfiles , git-media y git-fat . Desafortunadamente no podemos usar el tenedor git-bigfiles de git ya que somos una tienda de Eclipse y usamos una mezcla de git y EGit . Tampoco parece que git-media o git-fat pueden hacer lo que yo quiero, ya que si bien podría reemplazar archivos grandes existentes con equivalentes externos, aún tendría que volver a escribir el historial para eliminar archivos grandes que ya tenían ha cometido

Entonces, ¿es posible adelgazar un repositorio .git sin reescribir el historial, o deberíamos volver al plan de usar git filter-branch y una carga completa de redistribuciones?

Además, crea que esto debería ser posible, pero probablemente esté vinculado a las mismas limitaciones que las de la implementación actual de clones de poca profundidad de git .

Git ya admite múltiples ubicaciones posibles para el mismo blob, ya que cualquier blob dado podría estar en el almacén de objetos sueltos ( .git/objects ) o en un paquete de archivos (.git / objetos), por lo que teóricamente solo necesitaría algo como git-annex para estar conectado a ese nivel en lugar de hacerlo más arriba (es decir, tener el concepto de una descarga remota a pedido si lo desea). Desafortunadamente no puedo encontrar a nadie que haya implementado o sugerido algo como esto.


Honestamente no puedo pensar en una manera de hacer eso. Si piensa en lo que Git le "promete" a usted como usuario, con respecto a la integridad de los datos, no puedo pensar en una forma de eliminar un archivo del repositorio y mantener el mismo hash. En otras palabras, si lo que estás preguntando fuera posible, entonces Git sería mucho menos confiable ...


No conozco una solución que evite volver a escribir la historia.

En ese caso, limpiar la rpeo con una herramienta como BFG-repo cleaner es la solución más fácil (más fácil que git filter-branch ).


No, eso no es posible: tendrás que volver a escribir el historial. Pero aquí hay algunos consejos para eso:

  • Como mencionó VonC : Si se ajusta a su escenario, use BFG-repo cleaner - es mucho más fácil de usar que git filter-branch .
  • No necesitas clonar de nuevo! Simplemente ejecute estos comandos en lugar de git pull y estará bien (reemplace origin y master con su control remoto y rama):

    git fetch origin git reset --hard origin/master

    Pero tenga en cuenta que a diferencia de git pull , perderá todos los cambios locales que aún no se han enviado al servidor.

  • Ayuda mucho si usted (o alguien más en su equipo) entiende completamente cómo git ve la historia, y qué hacen git pull , git merge y git rebase (también como git rebase --onto ). Luego, brinde a todos los participantes una capacitación rápida sobre cómo manejar esta situación de reescritura (5-10 minutos deberían ser suficientes, lo que debe y no debe hacer).
  • Tenga en cuenta que git filter-branch no causa ningún daño en sí mismo, sino que provoca que muchos flujos de trabajo estándar causen daño. Si las personas no actúan en consecuencia y combinan la historia antigua, es posible que tenga que volver a escribir la historia nuevamente si no se da cuenta lo suficientemente pronto.
  • Puede evitar que las personas fusionen (empujando de manera más precisa) el historial anterior escribiendo (5 líneas) un enlace de actualización apropiado en el servidor. Solo verifique si el historial de la cabeza empujada contiene un antiguo compromiso específico.

Una especie de Puede usar la función de reemplazo de Git para dejar de lado el gran historial hinchado para que solo se descargue si es necesario. Es como un clon superficial, pero sin las limitaciones de un clon superficial.

La idea es que reinicies una rama creando una nueva confirmación de raíz, luego seleccionas la opción de confirmación de la antigua rama. Normalmente, perdería todo el historial de esta manera (lo que también significa que no tiene que clonar esos grandes archivos .jar ), pero si el historial es necesario, puede obtener los confirmos históricos y usar git replace para volver a unirlos sin problemas. .

Consulte la excelente publicación de Scott Chacon en el blog para obtener una explicación detallada y un recorrido.

Ventajas de este enfoque:

  • La historia no se modifica. Si necesita volver a un compromiso antiguo completo con .jars grandes y todo, todavía puede .jars .
  • Si no necesita mirar la historia antigua, el tamaño de su clon local es agradable y pequeño, y cualquier clonación reciente que realice no requerirá la descarga de toneladas de datos en su mayoría inútiles.

Desventajas de este enfoque:

  • El historial completo no está disponible de forma predeterminada: los usuarios deben saltar a través de algunos aros para obtener el historial.
  • Si necesita acceso frecuente al historial, de todos modos terminará descargando los compromisos hinchados.
  • Este enfoque todavía tiene algunos de los mismos problemas que la reescritura de la historia. Por ejemplo, si su nuevo repositorio se ve así:

    * modify bar (master) | * modify foo <--replace--> * modify foo (historical/master) | | * instructions * remove all of the big .jar files | * add another jar | * modify a jar |

    y alguien tiene una rama antigua fuera de la rama histórica en la que se fusionan:

    * merge feature xyz into master (master) |/__________________________ | / * modify bar * add feature xyz | | * modify foo <--replace--> * modify foo (historical/master) | | * instructions * remove all of the big .jar files | * add another jar | * modify a jar |

    luego, las grandes confirmaciones históricas volverán a aparecer en su repositorio principal y volverá a donde comenzó. Tenga en cuenta que esto no es peor que volver a escribir el historial, ya que alguien podría fusionarse accidentalmente en las confirmaciones de reescritura.

    Esto se puede mitigar agregando un enlace de update en su repositorio compartido para rechazar cualquier empuje que reintroduzca los comités de raíz históricos.