Chapter 21. Sharing Source

WHAT'S IN THIS CHAPTER?

  • Creating source trees

  • Configuring source control

  • Adding a project to source control

  • Checking out a project

  • Sharing, checking in, comparing, and merging project files

Software development has always been characterized as a rather solitary vocation. The stereotype of a single programmer working into the wee hours of the night, isolated in a basement or attic room, is now mostly the purview of fiction writers. Most modern development is done in teams — even when those teams are scattered across the globe and connected only by the Internet. Members of a team need ready access to the assets of projects they are working on. They need to collaborate on changes and distribute their work to other team members.

Software projects themselves may also need to share resources. A suite of applications developed for a single corporation might need to include a common set of graphics, or share a set of preprocessor definitions. It's impractical to put all of the applications into a single project, and (as you saw in Chapter 5) source file references outside the project folder can be problematic. Projects need a way to share common assets rationally.

Closely related to the subject of sharing source files is the concept of source control management (SCM), or just source control. Source control systems store and track the changes made to source files over time. It is usually the basis of a collaboration system among multiple developers, acting as a mediator between developers, arbitrating changes, and communicating those changes to others. Individual developers have come to appreciate the discipline, security, and accountability that source control systems bring to their development. Even if you are a single programmer working on a single project, you may still want to set up a source control system.

Source control is used for many, if not most, open source projects. If you want to participate in an open source project, you'll want to plug your Xcode project directly into the source control system used by that project. After it is configured, you'll be able to browse the comments and revisions for the project right in the Xcode interface. You can see what new files are available and immediately update your local copy.

SHARING VIA XCODE

Xcode provides two facilities for sharing source files between projects and developers: source trees and source control.

Source Tree Basics

Source trees define folder locations where shared source files reside. There is no restriction on where these are located or on how the source files are maintained. An earlier chapter mentioned a suite of application projects that all need to include the same corporate artwork; a source tree folder could be defined for those graphic files.

Any number of projects can refer to the files in a source tree's location. Changing the location of the source tree changes all of the references in all of your projects simultaneously. For example, if the graphics art department sends you a new CD with updated artwork, all you have to do is pop the disc into the CD-ROM drive, point that source tree to a folder on the CD, and rebuild your projects. Source tree locations are defined individually for each user, so a different programmer can use the same project with source tree locations specific to their environment.

Source Control Basics

Source control, often referred to as version control, is the principal method of tracking historical changes made to source files and sharing source files among a group of developers. It provides three important services:

  • A centralized repository

  • Security

  • Change history

The first service stores the master copy of all source files in a central repository. Changes made to a source file by one developer are sent back to the central repository, where those changes can then be distributed to other developers. This allows multiple developers to work on the same project in a rational manner.

The second service provides a degree of security, protecting against accidental damage to local project files and providing a point for centralized backups.

The third service, and probably the most important, keeps a record of all changes made to a project over time. Developers can quickly see exactly what has changed in each file, and review comments made by the programmer that explain why. Source control can also be used like a time machine. You can reconstruct the state of a project at any point during its lifetime. You don't need to archive a copy of your entire project at every point in its development. For example, if you're working on version 1.6 of your application, but suddenly need to debug version 1.2, simply query the source control system for a copy of your project as it existed on the day you finished version 1.2.

Source trees and source control serve different needs and each is designed to solve a different problem. Some aspects overlap, but generally they complement each other. They can be used independently or in combination. For example, a project from one source control repository can refer to files in a source tree that's being maintained in a different source control repository. What source file sharing and control techniques you employ will depend entirely on your needs and work environment.

SOURCE TREES

A source tree is a named path to a folder on your system. A source tree consists of a

  • Symbolic name

  • Display name

  • Path to the tree's location on your system

Any source file reference in a project can be relative to a source tree location. The location of the tree is independent of the location of the project folder.

For example, if you define a source tree named "buttons" and set it to the location ~/Development/Common/Buttons, any project that has a source reference relative to the "buttons" source tree will look for that file in the Buttons folder inside your local Development folder. If you move that folder, or decide to use a different folder, you simply redefine the "buttons" source tree and every source file reference based on that tree changes.

The "your" in "a source tree . . . on your system" is important. Source trees are defined individually for each user account. For someone else to use a project that refers to files in the buttons source tree, they must also have defined a source tree named "buttons." It doesn't have to have the same display name or be the same path, but it does have to have the same symbolic name. In fact, it makes sense that it wouldn't have the same path — another developer wouldn't have access to your /Users/yourname/Development/Common/Buttons folder.

The source trees defined for your account are global to all Xcode projects that you open. Consider this when deciding on a name for your source tree.

Define a Source Tree

Open the Source Tree tab of the Xcode Preferences, as shown in Figure 21-1.

FIGURE 21-1

Figure 21-1. FIGURE 21-1

Click the + button below the list to define a new source tree. Give it a symbolic name or key, a display name, and a path where the tree can be accessed on your system.

The symbolic name is how Xcode identifies a source tree. It should be simple and must be unique. I prefer to use so-called "reverse DNS" notation, like com.wiley.proXcode3.shared. The symbolic name is stored in each project file reference that's relative to a source tree. Any other developers you share the project with must define a source tree with the exact same symbolic name, or their project file references will be broken.

The display name is how the source tree will appear in the Xcode user interface. It can be anything you want, should be descriptive, and can be changed later. Different users can have different display names for the same source tree.

To delete a tree, select it in the list and click the – button.

Source Tree References

After you've defined a tree, use it to refer to source files in your project. The source trees you've defined automatically appear in all dialogs that add an existing file, folder, or framework to your project, as shown in Figure 21-2.

FIGURE 21-2

Figure 21-2. FIGURE 21-2

Source trees also appear in the reference section of every source file's Info window, as shown in Figure 21-3. You can make any source file's path type relative to any source tree that you've defined. If you are reorganizing a project to use source trees, select a group of source file references and change their path type to the new source tree as a group. Typically, you would set the path type of a group folder to that of a source tree, and leave the members of that group as Relative to Enclosing Group.

FIGURE 21-3

Figure 21-3. FIGURE 21-3

If you open a project that refers to a source tree that is not defined on your system, Xcode attempts to locate the file at the same path it used the last time that tree was defined. If this path is wrong, the file will turn red, indicating that it cannot be located. Define the required source tree, then close and reopen the project to resolve the reference.

