Git cheat sheet

This document is here to help NetSurf developers who are more used to non-distributed revision control systems, or to DVCSs which are not git. It also contains a few helpful hints for people used to git so it's worth a skim even to those who think they know it all already.

Git is one of the least obvious, least intuitive distributed revision control systems out there. However it is also very powerful and very fast. Git appears to have won the DVCS race for the most part, and as such, NetSurf uses it.

Identify yourself

Every commit you make with git contains an identity. (Actually you can differentiate between the identity of the author of a patch and the person committing it to the repository if you want.) As such, you must teach git who you are.

git config --global user.name "My Name"
git config --global user.email whoever@domain.name

Note that if you don't specify --global the name/email address will only be local to the git tree you are inside when you run the command.

The first time you make a commit, if you have not configured your identity, git will give you a reminder.

Useful bits and bobs

It's well worth running:

git config --global push.default current

Since it tells git to only push the branch you're on.

Repositories, Trees and Branches

Each git repository is a project. As such, NetSurf has many repositories. NetSurf's repositories reside on the NetSurf Gitano instance. We will refer to this as the server from now on.

You acquire a copy of a repository by asking git to clone it.

git clone git://git.netsurf-browser.org/buildsystem.git

By default, git will create a directory named after the repository and clone all the branches in that repository into it.

When you have a local clone of a repository, we refer to that as a tree. Git may also refer to it as a working tree and it is where changes are made and commits are done.

Each repository may have many branches. Git keeps them tucked away, showing you only one at a time in your tree. You can list your local branches with:

git branch

By default, a fresh clone will only contain one branch called master which is the equivalent of Subversion's trunk.

You can switch between local branches with:

git checkout branchname

Different bits of git documentation may also refer to refs. In git branches, tags, etc are all represented by their commits. To give those commits useful-to-a-human names, git has the concept of a ref which is simply a name given to a commit. Refs in the namespace refs/heads/ are referred to as branches.

Revisions and commits

There are no traditional revision numbers in git. Instead each commit is given a unique identifier. It is a long (40 character) hexadecimal string but it is also commonly shortened to its first 7 characters. For example, at the time of writing, the tip of the master branch in the NetSurf repository was 00f76b5.

Remotes

Because your git tree is also a full local copy of the repository, git keeps track of the server's copy of the repository in a data structure called a remote. The default name for a remote is origin and you will see that crop up in various places as we continue.

You can update your local view of the server with the command:

git remote update

Or, assuming you're on master you may find pull to be of more use:

git pull

If you pull then git first updates its view of the remote, and then attempts to merge in changes from the remote into your local branch. If you've not made changes locally then this will be done by fast-forwarding you to the server's revision.

You can see the branches available on any remotes you have registered in your tree with:

git branch -r

Making a branch to work on

We recommend that everyone work on branches, merging to master only when work is ready for others. In the past we've all worked on trunk because it was such a pain in Subversion to merge work. However one of git's strengths is its merge functionality so this habit should end.

Before making a new branch, it's customary to ensure that you've got everything up-to-date from the server:

git remote update

Then you can create a new branch, from the server's idea of master with:

git checkout -b username/branch origin/master

You should put your own username in for username (note it should be the username which the server has for you. You can find that out by running ssh nsgit@netsurf-browser.org whoami).

For the branch name, give it something reasonably descriptive but not too long. For example fandango-experiment is good, where experiment-with-new-layout-engine-idea is probably too long.

The origin/master is where you tell git that you want to track the master branch of the origin remote. This not only gives you a starting point for your branch, but also informs git where to get changes from if you run git pull while you have that branch checked out.

To then inform the server of your new branch, run:

git push origin username/branch

When you wish to inform the server of new commits on your branch, you can subsequently just run:

git push

Deleting a branch

To delete a branch from the server when it is no longer required:

git push origin :username/branch

Making changes and committing them

You can make any amount of local changes before you commit, although we recommend each commit be a reasonable self-contained "patch". Obviously it is better to commit early and often; and git does contain a variety of tools for helping you to turn a long line of small commits into a neater set of commits ready for merging. We're not too bothered about that with NetSurf for now; but if you want further reading on the subject, go and search the web for git rebase.

You can ask git about your working tree any time you like with:

git status

You can see changes in your working tree which you've not told git about yet, with:

git diff

When you have edited the code and you are ready to commit, you should run:

git add filename another/filename etc/etc/etc

You can run git add as many times as you like. Each time you do, you're saying to git I want you to remember this file just like it is right now.

If you need to remove files then run:

git rm filename

You can see the diff which git has prepared for committing, with

git diff --cached

Once you're happy you've told git about any edited, new or deleted files, you can run:

git commit

This will pop up an editor, telling you what will be committed and encouraging you to write a change comment. The first line of the change comment should be short (60 or so chars or less) and pithy. It will be shown on the IRC channel as the commit message and also forms what git refers to as the short log message. The rest of the message (ideally separated from the first line by a blank) should explain what you did and why. Normal good commit message etiquette applies here.

You can see the log with:

git log

Don't forget to git push your commits to the server if you want anyone else to see them.

Merging branches

Since we're encouraging work on branches, we also need to know how to merge those into the master branch. In order to keep things neat and tidy, we ask that branches be merged in the following way:

# Switch to the master branch
git checkout master
# Ensure we're up-to-date relative to the server
git pull
# Merge the local branch in
git merge --no-commit --no-ff username/branch
# Review the changes here (git diff --cached)
# Commit the changes
git commit

Note that the commit will default to a message about the merge. That is sufficient, although obviously any more useful message would be appreciated. If the branch is not a local one, but one retrieved from the server, then simply insert origin/ in front of the branch name (so it becomes origin/username/branch) to tell git the location of the branch.

Once the commit is done on master you can git push it to the server.

The options to git merge are important. The --no-commit causes git to leave the tree at the point that it has done the merge but hasn't committed it to the branch. By default, git will commit merges which had no conflicts. Since our code base is complex this is not always sufficient, hence the review step above. The --no-ff causes git to prepare a merge commit. Without it, if the master has not moved on from where the branch was created, git will instead simply shunt the commits onto the master branch. While not a bad thing in and of itself, this would mean that when you did git push the CIA.vc bot would announce every single commit from the branch.

Merging changes from a 3rd party's repo

To merge from the "foo" branch of Somebody's github clone of the NetSurf repo we can do this:

git remote add somebody git://github.com/Somebody/netsurf
git remote update somebody
git merge --no-ff --no-commit somebody/foo
git diff --cached

Check that the diff shows what we want to merge. If so, commit it.

If you don't want to keep the remote around:

git remote rm somebody