Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
О чем статья
Поделюсь как пришел к выбору git-annex в качестве инструмента управления коллекцией файлов, кратко опишу процесс установки и настройки, расскажу о подводных камнях на разных файловых системах и устройствах: ext4 под linux, sdcardfs на телефоне с андроид, флешка с ntfs на wsl с windows 10.
Если вы выбираете способ управления файлами или просто интересуетесь annex, статья для вас. При этом времени другим технологиям уделено минимум, статья именно про выбранный вариант.
Моя работа с ИТ не связана, поэтому в теме я знаю прикладной минимум или ещё меньше. Статья — скорее пользовательский бег по граблям, чем профессиональный отчёт. При этом найденные грабли помечены флажками, а тропинка утоптана.
Статью будет комфортнее читать при наличии базовых знаний git и ssh.
Дано
Куча данных:
Фотоальбом с видео/фото за 14 лет.
Коллекция контента: книги, музыка.
Скан-копии документов: договоры, счёта, гарантийный талоны.
Рабочие документы: офисные файлы, видеозаписи, markdown-документы.
Все эти 35 000 файлов и 85 Гб данных неравномерно нанесены на 3 ПК, 2 телефона, флешку и внешний HDD (бэкап).
В наличии Базовые знания git, ssh, десктопного linux на уровне пользователя.
Задачи
Синхронизация между устройствами. Одно хранилище в едином состоянии везде.
Возможность частичной загрузки. На работе нужна только папка с рабочими файлами, на телефоне — музыка, а на сервере — всё.
Управление коллекцией с любого устройства: видеть и менять структуру папок, переименовывать, добавлять, изменять и удалять файлы.
Шифрованный бэкап в гугл драйв, так как там докуплено 200 ГБ.
Управление количеством копий: нужно иметь не менее 2.
Управляемое версионирование с возможностью полностью удалять ненужные старые версии.
Поиск решения
Бэкапим и копируем вручную
Это не очень удобно: что-то забыл, что-то задвоил, но оно работало. Но когда два диска на разных компах одновременно начали алертить в SMART, я понял, что, помимо неудобства, есть риск лишиться некоторых файлов полностью.
Rsync
Вещь хорошая. Умеет синхронизировать, сжимать, шифровать.
Но:
Версиями не управляет.
Настройка на каждом из 4 устройств — слишком лениво.
Я довольно долго использовал rsync лишь для бэкапа на флешку. Спустя полгода флешка перешла в режим RO. Выяснилось, что при копировании на fat / exFat с ext4 многие файлы перезаписываются без необходимости. Что-то там не сложилось с метками времени.
Уверен, что вопрос в моём знании инструмента, но я двинулся дальше.
NAS
Была такая мысль. Приобретаем NAS от приличной компании, ставим клиенты на нужные устройства и горя не знаем. Останавливало то, что всё же это не полный контроль. Железо устаревает, обновления прекращаются. А некоторые события с известной компанией на букву «W» и потерянными данными, только подтвердили мою паранойю. Опять же, версионирование и контроль количества копий здесь недоступны (поправьте, если ошибаюсь).
git
Если у тебя в руках только git, все становится похожим на репозиторий... Попробовал организовать коллекцию, опираясь на эту статью.
На ПК — основные репы, на сервере — bare. Вместе с индексом git, вес получается более 130 Гб. Главный плюс:
+ возможность откатиться к любой версии файла.
Но минусов больше:
- регулярно меняются двоичные файлы: офисные документы, изображения, а значит быстро растёт объём. Я стараюсь вести документы в md, но это слабо помогает на фоне двоичного контента.
- я не могу получить на телефон отдельный файл, поработать с ним и запушить обратно, нужно тянуть все 130 Гб. А память она не резиновая, да и «Гиги» не бесконечные в сотовой сети. Вариант с submodule, не полностью решает вопрос и усложняет схему.
- Git операции в моем кейсе медленные, нагрузка на диск — дикая.
В общем не оставляет ощущение, что мы забиваем гвоздь отвёрткой.
svn
+ Возможность частичной загрузки репозитория.
- Нет сервера (дома вырубили инет, свет) — нет возможности сбросить / получить файлы.
- Я не знаю svn.
git lfs
Для моих целей — сплошной минус: надо ставить и настраивать отдельный сервер, где будет храниться содержимое, бэкапы в google disk недоступны. К слову, annex умеет хранить данные на сервере с поддержкой git-lfs.
Всемогущий git annex
Краткая справка
git-annex — система управления файлами без индексации содержимого. С помощью git индексируются только имена, пути файлов и хэш содержимого. Сам контент хранится в недрах .git/annex. Или не хранится, если на конкретном устройстве он не нужен.
Git annex можно использовать для раздельного управления файлами: текстовые храним в git и управляем версиями содержимого, annex управляет двоичными. Всё это можно настроить, в том числе опираясь на тип контента и / или его размер, но в этой статье я сфокусируюсь на полном управлении файлами посредством annex.
На первый взгляд, инструмент решает все задачи и предоставляет ряд приятных бонусов, в том числе, например, возможность хранить дерево файлов без содержимого в закрытой репе github, что предельно упрощает синхронизацию состояния репозитория между устройствами. Здесь есть и веб-приложение в качестве GUI и ассистент для автоматического добавления файлов и много других интересных мелочей.
Установка
Для начала достаточно установить git, и git-annex характерным для вашей системы способом. Например, в Manjaro:
sudo pamac install git git-annex
Для windows git-annex отсутствует, но можно установить его в подходящем дистрибутиве в WSL. В ubuntu 20.2 он есть в репозитории. Установка на телефоне в termux ненамного сложнее:
pkg install wget
wget https://git-annex.branchable.com/install/Android/git-annex-install
source git-annex-install
Для обновления достаточно запустить загруженный скрипт повторно.
Начало использования
Пожалуйста, потренируйтесь на некритичных или тестовых файлах. Хотя git annex дружелюбен и бережно относится к данным, способов отстрелить себе байты здесь достаточно!
Стартовать в моём случае лучше с нового репозитория. Запускать управление файлами с git-annex можно и в действующем хранилище git, но все ранние версии аннексированных файлов останутся в git, что мне не нужно.
Инициализируем новый гит реп, затем annex, потом добавим все файлы в annex, только после этого добавим файлы в git и закоммитим что получилось:
git init
git annex init homepc
git annex add .
add file.x
ok
add file.y
ok
...
git commit -am "git annex first commit"
При инициализации annex репозитория можно не указывать имя (у нас это homepc), это можно сделать позже командой describe.
Теперь все ваши файлы превратились в символьные ссылки, а содержимое отправилось в .git/annex/objects. Annex создал ветку для хранения своей технической информации "git-annex" и ветку промежуточной синхронизации "synced/master". Эти вещи использует сам annex, нам туда ходить не нужно.
Если ваша файловая система не поддерживает ссылки, например exfat или fat32, файлы будут продублированы в .git/annex, а ветка сменится на особую adjusted/unlocked. Пользоваться annex на "crippled filesystem" довольно накладно по занимаемому месту, но не накладнее, чем git, ведь с annex вы можете дропнуть ненужные в этом месте файлы с конкретного устройства.
Git annex, настраиваем и пробуем
Отмечу, что если команда git annex требует путь, а вы его не указали, считается что вы поставили точку вместо пути, указав текущий каталог.
Для начала давайте отредактируем какой-нибудь файл и сохраним его. И сразу, как говорит гугл хром: «Опаньки». По умолчанию, annex блокирует изменение файла, защищая его содержимое: снимает права на запись. Для изменения файла нужно: разблокировать его, изменить, добавить его в annex, ведь содержимое изменилось, заблокировать (по желанию), добавить в git, ведь хэш изменился и закоммитить:
git annex unlock myfile.docx
....edit
git commit -m "Улучшен myfile"
Заметьте, при git commit содержимое добавится в annex, благодаря установленным git annex хукам. В git зафиксируется только изменение ссылки. В данный момент, файл всё еще разблокирован, можно использовать git annex lock myfile.docx
для блокировки.
Можно не коммитить изменения, если осмысленное описание изменений вам не нужно, при синхронизации annex это сам, подставив имя ветки и репозитория в описание. При разблокировании ссылка заменяется обычным файлом. Поскольку содержимое у нас сохранено, если откатиться на коммит назад, git вернет ссылку на старый файл, содержимое которого в annex есть. Мы ничего не потеряли.
Если старые версии содержимого не нужны, запускаем поиск неиспользуемых файлов и удаляем все или часть из них.
git annex unused
1 ...
2 ...
3 ...
git annex dropunused all
Если файл используется в какой-то ветке, он не попадёт в список. При этом annex не позволит удалить единственную копию неиспользуемого файла без ключа --force, защищая ваши данные. Это поведение настраивается изменением параметра mincopies:
# смотрим текущую настройку
git annex mincopies
1
# устанавливаем своё значение
git annex mincopies 2
(recording state in git)
Существует второй параметр: numcopies. Это — желательное количество копий. Можно настроить репозиторий так, чтобы он получал файлы, общего количества копий которых не хватает до желательного.
Если редактировать файлы вам требуется часто, стоит перейти на «сдвинутую» (adjusted) ветку.
git annex adjust --unlock
Annex вернет все ваши файлы на место, заменив ими ссылки и откроет их для редактирования. Теперь вы на ветке "adjusted/master(unlocked)". Такую сдвинутую ветку можно сделать для любой исходной. При синхронизации все автоматом сольется в оригинальную ветку.
Внимательный читатель заметил, что файлы теперь задублированы: они и в рабочем каталоге и в хранилище annex. Если место для вас критично, вы можете заменить их на жёсткие ссылки. Достаточно установить параметр annex.thin в true:
git config annex.thin true
При этом вы берете на себя риски потери оригинального содержимого файла. Такой фокус не пройдёт на fat, но здесь всегда придётся пользоваться сдвинутой веткой, т.к. fat не поддерживает ссылки в принципе.
Git annex умеет создавать другие сдвинутые ветки, например, скрывая файлы, содержимого которых нет в репозитории. Здесь можно посмотреть подробнее.
Пару слов об удалении и перемещении. При удалении файлов, достаточно git add .
, так как содержимое файлов не менялось. Однако, при перемещении / переименовании, уже одним git не обойтись, т.к. ссылки ломаются, важно сделать
git annex add your_new_path
Это чинит ссылки, после чего нужно добавить и зафиксировать всё в git.
Как я писал выше, коммитить можно вручную или просто делать annex sync
, коммит пройдёт автоматически.
Настраиваем второй репозиторий
Давайте добавим второй репозиторий. Я хотел бы забекапить всё на домашний мини-сервер, который будет транслировать медиаконтент по dlna. Удобно: снял видео / фото на телефон, синхронизировал, и можно смотреть все на большом экране.
Соединяться будем по ssh. Предполагается, что настроен доступ по ключу.
Файлы на другом устройстве можно сохранить разными способами:
Список файлов в хэшированной структуре папок (имена и пути создаются по хэшу), "special remote" без истории git.
Простое дерево папок и файлов, обычный вид, "special remote" без истории git.
Bare репозиторий git с историей.
Обычный реп git с историей.
Сейчас нам подходит вариант 4, так как это и бэкап и источник медиа-данных для dlna-сервера.
На втором пк должен быть установлен git и annex.
Делаем на втором пк
mkdir myrepo
cd myrepo
git init
git annex init miniserver
Вы можете использовать git clone вместо первых трёх строк, тогда вы получите весь контент git быстрее. Git annex init делать обязательно, если хотим хранить в репозитории содержимое файлов. Если этого не сделать, annex пометит репозиторий как only git хранилище и файлы туда отправлять откажется.
Так как у нас не bare репозиторий, а пушить мы сюда хотим, настраиваем git:
git config receive.denyCurrentBranch updateInstead
Теперь при push в репозиторий, файлы будут обновляться.
На первом пк* Считаем, что в .ssh/config второй пк настроен как miniserver
git remote add miniserver ssh://miniserver/home/user/myrepo
git annex sync
Annex прочитает хэш-номер второго репа, его описание, после чего синхронизирует все git-ветки. Но данные всё ещё на одном ПК, на втором — битые ссылки. Также отмечу, что в текущей конфигурации минисервер не может сам пушить данные обратно или забирать с homepc. Если мы этого хотим, нужно настроить git receive.denyCurrentBranch на homepc, а затем на miniserver подключить homepc по аналогии с действиями выше.
Теперь отправим на miniserver файлы. На самом деле, annex поддерживает крутую систему групп и настроек wanted content. Можно очень гибко указать какие файлы по весу, количеству копий, типу папок, имени, типу содержимого нужны в каждом репозитории. Есть преднастроенные группы: backup, archive, client, transfer. Но тема обширная, в рамках этой статьи будем все делать вручную, хотя на бою стоит, конечно, ознакомиться с этой информацией здесь. При правильной настройке вы можете одним
git annex sync --content
Скопировать файлы в соответствующие подключенные репозитории и удалить ненужные файлы оттуда.
Чтобы сказать annex, что в репозитории нужны все файлы, присваиваем репозиторию группу backup и тип желательного содержимого standard — использовать дефолтные настройки группы:
git annex group here backup
git annex wanted here standard
# посмотреть / поменять файл настроек для содержимого и репозиториев
git annex vicfg
Ну а мы вернёмся к ручному труду. Чтобы отправить файл на miniserver делаем:
git annex copy myfile -t miniserver
ok
(recording state in git)
Если не указывать имя файла и запустить команду в корне репозитория, скопируются все файлы. Можно не только копировать, но и перемещать (move). Теперь содержимое файла можно дропнуть с первого ПК:
git annex drop myfile
locking myfile on miniserver
ok
(recording state in git)
Annex проверил, что файл действительно есть на miniserver, mincopies у нас 1, а значит можно файл безопасно удалить. Обратите внимание, что annex именно сходил на miniserver и проверил фактическое наличие файла, а не просто глянул в лог. Если сервер недоступен, annex не будет удалять файл без --force. Такой подход мне очень импонирует, так как снижает риски потерь содержимого.
После любой операции с файлами, annex сохраняет лог в своей ветке гит, но только локально. Чтобы все подключенные репозитории знали актуальное состояние содержимого: где лежит, сколько копий, важно сделать
git annex sync
Иначе miniserver сам не будет знать, что у него есть какой-то файл.
Если после sync добавить имя репозитория, синхронизация пройдёт только с ним.
Посмотреть местонахождение файлов:
# конкретный файл
git annex whereis file
# список файлов, которые здесь
git annex list -i here
# список файлов, которые не на miniserver
git annex list --not -i mserver
Настраиваем репозиторий на телефоне
На телефоне всё практически так же, как и в описании выше, кроме нескольких особенностей.
Во-первых, нужно решить где вы будете хранить файлы:
A) В папке termux.
+ ext4. Файлы хранятся правильно, не дублируются.
- открытие файла в другой программе превращается в квест. При использовании termux-open, файл копируется во временную папку или в Download и открывается. Посмотреть фотки, листая в сторонней программе, или открыть папку с музыкой во встроенном плеере не получится. В таком кейсе рекомендую использовать файловый менеджер с возможностью добавления папки termux как хранилища: Material files, Total commander, Mix и т.д. Встроенные в файлменеджер средства просмотра и прослушивания, отчасти компенсируют упомянутый минус.
B) В общедоступном хранилище в памяти телефона или на sd карте.
+ все программы имеют нормальный доступ к файлам.
- на хранилище Android с файловой системой sdcardfs не работают хуки annex для git, что критично.
- sdcardfs не поддерживает ссылки ни в каком виде, загруженные файлы дублируются.
С хуками вопрос можно решить двумя способами:
Перенести папку .git/hooks в домашнюю папку termux и настроить в git:
git config core.hooksPath /data/data/com.termux/files/home/annexhooks
Перенести всю папку .git в домашнюю папку termux, оставив вместо неё в рабочем репозитории текстовый файл .git с адресом:
echo "gitdir: /data/data/com.termux/files/home/mygitfolder" > .git
Во-вторых иногда annex просто повисает при работе на sdcardfs. Если операция работает более 20 минут, а в htop ни один запущенный процесс не тратит ни секунды времени, возможно пора жёстко закрыть termux и перезапустить операцию.
Для повышения скорости операций, регулярно, чаще чем в обычном git репе, стоит делать git gc && git prune --expire=now
Впрочем, это характерно для любого репозитория git annex.
Добавляем бэкап на внешнем носителе
Вариант с добавление репа git очевиден, рассмотрим экспорт дерева файлов без истории git на "crippled fs": exFat.
К примеру, я хочу иметь HDD диск, который супруга может подключить к ТВ или ПК с windows у друзей и посмотреть фоточки.
git annex initremote BigBlueHDD\
type=directory\
directory=/run/media/user/myhdd/OurFiles\
encryption=none\
exportree=yes
Параметр exporttree=yes
говорит annex, что экспортируем мы файлы натуральным деревом с их именами, в противном случае экспорт пройдёт в хешированное дерево.
Параметр encryption позволяет зашифровать файлы, но только при использовании special remotes: если файлы храним не в git репозитории. Чуть подробнее расскажу о данном параметре ниже, при подключении google drive. Здесь указано значение "none": без шифрования.
Есть и параметр importtree, но я крайне не рекомендую его использовать, если диск с "crippled fs". Ниже расскажу почему.
Если теперь вы сделаете
git annex sync --content BigBlueHDD
Файлики полетят на внешний диск. Какие возможны проблемы: annex не может скопировать файлы, имена которых недопустимы в windows: содержащие определённые символы, вроде двоеточия. Также есть баг с копированием на exfat файлов с несколькими точками в конце имени. Баг известен, но пока не исправлен.
Если вы сделаете sync --content в хранилище с настроенными на "yes" importtree и exporttree, получится такая загогулина:
Некоторые файлы могут не скопироваться по приведенным выше причинам.
При импорте, не найдя некоторых файлов на внешнем диске, annex посчитает, что они были удалены и удалит их с основного диска.
Впрочем, если вы проверили, что annex синхронизирует вашу коллекцию без ошибок и имена пишутся нормально, параметр можно включить. Но изменить параметр для уже созданного репозитория не выйдет, нужно добавлять новый.
Чтобы активировать ранее созданное хранилище BigBlueHDD в другом репозитории, просто инициализируем его с указанием пути. Все настройки special remote, кроме пути, сохранены в момент создания, менять их нельзя.
git annex enableremote BigBlueHDD /mnt/hdd
Подводные камни в WSL windows 10
Windows 10 я пользуясь на работе. У нас довольно строгие корпоративные ограничения и у юзеров нет административных прав, что, в целом, верно. К счастью есть возможность, установить и настроить wsl (после трёх заявок и пяти объяснений, что я выпью больше крови эникеев, при установке ffmpeg, optipng, jpegoptim, git и прочих утилит под виндой отдельными заявками, и регулярном их обновлении). Конечно, ни о какой инсайдерской версии Windows речь не идёт, так что монтирование внешних дисков, флешек с ext4 в моей версии wsl недоступно.
В ходе экспериментов с флешкой, выяснилось:
Флешку с fat32, exfat, использовать в annex не получается, при том что в git должно работать. Упирается в permission denied при внутренних операциях с содержимым, в частности с перемещением файлов. Единственный вариант: использование ntfs-флешки.
Для использования флешки с ntfs, нужно монтировать её с параметром metadata, характерным для wsl способом через
гландыdrvfs:sudo mount -t drvfs D: /mnt/d -o metadata
Метод научного тыка привёл меня к следующим выводам:
В самом репозитории на основном диске, стоит в git включить настройку annex.crippledfylesystem=true и перейти на сдвинутую разблокированную ветку, если всё это не произошло автоматически при git annex init.
НЕ следует переключать в true настройку annex.pidlock, это приведёт к знакомым проблемам с permission denied.
В annex в wsl ломается кодировка русских описаний коммитов. Вот в обычном git не ломается, а в annex — да. Возможно, что-то происходит при слиянии adjusted/unlocked ветки с базовой версией. Копать проблему не стал, перешёл на английский в коммитах.
Добавляем облачное хранилище на примере gdrive
Для бэкапа на облачные хранилища, annex использует rclone.
Установите rclone, запустите rclone config и настройте нужное хранилище, перед подключением его в git annex. В целом, с внешними хранилищами, включая gdrive, я.диск, ftp и другие логика одинакова: настраиваем их в rclone и подключаемые в annex.
За этой простой рекомендацией, для gdrive скрывается вполне реализуемый, но затейливый танец с бубном в консоли девелопера в гугл. Процесс описан здесь. Если кратко, вам нужно будет настроить использование google drive API приложением rclone, узнать ID нужной папки, внести секретный ключ и ID папки в настройки rclone, предоставить доступ rclone к вашей учётке. Последнее поможет сделать rclone config, но ключик истекает через пару дней и приходится его обновлять. С ftp всё гораздо проще.
Итак, добавляем хранилище на гугл диске. В примере "gbackup" — это имя облачного хранилища в rclone и, одновременно, имя репа в annex.
git annex initremote gbackup \
type=external \
externaltype=rclone \
target=gbackup \
prefix=repo \
chunk=50MiB \
encryption=shared \
rclone_layout=lower
target
— имя хранилища в rclone,
prefix
— папка внутри целевого каталога, в которой сформируется структура папок,
chunk
— на части какого размера пилить файлы. В примере мы делим файлы на части не более 50 Мбайт, это может упростить загрузку крупных файлов. Также это полезно, если какой-нибудь сервер ограничивает максимальный размер файла.
ecryption
— тип шифрования. Я выбрал "shared": каждый репозиторий с историей git, имеет ключ. О других вариантах вы можете почитать здесь.
rclone_layout
— настройка структуры папок. Я выбрал рекомендуемую: "lower" — до 2 уровней вложенности.
Выводы
Git-annex позволяет решить все обозначенные во вводной части статьи задачи. Для комфортного использования, конечно, стоит почитать walkthrough, попробовать возможности, связанные с ассистентом и webapp. Получать файлы можно по bittorrent, https, в общем, в статье раскрыта только верхушка айсберга.
При первом знакомстве с технологией, я не сразу понял зачем мне использовать git, который не отслеживает содержимое («фен, который не сушит»). На текущий момент, git-annex пользуюсь полгода и именно этот подход кажется мне самым гибким и удобным способом хранения данных.
Буду благодарен за обратную связь и дополнения в комментариях.