Пользователь
0,0
рейтинг
10 августа 2011 в 13:43

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

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 сервис.
Новиков Андрей @Envek
карма
80,3
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (29)

  • +1
    Итак, что же такое «запрос на извлечение (сделанных вами изменений)» (именно так я перевёл pull request)?Вы прикалываетесь? o_O Pull Request — это и в Африке Pull Request, какой еще запрос за извлечение? o_O
    • +4
      Честно говоря, до того, как сегодня утром я сел за написание этой статьи, я и сам не задумывался о переводе (вообще о возможности перевода) такого интуитивного термина. Дальше я и ссылаюсь только как «pull request» или «пулл реквест», но хотя бы один раз надо было написать, как же это по русски. И над переводом пришлось подумать… Да и в других местах я заходил в тупик, как то project maintainer и вообще, push, pull, fork, branch. Как это внятно перевести на русский, чтоб было понятно и читалось — не ясно.
      У вас есть перевод лучше? Я с радостью его приму.
      • –1
        Я же выше написал: зачем переводить такие вещи?
        • +8
          автор правильно сделал, что в начале статьи пояснил перевод.

          точно так же как википедия поясняет происхождение самых простых слов, например:
          Програ́мма — (от греч. προ — пред, греч. γράμμα — запись)

          дальше можно без зазрения совести использовать «пул реквест».
      • +2
        Если хочется перевести, я бы перевел «запрос на включение изменений». У меня лично «извлечение» почему-то вызывает ассоциации с хирургическим вмешательством.
        • +1
          Спасибо, ваш вариант мне нравится, хотя он и дальше от прямого перевода слова pull. Исправил в статье.
    • 0
      У нас в команде принят термин затягивание/вытягивание. И в некоторых локализованных программах так же.
    • +1
      Запрос на внесение, скорее уж.
  • +2
    Кстати, pull request — это не единственный способ обратить внимание разработчика на сделанные тобой изменения. Если изменение — это bugfix и спокойно укладывается в один коммит, то возможно, более логичным будет создать отчет об ошибке (в раздел issues) и включить в описание ошибки ссылку на коммит, который ее исправляет.

    Ссылку на коммит в теле сообщения специально оформлять не нужно, достаточно просто включить в текст его идентификатор. Парсер markdown распознает его, сократит и преобразует в правильную ссылку.
    • 0
      Issues и Pull Requests на GitHub'е идут общим потоком (у них единая нумерация)… Есть ли смысл? К тому же как потом разработчику стягивать эти изменения? Простынёй команд вида origin add, fetch, cherry-pick?
      К тому же, у pull request'ов есть киллер фича — зелёная кнопочка auto merge. Мэйнтэйнеру проще влить изменения, просто нажав на эту кнопочку :) (это, конечно, при условии, что нет конфликтов)
  • 0
    Спасибо за перевод. Использую git всего месяца 3. Когда начинал изучать тему, было немного сложновато разобраться.
    Git — гениальное изобретение. Пробовал использовать 1-ю модель совместной работы, немного запутанно и не всегда merge проходит гладко, так что теперь использую 2 модель.
    Тут не хватает только краткой информации о тегах.
    А вообще я думаю полезная статья для начинающих гиттеров :)
    • +1
      Начинающий гиттер, прочти вот это, особенно главу 8 и вот это. Остальное — в мануалах.
    • 0
      При активной разработке гладко проходящие мёржи — редкость и счастье. 2-я модель подходит только в случае, когда трудятся пара-тройка человек, иначе, думаю, начнётся хаос и кто-нибудь обязательно набедокурит в общем репозитории.
      Думаю, подробное описание тегов и веток (бранчей) — это уже за рамками этой статьи и само по себе немаленький разговор.
  • 0
    Я сделал форк, внёс изменения и запросил слияние 4 месяца назад…
    Вопрос: почему на мой запрос никто не отвечает… что не так?
    Я даже видео снял (в комменте ссылка) с демонстрацией исправлений…
    Ссылка: https://qt.gitorious.org/qt/qt/merge_requests/1184

    Буду рад если проясните мне моё положение…
    • 0
      1. Если это ещё не исправлено — ребейзлайн на текущую голову и новый запрос на пулл.
      2. Если в течение недели не отвечают — пинг. Когда ответят — см. п.1.
    • 0
      Это безответственность разработчиков Qt — никак не прореагировать на запрос.
      Они должны были как-то на него прореагировать: принять, отклонить или потребовать дополнений.
      Найдите способ как-то с ними связаться (в списке рассылки, IRC, Jabber) и обратите внимание на своё исправление явно.
      Так же, возможно, вы не включили в запрос юнит-тесты или что-нибудь ещё, это где-то должно быть оговорено.
      • 0
        Спасибо за ответ, это мой первый вклад в оупенсурс.
        Потому я был склонен думать, что сделал что-то не так…
        Не туда, не то и не так отправил… Но раз вы говорите, попробую в IRC пообщаться…
        • 0
          Вклад в опенсурс — это очень здорово. Дерзайте. Я сам иногда балуюсь кодингом на Qt, так что я за вас всеми конечностями ))
  • 0
    Кстати в GitExtensions уже встроена работа с GitHub, и поиск проект и форк и все.
  • 0
    Кто-нибудь может мне подсказать, как делать pull request не в мастер, а в другую ветку?
    • 0
      Отмена, разобрался.
  • 0
    Исправьте в статье, перед тем как сделать

    git push origin feature

    нужно добавить файлы в которых мы что то меняли и сделать комит:

    git add myfile.c
    git commit -m 'Fix myfile.c'
  • 0
    У вас ссылки на две картинки побились: Кнопка «Fork» и Событие закрытия пулл реквеста. А так: спасибо за статью.
    • 0
      После переезда Хабра на новый движок во многих старых статьях ссылки побились. Раньше тут все нормально было.
  • 0
    Всегда ли после принятия pull request нужно удалять ветку относящуюся к нему?
    Я не стал удалять ветку, сделал в нее еще коммит, но кнопки «Pull Request» на сайте не появилось.
    • 0
      Можно удалять, можно нет, на ваше усмотрение.
      Когда pull request принят или закрыт, добавление коммитов в ветку уже ни на что не влияет. Но можно создать новый pull request из этой ветки, в него будут включены коммиты, не вошедшие в первый.
    • 0
      Возможно вы не слили в свою ветку upstream/master (в терминах этой статьи) поэтому ГитХаб не может определить.
      После того, как ваш ПР приняли вам нужно было сделать что то вроде этого:
      git checkout master
      git fetch upstream
      git pull upstream master
      git checkout yourbranch
      git merge master
      

      И только потом делать новые коммиты в yourbranch.
      Но сейчас, поскольку коммиты уже сделаны, то можно попробовать сделать так:
      git checkout master
      git fetch upstream
      git pull upstream master
      git checkout -b yourbranch1
      git merge master
      git merge yourbranch
      

      Этим мы создали ветку yourbranch1 в которой ваш первый принятый ПР и ваши новые коммиты.
      Затем, если вы хотите сохранить имя старой ветки, то вам придется ее пересоздать:
      git branch -d yourbranch #Удаляем в локальном репозитории
      git push origin :yourbranch #Удаляем в удалённом репозитории
      

      Затем переименовываем yourbranch1 в yourbranch:
      git checkout yourbranch1
      git checkout -b yourbranch    #Вновь создаем yourbranch как копию yourbranch1
      git checkout -d yourbranch1   #Удаляем yourbranch1
      

      Все теперь можно пушить yourbranch:
      git push origin yourbranch
      

      Я не эксперт в гит. Тут написал, как бы сделал сам, оказавшись в вашей ситуации. Никакого вреда то что тут написано принести не может (осторожно, перед удалением веток убедитесь, что вы создали ветку-копию). Но, думаю, вашу проблему должно решить.
  • 0
    Такой вопрос, могу ли я забрать некоторые коммиты из форка своего репозитория если владелец форка не присылал мене pull request? Если да то как?
    • +1
      Способов множество, выбирайте любой:
      1. Добавляете форк в свой репозиторий как remote:
        git remote add FORKNAME git@github.com:AUTHOR/REPO.git
        
        и далее стягиваете его изменения к себе в локальную копию
        git fetch FORKNAME
        1. и либо мёрджите себе его ветку
          git merge FORKNAME/BRANCH
        2. либо отбираете отдельные коммиты
          git cherry-pick COMMIT_1_SHA COMMIT_2_SHA
      2. Ещё можно прибавить к URLу коммита на гитхабе расширение .patch и полученный файл можно скормить команде git am, если мне память не изменяет.

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