2010-02-18 36 views
18

Cuando uso git format-patch, no parece incluir fusiones. ¿Cómo puedo realizar una fusión y luego enviarla por correo electrónico a alguien como un conjunto de parches?Git: ¿Cómo crear parches para una combinación?

Por ejemplo, digamos que puedo combinar dos ramas y realizar otra cometen en la parte superior de la fusión:

git init 

echo "initial file" > test.txt 
git add test.txt 
git commit -m "Commit A" 

git checkout -b foo master 
echo "foo" > test.txt 
git commit -a -m "Commit B" 

git checkout -b bar master 
echo "bar" > test.txt 
git commit -a -m "Commit C" 

git merge foo 
echo "foobar" > test.txt 
git commit -a -m "Commit M" 

echo "2nd line" >> test.txt 
git commit -a -m "Commit D" 

Esto crea el siguiente árbol:

B 
/ \ 
A  M - D 
    \ /
    C 

ahora trato de partida de los Confirmación inicial y reproducción de los cambios anteriores:

git checkout -b replay master 
git format-patch --stdout master..bar | git am -3 

Esto produce un confl de fusión tic. En este escenario, git format-patch master..bar solo produce 3 parches, omitiendo "Commit M". ¿Cómo trato con esto?

Geoffrey Lee

Respuesta

5

Si examina el contenido de los dos primeros parches verá el tema:

diff --git a/test.txt b/test.txt 
--- a/test.txt 
+++ b/test.txt 
@@ -1 +1 @@ 
-initial file 
+foo 

diff --git a/test.txt b/test.txt 
index 7c21ad4..5716ca5 100644 
--- a/test.txt 
+++ b/test.txt 
@@ -1 +1 @@ 
-initial file 
+bar 

desde la perspectiva de la rama en que estaba trabajando en el momento (foo y bar) ambas de estas confirmaciones han eliminado la línea "archivo inicial" y la han reemplazado por otra cosa completamente distinta. AFAIK, no hay forma de evitar este tipo de conflicto cuando generas un parche de una progresión no lineal con cambios superpuestos (tu rama comete B y C en este caso).

Normalmente, las personas utilizan parches para agregar una sola función o corrección de errores de un estado de trabajo anterior bien conocido: el protocolo de parche simplemente no es lo suficientemente sofisticado como para administrar el historial de fusión como lo hace Git de forma nativa. Si quieres que alguien vea tu fusión, entonces necesitas empujar/tirar entre las ramas para no retroceder el parche/parche.

+0

Es difícil probar que es negativo, pero me gusta, eché un vistazo a este problema y me da la sensación de que tienes razón. – JasonSmith

+0

Sí, entiendo los problemas con los archivos de parche. Esperaba que hubiera una solución alternativa, porque uno supondría que los proyectos de Linux o Git se han encontrado con situaciones similares, y se basan completamente en enviar parches por correo electrónico en lugar de enviar/extraer. Voy a hacer ping a la lista de correo de Git y ver si tienen algún comentario adicional. Gracias. – geofflee

+0

si en vez había sustituido a la línea de mezcla anterior con $ git merge --squash foo $ git commit -a -m "Commit M" su parche habría aplicado limpiamente ... – omnisis

8

Tenga en cuenta que un desnudo git log -p no mostrará ningún contenido parche para la combinación de cometer "M", pero utilizando git log -p -c qué convencer a cabo. Sin embargo, git format-patch no acepta ningún argumento análogo al -c (o --combined, -cc) aceptado por git log.

Yo también estoy perplejo.

+1

Gracias, esta fue la respuesta exacta que estaba buscando a por qué git log -p no mostró parches para las asignaciones de fusión. –

21

No parece ser una persona entregando la solución se compromete a la git format-patch, pero Fwiw, puede dar formato a un parche que contiene la fusión efectiva cometió, compatible adecuada/con git am:

Al parecer, la guía Git Reference proporciona la primera pista:

git log -p espectáculo parche introducido en cada comprometerse

[...] Esto significa que para cualquier cometen se puede obtener la parche ese compromiso introducido en el proyecto. Puede hacer esto ejecutando git show [SHA] con un SHA de confirmación específico, o puede ejecutar git log -p, que le indica a Git que coloque el parche después de cada confirmación. [...]

