Site Navigation

If you're new here, you may want to subscribe to my RSS feed or follow me on Twitter. Thanks for visiting!

How to use Git source control with Xcode

Note from Ray: Tutorial Team member Felipe Laso Marsetti has ported this tutorial to iOS 7 as part of the iOS 7 feast. We hope you enjoy!

Whether you’re a solo developer or working on a team, if you’re not using source control for your projects, you should be.

Source control is amazing because it helps you more easily revert to older version of your code, see how your code has changed over time, and work as a team. And one of the best source control systems is built right into Xcode – Git!

Git is a distributed version control system initially developed by Linus Torvalds, the principal force behind the development of the Linux kernel. The nice thing about Git is there doesn’t have to be any central repository – everyone can have their own view of the code, and pull in changes from other sources.

In this tutorial, you’ll get hands on experience with Git and learn how to use it directly inside Xcode. This tutorial is fully updated for iOS 7 and covers Git using Xcode 5.

So without further ado, let’s Git going!

Gitting Started

Rather than ramble on about the theory of Git, you’re going to dive right in and try it out. You’ll create a new Xcode project and try out some typical tasks you will typically do on a day-to-day basis with Git source control.

So fire up Xcode and create a new Single View Application from the available templates.

Now, fill in the template options as follows:

Product Name : GitUseExample

Company identifier : As the name indicates, it’s your company identifier, if you have one. Otherwise, type whatever.

Class prefix : Leave this empty.

Device family : iPhone

Now click Next. The following dialog allows you to choose where to save your project. Choose the location and make sure “Create git repository on My Mac” is selected before you click the create button. Once you do that, click the “Create” button.

By checking that, Xcode will create an empty Git repository and use the basis of your new project as your first commit. Well, that’s exactly what you want!

All source control systems, including Git, store their data into a repository so that they can manage your project versions and keep track of changes throughout the development cycle. So think of a repository as a database for versions.

In the course of working on your project, you’ll add files, modify code, and change your project many times.

After you make a big set of changes and are in a “known good” state (typically one or more times per day), it’s a good idea to check in your changes back into the repository. This way, you have a record of “known good” states that you can always revert back to.

But what about the code that’s in your project already, that was created by the project template? Take a look at the following screen.

Your project is still blank, and since you didn’t make changes yet, you don’t need to commit. But, basically, Xcode has added and committed several files when you created your project. That commit is the initial commit performed automatically by Xcode :]

To check that, click the Source Control menu item and select History….

In the drop down window, notice there’s a commit along with some information about it like the commit hash, date and time, person who made the commit, files change, and commit message.

If you click on the text that says “Show 18 modified files” a new pane will drop down containing information about all of the files that were committed, as well as what was changed in each one.

Now, try to make some changes within your files. For example, open up AppDelegate.m and change application:didFinishLaunchingWithOptions: to the following:

After you save the file, you will note that AppDelegate.m now has a “M” badge next to the filename:

The “M” badge stands for “modified.” Specifically, it means you have modified a file but not yet checked in the changes to your local Git repository.

Before committing, quickly add a simple UIButton to the View Controller file and a method that you link to the button. This is just to illustrate the different status indicators you will get for source control.

Select Main.storyboard and drag a button to the screen from the Object Library. Change the button’s displayed text to whatever you want as shown in the screenshot below.

The last thing to do is to link the method to the button. Switch back to Main.storyboard, click the View Controller in the View Controller scene, and from the Connections Inspector (last tab on the right sidebar) click on clickTheButton: on the Received Actions panel and drag it to the button in the Storyboard Editor. Then choose “Touch Up Inside” from the list that pops up.

If you check the project navigator, you will notice that the status of three files, ViewController.h, ViewController.m and MainStoryboard.storyboard, has changed to display an M badge too. That’s because you just made changes to those files.

Build and run to make sure the project works, and that when you click the button you see the “This is a Git tutorial” message logged to the console.

w00t – you now have a set of code in a “known good” state, it’s time to commit!

Making Some Commit-ments

Committing files is simple – simply go to FileSource ControlCommit.

