Native Client‎ > ‎2: How Tos‎ > ‎

How to use git-svn with Native Client

Git is a distributed revision control system. Explaining git itself is beyond the scope of this document; instead the aim is to briefly explain the git-svn extension and how to use it with Native Client. Git-svn is a git extension that uses a local git repository backed by a central Subversion server. The commits in Subversion appear in the git repository as a remote branch, and you can fork your own branches off of svn HEAD, or any other commit in your repository. The git-svn extension fetches commits from svn and checks them into git, and when you are ready to commit to svn, it takes your git commits and checks them into the remote Subversion server. A tool called git-cl (part of the depot_tools) manages code review, try jobs, and the other tasks you would use gcl for in a Subversion environment.

Why Git?
Native Client uses Subversion as its centralized source control system. Why would we want to pile git on top of that? There are 2 reasons. First, in order to have multiple CLs in flight at once, using Subversion requires you have multiple checkouts of the source and all the deps, each of which is dedicated to a single set of changes. Conversely git's model uses a single directory and switches back and forth between different branches (or commits in a branch), modifying the files in the working directory. You might or might not think this is an advantage; that's largely a matter of taste. However the real benefit is that when you use git, you can commit to your local branches as often as you want, for whatever reason you want; to save your progress partway through a task, to try different approaches, or to facilitate easy switching between different CLs.

The setup described here is analogous to the preferred way to use git with Chromium, described at

Setup from scratch

If you are on Linux start by making sure you have the git-svn package:

sudo apt-get install git-svn

Create a directory to hold Native Client and all its deps (We'll call it $NACL_DIR) and cd into it.


[OPTIONAL] This svn command may help you avoid incomprehensible password prompts:

svn ls svn://
[HINT: On Linux systems, you can tell SVN not to use the GNOME keychain by setting "password-stores =" in your ~/.subversion/.config file.]

Setup your .gclient file, using git-style DEPS:

gclient config --git-deps
gclient sync -j16

You'll notice that all the DEPS checkouts tell you about big long hex numbers (git commit IDs) instead of short decimal numbers (svn revision numbers). This is the key difference from the old regime: all the DEPS checkouts are git checkouts, rather than svn checkouts. (You'll also notice that this first "gclient sync" run takes a while--it's getting the entire history of each DEPS repository, not just the current checkouts. Thereafter, the incremental updates are usually much faster than with svn.)

This gives you a setup very analogous to the all-svn setup you get from using plain gclient. Notably, your main native_client checkout is "managed" by gclient. This means that "gclient sync" will update your git checkout. (See below for how to get around this if you prefer to update native_client yourself).

If you don't need to commit, you are done. You can use a pure git checkout, and keep it up to date by running "gclient sync" from $NACL_DIR
If you do need to commit, you need to setup git-svn.

cd native_client
git svn init --prefix=origin/ -T trunk/src/native_client svn://
git config svn-remote.svn.fetch trunk/src/native_client:refs/remotes/origin/master
git svn fetch
git cl config   # just hit return at all the prompts

Now you have a native_client directory controlled by git-svn. The origin/master branch mirrors SVN trunk, and your local master branch tracks the master branch from the origin remote.
You should not make local commits to your master branch, or gclient may complain about not being able to fast-forward the merge (and then eat that complaint, not showing you the text until you hit "enter" on what looks like a hung update).

Your workflow might be like the following:

Update your master branch
cd native_client
git checkout master
gclient sync

The 'gclient sync' command will pull down the changes on origin/master and merge them into your local checked-out master branch.

Create a new branch for a new CL
  git checkout -b my_feature
(This is equivalent to git branch my_feature; git checkout my_feature)

Hack away, committing to git whenever you like

emacs $file
git commit -a (roughly equivalent to git add $file; git commit)
emacs $file
git commit -a
./scons $tests

I like to at least have a commit for every time I run a test (or try job) that takes sufficiently long that I won't sit and wait for it. That way when I come back and look at the results, I'm sure to have a snapshot of my code that was tested, even if I make new edits while the test is running. 

Send a try job
git try

Watch out! The command for sending try jobs is 'git try', not 'git cl try'.

Send out for code review
  git cl upload

This creates a new CL and associates it with the current branch (all commits on this branch are now assumed to be part of this CL). It then uploads it to the code review server. git cl upload also supports the -m option for the patch message, -r for reviewers, --cc for extra people to CC in the email, and a few others.

Wait for the review to come back. 
In the meantime, you can switch to another branch and work on something else
git checkout master
git checkout -b my_next_feature
The above sequence will base your next CL off of the same revision your first CL was based on. Alternatively you can base your new CL on your first one by omitting 'git checkout master'.

That mean reviewer wanted changes!
Edit based on results of review, then re-upload for more review

git checkout my_feature
emacs $file
git cl upload

Got LGTM! 

We are almost ready to commit to SVN. However, while you were waiting, there were other commits to nacl SVN, so we need to rebase my_feature against the new HEAD and make sure everything still works. First, update the local master:

git checkout master
gclient sync

Then, rebase your local changes off the new master:

git checkout my_feature
git rebase master
Test, try and update as needed:
./scons <tests>
git try
Everything works! Commit to SVN:
git cl dcommit

git cl dcommit squashes all the changes you made on your branch into a single diff and commits them to svn in a single commit.

Now you can update your local master again* (to include the change you just committed) and start a new branch for your next CL. 
git checkout master
gclient sync
git checkout -b my_even_newer_feature
(you may have to wait a few minutes for the change to propagate from SVN trunk to git master; see below)

If you have a CL already in flight (especially one that was branched off of your previous feature branch rather than master), you will probably want to rebase it against master now. After you have updated your master branch as above,

git checkout my_next_feature
git rebase master

Managing your own native_client checkout
You may not want gclient to mess with your main native_client checkout. You can prevent this, making gclient only keep your DEPS up to date (based on the content of the currently checked-out native_client/DEPS file). Then you are responsible for updating native_client itself. To get this behavior do the following in the $NACL_DIR directory (i.e. the directory above native_client)
gclient config --git-deps --unmanaged
Or equivalently, edit $NACL_DIR/.gclient and change 
   "managed"     : True,
   "managed"     : False,

In the above workflow, everywhere it says 'gclient sync', you can do 'git pull' to update your native_client directory, followed by 'gclient sync' to update the DEPS based on the native_client/DEPS file you just checked out.

Git mirroring and DEPS

One final note about the mirroring: how this works is that the infrastructure folks have a cron job that runs every few minutes (I think it may be every 3 minutes) to update the various git mirrors from the svn repositories.  If the DEPS file changes, then it regenerates .DEPS.git and does an automatic svn commit of the new .DEPS.git file.  (A git-based gclient checkout looks at .DEPS.git and ignores DEPS.)  So when you do a commit, you may have to wait a few minutes before you can see it in a git update.
Of special note is that when you update DEPS locally, gclient ignores it. It's not really practical to update .DEPS.git manually, so you just have to commit DEPS to svn and wait for the robot before "gclient sync" will take notice.  Since trybots still use old-fashioned svn checkouts, you can always do a try run with a DEPS change.

Other notes
  1. Unlike 'gcl upload', git 'cl upload' does not send out an email by default when it uploads. Use the --send-mail option to get this behavior.
  2. gitk is a nice tool to visualize the git tree. Use gitk --all to see all the branches including the remotes.