Как работать над одним проектом в github
Перейти к содержимому

Как работать над одним проектом в github

  • автор:

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

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

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

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

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

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

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

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

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

Вывод команды `git diff --check`

Рисунок 56. Вывод команды git diff —check

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

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

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

Последнее, что нужно иметь ввиду — это сообщение коммита. Привычка создавать качественные сообщения к коммитам позволяет упростить использование и взаимодействие посредством Git. Как правило, ваши сообщения должны начинаться кратким однострочным описанием не более 50 символов, затем должна идти пустая строка, после которой следует более детальное описание. Проект Git требует, чтобы детальное описание включало причину внесения изменений и сравнение с текущей реализацией — это хороший пример для подражания. Пишите сообщение коммита в императиве: «Fix bug» а не «Fixed bug» или «Fixes bug». Вот отличный шаблон хорошего сообщения коммита, который мы слегка адаптировали из шаблона, изначально написанного Тимом Поупом:

Краткое (не более 50 символов) резюме с заглавной буквы Более детальный, поясняющий текст, если он требуется. Старайтесь не превышать длину строки в 72 символа. В некоторых случаях первая строка подразумевается как тема письма, а всё остальное -- как тело письма. Пустая строка, отделяющая сводку от тела, имеет решающее значение (за исключением случаев, когда детального описания нет); в противном случае такие инструменты, как rebase, могут вас запутать. Сообщения коммитов следует писать используя неопределённую форму глагола совершенного вида повелительного наклонения: «Fix bug» (Исправить баг), а не «Fixed bug» (Исправлен баг) или «Fixes bug» (Исправляет баг). Это соглашение соответствует сообщениям коммитов, генерируемых такими командами, как `git merge` и `git revert`. Последующие абзацы идут после пустых строк. - Допускаются обозначения пунктов списка - Обычно, элементы списка обозначаются с помощью тире или звёздочки, с одним пробелом перед ними, а разделяются пустой строкой, но соглашения могут отличаться - Допускается обратный абзацный отступ.

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

Примечание
Делайте как мы говорим, а не как делаем сами.

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

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

Небольшая команда

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

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

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

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

# Компьютер Джессики $ git clone jessica@githost:simplegit.git Cloning into 'simplegit'. . $ 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

В последней строке примера приведена полезная информация, выводимая после каждой операции отправки изменений. Её базовый формат такой .. fromref → toref , где oldref — коммит, на который указывала ветка до отправки, newref — новый коммит, на который ветка указывает сейчас, fromref — имя отправленной локальной ветки, toref — имя ветки в удалённом репозитории, в которую были отправлены изменения. Далее вы увидите похожие результаты в выводе команд, поэтому имея общее представление о значении поможет вам лучше понимать различные состояния репозиториев. Дополнительную информацию можно найти в документации к команде git-push.

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

# Компьютер Джона $ 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

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

Расходящаяся история Джона

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

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

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

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

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

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

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

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

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

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

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

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

Тематическая ветка Джессики

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

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

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

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

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

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

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

$ git log --no-merges issue54..origin/master commit 738ee872852dfaa9d6634e0dea7a324040193016 Author: John Smith Date: Fri May 29 16:01:27 2009 -0700 Remove invalid default value

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

В данном случае, в выводе команды мы видим только один коммит, сделанный Джоном и ещё не слитый Джессикой. Если она сольёт 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 the 'recursive' strategy. lib/simplegit.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)

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

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

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

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

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

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

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

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

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

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

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

Команда с руководителем

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

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

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

# Компьютер Джессики $ 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 'Make ls-tree function recursive' [featureB e5b0fdc] Make 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(-)

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

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

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

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

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

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

$ git merge origin/featureBee Auto-merging lib/simplegit.rb Merge made by the 'recursive' strategy. 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

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

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

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

Теперь, она может посмотреть что именно было изменено путём сравнения полученной ветки featureA со своей локальной веткой:

$ git log featureA..origin/featureA commit aad881d154acdaeb2b6b18ea0e827ed8a6d671e6 Author: John Smith Date: Fri May 29 19:57:33 2009 -0700 Increase 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(-)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

$ git clone $ cd project $ git checkout -b featureA . work . $ git commit . work . $ git commit

