Глава 12. Управление изменениями с Mercurial Queues

Содержание

12.1. Проблема управления патчами
12.2. Предыстория Mercurial Queues
12.2.1. A patchwork quilt
12.2.2. От patchwork quilt до Mercurial Queues
12.3. Огромное преимущество MQ
12.4. Понимание патчей
12.5. Начало работы с Mercurial Queues
12.5.1. Создание нового патча
12.5.2. Обновление патча
12.5.3. Укладка и отслеживания патчей
12.5.4. Манипуляция стеком патчей
12.5.5. Вставка и извлечение нескольких патчей
12.5.6. Безопасные проверки и их основа
12.5.7. Работа с различными патчами сразу
12.6. Более подробно о патчах
12.6.1. The strip count
12.6.2. Стратегия для применения патчей
12.6.3. Некоторые причуды из представления патчей
12.6.4. Остерегайтесь неточностей
12.6.5. Обработка отказа
12.7. Подробнее о управление патчами
12.7.1. Удаление нежелательных патчей
12.7.2. Преобразование в и из постоянных ревизий
12.8. Получение максимальной производительности от MQ
12.9. Обновление патчей когда исходный код измененился
12.10. Идентификация патчей
12.11. Полезные вещи, которые необходимо знать
12.12. Управление патчами в репозитории
12.12.1. Поддержка MQ для репозитория патчей
12.12.2. Несколько вещей для отслеживания
12.13. Инструменты сторонних разработчиков для работы с патчами
12.14. Хорошие методы работы с патчами
12.15. Поваренная книга MQ
12.15.1. Управление «тривиальными» патчами
12.15.2. Объединение целых патчей
12.15.3. Слияние части одного патча с другим
12.16. Различия между quilt и MQ

12.1. Проблема управления патчами

Вот распространенная ситуация: вам нужно установить пакет программного обеспечения из исходных кодов, но вы обнаружили ошибку, которую вы должны исправить в исходниках, прежде чем начать работу с пакетом. Вы делаете изменения, забываете о пакете на некоторое время, и через несколько месяцев вам нужно провести обновление до новой версии пакета. Если новая версия пакета до сих пор с ошибкой, необходимо извлечь ваши исправления из дерева старых исходников и применить его на новой версии. Это утомительно, и тут легко сделать ошибку.

Это простой случай решения проблемы «управлением патчами». У вас есть «upstream» дерева исходных текстов, который вы не можете изменить, вы должны внести некоторые локальные изменения в части upstream дерева, и вы хотите иметь возможность сохранить эти изменения отдельно, так чтобы вы могли применить их в новых версиях upstream исходников.

Проблема управления патчами возникает во многих ситуациях. Вероятно, наиболее заметным является то, что пользователь проекта с открытым исходным кодом будет помогать сопровождающему проекта в исправлении ошибки или написанию новой функции только патчами.

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

Когда у вас есть несколько изменений, легко управлять одним патчем с использованием стандартных программ diff и patch (смотрите раздел Раздел 12.4, «Понимание патчей» для обсуждения этих инструментов). После того, как количество изменений растет, начинает иметь смысл сохранить патчи как отдельные «части работы», так чтобы, например, один патч содержал исправление только одной ошибки (патч может изменять несколько файлов, но он делает «только одну вещь»), и вы можете иметь несколько таких патчей для различных ошибок исправленых вами и локальными изменениями которые вам требуются. В этой ситуации, если вы предоставите патч, исправляющий ошибку сопровождающему пакет разработчику, и они включают ваши исправления в следующем релизе, вы можете просто удалить, один патч, при обновлении до новой версии.

Поддержка одного патча upstream дерева немного утомительно и чревато ошибками, но не трудно. Однако сложность задачи быстро растет с ростом числа исправлений которое вы должны поддерживать. Имея более чем небольшое количество патчей в руках, понимая, какие из них вы отправили разработчику и беспорядок будет впечатляющий.

К счастью, Mercurial включает в себя мощное расширение, Mercurial Queues (или проще «MQ»), которое упрощает задачу по массовому управлению патчами.

12.2. Предыстория Mercurial Queues

В конце 1990-х, некоторые разработчики ядра linux начали поддерживать «серии патчей», что изменило поведения ядра linux. Некоторые из этих серий были направлены на стабильность, некоторые добавляли фичи, у других был более спекулятивный характер.

Размер серии этих патчей быстро рос. В 2002 году Эндрю Мортон опубликовал некоторые скрипты, которые он использует для автоматизации задач управления своей очередью патчей. Эндрю был успешно использовал эти скрипты для управления сотней (иногда тысячей) патчей на ядро linux.

12.2.1. A patchwork quilt

В начале 2003 года Andreas Gruenbacher и Martin Quinson заимствовали подход сценариев Эндрю и опубликовали инструмент под названием «patchwork quilt» [web:quilt], или просто «quilt» (смотрите [gruenbacher:2005] для документ с его описанием). Поскольку quilt существенно автоматизировало управление исправлениями, он быстро приобрел много последователей среди открытых разработчиков программного обеспечения.

Quilt управляет стеком патчей над деревом директорий. Для начала, вам говорите quilt управлять деревом каталогов, а также указываете, какими файлами вы хотите управлять, он хранит названия и содержание этих файлов. Чтобы исправить ошибку, вы создаете новый патч (с помощью одной команды), редактируете файлы которые вам нужно исправить, потом «обновляете» патч.

На шаге обновления вызывается quilt для сканирования каталогов, он обновляет патч со всеми изменениями, которые вы сделали. Вы можете создать один патч поверх первого, который будет отслеживать изменения, необходимые для изменения дерева «дерево с применённым первым патчем» к «дереву применяется второй патч».

Вы можете управлять тем, какие патчи применяются к дереву. Если «извлечь» патч, изменения, внесенные этим патчем исчезнут из дерева каталогов. Quilt помнит, какие патчи вы извлекли, хотя, вы можете так же «вставить» убраный патч еще раз, и дерево каталогов, будет восстановлено с содержащим изменения в патче. Самое главное, вы можете запустить команду «обновить» в любой момент, а верхний применяемый патч будет обновляться. Это означает, что вы можете в любое время изменить и какие патчи применяются и какие изменения внесены в эти патчи.

Quilt ничего не знает об инструментах контроля версий, так что работает одинаково над распакованными архивами или рабочей копией subversion.

