How To Use Git Source Control with Xcode 8

Richard Critz
Update: Updated for Xcode 8 by Richard Critz. Original tutorial by Malek Trablesi and previously updated by Felipe Laso-Marsetti.

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 versions of your code, add new features without risk to your working app, 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 his or her 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.

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 some tasks that you will typically do on a day-to-day basis with Git source control.

Fire up Xcode and create a new Single View Application project.

Single View Application Template

Fill in the template options as follows:

GitUseExample Project Creation

  • Product Name: GitUseExample
  • Team: Your Apple Developer team if you have one, or None
  • Organization Name: Your name
  • Organization identifier: As the name indicates, it’s your organization’s identifier, if you have one. Otherwise, type whatever.
  • Language: Swift
  • Device family: iPhone
  • Use Core Data, Include Unit Tests, and Include UI Tests: not checked

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

GitUseExample Saving

Note: If you don’t see the checkbox, click the Options button.

Xcode will create your new project along with a new Git repository.

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. Think of a repository as a database for versions.

versions database

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 your project is in a “known good” state (typically one or more times per day), it’s a good idea to check your changes into the repository. This gives you a record of “known good” states that you can always return to.

But what about the code that was created by the project template?

Your project still contains only the template files. There is nothing yet for you to commit because Xcode did it for you when you created your project. :]

To check that, choose Source Control\History… from the menu.

Source Control History menu

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

GitUseExample History

Note: Normally, you could click the Show modified files button to see more information about the contents of a commit. Unfortunately, due to a bug in Xcode, this only works some of the time. You will learn a different, more reliable way to see this information below.

Now, make some changes to your project. Open AppDelegate.swift and change the method application(_:didFinishLaunchingWithOptions:) to the following:

func application(_ application: UIApplication, 
        didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
  print("Application did finish launching")
  return true
}

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

The “M” badge stands for “modified.” It means you have modified the file but have not yet committed the changes to your local Git repository.

Next, open ViewController.swift and add the following code after viewDidLoad():

@IBAction func buttonClicked(_ sender: UIButton) {
  print("This is a Git tutorial")
}

Now, open 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.

Modified View Controller

The last thing to do is to connect the action to the button. Click the yellow View Controller icon in the View Controller scene. From the Connections Inspector (last tab on the right sidebar), click the open circle next to buttonClicked: in the Received Actions panel and drag it to the button in the Storyboard Editor. Choose Touch Up Inside from the pop-up menu.

Connecting Button Action

If you check the Project navigator, you will notice that all three of the files you have edited have an “M” badge.

Modified Project Navigator Files

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

Button Action Console Log

w00t – your code is now in a “known good” state! It’s time to commit.

Making Some Commit-ments

Committing files is easy! Select Source Control\Commit… from the menu.

Commit

A new window will show, similar to the following:

Commit pane

As you can see, the screen is split into two panes. 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.

Annotated Commit Pane

Look more closely at what Xcode presents in the Commit window.

The left panel (1) shows all of the files in your project with uncommitted changes. By default, Xcode assumes you want to include all of them in this commit and checks their boxes. If you want to exclude a file from this commit, uncheck it. As you will see in a moment, you can also decide which individual changes within a file to include in this commit.

Notice the blue highlights in the code panes. These indicate changes you have made. Any change, even if it only adds or removes blank space, is tracked and will be marked with these blue highlights.

Try it for yourself. Click the Cancel button, open ViewController.swift, and add several new lines at the end of the file. Now select Source Control\Commit… again. Your results should be similar to the following:

Modified sections

As you can see, Git carefully tracks every single change you make throughout your development cycle.

In the area between the two source panes, all of your changes are enumerated by Xcode (labeled “2” above).

Each change is checked by default. To exclude a specific change from this commit, uncheck it.

For example, change 3 is not important since it consists only of the blank lines you added earlier. Uncheck it so that this change isn’t committed.

Excluded change

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

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

Don't Commit/Discard Change

Before you can complete a commit, Xcode requires you to enter a commit message in the bottom part of the screen (labeled “3” above). These messages help you better understand at a glance the purpose of each commit.

