Git
Chapters ▾ 2nd Edition

5.2 Распределенный Git - Участие в проекте

Участие в проекте

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

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

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

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

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

Правила создания коммитов

Прежде чем мы начнём рассматривать конкретные варианты использования, давайте вспомним о сообщениях к коммитам. Наличие четких рекомендаций по созданию коммитов и их соблюдение делают работу с Git и взаимодействие с другими гораздо проще. Проект Git предоставляет документ, в котором содержится ряд полезных советов по созданию коммитов для отправки патчей - вы можете ознакомиться с ними, прочитав файл Documentation/SubmittingPatches, находящийся в исходных кодах Git.

Для начала, вам не следует отправлять ненужные пробелы. Git предоставляет простой способ проверки - перед коммитом выполните команду git diff --check, которая выведет список ненужных пробелов.

Вывод команды `git diff --check`.
Рисунок 57. Вывод команды git diff --check.

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

Далее, постарайтесь делать коммит логически разделенного набора изменений. Если возможно, попытайтесь делать ваши изменения легко понятными - не нужно писать код все выходные, работая над пятью разными задачами, а в понедельник отправлять результат как один большой коммит. Даже если вы не делали коммиты на выходных, то в понедельник используйте область подготовленных файлов для того, чтобы разделить проделанную работу по принципу минимум один коммит на задачу, давая полезные коментарии к каждому из них. Если несколько изменений касаются одного файла, используйте git add --patch для частичного добавления файлов в индекс (детально описано в Интерактивное индексирование). Состояние проекта в конце ветки не зависит от количества сделанных вами коммитов, так как все изменения добавятся в один момент, поэтому постарайтесь облегчить задачу вашим коллегам, когда они будут просматривать ваши изменения. Такой подход так же облегчает ивлечение или отмену отдельных изменений, если это вдруг потребуется в будущем. Исправление истории описывает ряд полезных трюков Git для переписывания истории изменений и интерактивного инексирования - используйте эти инструменты для создания чистой и понятной истории перед отправкой проделанной работы кому-то ещё.

Последнее, что нужно иметь ввиду - это сообщение коммита. Привычка создавать качественные сообщения к коммитам позволяет упростить использование и взаимодействие по средствам Git. Как правило, ваши сообщения должны начинаться кратким однострочным описанием не более 50 символов, затем должна идти пустая строка, после которой следует более детальное описание. Проект Git требует, чтобы детальное описание включало вашу мотивацию при внесении изменения и сравнение с текущей реализацией - это хороший пример для подражания. Так же хорошей идеей будет использование фраз в повелительном наклонении настоящего времени. Другими словами - используйте команды. Вместо “Я добавил тесты для” или “Добавление тестов для”, используйте “Добавить тесты для”. Ниже представлен шаблон, написанный Tim Pope:

Краткое (50 символов или меньше) описание изменений

Текст более детального описания, если необходим. Старайтесь
не первышать длинну строки в 72 символа. В некоторых случаях
первая строка подразумевается как тема письма, а всё остальное -
тело письма. Пустая строка, разделяющая сообщение, критически важна
(если существует детальное описание); такие утилиты как rebase
могут запутаться, если вы запустите сразу две.

Последующие параграфы должны отделяться пустыми строками.

  - Списки тоже подходят

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

Вам и вашим разработчикам будет гораздо проще, если все сообщения ваших коммитов будут так выглядеть. В проекте Git все сообщения хорошо отформатированы - выполните команду git log --no-merges, чтобы увидеть как выглядит хорошо отформатированная история коммитов.

В последующих примерах, как и практически везде в этой книге, для краткости не используется расширенное форматирование; вместо этого используется опция -m команды git commit. Делайте как мы говорим, а не так как делаем мы.

Частная небольшая команда

Private Small Team

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

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

# Компьютер Джона
$ git clone john@githost:simplegit.git
Initialized empty Git repository in /home/john/simplegit/.git/
...
$ cd simplegit/
$ vim lib/simplegit.rb
$ git commit -am 'removed invalid default value'
[master 738ee87] removed invalid default value
 1 files changed, 1 insertions(+), 1 deletions(-)

