Pull Request

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

  1. Форкнуть понравившийся проект.
  2. Склонировать репозиторий.
  3. Создать ветку для внесения изменений.
  4. Внести изменения, сделать коммит.
  5. Проверить работоспособность с внесёнными изменениями.
  6. Сделать Pull Request.
  7. Участие в Code Review, при необходимости внести изменения в свой Pull Request.
  8. После принятия Pull Requesta синхронизировать свой репозиторий с основным.
  9. Повторить пункты с 3 - 8 необходимое количество раз.

Рассмотрим подробнее:

Форк проекта

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

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

Чтобы сделать форк репозитория, нужно нажать кнопку “Fork” в верху страницы. В результате репозиторий будет скопирован в наш аккаунт и у нас появится доступ на запись в нашу копию.

Здесь и далее будем считать, что мы работаем над репозиторием Spoon-Knife пользователя octocat, а ваше имя пользователя — username.

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

После того как мы форкнули репозиторий, для продолжения работы нам нужно получить его на локальную машину. Для этого нужно нажать на кнопку “clone” и скопировать ссылку. Стоит обратить внимание на выбранный слева протокол. Если у нас не настроен SSH, там должен быть указан HTTP. Открыть терминал и запустить следующую команду:

git clohe git@github.com:octocat/Spoon-Knife.git

Репозиторий склонируется в поддиректорию текущей дирректории.

Создание ветки

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

cd Spoon-Knife
git remote add upstream git://github.com/octocat/Spoon-Knife.git
git fetch upstream

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

git checkout -b feature #Создаёт новую ветвь, названную "feature" и делает её активной

Если после этого выполнить git status, он покажет:

On branch feature
nothing to commit, working directory clean

Эту команду стоит запомнить — когда мы не понимаем, в каком состоянии репозиторий, стоит просто выполнить её. Чаще всего в её выводе git покажет другие команды, которые делают то, что мы (скорее всего) и хотим сделать.

Теперь можно начать причинять добро.

Внесение изменений

Теперь приступаем к работе. Редактируем код, обновляем документацию, чиним тесты, дополняем README.

При этом стараемся делать коммиты часто, а сами коммиты — небольшими по объёму. Каждый коммит должен делать ровно одну вещь, и при этом поддерживать работоспособность проекта. Стремиться нужно к тому, чтобы в будущем можно было перейти на любой коммит и получить рабочий проект.

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

Итак, после редактирования файлов мы имеем следующую ситуацию - (это вывод git status):

On branch feature
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   README.md

no changes added to commit (use "git add" and/or "git commit -a")

В выводе есть все необходимые нам команды:

  • git add <file>… добавляет файл в содержимое коммита, который вы собираетесь записать
  • git checkout – <file>… откатывает ваши изменения файла

Поэтому делаем git add README.md, а затем git commit. Откроется редактор, в котором нужно ввести сообщение коммита.

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

Формат сообщения о коммите таков:

Краткое описание коммита (не более 50 символов)

Подробное описание коммита - зачем он сделан, почему нельзя сделать по-другому,
источники информации.

Служебная информация - теги, ссылки на задачи, какие задачи коммит закрывает.

Из всех частей сообщения, только первая является обязательной.

Она должна иметь вид <Глагол в настоящем времени, первом лице, множественном числе> <объект_изменения>. Говорим о том, что мы делаем в этом коммите: [мы] исправляем ошибку, [мы] добавляем возможность, [мы] обновляем документацию.

Последняя строка — это команда GitHub. Когда коммит с такой командой попадает в master, GitHub автоматически закроет указанную задачу. Можно использовать разные формы этой команды: Fix #123, fixes #123, close #123 и другие. Это экономит время на поддержку проекта.

Команда git log --oneline выводит историю коммитов в формате «1 коммит — 1 строка на экране». При этом он использует в качестве описания коммита первую строку — краткое описание. Поэтому оно обязательно должно быть отделено пустой строкой от остального описания — иначе однострочный вывод разъедется.

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

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

Как только мы сделали работу (или её часть), нужно отправить её в свою копию репозитория на GitHub:

git push origin feature #Загружает изменения в текущей ветви в origin в ветвь feature

Проверка работоспособности

Когда сделали правки, стоит их проверить — если только это не что-то абсолютно тривиальное.

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

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

Создаём Pull Request

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

Идём на страницу нашей копии репозитория на GitHub, выбираем ветвь feature и жмём кнопку Pull Request.

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

Там же мы можем посмотреть, какие коммиты попали в пулл реквест. А так же общий diff всех изменений в пулл реквесте.

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

ВАЖНО!!!: Договориться с владельцем «родительского» репозитория, в какую ветку вливать изменения (он может написать это в README)

Изменение базового репозитория меняет и список людей, которые получат уведомление о пулл реквесте. Каждый, кто имеет право «на запись» в базовый репозиторий, получит письмо и увидит уведомление на главной GitHub’а, в следующий раз, как на него зайдёт. Как только список коммитов нас удовлетворит, нужно нажать кнопку Update Commit Range.

Подсказка: если сразу после того, как мы отправили ветку в свой репозиторий (git push origin) зайти на страницу репозитория, там будет предложение создать Pull Request на вливание недавно отправленной ветки в master. Сделать это можно как в вашем форке, так и в исходном репозитории. Это будет отдельная кнопка вверху, и при её нажатии в качестве ветки для слияния будет указана та, куда вы делали git push.

Участие в Code Review

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

Помните, я говорил, что следует все изменения, которые пойдут в пулл, держать в отдельной ветке? Так вот, основное удобство: мы всегда можем добавить коммиты к уже существующему пулл реквесту, просто добавив их к этой ветке в нашем репозитории (да-да, просто git push origin feature, при условии, что вы указали в пулл реквесте feature как исходную ветвь)

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

  • Комментарии, оставленные к пулл реквесту;
  • Дополнительные коммиты, добавленные к ветви пулл реквеста;
  • Комментарии к изменённым строкам или файлам, оставленные к любому из коммитов, включенных в пулл реквест.

В комментариях к пулл реквесту можно использовать Markdown, то есть можно вставлять изображения и использовать всё форматирование, поддерживаемое Markdown.

Завершение работы

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

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

# забираем изменения
git checkout master
git pull upstream master
git push origin master

# удаляем ветку
git branch -d feature # В локальном репозитории
git push origin :feature # В удалённом репозитории

Если работа заняла большое время и оригинальный репозиторий успел уйти вперёд

Можно просто влить изменения из оригинального репозитория к себе:

git checkout master
git pull upstream master
git checkout feature
git merge master

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

git checkout master
git pull upstream master
git checkout feature
git rebase master #Всё отличие только здесь

Прочитать про то, как работает rebase можно в официальном руководстве. Там имеются и очень понятные иллюстрации. Так же есть статья в помощи GitHub.

ВНИМАНИЕ: Пожалуйста, учтите, что git rebase меняет id коммитов! Поэтому, все действия с этой командой стоит выполнять только на локальном репозитории, до того, как эти коммиты станут общедоступны, т.е. до того, как вы их push’нули на гитхаб.