Лекция 4. Работа с исходным текстом

Мы не очень поговорили о семействе утилит для вынимания информации об исполняемом файле. Формат elf довольно непростой, исполняемый бинарник выглядит многосекционно, и для того чтобы с этим работать существует целая пачка утилит из которых мы упомянули только ldd, objdump и ...

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

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

  1. Как учитывать эти тексты? Как систематизировать и увязать проект в единое целое. Только таймстемпов недостаточно. На них можно ориентироваться если разработчик один, но это неудобный инструмент. Лучше придумать версионирование.
  2. Как обмениваться этими текстами с коллегами. Когда компьютеры были большими, а хакеры волосатыми, обмены происходили эпизодично — обменами ленточками на конференциях, между конференциями как раз успевали скомпилировать и подправить, да и программы были строк на 500. Но эти времена давно прошли.

diff patch

Diff утилита, которая выдает построчные различия между файлами. Незаменима для разработки в стиле 80, когда огромный волосатый хакер смотрит на 2 программы и написать третью. Да и сейчас полезно.

К diff прилагаются инструменты, например скрипт на ed, применяющий изменения к файлу.

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

Но, главная задача diff — сгенерировать патч, то есть файл с построчными различиями предположительного одного и того же файла, пригодный для превращения первого файла на второй.

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

patch — программа, которую написал автор перла.

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

diff a b > p

В действительности очень редко когда далее делается

patch < p (->a)

На самом деле речь идет о совместной разработке. У соседа есть файл a', и к нему надо применить патч p.

Как правило патч-файл это файл в котором помимо измененных строк содержится ещё контекст — какие строчки не менялись и что было изменено между этими неизменными строчками.

618:
b=1
--- a=true
+++ a=false
c=4

Дифф в таком формате делает командочка diff -u

Но всё равно надо быть Ларри Воллом, чтобы приложить вот это изменение к уже изменившемуся файлу.

Есть несколько строгих правил применения патча:

  1. если контекст изменился, то патч не накладывается
  2. если изменились только пробелы, то можно попробовать их игнорировать (особенно концевые)
  3. Порядок применения патчей имеет значение.

Если патч наложился не целиком например на файл ash, то исходный файл переименовывается в ash.orig, модифицированный называется ash, и создастся ash.rej — неприменившиеся места. Куда должен был применяться отверженный патч нужно смотреть глазами. Чаще всего это связано с изменением контекста. Например, автор решил переименовать b в B.

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

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

Неудобство в следующем. Нету разумной технологии как на живых исходниках попав в ситуацию, когда несколько десятков режектов, очень сложно создать годный патч. Это трудно. В поддержку идет пакет patch-utils, Они смешные. Например у вас есть кумулятивные патчи (друг за другом), вы можете слепить их в один. Можно попытаться сравнить два патча. И многое другое. Позволяет работать с наборами патч-сетов.

Недостатки:

  1. Чтобы быстро изготовить новый патч люди все таки редактируют руками старый патч, это наиболее удобный способ. Хорошо если это было просто изменение контекста.
  2. Всё это безобразие никак не связано с версионированием файлов. Непонятно место файла в воркфлоу разработки.

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

Версионирование

Версионирование изобрели чуть ли не раньше, чем не патчи. Первая система sccs была написана в 1972 году. Большинства из нас ещё не было. Она используется до сих пор, потому что она была с вкраплением в бинарники — регистрирует статическую строковую переменную с информацией, и вытянуть её можно было даже из объектника. Было довольно остроумно. Но это только версионирование.

Следуюший кандидат rcs — добавляла ещё и полное хранение истории изменений в виде диффов. Был специальный каталог, в котором в виде диффов, начиная с нуля, валялись версии вашей программы.

Лектор всё это вполне ещё видел.

Зачем хранить дельту? Чтобы место занимать меньше. Когда компьютеры были большие, а программы маленькие, места для хранения были ещё меньше.

