Slug brett koonce header

Git at Scale: Managing Swift/Obj-C Code & Coders

Whether you’re working on a project alone or with thousands of other contributors, using a consistent commit pattern will make your life simpler. Following a few simple rules in the beginning can save you hours of troubleshooting down the road. We will walk through how to make a good pull request, then look at how we can rebase and improve commits to make maintenance easier. Finally, we will explore some common (and obscure) issues that come up with Xcode + git + Swift/Objective-C codebases and discuss strategies to deal with them.


Introduction

Hello, I’m Brett Koonce. I’m an iPhone/Android programmer. I used to do a lot of web dev, and I was a Unix system man for a long time.

Git at scale

Git at scale. By scale, basically, I don’t mean tens of thousands of commits. I think scale with Git is just simply how you use Git. If you use it consistently for one commit, two commits, a thousand commits, if you can consistently follow a certain pattern then you can work with yourself, and other people and the whole world.

But ultimately Git is just a tool and it’s how you use it, and not necessarily how you interact with other people.

Homebrew

The first question is, “Who here uses Homebrew?”, “Who uses Git?”, “Who has made a contribution to an open source project?” The purpose of this talk is to hope to commit some of you, if your hand’s down, into becoming somebody with your hand up.

We’re gonna go roughly through how to make a pull request and look at what a maintainer does on the other end, and into the nitty-gritty of how Xcode and Git work together. And finally, I’ll talk about my broader philosophies on project and release management.

Version Control

Why do we use version control and what is the purpose? Version control is like your project history and you’re saving progress every 10 minutes, so if your computer crashes, you can get back to where you were.

But if you step back a little bit, why do we even use Objective-C versus Swift? What is the purpose of different programming languages? I think it’s to allow you to handle complexity, to deal with bigger, better concepts with one programming language to scale, and to deal with more and more complicated ideas, basically.

So, that’s kind of the idea of functional programming.

Version Control and Collaboration

And, so, in the same way I think that the reason why version control is so important for us as coders is that it allows you to concentrate on a subset of changes.

If somebody just hands you a project file with 10,000 lines of code and says, “Oh, I had fixed some stuff,” you would have no clue what happened, right? But if you have a version control you can say only one thing happened in between these two versions. And I’m gonna set this change into the project.

So, I think it’s ultimately a tool to allow you to reason about changes and allows you to collaborate with other people because other people can then figure out what you were doing. To continue that thread,at some point in the future, you’re probably gonna have to deal with your own code. Git ultimately is a tool to allow you to collaborate with your future self.

Pull Request

Last month Linus Torvalds wrote a little wiki readme on what he thought a good pull request looks like. Linus is the guy who wrote Git. He had a weekend where he was basically bored and he’d already invented the Linux kernel, and his project maintainers were arguing about licenses for the umpteenth time.

Get more development news like this

And so he went and wrote his own version control system with Blackjack. It’s only 400 lines of code, and then he committed the actual code using his tool. It’s very meta. And put it out on the internet with a proper documentation, and a webpage.

Basically he wants you to have a good one-line description that fits in one line. And a bodied description that describes very clearly what the heck you did. Finally, you should link to whatever bugs you did along the way, and then push it up to the internet. And then ideally you should also try to use active language, for example, “fix this,” “add this,” or “make this”.

He gets very wired about everything being within that 80-character space, but for me I don’t really care if your descriptions are a little messy, as long as you actually cleanly described what it is that you did.

What I like is just a nice, single, clean tree with lots of release tags, common key words, little-bitty commits, lots of little commits. I also would love it if you get from point A to point B even if it takes 50 commits to get there, but I can figure out what you were doing along the way. It’s almost always trivial whenever you’re doing Git to squash things, to make them shorter, but it’s very hard to unsquash them once you’ve done that.

I’d rather have more history than less. Finally, I’d like to do very rapid merges and very rapid releases. On Homebrew, basically, every time we push to the master repo, that’s a new release of the app. Every day we push 100 times.

For example, one time somebody pushed some code that broke the production configuration the next time you upgraded it. So, during a five-minute window, if you had upgraded to this app, then the next time that you upgraded your Homebrew, things would catch on fire.

My basic configuration is just Git. I just use the command line Git. I use textmate and I’m using GitX, which is a visualization tool and just GitHub.

Demo Pull Request On Homebrew repo

