wiki:GitNotes
Last modified 18 months ago Last modified on 26.06.2017 19:04:30

Guide to Git in a Fawkes Context

Setup and Config

To make any of the below working properly, you will need to establish a proper git configuration first!

# Personal settings, they must match the main repository information!
git config --global user.name  "Your Name"
git config --global user.email "your.email@address.tld"

These two entries will be checked when pushing to the main repository, therefore be sure to get these right!

Also, here are some config commands for ease and beauty of use

# Have colored output for tools
git config --global color.ui auto
git config --global color.status auto
# Some useful aliases ("status -sb" requires git 1.7.2+)
git config --global alias.ci commit
git config --global alias.co checkout
git config --global alias.st "status -sb"
git config --global alias.stu "status -sb -uno"
git config --global alias.ff "pull --ff-only"
git config --global alias.unmerged "branch -a --no-merged HEAD"
git config --global alias.merged "branch -a --merged"
# On "git push" only push the current branch to its upstream
git config --global push.default upstream
# Use full vim when editing is required, color marking!
git config --global core.editor vim
# tab width of 2 for better readability, e.g. when using Emacs Smart Tabs
git config --global core.pager 'less -x 1,3'

And some advanced settings particularly useful when merging or looking into existing branches. Create them using git config to use them.

# Interactive rebase until where the current branch split off of the master branch
alias.master-rebase=!git rebase -i $(git merge-base HEAD master)

# Show last activity of all the branches
alias.show-branch-activity=!for branch in $(git branch -r | grep -v HEAD); do \
  echo -e $(git show --format="%ai %ar by %an" $branch | head -n 1) \\t$branch; done | sort -r

Clone Wars

The next thing you need to do is to clone the main repository. Get your initial clone of the main fawkes repository with the following commands. This assumes SSH access for developers, otherwise see the generic download instructions.

git clone git@git.fawkesrobotics.org:fawkes.git

or one of

git clone --recursive git@git.fawkesrobotics.org:fawkes-athome.git
git clone --recursive git@git.fawkesrobotics.org:fawkes-gamebots.git
git clone --recursive git@git.fawkesrobotics.org:fawkes-midsize.git
git clone --recursive git@git.fawkesrobotics.org:fawkes-nao.git
git clone --recursive git@git.fawkesrobotics.org:fawkes-robotino.git

to get the corresponding domain repository. The main fawkes repo is 'embedded' as a submodule in each of the domain repos. The --recursive parameters will cause submodules to be properly initialized and cloned.

It's not a branch, it's a Topic Branch!

Proper git-based development in general and the fawkes development guidelines in particular advice to develop each (independent) feature in a separate topic branch. This enables to switch between different working contexts very easily.

To create such a branch and to check it out to start working you do:

git checkout -b your-branch-name

where your-branch-name needs to be replaced appropriately. This creates a new branch based on the one currently checked out in your working copy. To start from a different start point, for example from the master branch, to start a new feature do

git checkout -b your-branch-name master

When pushing, we require that branch names are prefixed with a shortcut of yourname and a self-explanatory (short) name of the topic that is being developed in the branch. This makes things easier for others to follow. For examples see the git repository, it usually contains some examples.

Commit early and often

Once you have achieved reasonable progress, you might want to commit the changes. To do so, you need to tell git what files should be included in the commit by adding them to a staging area. This area is meant to give you a chance to properly compose your commit, possibly even taking only a few changes but not all of a single file, e.g. if it makes sense to separate them. Add files like the following

git add a-modified-file.h a-newly-created-file.cpp

To add specific parts that you changed but not all use the -p parameter like so:

git add -p a-modified-file.h a-newly-created-file.cpp

This will bring up an interactive mode to stage only specific changes

Describing Commits

To make is easier to use git tools we have a specific format we expect from commit messages. It is:

Subject

Longer text describing the change.

The subject is a brief explanation of the change and may not exceed 60 characters in length. It should start with a component name, e.g. the name of a plugin the code belongs to. The subject line has a specific relevance. Many git tools (e.g. gitk) show only the first line in the default view and therefore this line should be short and concise. Then follows an empty line and then a longer text describing the change in more detail. If you think you cannot fit the change in your subject chance is that you should break up the commits in more smaller commits. The longer text can be omitted if the subject line is already sufficiently clear. In the longer message think that you need to explain the change to someone else. An example:

tabletop-objects: ignore unknown pragmas

Disable unknown pragmas warning to fix a problem with PCL 1.6

advanced (Re-)Organizing your Commits

With the many different features to stage, edit, or even recompose commits it can be confusing at first when and how to commit. Here are some things to consider:

  • Commit one semantically grouped change, e.g. a particular sub-feature, modification to a specific class and adaptations it requires in other classes.
  • Git supports bisecting, that is binary search in the range of commits to track down a bug. Therefore make your commits a useful sequence
  • Commits should explain the history of a file. Fixing a typo does not help, therefore if at all possible squash commits before pushing (see below)