Примечание

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

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

$ git remote add myfork

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

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

$ git push -u myfork featureA

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

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

$ git request-pull origin/master myfork The following changes since commit 1edee6b1d61823a2de3b09c160d7080b8d1b3a40: Jessica Smith (1): Create new function are available in the git repository at: git://githost/simplegit.git featureA Jessica Smith (2): Add limit to log function Increase 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 $ git request-pull origin/master myfork . email generated request pull to maintainer . $ git fetch origin

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

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

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

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

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

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

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

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

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

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

$ git checkout -b featureBv2 origin/master $ git merge --squash featureB . change implementation . $ git commit $ git push myfork featureBv2

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

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

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

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

Публичный проект посредством 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-increase-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 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 #") + command("git log -n 20 #") 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 = YX]8g76G_2^sFbd port = 993 sslverify = false

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

$ cat *.patch |git imap-send Resolving imap.gmail.com. ok Connecting to [74.125.142.109]:993. ok Logging in. sending 2 messages 100% (2/2) done

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

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

[sendemail] smtpencryption = tls smtpserver = smtp.gmail.com smtpuser = user@gmail.com smtpserverport = 587

Отправить патчи можно командой git send-email :

$ git send-email *.patch 0001-add-limit-to-log-function.patch 0002-increase-log-output-to-30-from-25.patch Who should the emails appear to be from? [Jessica Smith ] Emails will be sent from: Jessica Smith 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 from \line 'From: Jessica Smith ' OK. Log says: Sendmail: /usr/sbin/sendmail -i jessica@example.com From: Jessica Smith To: jessica@example.com Subject: [PATCH 1/2] Add limit to log function Date: Sat, 30 May 2009 13:29:15 -0700 Message-Id: X-Mailer: git-send-email 1.6.2.rc1.20.g8c5b.dirty In-Reply-To: References: Result: OK

Помощь по конфигурации, дополнительные советы и рекомендации, а так же тестовое окружение для отправки патчей по email доступны здесь git-send-email.io.

Заключение

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

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

6.2 GitHub — Внесение собственного вклада в проекты

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

Создание ответвлений (fork)

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

Примечание

Исторически так сложилось, что англоязычный термин «fork» (создание ветвления проекта) имел негативный контекстный смысл, данный термин означал, что кто-то повёл или ведёт проект с открытым исходным кодом в другом, отличном от оригинала, направлении, иногда данный термин так же означал создание конкурирующего проекта с раздельными авторами. В контексте GitHub, «fork» (создание ветвления проекта) просто означает создание ветвления проекта в собственном пространстве имён, что позволяет вносить публичные изменения и делать свой собственный вклад в более открытом виде.

Таким образом, проекты не обеспокоены тем, чтобы пользователи, которые хотели бы выступать в роли соавторов, имели право на внесение изменений путём их отправки (push). Люди просто могут создавать свои собственные ветвления (fork), вносить туда изменения, а затем отправлять свои внесённые изменения в оригинальный репозиторий проекта путём создания запроса на принятие изменений (Pull Request), сами же запросы на принятие изменений (Pull Request) будут описаны далее. Запрос на принятие изменений (Pull Request) откроет новую ветвь с обсуждением отправляемого кода, и автор оригинального проекта, а так же другие его участники, могут принимать участие в обсуждении предлагаемых изменений до тех пор, пока автор проекта не будет ими доволен, после чего автор проекта может добавить предлагаемые изменения в проект.

Для того, чтобы создать ответвление проекта, зайдите на страницу проекта и нажмите кнопку «Создать ответвление» («Fork»), которая расположена в правом верхнем углу.

Кнопка «Создать ответвление» («Fork»)

Рисунок 88. Кнопка «Создать ответвление» («Fork»)

Через несколько секунд вы будете перенаправлены на собственную новую проектную страницу, содержащую вашу копию, в которой у вас есть права на запись.

Рабочий процесс с использованием GitHub

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

Вот как это обычно работает:

  1. Создайте форк проекта.
  2. Создайте тематическую ветку на основании ветки master .
  3. Создайте один или несколько коммитов с изменениями, улучшающих проект.
  4. Отправьте эту ветку в ваш проект на GitHub.
  5. Откройте запрос на слияние на GitHub.
  6. Обсуждайте его, вносите изменения, если нужно.
  7. Владелец проекта принимает решение о принятии изменений, либо об их отклонении.
  8. Получите обновлённую ветку master и отправьте её в свой форк.

Очень напоминает подход, описанный в разделе Диспетчер интеграции главы 5, но вместо использования электронной почты, команда сотрудничает через веб-интерфейс.

Давайте посмотрим, как можно предложить изменения в проект, размещённый на GitHub.

В большинстве случаев можно использовать официальный инструмент GitHub CLI вместо веб-интерфейса GitHub. Инструмент доступен в системах Windows, MacOS и Linux. Посетите страницу GitHub CLI homepage для получения инструкций по установке и использованию.

Создание запроса на слияние

Тони ищет, чего бы запустить на своём новеньком Arduino. Кажется, он нашёл классный пример на https://github.com/schacon/blink.

Проект, над которым мы хотим поработать

Рисунок 89. Проект, над которым мы хотим поработать

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

Для начала, нажмите кнопку «Fork», как было сказано выше, чтобы заполучить собственную копию проекта. Мы зарегистрированы на GitHub под именем «tonychacon», так что наша копия окажется по адресу https://github.com/tonychacon/blink , где мы сможем редактировать её. Мы клонируем его, создадим тематическую ветку, внесём необходимые изменения и, наконец, отправим их на GitHub.

$ git clone https://github.com/tonychacon/blink (1) Cloning into 'blink'. $ cd blink $ git checkout -b slow-blink (2) Switched to a new branch 'slow-blink' $ sed -i '' 's/1000/3000/' blink.ino (macOS) (3) # If you're on a Linux system, do this instead: # $ sed -i 's/1000/3000/' blink.ino (3) $ git diff --word-diff (4) diff --git a/blink.ino b/blink.ino index 15b9911..a6cc5a5 100644 --- a/blink.ino +++ b/blink.ino @@ -18,7 +18,7 @@ void setup() < // the loop routine runs over and over again forever: void loop() < digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level) [-delay(1000);-]// wait for a second digitalWrite(led, LOW); // turn the LED off by making the voltage LOW [-delay(1000);-] // wait for a second > $ git commit -a -m 'Change delay to 3 seconds' (5) [slow-blink 5ca509d] Change delay to 3 seconds 1 file changed, 2 insertions(+), 2 deletions(-) $ git push origin slow-blink (6) Username for 'https://github.com': tonychacon Password for 'https://tonychacon@github.com': Counting objects: 5, done. Delta compression using up to 8 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 340 bytes | 0 bytes/s, done. Total 3 (delta 1), reused 0 (delta 0) To https://github.com/tonychacon/blink * [new branch] slow-blink -> slow-blink
  1. Клонируем нашу копию
  2. Создаём тематическую ветку
  3. Вносим свои изменения
  4. Проверяем изменения
  5. Фиксируем изменения в тематической ветку
  6. Отправляем новую ветку в нашу копию на GitHub

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

Также можно зайти на страницу «Branches», по адресу https://github.com///branches , найти интересующую ветку и открыть запрос оттуда.

Кнопка открытия запроса на слияние

Рисунок 90. Кнопка открытия запроса на слияние

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

Также мы видим список коммитов в нашей тематической ветке, «опередивших» ветку master (в данном случае всего один коммит) и предпросмотр всех изменений, вносимых этими коммитами.

Создание запроса на слияние

Рисунок 91. Страница создания запроса на слияние

После создания запроса на слияние (путём нажатия кнопки «Create pull request» на этой странице) владелец форкнутого проекта получит уведомление о предложенных изменениях со ссылкой на страницу с информацией о запросе.

Примечание

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

Обработка запроса на слияние

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

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

Комментирование строки в запросе на слияние

Рисунок 92. Комментирование определённой строки в запросе на слияние

Как только владелец прокомментирует изменения, автор запроса на слияние (а также все подписавшиеся на этот репозиторий) получат уведомления. Далее мы рассмотрим как настроить уведомления, но сейчас, если Тони включил уведомления через электронную почту, он получит следующее письмо:

Уведомление по электронной почте

