Как частично объеденить проекты через cистему управления версиями?
====================================================================

*Сразу отмечу, что я работал только с Mercurial, но думаю советы и от пользователей Git тоже подойдут, так как принцип работы схожий.*  
  

Есть несколько веб проектов (сайты) на общей кодовой базе (самописная cms), которая постоянно обновляется. Уникальные модули и компоненты проектов также постоянно обновляются, всё происходит в разные промежутки времени в зависимости от найденных недочётов и требований проектов.  
  

Проекты имеют папку **engine** с одинаковыми файлами, папку **modules** со смешанными файлами (часть используется во всех проектах, часть уникальна для конкретного проекта и другим они не к чему) и папку **templates**, где есть папка с шаблоном проекта и папка с шаблоном "по умолчанию" - заготовка к новым проектам. Все проекты веду только я, в будущем надеюсь подключить помощников, но с таким зоопарком версий это пока сложно, и пока речь не об этом.   
  

Назовём файлы с кодом, который используется во всех проектах - *"общие компоненты"*, а файлы с кодом который в каждом проекте уникален - *"уникальные компоненты"*.  
  

Сейчас у меня на каждом проекте работает свой независимый Mercurial, просто для поддержания истории версий и пуша на удаленный сервер. Для того чтобы перебросить общие компоненты между проектами, делаю сравнение и тупо копирую из папки в папку, что не удобно - **приходится каждый раз вспоминать в каком файле делал обновления с момента прошлого копирования, и не правил ли я в том проекте ещё чего то, о чём забыл, что чревато убийством нужного кода.**  
  
**Что мне нужно:**  

- выделить базу *общих компонентов* из всех проектов, без *уникальных* (разве что шаблон по умолчанию, чтобы было на чём основывать новые проекты).  

- если в одном проекте обновился *уникальный компонент*, то он должен добавится в коммит этого проекта, но не должен повлиять на изменения файлов в других проектах. А если обновился файл из *общих компонентов*, то он должен стать доступным для обновления во всех проектах.  

- возможность обновлять всю кодовую базу общих компонентов на всех проектах сразу.  

- возможность создавать новые проекты, как на основе только общей базы так и какого-либо из проектов, но при этом обновляться дальше только "общими компонентами", а не тянуть за собой изменения в проекте, с короткого склонировались.  
  
**Это тоже надо, но после, тк.как сложно** - отдельным вопросом стоит файл текстовой базы, они должны частично обновляться но при этом сохранять свою уникальность. Пример. Есть фал texsts.php, общий и уникальный одновременно: переменная *maintext* содержит в себе приветственный текст, который на каждом сайте уникален, но для новых проектов есть заготовка. И тут я захотел добавить новую переменную *hellotext*, которая будет использоваться в новой возможности общей компоненты (для вывода приветственного сообщения пользователю), но ранее этой переменной не было совсем. Она должна появиться во всех проектах, но при не затереть переменную *maintext*.  

- а ещё надо обновлять структуру базы данных без самих данный, даже не знаю как такое сделать  
**Как я себе это вижу:**  

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

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

Если что то непонятно описал, пишите я перефразирую. 

 Верно, для одинаковых файлов вам поможет *Subrepository*.  

В Mercurial это очень интересная вещь. Алгоритм работы примерно такой:  

Внутри своего проекта создаёте папку c субрепозиторием, например library, в ней инициализируете (или клонируете готовый) проект общей библиотеки проекта:  

```
$ mkdir library
$ cd library
$ hg init
Указываете, что эта папка является субрепозиторием, для этого в корневом проекте создаётся файл .hgsub и указываете там библиотеку:  
$ echo 'library' >> .hgsub`  

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

Если изменения в library произошли отдельно от проекта (это часто бывает так как проект уникален а library единая для многих проектов) получить свежие обновления без прямого пула (*hg pull*) из самой library не возможно, так как Mercurial не умеет рекурсивно обновлять субрепозиториии. Но для этого можно использовать небольшую хитрость, можно явно указать какие субрепозитории необходимо обновить при обновлении основного репозитория, делается это при помощи секции *[hooks]* в персональный mercurial-настройках проекта (*.hg/hgrc*), например так:  

В файл .hg/hgrc пишем:  

```
[hooks]
preupdate = ./library/hgsubupdate.sh
```
А в файл hgsubupdate.sh:  
`hg pull -R library --update`  

Тогда, при выполнении команды *hg update* в основном репозитории рекурсивно обновятся и субрепозитории указанные в хуках.  

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

 По поводу первого. Как я понял, вам просто надо выделить общий код в отдельный репозиторий. Далее, для локальных проектов, делать просто   

```
hg pull http://bla.com/main/baseproject
git pull http://bla.com/main/baseproject.git
```
  

мержите и работаете дальше.   

Вместо полно пути можно использовать алиасы. Есть и в гите, и в [меркурии](http://www.selenic.com/hg/help/urls)  

Еще один вариант, который лучше - [Subrepository](http://mercurial.selenic.com/wiki/Subrepository). В каждом проекте есть директория с базовым проектом, и в основном коде проекта вы используете общую кодовую базу (подключаете классы, наследуете от какого-либо базового класса и т.д.).  

Выбирайте что больше подходит вам.  
  

По поводу второго вопроса:  

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

Наилучший вариант, это когда есть три файла:  

1. Настройки по-умолчанию в базовом проекте.  

2. Файл настроек проекта.  

3. Файл настроек разработчика.  
  

По поводу третьего вопроса:  

Вам необходимы [миграции БД](http://habrahabr.ru/post/121265/). Это вообще отдельный механизм который правильно сделать тяжело, а жить без него трудно. 

 Git submodule 

 Не уверен, насколько будет корректен мой ответ, Вы ведь насчет системы контроля версий спрашивали, но я бы порекомендовал использовать менеджер зависимостей. Если работаете с php, то есть composer, который делает все то, что указали в первом вопросе(не уверен, правда насчет других языков). По остальным двум пунктам -- присоединяюсь к [@maxaon](user/maxaon) 

 Автор, придумал решение? Аналогичный вопрос стоит.