Commit message

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

Commit History

This simple process of making changes and committing them is what you’ll be doing 90% of the time. Pretty easy, right? Now you have no excuse not to do it! :]

Note: Eagle-eyed readers will be thinking “I only changed 3 files. Why did I commit 4 files?” Git tracks every file in your project directory including all of the files Xcode uses behind the scenes. Usually, this is exactly what you want. You will see later how to alter this behavior when needed.

Branching Out

Another feature of Git supported by Xcode is the ability to commit your changes to a specific branch.

But wait, what’s a branch?

A branch is way to keep a set of commits together. By working on different branches, you can keep features separated and reduce your risk of totally breaking your project.

Believe it or not, you’re already using a branch. When a repository is first created, Git also creates a branch named “master” within that repository. All of your work so far has been on the master branch.

The master branch should always keep the main copy of your project. You use other branches as a way to store work in progress that is not yet ready for release. You can also use them to store experiments that may never be released.

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 MapForItinerary. At this point your project might look like this:

Added a file

Notice the status “A” for the new file MapForItinerary.swift. This indicates this is a new file that has not yet been committed to the repository.

Select Source Control\Commit… from the menu.

Commit for new file

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

Adding the map feature to your app represents a big change in your code. This is a perfect situation to use a branch. This will help to isolate the risk in case there are problems with your new map code.

Instead of clicking the Commit 3 Files button, click Cancel instead. Choose Source Control\GitUseExample\New Branch… from the menu. Notice that the current branch – master – shows in the menu.

New Branch

Xcode asks you to name your new branch.

New Branch name pane

Name the branch map_feature and click Create.

Xcode creates the new branch and switches to it. You can verify this by opening the Source Control menu.

Source Control menu showing current branch

Select Source Control\Commit…, enter a commit message, and click Commit 3 Files.

MapForItinerary commit message

Notice that all of the status letters next to the files in the Project navigator have been cleared. This means you no longer have any uncommitted changes.

Project navigator with no uncommitted changes

To see your branches, choose Source Control\GitUseExample\Configure GitUseExample… from the menu.

Configure Git project

Select the Branches tab in the resulting pane.

Git branches

Backing Out

You’re working on the latest revision of your project, building the newest cool feature. You take a break for a snack and have a sudden inspiration for a better way to build it. At this point, you may want to recover the last revision from source control and start fresh.

badstableversion

Git makes it trivial to do just that! :]

Open Main.storyboard and drag a new view controller onto the canvas from the Object library.

Two view controllers

Open MapForItinerary.swift and add the method sayHello().

class MapForItinerary: NSObject {
  
  func sayHello() {
    print("Hello from MapForItinerary")
  }

}

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 project. Select Main.storyboard in the Project navigator and then select Source Control\Discard Changes in “Main.storyboard”… from the menu.

Discard changes menu

Xcode will prompt you to confirm that you really wish to discard all changes in that file.

Discard prompt

Click Discard Changes. You should see the view controller (and the yellow warning icon) 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 changes, you can also discard individual changes.

MapForItinerary.swift still has the “M” badge on it. Select Source Control\Commit… from the menu. Click the down arrow next to the change number and choose Discard Change:

Selective discard

Poof! Your change is gone. Since there is nothing left to commit, just click Cancel to close the commit window.

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.

While it’s true that both of these options result in the change not being recorded in the repository, there is a big 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.

Git 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, Git will restore the last committed version of the file and only the last. And that’s actually where the limitation lies.

Over time, your project repository will grow to contain multiple revisions reflecting the history of your development. Suppose you wish to revert to the first or second version of a particular file. There is no way to do that just by discarding changes. Don’t despair, however, as Xcode and Git make this easy to do.

Select ViewController.swift in the Project navigator. Now select View\Version Editor\Show Comparison View from the menu. Alternatively, you can click the third button in the Editor section on the toolbar at the top right of the Xcode window.

Editor selection buttons

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

Comparison view

This allows you to compare two revisions of the selected file and works exactly like the comparison view in the Commit window. By default, your current source file is shown on the left and the most recent revision stored in the repository – Git calls this the HEAD – is shown on the right.