So, first, I’d just like to walk through a basic demo of how to make a pull request to the Homebrew repo.

Put the Homebrew repo on your computers and do user/local, if you wanted to install it, you’d just need to go over here. Brew, you can get this, then you need the Xcode install.

Then, on your computer, user/local, we would go to the Homebrew directory, go to the cellar. Basically, we just have a very long, straight tree.

If you go way back into the Git history of Homebrew, I think at one point in time, we’ve had two actual merged commits, out of approximately 80,000 commits, or so.

This is Baguette, which is just one of the packages in there. Tool from the Library of Congress, latest releases, 11 hours ago. Apparently they submitted a new version. Let’s update it and we’ll save it. Git’s just showing us basically we changed the version number by one.

We’ll attempt to install it. It’ll pull it off the local cache. It’ll notice that the local file doesn’t match the pre-saved SHA1 of the file because we download the new one, and it doesn’t match what the old one is.

So, we’ll just come here and update this to the new string and now we’ve updated both the URL and the actual SHA1. And we’ll pull the Baguette down. Now we’ve upgraded this package. That’s step one. And this is the master repo for Homebrew.

In order to make changes,you won’t have read and write access to this commit. So, basically, you’ll need to make your own fork. Homebrew is the one of the projects with the most forks, so a bunch of weird stuff happens there. Right here we have the master Homebrew repo. I just made a clone, a fork of the whole repo that’s in my repository. I have read-write access to this. Then on my machine is the local repository, which is the one we just edited to set all this up.

This origin right here is actually pointing to the Homebrew origin repository, but we don’t have write access there. Let’s add a remote. We can just use whatever name we want. Now we’ve added another, we’ve added our repo there.

Let’s make a branch next. Now we can see that we have our little branch with our little commit on top of it, and there’s the origin branches. So, now we would just, we push our little branch up.

Here’s our branch up on GitHub. Hit the old compare-and-pull request. Here’s the rules and guidelines, and we’ve now made a pull request up to the Homebrew core repository that’s right here. So, what’ll happen right now is a bunch of Jenkins servers will run it on a few different versions of OS X.

We support all the major versions, so it has to be checked on each one. Presumably once that’s there, somebody else can grab it and pull it in to the main repo. That’s the basic workflow for pushing a commit and a branch up to GitHub.

You’ve written the code, now what? You’re going to forget. You’re going to forget why the bug you’re dealing with was a big deal and what hardware you were testing it on; what tools that you were using at the same other time and what else was going on.

Coding a solution to a problem is only the first part; the second part is making it future-proof. A little bit of time spent now cleaning up your commit and making it sexier, or cleaner, will save you a bunch of time down the road.

Maintenance

What does a maintainer do? Your job is to take all these contributions from different people and sort of bash ‘em into a coherent mess, or make ‘em all semi-similar. To do that you need to act like a gatekeeper for the project and the project releases. You need to grow the project and bring in more contributors. One of the statistics that I was very proud of whenever we worked on Homebrew is that we would have every year usually the largest number of new contributors. Last year I believe it was 800 people.

Towards that end, a lot of what I do as a maintainer is actually not very sexy stuff. I move commits around, reword descriptions, fix spelling mistakes, white-space issues and I clean up trees whenever people did something wrong, and I merge and split branches all day long.

The best way to get good with Git rebase is to actually do it. The problem is that basically everybody wants to start doing rebases. My advice to you simply is to make a dummy repo, and then play around with commits there. Just do some simple stuff.

We have a simple commit right here, simple repo: one, two, three. What you can do is some silly tricks to sort of move stuff around and play games with it. I’ve made two branches in the same commit. If you wanna get rid of a commit, we can just drop it out. That’s a way we delete a commit.

Basically there’s six simple commands you can do here: you can reword commits, squash–which is just to combine two commits. Here I’ve taken the first two commits and combine them together. I’ve reworded the third commit.

I would advise you just to make about 30 or 40 different commits, break things apart into different branches, rebase them on top of each other.

The best way to learn this is to do it yourself.

Xcode

Whenever I push out a new version of my app, I’ll hit the release button on iTunes, and then I’ll immediately start prepping for the next version.

At the very least we’ll prepare a point update. Say, 2.01. I’ll bump an API. Internally we use an API number to communicate with your server that’ll save you a lot of time down the road trying to figure out which versions of your app are communicating with which versions of your server.

