Git for GNOME developers

Git is the revision control system used for the Linux kernel, Xorg, Freedesktop projects, Cairo, and many other projects. This page documents how you can use Git to do GNOME development.

DISCLAIMER: This page is NOT about switching GNOME to use Git instead of Subversion. Do not even comment about that here; it's not the right place to do it. This is about using Git in conjunction with GNOME's existing infrastructure.

See also Shaun's basic Git workflow: part 1 and part 2.

Why Git?

Roughly in order of importance / convenience:

Git concepts

Cloning - When you "clone" a repository, you do just that: you bring the whole development history into your computer. Git uses an efficient representation of the data so that it doesn't use a lot of space.

Master branch - The main branch in your local Git repository. Normally this reflects the contents of a remote repository. You shouldn't really hack on it; instead, create a branch based on the master and work on that one. You can merge your changes into the master branch later.

HEAD - The tip of a branch, i.e. the latest revision within a branch.

How do I use Git to hack on an existing GNOME project?

As of January 2007, GNOME uses Subversion as its revision control system. Fortunately, Git has good tools to let you use a SVN repository as the "mothership" while you enjoy most of Git's features on your own computer.

Getting Git

What version of Git is required? Ubuntu Feisty has Git and git-svn 1.4.4.2 but git-svn clone doesn't seem to be supported.

Git 1.5 is absolutely recommended; the current stable version is 1.5.5. In the 1.5 versions, git learned a much more pleasant interface and is partly because of that being used more and more.

Getting the code

Reuse an existing clone

The quickest way to get the code for an existing GNOME project is to reuse someone elses git-svn conversion. A list of such conversions is found towards the bottom of the page. In some cases the conversion may come as a tarball. If the directory .git/svn exists already then you should be good to go. Just run

git-svn rebase

and git will fetch any new commits to bring your local copy up to date. If the .git/svn file doesn't exist or you only have a git mirror from which you've made a clone, you need to reconstruct the git-svn metadata. To do this you need to find the original subversion url for the project and then run this command:

git-svn init http://svn.gnome.org/svn/modulename -s --rewrite-root svn+ssh://svn.gnome.org/svn/modulename

This tells git-svn that it should fetch from the http url but that it should record the svn+ssh url in its metadata; this allows anonymous users to update the git repository and also authenticated users to commit back. Next bring the local git-svn repository up to date:

git-svn rebase

Creating a fresh clone from subversion

The first thing to do is to clone an existing GNOME project. This means getting a copy of that project's whole or partial SVN history. For newish projects you may be able to download their whole history relatively quickly; for older/big projects like GTK+ you may prefer to download only part of the history (say, the past three months, or whatever is convenient for you).

First setup ssh to use your gnome.org user name by default by adding the following to your ~/.ssh/config file:

Host svn.gnome.org
        User username
        Compression yes
        CompressionLevel 3
# ovitters: Do NOT do something like ControlMaster! It will break your committing with SVN!
#       ControlMaster yes

By doing this it will enable the cloned repository to be used by other users. The reason for doing this is so that your username is not part of the url used, as git uses this to be able to reconstruct the svn history.

Downloading a project's whole history:

#the -s options assumes a standard trunk/branches/tags layout - if the repository is laid out differently this won't work
#read the man pages for git-svn for more information on the correct arguments in this case

git-svn clone http://svn.gnome.org/svn/modulename -s --rewrite-root svn+ssh://svn.gnome.org/svn/modulename

or only for trunk:

git-svn clone svn+ssh://svn.gnome.org/svn/modulename/trunk modulename

This will create a local repository with modulename's whole development history in a directory called ./modulename (if you want another name, mv it or change the end of the git-svn command line). This will take some time depending on the size of the project's history.

Alternatively, you can clone starting at certain SVN revision up to HEAD:

git-svn clone -r revnum svn+ssh://svn.gnome.org/svn/modulename/trunk modulename
cd modulename
git-svn rebase

First you need to find out a suitable revision number. You can use "svn log" as usual to get this.

