1. Ivan Galin
  2. hgbook-ru

Source

hgbook-ru / ru / ch12-mq.xml

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : -->

<chapter id="chap:mq">
  <?dbhtml filename="managing-change-with-mercurial-queues.html"?>
  <title>Управление изменениями с Mercurial Queues</title>

  <sect1 id="sec:mq:patch-mgmt">
    <title>Проблема управления патчами</title>

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

    <para id="x_3ad">Это простой случай решения проблемы <quote>управлением патчами</quote>. У вас есть <quote>upstream</quote> дерева исходных текстов, который вы не можете изменить, вы должны внести некоторые локальные изменения в части upstream дерева, и вы хотите иметь возможность сохранить эти изменения отдельно, так чтобы вы могли применить их в новых версиях upstream исходников.</para>

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

    <para id="x_3af">Поставщикам операционных систем, включающих программное обеспечение с открытыми исходниками, часто требуется вносить изменения в пакеты которые они распространяют, чтобы они правильно установить их в своей среде.</para>

    <para id="x_3b0">Когда у вас есть несколько изменений, легко управлять одним патчем с использованием стандартных программ <command>diff</command> и <command>patch</command> (смотрите раздел <xref linkend="sec:mq:patch"/> для обсуждения этих инструментов). После того, как количество изменений растет, начинает иметь смысл сохранить патчи как отдельные <quote>части работы</quote>, так чтобы, например, один патч содержал исправление только одной ошибки (патч может изменять несколько файлов, но он делает <quote>только одну вещь</quote>), и вы можете иметь несколько таких патчей для различных ошибок исправленых вами и локальными изменениями которые вам требуются. В этой ситуации, если вы предоставите патч, исправляющий ошибку сопровождающему пакет разработчику, и они включают ваши исправления в следующем релизе, вы можете просто удалить, один патч, при обновлении до новой версии.</para>

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

    <para id="x_3b2">К счастью, Mercurial включает в себя мощное расширение, Mercurial Queues (или проще <quote>MQ</quote>), которое упрощает задачу по массовому управлению патчами.</para>

  </sect1>
  <sect1 id="sec:mq:history">
    <title>Предыстория Mercurial Queues</title>

    <para id="x_3b3">В конце 1990-х, некоторые разработчики ядра linux начали поддерживать <quote>серии патчей</quote>, что изменило поведения ядра linux. Некоторые из этих серий были направлены на стабильность, некоторые добавляли фичи, у других был более спекулятивный характер.</para>

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

    <sect2 id="sec:mq:quilt">
      <title>A patchwork quilt</title>

      <para id="x_3b5">В начале 2003 года Andreas Gruenbacher и Martin Quinson заимствовали подход сценариев Эндрю и опубликовали инструмент под названием <quote>patchwork quilt</quote> <citation>web:quilt</citation>, или просто <quote>quilt</quote> (смотрите <citation>gruenbacher:2005</citation> для документ с его описанием). Поскольку quilt существенно автоматизировало управление исправлениями, он быстро приобрел много последователей среди открытых разработчиков программного обеспечения.</para>

      <para id="x_3b6">Quilt управляет <emphasis>стеком патчей</emphasis> над деревом директорий. Для начала, вам говорите quilt управлять деревом каталогов, а также указываете, какими файлами вы хотите управлять, он хранит названия и содержание этих файлов. Чтобы исправить ошибку, вы создаете новый патч (с помощью одной команды), редактируете файлы которые вам нужно исправить, потом <quote>обновляете</quote> патч.</para>

      <para id="x_3b7">На шаге обновления вызывается quilt для сканирования каталогов, он обновляет патч со всеми изменениями, которые вы сделали. Вы можете создать один патч поверх первого, который будет отслеживать изменения, необходимые для изменения дерева <quote>дерево с применённым первым патчем</quote> к <quote>дереву применяется второй патч</quote>.</para>

      <para id="x_3b8">Вы можете <emphasis>управлять</emphasis> тем, какие патчи применяются к дереву. Если <quote>извлечь</quote> патч, изменения, внесенные этим патчем исчезнут из дерева каталогов. Quilt  помнит, какие патчи вы извлекли, хотя, вы можете так же <quote>вставить</quote> убраный патч еще раз, и дерево каталогов, будет восстановлено с содержащим изменения в патче. Самое главное, вы можете запустить команду <quote>обновить</quote> в любой момент, а верхний применяемый патч будет обновляться. Это означает, что вы можете в любое время изменить и какие патчи применяются и какие изменения внесены в эти патчи.</para>

      <para id="x_3b9">Quilt ничего не знает об инструментах контроля версий, так что работает одинаково над  распакованными архивами или рабочей копией subversion.</para>
    </sect2>

    <sect2 id="sec:mq:quilt-mq">
      <title>От patchwork quilt до Mercurial Queues</title>

      <para id="x_3ba">В середине 2005 года, Крис Мейсон взял особенности quilt и написал расширение, которое он назвал Mercurial Queues, которое добавило похожее на quilt поведение к  Mercurial.</para>

      <para id="x_3bb">Ключевой разницей между quilt и MQ является то, что quilt ничего не знает о системах управления версиями, а MQ <emphasis>интегрирована</emphasis> в mercurial. Каждый патч, который вы вставляете отображается как ревизия Mercurial. Извлекаете патч, и ревизия уходит.</para>

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

    </sect2>
  </sect1>
  <sect1>
    <title>Огромное преимущество MQ</title>

    <para id="x_3bd">Я не могу переоценить значение того, что предлагает MQ объединяя патчи и контроль версий.</para>

    <para id="x_3be">Одна из основных причин того, что патчи сохраняются в мире свободного программного обеспечения и открытого исходного кода, несмотря на наличие более мощных инструментов контроля версий &emdash; это <emphasis>гибкость</emphasis> которую они предлагают.</para>

    <para id="x_3bf">Традиционные инструменты контроля версий делают окончательную и необратимую записи всего, что вы делаете. Хотя это имеет большое значение, это также несколько удручающе. Если вы хотите выполнить дикий эксперимент, вы должны быть осторожны в том, как вы это делаете, или вы рискуете оставить ненужные или что еще хуже, вводящие в заблуждение или дестабилизирующие &emdash; следы вашей ошибки и просчета и запишете ошибку в постоянную регистрацию изменений.</para>

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

    <para id="x_3c1">Для примера, интеграция патчей с контролем версий даёт понимание патчей и отладку последствий их взаимодействия с кодом <emphasis>чрезвычайно</emphasis> легче. Так как каждое применение патчей связанно с ревизией, вы можете прочитать <command role="hg-cmd">hg log</command> имя файла для просмотра ревизии и исправлений наложенных патчем. Вы можете использовать команду <command role="hg-cmd">hg bisect</command> для двоичного поиска по всем ревизиям и применять патчи, чтобы увидеть, где появилась ошибка и где она была исправлена. Вы можете использовать команду <command role="hg-cmd">hg annotate</command> чтобы посмотреть, какие ревизии или патчи изменили данную строку исходного файла. И так далее.</para>
  </sect1>

  <sect1 id="sec:mq:patch">
    <title>Понимание патчей</title>

    <para id="x_3c2">Потому что MQ не скрывает свой патч-ориентированный характер, было бы полезно, понять, что такое патчи, а также немного о том, какие инструменты работают с ними.</para>

    <para id="x_3c3">Традиционная команда unix <command>diff</command> сравнивает два файла и выводит список различий между ними. Команда <command>patch</command> понимает эти различия, как <emphasis>изменения</emphasis> которые нужно внести в файл. Посмотрите ниже на простом примере из этих команд в действии.</para>

      &interaction.mq.dodiff.diff;

    <para id="x_3c4">Тип файла, который генерирует <command>diff</command><command>patch</command> принимает в качестве входных данных), называется <quote>patch</quote> или <quote>diff</quote> нет никакой разницы между patch и diff. (Мы используем термин <quote>patch</quote>, так как он чаще используется.)</para>

    <para id="x_3c5">Патч может начинаться с произвольного текста; команда <command>patch</command> игнорирует этот текст, но MQ  использует его в качестве сообщения фиксации при создании ревизии. Чтобы найти начало содержания патча, <command>patch</command> ищет первую строку, которая начинается со строки <quote><literal>diff -</literal></quote>.</para>

    <para id="x_3c6">MQ работает с <emphasis>унифицированным</emphasis> diff (<command>patch</command> может принять некоторые другие форматы просмотра, но MQ не умеет). Унифицированный формат содержит два вида заголовков. <emphasis>Заголовок файла</emphasis> описывает измененные файлы, он содержит имя файла для модификации. Когда <command>patch</command> видит новый заголовок файла, он ищет файл с таким именем, чтобы начать изменения.</para>

    <para id="x_3c7">После заголовка файла идет серия <emphasis>порций</emphasis>. Каждая порция начинается с заголовка, который определяет диапазон номеров строк в файле, которые изменяет данная порция. После заголовка, порция начинается и заканчивается через несколько (обычно три) не измененных строки текста из файла, которые называются <emphasis>контекстом</emphasis> для порции. Если имеется только небольшой разрыв между последовательными порциями, <command>diff</command> не печатает новый заголовок порции, он просто объединяет порции вместе, вставляя несколько строк из контекста между изменениями.</para>

    <para id="x_3c8">Каждая строка в контексте начинается с пробела. В порции строку, которая начинается с <quote><literal>-</literal></quote> означает <quote>удалить эту строку</quote>, а строка, которая начинается с <quote><literal>+</literal></quote> означает <quote>вставить эту строку</quote> Например, изменившаяся строка представлена одним удалением и одной вставкой.</para>

    <para id="x_3c9">Мы вернемся к некоторым из наиболее тонких аспектов патчей позже (в разделе <xref linkend="sec:mq:adv-patch"/>), но теперь вы должны иметь достаточно информации, для использования MQ прямо сейчас.</para>
  </sect1>

  <sect1 id="sec:mq:start">
    <title>Начало работы с Mercurial Queues</title>

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

    <programlisting>[extensions]