I like to tag all my commits, that way if you get a bug that shows up in versions 2.03, you can go look at the tree, and hopefully it’ll be in the 2.03 section of things.

The way I’ve been doing releases lately is I have a hard-coded release branch; and, I rebase that branch on top of the master itself. This way if other people break the release configuration settings, I always know that I have a safe version in my copy.

Xcode + git

Who do you trust, Xcode or Git? Having done this for a while, I don’t trust Xcode at all for pretty much anything anymore. With Xcode, Apple will change the internals of stuff. You’ll open old project files. One programmer on your team will decide to upgrade their Xcode to the latest version, they’ll open up the file. It’ll do a couple of little minor magical tweaks,and then you’ll get elsewhere with your things.

Xcode is not to be trusted.

The main things that will break on you whenever you’re rebasing Xcode projects are, basically, within the project files,the provisioning settings, file imports, coredata files, and then the plist files themselves.

Demo

Let me do a quick demo of fixing a project file by hand. This is something that can scare people for sure. So, we did a quick reset here. Zach is a new programmer to our project. We assigned him to rewrite some UI. He did a great job with that part but unfortunately, he made some project file changes. He touched this file: contents.xcdworkspace file, and made this very minor-looking change right here. Which, broke how Xcode is importing things. Now if we were to make a change, and we try to commit within Xcode, we would get this sort of… Xcode is just bad right now.

Here’s a simple workflow. What I’m gonna do is a knockout commit. Here’s Zach’s changes. We’re gonna do a revert to that. Using the Git revert, basically this is a commit to just undo the last commit. This is a nifty little trick right here to sort of reset your prior commit.

We undid my revert of the thing. This file right here is the one that’s messing things up. So, we made another commit with basically our reversion of the XC workspace file, and then, finally, we just combine the two commits back together.

We will squash these two commits together. Now we have Zach’s commit but without the offending change. This is basically a very common workflow I do. I do a lot of this for renaming things, rewording commits, I can rewrite all your history, basically.

I can take your commits, move them forward and backwards in time and make it look like you were working at three a.m. I view my job as coming along and cleaning up after other people. Upgrading projects.

Pod Install

A lot of people use the pods install process. I think you should break apart your pods into two steps, basically. If you’re gonna add an actual new library to the thing, do a pod install step, and then make an actual commit just for that, and then do your upgrades as another one.

Then I only upgrade once each release cycle, that way hopefully over the period of, a week or two before the actual release, any low-level library bugs will be caught by the users. The other thing I really like to do is I always check in my source of my libraries so I have it for later.

At some point in time you’re gonna need to know how the thing is actually running; and the only way to actually do that is to go into the source code.

There are two basic models for how to upgrade all these libraries. One is to add a library to your project once, and never touch it again. And that’s actually probably for most projects would be the best approach. You generally do not actually need new versions of all these libraries.

On the other hand, you have completely online libraries, which is more of the Android model of things. Where that breaks down is at some point in time something’s crashing and it’s all tied to an imaginary version of a library that’s on a server that you don’t even have anymore.

As mentioned: check in your source, save it. It’ll save you a lot of hair pulling down the road.

The same concept for core data and Realm models. I only think that you should have one, you generally do not need to update your data models every day. Just coordinate with other people.

Releasing Projects

Releasing projects I think is super important. You should add a readme.md to all your projects, use markdown, document your build process, and then give the project to somebody else and have them push a build up to the store.

Whenever you do this, you’ll discover that not every key has been written down, some secret password that you thought wasn’t that important actually turns out to be super important.

Also try to keep your docs up to date. Every three months or six months, go in there and look, and make sure that it’s still actually working.

Git Grabbag

I had a bunch of random tricks here: Git stash and pop. If you’re gonna use Git stash, you might as well just make an actual commit cause then you can actually remember what the name is, and it literally buys you nothing to use the commands. I use Git stash to hide my changes whenever I want to reset some stuff.

For branch naming schemes, I do: year, month, date, and then build of the day release. I always number things with zeroes for the month, for example 02 for February, and 08 for the day of the week. This means later on you can go down there and hit alphabetical sort, and everything will be in alphabetical order for the last three years, which if you’re working on a project for a long period of time, that will save you some hair pulling. There’s a hand full of different Git UIs you can use out there.

GitX

I’ve been demoing GiX. There’s another one I saw the other day called Gitup is pretty slick. Both SourceTree and GitHub have very nice GUI clients that you can use.