Второй разработчик Джессика делает тоже самое - клонирует репозиторий и делает коммит:

# Компьютер Джессики
$ git clone jessica@githost:simplegit.git
Initialized empty Git repository in /home/jessica/simplegit/.git/
...
$ cd simplegit/
$ vim TODO
$ git commit -am 'add reset task'
[master fbff5bc] add reset task
 1 files changed, 1 insertions(+), 0 deletions(-)

Затем Джессика отправляет изменения на сервер:

# Компьютер Джессики
$ git push origin master
...
To jessica@githost:simplegit.git
   1edee6b..fbff5bc  master -> master

Джон так же пытается отправить свои изменения:

# Компьютер Джона
$ git push origin master
To john@githost:simplegit.git
 ! [rejected]        master -> master (non-fast forward)
error: failed to push some refs to 'john@githost:simplegit.git'

Джону запрещено отправлять изменения, так как Джессика уже отправила свои. Это особенно важно для понимания, особенно если вы привыкли к Subversion, потому что, как вы могли заметить, разработчики не редактировали один и тот же файл. Если Subversion автоматически делает слияние на сервере при условии, что редактировались разные файлы, то в Git вы должны слить изменения локально. Джон должен получить изменения Джессики и слить их локально, прежде чем сможет отправить свои:

$ git fetch origin
...
From john@githost:simplegit
 + 049d078...fbff5bc master     -> origin/master

В этот момент локальный репозиторий Джона выглядит примерно так:

Расходящаяся история Джона.
Рисунок 58. Расходящаяся история Джона.

У Джона есть ссылка на отправленные Джессикой изменения, но он должен применить их к своей работе прежде, чем сможет отправить свои:

$ git merge origin/master
Merge made by recursive.
 TODO |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

Процесс слияния проходит гладко - история коммитов у Джона выглядит примерно так:

Репозиторий Джона после слияния с `origin/master`.
Рисунок 59. Репозиторий Джона после слияния с origin/master.

Теперь Джон может протестировать свой код, чтобы убедиться в корректной работе своих изменений, после чего он может отправить объединенную работу на сервер:

$ git push origin master
...
To john@githost:simplegit.git
   fbff5bc..72bbc59  master -> master

В результате история коммитов у Джона выглядит так:

История коммитов у Джона после отправки на `origin` сервер.
Рисунок 60. История коммитов у Джона после отправки на origin сервер.

Тем временем Джессика продолжала работать в тематической ветке под названием issue54 и сделала три коммита в ней. Она ещё не получила изменения Джона, поэтому история коммитов у неё выглядит следующим образом:

Тематическая ветка Джессики.
Рисунок 61. Тематическая ветка Джессики.

Для синхронизации с Джоном Джессика получает изменения:

# Компьютер Джессики
$ git fetch origin
...
From jessica@githost:simplegit
   fbff5bc..72bbc59  master     -> origin/master

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

История коммитов Джессики после получения изменений Джона.
Рисунок 62. История коммитов Джессики после получения изменений Джона.

Джессика считает, что её тематическая ветка готова, но так же хочет знать какие изменения следует слить со своей работой перед отправкой на сервер. Для прояснения ситуации он выполняет команду git log:

$ git log --no-merges issue54..origin/master
commit 738ee872852dfaa9d6634e0dea7a324040193016
Author: John Smith <jsmith@example.com>
Date:   Fri May 29 16:01:27 2009 -0700

   removed invalid default value

issue54..origin/master - это синтаксис фильтра, который указывает Git отображать только список коммитов, которые существуют в последней ветке (в данном случае origin/master), но отсутствуют в первой (в данном случае issue54). Более детально этот синтаксис рассматривается в главе Диапазоны коммитов.