Всё это хорошо, но это индивидуальная разработка.

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

  1. Хранение кода. Где? Раньше у нас было просто дерево каталогов, но теперь надо чтобы несколько людей хранили в одном месте и видели изменения друг дружки. Как только возникает вопрос хранилища, возникает вопрос о том Как хранилище использовать. Теперь файлы общие, а не ваши личные.
    1. Права доступа.
    2. Но этого недостаточно. Дисциплина работы с хранилищем — и дисциплина оформления кода, и то, кто, когда и как помещает код в хранилище.
  2. История разработки. Не только как и когда модифицирован текст, но и кто его модифицировал. Один коллега подарил смешную девайсину на светодиодах и рассказал, что она у них светится красным светом, когда определенный товарищ комитит в репозиторий и надо оттуда это срочно вычищать.

Мы описали функциональность version control system. Их много, самая старая cvs. Она немного несет в себе болезнь нулевой версии — её придумывали в процессе разработки. Лектор пользовался cvs,но понял, что не постиг дао и слишком часто ломал репозиторий. Более современная реинкарнация — svn. Пытались сохранить воркфлоу cvs, но избавится от легаси.

Работа с cvs выглядит примерно так.

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

  1. Редактирование.
  2. Достижение критерия У вас есть некая цель, которую вы хотите достигнуть. Когда критерий достигнут, подзадача решена, вы должны зарегистрировать изменения.
  3. Регистрация изменений.

Большой цикл выглядит так

  1. Обновление рабочей копии
  2. Редактирование.
  3. Достижение критерия
  4. Регистрация изменений.
  5. Обновление хранилища.

Последние два пункта должны быть атомарны. В централизованных системах контроля это операция атомарна.

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

Картина очень напоминает наложение патча, но, опять же, на стероидах — вместо двух патчей есть много разных версий файлов без упорядоченности по времени. Но существует и на это инструмент. Во-первых, старый добрый diff3 — дает файл, в котором помечены изменения только в одном файле, только в другом, в обоих сразу. Первые два случая можно сразу применять.

Существует специальная дисциплина, уменьшающая количество конфликтов, про это много написано, а будет ещё больше.

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

Любая технология передачи данных ассиметричная может быть описана как pull или push. cvs это push технология. Основная приводящая к изменением операция — заталкивание.

Можно же помыслить и другую технологию, основанную на pull. У каждого свой репозиторий. Что-то на хакал и звонишь — втяни изменения, посмотри.

Пример push технологий tfs, source safe, svn

При pull-технологии вырисовывается следующее:

"Заплатите им деньги, они помержаться"

Bazar, git, mercurial, darks

Напоследок про git.

Про git с практической стороны

git-clone и указываете адрес хранилища. После чего у вас создается полная копия. Гит хранит именно объекты, причем все. Для этой цели он считает контрольную сумму файла и делает это его меткой. У него есть дерево таких меток, и вычисляет разности между такими деревьями. Хранит он вообще всё. Между деревьями можно переключаться.

git pull

Похакали

Сказали add на те файлы, которые хотим добавить в дерево

Потом git commit (регистрация)

git push (в удаленный репозиторий)

git merge tool запускает редактор, который открывает сразу 4 файла.

Недавно лектор открыл для себя гит как инструмент работы с совсем любыми исходниками.

Развернули

git init
git add .
git commit -m "Init"
git tag start
зверски хакаете, понимаете, что сделали полную чушь, 
git reset --hard start
git clean (удалить временные файлы, для которых не было адд)

Гит помнит всё.

Как отдать патчи пользователю

git-format patch start — от текущего сотояния до тэга старт преврати каждый коммит в патч

Потом используя это патчи можно пересобрать всё дерево заново git-am

Зачем превращать апстримные исходники в гит.

git rebase пытается старый набор патчей переприменить к новому дереву исходников. Нужен, что переписывать дерево исходников — была ветка от одного места, мы её приварили к другому.

git rebase --interactive Очень удобная вешь.

Как организуется информационное пространство. Нужно иметь пресловутый сервер публикации.

github

gitorius

Можете организовать самостоятельно, например с помощью gitolite.

Если процедура работы с хранилищем определена чётко, то над процедурами работы с информационным пространством народ ещё экспериментирует.

LecturesCMC/LinuxApplicationDevelopment2012/Conspects/04 (последним исправлял пользователь Nyarcel 2013-01-25 10:36:11)