Combinar de manera interactiva los files rastreados con files locales git y no rastreados

Uso un par de packages de software (como gitlab) que instalas clonando de su git repo. Por lo general, vienen con algún config.example (bajo control de versión), que copy en su propio file de config (no bajo control de versión o incluso ignorado en .gitignore ) y se adapta a sus necesidades.

Cuando el package original se actualiza y, por ejemplo, cambia las opciones del file de configuration que, obviamente, solo se reflejarán en config.example .

¿Hay una cadena de commands git que me falta que me pueden ayudar a comparar los cambios de config.example con el nuevo en upstream/HEAD y tal vez incluso combinarlos de forma interactiva en mi file de config local?

Sería increíble si pudiera get algo como el modo de parche interactivo en git add/commit --interactive .

git checkout --patch selecciona diff hunks, lo más simple aquí podría ser poner tu contenido en la ruta de subida, hacer eso y limpiar después de:

 cp config config.example git checkout -p upstream config.example mv config.example config git checkout @ config.example 

eso te dará la selección de git add --patch dos diferencias de git add --patch .

  • Puede usar vim como una herramienta de combinación con vimdiff .
  • Emacs puede hacer esto también con ediff-mode .

Probablemente puedas acercarte a esto de muchas maneras. Podría pensar en dos: git-merge-file y un buen patch viejo. El método git-merge-file ofrece algo de interactividad, mientras que el método patch no ofrece ninguno.

Solución con git-merge-file

config.example que tiene un file original config.example que utiliza para crear un file local sin versión, config.local . Ahora, cuando se actualizan las actualizaciones de config.example , puede seguir algo así como los siguientes pasos para fusionar cualquier cambio nuevo.

 $ git fetch $ git show master:config.example > config.example.base $ git show origin/master:config.example > config.example.latest $ git merge-file config.local config.example.base config.example.latest 

Esto actualizará config.local con los marcadores de conflicto habituales, que luego tendrá que resolver con su herramienta de combinación favorita ( ediff en Emacs es agradable, estoy seguro de que hay modos similares para Vim). Por ejemplo, los siguientes tres files,

config.example.base :

 Original: some config 

config.example.latest :

 Original: some config Upstream: new upstream config 

config.local :

 Original: some config My changes: some other config 

se fusionará así:

 Original: some config <<<<<<< config.local My changes: some other config ======= Upstream: new upstream config >>>>>>> config.example.latest 

Probablemente podrías escribir esto sin mucho esfuerzo. Por cierto, git-merge-file puede operar en 3 files, no necesitan ser controlados por versión bajo git . ¡Eso significa que uno podría usarlo para combinar tres files!

Solución con patch :

Suponiendo los mismos nombres de file, config.local y config.example , lo siguiente debería funcionar.

 $ git fetch $ git diff master..origin/master -- config.example | sed -e 's%\(^[-+]\{3\}\) .\+%\1 config.local%g' > /tmp/mypatch $ patch < /tmp/mypatch 

Si desea la combinación completa de git, puede hacer que git lo haga con contenido arbitrario configurando una input de índice como lo hace git read-tree y luego invocando el controller de combinación normal de git en esa input. Git se refiere a las diferentes versiones de contenido como "etapas"; son 1: el original, 2: tuyo, 3: el suyo. La fusión compara los cambios de 1 a 2 y de 1 a 3 y hace su trabajo. Para configurarlo, use git update-index :

 orig_example= # fill in the commit with the config.example you based yours on new_upstream= # fill in the name of the upstream branch ( while read; do printf "%s %s %s\t%s\n" $REPLY; done \ | git update-index --index-info ) <<EOD 100644 $(git rev-parse $orig_example:config.example) 1 config 100644 $(git hash-object -w config) 2 config 100644 $(git rev-parse $new_upstream:config.example) 3 config EOD 

y ha organizado una combinación de contenido personalizado para esa ruta. Ahora hazlo:

 git merge-index git-merge-one-file -- config 

y automatizará o abandonará los excrementos habituales del conflicto, lo arreglará como lo desee y se git rm --cached --ignore-unmatch (o mantendrá) la input del índice si lo desea.

La ruta que pones en el índice (la "configuration" en las tres inputs aquí), por cierto, no tiene que existir ni tener nada que ver con nada. Puedes ponerle el nombre "wip" o "deleteme" o cualquier cosa que desees. La fusión es del contenido id'd en la input del índice.

Creo que eso hará lo que quieras aquí. Si realmente quieres escoger y elegir entre los cambios en sentido ascendente, puedes poner tu propio contenido en config.example y hacer git checkout -p upstream -- config.example , que hace el inverso de git add -p , luego volver a poner las cosas como estaban.

Para diferenciar solo config.example en su repository local con el file correspondiente en el config.example upstream/HEAD , puede ejecutar:

 git diff upstream/HEAD config.example 

Desafortunadamente, no conozco una manera de hacer que git aplique directamente los cambios a un file que git no rastrea.

Hay una herramienta llamada sdiff que puede hacer lo que quieras.

Invocarlo (en su caso) con sdiff -o config config.example config

Lo siguiente debería funcionar:

 git diff <some-args> | perl -pe 's/path\/to\/changes\/file/path\/other/g' > t patch -p1 < t rm t