[[PageOutline]]
= Collaboration on !GitHub =
This page gives guidelines for distributed Django development.
{{{
#!html
It is not part of the official Django development process.
}}}
== Prerequisites ==
=== 1. Install git ===
If you want to learn more about `git`, see [http://git-scm.com/ the git webpage] or [http://git.or.cz/gitwiki/GitCheatSheet the git cheat sheet] for a quick overview. [http://www.python.org/dev/peps/pep-0374/ PEP 0374] is also a good read.
==== OS X ====
[http://code.google.com/p/git-osx-installer/ Git OS X Installer] or ideally use [http://mxcl.github.com/homebrew/ Homebrew]
{{{
$ brew install git
}}}
==== Ubuntu ====
If using a recent Ubuntu relase (9.04, 9.10 or later), you can install `git` from the main repository:
{{{
$ apt-get install git-core
}}}
=== 2. Configure recommended defaults ===
(From http://cheat.errtheblog.com/s/git)
Tell `git branch` and `git checkout` to setup new branches so that `git pull` will [http://www.diamondlinks.net/ link building] appropriately merge from that remote branch. Without this, you will have to add `--track` to your branch command or manually merge remote tracking branches with `fetch` and then `merge`
{{{
git config branch.autosetupmerge true
}}}
or globally
{{{
git config --global branch.autosetupmerge true
}}}
Possibly set your username:
{{{
git config --global user.name 'John Doe'
git config --global user.email johndoe@example.com
}}}
=== 3. Register an account on !GitHub ===
Go to https://github.com/signup/free and follow the instructions.
== Branchy development: working on several tasks in parallel ==
You want to work on several fixes and features (tasks) and want to
* keep the changes for a particular task self-contained in a branch,
* frequently update the codebase with upstream changes,
* have an integration branch for testing out how the different task branches interact.
=== Initial setup ===
1. Create a fork of the automatically updated (unofficial) Django SVN trunk mirror on !GitHub by clicking ''fork'' at http://github.com/django/django/tree/master
1. Clone the fork to your workstation:
{{{
git clone git@github.com:YOUR_NICK_HERE/django.git
}}}
1. Add the upstream Django SVN mirror for tracking:
{{{
cd django
git remote add upstream git://github.com/django/django.git
git fetch upstream
}}}
=== Working on a task ===
'''Goal''': keep the changes self-contained, create and update patches suitable for [http://www.truckaccidentlawyersource.com/ truck accident lawyer] attaching to [http://code.djangoproject.com/simpleticket tickets in Django trac].
1. Create a branch from master
{{{
git checkout -b ticket1234 master
}}}
1. Alternatively, if the remote branch already exists, create a corresponding local branch:
{{{
git checkout -b ticket1234 origin/ticket1234
}}}
1. Change files, commit often
{{{
git commit -am "Changed foo."
}}}
1. [wiki:RunningDjangoTests Run tests]
1. Create the remote branch and push changes into it
{{{
git push origin ticket1234
}}}
1. Create a patch, attach it to [http://code.djangoproject.com/simpleticket a relevant ticket in trac]
{{{
git diff master > ticket1234.diff
}}}
Repeat for every ticket you want to work on.
=== After upstream has changed ===
'''Goal''': bring in the changes in upstream to a task.
1. When the upstream Django SVN mirror is updated, pull the updates from it (fetching and merging in one step)
{{{
git checkout master
git pull upstream master
}}}
1. Switch to the ticket branch
{{{
git checkout ticket1234
}}}
1. Merge
{{{
git merge master
}}}
1. If there were any conflicts, handle them by fixing the corresponding files, mark the files as fixed by re-adding them and commit
{{{
git add file/that/was/fixed.py
git commit
}}}
1. [wiki:RunningDjangoTests Run tests]
1. Push changes to !GitHub (the remote branch was already created before)
{{{
git push
}}}
1. If the patch needs updating, create a new patch and attach it to the relevant ticket
{{{
git diff master > ticket1234.diff
}}}
Repeat for every ticket you have worked on.
=== Testing everything in the integration branch ===
'''Goal''': a place to try out the code from several tasks together. `master` can not be used for this as it is used for tracking upstream. This is a temporary branch that can be removed or reset to the same state as `master` after testing is done.
1. Create the branch
{{{
git checkout -b integration master
}}}
1. Merge in all task branches, fix any conflicts that occur in the task branches (i.e. they should merge cleanly)
{{{
git merge ticket1234
git merge ticket1235
...
}}}
1. [wiki:RunningDjangoTests Run tests]
1. Reset the branch back to `master`'s state (to re-integrate all tickets again later):
{{{
git reset --hard master
}}}
=== Deleting a remote branch ===
Deleting a remote branch is somewhat unintuitive:
1. Delete the local branch
{{{
git checkout master
git branch -d ticket1234
}}}
1. Delete the corresponding remote one
{{{
git push origin :ticket1234
}}}
=== If something went wrong ===
If you haven't done anything out of the ordinary, but see
{{{
$ git push
...
! [rejected] master -> master (non-fast forward)
...
}}}
then most likely your branch (`master` in this case) is out of sync with the remote branch.
{{{
git pull
}}}
should alleviate the problem and `git push` should work again.
Generally, `non-fast forward` means that the local history differs from the [http://www.mycaal.com/ loan modification] remote history and pushing would change remote history.
Although rewriting history may wreak havoc if someone has forked your repository after the changed point, it is sometimes necessary.
==== On `master` ====
You can reset `master` to a one-to-one copy of `upstream` as follows:
{{{
git checkout master
git reset --hard upstream/master
}}}
If the reset changes history, you'll get the `non-fast forward` error when doing a `git push`. A forced push is required in this case:
{{{
git push --force
}}}
==== On a branch ====
Switch to the branch
{{{
git checkout ticket1234
}}}
You can manually remove or glue commits together with
{{{
git rebase -i master
}}}
To abort the rebase, just remove all non-commented lines.
If something went wrong during rebase and you want to undo the rebase:
1. Find out the state identifier before the rebase:
{{{
git reflog
}}}
1. Switch back to that state
{{{
git reset --hard HEAD@{NUMBER_FROM_REFLOG_HERE}
}}}
This works for all other similar problems as well, e.g. for backing out a merge.
As above, if you've changed history, you need to issue a forced push:
{{{
git push --force
}}}
== Collaboration on a large feature ==
The process should be managed with the '''Dictator and Lieutenants Workflow''', described at http://whygitisbetterthanx.com/#any-workflow.
* ''Dictators'' are core Django developers, who eventually merge the `git` branches back to Django trunk.
* ''Lieutenants'' are developers, who take the responsibility of steering and coordinating the work on a large feature (e.g. aggregates).
* ''Contributors'' are developers who contribute -- under lieutenant's guidance -- to the development of the feature.
''The workflow described below should usually use a separate branch to keep the work isolated, so the instructions need updating.''
=== Lieutenant ===
1. Create a fork of the automatically updated (unofficial) Django SVN trunk mirror on !GitHub by clicking ''fork'' at http://github.com/django/django/tree/master
1. Describe the design of the feature you implement on the !GitHub project wiki, list tasks that need tackling so that contributors can pick them easily.
1. Clone the fork to your workstation:
{{{
git clone git@github.com:YOUR_NICK_HERE/django.git
}}}
1. Add the upstream Django SVN mirror for tracking:
{{{
cd django
git remote add upstream git://github.com/django/django.git
git fetch upstream
}}}
1. Work on code, commit locally as needed:
{{{
git commit -a -m "Implemented foo."
}}}
1. Publish changes to your public repository:
{{{
git push
}}}
1. When the upstream Django SVN mirror is updated, pull the updates from it (fetching and merging in one step):
{{{
git pull upstream master
}}}
1. When the feature is ready, get in contact with a core developer (the ''dictator'') to integrate your work.
==== Managing contributor submissions ====
When a contributor feels that his updates are ready to be merged back to lieutenant's repository, he should submit a '''pull request''' to the lieutenant (see below).
When a lieutenant receives a pull request:
1. Review the commit list that is listed in the pull request
1. If everything looks good, pull code from the the contributor's repository (this creates a new branch, if the branch already exists, skip the `remote add` step):
{{{
$ git remote add -f CONTRIBUTOR_NICK_HERE git://github.com/CONTRIBUTOR_NICK_HERE/django.git
$ git checkout -b CONTRIBUTOR_NICK_HERE/master
$ git pull CONTRIBUTOR_NICK_HERE master # for current or master:XXXXXXX, where XXXXXX is the changeset code received in pull request
}}}
1. Merge changes with the master branch:
{{{
$ git checkout master
$ git merge CONTRIBUTOR_NICK_HERE/master
}}}
1. Run tests
1. If tests pass, push changes to your public repository:
{{{
$ git push
}}}
=== Contributor ===
Largely, this is a copy-paste of the lieutenant workflow, only the tracked repositories and the final step (pull request) differ.
1. Create a fork of the lieutenant's repository on !GitHub by clicking ''fork'' at e.g. `http://github.com/LIEUTENANT_NICK_HERE/django/tree/master`.
1. Pick a task from the lieutenant's !GitHub project wiki, update the wiki as needed.
1. Clone the fork to your workstation:
{{{
git clone git@github.com:YOUR_NICK_HERE/django.git
}}}
1. Add the upstream lieutenant's repository for tracking:
{{{
cd django
git remote add upstream git://github.com/LIEUTENANT_NICK_HERE/django.git
git fetch upstream
}}}
1. Work on code, commit locally as needed:
{{{
git commit -a -m "Implemented foo."
}}}
1. Publish changes to your public repository:
{{{
git push
}}}
1. When the upstream lieutenant's repository is updated, pull the updates from it:
{{{
git pull upstream master
}}}
1. When the feature is ready, send the lieutenant a pull request by clicking ''pull request'' on your forked project's page.
=== Quality assurance ===
To assure that the contribution is in par with Django code quality standards, the actual development workflow should be similar to the following:
==== Contributor ====
1. Implement tests for the features to be added
1. Commit locally with a descriptive commit message
1. Implement the features
1. Commit locally
1. Run the test suite iteratively, fixing whatever the tests indicate needs fixing
1. Commit locally if something changed
1. Push your changes to !GitHub
==== Lieutenant ====
Specify the API and design of the feature on !GitHub wiki, try to add documentation that specifies the behaviour '''before''' implementation.
Integrating contributor's work:
1. Review contributor's changes (knowing that the contributor has assured that tests pass)
1. Pull changes from contributor's repo to your repo
1. Run the test suite
1. Push changes to the public code hosting service
1. Update docs if needed