понедельник, 23 апреля 2012 г.

Git. Submodule. Грабли.

Переход от одного средства разработки к другому - вообще довольно затруднительное занятие, почти всегда сопряжённое с трудностями, ошибками и неприятностями. Так и переход с svn на git. Последний вообще оказался довольно своеобразной системой. И здесь под своеобразием я понимаю не то, что он ведёт себя не как svn, а то, что его поведение не интуитивно.
Много неприятностей доставила система submodule'ей. Submodule - это отдельный репозиторий, который считается дочерним к корневому. Т.е. пусть существует 2 проекта, которые развиваются независимо друг от друга, но один использует другой. Тогда оба будут лежать в разных независимых репозиториях, но один из них для другого будет submodule'ем. Вроде всё логично. Но на практике возникают неожиданные проблемы.

Пример первый. Клоним репозиторий
git colne user@adress
git submodule init
git submodule update


и начинаем работу
cd repo/submodule_1
#...
git commit -m "..."
git push


Вроде всё нормально. До тех пор, пока не происходит попытки перейти на другую ветвь. Оказывается, после инициализации submodule'ей, они находятся в ветке no branch. Теперь получить написанный код можно будет только по хешу коммита. Чтобы такого не происходило, надо после
git submodule update
делать:
git submodule foreach git checkout master

Я ещё могу понять, почему submodule'и клонятся в no branch (хотя это на самом деле тоже не интуитивно, т.к. корневой репозиторий клонится сразу в master), но почему git позволяет делать commit в no branch?

Пример второй. Работали мы себе в submodule'е, делали коммиты. Потом понадобилось срочно поправить корневой реп и запушить его. Сделали. Вроде всё нормально. Но вот что получает человек, захотевший склонить репозиторий себе на другую машину:
Cloning into repo...
remote: Counting objects: 42, done.
remote: Compressing objects: 100% (36/36), done.
remote: Total 42 (delta 6), reused 38 (delta 5)
Receiving objects: 100% (42/42), 597.08 KiB, done.
Resolving deltas: 100% (6/6), done.
fatal: reference is not a tree: ...
Unable to checkout '...' in submodule path 'repo'


Произошло примерно следующее: git узнал, что в submodule'е были коммиты, записал эту информацию на сервер, но в submodule'е не было push. Т.е. информация о коммитах есть, а самих коммитов нет. Вполне законные вопросы: а какого такая ситуация вообще допускается и какого 2 независимых репозитория вдруг стали друг от друга зависеть? Очередная неприятная ситуация с непонятной ошибкой и неочевидными причинами её возникновения.

Пример третий. У меня есть несколько git-репозиториев, хранящихся на монтируемых устройствах. Т.е. когда я работаю с каким-либо проектом, я делаю mount  и работаю в точке монтирования. Но вот, в один прекрасный день я поучил сообщение:
$ git pull
remote: Counting objects: 50914, done.
remote: Compressing objects: 100% (29735/29735), done.
remote: Total 50912 (delta 19352), reused 50368 (delta 18906)
Receiving objects: 100% (50912/50912), 817.64 MiB | 5.95 MiB/s, done.
Resolving deltas: 100% (19352/19352), completed with 1 local objects.
From xxx.xxx.xxx.xxx:/repo
   cc7208e..55dc87e  master     -> origin/master
fatal: Not a git repository: /media/mount_point_1/repo


Оказывается, в файле .git в submodule'е прописывается абсолютный локальный путь до директории этого submodule'я. Но в зависимости от порядка монтирования устройств реальный путь на моей машине меняется. Т.е. я ещё должен помнить, в каком порядке мне подключать устройства с репозиториями. Зачем вообще так делать - я хз, но опять при довольно обычных действиях пользователя система ведёт себя неадекватно.

В общем, git не оправдал моих надежд как супер-мега средство, которое сделает мне хорошо. По крайней мере на данный момент проблем от него больше чем пользы.