12.2.2. От patchwork quilt до Mercurial Queues

В середине 2005 года, Крис Мейсон взял особенности quilt и написал расширение, которое он назвал Mercurial Queues, которое добавило похожее на quilt поведение к Mercurial.

Ключевой разницей между quilt и MQ является то, что quilt ничего не знает о системах управления версиями, а MQ интегрирована в mercurial. Каждый патч, который вы вставляете отображается как ревизия Mercurial. Извлекаете патч, и ревизия уходит.

Потому что quilt не зависит от инструментов контроля версий, он по-прежнему чрезвычайно полезен для программ, чтобы следить за ситуацией, когда вы не можете использовать Mercurial и MQ.

12.3. Огромное преимущество MQ

Я не могу переоценить значение того, что предлагает MQ объединяя патчи и контроль версий.

Одна из основных причин того, что патчи сохраняются в мире свободного программного обеспечения и открытого исходного кода, несмотря на наличие более мощных инструментов контроля версий — это гибкость которую они предлагают.

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

MQ, же, объединяя распределенную систему контроля версий и патчи позволяет гораздо легче выделять вашу работу. Ваш патч находится на вершине общей истории изменений, и вы можете их скрыть или снова показать. Если вам не нравится патч, вы можете оставить его. Если патч не совсем такой, каким вы хотите его видеть, это просто исправить, столько раз, сколько нужно, пока вы приведёте его в форму которую вы хотите.

Для примера, интеграция патчей с контролем версий даёт понимание патчей и отладку последствий их взаимодействия с кодом чрезвычайно легче. Так как каждое применение патчей связанно с ревизией, вы можете прочитать hg log имя файла для просмотра ревизии и исправлений наложенных патчем. Вы можете использовать команду hg bisect для двоичного поиска по всем ревизиям и применять патчи, чтобы увидеть, где появилась ошибка и где она была исправлена. Вы можете использовать команду hg annotate чтобы посмотреть, какие ревизии или патчи изменили данную строку исходного файла. И так далее.

12.4. Понимание патчей

Потому что MQ не скрывает свой патч-ориентированный характер, было бы полезно, понять, что такое патчи, а также немного о том, какие инструменты работают с ними.

Традиционная команда unix diff сравнивает два файла и выводит список различий между ними. Команда patch понимает эти различия, как изменения которые нужно внести в файл. Посмотрите ниже на простом примере из этих команд в действии.

$ echo 'this is my original thought' > oldfile
$ echo 'i have changed my mind' > newfile
$ diff -u oldfile newfile > tiny.patch
$ cat tiny.patch
--- oldfile	2012-02-02 14:09:55.119150885 +0000
+++ newfile	2012-02-02 14:09:55.119150885 +0000
@@ -1 +1 @@
-this is my original thought
+i have changed my mind
$ patch < tiny.patch
patching file oldfile
$ cat oldfile
i have changed my mind

Тип файла, который генерирует diffpatch принимает в качестве входных данных), называется «patch» или «diff» нет никакой разницы между patch и diff. (Мы используем термин «patch», так как он чаще используется.)

Патч может начинаться с произвольного текста; команда patch игнорирует этот текст, но MQ использует его в качестве сообщения фиксации при создании ревизии. Чтобы найти начало содержания патча, patch ищет первую строку, которая начинается со строки «diff -».

MQ работает с унифицированным diff (patch может принять некоторые другие форматы просмотра, но MQ не умеет). Унифицированный формат содержит два вида заголовков. Заголовок файла описывает измененные файлы, он содержит имя файла для модификации. Когда patch видит новый заголовок файла, он ищет файл с таким именем, чтобы начать изменения.

После заголовка файла идет серия порций. Каждая порция начинается с заголовка, который определяет диапазон номеров строк в файле, которые изменяет данная порция. После заголовка, порция начинается и заканчивается через несколько (обычно три) не измененных строки текста из файла, которые называются контекстом для порции. Если имеется только небольшой разрыв между последовательными порциями, diff не печатает новый заголовок порции, он просто объединяет порции вместе, вставляя несколько строк из контекста между изменениями.

Каждая строка в контексте начинается с пробела. В порции строку, которая начинается с «-» означает «удалить эту строку», а строка, которая начинается с «+» означает «вставить эту строку» Например, изменившаяся строка представлена одним удалением и одной вставкой.

Мы вернемся к некоторым из наиболее тонких аспектов патчей позже (в разделе Раздел 12.6, «Более подробно о патчах»), но теперь вы должны иметь достаточно информации, для использования MQ прямо сейчас.

12.5. Начало работы с Mercurial Queues

Потому что MQ реализован в виде расширения, вы должны явно разрешить его, перед тем как использовать. (Вам не нужно ничего скачивать; MQ поставляется в стандартном пакете Mercurial.) Для того чтобы включить MQ, отредактируйте файл ~/.hgrc, и добавьте следующие строки.

[extensions]
hgext.mq =

После включения расширения, будет доступен ряд новых команд. Чтобы убедиться, что расширение работает, вы можете использовать hg help для помощи по команде qinit.

$ hg help qinit
hg qinit [-c]

init a new queue repository (DEPRECATED)

    The queue repository is unversioned by default. If -c/--create-repo is
    specified, qinit will create a separate nested repository for patches
    (qinit -c may also be run later to convert an unversioned patch repository
    into a versioned one). You can use qcommit to commit changes to this queue
    repository.

    This command is deprecated. Without -c, it's implied by other relevant
    commands. With -c, use "hg init --mq" instead.

options:

 -c --create-repo create queue repository

use "hg -v help qinit" to show more info

Вы можете использовать MQ с любым Mercurial репозиторием, и его команды работают только с тем, что находится в хранилище. Чтобы начать работу, просто подготовьте репозиторий используя команду qinit.

$ hg init mq-sandbox
$ cd mq-sandbox
$ echo 'line 1' > file1
$ echo 'another line 1' > file2
$ hg add file1 file2
$ hg commit -m'first change'
$ hg qinit

Эта команда создает пустую папку с именем .hg/patches, где MQ будет хранить свои метаданных. Как и во многих командах Mercurial, команда qinit ничего не печатает, если всё проходит успешно.

12.5.1. Создание нового патча

Чтобы начать работу над новым патчем, используйте команду qnew. Эта команда имеет один аргумент, имя patch-а для создания.