Demo

So, let me demonstrate messing up an Xcode project file just for the fun of things. We’ve made some changes. This is a demonstration of what will break when doing a project file merge. This is the diff format of the actual plist file, but this is where stuff will go horribly wrong whenever you’re actually trying to do this in the real world because, as you can see, the diff algorithm has just detected that right here but we’ve actually got more changes way up here that it doesn’t see at all.

So, you come in here, delete this and that. And now you have multiple keys in your project file, and that’s gonna explode at some point in time down the road. If you’re wanting to do this by hand, you need to be really careful.

Why I Code

Sometimes I code just for myself, I’ll write crazy stuff that I’m the only person who knows what’s going on. A lot of time I code for money, but sometimes I like to code for other people. I’ve also done volunteer work where I went out and helped actual people and a made a difference in their lives.

There are not that many programmers around the world. The Homebrew project has 6 people who are working together around the world.

Here’s my first commit to Homebrew, which I made a little over five years ago, and it’s just a one-line spelling fix.You don’t need to be Linus Torvalds to actually contribute to this stuff.

I think anybody can do this stuff, and I view coding as the modern literacy in our age.

I’d like for you to go out and make a commit to something and find a project you like, and use it or help it. It doesn’t have to be code. You can go through the docs, or their wiki and make a change there.

Q&A

_Q: Which form of GitX are you using?

Brett: I’m using not the original one, but a different one that was made by some guy in the Netherlands. It’s wildly out of date. There’s a newer Cocoa version that was out there for a couple of years. I think I’m just using the original one from Peter. Whenever I was doing this a few years ago, most of the other Git things, they barfed once you got to 10,000 commits. I had to have one that would work a little faster. So, this is the basically the closest thing to GitK, but on the Mac without actually using GitK because I don’t like X11.

_Q: Do you think Apple will ever try to fix up Xcode, and raise it up to Git, will they ever put the effort into doing that?

Brett: Yea basically Apple maintains their own version of Git,and, so, whenever you upgrade the Apple command line utilities,usually they’re giving you a new version.And in Xcode itself has an embedded version of Git that’s in it. I would honestly advise you to use the Apple version, even though it’s kind of its own little weird fork because if you actually use the official versions of Git,you can get into weird situations,where two things are out of sync. Same concept, don’t use the latest version Git.

_Q: So, this is called the command line utilities would be the best code, and it can adapt? And will that avoid problems?

Brett: Not always, but like I said, you’re safe to be using Apple’s tools within Apple’s programs than to be using the official correct tools, cause Apple maintains their own customizations.

_Q: What about Eclipse?

Brett: Don’t use Eclipse; use Android Studio if you’re doing Android. Eclips usually use a system configuration, so I believe that then, the Apple tools are installed to the system, so you would technically be pulling in the Apple tools there, but then the same concept. I would install the official one because that’s probably gonna be closer to what the actually Java Eclipse developers are using.

_Q: I talked to a bunch of different developers, and they told me that people won’t really look at entry-level people to help out, and that you’re much better off doing something on your own. So, I was surprised when you came in here saying that the Homebrew folks are actually looking for entry-level people to come look at low-level pummeling,like fixing typos, and stuff like that.

Brett: A lot of the big projects,everybody and their brother wants to be in, but most of them, if you go and look, they’ll have a list of stuff for newbies.Specifically Django, I’ve done a couple of commits over there. They literally have a section on their website, “Do you wanna help out? Go here, talk to these people,”

_Q: Your previous commits,if you force push to master,how are you dealing with issues and conflicts with opportune dates?

Brett: I try not to force push to master.

_Q: But the game of strategy for, you are kind of massaging commits, and going into the commit industry, that’s gonna change the job for the commit.So, somebody put pulls. Are they just gonna take merge from it, or give us protection for dealing with that?

Brett: Generally I’m the last person to merge the thing before the thing. If you’re working with other people on a particular branch, then make somebody the branch master, and then if you’re rebasing people’s commits, then tell them that you did that, and tell them that they need to update to whatever the latest version is.

Next Up: Realm-Supported Open-Source Projects: SwiftLint

General link arrow white

About the content

This content has been published here with the express permission of the author.

Brett Koonce

Brett Koonce is an iOS/Android programmer and former maintainer of Homebrew. He is the CTO of Quarkworks, a mobile app consulting company. He likes yoga, cats and electronic music.