Рисунок 93. Комментарии, отправленные по электронной почте

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

Страница обсуждения запроса на слияние

Рисунок 94. Страница обсуждения запроса на слияние

Теперь участник может видеть что ему необходимо сделать для того, чтобы его изменения были приняты. К счастью, это тоже легко сделать. Используя почту, вам потребуется заново отправить свои изменения в список рассылки, а при использовании GitHub вы просто делаете коммит в тематическую ветку и повторяете push, что автоматически обновляет запрос на слияние. На рисунке Финальная стадия запроса на слияние также видно, что в обновлённом запросе на слияние старый комментарий к коду был свёрнут, так как он относится к строке, которая с тех пор изменилась.

Когда участник сделает это, владелец проекта снова получит уведомление, а на странице запроса будет отмечено, что проблема решена. Фактически, как только строка кода имеющая комментарий будет изменена, GitHub заметит это и удалит устаревшее отличие.

Финальная стадия запроса на слияние

Рисунок 95. Финальная стадия запроса на слияние

Примечательно, что если вы перейдёте на вкладку «Files Changed» в этом запросе на слияние, то увидите «унифицированную» разницу — это суммарные изменения, которые будут включены в основную ветку при слиянии тематической ветки. В терминологии git diff это эквивалентно команде git diff master…​ для ветки, на которой основан этот запрос на слияние. В Определение применяемых изменений детальнее описан данный тип отличий.

GitHub так же проверяет может ли запрос на слияние быть применён без конфликтов и предоставляет кнопку для осуществления слияния на сервере. Эта кнопка отображается только если у вас есть права на запись в репозиторий и возможно простейшее слияние. По нажатию на неё GitHub произведёт «non-fast-forward» слияние, что значит даже если слияние может быть осуществлено перемоткой вперед, всё равно будет создан коммит слияния.

При желании, можно стянуть ветку и произвести слияние локально. Если эта ветка будет слита в master ветку и отправлена на сервер, то GitHub автоматически закроет запрос на слияние.

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

Примечание
Не только ответвления

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

Продвинутые запросы на слияние

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

Запросы слияния как Патчи

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

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

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

Следование за исходным репозиторием

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

PR merge failure

Рисунок 96. Запрос имеет конфликты слияния

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

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

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

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

Предположим, что в примере «tonychacon», который мы использовали ранее, основной автор сделал изменения, которые конфликтуют с запросом на слияние. Рассмотрим это пошагово.

$ git remote add upstream https://github.com/schacon/blink (1) $ git fetch upstream (2) remote: Counting objects: 3, done. remote: Compressing objects: 100% (3/3), done. Unpacking objects: 100% (3/3), done. remote: Total 3 (delta 0), reused 0 (delta 0) From https://github.com/schacon/blink * [new branch] master -> upstream/master $ git merge upstream/master (3) Auto-merging blink.ino CONFLICT (content): Merge conflict in blink.ino Automatic merge failed; fix conflicts and then commit the result. $ vim blink.ino (4) $ git add blink.ino $ git commit [slow-blink 3c8d735] Merge remote-tracking branch 'upstream/master' \ into slower-blink $ git push origin slow-blink (5) Counting objects: 6, done. Delta compression using up to 8 threads. Compressing objects: 100% (6/6), done. Writing objects: 100% (6/6), 682 bytes | 0 bytes/s, done. Total 6 (delta 2), reused 0 (delta 0) To https://github.com/tonychacon/blink ef4725c..3c8d735 slower-blink -> slow-blink
  1. Добавляем исходный репозиторий как удалённый с именем upstream .
  2. Получаем последние изменения из него.
  3. Сливаем основную ветку в нашу тематическую.
  4. Исправляем указанный конфликт.
  5. Отправляем изменения в ту же тематическую ветку.

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

PR fixed

Рисунок 97. Запрос слияния без конфликтов

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

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

Ссылки

Возможно, ваш следующий вопрос будет: «Как мне сослаться на предыдущий запрос слияния?» Оказывается, существует много способов ссылаться на другие вещи практически везде, где у вас есть права записи на GitHub.

