Using Git

10 minute read

One of the greatest advantages of open source is the ability to view, modify and share the source code. This means diagnosing and fixing problems, or adding new features or documentation. In order to contribute these fixes/improvements to the project’s developers, you need to send them back your changes in an adequate manner. This can be done by creating and sending a patch file, or by doing a pull request. Scroll down for more details about how to do that.

For the version control system we use Git, a free and open source application that automates the process of keeping an annotated history of the project, allowing reversion of code changes, easy branching and merging, sharing and change tracking. The GNSS-SDR’s GitHub repository hosts all of the data files needed for version control, being the central “truth” repository resource of GNSS-SDR’s source code. Such reference repository is usually referred to as upstream.

A repository is simply a place where the history of the work is stored. The distributed nature of Git allows users to clone this repository, that is, to obtain an exact replica of the original repository at their own local hard drive. Then, users can build and use the software, modify the source code, share their work, and contribute back to GNSS-SDR. Git is a fantastic but complex source code management system - it has a steep learning curve, but it worth it. A good reference is the freely available Git Pro book, but there are plenty of good Git tutorials out there. If you have never used it, start from the Git Basics or this interactive learning game that takes just 15 minutes.

Branching in Git is one of its many great features. A branch represents an independent line of development or, more accurately, a directed acyclic graph of development. If you have used other version control systems, it is probably helpful to forget most of what you think about branches - in fact, it may be more helpful to think of them practically as contexts since that is how you will most often be using them. When you checkout different branches, you change contexts that you are working in, and you can quickly context-switch back and forth between several different branches. Every time you switch to a new branch, you will actually see different files in your local folder.

In GNSS-SDR, the source code comes in two flavours: “master” and “next” (these are in fact the names of the Git branches). While “master” is the main, most stable branch that contains the latest release, “next” is where all the development is happening, the most bleeding edge code out there. Once you have cloned the main repository, you can easily switch between these two branches (or any other created by you or fetched from other users) by going to your git-cloned repository and issuing the git checkout command with the name of the desired branch name, like this:

$ git checkout master   # now you are in the master branch
$ git checkout next     # now you are in the next branch

If you do not know in which branch you are, pay attention to the first line of this command’s output:

$ git status

How to work with the source code and submit your changes

If you still not have one, please sign up in GitHub and create your personal account (it’s free).

The first thing you need to do in order to submit your changes to the GNSS-SDR repository is to fork from it. This can be done either by following this link, by clicking in the following button:

Fork GNSS-SDR from GitHub

or by browsing to https://github.com/gnss-sdr/gnss-sdr and clicking in the Fork button at the upper-right corner of the screen: Fork button

Once you have forked the repository, open a terminal and (if you have not done so yet) configure Git with some basic information, such as your identity:

$ git config user.name "Your Name"
$ git config user.email "your@email.abc"

The email you specify should be the same one found in your GitHub email settings.

Then, go to your favourite working folder and type:

$ git clone https://github.com/YOUR_USERNAME/gnss-sdr

Now, you have a local copy of your fork of the GNSS-SDR repository into a directory called gnss-sdr.

When you fork a project in order to propose changes to the original repository, you can configure Git to pull changes from the original, or upstream, repository into the local clone of your fork. If you type git remote -v in a terminal and press Enter, you will see the current configured remote repository for your fork:

$ git remote -v
origin  https://github.com/YOUR_USERNAME/gnss-sdr.git (fetch)
origin  https://github.com/YOUR_USERNAME/gnss-sdr.git (push)

We can add the original, upstream repository like this:

$ git remote add upstream https://github.com/gnss-sdr/gnss-sdr.git

To verify the new upstream repository you have specified for your fork, type git remote -v again. You should see the URL for your fork as origin, and the URL for the original repository as upstream:

$ git remote -v
origin    https://github.com/YOUR_USERNAME/gnss-sdr.git (fetch)
origin    https://github.com/YOUR_USERNAME/gnss-sdr.git (push)
upstream  https://github.com/gnss-sdr/gnss-sdr.git (fetch)
upstream  https://github.com/gnss-sdr/gnss-sdr.git (push)

Now, you can keep your fork synced with the upstream repository with a few Git commands:

  1. Fetch the branches and their respective commits from the upstream repository. Commits to next will be stored in a local branch, upstream/next.

     $ git fetch upstream
    
  2. Check out your fork’s local next branch:

    $ git checkout next
    
  3. Merge the changes from upstream/next into your local next branch. This brings your fork’s next branch into sync with the upstream repository, without losing your local changes:

    $ git merge upstream/next
    

Now that you are up to date, go to the next branch and create a new branch off from it:

$ git checkout next
$ git checkout -b my_feature

Whenever you want to work on something, create a branch for it. Then, do your changes, stage modified and new files and do commits:

... (change files, compile, test) ...
$ git add file1.cc file1.h ...    # This is called file staging
$ git commit -m "adding stuff"    # Records staged files to the repository

As you edit files, Git sees them as modified, because you have changed them since your last commit. You stage these modified files and then commit all your staged changes, and the cycle repeats. The command git add puts the files into a “staging area”, an index where you get to determine what changes get shipped away in the next commit, that is, what files are going to be recorded to the repository. If you need more details about this process, check this Git tutorial on how to record changes in your repository. Next figure summarizes this workflow:

Git repository

Once you are done with your changes in your local repository, it’s time to push that changes to your Github repository:

$ git push origin my_feature

Then, go onto the GitHub site, visit your repository, switch to your my_feature branch, and click the Pull Request button that will do all the work for you.

Once a pull request is sent, interested parties can review the set of changes, discuss potential modifications, and even push follow-up commits if necessary.

How to use someone else’s branch

As a collaboration tool, Git can set up what is known as a remote to connect to other people’s repositories. Those repositories, in the Git distributed system, do not need to be on a single server, but can be anywhere. You can have several of them, each of which generally is either read-only or read/write for you. Collaborating with others involves managing these remote repositories and pushing and pulling data to and from them when you need to share work.

Now, someone might be doing something interesting you care about. Say this is Carles Fernández and you want to track his work:

$ git remote add cf git://github.com/carlesfernandez/gnss-sdr.git
$ git fetch cf    # This downloads all the content from Carles' repo.
$ git branch -r   # Lists remote branches

Then you can see all your available remote branches (including those of the remote repository you just added) and their name. You can then checkout a specific remote branch by typing:

$ git checkout cf/very_cool_feature

When checking out a remote branch, you can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by performing another checkout. If you want to create a new branch to retain commits you create, you may do so (now or later) by using -b with the checkout command again:

$  git checkout -b new_branch_name

Setting up tracking branches

When you create a new local branch off from next, it diverges from your local next at that point in time:

$ git checkout next
$ git checkout --track -b my_feature

If you want to keep that new branch updated with the new changes of next, you either need to pull changes through next and then rebase your branch, or remember where your merge point was.

What we have here is my_feature tracking next, and not origin/next; in other words, it is a local branch tracking another local branch. There are times when this is useful, but what if you want to track the remote one directly instead of having to pull through a local copy? The solution is to have a tracking branch.

In Git terminology, a tracking branch is a local branch that is connected to a remote branch. When you push and pull on that branch, it automatically pushes and pulls to the remote branch that it is connected with.

Checking out a local branch from a remote branch automatically creates a tracking branch. If you are on a tracking branch and type git push, Git automatically knows which server and branch to push to. Also, running git pull while on one of these branches fetches all the remote references and then automatically merges in the corresponding remote branch.

When you clone a repository, it automatically creates a master branch that tracks origin/master. That is why git push and git pull work out of the box with no other arguments: if you are on a tracking branch and type git push, Git automatically knows which server and branch to push to. However, you can set up other tracking branches if you wish - ones that do not track branches on origin and do not track the master branch.

Say you want to work off the next branch. First, you need a copy of that in your local repository - a tracking branch:

$ git fetch --all    # This downloads all available content
$ git branch -r      # Lists remote branches

Then, create a local tracking branch called my_feature from the remote branch called origin/next:

$ git checkout --track -b my_feature origin/next

Now you have a branch my_feature which is tracking origin/next. When there is an update in the upstream repository, and do a pull, you will see it updating both next, and also my_feature.

Important: Never, ever commit (write) to a local tracking branch. Always use them as a base to branch off!

Good coding practices

  • Keep you changes referred to the latest commit of upstream/next branch. In Git terminology, this is called rebasing a branch. When rebasing, you need to specify which branch you want to rebase onto:

    $ git checkout my_feature
    $ git fetch upstream
    $ git rebase upstream/next
    

    This simply reshuffles your my_feature branch patches on top of the current next branch in the upstream remote. Rebasing is a good idea while your feature branch is still in development. Check out Scott Chacon’s Git Pro book section about rebasing to find out more details about this concept.

  • Use an integrated development environment (IDE) with Git support. Most modern C++ IDEs have nice interfaces for using Git. An open source, well-known option is Eclipse, and we love using EGit, an Eclipse Team provider for Git.

  • Before creating the patch file, please be sure that after your modifications everything compiles and runs without problems, and clean up your work. Remove any junk lines or comments you may have left while fixing the code, and make sure your follow the recommended coding style (indentation, white spaces, naming conventions and so on). This will make other developers’ life easier.

  • Tell us about your branch! If you have significant changes, you can simply email us (again, best way is by mailing list, so other users can get to know about you work) and tell us about your code. All we need is the link to your remote branch.

Leave a Comment