10 August 2011

Pull request'ы на GitHub или Как мне внести изменения в чужой проект

Git
По просьбе tulskiy делаю вольный перевод частей официальной документации GitHub'а Fork A Repo и Send pull requests.

Итак, что же такое «запрос на включение (сделанных вами изменений)» (именно так я перевёл pull request)? В официальной документации гитхаба говорится следующее:
Pull request'ы позволяют вам рассказать другим о тех изменениях, которые вы разместили в своём GitHub-репозитории. Как только pull request отправлен, заинтересованные стороны рассматривают ваши изменения, обсуждают возможные правки или даже добавляют дополняющие коммиты, если нужно.

Говоря своим языком: Посылая pull request, вы говорите автору изначального репозитория (и всем заинтересованным лицам): «Смотрите, что я сделал, не хотите ли принять мои изменения и влить их в проект?»

Немного о моделях совместной разработки


На GitHub популярны две модели совместной разработки:
  1. Модель «Fork + Pull» позволяет любому склонировать (fork) существующий репозиторий и сливать изменения в свой личный fork без необходимости иметь доступ к оригинальному репозиторию. Затем, изменения должны быть включены в исходный репозиторий его хозяином. Эта модель уменьшает количество телодвижений для новых contributors и популярна для open source проектов, так как позволяет людям работать независимо, без единого координирования.
  2. Модель «общего репозитория» (The Shared Repository Model) чаще встречается у малых команд и организаций, работающих над закрытыми проектами. Каждый в команде имеет доступ «на запись» в один общий репозиторий, а для изолирования изменений применяются тематические ветви (topic branches).

Pull request'ы особенно полезны в модели «Fork + Pull», поскольку предоставляют способ уведомить мэйнтэйнеров проекта (т.е. хозяина оригинального репозитория) о изменениях в вашей копии репозитория. Впрочем, они так же полезны и в модели общего репозитория, где обычно используются для того, чтобы инициировать пересмотр или обсуждение кода перед тем, как включать его в основную ветвь разработки.

Делаем копию репозитория


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

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

Сделать это очень просто: на странице репозитория имеется кнопочка «Fork», которую и следует нажать.
Кнопка «Fork»

После чего, эту свою копию уже можно «стянуть» на свой компьютер:

git clone git@github.com:username/Spoon-Knife.git


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

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


Делаем работу


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

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


Вот, теперь творите добро (и пусть оно будет выражаться в коммитах).

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

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


Возвращаем изменения: Pull request


Итак, всё сделано. Вы написали код, он у вас в ветви feature как у вас на компьютере, так и на GitHub'е. Осталось только «заслать» его в оригинальный репозиторий.

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

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

Там же вы можете посмотреть, какие коммиты попали в пулл реквест:
Предпросмотр пулл реквеста, коммиты

А так же общий diff всех изменений в пулл реквесте:
image

По умолчанию, пулл реквесты считаются основанными на самой часто интегрируемой ветви родительского репозитория. В этом случае username/Spoon-Knife был скопирован с octocat/Spoon-Knife, так что pull request считается основанным на ветке master репозитория octocat/Spoon-Knife. В большинстве случаев, это будет корректно, но если не так, то вы можете нажать на кнопку «Change Commits»

Вы попадёте в форму выбора базовой и исходной ветвей:
Выбор коммитов для отправки

Слева выбираете в какую ветку будут вливаться изменения в родительском репозитории, справа — какие изменения будут браться с вашего репозитория. По примеру: справа octocat/Spoon-Knife/master, слева username/Spoon-Knife/feature. Здесь вы можете указывать не только ветки, но так же теги и id отдельных коммитов в соответствующем репозитории.
ВАЖНО: Договоритесь с владельцем «родительского» репозитория, в какую ветку будете вливать изменения (он может написать это в README)

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

Когда вы ввели название и описание и перепроверили список коммитов и изменения в файлы, попавшие в пулл реквест, нажмите кнопку Send pull request. Пулл реквест будет создан незамедлительно.

Что дальше?


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

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

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

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

Когда ваш pull request примут, не забудьте слить изменения в свой репозиторий (или удалить его, если больше не нужен):
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'нули на гитхаб.

Если вы хозяин: Как принять pull request


Если пулл реквест удовлетворяет всем условиям, то кто-либо с правом «на запись» (т.е. может сделать push) в целевой репозиторий, должен принять pull request одним из многих методов. Ниже описаны три наиболее популярных метода:

Auto Merge (автослияние)

Во многих случаях можно попросить github автоматически принять пулл реквест, используя большую зелёную кнопку Merge Pull Request, которая сама вольёт изменения, создаст мёрж-коммит и закроет пулл реквест.
Кнопка автослияния
Подробнее можно почитать в этом хабратопике: Кнопка слияния на GitHub.

Fetch and Merge (скачать и слить)

Основной метод вливания изменений. Он требует добавления remote, ведущего к репозиторию человека, отправившего pull request, скачивания изменений с этого репозитория, объединения нужной ветви, исправления конфликтов и выгрузки обновлённой ветви обратно в исходный репозиторий:
git checkout master
git remote add username git://github.com/username/Spoon-Knife.git
git fetch username
git merge username/feature
git push origin master


Patch and Apply (пропатчить и принять)

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

У каждого пулл реквеста есть свой .patch URL, с которого можно скачать текстовый патч, чтобы скормить его команде git-am:
git checkout master
curl https://github.com/octocat/Spoon-Knife/pull/50.patch | git am
git push origin master


Закрытие пулл реквеста

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

Вместо заключения

Надеюсь, это руководство поможет вам в улучшении многих open-source (и не только) проектов.
Несмотря на большое время пребывания на Хабрахабре, это мой первый топик. Пожалуйста, сообщайте в личку или в комментариях обо всех недочётах, которые я мог допустить. Буду исправлять.
Большое спасибо Antiarchitect и другим хабраюзерам за помощь в опубликовании статьи, а так же, разумеется, команде разработчиков GitHub, за столь удобный и бесплатный для open-source сервис.
Tags:gitgithubpull requestcollaborative developmentпулл реквестсовместная разработка
Hubs: Git
+76
312.3k 680
Comments 30