В данном случае, в выводе команды мы видим только один коммит, сделанный Джоном и ещё не слитый Джессикой. Если она сольёт origin/master, то это будет единственный коммит, который изменит локальное состояние.

Теперь, Джессика может слить изменения тематической ветки и изменения Джона (origin/master) в свою локальную ветку master, а затем отправить её на сервер. Для начала, следует переключиться на ветку master и слить изменения тематической ветки:

$ git checkout master
Switched to branch 'master'
Your branch is behind 'origin/master' by 2 commits, and can be fast-forwarded.

Обе ветки origin/master и issue54 являются отслеживаемыми, поэтому порядок слияния не важен. Конечный результат будет идентичным вне зависимости от порядка слияния, однако история коммитов будет немного отличаться. Джессика решает слить ветку issue54 первой:

$ git merge issue54
Updating fbff5bc..4af4298
Fast forward
 README           |    1 +
 lib/simplegit.rb |    6 +++++-
 2 files changed, 6 insertions(+), 1 deletions(-)

Проблем не возникает; как можно заметить, это простое перемещение вперед. Теперь Джессика сливает изменения Джона (origin/master):

$ git merge origin/master
Auto-merging lib/simplegit.rb
Merge made by recursive.
 lib/simplegit.rb |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

Слияние прошло чисто и теперь история коммитов у Джессики выглядит следующим образом:

История коммитов Джессики после слияния изменений Джона.
Рисунок 63. История коммитов Джессики после слияния изменений Джона.

Теперь Джессика может отправить свою ветку master в origin/master, при условии что Джон больше не отправлял изменений:

$ git push origin master
...
To jessica@githost:simplegit.git
   72bbc59..8059c15  master -> master

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

История коммитов Джессики после отправки на сервер.
Рисунок 64. История коммитов Джессики после отправки на сервер.

Это один из самых простых рабочих процессов. В течение некоторого времени вы работаете в тематической ветке, а затем сливаете изменения в ветку master когда всё готово. Чтобы поделиться проделанной работой, вы сливаете её в вашу ветку master, затем получаете и сливаете изменения из ветки origin/master если таковые имеются, и наконец, отправляете все изменения в ветку master на сервере. В общем виде последовательность выглядит так:

Общий вид последовательности событий в рабочем процессе для нескольких разработчиков.
Рисунок 65. Общий вид последовательности событий в рабочем процессе для нескольких разработчиков.

Частная управляемая команда

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

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

Давайте рассмотрим рабочий процесс Джессики, так как она работает над двумя функциями, параллельно сотрудничая с разными разработчиками. Предположим, что репозиторий уже склонирован и она решает работать сначала над функцией featureA. Джессика создаёт новую ветку для этой функции и некоторое время работает над ней:

# Jessica's Machine
$ git checkout -b featureA
Switched to a new branch 'featureA'
$ vim lib/simplegit.rb
$ git commit -am 'add limit to log function'
[featureA 3300904] add limit to log function
 1 files changed, 1 insertions(+), 1 deletions(-)

В данный момент ей необходимо поделиться проделанной работой с Джоном, поэтому Джессика отправляет ветку featureA на сервер. У Джессики нет доступа на запись в ветку master (он есть только у интеграторов), поэтому для совместной работы с Джоном она отправляет изменения в другую ветку:

$ git push -u origin featureA
...
To jessica@githost:simplegit.git
 * [new branch]      featureA -> featureA

Джессика отправляет письмо Джону с уведомлением, что внесенные ей изменения уже доступны в ветке featureA. Пока Джессика ждёт ответа от Джона, она решает поработать на другой функцией featureB вместе с Джози. Для начала, Джесика создаёт новую тематическую ветку, базируясь на состоянии ветки master на сервере:

# Компьтер Джессики
$ git fetch origin
$ git checkout -b featureB origin/master
Switched to a new branch 'featureB'

После этого, Джессика делает несколько коммитов в ветке featureB:

$ vim lib/simplegit.rb
$ git commit -am 'made the ls-tree function recursive'
[featureB e5b0fdc] made the ls-tree function recursive
 1 files changed, 1 insertions(+), 1 deletions(-)
