Git стратегия слияния поддерева или команда поддерева?

Я запускаю новый проект Zend Framework, в котором я буду сотрудничать с дизайнером. Я буду поддерживать этот код проекта с помощью git, и обычно дизайнеры не говорят git (или любой язык программирования), поэтому я хочу облегчить ему задачу, иначе я боюсь, что он не будет использовать git вообще. Мой план состоит в том, чтобы дать ему несколько GUI git, и с ним он должен использовать только базовые функции git, такие как фиксация, различие, выборка, слияние, push и pull.

Я использую gitolite для хранения общей копии нашего репозитория git, и поскольку у него есть расширенная система разрешений, я дам доступ дизайнеру RW только для выделенной ветки (дизайна) и доступа для чтения к другим ветвям.

Чтобы все было просто, я хотел бы поделиться с ним только некоторыми папками в основном проекте (который следует рекомендуемая структура ZF), для которого он действительно нуждается в доступе для выполнения своей работы. В то же время я хочу, чтобы мы оба все еще сливались друг с другом.

Сокращенная структура для его ветки должна быть следующей:

<project name>/
    application/
        layouts/
            scripts/
        views/
            scripts/
    public/
        css/
        images/
        js/

Я знаю, что я мог бы использовать подмодули для этой задачи, но мне будет больно поддерживать, потому что я должен разделить мой проект на (по крайней мере) 4 подрепозитория, он должен иметь доступ только к субрепозиториям, и у него будет 3 репозитория работать с. По этой причине, если это единственное решение, я откажусь от этой идеи.

Некоторые ссылки, которые я уже прочитал, заставляют меня думать, что то, что я прошу, возможно:

Вот мой вопрос:

  • Как создать уменьшенную ветвь design (git checkout -b design и git mv/rm?)
  • Как настроить git отслеживать изменения в ветвях (поэтому я могу git merge design от главной ветки и наоборот)

Обновление:

Я обнаружил еще один возможный подход к проблеме, заданной этими двумя вопросами SO

Я попытался реализовать первый после git rm all-unneeded-stuff в ветки проекта, я делаю фиксацию в главной ветке, которая включает в себя файл в пустых списках и другой файл в черных списках, но git merge терпит неудачу со следующим сообщение

CONFLICT (delete/modify): application/Bootstrap.php deleted in HEAD and modified in master. Version master of application/Bootstrap.php left in tree.

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

Я еще не пробовал второй подход (.gitignore), но если я понял, что подход не соответствует моим потребностям, потому что он будет игнорировать только черные списки в ветке дизайна, но они будут вывезены в ветки проекта, нарушив мои требования.

Я экспериментировал на GitHub

Обновление 2:

Я думаю, что в настоящее время для этого нет решения. При текущей реализации git это просто невозможно.

Я бы хотел, чтобы меня противоречили, но я боюсь, что этого не произойдет.

Ответ 1

Похоже, вы хотите ограничить доступ на чтение для каждого каталога. Это возможно, но единственное решение, о котором я знаю, далеко не просто. Он включает несколько версий одного и того же репозитория на вашем сервере, каждый из которых хранится в синхронизации, используя сложную магию крюка, чтобы отфильтровывать подкаталоги.

Я работаю над реализацией перехватов в свободное время с конечной целью публикации их в качестве программного обеспечения с открытым исходным кодом (возможно, как дополнение к гитолите), но, к сожалению, мое свободное время ограничено.

Хранилища

Общее решение включает в себя как минимум три варианта одного и того же репозитория: один репозиторий полномочий, который координирует два или более репозитория делегатов. Пользователи никогда не клонируют репозиторий полномочий; клонируются только делегированные репозитории.

Делегаты несут ответственность за пересылку входящих коммитов в репозиторий полномочий. Репозиторий полномочий отвечает за фильтрацию входящих коммитов для каждого другого репозитория делегатов. Затем результаты передаются другим делегатам.

Репозиторий полномочий строго не требуется — делегаты могут выполнять фильтрацию самостоятельно, а затем напрямую передавать результаты другим делегатам &mdash, но использование другого репозитория в качестве централизованного координатора значительно упрощает реализацию.

