Source Control Workflows in Xcode

Session 418 WWDC 2018

Xcode integrates with GitHub, Bitbucket, and GitLab to help you manage your source code, back up your files, and collaborate with others. Find out how to easily see the changes in your project right in the source editor, and learn how to use Xcode for common source control workflows with Git. See how conflicts occur, how to resolve them, and how to avoid them before they even happen.

[ Music ]

Hi. I'm Eric Dudiak, an engineer on the Xcode team.

And today we're going to talk about source control workflows in Xcode.

Now, developing apps requires making lots of changes in your source code and projects.

To help manage that change Xcode provides a number of source control tools.

So, today we're going to take a look at a few of them.

To start, we're going to take a look at how you create your first project using source control.

Next, we'll look at making and viewing changes to your project using source control.

We're also going to look at how you host and share your repositories with your team, including synchronizing changes.

We'll take a brief look at resolving and avoiding conflicts.

And finally, take a quick look at pull requests and forking.

So first, let's take a look at creating a new project using Git, the industry standard source control system, supported and included in Xcode.

The first step to using Git is to set up your author name and email.

This identifies you in Git commits and makes it easy to identify who made what changes when working on a team.

Xcode supports quickly setting this up in the preferences under the Git options of the Source Control tab.

When creating a new project in Xcode, you can also choose to create a Git repository.

During the Save operation for a new project you can simply check the box shown here, and Xcode will create one automatically.

Now let's look at what that does and what a Git repository is.

So you may be familiar with the typical Xcode project, represented here by a folder on disk.

This includes your source files and things like that.

Now, when you check the box to also create a Git repository, you'll get a .Git folder.

This represents the repository for your project and will be hidden from you normally.

Now it gets put into your project folder, making it a working copy.

The first thing that happens with a new repository created in Xcode is that the whole copy of your project is made and snapshotted at a point in time.

This snapshot of your project is known as a commit.

Each commit in Xcode gets a unique identifier.

As you make changes to our project source code you can create commits, which will take further snapshots of your project throughout time, while you see those changes at the point they were created.

These snapshots make up your project's history.

As you make more, they form something of a timeline.

And this information is what makes up your repository.

So now that we've discussed conceptually how this works, let's take a look at making changes and easily tracking them in Xcode.

One of the easiest ways to do this is with the Source Control Change bar.

It highlights the lines of code you change as you change them.

The change bar makes it easy to see where in a file you've made changes when you come back later.

As changes are made, the bar highlights the lines of files with modifications.

In large files, it also makes it quick to jump between your changes.

In fact, you can quickly jump between them from the Navigate menu in Xcode.

In addition to the change bar, you'll see status flags in the project navigator telling you which files have changed since you last committed.

So now that we've made some changes, let's talk about committing.

These are snapshots of your project at a point in time that you can reference later.

Let's take a brief look at creating these commits in Xcode.

So we've seen you can easily see changes as you make them in your project.

Now, when you're ready to record these changes to your repository, use the Source Control menu to create a commit.

The commit sheet will show all of the changes currently in your project.

They show the side-by-side comparison for you to review.

You can select which files and even which specific changes to include in the commit.

With the appropriate changes selected, enter a message to record the reason for your changes.

This message makes it easy to understand the reason for the changes later when going back to review them.

Now that we've made a couple commits, let's talk about viewing them.

You may want to refer back to them to better understand how your code and project have changed over time.

This can be useful for discovering the timeline of when code was introduced in your project, and why specific changes were made.

Xcode offers several ways to view this history.

First is the comparison mode, which we just saw a little bit in the commit sheet.

Xcode lets you view every file in your project as it has changed through time in this way.

To access it, use the Version Editor mode from the toolbar, seen here.

Clicking and holding the selector will let you jump to a specific mode of the version editor.

The comparison mode provides a side-by-side view of source code changes, allowing you to compare the file between two points in time.

While in the comparison mode, you can change what version of the file you are viewing using the jump bar at the bottom.

Next is the Author View of the version editor.

It is accessed in the same way in the toolbar.

The Author View groups code by the author who made the most recent change to a particular line in the file.

It shows the author, message, and date of the commit.

Additional information about the commit can be seen by clicking on one of the slices.

Last, it is possible to see a log of each change made to a file.

Just like authors and comparison, it is accessed from the toolbar.

The source control log looks at a file's entire history so you can see who made changes and why over the entire history of the file.