MQ будет использовать в качестве фактических имена файлов в директории .hg/patches, как вы можете увидеть ниже.

$ hg tip
changeset:   0:bf7787dcd87d
tag:         tip
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Thu Feb 02 14:10:01 2012 +0000
summary:     first change

$ hg qnew first.patch
$ hg tip
changeset:   1:322557d9d523
tag:         first.patch
tag:         qbase
tag:         qtip
tag:         tip
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Thu Feb 02 14:10:01 2012 +0000
summary:     [mq]: first.patch

$ ls .hg/patches
first.patch  series  status

Кроме того, сейчас в директории .hg/patches есть два файла, series и status. Файл series содержит список всех исправлений, которые знает MQ для этого репозитория, с одним патчем в каждой строке. Mercurial использует файл status для внутренней бухгалтерии; он отслеживает все патчи, которые MQ применил в этом репозитории.

[Примечание] Примечание

Вам может иногда понадобиться изменить файл series вручную, например, чтобы изменить последовательность, в которой применяются некоторые патчи. Тем не менее, вручную редактировать файл status почти всегда плохая идея, так как легко повредить данные MQ о происходящем.

После того как вы создали свой новый патч, вы можете редактировать файлы в рабочем каталоге, как вы это обычно делаете. Все нормальные команды mercurial, такие как hg diff и hg annotate работают так же, как раньше.

12.5.2. Обновление патча

Когда вы достигаете точки, где вы хотите сохранить свою работу, используйте команду qrefresh для обновления патча с которым вы работаете.

$ echo 'line 2' >> file1
$ hg diff
diff -r 322557d9d523 file1
--- a/file1	Thu Feb 02 14:10:01 2012 +0000
+++ b/file1	Thu Feb 02 14:10:02 2012 +0000
@@ -1,1 +1,2 @@
 line 1
+line 2
$ hg qrefresh
$ hg diff
$ hg tip --style=compact --patch
1[first.patch,qbase,qtip,tip]   919b9524323d   2012-02-02 14:10 +0000   bos
  [mq]: first.patch

diff -r bf7787dcd87d -r 919b9524323d file1
--- a/file1	Thu Feb 02 14:10:01 2012 +0000
+++ b/file1	Thu Feb 02 14:10:02 2012 +0000
@@ -1,1 +1,2 @@
 line 1
+line 2

Эта команда помещает сделанные вами изменения в рабочем каталоге на ваш патч, и обновляет свою соответствующую ревизию которая содержит эти изменения.

Вы можете запускать qrefresh так часто, как вам нравится, так что это хороший способ сохранять «контрольные точки» вашей работы. Обновляйте патч в подходящее время, попробуйте экспериментировать, и если эксперимент не получится, hg revert откатит ваши изменения обратно до последнего обновления.

$ echo 'line 3' >> file1
$ hg status
M file1
$ hg qrefresh
$ hg tip --style=compact --patch
1[first.patch,qbase,qtip,tip]   52117437a1b0   2012-02-02 14:10 +0000   bos
  [mq]: first.patch

diff -r bf7787dcd87d -r 52117437a1b0 file1
--- a/file1	Thu Feb 02 14:10:01 2012 +0000
+++ b/file1	Thu Feb 02 14:10:02 2012 +0000
@@ -1,1 +1,3 @@
 line 1
+line 2
+line 3

12.5.3. Укладка и отслеживания патчей

После того как вы закончили работу над патчем, или нужно работать с другим, вы можете использовать команду qnew еще раз для создания новых патчей. Mercurial сделает патч верхним из ваших существующих патчей.

$ hg qnew second.patch
$ hg log --style=compact --limit=2
2[qtip,second.patch,tip]   d535b10fd01c   2012-02-02 14:10 +0000   bos
  [mq]: second.patch

1[first.patch,qbase]   52117437a1b0   2012-02-02 14:10 +0000   bos
  [mq]: first.patch

$ echo 'line 4' >> file1
$ hg qrefresh
$ hg tip --style=compact --patch
2[qtip,second.patch,tip]   e933fc1784a2   2012-02-02 14:10 +0000   bos
  [mq]: second.patch

diff -r 52117437a1b0 -r e933fc1784a2 file1
--- a/file1	Thu Feb 02 14:10:02 2012 +0000
+++ b/file1	Thu Feb 02 14:10:03 2012 +0000
@@ -1,3 +1,4 @@
 line 1
 line 2
 line 3
+line 4

$ hg annotate file1
0: line 1
1: line 2
1: line 3
2: line 4

Обратите внимание, что патч содержит изменения в нашем предыдущем патче в качестве части своей контекста (вы можете увидеть это более четко в выводе hg annotate).

Пока, за исключением qnew и qrefresh, мы осторожно использовали только обычные команды Mercurial. Тем не менее, MQ предоставляет множество команд, которые проще использовать, когда вы думаете о патчах, как показано на рисунке ниже.

$ hg qseries
first.patch
second.patch
$ hg qapplied
first.patch
second.patch
  • Команда qseries перечисляет все патчи, о которых знает MQ в этом репозитории, от старых к новым (созданным совсем недавно).

  • Команда qapplied перечисляет все исправления, которые MQ применил в этом репозитории, опять от старых к новым.

12.5.4. Манипуляция стеком патчей

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

Применять патч соответствующей ревизии в репозитории, и последствия патча и ревизия видны в рабочей директории. Вы можете отменить применение патча использованием команды qpop. MQ до сих пор знает о, или управляет, извлеченными патчами, но патч уже не имеет соответствующего набора изменений в репозитории, и рабочий каталог не содержит изменения сделанные патчем. Рисунок 12.1, «Применение и отмена патчей в стеке патчей MQ» иллюстрирует разницу между применёнными и отслеживаемыми патчами.

Рисунок 12.1. Применение и отмена патчей в стеке патчей MQ

XXX add text

Вы можете повторно применить отменённый или извлечённый патч используя команду qpush. Она создает новую ревизию соответствующую патчу и изменения патча в очередной раз станут присутствовать в рабочей директории. Ниже приведены примеры qpop и qpush в действии.

$ hg qapplied
first.patch
second.patch
$ hg qpop
popping second.patch
now at: first.patch
$ hg qseries
first.patch
second.patch
$ hg qapplied
first.patch
$ cat file1
line 1
line 2
line 3

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