Хранилища делегатов

Каждый репозиторий делегатов содержит подмножество всех данных проекта (например, отфильтрован ноль или более подкаталогов). Все репозитории делегатов идентичны друг другу, за исключением того, что у каждого делегата есть другой набор файлов, отфильтрованных. Все они имеют один и тот же график истории фиксации, но коммиты будут иметь различное содержимое файла и, следовательно, разные идентификаторы SHA1. Они имеют одинаковый набор ветвей и тегов (другими словами, если проект имеет ветвь master, то каждый репозиторий делегатов также имеет ветвь master), но поскольку идентификаторы SHA1 для эквивалентных коммитов отличаются друг от друга, ссылки будут указывать на разные идентификаторы SHA1.

Например, ниже приведены графики содержимого двух репозиториев делегатов. Репозиторий everything.git не имеет ничего отфильтрованного, но в репозитории no-foo.git есть все в подкаталоге foo отфильтровано.

$ cd ~git/repositories/everything.git
$ git log --graph --oneline --decorate --date-order --all
* 2faaad9 (HEAD, master) barbaz
| * c3eb6a9 (release) foobar
* |   8b56913 Merge branch 'release'
|\ \  
| |/  
| * b8f899c qux
* | aad30f1 baz
|/  
* f4acd9f put a new file in subdirectory bar
* 2a15586 put a new file in subdirectory foo

$ cd ~git/repositories/no-foo.git
$ git log --graph --oneline --decorate --date-order --all
* 81c2189 (HEAD, master) barbaz
| * 6bbd85f (release) foobar
* |   c579c4b Merge branch 'release'
|\ \  
| |/  
| * 42c45c7 qux
* | 90ecdc7 baz
|/  
* 4d1cd8d put a new file in subdirectory bar
* 9cc719d put a new file in subdirectory foo

Обратите внимание, что два графика выглядят одинаково, имеют одни и те же сообщения фиксации, одинаковые имена ветвей и т.д. Единственное отличие - это идентификаторы SHA1 из-за того, что содержимое файла отличается.

(Боковое примечание: коммиты также могут быть отфильтрованы, чтобы пользователи другого делегата даже не знали, что сделана фиксация в отфильтрованном каталоге. Однако фиксация может быть отфильтрована только в том случае, если она касается только файлов в отфильтрованный каталог. В противном случае будут конфликты слияния, которые невозможно было бы автоматически разрешить с помощью перехватчиков.)

Репозиторий полномочий

Репозиторий полномочий - это надмножество всех полномочий делегатов. Все объекты фиксации в каждом репозитории делегатов автоматически помещаются в репозиторий полномочий через крючок в каждом репозитории делегатов. Таким образом, если есть два репозитория-делегата, в репозитории полномочий будут два изоморфных группы DAG (по одному от каждого делегата) (при условии, что делегаты не имеют общего корневого коммита).

У репозитория полномочий также будет версия каждой ветки проекта из каждого делегата с префиксом имени делегата. Продолжая приведенный выше пример, репозиторий делегатов everything.git имеет ветвь master, указывающую на commit 2faaad9, а делегат no-foo.git имеет ветвь master, указывающую на фиксацию 81c2189 с фильтрацией, но в противном случае. В этом случае authority.git будет иметь две главные ветки: everything/master, указывающие на 2faaad9 и no-foo/master, указывающие на 81c2189. Следующий график иллюстрирует это.

$ cd ~git/repositories/authority.git
$ git log --graph --oneline --decorate --date-order --all
* 2faaad9 (everything/master) barbaz
| * 81c2189 (no-foo/master) barbaz
| | * c3eb6a9 (everything/release) foobar
| | | * 6bbd85f (no-foo/release) foobar
* | | |   8b56913 Merge branch 'release'
|\ \ \ \  
| | |/ /  
| |/| |   
| | * |   c579c4b Merge branch 'release'
| | |\ \  
| | | |/  
| * | | b8f899c qux
| | | * 42c45c7 qux
* | | | aad30f1 baz
|/ / /  
| * | 90ecdc7 baz
| |/  
* | f4acd9f put a new file in subdirectory bar
| * 4d1cd8d put a new file in subdirectory bar
* | 2a15586 put a new file in subdirectory foo
 /  