To redefine your source trees, close all projects, redefine your source trees, and open the projects again. Xcode resolves the source file paths using the new source tree locations.

SOURCE CONTROL

Source control systems work by storing the master copies of your project files in a repository. A repository is a database of files. A repository might be on the same file system or on a remote system. Xcode might access a repository using direct file access or by sending requests over a network to a source control server. Regardless of the configuration, you interact with the source control system through Xcode or via a source control client installed on your system.

A file stored in a repository is called a managed file. When you want to work with a managed file, you check out the file from the repository. The copy of the file you checked out is called the working copy. If you make changes to the working copy, you later check in the file to record those changes in the repository.

After the changes have been recorded, other developers can check out the file to incorporate your changes into their working projects. You can compare your working copy with other versions in the repository to see exactly what has changed. You can also check out an earlier version of any file and use it instead — maybe because you don't like the changes made by another developer.

Xcode has integrated support for three major source control systems: Concurrent Versions System (CVS), Subversion, and Perforce. CVS was the reigning king of open source control systems but has since been usurped by Subversion. An installation of Subversion is included in the Xcode Developer Tools.

  • You can get more information about CVS at http://www.nongnu.org/cvs/.

  • Learn more about Subversion at http://subversion.tigris.org/. You'll also want to read Version Control with Subversion, available at http://svnbook.red-bean.com/. You can purchase a printed copy of the book or download an electronic version for free.

  • Perforce is a commercial source control system available from Perforce Software Inc. If you need a commercial source control management system, you'll want to check out Perforce at http://perforce.com/perforce/products.html.

