This document aims to define a process for contributing source code to GateIn Portal project repositories hosted on GitHub.
Main repository discussed here is https://github.com/gatein/gatein-portal which contains main code base on GateIn Portal. Most of rules discussed here will apply to other repositories hosted under the “gatein” organization on Github (https://github.com/gatein). However for some components workflow can be different and less restrictions apply.
This document assumes basic knowledge and understanding of Git. We recommend Scott Chacon's excellent Pro Git as a valuable piece of background reading. The book is released under the Creative Commons license and can be downloaded in electronic form for free At very least, we recommend that Chapter 2, Chapter 3 and Chapter 5 of Pro Git are read before proceeding.
Yes, authors of this doc were highly influenced by awesome documentation from Infinispan project we have borrowed some of their hints. Thanks guys, you rock!
General guidelines
All work on the project should happen in personal forks of the repository.
We are trying to keep history in straight line, so you should try to always rebase your commits on top of upstream before pushing your work to public repository. Generally we are trying to avoid bubbles in history (so git graph should ideally always looks like straight line). Good practice is also squashing your commits and use JIRA numbers in commit message (So ideal commit message can look like "GTNPORTAL-1234 Development of feature XY").
Use cases
Working on a new feature or fixing bug
There are two ways to share your work.
Just attaching patch to JIRA
- Clone the gatein repository
- Work on a feature or bug fix
- Generate a patch
- Attach the patch file to a JIRA issue
More information on generating patches suitable for attaching to a JIRA issue can be found in Chapter 5, Section 2 of Pro Git, under the section titled Public Large Project .
Using a GitHub pull request
1) Fork the gatein/gatein-portal repository into your personal GitHub account and clone it. If you already have a fork make sure you are synced with upstream.
If you have forked upstream into your personal repository already
> git remote add gatein-upstream git://github.com/gatein/gatein-portal.git
> git fetch gatein-upstream
> git fetch gatein-upstream --tags
> git checkout master
> git pull gatein-upstream master
> git push origin master
or
> git pull --rebase gatein-upstream master
2) Create a new branch for work you are going to do
> git checkout -b GTNPORTAL-1234 master
3) Implement a feature, test properly, then test again... make sure it builds and commit to your topic branch. Push to GitHub
> git status
> git commit -m “GTNPORTAL-1234 ...”
4) Now if this is unlikely case where no changes were made to the original repo made in parallel - push your branch to GitHub. If they were, skip and check the flow continuation below
> git push origin GTNPORTAL-1234
5) Send pull request for your branch (http://help.github.com/send-pull-requests/)
Work has been done in parallel in the repository
If there was work happening in gatein master in parallel you may want to sync and rebase before pushing your changes. Here is alternative way after point 3)
4) Make sure your private master is in sync with main repository
> git checkout master
> git fetch origin
or to sync with original remote repo
> git fetch gatein-upstream master
> git pull gatein-upstream master
5) If you use many smaller commits during your development, let's try to squash them into less commits to avoid mess in history. See bottom part of this document (section "Squash many commits into single one").
6) Rebase your personal repository master on top of upstream master to ensure that you have latest stuff from gatein upstream. Then you can merge your work on top of master.
> git checkout master
> git rebase gatein-upstream/master
> git merge GTNPORTAL-1234
and check that your history looks good:
> git log --pretty=oneline --graph --abbrev-commit
Ideally, you should see straigh line without bubbles.
6) Push to your private fork
> git push origin
7 ) Send pull request (http://help.github.com/send-pull-requests/)
Merging pull request
This applies for project admins who are gatekeepers of the repository.
GitHub UI should not be used for automatically applying pull requests because GitHub merges with no-ff which sometimes results in bubbles in history. Instead, please proceed like this:
Example flow:
git remote add someone https://github.com/someone/gatein-portal.git
git fetch someone
git checkout -b someone-master someone/master
git rebase master
git checkout master
git merge someone-master
# build with tests now
git branch -d someone-master
git push gatein-upstream master
Mailing list notifications
We’ll have a separate mailing list to receive pull request notifications, announce they are merged or request and discuss why they should be fixed. However for administrative reasons it is not yet available.
General hints and cheat sheets
Valuable links for starters:
Always handy
> git status
Show history in a nice way
> git log --pretty=oneline --graph --abbrev-commit
List branches
> git branch -a
List remotes
> git remote show
> git ls-remote --heads origin
> git remote show origin
> git remote show gatein
Git usage principles
We will describe a few principles you will use for contributing to GateIn.
Try to ensure straight history without bubbles when merging a branch
Below you can find some information which can help to undestand context of how to achieve linear history.
GIT provides several kind of merge possiblities (in a broad sense)
- Merge: takes several commits and unify them with a merge commit
- Rebase: replays commit of a branch on the HEAD of another branch
The picture shows what happens with one future and two futures (two futures happens when concurrent modification of the branch is done). We try to always ensure to have straigh history (Bottom picture of both cases) instead of bubbles (middle picture of both cases). So we want to always rebase the commits from target branch into your branch before merging this branch.
The picture below shows how the history looks when using no-ff merge (Merge which always adds new branch in the history and create new commit with two ancestors). This is something we want to avoid.
Example of how to rebase master branch on top of branch "bar_feature" and then merge "bar_feature" branch into master:
> echo ‘foo’ > main.txt
> git add main.txt
> git commit -m ‘added main.txt’
> git checkout -b bar_feature
> echo ‘bar’ > main.txt
> git add main.txt
> git commit -m ‘changed bar’
> echo ‘bar2’ > main.txt
> git add main.txt
> git commit -m ‘updated bar’
> git rebase master
> git checkout master
> git merge bar_feature
Note that this mainly apply to repository maintainers that will merge pull requests in the master branch. The author of the pull request should not care about it, specially if the pull request is done via a branch.
Single commits
When a single commit resolves a JIRA, it also makes sense to not have a branch. Which can be achieved by a cherry-pick or rebase of that commit onto the HEAD of the branch.
In that case we want to avoid merge and instead keep a linear history as only one single commit is related to the JIRA.
Squash many commits into single one
During development, it's common practice that you use many commits. But before publishing your work, you should try to reduce number of commits to ensure that history is straigh and clean. You should also try to use JIRA ID (like GTNPORTAL-1234) in commit message.
Example:
Let's assume that you made some important work on file README.txt and you used 5 commits during your work on this task. Your history looks like this:
> git log --pretty=oneline --graph
* b20995bb8b1f36167cb789054d528b5e1b7e9b3e Change 5 in README.txt
* 7a284a8564617978689a127599812e3ca0cd8e93 Change 4 in README.txt
* 72c29cd3ce3fd0d53efc4a4024a7f535112777b1 Change 3 in README.txt
* cfa98cf707902b731244ce1168d5b4f6107e35de Change 2 in README.txt
* 4bc091714510786c1aaa49438a07b60cfa04af21 Change 1 in README.txt
* 27daccc38c9882db80db495c25f009a94185356f GTNPORTAL-1233 Some previous work
Let's say that you want to attach JIRA ID to your commits and you want to use only 2 commits instead of 5. So you would like to squash commits 1 and 2 into single commit, and commits 3,4,5 into another single commit.
You can achieve it with command
> git rebase -i HEAD~5
and then in editor you can change the part with:
pick 4bc0917 Change 1 in README.txt
pick cfa98cf Change 2 in README.txt
pick 72c29cd Change 3 in README.txt
pick 7a284a8 Change 4 in README.txt
pick b20995b Change 5 in README.txt
to something like:
pick 4bc0917 Change 1 in README.txt
squash cfa98cf Change 2 in README.txt
pick 72c29cd Change 3 in README.txt
squash 7a284a8 Change 4 in README.txt
squash b20995b Change 5 in README.txt
Then press CTRL^X and in next dialog, you have opportunity to change commit messages and attach JIRA to them. Your commit messages can be changed to something like:
"GTNPORTAL-1234 Change 1 and 2 in README.txt" and "GTNPORTAL-1234 Change 3,4 and 5 in README.txt".
Now when you check yourt history, you will likely see something like:
> git log --pretty=oneline --graph
* 52b4b2e0442ec041b017bca1fc8a9c0dd44f5647 GTNPORTAL-1234 Change 3,4 and 5 in README.txt
* c817f419da20d349f5a39e197e2492a000dbbfd8 GTNPORTAL-1234 Change 1 and 2 in README.txt
* 27daccc38c9882db80db495c25f009a94185356f GTNPORTAL-1233 Some previous work
Atomic commits
Your commits should be atomic
- It should not span several commits
- A commit should not contain several concerns
This principle cannot sometimes be achieved but commits should try to honour it in order to
- Increase usability : other can follow your work more easily
- Decouple commits : if you commit is properly isolated it makes it easier to port to another branch
Git helps you by providing the capability to edit your commit history (before it is published in the public repository)
- Squash two commits in a single one
- Split a commit into several
Rebasing
Never rebase public history because bad things will happen.
You can use rebase when it is private, actually it should be done before merging a branch in order to improve your commit history.
Comments