Давайте начнём с перекрёстных ссылок для запросов слияния или проблем. Всем запросам слияния и проблемам присваиваются уникальные номера в пределах проекта. Например, у вас не может быть запроса на слияние с номером #3 и проблемы с номером #3. Если вы хотите сослаться на любой запрос слияния или проблему из другого места, просто добавьте # в комментарий или описание. Так же можно указывать более конкретно, если проблема или запрос слияния находятся где-то ещё; пишите username# если ссылаетесь на проблему или запрос слияния, находящиеся в ответвлённом репозитории, или username/repo# если ссылаетесь на другой репозиторий.

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

Перекрёстные ссылки в запросе слияния

Рисунок 98. Перекрёстные ссылки в запросе слияния

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

Отображение перекрёстных ссылок в запросе слияния

Рисунок 99. Отображение перекрёстных ссылок в запросе слияния

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

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

Отображение перекрёстных ссылок в закрытом запросе слияния

Рисунок 100. Отображение перекрёстных ссылок в закрытом запросе слияния

Кроме идентификационных номеров, можно ссылаться на конкретный коммит используя SHA-1. Следует указывать полный 40 символьный хеш SHA-1, но если GitHub увидит его в комментарии, то автоматически подставит ссылку на коммит. Как было сказано выше, вы можете ссылаться на коммиты как в других, так и в ответвлённых репозиториях точно так же, как делали это с Проблемами.

GitHub-версия разметки Markdown

Ссылки на другие Проблемы — это лишь часть интереснейших вещей, которые вы можете делать в текстовых полях на GitHub. Для «проблемы» или «запроса слияния» в полях описания, комментария, комментария кода и других вы можете использовать так называемую «GitHub-версию разметки Markdown». Разметка похожа на обычный текст, который основательно преобразуется.

Смотрите Пример написания и отображения текста с разметкой для примера как использовать разметку при написании комментариев и текста.

Пример разметки

Рисунок 101. Пример написания и отображения текста с разметкой

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

Списки задач

Список задач — это первая действительно важная возможность специфической разметки GitHub, особенно для запросов слияния. Список задач представляет собой список флажков для задач, которые вы хотите выполнить. Размещение его в описании Проблемы или запроса на слияние обычно указывает на то, что должно быть сделано до того, как проблема будет считаться решённой.

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

- [X] Write the code - [ ] Write all the tests - [ ] Document the code

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

Пример списка задач

Рисунок 102. Отображение списка задач в комментарии

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

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

Пример списка задач

Рисунок 103. Статистика задач в списке запросов слияния

Такая возможность невероятно полезна когда вы открываете запрос на слияние на раннем этапе реализации и отслеживаете прогресс с помощью него.

Отрывки кода

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

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

```java for(int i=0 ; i < 5 ; i++) < System.out.println("i is : " + i); >```

Если вы укажете название языка, как показано на примере, GitHub попробует применить к нему подсветку синтаксиса. Для приведённого примера код будет выглядеть как на Отображение обрамлённого кода.

Отображение обрамлённого кода

Рисунок 104. Отображение обрамлённого кода

Цитирование

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

Цитаты выглядят примерно так:

> Whether 'tis Nobler in the mind to suffer > The Slings and Arrows of outrageous Fortune, How big are these slings and in particular, these arrows?

После обработки комментарий будет выглядеть как Пример отображения цитаты.

Отображение цитаты

Рисунок 105. Пример отображения цитаты

Смайлики

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

Помощник по смайлам

Рисунок 106. Автодополнение для смайлов в действии

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

I :eyes: that :bug: and I :cold_sweat:. :trophy: for :microscope: it. :+1: and :sparkles: on this :ship:, it's :fire::poop:! :clap::tada::panda_face:

Такой комментарий будет выглядеть как на Перегруженный смайликами комментарий.

Смайлики

Рисунок 107. Перегруженный смайликами комментарий

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

Примечание

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

Картинки

Технически, картинки не относятся к разметке GitHub, но их использование очень полезно. В дополнение к ссылкам на картинки в комментариях, GitHub позволяет встраивать картинки в комментарии.

Перетаскивание картинки

Рисунок 108. Перетаскивание картинки для загрузки и встраивания

