2010-02-18 5 views
18

Lorsque j'utilise git format-patch, il ne semble pas inclure de fusions. Comment puis-je effectuer une fusion, puis l'envoyer par e-mail à quelqu'un sous la forme d'un ensemble de correctifs?Git: Comment créer des correctifs pour une fusion?

Par exemple, disons que je fusionner deux branches et d'effectuer une autre commettras au-dessus de la fusion:

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" 

Cela crée l'arborescence suivante:

B 
/ \ 
A  M - D 
    \ /
    C 

Maintenant, j'essaie de caisse de la validation initiale et rejouer les changements ci-dessus:

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

Ceci produit un confl fusion ict. Dans ce scénario, git format-patch master..bar ne produit que 3 patches, en omettant "Commit M". Comment puis-je faire face à cela?

-Geoffrey Lee

Répondre

5

Si vous examinez le contenu des deux premiers patches vous verrez la question:

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 

du point de vue de la branche que vous travaillez à l'heure (foo et bar) de ces deux commits ont supprimé la ligne "initial file" et l'ont remplacée par autre chose entièrement. AFAIK, il n'y a aucun moyen d'éviter ce genre de conflit lorsque vous générez un patch d'une progression non-linéaire avec des changements qui se chevauchent (votre branche commet B et C dans ce cas). Les gens utilisent normalement des correctifs pour ajouter une fonctionnalité ou un bogue corrigeant un bon état de travail antérieur connu - le protocole de correctif n'est simplement pas assez sophistiqué pour gérer l'historique de fusion comme le fait nativement Git. Si vous voulez que quelqu'un voie votre fusion, alors vous devez pousser/tirer entre les branches et ne pas relâcher diff/patch.

+0

Il est difficile de prouver un négatif, mais comme si, j'ai pris un problème à ce problème et j'ai l'impression que vous avez raison. – JasonSmith

+0

Oui, je comprends les problèmes avec les fichiers de correctifs. J'espérais qu'il y aurait une solution de rechange, car on supposerait que les projets Linux ou Git ont rencontré des situations similaires, et ils dépendent entièrement de soumettre des correctifs par e-mail plutôt que de pousser/tirer. Je vais faire un ping sur la liste de diffusion Git et voir s'ils ont des commentaires supplémentaires. Merci. – geofflee

+0

si vous aviez remplacé à la place de la ligne de fusion ci-dessus avec $ git merge --squash foo $ git commit -a -m "Commit M" votre patch aurait appliqué proprement ... – omnisis

8

Notez qu'un nu git log -p ne présentent aucun contenu de patch pour la fusion « M » commit, mais en utilisant git log -p -c ne l'amadouer dehors. Cependant, git format-patch n'accepte aucun argument analogue au -c (ou --combined, -cc) accepté par git log.

Je reste aussi perplexe.

+1

Merci, c'était la réponse exacte que je cherchais à savoir pourquoi git log -p n'affichait pas de correctifs pour les commits de fusion. –

21

Il ne semble pas être une solution de production individuelle engage à la git format-patch, mais FWIW, vous pouvez formater un patch contenant la fusion effective commit, adapté/compatible avec git am:

Apparemment, le guide Git Reference fournit la premier indice:

git log -p patch show introduit à chaque validation

[...] Cela signifie que pour tout vous commettras pouvez obtenir la patch que commit a introduit dans le projet. Vous pouvez le faire en exécutant git show [SHA] avec un commit SHA spécifique, ou vous pouvez exécuter git log -p, qui dit à Git de mettre le patch après chaque commit. [...]

Maintenant, la page de manuel de git-log donne le deuxième indice:

git log -p -m --first-parent

... Affiche l'histoire, y compris diffs de changement, mais seulement du point de vue de "branche principale", sauter des validations provenant de branches fusionnées et afficher les différences de changements introduites par les fusions. Cela n'a de sens que si vous suivez une politique stricte de fusion de toutes les branches de sujet lorsque vous restez sur une seule branche d'intégration.

ce qui signifie en mesures concrètes:

# 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 

Et cela peut être appliqué comme prévu:

git am feature.patch 

Encore une fois, ce ne contiendra pas l'individu engage, mais il produit un correctif compatible git am sur un commit de fusion.


Bien sûr, si vous n'avez pas besoin d'un git am correctif compatible en premier lieu, il est plus simple:

git diff origin/master > feature.patch 

Mais je suppose que vous avez déjà deviné autant, et si vous avez atterri Sur cette page, vous recherchez réellement la solution/solution de contournement décrite ci-dessus. ;)

+0

git diff [nom de la branche distante] fonctionne très bien. –

+1

Bonne réponse, merci beaucoup! J'ai peur que ce soit incomplet, car vous avez aussi besoin de --reverse dans git log, sinon "git am feature.patch" ne fonctionnera pas. Donc, comme ceci: git log --reverse -p --pretty = email --stat -m --premier parent origine/master..HEAD> feature.patch –

+0

Merci @DarkoMaksimovic, mis à jour la solution en conséquence. '--reverse' n'a pas été inclus précédemment, car la procédure décrite ne comportait qu'un seul commit. Mais tu as absolument raison! – sun

4

expansion réponse de sun, je suis venu à une commande qui peut produire une série de correctifs semblables à ce que git format-patch produirait si elle pouvait, et que vous pouvez nourrir git am pour produire une histoire avec l'individu engage:

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 

patches seront nommés xx0001.patch à xxLAST.patch

+0

Merci pour votre réponse! Cela fonctionne très bien! :) – mkdong

+0

Si vous appelez csplit avec '-z' et' -f '' 'alors vous n'avez pas besoin de supprimer le premier patch et il n'aura pas de préfixe" xx "." Je souhaite vraiment que git-format -patch avait une option intégrée pour faire quelque chose comme ça ... – Compholio

0

travail de la solution de Philippe de Muyter j'ai fait une version qui formate les patches de la même manière que patch git format (pour autant que je peux dire). Il suffit de définir RANGE pour la gamme désirée de commits (par exemple origin..HEAD) et aller:

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 

Notez que si vous utilisez cela avec git-quiltimport alors vous devrez exclure toute fusion ou vide vous engage obtenir l'erreur "Patch est vide.