rdc repo diff
rdc repo diff показывает, какие файлы изменились между двумя связанными репозиториями: форком и его родителем, или любыми двумя репозиториями, имеющими общего предка с copy-on-write. Передайте --name <fork> для сравнения форка с родителем, записанным в локальной конфигурации, или добавьте --base <repo> для сравнения с произвольным связанным репозиторием, где --base это базовая (старая) сторона, а --name целевая (новая) сторона. Команда предназначена только для чтения и никогда не расшифровывает образы. Она сравнивает их на уровне блоков на удаленной машине, поэтому стоимость определяется количеством измененных блоков, а не размером репозитория: репозиторий объемом 1 ГБ и репозиторий объемом 100 ГБ с одинаковыми редактированиями обрабатываются за одно и то же время. Если весь репозиторий изменился, количество блоков масштабируется с размером, как и стоимость.
When to use it
Итак: используйте repo diff перед продвижением форка. Агент ИИ разгулялся в форковой копии production-окружения, и вы хотите увидеть ровно какие файлы он трогал перед тем как вливать изменения обратно: repo diff --name <fork> -m <machine> дает вам список файлов за секунды. Буквально секунды. После восстановления при аварийном восстановлении отличите восстановленный форк от снимка, который он должен был воспроизвести, чтобы убедиться, что вернулся ожидаемый набор файлов и ничего больше не отклонилось. Для долгоживущего форка, работающего рядом с родителем неделями, diff показывает накопленное расхождение (редактирование конфига, нарастание логов, миграции схемы) без необходимости монтировать и вручную проходить оба дерева.
Не используйте его между несвязанными репозиториями. Обе стороны должны иметь общего предка с copy-on-write, потому что сравнение работает с общей историей блоков. Это также не инструмент двоичного diff: --content выдает вывод на уровне строк только для текстовых файлов, двоичные файлы выводятся как Binary files differ.
Command reference
Synopsis
rdc repo diff --name <fork> -m <machine> # diff a fork against its parent
rdc repo diff --name <fork> --base <repo> -m <machine> # diff against an arbitrary related repo
Options
| Option | Description | Default |
|---|---|---|
--name <name> | Репозиторий для инспекции (целевая, новая сторона). Обязателен. | required |
--base <name> | Репозиторий для сравнения (базовая, старая сторона). По умолчанию родитель --name, разрешается из локальной конфигурации. | parent of --name |
| (no format flag) | Вывод статуса имен: цветная буква A/M/D/R для каждого измененного файла плюс однострочное резюме. | on |
--name-only | По одному измененному пути на строку, без буквы статуса. Удобно для pipe. | off |
--stat | Величина изменения на файл (дельты байтов и блоков) с итоговым подвалом. | off |
--content <path> | Унифицированный текстовый diff одного файла. Только текст; двоичные файлы выводятся как Binary files differ. | off |
--json | Структурированный вывод для агентов и скриптов. | off |
--fast | Пропустить шаг подтверждения хеша содержимого и доверять фильтру блоков. Быстрее, но может перечислить файлы как Modified. | off |
-m, --machine <name> | Целевая машина. Обязательна. | required |
--debug | Подробная диагностика на stderr. | off |
--skip-router-restart | Пропустить шаг перезагрузки маршрутизатора. | off |
Examples
Default name-status against the parent
С одним только --name форк сравнивается с родителем, записанным в локальной конфигурации. Здесь форк test-1gb:fork1 имеет один измененный файл:
$ rdc repo diff --name test-1gb:fork1 -m hostinger
M hello.txt
1 file changed: 0 added, 1 modified, 0 deleted, 0 renamed
Diffing against an explicit base
Передайте --base для сравнения с произвольным связанным репозиторием. --base это базовая (старая) сторона, --name целевая (новая) сторона:
$ rdc repo diff --name test-1gb:fork1 --base test-1gb:latest -m hostinger
M hello.txt
1 file changed: 0 added, 1 modified, 0 deleted, 0 renamed
Change magnitude with --stat
--stat добавляет дельту байтов и блоков для каждого файла и итоговый подвал:
$ rdc repo diff --name test-1gb:fork1 --stat -m hostinger
hello.txt | +8 bytes, 1 block
1 file changed, 4096 bytes touched
Paths only, piped to a tool
--name-only выводит один путь на строку без буквы статуса, готов к передаче другой команде:
$ rdc repo diff --name test-1gb:fork1 --name-only -m hostinger | xargs -I{} echo "review: {}"
review: hello.txt
Line-level diff of one file
--content выдает унифицированный diff одного текстового файла:
$ rdc repo diff --name test-1gb:fork1 --content hello.txt -m hostinger
--- a/hello.txt
+++ b/hello.txt
@@ -1 +1 @@
-the original line of text in the parent
+the original line of text in the parent, now edited
Filtering JSON with jq
--json выводит структурированный конверт на stdout, так что хорошо передается в jq:
$ rdc repo diff --name test-1gb:fork1 --json -m hostinger | jq '.data.entries[] | select(.status=="M")'
{
"status": "M",
"path": "/hello.txt",
"type": "file",
"old_size": 53,
"size": 61,
"bytes_changed": 4096,
"blocks_changed": 1,
"inode": 13,
"content_changed": true,
"mode_changed": false,
"uid_changed": false,
"gid_changed": false
}
Output formats
Name-status (default)
Каждый измененный файл получает букву статуса и его путь. A добавлен, M изменен, D удален, R переименован (с показанным старым путем). За ним следует строка резюме с подсчетом по категориям.
--name-only
По одному пути на строку, без буквы статуса, без резюме. Используйте, когда следующая команда хочет получить чистый список файлов.
--stat
Каждая строка содержит дельту байтов и дельту блоков файла. Подвал сообщает общее количество файлов и общее количество затронутых байтов. Это показывает, где находится основная часть изменений, а не просто какие файлы переместились.
--content <path>
Стандартный унифицированный diff (---/+++ заголовки, @@ куски) для одного текстового файла. Двоичные файлы выводятся как Binary files differ и не содержат кусков.
--json
Полный структурированный результат. Данные выводятся на stdout; прогресс и диагностика выводятся на stderr, так что JSON хорошо передается в jq или другой парсер даже во время вывода прогресса.
JSON schema
CLI оборачивает результат renet в стандартный конверт (success, command, data, errors, warnings, metrics). Результат diff находится в data с полями в формате snake_case:
{
"success": true,
"command": "repo diff",
"data": {
"base": "<base-guid>",
"target": "<target-guid>",
"added": 0,
"modified": 1,
"deleted": 0,
"renamed": 0,
"strategy": "shared",
"fast": false,
"degraded": false,
"block_size": 4096,
"total_bytes_changed": 4096,
"entries": [
{
"status": "M",
"path": "/hello.txt",
"type": "file",
"old_size": 53,
"size": 61,
"bytes_changed": 4096,
"blocks_changed": 1,
"inode": 13,
"content_changed": true,
"mode_changed": false,
"uid_changed": false,
"gid_changed": false
}
]
}
}
Каждый объект в entries[] описывает один измененный путь:
| Field | Type | Description |
|---|---|---|
status | A | M | D | R | Добавлен, Изменен, Удален или Переименован. |
path | string | Путь на целевой стороне (или базовой стороне при удалении). |
old_path | string | Предыдущий путь. Присутствует только при переименованиях. |
type | file | dir | symlink | other | Тип записи. |
old_size | number | Размер в байтах на базовой стороне. |
size | number | Размер в байтах на целевой стороне. |
bytes_changed | number | Различающиеся байты, округленные до целых блоков. |
blocks_changed | number | Количество измененных блоков. |
inode | number | Номер inode, используется для обнаружения переименований. |
content_changed | boolean | Изменилось ли содержимое файла (не только метаданные). |
mode_changed | boolean | Изменился ли режим файла. old_mode/new_mode присутствуют, когда true. |
uid_changed | boolean | Изменился ли владелец. old_uid/new_uid присутствуют, когда true. |
gid_changed | boolean | Изменилась ли группа. old_gid/new_gid присутствуют, когда true. |
old_target / new_target | string | Цели симлинков. Присутствуют для измененных симлинков. |
Для полей конверта и правил автоматического обнаружения, которые выводят JSON в не-TTY окружениях, см. JSON Output Reference.
How it works
Репозиторий это файл образа LUKS2 на пуле btrfs, а форк это рефлинк с постоянным временем этого образа. repo diff сравнивает два зашифрованных образа на уровне блоков через FIEMAP, читая только метаданные файловой системы и никогда не расшифровывая ничего. Он сдвигает смещения измененного шифротекста на смещение данных LUKS для получения смещений ext4-устройства, затем преобразует эти смещения обратно в имена файлов через карту экстентов ext4 каждого файла. Финальный проход по идентичности inode обоих монтирований согласовывает результат в записи Added, Modified, Deleted и Renamed. Поскольку работа ограничена количеством измененных блоков, diff независим от размера репозитория, и поскольку он повторно использует живое монтирование на месте, он никогда не нарушает работающий репозиторий. Полный механизм описан в Git diff for encrypted disk images.
Limitations
- Только связанные форки. Обе стороны должны иметь общего предка с copy-on-write. Нет смысла сравнивать на уровне блоков несвязанные репозитории.
- Обнаружение переименований основано на inode. Файл считается переименованным, когда один и тот же inode появляется в новом пути. Удаление с последующим воссозданием (новый inode) выводится как запись Deleted плюс Added, а не переименование.
--contentтолько для текста. Выдает куски на уровне строк для текстовых файлов. Двоичные файлы выводятся какBinary files differ.--fastможет переучитать Modified. Доверяет фильтру блоков и пропускает подтверждение хеша содержимого, поэтому файл, чьи блоки переместились без изменения содержимого, может казаться Modified.- Время прохода по экстентам масштабируется с фрагментацией, а не размером. Сильно фрагментированная файловая система имеет больше экстентов для преобразования, что удлиняет проход даже при малом объеме байтов изменений.
See also
- rdc repo fork. Создайте форк с copy-on-write, который эта команда сравнивает.
- rdc repo status. Текущее состояние одного репозитория.
- rdc repo cat. Прочитайте один файл из репозитория.