2010-11-07 14 views
7

Supongamos que tengo una rama con la siguiente historia:¿Cómo git-apply un parche a una revisión anterior?

 
A - B - C - D 

entre B y C modifiqué un montón de archivos que incluye un archivo en particular, lo llaman foo.txt.

Luego, sobre la revisión D, he modificado ese mismo archivo, foo.txt.

Mientras tanto, un amigo mío tomó una instantánea de mi directorio en B y decidió que ajustar foo.txt de alguna manera. Vamos a llamar esa revisión E. Si fueran parte del mismo repositorio, que se vería así:

 
A - B - C - D 
    \ 
     E 

Sin embargo, ellos no tan sólo tenemos A - B - C - D en mi repositorio y luego él me envía un parche para las diferencias entre B y E.

Como también me he equivocado con foo.txt, no puedo aplicar el parche directamente a D, el contexto para el diff no coincidirá, está esperando que esté en o alrededor de B.

Parece que me gustaría crear un tipo de árbol de depósito hipotético que mencioné anteriormente, luego hacer una fusión entre D y E en este depósito ory para reproducir mis cambios C y D juntos en el orden correcto.

Así que mis preguntas:

  • ¿Es esta la manera más sensata para aplicar cambios basados ​​en 'líneas de base' histórica?
  • ¿Hay algún inconveniente que no estoy viendo?
  • ¿Cómo se puede lograr esto en git limpiamente?

Respuesta

6

Estás en lo cierto que lo haces quiero aprovechar las facultades de fusión de git. Todavía hay algunas posibilidades. Lo más indicativo de lo que realmente sucedió es probablemente la combinación, pero un método que da como resultado una versión modificada de E aplicada sobre D tampoco es tan malo. ¡Hablemos de cómo hacer eso!

Si el parche contiene las manchas a las que se aplica (la salida de git format-patch sí lo hace), entonces git am es capaz de intentar una fusión de tres vías. Los registros burbuja SHA1 en un aspecto como este: diff

diff --git a/foo.txt b/foo.txt 
index ca1df77..2c98844 100644 

Si usted tiene que, estás de suerte. Simplemente use git am --3way <patch>.Desafortunadamente, git am requiere el encabezado de estilo de correo electrónico que produce el parche de formato, por lo que si el parche proviene de git diff en lugar de git format-patch, tiene que fallar un poco. Usted puede agregar usted mismo:

From: Bobby Tables <[email protected]> 
Date: 2 Nov 2010 
Subject: [PATCH] protect against injection attack 

<original diff> 

git am debe ser capaz de trabajar con eso, y si no lo consigue todo exactamente cómo lo quería, siempre se puede git commit --amend para solucionarlo. (También podría tratar de usar git apply --build-fake-ancestor=foo.txt <patch>, pero en realidad no funciona de una manera fácil de usar. Creo engañar git am es más fácil.)

Si el parche no contiene ningún SHA1s blob (es decir, la que fue creado diff, no es un comando git), otra vez, dile a tu amigo cómo usar git, y estoy seguro de que puedes todavía kludge it. ¡Sabes a qué versión de foo.txt se aplica el parche! Para obtener su SHA1 del compromiso B, use:

git ls-tree <SHA1 of B>:<directory containing foo.txt> 

Será uno de los blobs de la lista. (Sé que hay una manera directa, pero no puedo pensar en eso ahora mismo). Luego puede agregar un encabezado falso de git diff. Supongamos que su hash es ABCDEF12:

diff --git a/foo.txt b/foo.txt 
index abcdef12.. 

Git en realidad no necesita nada, pero esa primera almohadilla; aunque la salida de git diff tendría el hash final y un modo, am no lo está buscando, así que puede salirse con la suya dejándolo. (Y sí, me acabo de probar esto, no había hecho esto antes.!)

Esto dará lugar a una historia como A - B - C - D - E', donde E' es el parche de su amigo, pero aplicado a D; es el resultado de una fusión tripartita entre el contenido en D, B y el parche de su amigo.

Por último, si usted no quiere ensuciar alrededor con nada de eso, se puede hacer lo que usted ha dicho:

git checkout -b bobby <SHA1 of B> 
# apply the patch 
git commit --author="Bobby Tables <[email protected]>" 
git checkout master 
git merge bobby 
# or `git cherry-pick bobby` to grab the single commit and apply to master 
# or `git rebase bobby master` to rebase C and D onto B 
+1

+1 para una buena respuesta, y por hacerme reír sobre "Tablas Bobby < [email protected]> ":) –

1

He echado un vistazo alrededor, te juro que debe haber una manera de hacer esto automágicamente en git, pero por ahora tu mejor opción es hacer lo que dices.

Asumiendo que su comprometen a B tiene ID 12345...:

$ git checkout 12345 
Note: checking out '12345....'. 

You are in 'detached HEAD' state. 
[...] 
$ git checkout -b friends_change 
$ git apply < patchfile.patch 
$ git status 
[... something interesting ...] 
$ git commit -m "Applying friend's patches to foo" 
$ git checkout master 
$ git merge friends_change 

Por supuesto, se puede reajustar su lugar, si no se ha publicado la historia a cualquiera:

$ git rebase friends_change 

Esto creará una árbol como este:

A - B - C - D (old master) 
    \ 
     E - C' - D' (new master) 
Cuestiones relacionadas