Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

Механизм Pull request

Так называемый "поток работ PR" (или "рабочий процесс PR"), применяемый в разработке Godot, является общепринятым для проектов, использующих Git, и должен быть знаком опытным пользователям и разработчикам свободного ПО. Основная идея заключается в том, что только небольшое число коммитов (commit, дословно - "совершать", "фиксировать"; подразумевается сохранение изменений в коде) записывается непосредственно в главной ветке исходного проекта. Вместо этого участники создают форки (fork, дословно - "вилка"; фактически создаётся полная копия проекта, которую участник может изменять по своему усмотрению), а затем используют интерфейс GitHub для запроса на принятие изменений (pull request, дословно - "запрос на вытягивание"; обычно используется англ. термин без перевода и транслитерации) одной из веток их форка в какую-либо ветку исходного (upstream, дословно - "восходящий поток") репозитория.

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

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

Исходный Git-репозиторий

Репозиторий на GitHub представляет собой Git-хранилище кода со встроенной система отслеживания ошибок и PR-системой.

Примечание

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

Система управления версиями Git - это инструмент, используемый для отслеживания последовательных изменений исходного кода; для эффективного участия в разработке Godot настоятельно рекомендуется изучить основы командной строки Git. Существует несколько графических интерфейсов для Git, но они часто порождают вредные привычки в отношении работы с Git и PR, поэтому мы рекомендуем не использовать их. В частности, не желательно использовать онлайн-редактор GitHub для внесения изменений в код (это допускается только для небольших исправлений или для работы с документацией), поскольку он создаёт отдельные коммиты для каждого изменения каждого файла, что быстро приводит к PR с нечитаемой историей Git (особенно после рецензирования).

См.также

Первые разделы "Книги" Git'а - это хорошее введение в философию инструмента и различные команды, которые необходимо освоить в повседневной работе. Вы можете прочитать их онлайн на сайте Git SCM. Вы также можете попробовать интерактивное руководство GitHub.

Ветки Git-репозитория Godot организованы следующим образом:

  • master - это ветка, в которой происходит разработка следующей основной версии. Она, как правило, нестабильна и не предназначена для использования в разработке игр. Но именно сюда следует делать PR проводить в первую очередь.

  • Стабильные ветки названы по имени их версии, например, 3.1 и 2.1. Они используются для переноса исправлений и улучшений из ветки master в текущий стабильный релиз (например, 3.1.2 или 2.1.6). Как правило, последняя стабильная ветка поддерживается до следующей минорной версии (например, ветка 3.0 поддерживалась до выхода Godot 3.1). Если вы хотите сделать PR для поддерживаемой стабильной ветки, пожалуйста, сначала проверьте, относятся ли ваши изменения также к ветке master, и если да, сделайте PR для ветки master в приоритете. Менеджеры релизов могут затем отбирать исправления для стабильной ветки, если это уместно.

  • Иногда создаются ветки для разработки отдельных функций - обычно они сливаются с веткой master через какое-то время.

Создание форка и клонирование

Первый шаг - это создание форка репозитория godotengine/godot на GitHub. Для этого вам потребуется создать учётную запись GitHub (если у вас её ещё нет) и войти в систему. В правом верхнем углу страницы вы увидите кнопку "Fork", как показано ниже:

../../_images/github_fork_button.png

Нажмите на неё, и через небольшой промежуток времени вас перенаправит на только что созданный форк репозитория Godot с вашим именем пользователя GitHub в качестве пространства имён:

../../_images/github_fork_url.png

Затем вы можете клонировать свой форк, то есть создать локальную копию удалённого репозитория (в терминологии Git - remote origin). Если вы ещё этого не сделали, установите Git (пользователи Windows или macOS могут скачать его с сайта Git SCM, пользователи Linux могут воспользоваться менеджером пакетов).

Примечание

Если вы используете Windows, то вам понадобится Git Bash для ввода команд. Пользователи macOS и Linux могут использовать системные терминалы.

Чтобы клонировать ваш форк из GitHub, используйте следующую команду:

$ git clone https://github.com/USERNAME/godot

Примечание

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

Через некоторое время в вашей текущей директории должен появиться каталог godot. Переместитесь в него с помощью команды cd:

$ cd godot

Для начала настройте ссылку на исходный репозиторий:

$ git remote add upstream https://github.com/godotengine/godot
$ git fetch upstream

