Михаил Иванов (m_ivanov) wrote,
Михаил Иванов
m_ivanov

Командная работа в Git

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

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

1. Общий принцип

Рабочий процесс у меня организован следующим образом.

Я ведущий разработчик тире руководитель отдела тире проджект менеджер. У меня в команде есть несколько разработчиков.

Моя работа заключается в следующем:

1) поговорить с заказчиком
2) превратить смутное и путанное пожелание заказчика в четко сформулированную задачу
3) поставить эту задачу разработчику
4) проверить результат выполнения задачи разработчиком
5) неудовлетворительный результат вернуть разработчику на доработку
6) удовлетворительный результат представить заказчику на утверждение или сразу отправить в продакшн
7) утвержденный заказчиком результат отправить в продакшн
8) неутвержденный заказчиком результат вернуть разработчику на доработку

Работа разработчика, соответственно, заключается в следующем:

1) получить от меня задачу
2) выполнить ее
3) отправить мне результат
4) если задача вернулась на доработку - доработать и снова отправить мне

Для управления задачами я использую Trac. Так же в Trac'е ведется документация по проекту, как программерская, так и пользовательская.

Сформулировав задачу, я записываю ее в Trac и назначаю какому-то разработчику. Разработчик, выполнив задачу, переназначает ее мне. Я проверяю результат и, либо снова переназначаю ее разработчику, либо отмечаю как выполненную.

Алгоритм работы с git'ом и общая картина происходящего схематически представлены на картинке:



Теперь от теоретической части перейдем к практической и разберемся, что все это значит и как работает.

2. Git


Установите Git. Прочитайте какой-нибудь мануал. Разберитесь с локальным репозиторием.

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

3. Доступ к репозиторию

При командной работе потребуется обеспечить доступ к репозиторию нескольким пользователям. Для удобного разруливания прав доступа к репозиторию я использую gitosis.

Gitosis - это набор скриптов, реализующих удобное управление git-репозиториями и доступом к ним. Работает это так:

- на сервере заводится пользователь, которому будут принадлежать все репозитории
- все обращения к репозиториям происходят по SSH, под именем этого пользователя, авторизация пользователей производится по ключам
- при входе по SSH автоматически запускаются скрипты gitosis, которые, в зависимости от настройки, разрешают или запрещают дальнейшие действия с репозиториями

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

4. Установка gitosis

Для установки gitosis'а нужно (сюрприз!) вытянуть установочный репозиторий gitosis'а с сервера разработчика gitosis'а:

$ git clone git://eagain.net/gitosis.git

Установить gitosis:

$ cd gitosis
$ su
# python setup.py install

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

Теперь нужно создать в системе пользователя, которому будут принадлежать все репозитории:

$ su
# adduser gituser

А затем инициализировать gitosis в домашнем каталоге созданного пользователя, с открытым ключом того, кто будет администратором gitosis'а. Открытый ключ, понятно, нужно положить куда-то, откуда его сможет прочитать пользователь gituser:

# su gituser
$ cd ~
$ gitosis-init < id_rsa.pub

Обратите внимание - пользователь gituser и администратор gitosis'а - это не одно лицо. Пользователь gituser является просто "хранителем" репозиториев и сам никаких действий вообще никогда не выполняет.

Я в качестве администратора gitosis'а использую другого зарегистрированного в системе пользователя. Но, вообще говоря, администратору вовсе не обязательно быть зарегистрированным в системе пользователем. Главное, указать при инициализации gitosis'а открытый ключ администратора.

После инициализации gitosis'а в домашнем каталоге пользователя gituser появится каталог repositories, в котором и будут храниться все репозитории. Сначала там будет только репозиторий gitosis-admin.git, в котором хранятся настройки самого gitosis'а.

Обратите внимание - по некоторым причинам, связанным с особенностями разных версий Питона, может понадобиться прописать права на исполнение скрипту post-update, находящемуся в репозитории gitosis'а:

$ chmod 755 ~/repositories/gitosis-admin.git/hooks/post-update

На этом установка gitosis'a закончена и начинается настройка.

5. Настройка gitosis'а и создание репозиториев

Настройка gitosis'а заключается в изменении администратором содержимого репозитория gitosis'а.

Становимся администратором gitosis'а (если администратор - зарегистрированный в системе пользователь) или вообще выходим из системы и логинимся там, где администратор зарегистрирован (например, на своем ноутбуке).

Теперь вытягиваем настроечный репозиторий gitosis'а:

$ git clone gituser@githost:gitosis-admin.git

Где githost - это имя сервера, на котором мы установили gitosis (и где будем хранить репозитории).

Обратите внимание - какое бы имя не имел администратор, обращение к серверу всегда выполняется под именем пользователя gituser.

После этого в домашнем каталоге администратора появится каталог gitosis-admin. В этом каталоге нас интересует файл gitosis.conf, именно в нем производится настройка всех репозиториев.

