Ветвление в стиле Git
Основная идея: Rediacc преобразует форки с копированием при записи в версионирование в стиле git. Каждый неизменяемый форк является коммитом: побайтово стабильным, замороженным образом, который отказывается монтироваться. Ветви являются именованными ссылками, указывающими на коммит. rdc repo checkout клонирует коммит через reflink обратно в перезаписываемый рабочий форк, а rdc repo merge объединяет две линии истории без изменения живого репозитория на месте.
Модель основана на двух хранилищах. Машина является хранилищем объектов: коммиты являются неизменяемыми образами форков, живущими в хранилище данных. Конфигурация CLI является хранилищем ссылок: имена ветвей, текущий HEAD и журнал ссылок хранятся в вашей локальной конфигурации, а не на машине. Это то же разделение, которое git использует между .git/objects и .git/refs.
Когда использовать
Обращайтесь к ветвлению, когда форк заслужил имя. Агент AI поработал в форке production, результат выглядит хорошо, и вы хотите замороженную, именованную контрольную точку, к которой можно вернуться или которую можно продвинуть позже: rdc repo commit замораживает её, rdc repo branch её именует. Перед рискованной миграцией зафиксируйте рабочий форк, чтобы у вас была точная точка отката, гарантированно не изменяющаяся (неизменяемый коммит отказывается монтироваться, поэтому ничто не может в него записывать). Для сравнения двух контрольных точек rdc repo diff работает между любыми двумя коммитами, поскольку они имеют общего предка с копированием при записи. Чтобы перенести проверенную линию работы обратно в целевой форк, rdc repo merge строит результат в клоне через reflink и атомарно подменяет его, поэтому запущенный целевой форк никогда не будет повреждён в процессе слияния.
Не используйте это вместо rdc repo fork, когда вам нужна только одноразовая копия. Простой форк является правильной единицей для эфемерной, поресурсной изоляции. Коммиты добавляют ценность, когда состояние стоит сохранить, назвать или отправить.
Связь коммитов и форков
Репозиторий является одним файлом образа LUKS на пуле btrfs. Форк является reflink-клоном этого образа с постоянным временем, поэтому форк репозитория размером 1 ГБ и репозитория размером 100 ГБ стоит одинаково. Коммит является форком, помеченным как неизменяемый: renet отказывается его монтировать, что сохраняет его образ побайтово стабильным навсегда. Эта побайтовая стабильность делает коммит надёжной точкой отката и детерминированной базой для перекрёстной машинной дельта-передачи.
rdc repo commit записывает сообщение коммита, автора, временную метку и родительский коммит внутри тома (поэтому метаданные путешествуют вместе с образом при push) и также зеркалирует их вне тома (чтобы rdc repo log мог обходить историю без разблокировки чего-либо). Рабочий форк, который вы зафиксировали, продолжает без изменений, точно так же как git оставляет ваше рабочее дерево нетронутым после коммита.
Команды
rdc repo commit
Заморозить смонтированный рабочий форк в новый неизменяемый коммит.
rdc repo commit --name <fork> --message "<message>" -m <machine>
| Параметр | Описание | По умолчанию |
|---|---|---|
--name <name> | Рабочий форк для фиксации. Должен быть смонтирован. Обязательно. | обязательно |
--message <msg> | Сообщение коммита. Обязательно. | обязательно |
--author <author> | Автор коммита, записываемый в метаданных коммита. | не задано |
-m, --machine <name> | Целевая машина. Обязательно. | обязательно |
--debug | Подробная диагностика в stderr. | выкл |
Новый коммит регистрируется в локальной конфигурации с immutable: true, а headCommit рабочего форка продвигается, чтобы указывать на него. Фиксация неизменяемого репозитория отклоняется: сначала извлеките его в перезаписываемый форк.
rdc repo branch
Создать именованную ссылку ветви, указывающую на текущий коммит рабочего форка.
rdc repo branch --branch <name> --name <fork>
| Параметр | Описание | По умолчанию |
|---|---|---|
--branch <branch> | Имя новой ветви. Обязательно. | обязательно |
--name <name> | Рабочий форк, чей текущий коммит будет указывать ветвь. Обязательно. | обязательно |
Это операция только с конфигурацией. На машине ничего не происходит. Ссылка ветви сопоставляет имя с headCommit рабочего форка, поэтому у форка должен быть хотя бы один коммит.
rdc repo checkout
Клонировать неизменяемый коммит (или верхушку ветви) через reflink в свежий перезаписываемый рабочий форк.
rdc repo checkout --ref <commit> --tag <newFork> -m <machine>
rdc repo checkout --ref <branchName> --from <fork> --tag <newFork> -m <machine>
| Параметр | Описание | По умолчанию |
|---|---|---|
--ref <commit|branch> | GUID коммита для извлечения или имя ветви, когда задан --from. Обязательно. | обязательно |
--tag <name> | Имя для нового перезаписываемого рабочего форка. Обязательно. | обязательно |
-m, --machine <name> | Целевая машина. Обязательно. | обязательно |
--from <workingFork> | Разрешить --ref как имя ветви в наборе ветвей этого рабочего форка. | прямой коммит |
--debug | Подробная диагностика в stderr. | выкл |
--skip-router-restart | Пропустить шаг перезапуска маршрутизатора. | выкл |
Извлечение повторно использует путь reflink форка, поэтому оно почти мгновенно и имеет постоянное время независимо от размера репозитория. headCommit нового рабочего форка устанавливается на извлечённый коммит.
rdc repo log
Обходить историю коммитов, достижимую из рабочего форка или коммита.
rdc repo log --name <fork> -m <machine>
| Параметр | Описание | По умолчанию |
|---|---|---|
--name <name> | Рабочий форк или коммит, с которого начинается обход истории. Обязательно. | обязательно |
-m, --machine <name> | Целевая машина. Обязательно. | обязательно |
--json | Вывести историю коммитов в формате JSON. | выкл |
--debug | Подробная диагностика в stderr. | выкл |
log обходит цепочку родителей, записанную rdc repo commit, читая внешнее зеркало состояния, поэтому никакой коммит не разблокируется и не монтируется. Только для чтения.
rdc repo merge
Объединить исходный коммит или форк в целевой рабочий форк без изменения живого целевого форка на месте.
rdc repo merge --name <target> --from <source> -m <machine>
rdc repo merge --name <target> --from <source> --resolve theirs -m <machine>
| Параметр | Описание | По умолчанию |
|---|---|---|
--name <name> | Целевой рабочий форк для слияния. Обязательно. | обязательно |
--from <source> | Исходный коммит или форк для слияния. Обязательно. | обязательно |
-m, --machine <name> | Целевая машина. Обязательно. | обязательно |
--force | Сначала остановить смонтированный или запущенный целевой форк, затем выполнить слияние. Никогда не изменяет живое монтирование. | выкл |
--resolve <ours|theirs> | Трёхстороннее слияние на уровне файлов: применить поофайловые изменения источника к цели, оставляя (ours) или принимая (theirs) версию источника для файлов, изменённых с обеих сторон. Не указывайте для полного принятия источника. | выкл |
--base <guid> | Коммит общего предка для трёхстороннего слияния (используется с --resolve). По умолчанию родительский коммит исходного коммита или текущий коммит цели. | авто |
--debug | Подробная диагностика в stderr. | выкл |
Результат строится в клоне через reflink и атомарно подменяется за crash-safe маркером, поэтому прерванное слияние оставляет исходный целевой форк нетронутым. Смонтированный или запущенный целевой форк отклоняется, если не задан --force, который чисто останавливает цель перед подменой.
Без --resolve слияние является полным принятием источника (цель становится источником). С --resolve это трёхстороннее слияние на уровне файлов относительно записанного родителя исходного коммита: файлы, изменённые только с одной стороны, берутся с этой стороны, а файлы, изменённые с обеих сторон, разрешаются флагом. Конфликтующие пути выводятся.
rdc repo gc
Сборка мусора из неизменяемых объектов коммитов на машине, которые не достигаются никакой ветвью или HEAD.
rdc repo gc -m <machine> # предварительный просмотр (по умолчанию)
rdc repo gc --apply -m <machine> # удалить недостижимые коммиты
| Параметр | Описание | По умолчанию |
|---|---|---|
-m, --machine <name> | Машина для сборки мусора. Обязательно. | обязательно |
--apply | Фактически удалить недостижимые коммиты (иначе предварительный просмотр). | выкл |
--debug | Подробная диагностика в stderr. | выкл |
Достижимость вычисляется из локальной конфигурации (хранилища ссылок): набор коммитов, достижимых при следовании каждой верхушки ветви и HEAD вниз по цепочке родителей. Неизменяемые коммиты на машине вне этого набора недостижимы. Смонтированный объект или рабочий форк никогда не собирается.
rdc repo fsck
Проверить соответствие ссылок конфигурации объектам, присутствующим на машине.
rdc repo fsck -m <machine>
| Параметр | Описание | По умолчанию |
|---|---|---|
-m, --machine <name> | Машина для проверки. Обязательно. | обязательно |
Сообщает о висячих ссылках (верхушка ветви или HEAD, указывающие на GUID без объекта на машине) и осиротевших коммитах (неизменяемый коммит на машине, которого не достигает ни одна ссылка). Только для чтения; освободите осиротевшие объекты с помощью rdc repo gc --apply.
Неизменяемые форки
rdc repo fork --immutable помечает новый форк как доступный только для чтения при создании, создавая базу, эквивалентную коммиту, без отдельного шага commit.
rdc repo fork --parent <name> --tag <tag> --immutable -m <machine>
Неизменяемый форк отказывается монтироваться, что сохраняет его образ побайтово стабильным навсегда. Это полезно как замороженная база для кросс-машинной дельта-передачи, где база должна быть идентичной на обоих концах. Чтобы вносить изменения, извлеките его (или снова разветвите) в перезаписываемую копию.
Примеры
Зафиксировать рабочий форк
$ rdc repo commit --name myapp:work --message "schema migration applied" -m server-1
Committed 4f3c2a1b9d8e: schema migration applied
Зафиксировать с явным автором
$ rdc repo commit --name myapp:work --message "nightly snapshot" --author ci-bot -m server-1
Committed 7a1b2c3d4e5f: nightly snapshot
Назвать ветвь в текущем коммите
$ rdc repo branch --branch staging --name myapp:work
Branch "staging" -> 4f3c2a1b9d8e
Извлечь коммит в свежий перезаписываемый форк
$ rdc repo checkout --ref 4f3c2a1b9d8e --tag rollback-test -m server-1
Извлечь верхушку ветви по имени
С --from значение --ref разрешается как имя ветви на заданном рабочем форке:
$ rdc repo checkout --ref staging --from myapp:work --tag staging-copy -m server-1
Обход истории
$ rdc repo log --name myapp:work -m server-1
commit 4f3c2a1b9d8e
Author: ci-bot Date: 2026-05-29T10:14:02Z
schema migration applied
commit 9d8e7a1b2c3d
Author: ci-bot Date: 2026-05-28T22:01:55Z
initial import
История в формате JSON
--json выдаёт структурированный обход, сначала новые:
$ rdc repo log --name myapp:work --json -m server-1
{
"success": true,
"start": "4f3c2a1b9d8e",
"entries": [
{
"guid": "4f3c2a1b9d8e",
"message": "schema migration applied",
"author": "ci-bot",
"parent": "9d8e7a1b2c3d",
"committed_at": "2026-05-29T10:14:02Z",
"immutable": true
}
]
}
Сравнение двух коммитов
rdc repo diff работает между любыми двумя коммитами, поскольку они имеют общего предка с копированием при записи. Извлеките один коммит, затем сравните его с другим:
$ rdc repo checkout --ref 4f3c2a1b9d8e --tag review -m server-1
$ rdc repo diff --name review --base myapp:work -m server-1
M db/schema.sql
1 file changed: 0 added, 1 modified, 0 deleted, 0 renamed
См. rdc repo diff для полного справочника diff.
Слияние проверенной линии обратно
$ rdc repo merge --name myapp:main --from myapp:work -m server-1
Merged myapp:work into myapp:main
Слияние в запущенный целевой форк
Смонтированный или запущенный целевой форк отклоняется, если не задан --force, который сначала останавливает его:
$ rdc repo merge --name myapp:main --from myapp:work --force -m server-1
Merged myapp:work into myapp:main
Трёхстороннее слияние на уровне файлов
Два форка (feature и hotfix), извлечённые из одного коммита, каждый изменил некоторые файлы. --resolve theirs применяет источник (hotfix) к цели (feature): файлы, изменённые только с одной стороны, берутся с этой стороны, а файлы, изменённые с обеих сторон, разрешаются в пользу источника. База определяется автоматически из общего предка (или укажите её с --base):
$ rdc repo merge --name myapp:feature --from myapp:hotfix --resolve theirs -m server-1
Merged myapp:hotfix into myapp:feature (three-way); 1 conflict(s) resolved --theirs: [config/app.yaml]
config/app.yaml изменился с обеих сторон и был разрешён в пользу источника; файл, добавленный только в hotfix, применяется, а файл, изменённый только в feature, сохраняется. Конфликтующие пути выводятся для проверки.
Создание неизменяемой базы напрямую
$ rdc repo fork --parent myapp --tag baseline-v1 --immutable -m server-1
Дельта-передача push и pull
Неизменяемый побайтово стабильный образ также является основой для блочной дельта-передачи. Когда одна и та же неизменяемая база существует на двух машинах, push или pull может вычислить изменённые блоки относительно этой базы и передать только их, вместо сканирования всего зашифрованного образа. Репозиторий размером 1 ГБ с несколькими изменёнными блоками тогда передаётся в мегабайтах.
Обычно вам не нужно передавать базу вручную. После полного push CLI сохраняет отправленный образ как неизменяемую базу на обеих машинах и записывает её, поэтому следующий push этого репозитория автоматически отправляет только дельту, без флага, даже для форка, который уже существует на цели. (Полный повторный push существующего форка всё ещё требует --force, так как он заменяет весь образ вместо применения проверенной дельты.) Передайте --delta-base <guid> для указания конкретной базы и --strategy <auto|physical|shared> для контроля того, как обнаруживаются изменённые блоки (auto правильно в почти всех случаях).
# Первый push является полной передачей; он также сохраняет повторно используемую базу на обоих концах.
$ rdc repo push --name myapp:work --to-machine backup-1 -m server-1
# После локальных изменений следующий push отправляет только изменённые блоки, без флага.
$ rdc repo push --name myapp:work --to-machine backup-1 -m server-1
# Указать явную базу (неизменяемый коммит, присутствующий на обеих машинах).
$ rdc repo push --name myapp:work --to-machine backup-1 --delta-base 4f3c2a1b9d8e -m server-1
# Дельта также работает в обратном направлении, извлекая только изменённые блоки из машины-источника.
$ rdc repo pull --name myapp:work --from-machine backup-1 --delta-base 4f3c2a1b9d8e -m server-1
# Повторное извлечение существующего локального репозитория (перезапись) с --force.
$ rdc repo pull --name myapp:work --from-machine backup-1 --force -m server-1
Дельта-передача применяется только между машинами (удалённый с базой FIEMAP). Push в облачное объектное хранилище всегда передаёт полный образ. База должна быть побайтово идентичной на обоих концах, что именно гарантирует неизменяемый коммит или форк с --immutable.
Схема JSON
rdc repo log --json оборачивает результат renet в стандартный конверт. Обойдённая история находится в entries, сначала новые:
| Поле | Тип | Описание |
|---|---|---|
success | boolean | Завершился ли обход. |
start | string | GUID, с которого начался обход. |
entries | array | Один объект на коммит, сначала новые. |
entries[].guid | string | GUID коммита. |
entries[].message | string | Сообщение коммита. Отсутствует, если пустое. |
entries[].author | string | Автор коммита. Отсутствует, если пустой. |
entries[].parent | string | GUID родительского коммита. Отсутствует у корня. |
entries[].committed_at | string | Временная метка коммита в формате RFC 3339. Отсутствует, если не задана. |
entries[].immutable | boolean | Помечен ли коммит как доступный только для чтения (всегда true для настоящего коммита). |
Для полей конверта и правил автоопределения, которые выдают JSON в средах не-TTY, см. Справочник JSON-вывода.
Ограничения
- Ссылки являются локальными. Имена ветвей,
HEADи журнал ссылок хранятся в вашей конфигурации CLI, а не на машине. Push коммита на другую машину отправляет объект коммита и его метаданные внутри тома, но ссылка ветви является концепцией на стороне конфигурации. - Коммит отказывается монтироваться. Это и есть суть: неизменяемость делает коммит побайтово стабильным. Чтобы запустить или изменить коммит, сначала извлеките его в перезаписываемый рабочий форк.
- Разрешение слияния выполняется на уровне файлов, а не строк. Поддерживаются как полное принятие источника (без
--resolve), так и трёхстороннее слияние на уровне файлов (--resolve ours|theirs). Трёхстороннее слияние разрешает конфликты целым файлом за раз согласно флагу; оно не создаёт фрагменты на уровне строк или маркеры слияния внутри файла. - История является цепочкой родителей.
rdc repo logобходит единственную ссылкуparent, записанную во время коммита. Он останавливается, когда достигает коммита, метаданные которого отсутствуют на запрашиваемой машине.
Смотрите также
- rdc repo diff. Сравнение на уровне файлов между любыми двумя связанными коммитами или форками.
- Репозитории. Создание, разветвление, монтирование и работа с репозиториями.