Представим себе что вы хотите исправить ошибку или добавить фичу в проект на GitHub.

Если это что-то простое, вроде опечатки в README.md, то это делается в два клика прямо на GitHub. Поговорим о более сложном случае, когда нужно не просто исправить ошибку, но и запустить необходимые скрипты для проверки, разбить работу на несколько коммитов, и так далее.

Спешите? Все команды сразу.

К делу

Начать следует с форка репозитория проекта, который планируется дополнить. Это делается нажатием кнопки "Fork" на странице репозитория прямо на GitHub.

Кнопка Fork находится в правом верхнем углу страницы.

Чтобы далеко не искать подходящий пример, сделайте форк репозитория с файлами для статьи о внедрении PHPUnit.

Затем склонируйте себе локально исходный репозиторий. Важно клонировать не ваш форк, а репозиторий исходного проекта. С таким подходом ветка main (или master) будет постоянно указывать на исходный проект.

$ git clone https://github.com/sanmai/phpunit-primer

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

$ cd phpunit-primer
$ git remote -v
origin  https://github.com/sanmai/phpunit-primer (fetch)
origin  https://github.com/sanmai/phpunit-primer (push)

За редким исключением, всем удалённым репозиториям в Git присваиваются имена. По умолчанию первый добавленный репозиторий называется origin, но в нашем случае это не подходит. Репозиторий исходного проекта переименуем в upstream, из чего будет понятно что оттуда делается только pull.

$ git remote rename origin upstream

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

$ git remote set-url --push upstream disabled

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

$ git remote -v
upstream    https://github.com/sanmai/phpunit-primer (fetch)
upstream    disabled (push)
$ git push
fatal: 'disabled' does not appear to be a git repository
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

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

Мы готовы к тому, чтобы подключить ваш форк, упомянув его SSH-ссылкой.

$ git remote add origin git@github.com:username/phpunit-primer.git

Ваш форк в Git будет называться origin, с тем расчетом чтобы команды в обычных подсказках, которые показывает Git, работали без необходимости что-то менять перед копированием и вставкой. Как, например, при первом пуше из новой ветки:

$ git push
fatal: The current branch test has no upstream branch.
To push the current branch and set the remote as upstream, use

    git push --set-upstream origin test

Что ещё?

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

Избежать этого можно если запретить коммиты в mainmaster) хуком .git/hooks/pre-commit с таким содержанием:

#!/bin/sh
set -e -x
test $(git rev-parse --abbrev-ref HEAD) != "main"
test $(git rev-parse --abbrev-ref HEAD) != "master"

С таким хуком при попытке коммита в main будет выводиться ошибка:

$ git checkout main
$ git commit
+ git rev-parse --abbrev-ref HEAD
+ test main != main

Готово!

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

  1. Загружаем последние изменения с исходного проекта в ветку main.

    $ git checkout main
    $ git pull
    
  2. Делаем новую ветку с понятным названием на основе ветки main.

    $ git checkout -b fix-for-issue-42
    Switched to a new branch 'fix-for-issue-42'
    
  3. Вносим всевозможные изменения, запускаем тесты и так далее.

    $ composer install
    $ git diff
    $ git add -p
    $ git diff --cached
    $ php vendor/bin/phpunit
    $ git commit
    
  4. Выгружаем коммиты с работой из ветки обратно на GitHub.

    $ git push --set-upstream origin fix-for-issue-42
    Total 0 (delta 0), reused 0 (delta 0)
    To github.com:username/phpunit-primer.git
    * [new branch]      fix-for-issue-42 -> fix-for-issue-42
    Branch 'fix-for-issue-42' set up to track remote branch 'fix-for-issue-42' from 'origin'.
    
  5. Открываем PR, ждем одобрения ведущих разработчиков.

  6. Радуемся сообщению о принятии изменений.

Всё сразу

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

UPSTREAM=example/example
MASTER=$USER/$(basename $UPSTREAM)

То все команды для начала работы и настройки копии будут следующие:

git clone https://github.com/$UPSTREAM
cd $(basename $UPSTREAM)
git remote rename origin upstream
git remote set-url --push upstream disabled
git remote add origin git@github.com:$MASTER.git
tee .git/hooks/pre-commit <<'EOF'
#!/bin/sh
set -e -x
test $(git rev-parse --abbrev-ref HEAD) != "main"
test $(git rev-parse --abbrev-ref HEAD) != "master"
EOF
chmod +x .git/hooks/pre-commit