¿Cómo aplicar un parche de git de un repositorio a otro?

80

Tengo dos repositorios, uno es el repositorio principal de una biblioteca y el otro es un proyecto que usa esa biblioteca.

Si hago una corrección en el proyecto subordinado, me gustaría una manera fácil de aplicar ese parche en sentido ascendente.

La ubicación del archivo es diferente en cada repositorio.

  • Repositorio principal: www.playdar.org/static/playdar.js
  • Proyecto: playlick.com/lib/playdar.js

Intenté usarlo git format-patch -- lib/playdar.jsen el proyecto playlick y luego git amen el repositorio principal de playdar, pero las diferentes ubicaciones de los archivos en el archivo de parche generaron un error.

¿Existe una manera fácil de aplicar el parche de una confirmación determinada en un archivo dado a otro archivo arbitrario en otro lugar?

Para obtener puntos de bonificación, ¿qué sucede si el archivo al que desea aplicar el parche no está en un repositorio de git?

James Wheare
fuente
similar: pregunta: stackoverflow.com/questions/3367254/…
koppor

Respuestas:

117

Si la edición manual del archivo de revisión está fuera de la pregunta o no factible, esto se puede hacer con las opciones estándar (disponible en git apply, git format-patchy GNU patch).

  1. -p<n>elimina nlos directorios principales de las rutas del parche.

  2. Después del procesamiento -p, --directory=<root>antepone roota cada una de las rutas en el parche antes de aplicar.

Ejemplo

Entonces, para su ejemplo, para tomar un parche que estaba originalmente encendido static/playdar.jsy aplicarlo lib/playdar.js, ejecutaría:

$ cat patch_file | git am     \ 
          -p1                 \ # remove 1 leading directory ('static/')
         --directory='lib/'     # prepend 'lib/'
vergenzt
fuente
1
¿Alguna posibilidad de hacer esta respuesta principal? Esto es mucho más fácil que editar manualmente un archivo de parche.
weston
Claro, esta es una respuesta mejor / más fácil, aunque es bueno saber la respuesta de @ araqnid.
James Wheare
Relevante para ajustar el directorio después de intentarlo por primera vez sin --directory: stackoverflow.com/questions/24121709/…
Ioannis Filippidis
38

El parche producido por git format-patches simplemente un archivo de texto; puede editar los encabezados de diferencias para que modifique una ruta diferente.

Entonces, por ejemplo, habría producido algo como esto:

diff --git a/lib/playdar.js b/lib/playdar.js
index 1234567..89abcde
-- a/lib/playdar.js
++ b/lib/playdar.js

Todo lo que tiene que hacer es cambiar lib/playdar.jsa static/playdar.jscontinuación, ejecute el parche a travésgit am"

El parche debe ser legible por la utilidad de parche GNU estándar para las personas que no tienen git--- pero no ejecutan format-patchlas opciones -M, -Cetc. para producir parches de cambio de nombre en ese caso, porque el soporte para ellos no es universal.

arácnido
fuente
1
Volver a visitar esta página más tarde ... Esta es una mejor respuesta a la pregunta que el "ganador" anterior que sugirió submódulos.
James Wheare
4

Suponiendo que ambos proyectos son proyectos de git, parece que los submódulos serían perfectos para ti. Esto permite que un proyecto de git se vincule dinámicamente a otro proyecto de git, esencialmente creando un repositorio de git dentro de otro repositorio de git, ambos con sus propias vidas distintas.

En otras palabras, agregue "repositorio principal" como un submódulo en "proyecto". Siempre que confirme / inserte cosas nuevas en el "repositorio principal", simplemente git pulllas devuelve al "proyecto".

Henrik Paul
fuente
Hmm, habiendo leído los documentos del submódulo, esto no suena como "una forma fácil", aunque puede ser la más sólida. Parece que tendría que crear un submódulo que contenga solo el playdar.jsarchivo y luego incluirlo en los otros dos proyectos (no quiero todo lo demás www.playdar.orgen el playlick.comproyecto). Podría recurrir a editar manualmente los archivos de parche por ahora para ser honesto . O continúe copiando y pegando entre los dos. Salud.
James Wheare
Aquí hay un tutorial claro y completo y un comienzo con el submódulo git para cualquier otra persona que se encuentre con esta pregunta: book.git-scm.com/5_submodules.html
James Wheare
2

Para completar la respuesta de Henrik e ir por el punto de bonificación

¿Qué pasa si el archivo al que desea aplicar el parche no está en un repositorio de git?

Si tiene acceso a los directorios del archivo candidato para un parche proveniente de un repositorio de git, ¡primero podría transformar ese árbol de directorios / archivos en un repositorio de git! (' git init': un repositorio de git es solo un .git dentro de un directorio raíz después de todo).
Luego, establecería ese repositorio como un submódulo para su proyecto principal.

VonC
fuente
2

El uso de la --relativeopción para format-patchpuede mejorar la abstracción (ocultar detalles irrelevantes sobre el repositorio desde el que se generó el parche).

[repository-with-changes]
git format-patch --relative=(path-to-library) (base-commit-for-patch) ## 'HEAD~1'

He descubierto que la --3wayopción es necesaria al aplicar el parche (para evitar does not exist in indexerrores); su kilometraje puede variar. El uso --directory=(...)es probable que sólo es necesario si su ruta de destino no es la raíz del repositorio.

[repository-to-update]
git am --3way --directory=(path-to-library) (patch-file)

  • format-patch creará un archivo de parche por confirmación en la rama actual desde 'base'.

  • La documentación para la --relativeopción parece faltar en algunos casos , pero parece funcionar de todos modos (a partir de la versión 2.7.4).

Brent Bradburn
fuente
1

Puede agregar un nuevo control remoto y extraerlo. Artículo con detalles.

$ cd <path-to-repoB>
$ git remote add repoA <git-URL-for-repoA>
$ git pull repoA
Der_Meister
fuente
1

Puede simplemente eliminar (cambiar el nombre) temporalmente el repositorio principal.

cd to/main/project
mv .git .git_
cd to/sub/project
git apply patchname
cd -
mv .git_ .git
ya.teck
fuente