По умолчанию в нем будет нечто такое:

[group gitosis-admin]
writable = gitosis-admin
members = admin@host

Что означает "пользователю admin разрешена запись в репозиторий gitosis-admin".

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

[group project-write]
writable = project
members = superdeveloper

[group project-read]
readonly = project
members = developer1 developer2 developer3 user1 user2

[group dev]
writable = dev
members = superdeveloper developer1 developer2 developer3

где superdeveloper - ведущий разработчик, developer* - разработчики, user* - прочие интересующиеся

Этот конфиг означает следующее:

1) ведущему разработчику позволено писать в главный репозиторий
2) всем позволено читать из главного репозитория
3) ведущему разработчику и всем разработчикам позволено писать в рабочий репозиторий

Все эти пользователи, как и администратор gitosis'а, вовсе не обязательно должны быть зарегистрированными в системе пользователями. Главное, чтобы у них были открытые ключи.

Открытые ключи указанных в конфиге пользователей нужно скопировать в каталог gitosis-admin/keydir. Файлы ключей должны иметь имена вида имя_пользователя.pub. В данном примере это будут имена superdeveloper.pub, developer1.pub, user1.pub и.т.д.

После правки конфига и копирования ключей нужно закоммитить изменения в локальный репозиторий:

$ git add .
$ git commit -am 'Add project Project'

И отправить коммит в центральный репозиторий, где сделанные настройки подхватит gitosis:

$ git push

Все, теперь наш сервер репозиториев настроен и нужные репозитории созданы (вру, репозитории еще не созданы, но будут созданы автоматически при первом коммите).

6. Назначение репозиториев

Если в предыдущем разделе у вас возник вопрос - почему для одного проекта нужно два репозитория - то вот ответ.

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

Я пробовал разные схемы командной работы и наиболее удобной на данный момент мне представляется "двухрепозиторная" схема. Работа в ней ведется примерно следующим образом:

1) я ставлю задачу разработчику
2) разработчик берет актуальную ветку проекта из главного репозитория и делает с нее локальный бранч
3) в этом бранче разработчик решает поставленную задачу
4) бранч с выполненной задачей разработчик отправляет в рабочий репозиторий
5) я беру из рабочего репозитория этот бранч и проверяю его
6) если задача выполнена правильно, я сливаю этот бранч с актуальной веткой проекта в главном репозитории

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

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

Во-вторых, актуальная ветка у меня никогда не бывает в нерабочем состоянии.

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

Поясню на примере.

Допустим, разработчик Р1 сделал правку П1 и отправил ее в ветку master. Я проверил эту правку и нашел ее плохой. Плохую правку нужно переделывать.

Пока я проверял, разработчик Р2 сделал правку П2 и тоже отправил ее в master. Эта правка оказалась хорошей. Хорошую правку нужно отправлять в продакшн.

Но теперь в ветке master находятся две правки, хорошая и плохая. Хорошую правку нужно бы отправить в продакшн, но присутствие плохой правки не позволяет это сделать. Придется ждать, пока плохая правка будет исправлена.

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

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

Для этого разработчики отправляют в репозиторий все свои бранчи по отдельности, не сливая их с master'ом. Сливанием занимаюсь я. Соответственно, в актуальную ветку попадают только проверенные мной правки. Плохие правки отправляются на доработку, а правки, ждущие утверждения заказчика, отправляются в тестовую ветку и просто... ждут. И никому не мешают.

Таким образом, актуальная ветка всегда находится в рабочем состоянии.

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

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

7. Первичная загрузка проекта в репозиторий

Обратите внимание - первичную загрузку делает ведущий разработчик, так как только у него есть право писать в главный репозиторий.

Создаем локальный репозиторий в каталоге проекта и загоняем в него файлы проекта:

$ cd /старый/каталог/проекта
$ git init
$ git add .
$ git commit -am 'poehali!'

Сообщаем локальному репозиторию о том, где находится главный репозиторий:

$ git remote add origin gituser@githost:project.git

Теперь оправляем коммит в главный репозиторий:

$ git push origin master

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

8. Репозиторий ведущего разработчика

Переходим в новый каталог (или вообще на другой комп) и вытягиваем главный репозиторий:

$ cd /новый/каталог/проекта
$ git clone gituser@githost:project.git

Это мы получили копию главного репозитория. В ней находится актуальная ветка. Теперь нужно создать тестовую ветку.

Просто скопируем актуальную ветку в главный репозиторий под именем тестовой:

$ git push origin master:stage

А затем вытянем тестовую ветку из главного репозитория уже под собственным именем:

$ git checkout -b stage origin/stage

Теперь локальный репозиторий содержит две ветки, master и stage. Обе ветки связаны с одноименными ветками в главном репозитории.

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

Кроме того, нужно указать местонахождение рабочего репозитория dev:

$ git remote add dev gituser@githost:dev.git


9. Инфраструктура проекта

Проект у нас включает не только репозитории, но и "исполняющие" узлы - продакшн и отладку.

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

Запуск продакшна, не вдаваясь в детали, был таким:

1) зарегистрировать в системе нового пользователя, из-под которого будет работать продакшн
2) добавить этого пользователя в gitosis (см. раздел 5, в моем примере это user*)
3) клонировать главный репозиторий project в каталог этого пользователя
4) Настроить виртуальный хост Apache (или что там вам больше нравится) на каталог проекта

И теперь для обновления продакшна достаточно просто зайти в этот каталог и выполнить одну-единственную команду:

$ git pull

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

Отладочная копия проекта устроена идентично, за исключением того, что после клонирования главного репозитория было сделано переключение с актуальной ветки master на тестовую ветку stage:

$ git checkout -b stage origin/stage

Теперь pull в отладке будет вытягивать обновления из тестовой ветки.

10. Репозиторий разработчика

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

Главный репозиторий нужно склонировать:

$ git clone gituser@githost:project.git

А рабочий репозиторий просто добавить в конфиг:

$ git remote add dev gituser@githost:dev.git

Эта настройка выполняется только один раз. Вся дальнейшая работа выполняется по стандартной схеме (распечатать памятку и прилепить на монитор).

11. Памятка разработчика

Проверить почту. Если пришло уведомление из Trac'а о новой задаче - начать работать:

$ quake exit

Создать новый бранч на основе актуальной ветки из главного репозитория (мы у себя договорились называть бранчи номерами тикетов из Trac'а):

$ git checkout -b new_branch origin/master

Или вернуться к работе над старым бранчем из рабочего репозитория

$ git checkout -b old_branch dev/old_branch

Вытянуть последние изменения:

$ git pull

---
Выполнить задачу в новом бранче. Тут происходит работа с кодом.
---

Если в процессе работы были созданы новые файлы - добавить их в бранч:

$ git add .

Сохранить изменения в локальном репозитории:

$ git commit -am 'komment'

Отправить новый бранч в рабочий репозиторий:

$ git push dev new_branch

Или повторно отправить туда же старый бранч:

$ git push

Выполненную задачу переназначить в Trac'е ведущему разработчику. Обратно разработчику она вернется либо с указанием на то, что нужно переделать, либо со статусом closed.

10 goto проверить почту;

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

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

$ git branch -D new_branch

Во-вторых, список бранчей в удаленных репозиториях, выдаваемый командой

$ git branch -r

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

$ git remote prune dev

которая удалит из локального кеша отсутствующие в удаленном репозитории бранчи. Обратите внимание - обновлять сведения нужно только о рабочем репозитории. В главном репозитории никаких изменений никогда не происходит, там всегда находятся одни и те же бранчи - master и stage.

12. Действия ведущего разработчика

Получив уведомление из Trac'а о переназначенной мне задаче, я открываю эту задачу и смотрю, про что там вообще было, чтобы знать, что проверять.

Затем я обновляю данные об удаленных репозиториях:

$ git remote update

Теперь мне будут видны новые бранчи в рабочем репозитории, в том числе бранч, соответствующий проверяемой задаче:

$ git branch -r

Затем я вытягиваю проверяемый бранч из рабочего репозитория к себе (поскольку название бранча соответствует номеру тикета, то я всегда точно знаю, какой бранч вытягивать):

$ git checkout -b branch dev/branch

Если это не новый бранч, а исправление старого, то нужно еще вытянуть обновления:

$ git pull

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

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

Если правка простая, то я просто сливаю проверенный бранч с актуальной веткой и отправляю обновленную актуальную ветку в главный репозиторий:

$ git checkout master
$ git merge branch
$ git push

После этого я удаляю бранч у себя локально и из рабочего репозитория, больше этот бранч никому и никогда не понадобится:

$ git branch -D branch
$ git push dev :branch

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

Если же правка требует согласования, то проверяемый бранч сливается не с актуальной веткой, а с тестовой:

$ git checkout stage
$ git merge branch
$ git push

После отправки обновленной тестовой ветки в главный репозиторий я удаляю проверяемый бранч только у себя локально:

$ git branch -D branch

В рабочем же репозитории этот бранч остается. Вполне возможно, что заказчик захочет что-то переделать и тогда этот бранч понадобится разработчику для исправления.

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

После того, как все бранчи слиты с нужными ветками и все это дело отправлено в главный репозиторий, я иду в продакшн и отладку и обновляю их.
Tags: git, gitosis, командная работа, разработка
Subscribe
  • Post a new comment

    Error

    default userpic

    Your IP address will be recorded 

    When you submit the form an invisible reCAPTCHA check will be performed.
    You must follow the Privacy Policy and Google Terms of use.
  • 23 comments