Если вернуться немного назад к Перекрёстные ссылки в запросе слияния, то над областью редактирования вы увидите небольшую подсказку «Parsed as Markdown». Нажав не неё, вы получите полную подсказку по использованию GitHub разметки.

Поддержание GitHub репозитория в актуальном состоянии

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

This branch is 5 commits behind progit:master.

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

Первый способ не требует конфигурации. Например, если вы сделали форк репозитория https://github.com/progit/progit2.git , то актуализировать ветку master можно следующим образом:

$ git checkout master (1) $ git pull https://github.com/progit/progit2.git (2) $ git push origin master (3)
  1. Если вы находитесь на другой ветке — перейти на ветку master .
  2. Получить изменения из репозитория https://github.com/progit/progit2.git и слить их с веткой master .
  3. Отправить локальную ветку master в ваш форк origin .

Каждый раз писать URL репозитория для получения изменений достаточно утомительно. Этот процесс можно автоматизировать слегка изменив настройки:

$ git remote add progit https://github.com/progit/progit2.git (1) $ git fetch progit (2) $ git branch --set-upstream-to=progit/master master (3) $ git config --local remote.pushDefault origin (4)
  1. Добавить исходный репозиторий как удалённый и назвать его progit .
  2. Получить ветки репозитория progit , в частности ветку master .
  3. Настроить локальную ветку master на получение изменений из репозитория progit .
  4. Установить origin как репозиторий по умолчанию для отправки.

После этого, процесс обновления становится гораздо проще:

$ git checkout master (1) $ git pull (2) $ git push (3)
  1. Если вы находитесь на другой ветке — перейти на ветку master .
  2. Получить изменения из репозитория progit и слить их с веткой master .
  3. Отправить локальную ветку master в ваш форк origin .

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

Пример работы над проектом в системе контроля версий (git)

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

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

Цепочка применения изменений в нашем случае будет выглядеть как «Feature» -> «Dev» -> «Production». Между «dev» и «production» еще может возникать «stage», но это уже зависит от конкретных особенностей проекта, в том числе финансовых.

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

Как это выглядит на пальцах?

Программист на локальной машине создает новую ветку, в котрой он будет работать над поставленной задачей. И тут надо запомнить первую и одну из самых главных тонкостей — ветка создается только от актуальной основной («master») ветки.

Почему именно так? Какими бы не были схожими ваши ветки, они все разные, причем иногда разные кардинально — начиная от прав доступа к файлам и заканчивая порой совершенно неопределимыми различиями, исток которых, по всей видимости, кроется чуть ли не в рознящихся контрольных суммах. Однако в конечном итоге любые изменения будут заливаться в основную ветку, на которой работает production. И стоит при создании новой ветки по невнимательности отнаследоваться от другой — и долгие часы разруливания конфликтов вам обеспечены.

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

Переключаемся на основную ветку:

git checkout master
git pull -p

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

Параметр «-p» слабо документирован, однако очень приятен в работе. Формально он никак не относится к самой команде pull, потому вы вряд ли найдете упоминание о нем в ее описании. Однако он передается в fetch, что приводит к удалению всех локальных веток, которые ранее существовали, но были удалены из удаленного репозитория. По сути это избавляет вас от исторического мусора, который со временем начинает доставлять боль при выводе списка веток, не говоря уже о невозможности пользоваться атодополнением.

git checkout -b [название-ветки]

Давайте договоримся, что далее будем ее назвать, например «task-name»

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

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

Как избежать этого? Если честно, то, к сожалению, практически никак. Рано или поздно нам придется исправлять возникающие конфликты. Вопрос — когда и сколько сразу. Здесь есть два подхода.

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

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

На самом деле все достаточно просто. Rebase — это процесс подтягивания изменений с основной ветки (вообще, с любой, но мы сейчас не об этом) в вашу. Иначе говоря, каждый божий день, перед работой и перед отправкой изменений на удаленный репозиторий вы заливаете все последние изменения с той ветки, от которой отнаследовались при создании вашей (конечно же, от master) и исправляете небольшую порцию конфликтов, если они есть.

Далее мы предполагаем, что вы находитесь на своей feature-ветке.

Ребейз можно делать на локальную ветку и тогда это будет выглядеть так:

git rebase master