Это создаст ссылку с именем upstream, указывающую на исходный репозиторий godotengine/godot. Она пригодится, когда вы захотите добавить к себе новые коммиты из его ветки master, чтобы обновить свой форк. Также у вас есть ещё одна ссылка, с именем origin, которая указывает на ваш форк (USERNAME/godot).

Описанные выше шаги вам нужно выполнить только один раз и повторять их не придётся, если вы не удалите локальную папку godot (однако, если хотите, вы можете перемещать её - соответствующие метаданные скрыты в подкаталоге .git).

Примечание

Brаnch it, pull it, code it, stage it, commit, push it, rebase it... technolоgic.

Такой негативный взгляд, стилизованный под песню Technologic группы Daft Punk, показывает общее представление новичков о рабочем процессе в Git: множество странных команд, которые нужно выучить или использовать копированием/вставкой в надежде, что они будут работать так, как ожидалось. На самом деле это неплохой способ обучения, если вы любопытны и не стесняетесь задавать вопросы поисковым системам, но на всякий случай мы собрали здесь основные команды, которые вам понадобятся при работе в Git.

In the following, we will assume as an example that you want to implement a feature in Godot's Project Manager, which is coded in the editor/project_manager.cpp file.

Ветвление

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

# Create the branch based on the current branch (master)
$ git branch better-project-manager

# Change the current branch to the new one
$ git checkout better-project-manager

Вместо этих двух команд можно использовать одну:

# Change the current branch to a new named one, based on the current branch
$ git checkout -b better-project-manager

Если вы захотите вернуться к ветке master, используйте:

$ git checkout master

Список веток в репозитории можно просмотреть командной git branch, при этом ветка, в которой вы сейчас находитесь, будет отмечена звёздочкой:

$ git branch
  2.1
* better-project-manager
  master

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

$ git checkout -b my-new-feature master

Обновление вашей ветки

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

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

$ git pull --rebase upstream master

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

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

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

Примечание

Если вы опустите аргумент --rebase, то у вас получится коммит слияния (merge в терминологии Git), который собирает историю из двух разных веток в одно целое. Если при этом возникнут какие-либо конфликты, они все будут разрешены в пределах этого коммита.

Хотя это допустимое действие и поведение git pull по умолчанию, оно не одобряется в нашем сообществе. Мы используем слияние только при принятии PR в исходный репозиторий.

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

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

Совет

Если в какой-то момент вы захотите откатить вашу локальную ветку до определённого коммита или до какой-либо ветки удалённого репозитория, то вы можете сделать это с помощью команд git reset --hard <commit ID> или git reset --hard <remote>/<branch> (например, git reset --hard upstream/master).

Имейте в виду, что такой сброс приведёт к удалению любых изменений в этой ветке. Если вы когда-нибудь сделаете коммит по ошибке - используйте команду git reflog, чтобы найти ID предыдущего коммита, и подставьте его в качестве аргумента в git reset --hard для возврата в это состояние.

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

Затем вы должны внести изменения в файл editor/project_manager.cpp нашего примера с помощью вашей обычной среды разработки (текстовый редактор, IDE и т.д.).

Однако по умолчанию изменённый файл не проиндексирован. Индексация позволяет разграничить изменения, которые будут включены в следующий коммит, и изменения, которые следует игнорировать. Чтобы перенести изменённый файл из вашего рабочего каталога в репозиторий Git, вам нужно проиндексировать его с помощью команды git add, а затем зафиксировать изменения с помощью команды git commit.

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

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

  • git checkout -- <files> отменит неустановленные изменения в указанных файлах.

  • ''git add <files>'' будет поэтапно вносить изменения в перечисленные файлы.

  • git diff --staged покажет текущие стадийные изменения, т.е. различия между промежуточной областью и последней фиксацией.

  • git reset HEAD <files> отменит изменения в перечисленных файлах.

  • git status покажет, какие изменения проиндексированы, а какие нет.

  • git commit зафиксирует проиндексированные изменения, создав коммит; при этом откроется текстовый редактор (вы можете задать предпочтительный редактор в переменной окружения GIT_EDITOR или в параметре core.editor в настройках Git), чтобы вы могли оставить примечания в журнале коммитов; вы также можете использовать git commit -m "Cool commit log" для записи в журнал напрямую.

  • git commit --amend позволяет вам внести текущие проиндексированные (с помощью git add) изменения в предыдущий коммит; это может пригодится, если вам нужно что-то исправить в предыдущем коммите (опечатку, стилистическую или иную мелкую ошибку).

  • git log покажет вам последние коммиты в текущей ветке; если вы делали локальные коммиты, они будут наверху списка.

  • git show покажет изменения, внесённые последним коммитом; также вы можете указать в качестве аргумента хеш коммита, изменения которого хотите увидеть.

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

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