Commit

After you staged the changes commit them (to your local repository!) using

git commit

Push the limit, push your branch

So far, you have only committed to your own branch in your own (local) clone of the repository. To share your development with others, you should push your branch to another repository. We have central repositories at git.fawkesrobotics.org that we use to share code with each other. The process of copying your changes to another repositories is called pushing. To push your local branch do:

git push name-of-remote-repo name-of-local-branch:name/of-remote-branch
            ^                   ^                    ^
            |                   |                    +- name of the branch in the remote repo
            |                   +- name of the branch in your local repo that you created earlier
            +- name of remote repository, usually "origin"

advanced Modifying Commit History

We have seen that staging is useful to compose commits such that they are useful. Sometimes during development you make changes only later (like adding documentation or fixing typos and small bugs) that ideally should have been in the original commit. Git can help with that. Note however, that you can do this only for commits that you have not pushed already to a remote repository or it will cause havoc, chaos, and confusion. The described process of modifying the history is called "interactive rebase". First use gitk or git log to determine the earliest commit for which you want to make modifications and note its hash, for now we assume it's 01ab23dc. Make sure your working copy is clean, i.e. there are no locally modified files (if there are any stash or commit them). Then do:

git rebase -i 01ab23dc^

This will bring up an editor with a list of the commits to modify. You can now reorder the commit by reordering the lines, squash commits, that is merging two commits into one (that is useful for integrating the documentation commit into the original commit), reword the commit message (e.g. typo in commit message or subject too long), or simply edit the commit.

Merging

Merging is the process of integrating a branch into another one. There are two main scenarios where a merge is required: merging a topic branch into the master branch, or merging many topic branches into a temporary merge branch. The former is primarily done by experienced developers after code has been reviewed. On the base repository this review is enforced. If you want your topic branch to be merged there ask Tim or Daniel for a merge.

During tournaments we will have a branch called "current", which we use to throw all changes and topic branches together. In that case, you will need to merge all your changes into this branch on the primary merge repository. You merge your changes with:

git merge your/topic-branch

This assumes that the branch in which to merge the topic branch is currently checked out, i.e. the branch named "current" is checked out.

Any news?

Get updates from the repo you cloned (the current branch) from by using

git ff --rebase

To just get changes from a remote, but not merge them, yet, do (replace origin with the remote name as required):

git fetch origin

Ready or not: git stash

You can put changes on hold for later, for example to cleanup the tree or to switch to a different topic branch to work on a different subject. To stash them away do

git stash
# Or alternatively with a useful description:
git stash save "unfinished code for ..."

You can list and view those stashes with

git stash list
git stash show

To retrieve a stash you have two options. The apply sub-command will retrieve the stash contents and apply them to the current state of the working copy. The pop sub-command will additionally drop the stash if it was successful in applying it. The drop command drops the stash without applying it.

git stash apply
git stash drop
git stash pop

The stashes are organized as a stack. Without a parameter, all stash commands operate on the most recent stash. The list sub-command gives the stash IDs.

Stuff to explain

Here or there? (git remote)

Learn what a remote is and see some convenience commands for the same. You can list existing remotes with

git remote

You can add a remote, e.g. for a machine you have a git repo checked out on and which you can access via ssh with

git remote add name-on-local-machine ssh://user@fully.qualified.domain.name/absolute/path/to/git/repo/checkout

Likewise, you can remove is with

git remote rm name-on-local-machine

To update the state of those remotes simply run

git remote update

Are you following me? (remote tracking branches)

If a branch is a remote tracking branch it has been associated to a branch in a remote repository and a git pull will fetch and merge changes from that tracked branch. To setup a remote tracking branch the following is the easiest way:

# First make sure you have all changes of the remote branch
git pull origin remote-branch
# Assuming that you do not have additional changes that can be pushed
# (verify with "git status" or "git st") do:
git push -u origin local-branch:remote-branch

Tool Support

There's a plethora of tools making life easier when working with git. Here's a brief collection

Git PS1 in bash

Most often it is important to know which branch you're currently on and also what the state of that branch is. However, calling git status everytime to see is a bit cumbersome. You can easily add the following lines to your .bashrc to include a condensed version of the above mentioned information in your bash prompt:

source /usr/share/git-core/contrib/completion/git-prompt.sh
export GIT_PS1_SHOWDIRTYSTATE=1
export GIT_PS1_SHOWSTASHSTATE=1
export GIT_PS1_SHOWUNTRACKEDFILES=1
#export GIT_PS1_SHOWUPSTREAM="git"
    
## prompt with git status
PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '

Further Information