From 4bd4d85d219999bf0a607f980d338ec4046b4fe4 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Sun, 7 May 2023 01:48:43 +0900 Subject: [PATCH 01/36] Update for GitHub --- src/doc/en/developer/coding_basics.rst | 4 +- src/doc/en/developer/coding_in_python.rst | 4 +- src/doc/en/developer/conf.py | 4 +- .../{advanced_git.rst => git_advanced.rst} | 65 +- src/doc/en/developer/git_background.rst | 85 ++- src/doc/en/developer/git_basic.rst | 468 ++++++++++++++ src/doc/en/developer/git_setup.rst | 61 +- src/doc/en/developer/git_trac.rst | 595 ------------------ src/doc/en/developer/github.rst | 407 ++++++++++++ src/doc/en/developer/index.rst | 201 +++--- src/doc/en/developer/manual_git.rst | 498 --------------- src/doc/en/developer/packaging.rst | 8 +- .../en/developer/packaging_sage_library.rst | 6 +- src/doc/en/developer/review.rst | 262 ++++++++ src/doc/en/developer/reviewer_checklist.rst | 137 ---- src/doc/en/developer/static/workflow.png | Bin 0 -> 61263 bytes src/doc/en/developer/ticket_badges.png | Bin 47315 -> 0 bytes src/doc/en/developer/trac.rst | 491 --------------- .../{walk_through.rst => walkthrough.rst} | 197 +++--- src/doc/en/developer/workflows.rst | 264 ++++---- src/doc/en/developer/workspace.rst | 6 +- src/doc/en/website/root_index.html | 4 +- src/doc/en/website/templates/index.html | 2 +- src/doc/en/website/templates/index_furo.html | 2 +- 24 files changed, 1566 insertions(+), 2205 deletions(-) rename src/doc/en/developer/{advanced_git.rst => git_advanced.rst} (85%) create mode 100644 src/doc/en/developer/git_basic.rst delete mode 100644 src/doc/en/developer/git_trac.rst create mode 100644 src/doc/en/developer/github.rst delete mode 100644 src/doc/en/developer/manual_git.rst create mode 100644 src/doc/en/developer/review.rst delete mode 100644 src/doc/en/developer/reviewer_checklist.rst create mode 100644 src/doc/en/developer/static/workflow.png delete mode 100644 src/doc/en/developer/ticket_badges.png delete mode 100644 src/doc/en/developer/trac.rst rename src/doc/en/developer/{walk_through.rst => walkthrough.rst} (58%) diff --git a/src/doc/en/developer/coding_basics.rst b/src/doc/en/developer/coding_basics.rst index 81be2129d6b..e8f96758630 100644 --- a/src/doc/en/developer/coding_basics.rst +++ b/src/doc/en/developer/coding_basics.rst @@ -473,7 +473,7 @@ information. You can use the existing functions of Sage as templates. Note the trailing underscores which makes the citations into hyperlinks. See below for more about the master bibliography file. For more about citations, see the `Sphinx/reST markup for citations - `_. For links to trac tickets + `_. For links to GitHub issues and PRs or wikipedia, see :ref:`chapter-sage_manuals_links`. - A **TESTS** block (highly recommended). @@ -1172,7 +1172,7 @@ framework. Here is a comprehensive list: .. CODE-BLOCK:: rest - The following should yield 4. See :trac:`2`. :: + The following should yield 4. See :issue:`2`. :: sage: 2+2 # optional - bug 5 diff --git a/src/doc/en/developer/coding_in_python.rst b/src/doc/en/developer/coding_in_python.rst index 22447c04512..7b5cbe739d2 100644 --- a/src/doc/en/developer/coding_in_python.rst +++ b/src/doc/en/developer/coding_in_python.rst @@ -45,7 +45,7 @@ using one of two mechanisms: The Sage library declares these packages as dependencies and ensures that versions that provide features of Python 3.11 are available. -Meta-ticket :trac:`29756` keeps track of newer Python features and serves +Meta-ticket :issue:`29756` keeps track of newer Python features and serves as a starting point for discussions on how to make use of them in the Sage library. @@ -599,7 +599,7 @@ in the future. We call this a *deprecation*. Deprecated code can only be removed one year after the first stable release in which it appeared. -Each deprecation warning contains the number of the trac ticket that defines +Each deprecation warning contains the number of the GitHub PR that defines it. We use 666 in the examples below. For each entry, consult the function's documentation for more information on its behaviour and optional arguments. diff --git a/src/doc/en/developer/conf.py b/src/doc/en/developer/conf.py index bde00a0970d..9dcfc261ed1 100644 --- a/src/doc/en/developer/conf.py +++ b/src/doc/en/developer/conf.py @@ -21,7 +21,7 @@ html_static_path = [] + html_common_static_path # General information about the project. -project = "Developer's Guide" +project = "Developer Guide" # The name for this set of Sphinx documents. html_title = project @@ -33,6 +33,6 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, document class [howto/manual]). latex_documents = [ - ('index', 'developer.tex', 'Developer\'s Guide', + ('index', 'developer.tex', 'Developer Guide', 'The Sage Development Team', 'manual'), ] diff --git a/src/doc/en/developer/advanced_git.rst b/src/doc/en/developer/git_advanced.rst similarity index 85% rename from src/doc/en/developer/advanced_git.rst rename to src/doc/en/developer/git_advanced.rst index 3adde7bd6c3..cae5924700e 100644 --- a/src/doc/en/developer/advanced_git.rst +++ b/src/doc/en/developer/git_advanced.rst @@ -6,21 +6,11 @@ Advanced Git ============ -.. WARNING:: - - **Sage development moved to GitHub in February 2023.** After the transition, - some parts of this guide (especially those related with `the Sage Trac - server `_) became obsolete and need to be - updated according to the new workflow on GitHub. See our `transition guide - from Trac to GitHub - `_ - for the preliminary version of the workflow. - This chapter covers some advanced uses of git that go beyond what is required to work with branches. These features can be used in Sage development, but are not really necessary to contribute to Sage. If you are just getting started with Sage development, you should read -:ref:`chapter-walkthrough` and :ref:`chapter-manual-git` instead. +:ref:`chapter-walkthrough` and :ref:`chapter-git-basic` instead. Detached Heads and Reviewing Tickets @@ -34,46 +24,51 @@ without a branch, this is called "detached head". If you have the commit already in your local history, you can directly check it out without requiring internet access:: - [user@localhost sage]$ git checkout a63227d0636e29a8212c32eb9ca84e9588bbf80b - Note: checking out 'a63227d0636e29a8212c32eb9ca84e9588bbf80b'. + [user@localhost sage]$ git checkout f9a0d54099d758ccec731a38929902b2b9d0b988 + Note: switching to 'f9a0d54099d758ccec731a38929902b2b9d0b988'. You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this - state without impacting any branches by performing another checkout. + state without impacting any branches by switching back to a branch. If you want to create a new branch to retain commits you create, you may - do so (now or later) by using -b with the checkout command again. Example: + do so (now or later) by using -c with the switch command. Example: - git checkout -b new_branch_name + git switch -c + + Or undo this operation with: + + git switch - + + Turn off this advice by setting config variable advice.detachedHead to false - HEAD is now at a63227d... Szekeres Snark Graph constructor + HEAD is now at f9a0d54099 Fix a slow doctest in matrix_integer_dense_hnf.py If it is not stored in your local git repository, you need to download -it from the trac server first:: +it from the upstream repo first:: - [user@localhost sage]$ git fetch trac a63227d0636e29a8212c32eb9ca84e9588bbf80b - From ssh://trac/sage - * branch a63227d0636e29a8212c32eb9ca84e9588bbf80b -> FETCH_HEAD + [user@localhost sage]$ git fetch upstream f9a0d54099d758ccec731a38929902b2b9d0b988 + From /~https://github.com/sagemath/sage + * branch f9a0d54099d758ccec731a38929902b2b9d0b988 -> FETCH_HEAD [user@localhost sage]$ git checkout FETCH_HEAD - HEAD is now at a63227d... Szekeres Snark Graph constructor + HEAD is now at f9a0d54099 Fix a slow doctest in matrix_integer_dense_hnf.py Either way, you end up with your current HEAD and working directory that is not associated to any local branch:: [user@localhost sage]$ git status - # HEAD detached at a63227d - nothing to commit, working directory clean + HEAD detached at f9a0d54099 + nothing to commit, working tree clean This is perfectly fine. You can switch to an existing branch (with the usual ``git checkout my_branch``) and back to your detached head. -Detached heads can be used to your advantage when reviewing -tickets. Just check out the commit (look at the "Commit:" field on the -trac ticket) that you are reviewing as a detached head. Then you can -look at the changes and run tests in the detached head. When you are -finished with the review, you just abandon the detached head. That way -you never create a new local branch, so you don't have to type ``git -branch -D my_branch`` at the end to delete the local branch that you +Detached heads can be used to your advantage when reviewing tickets. Just check +out the commit (look at the "Commits" tab of the PR) that you are reviewing as +a detached head. Then you can look at the changes and run tests in the detached +head. When you are finished with the review, you just abandon the detached +head. That way you never create a new local branch, so you don't have to type +``git branch -D my_branch`` at the end to delete the local branch that you created only to review the ticket. @@ -192,12 +187,12 @@ Reset and Recovery Git makes it very hard to truly mess up. Here is a short way to get back onto your feet, no matter what. First, if you just want to go back to a working Sage installation you can always abandon your -working branch by switching to your local copy of the ``master`` +working branch by switching to your local copy of the ``develop`` branch:: - [user@localhost sage]$ git checkout master + [user@localhost sage]$ git checkout develop -As long as you did not make any changes to the ``master`` branch +As long as you did not make any changes to the ``develop`` branch directly, this will give you back a working Sage. If you want to keep your branch but go back to a previous commit you @@ -264,7 +259,7 @@ file is in exactly the same state as when the hash was computed. This also means that you can't change history without modifying the hash. If others branched off your code and then you rewrite history, then the others are thoroughly screwed. So, ideally, you would only -rewrite history on branches that you have not yet pushed to trac. +rewrite history on branches that you have not yet pushed to a public repo. As an advanced example, consider three commits A, B, C that were made on top of each other. For simplicity, we'll assume they just added a diff --git a/src/doc/en/developer/git_background.rst b/src/doc/en/developer/git_background.rst index dbea3ed11eb..bff3be6f9a2 100644 --- a/src/doc/en/developer/git_background.rst +++ b/src/doc/en/developer/git_background.rst @@ -2,59 +2,54 @@ .. _chapter-git-background: -=================== -Tips and References -=================== +======================= +Git Tips and References +======================= -This chapter contains additional material about the git revision +This chapter contains additional material about the Git revision control system. See :ref:`chapter-git-setup` for the minimal steps needed for Sage development. - - - - .. _section-git-configuration: Configuration Tips ================== -Your personal git configurations are saved in the ``~/.gitconfig`` +Your personal Git configurations are saved in the ``~/.gitconfig`` file in your home directory. Here is an example: .. CODE-BLOCK:: text [user] - name = Your Name - email = you@yourdomain.example.com + name = Alice Adventure + email = alice@wonderland.com [core] editor = emacs -You can edit this file directly or you can use git to make changes for +You can edit this file directly or you can use Git to make changes for you:: - [user@localhost ~] git config --global user.name "Your Name" - [user@localhost ~] git config --global user.email you@yourdomain.example.com - [user@localhost ~] git config --global core.editor vim - + [alice@localhost ~] git config --global user.name "Alice Adventure" + [alice@localhost ~] git config --global user.email alice@wonderland.com + [alice@localhost ~] git config --global core.editor vim Aliases ------- -Aliases are personal shortcuts for git commands. For example, you +Aliases are personal shortcuts for Git commands. For example, you might want to be able to shorten ``git checkout`` to ``git co``. Or you may want to alias ``git diff --color-words`` (which gives a nicely formatted output of the diff) to ``git wdiff``. You can do this with:: - [user@localhost ~] git config --global alias.ci "commit -a" - [user@localhost ~] git config --global alias.co checkout - [user@localhost ~] git config --global alias.st "status -a" - [user@localhost ~] git config --global alias.stat "status -a" - [user@localhost ~] git config --global alias.br branch - [user@localhost ~] git config --global alias.wdiff "diff --color-words" + [alice@localhost ~] git config --global alias.ci "commit -a" + [alice@localhost ~] git config --global alias.co checkout + [alice@localhost ~] git config --global alias.st "status -a" + [alice@localhost ~] git config --global alias.stat "status -a" + [alice@localhost ~] git config --global alias.br branch + [alice@localhost ~] git config --global alias.wdiff "diff --color-words" The above commands will create an ``alias`` section in your ``.gitconfig`` file with contents like this: @@ -75,9 +70,10 @@ Editor To set the editor to use for editing commit messages, you can use:: - [user@localhost ~] git config --global core.editor vim + [alice@localhost ~] git config --global core.editor vim + +or set the ``EDITOR`` environment variable. -or set the `EDITOR` environment variable. Merging ------- @@ -91,7 +87,7 @@ To enforce summaries when doing merges (``~/.gitconfig`` file again): Or from the command line:: - [user@localhost ~] git config --global merge.log true + [alice@localhost ~] git config --global merge.log true .. _section-fancy-log: @@ -99,16 +95,16 @@ Or from the command line:: Fancy Log Output ---------------- -Here is an alias to get a fancy log output; it should go in the +Here is an alias to get a fancy log output. It should go in the ``alias`` section of your ``.gitconfig`` file: .. CODE-BLOCK:: text lg = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)[%an]%Creset' --abbrev-commit --date=relative -Using this ``lg`` alias gives you the changelog with a colored ascii graph:: +Using this ``lg`` alias gives you the changelog with a colored ASCII graph:: - [user@localhost ~] git lg + [alice@localhost ~] git lg * 6d8e1ee - (HEAD, origin/my-fancy-feature, my-fancy-feature) NF - a fancy file (45 minutes ago) [Matthew Brett] * d304a73 - (origin/placeholder, placeholder) Merge pull request #48 from hhuuggoo/master (2 weeks ago) [Jonathan Terhorst] |\ @@ -129,7 +125,6 @@ Using this ``lg`` alias gives you the changelog with a colored ascii graph:: .. _section-git-tutorials: - Tutorials and Summaries ======================= @@ -146,13 +141,13 @@ Beginner `_ is an extended introduction with intermediate detail. -* The `git parable +* The `Git parable `_ is - an easy read explaining the concepts behind git. + an easy read explaining the concepts behind Git. * `Git foundation `_ - expands on the `git parable`_. + expands on the `Git parable`_. * Although it also contains more advanced material about branches and detached head and the like, the visual summaries of merging and branches @@ -165,49 +160,49 @@ Advanced * `Github help `_ has an excellent series of how-to guides. -* The `pro git book `_ is a good in-depth book on git. +* The `pro Git book `_ is a good in-depth book on Git. * `Github Training `_ has an excellent series of tutorials as well as videos and screencasts. -* The `git tutorial `_. +* The `Git tutorial `_. * `Git ready `_ is a nice series of tutorials. -* `Fernando Perez' git page +* `Fernando Perez' Git page `_ contains many links and tips. -* A good but technical page on `git concepts +* A good but technical page on `Git concepts `_ -* `Git svn crash course `_: git +* `Git svn crash course `_: Git for those of us used to `subversion `_ + Summaries/Cheat Sheets ---------------------- -* A `git cheat sheet `_ is a +* A `Git cheat sheet `_ is a page giving summaries of common commands. -* The `git user manual +* The `Git user manual `_. - Git Best Practices ================== -There are many ways of working with git; here are some posts on the +There are many ways of working with Git. Here are some posts on the rules of thumb that other projects have come up with: -* Linus Torvalds on `git management +* Linus Torvalds on `Git management `_ -* Linus Torvalds on `linux git workflow +* Linus Torvalds on `linux Git workflow `_. Summary: - use the git tools to make the history of your edits as clean as + use the Git tools to make the history of your edits as clean as possible; merge from upstream edits as little as possible in branches where you are doing active development. diff --git a/src/doc/en/developer/git_basic.rst b/src/doc/en/developer/git_basic.rst new file mode 100644 index 00000000000..3d4c27e80d1 --- /dev/null +++ b/src/doc/en/developer/git_basic.rst @@ -0,0 +1,468 @@ +.. highlight:: shell-session + +.. _chapter-git-basic: + +========== +Git Basics +========== + +.. _section-git-ssh: + +Git authentication through SSH +============================== + +In order to push changes securely to a remote repository, git uses +public-key cryptography. This section will show you how to set up the +necessary cryptographic keys for Secure Shell (SSH). + + +Generating your SSH Keys +------------------------ + +Check whether you have already have suitable SSH keys by inspecting ``.ssh`` +directory in your home directory. If you don't have suitable SSH keys yet, you +can create a key pair with the ``ssh-keygen`` tool. + +Follow either `the detailed instructions +`_ +or the following brief instructions:: + + [alice@localhost ~]$ ssh-keygen + Generating public/private rsa key pair. + Enter file in which to save the key (/home/user/.ssh/id_rsa): + Enter passphrase (empty for no passphrase): + Enter same passphrase again: + Your identification has been saved in /home/user/.ssh/id_rsa. + Your public key has been saved in /home/user/.ssh/id_rsa.pub. + The key fingerprint is: + ce:32:b3:de:38:56:80:c9:11:f0:b3:88:f2:1c:89:0a user@localhost + The key's randomart image is: + +--[ RSA 2048]----+ + | .... | + | .. | + | .o+ | + | o o+o. | + |E + . .S | + |+o . o. | + |. o +.o | + | oB | + | o+.. | + +-----------------+ + +This will generate a new random private RSA key +in the ``.ssh`` folder in your home directory. By default, they are + +``~/.ssh/id_rsa`` + Your private key. Keep safe. **Never** hand it out to anybody. + +``~/.ssh/id_rsa.pub`` + The corresponding public key. This and only this file can be safely + disclosed to third parties. + +The ``ssh-keygen`` tool will let you generate a key with a different +file name, or protect it with a passphrase. Depending on how much you +trust your own computer or system administrator, you can leave the +passphrase empty to be able to login without any human intervention. + + +.. _section-github-ssh-key: + +Linking your Public Key to your GitHub Account +---------------------------------------------- + +In order to push your code directly to a branch on your remote ``origin``, Your +GitHub account needs to know your public key. + +No action needed if you have already contributed to any other project on GitHub +and set up git credentials or SSH keys for this. + +New users of GitHub should follow either `Caching your GitHub credentials in +Git +`_ +or generate an SSH keypair, or use an already existing one, and upload the +public key to your GitHub account settings `Connecting to GitHub with SSH +`_ + + +Adding your Public Key for authentication on another server +----------------------------------------------------------- + +If you have an account on a lab or department computer that allows you +to log in remotely via SSH, you can now also use your SSH keys to +log in. Just copy the **public** key file (ending in ``.pub``) to +``~/.ssh/authorized_keys`` on the remote computer and make sure that +the file is only read/writable by yourself. Voila, the next time you +ssh into that machine you don't have to provide your password. + +.. NOTE:: + + In the command above we set up the remote to only track the + ``master`` branch on the Sage repo (the ``-t master`` + option). This avoids clutter by not automatically downloading all + branches ever created. But it also means that you will not fetch + everything that is on the repo by default, and you need to explicitly + tell git which branch you want to get from the repo. See the + :ref:`section-git-checkout` section for examples. + +Note that write operations (``push``) use the ssh protocol (specified by the ``git@`` +part). For this to work, you need to have a GitHub account and to set up your ssh public +key. Authentication is necessary if you want to upload anything to ensure +that it really is from you. + +The above instructions set up the remote to perform read-only operations (``fetch``) +using HTTPS from a mirror of the Sage repo instead. The mirror is faster and +more reliable than our git server. However, this configuration is not recommended if +you use VS Code as an IDE. + +If you want to use SSH only for both ``fetch`` and ``push``, use the +following commands instead:: + + [alice@localhost sage]$ git remote add trac git@trac.sagemath.org:sage.git -t master + [alice@localhost sage]$ git remote -v + origin /~https://github.com/sagemath/sage.git (fetch) + origin /~https://github.com/sagemath/sage.git (push) + trac git@trac.sagemath.org:sage.git (fetch) + trac git@trac.sagemath.org:sage.git (push) + + +.. _section-git-checkout: + +Checking out a branch +--------------------- + +If you want to work with the changes in a PR, you must +make a local copy. In particular, Git has no concept of directly +working with the remote branch, the remotes are only bookmarks for +things that you can get from/to the remote server. Hence, the first +thing you should do is to get everything from the Sage repo's branch +into your local repository. This is achieved by:: + + [user@localhost sage]$ git fetch upstream u/user/description + remote: Counting objects: 62, done. + remote: Compressing objects: 100% (48/48), done. + remote: Total 48 (delta 42), reused 0 (delta 0) + Unpacking objects: 100% (48/48), done. + From trac.sagemath.org:sage + * [new branch] u/user/description -> FETCH_HEAD + +The ``u/user/description`` branch is now temporarily (until you fetch +something else) stored in your local git database under the alias +``FETCH_HEAD``. In the second step, we make it available as a new +local branch and switch to it. Your local branch can have a different +name, for example:: + + [user@localhost sage]$ git checkout -b my_branch FETCH_HEAD + Switched to a new branch 'my_branch' + +creates a new branch in your local git repository named ``my_branch`` +and modifies your local Sage filesystem tree to the state of the files +in that ticket. You can now edit files and commit changes to your +local branch. + + +.. _section-git-push: + +Pushing your changes to a remote +-------------------------------- + +Push your branch to the remote with either:: + + [alice@localhost sage]$ git push --set-upstream origin HEAD:branch-name + +if you started the branch yourself and do not follow any other branch, or use:: + + [alice@localhost sage]$ git push origin HEAD:u/user/description + +if your branch already has an upstream branch. + +Here, ``HEAD`` means that you are pushing the most recent commit (and, by +extension, all of its parent commits) of the current local branch to the remote +branch. + + +.. _section-git-pull: + +Getting changes from a remote +----------------------------- + +A common task during development is to synchronize your local copy of +the branch with the branch on the GitHub sage repo. In particular, assume you +downloaded the branch of a PR made by someone else, say Bob, and made some suggestions for +improvements on the PR. Now Bob incorporated +your suggestions into his branch, and you want to get the added +changes to complete your review. Assuming that you originally got +your local branch as in :ref:`section-git-checkout`, you can just +issue:: + + [bob@localhost sage]$ git pull upstream pull/12345/head + From /~https://github.com/sagemath/sage + * branch refs/pull/35608/head -> FETCH_HEAD + Merge made by the 'ort' strategy. + src/doc/common/python3.inv | Bin 98082 -> 131309 bytes + src/doc/common/update-python-inv.sh | 7 ++++--- + 2 files changed, 4 insertions(+), 3 deletions(-) + +This command downloads the changes from the branch of the PR and merges +them into your local branch. + + +.. _section-git-pull-master: + +Updating Master +--------------- + +The ``master`` branch can be updated just like any other branch. However, your +local copy of the master branch should stay **identical** to the GitHub Sage repo master +branch. + +If you accidentally added commits to your local copy of ``master``, you must +delete them before updating the branch. + +One way to ensure that you are notified of potential problems is to use ``git +pull --ff-only``, which will raise an error if a non-trivial merge would be +required:: + + [user@localhost sage]$ git checkout master + [user@localhost sage]$ git pull --ff-only upstream master + +If this pull fails, then something is wrong with the local copy of the +master branch. To switch to the correct Sage master branch, use:: + + [user@localhost sage]$ git checkout master + [user@localhost sage]$ git reset --hard upstream/master + + +.. _section-git-merge: + +Merging and Rebasing +==================== + +Sometimes, a new version of Sage is released while you work on a git branch. + +Let us assume you started ``my_branch`` at commit ``B``. After a while, your +branch has advanced to commit ``Z``, but you updated ``master`` (see +:ref:`section-git-pull-master`) and now your git history looks like this (see +:ref:`section_walkthrough_logs`): + +.. CODE-BLOCK:: text + + X---Y---Z my_branch + / + A---B---C---D master + +How should you deal with such changes? In principle, there are two ways: + + +* **Rebase:** The first solution is to **replay** commits ``X,Y,Z`` atop of the + new ``master``. This is called **rebase**, and it rewrites your current + branch: + + .. CODE-BLOCK:: text + + git checkout my_branch + git rebase -i master + + In terms of the commit graph, this results in: + + .. CODE-BLOCK:: text + + X'--Y'--Z' my_branch + / + A---B---C---D master + + Note that this operation rewrites the history of ``my_branch`` (see + :ref:`section-git-rewriting-history`). This can lead to problems if somebody + began to write code atop of your commits ``X,Y,Z``. It is safe otherwise. + + **Alternatively**, you can rebase ``my_branch`` while updating master at the + same time (see :ref:`section-git-pull`): + + .. CODE-BLOCK:: text + + git checkout my_branch + git pull -r master + +* **Merging** your branch with ``master`` will create a new commit above the two + of them: + + .. CODE-BLOCK:: text + + git checkout my_branch + git merge master + + The result is the following commit graph: + + .. CODE-BLOCK:: text + + X---Y---Z---W my_branch + / / + A---B---C-------D master + + - **Pros:** you did not rewrite history (see + :ref:`section-git-rewriting-history`).The additional commit is then easily + pushed to the git repository and distributed to your collaborators. + + - **Cons:** it introduced an extra merge commit that would + not be there had you used rebase. + + **Alternatively**, you can merge ``my_branch`` while updating master at the + same time (see :ref:`section-git-pull`): + + .. CODE-BLOCK:: text + + git checkout my_branch + git pull master + +**In case of doubt** use merge rather than rebase. There is less risk involved, +and rebase in this case is only useful for branches with a very long history. + + +.. _section-git-mergetool: + +Merge Tools +=========== + +Simple conflicts can be easily solved with Git only (see :ref:`section-git-conflict`) + +For more complicated ones, a range of specialized programs are +available. Because the conflict marker includes the hash of the most recent +common parent, you can use a three-way diff:: + + [alice@laptop]$ git mergetool + + This message is displayed because 'merge.tool' is not configured. + See 'git mergetool --tool-help' or 'git help config' for more details. + 'git mergetool' will now attempt to use one of the following tools: + meld opendiff kdiff3 [...] merge araxis bc3 codecompare emerge vimdiff + Merging: + fibonacci.py + + Normal merge conflict for 'fibonacci.py': + {local}: modified file + {remote}: modified file + Hit return to start merge resolution tool (meld): + +If you don't have a favourite merge tool we suggest you try `meld +`_ (cross-platform). The result looks like the following +screenshot. + +.. IMAGE:: static/meld-screenshot.png + +The middle file is the most recent common parent; on the right is +Bob's version and on the left is Alice's conflicting version. Clicking +on the arrow moves the marked change to the file in the adjacent +pane. + + +.. _section-git-conflict: + +Conflict Resolution +------------------- + +Merge conflicts happen if there are overlapping edits, and they are an +unavoidable consequence of distributed development. Fortunately, +resolving them is common and easy with Git. As a hypothetical example, +consider the following code snippet: + +.. CODE-BLOCK:: python + + def fibonacci(i): + """ + Return the `i`-th Fibonacci number + """ + return fibonacci(i-1) * fibonacci(i-2) + +This is clearly wrong; Two developers, namely Alice and Bob, decide to +fix it. Bob corrected the seed values: + +.. CODE-BLOCK:: python + + def fibonacci(i): + """ + Return the `i`-th Fibonacci number + """ + if i > 1: + return fibonacci(i-1) * fibonacci(i-2) + return [0, 1][i] + +and turned those changes into a new commit:: + + [alice@laptop sage]$ git add fibonacci.py + [alice@laptop sage]$ git commit -m 'return correct seed values' + +He made her changes a PR to the GitHub sage repo and quickly got merged to the ``develop`` branch. Yes, his `fibonacci` function is not yet perfect but is certainly better than the original. Meanwhile, Alice changed the +multiplication to an addition since that is the correct recursion +formula: + +.. CODE-BLOCK:: python + + def fibonacci(i): + """ + Return the `i`-th Fibonacci number + """ + return fibonacci(i-1) + fibonacci(i-2) + +and merged his branch with the latest ``develop`` branch fetched from the GitHub Sage repo:: + + [bob@home sage]$ git add fibonacci.py + [bob@home sage]$ git commit -m 'corrected recursion formula, must be + instead of *' + [bob@home sage]$ git merge develop + ... + CONFLICT (content): Merge conflict in fibonacci.py + Automatic merge failed; fix conflicts and then commit the result. + +The file now looks like this: + +.. skip # doctester confuses >>> with input marker + +.. CODE-BLOCK:: python + + def fibonacci(i): + """ + Return the `i`-th Fibonacci number + """ + <<<<<<< HEAD + if i > 1: + return fibonacci(i-1) * fibonacci(i-2) + return [0, 1][i] + ======= + return fibonacci(i-1) + fibonacci(i-2) + >>>>>>> 41675dfaedbfb89dcff0a47e520be4aa2b6c5d1b + +The conflict is shown between the conflict markers ``<<<<<<<`` and +``>>>>>>>``. The first half (up to the ``=======`` marker) is Alice's +current version, the second half is Bob's version. The 40-digit hex +number after the second conflict marker is the SHA1 hash of the most +recent common parent of both. + +It is now Alice's job to resolve the conflict by reconciling their +changes, for example by editing the file. Her result is: + +.. CODE-BLOCK:: python + + def fibonacci(i): + """ + Return the `i`-th Fibonacci number + """ + if i > 1: + return fibonacci(i-1) + fibonacci(i-2) + return [0, 1][i] + +And then upload both her original change *and* her merge commit to the GitHub Sage repo:: + + [alice@laptop sage]$ git add fibonacci.py + [alice@laptop sage]$ git commit -m "merged Bob's changes with mine" + +The resulting commit graph now has a loop:: + + [alice@laptop sage]$ git log --graph --oneline + * 6316447 merged Bob's changes with mine + |\ + | * 41675df corrected recursion formula, must be + instead of * + * | 14ae1d3 return correct seed values + |/ + * 14afe53 initial commit + [alice@laptop sage]$ git push origin + +This time, there is no merge conflict since Alice's branch already merged the ``develop`` branch. + diff --git a/src/doc/en/developer/git_setup.rst b/src/doc/en/developer/git_setup.rst index e2d59c8dfda..826c2d31a84 100644 --- a/src/doc/en/developer/git_setup.rst +++ b/src/doc/en/developer/git_setup.rst @@ -4,15 +4,11 @@ Setting Up Git ============== -To work on the Sage source code, you need +To work on the Sage source code, you need a working Git installation, +configured at least to use your name and email address for commits. -* a working ``git`` installation, see :ref:`section-git-install`. - -* configure ``git`` to use your name and email address for commits, see - :ref:`section-git-setup-name`. - -The :ref:`chapter-git-background` chapter contains further information -about ``git`` that might be useful to some but are not required. +For further information about Git, see :ref:`chapter-git-background`, but this +is not required for a beginner. .. _section-git-install: @@ -20,9 +16,9 @@ about ``git`` that might be useful to some but are not required. Installing Git -------------- -First, try ``git`` on the command line. Most distributions will have -it installed by default if other development tools are installed. If -that fails, use the following to install git: +First, try Git on the command line. Most platforms will have it installed by +default if other development tools are installed. If that fails, use the +following to install Git: Debian / Ubuntu ``sudo apt-get install git-core`` @@ -31,52 +27,53 @@ Fedora ``sudo yum install git-core`` Windows (Cygwin) - Install the Cygwin package ``git``. Do not attempt to use native - Windows installations of ``git``. + Install the Cygwin package Git. Do not attempt to use native + Windows installations of Git. Windows (WSL) We strongly recommend to install the package using the Linux distribution's package manager. Native Windows installations of - ``git`` may also work, but there are possible pitfalls. + Git may also work, but there are possible pitfalls. macOS Install the Xcode Command Line Tools. Some further resources for installation help are: -* `Section 1.5 of the git book +* `Section 1.5 of the Git book `_ -* The `git homepage `_ for the most recent - information. +* The `Git homepage `_ for the most recent + information -* `Github install help pages `_ +* `Git install help page on GitHub `_ .. _section-git-setup-name: -Your Name and Email -------------------- +Configuring Git +--------------- -The commit message of any change contains your name and email address -to acknowledge your contribution and to have a point of contact if -there are questions in the future; filling it in is required if you -want to share your changes. The simplest way to do this is from the -command line: +The commit message of any change contains your name and email address to +acknowledge your contribution and to have a point of contact if there are +questions in the future. Filling it in is required if you want to share your +changes. The simplest way to do this is from the command line. Assuming your +name ``alice`` and email address ``alice@wonderland.com``, .. CODE-BLOCK:: shell-session - [user@localhost ~] git config --global user.name "Your Name" - [user@localhost ~] git config --global user.email you@yourdomain.example.com + [alice@localhost ~]$ git config --global user.name "Alice Adventure" + [alice@localhost ~]$ git config --global user.email alice@wonderland.com -This will write the settings into your :ref:`git configuration file +This will write the settings into your :ref:`Git configuration file ` with your name and email: .. CODE-BLOCK:: text [user] - name = Your Name - email = you@yourdomain.example.com + name = Alice Adventure + email = alice@wonderland.com + +Of course, replace ``Alice Adventure`` and ``alice@wonderland.com`` with your +actual name and email address. -Of course you'll need to replace ``Your Name`` and ``you@yourdomain.example.com`` -with your actual name and email address. diff --git a/src/doc/en/developer/git_trac.rst b/src/doc/en/developer/git_trac.rst deleted file mode 100644 index 155510b00b2..00000000000 --- a/src/doc/en/developer/git_trac.rst +++ /dev/null @@ -1,595 +0,0 @@ -.. highlight:: shell-session - -.. _chapter-git_trac: - -==================================== -Optional: Using the Git-Trac Command -==================================== - -.. WARNING:: - - **Sage development moved to GitHub in February 2023.** After the transition, - some parts of this guide (especially those related with `the Sage Trac - server `_) became obsolete and need to be - updated according to the new workflow on GitHub. See our `transition guide - from Trac to GitHub - `_ - for the preliminary version of the workflow. - -Git is a separate project from trac, and the two do not know how to -talk to each other. To simplify the development, we have a special -``git trac`` subcommand for the git suite. Note that this really is -only to simplify interaction with our trac issue management, you can -perform every development task with just git and a web browser. - -.. _section-git_trac-install: - -Installing the Git-Trac Command -=============================== - -:: - - [user@localhost]$ git clone /~https://github.com/sagemath/git-trac-command.git - Cloning into 'git-trac-command'... - [...] - Checking connectivity... done. - [user@localhost]$ source git-trac-command/enable.sh - Prepending the git-trac command to your search PATH - -This creates a directory ``git-trac-command``. - -Sourcing the ``enable.sh`` script in there is just a quick and dirty -way to enable it temporarily. For a more permanent installation on -your system later, make sure to put the ``git-trac`` command in your -``PATH``. Assuming that ``~/bin`` is already in your ``PATH``, you can -do this by symlinking:: - - [user@localhost]$ echo $PATH - /home/user/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin - [user@localhost]$ cd git-trac-command - [user@localhost git-trac-command]$ ln -s `pwd`/git-trac ~/bin/ - -See the `git-trac README `_ for -more details. At this point you leave ``git-trac-command`` subdirectory, and only go -there whenever you need to update the ``git-trac`` command. - - - -.. _section-git_trac-setup: - -Git and Trac Configuration -========================== - -.. NOTE:: - - * `trac `_ uses username/password for - authentication. - - * Our `git repository server `_ uses SSH - public key authentication for write access. - -You need to set up both authentication mechanisms to be able to upload -your changes with "git trac". For read-only access neither -authentication mechanism is needed. To set up ``git trac``, first go -to the Sage directory and tell ``git trac`` about your trac account:: - - [user@localhost sage]$ git trac config --user USERNAME --pass 'PASSWORD' - Trac xmlrpc URL: - https://trac.sagemath.org/xmlrpc (anonymous) - https://trac.sagemath.org/login/xmlrpc (authenticated) - realm sage.math.washington.edu - Username: USERNAME - Password: PASSWORD - Retrieving SSH keys... - 1024 ab:1b:7c:c9:9b:48:fe:dd:59:56:1e:9d:a4:a6:51:9d My SSH Key - -where you have to replace USERNAME with your trac user name and -PASSWORD with your trac password. If you don't have a trac account, -use ``git trac config`` without any arguments. The single quotes in -``'PASSWORD'`` escape special characters that you might have in your -password. The password is stored in plain-text in ``.git/config``, so -make sure that it is not readable by other users on your system. For -example, by running ``chmod 0600 .git/config`` if your home directory -is not already private. - -Instead of a username and password you may also configure authentication via -a generated token by passing ``--token=`` instead of ``--pass``:: - - [user@localhost sage]$ git trac config --user= --token= - -This is required if you authenticate to Trac with your GitHub account, as -you do not have a Trac password. Logged in users can find their token -under :trac:`the token tab in preferences on the trac site `. - -.. NOTE:: - - The username to be entered here is NOT the GitHub username, but rather the trac username which is gh- - as given on the top right corner of the trac server. - -If both a token and a username/password are configured, the token-based -authentication takes precedence. - -If you do not want to store your trac username/password/token on disk you -can temporarily override it with the environment variables -``TRAC_USERNAME``, ``TRAC_PASSWORD``, and ``TRAC_TOKEN`` respectively. -These take precedence over any other configuration. - -If there is no SSH key listed then you haven't uploaded your SSH -public key to the trac server. You should do that now following the -instructions to :ref:`section-trac-ssh-key`, if you want to upload -any changes. You may have to add your private key to your authentication agent:: - - [user@localhost sage]$ ssh-add - -.. NOTE:: - - The ``git trac config`` command will automatically add a ``trac`` - remote git repository to your list of remotes if necessary. - -If you followed the above instructions then you will have two remote -repositories set up:: - - [user@localhost sage]$ git remote -v - origin /~https://github.com/sagemath/sage.git (fetch) - origin /~https://github.com/sagemath/sage.git (push) - trac git@trac.sagemath.org:sage.git (fetch) - trac git@trac.sagemath.org:sage.git (push) - -The ``git@...`` part of the push url means that write access is -secured with SSH keys, which you must have set up as in -:ref:`section-trac-ssh-key`. Read-only access happens through the -fetch url and does not require SSH. - - -Trac Tickets and Git Branches -============================= - -Now let's start adding code to Sage! - -.. _section-git_trac-create: - -Create a Ticket ---------------- - -Suppose you have written an algorithm for calculating the last twin prime, and -want to add it to Sage. You would first open a ticket for that:: - - [user@localhost sage]$ git trac create 'Last Twin Prime' - Remote branch: u/user/last_twin_prime - Newly-created ticket number: 12345 - Ticket URL: https://trac.sagemath.org/12345 - Local branch: t/12345/last_twin_prime - -This will create a new trac ticket titled "Last Twin Prime" with a -*remote branch* ``u/user/last_twin_prime`` attached to it. The remote -branch name is automatically derived from the ticket title; If you -don't like this then you can use the ``-b`` switch to specify it -explicitly. See ``git trac create -h`` for details. This new branch is -automatically checked out for you with the *local branch* name -``t/12345/last_twin_prime``. - -.. NOTE:: - - Only some trac fields are filled in automatically. See - :ref:`section-trac-fields` for what trac fields are available and - how we use them. - - - -.. _section-git_trac-checkout: - -Check out an Existing Ticket ----------------------------- - -Alternatively, you can use the `web interface to the Sage trac -development server `_ to open a new ticket. -Just log in and click on "Create Ticket". - -Or maybe somebody else already opened a ticket. Then, to get a suitable -local branch to make your edits, you would just run:: - - [user@localhost sage]$ git trac checkout 12345 - Loading ticket #12345... - Checking out Trac #13744 remote branch u/user/last_twin_prime -> local branch t/12345/last_twin_prime... - -The ``git trac checkout`` command downloads an existing branch (as -specified in the "Branch:" field on the trac ticket) or creates a new -one if there is none yet. Just like the create command, you can -specify the remote branch name explicitly using the ``-b`` switch if -you want. - -.. _section-git_trac-branch-names: - -Note on Branch Names --------------------- - -The "Branch:" field of a trac ticket (see :ref:`section-trac-fields`) indicates -the git branch containing its code. Our git server implements the following -access restrictions for **remote branch names**: - -* You can read/write/create a branch named - ``u/your_username/whatever_you_like``. Everybody else can read. - -* Everybody can read/write/create a branch named ``public/whatever_you_like``. - -Depending on your style of collaboration, you can use one or the -other. The ``git trac`` subcommands defaults to the former. - -As a convention, the ``git trac`` subcommand uses **local branch -names** of the form ``t/12345/description``, where the number is the -trac ticket number. The script uses this number to figure out the -ticket from the local branch name. You can rename the local branches -if you want, but if they don't contain the ticket number then you will -have to specify the ticket number manually when you are uploading your -changes. - -.. _section-git_trac-editing: - -Making Changes --------------- - -Once you have checked out a ticket, edit the appropriate files and -commit your changes to the branch as described in -:ref:`section-walkthrough-add-edit` and -:ref:`section-walkthrough-commit`. - -.. _section-git_trac-push: - -Uploading Changes to Trac -========================= - -.. _section-git_trac-push-auto: - -Automatic Push --------------- - -At some point, you may wish to share your changes with the rest of us: -maybe it is ready for review, or maybe you are collaborating with -someone and want to share your changes "up until now". This is simply -done by:: - - [user@localhost sage]$ git trac push - Pushing to Trac #12345... - Guessed remote branch: u/user/last_twin_prime - - To git@trac.sagemath.org:sage.git - * [new branch] HEAD -> u/user/last_twin_prime - - Changing the trac "Branch:" field... - -This uploads your changes to a remote branch on the `Sage git server -`_. The ``git trac`` command uses -the following logic to find out the remote branch name: - -* By default, the remote branch name will be whatever is already on - the trac ticket. - -* If there is no remote branch yet, the branch will be called - ``u/user/description`` (``u/user/last_twin_prime`` in the example). - -* You can use the ``--branch`` option to specify the remote branch - name explicitly, but it needs to follow the naming convention from - :ref:`section-git_trac-branch-names` for you to have write - permission. - - -.. _section-git_trac-push-with-ticket-number: - -Specifying the Ticket Number ----------------------------- - -You can upload any local branch to an existing ticket, whether or not -you created the local branch with ``git trac``. This works exactly -like in the case where you started with a ticket, except that you have -to specify the ticket number (since there is no way to tell which -ticket you have in mind). That is:: - - [user@localhost sage]$ git trac push TICKETNUM - -where you have to replace ``TICKETNUM`` with the number of the trac -ticket. - - -.. _section-git_trac-push-finish: - -Finishing It Up ---------------- - -It is common to go through a few iterations of commits before you -upload, and you will probably also have pushed your changes a few -times before your changes are ready for review. - -Once you are happy with the changes you uploaded, they must be -reviewed by somebody else before they can be included in the next -version of Sage. To mark your ticket as ready for review, you should -set it to ``needs_review`` on the trac server. Also, add yourself as -the (or one of the) author(s) for that ticket by inserting the -following as the first line: - -.. CODE-BLOCK:: text - - Authors: Your Real Name - - -.. _section-git_trac-pull: - -Downloading Changes from Trac -============================= - -If somebody else worked on a ticket, or if you just switched -computers, you'll want to get the latest version of the branch from a -ticket into your local branch. This is done with:: - - [user@localhost sage]$ git trac pull - -Technically, this does a *merge* (just like the standard ``git pull``) -command. See :ref:`section-git-merge` for more background information. - - -.. _section-git_trac-merge: - -Merging -======= - -As soon as you are working on a bigger project that spans multiple -tickets you will want to base your work on branches that have not been -merged into Sage yet. This is natural in collaborative development, -and in fact you are very much encouraged to split your work into -logically different parts. Ideally, each part that is useful on its -own and can be reviewed independently should be a different ticket -instead of a huge patch bomb. - -For this purpose, you can incorporate branches from other tickets (or -just other local branches) into your current branch. This is called -merging, and all it does is include commits from other branches into -your current branch. In particular, this is done when a new Sage -release is made: the finished tickets are merged with the Sage master -and the result is the next Sage version. Git is smart enough to not -merge commits twice. In particular, it is possible to merge two -branches, one of which had already merged the other branch. The syntax -for merging is easy:: - - [user@localhost sage]$ git merge other_branch - -This creates a new "merge" commit, joining your current branch and -``other_branch``. - -.. WARNING:: - - You should avoid merging branches both ways. Once A merged B and B - merged A, there is no way to distinguish commits that were - originally made in A or B. Effectively, merging both ways combines - the branches and makes individual review impossible. - - In practice, you should only merge when one of the following holds: - - * Either two tickets conflict, then you have to merge one into the - other in order to resolve the merge conflict. - - * Or you definitely need a feature that has been developed as part - of another branch. - -A special case of merging is merging in the ``develop`` branch. This -brings your local branch up to date with the newest Sage version. The -above warning against unnecessary merges still applies, though. Try to -do all of your development with the Sage version that you originally -started with. The only reason for merging in the ``develop`` branch is if -you need a new feature or if your branch conflicts. See -:ref:`section-git-update-latest` for details. - - -.. _section-git_trac-collaborate: - -Collaboration and conflict resolution -===================================== - -Exchanging Branches -------------------- - -It is very easy to collaborate by just going through the above steps -any number of times. For example, Alice starts a ticket and adds some -initial code:: - - [alice@laptop sage]$ git trac create "A and B Ticket" - ... EDIT EDIT ... - [alice@laptop sage]$ git add . - [alice@laptop sage]$ git commit - [alice@laptop sage]$ git trac push - -The trac ticket now has "Branch:" set to -``u/alice/a_and_b_ticket``. Bob downloads the branch and works some -more on it:: - - [bob@home sage]$ git trac checkout TICKET_NUMBER - ... EDIT EDIT ... - [bob@home sage]$ git add . - [bob@home sage]$ git commit - [bob@home sage]$ git trac push - -The trac ticket now has "Branch:" set to ``u/bob/a_and_b_ticket``, -since Bob cannot write to ``u/alice/...``. Now the two authors just -pull/push in their collaboration:: - - [alice@laptop sage]$ git trac pull - ... EDIT EDIT ... - [alice@laptop sage]$ git add . - [alice@laptop sage]$ git commit - [alice@laptop sage]$ git trac push - - [bob@home sage]$ git trac pull - ... EDIT EDIT ... - [bob@home sage]$ git add . - [bob@home sage]$ git commit - [bob@home sage]$ git trac push - -Alice and Bob need not alternate, they can also add further commits on -top of their own remote branch. As long as their changes do not -conflict (edit the same lines simultaneously), this is fine. - - -.. _section-git_trac-conflict: - -Conflict Resolution -------------------- - -Merge conflicts happen if there are overlapping edits, and they are an -unavoidable consequence of distributed development. Fortunately, -resolving them is common and easy with git. As a hypothetical example, -consider the following code snippet: - -.. CODE-BLOCK:: python - - def fibonacci(i): - """ - Return the `i`-th Fibonacci number - """ - return fibonacci(i-1) * fibonacci(i-2) - -This is clearly wrong; Two developers, namely Alice and Bob, decide to -fix it. First, in a cabin in the woods far away from any internet -connection, Alice corrects the seed values: - -.. CODE-BLOCK:: python - - def fibonacci(i): - """ - Return the `i`-th Fibonacci number - """ - if i > 1: - return fibonacci(i-1) * fibonacci(i-2) - return [0, 1][i] - -and turns those changes into a new commit:: - - [alice@laptop sage]$ git add fibonacci.py - [alice@laptop sage]$ git commit -m 'return correct seed values' - -However, not having an internet connection, she cannot immediately -send her changes to the trac server. Meanwhile, Bob changes the -multiplication to an addition since that is the correct recursion -formula: - -.. CODE-BLOCK:: python - - def fibonacci(i): - """ - Return the `i`-th Fibonacci number - """ - return fibonacci(i-1) + fibonacci(i-2) - -and immediately uploads his change:: - - [bob@home sage]$ git add fibonacci.py - [bob@home sage]$ git commit -m 'corrected recursion formula, must be + instead of *' - [bob@home sage]$ git trac push - -Eventually, Alice returns to civilization. In her mailbox, she finds a -trac notification email that Bob has uploaded further changes to their -joint project. Hence, she starts out by getting his changes into her -own local branch:: - - [alice@laptop sage]$ git trac pull - ... - CONFLICT (content): Merge conflict in fibonacci.py - Automatic merge failed; fix conflicts and then commit the result. - -The file now looks like this: - -.. skip # doctester confuses >>> with input marker - -.. CODE-BLOCK:: python - - def fibonacci(i): - """ - Return the `i`-th Fibonacci number - """ - <<<<<<< HEAD - if i > 1: - return fibonacci(i-1) * fibonacci(i-2) - return [0, 1][i] - ======= - return fibonacci(i-1) + fibonacci(i-2) - >>>>>>> 41675dfaedbfb89dcff0a47e520be4aa2b6c5d1b - -The conflict is shown between the conflict markers ``<<<<<<<`` and -``>>>>>>>``. The first half (up to the ``=======`` marker) is Alice's -current version, the second half is Bob's version. The 40-digit hex -number after the second conflict marker is the SHA1 hash of the most -recent common parent of both. - -It is now Alice's job to resolve the conflict by reconciling their -changes, for example by editing the file. Her result is: - -.. CODE-BLOCK:: python - - def fibonacci(i): - """ - Return the `i`-th Fibonacci number - """ - if i > 1: - return fibonacci(i-1) + fibonacci(i-2) - return [0, 1][i] - -And then upload both her original change *and* her merge commit to trac:: - - [alice@laptop sage]$ git add fibonacci.py - [alice@laptop sage]$ git commit -m "merged Bob's changes with mine" - -The resulting commit graph now has a loop:: - - [alice@laptop sage]$ git log --graph --oneline - * 6316447 merged Bob's changes with mine - |\ - | * 41675df corrected recursion formula, must be + instead of * - * | 14ae1d3 return correct seed values - |/ - * 14afe53 initial commit - -If Bob decides to do further work on the ticket then he will have to -pull Alice's changes. However, this time there is no conflict on his -end: git downloads both Alice's conflicting commit and her resolution. - - -.. _section-git_trac-review: - -Reviewing -========= - -For an explanation of what should be checked by the reviewer, see -:ref:`chapter-review`. - -If you go to the `web interface to the Sage trac development server -`_ then you can click on the "Branch:" field and see -the code that is added by combining all commits of the ticket. This is what -needs to be reviewed. - -The ``git trac`` command gives you two commands that might be handy -(replace ``12345`` with the actual ticket number) if you do not want -to use the web interface: - -* ``git trac print 12345`` displays the trac ticket directly in your - terminal. - -* ``git trac review 12345`` downloads the branch from the ticket and - shows you what is being added, analogous to clicking on the - "Branch:" field. - -To review tickets with minimal recompiling, start by building the "develop" -branch, that is, the latest beta. Just checking out an older ticket would most -likely reset the Sage tree to an older version, so you would have to compile -older versions of packages to make it work. Instead, you can create an anonymous -("detached HEAD") merge of the ticket and the develop branch using :: - - $ git trac try 12345 - -This will only touch files that are really modified by the ticket. In particular, -if only Python files are changed by the ticket (which is true for most tickets) -then you just have to run ``sage -b`` to rebuild the Sage library. If files other -than Python have been changed, you must run ``make``. When you are finished -reviewing, just check out a named branch, for example :: - - $ git checkout develop - -If you want to edit the ticket branch (that is, add additional commits) you cannot -use ``git trac try``. You must :ref:`section-git_trac-checkout` to get the actual ticket -branch as a starting point. diff --git a/src/doc/en/developer/github.rst b/src/doc/en/developer/github.rst new file mode 100644 index 00000000000..0a4682149c6 --- /dev/null +++ b/src/doc/en/developer/github.rst @@ -0,0 +1,407 @@ +.. highlight:: shell-session + + +.. _chapter-github: + +============================= +The Sage Repository on GitHub +============================= + +The center of Sage development is `the SageMath organization on GitHub +`_, which consists of many repositories related +with Sage. The most important one among them is of course `the Sage repository +`_, which we call "the Sage repo" for short. + + +.. _section-github-account: + +Obtaining a GitHub account +========================== + +To share your work on Sage, you need a GitHub account. If you do not have one +yet, choose a username and `create an account `_. In +the following, we assume your username "alice". So you always read your own +username if you see "alice". + +Forking the Sage repository +=========================== + +The first step is to create your personal `GitHub fork +`_ +of the Sage repo. Simply click "Fork" on `the Sage repo +`_. Then your fork of the Sage repo is created at +/~https://github.com/alice/sage. + +Next if you don't have a local Git repo of Sage, then start afresh `cloning +your fork +`_:: + + [alice@localhost ~]$ git clone /~https://github.com/alice/sage.git + Cloning into 'sage'... + remote: Enumerating objects: 914565, done. + remote: Counting objects: 100% (2738/2738), done. + remote: Compressing objects: 100% (855/855), done. + remote: Total 914565 (delta 1950), reused 2493 (delta 1875), pack-reused 911827 + Receiving objects: 100% (914565/914565), 331.09 MiB | 11.22 MiB/s, done. + Resolving deltas: 100% (725438/725438), done. + Updating files: 100% (9936/9936), done. + [alice@localhost ~]$ cd sage + [alice@localhost sage]$ git remote -v + origin /~https://github.com/alice/sage.git (fetch) + origin /~https://github.com/alice/sage.git (push) + +If you already have a local Git repo and only want to link your fork as ``origin`` remote, then do:: + + [alice@localhost sage]$ git remote add origin /~https://github.com/alice/sage.git + [alice@localhost sage]$ git remote -v + origin /~https://github.com/alice/sage.git (fetch) + origin /~https://github.com/alice/sage.git (push) + [alice@localhost sage]$ git fetch origin + remote: Enumerating objects: 1136, done. + remote: Counting objects: 100% (1084/1084), done. + remote: Compressing objects: 100% (308/308), done. + remote: Total 1136 (delta 825), reused 982 (delta 776), pack-reused 52 + Receiving objects: 100% (1136/1136), 2.62 MiB | 5.30 MiB/s, done. + Resolving deltas: 100% (838/838), completed with 145 local objects. + From /~https://github.com/alice/sage + * [new branch] develop -> origin/develop + +You also add the Sage repo ``sagemath/sage`` as your remote "upstream":: + + [alice@localhost sage]$ git remote add upstream /~https://github.com/sagemath/sage.git + [alice@localhost sage]$ git remote -v + origin /~https://github.com/alice/sage.git (fetch) + origin /~https://github.com/alice/sage.git (push) + upstream /~https://github.com/sagemath/sage.git (fetch) + upstream /~https://github.com/sagemath/sage.git (push) + +To prevent accidental pushes to ``upstream`` (instead of ``origin``), you may want to disable it by running:: + + [alice@localhost sage]$ git remote set-url --push upstream DISABLE + +Of course, you can give arbitrary names to your Git remotes, but ``origin`` and +``upstream`` are the established defaults, which will make it easier to use tools +such as the GitHub CLI. + +.. _chapter-github-cli: + +Using the GitHub CLI +==================== + +GitHub provides a command-line interface, the GitHub CLI, that can be used +instead of the web interface. The central component of the GitHub CLI is the +``gh`` command that you can use in your terminal. + +Installation +------------ + +This :ref:`spkg_github_cli` documents how to install the ``gh`` command for your platform. Or see `GitHub CLI `_ from GitHub. + +Configuration +------------- + +You authenticate with your GitHub account. Typically the authorization proceeds as follows:: + + [alice@localhost sage]$ gh auth login + ? What is your preferred protocol for Git operations? HTTPS + ? Authenticate Git with your GitHub credentials? Yes + ? How would you like to authenticate GitHub CLI? Login with a web browser + + ! First copy your one-time code: 3DA8-5ADA + Press Enter to open github.com in your browser... + ✓ Authentication complete. + - gh config set -h github.com git_protocol https + ✓ Configured git protocol + ✓ Logged in as sage + +where a web browser is used to enter credentials. You can also use an +authentication token instead, in which case you must first generate `a Personal +Access Token here `_. + +Next set the default repo for the ``gh`` command:: + + [alice@localhost sage]$ gh repo set-default sagemath/sage + +and check:: + + [alice@localhost sage]$ gh repo view + sagemath/sage + ... + +which will show the default repo along with its readme, which is quite long. + + +.. _section-github-bug-report: + +Reporting Bugs +============== + +If you think you have found a bug in Sage, here is the procedure: + +- Search through our Google groups for postings related to your possible bug (it + may have been fixed/reported already): + + * ``sage-devel``: ``_ + * ``sage-support``: ``_ + + You also search `the GitHub issues + `_ to see if anyone else has already + opened an issue about your bug. + +- If you do not find anything, and you are not sure that you have found a bug, + ask about it on `sage-devel `_. + +- If you are sure that you have found a bug, then create on GitHub a new issue about the bug. + In this case, follow the :ref:`section-github-create-issue`. + + A bug report should contain: + + - An explicit and **reproducible example** illustrating your bug (and/or the + steps required to reproduce the buggy behavior). It also helps to describe what + is the expected behaviour. + + - The **version of Sage** you run, as well as the version of the optional + packages that may be involved in the bug. + + - Describe your **operating system** as accurately as you can and the + architecture of your CPU (32 bit, 64 bit, ...). + +Thank you in advance for reporting bugs to improve Sage! + + +.. _section-github-new-enhancement: + +Planning an enhancement +======================= + +In addition to bug reports, you should also open +an issue if you have some new code or an idea that makes Sage better. If +you have a feature request, start a discussion on ``sage-devel`` first, and +then if there seems to be a general agreement that you have a good idea, open an +issue describing the idea. + +Before opening a new issue, consider the following points: + +- Make sure that nobody else has opened an issue (or even a PR) about the same + or closely related issue. Search through the existing issues and PRs with + some key words. + +- It is much better to open several specific issues than one that + is very broad. Indeed, a single issue which deals with lots of + different issues can be quite problematic, and should be avoided. + +- Be precise: If foo does not work on macOS but is fine on Linux, + mention that in the title. Use the keyword option so that + searches will pick up the issue. + +- The problem described in the issue must be solvable. For + example, it would be silly to open an issue whose purpose was + "Make Sage the best mathematical software in the world". There is + no metric to measure this properly and it is highly subjective. + +- If appropriate, provide URLs to background information or sage-devel + conversation relevant to the issue you are reporting. + + +.. _section-github-create-issue: + +Opening an issue +================= + +Whether it's reporting a bug or planning an enhancement, `issue +`_ +should be opened on our Sage repo on GitHub `sagemath/sage +`_. + +- Think of an apt title. People scan through the titles of issues to decide + which ones to look into further. So write a title that concisely describes + what the issue about. + +- Describe the issue in detail in the issue body. What is the issue? How can we + solve the issue? Add links to relevant issues/PR, and other resources. + + You may use GitHub mention ``@USERNAME`` to get attention from the people + who would be interested in the issue or has expertise in this issue. + +- Add appropriate labels to the created issue: + + - **Type** labels with prefix ``t:`` such as ``t: bug``, ``t: enhancement``, + ``t: feature``, ``t: performance``, ``t: refactoring``, + ``t: tests`` + + - **Component** labels with prefix ``c:`` such as ``c: basic arithmetic``, + ``c: linear algebra``, ``c: geometry``, etc. + + - **Priority** labels with prefix ``p:`` such as ``p: trivial / 5``, + ``p: minor / 4``, ``p: major / 3``, ``p: critical / 2``, and ``p: blocker / 1`` + + If the issue is not expected to be solved in a near future, you may add + ``wishlist item`` label. + + +.. _section-github-create-pr: + +Creating a Pull Request +======================= + +If you worked on an issue, and prepared a fix for a bug or wrote code for +enhancing Sage, then you create a PR on the Sage repo `sagemath/sage +`_. + +In addition to what were said about opening an issue, the following applies: + +- The title should concisely describe what the PR does. If the PR solves an + issue, describe briefly what the PR solves about the issue (do not simply put + the issue number in the title). + +- Explain what the PR solves in detail in the body. If the PR solves an issue, + you may mention the issue here. + +- Add type, component, and priority labels. If this PR solves an existing + issue, please duplicate the labels of the issue to this PR. + +- **Dependencies**: Use the phrase ``Depends on``, followed by the issue or PR + reference. Repeat this in separate lines if there is more than one + dependency. This format is understood by various dependency managers. + +If you are working on a PR and the PR is not yet quite ready for review, then +open the PR as *draft*. + + +.. _section-github-pr-status: + +The Status of a PR +================== + +If a PR is in the state of draft, the review process does not start. Otherwise, +review process will start for the PR as soon as a reviewer gets interested with +the PR, and the status of the PR will be indicated by **status** labels. + +- ``s: needs review``: The code is ready to be peer-reviewed. If the code is not + yours, then you can review it. See :ref:`chapter-review`. + +- ``s: needs work``: Something needs to be changed in the code. The reason should + appear in the comments. + +- ``s: needs info``: The author of the PR or someone else should answer to a + question or provide information to proceed the review process. + +- ``s: positive review``: The PR has been reviewed positively, and the release manager + will merge it to the ``develop`` branch of the Sage repo in due time. + +If the PR does not get positive review and it is decided to close the PR, then +the PR will get one of **resolution** labels: ``r: duplicate``, ``r: invalid``, +``r: wontfix``, ``r: worksforme``. + + +.. _section-github-stopgaps: + + +The stopgap +=========== + +When Sage returns wrong results, an issue and a PR should be created: + +- A "stopgap" issue with all available details. +- A "stopgap" PR (e.g. :issue:`12699`) + +The stopgap PR does not fix the problem but adds a warning that will be +printed whenever anyone uses the relevant code, until the problem is +finally fixed. + +To produce the warning message, use code like the following: + +.. CODE-BLOCK:: python + + from sage.misc.stopgap import stopgap + stopgap("This code contains bugs and may be mathematically unreliable.", + ISSUE_NUM) + +Replace ``ISSUE_NUM`` by the reference number for the stopgap issue. On the stopgap issue, enter the reference number for the stopgap PR. Stopgap issues should be marked as critical. + +.. NOTE:: + + If mathematically valid code causes Sage to raise an error or + crash, for example, there is no need for a stopgap. Rather, + stopgaps are to warn users that they may be using buggy code; if + Sage crashes, this is not an issue. + + +Commenting Issues and PRs +========================= + +Anyone can comment on an issue or a PR. If a PR is linked to an issue, +you may not be sure where the comment should go. Then + +- Comments on the reported issue should go on the issue. + +- Comments on the submitted code should go on the PR. + + +Checking PRs +============ + +If you manage to fix a bug or enhance Sage, you are our hero. See +:ref:`chapter-walkthrough` for making changes to the Sage source code and +:ref:`section-github-create-pr` to create a PR for the changes. + +For each push to a PR, automated tests run on GitHub Actions. + +* A `linting workflow + `_ + checks that the code of the current branch adheres to the style guidelines + using :ref:`section-tools-pycodestyle` (in the ``pycodestyle-minimal`` + configuration) and :ref:`section-tools-relint`. + + In order to see details when it fails, you can click on the check + and then select the most recent workflow run. + +* The `incremental build and test workflow + `_ + on GitHub Actions builds Sage for the current branch (incrementally + on top of an installation of the ``develop`` branch) and runs the + test. + + Details are again available by clicking on the check. + + The automatic workflow runs on a container based on + ``ubuntu-focal-standard``. To request a run of the workflow on a different + platform, you can issue a `workflow_dispatch + `_. + You can select any of the platforms for which a `prebuilt container image + `_ + exists. + +* The `Documentation workflow + `_ + on GitHub Actions builds the HTML documentation for the current branch. + + A link to the built doc is added in a comment, you can easily inspect changes + to the documentation without the need to locally rebuild the docs yourself. + If the doc build fails, you can go to Actions tab and examine `Build documentation workflow + `_ and + choose the particular branch to see what went wrong. + + +Final Notes +=========== + +The following are some other relevant issues: + +* Every bug fixed should result in a doctest. + +* This is not an issue with defects, but there are many enhancements + possible for Sage and too few developers to implement all the good + ideas. + +* If you are a developer, be nice and try to solve a stale/old issue + every once in a while. + +* Some people regularly do triage. In this context, this means that we + look at new bugs and classify them according to our perceived + priority. It is very likely that different people will see + priorities of bugs very differently from us, so please let us know + if you see a problem with specific PRs. + + + diff --git a/src/doc/en/developer/index.rst b/src/doc/en/developer/index.rst index 670f51dbc97..cf204c5afae 100644 --- a/src/doc/en/developer/index.rst +++ b/src/doc/en/developer/index.rst @@ -1,18 +1,16 @@ .. _developers-guide: -====================================== -Welcome to the Sage Developer's Guide! -====================================== +=============================== +Welcome to Sage Developer Guide +=============================== -.. WARNING:: +.. NOTE:: - **Sage development moved to GitHub in February 2023.** After the transition, - some parts of this guide (especially those related with `the Sage Trac - server `_) became obsolete and need to be - updated according to the new workflow on GitHub. See our `transition guide - from Trac to GitHub - `_ - for the preliminary version of the workflow. + Sage development moved to `GitHub `_ in + February 2023, from `the Sage Trac server `_, + which had been the center of Sage development for a long time. After the + transition, this guide was updated accordingly. However, the legacy is + still with us in many aspects of the current Sage development. Everybody who uses Sage is encouraged to contribute something back to Sage at some point. You could: @@ -30,153 +28,162 @@ reporting bugs to modifying and extending Sage and its documentation. We also discuss how to share your new and modified code with other Sage users around the globe. -Here are brief overviews of each part; for more details, see the extended table -of contents below. No matter where you start, good luck and welcome to Sage -development! +To begin with, you need of course your own copy of Sage source code to change +it. Use our `Installation guide +`_ to get it and for +instructions to build Sage from source. If you have never worked on software +before, pay close attention to the `prerequisites to build +`_ on +your platform. -- **Trac server:** all changes go through `the Sage Trac server - `_ at some point. It contains bug reports, upgrade - requests, changes in progress, and those already part of Sage - today. :ref:`Click here ` for more information. +Now here is a brief overview of this guide. - Importantly, you will need to :ref:`create a trac account - ` in order to contribute. +- :ref:`section-first-steps`: To share changes with the Sage community, you + need to learn about revision control. We use the software Git for this + purpose. Here we walk you through from setting up Git on your platform + and to preparing a local branch to share with all Sage users. -- **Source code:** You need your own copy of Sage's source code to change it. - `Go there `_ to get it - and for instructions to build it. + .. NOTE:: - If you have never worked on software before, pay close attention to the - `prerequisites to compile - `_ on your - system. + As an easy way to get started, you can run and edit Sage's code and contribute + your changes using `Gitpod `_, a free online development + environment based on VS Code. It will launch a pre-made workspace with all + dependencies and tools installed so that you can start contributing straight + away. Start by `going to Gitpod + `_, and read :ref:`our + Gitpod guidelines ` to learn more. - As an easy way to get started, you can run and edit Sage's code and contribute - your changes using `Gitpod `_, - a free online development environment based on VS Code. - It will launch a pre-made workspace with all dependencies and tools installed - so that you can start contributing straight away. - Start by `going to Gitpod `_, - and read :ref:`our Gitpod guidelines ` to learn more. +- :ref:`section-development-on-github`: All changes go through `the Sage + repository on GitHub `_ at some point. It contains + bug reports, enhancement proposals, changes in progress, and indeed all the + history of Sage today. You have to be familiar with it to be involved in Sage + development. -- **Conventions:** read our :ref:`conventions and guidelines - ` for code and documentation. +- :ref:`section-git-tricks-and-tips`: Here we give in-depth guide for working + with Git for Sage development. Read this when you need help on Git in a + tricky situation such as merge conflict. - For everything related to manuals, tutorials, and languages, :ref:`click here - `. +- :ref:`section-writing-code-for-sage`: This gives guide on conventions in + writing code and documentation. To be a good developer, read this part once + in a while -- **Git (revision control):** To share changes with the Sage community, you will - need to learn about revision control; we use the software Git for this - purpose. +- :ref:`section-testing-sage`: We value testing Sage highest. Every change of + Sage has a risk to break Sage, and must be tested before merge to Sage. This + part explains abundant tools to keep Sage working. - - :ref:`How to install it? ` - - :ref:`How to configure it for use with Trac? ` - - :ref:`Here is ` an overview of our development flow. +- :ref:`section-updating-documentation`: All featuers of Sage is documented in + our manuals. This part explains the technical aspect of updating + documentation on Sage -Git and Trac for Sage development -================================= +- :ref:`section-more-on-coding`: When you need to know the technical details of + Sage for coding, read this. -First Steps with Git --------------------- +- :ref:`section-packaging`: Sage is composed of many third-party packages and + its own distribution packages. This part is for advanced developers. -Sage uses git for version control. +For more details, see the table of contents below. No matter where +you start, good luck and welcome to Sage development! + +Table of Contents +================= + + +.. _section-first-steps: + +First Steps +----------- .. toctree:: - :maxdepth: 3 + :maxdepth: 2 git_setup - walk_through + walkthrough -.. _section-git-tricks-and-tips: -Using Git with Trac -------------------- +.. _section-development-on-github: -To contribute back your changes to Sage source code to the project, -you will need a ticket on the -`Sage trac server `_. +Working on GitHub +----------------- .. toctree:: :maxdepth: 2 - trac - manual_git - git_background - advanced_git + github workflows - git_trac + review + + +.. _section-git-tricks-and-tips: + +Working with Git +---------------- + +.. toctree:: + :maxdepth: 2 + + git_basic + git_advanced + git_background .. _section-writing-code-for-sage: Writing Code for Sage -===================== +--------------------- .. toctree:: - :maxdepth: 3 + :maxdepth: 2 workspace coding_basics - reviewer_checklist -Running Sage's tests --------------------- -.. toctree:: - :maxdepth: 3 +.. _section-testing-sage: - doctesting - -Testing on multiple platforms ------------------------------ +Testing Sage +------------ .. toctree:: - :maxdepth: 3 + :maxdepth: 2 + doctesting portability_testing - -Additional development and testing tools ----------------------------------------- - -.. toctree:: - :maxdepth: 3 - tools -Contributing to Manuals and Tutorials -------------------------------------- +.. _section-updating-documentation: + +Updating Sage Documentation +--------------------------- .. toctree:: - :maxdepth: 3 + :maxdepth: 2 sage_manuals -Sage Coding Details -------------------- + +.. _section-more-on-coding: + +More on Coding for Sage +----------------------- .. toctree:: - :maxdepth: 3 + :maxdepth: 2 coding_in_python coding_in_cython coding_in_other -Packaging the Sage Library --------------------------- -.. toctree:: - :maxdepth: 3 +.. _section-packaging: - packaging_sage_library - -Packaging Third-Party Code --------------------------- +Packaging +--------- .. toctree:: - :maxdepth: 3 + :maxdepth: 2 packaging - + packaging_sage_library Indices and tables ================== diff --git a/src/doc/en/developer/manual_git.rst b/src/doc/en/developer/manual_git.rst deleted file mode 100644 index cf0c109dacc..00000000000 --- a/src/doc/en/developer/manual_git.rst +++ /dev/null @@ -1,498 +0,0 @@ -.. highlight:: shell-session - -.. _chapter-manual-git: - -=================================== -Using Git with the Sage Trac Server -=================================== - -.. WARNING:: - - **Sage development moved to GitHub in February 2023.** After the transition, - some parts of this guide (especially those related with `the Sage Trac - server `_) became obsolete and need to be - updated according to the new workflow on GitHub. See our `transition guide - from Trac to GitHub - `_ - for the preliminary version of the workflow. - -Now we continue our introduction to git from :ref:`chapter-walkthrough`. -We discuss how to push your local changes to a remote repository -so that your changes can be reviewed for inclusion in Sage. - -In the following, we assume that you are in the source directory of Sage (``SAGE_ROOT``), -obtained either from a source tarball or by cloning a Sage git repository -such as /~https://github.com/sagemath/sage.git, as described in the -`README `_. -In either case, this source directory is actually the main worktree of -a local git repository. - -Using the following command, we can see which remote repository or repositories -are associated with this local repository:: - - [user@localhost sage]$ git remote -v - origin /~https://github.com/sagemath/sage.git (fetch) - origin /~https://github.com/sagemath/sage.git (push) - -.. _section-git-ssh: - -Git authentication through SSH -============================== - -In order to push changes securely to a remote repository, git uses -public-key cryptography. This section will show you how to set up the -necessary cryptographic keys for Secure Shell (SSH). - - -Checking whether you have already have suitable SSH keys --------------------------------------------------------- - -Follow the instructions in -https://docs.gitlab.com/ee/user/ssh.html#see-if-you-have-an-existing-ssh-key-pair. - - -Generating your SSH Keys ------------------------- - -If you don't have suitable SSH keys yet, you can create a key pair -with the ``ssh-keygen`` tool. - -Follow either the detailed instructions at -https://docs.gitlab.com/ee/user/ssh.html#generate-an-ssh-key-pair -or the following brief instructions:: - - [user@localhost ~]$ ssh-keygen - Generating public/private rsa key pair. - Enter file in which to save the key (/home/user/.ssh/id_rsa): - Enter passphrase (empty for no passphrase): - Enter same passphrase again: - Your identification has been saved in /home/user/.ssh/id_rsa. - Your public key has been saved in /home/user/.ssh/id_rsa.pub. - The key fingerprint is: - ce:32:b3:de:38:56:80:c9:11:f0:b3:88:f2:1c:89:0a user@localhost - The key's randomart image is: - +--[ RSA 2048]----+ - | .... | - | .. | - | .o+ | - | o o+o. | - |E + . .S | - |+o . o. | - |. o +.o | - | oB | - | o+.. | - +-----------------+ - -This will generate a new random private RSA key -in the ``.ssh`` folder in your home directory. By default, they are - -``~/.ssh/id_rsa`` - Your private key. Keep safe. **Never** hand it out to anybody. - -``~/.ssh/id_rsa.pub`` - The corresponding public key. This and only this file can be safely - disclosed to third parties. - -The ``ssh-keygen`` tool will let you generate a key with a different -file name, or protect it with a passphrase. Depending on how much you -trust your own computer or system administrator, you can leave the -passphrase empty to be able to login without any human intervention. - - -.. _section-trac-ssh-key: - -Linking your Public Key to your Trac Account --------------------------------------------- - -In order to push your code directly to a branch on the git repository -trac.sagemath.org, the Sage trac server needs to know your public -key. You can upload it in the preferences, that is - -1. Go to https://trac.sagemath.org - -2. Log in with your trac username/password - -3. Click on "Preferences" - -4. Go to the "SSH Keys" tab - -5. Paste the content of your public key file - (e.g. ``~/.ssh/id_rsa.pub``) - -6. Click on "Save changes" - -Note that this does **not** allow you to ssh into any account on trac, -it is only used to authenticate you to the gitolite installation on -trac. You can test that you are being authenticated correctly by -issuing some basic gitolite commands, for example:: - - [user@localhost ~]$ ssh git@trac.sagemath.org info - hello user, this is git@trac running gitolite3 (unknown) on git 1.7.9.5 - - R W sage - [user@localhost ~]$ ssh git@trac.sagemath.org help - hello user, this is gitolite3 (unknown) on git 1.7.9.5 - - list of remote commands available: - - desc - help - info - perms - writable - -Adding your Public Key for authentication on another server ------------------------------------------------------------ - -If you have an account on a lab or department computer that allows you -to log in remotely via SSH, you can now also use your SSH keys to -log in. Just copy the **public** key file (ending in ``.pub``) to -``~/.ssh/authorized_keys`` on the remote computer and make sure that -the file is only read/writeable by yourself. Voila, the next time you -ssh into that machine you don't have to provide your password. - - -.. _section-git-trac: - -The git repository trac.sagemath.org -==================================== - -The Sage trac server is another git repository for the Sage source tree, it is -served via the ssh protocol. To add it as a remote repository to your local git -repository, use these commands:: - - [user@localhost sage]$ git remote add trac /~https://github.com/sagemath/sagetrac-mirror.git -t master - [user@localhost sage]$ git remote set-url --push trac git@trac.sagemath.org:sage.git - -.. WARNING:: - - **Sage development moved to GitHub in February 2023.** After the move, the - Sage trac server git@trac.sagemath.org:sage.git no longer operates, but all - branches are available (in read-only mode) on - /~https://github.com/sagemath/sagetrac-mirror.git. - -Instead of ``trac`` you can use any other name you want, of course. -To verify that it is set up correctly:: - - [user@localhost sage]$ git remote -v - origin /~https://github.com/sagemath/sage.git (fetch) - origin /~https://github.com/sagemath/sage.git (push) - trac /~https://github.com/sagemath/sagetrac-mirror.git (fetch) - trac git@trac.sagemath.org:sage.git (push) - -It is perfectly fine to have multiple remote repositories for git, -think of them as bookmarks. You can then use ``git pull`` to get -changes and ``git push`` to upload your local changes using:: - - [user@localhost sage]$ git trac [ARGS] - -.. NOTE:: - - In the command above we set up the remote to only track the - ``master`` branch on the trac server (the ``-t master`` - option). This avoids clutter by not automatically downloading all - branches ever created. But it also means that you will not fetch - everything that is on trac by default, and you need to explicitly - tell git which branch you want to get from trac. See the - :ref:`section-git-checkout` section for examples. - -Note that write operations (``push``) use the ssh protocol (specified by the ``git@`` -part). For this to work, you need to have a trac account and to set up your ssh public -key as described in `Trac authentication through ssh -`_. -Authentication is necessary if you want to upload anything to ensure -that it really is from you. - -The above instructions set up the remote to perform read-only operations (``fetch``) -using HTTPS from a mirror of the trac repository instead. The mirror is faster and -more reliable than our git server. However, this configuration is not recommended if -you use VS Code as an IDE. - -If you want to use ssh only for both ``fetch`` and ``push``, use the -following commands instead:: - - [user@localhost sage]$ git remote add trac git@trac.sagemath.org:sage.git -t master - [user@localhost sage]$ git remote -v - origin /~https://github.com/sagemath/sage.git (fetch) - origin /~https://github.com/sagemath/sage.git (push) - trac git@trac.sagemath.org:sage.git (fetch) - trac git@trac.sagemath.org:sage.git (push) - -* The Patch buildbot will automatically test your ticket. See :trac:`wiki/patchbot` - for more information about its features and limitations. Make sure that you - look at the log, especially if the patch buildbot did not give you - the green blob. - - -.. _section-git-checkout: - -Checking Out Tickets --------------------- - -Trac tickets that are finished or in the process of being worked on -can have a git branch attached to them. This is the "Branch:" field in -the ticket description. The branch name is generally of the form -``u/user/description``, where ``user`` is the name of the user who -made the branch and ``description`` is some free-form short -description (and can include further slashes). - -If you want to work with the changes in that remote branch, you must -make a local copy. In particular, git has no concept of directly -working with the remote branch, the remotes are only bookmarks for -things that you can get from/to the remote server. Hence, the first -thing you should do is to get everything from the trac server's branch -into your local repository. This is achieved by:: - - [user@localhost sage]$ git fetch trac u/user/description - remote: Counting objects: 62, done. - remote: Compressing objects: 100% (48/48), done. - remote: Total 48 (delta 42), reused 0 (delta 0) - Unpacking objects: 100% (48/48), done. - From trac.sagemath.org:sage - * [new branch] u/user/description -> FETCH_HEAD - -The ``u/user/description`` branch is now temporarily (until you fetch -something else) stored in your local git database under the alias -``FETCH_HEAD``. In the second step, we make it available as a new -local branch and switch to it. Your local branch can have a different -name, for example:: - - [user@localhost sage]$ git checkout -b my_branch FETCH_HEAD - Switched to a new branch 'my_branch' - -creates a new branch in your local git repository named ``my_branch`` -and modifies your local Sage filesystem tree to the state of the files -in that ticket. You can now edit files and commit changes to your -local branch. - - -.. _section-git-push: - -Pushing Your Changes to a Ticket --------------------------------- - -To add your local branch to a trac ticket, you should first decide on -a name on the Sage trac repository. - -For read/write permissions on git branches, see -:ref:`section-git_trac-branch-names` - -In order to avoid name clashes, you can use -``u/your_username/a_description_of_your_branch`` (the description can contain -slashes, but no spaces). Then: - -- **Fill** the ``Branch`` field of the trac ticket with that name. - -- **Push** your branch to trac with either:: - - [user@localhost sage]$ git push --set-upstream trac HEAD:u/user/description - - if you started the branch yourself and do not follow any other branch, - or use:: - - [user@localhost sage]$ git push trac HEAD:u/user/description - - if your branch already has an upstream branch. - -Here, ``HEAD`` means that you are pushing the most recent commit (and, by -extension, all of its parent commits) of the current local branch to the remote -branch. - -The ``Branch`` field on the trac ticket can appear in red/green. See -:ref:`section-trac-fields` to learn what it means. - - -.. _section-git-pull: - -Getting Changes ---------------- - -A common task during development is to synchronize your local copy of -the branch with the branch on trac. In particular, assume you -downloaded somebody else's branch and made some suggestions for -improvements on the trac ticket. Now the original author incorporated -your suggestions into his branch, and you want to get the added -changesets to complete your review. Assuming that you originally got -your local branch as in :ref:`section-git-checkout`, you can just -issue:: - - [user@localhost sage]$ git pull trac u/user/description - From trac.sagemath.org:sage - * branch u/user/description -> FETCH_HEAD - Updating 8237337..07152d8 - Fast-forward - src/sage/tests/cmdline.py | 3 ++- - 1 file changed, 2 insertions(+), 1 deletions(-) - -where now ``user`` is the other developer's trac username and -``description`` is some description that he chose. This command will -download the changes from the originally-used remote branch and merge -them into your local branch. If you haven't published your local -commits yet then you can also rebase them via:: - - [user@localhost sage]$ git pull -r trac u/user/description - From trac.sagemath.org:sage - * branch u/user/description -> FETCH_HEAD - First, rewinding head to replay your work on top of it... - Applying: my local commit - -See :ref:`section-git-merge` section for an in-depth explanation of -merge vs. rebase. - -So far, we assumed that there are no conflicts. It is unavoidable in -distributed development that, sometimes, the same location in a source -source file is changed by more than one person. Reconciling these -conflicting edits is explained in the :ref:`section-git_trac-conflict` -section. - - -.. _section-git-pull-master: - -Updating Master ---------------- - -The ``master`` branch can be updated just like any other branch. However, your -local copy of the master branch should stay **identical** to the trac master -branch. - -If you accidentally added commits to your local copy of ``master``, you must -delete them before updating the branch. - -One way to ensure that you are notified of potential problems is to use ``git -pull --ff-only``, which will raise an error if a non-trivial merge would be -required:: - - [user@localhost sage]$ git checkout master - [user@localhost sage]$ git pull --ff-only trac master - -If this pull fails, then something is wrong with the local copy of the -master branch. To switch to the correct Sage master branch, use:: - - [user@localhost sage]$ git checkout master - [user@localhost sage]$ git reset --hard trac/master - - -.. _section-git-merge: - -Merging and Rebasing -==================== - -Sometimes, a new version of Sage is released while you work on a git branch. - -Let us assume you started ``my_branch`` at commit ``B``. After a while, your -branch has advanced to commit ``Z``, but you updated ``master`` (see -:ref:`section-git-pull-master`) and now your git history looks like this (see -:ref:`section_walkthrough_logs`): - -.. CODE-BLOCK:: text - - X---Y---Z my_branch - / - A---B---C---D master - -How should you deal with such changes? In principle, there are two ways: - - -* **Rebase:** The first solution is to **replay** commits ``X,Y,Z`` atop of the - new ``master``. This is called **rebase**, and it rewrites your current - branch: - - .. CODE-BLOCK:: text - - git checkout my_branch - git rebase -i master - - In terms of the commit graph, this results in: - - .. CODE-BLOCK:: text - - X'--Y'--Z' my_branch - / - A---B---C---D master - - Note that this operation rewrites the history of ``my_branch`` (see - :ref:`section-git-rewriting-history`). This can lead to problems if somebody - began to write code atop of your commits ``X,Y,Z``. It is safe otherwise. - - **Alternatively**, you can rebase ``my_branch`` while updating master at the - same time (see :ref:`section-git-pull`): - - .. CODE-BLOCK:: text - - git checkout my_branch - git pull -r master - -* **Merging** your branch with ``master`` will create a new commit above the two - of them: - - .. CODE-BLOCK:: text - - git checkout my_branch - git merge master - - The result is the following commit graph: - - .. CODE-BLOCK:: text - - X---Y---Z---W my_branch - / / - A---B---C-------D master - - - **Pros:** you did not rewrite history (see - :ref:`section-git-rewriting-history`).The additional commit is then easily - pushed to the git repository and distributed to your collaborators. - - - **Cons:** it introduced an extra merge commit that would - not be there had you used rebase. - - **Alternatively**, you can merge ``my_branch`` while updating master at the - same time (see :ref:`section-git-pull`): - - .. CODE-BLOCK:: text - - git checkout my_branch - git pull master - -**In case of doubt** use merge rather than rebase. There is less risk involved, -and rebase in this case is only useful for branches with a very long history. - -Finally, **do nothing unless necessary:** it is perfectly fine for your branch -to be behind ``master``. You can always merge/rebase if/when your branch's name -appears in red on its trac page (see :ref:`section-trac-fields`), or when you -will really need a feature that is only available in the current master. - -.. _section-git-mergetool: - -Merge Tools -=========== - -Simple conflicts can be easily solved with git only (see :ref:`section-git_trac-conflict`) - -For more complicated ones, a range of specialized programs are -available. Because the conflict marker includes the hash of the most recent -common parent, you can use a three-way diff:: - - [alice@laptop]$ git mergetool - - This message is displayed because 'merge.tool' is not configured. - See 'git mergetool --tool-help' or 'git help config' for more details. - 'git mergetool' will now attempt to use one of the following tools: - meld opendiff kdiff3 [...] merge araxis bc3 codecompare emerge vimdiff - Merging: - fibonacci.py - - Normal merge conflict for 'fibonacci.py': - {local}: modified file - {remote}: modified file - Hit return to start merge resolution tool (meld): - -If you don't have a favourite merge tool we suggest you try `meld -`_ (cross-platform). The result looks like the following -screenshot. - -.. IMAGE:: static/meld-screenshot.png - -The middle file is the most recent common parent; on the right is -Bob's version and on the left is Alice's conflicting version. Clicking -on the arrow moves the marked change to the file in the adjacent -pane. diff --git a/src/doc/en/developer/packaging.rst b/src/doc/en/developer/packaging.rst index 8f084b38ff6..b55df2225cb 100644 --- a/src/doc/en/developer/packaging.rst +++ b/src/doc/en/developer/packaging.rst @@ -2,9 +2,9 @@ .. _chapter-packaging: -========================== -Packaging Third-Party Code -========================== +=================================== +Packaging Third-Party Code for Sage +=================================== One of the mottoes of the Sage project is to not reinvent the wheel: If an algorithm is already implemented in a well-tested library then @@ -1245,6 +1245,6 @@ must meet the following requirements: this is not possible. - **Refereeing**. The code must be refereed, as discussed in - :ref:`chapter-sage-trac`. + :ref:`chapter-github`. diff --git a/src/doc/en/developer/packaging_sage_library.rst b/src/doc/en/developer/packaging_sage_library.rst index 229c3dc01d7..ef344d657a6 100644 --- a/src/doc/en/developer/packaging_sage_library.rst +++ b/src/doc/en/developer/packaging_sage_library.rst @@ -1,9 +1,9 @@ .. _chapter-modularization: -============================ - Packaging the Sage Library -============================ +=========================================== +Packaging the Sage Library for Distribution +=========================================== Modules, packages, distribution packages diff --git a/src/doc/en/developer/review.rst b/src/doc/en/developer/review.rst new file mode 100644 index 00000000000..2ca7bccc8f1 --- /dev/null +++ b/src/doc/en/developer/review.rst @@ -0,0 +1,262 @@ +.. nodoctest + +.. _chapter-review: + +============== +Reviewing Code +============== + +All code that goes into Sage is peer-reviewed. Two reasons for this are: + +- Because a developer cannot think of everything at once +- Because a fresh pair of eyes may spot a mathematical error, + a corner-case in the code, insufficient documentation, a missing + consistency check, etc. + +Anybody (e.g. you) can do this job for somebody else's PR. This document +lists things that the reviewer must check before deciding that a PR is +ready for inclusion into Sage. + +You can now begin the review by reading the diff code. + +**Read the diff:** + +**Build the code:** while you read the code, you can :ref:`rebuild Sage with the +new code `. If you do not know how to **download the +code**, :ref:`click here `. + +The following should generally be checked while reading and testing the code: + +- **The purpose**: Does the code address the PR's stated aim? Can it + introduce any new problems? Does testing the new or fixed functionality + with a variety of input, not just the examples in the documentation, + give expected and robust output (and no unexpected errors or crashes)? + +- **User documentation**: Is the use of the new code clear to a user? Are all + mathematical notions involved standard, or is there explanation (or a link + to one) provided? Can he/she find the new code easily if he/she needs it? + +- **Code documentation**: Is the code sufficiently commented so that a developer + does not have to wonder what exactly it does? + +- **Conventions**: Does the code respect :ref:`Sage's conventions + `? :ref:`Python's convention `? + :ref:`Cython's convention `? + +- **Doctest coverage**: Do all functions contain doctests? Use ``sage -coverage + `` to check it. Are all aspects of the new/modified methods and classes + tested (see :ref:`section-doctest-writing`)? + +- **Bugfixes**: If the PR contains a bugfix, does it add a doctest + illustrating that the bug has been fixed? This new doctest should contain the + issue or PR number, for example ``See :issue:`12345```. + +- **Speedup**: Can the PR make any existing code slower? if the PR + claims to speed up some computation, does the PR contain code examples to + illustrate the claim? The PR should explain how the speedup is achieved. + +- **Build the manuals**: Does the reference manual build without + errors (check both html and pdf)? See :ref:`chapter-sage_manuals` to + learn how to build the manuals. + +- **Look at the manuals**: Does the reference manual look okay? The + changes may have typos that allow the documentation to build without + apparent errors but that may cause badly formatted output or broken + hyperlinks. + +- **Run the tests**: Do all doctests pass without errors? Unrelated components + of Sage may be affected by the change. Check all tests in the whole library, + including "long" doctests (this can be done with ``make ptestlong``) and any + optional doctests related to the functionality. See :ref:`chapter-doctesting` + for more information. + +You are now ready to change the ticket's status (see +:ref:`section-github-pr-status`): + +- **positive review**: If the answers to the questions above and other + reasonable questions are *"yes"*, you can set the PR to + ``positive_review``. + +- **needs_work**: If something is not as it should, write a list of all points + that need to be addressed in a comment and change the PR's status to + ``needs_work``. + +- **needs_info**: If something is not clear to you and prevents you from going + further with the review, ask your question and set the PR's status to + ``needs_info``. + +- If you **do not know what to do**, for instance if you don't feel experienced + enough to take a final decision, explain what you already did in a comment and + ask if someone else could take a look. + +For more advice on reviewing, see [WSblog]_. + +.. NOTE:: + + "The perfect is the enemy of the good" + + The point of the review is to ensure that the Sage code guidelines + are followed and that the implementation is mathematically + correct. Please refrain from additional feature requests or + open-ended discussion about alternative implementations. If you + want the patch written differently, your suggestion should be a + clear and actionable request. + +REFERENCES: + +.. [WSblog] William Stein, How to Referee Sage Trac Tickets, + http://sagemath.blogspot.com/2010/10/how-to-referee-sage-trac-tickets.html + (Caveat: mercurial was replaced with git) + +.. _section-workflows-review: + +Reviewing a change +================== + +Use the checks on GitHub Actions. + +Use `pull request reviews +`_. +You can add comments directly to changed lines. + +Smaller suggestions can be made `through the github web interface +`_ as +part of the `pull request review +`_. + +If you want to be able to make changes directly to others' PRs** (when the +author selects to allow edits from maintainers), please contact one of the +`Sagemath Github admins +`_, who can give +you the relevant permissions. + +Change the status of the PR (e.g., "positive review" or "needs work"), choose +the `correct type of your pull request review +`_ +(i.e. "approve" vs "request changes") + +For trying the branch of a PR locally, use:: + + git fetch upstream pull/PULL-REQUEST-ID/head:LOCAL-BRANCH-NAME + +Consult https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally. + +Alternatively,:: + + gh pr checkout PULL-REQUEST-ID + +Consult https://cli.github.com/manual/gh_pr_checkout. + + +Reviewing and Closing PRs +========================= + +PRs can be closed when they have positive review or for other reasons. To +learn how to review, please see :ref:`chapter-review`. + +Use one of the *resolution labels* ``r: duplicate``, ``r: invalid``, ``r: wontfix``. Add a comment explaining why the issue has been closed if that's not already clear from the discussion. + +Users with the necessary permissions can then directly `close the issue `_: In the dropdown menu on the "Close issue" button, select "Close as not planned" + +Otherwise, use the labels "needs review" or "positive review", and someone else with the necessary rights will take care of closing the issue. + +If you think an issue has been prematurely be closed, feel free to reopen it. + + +Reasons to Invalidate PRs +========================= + +**One Issue Per One Issue**: An issue must cover only one issue +and should not be a laundry list of unrelated issues. If an issue +covers more than one issue, we cannot close it and while some of +the patches have been applied to a given release, the issue would +remain in limbo. + +**No Patch Bombs**: Code that goes into Sage is peer-reviewed. If +you show up with an 80,000 lines of code bundle that completely +rips out a subsystem and replaces it with something else, you can +imagine that the review process will be a little tedious. These +huge patch bombs are problematic for several reasons and we prefer +small, gradual changes that are easy to review and apply. This is +not always possible (e.g. coercion rewrite), but it is still highly +recommended that you avoid this style of development unless there +is no way around it. + +**Sage Specific**: Sage's philosophy is that we ship everything +(or close to it) in one source tarball to make debugging possible. +You can imagine the combinatorial explosion we would have to deal +with if you replaced only ten components of Sage with external +packages. Once you start replacing some of the more essential +components of Sage that are commonly packaged (e.g. Pari, GAP, +lisp, gmp), it is no longer a problem that belongs in our tracker. +If your distribution's Pari package is buggy for example, file a +bug report with them. We are usually willing and able to solve +the problem, but there are no guarantees that we will help you +out. Looking at the open number of tickets that are Sage specific, +you hopefully will understand why. + +**No Support Discussions**: The trac installation is not meant to +be a system to track down problems when using Sage. Tickets should +be clearly a bug and not "I tried to do X and I couldn't get it to +work. How do I do this?" That is usually not a bug in Sage and it +is likely that ``sage-support`` can answer that question for you. If +it turns out that you did hit a bug, somebody will open a concise +and to-the-point ticket. + +**Solution Must Be Achievable**: Tickets must be achievable. Many +times, tickets that fall into this category usually ran afoul to +some of the other rules listed above. An example would be to +"Make Sage the best CAS in the world". There is no metric to +measure this properly and it is highly subjective. + +The Release Process +=================== + +The Sage Release Manager uses the following procedure to make releases, as of +2022. + +**Beta Release Stage**: For preparing a new beta release or the first release +candidate, all positively reviewed tickets with the forthcoming release +milestone are considered. Tickets that have unmerged dependencies are ignored. +The Release Manager merges tickets in batches of 10 to 20 tickets, taking the +ticket priority into account. If a merge conflict of a ticket to the Release +Manager's branch occurs, the ticket is set back to "needs work" status by the +Release Manager, and the list of the tickets already merged to the Release +Manager's branch is posted. The author of the ticket needs to identify +conflicting tickets in the list, make merge commits and declare them as +dependencies, before setting back to "positive review" status. Each batch of +merged tickets then undergoes integration testing. If problems are detected, a +ticket will be set back to "needs work" status and unmerged. When a batch of +tickets is ready, the Release Manager closes these tickets and proceeds to the +next batch. After a few batches, a new beta release is tagged, pushed to the +``develop`` branch on the main git repository, and announced on +``sage-release``. + +**Release Candidate Stage**: After the first release candidate has been made, +the project is in the release candidate stage, and a modified procedure is +used. Now **only tickets with a priority set to "blocker" are considered**. +Tickets with all other priorities, including "critical", are ignored. Hence if +a ticket is important enough to merit inclusion in this stage, it should be set +to "blocker". + +**Blocker Tickets**: The goal of the release process is to make a stable +release of high quality. Be aware that there is a risk/benefit trade-off in +merging a ticket. The benefit of merging a ticket is the improvement that the +ticket brings, such as fixing a bug. However, any code change has a risk of +introducing unforeseen new problems and thus delaying the release: If a new +issue triggers another release candidate, it delays the release by 1-2 weeks. +Hence developers should use "blocker" priority sparingly and should indicate +the rationale on the ticket. Though there is no one fixed rule or authority +that determines what is appropriate for "blocker" status, + +- Tickets introducing new features are usually not blockers -- unless perhaps + they round out a set of features that were the focus of development of this + release cycle. + +- Tickets that make big changes to the code, for example refactoring tickets, + are usually not blockers. + +**Final Release**: If there is no blocker ticket for the last release +candidate, the Release Manager turns it to the final release. It is tagged with +the release milestone, and announced on ``sage-release``. + diff --git a/src/doc/en/developer/reviewer_checklist.rst b/src/doc/en/developer/reviewer_checklist.rst deleted file mode 100644 index 2f352b1dbb3..00000000000 --- a/src/doc/en/developer/reviewer_checklist.rst +++ /dev/null @@ -1,137 +0,0 @@ -.. nodoctest - -.. _chapter-review: - -========================= -The reviewer's check list -========================= - -.. WARNING:: - - **Sage development moved to GitHub in February 2023.** After the transition, - some parts of this guide (especially those related with `the Sage Trac - server `_) became obsolete and need to be - updated according to the new workflow on GitHub. See our `transition guide - from Trac to GitHub - `_ - for the preliminary version of the workflow. - -All code that goes into Sage is peer-reviewed. Two reasons for this are: - -- Because a developer cannot think of everything at once; -- Because a fresh pair of eyes may spot a mathematical error, - a corner-case in the code, insufficient documentation, a missing - consistency check, etc. - -Anybody (e.g. you) can do this job for somebody else's ticket. This document -lists things that the reviewer must check before deciding that a ticket is -ready for inclusion into Sage. - -- Do you know what the **trac server** is? If not, :ref:`click here - `. - -- Do you have a **trac account**? If not, :ref:`click here - `. - -You can now begin the review by reading the diff code. - -**Read the diff:** the diff (i.e. the ticket's content) can be obtained by -clicking on the (green) branch's name that appears on the trac ticket. If that -name appears in red (see :ref:`section-trac-fields`) you can say so in a comment -and set the ticket to ``needs_work`` (see :ref:`section-trac-ticket-status`). - -**Build the code:** while you read the code, you can :ref:`rebuild Sage with the -new code `. If you do not know how to **download the -code**, :ref:`click here ` (with git trac) or -:ref:`here ` (git only). - - -The following should generally be checked while reading and testing the code: - -- **The purpose**: Does the code address the ticket's stated aim? Can it - introduce any new problems? Does testing the new or fixed functionality - with a variety of input, not just the examples in the documentation, - give expected and robust output (and no unexpected errors or crashes)? - -- **User documentation**: Is the use of the new code clear to a user? Are all - mathematical notions involved standard, or is there explanation (or a link - to one) provided? Can he/she find the new code easily if he/she needs it? - -- **Code documentation**: Is the code sufficiently commented so that a developer - does not have to wonder what exactly it does? - -- **Conventions**: Does the code respect :ref:`Sage's conventions - `? :ref:`Python's convention `? - :ref:`Cython's convention `? - -- **Doctest coverage**: Do all functions contain doctests? Use ``sage -coverage - `` to check it. Are all aspects of the new/modified methods and classes - tested (see :ref:`section-doctest-writing`)? - -- **Bugfixes**: If the ticket contains a bugfix, does it add a doctest - illustrating that the bug has been fixed? This new doctest should contain the - ticket number, for example ``See :trac:`12345```. - -- **Speedup**: Can the ticket make any existing code slower? if the ticket - claims to speed up some computation, does the ticket contain code examples to - illustrate the claim? The ticket should explain how the speedup is achieved. - -- **Build the manuals**: Does the reference manual build without - errors (check both html and pdf)? See :ref:`chapter-sage_manuals` to - learn how to build the manuals. - -- **Look at the manuals**: Does the reference manual look okay? The - changes may have typos that allow the documentation to build without - apparent errors but that may cause badly formatted output or broken - hyperlinks. - -- **Run the tests**: Do all doctests pass without errors? Unrelated components - of Sage may be affected by the change. Check all tests in the whole library, - including "long" doctests (this can be done with ``make ptestlong``) and any - optional doctests related to the functionality. See :ref:`chapter-doctesting` - for more information. - -You are now ready to change the ticket's status (see -:ref:`section-trac-ticket-status`): - -- **positive review**: If the answers to the questions above and other - reasonable questions are *"yes"*, you can set the ticket to - ``positive_review``. Add your full name to the "reviewer" field (see - :ref:`section-trac-fields`). - -- **needs_work**: If something is not as it should, write a list of all points - that need to be addressed in a comment and change the ticket's status to - ``needs_work``. - -- **needs_info**: If something is not clear to you and prevents you from going - further with the review, ask your question and set the ticket's status to - ``needs_info``. - -- If you **do not know what to do**, for instance if you don't feel experienced - enough to take a final decision, explain what you already did in a comment and - ask if someone else could take a look. - -**Reviewer's commit**: if you can fix the issues yourself, you may make a commit -in your own name and mark the commit as a reviewer's patch. To learn how -:ref:`click here ` (git trac) or :ref:`here -` (git only). This contribution must also be reviewed, for -example by the author of the original patch. - -For more advice on reviewing, see [WSblog]_. - -.. NOTE:: - - "The perfect is the enemy of the good" - - The point of the review is to ensure that the Sage code guidelines - are followed and that the implementation is mathematically - correct. Please refrain from additional feature requests or - open-ended discussion about alternative implementations. If you - want the patch written differently, your suggestion should be a - clear and actionable request. - -REFERENCES: - -.. [WSblog] William Stein, How to Referee Sage Trac Tickets, - http://sagemath.blogspot.com/2010/10/how-to-referee-sage-trac-tickets.html - (Caveat: mercurial was replaced with git) diff --git a/src/doc/en/developer/static/workflow.png b/src/doc/en/developer/static/workflow.png new file mode 100644 index 0000000000000000000000000000000000000000..a04a1d945654ffa2f484c095f93fe0c946b7c5c0 GIT binary patch literal 61263 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%0D(-1`MO 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 literal 0 HcmV?d00001 diff --git a/src/doc/en/developer/ticket_badges.png b/src/doc/en/developer/ticket_badges.png deleted file mode 100644 index 1da26d4e8674acbde1ed993454c68161f2d11a0c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47315 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 j#~gDp0%-=JWWdbB5LsP8PO< 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 OM^0K`_) became obsolete and need to be - updated according to the new workflow on GitHub. See our `transition guide - from Trac to GitHub - `_ - for the preliminary version of the workflow. - -Sometimes you will only want to work on local changes to Sage, for -your own private needs. However, typically it is beneficial to -share code and ideas with others; the manner in which the -`Sage project `_ does this (as well as fixing -bugs and upgrading components) is in a very collaborative and -public setting on `the Sage Trac server `_ -(the Sage bug and enhancement tracker). - -The purpose of the Sage trac server is to - -1. Provide a place for discussion on issues and store a permanent - record. - -2. Provide a repository of source code and all proposed changes. - -3. Link these two together. - -There is also a :trac:`wiki ` for more general -organizational web pages, like Sage development workshops. - -Thus if you find a bug in Sage, if you have new code to submit, want -to review new code already written but not yet included in Sage, or if -you have corrections for the documentation, you should post on the -trac server. Items on the server are called *tickets*, and anyone may -search or browse the tickets. For a list of recent changes, just visit -the :trac:`Sage trac timeline `. - -.. WARNING:: - - **Sage development moved to GitHub in February 2023.** - All functions of our Trac server were taken over by our main repository, - /~https://github.com/sagemath/sage. - - -.. _section-trac-account: - -Obtaining an Account -==================== - -If you do not have an account on GitHub yet, choose a user name and -`create an account `_. - -Using your GitHub account, you can log in to: - -- `the Sage trac server `_, so that you can - open tickets and participate in discussions on existing tickets. - - On the Sage trac server, click the link "GitHub Login" in the top - right corner and follow the prompts. - - Within the Sage trac server, your GitHub user name will be prefixed - with the letters ``gh-`` (which stand for "GitHub"). - -- GitLab, where the mirror repository `sagemath/sage - `_ accepts Merge Requests. - - In GitLab, click the button "Sign in / Register" in the top right - corner, and then use the button "Sign in with GitHub" and follow the - prompts. - -Users with legacy sage-trac accounts (account names not starting with -"gh-") should use the Login link. Do not to use GitHub login, as it -will be treated as a separate user from their original account (unless -you actively prefer to switch). - - -.. _trac-bug-report: - -Reporting Bugs -============== - -If you think you have found a bug in Sage, here is the procedure: - -- Search through our Google groups for postings related to your possible bug (it - may have been fixed/reported already): - - * ``sage-devel``: ``_ - * ``sage-support``: ``_ - - Similarly, you can search :ref:`chapter-sage-trac` to see if anyone else has - opened a ticket about your bug. - -- If you do not find anything, and you are not sure that you have found a bug, - ask about it on `sage-devel `_. A - bug report should contain: - - - An explicit and **reproducible example** illustrating your bug (and/or the - steps required to reproduce the buggy behavior). - - - The **version** of Sage you run, as well as the version of the optional - packages that may be involved in the bug. - - - Describe your **operating system** as accurately as you can and your - architecture (32-bit, 64-bit, ...). - -- You might be asked to open a new ticket. In this case, follow the - :ref:`section-trac-new-ticket`. - -Thank you in advance for reporting bugs to improve Sage in the future! - -.. _section-trac-new-ticket: - -Guidelines for Opening Tickets -============================== - -In addition to bug reports (see :ref:`trac-bug-report`), you should also open a -ticket if you have some new code that makes Sage a better tool. If you have a -feature request, start a discussion on ``sage-devel`` first, and then if there -seems to be general agreement that you have a good idea, open a ticket -describing the idea. - -- Do you already have a **trac account**? If not, :ref:`click here - `. - -**Before** opening a new ticket, consider the following points: - -- Make sure that nobody else has opened a ticket about the same or closely - related issue. - -- It is much better to open several specific tickets than one that - is very broad. Indeed, a single ticket which deals with lots of - different issues can be quite problematic, and should be avoided. - -- Be precise: If foo does not work on OS X but is fine on Linux, - mention that in the title. Use the keyword option so that - searches will pick up the issue. - -- The problem described in the ticket must be solvable. For - example, it would be silly to open a ticket whose purpose was - "Make Sage the best mathematical software in the world". There is - no metric to measure this properly and it is highly subjective. - -- For bug reports: the ticket's description should contain the information - described at :ref:`trac-bug-report`. - -- If appropriate, provide URLs to background information or sage-devel - conversation relevant to the problem you are reporting. - -**When creating** the ticket, you may find useful to read -:ref:`section-trac-fields`. - -Unless you know what you are doing, leave the milestone field to its default -value. - -.. _section-trac-fields: - -The Ticket Fields -================= - -When you open a new ticket or change an existing ticket, you will find a variety -of fields that can be changed. Here is a comprehensive overview (for the -'status' field, see :ref:`section-trac-ticket-status`): - -* **Reported by:** The trac account name of whoever created the - ticket. Cannot be changed. - -* **Owned by:** Trac account name of owner, by default the person in charge of - the Component (see below). Generally not used in the Sage trac. - -* **Type:** One of ``enhancement`` (e.g. a new feature), ``defect`` (e.g. a bug - fix), or ``task`` (rarely used). - -* **Priority:** The priority of the ticket. Keep in mind that the - "blocker" label should be used very sparingly. - -* **Milestone:** Milestones are usually goals to be met while working - toward a release. In Sage’s trac, we use milestones instead of - releases. Each ticket must have a milestone assigned. If you are - unsure, assign it to the current milestone. - -* **Component:** A list of components of Sage, pick one that most - closely matches the ticket. - -* **Keywords:** List of keywords. Fill in any keywords that you think - will make your ticket easier to find. Tickets that have been worked - on at Sage days ``NN`` (some number) ofter have ``sdNN`` as keyword. - -* **Cc:** List of trac user names to Cc (send emails for changes on - the ticket). Note that users that enter a comment are automatically - substcribed to email updates and don't need to be listed under Cc. - -* **Merged in:** The Sage release where the ticket was merged in. Only - changed by the release manager. - -* **Authors:** Real name of the ticket author(s). Set this field only if you - intend to provide code. - -* **Reviewers:** Real name of the ticket reviewer(s). - -* **Report Upstream:** If the ticket is a bug in an upstream component - of Sage, this field is used to summarize the communication with the - upstream developers. - -* **Work issues:** Issues that need to be resolved before the ticket - can leave the "needs work" status. - -* **Branch:** The Git branch containing the ticket's code (see - :ref:`section-walkthrough-branch`). It is displayed in green color, - unless there is a conflict between the branch and the latest beta - release (red color). In this case, the branch should be merged or - rebased. - -* **Dependencies:** Does the ticket depend on another ticket? - Sometimes, a ticket requires that another ticket be applied - first. If this is the case, put the dependencies as a - comma-separated list (``#1234, #5678``) into the "Dependencies:" - field. - -* **Stopgaps:** See :ref:`section-trac-stopgaps`. - -.. _section-trac-ticket-status: - -The Status of a Ticket -====================== - -The status of a ticket appears right next to its number, at the top-left corner -of its page. It indicates who has to work on it. - -- **new** -- the ticket has only been created (or the author forgot to change - the status to something else). - - If you intend to work on the code yourself, put your name in the Authors - field, or leave a comment to say so. It could avoid having two persons doing - the same job. - -- **needs_review** -- the code is ready to be peer-reviewed. If the code is not - yours, then you can review it. See :ref:`chapter-review`. - -- **needs_work** -- something needs to be changed in the code. The reason should - appear in the comments. - -- **needs_info** -- somebody has to answer a question before anything else can - happen. It should be clear from the comments. - -- **positive_review** -- the ticket has been reviewed, and the release manager - will close it. - -The status of a ticket can be changed using a form at the bottom of the ticket's -page. Leave a comment explaining your reasons whenever you change it. - -.. _section-trac-stopgaps: - -Stopgaps -======== - -When Sage returns wrong results, two tickets should be opened: - -- A main ticket with all available details. -- A "stopgap" ticket (e.g. :trac:`12699`) - -This second ticket does not fix the problem but adds a warning that will be -printed whenever anyone uses the relevant code. This, until the problem is -finally fixed. - -To produce the warning message, use code like the following: - -.. CODE-BLOCK:: python - - from sage.misc.stopgap import stopgap - stopgap("This code contains bugs and may be mathematically unreliable.", - TICKET_NUM) - -Replace ``TICKET_NUM`` by the ticket number for the main ticket. On the main -trac ticket, enter the ticket number for the stopgap ticket in the "Stopgaps" -field (see :ref:`section-trac-fields`). Stopgap tickets should be marked as -blockers. - -.. NOTE:: - - If mathematically valid code causes Sage to raise an error or - crash, for example, there is no need for a stopgap. Rather, - stopgaps are to warn users that they may be using buggy code; if - Sage crashes, this is not an issue. - - -Working on Tickets -================== - -If you manage to fix a bug or enhance Sage you are our hero. See -:ref:`chapter-walkthrough` for making changes to the Sage source -code, uploading them to the Sage trac server, and finally putting your -new branch on the trac ticket. - -.. image:: ticket_badges.png - -After pushing a branch to a ticket, the ticket will show badges -linking to results of automated tests that run on the patchbot and -other tests that run on GitHub Actions. - -* A `linting workflow - `_ - runs on all pushes to a branch on Trac. It checks that the code of - the current branch adheres to the style guidelines using - :ref:`section-tools-pycodestyle` (in the ``pycodestyle-minimal`` - configuration) and :ref:`section-tools-relint`. - - In order to see details when it fails, you can click on the badge - and then select the most recent workflow run. - -* The `incremental build and test workflow - `_ - on GitHub Actions builds Sage for the current branch (incrementally - on top of an installation of the ``develop`` branch) and runs the - test. Note that in contrast to the patchbot, the ticket branch is - not merged into the current beta version. - - Details are again available by clicking on the badge. - - The automatic workflow runs on a container based on - ``ubuntu-focal-standard``. To request a run of the workflow on a - different platform, you can issue a `workflow_dispatch - `_. - You can select any of the platforms for which a `prebuilt container - image - `_ - exists. - -* The `build documentation workflow - `_ - on GitHub Actions builds the HTML documentation for the current - branch. - - If you click on the badge, you get the HTML output of the successful - run. The idea is to use this to easily inspect changes to the - documentation without the need to locally rebuild the docs - yourself. If the doc build fails, you can go to `the Actions tab of - sagemath/sagetrac-mirror repo - `_ - and choose the particular branch to see what went wrong. - -* The patch buildbot will automatically test your ticket. See :trac:`wiki/patchbot` - for more information about its features and limitations. Make sure that you - look at the log, especially if the patch buildbot did not give you - the green blob. - -.. WARNING:: - - **Sage development moved to GitHub in February 2023.** - After the move, the patch buildbot no longer operates. - -The following are some other relevant issues: - -* Every bug fixed should result in a doctest. - -* This is not an issue with defects, but there are many enhancements - possible for Sage and too few developers to implement all the good - ideas. The trac server is useful for keeping ideas in a central - place because in the Google groups they tend to get lost once they - drop off the first page. - -* If you are a developer, be nice and try to solve a stale/old ticket - every once in a while. - -* Some people regularly do triage. In this context, this means that we - look at new bugs and classify them according to our perceived - priority. It is very likely that different people will see - priorities of bugs very differently from us, so please let us know - if you see a problem with specific tickets. - -Reviewing and Closing Tickets -============================= - -Tickets can be closed when they have positive review or for other reasons. To -learn how to review, please see :ref:`chapter-review`. - -Only the Sage release manager will close tickets. Most likely, this is -not you nor will your trac account have the necessary permissions. If -you feel strongly that a ticket should be closed or deleted, then -change the status of the ticket to *needs review* and change the -milestone to *sage-duplicate/invalid/wontfix*. You should also -comment on the ticket, explaining why it should be closed. If another -developer agrees, he sets the ticket to *positive review*. - -A related issue is re-opening tickets. You should refrain from -re-opening a ticket that is already closed. Instead, open a new ticket -and provide a link in the description to the old ticket. - -Reasons to Invalidate Tickets -============================= - -**One Issue Per Ticket**: A ticket must cover only one issue -and should not be a laundry list of unrelated issues. If a ticket -covers more than one issue, we cannot close it and while some of -the patches have been applied to a given release, the ticket would -remain in limbo. - -**No Patch Bombs**: Code that goes into Sage is peer-reviewed. If -you show up with an 80,000 lines of code bundle that completely -rips out a subsystem and replaces it with something else, you can -imagine that the review process will be a little tedious. These -huge patch bombs are problematic for several reasons and we prefer -small, gradual changes that are easy to review and apply. This is -not always possible (e.g. coercion rewrite), but it is still highly -recommended that you avoid this style of development unless there -is no way around it. - -**Sage Specific**: Sage's philosophy is that we ship everything -(or close to it) in one source tarball to make debugging possible. -You can imagine the combinatorial explosion we would have to deal -with if you replaced only ten components of Sage with external -packages. Once you start replacing some of the more essential -components of Sage that are commonly packaged (e.g. Pari, GAP, -lisp, gmp), it is no longer a problem that belongs in our tracker. -If your distribution's Pari package is buggy for example, file a -bug report with them. We are usually willing and able to solve -the problem, but there are no guarantees that we will help you -out. Looking at the open number of tickets that are Sage specific, -you hopefully will understand why. - -**No Support Discussions**: The trac installation is not meant to -be a system to track down problems when using Sage. Tickets should -be clearly a bug and not "I tried to do X and I couldn't get it to -work. How do I do this?" That is usually not a bug in Sage and it -is likely that ``sage-support`` can answer that question for you. If -it turns out that you did hit a bug, somebody will open a concise -and to-the-point ticket. - -**Solution Must Be Achievable**: Tickets must be achievable. Many -times, tickets that fall into this category usually ran afoul to -some of the other rules listed above. An example would be to -"Make Sage the best CAS in the world". There is no metric to -measure this properly and it is highly subjective. - -The Release Process -=================== - -The Sage Release Manager uses the following procedure to make releases, as of -2022. - -**Beta Release Stage**: For preparing a new beta release or the first release -candidate, all positively reviewed tickets with the forthcoming release -milestone are considered. Tickets that have unmerged dependencies are ignored. -The Release Manager merges tickets in batches of 10 to 20 tickets, taking the -ticket priority into account. If a merge conflict of a ticket to the Release -Manager's branch occurs, the ticket is set back to "needs work" status by the -Release Manager, and the list of the tickets already merged to the Release -Manager's branch is posted. The author of the ticket needs to identify -conflicting tickets in the list, make merge commits and declare them as -dependencies, before setting back to "positive review" status. Each batch of -merged tickets then undergoes integration testing. If problems are detected, a -ticket will be set back to "needs work" status and unmerged. When a batch of -tickets is ready, the Release Manager closes these tickets and proceeds to the -next batch. After a few batches, a new beta release is tagged, pushed to the -``develop`` branch on the main git repository, and announced on -``sage-release``. - -**Release Candidate Stage**: After the first release candidate has been made, -the project is in the release candidate stage, and a modified procedure is -used. Now **only tickets with a priority set to "blocker" are considered**. -Tickets with all other priorities, including "critical", are ignored. Hence if -a ticket is important enough to merit inclusion in this stage, it should be set -to "blocker". - -**Blocker Tickets**: The goal of the release process is to make a stable -release of high quality. Be aware that there is a risk/benefit trade-off in -merging a ticket. The benefit of merging a ticket is the improvement that the -ticket brings, such as fixing a bug. However, any code change has a risk of -introducing unforeseen new problems and thus delaying the release: If a new -issue triggers another release candidate, it delays the release by 1-2 weeks. -Hence developers should use "blocker" priority sparingly and should indicate -the rationale on the ticket. Though there is no one fixed rule or authority -that determines what is appropriate for "blocker" status, - -- Tickets introducing new features are usually not blockers -- unless perhaps - they round out a set of features that were the focus of development of this - release cycle. - -- Tickets that make big changes to the code, for example refactoring tickets, - are usually not blockers. - -**Final Release**: If there is no blocker ticket for the last release -candidate, the Release Manager turns it to the final release. It is tagged with -the release milestone, and announced on ``sage-release``. diff --git a/src/doc/en/developer/walk_through.rst b/src/doc/en/developer/walkthrough.rst similarity index 58% rename from src/doc/en/developer/walk_through.rst rename to src/doc/en/developer/walkthrough.rst index eb1620aea7c..185c91173e0 100644 --- a/src/doc/en/developer/walk_through.rst +++ b/src/doc/en/developer/walkthrough.rst @@ -3,54 +3,40 @@ .. _chapter-walkthrough: ======================== -Sage Development Process +Development Walk-through ======================== -.. WARNING:: +This section is a concise overview of the Sage development process. We will see +how to make changes to the Sage source code and record them in the Git revision +control system. - **Sage development moved to GitHub in February 2023.** After the transition, - some parts of this guide (especially those related with `the Sage Trac - server `_) became obsolete and need to be - updated according to the new workflow on GitHub. See our `transition guide - from Trac to GitHub - `_ - for the preliminary version of the workflow. - -This section is a concise overview of the Sage development process. In -it, we will see how to make changes to the Sage source code and record -them in the ``git`` revision control system. - -We also have a handy `one-page "cheat sheet" +We also have a handy `one-page cheat sheet `_ of commonly used git commands that you can print out and leave on your desk. We have some :ref:`recommended references and tutorials ` as well. -In the following sections on :ref:`chapter-sage-trac` and -:ref:`section-git-tricks-and-tips` we will look at communicating these -changes back to the Sage project. All changes to Sage source code -have to go through the `Sage Trac development server -`_. - -As an alternative to using the Trac server directly, you can fork and -create a Merge Request (MR) at `GitLab `_ -which will automatically fetch your code and open a ticket on our trac -server. +In the sections of the following chapter :ref:`section-development-on-github`, +we will look at communicating these changes back to the Sage project. All +changes to Sage source code have to go through `the Sage repository +`_ on GitHub. -Pull Requests (PR) on GitHub are currently not supported by the -SageMath project. +Git is a tool to exchange commits (organized into branches) with other +developers. As a distributed revision control system, it does not have the +notion of a central server. `The Sage repository +`_ on GitHub is just one of many possible +remote repositories from your (or rather your Git) point of view. +For examples, we assume your name Alice. Always replace it with your own name. .. _section-walkthrough-setup-git: -Configuring Git -=============== +Checking Git +============ -One way or another, ``git`` is what Sage uses for tracking changes. -So first, open a shell (for instance, Terminal on Mac) and check that -``git`` works:: +First, open a shell (for instance, Terminal on Mac) and check that Git works:: - [user@localhost]$ git + [alice@localhost ~]$ git usage: git [--version] [--help] [-C ] [-c name=value] ... The most commonly used git commands are: @@ -62,35 +48,35 @@ So first, open a shell (for instance, Terminal on Mac) and check that concept guides. See 'git help ' or 'git help ' to read about a specific subcommand or concept. - -Don't worry about the giant list of subcommands. You really only need -a handful for effective development, and we will walk you through them -in this guide. If you got a "command not found" error, then you don't -have git installed. Now is the time to install it; see +Don't worry about the giant list of subcommands. You really only need a handful +of them for effective development, and we will walk you through them in this +guide. If you got a "command not found" error, then you don't have Git +installed; now is the time to install it. See :ref:`chapter-git-setup` for instructions. -Because we also track who does changes in Sage with git, you must tell -git how you want to be known. This only needs to be done once:: +Because we also track who does what changes with Git, you must tell +Git how you want to be known. Check it:: - [user@localhost]$ git config --global user.name "Your Name" - [user@localhost]$ git config --global user.email you@yourdomain.example.com - -If you have multiple accounts / computers use the same name on each of -them. This name/email combination ends up in commits, so do it now -before you forget! + [alice@localhost ~]$ git config --global user.name + Alice Adventure + [alice@localhost ~]$ git config --global user.email + alice@wonderland.com +If you have multiple computers, then use the same name on each of them. This +name/email combination ends up in commits, so if not done yet, do it now before +you forget! This only needs to be done once. See :ref:`section-git-setup-name` +for instructions. .. _section-walkthrough-sage-source: -Obtaining and Compiling the Sage Source Code -============================================ +Obtaining the Sage Source Code +============================== -Obviously one needs the Sage source code to develop. You can use your +Obviously one needs the Sage source code to develop. You can use your local installation of Sage, or (to start from scratch) download it -from GitHub which is a public read-only mirror (=faster) of our -internal git repository:: +from our GitHub Sage repository:: - [user@localhost ~]$ git clone /~https://github.com/sagemath/sage.git + [alice@localhost ~]$ git clone /~https://github.com/sagemath/sage.git Cloning into 'sage'... [...] Checking connectivity... done. @@ -99,15 +85,15 @@ This creates a directory named ``sage`` containing the sources for the current stable and development releases of Sage. You next need to switch to the develop branch (latest development release):: - [user@localhost ~]$ cd sage - [user@localhost sage]$ git checkout develop + [alice@localhost ~]$ cd sage + [alice@localhost sage]$ git checkout develop -Next, compile Sage, following the instruction in the file -`README.md `_ in ``SAGE_ROOT``. -Additional details can be found in the -`section on installation from source <../installation/source.html>`_ -in the Sage installation guide. -If you wish to use conda-forge, see the `section on conda +Next, build Sage, following the instruction in the file `README.md +`_ in ``SAGE_ROOT``. If all +prerequisites to build are in place, the commands ``./configure && make -j4`` +will do it. Additional details can be found in the section on `installation +from source <../installation/source.html>`_ in the Sage installation guide. If +you wish to use conda-forge, see the section on `conda <../installation/conda.html>`_. .. NOTE:: @@ -117,28 +103,20 @@ If you wish to use conda-forge, see the `section on conda capitalization when changing into :envvar:`SAGE_ROOT` can lead to build errors for dependencies requiring exact capitalization in path names. -For the experts, note that the repository at -`git.sagemath.org `_ is where development -actually takes place. - -.. WARNING:: - - **Sage development moved to GitHub in February 2023.** - Now /~https://github.com/sagemath/sage.git is the primary repository. .. _section-walkthrough-branch: Branching Out ============= -In order to start modifying Sage, we want to make a *branch* of Sage. -A branch is a copy (except that it doesn't take up twice the space) of -the Sage source code where you can store your modifications to the -Sage source code and which you can upload to trac tickets. +In order to start modifying Sage, we want to make a new *branch* in the local +Sage repo. A branch is a copy (except that it doesn't take up twice the space) +of the Sage source code where you can store your modifications to the Sage +source code (and which you can push to your fork of the Sage repository on GitHub). To begin with, type the command ``git branch``. You will see the following:: - [user@localhost sage]$ git branch + [alice@localhost sage]$ git branch * develop master @@ -146,34 +124,34 @@ The asterisk shows you which branch you are on. Without an argument, the ``git branch`` command displays a list of all local branches with the current one marked by an asterisk. -It is easy to create a new branch; first make sure you are on the branch from +It is easy to create a new branch. First make sure you are on the branch from which you want to branch out. That is, if you are not currently on the ``develop`` branch, type the command ``git checkout develop``:: - [user@localhost sage]$ git checkout develop + [alice@localhost sage]$ git checkout develop Switched to branch 'develop' Your branch is up-to-date with 'origin/develop'. Then use the ``git branch`` command to create a new branch, as follows:: - [user@localhost sage]$ git branch last_twin_prime + [alice@localhost sage]$ git branch last_twin_prime Also note that ``git branch`` creates a new branch, but does not switch to it. For this, you have to use ``git checkout``:: - [user@localhost sage]$ git checkout last_twin_prime + [alice@localhost sage]$ git checkout last_twin_prime Switched to branch 'last_twin_prime' Now if you use the command ``git branch``, you will see the following:: - [user@localhost sage]$ git branch + [alice@localhost sage]$ git branch develop * last_twin_prime master -Note that unless you explicitly upload ("push") a branch to a remote -git repository, the branch is a local branch that is only on your computer -and not visible to anyone else. +Note that unless you explicitly push a branch to a remote Git repository, the +branch is a local branch that is only on your computer and not visible to +anyone else. To avoid typing the new branch name twice you can use the shortcut ``git checkout -b my_new_branch`` to create and switch to the new @@ -184,15 +162,15 @@ branch in one command. The History =========== -It is always a good idea to check that you are making your edits on -the version that you think you are on. The first one shows you the -topmost commit in detail, including its changes to the sources:: +It is always a good idea to check that you are making your edits on the branch +that you think you are on. The following command shows you the topmost commit +in detail, including its changes to files:: - [user@localhost sage]$ git show + [alice@localhost sage]$ git show To dig deeper, you can inspect the log:: - [user@localhost sage]$ git log + [alice@localhost sage]$ git log By default, this lists all commits in reverse chronological order. @@ -207,16 +185,15 @@ By default, this lists all commits in reverse chronological order. Editing the Source Code ======================= -Once you have your own branch, feel free to make any changes as you -like. :ref:`Subsequent chapters ` of -this developer guide explain how your code should look like to fit -into Sage, and how we ensure high code quality throughout. +Once you have your own branch, feel free to make any changes as you like. The +chapter :ref:`section-writing-code-for-sage` explains how your code should look +like to fit into Sage, and how we ensure high code quality throughout. -*Status* is probably the most important git command. It tells +The Git command ``git status`` is probably the most important one of all. It tells you which files changed, and how to continue with recording the changes:: - [user@localhost sage]$ git status + [alice@localhost sage]$ git status On branch last_twin_prime Changes not staged for commit: (use "git add ..." to update what will be committed) @@ -234,40 +211,38 @@ changes:: To dig deeper into what was changed in the files you can use:: - [user@localhost sage]$ git diff some_file.py + [alice@localhost sage]$ git diff some_file.py to show you the differences. - .. _section-walkthrough-make: Rebuilding Sage =============== -Once you have made any changes you of course want to build Sage and -try out your edits. As long as you only modified the Sage library -(that is, Python and Cython files under ``src/sage/...``) you just -have to run:: +Once you have made any changes, you of course want to build Sage and try out +your edits. As long as you only modified the Sage library (that is, Python and +Cython files under ``src/sage/...``) you just have to run:: [user@localhost sage]$ ./sage -br -to rebuild the Sage library and then start Sage. This should be quite -fast. If you made changes to +to rebuild the Sage library and then start Sage (this is not exactly true, +since if you only modified Python files, there is no need to rebuild). This +should be quite fast. If you made changes to :ref:`third-party packages `, then you have to run :: [user@localhost sage]$ make build as if you were `installing Sage from scratch -`_. -However, this time only packages which were changed (or which depend -on a changed package) will be recompiled, -so it should be much faster than compiling Sage -the first time. +`_. However, this +time only packages which were changed (or which depend on a changed package) +will be recompiled, so it should be much faster than compiling Sage the first +time. .. NOTE:: - If you have `pulled a branch from trac + If you have `pulled a branch from the GitHub Sage repository `_, it may depend on changes to third-party packages, so ``./sage -br`` may fail. If this happens (and you believe the code in this branch @@ -299,16 +274,16 @@ and build the documentation (see :ref:`chapter-sage_manuals`). .. _section-walkthrough-commit: -Commits (Snapshots) -=================== +Making commits +============== Whenever you have reached your goal, a milestone towards it, or just feel like you got some work done you should *commit* your changes. A commit is just a snapshot of the state of all files in -the *repository* (the program you are working on). +the *repository*. -Unlike with some other revision control programs, in git you first -need to *stage* the changed files, which tells git which files you +Unlike with some other revision control programs, in Git you first +need to *stage* the changed files, which tells Git which files you want to be part of the next commit:: [user@localhost sage]$ git status diff --git a/src/doc/en/developer/workflows.rst b/src/doc/en/developer/workflows.rst index e37c2fca267..23cf92dac8b 100644 --- a/src/doc/en/developer/workflows.rst +++ b/src/doc/en/developer/workflows.rst @@ -2,200 +2,176 @@ .. _chapter-workflows: -======================= -Distributed Development -======================= +===================== +Using Git with GItHub +===================== -.. WARNING:: +We continue our introduction to Sage development from :ref:`chapter-walkthrough`. +We discuss how to push your local changes to your fork of the GitHub Sage repository +so that your changes can be reviewed for inclusion in Sage. - **Sage development moved to GitHub in February 2023.** After the transition, - some parts of this guide (especially those related with `the Sage Trac - server `_) became obsolete and need to be - updated according to the new workflow on GitHub. See our `transition guide - from Trac to GitHub - `_ - for the preliminary version of the workflow. +Before proceeding, check that you have ``origin`` and ``upstream`` remotes right:: -Git is a tool to exchange commits (organized into branches) with other -developers. As a distributed revision control system, it does not have -the notion of a central server. The Sage trac server is just one of -many possible remote repositories from your point of view. This lets -you use and experiment with different ways to interact with other -developers. In this chapter, we describe some common ways to develop -for Sage. + [alice@localhost sage]$ git remote -v + origin /~https://github.com/alice/sage.git (fetch) + origin /~https://github.com/alice/sage.git (push) + upstream /~https://github.com/sagemath/sage.git (fetch) + upstream /~https://github.com/sagemath/sage.git (push) -For simplicity, let us assume two developers (Alice and Bob) are -collaborating on a ticket. The first step of opening the ticket is -always the same, and could be performed by either Alice or Bob or a -third person. +Development Workflow at a Glance +================================ - - - -Simple Workflow -=============== - -.. IMAGE:: static/flowchart.* +.. IMAGE:: static/workflow.* :align: center - 1. Alice creates a :ref:`new local branch ` and - :ref:`commits ` changes to the Sage sources. - -2. Alice :ref:`uploads her branch ` to the trac - server. This fills in the "Branch:" field with her remote branch - name ``u/alice/description``. + :ref:`commits ` changes to the Sage source files. -3. Bob :ref:`downloads Alice's branch `, looks - through the source, and leaves a comment on the ticket about a - mistake in Alice's code. +2. Alice pushes the local branch to the remote ``origin`` her fork of the Sage + repo on GitHub, and with it :ref:`creates a PR ` to + the Sage repo. When ready, Alice set the PR to ``needs review`` status. -4. Alice fixes the bug on top of her current branch, and uploads the - updated branch. +3. Bob, a developer acting as reviewer, :ref:`examines the PR + `, looks through the changes, leaves comments + on the PR, and requests fixes. -5. Bob :ref:`retrieves Alice's updates ` and reviews - the changes. +4. Alice makes more commits on top of her local branch, and pushes the new + commits to the remote ``origin``. These new commits are reflected in the PR. -6. Once Bob is satisfied, he sets the ticket to positive review. The - "Author:" field is set to Alice's full name, and the "Reviewer:" - field is set to Bob's full name. +5. Bob looks through the changes in the new commits and reviews the changes. -Alternatively, Bob might want to make some changes himself. Then, -instead, we would have +6. After a few of iterations of commenting and fixing, finally the reviewer Bob + is satisfied, and then he approves the PR and sets it to ``positive review`` + status. -3. Bob :ref:`downloads Alice's branch `, makes - changes, and :ref:`commits ` them to his local - branch. -4. Bob :ref:`uploads his branch ` to the trac - server. This fills in the "Branch:" field with his remote branch name - ``u/bob/description``. - -5. Alice :ref:`downloads Bob's branch ` and - reviews his changes. - -6. Once Alice is satisfied, she sets the ticket to positive review. If - both contributions are of comparable size, then the "Author:" and - "Reviewer:" fields are set to both Alice's and Bob's full name. +.. _section-workflows-pr-create: +Creating a New PR +================= +Suppose you have written an algorithm for calculating the last twin prime, +committed the code to a local branch based upon ``develop`` branch. Now you +want to add it to Sage. You would first open a PR for that:: + [alice@localhost sage]$ gh pr create + ? Where should we push the 'last-twin-prime' branch? user/sage -Public Repository -================= + Creating pull request for user:last-twin-prime into develop in sagemath/sage -In addition to the user branches (``u//`` on the -Sage trac server with ```` replaced by your trac user name) that -only you can write to, you can also create a public branch that -everybody with a trac account can write to. These start with -``public/`` plus some description. To avoid branch name collisions it -is a good idea to include your trac user name in the branch name, so -it is recommended that you use ``public//`` as the -branch name. Now all ticket authors push to the same remote branch. + ? Title Last twin prime + ? Choose a template PULL_REQUEST_TEMPLATE.md + ? Body + ? What's next? Submit as draft + /~https://github.com/sagemath/sage/pull/12345 -1. Alice creates a :ref:`new local branch ` and - :ref:`commits ` some changes to the Sage library. +This will create a new PR titled "Last twin prime" in the Sage repo for the +branch pushed to your fork ``alice/sage`` from the local branch on your +desktop. The title is automatically derived from the last commit title. If you +don't like this, then you can use the ``-t`` switch to specify it explicitly. +See the manual page of `gh pr create +`_ for details. -2. Alice :ref:`uploads her branch ` as a public - branch to the trac server. This fills in the "Branch:" field with - her remote branch name ``public/alice/description``. +If you did not provide enough details about the PR at the prompts, you may want +to edit the PR further via the web interface. -3. Bob :ref:`downloads Alice's branch ` and - makes changes to his local copy. -4. Bob :ref:`commits ` changes to his local branch - of the Sage sources. +.. _section-workflows-pr-checkout: -5. Bob uploads his changes to the joint remote repository:: +Checking out an Existing PR +=========================== - [bob@localhost sage]$ git push trac local_branch:public/alice/description +If you want to base your work on an existing PR or want to review the code of a PR, +then you would run:: -6. Alice :ref:`retrieves Bob's updates `, makes - more changes, commits, and pushes them to trac. + [alice@localhost sage]$ gh pr checkout 12345 + gh pr checkout 12345 + remote: Enumerating objects: 7, done. + remote: Counting objects: 100% (7/7), done. + remote: Compressing objects: 100% (7/7), done. + remote: Total 7 (delta 0), reused 0 (delta 0), pack-reused 0 + Unpacking objects: 100% (7/7), 25.50 KiB | 2.83 MiB/s, done. + From /~https://github.com/sagemath/sage + * [new ref] refs/pull/12345/head -> last-twin-prime + Switched to branch 'last-twin-prime' -7. Charly reviews the final version, and then sets the ticket to - positive review. The "Author:" field is set to Alice's and Bob's - full name, and the "Reviewer:" field is set to Charly's full name. +The ``gh pr checkout`` command downloads the branch attached to the PR. Just +like the ``create`` command, you can specify the local branch name explicitly using +the ``-b`` switch if you want. +.. _section-workflows-push: +Uploading More Changes to GitHub +================================ -GitHub -====== +Once you have created a PR, edit the appropriate files and +commit your changes to your local branch as described in +:ref:`section-walkthrough-add-edit` and +:ref:`section-walkthrough-commit`. -Yet another possible workflow is to use GitHub (or any other -third-party git repository) to collaboratively edit your new branch, -and only push the result to trac once you and your ticket co-authors -are satisfied. +If you are ready to share the changes up to now, upload +your new commits to your fork by:: + [alice@localhost sage]$ git push origin + Enumerating objects: 13, done. + Counting objects: 100% (13/13), done. + Delta compression using up to 12 threads + Compressing objects: 100% (7/7), done. + Writing objects: 100% (7/7), 1.98 KiB | 1.98 MiB/s, done. + Total 7 (delta 6), reused 0 (delta 0), pack-reused 0 + remote: Resolving deltas: 100% (6/6), completed with 6 local objects. + To /~https://github.com/user/sage.git + + 352d842907...56ffdab967 last-twin-prime -> last-twin-prime -Fork ----- +Note that you do not push the branch to the remote ``upstream`` the Sage repo. +Instead the new commits pushed to the remote ``origin`` are shown in the PR at +the Sage repository. -The first step is to create your own fork of the Sage repository; -simply click "Fork" on the `Sage GitHub repository -`_. Then add it as one of the -remotes to your local Sage repository. In the following, we will use -the label "github" for this remote repository, though you are of -course free to use a different one:: - $ git remote add github git@github.com:github_user_name/sage.git - $ git remote -v - github git@github.com:github_user_name/sage.git (fetch) - github git@github.com:github_user_name/sage.git (push) - trac git@trac.sagemath.org:sage.git (fetch) - trac git@trac.sagemath.org:sage.git (push) - $ git fetch github - remote: Counting objects: 107, done. - remote: Compressing objects: 100% (63/63), done. - remote: Total 74 (delta 41), reused 40 (delta 10) - Unpacking objects: 100% (74/74), done. - From github.com:github_user_name/sage - * [new branch] master -> github/master +.. _section-workflows-finish: +Finishing It Up +=============== -Develop -------- +It is common to go through a few iterations of commits before you +push the branch, and you will probably also have pushed your branch a few +times before your branch is ready for review. -You now use the github repository to develop your ticket branch; First -create a new branch:: +Once you are happy with the changes you pushed, they must be +reviewed by someone else before they can be included in the next +version of Sage. To mark your PR as ready for review, you should +set it to ``needs_review`` status. - $ git checkout -b my_branch --track github/master - Branch my_branch set up to track remote branch master from github. - Switched to a new branch 'my_branch' - $ git push github my_branch - Total 0 (delta 0), reused 0 (delta 0) - To git@github.com:github_user_name/sage.git - * [new branch] my_branch -> my_branch -Because of the ``--track`` option, the ``git pull`` command will -default to downloading your coauthor's changes from your github -branch. Alternatively, you can create a new branch on your fork's -GitHub webpage. +.. _section-workflows-merge: -At this point you can use the GitHub workflow that you prefer. In -particular, your choices are +Merging Upstream develop Branch +=============================== -* Give your coauthors write permissions to your github fork. Every - author edits/commits to their own local copy and they jointly push - to your github branch. +It commonly happens that ``develop`` branch at the remote ``upstream`` was updated and you need to merge the upstream changes to your local branch. Then you do:: -* Have every coauthor create their own fork and send you (the lead - author) pull requests to your GitHub fork. + [alice@localhost sage]$ git checkout develop + .... + [alice@localhost sage]$ git pull upstream + .... -* Use the GitHub web page editing & committing feature, that way you - can make changes without ever using your local machine. +This fast-forwards your local ``develop`` branch to the upstream +``develop`` branch. +Now you go back to your working branch and merge the updated ``develop`` branch:: -Push to Trac ------------- + [alice@localhost sage]$ git checkout last_twin_prime + .... + [alice@localhost sage]$ git merge develop + .... -When you are satisfied with your branch, you push it to the Sage trac -server:: +If there was no upstream changes conflicting with the changes you made locally, +this merge operation will finish cleanly. Otherwise, you are in *merge +conflict*. This rarely happens since Git is smart in merging changes. However, +once merge conflict happens, you have to manually resolve the conflicts. The +conflict resolving procedure is explained in :ref:`section-git-conflict`. - $ git push trac HEAD:u/user/description -and then fill in the "Branch" field in the trac ticket description as -explained in :ref:`section-git-push`. diff --git a/src/doc/en/developer/workspace.rst b/src/doc/en/developer/workspace.rst index d820721e11b..2247d099fa5 100644 --- a/src/doc/en/developer/workspace.rst +++ b/src/doc/en/developer/workspace.rst @@ -22,7 +22,7 @@ Gitpod `Gitpod `_ is a free service that will let you build and run Sage from an online development environment based on VS Code. -Without needing to install anything on your computer, Gitpod creates a virtual +Without needing to install anything on your computer, Gitpod creates a virtual fully-functional workspace with all the dependencies and tools pre-installed. To get started, `go to Gitpod `_ @@ -31,7 +31,7 @@ Wait while Gitpod creates a workspace. The first time, it may take some time to build Sage. You can now run and edit Sage's code. Contributing your changes follows the normal -:ref:`Git workflow `. +:ref:`Git workflow `. For this to work, you first have to authorize Gitpod with Trac: 1. In the running Gitpod workspace, generate a new SSH key pair by ``ssh-keygen -f tempkey``. @@ -44,5 +44,5 @@ For this to work, you first have to authorize Gitpod with Trac: After following this procedure, every new Gitpod workspace will have a working ``trac`` remote to which you can push your changes. -You can also `use your VS Code Desktop `_ to keep +You can also `use your VS Code Desktop `_ to keep your local IDE configuration while still benefiting from Gitpod’s high-spec servers and automated prebuilds. diff --git a/src/doc/en/website/root_index.html b/src/doc/en/website/root_index.html index 0fe6ab69968..3ae4d4cd272 100644 --- a/src/doc/en/website/root_index.html +++ b/src/doc/en/website/root_index.html @@ -47,7 +47,7 @@

Sage Documentation

FAQ
Reference Manual
Installation Guide
-
Developer's Guide
+
Developer Guide
French
@@ -120,7 +120,7 @@

Sage Documentation

FAQ
Reference Manual
Installation Guide
-
Developer's Guide
+
Developer Guide
French
diff --git a/src/doc/en/website/templates/index.html b/src/doc/en/website/templates/index.html index cdadfb78ffd..775f1eb3841 100644 --- a/src/doc/en/website/templates/index.html +++ b/src/doc/en/website/templates/index.html @@ -207,7 +207,7 @@