Git internals: Modificando `git-merge-one-file` para no usar el tree de trabajo

¿Es posible modificar el progtwig pnetworkingeterminado git-merge-one-file para hacer todo en el índice sin tocar el tree de trabajo, dejándolo completamente sin modificar?

ACTUALIZACIÓN Y DETALLES

Así que ahora entiendo que una fusión de nivel de file (donde la fusión actúa sobre líneas en el file en lugar de files integers) no puede ocurrir sin utilizar un tree de trabajo. (A diferencia de una combinación que actúa en files completos). Así que voy a tener que usar un tree de trabajo.

Otro detalle: estoy de acuerdo con que la solución funcione solo en los casos en que la fusión se puede realizar automáticamente sin resolución manual. Está bien si solo muestra un post de error si la combinación no es automática. (Y por supuesto, deja todo limpio).

Otro detalle: no estoy usando git-merge-one-file directamente, lo estoy usando dentro de este script: https://gist.github.com/cool-RR/6575042

Traté de seguir los consejos de @torek y utilizar un tree de trabajo temporal (como se puede ver en el script), porque esa parece ser la mejor dirección hasta ahora. El problema es que obtengo estos errores:

 git checkout-index: my_file is not in the cache error: my_file: cannot add to the index - missing --add option? 

Busqué estos posts de error en Google pero no pude encontrar nada útil.

¿Alguna idea de qué hacer?

Mientras que git necesita un lugar para hacer su trabajo, puede apuntarlo a una location de tree de trabajo diferente durante la operación de "fusionar un file".

No tengo idea si / cómo funciona esto "fuera de la caja" para merge-one-file , pero la variable env para establecer es GIT_WORK_TREE :

 env GIT_WORK_TREE=/some/where/else git ... 

(puede dejar fuera env con la mayoría, pero no todas las conchas).

Un método más o less equivalente que podría "sentirse más seguro" 🙂 o ser más conveniente para algunos propósitos es trabajar en el otro directory y usar GIT_DIR en la location del repository:

 cd /some/where/else env GIT_DIR=/place/with/repo/.git git ... 

Incluso puede combinarlos, configurando tanto GIT_DIR como GIT_WORK_TREE .

No, pero la forma más sencilla de hacerlo es esconder, fusionar, escenificar y desestabilizar:

 git stash save git merge-file foo.txt base-foo.txt their-foo.txt git add foo.txt git stash pop 

Si no quiere esconderse, entonces se le deja la opción de parche y diferencia: guarda los cambios del tree de trabajo en un parche, elimina los cambios en el tree de trabajo, realiza los cambios necesarios y vuelve a aplicar el parche

 git diff -p --raw foo.txt > foo.txt.diff git checkout -- foo.txt git merge-file foo.txt base-foo.txt their-foo.txt patch -p1 < foo.txt.diff 

Para fusionar los cambios en dos files diferentes, debe examinar su contenido: fusionar cambios está trabajando en el contenido del file. El trabajo sobre los contenidos se realiza en treees de trabajo. Hacer el trabajo en otro lado y pretender que no es un tree de trabajo es solo un juego de palabras.

Si desea dejar intacto su tree de trabajo actual mientras realiza una fusión, utilice otro tree de trabajo. git clone es barato, está hecho para cosas como esta:

 # merge into branch m2 from branch m1 but leave your (non-m2) worktree untouched: git clone --no-checkout -b m2 . ../m2-work cd ../m2-work git reset # this will warn about the empty worktree, you could instead do # git read-tree HEAD to get the same effect without the chatter git merge origin/m1 git push origin m2 

observe el --no-checkout en el clon. Merge tiene que tener un worktree para hacer su trabajo, pero no le interesa ningún contenido de file real que no sea el que necesita comparación.

Una fusión en git es una fusión tripartita entre:

  • la fuente (' remote ' o ' theirs ', lo que quiere fusionar)
  • el destino (' local ' o ' ours ', que siempre es el tree de trabajo, donde HEAD está desprotegido)
  • el ancestro común (o ' base ')

Vea el ' local ', ' base ', ' remote ', ' merged ' ilustrado en " git rebase , manteniendo un logging de ' local ' y ' remote ' ".
Puedes ver un ejemplo en " git revert no funciona como se esperaba ".

git read-tree mencionado en " Subtree Merging " y " Git Objects " (y que está usando en su esencia ) trata de fusionar treees (para la fusión de subtreees), no de contenido de files (blob).
git write-tree se puede usar para crear un object de tree, pero su documentation menciona "El índice debe estar en un estado completamente fusionado". (un poco difícil cuando quieres usar el índice para fusionar files).

El índice de git ( documentado aquí ) está allí para registrar lo que ha escenificado (el resultado ' merged '), como parte de la resolución de fusión, desde su tree de trabajo.
No tiene toda la información sobre el contenido del file, solo pointers ("input de índice") a dicho contenido. Simplemente no es la estructura correcta para hacer una fusión.


Incluso el script git-merge-one-file.sh sí menciona:

 require_work_tree 

La function proviene del script git-sh-setup.sh (ver su documentation ):

 test "$(git rev-parse --is-inside-work-tree 2>/dev/null)" = true || die "fatal: $0 cannot be used without a working tree." 

Ese requisito proviene de commit 6aaeca90 ( peff Jeff King ):

La herramienta fusionar un file es anterior a la invención de GIT_WORK_TREE .

En su mayor parte, merge-one-file solo funciona con GIT_WORK_TREE ; la mayor parte de su trabajo pesado se realiza mediante commands de plomería que respetan GIT_WORK_TREE correctamente.


Si realmente no necesita usar el tree de trabajo, puede probar y seguir la ruta elegida para Fusionar notas :
notes-merge.c crea su propio tree de trabajo para fusionar notas de git .

Jeff King me ayudó a resolver el problema y actualicé el script para que funcione:

https://gist.github.com/cool-RR/6575042

 #!bash if [ -n "$2" ]; then export SOURCE=$1 ; export DESTINATION=$2 ; else export SOURCE=HEAD ; export DESTINATION=$1 ; fi export GIT_INDEX_FILE=`git rev-parse --show-toplevel`/.git/aux-merge-index ; export GIT_WORK_TREE=`create_temporary_folder gm_`; echo $GIT_INDEX_FILE trap 'rm -f '"'$GIT_INDEX_FILE'"'; rm -rf '"'$GIT_WORK_TREE'" 0 1 2 3 15 ; mkdir $GIT_WORK_TREE/.git set -e ; git read-tree -im `git merge-base $DESTINATION $SOURCE` $DESTINATION $SOURCE ; #echo Finished read-tree #sleep 1000 git merge-index git-merge-one-file -a #echo Finished merge-index git write-tree \ | xargs -i@ git commit-tree @ -p $DESTINATION -p $SOURCE -m "Merge $SOURCE into $DESTINATION" \ | xargs git update-ref -m"Merge $SOURCE into $DESTINATION" refs/heads/$DESTINATION ; exit 0