Об этом проекте
9vcs — это система контроля версий, построенная на базе 9P (протокола Plan 9) с использованием библиотеки Go 9p от автора. Она предназначена для небольших доверенных команд, которым нужен полноценный контроль версий без использования хостинг-платформ. История хранится в виде набора контентно-адресуемых патчей, а не снимков (snapshots); в файле README содержится руководство по практическому использованию, а обоснование архитектуры вынесено в PLAN.md. Терминология намеренно отличается от GitHub: здесь нет понятий clone, push, pull, fork или pull request, а специальная шпаргалка сопоставляет привычные термины с эквивалентами в 9vcs.
Требования и установка
Единственным требованием является Go 1.26.5 или более поздней версии. И 9vcs, и библиотека 9p написаны на чистом Go с использованием только стандартной библиотеки, не требуют демонов, баз данных или внешних сервисов. Предварительно скомпилированные бинарные файлы публикуются на странице Releases в виде архивов tar.gz для linux/amd64, linux/arm64, darwin/arm64 и darwin/amd64, каждый с контрольной суммой .sha256 для проверки; архив содержит бинарный файл, LICENSE и README. Сборка из исходников выполняется одной командой go build для cmd/9vcs. Также доступны некоторые установщики.
Индивидуальный рабочий процесс
После выполнения init в директории команда status выводит по одной строке для каждого измененного пути, используя A для новых, M для измененных, D для удаленных и U для неразрешенных конфликтов; она сообщает только о «грязных» файлах, а не о разнице (diff). Перемещения не отслеживаются как переименования — перемещенный файл отображается как удаление плюс добавление, что соответствует способу хранения патча. Команда diff показывает построчные изменения в рабочем дереве, может сравнивать с другой точкой или сравнивать две точки напрямую без участия рабочего дерева. Команда log выводит список записанных патчей (от новых к старым) с указанием автора, отпечатка (fingerprint) или статуса подписи, а также затронутых путей. Команда record создает патч. Область подготовки (staging area) или этап индексации отсутствуют: рабочее дерево само является областью подготовки, и record сравнивает его с текущим head.
Ветки и слияние
Команда branch выводит список, создает ветку от HEAD, другой ветки или хеша, а checkout переключает ветки или создает и переключает их с флагом -b. Слияние порождает реальные конфликты графа патчей — построчные разветвления, бинарные конфликты с сопутствующим файлом сравнения и конфликты типа «изменение/удаление» — вместо поверхностного трехстороннего текстового diff. Чистое слияние завершается командой record. При возникновении конфликтов в текстовых файлах остаются маркеры конфликтов, а для бинарных файлов создается sidecar-файл, названный по пути и короткому хешу для сравнения; пользователь редактирует файлы и затем выполняет record. Слияние можно отменить, что вернет рабочее дерево в состояние до слияния и очистит статус слияния, независимо от того, было ли оно запущено через merge или apply.
Шаблоны игнорирования
Файл .9vcsignore в корне репозитория работает аналогично .gitignore и предназначен для записи и совместного использования. Один шаблон на строку; пустые строки и комментарии с решеткой игнорируются. Шаблоны без слеша совпадают на любой глубине, шаблоны со слешем привязаны к корню репозитория, а завершающий слеш ограничивает совпадение только реальными директориями. Отрицание с восклицательным знаком и шаблоны с двойной звездочкой явно не поддерживаются. Шаблоны игнорирования лишь исключают новые файлы из record и diff; добавление шаблона позже не делает уже записанный файл удаленным.
Командная работа
При первом использовании создается долгоживущая пара ключей Ed25519, а команда identity show выводит отпечаток, который участники команды обмениваются вне системы для авторизации друг друга. Тот, кто хостит общую историю, запускает serve на определенном порту; сервер считывает файл .9vcs/authorized-peers в текущем репозитории (одна строка с отпечатком и правами на каждого участника). Права включают: read (только получение истории), propose (чтение плюс отправка подписанного бандла в область предложений) и write (полный доступ, включая перемещение ссылок веток). Команды add-collaborator нет — достаточно отредактировать файл; файл считывается один раз при запуске, поэтому изменения требуют перезапуска, что важно при отзыве скомпрометированного ключа.
Команда serve работает на переднем плане без фонового демона. Сетевой push отклоняет перемещение ветки, которая в данный момент развернута (checked out) на обслуживающей машине, поэтому хосту следует использовать другую ветку или отдельную директорию для обслуживания. Новый участник начинает с init и import, указывая отпечаток пира, хост, порт и имя ветки; import работает только в режиме fast-forward и загружает ветку, а также все патчи и блобы, от которых она транзитивно зависит. Команда reconcile используется для постоянной синхронизации: она делает pull, если пир опережает локальную копию, и push, если локальная копия опережает пира; при реальном расхождении она загружает недостающее и просит пользователя переключиться и выполнить слияние локально, а не через сеть. Обе команды могут явно закреплять отпечаток пира, иначе они используют локальное хранилище trust-on-first-use, которое один раз запрашивает подтверждение для нового адреса, молча проверяет известные адреса и громко отказывает, если отпечаток внезапно изменился.
Офлайн-обмен и предложения
Бандлы позволяют перемещать изменения без запущенного сервера: export записывает подписанный бандл в файл для отправки по почте, в чате или на съемном носителе; получатель проверяет его с помощью bundle show (подписавший, сообщение, патчи), импортирует для проверки подписи и локального хранения без изменения ссылок, просматривает через diff, а затем применяет и записывает. Import сам по себе никогда не перемещает ссылку. Пользователи с правом propose могут отправлять изменения напрямую через offer, который публикует подписанный бандл на сервере сопровождающего; сопровождающий просматривает список ожидающих предложений, применяет одно для загрузки, проверки и локального хранения, интегрирует его с помощью apply и удаляет из очереди. Поскольку предложения существуют только внутри пространства имен живого сервера, сопровождающий подключается к своему экземпляру serve, а значит, его собственный отпечаток также должен быть в файле authorized-peers.
Интеграция с 9sh и обработка пространств имен
Обычно репозиторий определяется путем поиска вверх от текущей директории, как в git. Внутри 9sh (оболочки в стиле Plan 9) флаг -C перед именем команды предлагает второй путь: он сначала пробует путь в пространстве имен 9sh — относительный путь под local или абсолютный путь, который может разрешиться в любой привязанный объект 9sh, — и только затем переходит к обычному пути ОС. Вне сессии 9sh флаг ведет себя как git -C.
Восстановление
Документированные пути восстановления включают отмену слияния или apply, запись или ручной сброс незафиксированных изменений, которые блокируют checkout, merge или apply, восстановление отдельных путей до их записанного состояния в head с помощью restore (путь без записанного состояния удаляется, поэтому отмена переименования означает указание обоих путей), а также повторное закрепление отпечатка пира, который изменился законным образом.
Статус проекта
В README разграничивается использование 9vcs командой и разработка самого инструмента. Разработка следует принципу trunk-based development с короткоживущими ветками, защищенной веткой master, куда никто не пушит напрямую, и CI, охватывающим build, vet, gofmt и тесты с race-детектором перед слиянием; используются только squash-слияния и автоматическое удаление веток. Версионирование следует semver, сейчас проект находится в диапазоне v0.x. До версии v1.0.0 совместимость формата патчей и бандлов на диске не гарантируется; начиная с v1.0.0 любой патч или бандл, записанный в выпущенном формате, останется декодируемым, а будущие несовместимые изменения будут обрабатываться через диспетчеризацию версий и инструменты миграции. Журнал изменений (changelog) фиксирует содержимое каждого релиза.
Comments
0 Rating appears after 10 ratings
Sign in to join the discussion.