If you want a branch other than trunk, substitute "trunk" for "branches/modulename" and the last modulename by the name of the branch in your call to git-svn

Doing local development

The cardinal rule of doing development with Git is: don't use the master branch. Think of the master branch as the "pristine copy" of upstream's sources.

Create a branch:

cd modulename
git checkout -b mybranch

This creates a branch starting from wherever you are, which at this point usually means "HEAD in the master branch".

With Git it is customary to do many small, logical commits, instead of big commits which modify many things at once.

Modify some files:

emacs file1.c file2.c ChangeLog

Mark them and commit them:

git add file1.c file2.c ChangeLog
git commit -m "Colorize the frobs in funny ways"

Note you need to tell Git what to commit: the "add" and "commit" steps are separate. This is useful when you need to keep a modified file around but not commit it just yet.

Merging your branch and committing to SVN

Once you are finished with your work in mybranch, you'll want to merge it to your master branch (remember, that's the one which tracks the upstream SVN repository) and then actually push the changes to SVN.

Switch to your master branch:

git checkout master

Merge the changes from your branch:

git merge --squash mybranch

IMPORTANT: The --squash option is important. Git keeps information about what got merged where, but SVN doesn't. Git-svn cannot handle "normal" merges as done with Git; the --squash option tells "git merge" to act as if this were a manual commit, not a merge (more info). Notice that in the upstream Git repo some patches which would allow git-svn to use a real merge instead of the faked one with --squash are already included (Link to Git commit).

Commit the changes:

git commit -a -m "Merge mybranch into the master branch to implement feature X"

Finally, send the changes to the SVN repository:

git-svn dcommit

Updating from SVN periodically

Most of your work will happen offline, that is, you won't need to contact the svn.gnome.org since git-svn already took care of mirroring the development history for you. However, you'll periodically want to update your sources to the latest from upstream.

Get the latest changes from svn.gnome.org:

git checkout master
git-svn rebase

Using git with jhbuild

JHBuild has support for using git to manage svn and cvs repositories. To enable this support add the following to your .jhbuildrc file:

svn_program = git-svn
cvs_program = git-cvsimport

This will then cause jhbuild to check out cvs and svn repositories using git, from the current revision. If you want the full development history you need to checkout the module as described above.

Note: This will not convert existing cvs/svn checkouts to use git and thus requires you to manually delete any existing svn/cvs checkouts before using this feature

Sending patches

Git encourages you to do many small, self-contained commits. For example, say you want to add a feature to a program, but first you need to refactor the code a bit to allow your feature to fit in. A first patch would do just the refactoring. A second patch would actually add your feature. A third patch would update the documentation. If your feature is very complex, you may want to add stub functions in a commit, then actually fill them in in further commits, etc.

The idea is to submit small patches for review by maintainers, instead of burdening them with the information overload of a single mega-patch.

Let's say you created a branch mybranch off the master branch. Hopefully this is not your original work branch, but instead a "cleaned up" branch with actual logical commits, which you would be proud to show to your mom, instead of experimental / "fix whitespace" / "remove debug printf" commits that you had in the work branch. You have several commits in that branch. If you are in mybranch, you could generate a patchset like this:

mkdir ~/patchset
git format-patch -n -o ~/patchset master

This will create ~/patchset/0001-xxx.patch, ~/patchset/0002-yyy.patch, etc., for all the commits in your branch. Each file is intended to be a separate mail message which contains your commit message, a diffstat, and the actual patch. These files are in Unix MBOX format; you can import them into Evolution or your mailer of choice to send them out to the maintainer. If you import them in Evolution, you can then select "Edit as new message" from the context menu of one of those mails to enter the To: header and actually send out the mail.

If you want to attach a patch or an entire patchset to GNOME's Bugzilla, you can use the most excellent git-send-bugzilla script written by Steve Frécinaux. This Perl script will attach a set of commits as single patches, complete with commit message and diffstat, to a bug given its number.

Full repository checkouts

GitForGnomeDevelopers (last edited 2008-06-01 23:42:13 by JamesSharpe)