Skip to content

Tracking Funambol C++ Client Library with Git

I have imported all source code of the Funambol C++ client library (now known as CPP SDK) into one git repository. It contains both the old and the new repository that Funambol started to use two weeks ago. All tags and branchs are available.

I’ll spare you the gory details of the conversion. It involved creating a temporary SVN hierarchy (to speed up scanning of the huge core repository), manually converting SVN branches to git tags, removing artifacts, converting the new SVN, then splicing the two trunks together with git-filter-branch.


Having the complete history of the project in one repository is mostly relevant for code archeology. The git repository goes back as far as the core Funambol Subversion repository. Only code from before a big directory layout change beginning 2006 is missing: at that time CVS was still used and because CVS didn’t support file movement well enough, the new structure was created from scratch without the older history.

But my main motivation wasn’t archeology; I wanted to make my life as an external user and contributor to the library easier:

  • I’m going to maintain a release branch for SyncEvolution in that repository and tag the source that I use for each release there. Currently I’m using r_v70ga_syncevolution, based on r_v70ga.
  • Because git is a distributed version control system, external contributors to the Funambol project without SVN commit rights can clone the git repository, develop their enhancement locally (including committing it to their repository clone), then submit a clean patch series for inclusion in SVN.

Using the Git Repository

The first step is creating a local copy of it:

$ git clone git://
Initialized empty Git repository in funambol-cpp-client-api/.git/
remote: Counting objects: 18554, done.
remote: Compressing objects: 100% (5159/5159), done.
remote: Total 18554 (delta 13347), reused 18291 (delta 13090)
Receiving objects: 100% (18554/18554), 6.18 MiB | 513 KiB/s, done.
Resolving deltas: 100% (13347/13347), done.
$ cd funambol-cpp-client-api

This creates a complete copy, including all branches and tags:

funambol-cpp-client-api$ git branch -r
funambol-cpp-client-api$ git tag -l

Cloning automatically checks out the local master branch. Note that there is another “master” branch: “origin/master” is identical with the “master” branch in funambol-cpp-client-api, which in turn is identical with the trunk in the upstream Subversion.

The local master branch is where modifications can be committed. I’m not going into details of how this is done; the git tutorial does that well enough. Be sure to read about “git rebase –interactive”, “git stash”, “git add –interactive”.

I’m going to keep the funambol-cpp-client-api up-to-date once a day. To get the latest changes:

funambol-cpp-client-api$ git branch
* master
funambol-cpp-client-api$ git pull
remote: Counting objects: 15, done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 9 (delta 6), reused 1 (delta 0)
Unpacking objects: 100% (9/9), done.
From git://
5af9131..b2c2c31 master -> origin/master
5af9131..b2c2c31 trunk -> origin/trunk
Updating 5af9131..b2c2c31
Fast forward
.patches/.cvsignore | 2 --
src/cpp/posix/base/autotoolsadapter.cpp | 16 ++++++++++++++++
2 files changed, 16 insertions(+), 2 deletions(-)
delete mode 100644 .patches/.cvsignore
delete mode 100644 .patches/series

As part of cloning the local master branch was automatically set up so that it tracks the origin/master branch. The second command gets all changes since the last clone or pull and merges them into the local master branch.

Interacting with Subversion

When cloning as described above one can work with git, but not with Subversion. For that “git svn” needs to be configured and initialized first:

funambol-cpp-client-api$ git svn init --user guest --trunk trunk/cpp-sdk
funambol-cpp-client-api$ trunk=`git show-ref --hash remotes/origin/trunk` && echo $trunk >.git/refs/remotes/trunk
funambol-cpp-client-api$ cat .git/config
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[remote "origin"]
url = git://
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
[svn-remote "svn"]
url =
fetch = trunk/cpp-sdk:refs/remotes/trunk
funambol-cpp-client-api$ git svn fetch
Rebuilding .git/svn/trunk/.rev_map.3966be30-755a-0410-815e-dd79f492ac3c ...
r21 = dae47f4a7449b6091e01e7a6c205365b69b8bf5d
r133 = b2c2c317dc98874ff57245308c640841ae81fe15
Done rebuilding .git/svn/trunk/.rev_map.3966be30-755a-0410-815e-dd79f492ac3c

The trick here is to create a branch which is identical with the existing one and has the name as configured by “git svn init”. The last step then is a lot faster because git only has to recreate some meta data for revisions that already exist in the git repository. With a configuration as described above “git svn fetch” ensures that trunk is up-to-date. Fetching branches and tags is possible with additional configuration entries, but usually slower, and therefore not shown here.

“git pull” automatically merges. After “git svn fetch” this has to be done manually, otherwise the local master branch wouldn’t get updated. In this case no new revisions were pulled into remotes/trunk, so nothing needs to be merged:

funambol-cpp-client-api$ git merge remotes/trunk
Already up-to-date.

Note that conflicts are avoided because “svn fetch” and “svn pull” add new revisions to different branches. Just be careful with merging into your local master branch.

When the local master differs from Subversion, then “svn dcommit” can be used to commit these changes to Subversion. A different username with write permissions has to be specified because “guest” has no write permissions.

Alternatively, “git format-patch remotes/trunk” (or origin/trunk, depending on what master was merged with) prepares a set of patches that can be mailed with “git send-email”.

Pushing to Github

This section is only for my own reference. The git repository that I created with “svn fetch” from the Subversion repository has the following branches and a corresponding push config:

$ git branch -r
$ cat .git/config
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
# [svn-remote "svn-old"]
# url =
# fetch = trunk/funambol/client-api/native:refs/remotes/funambol/client-api/native/trunk
# branches = temp/branches/*:refs/remotes/funambol/client-api/native/branches/*
# tags = temp/tags/*:refs/remotes/funambol/client-api/native/tags/*
[svn-remote "svn"]
url =
fetch = trunk/cpp-sdk:refs/remotes/funambol/client-api/native/trunk
branches = branches/cpp-sdk/*:refs/remotes/funambol/client-api/native/branches/*
tags = tags/cpp-sdk/*:refs/remotes/funambol/client-api/native/tags/*
[remote "github"]
url =
push = +refs/remotes/funambol/client-api/native/branches/*:refs/heads/*;
push = +refs/tags/*:refs/tags/*;
push = +refs/heads/r_v70ga_syncevolution:refs/heads/r_v70ga_syncevolution;
push = +refs/remotes/funambol/client-api/native/trunk:refs/heads/trunk;
push = +refs/remotes/funambol/client-api/native/trunk:refs/heads/master;

I have to be extra careful to not use “svn dcommit” on branches which get pushed into github: “dcommit” rewrites these branches, which then will cause problems for users who pulled those branches from github and based their own work on it.

Post a Comment

Your email is never published nor shared. Required fields are marked *