Git: Как создать патчи для слияния? - PullRequest
24 голосов
/ 18 февраля 2010

Когда я использую git format-patch, он не включает слияния. Как я могу выполнить слияние, а затем отправить его кому-нибудь по электронной почте в виде набора исправлений?

Например, предположим, что я объединяю две ветви и выполняю еще один коммит поверх объединения:

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"

Это создает следующее дерево:

    B
  /   \
A       M - D 
  \   /
    C

Теперь я пытаюсь оформить первоначальный коммит и повторить указанные выше изменения:

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

Это вызывает конфликт слияния. В этом сценарии git format-patch master..bar производит только 3 патча, опуская «Commit M». Как мне справиться с этим?

-Джеффри Ли

Ответы [ 5 ]

28 голосов
/ 12 января 2012

Похоже, что не существует решения, создающего отдельные коммиты а-ля git format-patch, но FWIW, вы можете отформатировать патч, содержащий эффективный коммит слияния, подходящий / совместимый с git am:

Очевидно, руководство Git Reference содержит первый совет:

git log -p показ патча, вводимого при каждом коммите

[...] Это означает, что для любого коммита вы можете получить патч, коммит, внедренный в проект. Вы можете сделать это, запустив git show [SHA] с определенным SHA коммита, или вы можете запустить git log -p, который говорит Git устанавливать патч после каждого коммита. [...]

Теперь страница руководства git-log дает второй намек:

git log -p -m --first-parent

... Показывает историю, включая изменения различий, но только с точки зрения «основной ветви», пропуская коммиты, полученные из объединенных ветвей, и показывая полные различия изменений, внесенных слияниями. Это имеет смысл только при соблюдении строгой политики объединения всех веток тем при нахождении в одной ветке интеграции.

Что в свою очередь означает конкретные шаги:

# 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

И это можно применять по назначению:

git am feature.patch

Опять же, он не будет содержать отдельные коммиты, но выдает git am совместимый патч из коммита слияния.


Конечно, если вам не нужен git am совместимый патч, тогда он намного проще:

git diff origin/master > feature.patch

Но я полагаю, вы уже рассчитали столько же, и если вы попали на эту страницу здесь, вы на самом деле ищете обходной путь / решение, которое я описал выше. ;)

9 голосов
/ 18 февраля 2010

Обратите внимание, что голый git log -p не будет отображать никакого содержимого патча для коммита слияния "M", но использование git log -p -c действительно вытеснит его. Однако git format-patch не принимает никаких аргументов, аналогичных -c (или --combined, -cc), принятым git log.

Я тоже остаюсь в тупике.

5 голосов
/ 18 декабря 2013

Расширяя ответ sun, я пришел к команде, которая может создать серию патчей, аналогичную той, которую выдает git format-patch, если это возможно, и которую можно передать git am для создания истории сотдельные коммиты:

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

Патчи будут называться от xx0001.patch до xxLAST.patch

5 голосов
/ 18 февраля 2010

Если вы изучите содержимое первых двух патчей, вы увидите проблему:

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

с точки зрения ветки, над которой вы работали в то время (foo и bar), оба эти коммита удалили строку «исходный файл» и заменили ее чем-то совершенно другим. AFAIK, нет способа избежать такого рода конфликтов, когда вы генерируете патч нелинейной прогрессии с перекрывающимися изменениями (ваша ветвь фиксирует B и C в этом случае).

Люди обычно используют патчи для добавления одной функции или исправления ошибки из известного хорошего предшествующего рабочего состояния - протокол патчей просто не достаточно сложен для обработки истории слияний, как это делает Git изначально. Если вы хотите, чтобы кто-то увидел ваше слияние, вам нужно нажимать / тянуть между ветками, а не возвращать diff / patch.

0 голосов
/ 23 августа 2017

Работая на основе решения Филиппа де Мюйтера, я сделал версию, которая форматирует патчи так же, как git-format-patch (насколько я могу судить). Просто установите RANGE на желаемый диапазон коммитов (например, origin..HEAD) и перейдите:

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

Обратите внимание, что если вы используете это с git-quiltimport, вам нужно будет исключить любые пустые коммиты слияния, или вы получите сообщение об ошибке: «Патч пуст. Был ли он разбит неправильно?».

...