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 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 flavors: “
next” (these are in fact the names of the Git branches). While
master” is the main, most stable branch that contains the latest
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:
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:
Once you have forked the repository, open a terminal, go to your favorite working folder and type:
$ git clone https://github.com/YOUR_USERNAME/gnss-sdr
YOUR_USERNAME is your GitHub user name. Now, you have a local copy of your fork of the GNSS-SDR repository into a
gnss-sdr. Change to that directory:
$ cd gnss-sdr
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 "firstname.lastname@example.org"
The email you specify should be the same one found in your GitHub email settings.
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
$ 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:
Fetch the branches and their respective commits from the upstream repository. Commits to
nextwill be stored in a local branch,
$ git fetch upstream
Check out your fork’s local
$ git checkout next
Merge the changes from
upstream/nextinto your local
nextbranch. This brings your fork’s
nextbranch 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:
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
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
pull on that branch,
it automatically pushes and pulls to the remote branch that it is
Checking out a local branch from a remote branch automatically creates a
tracking branch. If you are on a tracking branch and type
Git automatically knows which server and branch to push to. Also,
git pull while on one of these branches fetches all the remote
references and then automatically merges in the corresponding remote
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
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
$ git checkout --track -b my_feature origin/next
Now you have a branch
my_feature which is tracking
there is an update in the upstream repository, and do a
pull, you will
see it updating both
next, and also
Important: Never, ever commit (write) to a local tracking branch. Always use them as a base to branch off!
Good coding practices
Keep your changes referred to the latest commit of
upstream/nextbranch. 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_featurebranch patches on top of the current
next branchin the
upstreamremote. 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 you 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, the 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.
Summary: GNSS-SDR’s Git branching model
GNSS-SDR’s reference Git repository, also referred to as upstream, is hosted online by GitHub at https://github.com/gnss-sdr/gnss-sdr. That repository holds two development branches with infinite lifetime,
- We consider
upstream/masterto be the branch where the source code of HEAD (i.e., the result of the latest change) reflects the source code of the latest stable release, plus maybe some hot fixes (e.g., building issues).
- We consider
upstream/nextto be the main branch where the source code of HEAD always reflects a state with the latest delivered development changes ready for the next release. Some would call this the “integration branch”, or the “current baseline” for new developments. If you are building the software from sources, or you want to start a new improvement, this is the branch to start with.
When the source code in the
next branch reaches a stable point and is ready to be released, all of the changes are merged back into
master and then tagged with a release number. List of GNSS-SDR releases here.
Everyone is free to clone the upstream repository (i.e., to download an exact copy of the whole source code repository content, including all development branches and associated history) and to create an infinite number of new “feature branches”, such as
adding-a-new-algorithm-for-Galileo-E1b-signal-acquisition, in which specific improvements can be implemented in a nonlinear and distributed manner. The relation of Git to the PDCA (Plan-Do-Check-Act) cycles is described in the Testing the software receiver tutorial.
Once a feature branch is ready for production, it can be merged back into
upstream/next and eventually included into the next
upstream/master release. The acceptance of new contributions to upstream is always supervised and validated by the Developer Team, and every single change in the whole process (including the authorship and a time tag) is registered in the repository for future reference.
Next figure shows the Git branching model followed in GNSS-SDR’s upstream repository:
A graphical representation of GNSS-SDR’s Git branching model, where vertical lines represent branches, colored dots represent commits, from older (top) to newer (bottom), and arrows across branches represent merges. Feature branches can be either in the upstream repository or in any other fork. This model is a modified version from V. Driessen.