Git bootcamp and cheat sheet

Python Developer's Guide

Note

This section provides instructions on common tasks in CPython’s workflow. It’s designed to assist new contributors who have some familiarity with Git and GitHub.

If you are new to Git and GitHub, please become comfortable with these instructions before submitting a pull request. As there are several ways to accomplish these tasks using Git and GitHub, this section reflects one method suitable for new contributors. Experienced contributors may desire a different approach.

In this section, we will go over some commonly used Git commands that are relevant to CPython’s workflow.

Note

Setting up Git aliases for common tasks can be useful to you. You can get more information about that in

Git documentation

Forking CPython GitHub repository

You will only need to do this once.

Go to

https://github.com/python/cpython

.

Press Fork located near the top right of the page.

Uncheck “Copy the main branch only”.

Press the Create fork button.

Your forked CPython repository will be created at https://github.com/<username>/cpython.

Cloning a forked CPython repository

You will only need to do this once per machine. From your command line:

$ [email protected]:<username>/cpython.git It is also recommended to configure an upstream remote repository:

$ cdcpython $ gitremoteaddupstreamhttps://github.com/python/cpython You can also use SSH-based or HTTPS-based URLs.

Configure the remotes

Configure git to pull main from the upstream remote:

$ gitconfig--localbranch.main.remoteupstream Since one should never attempt to push to upstream, configure git to push always to origin:

$ [email protected]:<username>/cpython.git Listing the remote repositories

To list the remote repositories that are configured, along with their URLs:

$ gitremote-v You should have two remote repositories: origin pointing to your forked CPython repository, and upstream pointing to the official CPython repository:

origin [email protected]:<username>/cpython.git (fetch) origin [email protected]:<username>/cpython.git (push) upstream https://github.com/python/cpython (fetch) upstream [email protected]:<username>/cpython.git (push) To verify the upstream for main:

$ gitconfigbranch.main.remote It should emit upstream, indicating to track/pull changes for main from the upstream remote.

Once this is verified, update your local clone with the upstream branches:

$ gitfetchupstream Setting up your name and email address

$ gitconfig--globaluser.name"Your Name"$ [email protected] The --global flag sets these parameters globally while the --local flag sets them only for the current project.

Enabling autocrlf on Windows

The autocrlf option will fix automatically any Windows-specific line endings. This should be enabled on Windows, since the public repository has a hook which will reject all commits having the wrong line endings:

$ gitconfig--globalcore.autocrlfinput Creating and switching branches

Important

Never commit directly to the main branch.

Create a new branch from main and switch to it:

$ gitswitch-c<branch-name>main This is equivalent to:

$ # create a new branch from main$ gitbranch<branch-name>main $ # switch to the new branch$ gitswitch<branch-name> To find the branch you are currently on:

$ gitbranch The current branch will have an asterisk next to the branch name. Note, this will only list all of your local branches.

To list all the branches, including the remote branches:

$ gitbranch-a To switch to a different branch:

$ gitswitch<another-branch-name> Other releases are just branches in the repository. For example, to work on the 3.12 release from the upstream remote:

$ gitswitch-c3.12upstream/3.12 Deleting branches

To delete a local branch that you no longer need:

$ gitswitchmain $ gitbranch-D<branch-name> To delete a remote branch:

$ gitpushorigin-d<branch-name> You may specify more than one branch for deletion.

Renaming branch

The CPython repository’s default branch was renamed from master to main after the Python 3.10b1 release.

If you have a fork on GitHub (as described in

Forking CPython GitHub repository

) that was created before the rename, you should visit the GitHub page for your fork to rename the branch there. You only have to do this once. GitHub should provide you with a dialog for this. If it doesn’t (or the dialog was already dismissed), you can rename the branch in your fork manually

by following these GitHub instructions

.

After renaming the branch in your fork, you need to update any local clones as well. This only has to be done once per clone:

$ gitbranch-mmastermain $ gitfetchorigin $ gitbranch-uorigin/mainmain $ gitremoteset-headorigin-a (GitHub also provides these instructions after you rename the branch.)

If you do not have a fork on GitHub, but rather a direct clone of the main repo created before the branch rename, you still have to update your local clones. This still only has to be done once per clone. In that case, you can rename your local branch as follows:

