How To Use Git Source Control with Xcode in iOS 6
This tutorial is by Malek Trabelsi, a passionate iOS developer from Tunisia focused primarily on mobile and web technologies.
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, the command line, and even how to integrate your Xcode projects with Github, a popular online git repository. This tutorial is fully updated for iOS 6 and covers the new git features introduced in Xcode 4.5.
So without further ado, let’s Git going!
Gitting Started
Rather than ramble on about the theory of git, we’re going to dive right in and try it out. We’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 project, or go to FileNewProject, pick the Single View Application template from the iOSApplication menu, and hit Next.
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
- Use Storyboard : Check this
- Use Automatic Reference Counting : Check this too
Now click Next. The following dialog allows you to choose where to save your project. Choose the location and make sure “Create local git repository for this project” 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 we 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 our 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 :]
Let’s check that, click the organizer icon in the top right.
In the window that appears, switch to Repositories.
If you look on the left side, you may notice that Xcode has detected a new project repository and added it to the list.
Click on it and you will see all the details about the new files added to your blank project and a typical commit put by Xcode to indicate that a commit action was performed :]
Now, try to make some changes within your files. For example, open up AppDelegate.m and change the application:didiFinishLaunchingWithOptions: delegate method to the following:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSLog(@"application did finish launching");
// Override point for customization after application launch.
return YES;
}
|
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, let’s 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 MainStoryBoard.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.
Now, switch to ViewController.h and replace it with the following:
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
-(IBAction)clickTheButton:(id)sender;
@end
|
Switch to ViewController.m and add the following code to implement the method you just declared in the header file:
-(IBAction)clickTheButton:(id)sender{
NSLog(@"This is a Git tutorial");
}
|
The last thing to do is to link the method to the button. Switch back to MainStoryBoard.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 – we 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 Enter key to make a new line (or several). Then go to FileSource ControlCommit, and you should see the result below:
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 us.
The left pane shows your project files, you can notice these files with the M status which you just modified. Xcode, by default, check all the files with the M status and assume 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.
And a new feature in Xcode 4.5 is n addition to discarding a file by unchecking it in the list, you now decide on a change-by-change basis in a particular file which to commit and which not to!
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, right? So, to eliminate a specific change from being committed, just uncheck it.
For example, change 1 is not important, since it’s a blank line. So uncheck that one so that change isn’t checked in.
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”.
This is an extremely useful new feature. In the older versions of Xcode, if you wanted to exclude a method from a commit, you’d have to remove it from your code! This can be quite annoying, so this new approach gives you a lot more flexibility.
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 Organizer and click Refresh, 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 new feature introduced by Xcode 4.5, is to allow you to commit 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 its first created, your project is associated to a branch called “master”. This branch is created automatically by Xcode and, as its name indicate, 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 mainline.
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 MapForIternerary. 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 a new file that has not yet been committed to the repository.
Now, go to File/Source Control/Commit to switch to the version editor.
If you select one of the files with the A status, you may notice that Xcode didn’t provide any earlier version to compare. 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 need to assign These new changes to another specific branch to deal with. That will help to isolate the risk in case there are some problems with your new map code, as we’ll discuss later.
The good news that as of Xcode 4.5, this is very easy. Instead of clicking the “Commit 4 Files” button, click the “Commit to Branch” button instead. This dialog will appear:
By default Xcode selects the “master” branch as the branch to commit to. Let’s switch that – click on it to bring up a dialog of other branches in the repository you can commit to.
Oops, there is no branch but the master. Ok, so click New Branch to create a new one and call it map_feature.
Click Create Branch button, you will notice now that Xcode switched to the new branch that you just created.
Now, that we switched to the branch we want, in the same window shown above, click the Commit button.
Now what? Oh yeah, we were missing something quite important. In every commit operation, you must leave a comment message. Comments are very mandatory since they help you later in case you return to see what this commit was all about :]
Here, our commit is around the map feature, so feel free to write something meaningful for your later needs, something like “Started work on new map feature, not quite done yet.”
Once you finish, click the commit button again. You’ve already chosen the “map_feature” branch so don’t need to choose the “Commit to Branch” option again.
Don’t wait for an affirmation message from Xcode. Instead, if everything goes fine, you will not get errror message and, the most important, you will see the status A near all your map files cleared. That’s because Xcode has added those files when you committed.
In addition, you can now see the new branch in Organizer. Select the Branches icon:
And then you can flip between the two branches to see the commit logs:
Backing Out
Let’s say you’re working on the latest revision to your project, adding code, building and so on. But it appears that you’ve taken a wrong turn somewhere and the project isn’t compiling properly and throws up a lot of errors. 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 some changes to it to look like this:
#import <Foundation/Foundation.h>
@interface MapForItinerary : NSObject
-(void)fakeMethod;//this is a fake method just to make the discard action clear for you
@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 go to FileSource ControlDiscard Changes.
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 thanks to a new feature in Xcode 4.5.
As this point, MapForItinerary.m should still have the “M” change on it. Go to FileSource ControlCommit, and find the change to MapForIternerary.m. Click the badge in the middle and choose Discard Change:
A prompt window will appear, click Ok, the change should disappear. If you want to cancel your action, press cmd+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 we 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.
All Your Changes Are Belong to Us
Each time you commit your changes to one or many files, Xcode creates a revision and assigns it an ID, the name of the performer (who committed what), and the date and time.
That helps you a lot as you progress in your development, because at some point you may need to remember which revision contains a particular change.
Xcode makes it so easy to see all your project revisions. Just go to WindowOrganizer or click on the top right button on the toolbar.
In the Organizer window, switch to the Repositories tab. You should see something like this:
In the left sidebar, you will see a list of all your Xcode projects. Scroll to the one you’re working on (or are interested in) and click on it.
As seen in the screen above, the main pane will show all the revisions you’ve made to the code during your development from the most recent to the oldest.
To see detailed informations on each revision (all commit actions, etc.), just expand the disclosure triangle next to the revision title. You should see an expanded view like below:
Git shows the number of files committed for that revision, the date on which you made the commit and the file names.
Click the “View Changes” button. You will get a new screen as shown below.
This is the ideal way to review all changes you made per file. The comment pane at the bottom also helps you to identify the changes and, perhaps, why you made those changes in the first place :]
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 ViewVersion EditorShow 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.
This allows you to compare two revisions of the selected file. In our 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 July the 3rd 2012. 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 copy the content from the right editing pane to the left one. 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 does deal with each revision.
Let’s switch to the blame view, click on the blame view icon on the bottom Xcode pane.
You will get something like this below.
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, merged together in one place.
Scroll your view down to see all commits labeled with date and comments. To see more details around a committed change, press the info button next to the date, you will see the revision ID this change belong to, 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
In the previous parts, we talked about branches and how to commit to a particular branch other than the master. Basically, 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 trunk. So, as you work on a project, you might branch off a development stream from the trunk 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 the trunk.
Let’s practice working with branches!
Go to the Xcode Organizer and switch to the Repositories view. Select your project from the left sidebar and make sure that the Branches folder/group is selected. You should now see the “Add Branch” and “Remove Branch” buttons at the bottom of the window.
Click the “Add Branch” button. You will get a new dialog box:
Name the Branch “New-Branch”. For the Starting Point, you need to specify the branch to base the new branch on.
In our case, we only have a 2 branches: master and map_feature branch. map_feature branch is still unstable since you didn’t finish working on the new feature yet, let’s pick the master branch as a starting point as we know it holds a stable version of our project.
Finally, make sure that the “Automatically switch to this branch” checkbox is checked and click the “Create” button.
The current branch is displayed on the top right-hand corner when you select the project directory in the left sidebar of the Organizer.
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 will get a dialog box with a list of all available branches. Select “New-Branch” and click “OK”.
Now, let’s see how changes performed on a branch will be applied to the trunk when merging.
What you are going to do is to add a simple UILabel to the user interface in the new branch you created. Then, you will merge the new branch back to the trunk and verify it works.
If you don’t have the new branch selected, switch to it now. Then select MainStoryboard.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 by selecting FileSource ControlCommit. Add a commit comment and complete your commit.
Now switch to the master branch (the trunk) by selecting the Organizer, then selecting your project directory from the left pane, clicking the “Switch Branch” icon on the bottom pane, selecting the “master” branch and clicking “OK.”
Once you do the switch, you should see the Current Branch changed to master in the top right corner similar to the image below.
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 with the trunk or, in other words, apply the changes performed in the branch to the trunk.
Go to FileSource ControlMerge. A new dialog will ask you which branch you’d like to merge into your current branch. Select the new branch you created and click “Choose.”
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 our 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 should select the “Right” button to transfer the changes from the right pane to the left pane.
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 superfluous. 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 the MainStoryboard.storyboard or when you run your application. Now your changes are in the trunk because of the merge!
Ignore Me At Your Peril
Git uses a special file called .gitignore, which holds information about files you don’t want to commit to the Git repository. These files are simply ignored when you perform a Git commit.
But, why should you think about ignoring some of your files since they depend on your project?
Well, actually, it’s not a mandatory step to commit every single file. I
mean, there are some files that need to be ignored like your setting
file .DS_Store, or your private shemes files like .xcuserdatad.
For this tutorial, i am going to ignore these files:
.DS_Store build/* *.xcuserdatad |
But in other cases, that really depends on you project files. For example, in many cases, i used to ignore temporary files produced by vim (.swp files).
Actually, you may need to know that there is two choices when you want to ignore files with Git. The first one is called “Per-repository”, and is assigned to one repository (one project), and if you create such file and push it to your remote repository(in case you want to share your project with others), everyone who will clone your repository to his local machine, will take advantage of this .gitignore file you just created :]
A .gitignore file is not added to a repository by default. So you need to add it manually. Open up TextEdit and add the following:
build/* .DS_Store *.xcuserdatad |
Let’s take the above line by line:
- build/* : This will exclude the whole build directory which is created by Xcode when creating the application binary.
- DS_Store : This rule will exclude the OS X folder attributes.
- xcuserdata : Exclude user data files.
Save the file in the root directory of your project and name it .gitignore.
The second choice goes here: What if you work with a lot of Git repositories (hence lot of projects), and you want all your project to ignore the same files. You don’t want surely to create the gitignore file for each repository. In such case, you may need to set a called “global” .gitignore file.
To do so, just create a .gitignore_global file in the same way you did for the .gitignore file and place it in your home directory, this file will affect every repository you clone from a remote repository.
But, to do so, you need to tell Git to use this file, don’t you? ;]
Ok, fortunately, that’s quite easy with the xcrun command, just type in the command line:
xcrun git config --global core.excludesfile ~/.gitignore_global |
Once you run this command, Git will use this file each time you clone a repository, and hence, assign it to the following file.
For more examples of good files to put in your .gitignore file, check out our forum discussion on the matter.
Xcode, git and github
All the work you’ve done so far has been using a local repository saved on your computer. The GitHub website allows you to move your project to a remote repository saved on the GitHub servers. This is great because it allows you to easily share your code with others and work on a project as a group.
Note that GitHub uses the Git source control system which will simplify our work since you are already familiar with Git.
If you don’t have a GitHub account already, create one and set it up by following the official GitHub guide.
Once that’s done, create a repository for your project on GitHub. To create a repo, click the “Create a New Repo” button on the GitHub site. It should present you with a screen similar to the following:
After you fill in all the fields above, click the “Create repository” button. It will take you to a setup instructions screen which shows you how to set up Git on your local machine to work with the newly created repository on GitHub. You can ignore most of the instructions but pay attention to the “git remote add origin” line under the “Next Steps” section. What appears after “git remote add origin” is your GitHub SSH URL and you’re going to need it soon. So make a note of it.
Now back in Xcode, select WindowOrganizer and switch to the Repositories tab. Then click your project’s Remotes folder from the left sidebar. Click on the “Add Remote” icon – it should a dialog similar to the following:
You will see two fields. For Remote Name, type something like MyNewRemoteRepository. And for Location you will need to paste your SSH URL given by GitHub. If you noted down the SSH URL earlier after creating the GitHub repo, you should be set. In my case, the SSH URL was this:
git@github.com:lucaazori/GitTestExampleRemoteRepository.git
|
The SSH URL can also be found on your GitHub repository page:
So take that URL and paste it in the location field to get something like this:
Now, click the “Create” button.
If everything worked out OK, you should see the new remote repository:
A connection between Xcode and your remote GitHub repository is now successfully established! All you need to do now is to push your project to the remote repository, then you will see it on the GitHub server.
Select FileSource ControlPush. A new dialog will show up. You need to choose the branch on the remote repository that you want to push your code to. Since your repository is new, there will only be one option. Select it and click the “Push” button. Xcode will push the changes, and that’s it!
Now go back to your GitHub repository via your browser. Refresh the page and you should see the new branch you just pushed.
You can refer to the project you worked on throughout this tutorial on my GitHub repository.
Set up Git from the Command-line
Although Xcode has all Git functions built-in, it would be useful to know how to use Git via the command-line as well.
To do this, open up a Terminal and change your current directory to your project folder for this tutorial. In my case, I ran the following command:
cd /Users/Malek/desktop/GitUseExample |
For your machine, just replace the /Users/Malek/desktop part with the actual path on your computer. At this point, you can run commands from within your project folder. To create an empty Git repository in the current folder, you can use the following command:
xcrun git init |
Note that xcrun uses the version of git that is built right into Xcode – so you don’t have to install git yourself!
If you were to run that command from the command-line, you should see something like this:
xcrun git init Reinitialized existing Git repository in /Users/Malek/desktop/GitUseExample/.git/ |
Note that Git creates a folder named .git in your project directory. This is a hidden folder. If you want to be able to see it using Finder, just execute this command from your Terminal session:
defaults write com.apple.finder AppleShowAllFiles TRUE |
Then press the Alt button and right-click the finder (or click the Finder icon while holding down ATL+Control). The context menu that pops up should have a “Relaunch” option at the bottom.
Select “Relaunch” to relaunch Finder with the option you enabled above being in effect. That option allows finder to show all files including hidden files. You should now be able to see the .git folder within your project directory :]
Let’s go back to git commands. In Terminal, type this command (note the space between “add” and the “.”):
xcrun git add . |
This command will simply add all your project files into the Git repository.
When you want to commit your changes, just use the following command:
xcrun git commit -m "A comment goes here :]" |
The command above will perform a commit operation, saving the current status of the whole project to source control. Note that the commit message is optional. However, in practice, you should enter a message that is meaningful.
If the operation was successful, you should see a summary that lists all your committed files as shown below:
Where To Go From Here?
Congratulations, you now know how to use git source control from Xcode, use branches, work with Github, use the command line, 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, here is a list of great resources to check out:
- Wikipedia Git article.
- Wikipedia GitHub article.
- Apple Git developer guide.
- The GitHub official help guides.
Also, once you get the basics, you may need to take a look on branching methodologies. Personally, I follow this model in my own projects.
I hope you enjoyed this tutorial, and I am looking forward for your comments! :]
This tutorial is by Malek Trabelsi, a passionate iOS developer from Tunisia focused primarily on mobile and web technologies.