Skip to content

Add Git Bootcamp page #144

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 16, 2017
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
268 changes: 268 additions & 0 deletions gitbootcamp.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
.. _gitbootcamp:

Git Bootcamp and Cheat Sheet
============================

In this section, we'll go over some commonly used git commands that are
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a nit: I think this should either be "Git" or :cmd:`git`.

relevant to CPython's workflow.

.. contents::


Forking CPython GitHub Repository
---------------------------------

You'll only need to do this once.

1. Go to https://github.com/python/cpython.

2. Press ``Fork``.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Press the Fork button on the top-right".


3. When asked where to fork the repository, choose to fork it to your username.

4. A fork will be created at https://github.com/<username>/cpython.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your fork?



Cloning The Forked CPython Repository
-------------------------------------

From your command line::
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this section you could also mention that this is only necessary once.


$ git clone [email protected]:<username>/cpython.git
$ cd cpython
$ git remote add upstream [email protected]:python/cpython.git
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using $ has the disadvantage that is not possible to copy/paste all the instructions at once, but it makes clear that these are shell commands. Since most of the commands are one-liners, it might be ok to leave the $.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is possible to set the default syntax highlight on a page-by-page basis, and specify that this page should use shell highlight. I'm not sure what the default is for the devguide.

You should be able to set it by adding .. highlight:: console (http://pygments.org/docs/lexers/#pygments.lexers.shell.BashSessionLexer) at the beginning of the page.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps explain briefly (even with an inline comment), why adding the remote is necessary/useful.



Listing the Remote Repositories
-------------------------------

::
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A sentence to explain why this is useful would be nice.
Is this useful to double-check that both origin and upstream are configured correctly?


$ git remote -v


Creating and Switching Branches
-------------------------------

.. note::
Never commit directly to the ``master`` branch.

Create a new branch::
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Create a new branch some-branch from master, and switch to it:".


$ git checkout -b some-branch master # creates a new branch off master
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a double space in front of #, or put the comment on the previous line.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use branch-name instead of some-branch, or whatever we have used elsewhere if this is already documented.
Also consider using enclosing variables within <,,,> (while it's quite evident what are the parts that need to be replaced, while copy/pasting several commands, I've more than once replaced the wrong arg -- e.g. I replaced master with the name of the new branch I wanted to create -- and the <...> make the variable arg stand out).


which is equal to::
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might want to mention it's equivalent if you are starting from the master branch, otherwise explicitly add the master part to the git branch step.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I made the change in line 56.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"This is equivalent to:"


$ git branch some-branch master # create 'some-branch' off 'master', without checking it out
$ git checkout some-branch # check out 'some-branch'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above, either double space before the comment or comments on separate lines.


To find out which branch you are in now::

$ git branch

The current branch will have an asterisk next to the branch name. Note, this
will list all of your local branches.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lately I've been using git status instead of git branch, as it tells the current branch at the beginning.
Should this be mentioned instead/in addition to git branch?


To list all the branches, including the remote branches::

$ git branch -a

To switch to a different branch::

$ git checkout another-branch-name


Delete Local Branch
-------------------

To delete branch that you no longer need::

$ git branch -D branch-to-delete
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be -d? or are they equivalent?
Also you might want to mention how to delete the branch on the fork, and when is this useful, e.g.:

To delete a local branch that you no longer need (e.g. because the PR has been merged), use::

   $ git branch -d <branch_name>

If you also pushed the branch on your fork, you can delete it with::

   $ git push origin --delete <branch_name>

or through the `delete branch` (?) button that appears after a PR has been merged.

As far as I understand:

  • in order to create the pr I have to first push the branch on my fork
  • after I create the PR the branch exists in my local clone and in my GH fork, but not on the upstream CPython repo
  • after the PR is merged, I can delete the local branch with git branch -d <branch_name> and the fork one either with the button or with git push origin --delete <branch_name>
  • after this both the local and the fork branches should have been deleted.
  • it shouldn't be a problem to leave the branch around, especially in the fork, and perhaps there's an easy way to delete all the merged branches every once in a while (git prune?)

Please correct me if I'm wrong.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The capital -D indicates 'force delete', that will delete branch that has not yet been merged.
I use this in cherry_picker.py and just got used to it.



Staging and Committing Files
----------------------------

1. To show the current changes::

$ git status
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe mention git diff HEAD here?


2. To stage the files to be included in your commit::

$ git add /path/to/file1 path/to/file2 path/to/file3
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra / in the first path?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will remove.


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

$ git commit -m "This is the commit message. Prefix it with bpo-XXXX."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use $ git commit -m "bpo-NNNNN: this is the commit message." and add "Remember to include the issue number (bpo-NNNNN) in each commit message." on the next line.



Reverting Changes
-----------------

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

$ git checkout path/to/file
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line has a 4-spaces indent, instead of 3-spaces.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in the next commit.


If the change has been committed, and now you want to reset it to whatever
the origin is at::

$ git reset --hard HEAD
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the difference between this and git reset --hard HEAD^ (note the trailing ^)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure... I don't normally use the ^

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this section a bit confusing/misleading.
When compared to HG, the git checkout <path> is equivalent to hg revert <path>, and it only reverts local changes to <path> that haven't been committed yet.
OTOH, git reset --hard HEAD seems equivalent to hg rollback, i.e. it undoes the last commit and edit the history (and I assume this doesn't work if the commit has been pushed already -- this is not mentioned but should be added).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two more scenarios where I used hg revert that are not covered here:

  1. how to revert all changes in the working copy (i.e. the equivalent of hg revert -a -- git reset? git reset --hard?).
  2. how to revert the fix while leaving the test to verify if they fail without the fix.

The second case works differently since we are using PRs. With HG I could apply a patch and do hg revert Lib/ to revert all the changes in Lib while leaving the tests unchanged. I think the git equivalent would be something like git checkout master Lib/, after I pulled a PR as described in a later section.



Stashing Changes
----------------

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coming from HG, the concept of staging and stashing are foreign. While I am now somewhat familiar with them, I'd prefer a goal-oriented approach, i.e.:

If you want to temporarily revert changes that are not ready to be committed yet, use::

   $ git stash

You can then retrieve those changes by using::

   $ git stash pop

(Feel free to change the wording if it doesn't accurately reflect what git stash does.)


$ git stash

To re-apply last stashed change::

$ git stash pop


Pushing Changes
---------------

Once your changes are ready for a review or a pull request, you'll need to push
them to the remote repository.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"...push them to your GitHub fork."


::

$ git checkout some-branch
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I omit this, will the next command fail?
Usually I push after I commit, so I'm already in the right branch and I don't need to switch, but I think the following command should work regardless of the branch I'm in.

$ git push origin some-branch


Creating a Pull Request
-----------------------

1. Go to https://github.com/python/cpython.

2. Click ``compare across forks`` link.

3. Select the base fork: ``python/cpython`` and base branch: ``master``

4. Select the head fork: ``<username>/cpython`` and base branch: the branch
containing your changes.

5. Press ``Create Pull Request`` button.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it easier to just go on my fork, and press the "Compare & pull request" button?
See e.g. https://www.drupal.org/files/pull_request_test_highlighted.png

I'm not sure when that button is available, but so far I've always found it after pushing a new branch.

Edit: after reading below I'm not sure if this works for other branches, but maybe it does?



Syncing With Upstream
---------------------
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be easier to configure the remote to upstream. This could be explained either in the "Cloning The Forked CPython Repository" section, or in this section, as an alternative to the provided commands.


Scenario:

- You forked cpython repository some time ago.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either CPython or ``cpython``.

- Time passes.
- There have been new commits made in upstream cpython repository.
- Your forked cpython repository is no longer up to date.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So far I've used "(GH) fork" to refer to the GitHub fork at github.com/myusername/cpython, and "(local) clone" to refer to the local copy on my pc.
I'm not sure if this terminology is correct, but IIUC the commands below update both, not only the fork.

- You now want to update your forked cpython repository to be the same as
upstream.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I like the goal-oriented approach, I somewhat dislike the format. I think this whole bullet list can be replaced with a single sentence: "If you want to update your repo with the latest commits from the upstream CPython repo, use::"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to want to update master on my fork, if I awalys pull from upstream?
So far I've been always pulling from upstream, and only pushed branches to my fork. I'm not sure if pushing branches also updates master.


Solution::

$ git checkout master
$ git pull --rebase upstream master
$ git push origin master
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW I've been using git pull -v since it shows where it's pulling from (IIRC without -v it doesn't say, and by default using git pull in a branch, pulls from master and not from upstream). Here you are mentioning the branches explicitly, so maybe it's not necessary (although it might be if you accidentally reverse the order).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline comments would be useful:

   $ git checkout master  # switch to the `master` branch
   $ git pull --rebase upstream master  # pull new changes from `upstream` to `master`
   $ git push origin master  # push the new changes to my local fork (`origin`)


The `--rebase` is only needed if you have local changes to the branch.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Above you mentioned that you should never commit to master, so I would assume --rebase is never needed.


Another scenario:

- You created ``some-branch`` some time ago.
- Time passes.
- You made some commits to ``some-branch``.
- Meanwhile, there are recent changes from upstream cpython repository.
- You want to incorporate the recent changes from upstream into ``some-branch``.

Solution::

$ git checkout some-branch
$ git fetch upstream
$ git rebase upstream/master
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recently came across this scenario, and the solution I used was:

git pull -v (if remote == upstream)
git pull -v upstream master (if not)
git checkout <branch>
git rebase master

This has the advantage of also updating the local master. I think this also assumes that the first command is done in the master branch, otherwise it doesn't work (whereas the alternative should always work).

Perhaps this should also mention two other things:

  1. rebasing might result in conflicts (with a link to a section that explains how to solve them);
  2. when is it necessary to update/rebase? IOW should I always update before every commit, or I can just update my pr without updating?



Backporting Merged Changes
--------------------------
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Who is the target here? Are contributors supposed to do this or is it only for core devs?
Perhaps this should be clarified at the beginning of the page, with a note stating something like "These commands apply to all contributors, unless otherwise specified." and then add notes like "These commands only apply to core-devs." to the relevant sections.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think core dev who merged the PR is expected to do the backport, mainly because it involves removing needs backport to X.Y label, and applying cherry-pick for X.Y label. Contributor don't have the right to apply and remove labels themselves. It's fine to ask the contributor to do the cherry-pick themselves, but core devs should still apply/remove the appropriate labels.

For now I will just state here that the core dev is expected to do the backport. Once the cherry-picking bot is in place, this section will just be obsolete.


When a pull request has been merged to master, and it needs to be backported
into one of the maintenance branches.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentence seems incomplete.
"After your PR has been merged to master, it might need to be backported to the maintanance branches."?


First, obtain the commit sha1 from the merged pull request:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Below you use "hash", so perhaps s/sha1/hash/


1. Go to the merged pull request page, for example::

https://github.com/python/cpython/pull/PR-ID

2. Scroll down and find the activity that says something like::

CoreDeveloper merged commit <hash> into python:master ...

3. Follow the link to <hash>.

4. Copy the complete hash value.

The commit hash will be used below.

To backport the commit to 3.6::

$ git fetch upstream
$ git checkout -b backport-someissue-3.6 upstream/3.6
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be done while in the master branch, but it's not specified.

Would this work too?

git pull -v (or git pull -v upstream master)
git checkout -b backport-bpoNNNNN-3.6 3.6

Coming from HG, my thinking is more or less:

  1. update the local repo
  2. update the working copy based off the updated local repo

Whereas by reading this commands it seems to me that a local upstream exists and needs to be updated (even though upstream itself is remote), and by updating the local upstream, master doesn't get updated (and I will need to remember to update it next time I create a branch from it).

I therefore find my approach (update master from upstream, branch off master) simpler, but I'm not sure if it's "idiomatic".
I'm also not sure if updating master also updates the local 3.6, or if I need to update it separately. If I do, perhaps it's easier to just update the branch you are branching from every time...

$ git cherry-pick -x hashvalue
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/hashvalue//?

$ git push origin backport-someissue-3.6
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline comments might be useful.


Go to https://github.com/python/cpython to create the pull request. Select
``3.6`` as the base branch, and ``backport-someissue-3.6`` as the head branch.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't tried tried backporting a changeset yet, but I think the "Compare & create PR" button on my fork should work for this too. Hopefully it's smart enough to compare with 3.6 (but that might depend on how the branch got created?), but if not, I believe it's possible to manually select the branch after pressing the button.
I think both cases should be covered in the "Creating a Pull Request" section, and you can link to it from here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Compare & create PR by default will select the python:master as base branch, we'll need to manually select the appropriate base branch. :)

With cherry_picker.py, the correct maintenance branch will be pre-selected :)
So I'm replacing this whole section with a reference to cherry_picker.py, now that it's been migrated to the core-workflow repo.


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You forgot to mention your script. 😄

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once it's moved to core-workflow, I'll update this :)


Downloading Other's Patches
---------------------------

Scenario:

- A contributor made a pull request to cpython.
- Before merging it, you want to be able to test their changes locally.

Set up the following git alias::

$ git config --global alias.pr '!sh -c "git fetch upstream pull/${1}/head:pr_${1} && git checkout pr_${1}" -'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the third variant of what I believe is the same thing. The other two I saw are:

  1. git fetch <cpythonorigin> pull/<prnumber>/head:pr/<prnumber> that creates a pr/prnumber branch.
  2. Adding fetch = +refs/pull/*/head:refs/remotes/upstream/pr/* under [remote "upstream"] in the .git/config and then use git checkout pr/prnumber .

The first one seem equivalent to the alias, except that it needs to be done manually every time. The second one doesn't require to create an additional alias but it needs to be added to each .git/config (I guess it's also possible to add it with git config ..., and maybe even set it as global. The alias also seem to invoke sh, so I guess it won't work on Windows.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This alias was suggested by @zware. I don't actually know about the other variants. So I'll just leave this as is.


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::

$ git pr <pr_number>

For example, to fetch and checkout pull request #777::

$ git pr 777
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the only example in the page -- elsewhere you only used .
I'm not sure it's needed -- it doesn't hurt having it, but it's not particularly useful either, and it's a bit inconsistent with the rest.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll remove the example to make it consistent.


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another scenario that is not covered here is: how can I update a PR that I pulled from a contributor (e.g. to add Misc/NEWS)?
This came up on #python-dev a few days ago, and afaiu:

  • you can add your changes and push to the contributor repo using git push -u [email protected]:<contributor>/cpython.git <branch> (or add a remote with git remote add <contributor> [email protected]:<contributor>/cpython.git and then git push -u <contributor> <branch>). In order to do this the contributor must have allowed the maintainers to edit the prs (should be allowed by default).
  • you can add your changes, push the branch to your fork, and create a new and separate PR that replaces the original one.

It was not clear how the author is determined if multiple users (contributor and core-dev) worked on the same PR: author of the first commit? owner of the repo the PR comes from? author with most commits/loc in the PR?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't had to do this myself, so I don't have the answer ...


Accepting and Merging A Pull Request
------------------------------------
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be marked as core-devs only, since contributors can't accept/merge.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mentioned in the next line.


Pull requests can be accepted and merged by a Python Core Developer.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A question about "merging etiquette": if another core-dev submitted a PR, and it looks good to me, should I go ahead and merge it, or should I just leave a positive review and let the original core-dev merge it?
I think the latter is better, but I've seen the former happen.
Perhaps this could be clarified here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this is to be asked at core-workflow or python-committers? :)


1. At the bottom of the pull request page, click the ``Squash and merge``
button.

2. Adjust and clean up the commit message. Replace the reference
to GitHub PR #XXX into GH-XXX.

Example of good commit message::

bpo-12345: Improve the spam module (GH-777)

* Add method A to the spam module
* Update the documentation of the spam module

Example of bad commit message::

bpo-12345: Improve the spam module (#777)

* Improve the spam module
* merge from master
* adjust code based on review comment
* rebased

3. Press the ``Confirm squash and merge`` button.
3 changes: 3 additions & 0 deletions index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ contributing to Python:
* :doc:`help`
* PEPs_ (Python Enhancement Proposals)
* :doc:`gitdevs`
* :doc:`gitbootcamp`

.. _branchstatus:

Expand Down Expand Up @@ -161,6 +162,7 @@ Guide for contributing to Python:
* :doc:`buildbots`
* :doc:`coverity`
* :doc:`gitdevs`
* :doc:`gitbootcamp`

It is **recommended** that the above documents be read in the order listed. You
can stop where you feel comfortable and begin contributing immediately without
Expand Down Expand Up @@ -299,6 +301,7 @@ Full Table of Contents
buildslave
gitdevs
motivations
gitbootcamp


.. _Buildbot status: https://www.python.org/dev/buildbot/
Expand Down