$ vim lib/simplegit.rb
$ git commit -am 'add ls-files'
[featureB 8512791] add ls-files
 1 files changed, 5 insertions(+), 0 deletions(-)

Репозиторий Джессики выглядит следующим образом:

Начальное состояние истории коммитов Джессики.
Рисунок 66. Начальное состояние истории коммитов Джессики.

Джессика готова отправить свою работу, но получает письмо Джози, что начальная работа уже отправлена на сервер в ветку featureBee. Теперь Джессике нужно слить изменения перед отправкой на сервер. Эти изменения она получает командой git fetch:

$ git fetch origin
...
From jessica@githost:simplegit
 * [new branch]      featureBee -> origin/featureBee

Теперь Джессика может слить полученные изменения со своими при помощи команды git merge:

$ git merge origin/featureBee
Auto-merging lib/simplegit.rb
Merge made by recursive.
 lib/simplegit.rb |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

Одна небольшая проблема - ей нужно отправить слитые изменения из локальной ветки featureB в ветку featureBee на сервере. Для этого в комаде git push Джессика указывает названия локальной и удаленной веток, разделенных двоеточием:

$ git push -u origin featureB:featureBee
...
To jessica@githost:simplegit.git
   fba9af8..cd685d1  featureB -> featureBee

Это называется спецификация ссылок. В главе Спецификации ссылок приведено более детальное описание спецификаций ссылок Git и различные способы их использования. Так же обратите внимание на флаг -u; это сокращение для --set-upstream, который настраивает ветки для упрощения отправки и получения изменений в дальнейшем.

После этого, Джессика получает письмо от Джона, в котором он сообщает, что отправил некоторые изменения в ветку featureA и просит их проверить. Джесика выполняет команду git fetch для получения этих изменений:

$ git fetch origin
...
From jessica@githost:simplegit
   3300904..aad881d  featureA   -> origin/featureA

Теперь, она может посмотреть что именно было изменено командой git log:

$ git log featureA..origin/featureA
commit aad881d154acdaeb2b6b18ea0e827ed8a6d671e6
Author: John Smith <jsmith@example.com>
Date:   Fri May 29 19:57:33 2009 -0700

    changed log output to 30 from 25

Наконец, Джессика сливает изменения Джона в свою ветку featureA:

$ git checkout featureA
Switched to branch 'featureA'
$ git merge origin/featureA
Updating 3300904..aad881d
Fast forward
 lib/simplegit.rb |   10 +++++++++-
1 files changed, 9 insertions(+), 1 deletions(-)

Джессика решает немного подправить, делает коммит и снова отправляет изменения на сервер:

$ git commit -am 'small tweak'
[featureA 774b3ed] small tweak
 1 files changed, 1 insertions(+), 1 deletions(-)
$ git push
...
To jessica@githost:simplegit.git
   3300904..774b3ed  featureA -> featureA

История коммитов у Джессики сейчас выглядит так:

История коммитов Джессики после изменений в тематической ветке.
Рисунок 67. История коммитов Джессики после изменений в тематической ветке.

Джессика, Джози и Джон информируют интеграторов, что ветки featureA и featureBee на сервере готовы к слиянию в основную. После того как интеграторы сольют эти ветки в основную, полученные изменения будут содержать коммит слияния, а история коммитов будет иметь вид:

История коммитов Джессики после слияния тематических веток.
Рисунок 68. История коммитов Джессики после слияния тематических веток.

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

Основная последовательность описанного рабочего процесса управляемой команды.
Рисунок 69. Основная последовательность описанного рабочего процесса управляемой команды.

Форк публичного проекта

Участие в публичном проекте сильно отличается. Так как у вас нет доступа обновлять ветки пероекта напрямую, то передавать проделанную работу следует другим способом. В первом примере рассматривается участие в публичном проекте по средствам форка на Git платформах, где возможно его простое создание. Много Git хостинг сайтов поддерживают такую функцию (включая GitHub, BitBucket, Google Code, repo.or.cz и другие), как и большинство тех, кто поддерживате проекты, ожидают такой стиль участия.

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

