Git vs Svn

From GnuCash
Jump to: navigation, search

GnuCash has long been using svn as the primary version control system, but finished the process of migrating to git at the begin of 2014.

Superficially git and svn share the same goal: make writing code more manageable by slicing the changes up in smaller parts (commits) and keep a history of these changes. This is very oversimplified, but sufficient for this page.

When you zoom in some more, you will notice that git and svn use some very different techniques to do so. This page will list some of these differences. The focus will be on differences that matter to GnuCash.


I know from experience it takes some time to fully grasp git branches when coming from svn. So I'll just add a simplified, explicit example on git branches for others still deeply in the svn mindset (best viewed with a fixed-width font).

Quick reminder of how it works in svn: a branch only exists by convention. A directory in the repository is created that is agreed upon to hold "branches", next one master directory (usually called "trunk") which keeps the main branch. Whenever someone wishes to create a branch, a copy of the trunk branch is made into the branch directory. So a branch in svn is just a commit like any other, but in a separate directory commonly agreed upon.

In git this is totally different. For starters the primary branch is usually called "master" rather than "trunk".

Suppose you have this tree of commits in your local repository

 A - B    (master)
     C    (feature-x)

A is the parent commit, B is a descendant of this commit on the master branch. C is a descendant of A on the feature-x branch.

Two things are very important to point out here: - In git a commit can have multiple parents or children. This is not so in svn where each commit has exactly one parent on zero or one children. That's one of the main reasons svn has to resort to a separate directory to define branches. - This is a good moment to explain what branch means in git. The master 'branch' is some kind of label that is currently attached to commit B. The feature-x 'branch' is equally some kind of label attached to commit C

When you merge feature-x into master in git, you will get this tree:

 A - B - D (master)
   \   /
     C     (feature-x)

The 'master branch' is now a label attached to commit D. All the ancestors of commit D are considered part of the master branch. But note already how the branch itself is just a label moving from commit to commit as changes are committed.

And observe again how commits can have multiple parents and children.

Now this all happened in your local git repository. The upstream git repository doesn't know about any of this yet. At some point you want to push your work upstream. Let's assume for simplicity that commit A and B were already in the upstream repo as well. This means the upstream repo looks like this:

 A - B    (master)

'master' is a public branch. Let's push our local master changes upstream. After a successful push, upstream will look like this:

 A - B - D (master)
   \   /

You might say that's exactly like the local repo, but there's one slight, yet important difference: the *branch* feature-x wasn't pushed upstream (note that I didn't put that name next to the C commit).

The *commits* that were on that branch and are ancestors of commit D are (commit C in our example). These are required to regenerate commit D, so they are pushed upstream as well.

However the *branch* itself is just a name tied to a commit. This name can move to another commit or even be removed without affecting the commits themselves in any way. So unless you explicitly push that branch (name) to the upstream repository it won't appear in there. That allows to have many local branches and yet keep a manageable list of branches in the public repository.


This section shows only a subset of possible pitfalls.

In git this is a branch related command, to get a repo use git clone.
In git this is a local command, use git push to update the project repo.

(Feel free to complete this list.)