# It's nice to know where you're starting from
$ git log

# Do changes to the Project Manager with the nano text editor
$ nano editor/project_manager.cpp

# Find an unrelated bug in Control and fix it
$ nano scene/gui/control.cpp

# Review changes
$ git status
$ git diff

# We'll do two commits for our unrelated changes,
# starting by the Control changes necessary for the PM enhancements
$ git add scene/gui/control.cpp
$ git commit -m "Fix handling of margins in Control"

# Check we did good
$ git log
$ git show
$ git status

# Make our second commit
$ git add editor/project_manager.cpp
$ git commit -m "Add a pretty banner to the Project Manager"
$ git log

Это добавит к ветке better-project-manager два новых коммита, которых нет в ветке master. Однако эти изменения всё ещё являются локальными: ни ваш удалённый форк, ни исходный репозиторий пока не знают о них.

Отправка изменений на GitHub

Тут вам пригодится команда git push. Git всегда выполняет коммит в локальном репозитории (в отличие от Subversion, где коммит изменяет удалённый репозиторий напрямую). Вам нужно вручную отправлять новые коммиты в удалённый репозиторий, если вы хотите сделать их доступными для всех. Вот синтаксис этой команды:

$ git push <remote> <local branch>[:<remote branch>]

Часть в квадратных скобках, задающую имя ветки в удалённом репозитории, можно опустить, если вы хотите, чтобы удалённая ветка имела то же имя, что и локальная; в нашем примере мы так и сделаем:

$ git push origin better-project-manager

Git will ask you for your username and password. For your password, enter your GitHub Personal Access Token (PAT). If you do not have a GitHub Personal Access Token, or do not have one with the correct permissions for your newly forked repository, you will need to create one. Follow this link to create your Personal Access Token: Creating a personal access token.

After you have successfully verified your account using your PAT, the changes will be sent to your remote repository. If you check the fork's page on GitHub, you should see a new branch with your added commits.

Запрос на принятие изменений

После того как изменения будут загружены на GitHub, на странице репозитория вы увидите строку "This branch is 2 commits ahead of godotengine:master" ("Эта ветка опережает godotengine:master на 2 коммита"). Также там может быть указано отставание на несколько коммитов, если ваша ветка не была синхронизирована с веткой master исходного репозитория.

../../_images/github_fork_make_pr.png

В этой строке есть ссылка "Pull request". Нажав на неё, вы откроете форму, которая позволит вам отправить запрос на исправление изменений в репозитории godotengine/godot. Там должны быть показаны два ваших коммита и написано "Able to merge". Если нет (например, коммитов намного больше, или говорится о конфликтах слияния), не создавайте PR, что-то пошло не так. Зайдите в наш чат Godot Contributors Chat и попросите поддержки :)

Используйте явное название для PR и поместите необходимые детали в область комментариев. Вы можете перетащить скриншоты, GIF-файлы или заархивированные проекты, если это уместно, чтобы продемонстрировать, что реализует ваша работа. Нажмите "Create a pull request", и тадаа!

Изменение PR

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

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

# Check out your branch again if you had changed in the meantime
$ git checkout better-project-manager

# Fix a mistake
$ nano editor/project_manager.cpp
$ git add editor/project_manager.cpp
$ git commit -m "Fix a typo in the banner's title"
$ git push origin better-project-manager

Однако имейте в виду, что в нашем рабочем процессе PR мы отдаём предпочтение коммитам, которые переводят кодовую базу из одного функционального состояния в другое функциональное состояние, без промежуточных коммитов, исправляющих ошибки в вашем собственном коде или проблемы со стилем. В большинстве случаев мы отдаём предпочтение одному коммиту в данном PR (если только нет веской причины хранить изменения отдельно). Вместо того чтобы создавать новый коммит, используйте git commit --amend для внесения изменений в предыдущий коммит с вашими исправлениями. Тогда приведенный выше пример станет:

# Check out your branch again if you had changed in the meantime
$ git checkout better-project-manager

# Fix a mistake
$ nano editor/project_manager.cpp
$ git add editor/project_manager.cpp
# --amend will change the previous commit, so you will have the opportunity
# to edit its commit message if relevant.
$ git commit --amend
# As we modified the last commit, it no longer matches the one from your
# remote branch, so we need to force push to overwrite that branch.
$ git push --force origin better-project-manager