$ gitbranch-mmastermain $ gitfetchupstream $ gitbranch-uupstream/mainmain Staging and committing files

To show the current changes:

$ gitstatus

To stage the files to be included in your commit:

$ gitadd-p# to review and add changes to existing files$ gitadd<filename1><filename2># to add new files

To commit the files that have been staged (done in step 2):

gitcommit-m"This is the commit message."

Reverting changes

To revert changes to a file that has not been committed yet:

$ gitcheckout<filename> If the change has been committed, and now you want to reset it to whatever the origin is at:

$ gitreset--hardHEAD Stashing changes

To stash away changes that are not ready to be committed yet:

$ gitstash To re-apply the last stashed change:

$ gitstashpop Comparing changes

View all non-committed changes:

$ gitdiff Compare to the main branch:

$ gitdiffmain Exclude generated files from diff using an attr

pathspec

(note the single quotes):

$ gitdiffmain':(attr:!generated)'Exclude generated files from diff by default:

$ gitconfigdiff.generated.binarytrueThe generated

attribute

is defined in

.gitattributes

, found in the repository root.

Pushing changes

Once your changes are ready for a review or a pull request, you will need to push them to the remote repository.

$ gitswitch<branch-name> $ gitpushorigin<branch-name> Creating a pull request

Go to

https://github.com/python/cpython

.

Press the New pull request button.

Click the compareacrossforks link.

Select the base repository: python/cpython and base branch: main.

Select the head repository: <username>/cpython and head branch: the branch containing your changes.

Press the Create pull request button.

You should include the issue number in the title of the PR, in the format gh-NNNNN:<PRTitle>.

Linking to issues and pull requests

