The more I use Git, the more I am really impressed with some of its capabilities. What is Git? Git is a very advanced distributed source code control system that can do some very neat things. For the average developer using open source software, we are mainly concerned about maintaining patch sets (often called a topic branch). Git includes a rebase capability that is very useful for a number of different operations related to maintaining a branch of code including moving a branch forward, moving a branch around on an upstream branch to look for breakage, and merging changesets to create patch files. This article provides a brief overview of some of these operations and several tips.
When in doubt, make a branch
The most important concept to understand when working with Git is to get used to creating and destroying branches. This is different than most other SCMs where all branches are stored on a central repository, and you don’t want to clutter the branch namespace. With Git, get used to branching without even thinking about it. For example, the following are the branches in a Git repository I use to maintain patch sets for the Compulab cm-x270:
Most of the names are self explanatory. The cmx270 branch is where I track the latest kernel version. The svs branch is for a customer platform based off the Compulab cm-x270. To view the changes on any particular branch:
an Git will start showing you a list of changes on that branch. This works even if you are not working on that branch, so it is very handy for seeing what changes are in other branches. With Git is also very easy to move changes between branches using git-cherry-pick. For example, if we are working on the cmx270 branch, and we want to pull the 3rd changeset from the HEAD of the cmx270_2.6.22 branch into the cmx270 branch, we would do something like:
Git is really good at maintaining patch sets
Many developers use quilt to maintain patch sets. Once you understand how Git works, Git is even better at maintaining patch sets. Although there are some add-on tools available for maintaining patch files in a git repository like Patchy GIT and Stacked GIT, I’ve found that core Git works well enough. Actually the Patchy GIT maintainer has quit working on Patchy GIT because core GIT now meets his needs. The fundamentals are you simply export each Git commit as a patch using the git-format-patch command as shown below:
git-format-patch -o tmp/ master
Produces the following files (all of the commits since the branch has left the master):
Pretty slick! HEAD~8 means to back up 8 revisions from the current HEAD and then start outputting changes as patch files. The name of the patch file is the commit comment.
Moving your patch set forward in time
If you are maintaining a patch set for the Linux kernel, you might want to move your patchset up to the latest version for testing, and also maintain patchsets for each stable version of the kernel. Lets say we already have a patch set for the 2.6.20 version. The first thing we need to do is create a branch. In this example, I am maintaining a patch set for the Compulab cm-x270 computer module.
git-branch cmx270_2.6.20 v2.6.20 (branch name, tag name in repository)
At this point, I would manually apply each patch, and then commit each patch as its own changeset. Now, I want to move the changeset forward to kernel version 2.6.21. The easiest way to do this is create a new branch, and simply rebase your patchset to the new kernel version. Make sure you create a new branch first, or your existing branch will be moved!
git-checkout -b cmx270_2.6.21 cmx270_2.6.20
git-rebase --onto v2.6.21 master (tag point to relocate branch, upstream branch)
The upstream branch is required to be specified so that Git knows where our branch changes start — specifically where the cmx270_2.6.21 branch diverges from the master branch.
There may be a few things to clean up along the way if changes don’t apply cleanly. The most critical thing to know is that if you want to skip a patch, you must run “git-reset –hard” before running “git-rebase –skip”. This is often handy if you have an especially troublesome patch that you don’t want to apply now. You can skip it and then cherry-pick it later. Otherwise the process is fairly straightforward. After you are finished, you can now export a set of patch files that can be applied to the 2.6.21 kernel.
Bisecting the master branch to determine when things broke
You may run into a situation where something broke on the main branch, and you don’t know where. Most of the time, you would just run git-bisect to do a binary search to find where things broke. This does not work so well if the platform you are testing is not supported in the main branch — the changes in your topic branch are required to make the system run. Once again, git-rebase to the rescue. The first thing we must do is find the mid-point between the known good and bad points.
git-rev-list --bisect v2.6.20 v2.6.21
Now, rebase out changes to the above rev and test.
git rebase --onto 13f7e5acc8b329080672c13f05f252ace5b79825 master
The build failed, so now we have a new “bad” point and can continue our binary search.
git-rev-list --bisect v2.6.20 13f7e5acc8b329080672c13f05f252ace5b79825
If you can build working kernels from the main tree, then you can directly use git-bisect — another good reason to get your changes into the mainstream source, but this is not always possible.
Another option for doing this type of binary search is to use git-bisect, and then cherry pick your changes at each bisect point. It may also be possible to use git-merge, but I have not figured out how yet.
That is it for now. Git is very useful for maintaining multiple patch sets for different versions of an open source project, and is just another thing that makes working with open source projects a real pleasure. Future articles will cover how to further manipulate patches such as how to combine two patch files.