In this installment in my series describing a Git workflow for open source collaboration we'll look at the workflow for developing code to be contributed back to the project (e.g., bug fixes, new features, etc.).
Add a Feature
After reading parts I and II your local repo is all set up and ready to go. Let's say you want to add a feature to the project. The first step is to create a topic branch for your changes. A topic branch is not a special type of branch from a technical perspective, it's just a regular Git branch, but I'm going to use that term to refer to a branch that you create for one specific purpose. It might be to fix a bug or to add a feature. This brings us to our first rule:
Rule #1 - All Changes Should Be Made in a Topic Branch
That's right. Never make changes directly in the develop branch, and never, ever, touch the master branch. If you want to add a feature you need to create a new topic branch for your feature. Let's call it newFeature and create it by issuing the command from within the develop branch:
git checkout -b newFeature
You'll recall from the previous post that the -b option tells Git to create a new branch. Make some code changes and commit often. Don't worry about creating too many commits as you're going to squash them down before merging them back into the develop branch. One thing you want to keep on top of, though, is keeping your code up to date with the code in the main repo. If changes happen to code in the main repo you want to get those changes into your topic branch as soon as possible, to minimize the chances of merge conflicts later. This brings us to the second rule:
Rule #2 - Keep Your Topic Branch Up To Date via git pull --rebase
You'll keep your topic branch up to date by pulling any changes from the main repo's develop branch into your topic branch using the following command, which you would issue from within your topic branch:
git pull --rebase upstream develop
This brings your topic branch up to date and avoids adding merge commits via the --rebase option. It may generate merge conflicts, which you can address at this time. Continue making code changes, committing changes to your branch, and keeping your branch up-to-date until your feature is finished.
Merge Your Changes Back into the Develop Branch
When your feature is complete, you need to merge the changes back into the develop branch so you can push them to your fork, but first, if you've created a lot of little commits, you may want to squash them into one commit.
Rule #3 (Optional) - Squash Meaningless Commits in Your Topic Branch via git rebase -i
You can do this using git rebase in interactive mode, which is explained below. Note that this is really only necessary if your feature is quite small and does in fact contain a lot of meaningless commits. If you built your feature up in logical pieces and your commits represent actual functionality then you won't want to squash them, so calling this a rule is a bit of an overstatement, which is why it is optional.
Note also that you can continually squash small commits during development of your feature so that you can commit often yet still end up with a smaller number of meaningful commits. You would do this using the HEAD~n syntax for specifying which commits to include in the rebase. More information on that option can be found in a blog post that I wrote about git rebase awhile ago.
To keep things (relatively) simple we'll assume you want to squash all of the commits in your topic branch before merging it back into your develop branch. To do this, from within your newFeature branch, issue the following command:
git rebase -i develop
This tells Git that you want to rebase all of the commits that exist in the current newFeature branch and do not exist in the develop branch. A git-rebase-todo file will open in the the text editor that Git wants to use (I have mine configured to use TextMate), with some rebasing instructions. The file will look something like this:
This is quite convenient as git actually provides you with some instructions. In this example we want to combine the second and third commit with the first, so we'll edit the file to look like this:
Note that you can just use the letter "s" in place of the whole word "squash" when editing the file. When you save and close the file, git will pop open another file in the editor to edit your commit message:
From here you can choose to accept the three individual commit messages that were picked up from the three commits, or you can remove them (or comment them out) and provide your own commit message(s). When you save and close this file you'll be back at the command line with a message similar to this:
1 files changed, 2 insertions(+), 0 deletions(-)
Successfully rebased and updated refs/heads/newFeature.
[detached HEAD 55510e4] added a cool new feature
You are now ready to merge your changes back into the develop branch.
Rule #4 - Merge Your Topic Branch Into Develop via git merge --no-ff
Switch back to the develop branch:
git checkout develop
and issue the command:
git merge newFeature --no-ff
The --no-ff option will force a merge commit to be created, which provides an indicator that the changes came from a branch and also provides a point from which things can be rolled back. At this point you'll need to address any merge conflicts that come up, but there shouldn't be any as you've dealt with any while you were keeping your topic branch up to date with git pull --rebase. You should make sure your develop branch is still up to date with the main repo by issuing one final git pull --rebase upstream develop command and then push your changes to your GitHub fork:
git push origin develop
Part IV in the series will describe submitting your changes to the project as a contribution, using GitHub's pull request system.