To display earlier versions from your repository, click on the clock icon at the bottom of the right pane (marked in red below) and select an earlier revision.

Revision list icon

Select the revision just prior the HEAD revision as shown below. The exact information you see will be different from the screenshot.

Previous revision list

Now, to revert to that file version, just click the arrow next to the change number in the comparison pane and select Discard Change. It’s that easy! :]

Discard Changes

Once you finish reverting to an earlier version, you will need to commit this “new” version of the file as the most recent. Go ahead and do that now.

How do you know which earlier commit is the one you want? While you can certainly use the History command you learned earlier, there is a better way. Click and hold on the Version Editor button and select Log. You can also choose View\Version Editor\Show Log View from the menu.

Select Log View

Xcode will list the commits that contain changes to the current file. Notice that the listing for each commit contains a commit identifier.

Revision history

These identifiers match the ones shown in the revision history list you were using earlier.

Annotated Revision history list

You can also click Show modified files to explore the differences in more detail. Try it now!

Another incredibly useful view into your project is called the Blame View. This view shows you which commit contributes each line of your file.

Switch to the Blame View. Click and hold on the Version Editor button and select Blame. You can also choose View\Version Editor\Show Blame View from the menu.

Select Blame View

Your screen will look something like this:

Blame View

To see more details about a committed change, press the info button next to the date. The resulting pop-up shows you who made the commit, when it was made, the commit message, and the commit identifier. It also has a button to show all of the files modified in the commit and a button to open the current file in Comparison View, comparing the this commit with the HEAD commit.

Show more blame info

Merging Branches

You learned earlier that Git allows you to work on multiple streams of revisions known as branches. You also learned that it is good practice to do all of your development on a branch other than the master branch. What, then, are you to do when you finish development of a feature and want to release it? Simple! You merge your development branch into your master branch.

Your new map feature is not yet finished but your designer has asked for another label on the main interface. To implement this, you will need to leave your map_feature branch behind for now and create a new branch from the “known good” state that is your master branch.

Select Source Control\GitUseExample\Switch to Branch… from the menu.

Switch to Branch

From the list of available branches, select master and click Switch.

Switching to master

It is important to ensure that you are starting your new branch from the correct “known good” state.

Now, select Source Control\GitUseExample\New Branch… from the menu.

Create new branch

Name the branch new_label and click Create.

Name new_label branch

You can verify that you are working on the new_label branch by clicking Source Control and looking at the name of the branch under Working Copies.

Verify your branch

Now, it’s time to add that new label your designer requested.

Switch back to the Standard Editor view, select Main.storyboard, and drag a UILabel on to the main view.

My new label

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

Now switch to the master branch and run the app again. As expected, the new UILabel you added in the branch is not there. The final job is to merge the new branch back to master.

Select Source Control\GitUseExample\Merge from Branch… from the menu.

Merge from branch

From the list of available branches, select new_label and click Merge.

Select merge source

The merge window will appear allowing you to control the merge process. The merge source (the “merge from” branch) will appear on the right. Your current source, as modified by the merge, will appear on the left. Use the buttons at the bottom of the screen to control the direction of the merge. For a simple merge such as this one, Xcode’s default setting will be the correct one.

Merge window

Finally, click the Merge button to start the process :]

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! Use one of the methods of viewing your commit history that you learned to verify that this change appears in the history.

Log view commit history

Ignoring generated files

Way back at your first commit you saw that, in addition to your source files, Git tracks revisions to files managed by Xcode. This is important because those files are just as necessary to your project as your source files. You need them to rebuild your app or to collaborate with others.

However, as it does its work, Xcode also generates other files that change with each build. It is not important to save these as Xcode can automatically regenerate them. In fact, saving them causes Git to do unnecessary work and makes it harder to find the significant changes in your commit history.

Git provides a mechanism to ignore these files: the aptly-named .gitignore file. The period at the beginning of its name causes macOS to treat it as a hidden file, so it doesn’t normally appear when you look at your project in Xcode or Finder. Never fear, though, because Git will find and use it without a problem.

