% Copyright 2010 Ivan Borzenkov % % This program is free software: you can redistribute it and/or modify % it under the terms of the GNU General Public License as published by % the Free Software Foundation, either version 2 of the License, or % (at your option) any later version. % % This program is distributed in the hope that it will be useful, % but WITHOUT ANY WARRANTY; without even the implied warranty of % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the % GNU General Public License for more details. % % You should have received a copy of the GNU General Public License % along with this program. If not, see . % %\documentclass[a4paper]{article} \documentclass{article} \usepackage{graphicx} \usepackage{subfigure} \usepackage{fancyvrb} %\usepackage{times} \usepackage[T2A]{fontenc} \usepackage[utf8]{inputenc} \usepackage[russian]{babel} \usepackage{cmap} \usepackage{url} %\usepackage{lineno} %\linenumbers %\renewcommand{\baselinestretch}{1.5} % Change url font to textsf (and check what breaks in PDF/HTML/...) \fvset{xleftmargin=3em,commandchars=\\\{\}} \newcommand{\quilt}[1]{\textsf{quilt #1}} \renewcommand{\sh}[1]{\textsl{#1}} \newcommand{\prog}[1]{\textit{#1}} \title{Как Выжить С Множеством Патчей\\ {\Large или}\\ Введение в Quilt\footnote{ Quilt это проект под лицензией GPL размещенный на хостинге GNU Savannah. Некоторые идеи для этого документа были взяты из файла \textit{docco.txt} в пакете~\cite{akpm02} скриптов менеджера патчей Эндрю Мортона. Текст примеров был взят из произведения Вильяма Шекспира \textit{Сон В Летнюю Ночь}. }} \author{Andreas Grünbacher, SuSE Labs \\ %\em{SUSE Labs, SUSE LINUX AG} \\ {\normalsize agruen@suse.de} } %\date{} \begin{document} \maketitle \thispagestyle{empty} \begin{abstract} Просмотрев различные стратегии работы с пакетами, состоящими из базового исходного кода, к которому применяется целый ряд исправлений, представляю в этом документе коллекцию скриптов - \textit{quilt,} которая была специально написана для оказания помощи в работе с множеством патчей и решения общих задач по управлению ими. \end{abstract} \section{Введение} % Необходимые условия: UNIX, патчи, использование GNU diff и GNU patch. % Во-первых, почему патчи? В старые времена программное обеспечение определенного поставщика (издателя. Здесь уместнее говорить о дистрибутиве и дистро-специфиченых патчах) в мире открытого исходного кода состояло из файла с официальной версией программного обеспечения, а также файлов с дополнительными патчами, необходимыми для адаптации пакета к конкретным потребностями. Официальный пакет программного обеспечения, как правило, содержался в файле \textsf{package.tar.gz}, а патч был в \textsf{package.diff.} Вместо того, чтобы изменять оригинальный пакет исходников, локальные изменения хранились отдельно. При формировании пакета программного обеспечения tar архив распаковывался, и к нему применялся патч. Со временем патч-файл стал содежать несколько независимых изменений. Некоторые из этих изменений были интегрированы в более поздних версиях программного обеспечения, в то время как другие дополнения или переделки оставались внешними. Всякий раз, когда выпускалась новая официальная версия патч пересматривался: необходимо было отделить изменения, которые уже были включены в официальную версию от остальных. Значительное улучшение наступило, когда в пакетах поставщиков были разрешены множественные патчи - так патчи обрабатываются сегодня: ряд патчей наносится поверх друг друга. Каждое исправление обычно состоит из логически связанного набора изменений. Когда некоторые патчи переносятся в апстрим, они могут быть просто удалены из пакета конкретного поставщика. Остальные патчи часто продолжают применяться так-же. Некоторые из оставшихся патчей, возможно, придется поддерживать в целом ряде последующих версий, поскольку они слишком специфичны для включения в базовую версию пакета программного обеспечения и т.д. Эти патчи часто рассинхронизируются и нуждаются в обновлении. Для большинства пакетов, число исправлений, остается относительно низким, так что поддержание этих исправлений возможно без специальных инструментов. Однако некоторые пакеты имеют десятки патчей. Яркий пример - пакет исходных кодов ядра (kernel-source-\textit{2.4.x}) с более чем 1\,000 патчей. Сложность управления таким огромным количеством патчей, без инструментов можно легко себе представить. В этом документе рассматриваются различные стратегии работы с большими наборами исправлений. Патчи обычно генерируются утилитой \prog{diff}, и применяется с помощью \prog{patch}. Различные форматы файлов патча определяется как часть спецификации утилиты \prog{diff} в POSIX.1~\cite{posix-2001-diff}, однако наиболее часто используемый формат сегодня \textit{unified diff}, не охвачен POSIX.1. Хорошее описание форматов патч-файлов находится на info странице~\cite{info-diff} утилиты \prog{GNU diff}. Вопрос, на который мы попытаемся ответить в данном документе - как патчи лучше хранить в условиях изменений как в оригинальной версии пакета программного обеспечения, а также предшествующих им патчей. Посмотрев на некоторые существующие подходы, рассматривая коллекцию сценариев управление патчами известную как \textit{quilt} (~\cite{quilt}), начинем с основных понятий, и будем двигаться к более сложным задачам. % - quilt % (wet people's mouths about the features) % Как это на самом деле связано с множественными патчами? \section{Существующие подходы} \label{sec:existing} Самое простое решение для использования патча - применять все предыдущие патчи. %\footnote{ В базовом CVS имеется скрипт %\textit{sequence-patch}, который просто применяет все патчи до %указанного патча. } Создается копия исходного дерева.\footnote{ Два экземпляра могут быть жестко связаны друг с другом, что значительно ускоряет как копирование и окончательный ``Diffing''. Если используются жесткие ссылки, необходимо позаботиться о том, чтобы инструменты, используемые для обновления одной копии исходного дерева создавали новые файлы, а не перезаписывали общие файлы. Редакторы, такие как \prog{emacs} и \prog{vi}, а так-же утилиты типа \prog{patch}, поддерживают это.} Следующий патч из последовательности (который является одним из тех, которые будут использованы) применяется только к одному из этих исходных деревьев. Это дерево исходных текстов затем изменяется, чтобы отразить желаемый результат. Новая версия патча получается сравнением двух исходных деревьев с помощью \prog{diff}, и записи результата в файл. Этот простой подход весьма подвержен ошибкам, и оставляет желать лучшего. Несколько человек независимо друг от друга написали скрипты для автоматизации и совершенствования этого процесса. Системы контроля версий, такие как \prog{CVS} или \prog{RCS} могут быть разумной альтернативой в некоторых случаях. В систему контроля версий вносится состояние рабочего дерева с рядом примененных патчей. Затем применяется следующий патч. После обновления рабочего дерева до необходимого состояния, с помощью \prog{cvs diff} или другой программы создается diff между копией кодов и рабочим деревом. В этом случае система контроля версий используется только для хранения и сравнения со старой версией, но переключение между различными патчами не упрощается. Стреляем из пушки по воробьям. % TODO: Mention some approaches here; RCS and CVS ... Один из наиболее передовых подходов - сценарии управления патчами Эндрю Мортона~\cite{akpm02}. Автор этого документа не нашел ни одного из возможных решений которое будет масштабироваться до конкретных требований пакета исходного кода ядро SUSE, и стал улучшать сценарии Эндрю Мортона, пока они не стал тем, чем они являются сейчас~\cite{quilt}. % - Re and Rd scripts (Czech scripts using RCS, replaces the % now-obsolete rpmpatch that supports one .dif only). % - Werner's scripts % What couldn't be done: % - Patches in sub-directories % - Many patches % - Retaining documentation (akpm's scripts do part of this) % Actually merging rejects is not handled; use tools like: % - wiggle % - Other merge tools (e.g., graphical ones) \section{Quilt: основные понятия и операции} \label{sec:basic} Далее в документе обсуждается коллекция скриптов \textit{quilt.} С \textit{quilt} вся работа происходит в одном дереве каталогов. Начиная с версии 0.30, команды могут быть вызваны из любой точки исходного дерева. Команды имеют вид ``\quilt{cmd}'' и похожие на CVS командам. Они могут быть сокращены, если определенная часть команды является уникальным. Все команды выводят текст с помощью по их использованию с ключом ``\quilt{cmd -h}'. Quilt управляет стеком патчей. Патчи применяются инкрементно на базовае дерево плюс все предыдущие патчи. Они могут быть добавлены на вершину стека (\quilt{push}) и убраны из стека (\quilt{pop}). Доступны команды для запросов содержания серии файл (\quilt{series}, см. ниже), содержимое стека (\quilt{applied}, \quilt{previous}, \quilt{top}), и патчи, которые не применяются в определенный момент (\quilt{next}, \quilt{unapplied}). По умолчанию, большинство команд применяется к верхнему патчу на стеке. При изменении файлов в рабочей папке эти изменения становятся частью рабочего состояния верхнего патча, при условии, что эти файлы являются частью патча. Файлы, которые не являются частью исправление должны быть добавлено до изменения чтобы quilt были известны оригинальные версии файлов. Команда \quilt{refresh} восстанавливает патч. После обновления, патч и рабочее состояние те же. Патч-файлы находятся в подкаталоге \textsf{patches} дерева исходных текстов (см. Рисунок ~\ref{fig:dir-layout}). Можно использовать переменную окружения \textsf{QUILT\_PATCHES} для переопределения этого места. Каталог \textsf{patches} может содержать подкаталоги или быть символической ссылкой. Файл \textsf{series} содержит список имен патч файлов, определяет порядок, в котором они применяются. Если есть средства, которые могут автоматически сгенерировать файл \textsf{series} (см. раздел~\ref{sec:rpm}), то они обычно предоставляются вместе с набором исправлений. В \textsf{series} каждое имя файла патчей находится на отдельной строке. Патч-файлы идентифицируются по путям, относительным к каталогу \textsf{patches}; патчи могут быть в подкаталогах ниже каталога \textsf{patches}. Строки в файле \textsf{series}, начинающиеся с символа решетки (\texttt{\#}) игнорируются. Когда quilt добавляет, удаляет или переименовывает патчи, она автоматически обновляет серию файлов. Пользователи quilt могут изменить файл \textsf{series} когда применяются некоторые патчи, пока патчи остаются в исходном порядке. Разные файлы \textsf{series} могут быть использованы для сборки патчей по-разному, что соответствует, например, различным ветвям разработки. \begin{figure} \begin{center} \begin{minipage}{6cm} \begin{small} \begin{Verbatim} work/ -+- ... |- patches/ -+- series | |- patch2.diff | |- patch1.diff | +- ... +- .pc/ -+- applied-patches |- patch1.diff/ -+- ... |- patch2.diff/ -+- ... +- ... \end{Verbatim} \end{small} \end{minipage} \caption{Файлы Quilt в исходном дереве.} \label{fig:dir-layout} \end{center} \end{figure} До применения патча (или ``помещения в стек'') копии всех файлов сохраняются в каталоге \textsf{.pc/\textit{patch}}.\footnote{ Имя патч файла используется в качестве имени подкаталога ниже директории \textsf{.pc}. \prog{GNU patch}, который используется внутри quilt чтобы применять патчи, создает резервные копии файлов и применяет патч за один шаг.} Патч будет добавлен в список примененных в данный момент патчей (\textsf{.pc/applied-patches}). Позже, когда восстанавливается патч (\quilt{refresh}), резервные копии в \textsf{.pc/\textit{patch}} сопоставляются с текущими версиями файлов в исходном дереве, используя \prog{GNU diff}. Документацию, связанную с патчем можно добавить в начале файла. Quilt тщательное сохраняет весь текст, который предшествует фактически патчу при выполнении обновления. Файл \textsf{series} ищется в корневом каталоге дерева исходных текстов, в каталоге патчей, а также в каталоге \textsf{.pc}. Используется первый найденный файл \textsf{series}, также он может быть символической ссылки, или файлом с несколькими жесткими ссылками. Как правило, только один файл \textsf{series} используется для набора патчей, поэтому подкаталог \textsf{patches} - это удобное расположение. Хотя патчи применяются для дерева исходных текстов, директория \textsf{.pc} играет важную роль во многих операциях, в том числе принятия патчей из стека (\quilt{pop}), и обновлении патчей (\quilt{refresh}). Файлы в папке \textsf{.pc} автоматически удаляются, когда они больше не нужны, так как правило, нет необходимости чистить их вручную. Переменные окружения \textsf{QUILT\_PC} можно использовать для переопределения расположения \textsf{.pc} каталога. \section{Примеры} В этом разделе демонстрируется, как новые патчи создаются и обновляются, и как решаются конфликты. Давайте начнем с короткого текстового файла: \begin{small} \begin{Verbatim} Yet mark'd I where the bolt of Cupid fell: It fell upon a little western flower, Before milk-white, now purple with love's wound, And girls call it love-in-idleness. \end{Verbatim} \end{small} Новые патчи создаются с помощью \quilt{new}. Новый патч автоматически становится верхним на стеке. Файлы должны быть добавлены с помощью \quilt{add} до их изменения. Заметим, что это несколько отличается от стиля CVS взаимодействия: с CVS файлы в репозитории, и добавить их до коммита (но после внесения изменений в них) вполне достаточно. Файлы, как правило, добавляются и тут же изменяются. Команда \quilt{edit} добавляет файл и загружает его в редактор по умолчанию. Используется (переменная окружения \textsf{EDITOR} которая определяет редактор по умолчанию. Если она не установлена, используется \prog{vi}.) \begin{small} \begin{Verbatim} \sh{$ quilt new flower.diff} Patch flower.diff is now on top \sh{$ quilt edit Oberon.txt} File Oberon.txt added to patch flower.diff \end{Verbatim} \end{small} Давайте предположим, что следующие строки были добавлены в \textsf{Oberon.txt} во время редактирования: \begin{small} \begin{Verbatim} The juice of it on sleeping eye-lids laid Will make a man or woman madly dote Upon the next live creature that it sees. \end{Verbatim} \end{small} Сам файл патча создется (а позднее обновляется) с помощью \quilt{refresh}. Результат выглядит следующим образом: \footnote{Временные метки в патчах исключены из вывода в примерах.} \begin{small} \begin{Verbatim} \sh{$ quilt refresh} \sh{$ cat patches/flower.diff} Index: example1/Oberon.txt =================================================================== --- example1.orig/Oberon.txt +++ example1/Oberon.txt @@ -2,3 +2,6 @@ It fell upon a little western flower, Before milk-white, now purple with love's wound, And girls call it love-in-idleness. +The juice of it on sleeping eye-lids laid +Will make a man or woman madly dote +Upon the next live creature that it sees. \end{Verbatim} \end{small} Теперь давайте предположим, что в тексте была пропущена строка, и она должна быть вставлена. Файл \textsf{Oberon.txt} уже является частью патча \textsf{flower.diff}, поэтому он может быть немедленно изменен в редакторе без использования команды \quilt{add}. Кроме того, можно использовать команду \quilt{edit}, она просто открывает редактор по умолчанию, если файл уже является частью патча. После того как строка добавлена, мы используем \quilt{diff -z}, чтобы увидеть изменение правок, которые мы сделали: \begin{small} \begin{Verbatim} \sh{$ quilt diff -z} Index: example1/Oberon.txt =================================================================== --- example1.orig/Oberon.txt +++ example1/Oberon.txt @@ -2,6 +2,7 @@ It fell upon a little western flower, Before milk-white, now purple with love's wound, And girls call it love-in-idleness. +Fetch me that flower; the herb I shew'd thee once: The juice of it on sleeping eye-lids laid Will make a man or woman madly dote Upon the next live creature that it sees. \end{Verbatim} \end{small} Изменения самого верхнего патча могут быть просмотрены командой \quilt{diff} без аргументов. Это не изменит сам файл патча. Эти изменения добавятся к патчу путем его обновления командой \quilt{refresh}. Затем мы удаляем пакет из стека командой \quilt{pop}: \begin{small} \begin{Verbatim} \sh{$ quilt refresh} Refreshed patch flower.diff \sh{$ quilt pop} Removing flower.diff Restoring Oberon.txt No patches applied \end{Verbatim} \end{small} Далее, давайте предположим, что \textsf{Oberon.txt} был изменен в ``апстриме'': слово \textit{girl} не очень хорошо подходит, и поэтому оно было заменено на \textit{maiden.} \textsf{Oberon.txt} теперь содержит: \begin{small} \begin{Verbatim} Yet mark'd I where the bolt of Cupid fell: It fell upon a little western flower, Before milk-white, now purple with love's wound, And maidens call it love-in-idleness. \end{Verbatim} \end{small} Это приводит к тому, что \textsf{flower.diff} больше не применяется корректно. При попытке добавить \textsf{flower.diff} в стек с помощью \quilt{push}, мы получим следующий результат: \begin{small} \begin{Verbatim} \sh{$ quilt push} Applying flower.diff patching file Oberon.txt Hunk #1 FAILED at 2. 1 out of 1 hunk FAILED -- rejects in file Oberon.txt Patch flower.diff does not apply (enforce with -f) \end{Verbatim} \end{small} Quilt не может автоматически применить патчи, в которых есть отвергнутые изменения. Патчи, которые не применяются без ошибок можно ``применить насильно'' командой \quilt{push -f}, в результате чего будут созданы файлы отвергнутых изменений, для каждого файла, который имеет конфликты. Эти конфликты должны разрешаться вручную, после чего данный патч может быть обновлен (\quilt{refresh}). Quilt помнит, когда патч был применен насильно. Он отказывается добавлять дальнейшие патчи на верх стека, и не удаляет их из стека. Примененный насильно патч может быть ``насильно'' удален из стека командой \quilt{pop -f}, однако вот что происходит, когда \textsf{flower.diff} применяется ``насильно'' : \begin{small} \begin{Verbatim} \sh{$ quilt push -f} Applying flower.diff patching file Oberon.txt Hunk #1 FAILED at 2. 1 out of 1 hunk FAILED -- saving rejects to file Oberon.txt.rej Applied flower.diff (forced; needs refresh) \end{Verbatim} \end{small} После повторного добавления строк из \textsf{flower.diff} в \textsf{Oberon.txt}, мы обновим патч командой \quilt{refresh}. \begin{small} \begin{Verbatim} \sh{$ quilt edit Oberon.txt} \sh{$ quilt refresh} Refreshed patch flower.diff \end{Verbatim} \end{small} Наша последняя версия \textsf{Oberon.txt} содержит: \begin{small} \begin{Verbatim} Yet mark'd I where the bolt of Cupid fell: It fell upon a little western flower, Before milk-white, now purple with love's wound, And maidens call it love-in-idleness. Fetch me that flower; the herb I shew'd thee once: The juice of it on sleeping eye-lids laid Will make a man or woman madly dote Upon the next live creature that it sees. \end{Verbatim} \end{small} \section{Дальнейшие команды и понятия} В этом разделе представлено несколько основных команд, а затем описаны дополнительные понятия, которые могут быть не очевидны. Мы не описываем все особенности quilt здесь, так много команд quilt совершенно интуитивны, кроме того, справка, которая описывает доступные параметры для каждой команды, доступна через \quilt{\textit{cmd} -h}. Команда \quilt{top} показывает название верхнего патча. Команда \quilt{files} - какие файлы патч содержит. Команда \quilt{patches} - какие патчи изменяют указанный файл. В нашем предыдущем примере, мы получим следующие результаты: \begin{small} \begin{Verbatim} \sh{$ quilt top} flower.diff \sh{$ quilt files} Oberon.txt \sh{$ quilt patches Oberon.txt} flower.diff \end{Verbatim} \end{small} Команды \quilt{push} и \quilt{pop} дополнительно принимают число или имя исправления в качестве аргумента. Если указано число, определенное количество исправлений добавляется (\quilt{push}) или удаляется (\quilt{pop}). Если задано имя патча, патчи добавляются (\quilt{push}) или удаляются (\quilt{pop}), пока указанный патч не будет находится на вершине стека. С опцией \textsf{-a} все патчи в серии файлов добавляются (\quilt{push}), или все примененные патчи удаляются из стека (\quilt{pop}). \subsection{Уровни Вложенности Патчей} Quilt предполагает, что патчи применяются с уровнем вложенности 1 (опция \textsf{-p1} программы \prog{GNU patch}) по умолчанию: верхняя директория в именах файлов в патчах игнорируется. Quilt помнит уровнь каждого патча в папке \textsf{series}. При создании (\quilt{diff}) или обновлении патча (\quilt{refresh}), может быть определен уровень вложенности и в ряд файлов будут внесены соответствующие изменения. Quilt можно применять патчи с произвольным уровнем вложенности, а также генерировать патчи с уровнем равным нулю или единице. Когда уровень вложенности равен единице название каталога, который содержит рабочие дерево используется в качестве дополнительного компонента пути. (Так, в нашем примере \textsf{Oberon.txt} содержится в каталоге \textsf{example1}.) \subsection{Импорт Патчей} Команда \quilt{import} автоматизирует импорт исправлений в quilt. Команда копирует патч в каталог \textsf{patches} и добавляет его в \textsf{series}. Для патча уровень вложенности которого отличается от единицы он добавляется после имени файла патча. (Запись для файла \textsf{a.diff} с нулевым уровнем будет выглядеть так ``{\small \verb|a.diff -p0|}''.) Другая распространенная операция это добавить новые патчи на самый верх стека. Это можно сделать вручную сначала добавить все файлы, содержащиеся в дополнительных патчах для исправления коммандой \quilt{add}, \footnote{ Утилита \prog{lsdiff}, которая является частью пакета \textit{patchutils} генерирует список измененных в патче файлов.}, а затем применять патч на рабочее дерево. Команда \quilt{fold} объединяет эти шаги. \subsection{Делимся патчами с другими} Для обмена набором патчей с кем-то еще все, что необходимо, это файл \textsf{series}, который содержит список патчей и каким образом они применяются, и сами патчи. Каталог \textsl{.pc} содержит только рабочее состояние quilt, и не должен распространяться. Убедитесь, что все патчи актуальны, и обновите их по мере необходимости. Опция \textsf{-{}-combine} команды \quilt{diff} может быть использована для получения одного большого патча из всех исправлений в серии файлов. \subsection{Объединение с ``апстримом''} Концепция объединения ваших патчей с апстримом идентична применению ваших патчей на более новой версии программы. До слияния, убедитесь, что все ваши патчи удалены с помощью \quilt{pop -a}. Затем обновите оригинальных исходный код. И наконец, удалить устаревшие патчи из файла \textsf{series} и запустите \quilt{push} для применения остальных, урегулируйте конфликты и обновите патчи по мере необходимости. \subsection{Ветвление} \label{sec:forking} Есть ситуации, в которой обновления патча на месте не является идеальным: один и тот же патч может быть использован нескольких сериях, Он может также служить удобным местом хранения документации старых версий патча, а также создавать новые с различными именами. Это можно сделать вручную, создав копию патча (который не должен быть применен), и обновить его патча в файле \textsf{series}. Команда \quilt{fork} упрощает это: она создает копию верхнего патча в серии, и обновляет файл \textsf{series}. Если патч указывается явно, \quilt{fork} сгенерирует следующую последовательность имен патчей: \textsf{patch.diff}, \textsf{patch-2.diff}, \textsf{patch-3.diff},\dots \subsection{Зависимости} \label{sec:dependencies} Когда число исправлений в проект растет, становится все труднее найти правильное место для добавления новых исправлений в серию патчей. В определенный момент, вы добавляете патч в конец, поскольку нахождение нужного места стало слишком сложным. В долгосрочной перспективе, беспорядок накапливается. Чтобы избежать этого, сохраняя общую картину, команда \quilt{graph} генерирует \textit{dot} графики, показывающие зависимость между патчами.\footnote{Команда \quilt{graph} вычисляет зависимости на основе того какие патчи меняют какие файлы и дополнительно проверяет на перекрывающиеся изменения в файлы. Хотя первый подход часто приводит к ложных срабатываниям, последний подход может привести к ложным негативам (то есть, \quilt{graph} может не учитывать зависимостей). } Вывод этой команды можно изобразить с помощью инструментов, таких как AT\&T Research's Graph Visualization Project (GraphViz, \url{http://www.graphviz.org/}). Команда \quilt{graph} поддерживает различные виды графиков. \subsection{Расширенный Diffing} Quilt позволяет нам сравнивать и обновлять патчи не только на вершине стека (\quilt{diff -P \textit{patch}}) и \quilt{refresh \textit{patch}}). Это полезно в ряде случаев, например, когда %\begin{itemize} % %\item когда верхний патч был изменен, но изменения еще не завершены, обновление патча даст файл, который будет находиться в неустойчивом состоянии. Без этого патч не может быть удален из стека, или же эти изменения будут потеряны. % %\item доставание патча, а затем возврат их с модифицированными метками времени. Это может вызвать много времени компиляций. % %\item Это просто удобно иметь возможность исправить мелкие ошибки в патчей ниже в стеке без особых церемоний. % %\end{itemize} % патчи выше по стеку затрагивают те-же файлы, что и этот патч. Мы можем изобразить это как тень, которую бросают патчи выше по стеку на эти файлы. При обновлении патча, изменения в файлах, которые не являются теневыми (а значит, в последний раз были изменены патчем, который в настоящее время обновляется), принимаются во внимание, теневые изменения не будут обновлены. Команда \quilt{diff} позволяет объединить несколько патчей в один по желанию с указанием диапазона включаемых патчей (см. \quilt{diff -h}). Комбинированный патч будет изменять каждый файл, содержащиеся в этих патчах только один раз. Результат применения комбинированного патча такой же, как применения всех патчей в указанном диапазоне последовательно. Иногда бывает удобно использовать другой инструмент вместо \prog{GNU diff} для сравнения файлов (например, графическая утилита \ Prog tkdiff ()). Quilt не будет использовать другие инструменты, кроме \prog{GNU diff} при обновлении патчей (\quilt{refresh}), но \quilt{diff} принимает аргумент \textsf{-{}-diff=\textit{utility}}. С этим аргументом, указанный утилита вызывается для каждого файла, в который вносятся изменения с передачей в качестве аргументов исходного и измененного файлов. Для новых файлов, первый аргумент будет \textsf{/dev/null}, а для удаленных - второй. Когда команде \quilt{diff} передается список имен файлов, просмотр будет ограничен только этими файлами. С параметром \textsf{-R} меняются местами оригинальные и новые файлы, в результате чего получается обратный diff. Иногда бывает полезно создать diff-файл между произвольным состоянием рабочего дерева и текущей версией. Это может быть использовано для создания различий между разными версиями патчей (см. раздел ~\ref{sec:forking}), и т.д. Для этой цели quilt позволяет сделать снимок рабочего каталога (\quilt{snapshot}). Позднее файл различий с этим снимком рабочего деревф может быть создан с помощью команды \quilt{diff -{}-snapshot}. В настоящее время поддерживается только один снимок. Он хранится в каталоге \textsf{.pc/.snap}. Для экономия свободного места на диске, он может быть удален командой \quilt{snapshot -d}, или путем удаления каталога \textsf{.pc/.snap} вручную. \subsection{Работа с RPM пакетами} \label{sec:rpm} Несколько дистрибутивов Linux основаны на пакетном менеджере RPM~\cite{max-rpm}. RPM пакет состоит из спецификации, определяющий, как строится пакет, а также ряда дополнительных файлов, таких как TAR архивы, патчи, и т.д. Большинство RPM пакетов содержат официальный пакет программного обеспечения а также ряд патчей. Прежде чем этими патчами можно манипулировать в quilt, должен быть создан файл \textsf{series}, который содержит список исправлений вместе с их уровнями вложенности. Команда \quilt{setup} автоматизирует сборку большинства пакетов RPM. При передаче spec-файла в качестве аргумента, она выполняет раздел \textsf{\%prep} spec-файла, который должен извлечь официальный пакет программного обеспечения, а также применить патчи. В этом случае, quilt запоминет TAR архивов и патчи которые нужно применить, и создает файл \textsf{series}. На основании этого файла, \quilt{setup} распаковывает архивы, а также копирует патчи в подкаталог \textsf{patches}. Некоторая мета-информация, такая как имена файлов хранится в виде комментариев в файле \textsf{series}. \quilt{setup} также принимает файл \textsf{series} в качестве аргумента (который должен содержать некоторую мета-информацию), и предусматривает создание рабочего дерева из этого файла. \section{Настройка Quilt} После запуска, quilt выполняет файл \textsf{.quiltrc} в домашнем каталоге пользователя, или указанный в опции \textsf{-{}-quiltrc} файл. Этот файл представляет собой обычный сценарий Bash. Параметры по умолчанию могут быть переданы в любой команде, определив переменную \textsf{QUILT\_\textit{COMMAND}\_ARGS} (например, \textsf{QUILT\_DIFF\_ARGS="-{}-color=auto"} подсвечивает вывод \quilt{diff} при выводе на терминал). В дополнение к этому, в quilt признаются следующие переменные: \begin{description} \item[\textsf{QUILT\_DIFF\_OPTS}] Дополнительные параметры, которые quilt передает программе \prog{GNU diff} при генерации патчей. Полезная опция для исходного кода на языке С ``\textsf{-p}'', которая заставляет программу \prog{GNU diff} показать в результирующем патче функции которые были изменены. \item[\textsf{QUILT\_PATCH\_OPTS}] Дополнительные опции, которые quilt передает программе \prog{GNU patch} при применении исправлений. (Например, в некоторых версиях \prog{GNU patch} поддерживает опцию ``\textsf{-{}-unified-reject-files}'' для создания файлов откланенных изменений в стиле unified-diff. \item[\textsf{QUILT\_PATCHES}] Расположение файлов исправлений (см. раздел ~\ref{sec:basic}). По умолчанию этот параметр равен ``\textsf{patches}''. \end{description} \section{Ловушки и известные проблемы} Как упоминалось ранее, файлы должны быть добавлены, прежде чем они могут быть изменены. Если игнорировать этот шаг, будет происходить одна из следующих проблем: если файл входит в патч ниже в стеке, то изменения будут опубликованы в этот патч при обновлении, и для этого патча команда \quilt{pop} завершится неудачно, до тех пор пока он не будет обновлен. Если файл не входит в какой-то патч, то будет изменен оригинальный файл в рабочем дереве. Файлы патчей могут изменить тот же файл несколько раз. \prog{GNU patch} содержит ошибку, которая повреждает резервные копиии файлов в этом случае. Исправление ошибки доступно , и будет интегрировано в более поздней версии \textit{GNU patch}. Исправление уже включено в версию \textit{GNU patch} из SUSE. Есть некоторые пакеты, которые предполагают, что это хорошая идея, чтобы удалить все пустые файлы по рабочим деревьев, в том числе в каталоге \textsf{.pc}. Цель \textit{make clean} в исходных кодах ядра Linux является примером. Quilt использует файлы нулевой длины в \textsf{.pc} для отметки файлов, добавленных патчами, поэтому такие пакеты могут повреждать каталог \textsf{.pc}. Чтобы обойти проблему, создайте символическую ссылку \textsf{.pc} в рабочем дерева, которая указывает на каталог снаружи. Это может случиться, что файлы в каталоге \textsf{patches} рассинхронизируются с рабочим деревом (например, они могут быть случайно обновлены из CVS или других систем контроля версий). Файлы в каталоге \textsf{.pc} также могут быть рассинхронизированы, особенно если файлы не добавили до изменения их (\quilt{add} / \quilt{edit}). Если это произойдет, это возможно восстановить исходное дерево, но очень часто лучшим решением будет начинать все с исходной рабочей директории и подкаталога \textsf{patches}. Нет необходимости сохранять какие-либо файлы из каталога \textsf{.pc} в этом случае. % - Patches cannot automatically be reverse applied (?) % - Does not auto-detect is a patch has been cleanly integrated % - Speed % - Push a patch that is not in the series file (after changing % applied-patches to include the full patch name)? % - Other back-ends (RCS etc.) % - Pop and push modify file timestamps, which causes recompiles. % Ccache ameliorates this. % - patchutils: Very useful: lsdiff, filterdiff, etc. \section{Резюме} Мы показали, как коллекция сценариев \textit{quilt} решает различные проблемы, которые возникают при работе с патчами для программного обеспечения. Quilt гораздо лучше чем традиционные средства работы с патчами (\prog{GNU patch}, \prog{GNU diff}, и т.д.), и предлагает множество функций, которые недоступны в конкурирующих решениях. Вступайте в клуб! Домашняя страница проекта - \url{http://savannah.nongnu.org/projects/quilt/}. Список рассылки разработчиков - \url{http://mail.nongnu.org/mailman/listinfo/quilt-dev}. Конечно, мы всегда рады дополнительным функциям, которые вы предложите добавить в quilt. Переведено на русский: Ivan Borzenkov . \begin{thebibliography}{XX} \bibitem{akpm02} Andrew Morton: Patch Management Scripts, \url{http://lwn.net/Articles/13518/} and \url{http://userweb.kernel.org/~akpm/stuff/patch-scripts.tar.gz}. \bibitem{quilt} Andreas Grünbacher et al.: Patchwork Quilt, \url{http://savannah.nongnu.org/projects/quilt}. \bibitem{posix-2001-diff} IEEE Std. 1003.1-2001: Standard for Information Technology, Portable Operating System Interface (POSIX), Shell and Utilities, diff command, pp.~317. Online version available from the The Austin Common Standards Revision Group, \url{http://www.opengroup.org/austin/}. \bibitem{info-diff} \textit{GNU diff} info pages (\textsf{info Diff}), section \textit{Output Formats.} \bibitem{max-rpm} Edward C. Bailey: Maximum RPM: Taking the Red Hat Package Manager to the Limit, \url{http://www.rpm.org/max-rpm/}. \end{thebibliography} % Add a "quick-reference card"? \end{document}