One of the things I found difficult to get my head around, coming from a very commercial development background, is how version control and change management works in the Android open source community.
The tools are great, but the documentation is scarce and doesn’t join up to give a clear overall picture of how to use them together to easily control what you are doing.
In this guide, I hope to provide a picture of how I use the four tools in the title to control branching, merging, storing and submitting my code base for review
Note that I may not be doing it in the best or most efficient way. This is an approach I have learned by trial and error, so I welcome comments from those more experienced than myself or indeed anyone who sees a better way to do this.
This guide assumes that you are using a machine and an environment capable of building the Android OS. If you need some pointers, I can thoroughly recommend Fattire’s guide to building CM10 for the Nexus 7.
Don’t be put off by the guide being for CM10 or the target platform being the Nexus 7. I easily adapted the techniques in that guide to build both AOSP source and CM10 for the Motorola Xoom, so it’s a good all rounder to get you up to speed with the techniques needed to produce your first build.
The first tool I want to discuss is repo. I am developing on a CM10 code base, so I used repo initially to download the code base and synchronise my code tree with CyanogenMod, but you can use the techniques I use for any existing Android ROM.
The first thing to understand about repo is that it is a tool built on top of git to help you to manage projects. You can do basic change management in git, but repo adds a level to that, and it can be used to keep in sync with your code base and also to submit changes back for review. You will save yourself heartache down the line if you work with it rather than fighting it, as I initially did by trying to do everything with git.
To create a branch with repo, from the root directory of your code tree (usually “~/android/system”), you would use:
repo start "branch" "path"
Replace “branch” with the name of your branch, and “path” with the path to where it lives in your code tree.
If I want to edit something in the CM10 code, the first thing I do is to find the CM10 project where it lives and start a “master” branch which I will keep as pure CM10. For example, if I wanted to edit the framework, I would do this:
repo start master frameworks/base
To demonstrate what that has done, try this:
cd frameworks/base; git status
You should see that you have created a git branch in the directory frameworks/base, called master, and checked it out.
I don’t work in my master branch though; I have several other branches which I use for development. My first CM10 ROM was for the Motorola Xoom, so I created a “jellybean” branch to work in. I realise now that name isn’t so appropriate, but I was naive when I started and it hasn’t hurt me so long as I remember that my jellybean is jellybean on the Xoom!
I also use a “tablet-ui” branch to stash my tablet mods, a “tablet-build” branch to control my tablet mode builds for the Nexus 7, and a “tablet-patch” branch to create patches for CM10 with my tablet mods in them.
I started these branches with repo in exactly the same way as my master branch, but I started them in many different projects where I need them, including Settings for instance, keeping the name of the branch consistent in each project.
To switch branches in a project, change directory to that project and use:
git checkout "branch"
In summary, use repo for syncing with your code base and remote projects, and to create branches which you want to store remotely so that you can share your work
Github is a tool for both storing your work remotely, and sharing it with others.
First thing is to set up a github account and follow all relevant instructions. I’m going to guess you can handle that.
Assuming you have a project you want to work on, the next thing you want to do is to fork the project from your code base in github. Let’s imagine you want to work in frameworks/base, and your code base is CyanogenMod. Just go to the correct project on the github website, and select “Fork”.
Now you have your own version of the project in your github account, you want to hook it up to your local project. To do that, grab the URL of your forked project, change directory to your local project, and use the following code:
git remote add "name" "url"
Where “name” is the name of the project which you want to use when referring to it locally. For instance, with my environment, I use “upstream” to refer to my own github, and “cyanogenmod” to refer to the cyanogenmod github.
That brings us neatly onto the idea that you can have more than one remote project connected to your local one, which will be useful later in this section, when we want to merge changes from your code base into your local project.
Now you have hooked up your local project with your remote one, you can push changes you make locally into your remote project on github. It’s a really simple command:
git push "name" "branch"
Where “name” is the local name of the remote project, and “branch” is the local branch you want to push. That will add your local commits to your remote project. We’ll talk about how to delete a remote commit in the section on git.
So you’ve hooked up your local project to your remote one, but now you need to set up repo so that you sync with your own project, not your code base. You do that by navigating to your “.repo” directory, and adding something like the following lines to the end of your “local_manifest.xml”:
<remove-project name="CyanogenMod/android_frameworks_base" />
<project name="zigackly/android_frameworks_base" path="frameworks/base" remote="github" revision="jellybean" />
Remember to change “zigackly” to your own github login, and the revision to the remote branch you want to sync with when you do a repo sync! I’ve removed the CyanogenMod project because that’s my code base, but if you are using a different code base, then refer to that instead.
Now the last thing you will want to know is how you merge your code base with your own project, to keep up to date whilst preserving your own commits. First, make sure you have hooked up the remote project in your code base to your local project, with something like:
git remote add cyanogenmod https://github.com/CyanogenMod/android_frameworks_base
Now you can fetch the project from the code base and merge it like this:
git fetch cyanogenmod; git merge cyanogenmod/jellybean
That will merge the remote jellybean branch onto your local branch. Make sure you are in the right local branch before doing that! After your merge, you can push your changes back to your remote project.
I’ll discuss resolving any conflicts you encounter in the next section.
Git is essentially a change management tool, but it might be a little different from what you’re used to in that it stores changes as edits to a number of files rather than revisions of particular ones, and readily allows cherry picking and merging of these “commits” onto other branches.
I don’t want to discuss everything you can do with git here, so I’m going to largely ignore branching, since we’ve already discussed how to do that with repo.
This section will show you how to commit your changes, how to delete a commit, how to cherry pick a change from a different branch or remote project, and how to resolve conflicts.
To commit a change, first change some files in your project, and then change directory back to the root of the project (e.g. frameworks/base). Now use “git status” to see what you’ve changed. Add the changes to the commit one at a time, using:
git add "file"
When you have added all the files you changed, commit the change using:
git commit -am "commit message"
To delete the most recent commit, simply use this command:
git reset --hard HEAD^
If you didn’t use the “^”, it would discard any uncommitted changes you have made.
The way I delete a previous commit is not necessarily the most efficient way to do it, but I have had problems with rebasing where it just failed for no reason I could see, and that leaves a big mess, so the way I do it now is to first create a temporary branch with:
git branch "branchname"
Then I delete all my commits, one by one, until I have deleted the one I want to delete. Then I use:
git log "branchname"
That lists all the commits I preserved in my temporary branch. Now I can cherry pick the commits one by one back into this branch. Slow but sure.
To delete a branch, use:
git branch -D "branchname"
To push a branch with deleted commits back into your remote project, use the “-f” option to force the push:
git push -f "name" "branch"
To cherry pick a commit from another branch, use “git log” as in the example above, to list all the commits in that branch, then copy the commit ID you want, and then do this:
git cherry-pick "commit ID"
To cherry pick a commit from a remote project, first hook the project up to your local one and fetch it, the way we did in the github section, then just use the commit ID of the commit you want to cherry pick.
The process is very easy unless there is a conflict with what you want to merge or cherry pick. If that’s the case git will tell you so, and you have to resolve it.
If you have conflicts resulting from a cherry pick or a merge, use “git status” to tell you which files are affected. To resolve created or deleted files, just use “git add” with the filename.
The most difficult thing to resolve is files where both branches have changed in the same place, and this is where you have to use your judgement.
Git will create a file containing the conflicts, embedded in the code, in the form:
changes made on my branch
changes made on the branch I'm merging
You need to go in and edit these sections to leave the code you want to keep, removing the markers (“<<<<<<<“, “=======” and “>>>>>>>”), so that what remains makes sense. Once you’re done, “git add” the files, and use “git commit” to finish committing the changes.
Gerrit is the code review tool used by CyanogenMod and other ROMs to review and either accept or reject changes to the code base suggested by the community.
In this section I will talk about how to submit and patch changes for CyanogenMod.
First register on the CyanogenMod gerrit tool, and add your keys, using these instructions.
The next thing you will need is a branch which is synced with the CyanogenMod code base, with none of your commits in the project you want to submit. If you are doing your own development, that means syncing your code tree with CyanogenMod instead of your github.
To do this, I first remove the lines I added to my local manifest in the “repo” section above. I then switch to my master branch, and “repo sync”.
Now I create a “gerrit” branch using repo, and cherry-pick the commit I want to sync into that branch.
To submit the change, change directory to the root of your code tree, and use:
repo upload "project"
Repo will take you to a file in your default editor where you will need to uncomment the branch and the commit you want to upload for review. In the example we are discussing, you would uncomment the gerrit branch, and the commit (there should only be one listed), and then exit the editor to finish the upload.
Once your review is up and running, people will suggest changes to your code. You can use these instructions for how to submit patch sets.
Once you’re finished, remember to change your local manifest back in “.repo” and do a “repo sync” on your master branch, so that you are back where you started.
That’s it! Hopefully this will help you in developing that perfect ROM, but if you get stuck just ask and I’ll do my best to help.