This chapter concentrates on setting up Subversion and configuring Xcode to use it. Subversion was chosen for this book over CVS and Perforce for four reasons:

  • Subversion is, in many ways, superior to CVS — which it was designed to replace.

  • Subversion is less expensive than Perforce (it's free).

  • Subversion is very popular among open source projects.

  • Subversion comes pre-installed with Snow Leopard and is the de facto standard for Xcode source control.

That's not to say that CVS or Perforce are bad choices. If you already have a CVS or Perforce repository, then your best choice is clear.

Using Source Control

Using source control through Xcode can be broadly divided into three phases:

  1. Define a repository.

  2. Check out a project from a repository.

  3. Use source control from within the project.

The next section describes how to define a repository. The sections starting with "Browsing Repositories" describe how to view the contents of a repository and check out an Xcode project. Once you have a project checked out under source control, all of your subsequent interaction will be through the project, described starting in the "Source Control Interface" section.

Defining a Repository

As mentioned earlier, a repository is a centralized collection of managed files. Before you can use source control, you must define the location and characteristics of the repository, or repositories, that you want to use. Just like source trees, each developer must create an equivalent repository definition if they expect to share the same set of managed files. The definitions might be the same or different. For example, you might create a source control repository on your local drive. Later, you decide to collaborate with another programmer and open access to that repository via the Internet. Your repository definition would still refer to your local file system while his would refer to a remote repository.

All of the repositories you use are defined in the SCM tab of the Xcode Preferences (Command+,), as shown in Figure 21-4.

FIGURE 21-4

Figure 21-4. FIGURE 21-4

Each repository definition has three parts:

  • The source control client used to access the repository

  • The access method and parameters

  • A path to the root directory within the repository

Click the + button below the list of repositories to define a new repository. A dialog sheet will prompt you for a repository name and the client that will be used to access the repository. The client you choose — SVN, Subversion, or Perforce — will determine what settings are needed to define the repository. The name identifies the repository definition in Xcode and can be changed later.

Clicking OK creates a new repository definition. Fill in the settings as required. What those settings are is entirely dependent on how the repository was created and where it resides. If you didn't set up the repository, contact the administrator of the repository for the correct settings. In some situations they may need to create a user account and password for you, or you might have to register an SSH key.

For Subversion repositories, the settings to configure are:

SUBVERSION SCM SETTING

DESCRIPTION

Scheme

http, https, file, svn+ssh, and so on

Host

Host name or IP address of server (for network schemes)

Path

Path to root directory in repository (may include the path to the repository itself)

Port

Network port to use (for network schemes)

User

User account name (if required)

Password

Password for user (if required)

The URL setting and the other settings mirror each other. Whatever you enter for the other settings is combined into a single URL, and whatever you enter into the URL setting is decomposed into its individual settings. If your Subversion administrator or open source project provides an URL to the repository, paste that into the URL field; Xcode will fill in the settings for you. Conversely, if you need an URL to access your repository via the source control client or some third-party tool, Xcode has already constructed it for you. Copy the URL and paste it into your client.

The path setting defines the top-level directory within the repository where this repository definition is anchored. You may create a single repository definition that points to the top-level directory in the repository, or you can define multiple repository definitions that point to individual subdirectories within a single repository. Which you choose is a matter of personal preference, style, and any security restrictions that might be in place. I tend to define a repository definition for each set of related projects, even if more than one set shares a single repository. When I browse that repository, I'm not distracted by unrelated directories.

The path setting might also describe the location of the repository itself. That is, the first part of the path is the location of the repository database, and any remainder is a path within the repository structure. It's not obvious where the path to the repository ends and the path within the repository begins, but it's usually immaterial.

Repository on a Local File System

If you've created a Subversion repository on a local volume, you can use the file scheme to access it directly. Set the following two settings:

  • Scheme: file

  • Path: /Complete/Path/To/Repository/Subpath/To/Root

The file scheme path defines the location of the repository database and must begin with an absolute file system path to the repository database. Any remaining path components are interpreted as subdirectories within the repository.

Creating a local repository is best when you need source control for archiving and version tracking. If you plan to collaborate with other developers, you'll probably want to create your repository on a networked server.

If you don't have access to an existing Subversion repository, or just want to get started using Subversion for your own projects, jump ahead to the "Creating a Subversion Repository" section, and then come back here to define it in Xcode.

Repository Access via a Web Server

Subversion repositories designed for wide consumption are often set up behind a web server, such as Apache. Source control requests are made through the web server and passed along to the source control server. This is very common with open source projects. If you don't have a URL for the project's repository, you will need to configure the following settings:

  • Scheme: http or https

  • Host: www.host.net or 10.0.254.1

  • Path: /path/to/repository/root/directory

  • Port: alternate port (optional)

  • User: account name (optional)

  • Password: account password (optional)

Setting up Subversion to work behind a web server is a non-trivial exercise. You're most likely to encounter this for repositories that have already been created.

Subversion Server via a Network or SSH

Subversion supports a couple of its own client/server schemes. The svn scheme lets you set up a Subversion server on a computer and interact with it via a dedicated IP port. This is useful for development groups on a local area network, and is much easier to set up than a web-hosted Subversion server. It has the disadvantage of limited security, and the non-HTTP IP port might be blocked by firewalls, neither of which are typically concerns on a private LAN.

To set up a Subversion server, see the "Server Configuration" chapter of the Version Control with Subversion book.

A less well-known scheme is svn+ssh — also described in Version Control with Subversion. This scheme performs Subversion actions by executing source control client requests on a remote computer via SSH. The remote computer doesn't need to be running a Subversion server, but it does need to be transparently accessible via SSH. This usually means that you've installed pre-authorized SSH keys for the remote account on the server.

Because there's no server process involved, the svn+ssh scheme is easy to set up — not that much more difficult that setting up a local repository. You will also want to install pre-authorized SSH keys so that Xcode doesn't prompt you for a password or passphrase every time it needs to interact with the repository. If you followed the instructions for setting up remote debugging in Chapter 18, you're already done. Security in svn+ssh is excellent, because it's supplied by SSH.

The primary disadvantage to svn+ssh is performance; invoking the source control client via SSH is slower than a dedicated server would be. Nevertheless, I highly recommend this configuration for small, geographically scattered teams that need secure source control with modest performance and minimal maintenance.

Checking Your Repository Definition

The repository definition pane automatically tests the settings as you enter them. A status indicator at the bottom of the pane shows Xcode's success in using those settings. The possible results are:

  • Incomplete Configuration

  • Authenticated

  • Error: Description of error

The first message means that your definition is still missing some required setting or settings.

A green "Authenticated" message means that Xcode has successfully accessed the described repository using your settings. Congratulations, you are ready to start using your repository!

A red "Error" message indicates that the settings are wrong or that some other configuration or communications issue is preventing Xcode from accessing the repository. Read the error description for clues and carefully review your settings.

Source Control Options

Before running off to use your newly defined repository, take a quick look at the Options tab of the SCM preferences pane, as shown in Figure 21-5.

FIGURE 21-5

Figure 21-5. FIGURE 21-5

I recommend checking the Configure SCM Automatically option. When you check out a project via source control, the working files are under the influence of the source control system — but the project will not necessarily be configured to use source control. Turning this option on allows Xcode to configure your project automatically to use source control when it detects that it's appropriate. Otherwise, you'll have to configure the project manually as described later in the "Configuring a Project for Source Control" section.

The Save File Before SCM Operations automatically saves your files to disk before performing any source control action (check in, check out, and so on). I recommend setting this option for the same reasons I recommend setting the Always Save Files option in the Building pane.

The remaining options control how files are compared in Xcode. Most often this is when you want to compare the working version of your file with a different version in the repository. Some of these settings also apply to comparing files in snapshots.

The Comparisons Handling option selects the application and layout used when comparing two files. Xcode will obtain both versions of the file and pass them to the application for side-by-side comparison. Your choices are:

  • Xcode

  • FileMerge

  • BBEdit

  • Other

Using Xcode is the fastest, but also the least capable. Xcode's built-in comparison window is adequate for comparing two files but is awkward if you want to selectively replace individual sections of your working files with those from the revision.

The FileMerge application is a standalone file comparison utility included in the Xcode Development Tools package. If you own Bare Bone's BBEdit application, you can use its excellent file comparison interface instead.

The Other setting lets you select any third-party comparison tool that accepts two filename arguments and compares them.

Xcode can also compare files using the traditional POSIX diff tool. The settings under Differencing define some format arguments that Xcode will pass to diff. See the diff man page for a complete description of these settings.

Your SSH Certificates

If access to your repositories requires a private SSH encryption key, those keys will appear in the SSH tab of the SCM preferences pane.

The keys that appear here will be any keys installed in your account's .ssh directory and any SSH keys in your keychain.

You can add security by entering a passphrase for one or more keys. Xcode will prompt you for this phrase whenever it needs to use the selected SSH key. Leave the passphrase field empty to bypass the prompt.

Creating a Subversion Repository

If you're setting up Subversion for your own personal use, or just want to try out source control, the simplest solution is to create a local repository. The following Terminal command creates an empty repository named Subversion in your Documents folder:

svnadmin create ~/Documents/Subversion

Now go to Xcode's SCM preferences pane and create a repository definition, as described in the "Defining a Repository" section. For the example given, your settings will be:

  • Client: Subversion

  • Scheme: file

  • Path: /User/youraccount/Documents/Subversion

You can elect to use a different repository name and location as you see fit. Note that you can't use a user-relative path (~/Documents/...) in the repository definition setting.

Filtering Out Noise

Ideally, your source control repository should contain only the files needed to build your project. This includes your project document, source files, resources files, and so on. It should not contain any intermediate products (object files, compiled data models, help indexes), superficial files (code sense index, disassembly files), or any final products (applications, bundles). There are a number of techniques for excluding these so-called noise files from your repository.

Note

The "no products" rule isn't universal. Repositories are sometimes used to store finished products. This is particularly true of frameworks, libraries, and other products that are source files for other projects. A finished framework that's used by a large number of developers can be checked into a repository and treated like any other immutable project resource. This spares the project from having to check out, and maintain a dependency on, the project that produces the framework.

Xcode's default folder organization conveniently places all intermediate files, project indexes, and products inside the project's build folder. Wow, that makes it easy! All you have to do is leave the build folder out of your repository and 98 percent of your noise files have been eliminated. There are basically two approaches to this, and which one you take determines whether you address other noise files at the same time.

Omitting the build Folder

The simplest technique — as long as you consistently remember to do it — is simply to leave the build folder out of repository. With no build folder in the repository, your project's build folder will not be a working folder (that is, managed by source control), and the source control system will ignore its content. To accomplish this, make sure the project has no build folder when it's first imported (see the "Adding a Project to Source Control" section). Do this by either:

  • Closing the project and deleting the build folder before importing it into the repository

  • Redefining the location of the build folder so that the intermediate files and products are written to a location outside the project folder

Either of these will eliminate the build folder from the project when it is imported, so there will be no build folder in the repository. After that, just be careful not to do anything that would inadvertently add or import the build folder.

Excluding the build Folder

A more rational approach, in my mind, is to configure Subversion so that it automatically ignores the build folder. Once that's done, you don't have to think about it. Whenever you import a project or check in files, Subversion will automatically ignore the contents of the build folder. To do that, set Subversion's global-ignores property in the ~/.subversion/config file. An excerpt of my ~/.subversion/config file is shown here:

### Set global-ignores to a set of whitespace-delimited globs
### which Subversion will ignore in its 'status' output.
# global-ignores = *.o *.lo *.la #*# .*.rej *.rej .*~ *~ .#* .DS_Store
global-ignores = .DS_Store build *.mode* *~.nib *~

I commented out the default definition of global-ignores and replaced it with my own. The global-ignores property is a space-separated list of filename patterns (globbing) that Subversion will exclude/ignore on your entire system. The patterns I defined exclude the following:

PATTERN

FILE DESCRIPTION

.DS_Store

The hidden .DS_Store file used by Mac OS X to manage the appearance of folders in the Finder

build

Any file or folder named 'build'

*.mode*

Window position, toolbar, and other superficial layout information stored per-user inside the project document

*~.nib

Backup .nib files created by Interface Builder

*~

Generic backup files created by some editor applications

These patterns automatically exclude all intermediate, product, and noise files from my repository.

Warning

The hazard of setting an exclude pattern like "build" is that you might someday encounter a project that has a subdirectory named build that contains source files. It's unlikely, but if it did happen Subversion would dutifully ignore that folder too, probably to the detriment of the project.

You can configure per-folder filters in Subversion. These can add additional ignore rules for a specific project or override the global rule. See the Version Control with Subversion book for the details.

Excluding Other Noise Files

Generally, noise files are files that are not significant to building the project. It's not that they don't contain any useful information, but deleting them won't affect the build. The advantage of checking noise files into source control is that they often contain information that you want to preserve or share. When I check out a project, it would be nice if that project remembered all of my settings and preferences.

The disadvantage is that noise files tend to change often. The source control system will want you to check any modified files back into the repository along with a change comment. This often results in many revisions to the repository that don't represent significant changes to the project. This can be annoying to other developers who must constantly wade through repository changes that have no real impact on their work.

The noise files I exclude are .DS_Store and the *.mode* files found inside the project document. These are classic noise files because they store window position and other ephemeral layout information that has little bearing on my workflow. If I export a project, and it doesn't save the size of the project window, it won't cause me any grief.

The noise files that are most contentious are the per-user *.pbxuser files stored inside the project document. Each user.pbxuser file stores preferences and other settings for that developer, such as breakpoints, smart groups, and bookmarks. If you've worked on a project for a significant amount of time, this can represent a lot of customization that you might not want to lose when the project is checked in. On the other hand, every time you set a breakpoint your project document will have appeared to have changed, which can result in a lot of extraneous project document updates that other developers must deal with.

As a rule, the more developers working on the same project the more problematic noise files become. You and your other developers will have to decide whether the noise is worth the extra data.

Browsing Repositories

Once you have one or more repositories defined, you can open the Repositories window by choosing the SCM

Browsing Repositories
FIGURE 21-6

Figure 21-6. FIGURE 21-6

On the left are the repositories that you've defined. Select one and the browser in the upper pane will let you navigate through the most recent content of each repository directory. The lower pane shows a running log of the actual SCM commands that Xcode is issuing in order to perform whatever actions you request.

The Reload toolbar button re-fetches the information from the repository and refreshes the display. The Configure button jumps to the SCM pane of the Xcode preferences — the one you've been using in the previous sections.

You don't typically spend a lot of time in the repositories window. The significant actions you'll perform here are checking out and importing/exporting entire projects, which are all described in subsequent sections. Once you do that, almost everything else is done through the project.

The repository window does provide a basic set of repository commands. Besides perusing the contents of a repository, you can:

  • Create a new repository subdirectory

  • Copy a repository file or directory

  • Move (rename) a repository file or directory

  • Delete a repository file or directory

These commands are all self-explanatory. Just remember that you're modifying the content of the repository, not the files in your file system.

While the command set looks simple, there's actually a lot you can do with it. If you've taken time to skim the Version Control with Subversion book, you already know that Subversion doesn't use metadata (a.k.a., tags) to demarcate releases or branches. Instead, it uses a convention of subdirectories. The main development files are stored in a trunk directory. Releases are defined by copying the entire trunk into a subdirectory of the tags directory. Similarly, copying a project folder into a branches subdirectory creates a branch. Thus, tagging milestones and branching are simply matters of copying one repository directory into another.

Speaking of subdirectory conventions, if you've just created a new Subversion repository for a single project, you'll want to start by creating a trunk subdirectory, as shown in Figure 21-7. You can create tags and branches directories when you need them.

FIGURE 21-7

Figure 21-7. FIGURE 21-7

When you create the subdirectory, Xcode prompts you for a comment. Every change to the repository is recorded, annotated with an explanation that you provide, and no information is actually destroyed. Deleting a repository folder doesn't delete it from the repository; it simply means that, in the most current version of the repository, the directory no longer exists. Its previous incarnations are still, and will always be, available.

If you plan to store multiple projects in a single repository, I recommend creating top-level folders for each project, and then creating trunk, tags, and branches subfolders within each project folder. Other developers prefer to create single top-level trunk, tags, and branches directories and then have subdirectories for each project within those. There are advantages and disadvantages to each. Consult the Version Control with Subversion book before embarking on any major repository design.

Adding a Project to Source Control

So far, you've created a repository, defined it in Xcode, and can browse the repository in the repository window. Now you want to place your project under source control. The basic steps for putting a project under source control are:

  1. Create the project.

  2. Import the project into the repository.

  3. Check out the project under source control.

  4. Configure the project to use source control.

The first two steps are required because placing a project under source control presents a bit of a chicken-and-egg problem; a project can't be under source control until it's been checked out of the repository, and it can't add files to a repository until the project is under source control. To get around this conundrum, you begin by importing a project into the repository.

Importing takes a project folder — actually any arbitrary file or folder — that is not under source control and adds it to the repository. You can do this with anything at any time, but the most common reason is to get a project into source control in the first place.

In the repositories window, select the repository directory where you want the project folder copied into the repository, or select just the repository to import it into the repository's root directory. Click the Import button in the toolbar and choose the project folder to import, as shown in Figure 21-8.

FIGURE 21-8

Figure 21-8. FIGURE 21-8

Click the Import button and Xcode copies the entire project directory into the repository as a new subdirectory.

Warning

Before checking in your project, make sure you have either deleted your build folder, relocated your build folder, or followed the directions in the section "Filtering Out Noise," so that the files produced by your project aren't copied into the repository. Only the documents in a project that produce something should be under source control — not any of its results.

You might think that all you have to do now is flip a switch in the project and start using source control, but you're not there yet. You must now check the project back out of the repository — only then do you have a set of working files that are under source control.

Checking Out a Project

Checking out a project (or any set of files) from source control creates a working copy of the managed files in the repository. Working files are special. The source control system maintains additional information about the file and its relationship to its managed counterpart in the repository. Before you can use source control from within an Xcode project you must first check the project out.

Somewhat counterintuitively, you must check out your project to someplace other than its original project folder. Either checkout the project to a new location, or first trash, move, or rename the original project folder. I wouldn't delete it yet — just in case something goes wrong.

In the repositories window, select the repository folder you want to check out. If you want to check out the entire repository, select only the repository. Click the Checkout button and select a location where the working project files will be written, as shown in Figure 21-9.

FIGURE 21-9

Figure 21-9. FIGURE 21-9

The default name for the enclosing folder will be the repository or folder you're checking out. If you want to change it, do so now. The reasons you might want to change it are varied, but one common reason is that Xcode and traditional Subversion repositories don't use the same directory structure.

In an "old school" Subversion repository, a project folder named MyProject would appear in the repository as /MyProject/trunk, /MyProject/tags, and /MyProject/branches. When you check out trunk, the folder "trunk" becomes the project folder. This is probably not what you wanted. The solution is to check out trunk from the repository, but rename it to MyProject before clicking Checkout.

All of the files in your project have now been checked out, and all of the working copies of those files are under source control. You can now skip ahead to the "Configuring a Project for Source Control" section.

Exporting a Project

Exporting a project is the inverse of importing one, and is the complement to checking one out. Exporting a project produces copies of the managed files in the repository, but does not create working files. In other words, it simply copies the files in the repository to your local drive without any of the source control metadata required to manage them. An exported project is not under source control.

Use the export command when you simply want to obtain an unencumbered copy of a project or some files. For example, let's say there's an open source project that you're interested in looking at. You're not a contributor to the project (yet), and have no need to track changes or compare files to older revisions. You would use the export command to fetch a copy of the project sans any source control information or ties to its originating repository.

Similarly, you might be working on version 1.6 of your application and need to build version 1.2. If you have no intention of creating a new branch or reviewing changes, simply export the project from the 1.2 tag directory into a new folder and build it.

Configuring a Project for Source Control

The last step is to configure the project to use source control. Open the Info window for the project group, choose the Project

Configuring a Project for Source Control
FIGURE 21-10

Figure 21-10. FIGURE 21-10

This portion of the project's Info window describes the project's name, location, and the roots of the project. The project roots is a list of directories that encompass the assets of the project. For most projects, everything is contained within the project's folder, which appears in the list as <Project File Directory>.

Because you just checked the entire project out from source control, the first order of business is to enable source control for the project folder. Click the Configure Roots & SCM button. A sheet appears listing all of the roots of this project and the source control repository each is associated with. For now, all you are interested in is the repository for your <Project File Directory>. If the Configure SCM Automatically preference is on and Xcode was able to unambiguously determine which repository the project came from, the repository for your project may already be configured. If that's the case, you can skip to the next section and start using source control from within your project.

If SCM has not been configured for your project, do so now. First, select the source control management system you're using from the pop-up menu at the top of the sheet. All of your project roots must use the same source control system. For example, you can't have one root managed by Subversion and another by Perforce.

Now select the repository from where your project was checked out, as shown in Figure 21-11. Here, Xcode plays matchmaker; it compares the source control information in your working directories with the definitions of your repositories, and suggests which repositories appear to be valid choices.

FIGURE 21-11

Figure 21-11. FIGURE 21-11

Click OK to accept your changes. Once at least one project root has been configured to use source control, the source control interface within Xcode is enabled. If this is a single, self-contained project, move on to the "Source Control Interface" section.

Setting a root's repository to none disables source control management for that directory tree.

The Configure SCM Repositories command at the bottom of the menu jumps to the SCM pane of the Xcode preferences, which you used at the beginning of this chapter to define the repository.

Project Roots

A single project can administer source control in multiple directory trees by defining additional project roots. As a rule, you should always define the repository for your project's folder. If your project incorporates assets outside your project folder, those assets are under source control, and you want to manage them from within your project, add each top-level directory as a project root by following these steps:

  1. Click the Configure Roots and SCM button.

  2. Click the + button at the bottom to define a new root.

  3. Choose the top-level folder that contains the working (checked out) assets.

  4. Choose the repository that the working files came from.

Naturally, if your additional root directories were checked out of repositories other than the one your project is in, you'll first have to define those repositories in the SCM pane of the Xcode preferences.

There are a number of scenarios in which a project would have multiple roots:

  • The project builds several subprojects and you want to have source control over the files in those subprojects from within your project.

  • Your project uses the files in a source tree directory that is checked out from a repository.

  • Your project includes a shared development asset, like a framework, and you'd like to be able to update it via source control from within the project.

The paths to your project roots are all always relative to your project folder. If you add roots to a project and then change anything that invalidates this relationship — relocating a source tree, for example — the source control features for those files will simply stop working until you update your root paths.

Source Control Interface

Interaction with source control in Xcode is done primarily through the commands in the SCM (Source Control Manager) menu and the SCM tab in various Info windows. File-specific commands apply to either the selected file or group of files in the project window, or to the currently active editor pane. This chapter often refers to "selecting a file" before executing one of the SCM commands. It's implied that the same command would apply equally to the file in the active editor pane.

As soon as you enable source control for a project, a new SCM tab appears in the project's Info window, as shown in Figure 21-12. A similar SCM tab appears in the Info window of all project files that are under source control.

FIGURE 21-12

Figure 21-12. FIGURE 21-12

The State field shows the current status of the project document or source file. This tells you if the file is up-to-date (that is, it is the same as the most recent version of the file stored in the repository), if it has been modified locally, if it needs to be updated, and so on. The Local field shows the version of the file that you last checked out of the repository — also called the base revision — and the Latest field shows the most recent version stored in the repository.

Below the file's status is the list of revisions. Each revision has a number, an author, a date, and a comment. Select one to show the full details of the revision in the lower pane of the window.

Use the four buttons below the detailed description to update or see the annotation view of a selected revision in the list. You can select one revision in the list and compare or diff it against the working copy of the file, or you can select two revisions in the list and compare them to each other. All of these commands are explained later in this chapter.

SCM Smart Group and Window

The SCM smart group in the Groups & Files pane lists the pending source control operations for the project, as shown in Figure 21-13. The details pane for the SCM smart group displays an additional source control status column — the left column, next to the document icons shown in Figure 21-13.

FIGURE 21-13

Figure 21-13. FIGURE 21-13

The SCM column shows the status of any pending source control actions or conflicts. When you make changes to a project in Xcode, you are only changing the local copy of the project. None of the changes you make propagate back to the source control repository until you commit those changes. As you work on the project — adding, removing, and editing files — Xcode keeps track of all of the changes that you've made. These appear in the SCM smart group. When you commit your changes, Xcode executes the pending actions in the SCM smart group, dutifully adding, removing, and modifying files in the repository to mirror those in your project.

You can also view the SCM status of files in any other details pane by enabling the SCM column, as shown in Figure 21-14. Right/Control+click the column titles to show or hide the SCM column.

FIGURE 21-14

Figure 21-14. FIGURE 21-14

The SCM column contains a single character that indicates the status or pending SCM operation of each file, as described in the following table:

SCM STATUS

DESCRIPTION

(blank)

The file is up to date. It is the same file as the latest version in the repository. You have not modified it, nor are there any newer versions available to be checked out.

M

The file has been modified locally. The changes will be written to the repository the next time the file is committed.

U

The file needs updating. The file has not been modified locally, but the repository now contains a newer version of the file than the one you have checked out.

C

The modifications to the file are in conflict. There is a newer version of this file in the repository and you have modified the local copy of the file. This usually occurs when two or more developers are making changes to the same file simultaneously. The "Resolving Conflicts" section tells you how to merge your changes with those in the repository.

A

The file will be added to the repository. The file is not currently in the repository or under source control, but you have instructed Xcode to import it when you commit it or the entire project.

R

The file will be removed from the repository. You have deleted (or renamed) the local file. The next time you commit the project, the corresponding file in the repository will also be deleted.

?

The repository has no information about this file. This is usually because it is a new file whose status hasn't been changed to "add" yet.

-(hyphen)

The file is in a directory that does not exist in the repository. When you're adding folders to your project, you first add them to the repository using your source control client. After the enclosing folder is added, you can add files to the repository using either Xcode or your client tool.

Choose the SCM

FIGURE 21-14

Double-clicking the SCM smart group or choosing the SCM

FIGURE 21-14
FIGURE 21-15

Figure 21-15. FIGURE 21-15

This window shows more detailed information about the SCM status of each file. Like the SCM smart group and details pane, it lists the name and status of each file. It also lists:

  • The base revision of the working file

  • The author of the base revision

  • The check-in date of the base revision

  • Update status, if there's a newer version of the file

Note

The base revision is the version of the file you checked out of the repository. It's the original version of your working copy, before you made any changes. It is not necessarily the current version of the file or the latest version in the repository.

In the lower-left corner of the window are a small log button and a pop-up menu. Clicking the log button toggles the display of the SCM transaction log, as shown in the middle of Figure 21-15. The log shows the commands issued to, and the results from, the source control client used for this project.

The menu button next the log button selects one of three display modes for the SCM window:

  • All

  • Interesting

  • Flat

The All setting displays all of the files in your project. The Interesting and Flat settings show only pending SCM actions (modified files, new files, files that need updating, pending deletes). The Interesting choice organizes items hierarchically, whereas Flat lists them alphabetically.

At the bottom of the window is a standard editor pane, which you can collapse by dragging the pane divider to the bottom of the window.

Committing Changes

No change occurs in the repository until you commit it. Choose the SCM

Committing Changes

Committing individual files is usually safe, as long as you are sure the changes being checked in will be valid in the absence of other changes that you have yet to commit. Whenever a commit includes the addition or removal of files, you should endeavor to commit those actions along with the corresponding changes made to the project document. The safest action in this situation is to commit the entire project. The "Source Control and the Project Document" section explains this in more detail.

Note

In general, your goal when checking in changes should be to maintain the integrity of the project in the repository. If you make changes in two files that depend on each other, check both of those files in together. If you don't, you'll have a version of the project in the repository that won't build because a change made to a source file was checked in, but the matching change made in its header file was not.

When you commit a change, Xcode prompts you for a comment, as shown in Figure 21-16. You should be descriptive and complete. The comments you enter will be attached to every transaction required to complete the commit. Source control comments are another form of documentation. The modifications made in your project are only comprehensible if you explain what you changed and why, both clearly and accurately.

FIGURE 21-16

Figure 21-16. FIGURE 21-16

Discarding Changes

If, for whatever reason, you decide to abandon the changes that you've made to a file, you can discard them and revert to the base revision — the version you originally checked out from source control. This is not necessarily the latest version of the file in the repository. When you discard changes to a file, Xcode presents the warning dialog shown in Figure 21-17.

FIGURE 21-17

Figure 21-17. FIGURE 21-17

You can't discard changes by simply checking out a different revision of the file (as discussed in the "Updating Files" section). Xcode won't overwrite your local file with a revision from the repository until you explicitly discard your changes. This prevents you from accidentally wiping out hours, if not days, worth of work. If you do try to update a modified file, you will only end up changing the base revision associated with that file. This action will either associate the file with the latest revision (containing as-yet uncommitted changes), or it will put the file in conflict mode. The "Resolving Conflicts" section explains how to merge conflicting changes.

Adding Files to Source Control

Adding a file to the project involves first adding the file to the project and then adding it to the repository. How you add the file to the project is entirely up to you. You can use any method discussed in the book so far for adding an existing file or creating a new one. (Chapter 5 contains detailed instructions on adding files to a project.) After the file is added to the project, the SCM status of the file may be ?, -, or blank, indicating that the file, or the folder that contains the file, is unknown to the repository.

In the case of an unknown file (?), select the file in the source group or details pane and choose the SCM

Adding Files to Source Control

Warning

If you've accidentally created a pending add (delete, rename, or similar) action, you can usually cancel it using your source control client. In Subversion, use the catch-all revert command, as in svn revert Help. The pending add action for the Help folder is forgotten, essentially taking you back to the time before you added it. Revert only works with pending actions; once you've committed an action, the change becomes part of the repository — forever.

If you've created files inside a folder that is not in the repository, you'll need to add that folder to the repository before Xcode will pay any attention to its files. Use one of these two methods to add a folder to your project:

  • If the folder is represented by a source item in the project, select it and use the SCM

    Adding Files to Source Control
  • If the folder does not appear as a source item in Xcode, use your source control client to add the folder, and optionally its contents. For example, the Subversion command to add an existing Help folder is:

    cd ~/Development/MyProject
    svn add Help

If you add just the folder, the status of the files in that folder will change to ?, indicating that Xcode now sees them as unknown files. You can now use Xcode to add those files to the repository.

Warning

If you find yourself tempted to import the new file or folder, don't. The folder that -contains the new file is under source control, but the new files aren't — thus, the ? status. If you import the files into the repository you now have a local file (not under source control) that duplicates a managed file in the repository. Subversion treats this situation as a conflict and will refuse to check the file in or out until it's resolved. If you find yourself in this situation, discard the local copy and check out the file from the repository. Now you have a working copy under source control.

Whenever you make source control changes using the client tool, the repositories window, or through any other means outside the project, choose SCM

Adding Files to Source Control

Deleting Files under Source Control

Deleting a file requires that you remove it from both the project and the source control repository. When you delete a file reference from a project, Xcode gives you the option of removing just the file reference or physically deleting the source file. If you choose the latter, and the file exists in the repository, Xcode presents a second dialog, shown in Figure 21-18.

FIGURE 21-18

Figure 21-18. FIGURE 21-18

If you choose to remove the file, a repository remove action is added to the SCM smart group and the file is removed from the repository when the action is committed. If you choose not to remove the file, the file is deleted locally but remains in the repository. If you choose to remove the repository file as well, the project displays a grey (disabled) reference to the file in the project, indicating a reference to a file that has been deleted. After the action has been committed and the file in the repository has been removed, the phantom reference to the deleted file will disappear.

Renaming Files under Source Control

Renaming a file in a source control repository is as simple as renaming the source item in your project. In the repository, the action consists of removing an existing file and adding a new file with a different name. When you rename a source file in Xcode, it presents the dialog box shown in Figure 21-19.

FIGURE 21-19

Figure 21-19. FIGURE 21-19

If you choose to rename the file in the repository, Xcode creates two SCM actions: the first action removes the old file, and the second action adds the same file back into the repository with its new name. Commit these two actions together to complete the rename.

Updating Files

When a newer version of a file exists in the repository, Xcode displays a U as the SCM status for the file. This indicates that you must update this file if you want the latest version.

If you want to retrieve the latest version of a file, select one or more files in the project window and choose the SCM

Updating Files

You can also retrieve an earlier version of a file. Select a single file and choose the SCM

Updating Files
FIGURE 21-20

Figure 21-20. FIGURE 21-20

You can also update (check out) multiple files at or near a specific revision using the SCM

FIGURE 21-20

Note

When you revert to an older revision of a file, the SCM information about that file may revert as well. This depends on which source control system you are using. If it does revert, the file's SCM history appears to be current when it isn't, listing only the revision information up to the revision you retrieved. The file does not display a U indicating that a newer version of the file is available. Use the SCM

FIGURE 21-20

You can bring an entire project up to date by selecting the project group in the Groups & Files pane of the project window and choosing the SCM

FIGURE 21-20

Warning

Whenever you update the project document itself, close the project and reopen it. Changes made to the project document by updating will not be "seen" by Xcode until it is forced to reread the project document.

Comparing Revisions

You can compare the files in your project with other revisions using the SCM

Comparing Revisions

Choose the file you want to compare, and then choose the Latest, Base, Revisions, or Specific Revision command from either the Compare With or Diff With submenu:

  • Latest compares your working file against the latest revision in the repository.

  • Base compares your working file against the base revision that you most recently checked out, which may or may not be the most recent revision.

  • Revisions presents a list of file revisions, along with their comments, for you to choose from.

  • Specific Revision prompts for a revision number — which Xcode assumes you already know — and then retrieves that revision, or the most recent revision that's older than that revision.

If your working file is already the same as the latest or base revision, the appropriate commands are disabled. If you want to browse the revisions and their comments, choose the Revisions command.

All of the various Compare With commands use the comparison utility selected in the SCM preferences pane (see the "Source Control Options" section, earlier in this chapter). I recommend using FileMerge or BBEdit (if you own it). This chapter demonstrates using FileMerge.

You've already used Xcode's built-in comparison editor when comparing files in snapshots. This choice has the advantage of being fast, and it doesn't launch another application, but Xcode's tool is significantly limited in its ability to selectively borrow and undo changes from the other file, which I feel makes it more of a hindrance when working with source control.

When launched by Xcode, FileMerge shows a side-by-side comparison of the two files, as shown in Figure 21-21.

FIGURE 21-21

Figure 21-21. FIGURE 21-21

The window graphically illustrates the changes made between the two files. In the example shown in Figure 21-21, four groups of changes (visible in the window) were detected. Scroll through the file to see other differences, or use the Find

FIGURE 21-21

The Compare With commands give you one additional command not available in the Diff With submenu. SCM

FIGURE 21-21

If you use one of the Diff With commands, Xcode opens a text window that displays the output of the diff command, as shown in Figure 21-22. The SCM options, described in the "Source Control Options" section, let you choose a number of diff formatting options.

FIGURE 21-22

Figure 21-22. FIGURE 21-22

Merging Changes

The file comparison and merge utilities can also be used to combine the differences between two files. This is useful in a number of situations. One of the very liberating aspects of working with a source control system is the freedom to make experimental changes to your code. If you decide that you don't like the changes, you can compare your working copy with an earlier version of your source code stored in the repository. The file comparison utilities let you "undo" your changes by selectively moving blocks of code from the earlier version back into your working one. This is a much more surgical correction than merely discarding all of the changes in a file.

Both the FileMerge utility and BBEdit's compare files function place both old and new versions of the file on the screen side by side. Both highlight the blocks of text that differ between the two. For each difference, you can choose whether to keep it as it is or revert to the alternative version in the other file.

The FileMerge utility does this with a merged file pane at the bottom of the window. The merged file contains all of the common elements of both files and selected blocks of text from either the right or the left file pane. To create the merged version, select each difference and choose what to include in the merged version from the following table:

ACTION ITEM

KEYBOARD SHORTCUT

RESULT

Choose left

Use content from left file

Choose right

Use content from right file

Choose both (left first)

 

Include contents of left file followed by the right file

Choose both (right first)

 

Include contents of right file followed by the left file

Choose neither

 

Ignore the contents of both files

Using ↑ the and ↓ keys, navigate to the next change and then use ← or → to select which change you want to keep. When you are finished, save the file as a new document, or replace one of the existing files with the merged version.

BBEdit works a little differently. In BBEdit there is no third file. It presents the two files in a separate editor windows (see Figure 21-23). A third window displays the list of differences between the two. Select a difference to highlight the corresponding regions in both files. From the differences window, you can replace the block in either file with the contents of the other using the Apply to New or Apply to Old button. When you are done, save the modified files back to their original locations, or use the File

Merging Changes
FIGURE 21-23

Figure 21-23. FIGURE 21-23

Viewing Annotations

Another way of seeing how a file has evolved is to look at its annotation view. Select a file and choose a command from the SCM

Viewing Annotations

The annotated listing of a file, shown in Figure 21-24, displays the revision number in which each line was added and the author of that line. In this example, you can see that the maxPrime property was added by daphne in revision 10 of the file. The rest of the file was from the original when james first checked it in. Annotated listings do not show lines that have been removed, nor do they show the previous versions of lines that have been altered.

FIGURE 21-24

Figure 21-24. FIGURE 21-24

Resolving Conflicts

When a newer revision of a file appears in the repository, and you have modified that same file locally, the SCM status of the file changes to C to indicate a conflict. Before committing your version of the file, you need to join it with the latest revision checked into the repository to add, combine, or discard the changes made by the other developer with yours — or not.

When a conflict occurs, you have two choices. You can abandon the changes that you've made, replacing them wholesale with the changes made in the latest revision of the file. To abandon your changes, select the conflicting file and choose SCM

Resolving Conflicts

The other choice is to merge your changes manually with those checked into source control. Select the file and choose the SCM

Resolving Conflicts

If the changes conflict, the overlapping sections of the file that are different are noted by comments, as shown in Figure 21-25. The SCM status for the file changes to a green C, indicating that the file is in conflict resolution.

FIGURE 21-25

Figure 21-25. FIGURE 21-25

Edit the file, removing or combining the differences, until you have successfully integrated your changes with those of the other developer. If you are using Subversion, choose the SCM

FIGURE 21-25

You can now commit your combined changes, creating a new revision that contains the changes from both versions. When you commit your merged file, document your changes and the fact that these were combined with changes from the previous revision.

Going Offline

At some point, you may find yourself working on a project without access to the source control system or repository from which it was checked out. For example, you might have moved the project to another computer, or are using it on laptop that is no longer in contact with your source control server.

You can temporarily disable SCM control of your project using the SCM

Going Offline

You can continue to edit, add, and rename files in your project. These actions simply create pending SCM actions. When you are back in touch with the source control repository, restore SCM functionality by choosing the SCM

Going Offline

Source Control and the Project Document

The project document requires some special handing when a project is under the control of a source control system. The project document is a package containing several files. The project.pbxproj file contains the structure and common settings of the project document bundle. This includes source file references, target definitions, and build settings. If you change any of these values, the project.pbxproj file appears in the SCM smart group as a modified file, as shown in Figure 21-26. The changes made to the project won't be stored in the repository until you commit this file.

FIGURE 21-26

Figure 21-26. FIGURE 21-26

Each user has a personalized settings file in the project document package. These were described in the "Excluding Other Noise Files" section. When, and whether, you check these files into the repository is up to you. Being part of the project document, modifications to these files will make it appear that the project document has changed — unless they are explicitly excluded from the repository. Here are some points to keep in mind:

  • Whenever you add, remove, or rename files in a project, commit the changes to the project document and all of the add and remove actions simultaneously, or check in the file changes before the project document. This will avoid the situation where the project has file references to files that are not (yet) in the repository.

  • Whenever you update the project document files from the repository, close the project and reopen it from the File

    FIGURE 21-26
  • To make the most of personal settings in the .pbxuser files, make sure all of the developers working on a project have distinct account names. If you work on a project from different systems, use the same account name on both or you will be treated as a different developer.

SOURCE CONTROL VS. SNAPSHOTS

As explained in the sidebar "How Source Control Manages Working Files," a project under source control contains hidden metadata that keeps track of the state of the working files.

Snapshots work by making a literal copy of every file in your project folder. I think you can immediately see the potential conflict.

Interleaving source control actions with snapshot restores can result in stale source control information. Restoring a snapshot after performing some source control action also restores the now obsolete source control metadata. This will result in inconsistent and erroneous SCM status information.

If you need to restore your project from a snapshot, be mindful of restoring old source control metadata by using one of these techniques:

  • Avoid performing any source control action between taking a snapshot and restoring from it.

  • If source control actions have occurred, then either:

    • Restore individual changes or files from the snapshot without restoring the entire project.

    • After a restoring from a snapshot, immediately perform an SCM

      SOURCE CONTROL VS. SNAPSHOTS

SUMMARY

Source control management allows Xcode to become a member of a community. It also provides important tools for tracking, auditing, documenting, and protecting your hard work. Source trees let projects share common assets, maximizing their reuse, in a manner that won't break your projects when they're moved to another location or environment. Together, source control management and source trees allow you to benefit from the value of work that has already been done, and the work of your colleagues, in a portable and interactive manner.

The next chapter explores the multifaceted Organizer window, where you can automate your projects, keep notes, and configure test devices. Interestingly, it's a great place to add source control scripts.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset