Kodomo

Пользователь

Репозитории

конфликты – они в головах, а не в репозиториях

— народная мудрость

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

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

Что такое репозиторий и зачем он нужен

Репозиторий – это хранилище файлов, в котором кроме самих файлов хранится ещё и всякая полезная всячина: прежние версии каждого файла, комментарии к каждому изменению и заметка, кто это изменение внёс. Кроме того, репозиторий отвечает за обмен свежими версиями между соавторами проекта.

Зачем он может понадобиться вам? (За исключением того, что я буду требовать, чтобы все плоды вашего труда в рамках моего курса хранились в репозиториях)

Пример. Вы пишете курсовую работу. Вы написали две трети, пришёл научный руководитель, и сказал: "что за ерунда? всё переписать!". Вы переписали снова две трети, пришёл научный руководитель, и сказал: "а в прошлой-то версии 2-я глава была лучше". И тогда вы начинаете сохранять рядом кучу файликов, то ли пронумерованных цифирками, то ли датами. И не дай боже вам приходит в голову идея прежде, чем переносить изменение из прошлой версии в новую, исправить что-то на месте в старой версии – и оказывается у вас несколько файлов, про которые у вас и только у вас имеется в голове тайное знание, где хранится наиболее свежий вариант чего.

Второй пример. Собрались вы в соавторстве с кем-нибудь писать работу. Разделили ответственность: я пишу первую половину, ты – вторую. Написали, склеили – плохо читается. Потом вам соавтор пишет (спутся два дня – а вы как раз только обнаружили кучу опечаток и правите их), что он сделал всё хорошо – и вот результирующий текст. Пристальный просмотр выявляет, что текст-то может и лучше, а всё те же опечатки во всё тех же местах остались. Ещё более пристальный просмотр выявляет, что вы таки не поняли, что же таки соавтор поправил и вы идёте с ним выянять, которые из 200 страниц вам надо перечитывать на тему улучшений.

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

Работа в darcs

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

Все команды, которые я пишу на этом занятии – команды UNIX (или Linux – как хотите называйте). $ в начале строки символизирует приглашение командной строки (у вас оно наверняка не совсем такое). Команды следует исполнять на kodomo через ssh (напр. !PuTTY) и не забывать о том, что домашняя директория в Linux у вас совпадает с диском H: в windows – редактировать вам вероятнее всего удобнее будет в windows.

Создание репозитория

Итак, создавать репозиторий можно двумя способами, создать пустой репозиторий:

$ mkdir project
$ cd project
$ darcs init

или присоединиться к чужому репозиторию:

$ darcs get .../project
Copying patches, to get lazy repository hit ctrl-C...
Finished getting.
$ cd project

внутри репозитория лежит папочка _darcs, в которой, собственно, и хранится вся история проекта, настройки, и прочая служебная-вспомогательная ерунда. Не трогайте эту папочку почём зря.

Запись изменений

Теперь, предположим, мы хотим создать в репозитории новый текстовый файл. (Важное замечание: вообще, в репозитории лучше хранить именно текстовые файлы. К ним относятся что обычные тексты, набранные, например, в FAR, что исходные коды программ большинства языков программирования. А вот файлы word к ним не относятся – их, конечно, тоже можно хранить в репозитории, но толку от этого будет уже не ощутимо больше, чем от того, чтобы просто хранить отдельными нумерованными файлами старые версии и приписывать к ним руками комментарии, что где поменялось).

$ редактируем hello.txt, пишем туда Hello, wnoderful world!
$ cat hello.txt
Hello, wnoderful world!
$ darcs add hello.txt
$ darcs record hello.txt
... [тут начинается диалог с darcs на тему того, что записывать, а что нет -- мы представляемся системе (только в первый раз), говорим записать всё, добавляем комментарий "added hello.txt with dummy message" и отказываемся писать длинное описание изменения] ...

Командочка cat к darcs не имеет никакого отношения. Она выводит на экран содержимое файла. Привожу я её здесь, чтобы показать, в каком состоянии находится проект в тот момент, когда мы начинаем работать с darcs.

Команда darcs add hello.txt говорит darcs о том, что отныне он должен следить за изменениями ещё и в файле hello.txt. При этом в истории изменений с точки зрения репозитория файл hello.txt ещё не появился.

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

И тут мы заметили, что в содержимом у нас опечатка!

$ редактируем hello.txt, пишем туда Hello, wonderful world!
$ cat hello.txt
Hello, wonderful world!
$ darcs whatsnew
hunk ./hello.txt 1
-Hello, wnoderful world!
+Hello, wonderful world!
$ darcs rec
... [снова диалог с darcs, на сей раз изменение назовём "hello.txt: fixed typo"] ...
$ darcs changes
Sat Sep 19 17:55:11 MSD 2009  Danya Alexeyevsky <dendik@localhost>
  * hello.txt: fixed typo

Sat Sep 19 17:54:15 MSD 2009  Danya Alexeyevsky <dendik@localhost>
  * added hello.txt with dummy message

Очень полезная (на мой вкус) команда darcs whatsnew (сокращается до darcs wh) сообщает, что изменилось в рабочей директории по сравнению с последним записанным изменением.

Командочка darcs changes выдаёт то, как увидят ваши правки в первую очередь ваши соавторы. Да и вы, когда будете искать прежнюю версию, тоже будете смотреть именно сюда, чтобы выяснять, в какой именно версии затерялись полезные строчки. (Или просто ностальгии ради).

