Tutorial: Managing iPhone Third Party Library Dependencies Using Git+Braid
Git is a distributed version control system (DVCS) that has been gaining adoption in both the Ruby and iPhone development communities. DVCS’s allow a developer to work with version control without connecting to a remote, central repository. All commits, branches, tags, and query operations occur locally. Because these operations are local, they are fast. To share your work, DVCS’s provide operations to push to or pull changes from a remote repository.
I am currently finishing up a contract iPhone project that makes use of several open source libraries. Since the final project is being delivered to the client in source code form, I needed an easy checkout and build experience for the client. I opted to include copies of the third party projects in a Vendor directory in my project’s source tree.
I like to keep the third party code in a separate group (Vendor) in my XCode project. In that directory I further break up each project into its own group. Some libraries are consumed by building the source files with your project. For those projects, I simply add the files using the Add -> Existing Files… menu option. Other libraries are setup to create a static library, which is built using a dependent XCode project. For those I follow the build instructions to add the dependent project and its library, making sure to use paths relative to the project root.
Git includes a feature called submodules which could be used to manage this code. However, submodules are not cloned when the root project is cloned. Instead, the user must init and update the submodules after the initial clone of the root repsoitory. Updating a submodule is also tricky. The user making the change has to carefully make changes in both the submodule and parent project, making sure to push changes in the submodule before pushing changes in the parent project. Consuming those changes is not much easier. That user needs to run
git submodule update after pulling new changes.
Braid is Ruby gem that provides a command line tool to manage vendored project directories in a Git repository. Braid supports pulling from upstream projects hosted using git or subversion. By using braid, it is easy to check in a vendor branch of an upstream project using the
braid add command. When the project changes upstream, you can easily pull those changes using
braid update. You can even make changes to the vendored code and see those changes using
Braid manages copies of the code in the host project. This means that other developers do not need to interact with braid to get the latest vendored code, it is included in the changes they get when using git alone. You only need braid if you want to get updates from the upstream project or see what local changes have been committed.
Installing braid is quite simple since Ruby and Rubygems are included with OS X. Simply run the following command in Terminal.
$ sudo gem install braid
Once that command finishes, you will now have new commmand “braid” in your path. Braid includes simple help messages which can be printed using “braid help”
$ braid help NAME braid SYNOPSIS braid (add|update|remove|setup|diff|version) [options]+ DESCRIPTION braid is a simple tool to help track git or svn repositories inside a git repository. Run 'braid help commandname' for more details. All operations will be executed in the braid/track branch. You can then merge back or cherry-pick changes. PARAMETERS --help, -h
Adding a new library
Adding a library to your project is simple using the
braid add command. Your working directory needs to be clean in order to add or update a braid managed directory. As an example, let’s add the Three20 project at Vendor/three20.
$ braid add git://github.com/joehewitt/three20.git Vendor/three20 Braid: Adding git mirror of 'git://github.com/joehewitt/three20.git'. Braid: Setup: Creating remote for 'Vendor/three20'. Braid: Added mirror 'Vendor/three20' at '3274e6d'
If we check the commit that braid created, we will see that braid committed all the source for the project. It also created a .braids file, which it uses to keep track of all the braid managed directories.
$ cat .braids --- Vendor/three20: squashed: true url: git://github.com/joehewitt/three20.git type: git revision: 3274e6d0515bbbcbdbabe8ef2a411ae1efded092 branch: master remote: braid/Vendor/three20
The squashed value controls whether braid adds a single commit or all the history from the vendored project. The default is true since most users do not want the vendored project history included in their project’s history.
Updating an existing library
Once a project is installed with braid, future upstream changes can easily be merged in using the
braid update command. The update command grabs the latest changes from the upstream project and commits them to your project as a single commit.
braid update will update all braid managed directories to their latest versions. Running
braid update subdirectory will only update a single directory to the latest versions.
If you have committed any changes in a braid managed directory, braid will attempt to the merge the upstream changes with your version of the code. If there is a conflict, you will need to resolve the conflict and commit the pending version.
Drawbacks of using braid
Braid works really well for pulling down changes from upstream. But what if you want to submit a patch upstream? Braid includes a command,
braid diff, which will show the differences between your version of the project and the upstream version. Unfortunately, there is no way to push local commits to the upstream project, nor format patches from each local commit. If you find you need to push changes back often, a git submodule may be a better fit.