12.5.5. Вставка и извлечение нескольких патчей

Хотя по умолчанию qpush и qpop работают над одним патчем за раз, вы можете вставить и извлечь много патчей в один проход. Опция -a команды qpush приводит к вставке всех неприменённых патчей, а опция -a для qpop приводит к извлечению всех применённых патчей. (Для некоторых других способов вставки и извлечения многих патчей, смотрите раздел Раздел 12.8, «Получение максимальной производительности от MQ» ниже).

$ hg qpush -a
applying second.patch
now at: second.patch
$ cat file1
line 1
line 2
line 3
line 4

12.5.6. Безопасные проверки и их основа

Некоторые команды MQ проверяют рабочий каталог, прежде чем что-то делать, и не работают, если они находят какие-либо изменения. Они делают это, чтобы вы не потеряли изменения, которые вы уже сделали, но еще не включили в патч. Пример ниже иллюстрирует это, команда qnew не будет создавать новый патч, если есть изменения, вызванных в этом случае hg add для file3.

$ echo 'file 3, line 1' >> file3
$ hg qnew add-file3.patch
$ hg qnew -f add-file3.patch
abort: patch "add-file3.patch" already exists

Команды, которые проверяют рабочий каталог, все принимают опцию «Я знаю, что я делаю», которая всегда имеет имя -f. Точное значение -f зависит от команды. Например, в hg qnew -f будет включать любые не сохранённые изменения в новый патч который она создает, но hg qpop -f отменит изменения во всех файлах, измененные в результате применения патчей, которые она извлекает. Не забудьте прочитать документацию по опции -f для нужной вам команды, прежде чем использовать её!

12.5.7. Работа с различными патчами сразу

Команда qrefresh всегда обновляет верхний применяемый патч. Это означает, что вы можете приостановить работу по одному патчу (обновив его), извлечь или поместить другой патч наверх, и работать с тем патчем некоторое время.

Вот пример, который показывает, как можно использовать эту способность. Скажем вы разрабатываете новую функцию, как два патча. Первым из них является изменение ядра вашего программного обеспечения, а второй — слой поверх первого — изменения пользовательского интерфейса, использующее код, который вы только что добавили к ядру. Если вы заметили ошибку в ядре в то время как вы работаете на исправлением UI, вы можете легко исправить ядро. Просто вызовите qrefresh для патча пользовательского интерфейса, чтобы сохранить незавершенные изменения и qpop вплоть до патча ядра. Исправьте ошибку ядра, запустите qrefresh для патча ядра и qpush поднявшись к патчу пользовательского интерфейса и работайте с того места, где вы остановились.

12.6. Более подробно о патчах

MQ использует команду GNU patch для применения патчей, так что полезно узнать некоторые более детальные аспекты, как работает patch, и о самих патчах.

12.6.1. The strip count

Если вы посмотрите на заголовок файла патча, вы заметите, что путь к файлу, как правило содержит дополнительные компоненты в начале, которых нет в настоящем пути. Это остаток пути который люди использовали для создания патчей (люди до сих пор это делают, но уже реже с появлением современных средств контроля версий).

Алиса распаковала архив, изменила свои файлы, а затем решила, что она хочет создать патч. Она переименовывает свой рабочий каталог, распаковывает архив еще раз (это и обусловило необходимость переименования), и использует опции -r и -N команды diff для рекурсивно создания патча между не измененным каталогом и измененным. Результатом будет то, что имя не измененного каталога будет в начале пути каждого файла в заголовке иметь имя левого каталога, а путь каталога измененного будет в начале пути иметь правый каталог.

Кто-то получает патч от Алисы, вряд ли будет иметь неизмененный и измененый каталоги с точно такими же именами, команда patch имеет опцию -p указывающую на количество ведущих компонентов пути удаляют при применении патча. Это число называется счётчик удаления.

Опция «-p1» означает «использовать счётчик удаления с первого элемента». Если patch видит имя файл foo/bar/baz в заголовке файла, он удаляет foo и попытаться применить патч к файлу с именем bar/baz. (Строго говоря, счётчик удаления относится к количеству разделителей пути (и компонентов, которые следуют за ними). Счётчик удаления в значении 1 преобразует foo/bar в bar, но /foo/bar (обратите внимание на дополнительный ведущий слэш ) в foo/bar.)

«Стандартный» счётчик удаления для патчей равен 1, почти все патчи содержат один ведущий компонент пути, который необходимо отрезать. Команда Mercurial hg diff генерирует путь в этой форме, и команда hg import и MQ ожидают патчей со счётчиком удаления равным 1.

Если вы получили патч от кого-то, и вы хотите добавить патч в свою очередь, и патч требует другого счётчика удаления, чем 1, вы не можете просто применить qimport к патчу, потому что qimport еще не имеет опции -p (смотрите заявку issue 311) . Лучше всего, чтобы запустить qnew для создания собственного патча, а затем использовать patch -pN, чтобы применить их исправления, а затем запустить hg addremove чтобу узнать какие файлы добавлены или удалены патчем, а затем выполнить hg qrefresh. Эта сложность может стать ненужной, смотрите issue 311 для деталей.

12.6.2. Стратегия для применения патчей

Когда patch применяет блок, он пытается использовать последовательно несколько менее точные стратегии, чтобы попытаться применить блок. Этот метод уменьшения точности часто дает возможность применить патч, который был создан на старой версии файла, и применить его на новой версии этого файла.

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

Если поиск по контексту не удается, patch устраняет первую и последнюю строку контекста, и попытается найти сокращённый контекст. Если блок с ограниченным контекстом успешно применится, он выводит сообщение о том, что он применяет блок с фактором промаха (число после фактора промаха это коэффициент показывающий, сколько строк из контекста обрезано в патче, чтобы патч применился).

Если ни один из этих методов сработал, patch печатает сообщение о том, что блок отклонен. Он сохраняет отклоненные блоки (называя просто «rejects») в файле с тем же именем и расширением .rej. Он также сохраняет не измененную копию файла с расширением .orig; копия файла без расширения будет содержать изменения, из применённых блоков, которые применились чисто. Если у вас есть патч, который изменяет foo с 6 блоками, а один из них не применяются, вы получите: неизмененный foo.orig, foo.rej содержащие один блок, и foo, содержащий изменения, внесенные в пяти успешных блоках

12.6.3. Некоторые причуды из представления патчей