hgext.mq =</programlisting>

    <para id="x_3cb">После включения расширения, будет доступен ряд новых команд. Чтобы убедиться, что расширение работает, вы можете использовать <command role="hg-cmd">hg help</command> для помощи по команде <command role="hg-ext-mq">qinit</command>.</para>

    &interaction.mq.qinit-help.help;

    <para id="x_3cc">Вы можете использовать MQ с <emphasis>любым</emphasis> Mercurial репозиторием, и его команды работают только с тем, что находится в хранилище. Чтобы начать работу, просто подготовьте репозиторий используя команду <command role="hg-ext-mq">qinit</command>.</para>

    &interaction.mq.tutorial.qinit;

    <para id="x_3cd">Эта команда создает пустую папку с именем <filename role="special" class="directory">.hg/patches</filename>, где MQ будет хранить свои метаданных. Как и во многих командах Mercurial, команда <command role="hg-ext-mq">qinit</command> ничего не печатает, если всё проходит успешно.</para>

    <sect2>
      <title>Создание нового патча</title>

      <para id="x_3ce">Чтобы начать работу над новым патчем, используйте команду <command role="hg-ext-mq">qnew</command>. Эта команда имеет один аргумент, имя patch-а для создания.</para>

      <para id="x_3cf">MQ будет использовать в качестве фактических имена файлов  в директории <filename role="special" class="directory">.hg/patches</filename>, как вы можете увидеть ниже.</para>

      &interaction.mq.tutorial.qnew;

      <para id="x_3d0">Кроме того, сейчас в директории <filename role="special" class="directory">.hg/patches</filename> есть два файла, <filename role="special">series</filename> и <filename role="special">status</filename>. Файл <filename role="special">series</filename> содержит список всех исправлений, которые знает MQ для этого репозитория, с одним патчем в каждой строке. Mercurial использует файл <filename role="special">status</filename> для внутренней бухгалтерии; он отслеживает все патчи, которые MQ <emphasis>применил</emphasis> в этом репозитории.</para>

      <note>
	<para id="x_3d1">Вам может иногда понадобиться изменить файл <filename role="special">series</filename> вручную, например, чтобы изменить последовательность, в которой применяются некоторые патчи. Тем не менее, вручную редактировать файл <filename role="special">status</filename> почти всегда плохая идея, так как легко повредить данные MQ о происходящем.</para>
      </note>

      <para id="x_3d2">После того как вы создали свой новый патч, вы можете редактировать файлы в рабочем каталоге, как вы это обычно делаете. Все нормальные команды mercurial, такие как <command role="hg-cmd">hg diff</command> и <command role="hg-cmd">hg annotate</command> работают так же, как раньше.</para>
    </sect2>

    <sect2>
      <title>Обновление патча</title>

      <para id="x_3d3">Когда вы достигаете точки, где вы хотите сохранить свою работу, используйте команду <command role="hg-ext-mq">qrefresh</command> для обновления патча с которым вы работаете.</para>

      &interaction.mq.tutorial.qrefresh;

      <para id="x_3d4">Эта команда помещает сделанные вами изменения в рабочем каталоге на ваш патч, и обновляет свою соответствующую ревизию которая содержит эти изменения.</para>

      <para id="x_3d5">Вы можете запускать <command role="hg-ext-mq">qrefresh</command> так часто, как вам нравится, так что это хороший способ сохранять <quote>контрольные точки</quote> вашей работы. Обновляйте патч в подходящее время, попробуйте экспериментировать, и если эксперимент не получится, <command role="hg-cmd">hg revert</command> откатит ваши изменения обратно до последнего обновления.</para>

      &interaction.mq.tutorial.qrefresh2;
    </sect2>

    <sect2>
      <title>Укладка и отслеживания патчей</title>

      <para id="x_3d6">После того как вы закончили работу над патчем, или нужно работать с другим, вы можете использовать команду <command role="hg-ext-mq">qnew</command> еще раз для создания новых патчей. Mercurial сделает патч верхним из ваших существующих патчей.</para>

      &interaction.mq.tutorial.qnew2;

      <para id="x_3d7">Обратите внимание, что патч содержит изменения в нашем предыдущем патче в качестве части своей контекста (вы можете увидеть это более четко в выводе <command role="hg-cmd">hg annotate</command>).</para>

      <para id="x_3d8">Пока, за исключением <command role="hg-ext-mq">qnew</command> и <command role="hg-ext-mq">qrefresh</command>, мы осторожно использовали только обычные команды Mercurial. Тем не менее, MQ предоставляет множество команд, которые проще использовать, когда вы думаете о патчах, как показано на рисунке ниже.</para>

      &interaction.mq.tutorial.qseries;

      <itemizedlist>
	<listitem><para id="x_3d9">Команда <command role="hg-ext-mq">qseries</command> перечисляет все патчи, о которых знает MQ в этом репозитории, от старых к новым (<emphasis>созданным</emphasis> совсем недавно).</para>
	</listitem>
	<listitem><para id="x_3da">Команда <command role="hg-ext-mq">qapplied</command> перечисляет все исправления, которые MQ <emphasis>применил</emphasis> в этом репозитории, опять от старых к новым.</para>
	</listitem></itemizedlist>
    </sect2>

    <sect2>
      <title>Манипуляция стеком патчей</title>

      <para id="x_3db">Предыдущее обсуждение предполагает, что должна быть разница между <quote>знанием</quote> и <quote>применением</quote> патчей и она есть. MQ может управлять патчами применёнными без него к репозиторию.</para>

      <para id="x_3dc"><emphasis>Применять</emphasis> патч соответствующей ревизии в репозитории, и последствия патча и ревизия видны в рабочей директории. Вы можете отменить применение патча использованием команды <command role="hg-ext-mq">qpop</command>. MQ до сих пор <emphasis>знает о</emphasis>, или управляет, извлеченными патчами, но патч уже не имеет соответствующего набора изменений в репозитории, и рабочий каталог не содержит изменения сделанные патчем. <xref linkend="fig:mq:stack"/> иллюстрирует разницу между применёнными и отслеживаемыми патчами.</para>

      <figure id="fig:mq:stack">
	<title>Применение и отмена патчей в стеке патчей MQ</title>
	<mediaobject>
	  <imageobject><imagedata fileref="figs/mq-stack.png"/></imageobject>
	  <textobject><phrase>XXX add text</phrase></textobject>
	</mediaobject>
      </figure>

      <para id="x_3de">Вы можете повторно применить отменённый или извлечённый патч используя команду <command role="hg-ext-mq">qpush</command>. Она создает новую ревизию соответствующую патчу и изменения патча в очередной раз станут присутствовать в рабочей директории. Ниже приведены примеры <command role="hg-ext-mq">qpop</command> и <command role="hg-ext-mq">qpush</command> в действии.</para>

      &interaction.mq.tutorial.qpop;

      <para id="x_3df">Обратите внимание, что как только мы извлекли патч или два патча, вывод <command role="hg-ext-mq">qseries</command> остается неизменным, в то время как у <command role="hg-ext-mq">qapplied</command> изменился.</para>

    </sect2>

    <sect2>
      <title>Вставка и извлечение нескольких патчей</title>

      <para id="x_3e0">Хотя по умолчанию <command role="hg-ext-mq">qpush</command> и <command role="hg-ext-mq">qpop</command> работают над одним патчем за раз, вы можете вставить и извлечь много патчей в один проход. Опция <option role="hg-ext-mq-cmd-qpush-opt">-a</option> команды <command role="hg-ext-mq">qpush</command> приводит к вставке  всех неприменённых патчей, а опция <option role="hg-ext-mq-cmd-qpop-opt">-a</option> для <command role="hg-ext-mq">qpop</command> приводит к извлечению всех применённых патчей. (Для некоторых других способов вставки и извлечения многих патчей, смотрите раздел <xref linkend="sec:mq:perf"/> ниже).</para>

      &interaction.mq.tutorial.qpush-a;
    </sect2>

    <sect2>
      <title>Безопасные проверки и их основа</title>

      <para id="x_3e1">Некоторые команды MQ проверяют рабочий каталог, прежде чем что-то делать, и не работают, если они находят какие-либо изменения. Они делают это, чтобы вы не потеряли изменения, которые вы уже сделали, но еще не включили в патч. Пример ниже иллюстрирует это, команда <command role="hg-ext-mq">qnew</command> не будет создавать новый патч, если есть изменения, вызванных в этом случае <command role="hg-cmd">hg add</command> для <filename>file3</filename>.</para>

      &interaction.mq.tutorial.add;

      <para id="x_3e2">Команды, которые проверяют рабочий каталог, все принимают опцию <quote>Я знаю, что я делаю</quote>, которая всегда имеет имя <option>-f</option>. Точное значение <option>-f</option> зависит от команды. Например, в <command role="hg-cmd">hg qnew <option role="hg-ext-mq-cmd-qnew-opt">-f</option></command> будет включать любые не сохранённые изменения в новый патч который она создает, но <command role="hg-cmd">hg qpop <option role="hg-ext-mq-cmd-qpop-opt">-f</option></command> отменит изменения во всех файлах, измененные в результате применения патчей, которые она извлекает. Не забудьте прочитать документацию по опции -f для нужной вам команды, прежде чем использовать её!</para>
    </sect2>

    <sect2>
      <title>Работа с различными патчами сразу</title>

      <para id="x_3e3">Команда <command role="hg-ext-mq">qrefresh</command> всегда обновляет <emphasis>верхний</emphasis> применяемый патч. Это означает, что вы можете приостановить работу по одному патчу (обновив его), извлечь или поместить другой патч наверх, и работать с <emphasis>тем</emphasis> патчем некоторое время.</para>

      <para id="x_3e4">Вот пример, который показывает, как можно использовать эту способность. Скажем вы разрабатываете новую функцию, как два патча. Первым из них является изменение ядра вашего программного обеспечения, а второй &emdash; слой поверх первого &emdash; изменения пользовательского интерфейса, использующее код, который вы только что добавили к ядру. Если вы заметили ошибку в ядре в то время как вы работаете на исправлением UI, вы можете легко исправить ядро. Просто вызовите <command role="hg-ext-mq">qrefresh</command> для патча пользовательского интерфейса, чтобы сохранить незавершенные изменения и <command role="hg-ext-mq">qpop</command> вплоть до патча ядра. Исправьте ошибку ядра, запустите <command role="hg-ext-mq">qrefresh</command> для патча ядра и <command role="hg-ext-mq">qpush</command> поднявшись к патчу пользовательского интерфейса и работайте с того места, где вы остановились.</para>
    </sect2>
  </sect1>

  <sect1 id="sec:mq:adv-patch">
    <title>Более подробно о патчах</title>

    <para id="x_3e5">MQ использует команду GNU <command>patch</command> для применения патчей, так что полезно узнать некоторые более детальные аспекты, как работает <command>patch</command>, и о самих патчах.</para>

    <sect2>
      <title>The strip count</title>

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

      <para id="x_3e7">Алиса распаковала архив, изменила свои файлы, а затем решила, что она хочет создать патч. Она переименовывает свой рабочий каталог, распаковывает архив еще раз (это и обусловило необходимость переименования), и использует опции <option role="cmd-opt-diff">-r</option> и <option role="cmd-opt-diff">-N</option> команды <command>diff</command> для рекурсивно создания патча между не измененным каталогом и измененным. Результатом будет то, что имя не измененного каталога будет в начале пути каждого файла в заголовке  иметь имя левого каталога, а путь каталога измененного будет в начале пути иметь правый каталог.</para>

      <para id="x_3e8">Кто-то получает патч от Алисы, вряд ли будет иметь неизмененный и измененый каталоги с точно такими же именами, команда <command>patch</command> имеет опцию <option role="cmd-opt-patch">-p</option> указывающую на количество ведущих компонентов пути удаляют при применении патча. Это число называется <emphasis>счётчик удаления</emphasis>.</para>

      <para id="x_3e9">Опция <quote><literal>-p1</literal></quote> означает <quote>использовать счётчик удаления с первого элемента</quote>. Если <command>patch</command> видит имя файл <filename>foo/bar/baz</filename> в заголовке файла, он удаляет <filename>foo</filename> и попытаться применить патч к файлу с именем <filename>bar/baz</filename>. (Строго говоря, счётчик удаления относится к количеству <emphasis>разделителей пути</emphasis> (и компонентов, которые следуют за ними). Счётчик удаления в значении 1 преобразует <filename>foo/bar</filename> в <filename>bar</filename>, но <filename>/foo/bar</filename> (обратите внимание на дополнительный ведущий слэш ) в <filename>foo/bar</filename>.)</para>

      <para id="x_3ea"><quote>Стандартный</quote> счётчик удаления для патчей равен 1, почти все патчи содержат один ведущий компонент пути, который необходимо отрезать. Команда Mercurial <command role="hg-cmd">hg diff</command>  генерирует путь в этой форме, и команда <command role="hg-cmd">hg import</command> и MQ ожидают патчей со счётчиком удаления равным 1.</para>

      <para id="x_3eb">Если вы получили патч от кого-то, и вы хотите добавить патч в свою очередь, и патч требует другого счётчика удаления, чем 1, вы не можете просто применить <command role="hg-ext-mq">qimport</command> к патчу, потому что <command role="hg-ext-mq">qimport</command> еще не имеет опции <literal>-p</literal> (смотрите заявку <ulink role="hg-bug" url="http://www.selenic.com/mercurial/bts/issue311">issue 311</ulink>) . Лучше всего, чтобы запустить <command role="hg-ext-mq">qnew</command> для создания собственного патча, а затем использовать <command>patch -pN</command>, чтобы применить их исправления, а затем запустить <command role="hg-cmd">hg addremove</command> чтобу узнать какие файлы добавлены или удалены патчем, а затем выполнить <command role="hg-ext-mq">hg qrefresh</command>. Эта сложность может стать ненужной, смотрите <ulink role="hg-bug" url="http://www.selenic.com/mercurial/bts/issue311">issue 311</ulink> для деталей.</para>
    </sect2>

    <sect2>
      <title>Стратегия для применения патчей</title>

      <para id="x_3ec">Когда <command>patch</command> применяет блок, он пытается использовать последовательно несколько менее точные стратегии, чтобы попытаться применить блок. Этот метод уменьшения точности часто дает возможность применить патч, который был создан на старой версии файла, и применить его на новой версии этого файла.</para>

      <para id="x_3ed">Во-первых, <command>patch</command> ищет точное совпадение, где номера строки, контекст, и текст, который будет изменен совпадает точно. Если он не может сделать точное соответствие, то он пытается найти точное соответствие для контекста, без информации о номерах строк. Если это удаётся, он сообщает что блок был применен, но имеет <emphasis>смещение</emphasis> от первоначального номера строки.</para>

      <para id="x_3ee">Если поиск по контексту не удается, <command>patch</command> устраняет первую и последнюю строку контекста, и попытается найти <emphasis>сокращённый</emphasis> контекст. Если блок с ограниченным контекстом успешно применится, он выводит сообщение о том, что он применяет блок с <emphasis>фактором промаха</emphasis> (число после фактора промаха это коэффициент показывающий, сколько строк из контекста обрезано в патче, чтобы патч применился).</para>

      <para id="x_3ef">Если ни один из этих методов сработал, <command>patch</command> печатает сообщение о том, что блок отклонен. Он сохраняет отклоненные блоки (называя просто <quote>rejects</quote>) в файле с тем же именем и расширением <filename role="special">.rej</filename>. Он также сохраняет не измененную копию файла с расширением <filename role="special">.orig</filename>; копия файла без расширения будет содержать изменения, из применённых блоков, которые применились чисто. Если у вас есть патч, который изменяет <filename>foo</filename> с 6 блоками, а один из них не применяются, вы получите: неизмененный <filename>foo.orig</filename>, <filename>foo.rej</filename> содержащие один блок, и <filename>foo</filename>, содержащий изменения, внесенные в пяти успешных блоках</para>
    </sect2>

    <sect2>
      <title>Некоторые причуды из представления патчей</title>

      <para id="x_3f0">Есть несколько полезных фактов о том, как <command>patch</command> работает с файлами.</para>
      <itemizedlist>
	<listitem><para id="x_3f1">Это уже должно быть очевидно, но <command>patch</command> не может справиться с бинарными файлами.</para>
	</listitem>
	<listitem><para id="x_3f2">Он также не заботится о бите исполняемости, создает новые файлы читаемыми, но не исполняемыми.</para>
	</listitem>
	<listitem><para id="x_3f3"><command>patch</command> считает удалённый файл, как различие между фалом который удалён и пустым файлом. Так что ваша идея <quote>Я удалил этот файл</quote> в патче выглядят как <quote>каждая строка этого файла была удалена</quote>.</para>
	</listitem>
	<listitem><para id="x_3f4">Это относится и к добавленным файлам как к различию между пустым файлом и файлом который будет добавлен. Таким образом, в патче, ваша идея <quote>я добавил этот файл</quote> выглядит как <quote>каждая строка этого файла был добавлена</quote>.</para>
	</listitem>
	<listitem><para id="x_3f5">Это относится и к переименованным файлам, как удаление старого файла, и добавление нового. Это означает, что переименованные файлы будут иметь большой след в блоке. (Заметим также, что mercurial в настоящее время не попытаться сделать вывод, когда файлы были переименованы или скопированы и исправлены.)</para>
	</listitem>
	<listitem><para id="x_3f6"><command>patch</command> не может представлять пустые файлы, так что вы не можете использовать патч для представления понятия <quote>Я добавила пустой файл в дерево</quote>.</para>
	</listitem></itemizedlist>
    </sect2>

    <sect2>
      <title>Остерегайтесь неточностей</title>

      <para id="x_3f7">Применение блоков со смещением, или фактором неточности, часто будет полностью успешным, такие неточные методы естественно оставляют открытой возможность повреждать исправленный файл. Большинство случаев обычно связаны с применением патча два раза, или на неверное место расположения файла. Если <command>patch</command> или <command role="hg-ext-mq">qpush</command> постоянно упоминает о смещении или факторе неточности, вы должны убедиться, что измененные файлы правильны.</para>

      <para id="x_3f8">Часто хорошая идея, обновлять патч, который применился со смещением или фактором неточности, обновление патча порождает новые контекстные связи, которые помогут применить патч чисто. Я говорю: <quote>часто</quote>, а не <quote>всегда</quote>, потому что иногда обновление патча сделает его не применяемым для различных ревизий основных файлов. В некоторых случаях, например, когда вы поддерживаете патч, который должен находится на вершине нескольких версий исходного дерева, это приемлемо иметь патч применяемый с некоторыми неточностями, если вы убедились в правильности результатов применения патча в таких случаях.</para>
    </sect2>

    <sect2>
      <title>Обработка отказа</title>

      <para id="x_3f9">Если <command role="hg-ext-mq">qpush</command> не удается применить патч, он выведет сообщение об ошибке и завершит работу. Если он оставит <filename role="special">.rej</filename> файлы, как правило, лучше устранить отклонения блоков перед добавлением других патчей или какой-либо дальнейшей работой.</para>

      <para id="x_3fa">Если ваш патч <emphasis>применялся</emphasis> чисто, и больше не может, потому что вы изменили исходный код, на котором основан ваши патч, Mercurial Queues может помочь, смотрите в разделе <xref linkend="sec:mq:merge"/> для подробностей.</para>

      <para id="x_3fb">К сожалению, не так много методов для решения отклоненных блоков. Чаще всего, вам понадобится просмотреть <filename role="special">.rej</filename> файл и отредактировать целевой файл, применяя отклоненный блок вручную.</para>

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

      <itemizedlist>
	<listitem><para id="x_3fe">Контекст, в середине блока изменился.</para>
	</listitem>
	<listitem><para id="x_3ff">Часть контекста блока отсутствуют в начале или в конце.</para>
	</listitem>
	<listitem><para id="x_400">Большой блок может применится лучше полностью или частично, если его разбить на мелкие куски.</para>
	</listitem>
	<listitem><para id="x_401">Блок удаляет строки с несколько иным содержанием, чем в настоящее время в файле.</para>
	</listitem></itemizedlist>

      <para id="x_402">Если вы используете <command>mpatch</command>, вы должны быть особенно осторожным, и проверять свои результаты, когда закончите. В самом деле, <command>mpatch</command> применяет метод двойной проверки вывода инструмента, автоматически передавая вас программе объединения, когда он сделает свою работу, так что вы можете проверить свою работу и закончить всё оставшееся слияние.</para>
    </sect2>
  </sect1>

  <sect1>
    <title>Подробнее о управление патчами</title>

    <para id="x_6db">По мере того как вы познаёте MQ, вы пожелаете выполнять другие виды операций по управлению патчами.</para>

    <sect2>
      <title>Удаление нежелательных патчей</title>

      <para id="x_6dc">Если вы хотите, избавиться от патча, воспользуйтесь  командой <command role="hg-ext-mq">hg qdelete</command>, чтобы удалить файл патча и удалить его из серии патчей. Если вы попытаетесь удалить патч, который все еще применяется, <command role="hg-ext-mq">hg qdelete</command> откажется.</para>

      &interaction.ch11-qdelete.go;
    </sect2>

    <sect2>
      <title>Преобразование в и из постоянных ревизий</title>

      <para id="x_6dd">Как только вы закончите работу над патчем и хотите превратить его в постоянную ревизию, используйте команду <command role="hg-ext-mq">hg qfinish</command>. При переходе к ревизии команда определяет патч, который вы хотите превратить в обычную ревизию, этот патч уже должен быть применен.</para>

      &interaction.ch11-qdelete.convert;

      <para id="x_6e0">Команда <command role="hg-ext-mq">hg qfinish</command> принимает опцию <option>--all</option> или <option>-a</option>, который преобразует все применённые патчи в обычные ревизии.</para>

      <para id="x_6de">Кроме того, можно превратить существующую ревизию в патч, с помощью опции <option>-r</option>  команды <command role="hg-ext-mq">hg qimport</command>.</para>

      &interaction.ch11-qdelete.import;

      <para id="x_6df">Обратите внимание, что это имеет смысл только для преобразования набора изменений в патч, если вы не распространяете ревизии в любой другой репозитарий. ID импортируемого набора изменений будет изменяться каждый раз, когда вы обновляете патч, который делает Mercurial рассматривать его как не связанный с первоначальной ревизией, если вы его передадите в любой другой репозитарий.</para>
    </sect2>
  </sect1>

  <sect1 id="sec:mq:perf">
    <title>Получение максимальной производительности от MQ</title>

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

    <para id="x_404">На моём старом, медленном ноутбуке, я выполнил <command role="hg-cmd">hg qpush <option role="hg-ext-mq-cmd-qpush-opt">-a</option></command> для всех 1738 патчей в 3,5 минуты, а <command role="hg-cmd">hg qpop <option role="hg-ext-mq-cmd-qpop-opt">-a</option></command> всего в 30 секунд. (На новом ноутбуке, время добавления всех патчей снизилось до 2 минут). Я выполнил <command role="hg-ext-mq">qrefresh</command> для одного большого патча (который состоял из 22779 строк изменений в 287 файлах) за 6,6 секунды.</para>

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

    <para id="x_406">Прежде всего, попытайтесь выполнять <quote>пакетные</quote> операции вместе. Каждый раз, когда вы запускаете <command role="hg-ext-mq">qpush</command> или <command role="hg-ext-mq">qpop</command>, эти команды проверяют рабочую директорию один раз, чтобы убедиться, что вы не внесли каких нибудь изменений, а затем забыл запустить <command role="hg-ext-mq">qrefresh</command>. На небольшом дереве, время этой проверки невелико. Однако, на средних деревьях (содержащих десятки тысяч файлов), это может занять секунду или даже больше.</para>

    <para id="x_407">Команды <command role="hg-ext-mq">qpush</command> и <command role="hg-ext-mq">qpop</command> позволяют вставлять и извлекать несколько патчей сразу. Вы можете указать <quote>патч назначения</quote> который вы хотите применить последним. <command role="hg-ext-mq">qpush</command> с указанным назначением, то он будет вставлять патчи до этого патча в верх стека применения. <command role="hg-ext-mq">qpop</command> с назначением, MQ извлекает патчи до того как патч назначения не окажется на самом верху.</para>

    <para id="x_408">Вы можете определить патч назначения, используя либо название патча, или номер. Если вы используете числовую адресацию, патчи начинаются с нуля, это означает, что первый патч равен нулю, второй единице, и так далее.</para>
  </sect1>

  <sect1 id="sec:mq:merge">
    <title>Обновление патчей когда исходный код измененился</title>

    <para id="x_409">Как правило имея патч на верху стека вы не изменяете напрямую нижележащий код в репозитории. Если вы работаете над изменениями в коде третьих сторон, или над особенностью, которая занимает больше времени, чем скорость развития изменений кода внизу, вам часто нужно синхронизироваться с исходным кодом, и исправлять блоки в своих патчах, которые уже не применяются. Это называется <emphasis>перебазирование</emphasis> серии патчей.</para>

    <para id="x_40a">Проще всего это сделать так: выполнить <command role="hg-cmd">hg qpop <option role="hg-ext-mq-cmd-qpop-opt">hg -a</option></command> для ваших патчей, затем <command role="hg-cmd">hg pull</command> ревизий из основного репозитория, и, наконец выполнить <command role="hg-cmd">hg qpush <option role="hg-ext-mq-cmd-qpop-opt">-a</option></command> ваших патчей снова. MQ будет останавливать вставку каждый раз, когда проходит через патч, который не применяется из-за конфликтов, что позволяет исправлять ваши конфликты, и выполнять <command role="hg-ext-mq">qrefresh</command> для пострадавших патчей, и продолжать вставку, пока не исправите весь стек.</para>

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

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

    <para id="x_40d">Этот процесс немного сложнее.</para>
    <orderedlist>
      <listitem><para id="x_40e">Во-первых, выполняем <command role="hg-cmd">hg qpush -a</command> применяя все ваши патча к старшей ревизии, к которой вы знаете, что они применяются чисто.</para>
      </listitem>
      <listitem><para id="x_40f">Сохранить резервную копию исправляемого каталога с помощью команды <command role="hg-cmd">hg qsave <option role="hg-ext-mq-cmd-qsave-opt">hg -e</option> <option role="hg-ext-mq-cmd-qsave-opt">hg -c</option></command>. Она выводит имя директории, в которой она сохранит патчи. Это сохранит исправления в каталоге называемом <filename role="special" class="directory">.hg/patches.N</filename>, где <literal>N</literal> является малым числом. Она также <quote>сохраняет ревизию</quote> поверх которой применяются патчи, и записывает состояние файлов <filename role="special">series</filename> и <filename role="special">status</filename>.</para>
      </listitem>
      <listitem><para id="x_410">Используйте <command role="hg-cmd">hg pull</command> для внесения изменений в основной репозиторий. (Не запускать <command role="hg-cmd">hg pull -u</command>, смотрите ниже, почему.)</para>
      </listitem>
      <listitem><para id="x_411">Обновитесь до новой верхней ревизии, используя <command role="hg-cmd">hg update <option role="hg-opt-update">-C</option></command>, переписываяизменения ваших патчей.</para>
      </listitem>
      <listitem><para id="x_412">Объедините все патчи <command>hg qpush -m -a</command>.  Опция <option role="hg-ext-mq-cmd-qpush-opt">-m</option> <command role="hg-ext-mq">qpush</command> говорит MQ выполнить трехстороннее слияние, если патч не может применится.</para>
      </listitem></orderedlist>

    <para id="x_413">В <command role="hg-cmd">hg qpush <option role="hg-ext-mq-cmd-qpush-opt">hg -m</option></command>, каждый патч в файле <filename role="special">series</filename> применяется в обычном режиме. Если исправление применяется с неточностями или отвергается, MQ смотрит на вашу очередь <command role="hg-ext-mq">qsave</command>d, и также выполняет трехстороннее слияние с соответствующей ревизией. Для слияния Mercurial использует нормальный механизм слияния, поэтому оно может быть передано GUI-инструменту слияния, чтобы помочь вам в решении проблем.</para>

    <para id="x_414">Когда вы закончите разрешение эффектов патча, MQ обновит ваш патч, основанный на результате слияния.</para>

    <para id="x_415">В конце этого процесса, репозиторий будет иметь одну дополнительную голову от старой очереди патчей, а копия старой очереди патч будет лежать в <filename role="special" class="directory">.hg/patches.N</filename>. Вы можете удалить дополнительную голову с помощью <command role="hg-cmd">hg qpop -a -n patches.N</command> или <command role="hg-cmd">hg strip</command>. Вы можете удалить <filename role="special" class="directory">.hg/patches.N</filename> когда вы уверены, что вам она больше не нужна в качестве резервной копии.</para>
  </sect1>

  <sect1>
    <title>Идентификация патчей</title>

    <para id="x_416">Команды MQ, которые работают с патчами позволяют ссылаться на патч либо используя его имя или номер. По имени достаточно очевидно; передать имя <filename>foo.patch</filename> команде <command role="hg-ext-mq">qpush</command>, например, и она будет вставлять и применять патчи до <filename>foo.patch</filename>.</para>

    <para id="x_417">Для сокращения, вы можете обратиться к патчу с использованием имени и цифрового смещения; <literal>foo.patch-2</literal> означает <quote>второй патч перед <literal>foo.patch</literal></quote>, а <literal>bar.patch+4</literal> означает <quote>четвёртый патч после <literal>bar.patch</literal></quote>.</para>

    <para id="x_418">Ссылка на патч по индексу не сильно отличается. Первый патч напечатаны в выходе <command role="hg-ext-mq">qseries</command> это нулевой патч нулю (да, это один из тех систем отсчёта, которые начинаются с нуля); второй патч 1, и так далее.</para>

    <para id="x_419">MQ также позволяет легко работать с патчами, как вы используете нормальные команды Mercurial команд. Каждая команда, которая принимает id ревизии также примет название применяемого патч. MQ увеличивает количество тегов в репозитории, как правило по одному для каждого применённого патча. Кроме того, специальные теги <literal role="tag">qbase</literal> и <literal role="tag">qtip</literal> определяют <quote>самый нижний</quote> и <quote>самый верхний</quote> применённые патчи, соответственно.</para>

    <para id="x_41a">Эти дополнительное тегирование нормальными тегами Mercurial делает возможность внесения патчей еще более легким.</para>
    <itemizedlist>
      <listitem><para id="x_41b">Хотите отправить по email patchbomb с вашей последней серией патчей?</para>
	<programlisting>hg email qbase:qtip</programlisting>
	<para id="x_41c">(Не знаете, что такое <quote>patchbombing</quote>? Смотрите раздел <xref linkend="sec:hgext:patchbomb"/>.)</para>
      </listitem>
      <listitem><para id="x_41d">Нужно, увидеть все патчи начиная с <literal>foo.patch</literal> которые коснулись файлов в подкаталоге вашего дерева?</para>
	<programlisting>hg log -r foo.patch:qtip subdir</programlisting>
      </listitem>
    </itemizedlist>

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

    <para id="x_41f">Другим полезным следствием представления патчей именами тегов является то, что когда вы выполните команду <command role="hg-cmd">hg log</command>, она будет отображать название патча, как тег, просто как часть нормального вывода. Это позволяет визуально различать применённые патчи от лежащие в основе <quote>обычных</quote> ревизий. В следующем примере показано несколько простых команд Mercurial использующихся с применёнными патчами.</para>

    &interaction.mq.id.output;
  </sect1>

  <sect1>
    <title>Полезные вещи, которые необходимо знать</title>

    <para id="x_420">Есть ряд аспектов использования MQ, которые не вписываются аккуратно в отдельные разделы, но их хорошо бы знать. Вот они, в одном месте.</para>

    <itemizedlist>
      <listitem><para id="x_421">Обычно, когда вы применяете к патчу <command role="hg-ext-mq">qpop</command> и потом <command role="hg-ext-mq">qpush</command>, ревизия представляющая патч после извлечения/вставки будет иметь <emphasis>другой идентификатор</emphasis>, чем ревизия, которые представляла патч ранее. Смотрите раздел <xref linkend="sec:mqref:cmd:qpush"/> для информации о том, почему так происходит.</para>
      </listitem>
      <listitem><para id="x_422">Это не очень хорошая идея, использовать <command role="hg-cmd">hg merge</command> изменений из другой ветви с ревизией патча, по крайней мере, если вы хотите сохранить <quote>патченость</quote> этой ревизии и ревизий ниже её в стеке патчей. Если вы попытаетесь сделать это, всё пройдёт успешно, но MQ будет запутан.</para>
      </listitem></itemizedlist>
  </sect1>

  <sect1 id="sec:mq:repo">
    <title>Управление патчами в репозитории</title>

    <para id="x_423">Так как каталог  <filename role="special" class="directory">.hg/patches</filename> находится вне рабочего каталога репозитория Mercurial, <quote>основной</quote> Mercurial репозиторий ничего не знает об управлении или наличие патчей.</para>

    <para id="x_424">Это открывает интересные возможности управления содержанием каталога патчей как отдельным Mercurial репозиторием внутри собственного репозитория. Это может быть полезным для работы. Например, вы можете работать над патчем некоторое время, выполнить <command role="hg-ext-mq">qrefresh</command>, потом <command role="hg-cmd">hg commit</command> для текущего состояния патча. Это позволяет вам <quote>откатываться</quote> на этоу версию патча позже.</para>

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

    <para id="x_426">Управление патчами в репозитории позволяет нескольким разработчикам работать над одной и той же серией патчей не сталкиваясь друг с другом, всегд поверх основной части базы исходников, которые они могут или не могут контролировать.</para>

    <sect2>
      <title>Поддержка MQ для репозитория патчей</title>

      <para id="x_427">MQ позволяет работать с директорией <filename role="special" class="directory">.hg/patches</filename> как с репозиторием, когда вы готовите репозиторий для работы с патчами используя <command role="hg-ext-mq">qinit</command>, вы можете передать опцию <option role="hg-ext-mq-cmd-qinit-opt">hg -c</option> для создания каталога <filename role="special" class="directory">.hg/patches</filename> как репозиторий Mercurial.</para>

      <note>
	<para id="x_428">Если вы забыли использовать опцию <option role="hg-ext-mq-cmd-qinit-opt">hg -c</option>, вы можете просто пойти в каталог <filename role="special" class="directory">.hg/patches</filename>  в любое время и выполнить <command role="hg-cmd">hg init</command>. Не забудьте добавить запись в <filename role="special">.hgignore</filename> о файле <filename role="special">status</filename>, хотя (<command role="hg-cmd">hg qinit <option role="hg-ext-mq-cmd-qinit-opt">hg -c</option></command> делает это за вас автоматически); Вы ведь <emphasis>действительно</emphasis> не хотите, отслеживать файл <filename role="special">status</filename>.</para>
      </note>

      <para id="x_42a">Для удобства, если MQ замечает, что каталог <filename role="special" class="directory">.hg/patches</filename> репозиторий, он будет автоматически выполнять <command role="hg-cmd">hg add</command> каждый патч, который вы создаете или импортируете.</para>

      <para id="x_42b">MQ обеспечивает короткую команду <command role="hg-ext-mq">qcommit</command>, запускающую <command role="hg-cmd">hg commit</command> в директории <filename role="special" class="directory">.hg/patches</filename>.</para>

      <para id="x_42c">Наконец, для удобства управления директорией патчей, можно определить alias <command>mq</command> на системах Unix. Например, в системах linux c помощью оболочки <command>bash</command>, вы можете включить следующий фрагмент в файле <filename role="home">~/.bashrc</filename>.</para>

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

      <para id="x_42d">После этого можно отдавать команды вида <command>mq pull</command> из главного репозитория.</para>
    </sect2>

    <sect2>
      <title>Несколько вещей для отслеживания</title>

      <para id="x_42e">MQ поддерживает для работы с репозиторием полные патчи ограниченного небольшими отношениями.</para>

      <para id="x_42f">MQ не умеет автоматически обнаруживать изменения, внесенные в каталог патчей. Если вы выполнили <command role="hg-cmd">hg pull</command>, вручную отредактировали, или сделали <command role="hg-cmd">hg update</command> изменений для патчей или файлом <filename role="special">series</filename>, вам придется выполнить <command role="hg-cmd">hg qpop <option role="hg-ext-mq-cmd-qpop-opt">-a</option></command> and then <command role="hg-cmd">hg qpush <option role="hg-ext-mq-cmd-qpush-opt">-a</option></command> в базовом хранилище, чтобы увидеть эти изменения там. Если вы забудете это сделать, вы можете спутать MQ планы применения патчей.</para>

    </sect2>
  </sect1>
  <sect1 id="sec:mq:tools">
    <title>Инструменты сторонних разработчиков для работы с патчами</title>

    <para id="x_430">Когда вы поработаете с патчами некоторое время, вы обнаружите недостаток инструментов, которые помогут вам понять и управлять патчами, с которыми вы имеете дело.</para>

    <para id="x_431">Команда <command>diffstat</command> <citation>web:diffstat</citation> генерирует гистограммы изменений, внесенных с каждым файлом в патч. Она обеспечивает хороший способ <quote>получить смысл</quote> патча &emdash; какие файлы он затрагивает, и сколько изменений применит к каждому файлу и в целом. (Я считаю, что это хорошая идея использовать опцию <option role="cmd-opt-diffstat">-p</option> <command>diffstat</command> для отображения статистики различий как нечто само собой разумеющееся, так как иначе он будет пытаться делать умные вещи с префиксами имен файлов, которые неизбежно запутают по крайней мере меня).</para>