$ git clone (url)
$ cd project
$ git checkout -b featureA
# (work)
$ git commit
# (work)
$ git commit

Если вы захотите использовать rebase -i для схлопывания коммитов в единый или для перестраивания коммитов, чтобы облегчить модерирование ваших патчей – воспользуйтесь разделом Исправление истории для получения детальной информации об интерактивном перебазировании.

Когда работа в тематической ветке завершена и вы готовы передать изменения исходному проекту, перейдите на страницу исходного проекта и нажмите кнопку “Fork”, тем самым создавая доступный для записи форк проекта. Затем нужно добавить URL на созданный проект как второй удаленный репозиторий, в нашем случае с именем myfork:

$ git remote add myfork (url)

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

$ git push -u myfork featureA

Когда ваши изменения отправлены в ваш форк, следует уведомить об этом сопровождающего проекта. Обычно, это называется запросом слияния, который вы можете создать используя веб сайт - GitHub использует собственный механизм запросов слияния, который будет рассмотрен в разделе GitHub - или используя команду git request-pull отправить её вывод по почте.

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

$ git request-pull origin/master myfork
The following changes since commit 1edee6b1d61823a2de3b09c160d7080b8d1b3a40:
  John Smith (1):
        added a new function

are available in the git repository at:

  git://githost/simplegit.git featureA

Jessica Smith (2):
      add limit to log function
      change log output to 30 from 25

 lib/simplegit.rb |   10 +++++++++-
 1 files changed, 9 insertions(+), 1 deletions(-)

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

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

$ git checkout -b featureB origin/master
# (work)
$ git commit
$ git push myfork featureB
# (email maintainer)
$ git fetch origin

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

История коммитов в начале работы над `featureB`.
Рисунок 70. История коммитов в начале работы над featureB.

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

$ git checkout featureA
$ git rebase origin/master
$ git push -f myfork featureA

Эти действия перепишут историю ваших коммитов, которая станет похожа на История коммитов после работы над featureA..

История коммитов после работы над `featureA`.
Рисунок 71. История коммитов после работы над featureA.

Так как вы перебазировали ветку, то должны указать флаг -f во время отправки на сервер, чтобы переписать историю ветки featureA коммитами, не являющимися её потомками. Альтернативным решением может быть отправка этих исправлений в ветку с другим названием (например, featureAv2).

Давайте рассмотрим ещё один возможный сценарий: сопровождающий посмотрел вашу вторую ветку и ему понравилась идея, но он хочет попросить вас изменить некоторые детали. Возможо, вы так же захотите перебазировать эту ветку относительно текущего состояния ветки master. Вы создаёте новую ветку базируясь на текущей origin/master, сбрасываете все изменения в неё, разрашаете возможные конфликты, делаете изменения в реализации и отправляете её как новую ветку:

$ git checkout -b featureBv2 origin/master
$ git merge --no-commit --squash featureB
# (change implementation)
$ git commit
$ git push myfork featureBv2

Опция --squash берет все изменения из указанной ветки, объединяет их и создаёт новый коммит в текущей ветке без создания коммита слияния. Опция --no-commit указывает Git не создавать новый коммит автоматически. Это возволит взять изменения из другой ветки, внести дополнительные изменения и только потом создать новый коммит.

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

История коммитов после работы над `featureBv2`.
Рисунок 72. История коммитов после работы над featureBv2.

Публичный проект по средствам E-Mail

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

Рабочий процесс схожий - вы создаёте тематическую ветку для каждого набора патчей, над которыми собираетесь работать. Основное отличие - это способ их передачи проекту. Вместо того, чтобы создать форк и отправить в него свои изменения, вы генерируете e-mail версию для каждого набора коммитов и отправляете её в почтовую рассылку разработчиков:

$ git checkout -b topicA
# (work)
$ git commit
# (work)
$ git commit

Сейчас у вас два коммита, которые вы хотите отправить в почтовую рассылку. Используйте команду git format-patch для генерации файлов в формате mbox, которые можно отправить по почте - это обернет каждый коммит в e-mail сообщение, где первая строка из сообщение коммита будет темой письма, а остальные строки плюс сам патч будут телом письма. Применение патча в формате e-mail, сгенерированного с помощью команды format-patch, сохраняет всю информацию о коммите должным образом.

$ git format-patch -M origin/master
0001-add-limit-to-log-function.patch
0002-changed-log-output-to-30-from-25.patch

Команда format-patch выводит список имен файлов патчей, которые она создаёт. Флаг -M указывает Git искать переименования. В итоге файлы выглядят вот так:

$ cat 0001-add-limit-to-log-function.patch
From 330090432754092d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00:00 2001
From: Jessica Smith <jessica@example.com>
Date: Sun, 6 Apr 2008 10:17:23 -0700
Subject: [PATCH 1/2] add limit to log function

Limit log functionality to the first 20

---
 lib/simplegit.rb |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/lib/simplegit.rb b/lib/simplegit.rb
index 76f47bc..f9815f1 100644
--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -14,7 +14,7 @@ class SimpleGit
   end

   def log(treeish = 'master')
-    command("git log #{treeish}")
+    command("git log -n 20 #{treeish}")
   end

   def ls_tree(treeish = 'master')
--
2.1.0

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

Для отправки в список рассылки можно либо вставить файлы в почтовую программу, либо отправить их командной строки. Вставка текста обычно сопровождается проблемами форматирования, особенно при использовании “умных” клиентов, которые не заботятся о переносе строк и пробелах соответствующим образом. К счастью, Git предоставляет утилиту, которая умеет отправлять корректно отформатированные патчи по протоколу IMAP. Позже мы покажем как отправлять патчи через Gmail, так сложилось что мы знаем этот почтовый агент лучше других; вы можете воспользоваться инструкциями по использованию большого числа почтовых программ в вышеупомянутом файле Documentation/SubmittingPatches из исходных кодов Git.

Для начала, следует настроить IMAP секцию в файле ~/.gitconfig. Каждое отдельное значение можно установить вызовом команды git config, а можно указать вручную сразу в файле. В итоге файл конфигурации должен выглядеть следующим образом:

[imap]
  folder = "[Gmail]/Drafts"
  host = imaps://imap.gmail.com
  user = user@gmail.com
  pass = p4ssw0rd
  port = 993
  sslverify = false

Если ваш сервер IMAP не использует SSL, то последние две строки не обязательны, а значение host должно быть imap:// вместо imaps://. Как только все сделано, воспользуйтесь командой git send-email для помещения ваших патчей в папку Drafts на указанном сервере:

$ git send-email *.patch
0001-added-limit-to-log-function.patch
0002-changed-log-output-to-30-from-25.patch
Who should the emails appear to be from? [Jessica Smith <jessica@example.com>]
Emails will be sent from: Jessica Smith <jessica@example.com>
Who should the emails be sent to? jessica@example.com
Message-ID to be used as In-Reply-To for the first email? y

Во время выполнения команды, Git выводит много отладочной информации для каждого отправляемого патча, которая выглядит примерно так:

(mbox) Adding cc: Jessica Smith <jessica@example.com> from
  \line 'From: Jessica Smith <jessica@example.com>'
OK. Log says:
Sendmail: /usr/sbin/sendmail -i jessica@example.com
From: Jessica Smith <jessica@example.com>
To: jessica@example.com
Subject: [PATCH 1/2] added limit to log function
Date: Sat, 30 May 2009 13:29:15 -0700
Message-Id: <1243715356-61726-1-git-send-email-jessica@example.com>
X-Mailer: git-send-email 1.6.2.rc1.20.g8c5b.dirty
In-Reply-To: <y>
References: <y>

Result: OK

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

Заключение

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