Now, sometimes you want a way to see changes made using source control across not just a single file, but your entire project.

To see this, you can select the Source Control Navigator as the second navigator in Xcode.

The Source Control Navigator provides a view of your whole repository by listing branches and tags.

Branches are the individual streams of history that make up your project, such as the current in development work.

When you start a new project, you'll have just one branch, typically named Master.

Selecting it will show the history of that branch, which can be seen here.

In this history view, you can see the history is marked with tags shown in purple.

Tags are bookmarks of particularly important points in your project such as shipped versions.

For more information on branches, viewing history, and tags, please see the 2017 WWDC Session, GitHub, and the new Source control Workflows in Xcode 9.

Now that we've looked at the benefits of having a project under source control, let's look at taking things to the next level.

So far we've seen what happens when a project is just locally managed by Git on your machine.

But in many cases, you'll want to store a copy on a server.

This provides an off-site backup as well as a means to synchronize your changes from one machine to another.

This is also the basis of collaboration in Xcode projects.

Xcode works with any server that supports hosting Git repositories.

In these cases, the features are limited to standard Git operations such as push and pull.

And there is no account to sign into.

You just authenticate on demand as needed.

Xcode also supports many common hosting solutions such as GitHub, BitBucket, and GitLab.com.

With these services, additional features are supported, such as searching for repositories to clone and creating new repositories on the server directly from Xcode.

In addition to the cloud versions of these services, self-hosted and enterprise versions used on premise by larger teams are also supported in Xcode.

So let's take a look at adding one of these hosted accounts.

You can add this account type from Xcode's preferences under the Account Preference pane.

You just sign into the account using your username and password, just like you would on the website.

If you're signing into a self-hosted version, you'll also get a chance to provide the URL for the specific server that you want to connect to.

Now that Xcode is signed into a hosted source control account, we can start sending our changes up to the server and getting other changes from it.

But before we start pushing and pulling code in Xcode, let's take a brief moment to discuss how Git and these hosting solutions handle security.

Git supports two main methods for securing changes when connecting to a server.

The first, HTTPS, is the same way most websites are secured.

It trusts the server based on a certificate and uses a username and password to authenticate you as a user.

The other method, SSH, works a bit differently, particularly with these hosted solutions.

In general, SSH connections for Git are secured using a public and private key pair that is generated on each machine.

The public portion of the pair is then uploaded to the hosting site.

This means that it is easy to have one set of keys per machine.

And often even one per service so they can be managed and revoked much more easily than a password.

Xcode can help you easily do these setup steps for SSH.

When you sign into an account but have not created an SSH key pair locally, Xcode will offer to create a pair.

The private portion of the SSH key should be protected by a pass phrase.

This adds an additional layer of security and prevents it from being used even in the event someone else were to get a copy of your private key.

Once the key pair has been created, Xcode can also upload the public portion of the key directly to the hosting site.

With the public key upload complete, Xcode can transmit Git data securely to the server over the SSH protocol in addition to HTTPS.

That also means we can now create new repositories on the server and clone existing ones.

So let's take a quick look at that.

If we go back to our local only repository, we can now create a new remote for it.

This is a full copy of that repository up on the hosted site.

We can do this from the Source Control Navigator in the Context menu by selecting Create New Remote.

We then have a few options depending on the hosting site.

And we can choose to make it either public or private, based on our exact needs for this project.

It can then be shared with other developers or synched across machines.

And when you want to download a project that has already been hosted on a server, such as on a new machine or when joining a team, you can browse and search for a repository in the clone window, accessed from the source control menu in Xcode.

If you already had the URL to use for the repository, you can directly enter it in the search field.

Additionally, all of the hosting solutions supported in Xcode offer the option to clone projects directly in Xcode via a button on their websites.

For additional information on using hosted services including creating remote repositories and the varies clone workflows, please refer to the 2017 WWDC session GitHub and the New Source control Workflows in Xcode 9.

Now that we have a local and remote copy of our project, we need to make sure that they stay in sync.

In Git that is done by operations known as pulling and pushing.

After you commit one or more changes locally, you will want to push to upload them to the server.

This can be done either directly while committing, right in the commit sheet, or from the Xcode source control menu.

The push sheet allows you to select exactly what branch you want to push to and optionally allows you to include tags you've created locally during the push.

Now, when working with others, it'll often be necessary to get their changes locally.

For this you'll need to do what is referred to in Git as a pull.

This, similarly, can be done from the source control menu.