&interaction.mq.tools.tools;

    <para id="x_432">Пакет <literal role="package">patchutils</literal> <citation>web:patchutils</citation> имеет неоценимое значение. Он предоставляет набор небольших утилит, которые следуют <quote>Unix философии</quote> каждая делает одну полезную вещь с патчем. Из команд <literal role="package">patchutils</literal> я использую больше всего <command>filterdiff</command>, которая извлекает из подмножеств файла патча. Например, если патч, который изменяет сотни файлов в десятках каталогов, один вызов <command>filterdiff</command> может генерировать меньший патч, который затрагивает только файлы, имена которых совпадают с неким шаблоном. Смотрите раздел <xref linkend="mq-collab:tips:interdiff"/> для других примеров.</para>

  </sect1>
  <sect1>
    <title>Хорошие методы работы с патчами</title>

    <para id="x_433">Если вы работаете над серией патчей представляя их свободному программному обеспечению или проекту с открытым кодом, или серии, которую вы намерены рассматривать как последовательность регулярных ревизий которую вы закончили, вы можете использовать некоторые простые методы, чтобы сохранить вашу работу организованной.</para>

    <para id="x_434">Давайте вашим патчам описательные имена. Хорошее имя для патча может быть <filename>rework-device-alloc.patch</filename>, потому что оно будет сразу же давать вам подсказку на цель исправления. Длинные имена не должны быть проблемой, вы не будете вводить имена часто, а <emphasis>будете</emphasis> работать с такими командами, как <command role="hg-ext-mq">qapplied</command> и <command role="hg-ext-mq">qtop</command> снова и снова. Хорошее имя становится особенно важным, когда у вас есть несколько патчей над которыми вы работаете, или если вы жонглировали целым рядом различных задач, и ваши патчи только получили только часть вашего внимания.</para>

    <para id="x_435">Помните о том, над каким патч вы работаете. Используйте команду <command role="hg-ext-mq">qtop</command> и просматривайте текст ваших патчей чаще, например, с использованием <command role="hg-cmd">hg tip <option role="hg-opt-tip">-p</option></command> &emdash; что быть уверенным в том, где вы находитесь. Я несколько раз работал, и запускал <command role="hg-ext-mq">qrefresh</command> не для того патча, которого хотел, и часто сложно перенести изменения в правильный патч после внесения их в неверный.</para>

    <para id="x_436">По этой причине, стоит потратить немного времени, чтобы узнать, как использовать некоторые из утилит сторонних разработчиков, которые я описал в разделе <xref linkend="sec:mq:tools"/>, в частности, <command>diffstat</command> и <command>filterdiff</command>. Первий даст вам представление о том, какие изменения патч делает, а второй позволяет легко перемещать выбраные блоки из одного патча и в другой.</para>

  </sect1>
  <sect1>
    <title>Поваренная книга MQ</title>

    <sect2>
      <title>Управление <quote>тривиальными</quote> патчами</title>

      <para id="x_437">Так как накладные расходы на добавление файлов в новый репозиторий Mercurial настолько низки, есть смысл, управлять патчами его путём, даже если вы просто хотите внести некоторые изменения в архив с исходными кодами, который вы скачивали.</para>

      <para id="x_438">Начните с загрузки и распаковки архива исходных кодов, и превратите его в репозиторий Mercurial.</para>

      &interaction.mq.tarball.download;

      <para id="x_439">Продолжим создавать стек патчей и делать изменения</para>

      &interaction.mq.tarball.qinit;

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

      &interaction.mq.tarball.newsource;

      <para id="x_43b">С помощью конвеера, начинающегося с <command role="hg-cmd">hg locate</command> выше удаляем все файлы в рабочем каталоге, так что команда <command role="hg-cmd">hg commit</command> с опцией <option role="hg-opt-commit">--addremove</option> может сообщить, какие файлы были действительно удалены в новой версии исходников.</para>

      <para id="x_43c">Наконец, применяем ваши патчи поверх нового дерева</para>

      &interaction.mq.tarball.repush;
    </sect2>

    <sect2 id="sec:mq:combine">
      <title>Объединение целых патчей</title>

      <para id="x_43d">MQ обеспечивает команду <command role="hg-ext-mq">qfold</command>, которая позволяет объединить целые патчи. Она <quote>складывает</quote> патчи имена которых вы указали, в порядке указанном вами, наверх применяемого патча, и объединяет их описания в конечное описание. Патчи должны быть неприменёнными, перед тем как вы их сложите.</para>

      <para id="x_43e">Порядок, в котором вы складываете патчи важен. Если верхним применяется патч <literal>foo</literal>, и вы складываете <command role="hg-ext-mq">qfold</command> <literal>bar</literal> и <literal>quux</literal> в него, вы в конечном итоге с помощью патча, получите тот же эффект, как при применении сначала <literal>foo</literal>, потом <literal>bar</literal>, а затем <literal>quux</literal>.</para>
    </sect2>

    <sect2>
      <title>Слияние части одного патча с другим</title>

      <para id="x_43f">Слияние <emphasis>частей</emphasis> патча в другой сложнее, чем слияние целых патчей.</para>

      <para id="x_440">Если вы хотите переместить изменения некоторых файлов, вы можете использовать опции <option role="cmd-opt-filterdiff">-i</option> и <option role="cmd-opt-filterdiff">-x</option> команды <command>filterdiff</command> для выбора изменений из одного патча, присоедините ее выход к концу патча, в который хотите их влить. Вам, как правило, не требуется вносить изменения в патч измененный объединением. Вместо этого, MQ сообщит что некоторые блоки отвергнуты, когда вы попытаетесь сделать <command role="hg-ext-mq">qpush</command> (те блоки которые вы переместили в другой патч), и вы можете просто сделать <command role="hg-ext-mq">qrefresh</command> для удаления из патча  дубликатов блоков.</para>

      <para id="x_441">Если у вас есть патч, который имеет несколько кусков изменяющих файл, и вы только хотите перенести часть из них, работа становится более грязным делом, но все равно можно частично автоматизировать его. Использование <command>lsdiff -nvv</command> печатает различные метаданные о патче.</para>

      &interaction.mq.tools.lsdiff;

      <para id="x_442">Это команда выводит три различных номера:</para>
      <itemizedlist>
	<listitem><para id="x_443">(В первой колонке) <emphasis>номер файла</emphasis> для идентификации каждого файла модифицируемого в патче;</para>
	</listitem>
	<listitem><para id="x_444">(На следующей строке с отступом) номер строки в рамках изменяемого файла, где начинается блок, и</para>
	</listitem>
	<listitem><para id="x_445">(на той же линии) <emphasis>номер блока</emphasis> идентифицирующего блок.</para>
	</listitem></itemizedlist>

      <para id="x_446">Вам придется использовать некоторый визуальный осмотр, и чтение патча, чтобы идентифицировать файл и блок с номерами которые вы хотите, но вы можете передать их в <command>filterdiff</command> используя опции <option role="cmd-opt-filterdiff">--files</option> и <option role="cmd-opt-filterdiff">--hunks</option>, чтобы выбрать именно тот файл и блок который вы хотите извлечь.</para>

      <para id="x_447">После этого вы можете вставить этот блок в конец патч назначения и продолжать так как описано в разделе <xref linkend="sec:mq:combine"/>.</para>

    </sect2>
  </sect1>
  <sect1>
    <title>Различия между quilt и MQ</title>

    <para id="x_448">Если вы уже знакомы с quilt, MQ обеспечивает аналогичный набор команд. Есть некоторые различия в том, как они работают.</para>

    <para id="x_449">Вы уже заметили, что для большинства команд quilt есть аналоги в MQ, только начинаются они с <quote><literal>q</literal></quote>. Исключения в quilt команды <literal>add</literal> и <literal>remove</literal>, аналоги для них есть в обычном Mercurial команды <command role="hg-cmd">hg add</command> и <command role="hg-cmd">hg remove</command>. Нет эквивалента в MQ команды quilt <literal>edit</literal>.</para>

  </sect1>
</chapter>

<!--
local variables: 
sgml-parent-document: ("00book.xml" "book" "chapter")
end:
-->