Development workflow#
Workflow summary#
To keep your work well organized, with readable history, and in turn make iteasier for project maintainers (that might be you) to see what you've done, andwhy you did it, we recommend the following:
Don't make changes in your local
main
branch!Before starting a new set of changes, fetch all changes from
upstream/main
, and start a newfeature branch from that.Make a new branch for each feature or bug fix — "one task, one branch".
Name your branch for the purpose of the changes - e.g.
bugfix-for-issue-14
orrefactor-database-code
.If you get stuck, reach out on Gitter ordiscourse.
When you're ready or need feedback on your code, open a pull request so that theMatplotlib developers can give feedback and eventually include your suggestedcode into the
main
branch.
Overview#
Aftersetting up a development environment, the typicalworkflow is:
Fetch all changes from
upstream/main
:gitfetchupstream
Start a newfeature branch from
upstream/main
:gitcheckout-bmy-featureupstream/main
When you're done editing, e.g.,
lib/matplotlib/collections.py
, record your changes in Git:gitaddlib/matplotlib/collections.pygitcommit-m'a commit message'
Push the changes to your GitHub fork:
gitpush-uoriginmy-feature
Update themain
branch#
First make sure you have followedSetting up Matplotlib for development.
From time to time you should fetch the upstream changes from GitHub:
gitfetchupstream
This will pull down any commits you don't have, and set the remote branches topoint to the right commit.
Make a new feature branch#
When you are ready to make some changes to the code, you should start a newbranch. Branches that are for a collection of related edits are often called'feature branches'. Making a new branch for each set of related changes will make iteasier for someone reviewing your branch to see what you are doing.
Choose an informative name for the branch to remind yourself and the rest of uswhat the changes in the branch are for. For exampleadd-ability-to-fly
, orbugfix-for-issue-42
.
The process for creating a new feature branch is:
# Update the main branchgitfetchupstream# Make new feature branch starting at current maingitbranchmy-new-featureupstream/maingitcheckoutmy-new-feature
If you started making changes on your localmain
branch, you can convert thebranch to a feature branch by renaming it:
gitbranch-m<newname>
Generally, you will want to keep your feature branches on your public GitHubfork of Matplotlib. To do this, yougitpush
this new branch up to yourGitHub repo. Generally, if you followed the instructions in these pages, and bydefault, git will have a link to your fork of the GitHub repo, calledorigin
. You push up to your own fork with:
gitpushoriginmy-new-feature
The editing workflow#
Make some changes
Save the changes
See which files have changed with
gitstatus
.You'll see a listing like this one:# On branch ny-new-feature# Changed but not updated:# (use "git add <file>..." to update what will be committed)# (use "git checkout -- <file>..." to discard changes in working directory)## modified: README## Untracked files:# (use "git add <file>..." to include in what will be committed)## INSTALLno changes added to commit (use "git add" and/or "git commit -a")
Check what the actual changes are with
gitdiff
.Add any new files to version control
gitaddnew_file_name
.To commitall modified files into the local copy of your repo, type:
gitcommit-am'A commit message'
Note the
-am
options tocommit
. Them
flag signals that you aregoing to type a message on the command line. Thea
flag stages everyfile that has been modified, except files listed in.gitignore
. For moreinformation, see thegit commit manual page.To push the changes up to your forked repo on GitHub, do a
gitpush
.
Verify your changes#
Check that your change does what you intend. For code changes:
If the issue you are working on provided a code example, run that exampleagainst your branch and check that you now get the desired result. Note thatadapting the issue example is often a good way to create a new test.
Run the tests to check that your change has not had unintended consequenceson existing functionality. SeeRunning the tests.
For documentation changes, build the documentation locally to check thatit renders how you intended and that any new links work correctly. SeeBuild the docs.
This is also a good time to look through theSummary for pull request authors andaddress as many of the relevant points as you can.
Open a pull request#
When you are ready to ask for someone to review your code and consider a merge,submit your Pull Request (PR).
Go to the web page ofyour fork of the Matplotlib repo, and clickCompare&pullrequest
to send your changes to the maintainers for review.The base repository ismatplotlib/matplotlib
and the base branch isgenerallymain
.
Enter a title for the set of changes with some explanation of what you've done.Mention anything you'd like particular attention for - such as acomplicated change or some code you are not happy with.
If you don't think your request is ready to be merged, just say so in your pullrequest message and use the "Draft PR" feature of GitHub. This is a good way ofgetting some preliminary code review.
For more guidance on the mechanics of making a pull request, see GitHub'spull request tutorial.
Update a pull request#
When updating your pull request after making revisions, instead of adding newcommits, please consider amending your initial commit(s) to keep the commithistory clean.
You can achieve this by using
gitcommit-a--amend--no-editgitpush[your-remote-repo][your-branch]--force-with-lease
Tip
Instead of typing your branch name every time, you only need to type the following once to link the remote branch to the local branch:
gitpush--set-upstreamoriginmy-new-feature
From now on git will know thatmy-new-feature
is related to themy-new-feature
branch in the GitHub repo. After this, you will be able topush your changes with:
gitpush
Manage commit history#
Explore your repository#
To see a graphical representation of the repository branches andcommits:
gitk--all
To see a linear list of commits for this branch:
gitlog
Recover from mistakes#
Sometimes, you mess up merges or rebases. Luckily, in git it isrelatively straightforward to recover from such mistakes.
If you mess up during a rebase:
gitrebase--abort
If you notice you messed up after the rebase:
# reset branch back to the saved pointgitreset--hardtmp
If you forgot to make a backup branch:
# look at the reflog of the branchgitreflogshowcool-feature8630830cool-feature@{0}:commit:BUG:io:closefilehandlesimmediately278dd2acool-feature@{1}:rebasefinished:refs/heads/my-feature-branchonto11ee694744f2552d26aa21acool-feature@{2}:commit:BUG:lib:makeseek_gzip_factorynotleakgzipobj...# reset the branch to where it was before the botched rebasegitreset--hardcool-feature@{2}
Rewrite commit history#
Note
Do this only for your own feature branches.
Is there an embarrassing typo in a commit you made? Or perhaps youmade several false starts you don't want posterity to see.
This can be done viainteractive rebasing.
Suppose that the commit history looks like this:
gitlog--onelineeadc391Fixsomeremainingbugsa815645Modifyitsothatitworks2dec1acFixafewbugs+disable13d7934Firstimplementation6ad92e5*maskedisnowaninstanceofanewobject,MaskedConstant29001edAddpre-nepforacopuleofstructured_array_extensions....
and6ad92e5
is the last commit in thecool-feature
branch. Suppose wewant to make the following changes:
Rewrite the commit message for
13d7934
to something more sensible.Combine the commits
2dec1ac
,a815645
,eadc391
into a single one.
We do as follows:
# make a backup of the current stategitbranchtmpHEAD# interactive rebasegitrebase-i6ad92e5
This will open an editor with the following text in it:
pick13d7934Firstimplementationpick2dec1acFixafewbugs+disablepicka815645Modifyitsothatitworkspickeadc391Fixsomeremainingbugs# Rebase 6ad92e5..eadc391 onto 6ad92e5## Commands:# p, pick = use commit# r, reword = use commit, but edit the commit message# e, edit = use commit, but stop for amending# s, squash = use commit, but meld into previous commit# f, fixup = like "squash", but discard this commit's log message## If you remove a line here THAT COMMIT WILL BE LOST.# However, if you remove everything, the rebase will be aborted.#
To achieve what we want, we will make the following changes to it:
r13d7934Firstimplementationpick2dec1acFixafewbugs+disablefa815645Modifyitsothatitworksfeadc391Fixsomeremainingbugs
This means that (i) we want to edit the commit message for13d7934
, and (ii) collapse the last three commits into one. Now wesave and quit the editor.
Git will then immediately bring up an editor for editing the commitmessage. After revising it, we get the output:
[detachedHEAD721fc64]FOO:Firstimplementation2fileschanged,199insertions(+),66deletions(-)[detachedHEAD0f22701]Fixafewbugs+disable1fileschanged,79insertions(+),61deletions(-)Successfullyrebasedandupdatedrefs/heads/my-feature-branch.
and now, the history looks like this:
0f22701Fixafewbugs+disable721fc64ENH:Sophisticatedfeature6ad92e5*maskedisnowaninstanceofanewobject,MaskedConstant
If it went wrong, recovery is again possible as explainedabove.
If you have not yet pushed this branch to github, you can carry on as normal,however if youhave already pushed this commit seePush with force for howto replace your already published commits with the new ones.
Rebase ontoupstream/main
#
Let's say you thought of some work you'd like to do. YouUpdate the main branch andMake a new feature branch calledcool-feature
. At this stage,main
is at some commit, let's call it E.Now you make some new commits on yourcool-feature
branch, let's call themA, B, C. Maybe your changes take a while, or you come back to them after awhile. In the meantime,main
has progressed from commit E to commit (say) G:
A---B---C cool-feature /D---E---F---G main
At this stage you consider mergingmain
into your feature branch, and youremember that this page sternly advises you not to do that, because thehistory will get messy. Most of the time, you can just ask for a review withoutworrying about whethermain
has got a little ahead; however sometimes, the changes inmain
might affect your changes, and you need to harmonize them. In thissituation you may prefer to do a rebase.
rebase
takes your changes (A, B, C) and replays them as if they had beenmade to the current state ofmain
. In other words, in this case, it takesthe changes represented by A, B, C and replays them on top of G. After therebase, your history will look like this:
A'--B'--C' cool-feature /D---E---F---G main
Seerebase without tears for more detail.
To do a rebase onupstream/main
:
# Fetch changes from upstream/maingitfetchupstream# go to the feature branchgitcheckoutcool-feature# make a backup in case you mess upgitbranchtmpcool-feature# rebase cool-feature onto maingitrebase--ontoupstream/mainupstream/maincool-feature
In this situation, where you are already on branchcool-feature
, the lastcommand can be written more succinctly as:
gitrebaseupstream/main
When all looks good, you can delete your backup branch:
gitbranch-Dtmp
If it doesn't look good you may need to have a look atRecover from mistakes.
If you have made changes to files that have also changed inmain
, this maygenerate merge conflicts that you need to resolve - see thegit rebase manpage for some instructions at the end of the "Description" section. There issome related help on merging in the git user manual - seeresolving a merge.
If you have not yet pushed this branch to github, you can carry on as normal,however if youhave already pushed this commit seePush with force for howto replace your already published commits with the new ones.
Push with force#
If you have in some way re-written already pushed history (e.g. viaRewrite commit history orRebase onto upstream/main) leaving you witha git history that looks something like
A'--E cool-feature /D---A---B---C origin/cool-feature
where you have pushed the commitsA,B,C
to your fork on GitHub (under theremote nameorigin) but now have the commitsA'
andE
on your localbranchcool-feature. If you try to push the new commits to GitHub, it willfail and show an error that looks like
$gitpushPushingtogithub.com:origin/matplotlib.gitTogithub.com:origin/matplotlib.git![rejected]cool_feature->cool_feature(non-fast-forward)error:failedtopushsomerefsto'github.com:origin/matplotlib.git'hint:Updateswererejectedbecausethetipofyourcurrentbranchisbehindhint:itsremotecounterpart.Integratetheremotechanges(e.g.hint:'git pull ...')beforepushingagain.hint:Seethe'Note about fast-forwards'in'git push --help'fordetails.
If this push had succeeded, the commitsA
,B
, andC
would nolonger be referenced by any branch and they would be discarded:
D---A'---E cool-feature, origin/cool-feature
By defaultgitpush
helpfully tries to protect you from accidentallydiscarding commits by rejecting the push to the remote. When this happens,GitHub also adds the helpful suggestion to pull the remote changes and then trypushing again. In some cases, such as if you and a colleague are bothcommitting and pushing to the same branch, this is a correct course of action.
However, in the case of having intentionally re-written history, wewant todiscard the commits on the remote and replace them with the new-and-improvedversions from our local branch. In this case, what we want to do is
$gitpush--force-with-lease
which tells git you are aware of the risks and want to do the push anyway. Werecommend using--force-with-lease
over the--force
flag. The--force
will do the push no matter what, whereas--force-with-lease
will only do the push if the remote branch is where the localgit
clientthought it was.
Be judicious with force-pushing. It is effectively re-writing publishedhistory, and if anyone has fetched the old commits, it will have a different viewof history which can cause confusion.
Automated tests#
Whenever a pull request is created or updated, various automated test toolswill run on all supported platforms and versions of Python.
tox is not used in the automated testing. It is supported for testinglocally.
Codecov and CodeQL are currently for information only. Their failure is notnecessarily a blocker.
Make sure the Linting, GitHub Actions, AppVeyor, CircleCI, and Azure pipelines arepassing before merging. All checks are listed at the bottom of the GitHub page of yourpull request.
Name | Check | Tips for finding cause of failure |
---|---|---|
Linting | Errors are displayed as annotations on the pull request diff. | |
Mypy Stubtest | Errors are displayed as annotations on the pull request diff. | |
CircleCI | Search the CircleCI log for | |
GitHub Actions AppVeyor Azure pipelines | Search the log for FAILURES . Subsequent section should contain informationon failed tests.On Azure, find the images asartifacts of the Azure job: 1. ClickDetails on the check on the GitHub PR page. 2. ClickView more details on Azure Pipelines to go to Azure. 3. On the overview pageartifacts are listed in the sectionRelated. |
Skip CI checks#
If you know only a subset of CI checks need to be run, you can skip unneeded CI checkson individual commits by including the following strings in the commit message:
String | Effect | Notes |
---|---|---|
| Only run documentation checks. | For when you have only changed documentation. [cidoc] is applied automatically when the changes are only to files indoc/**/ orgalleries/**/ |
| Skip documentation checks. | For when you didn't change documentation. |
| Skip AppVeyor run. | Substring must be in first line of commit message. |
| Skip Azure Pipelines. | |
| Skip GitHub Actions. | |
| Skip all CI checks. | Use only for changes where documentation checks and unit tests do not apply. |
[skipactions]
and[skipci]
only skip Github Actions CI workflows that aretriggered onon:push
andon:pull_request
events. For more information,seeSkipping workflow runs.