¿Existe la posibilidad de agregar y confirmar files fuera de un repository git?

Tenemos files de text distribuidos en todos los rincones del sistema, y ​​planeábamos agregar todas las modificaciones realizadas en estos files a un repository git.

Cada vez que se realiza una modificación en estos files, se crea mediante un script. Entonces, planeábamos agregar nuevos commands a esa secuencia de commands para agregar los files a un repository de git. Pero, estas modificaciones son concurrentes.

Podríamos crear una ruta a cada file que representa la location original desde sus routes originales.

¿Existe la posibilidad de agregar estos files a un repository git al mismo time?

Como una operación atómica que une agregar + comprometer y apuntar a ambos: la ruta del file externo y su ruta corresponsal del repository. Algo como:

git --user="Script1 <script1@localhost>" --git-dir=/home/repo/filescollection.git/.git add --external-path=/home/user1/file.txt --repo-path=home_user1_files.txt

No, no es posible. Puede considerar crear un enorme repository de git, múltiples repositorys de git, repensar la estructura de su file para contenerlo todo en un directory (si es posible) o usar algo como Docker para crear una image de toda la computadora.

La respuesta es no y sí. 1

Si planea usar solo commands de "porcelana" de Git, es claramente "no", ya que funcionan con el concepto de un tree de trabajo (único) que contiene todos los files de formatting normal, más un índice (que contiene el estado actual) de ese tree de trabajo y construyendo el próximo commit). Hay un file HEAD que contiene la noción del nombre de la twig actual. Necesita al less dos commands de porcelana separados, en esta secuencia:

 git add <path> git commit <arguments> 

para actualizar el índice (único) de la versión (individual) del tree de trabajo del file en <path> , luego realice una confirmación utilizando ese índice y el HEAD actual. Git hará un locking de las cosas que actualiza al hacer la confirmación, pero necesitas que la secuencia de agregar y luego confirmar se muestre atómica, por lo que debes astackr tu propio locking sobre estas.

(Esto sigue siendo cierto incluso si usa los --work-tree y / o --git-dir para networkingirigir varias partes de varios pasos: el índice compartido debe permanecer estable entre los pasos "agregar" y "confirmar").

Por otro lado, si está dispuesto a salirse de la comodidad de la porcelana pura, puede hacer que el compromiso se haga como una entidad atómica, pero todavía está mirando una especie de raza, por lo que debe resolver eso antes de que el la respuesta realmente cambia de "no" a "sí". Para ver cómo funciona esto, debemos tomar git add y git commit steps.

Primero, git add es esencialmente git update-index . Podemos crear un nuevo índice privado temporal y poblarlo de algún compromiso específico que elijamos:

 commit_id=...insert some magic here, see below... export GIT_INDEX_FILE=$(mktemp) # remember to clean it up later too git read-tree $commit_id 

Ahora podemos replace cualquier file dado dentro de ese índice usando git update-index (o de hecho, el git add más familiar y confortable: la variable de entorno también funciona allí). Debido a que este es nuestro índice privado, está aislado de todos los demás processs que pueden estar modificando cualquier otro índice.

Ahora podemos hacer los pasos que hace el git commit :

 tree_id=$(git write-tree) 

Esto convierte el índice (que ahora es nuestro índice temporal) en un nuevo tree de nivel superior, con subtreees para cualquier subdirectory, todo basado en lo que leemos en el índice anterior (con git read-tree ) y actualizado (con git update-index o git add ). Este tree de nivel superior y todos los subtreees necesarios que aún no estaban en el repository ahora se almacenan en el repository. El nuevo object está a salvo de git gc automático durante el time de caducidad configurado (pnetworkingeterminado 14 días), por lo que este es el time que tenemos para finalizar nuestra confirmación. El command imprime el ID del nuevo tree en su salida estándar, que $tree_id variable $tree_id .

A continuación, debemos escribir un object de confirmación, refiriéndonos al tree que acabamos de crear, con un hash principal apropiado. El hash padre correcto obviamente es $commit_id . Debemos build un post de compromiso y luego ejecutar:

 new=$(git commit-tree -p $commit_id $tree_id < message_file) 

o similar. Esto escribe el object de confirmación en el repository y, al igual que git write-tree , imprime el ID del nuevo object, que capturamos en $new . (Tenga en count que este paso utiliza el autor y el nombre y el correo electrónico del -c user.name=... , que puede proporcionar como -c user.name=... y -c user.email=... arguments.)

Por último, y lo más importante, estamos listos para grabar este nuevo object en alguna parte. Aquí es donde debemos resolver nuestra raza (cada uno de los pasos de escritura de objects hizo su propio locking para asegurarse de que esa parte fuera apropiadamente atómica).

Supongo que le gustaría almacenar estos bajo algunos nombres de twig, y ​​que estos nombres de twig pueden ser leídos y actualizados por otros processs. (Si son de solo lectura, nunca actualizados por otra cosa, ahora estamos en casa sin cargo.) Tenemos una operación de actualización atómica, en forma de git update-ref :

 git update-ref [-m <reason>] <refname> <newvalue> <oldvalue> 

La parte opcional -m <reason> se almacena en el reflog, si hay un reflog para esta reference. (Este paso también utiliza user.name y user.email , así que user.email aquí si lo desea). La parte refname es el nombre completo de la reference, por ejemplo, refs/heads/branch para branch branch . La parte de valor nuevo es la ID de hash que queremos almacenar, y la parte de valor antiguo , que suministraremos para verificar las razas, es el valor que esperamos que almacene esa twig en este momento.

Ahora, asumiendo que estamos compitiendo con algún otro process, hay dos posibles casos:

  • Ganamos la carrera: el tree que leemos, al principio, es el tree que va con el compromiso que se encuentra actualmente en la punta de la twig. Nuestro compromiso, por lo tanto, está listo para ser agregado a la sucursal, de manera lineal y directa.

o:

  • Perdimos la carrera: el tree que leemos, al inicio, es válido, pero el nombre de la twig ahora apunta a una nueva confirmación. Nuestro compromiso es, por lo tanto, inútil, o necesita ser puesto en una twig lateral, o algo así. Podríamos comenzar de nuevo y hacer todo de nuevo, si nuestro compromiso es realmente inútil: quizás esta vez ganemos la carrera.

Qué hacer con el caso de "perder la carrera" depende de usted. Pero ahora vemos de dónde viene la "magia": el ID de compromiso que queremos, cuando comenzamos todo este process, es el hash de compromiso actual asociado con la reference. Entonces, la "magia" es justa:

 commit_id=$(git rev-parse $refname) 

que lee el valor actual de la reference (si es un nombre de twig, podemos suponer que el tipo del object subyacente es commit ).

Dado que el paso de update-ref tiene su propia atomicidad (impuesta mediante locking), de ahí obtenemos nuestra atomicidad. La cuestión de qué hacer con los fallos , sin embargo, es la parte difícil. Recuerde también considerar, y hacer algo al respecto, fallas en cada paso intermedio, por ejemplo, si git rev-parse falla, o cualquiera de git read-tree o git write-tree o git commit-tree fallan también.


1 No vayan a los Elfos a pedir consejo, porque dirán tanto no como sí.