Структура репоризитория и проекта
Все C/C++ проекты храняться в едином репозитории, работа которого опирается на систему контроля версий git. Каждый проект представляет собой git-репозиторий (как правило bare).
Для работы над конкретным проектом его необходимо склонировать, а по завершении работы, слить изменения в основной репозиторий. В процессе работы изменения, внесенные другими членами команды, подтягиваются из основного репозитория. В исключительных случаях изменения могут подтягиваться непосредственно от членов команды, но злоупотреблять этим не стоит.
Каждый C/С++ проект, как правило, включает в себя
Исходники (.c, .cpp, .h, .hpp, ...)
Директория src
Инструкции по сборке (файлы Makefile, CMakeLists.txt)
Файлы CMakeLists.txt, Makefile, ... в корне проекта, возможно дополнительные диреткории (например cmake со скриптами, расширяющими функциональность cmake))
Публичные заголовочные файлы (для проектов-библиотек)
Директория include
Документацию
Исходники и скрипты для генерации документации располагаются в директории doc.
Ресурсы
Директория res
Тесты
Директория test
Подпроекты
Существует два способа организации подпроектов средствами git. Первый - с помощью подмодулей. Второй - с помощью ветвлений и удаленных репозиториев. В первом случае информация о подпроектах инкапсулирована в отдельных репозиториях (по 1 на каждый проект), а файлы подпроектов располагаются в специальных поддерикториях суперпроекта. Во втором случае файлы подпроекта являются неотъемлемой частью дерева суперпроекта, а манипуляция подпроектами осуществляется с помощью ветвлений.
Основные различия встают во весь рост при клонировании суперпроекта. В случае с ветвлениями, пользователь, клонирующий суперпроект, может и не подозревать о наличии подпроектов в нём. Выделение подпроекта опционально. При использовании подмодулей клонирующий должен четко понимать, что проект имеет зависимости и аккуратно к ним относится. В качестве "печеньки" клонирующий получает менее нагруженный репозиторий.
Рассмотрим оба способа на примере.
Допустим мы работали над библиотекой core и на определённом этапе работы получили следующее дерево проекта:
.
├── cmake
│ └── nx_lib.cmake
├── doc
│ ├── pages
│ │ ├── index.hpp
│ │ └── readme.md
│ └── doxygen
├── include
│ └── core
│ ├── base.hpp
│ ├── core.hpp
│ └── data.hpp
├── src
│ ├── base.cpp
│ ├── _base_impl.cpp
│ ├── _base_impl.hpp
│ ├── core.cpp
│ └── data.cpp
├── test
│ ├── tests
│ │ ├── tbase.hpp
│ │ ├── tcore.hpp
│ │ └── tdata.hpp
│ └── test.cpp
└── CMakeLists.txt
Далее мы создали проект a, использующий core как библиотеку, причём мы бы хотели включить core в a как подпроект.
Ветвление и удалённые репозитории
Добавляем в проект a новую ветку, содержимое которой - клон проекта core.
git remote add core /repo/core.git
git fetch core
git checkout -b core@rmt core/master
Мы использовали суффикс @rmt, чтобы пометить ветку-подпроект и отличать её от обычных веток. В нашей текущей дериктории находится проект core. Теперь в проекте a, в директорию core допишем файлы проекта core:
git checkout master
git read-tree --prefix=core -u core@rmt
Вот собственно и все. Теперь в суперпроекте a есть директория с подпроектом core. Причем фалы этой директории находятся под версионным контролем проекта а. Если проект core изменился, мы можем влить изменения следующим образом:
git checkout core@rmt
git pull
git checkout master
git merge --squash -s subtree --no-commit core@rmt
Допустим вы ушли в отпуск, а по возвращении обнаружили, что коллега работал над проектом a, не подозревая, что есть подпроект core. В итоге в директории core проекта a появились некоторые изменения, которые вы бы хотели слить в подпроект core. Все что нужно - слить изменения в ветку core@rmt и отправить изменения. Существует одна загвоздка - не существует коммита, который бы указывал на директорию core. Его придется создать низкоуровневой командой commit-tree. Этот "оторванный" коммит в последующем примкнет к истории в ветке core@rmt:
$>echo "core-changes" | git commit-tree HEAD:core
485a320dae49bc6b08c4b5d363d0674b1369b2a6
$>git checkout core@rmt
$>git merge 485a32
$>git push core core@rmt:master
Еще одна частая ситуация - разделяемые подпроекты. Допустим теперь мы работаем над проектом super, который зависит от подпроекта core. Мы уже знаем как действовать...
git remote add core /repo/core.git
git fetch core
git checkout -b core@rmt core/master
git checkout master
git read-tree --prefix=core -u core@rmt
Теперь мы хотели бы добавить подпроект a, но так, чтобы зависимость a от core не дублировалась, но подпроекты a и core сохраняли возможность подтягивать изменения и отправлять. Сначала добавим подпроект a:
git remote add core /repo/core.git
git fetch core
git checkout -b core@rmt core/master
git checkout master
git read-tree --prefix=core -u core@rmt
Пользуясь тем, что в отличии от подмодулей, вся информация о подпроектах инкапсулирована в подветках, а не в файлах, заменяем папку core подпроекта a на симлинк и сообщаем git, чтобы он игнорил данный путь.
git reset a/core/*
git rm -r a/core
ln -s ../core a/core
git add a/core
echo "a/core" >> .gitignore
git add .gitignore
git commit -m"add a as subproject"
Очень важно в ветке a@rmt также заменить директорию core на символическую ссылку. Иначе когда-нибудь случайно поменяете какой-нибудь файл в папке core и не сможете сменить ветку на master, так как имеются изменения, которые нельзя закоммитить так как они игнорятся.
git checkout a@rmt
rm -r core
ln -s ../core core
И напоследок следует упомянуть маленькую, но очень важную деталь. При таком подходе подпроекты не обязаны располагаться в отдельной папке, что позволяет подпроектам быть совсем маленькими. Пусть к примеру имеется очередная реализация строки в C++:
$>ls -la string
.git string.cpp string.hpp
$>cd proj/src
git remote add string /repo/string
git fetch string
git checkout -b string@rmt string/master
git checkout master
git read-tree --prefix= -u string@rmt
Потрясающе! Мы добавили файлы в дерево проекта, но при этом можем управлять ими как подпроектом с помощью ветки string@rmt!
И напоследок вернёмся к нашему super. Все подпроекты в нём - это ветки с суффиксом @rmt:
$>git branch | grep @rmt
a@rmt
core@rmt
Изменения в подпроекте core можно получить так:
git diff-tree -p HEAD:core core@rmt
Изменения в удаленном подпроекте core можно получить так:
git diff-tree -p HEAD:core core/master
Подмодули
TODO:
Comments
comments powered by Disqus