Ahora, la página del manual de git-log da la segunda pista:

git log -p -m --first-parent

... muestra la historia incluyendo diferenciaciones de cambio, pero sólo desde el punto de vista "rama principal", omitiendo commits que provienen de ramas fusionadas, y mostrando las diferencias completas de cambios introducidas por las fusiones. Esto tiene sentido solo cuando se sigue una política estricta de fusión de todas las ramas de tema cuando se permanece en una sola rama de integración.

que a su vez significa en medidas concretas:

# Perform the merge: 
git checkout master 
git merge feature 
... resolve conflicts or whatever ... 
git commit 

# Format a patch: 
git log -p --reverse --pretty=email --stat -m --first-parent origin/master..HEAD > feature.patch 

Y esto se puede aplicar como se pretende:

git am feature.patch 

Una vez más, esto no va a contener el individuo se compromete, pero produce un parche compatible git am fuera de un compromiso de fusión.


Por supuesto, si usted no necesita un parche compatible git am, en primer lugar, a continuación, es la manera más simple:

git diff origin/master > feature.patch 

pero supongo que ya figuraba como mucho, y si usted aterrizó en esta página, en realidad está buscando la solución/solución que he descrito anteriormente. ;)

+0

git diff [nombre de la rama remota] funciona de maravilla gracias. –

+1

¡Gran respuesta, muchas gracias! Me temo que está incompleto, ya que también es necesario --ver en git log, de lo contrario "git am feature.patch" no funcionará. Entonces, así: git log --reverse -p --pretty = email --stat -m --first-parent origin/master..HEAD> feature.patch –

+0

Gracias @DarkoMaksimovic, actualizó la solución en consecuencia. '--reverse' no se incluyó anteriormente, porque el procedimiento descrito solo consistía en una única confirmación. ¡Pero tienes toda la razón! – sun

4

Ampliación sun 's respuesta, llegué a un comando que puede producir una serie de parches similares a lo git format-patch producirían si se pudiera, y que se puede alimentar a git am para producir una historia con el individuo se compromete:

git log -p --pretty=email --stat -m --first-parent --reverse origin/master..HEAD | \ 
csplit -b %04d.patch - '/^From [a-z0-9]\{40\} .*$/' '{*}' 
rm xx0000.patch 

parches serán nombrados xx0001.patch a xxLAST.patch

+0

Gracias por su respuesta! ¡Funciona genial! :) – mkdong

+0

Si llama a csplit con '-z' y' -f '' ', entonces no necesita eliminar el primer parche y no tendrá un prefijo" xx ". Realmente me gustaría que el formato git -patch tenía una opción integrada para hacer algo como esto ... – Compholio

0

Trabajando a partir de la solución de Philippe de Muyter hice una versión que da formato a los parches de la misma manera que git-format-patch (por lo que puedo decir). Sólo hay que establecer rango en el rango deseado de confirmaciones (por ejemplo origin..HEAD) e ir:

LIST=$(git log --oneline --first-parent --reverse ${RANGE}); 
I=0; 
IFS=$'\n'; 
for ITEM in ${LIST}; do 
    NNNN=$(printf "%04d\n" $I); 
    COMMIT=$(echo "${ITEM}" | sed 's|^\([^ ]*\) \(.*\)|\1|'); 
    TITLE=$(echo "${ITEM}" | sed 's|^\([^ ]*\) \(.*\)|\2|' | sed 's|[ -/~]|-|g' | sed 's|--*|-|g' | sed 's|^\(.\{52\}\).*|\1|'); 
    FILENAME="${NNNN}-${TITLE}.patch"; 
    echo "${FILENAME}"; 
    git log -p --pretty=email --stat -m --first-parent ${COMMIT}~1..${COMMIT} > ${FILENAME}; 
    I=$(($I+1)); 
done 

Tenga en cuenta que si usted está usando este con git-quiltimport, entonces tendrá que excluir cualquier combinación de vacío se compromete o se le aparece el error "El parche está vacío. ¿Se ha dividido incorrectamente?".

Cuestiones relacionadas