Интерактивный режим rebase

If you didn't follow the above steps closely to amend changes into a commit instead of creating fixup commits, or if you authored your changes without being aware of our workflow and Git usage tips, reviewers might request you to rebase your branch to squash some or all of the commits into one.

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

Чтобы втиснуть эти посторонние коммиты в основной, нам придется переписать историю. Верно, у нас есть такая возможность. Вы можете прочитать, что это плохая практика, и это правда, когда речь идёт о ветках восходящего репозитория. Но в своём форке вы можете делать всё, что хотите, и всё разрешено для получения аккуратных PR :)

Для этого применяется интерактивный режим команды rebase: git rebase -i. В качестве аргумента используется ID коммита или имя ветки, что позволяет вам редактировать все коммиты между указанной точкой и текущим состоянием вашей рабочей ветки, называемым HEAD.

Хотя вы можете присвоить любой ID коммита git rebase -i и просмотреть всё, что между ними, наиболее распространенный и удобный рабочий процесс включает ребазинг на восходящую ветку master, что вы можете сделать с помощью:

$ git rebase -i upstream/master

Примечание

Ссылаться на ветки в Git немного сложно из-за различия между удалёнными и локальными ветками. Здесь Upstream/master (с символом "/") - это локальная ветка, которая была извлечена из ветки master исходного репозитория.

Интерактивный режим rebase работает только с локальными ветками, поэтому здесь важен символ "/". Так как исходный репозиторий часто изменяется, ваша локальная ветка Upstream/master может оказаться устаревшей - во избежание этого вам необходимо обновить её с помощью git fetch Upstream master. В отличие от команды git pull --rebase Upstream master, которая будет обновлять вашу текущую ветку, fetch обновит только ссылку Upstream/master (которая отличается от вашей локальной ветки master... да, это сбивает с толку, но постепенно вы разберётесь и привыкните).

В результате откроется текстовый редактор (vi по умолчанию, см. Git docs для настройки вашего любимого редактора) с чем-то, что может выглядеть следующим образом:

pick 1b4aad7 Add a pretty banner to the Project Manager
pick e07077e Fix a typo in the banner's title

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

pick 1b4aad7 Add a pretty banner to the Project Manager
fixup e07077e Fix a typo in the banner's title

После сохранения и выхода из редактора произойдёт обновление. Второй коммит будет объединён с первым, а git log и git show теперь должны подтвердить, что у вас есть только один коммит, включающий в себя все необходимые изменения.

Однако вы переписали историю только локально, и теперь ваши локальная и удалённая ветки конфликтуют. Коммит 1b4aad7 в приведённом выше примере был изменён, получив при этом новый хеш. Если вы теперь попытаетесь отправить изменения в свой удаленный репозиторий, это вызовет ошибку:

$ git push origin better-project-manager
To https://github.com/akien-mga/godot
 ! [rejected]        better-project-manager -> better-project-manager (non-fast-forward)
error: failed to push some refs to 'https://akien-mga@github.com/akien-mga/godot'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart.

This is reasonable behavior, Git will not let you push changes that would override remote content. But that's actually what we want to do here, so we will have to force it:

$ git push --force origin better-project-manager

Теперь Git просто заменит вашу удалённую ветку на обновлённую локальную (поэтому используйте опцию --force с осторожностью, предварительно проверяя отправляемые изменения с помощью git log). Открытый из этой ветки PR так же будет обновлён автоматически.

Rebasing onto another branch

If you have accidentally opened your PR on the wrong branch, or need to target another branch for some reason, you might need to filter out a lot of commits that differ between the old branch (for example 4.2) and the new branch (for example master). This can make rebasing difficult and tedious. Fortunately git has a command just for this situation, git rebase --onto.

If your PR was created from the 4.2 branch and you want to update it to instead start at master the following steps should fix this in one step:

$ git rebase -i --onto master 4.2

This will take all the commits on your branch after the 4.2 branch, and then splice them on top of master, ignoring any commits from the 4.2 branch not on the master branch. You may still need to do some fixing, but this command should save you a lot of tedious work removing commits.

Just like above for the interactive rebase you need to force push your branch to handle the different changes:

$ git push --force origin better-project-manager

Удаление ветки в Git

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

To delete our better Project Manager branch locally, use this command:

$ git branch -d better-project-manager

При необходимости удалить ветку до слияния вместо опции -d нужно использовать -D.

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

$ git push origin -d better-project-manager

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