Но на самом деле это не сильно удобно, потому что локальная ветка имеет все шансы быть неактуальной (а регулярно переключаться на нее и делать git pull просто так на всякий случай — занятие, как бы это сказать, из области психиатрии). Поэтому мы сделаем иначе:

git pull -r origin master

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

В процессе ребейза все ваши коммиты последовательно сливаются в master.

При возникновении конфликтов, о чем git нам непременно сообщит кучей гневных сообщений, мы их правим (некоторые подробности чуть ниже по тексту), добавляем исправленные файлы:

git add -u

и продолжаем процесс ребейза:

git rebase --continue

Исправление текущего конфликта в редких случаях можно пропустить:

git rebase --skip

Или полностью откатить весь процесс:

git rebase --abort

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

Поэтому мы будем вынуждены отправить нашу измененную ветку насильно:

git push -f

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

git checkout -b task-name-backup

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

git checkout task-name

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

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

Ребейзим ветку на master (не обязательно, но не помешает, к тому же должно уменьшить расхождение между dev и master):

git pull -r origin master

Переключаемся на dev и актуализируем ее:

git checkout dev git pull

Внимание — короткая дорога:

git checkout -B dev origin/dev

Так мы одномоментно переключимся и сбросим ветку до состояния origin — меньше телодвижений всяко приятней.

git merge task-name

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

Смотрим список того, что конфликтует:

git status

Проводим некоторое время над разрешением конфликтов. Все конфликты git отмечает примерно следующим образом:

 ======= >>>>>>> task-name

Собственно, по этим многочисленным угловым скобкам и можно провести поиск в каждом конфликтующем файле.

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

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

Как и вслучае с ребейзом, процесс мержа можно откатить:

git merge --abort

Когда все конфликты разрешены, а соответствующее им обрамление удалено, делаем git add исправленных файлов. Впрочем, делаем в большинстве случаев добавление всех измененных файлов:

git add -u

Создаем коммит с указанием того, что решали конфликты слияния (далее дева этот коммит не пойдет и карму нам не испортит)

git commit -m "Task-name - fixing merge conflicts"

И заливаем все это на удаленный репозиторий.

git push

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

И тут еще одна тонкость. Я бы сказал — жирность: dev и master сайты находятся в этой цепочке не последовательно, а параллельно. Иными словами, мы не переносим изменения из dev в master. Feature-ветка мержится в master напрямую. Во многом именно поэтому мы наследуемся именно от мастера и регулярно на этот мастер ребейзим.

Конечно, такой процесс неизбежно приведет к росту расхождений между рабочим и тестовым сайтом, но по мере необходимости последний можно актуализировать, попросту делая push —force мастера в дев.

Какие могут быть проблемы и как их избежать

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

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

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

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

Приглашение участников совместной работы в личный репозиторий

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

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

About collaboration in a personal repository

To collaborate with users in a repository that belongs to your personal account on GitHub.com, you can invite the users as collaborators.

If you want to grant more granular access to the repository, you can create a repository within an organization. For more information, see «Access permissions on GitHub.»

Private forks inherit the permissions structure of the upstream repository. This helps owners of private repositories maintain control over their code. For example, if the upstream repository is private and gives read/write access to a team, then the same team will have read/write access to any forks of the private upstream repository. Only team permissions (not individual permissions) are inherited by private forks.

Inviting a collaborator to a personal repository

You can send an invitation to collaborate in your repository directly to someone on GitHub.com, or to the person’s email address

GitHub limits the number of people who can be invited to a repository within a 24-hour period. If you exceed this limit, either wait 24 hours or create an organization to collaborate with more people. For more information, see «Creating a new organization from scratch.»

  1. Ask for the username of the person you’re inviting as a collaborator. If they don’t have a username yet, they can sign up for GitHub. For more information, see «Signing up for a new GitHub account.»
  2. On GitHub.com, navigate to the main page of the repository.
  3. Under your repository name, click

Settings. If you cannot see the «Settings» tab, select the

Screenshot of a repository header showing the tabs. The

dropdown menu, then click Settings.

Further reading

  • «Permission levels for a personal account repository»
  • «Removing a collaborator from a personal repository»
  • «Removing yourself from a collaborator’s repository»
  • «Organizing members into teams»

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *