Главная трудность в описании этого процесса состоит в том, что существует большое количество способов организации этого процесса.
Так как Git очень гибок, разработчики могут осуществлять совместную работу по-разному, и сложно описать то, как именно вы должны это делать. Все проекты немного разные. Многое зависит от таких факторов как количество разработчиков, выбранный рабочий процесс, ваши права доступа к репозиториям, и, возможно, способ, которым внешние разработчики вносят свои изменения в проект.
Первый фактор это количество разработчиков. Сколько разработчиков активно вносят свои изменения в проект и как часто? Во многих случаях это два-три разработчика с несколькими коммитами в день, возможно, даже меньше, если это какой-нибудь вялотекущий проект. В по-настоящему больших компаниях или проектах число разработчиков может измеряться тысячами, с десятками или даже сотнями коммитов, поступающих каждый день. Это важно, поскольку с увеличением числа разработчиков вам становится труднее убеждаться в том, что ваш код можно будет чисто применить или легко слить. Изменения, которые вы отправляете, могут оказаться устаревшими или частично неработоспособными, потому что пока вы ожидали их одобрения или применения, были влиты изменения других разработчиков. Как сохранить свой код согласованным, а коммиты применимыми?
Следующий фактор это рабочий процесс, используемый в проекте. Он централизован, и каждый разработчик имеет равные права на запись в основной репозиторий? Есть ли у проекта менеджер, который проверяет все патчи? Все ли патчи проверяются и одобряются? Вы вовлечены в этот процесс? Есть ли у менеджера помощники, и должны ли вы сначала отправлять свою работу им?
Следующий пункт это доступ к фиксации изменений. Рабочий процесс, требуемый для командной работы, может сильно отличаться в зависимости от того, имеете ли вы доступ на запись, или нет. Если у вас нет доступа на запись, то как в проекте принято принимать изменения разработчиков? Вообще, существует ли какая-либо политика? Какой объем работы вы вносите за раз? Как часто вы это делаете?
Все эти вопросы могут повлиять на то, насколько эффективно вы будете вносить свои изменения в общий проект, и какие рабочие процессы доступны вам и предпочтительны для вас. Вы рассмотрите несколько вариантов, от простого к сложному. На основе этих примеров вы сможете сконструировать тот рабочий процесс, который нужен именно вам.
Прежде чем вы приступите к рассмотрению конкретных примеров, познакомьтесь с коротким замечанием о сообщениях коммитов. Хороший стиль создания коммитов значительно облегчает работу с Git и взаимодействие с другими разработчиками.
Во-первых, не стоит отправлять коммиты, в которых сообщение содержит пробелы на концах строк. Такие коммиты раздражают других разработчиков.
Далее, старайтесь делать так, чтобы каждый коммит логически был отдельным набором изменений. Если можете, старайтесь делать ваши изменения «удобоваримыми» и «читабельными»: не стоит писать код все выходные, работая над пятью задачами, а затем отправлять их все в понедельник одним огромным коммитом. Даже если вы не фиксировали никаких изменений в течение выходных, воспользуйтесь в понедельник индексом, чтобы разбить свою работу на части, как минимум по одному коммиту для каждой проблемы с полезным сообщением к каждому.
Последняя вещь, которую стоит иметь в виду, это сообщение коммита. Написание качественных сообщений к коммитам должно войти в привычку, это сильно упростит взаимодействие с использованием Git. По общему правилу, ваше сообщение должно начинаться с одной строки не длиннее 50 символов, лаконично описывающей набор изменений. Затем следует пустая строка, за которой располагается более подробное описание изменений.
В документации Git содержится требование, чтобы детальное объяснение включало в себя причины изменения и описывало изменение прежнего поведения. Это хорошая рекомендация, которой стоит последовать.
Другая хорошая идея это использование повелительного наклонения глаголов в настоящем времени. Другими словами, пишите команды. Вместо «Я добавил тесты для ...» или «Добавление тестов для ...» используйте «Добавить тесты для ...». Вот шаблон, изначально написанный Тимом Поупом (Tim Pope):
Краткое описание (не более 50 символов) с прописной буквы
Более детальный, поясняющий текст, если он требуется. Старайтесь
не превышать длину строки в 72 символа. В некоторых случаях первая
строка используется в качестве темы электронного письма, а всё остальное
как тело письма. Пустая строка, отделяющая сводку от тела, имеет решающее
значение (за исключением случаев, когда детального описания нет);
в противном случае такие инструменты, как rebase, могут вас запутать.
Сообщения коммитов следует писать используя неопределённую форму глагола
совершенного вида повелительного наклонения: «Fix bug» (Исправить ошибку),
а не «Fixed bug» (Исправлена ошибка) или «Fixes bug» (Исправляет ошибку).
Это соглашение соответствует тому, как генерируют коммиты команды
`git merge` и `git revert`.
Абзацы, следующие за поясняющим текстом, идут после пустых строк.
- Можно использовать маркированные списки;
- Обычно элементы списка обозначаются с помощью тире или звёздочки,
за которой следует пробел, а разделяются пустой строкой, но
соглашения могут отличаться;
- Допускается обратный абзацный отступ.
Если все ваши сообщения коммитов будут выглядеть как это, все будет намного проще для вас и для разработчиков, с которыми вы взаимодействуете.
Наиболее простой тип организации, с которой вы легко можете столкнуться это частный проект с одним или двумя другими разработчиками. Под термином частный подразумевается закрытый код, недоступный для чтения остальному миру. Вы и все остальные разработчики имеете право записи в репозиторий.
В этой среде вы можете придерживаться рабочего процесса, похожего на тот, который вы бы использовали в Subversion или другой централизованной системе. Вы по-прежнему получите такие преимущества, как локальные коммиты (коммиты в offline) и возможность гораздо более простого ветвления и слияния, но сам рабочий процесс может оставаться очень похожим. Главное отличие заключается в том, что слияние происходит на стороне клиента, а не на стороне сервера во время фиксации изменений.
Посмотрите, как выглядел бы процесс, когда два разработчика начинают работать вместе с общим репозиторием.
Первый разработчик, Андрей, клонирует репозиторий (Git: Клонировать (Git: Clone)), делает изменения и фиксирует их в своем локальном репозитории — Фиксация (Commit). В результате создается локальный коммит issue-56.
Второй разработчик, Василий, выполняет то же самое: клонирует репозиторий, делает изменения и фиксирует их коммитом issue-126:
Теперь Василий отправляет свою работу в удаленный репозиторий — Отправка (Push).
Вскоре после этого Андрей также пытается отправить свои изменения — Отправка (Push). Андрей не может выполнить отправку изменений, так как за это время Василий уже отправил свои.
Это очень важно понять, особенно если вы привыкли к Subversion, так как вы видите, что эти два разработчика не редактировали один и тот же файл. Хотя в том случае, когда редактировались разные файлы Subversion и выполняет автоматическое слияние на сервере, при использовании Git вы должны сначала слить коммиты локально. Другими словами Андрей должен сначала получить изменения Василия, влить их в свой локальный репозиторий, а затем уже отправить результат в удаленный репозиторий.
В качестве первого шага Андрей выполняет команду Вытягивание (Pull). Слияние прошло без проблем, история коммитов Андрея теперь выглядит как на рисунке:
Теперь Андрей может протестировать свой код, дабы удостовериться, что он по-прежнему работает нормально, а затем отправить свою работу, уже объединенную с работой Василия, в удаленный репозиторий — Отправка (Push).
В результате история коммитов Андрея выглядит так, как показано на рисунке.
Тем временем Василий создал тематическую ветку с названием issue-127 и сделал в ней три коммита. Он еще не получил изменения Андрея, поэтому история коммитов у него выглядит следующим образом.
Василий хочет синхронизировать свою работу с Андреем, так что он извлекает изменения с сервера — Принесение (Fetch).
Эта команда извлекает наработки Андрея, которые он успел выложить. История коммитов Василия теперь выглядит как на рисунке.
Теперь Василий может влить свою тематическую ветку в ветку main, влить работу Андрея (origin/main) в свою ветку main и затем отправить изменения на сервер.
Сначала он переключается на свою основную ветку main, чтобы объединить всю эту работу.
Он может влить сначала ветку origin/main, а может и issue-127 — обе они находятся выше в истории коммитов, так что не важно, какой порядок слияния он выберет. Конечное состояние репозитория должно получиться идентичным независимо от того, какой порядок слияния он выберет, только история коммитов будет немного разная.
Он решает влить ветку issue-127 первой — . ( )
Никаких проблем не возникло. Как видите, это была обычная перемотка вперед.
Теперь Василий вливает работу Андрея (origin/main) — . ( )
Слияние проходит нормально, и теперь история коммитов Василия выглядит так, как показано на рисунке.
Теперь указатель origin/main доступен из ветки main Василия, так что он может спокойно выполнить Отправка (Push) (полагая, что Андрей не отправлял свои изменения за это время). В результате история коммитов Василия выглядит так, как показано на следующем рисунке.
Каждый разработчик несколько раз выполнял коммиты и успешно сливал свою работу с работой другого. Это один из простейших рабочих процессов.
Вы работаете некоторое время, преимущественно в тематической ветке. Когда вы готовы поделиться этой работой с другими, вы вливаете ее в ветку main, извлекаете изменения с сервера и вливаете origin/main (если за это время произошли изменения), и, наконец, отправляете свои изменения в ветку main на сервер. Общая последовательность действий выглядит так, как показано на рисунке.
В этом сценарии вы рассмотрите роли разработчиков в более крупном закрытом проекте. Вы научитесь работе в окружении, где маленькие группы совместно работают над задачами, а затем результаты их деятельности интегрируются отдельным менеджером.
Представьте, что Андрей и Василий работают вместе над одной задачей (назовите ее issue-18), в то время как Василий и третий разработчик, Николай, работают над другой (назовите ее issue-300).
В этом случае компания использует рабочий процесс с менеджером по интеграции, при котором работа частных групп объединяется только определенными менеджерами, и ветка main основного репозитория обновляется только этими менеджерами. В этом случае вся работа выполняется в ветках отдельных команд разработчиков и впоследствии объединяется воедино менеджерами.
Проследите за рабочим процессом Василия, который работает над двумя задачами одновременно, взаимодействуя одновременно с двумя разными разработчиками.
Допустим, что он уже склонировал основной репозиторий. Сначала он решает заняться задачей issue-18. Он создает новую ветку для этой задачи и выполняет в ней некоторую работу.
На этом этапе ему требуется поделиться своей работой с Андреем, так что он отправляет коммиты, выполненные на ветке issue-18, на сервер. Так как Василий не имеет права на изменение ветки main на сервере (только менеджеры могут делать это), он вынужден отправлять свои изменения в другую ветку, issue-18, чтобы обмениваться работой с Андреем.
Василий сообщает по электронной почте Андрею, что он выложил свои наработки в ветку issue-18, и что Андрей может проверить их.
Пока Василий ждет ответа от Андрея, он решает начать работу над веткой issue-300 вместе с Николаем. Для начала он создает новую ветку для этой задачи, используя в качестве основы ветку main на сервере.
Теперь Василий делает пару коммитов в ветке issue-300.
Василий уже готов отправить свою работу на сервер, но получает от Николая сообщение о том, что Николай уже выложил некоторые свои наработки на сервер, но немного ошибся и назвал ветку issue-3.100, вместо того, чтобы назвать ее issue-300.
Теперь Василий должен сначала влить эти изменения в свои, прежде чем он сможет отправить свою работу на сервер. Он может извлечь изменения Николая командой Принесение (Fetch).
Теперь Василий может влить эти изменения в свои наработки командой . ( )
Есть небольшая проблема: ему нужно выложить изменения из своей ветки issue-300 в ветку issue-3.100 на сервере. Он может сделать это, настроив для нее refspec.
Для этого Василий запускает командную строку. Он выполняет команду git push origin
issue-300:issue-3.100
, которая отправит локальную ветку
issue-300 в ветку issue-3.100
удаленного репозитория.
> git push origin issue-300:issue-3.100
Enumerating objects: 12, done.
Counting objects: 100% (11/11), done.
Delta compression using up to 12 threads
Compressing objects: 100% (8/8), done.
Writing objects: 100% (8/8), 924 bytes | 924.00 KiB/s, done.
Total 8 (delta 3), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (3/3), completed with 1 local object.
To https://github.com/1C-EDT-Developer/coldevex
fd0c501..ddf5d7d issue-300 -> issue-3.100
В результате его история коммитов имеет следующий вид.
Обратите внимание, что, как Василий и хотел, его локальная ветка issue-300 была отправлена на сервер в ветку issue-3.100.
Далее, Андрей сообщает Василию по почте, что он добавил некоторые изменения в ветку issue-18, и просит его проверить их. Василий выполняет Принесение (Fetch), чтобы получить внесенные Андреем изменения.
Наконец, он сливает работу Андрея в свою собственную ветку issue-18.
Василий хочет кое-что подправить, так что он опять делает коммит и затем отправляет изменения на сервер.
Василий, Николай и Андрей информируют менеджера, что ветки issue-18 и issue-3.100 на сервере готовы к интеграции в основную ветку разработки. После того, как менеджер вольет эти ветки в основную ветку main, извлечение данных с сервера приведет к появлению нового коммита слияния. История коммитов Василия, например, станет выглядеть следующим образом.
Множество команд разработчиков переходят на Git именно из-за возможности параллельной работы нескольких команд с последующим объединением разных линий разработки. Огромное преимущество Git это возможность маленьких подгрупп большой команды работать вместе через удаленные ветки, не мешая при этом всей команде. Последовательность событий в рассмотренном здесь рабочем процессе представлена на следующем рисунке.
В этом разделе вы рассмотрели ряд общепринятых рабочих процессов, применяемых в разных типах проектов, использующих Git, c которыми вы наверняка столкнетесь. Также были представлены несколько новых инструментов, призванных помочь вам в организации этих процессов. Далее вы рассмотрите, как осуществляется работа с противоположной стороны баррикады — как сопровождать проект, использующий Git.
По материалам книги Pro Git (авторы Scott Chacon и Ben Straub, издательство Apress). Книга распространяется по лицензии Creative Commons Attribution Non Commercial Share Alike 3.0 license.