Есть несколько полезных фактов о том, как patch работает с файлами.

  • Это уже должно быть очевидно, но patch не может справиться с бинарными файлами.

  • Он также не заботится о бите исполняемости, создает новые файлы читаемыми, но не исполняемыми.

  • patch считает удалённый файл, как различие между фалом который удалён и пустым файлом. Так что ваша идея «Я удалил этот файл» в патче выглядят как «каждая строка этого файла была удалена».

  • Это относится и к добавленным файлам как к различию между пустым файлом и файлом который будет добавлен. Таким образом, в патче, ваша идея «я добавил этот файл» выглядит как «каждая строка этого файла был добавлена».

  • Это относится и к переименованным файлам, как удаление старого файла, и добавление нового. Это означает, что переименованные файлы будут иметь большой след в блоке. (Заметим также, что mercurial в настоящее время не попытаться сделать вывод, когда файлы были переименованы или скопированы и исправлены.)

  • patch не может представлять пустые файлы, так что вы не можете использовать патч для представления понятия «Я добавила пустой файл в дерево».

12.6.4. Остерегайтесь неточностей

Применение блоков со смещением, или фактором неточности, часто будет полностью успешным, такие неточные методы естественно оставляют открытой возможность повреждать исправленный файл. Большинство случаев обычно связаны с применением патча два раза, или на неверное место расположения файла. Если patch или qpush постоянно упоминает о смещении или факторе неточности, вы должны убедиться, что измененные файлы правильны.

Часто хорошая идея, обновлять патч, который применился со смещением или фактором неточности, обновление патча порождает новые контекстные связи, которые помогут применить патч чисто. Я говорю: «часто», а не «всегда», потому что иногда обновление патча сделает его не применяемым для различных ревизий основных файлов. В некоторых случаях, например, когда вы поддерживаете патч, который должен находится на вершине нескольких версий исходного дерева, это приемлемо иметь патч применяемый с некоторыми неточностями, если вы убедились в правильности результатов применения патча в таких случаях.

12.6.5. Обработка отказа

Если qpush не удается применить патч, он выведет сообщение об ошибке и завершит работу. Если он оставит .rej файлы, как правило, лучше устранить отклонения блоков перед добавлением других патчей или какой-либо дальнейшей работой.

Если ваш патч применялся чисто, и больше не может, потому что вы изменили исходный код, на котором основан ваши патч, Mercurial Queues может помочь, смотрите в разделе Раздел 12.9, «Обновление патчей когда исходный код измененился» для подробностей.

К сожалению, не так много методов для решения отклоненных блоков. Чаще всего, вам понадобится просмотреть .rej файл и отредактировать целевой файл, применяя отклоненный блок вручную.

