Git recupera un solo file del repository remoto programáticamente

Diré desde el principio que esta pregunta es similar en naturaleza a esto . Hay una diferencia key que lo hace único: quiero usar el protocolo raw git (consulte aquí y aquí si no está familiarizado con el protocolo básico de networking del package).

Estoy escribiendo una aplicación usando Scala y JGit que se conectará a un repository anónimo de git. Deseo solicitar un solo blob (piense en "/path/to/file.txt" @ "refs / heads / branch1"). En última instancia, mi objective es recuperar programáticamente un solo file de un repository remoto. Parece una cosa muy útil para poder hacer.

Anywho, he estado profundizando en los aspectos internos de este protocolo. Parece que la versión básica de esto es "Quiero estos objects, tengo estos objects" – y bam, hay un file de package con todo lo que no tiene. El núcleo de mi pregunta es esta: ¿cómo pido git-upload-packfile para un solo object de una manera no recursiva ? Estoy bien descargando un solo object de confirmación, luego solicitando el tree, luego un subtree, luego otro subtree y finalmente el blob. La velocidad no es demasiado importante aquí, principalmente estoy tratando de ahorrar en el ancho de banda. Pero parece que simplemente no hay forma de decirle a git-upload-packfile, "por favor solo dame el object que pedí".

Sí, está la list "tener", que básicamente excluirá que los objects caigan, sin embargo, eso requiere un conocimiento a priori de los contenidos de un repository (no tengo un repository local, restring). Podría generar una list de todos los posts posibles y enviarlos a todos excepto el que yo quiera, pero eso es más que ridículo (consume mucho time, consume ancho de banda y es un crimen contra los progtwigdores de todo el mundo)

Otra posible solución en la que he estado profundizando es utilizar git-upload-archive en el lado remoto, aunque admito que no he dedicado mucho time a investigarlo.

Estoy más que dispuesto a reescribir JGit si se trata de eso, así que no lo lean como "cómo hago que JGit lo haga …". Solo quiero saber si el protocolo en sí mismo es capaz de esto. Siento que hay alguna manera maravillosamente inteligente de abusar del protocolo para lograr lo que quiero. ¿Alguna idea?

Respondiendo mi propia pregunta. Encontré una respuesta aceptable (aunque apenas documentada). Tuve que search MUCHO código C para resolver esto.

En primer lugar, los requisitos anteriores no se pueden lograr usando git-upload-packfile porque simplemente no es para lo que el progtwig fue diseñado. La respuesta correcta como sospeché es git-upload-archive . Lamentablemente, el protocolo casi no está documentado en absoluto. Aquí están mis notas en caso de que alguien más tenga requisitos similares.

Básicamente, lo que bash simular aquí (en scala) es el siguiente command:

 git archive --format=tar --remote=ssh://dave@ssh.mycompany.com/cornballer.git \ > master plans/documents/cornballer-blueprint.pdf | tar -x 

Excepto en el software, con suerte utilizando JGit. Tristemente, JGit aún no admite los commands de file git. Así que aquí hay una descripción general de alto nivel sobre cómo agregar soporte (puedo bifurcar JGit y agregarlo en otro momento).

Miremos el protocolo (de Documentation / technical / pack-protocol.txt):

 git-proto-request = request-command SP pathname NUL [ host-parameter NUL ] request-command = "git-upload-pack" / "git-receive-pack" / "git-upload-archive" ; case sensitive pathname = *( %x01-ff ) ; exclude NUL host-parameter = "host=" hostname [ ":" port ] 

Entonces, la primera parte del protocolo es algo como esto:

  1. Establezca un transporte con el control remoto (ya sea ssh y luego ejecute git-upload-archive o use el protocolo git anónimo)
  2. Enviar git-upload-archive /cornballer.git\0host=ssh.mycompany.com\0 (como una línea de package)

En este punto, la connection está establecida. G Puede devolver un error si el command no es compatible o si hubo algún tipo de problema. Todavía no he descubierto cómo verificar esto.

Luego viene la parte no documentada. Básicamente enviamos arguments de línea de command para git-archive través del cable. Son exactamente los mismos que el command git-archive con una exception: todos tienen el prefijo argument[SPACE] . Cada argumento está escrito (al less en la implementación de reference) como una línea de package separada. Entonces para el ejemplo anterior:

  1. Enviar argument --format=tar (como una línea de package)
  2. Enviar argument master (como una línea de package)
  3. Enviar argument plans/documents/cornballer-blueprint.pdf (como una línea de package)
  4. Enviar un package de descarga ( 0000 )

En este punto, le hemos dado al process remoto de git-archive todo el command. Ahora leemos la respuesta. Leemos una línea de package desde el server, que será una de las siguientes respuestas:

  1. ACK (es decir, éxito: listo para enviar el file)
  2. NACK [message] – algún tipo de error, solo encontró una instancia de su uso – "no se puede generar el subprocess"
  3. ERR [message] – se produjo un error

Si se envía un ACK , será seguido por un package de descarga ( 0000 ) y luego los datos de tarificación sin procesar. En este punto, lee repetidamente las líneas de packages que ingresan en la banda lateral n. ° 1 (el canal de datos principal). Cuando alcanzas un package de descarga, dejas de leer. Bastante simple.

Entonces ahora tienes el file remoto, pero ¿y si quisieras hacer algún tipo de almacenamiento en caching inteligente? Una de las razones por las que estaba tan entusiasmado con el uso de git-upload-packfile es porque me permitía registrar el ID de confirmación y, por lo tanto, almacenarlo en la memory caching localmente y solo actualizarlo según fuera necesario. Un file tar no nos dice esa información ¿verdad? ¡Incorrecto!

Desde la página man de git-archive:

Además, el ID de confirmación se almacena en un encabezado pax extendido global si se utiliza el formatting tar; se puede extraer usando git get-tar-commit-id. En los files ZIP, se almacena como un comentario de file.

Bueno, eso es una gran noticia! Eso es literalmente todo lo que quería. En caso de que te estés preguntando cómo se ve el encabezado, aquí hay una muestra (no, no voy a diseccionar los encabezados de pax):

 pax_global_header00006660000000000000000000000064121002672560014513gustar00rootroot0000000000000052 comment=326756f834865880c9832b64238e7665632e9b67 

Entonces, desde mi punto de vista, simplemente necesito configurar una tubería para ejecutar automáticamente los pasos anteriores, ejecutarla a través de un paso untar (programáticamente) para llevar a cabo la funcionalidad deseada "recuperar un solo file desde git".