¿Cómo aplicar un parche de Git a un archivo con un nombre y una ruta diferentes?

100

Tengo dos repositorios. En uno, hago cambios en el archivo ./hello.test. Confirmo los cambios y creo un parche a partir de ese compromiso con git format-patch -1 HEAD. Ahora, tengo un segundo depósito que contiene un archivo que tiene el mismo contenido que hello.test pero se coloca en un directorio diferente con un nombre diferente: ./blue/red/hi.test. ¿Cómo hago para aplicar el parche mencionado anteriormente al hi.testarchivo? Lo intenté, git am --directory='blue/red' < patch_filepero eso, por supuesto, se queja de que los archivos no tienen el mismo nombre (¿y pensé que a Git no le importaba?). Sé que probablemente podría editar el diff para aplicarlo a ese archivo específico, pero estoy buscando una solución de comando.

mart1n
fuente
Relacionado con: stackoverflow.com/q/3367254/1959808
Ioannis Filippidis

Respuestas:

97

Puede crear el parche usando git diffy luego aplicarlo usando la patchutilidad, que le permite especificar el archivo al que desea aplicar la diferencia.

Por ejemplo:

cd first-repo
git diff HEAD^ -- hello.test > ~/patch_file

cd ../second-repo
patch -p1 blue/red/hi.test ~/patch_file
Georgebrock
fuente
7
Ah, bonito, no pensé en eso. Sin embargo, ¿hay alguna forma de hacer esto con los comandos de Git para que los datos de confirmación (fecha y hora, autor de confirmación, mensaje de confirmación) se mantengan iguales?
Martes
2
Es posible que haya algo que puedas hacer con amo apply, pero no puedo encontrarlo. Si se encuentra duplicando muchos cambios, podría haber una solución mejor usando submódulos, o lo que sea que su idioma de elección proporcione para compartir código (por ejemplo, en Ruby podría extraer el código duplicado como una gema).
georgebrock
1
En realidad, esto está relacionado con la documentación (los archivos fuente son XML). Los submódulos no son realmente una opción, ya que tendría que defenderlos en nuestra infraestructura existente.
martes
52

Existe una solución simple que no implica la edición manual de parches ni un script externo.

En el primer repositorio (esto también puede exportar un rango de confirmación, utilícelo -1si desea seleccionar solo una confirmación):

git format-patch --relative <committish> --stdout > ~/patch

En el segundo repositorio:

git am --directory blue/red/ ~/patch

En lugar de usar --relativein git format-patch, otra solución es usar la -p<n>opción in git ampara eliminar ndirectorios de la ruta de los parches, como se menciona en una respuesta a una pregunta similar .

También es posible ejecutar git format-patch --relative <committish>sin el --stdout, y generará un conjunto de .patcharchivos. Estos archivos se pueden alimentar directamente git amcon git am --directory blue/red/ path/to/*.patch.

magiraud
fuente
9
Esto todavía se basa en el hecho de que los nombres de los archivos son los mismos, ¿verdad?
Martes
3
Debe tenerse en cuenta que la --directoryopción parece requerir que especifique la ruta completa del directorio en relación con la raíz del repositorio; algo así como --directory=./mientras chdir'd en un subdirectorio en el repositorio no funcionará.
Reid
1
Uso de --3wayayudas con does not exist in index:git am --3way --directory (relative-path) (patch)
Brent Bradburn
Use la -ktecla en ambos comandos para no eliminar la primera línea del mensaje de confirmación.
ruvim
El uso --3wayno solo ayuda con los errores "no existe en el índice" (como lo señala @nobar), sino que también le permite manejar limpiamente los conflictos de fusión. En lugar de dejar intactos los archivos en conflicto, se agrega un bloque de conflicto que luego se puede resolver.
Daniel Wolf
11

Respondiendo a mi propia pregunta con un script que hace exactamente esto: https://github.com/mprpic/apply-patch-to-file

En lugar de modificar el archivo de parche manualmente, solicita al usuario el archivo de destino, modifica el parche y lo aplica al repositorio en el que se encuentra actualmente.

mart1n
fuente
5

Sobre la base de la respuesta de @georgebrock, aquí hay una solución que utilicé:

Primero, cree los archivos de parche como de costumbre (p. Ej. git format-patch commitA..commitB).

Luego, asegúrese de que su repositorio de destino esté limpio (no debe haber archivos modificados o sin seguimiento) y aplique los parches de esta manera:

cd second-repo
git am ~/00*.patch

Por cada archivo de parche, obtendrá un error como "error: XYZ no existe en el índice". Ahora puede aplicar este archivo de parche manualmente:

patch --directory blue/red < ~/0001-*.patch
git add -a
git am --continue

Tienes que seguir estos tres pasos para cada archivo de parche.

Esto preservará el mensaje de confirmación original, etc. sin requerir ningún git format-patchcomando especial o editar los archivos de parche.

Oliver
fuente
1
Buena respuesta, creo que esta es la mejor base para cualquier tipo de manipulación de parches "no estándar". Lo hago en 3 pasos. (1) Comprometerse con el texto - git format-patch -1 commitA --stdout > thing.diff; (2) Edite el archivo de parche hasta que haga lo que necesito; (3) Texto para confirmar git am --3way thing.diff que tiene la ventaja de que puede aceptar las partes del parche que se aplican limpiamente y utilizar gitel proceso estándar de resolución de conflictos para las partes que no lo hacen.
We Are All Monica
2

Entiendo que los dos archivos son exactamente iguales en su situación, por lo que es probable que el parche tenga éxito.

Sin embargo, en caso de que desee aplicar un parche a un archivo similar, pero no exactamente el mismo, o si desea realizar un parche interactivo, utilizará la combinación de tres vías.

Digamos que modificado el archivo A, denotan nos dejó A~1que la versión anterior, y que desea aplicar el diff entre A~1a Aal archivo B.

Abra una herramienta de combinación de tres vías, por ejemplo Beyond Compare, la ruta del panel izquierdo es A, el panel central es el ancestro común, por lo que la ruta es A~1, la ruta del panel derecho es B. A continuación, la muestra panel inferior el resultado de aplicar el diff entre A~1a Aal archivo B.

La siguiente figura ilustra la idea.

ingrese la descripción de la imagen aquí

Gqqnbig
fuente
0

FYI: Recientemente tuve problemas al intentar descargar un parche de Github y aplicarlo a un archivo local (que era una "anulación" en una nueva ubicación).

git amno aplicaría el parche porque el archivo "no estaba en el índice" o "sucio". Pero descubrí que el patchcomando simple podría aplicar el parche. Me pidió el nombre del archivo a parchear.

Hice el trabajo, de todos modos ...

Mike Robinson
fuente