Разработчик ядра Linux, Крис Мейсон (автор Mercurial Queues), пишет инструмент под названием mpatch ((http://oss.oracle.com/~mason/mpatch/), который принимает простой подход к автоматизации применения блоков отклонённых patch. Команда mpatch может помочь с 4-я распространенными причинами отклонения блока:

  • Контекст, в середине блока изменился.

  • Часть контекста блока отсутствуют в начале или в конце.

  • Большой блок может применится лучше полностью или частично, если его разбить на мелкие куски.

  • Блок удаляет строки с несколько иным содержанием, чем в настоящее время в файле.

Если вы используете mpatch, вы должны быть особенно осторожным, и проверять свои результаты, когда закончите. В самом деле, mpatch применяет метод двойной проверки вывода инструмента, автоматически передавая вас программе объединения, когда он сделает свою работу, так что вы можете проверить свою работу и закончить всё оставшееся слияние.

12.7. Подробнее о управление патчами

По мере того как вы познаёте MQ, вы пожелаете выполнять другие виды операций по управлению патчами.

12.7.1. Удаление нежелательных патчей

Если вы хотите, избавиться от патча, воспользуйтесь командой hg qdelete, чтобы удалить файл патча и удалить его из серии патчей. Если вы попытаетесь удалить патч, который все еще применяется, hg qdelete откажется.

$ hg init myrepo
$ cd myrepo
$ hg qinit
$ hg qnew bad.patch
$ echo a > a
$ hg add a
$ hg qrefresh
$ hg qdelete bad.patch
abort: cannot delete applied patch bad.patch
$ hg qpop
popping bad.patch
patch queue now empty
$ hg qdelete bad.patch

12.7.2. Преобразование в и из постоянных ревизий

Как только вы закончите работу над патчем и хотите превратить его в постоянную ревизию, используйте команду hg qfinish. При переходе к ревизии команда определяет патч, который вы хотите превратить в обычную ревизию, этот патч уже должен быть применен.

$ hg qnew good.patch
$ echo a > a
$ hg add a
$ hg qrefresh -m 'Good change'
$ hg qfinish tip
$ hg qapplied
$ hg tip --style=compact
0[tip]   ea28b7ac83fb   2012-02-02 14:09 +0000   bos
  Good change

Команда hg qfinish принимает опцию --all или -a, который преобразует все применённые патчи в обычные ревизии.

Кроме того, можно превратить существующую ревизию в патч, с помощью опции -r команды hg qimport.

$ hg qimport -r tip
$ hg qapplied
0.diff

Обратите внимание, что это имеет смысл только для преобразования набора изменений в патч, если вы не распространяете ревизии в любой другой репозитарий. ID импортируемого набора изменений будет изменяться каждый раз, когда вы обновляете патч, который делает Mercurial рассматривать его как не связанный с первоначальной ревизией, если вы его передадите в любой другой репозитарий.

12.8. Получение максимальной производительности от MQ

MQ очень эффективен при обработке большого количества исправлений. Я провел несколько экспериментов производительности в середине 2006 года для доклада, который я делал в 2006 на конференции EuroPython (на современном оборудовании, вы должны ожидать более высокую производительность, чем вы увидите ниже). Я использовал в качестве моего набора данных серии патчей linux 2.6.17-mm1, которая состоит из 1738 патчей. Я применил их на поверх репозитория linux ядра, содержащее всего 27472 ревизии между linux 2.6.12-rc2 и linux 2.6.17.

На моём старом, медленном ноутбуке, я выполнил hg qpush -a для всех 1738 патчей в 3,5 минуты, а hg qpop -a всего в 30 секунд. (На новом ноутбуке, время добавления всех патчей снизилось до 2 минут). Я выполнил qrefresh для одного большого патча (который состоял из 22779 строк изменений в 287 файлах) за 6,6 секунды.

Очевидно, что MQ хорошо приспособлен к эксплуатации на больших деревьях, но есть несколько трюков которые можно использовать для получения наилучшей производительности.

Прежде всего, попытайтесь выполнять «пакетные» операции вместе. Каждый раз, когда вы запускаете qpush или qpop, эти команды проверяют рабочую директорию один раз, чтобы убедиться, что вы не внесли каких нибудь изменений, а затем забыл запустить qrefresh. На небольшом дереве, время этой проверки невелико. Однако, на средних деревьях (содержащих десятки тысяч файлов), это может занять секунду или даже больше.

Команды qpush и qpop позволяют вставлять и извлекать несколько патчей сразу. Вы можете указать «патч назначения» который вы хотите применить последним. qpush с указанным назначением, то он будет вставлять патчи до этого патча в верх стека применения. qpop с назначением, MQ извлекает патчи до того как патч назначения не окажется на самом верху.

Вы можете определить патч назначения, используя либо название патча, или номер. Если вы используете числовую адресацию, патчи начинаются с нуля, это означает, что первый патч равен нулю, второй единице, и так далее.

12.9. Обновление патчей когда исходный код измененился

Как правило имея патч на верху стека вы не изменяете напрямую нижележащий код в репозитории. Если вы работаете над изменениями в коде третьих сторон, или над особенностью, которая занимает больше времени, чем скорость развития изменений кода внизу, вам часто нужно синхронизироваться с исходным кодом, и исправлять блоки в своих патчах, которые уже не применяются. Это называется перебазирование серии патчей.

Проще всего это сделать так: выполнить hg qpop hg -a для ваших патчей, затем hg pull ревизий из основного репозитория, и, наконец выполнить hg qpush -a ваших патчей снова. MQ будет останавливать вставку каждый раз, когда проходит через патч, который не применяется из-за конфликтов, что позволяет исправлять ваши конфликты, и выполнять qrefresh для пострадавших патчей, и продолжать вставку, пока не исправите весь стек.

Этот подход прост в использовании и работает хорошо, если вы не ожидаете изменений в исходный код, которые повлияют на эффективность ваших патчей. Если ваш стек патчей касается кода, который модифицируются часто или агрессивно в базовом репозитории, однако, исправление отклонённых блоков вручную быстро становится скучным.

Можно частично автоматизировать процесс перебазирования. Если ваши патчи применяются чисто к другим ревизиям из репозитория, MQ может использовать эту информацию, чтобы помочь вам в разрешении конфликтов между патчами и различными ревизиями.

Этот процесс немного сложнее.

  1. Во-первых, выполняем hg qpush -a применяя все ваши патча к старшей ревизии, к которой вы знаете, что они применяются чисто.

  2. Сохранить резервную копию исправляемого каталога с помощью команды hg qsave hg -e hg -c. Она выводит имя директории, в которой она сохранит патчи. Это сохранит исправления в каталоге называемом .hg/patches.N, где N является малым числом. Она также «сохраняет ревизию» поверх которой применяются патчи, и записывает состояние файлов series и status.

  3. Используйте hg pull для внесения изменений в основной репозиторий. (Не запускать hg pull -u, смотрите ниже, почему.)

  4. Обновитесь до новой верхней ревизии, используя hg update -C, переписываяизменения ваших патчей.

  5. Объедините все патчи hg qpush -m -a. Опция -m qpush говорит MQ выполнить трехстороннее слияние, если патч не может применится.

В hg qpush hg -m, каждый патч в файле series применяется в обычном режиме. Если исправление применяется с неточностями или отвергается, MQ смотрит на вашу очередь qsaved, и также выполняет трехстороннее слияние с соответствующей ревизией. Для слияния Mercurial использует нормальный механизм слияния, поэтому оно может быть передано GUI-инструменту слияния, чтобы помочь вам в решении проблем.

Когда вы закончите разрешение эффектов патча, MQ обновит ваш патч, основанный на результате слияния.

В конце этого процесса, репозиторий будет иметь одну дополнительную голову от старой очереди патчей, а копия старой очереди патч будет лежать в .hg/patches.N. Вы можете удалить дополнительную голову с помощью hg qpop -a -n patches.N или hg strip. Вы можете удалить .hg/patches.N когда вы уверены, что вам она больше не нужна в качестве резервной копии.

12.10. Идентификация патчей

Команды MQ, которые работают с патчами позволяют ссылаться на патч либо используя его имя или номер. По имени достаточно очевидно; передать имя foo.patch команде qpush, например, и она будет вставлять и применять патчи до foo.patch.

Для сокращения, вы можете обратиться к патчу с использованием имени и цифрового смещения; foo.patch-2 означает «второй патч перед foo.patch», а bar.patch+4 означает «четвёртый патч после bar.patch».

Ссылка на патч по индексу не сильно отличается. Первый патч напечатаны в выходе qseries это нулевой патч нулю (да, это один из тех систем отсчёта, которые начинаются с нуля); второй патч 1, и так далее.

MQ также позволяет легко работать с патчами, как вы используете нормальные команды Mercurial команд. Каждая команда, которая принимает id ревизии также примет название применяемого патч. MQ увеличивает количество тегов в репозитории, как правило по одному для каждого применённого патча. Кроме того, специальные теги qbase и qtip определяют «самый нижний» и «самый верхний» применённые патчи, соответственно.

Эти дополнительное тегирование нормальными тегами Mercurial делает возможность внесения патчей еще более легким.

Потому что MQ делает имена патчей доступными для остальных команд Mercurial через обычный внутренний механизм тегов, вам не нужно вводить полное имя файла с патчем, если вы хотите идентифицировать его по имени.

Другим полезным следствием представления патчей именами тегов является то, что когда вы выполните команду hg log, она будет отображать название патча, как тег, просто как часть нормального вывода. Это позволяет визуально различать применённые патчи от лежащие в основе «обычных» ревизий. В следующем примере показано несколько простых команд Mercurial использующихся с применёнными патчами.

$ hg qapplied
first.patch
second.patch
$ hg log -r qbase:qtip
changeset:   1:8bd990ad127b
tag:         first.patch
tag:         qbase
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Thu Feb 02 14:09:58 2012 +0000
summary:     [mq]: first.patch

changeset:   2:ce57e98425d7
tag:         qtip
tag:         second.patch
tag:         tip
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Thu Feb 02 14:09:59 2012 +0000
summary:     [mq]: second.patch

$ hg export second.patch
# HG changeset patch
# User Bryan O'Sullivan <bos@serpentine.com>
# Date 1328191799 0
# Node ID ce57e98425d79f974ee096cc6fdf4cc5e4b55918
# Parent  8bd990ad127bfbc3153a923091f86021aa7a7818
[mq]: second.patch

diff -r 8bd990ad127b -r ce57e98425d7 other.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/other.c	Thu Feb 02 14:09:59 2012 +0000
@@ -0,0 +1,1 @@
+double u;

12.11. Полезные вещи, которые необходимо знать

Есть ряд аспектов использования MQ, которые не вписываются аккуратно в отдельные разделы, но их хорошо бы знать. Вот они, в одном месте.

  • Обычно, когда вы применяете к патчу qpop и потом qpush, ревизия представляющая патч после извлечения/вставки будет иметь другой идентификатор, чем ревизия, которые представляла патч ранее. Смотрите раздел Раздел B.1.14, «qpush — вставляет патчи в стек» для информации о том, почему так происходит.

  • Это не очень хорошая идея, использовать hg merge изменений из другой ветви с ревизией патча, по крайней мере, если вы хотите сохранить «патченость» этой ревизии и ревизий ниже её в стеке патчей. Если вы попытаетесь сделать это, всё пройдёт успешно, но MQ будет запутан.

12.12. Управление патчами в репозитории

Так как каталог .hg/patches находится вне рабочего каталога репозитория Mercurial, «основной» Mercurial репозиторий ничего не знает об управлении или наличие патчей.

Это открывает интересные возможности управления содержанием каталога патчей как отдельным Mercurial репозиторием внутри собственного репозитория. Это может быть полезным для работы. Например, вы можете работать над патчем некоторое время, выполнить qrefresh, потом hg commit для текущего состояния патча. Это позволяет вам «откатываться» на этоу версию патча позже.

Вы можете поделиться различными версиями одного и того же стека патчей между несколькими обычными репозиториями. Я использую это, когда я занимаюсь разработкой фишек ядра linux. У меня есть чистый экземпляр с исходными текстами ядра для нескольких архитектур процессоров, а также клонированные реозитории каждый, из который содержит патчи над, которыми я работаю. Когда я хочу проверить изменения на различных архитектурах, я вставляю свои текущие патчи для исправления м храни ревизий, связанные с этим деревом ядра, извлечение и вставка всех моих патчей сборкой и тестированием

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

12.12.1. Поддержка MQ для репозитория патчей

MQ позволяет работать с директорией .hg/patches как с репозиторием, когда вы готовите репозиторий для работы с патчами используя qinit, вы можете передать опцию hg -c для создания каталога .hg/patches как репозиторий Mercurial.

[Примечание] Примечание

Если вы забыли использовать опцию hg -c, вы можете просто пойти в каталог .hg/patches в любое время и выполнить hg init. Не забудьте добавить запись в .hgignore о файле status, хотя (hg qinit hg -c делает это за вас автоматически); Вы ведь действительно не хотите, отслеживать файл status.

Для удобства, если MQ замечает, что каталог .hg/patches репозиторий, он будет автоматически выполнять hg add каждый патч, который вы создаете или импортируете.

MQ обеспечивает короткую команду qcommit, запускающую hg commit в директории .hg/patches.

Наконец, для удобства управления директорией патчей, можно определить alias mq на системах Unix. Например, в системах linux c помощью оболочки bash, вы можете включить следующий фрагмент в файле ~/.bashrc.

alias mq=`hg -R $(hg root)/.hg/patches'

После этого можно отдавать команды вида mq pull из главного репозитория.

12.12.2. Несколько вещей для отслеживания

MQ поддерживает для работы с репозиторием полные патчи ограниченного небольшими отношениями.

MQ не умеет автоматически обнаруживать изменения, внесенные в каталог патчей. Если вы выполнили hg pull, вручную отредактировали, или сделали hg update изменений для патчей или файлом series, вам придется выполнить hg qpop -a and then hg qpush -a в базовом хранилище, чтобы увидеть эти изменения там. Если вы забудете это сделать, вы можете спутать MQ планы применения патчей.

12.13. Инструменты сторонних разработчиков для работы с патчами

Когда вы поработаете с патчами некоторое время, вы обнаружите недостаток инструментов, которые помогут вам понять и управлять патчами, с которыми вы имеете дело.

Команда diffstat [web:diffstat] генерирует гистограммы изменений, внесенных с каждым файлом в патч. Она обеспечивает хороший способ «получить смысл» патча — какие файлы он затрагивает, и сколько изменений применит к каждому файлу и в целом. (Я считаю, что это хорошая идея использовать опцию -p diffstat для отображения статистики различий как нечто само собой разумеющееся, так как иначе он будет пытаться делать умные вещи с префиксами имен файлов, которые неизбежно запутают по крайней мере меня).

$ diffstat -p1 remove-redundant-null-checks.patch
bash: diffstat: command not found
$ filterdiff -i '*/video/*' remove-redundant-null-checks.patch
bash: filterdiff: command not found

Пакет patchutils [web:patchutils] имеет неоценимое значение. Он предоставляет набор небольших утилит, которые следуют «Unix философии» каждая делает одну полезную вещь с патчем. Из команд patchutils я использую больше всего filterdiff, которая извлекает из подмножеств файла патча. Например, если патч, который изменяет сотни файлов в десятках каталогов, один вызов filterdiff может генерировать меньший патч, который затрагивает только файлы, имена которых совпадают с неким шаблоном. Смотрите раздел Раздел 13.9.2, «Просмотр истории патча» для других примеров.

12.14. Хорошие методы работы с патчами

Если вы работаете над серией патчей представляя их свободному программному обеспечению или проекту с открытым кодом, или серии, которую вы намерены рассматривать как последовательность регулярных ревизий которую вы закончили, вы можете использовать некоторые простые методы, чтобы сохранить вашу работу организованной.

Давайте вашим патчам описательные имена. Хорошее имя для патча может быть rework-device-alloc.patch, потому что оно будет сразу же давать вам подсказку на цель исправления. Длинные имена не должны быть проблемой, вы не будете вводить имена часто, а будете работать с такими командами, как qapplied и qtop снова и снова. Хорошее имя становится особенно важным, когда у вас есть несколько патчей над которыми вы работаете, или если вы жонглировали целым рядом различных задач, и ваши патчи только получили только часть вашего внимания.

Помните о том, над каким патч вы работаете. Используйте команду qtop и просматривайте текст ваших патчей чаще, например, с использованием hg tip -p — что быть уверенным в том, где вы находитесь. Я несколько раз работал, и запускал qrefresh не для того патча, которого хотел, и часто сложно перенести изменения в правильный патч после внесения их в неверный.

По этой причине, стоит потратить немного времени, чтобы узнать, как использовать некоторые из утилит сторонних разработчиков, которые я описал в разделе Раздел 12.13, «Инструменты сторонних разработчиков для работы с патчами», в частности, diffstat и filterdiff. Первий даст вам представление о том, какие изменения патч делает, а второй позволяет легко перемещать выбраные блоки из одного патча и в другой.

12.15. Поваренная книга MQ

12.15.1. Управление «тривиальными» патчами

Так как накладные расходы на добавление файлов в новый репозиторий Mercurial настолько низки, есть смысл, управлять патчами его путём, даже если вы просто хотите внести некоторые изменения в архив с исходными кодами, который вы скачивали.

Начните с загрузки и распаковки архива исходных кодов, и превратите его в репозиторий Mercurial.

$ download netplug-1.2.5.tar.bz2
$ tar jxf netplug-1.2.5.tar.bz2
$ cd netplug-1.2.5
$ hg init
$ hg commit -q --addremove --message netplug-1.2.5
$ cd ..
$ hg clone netplug-1.2.5 netplug
updating to branch default
18 files updated, 0 files merged, 0 files removed, 0 files unresolved

Продолжим создавать стек патчей и делать изменения

$ cd netplug
$ hg qinit
$ hg qnew -m 'fix build problem with gcc 4' build-fix.patch
$ perl -pi -e 's/int addr_len/socklen_t addr_len/' netlink.c
$ hg qrefresh
$ hg tip -p
changeset:   1:b51e7b2c8901
tag:         build-fix.patch
tag:         qbase
tag:         qtip
tag:         tip
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Thu Feb 02 14:10:00 2012 +0000
summary:     fix build problem with gcc 4

diff -r fddb0fa5f203 -r b51e7b2c8901 netlink.c
--- a/netlink.c	Thu Feb 02 14:09:59 2012 +0000
+++ b/netlink.c	Thu Feb 02 14:10:00 2012 +0000
@@ -275,7 +275,7 @@
         exit(1);
     }
 
-    int addr_len = sizeof(addr);
+    socklen_t addr_len = sizeof(addr);
 
     if (getsockname(fd, (struct sockaddr *) &addr, &addr_len) == -1) {
         do_log(LOG_ERR, "Could not get socket details: %m");

Давайте предположим, что прошло несколько недель или месяцев, и ваш автор вашего пакета сообщит о выходе новой версии. Во-первых, вносим эти изменения в репозиторий.

$ hg qpop -a
popping build-fix.patch
patch queue now empty
$ cd ..
$ download netplug-1.2.8.tar.bz2
$ hg clone netplug-1.2.5 netplug-1.2.8
updating to branch default
18 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cd netplug-1.2.8
$ hg locate -0 | xargs -0 rm
$ cd ..
$ tar jxf netplug-1.2.8.tar.bz2
$ cd netplug-1.2.8
$ hg commit --addremove --message netplug-1.2.8

С помощью конвеера, начинающегося с hg locate выше удаляем все файлы в рабочем каталоге, так что команда hg commit с опцией --addremove может сообщить, какие файлы были действительно удалены в новой версии исходников.

Наконец, применяем ваши патчи поверх нового дерева

$ cd ../netplug
$ hg pull ../netplug-1.2.8
pulling from ../netplug-1.2.8
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 12 changes to 12 files
(run 'hg update' to get a working copy)
$ hg qpush -a
(working directory not at a head)
applying build-fix.patch
now at: build-fix.patch

12.15.2. Объединение целых патчей

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

Порядок, в котором вы складываете патчи важен. Если верхним применяется патч foo, и вы складываете qfold bar и quux в него, вы в конечном итоге с помощью патча, получите тот же эффект, как при применении сначала foo, потом bar, а затем quux.

12.15.3. Слияние части одного патча с другим

Слияние частей патча в другой сложнее, чем слияние целых патчей.

Если вы хотите переместить изменения некоторых файлов, вы можете использовать опции -i и -x команды filterdiff для выбора изменений из одного патча, присоедините ее выход к концу патча, в который хотите их влить. Вам, как правило, не требуется вносить изменения в патч измененный объединением. Вместо этого, MQ сообщит что некоторые блоки отвергнуты, когда вы попытаетесь сделать qpush (те блоки которые вы переместили в другой патч), и вы можете просто сделать qrefresh для удаления из патча дубликатов блоков.

Если у вас есть патч, который имеет несколько кусков изменяющих файл, и вы только хотите перенести часть из них, работа становится более грязным делом, но все равно можно частично автоматизировать его. Использование lsdiff -nvv печатает различные метаданные о патче.

$ lsdiff -nvv remove-redundant-null-checks.patch
bash: lsdiff: command not found

Это команда выводит три различных номера:

  • (В первой колонке) номер файла для идентификации каждого файла модифицируемого в патче;

  • (На следующей строке с отступом) номер строки в рамках изменяемого файла, где начинается блок, и

  • (на той же линии) номер блока идентифицирующего блок.

Вам придется использовать некоторый визуальный осмотр, и чтение патча, чтобы идентифицировать файл и блок с номерами которые вы хотите, но вы можете передать их в filterdiff используя опции --files и --hunks, чтобы выбрать именно тот файл и блок который вы хотите извлечь.

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

12.16. Различия между quilt и MQ

Если вы уже знакомы с quilt, MQ обеспечивает аналогичный набор команд. Есть некоторые различия в том, как они работают.

Вы уже заметили, что для большинства команд quilt есть аналоги в MQ, только начинаются они с «q». Исключения в quilt команды add и remove, аналоги для них есть в обычном Mercurial команды hg add и hg remove. Нет эквивалента в MQ команды quilt edit.