Rather than working out for yourself everything to put in your .gitignore file, you can download one from gitignore.io.

First, open a Terminal window and enter the following command. You only need to do this step once, not for every project.

$ git config --global alias.ignore '!gi() { curl -L -s https://www.gitignore.io/api/$@ ;}; gi'

Now, for any project using Git, do the following in a Terminal window:

cd <directory where your project is stored>
git ignore swift,macos >.gitignore
git add .gitignore
git commit -m "Add .gitignore file"

This downloads the most current .gitignore configuration for writing Swift code on macOS. Your terminal session should look similar to this:

adding a .gitignore file

Note that you added the .gitignore file to your repository for Git to track since the filtering it provides is also an important part of your project.

Xcode 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 publish 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.

If you don’t already have a GitHub account, go to GitHub and sign up for one.

Once that’s done, create a repository for your project on GitHub. Click the + button in the upper right of the GitHub site and select New repository.

GitHub New repository

GitHub will present you with a screen similar to the following:

GitHub New repo sheet

Fill in the repository name and click Create repository. GitHub will create your repository and take you to the Quick Setup screen. You can ignore most of this screen. You need to save the HTTPS URL for your repository to your clipboard. Make sure the HTTPS button is selected and click the clipboard icon.

GitHub Quick setup

In Xcode, select Source Control\GitUseExample\Configure GitUseExample… from the menu. Select the Remotes tab, then click the + button and select Add Remote….

Add remote

The remote’s name will default to “origin”. Leave that as is and paste your GitHub HTTPS string into the Address field. Click Add Remote.

Remote address

Finally, click Done. You are now ready to publish your project on GitHub!

Select Source Control\Push… from the menu. Xcode will prompt you for the remote’s name and branch. Since you only have one remote configured, the default will be correct.

Remote branch name

Click Push. Xcode will prompt you for your GitHub login credentials.

GitHub login

Enter your credentials and click OK.

Xcode will save your credentials to the macOS Keychain. You can manage them on the Accounts tab in Xcode Preferences. After a few seconds, Xcode will complete the push.

Check your GitHub page to verify that your files are there.

Verify on GitHub

Note: If you have set up your SSH credentials on GitHub (see GitHub SSH Instructions), Xcode supports using SSH to connect to GitHub. Just use the SSH connection information instead of the HTTPS string when adding your remote.

Now it’s time to make one final change to your project. Open ViewController.swift and change the buttonClicked() method as follows:

@IBAction func buttonClicked(_ sender: UIButton) {
  print("You finished!")
}

Select Source Control\Commit… from the menu. Enter a commit message and then check the Push to remote: box in the lower left corner. Again, since you only have one remote configured, the default will be correct.

Commit and Push

Click Commit 1 File and Push. After a few seconds, Xcode will complete the commit and the push. Look for your new commit on your GitHub page.

GitHub push confirmation

Success! :]

Where To Go From Here?

Congratulations, you now know how to use Git source control from Xcode, use branches, merge branches, work with GitHub, 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:

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

Team

Each tutorial at www.raywenderlich.com is created by a team of dedicated developers so that it meets our high quality standards. The team members who worked on this tutorial are:

Richard Critz

Richard is on career number three as a professional photographer (after first being a software engineer doing mainframe O/S development for 20+ years and then a stint as a corporate pilot) doing contract iOS development on the side. Some would say he just can't make up his mind. Actually, he just likes diversity!

When he's not working on either of those, he's probably playing League of Legends (with or without the rest of his family).

On Twitter, while being mainly read-only, he can be found @rcritz. The rest of his professional life can be found at www.rwcfoto.com

Other Items of Interest

Save time.
Learn more with our video courses.

raywenderlich.com Weekly

Sign up to receive the latest tutorials from raywenderlich.com each week, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

PragmaConf 2016 Come check out Alt U

Our Books

Our Team

Video Team

... 20 total!

Swift Team

... 15 total!

iOS Team

... 43 total!

Android Team

... 14 total!

macOS Team

... 11 total!

Unity Team

... 11 total!

Articles Team

... 12 total!

Resident Authors Team

... 15 total!