You can link to issues and pull requests using gh-NNNNN (this form is preferred over #NNNNN). If the reference appears in a list, the link will be expanded to show the status and title of the issue/PR.

When you create a PR that includes gh-NNNNN in the title,

bedevere

will automatically add a link to the issue in the first message.

In addition, pull requests support

special keywords

that can be used to link to an issue and automatically close it when the PR is merged. However, issues often require multiple PRs before they can be closed (for example, backports to other branches), so this features is only useful if you know for sure that a single PR is enough to address and close the issue.

Updating your CPython fork

Scenario:

You forked the CPython repository some time ago.

Time passes.

There have been new commits made in the upstream CPython repository.

Your forked CPython repository is no longer up to date.

You now want to update your forked CPython repository to be the same as the upstream CPython repository.

Please do not try to solve this by creating a pull request from python:main to <username>:main as the authors of the pull requests will get notified unnecessarily.

Solution:

$ gitswitchmain $ gitpullupstreammain $ gitpushoriginmain Note

For the above commands to work, please follow the instructions found in the

Get the source code

section.

Another scenario:

You created some-branch some time ago.

Time passes.

You made some commits to some-branch.

Meanwhile, there are recent changes from the upstream CPython repository.

You want to incorporate the recent changes from the upstream CPython repository into some-branch.

Solution:

$ gitswitchsome-branch $ gitfetchupstream $ gitmergeupstream/main $ gitpushoriginsome-branch You may see error messages like “CONFLICT” and “Automatic merge failed;” when you run gitmergeupstream/main.

When it happens, you need to resolve conflict. See these articles about resolving conflicts:

About merge conflicts

Resolving a merge conflict using the command line

Applying a patch to Git

Scenario:

A patch exists but there is no pull request for it.

Solution:

Download the patch locally.

Apply the patch:

$ gitapply/path/to/patch.diff If there are errors, update to a revision from when the patch was created and then try the gitapply again:

$ gitcheckout$(gitrev-list-n1--before="yyyy-mm-dd hh:mm:ss"main)$ gitapply/path/to/patch.diff If the patch still won’t apply, then a patch tool will not be able to apply the patch and it will need to be re-implemented manually.

If the apply was successful, create a new branch and switch to it.

Stage and commit the changes.

If the patch was applied to an old revision, it needs to be updated and merge conflicts need to be resolved:

$ gitrebasemain $ gitmergetool For very old changes, gitmerge--no-ff may be easier than a rebase, with regards to resolving conflicts.

Push the changes and open a pull request.

Checking out others’ pull requests

Scenario:

A contributor made a pull request to CPython.

Before merging it, you want to be able to test their changes locally.

If you’ve got

GitHub CLI

or

hub

installed, you can do:

$ ghco<pr_number># GitHub CLI$ hubprcheckout<pr_number># hubBoth of these tools will configure a remote URL for the branch, so you can gitpush if the pull request author checked “Allow edits from maintainers” when creating the pull request.

Otherwise, you can run the following commands:

$gitfetchupstreampull/NNNNN/head:pr_NNNNN $gitswitchpr_NNNNN Or set up a Git alias:

Unix/macOS

gitconfig--globalalias.pr'!sh -c "git fetch upstream pull/${1}/head:pr_${1} && git switch pr_${1}" -'Windows cmd

git config --global alias.pr "!sh -c 'git fetch upstream pull/${1}/head:pr_${1} && git switch pr_${1}' -"Windows Powershell

gitconfig--globalalias.pr'!f() { git fetch upstream pull/$1/head:pr_$1 && git checkout pr_$1; }; f'The alias only needs to be done once. After the alias is set up, you can get a local copy of a pull request as follows:

$ gitpr<pr_number> Accepting and merging a pull request

Pull requests can be accepted and merged by a Python Core Developer. You can read more about what to look for before accepting a change

here

.

All pull requests have required checks that need to pass before a change can be merged. See

“Keeping CI green”

for some simple things you can do to help the checks turn green.

At any point, a core developer can schedule an automatic merge of the change by clicking the gray Enable auto-merge (squash) button. You will find it at the bottom of the pull request page. The auto-merge will only happen if all the required checks pass, but the PR does not need to have been approved for a successful auto-merge to take place.

If all required checks are already finished on a PR you’re reviewing, in place of the gray Enable auto-merge button you will find a green Squash and merge button.

In either case, adjust and clean up the commit message.

✅ Here’s an example of a good commit message:

gh-12345: Improve the spam module (GH-777) * Add method A to the spam module * Update the documentation of the spam module ❌ Here’s an example of a bad commit message:

gh-12345: Improve the spam module (#777) * Improve the spam module * merge from main * adjust code based on review comment * rebased The bad example contains bullet points that are a direct effect of the PR life cycle, while being irrelevant to the final change.

Finally, press the Confirm squash and merge button.

Cancelling an automatic merge

If you notice a problem with a pull request that was accepted and where auto-merge was enabled, you can still cancel the workflow before GitHub automatically merges the change.

Press the gray Disable auto-merge button on the bottom of the pull request page to disable automatic merging entirely. This is the recommended approach.

To pause automatic merging, apply the “DO-NOT-MERGE” label to the PR or submit a review requesting changes. The latter will put an “awaiting changes” label on the PR, which pauses the auto-merge similarly to “DO-NOT-MERGE”. After the author submits a fix and re-requests review, you can resume the auto-merge process either by submitting an approving review or by dismissing your previous review that requested changes.

Note that pushing new changes after the auto-merge flow was enabled does NOT stop it.

Backporting merged changes

After a pull request has been merged into main, it may need to be backported to one or more

maintenance

or

security

branches. This is indicated by the needsbackporttoX.Y labels on the pull request.

miss-islington will automatically attempt to create backport PRs for the versions indicated by these labels. If miss-islington cannot create a backport PR due to conflicts, you can use the

cherry-picker

tool to create the backport and resolve the conflicts manually.

You need the commit hash of the squashed commit that was merged into the main branch. miss-islington should post a comment when it is unable to create the backport automatically, including the full command and commit hash. If that comment is not posted, look for an event on the merged pull request similar to:

<core_developer> merged commit <commit_sha1> into python:main <sometime> ago. By following the link to <commit_sha1>, you can get the full commit hash.

Alternatively, the commit hash can also be obtained with the following Git commands:

$ gitfetchupstream $ gitrev-parse":/gh-<PR number>"These commands print the hash of the commit whose message contains gh-<PRnumber>.

You can then use the commit hash and the

cherry-picker

tool to create the backport. In the following command, <branch> is the target maintenance branch (for example, 3.12):

$ cherry_picker <commit_sha1> <branch> Then, follow the instructions provided. You will have to identify the files with conflicts, fix them, and build and run applicable tests if necessary. When you are finished, gitadd all modified files and run cherry_picker--continue to push the backport.

The tool usually generates the commit message automatically. If it does not, use the following format: Keep the original commit message unchanged, except for removing the backport pull request number ((#XXXXX)). At the end of the message, append a (cherrypickedfromcommit<commit_sha1>) line.

The format of a correct backport commit message is:

[<branch>] gh-XXXXX: <original commit title> (GH-XXXXX) <original commit body> (cherry picked from commit <commit_sha1>) Here gh-XXXXX is the GitHub issue number, and (GH-XXXXX) is the original pull request number.

An example of a bad backport commit message:

gh-XXXXX: Custom title (GH-XXXXX) (#XXXXX) * Custom message When opening the backport PR, its title PR must follow the same format as the commit title, beginning with the [<branch>] prefix and referencing the original PR with a (GH-XXXXX) suffix. For example:

[3.15] gh-12345: Fix the spam module (GH-24680) After the backport PR is opened, miss-islington will link it to the original PR and remove the corresponding backport label.

Editing a pull request prior to merging

When a pull request submitter has enabled the

Allow edits from maintainers

option, Python Core Developers may decide to make any remaining edits needed prior to merging themselves, rather than asking the submitter to do them. This can be particularly appropriate when the remaining changes are bookkeeping items like updating a news entry.

To edit an open pull request that targets main:

In the pull request page, under the description, there is some information about the contributor’s forked CPython repository and branch name that will be useful later:

<contributor> wants to merge 1 commit into python:main from <contributor>:<branch_name> Fetch the pull request, using the

git pr

alias:

$ gitpr<pr_number> This will checkout the contributor’s branch at <pr_number>.

Make and commit your changes on the branch. For example, merge in changes made to main since the PR was submitted (any merge commits will be removed by the later SquashandMerge when accepting the change):

$ gitfetchupstream $ gitmergeupstream/main $ gitadd<filename> $ gitcommit-m"<message>"

Push the changes back to the contributor’s PR branch:

$ [email protected]:<contributor>/cpython<pr_number>:<branch_name>

Optionally,

delete the PR branch

.

GitHub CLI

GitHub CLI

is a command-line interface that allows you to create, update, and check GitHub issues and pull requests.

You can install GitHub CLI

by following these instructions

. After installing, you need to authenticate:

$ ghauthlogin Examples of useful commands:

Create a PR:

$ ghprcreate

Check out another PR:

$ ghco<pr-id>

Set ssh as the Git protocol:

$ ghconfigsetgit_protocolssh

Set the browser:

$ ghconfigsetbrowser<browser-path>

Git worktree

With Git worktrees, you can have multiple isolated working trees associated with a single repository (the .git directory). This allows you to work simultaneously on different version branches, eliminating the need for multiple independent clones that need to be maintained and updated separately. In addition, it reduces cloning overhead and saves disk space.

Setting up Git worktree

With an existing CPython clone (see

Cloning a forked CPython repository

), rename the cpython directory to main and move it into a new cpython directory, so we have a structure like:

cpython └── main (.git is here) Next, create worktrees for the other branches:

$ cdcpython/main $ gitworktreeadd-b3.11../3.11upstream/3.11 $ gitworktreeadd-b3.12../3.12upstream/3.12 This gives a structure like this, with the code for each branch checked out in its own directory:

cpython ├── 3.11 ├── 3.12 └── main Using Git worktree

List your worktrees, for example:

$ gitworktreelist /Users/my-name/cpython/main b3d24c40df [main]/Users/my-name/cpython/3.11 da1736b06a [3.11]/Users/my-name/cpython/3.12 cf29a2f25e [3.12]Change into a directory to work from that branch. For example:

$ cd../3.12 $ gitswitch-cmy-3.12-bugfix-branch# create new branch$ # make changes, test them, commit$ gitpushoriginmy-3.12-bugfix-branch $ # create PR$ gitswitch3.12# switch back to the 3.12 branch...