* 9cc719d put a new file in subdirectory foo

Обратите внимание, что есть две версии каждой фиксации, по одному для каждого делегата. Также обратите внимание на названия ветвей.

Крючки

Хранилища делегатов

Каждый фид делегата фиксирует репозиторий полномочий.

Когда пользователь обновляет ссылку (через git push) в репозитории делегатов, этот репозиторий update автоматически выполняет git push в репозитории полномочий. Однако вместо использования стандартного push refspec он использует refspec, который заставляет ссылку в репозитории полномочий префикс имени репозитория делегата (например, если репозиторий делегата имеет имя foo.git, то он будет использовать push refspecs, например +refs/heads/master:refs/heads/foo/master и +refs/tags/v1.0:refs/tags/foo/v1.0).

Репозиторий полномочий

Репозиторий полномочий фильтрует входящие коммиты и выталкивает их в другие репозитории делегатов.

Когда репозиторий делегатов перемещается в репозиторий полномочий, полномочия update hook:

  • Проверяет, пытается ли пользователь создать файл в одном из отфильтрованных каталогов. Если это так, он выходит с ошибкой (в противном случае могут возникнуть конфликты слияния, которые не могут быть разрешены автоматически).
  • Возвращает обратно в подкаталоги, которые были первоначально отфильтрованы, чтобы сформировать дерево, которое не отфильтровано.
  • Для каждого другого делегата отфильтруйте нефильтрованное дерево, чтобы сделать эквивалентную фиксацию с удаленным содержимым.
  • Нажмите эквивалентную фиксацию в репозитории делегатов.

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

Ваш случай

В вашем примере у вас есть два репозитория-делегата, например:

  • everything.git (для вас)
  • zend-project.git (для вашего дизайнера)

Филиалы в authority.git будут иметь префикс everything и zend-project, соответствующий двум репозиториям делегатов.

Когда вы нажмете на master в everything.git, произойдет следующее:

  • Крючок update в everything.git будет вызывать входящие коммиты в ветвь everything/master в authority.git.
  • Для каждого входящего коммита крюк update в authority.git будет:
    • Создайте новый древовидный объект, который на 100% идентичен дереву фиксации, но удалит все за пределами подкаталогов application и public.
    • Создайте новый объект фиксации, используя новое дерево и эквивалентные родительские элементы, но повторно используйте оригинальное сообщение фиксации, автора и отметку времени.
    • Обновить zend-project/master, чтобы указать на новую фиксацию.
  • Нажмите zend-project/master в authority.git на master в zend-project.git.

Когда ваш дизайнер нажимает на master в zend-project.git, произойдет следующее:

  • Захват update в zend-project.git будет толкать входящие коммиты в ветвь zend-project/master в authority.git.
  • Для каждого входящего коммита крюк update в authority.git будет:
    • Проверьте, не были ли созданы новые файлы вне подкаталогов application или public. Если это так, верните сообщение с сообщением об ошибке.
    • Создайте новый древовидный объект, который на 100% идентичен дереву фиксации, кроме как с другими подкаталогами из everything/master, привитыми в.
    • Создайте новый объект фиксации, используя новое дерево и эквивалентные родительские элементы, но повторно используйте оригинальное сообщение фиксации, автора и отметку времени.
    • Обновить everything/master, чтобы указать на новую фиксацию.
  • Нажмите everything/master в authority.git до master в everything.git.

Примечания

Вышеописанный способ реализации управления доступом к каждому каталогу. Это должно быть удобно, если вы действительно не хотите, чтобы определенные пользователи могли получить доступ к частям репозитория. В вашем случае удобство для вашего дизайнера может быть более важным, чем ограничение доступа. Если это так, может быть более простой способ выполнить то, что вы хотите.

Надеюсь, я смог объяснить это достаточно ясно.