A new window will show, similar to the following:

As you can see, the screen is split in to two. The left pane shows the file in its current state with all changes made since the last commit. Since this is your first commit, you will see all changes made since the creation of the project.

The right pane shows the file before you made your changes.

Note the blue selections in the code panes. Those indicate the code you added. Even if you add a blank line or white space, Xcode assumes it’s a change and will indicate it with those blue selections.

Try it for yourself: tap the “Cancel” button, go back to ViewController.h, press the Return key to make a new line (or several). Then go to the Source Control menu and select Commit one more time. You should result similar to the following:

This might be a bit obvious, but Git does carefully track every single change you make throughout your development cycle.

Let’s take a deeper look on that screen to see what Xcode has prepared for you.

The left pane shows your project files, you can notice these files with the M status which you just modified. Xcode, by default, checks all the files with the M status and assumes that you want to commit all of them.

If you want to eliminate one or more files from being committed, you simply need to uncheck it. You can also decide what to commit within the file and discard individual changes made.

This is called “cherry picking” changes. Let’s try it out!

In the version editor, all your changes are enumerated by Xcode. If you scroll the editor, you will see change 1,2, etc., as shown in the screenshot above.

Each change is checked by default. To eliminate a specific change from being committed just uncheck it.

For example, change 1 is not important since it’s a class extension and that could be useful later on. So uncheck that one so that change isn’t committed.

Notice the indicator has turned to gray, to make it clear that change is now excluded from the commit process.

Another way to exclude an individual change in the same file, is to click on the arrow next the change number. Two options will appear: “Commit” (or “Don’t Commit” in case the change is checked) and “Discard Change”. In this case, you just need to select “Don’t Commit”.

Now let’s commit the rest of the changes. Before you do, be sure to enter a commit message in the bottom part of the screen. This helps you better understand at a glance what each set of changes contain.

Then click commit. Congrats, you have made your first commit! If you go back to the History pane you should see your new commit in the log:

This simple process (making changes, and then committing them) is what you’ll be doing 90% of the time. Pretty easy, eh – so you have no excuse not to do it! :]

Branching Off

Another feature in Xcode is committing your changes to a specific branch.

But wait a minute – what’s a branch?

Actually, believe it or not you’re already using a branch. When it’s first created, your project is associated to a branch called “master”. This branch is created automatically by Xcode and, as its name indicates, is the main branch of your project.

“But what is a main branch?”, you may ask. Hmm, ok. Take a look at the following picture:

Basically, the master branch should always keep the main copy of your project. So, when you are working on a project, master should usually refer to the release version.

But you can have other branches as well. One good use for branches is to keep track of experimental new features that aren’t quite ready for the release version.

For example, let’s say you’re adding a new map feature into your app, but it isn’t quite ready for production. To simulate this, create a new class derived from NSObject and name it MapForItenerary. At this point your project might look like this:

Notice the status “A” for the two new files MapForItinerary.h and MapForItinerary.m. This indicates there is a new file that has not yet been committed to the repository.

Now, go to the Commit option in the Source Control menu.

If you select one of the files with the A status, you may notice that Xcode didn’t provide any earlier version to compare to. This is because the file hasn’t been committed to the repository yet, so there is nothing to compare it with.

Ok, so you added 2 files (MapForItinerary.h, MapForItinerary.m) to the project – Xcode detected that and is ready to commit. However, you may want to commit these new changes to another specific branch. That will help to isolate the risk in case there are some problems with your new map code, as you’ll see later.

Instead of clicking the “Commit 4 Files” button, click the “Cancel” button instead and go to Source Control, select the current branch under “Working Copies” and then select “New Branch…”.

The following dialog will appear to give you the option to name your new branch:

Name the branch “map_feature” and click the “Create” button. You are automatically switched to the new branch so go ahead and commit these changes and give them a descriptive message:

Once you finish, click the commit button again.

You will see the status letters next to your map files has been cleared. That’s because Xcode has added those files when you committed.

You can now see your branches in the Source Control menu by selecting “Configure GitUseExample” under “Working Copies”:

And then, inside the configuration pane, select the “Branches” tab:

Backing Out

Let’s say you’re working on the latest revision of your project, adding code, and so on. But it appears that you’ve taken a wrong turn somewhere and the project isn’t compiling properly. At this point, you may want to recover the last revision from source control and start fresh.

Git provides this kind of project backup :]

Let’s try it. Go to MapForItinerary.h and make the following changes:

@interface MapForItinerary :NSObject// This is a fake method just to make the discard action clear for you-(void)fakeMethod;
@end

And replace MapForItinerary.m to look like this:

#import "MapForItinerary.h"@implementation MapForItinerary
-(void)fakeMethod
{// This is a fake method just to make the discard action clear for you
NSLog(@" Discarding changes allow you to get the latest revision you have worked on it.");
}@end

You’ll notice that the status of the modified files has changed to M, meaning that the files are locally modified and are waiting to be committed.

At this point, you can selectively discard the changes you’ve made to the file. Select MapForItinerary.h in the project navigator and then select Discard Changes in “MapItinerary.h” from the Source Control menu.

A prompt will show up, asking you whether you really want to discard the changes you made to that file.

Click the “Discard Changes” button. You should see the code you just added vanish! This can be extremely useful when you’ve added some changes but they aren’t working, and you want to get back to the last known good state.

In addition to discarding an entire file’s worth of changes, you can discard individual changes as well.

As this point, MapForItinerary.m should still have the “M” change on it. Select the commit option from the Source Control menu, and find the change to MapForIternerary.m. Click the badge in the middle and choose Discard Change:

A prompt window will appear, click OK and the change should disappear. If you want to cancel your action, press Command + Z. Boom – the change is gone! Go ahead and enter a log message and finish the commit.

Now that you’ve tried out “Discard Change”, you might wonder what the difference is between that and the “Don’t Commit” option you chose earlier.

It’s true that both of these options result in the change not being pushed to the repository, but there is a difference:

Don’t Commit lets you skip the change so that it won’t be committed with other changes, but it will remain in the local source code.

Discard Changes not only skips the change, but also deletes it from the local source code.

Time Travel

Discarding changes is a good way to revert back to working code and save you time. However, it can be a little limiting in some cases.

Let’s go back to the commit action for a moment. Commit allows you to save multiple revisions for your project where each revision has specific changes. These are all stored into a repository managed for you by Git.

If you choose to discard changes made to a file, this will give you the last committed version of the file and only the last. And that’s actually where the limitation lies.

Let’s say your project repository contains several revisions over time and that you want to get the first one for a particular file or the second version, but not necessarily the last. Discarding changes doesn’t get you that. But there’s a way this can be done easily with Xcode and Git.

Select the file that you want to revert back to from a previous revision from the Project Navigator. Let’s say ViewController.m, then go to View > Version Editor > Show Version Editor. (Alternatively, you can tap the third button under the Editor section on the toolbar at the top right of the Xcode window.)

The version editor is split into two panes as shown below:

This allows you to compare two revisions of the selected file. In your case, two revisions of ViewController.m. The comparison is based on the time line, so click the timeline viewer icon (marked on the screenshot below) to show it up.

Now, you can move up and down the timeline using your mouse cursor to select the version you want to get back to.

For example, the oldest version of the ViewController.m file, as shown in the image above, was committed on September the 24th. If that is the version that you are looking for then simply tap the indicator for the revision to open that specific version in the right editing pane :]

Now, to revert to that file version, just click the middle section of a change in the comparison pane and select “Discard Change”. It’s that easy! :]

Another useful feature which is good to know about is the Blame view. This view allows you to catch every commit on your file easily, in real time, so that you can distinguish which commit deals with each revision.

Let’s switch to the blame view. Click and hold down the left mouse button on the version editor button, then select “Blame”.

You will get something like this:

Choose the file you want to view its commits from the project navigator pane, for example, ViewController.m.

In the right sidebar, you may notice all the commits you did for this file. Actually, blame view is the best way to see all the commits from different revisions related to a specific file, all merged together in one place.