И так далее. Всё, что нужно для работы в репозитории в-одиночку – это команда darcs record и изредка darcs add. Добавьте команд по чтению истории по вкусу, – и вы получили ответ на первый вопрос – как хранить хронологию изменений к чему-нибудь (тексту или программе).

Обмен изменениями между репозиториями (и соавторами)

Наконец, третья группа команд – обмен изменениями между репозиториями. Предположим, что вы создали репозиторий с помощью darcs get (тогда darcs уже знает, откуда он его получил, и ему не нужно подсказывать, куда отправлять ваши изменения).

$ darcs get .../project
...
$ cd project
$ ls
hello.txt
$ редактируем hello.txt, допишем несколько страниц об истории фразы "hello world"...
$ darcs rec
...
$ darcs push
...

Команда darcs push отправляет все записанные изменения туда же, откуда вы скачали репозиторий.

Спустя два дня:

$ darcs pull
$ редактируем hello.txt, обнаруживаем в нём новую главу -- об области применения фразы "hello world"
$ rm hello.txt
$ darcs rec
... [комментарий: "removed obsolete hello.txt"]
$ darcs push

Команда darcs pull вытягивает в локальный репозиторий изменения, которые сделал соавтор. Казалось бы, досадно удалять файл, на который вы с соавтором потратили два дня – но помните, в каждом из вашего репозитория и репозитория соавтора хранится вся история его изменений, а значит, и, например, последнюю его версию вытащить из репозитория не проблема (как? читайте документацию!). darcs record увидит удаление файла как два изменения: сначала вы стёрли в файле каждую строку, и лишь потом сказали darcs удалить сам файл. Когда ваш коллега скажет darcs pull, файл hello.txt у него в рабочей директории исчезнет (а история и возможность отменить удаление файла останутся).

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

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

Первый тип конфликтов хороший. Если он случился (а случиться он может только во время darcs pull), то darcs вам разметит в том файле, который вы вот сейчас прямо редактируете стрелочками и звёздочками фрагмент файла, в котором хранится версия вашего соавтора и соответствующий ему кусок в том же месте, который только что написали вы. darcs whatsnew очень полезна в этом случае, чтобы увидеть эти стрелочки и понять, где же именно находится конфликт. Чтобы его разрешить, нужно посмотреть на получившиеся две версии и собрать из них одну осмысленную – тут совет один: полагаться на здравый смысл. Важно (/!\): darcs record этих самых стрелочек да чёрточек, которыми darcs разметил конфликт, увидеть не должен (это должно следовать очевидным образом из того же здравого смысла; если оно не следует из здравого смысла, подуймайте снова и выберите себе более подходящий здравый смысл для ситуации).

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

Как избегать второго типа конфликтов? Нужно придерживаться достаточно строгой дисциплины работы с репозиторием: всегда перед record и push делать pull, притом желательно тратить поменьше времени на record (дабы другой соавтор влезть не успел и всё попортить):

$ darcs pull -a && darcs rec -a -m "..." && darcs push -a

Документация

В darcs встроена хорошая документация. Читайте darcs help или darcs команда --help.

Прекрасное руководство по darcs на английском языке обитает тут: http://darcs.net/manual (оно меняется вслед за версиями darcs – сейчас оно соответствует той версии, которая установлена на kodomo и kodomo-count).

Судя по всему, русская документация, какая была, канула в лету с обновлением сайта darcs.net.

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

Полезные добавки

Полезные замечания для тех, кто ленится читать help или хотя бы контекстную помощь в диалогах с darcs:

Историческая демагогия (наверное, близкая к исторической справке)

Видимо, история репозиториев начинается с создания команд diff и patch (живых и активно используемых и поныне), первая из которых умела сравнивать два файла (старый и новый) и выдавать построчные различия между ними – соучастник проекта правил какой-нибудь файл, делал diff между старой и новой версией и слал автору проекта с комментарием, что он исправил. Автор проекта использовал patch и менял старый файл так, чтобы он совпадал с тем, как оно было у соучастника. Командочки diff и patch возникли на заре UNIX (если не раньше) и им много больше лет, чем каждому из нас или вас.

Следующей возникла командочка rcs (Revision Control System), которая умела хранить историю изменений одного файла в соеднем файлике в виде последовательностей тех самых diff'ов с комментариями и датами.

Сильно позже и этой командочки стало не хватать, и возник монстр под названием CVS (Concurrent Versioning System), который являлся обвязкой к rcs, и был, в сущности полноценной системой контроля версий, в которую с каждым годом запихивали коленкой каждую новую идею, которая казалась авторам полезной.

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

Где-то годах в 90-х оказалось довольно много способных программистов, которые видели эти недостатки и пытались их решать. Тогда появились проекты: SVN (subversion), GNU Arch (tla), darcs, mercury, bazaar, git и пр. Имя им легион.

Я рассказываю вам про darcs, как репозиторий, с которым проще всего (на мой субъективный вкус) работать. Darcs возник из курсовой, а затем аспирантской (точнее, ph.d.) работы физика David Roundy на тему "теория патчей".

С другой стороны, самый популярный среди программистов репозиторий сейчас git, который умеет всё то же, что и darcs (и много больше – чем сторонники git хвастаются – но нужно ли оно на самом деле?), но чтобы работать с git, нужно освоить существенно больше теории (о том, как git устроен). Впрочем, переходить с darcs на git, как правило, оказывается тоже довольно просто.

План