Now, Xcode offers two options for pulling, either the Git default of using a merge, or Xcode can pull using a rebase operation.

These work slightly differently in Git.

So let's take a look at them.

Here we will look at a conceptual timeline of commits.

If you've made local changes and others have also made changes while you were working, you will have to pull before you push.

In this situation, your work, shown in green, has diverged from your coworker's work, shown here in blue.

To rectify this, you'll have two options in Git: merging and rebasing.

Let's take a look at both.

So again, in this scenario you have two changes to push and three to pull.

When merging, this situation is resolved by creating a new commit, after both yours and your coworker's, that indicates how the divergence should be handled.

With the commits now unified back into a single branch, this can be pushed up to the server and synchronized across all your machines.

A rebase pull works a little differently.

Instead of creating a new merge commit, your local changes are set aside and then replayed after the changes you just pulled.

This can make looking back at history much more simple, as there is no merge commit or divergence in history.

Sometimes when pulling, you will have made local commits that change something in the same place as someone else is making changes.

This can cause what is known in Git as a conflict, where it is unclear how to have both changes coexist.

Xcode allows you to resolve conflicts when pulling or merging a branch.

Xcode will present a sheet similar to the commit sheet with options to take your change, or the other change.

It is also possible to manually edit the file, or take both changes if there are better ways to combine the work.

In this case, we see that two users have both made changes on the same line.

It seems that the local changes are a bit more up to date, so the easiest way to resolve this is to take the left changes.

Now, with all the conflicts resolved, the pull can continue.

If this were a merge pull, conflicts are all resolved at once and the resolution is stored in the merge commit.

If we were doing a pull rebase, it is possible to have to resolve a set of conflicts multiple times as each individual commit is replayed on top of your coworker's changes.

The resolution information in this case is stored on the original commit as if it never occurred.

Now, because conflicts can be a bit annoying to resolve, it can be very useful to anticipate conflicts and avoid having them happen in the first place.

In Xcode Source Control Preferences, the Change bar can optionally be shown to show coworker's changes as they push them.

This makes it easy to tell what part of a file is out of date.

The changes are fetched from the server at a 10-minute interval.

Here we see the Change bar that we saw before, when upstream changes are showing, turns to red to indicate where our coworkers have been making changes.

This indicates a conflict will have to be resolved to reintegrate the local changes.

In many cases it can be easier to pull before making changes to a file that already has changes upstream.

Some details about the conflicting commit can be seen by clicking on the Change bar.

All of the hosting solutions supported in Xcode also support two other common workflows, pull requests and forks.

These are based on Git concepts and features, but are distinct from Git's feature set.

Pull requests are a method for doing code reviews.

It is usually best practice to do all disruptive work to a project on a branch.

This is an isolated line of commit history that is separate from the other history of a project.

Just like when pulling, the work done on a branch will diverge from the main branch of code.

So a merge will be necessary to resolve any conflicts and reintegrate the work.

A pull request, shown here in yellow, is a way to see what will be merged and allow the commenting on that work by other individuals.

Often this will mean making additional changes on the branch before merging and integrating it.

Since pull requests are built on Git's branching model, you can always locally check out that branch from the source control navigator in Xcode when reviewing it.

This lets you build and test the work on your local machine before approving it and merging it back into the main branch.

Forks take advantage of the distributed nature of Git.

Just like how the server copy of the repository and the local copy on your machine are two copies of the same repository, you can create multiple copies of a repository on the same server.

This is often useful when there is a canonical copy that is tightly managed, such as a large open-source project.

A fork can be useful for making experimental changes without disrupting the main copy.

This can be more useful than a branch when there are many contributors, because each fork can have its own named branches, minimizing the amount of noise in the main repository.

Just like the local copy, the fork can be synced with the main copy.

This is often done in the form of PR's and is where pull requests get their name.

Xcode and Git allow setting multiple remotes on your local repository.

So you can sync with both the name and the fork from the same local copy on your machine.

So that covers the basics of using source control and Git in Xcode.

We've seen how to use Git locally to manage your project.

We've taken a look at making and viewing changes in your project.

We also saw how repositories can be hosted and changes synchronized when working with teams.

And when syncing those changes, how you resolve and even avoid conflicts.

And last, we took a brief look at some additional hosting features like pull requests and forks.

More information for this session is available at Developer.apple.com.

And thank you.

Apple, Inc. AAPL
1 Infinite Loop Cupertino CA 95014 US