To see more details around a committed change press the info button next to the date, you will see the revision ID that belongs to the change, who did that change, and other eventual files changed in the same revision. That’s better than going to your project repository in the organizer and searching for such changes, isn’t it? :]

Branching : Isolating the risk

Git allows you to work on multiple streams of revisions and these different streams are known as branches. The main code you work on for a project is called the main branch. So, as you work on a project, you might branch off a development stream from the main branch to a secondary branch – sort of like a tree.

Typically, it’s good coding practice to always work on a copy of your project – the development branch, if you will. And once you complete a development milestone, you normally merge the development branch back to master.

Let’s practice working with branches!

Go to the Source Control menu item and, under Working Copies, select “Switch to Branch…”.

From the list of available branches select master and click Done.

When you create a new branch you start off from the state of the current branch you are working on. In this case you are switching to master so you start with the work from that branch. You don’t want to use map_feature for now as it’s still not finished and it’s related to another feature that may not make it into the app any time soon.

Go to the Source Control menu and, also under Working copies, select “New Branch…”.

Name the branch “New-Branch” and click Create.

To switch between branches using Xcode, first make sure that the project directory is selected and then click the “Switch Branch” icon on the bottom right corner.

You can verify that you are working on “New-Branch” by clicking in the Source Control menu item and looking at the name of the branch under Working Copies.

Now, let’s see how changes performed on a branch will be applied to master when merging.

What you are going to do is add a simple UILabel to the user interface in the new branch you created. Then, you will merge the new branch back to master and verify it works.

Switch back to the standard editor view, select Main.storyboard, bring up the object library, and drag a UILabel on to the main view.

Save and run to make sure that all is OK and then commit changes to the repository. Be sure to add a commit message.

Now switch to the master branch and run the app again. You’ll notice that the new UILabel you added in the branch is not there. Obviously, that’s because you changed the working branch. The final job is to merge the new branch back to master, or, in other words, apply the changes performed in New-Branch to master.

Go to Source Control > Working Copies > Merge from Branch….

From the list of available options select New-Branch and click Merge.

A new window will show up where you can use the left and right buttons on the bottom menu to specify the direction of the merge. In your case, you want to merge the new branch into the master branch which is the current one. Since the current branch is on the left and the new branch is on the right, you can leave the switch at the bottom of the pane as is (shown in the image).

Finally, click the “Merge” button to start the process :] You might be prompted at this point to enable automatic snapshots. Since you have your changes going to a Git repository, the snapshot feature is not necessary, so select “Disable.”

If all goes well, you should see the changes (the UILabel) from the new branch appear in the user interface when you click on Main.storyboard or when you run your application. Now your changes are in the master branch because of the merge!

Where To Go From Here?

Congratulations, you now know how to use Git source control from Xcode, use branches, merge branches, and more!

At this point you have most of the tools you’ll need on a day-to-day basis to work with Git source control in Xcode. If you’d like to learn more, check out our new book iOS 7 by Tutorials, which includes two complete chapters that go into much more detail into Xcode and Git integration.

I hope you enjoyed this tutorial, and I am looking forward for your comments! :]

User Comments

33 Comments

Great tutorial! Helped out a lot. There are a few things I would like to have seen.

1.) How to add Source Control to an existing project. I've got lots of projects I'd like to get on this system and it isn't clearly explained how to do that.

2.) How to deal with annoying messages you can run into when trying to do merges. I don't know what stashing is, but I had to do that in order to merge a branch. It required a git command not available in Xcode. I also had a problem because I added a test case target to the master branch that wasn't the same as the one added in the feature branch. Xcode went ballistic.

3.) How to have Xcode avoid merging stuff that's not really important, things like where breakpoints were placed in the code and other user settings that somehow change when you do little more than look at a file.

I'm obviously a fan of source control, but it's definitely not as easy to execute as it might seem from reading the articles currently available.

I cloned a team member's xcode project to my local machine. Everything looked good except for the storyboard. For some reason the storyboard was cloned with "No Scenes". Any idea on why that would happen?