понедельник, 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 не оправдал моих надежд как супер-мега средство, которое сделает мне хорошо. По крайней мере на данный момент проблем от него больше чем пользы.

10 комментариев:

  1. C устройствами, логичнее экспортировать репозиторий в файл. И на самом устройстве хранить только файл.

    С субмодулями, как понимаю, так делать не надо. Лучше править там, где субмодуль является корнем.

    ОтветитьУдалить
    Ответы
    1. Репозиторий и находится в файле. На самом деле там просто криптоконтейнер, в который склонен корневой репозиторий и его субмодули. Но в корневом репе адреса субмодулей прописываются абсолютно, что делает затруднительным их использование в другой точке монтирования.

      Ну и субмодуль в принципе не всегда является корнем. Просто с ними надо быть крайне осторожным и по возможности избегать их использования.

      Удалить
  2. > Репозиторий и находится в файле.
    Я про
    http://diseaz.github.com/gitmagic/ch06.html#_git_3
    > Ну и субмодуль в принципе не всегда является корнем.
    Тогда смысл его иметь как субмодуль? Для удобства и симлинками можно воспользоваться.

    ОтветитьУдалить
    Ответы
    1. Я так понял, git bundle используется просто для переноса проекта на другую машину. Хотя, я не уверен, что он корректно обработает submodule. У меня на устройстве рабочая копия. Там проблема в том, что в файле main_repo/any_submodule/.git лежит строка
      gitdir: /absolute/path/to/submodule

      Соответственно, когда я монтирую 3 своих криптоконтейнера в произвольном порядке, фактический путь меняется, а путь в конфиге - нет. Из-за этого git не может найти submodule.

      Смысл во-первых чтобы логически отделить разные проекты. Но есть и более экзотический случай. Предположим, есть 2 несвязанных проекта A и B и есть небольшой модуль M, который используется обоими. Нужно как-то хранить его рядом с обоими репозиториями и при этом делать так, чтобы при изменении M из проекта A можно было легко без костылей за pull'ить его в проект B. Держать 2 копии модуля S в разных репах и руками мержить - не вариант.

      Удалить
    2. Я к тому, что все хранить на одной машине. Без внешних устройств. А с устройствами возиться только когда нужно синхронизировать, и только через bundle. Т.е. на устройстве хранить только сбандленный файл, а не кусок репа или реп.

      С логическим отделением сталкивался. Ничего лучшего чем создать проект Ц и работать с ним отдельно не придумал.
      + Если еще появятся проекты D и E, которые его юзают, будет удобнее прикреплять Ц к ним.
      Но вообще, не даром для erlang есть rebar, который частично дублирует функционал git относительно подпроектов.

      Удалить
  3. может быть git-subtree окажется более полезным.

    ОтветитьУдалить
    Ответы
    1. Не сталкивался с ним, но как я понял из описания, subtree является частью репозитория и не получится склонить его отдельно. Оно конечно не критично, но иногда эта функция бывает полезна.

      Но да, скорей всего в следующий раз я попробую его, т.к. submodule постоянно вызывает какие-то проблемы :(

      Удалить
  4. Посмотри опции git push, там есть спец опция для субмодулей, чтобы проверять, есть ли там коммит.
    "Вполне законные вопросы: а какого такая ситуация вообще допускается и какого 2 независимых репозитория вдруг стали друг от друга зависеть?"
    не понимаю, репо с субмодулями зависит от репо-источников своих субмодуля. Не ужели у тебя ошибка такая что стало вдруг: от репо с субмодулями зависит репо-источник субмодуля?

    ОтветитьУдалить
    Ответы
    1. > Посмотри опции git push, там есть спец опция для субмодулей, чтобы проверять, есть ли там коммит.

      Да, вопрос только почему она не включается сама в довольно очевидной ситуации.

      > не понимаю, репо с субмодулями зависит от репо-источников своих субмодуля. Не ужели у тебя ошибка такая что стало вдруг: от репо с субмодулями зависит репо-источник субмодуля?

      Зачем им друг от друга зависеть? В обе стороны. Нет, понятно, что в охватывающем репо должна быть возможность скачать репо-субмодуль, но почему сделано так, что действия в субмодуле как-то отражаются в охватывающем репо?

      Удалить
  5. ППКС. Синхрофазотрон, которым предлагается забивать гвозди.

    ОтветитьУдалить