Git Guide

Here is a quick guide to git usage.

Installation

You need to install the git-core, git-gui and gitk packages. On recent distributions, git-core is a transitional package which depends on git package.

sudo apt-get install git-core git-gui gitk

Basic usage

Tell your name and address to git:

$ git config --global user.name "Luke Skywalker"
$ git config --global user.email luke.skywalker@apbteam.org

Clone a git repository

Clone the git repository using:

$ git clone http://git.ni.fr.eu.org/apbteam.git

Once this is done, you will get a apbteam directory with all project files. Git stores its metadata in the .git directory. A git clone contains a full copy of the remote repository with full history and all branches.

You can list local branches using:

$ git branch
* master

Here, there is a single branch named master which is the checked out one.

You can list available branches fetched from the server using:

$ git branch -r
  origin/HEAD
  origin/master

(-r means remote)

You can work on our local branch, doing commits, and examining history.

Submodules

Submodules allow foreign repositories to be embedded within a dedicated subdirectory of the source tree, always pointed at a particular commit.

As we use submodules in our repository, this requires an additional setup after the original clone operation:

$ git submodule init

If git responds with {{{remote (origin) does not have a url defined in .git/config}}}, you should setup this url used as base URL for submodules:

$ git config remote.origin.url git://apbteam.org/apbteam.git
$ git submodule init

Now you will be able to update submodules using:

$ git submodule update

Work on local repository

All your commits are local to your repository. Once happy with them, you will be able to push these commits to the remote repository.

Unknown files must be added so that git know them.

$ git add my_new_file.h

You can make a commit with every modified files:

$ git commit -a

Or with modified file in a single directory:

$ git commit my_directory

Or you can stage your changes for the next commit:

$ git add my_modified_file.h
$ git add -u my_updated_directory
$ git rm a_removed_file.c
$ git commit

You can also use git citool if you prefer a GUI. It will present you all modified and unknown files so that you can select what will go in the commit. You can even select parts of files.

Git commit tool interface

You can inspect the current repository state:

$ git status

And issue diff commands to review changes:

$ git diff
$ git diff --cached # To see changes in the stage area (index)
$ git diff origin/master

You can also use gitk to review changes with a GUI.

gitk interface

Update local repository

To fetch changes from the origin repository, use:

$ git fetch

This will update remote branches. You may use gitk --all to review changes using a GUI.

You could merge changes from the origin repository using:

$ git merge origin/master # Do not do this!

But WAIT! This is called an downstream merge and considered bad practice. Usually, you will prefer to rebase your work, this means to pretend your work is based on what you have just fetched. This will ensure a clean, merge-free history, avoiding spaghetti history. This is simple also:

$ git rebase origin/master

If there is conflicts, git will guide you how to solve them, just read carefully its instructions.

You can fetch and rebase with one command:

$ git pull --rebase origin master

Push changes to the remote repository

As simple as:

$ git push origin master

This will push commits you have on your local branch master to the remote branch with the same name.

If a push would not result in a fast forward of the remote branch, then it will fail with an error like:

error: remote 'refs/heads/master' is not an ancestor of
 local  'refs/heads/master'.
 Maybe you are not up-to-date and need to pull first?

This means that either you messed with your history (you changed commits which were published yet) or that you need to fetch changes from the remote repository (more probable).

Please do not do any non fast-forward push!

You can also specify explicit source and destination branches:

$ git push origin my_branch:master

This will push commits you have on your local branch my_branch to the remote branch master.

History rewrite

If you made rubbish with your last commit, fix it, then use the git commit --amend to change your last commit.

You can also use git rebase --interactive to changes previous commits.

But WAIT! NEVER NEVER NEVER change commits which are pushed to another repository. This will result in a non-fast-forward push which will be considered very rude by other developers.

Feature branch work flow

As a general rule, you should try to split your changes into small logical steps, and commit each of them. They should be consistent, working independently of any later commits, pass the test suite, etc. This makes the review process much easier, and the history much more useful for later inspection and analysis, for example with git-blame and git-bisect.

First, and while in your master branch (git checkout master), pull in the most recent changes:

$ git checkout master
$ git pull origin master

This should never create a merge commit because we are never working directly in master.

Then, check out a new feature branch, with a short and descriptive name:

$ git checkout -b my_new_feature

Do some work on this branch, committing early and often (for instance, whenever your tests pass). Rebase against the upstream frequently to prevent your branch from diverging significantly:

$ git fetch
$ git rebase origin/master

Once work on the feature is complete, you will have a branch with a lot of small commits like "add new circle command", "fix angle computation", "add missing #include", "oh crap – add angle tests" and so on. This is useful while developing but larger, incremental commits are more easier to maintain. We will use an interactive rebase to squash them together.

We want the rebase to affect only the commits we've made to this branch, not the commits that exist on the upstream. To ensure that we only deal with the "local" commits, use:

git rebase -i origin/master

Git will display an editor window with a list of the commits to be modified, something like:

pick 3dcd585 add new circle command
pick 9f5c362 fix angle computation
pick dcd4813 add missing #include
pick 977a754 oh crap - add angle tests

Now we tell git what we to do. Change these lines to:

pick 3dcd585 add new circle command
squash dcd4813 add missing #include
pick 9f5c362 fix angle computation
squash 977a754 oh crap - add angle tests

Save and close the file. This will squash commits together and present us with a new editor window where we can give the new commit a message.

These commits are now ready to be merged back into upstream. First rebase against any recent changes in the upstream, then push your changes:

$ git fetch
$ git rebase origin/master
$ git push origin my_new_feature:master

(text copied/inspired from http://reinh.com/blog/2009/03/02/a-git-workflow-for-agile-teams.html)

More documentation

See: