理解 XCode 中的 Git 版本控制

在应用程序开发过程中,一个相当显著的部分是开发人员管理代码变更的方式。这些是必须包含的功能,存储和处理工作代码版本在不同阶段稳定阶段的副本,并能够恢复代码当存在缺陷或者产生问题的时候。更有甚者,当多个程序员工作在同一个项目时,跟踪所有的变更是一个单向的路径。幸运的是,开发人员不必去发明一种方法来做这些事情,有一个特别的软件解决方案,叫做版本控制系统(Version Control Systems

版本控制系统或者叫做修订控制系统,实际上是一种能够一直监视代码文件的变更并存储他们为了将来引用的机制(软件)。除了这些之外,版本控制系统也保存了额外的必要数据,例如哪个开发人员做了变更,变更发生在什么时候,实际上修订了什么,其他类型的历史不仅仅是数据。而且,版本控制系统提供了比较代码不同版本的功能,如果需要的话,可以恢复特定文件或者整个项目到以前的版本,并追查恶意的代码最终实现无缺陷产品。

git-source-code
 

使用版本控制系统,开发人员可以工作在项目的不同路径上,通常叫做分支(branch),当他们的代码完成时,所有的代码将合并到一起以便构建应用程序的最终发布版本。这个过程叫做合并代码(merging),它包含了版本控制系统的一个特殊特性。实际上,在开发团队和软件公司中代码的版本控制是一个强制性的工作,团队中的每个人负责项目的一部分,最后所有的代码被集中到一起放置在一个地方。

对个人开发者来说使用版本控制系统不是必要的,但是仍然是强烈推荐的。一旦遇到严重的问题或者说以代码都乱套了的时候,使用版本控制系统将更容易追查缺陷或者恢复代码到稳定版本和代码的工作版本。事实上,许多的个人程序员,特别是新人,根本不使用版本控制系统,当他们增加新功能或者是修改代码的时候,通常手动复制项目的副本。这是一个非常不好的习惯,源码控制能更好并且更有效率的完成这些工作,同时提供了前面描述的额外功能。

作为最出名的版本控制系统之一,git由Linux创建者 Linus Torvalds开发。Git在虚拟目录(仓库)中组织一切,实际上,任何版本的跟踪都可以适用于他们。它既可以通过命令行使用,也可以通过桌面应用程序。如果git对你来说很陌生,那么我建议你在网络上阅读一些关于git的文章,而进一步讨论git超出了本教程的范围。

自从Xcode的第五版本起,就集成了git的丰富功能,包括各种选项菜单的按钮和管理源代码的子菜单。正像你稍后看到的,使用版本控制git是非常容易和快速的,在你看完本教程之后,没有理由不使用它。

总结而言之,你已经理解了我们的任务是学习怎样在Xcode中使用版本控制git,这一切将通过了解Xcode提供的每个相关特性来完成。如果你不熟悉所有的东西,或者在我们开始之前你需要获取更多的知识, 请在网络上搜索相关信息。我不得不说,在本教程中我假设你关于版本控制系统拥有最基本的知识,比如git是什么,因此我们要把它视为理所当然的事情,而关注Xcode怎么管理这一切。

Git示例演示

与其他教程的演示程序相反,本文中我们不会实现一个应用去展示iOS SDK的具体特性,也不会有一个最终的开发样本。实际上我们所做的就是去创建一个示例项目,仅仅在几个点上添加几行代码,我们将使用它作为一个工具去测试Xcode提供的所有源代码控制管理选项。换句话说,我们重点讨论的是IDE,而不是iOS。

除此之外,这一次不会有可供下载的例程了。取而代之的是,我邀请你一起来一步步实现这个 demo,并且在需要写代码的地方手动添加代码(别担心,不会很多)。这样跟下来是有必要的,因为我们将会重复进行多种与版本控制有关的操作,并且我们必须即时看到结果。如果我只是提供一个操作已经完成的例程,就不可能有这样的效果了,因为这样下来你自己的练习部分近乎于零。

那么,让我们开始吧!我们用 Xcode 来近距离地看一看版本控制系统的精华所在。

创建 Git 仓库

每次在 Xcode 中创建新项目,都会让开发者选择是否要添加一个本地 git repository。新建一个 project 涉及分为3步的引导过程,其中在第3步也就是最后一步中,Xcode 提供了一个勾选框和相应的说明,如果勾选了,一个 git repository 就会添加到保存 project 的目录中。这个选项很容易被忽略,或者被当做一个 Xcode 的没用特性,这种事经常发生,尤其是对于从来没用过版本控制和 git 的开发者,或者新手程序员。

具体细节如下,启动Xcode并创建一个新项目。首先,选择“单视图应用程序(Single View Application)”作为应用程序的模板,同时在iOS选项部分,选择“应用程序(Application)”项。

点击“下一步(Next)”按钮到第二步,设定“产品名(Product Name)”字段为“GitDemo”,同时确保在“设备(Devices)”下拉菜单中选择“iPhone”。在这里不需要iPad或者普通应用。

再次点击下一步按钮,进入最后一步。在此,首先选择保存项目的目录。然后选中窗口的底部的单选框,并选中在“My Mac”上创建git仓库。

默认情况下,这个勾选框总是被选中的,然后每个 project 都会创建一个 git repo。如果你的项目不想用 git 和版本控制,只需取消选中,但我不建议这样做。总之,本教程中我们希望启用git,因此确保你选中了勾选框。最后,点击 Create 按钮。

等待 project 创建完成吧,然后打开一个 finder 窗口,来到我们保存 project 的目录下。在这里,找到 .git 子目录,这是 Xcode 自动创建的目录,用来存储 git repository 相关的数据。

Version Control Xcode GIT Directory

如果你看不到 .git 目录,你必须把电脑上的隐藏文件改为可见。首先,打开Terminal(终端) 窗口,然后输入以下命令:

对于 OS X Mavericks 10.9:


defaults write com.apple.finder AppleShowAllFiles TRUE

对于此前的 OS X 版本:


defaults write com.apple.Finder AppleShowAllFiles TRUE

然后,只需重启 Finder 应用,所以再输入一条命令:


killall Finder

因此,如你所见,这个 app 的本地 git repository 实际就保存在这里。相应地,你创建的任何新应用都会随之带来一个 .git 子目录,只要你保持相应的选项是勾选的。

显然,用 Xcode 来为 project 添加 git repository 是不费吹灰之力的。然而,如果你在创建 project 时没有添加 git repository,或者想要稍后再添加,怎么办呢?好消息是,你随时都可以为 project 添加 repository,但是不用 Xcode 了。尽管这样的情况很少见,我还是来讲解一下。

注意,如果你不想看的话,可以尽管跳过本教程的下一节。但我建议还是读下去,因为紧接着再下一节的内容会非常重要。

在开始讲之前,你首先需要在 Xcode 里下载 Command Line Tools ,因为我们接下来要用 Terminal ,需要一些工具。如果你已经下载了这个包,就进行下一步。如果没有,要安装 command line tools,点击Xcode里的 Xcode > Preferences… 菜单,然后选择 Downloads 一项。在窗口的上部,Components 一栏下,点击 Command Line Tools 右侧画着向下箭头的按钮。一旦下载结束,下载按钮会变成对勾符号。

Version Control Command Line Tool

然后,为了这个例子再创建一个 Xcode project,我们一切完成之后再把它删除。这一次确保要取消勾选 Create git repository 选项。在这个例子里,我们不需要 Xcode 来为我们准备 repository 了。把这个项目命名为 NoGitExample,并且保存在桌面上,这样就能直接使用我接下来的提供的指令了。

一切就绪,打开一个 Terminal 窗口(如果之前已经有打开的窗口,确定要先关闭再重启,这样才能应用安装 command line tools 时做出的改变)。首先,来到保存新 project 的目录下:


cd /Users/YOUR-USERNAME/Desktop/NoGitExample

别忘了把上面的指令改为你自己 Mac 的用户名。接下来:


git init

这会初始化一个空的 repository。然后如果你打开 Finder 或者在 terminal 输入 ls 指令,你会看到 .git 子目录已经创建出来了。太棒了。继续往下:


git add .

用这个指令,当前目录(点号[.])的所有内容都会添加到 repository 中。最后,提交全部(也就是持久保存所做的更改):


git commit -m 'Initial commit'

Terminal 窗口中会出现提交到本地 git repository 的文件列表。下图就是我的terminal的样子:

Version Control Terminal

git repository 已经准备好了,但是如果你回到 Xcode,打开 Source Control 菜单,你会发现一切都还是不可用的。

Version Control Disable Source Control

这是因为 Xcode 不会自动被通知到,我们已经手动添加了 git repository。因此,点击菜单 Xcode > Quit Xcode 来关闭 Xcode ,然后再重启。现在,在 NoGitExample 项目中,如果你再打开 Source Control 菜单,你会看到这些的选项都可用了,跟我们创建 project 时就添加了 git repository 的效果相同。

Version Control Enable Source Control

到了这一步,就可以关闭这个 NoGitExample 项目,也可以把它从桌面上删除了。

现在你已经能够知道怎么为一个 project 添加 git repository了。并且即使你在创建 project 时有意或无意没有添加 git repository,也可以随时手动添加。

提交更改

说到所谓的“提交更改到 repository”,我们实际的意思是:存储我们项目的一个新版本。新版本包含目前已作出的所有更改,比如代码修改或者新添加的文件。一般来说,一次提交应该发生在一定量的工作已经完成,并且项目处于稳定状态之时。关于提交的频率应该是多久一次,并没有一定之规,但我建议:如果你认为从上一次提交到现在之间,你所做的工作如果意外丢失,会造成巨大的时间精力浪费,那就一定要提交一下了。

Xcode 默认会在新项目创建时做一次初始提交,目的是保存一个项目初始状态的版本。这次提交是在幕后完成的,不会打扰你,也不会要求你确认。如果你在创建项目时没有添加 git repository,是如前所述在之后手动添加的,初始提交是通过这个指令完成的:git commit -m ‘Initial commit’ 我们之前用过这个指令。

实际上,你可以看到初始提交的相关信息,只需点击菜单 Source Control > History… 。在这里记录了你对项目的每一次提交。

Version Control Initial Commit

我们现在来尝试一些操作吧,首先要对项目做点改动。来到 ViewController.m 文件,在 private class 部分添加以下的 property 声明:

1
2
3
4
5

@interface ViewController ()

@property (nonatomic) int sum;

@end

接下来,修改 viewDidLoad 方法如下:

1
2
3
4
5
6
7
8
9
10
11
12

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.

    int a = 5;
    int b = 10;

    self.sum = a + b;

    NSLog("The result is: %d", self.sum);
}

如果你看一眼 Project Navigator (即左边栏),你会注意到在 ViewController.m 文件后面,多了一个字母 M,如下图所示:

Version Control Modify File

这意味着这个文件已经被修改了,并且相比上次提交的版本确实有改动。一般来说,每次你改动一个文件之后,字母 M 就表示已有改动,还未提交。

让我们来看看怎么提交代码。这是很简单的,仅需要打开Source Control > Commit菜单,如下窗口所示:

Version Control Xcode - Commit Window

一步一步的让我们看看它告诉了我们什么。在左边方框(在图片中标识为#1的部分),这儿列出了所有已修改的文件。在我们的例子中,仅仅ViewController.m文件被修,因此仅仅显示了一个文件。如果你仔细观察,你将看到文件的左侧有一个缺省选中的复选框。假如你不选中它,已经修改的文件将不会被提交。但是现在这不是我们想要的东西,因此让它被选中。

在窗口的中间位置(标识为#2的部分),有两个预览框。左侧是文件的当前版本(没有提交的),右侧是文件的最近提交的版本。截图描绘了ViewController.m文件的原始状态。

左栏的蓝色区域(图中标识为#3的部分),在右栏变成一条线,这显示了文件中的实际改动。这样的表示法让所有的改动一目了然,并且能对应到改动的具体位置(行号)。也许你注意到了,窗口的中央,在两个预览栏之间,有一些小的圆角标签,上面写着数字(图中标识为#4)。这些标签一个一个地数出了全部的改动,上面的数字就是计数的序号。在数字左边,有一个对勾符号。如果出现了这个对勾,说明对应的改动可以正常提交到 repository。尽管如此,你还是可以选择跳过,暂时不提交文件的某一个或几个改动,甚至抛弃所做的更改,只需点击数字右边的小下三角。含有两个选项的小菜单将会浮现:

Version Control Xcode - Commit Window

如果你选择 Don’t Commit 选项,对勾符号会换成禁止符号,对应的改动就不会提交到 repository 了。

Version Control Xcode - Commit Don't

如果你选择菜单中的 Discard Change 选项,会出现一个确认窗口,提醒你选中的改动将会被回滚,并且回滚是无法被撤销的。

Version Control Xcode - Commit Don't

如果你点击 OK 按钮,响应区域中的改动会消失无踪,仿佛从没来到世上。

如果你有留心观察上面的提交窗口截图,你会发现所有的修改都会被 Xcode 认为是改动,即使一个空行也不例外。事实上,空行是屏幕上不显示的换行符,所以它被收集为一项改动是合情合理的。

总之,对于这个样例,确保你没有丢弃任何改动,允许一切都提交。因此你应该看到所有的圆角 label 上都有对勾。

在这两栏之下是一片空白区域,中间写着“Enter commit message here”。这个区域用来附加一些简短信息,描述这个版本所做的更改。点击它,填上下图所示的内容:

Version Control Xcode - Commit Message

填写(有意义的)提交信息至关重要,尤其是在提交次数很多的情况下。所以,要把它当做不可或缺的一步。

既然我们已经浏览了一遍这个窗口的主要内容,下面我们来做第一次提交吧。在窗口的右下角,有一个按钮写着:Commit 1 file.

Version Control Xcode - Commit Button

在这个按钮里,总是会写着提交的文件总数。点击它,然后恭喜!你的第一次提交已经诞生,将会永久载入历史,不仅是你个人的历史,也是 git 的历史。这是什么意思呢?只要打开菜单上的 Source Control > History…,就可以看到它列在这里。

Version Control Xcode - Commit History

如你所见,我们之前写的提交信息以及改动的文件总数都出现在这里了。在 Xcode 做出的初始提交中,提交了所有文件;但是我们只改动了其中一个。

除此之外,如果你关闭 history 窗口,再看看 Project Navigator 左边栏,ViewController.m 文件旁边的 M 字母也消失啦!

现在,我们准备再做一次提交吧。这一次,我们来为项目添加一些新文件,最好的方式莫过于创建一个新类。那么,按下键盘上的 Command + N 键,然后添加一个 Objective-C class。把它设为 NSObject 的子类,命名为 TestClass。最后,添加到项目中。

一旦上面这些都完成了,注意左边栏 Project Navigator 中的两个类文件旁边都出现了一个字母 A ;意思是这两个文件是后添加到项目中的,因此自然它们还没有被提交过。

打开 ViewController.h 文件,然后 import 我们的类:

1

#import "TestClass.h"

接下来,打开 ViewController.m 文件,如下声明一个 private 属性:

1
2
3
4
5
6
7

@interface ViewController ()

@property (nonatomic) int sum;

@property (nonatomic, strong) TestClass *testClass;

@end

再看左边栏 Project Navigator,注意到现在有4个没有提交的文件了。两个类文件是我们刚添加的,还有另外两个本来就有的文件。我们需要将这些改动也提交,因此打开 Source Control > Commit… 菜单。

Version Control Xcode - Commit

这一次,选中了5个有待提交的文件。第5个文件(显示在第1行)是项目的配置文件,是在添加新类时由 Xcode 自动修改的。如果你点击 TestClass.h 或 TestClass.m 文件,左(译者注:原文可能笔误,此处应该为“右”)栏会变为一片空白,如下图所示:

Version Control Xcode - Commit

这是因为这两个文件之前没有提交过,因此没有之前的版本可以对比。所以,右栏里只写着“File was added” 就是再正常不过的了。

来到提交信息区域,填写提交信息: TestClass class was added to the project。完成之后,点击 Commit 5 files 按钮,让 Xcode 提交更改到 git repository。 

第二次手动提交已经顺利完成了。可以打开提交历史来验证,在菜单 Source Control > History… :

Version Control Xcode - Commit History

版本对比

在你提交了多个版本之后,对比各个版本、跟踪代码的变化是非常容易的。当新添的代码不能如预期工作时,版本对比显得尤为重要,因为你需要找到从上个稳定版本以来的所有变化。

要比较两个不同版本的文件,或者点击菜单里的 View > Version Editor > Show Version Editor,或者点击工具栏上的 Version Editor 按钮,如下图所示:

Version Control Xcode - Editor Button

一旦上面这一步完成,编辑器就会分裂成两部分。最开始,左、右栏都显示当前版本的文件。要把任意一栏切换为某个之前提交的版本,来到这一栏底部的工具栏,点击最后一个按钮,上面有时钟标志:

Version Control Xcode - Previous Version

一瞬间,选择的版本对应的差异就显示在屏幕上了。一般来说,左栏用来显示当前版本的文件,而右栏用来访问旧的版本。之前提到过的蓝色区域表示了更改的代码,能轻松跟踪代码的增加。因此,再往下进行,选择任意一个之前的提交,观察两栏显示出的差异。

Version Control Xcode - Compare

你应该注意到,在两个编辑器的方框之间,有我们在提交窗口中第一次看到的圆形标签。点击它们中任意一个向下箭头,将显示放弃改变的选项。如果你点击它,Xcode会要求你确认,如果你同意了,已经选择的代码将永远的被放弃,没有任何的机会恢复。因此,要小心,不要放弃任何一片代码,甚至是在偶然的情况下。

除了上面介绍的方法,你还有一个方法你可以恢复到以前的版本。如果你仔细观察两个方框下的工具条,在它的中间有一个带时钟和箭头的按钮。

Version Control Xcode - Time Button

如果你点击了它,两个方框之间列将改变并且标签将被以前提交的一系列时间戳替换。请注意不是他们所有都代表了真实的提交,这取决与提交的总量,圆角矩形的真实数字匹配以前版本的实际数量。例如,在我们的应用程序中在底部仅仅有两个形状匹配了真实的提交。

在这一栏底部,有两个箭头。左边的箭头属于左栏,指向右边;右边的属于右栏,指向左边。把这两个箭头拖到任何一个历史版本,你可以立即看到这个版本出现在对应的一栏中。如果你想对比当前版本和任何一个历史版本,只需让一个箭头指向 local 一行,然后拖动另外一个箭头。时间戳的顺序,从顶到底是从最新到最旧。这意味着写着 base 的一行,代表上一次提交的版本;而再往上看,就是那些更老的版本。下图总结了我刚才所描述的:

Version Control Xcode - Timestamp

现在你知道如何对比版本,并跟踪代码在各个版本的变化了。现在随便玩一玩这个特性吧,之后我们再往下讲。

是谁的责任?

在比较文件的版本之前,Xcode允许追查谁提交了代码,还有谁修改了那部分的代码。如果一个项目是多人开发的话,这是一个非常有用的功能。只需简单地打开视图View > 文本编辑Version Editor > 展示责任视图Show Blame View 菜单,或者保持鼠标按钮在工具栏的文本编辑Version Editor按钮上,然后选择责任Blame选项。一个如下图所示的新窗口就会展示在你面前:

Version Control Xcode - Blame Window

正如你所看到的,被选文件的代码是根据所提交的人的不同而被水平线分割了好几部分。根据提交的信息,每一部分的代码的作者,以及其他相关信息会展现在窗口的右侧中的一个特殊的窗格里。

如果你还没有完成上一步,打开责任视图,注意看 Xcode 是怎么用不同提交和不同作者来区别呈现代码块的。这样的界面可以非常快速地定位到某一块代码是什么时间提交,谁提交的,还可以获得一些额外信息。这些额外信息只要把鼠标移到责任栏上就可以看到。当鼠标放在一个提交片段上时,你可以看到最右出现了一个画着 i 标识的小按钮。如果你点击它,就会选中这段代码,并弹出一个窗口,上面显示着全部的提交信息。通过这个窗口,你可以切换到对比窗口(标为 #1)以及本次提交修改的文件(标为 #2)。

Version Control Xcode - Blame Details

除了上一节我们看到的对比视图和刚刚介绍的责任视图之外,还有一个日志视图,可以通过 View > Version Editor > Show Log View 访问到;或者在工具栏上长按 Version Editor 按钮,然后选择 Log 选项。我不再详细说了,留给你自己试试吧。毕竟,理解日志是什么、怎么用并不难。

分支

想象你的项目有一个可用的版本,准备发布,或者已经发布了;现在你想开发一个新特性,但又不想意外毁坏了目前稳定可用的程序。在这种情况下,要避免可能的灾难性后果,保证你的项目不会被弄毁,你应该怎么做呢?答案很简单:用分支

要清楚理解什么是分支,可以把你的项目想象成一棵树,其中树干永远是项目主要的、稳定的、可用的版本。而任何新添加的特性,都必须先成为树干的一部分,然后才能进入发布阶段。在版本控制系统中的“分支”,就像树的树枝一样,从树干长出来,沿着一个不同的方向生长。在 git (最终在 Xcode)中,你可以创建分支来为代码开辟一条新路(如实现一个新特性),而不用担心在开发时会损坏当前可用的版本。

实际上,git 总会默认创建一个分支,名为 master。Xcode 进行的初始提交,就是在这个分支中进行的。一般来说,单打独斗的开发者会只用这一个分支工作,尽管这是个坏习惯。无论你是单独工作还是在团队里,我认为都应该在你要做重大改动或添加时使用分支,这样可以避免陷入麻烦。当然,在团队合作的项目里,个人开发的部分几乎是必须要在一个单独的分支里完成的。

记住以下两点很重要:

1. 提交到 App Store 或客户手中的最终产品,一定是 master 分支的版本。

2. 任何处于次级分支的代码或实现的特性,都必须先合并到 master 分支,之后才能被包括进应用的正式发布(稍后还将谈到)。

当你开了一个新分支开始工作时,事实上这个新分支就是从当前的地方开始的,即使还有没提交的更改也不例外。从此以后,再做出的代码更改就是只在新分支上了。

现在回到Xcode。去创建新的分支,到资源控制Source Control > GitDemo – master > 新分支New Branch…菜单,你会看到如下图所示的窗口:

Version Control Xcode - Add Branch

给该分支取个名。我给它取名为(正如你在下图所看到的)AnotherBranch,在这里你取什么名它都不会有所不同。然后点击OK按钮,等待一段时间直到新分支被创建好,且当前的代码被复制到该分支上。

你可以很容易地找到你当前活跃的分支是哪个。简单地打开资源控制Source Control菜单,旁边的项目名称的选项那里就可以看到你当前的分支。

Version Control Xcode - Current Branch

现在,让我们提交代码到新的分支。在做这个之前,让我们增加一些新的的代码,因此进入私有类区域,增加如下的方法声明:

1
2
3
4
5
6
7

@interface ViewController ()

...

-(void)sayHello;

@end

然后,实现它:

1
2
3

-(void)sayHello{
    NSLog("Hello");
}

最后,在 viewDidLoad方法中调用它:

1
2
3
4
5
6

- (void)didReceiveMemoryWarning
{
    ...    

    [self sayHello];
}

现在,切换到 Source Control > Commit菜单,将显示版本比较窗口。你将看到仅仅一个已修改的文件 ViewController.m将要提交,并且新增加的代码高亮显示。

提交代码并且增加如下的提交信息:第一次提交到分支,然后点击Commit 1 File按钮。改变将发生在AnotherBranch分支

打开版本编辑器(菜单视图 View > 版本编辑器Version Editor > 显示版本编辑器Show Version Editor),然后再到右边编辑窗口下面的工具栏中。你会看到被选择的分支是AnotherBranche。点击它,这时候该分支和主分支都会出现。从主分支上,选择你想要的任何一个版本,Xcode就会把AnotherBranch分支上的当前版本和主分支上被选择的版本之间的变化高亮出来。采用这种方式,以及我们之前的教程所谈到的比较版本部分的内容,你都可以很轻松地追踪到你工程中所有分支之间代码的变化。

Xcode Version Control - Compare Branch

最后,切换到另一个分支,或者主分支,进入资源控制Source Control > GitDemo – AnotherBranch > 切换到另一个分支Switch to Branch… 菜单:

Xcode Version Control - Switch Branch

从出现的窗口中,选择你想切换到的分支,在我们这个例子中选的是主分支:

Xcode Version Control - Select Branch

选择它并点击切换Switch按钮。主分支就会再次变成当前活跃的分支,你会发现那些只有在AnotherBranch分支中所做的变化都不会在这里出现。这很棒,因为我们能够成功地推动我们的工程,而不需要修改稳定的版本。

合并分支

在主分支以外的分支上工作,这在执行状态中是很好的习惯。然而,如果任何代码的添加或者修改都意味着要在应用程序下一个版本中出现的话,这就必须放到主分支上,所以在这一部分,我们将会看如何完成这项任务。正如标题所总结的一样,我们谈论的这部分功能就叫合并,以及Xcode提供的一种快速合并两个分支版本的方法。

让我们做一个小实验来看看如何合并工作。首先要确定你现在处在主分支上。如果不是,请到资源控制器Source Control > GitDemo – AnotherBranch > 切换到另一个分支Switch To Branch…菜单中,并在弹出窗口里选择主分支。

接着,用资源控制器Source Control > GitDemo – master > 新建分支New Branch… 菜单创建一个新的分支,取名为LastBranch

Xcode Version Control - Last Branch

给Xcode一点时间去准备。现在,在ViewController.m文件中创建一个或多个假的私有方法,并声明它:

1
2
3
4
5
6
7
8

@interface ViewController ()

...


-(void)sayByeBye;

@end

然后实现它:

1
2
3

-(void)sayByeBye{
    NSLog("Bye - Bye");
}

最后,在viewDidLoad方法中调用它:

1
2
3
4
5
6

- (void)viewDidLoad
{
    ...    

    [self sayByeBye];
}

在合并之前,该分支上做的修改必须先提交到本地仓库。因此,在资源控制器Source Control > 提交Commit...去执行提交。

言归正传。合并两个不同的分支到一个分支上,你有两种选择:

  1. 从分支上合并Merge From Branch: 你可以选择在分支上做过的任何修改来合并到当前工作的分支上。

  2. 合并到分支上Merge Into Branch: 你可以选择在当前工作的分支上做过的任何修改合并到分支上。

这两种选择你都可以在资源控制Source Control > GitDemo菜单里找到。注意,当你当前活跃的分支是主分支的话,第二个选择是不可用的。

现在假设有一个开发者在我们之前创建的AnotherBranch分支上开发并在上面实现了sayHello的方法,而另一个开发者在LastBranch上开发并实现了sayByeBye的方法,而你的任务就是把这两个新增的都添加到应用程序中下一个稳定版本。你打算怎么做?简单来说,当具备主分支活跃,你得先从其他两个分支进行如下合并:

首先,确保你工作的是主分支,如果不是要先切换到主分支上。

然后,打开资源控制器Source Control > GitDemo - master > 从分支合并Merge From Branch...,然后从窗口打开并选择AnotherBranch分支并点击合并Merge按钮。

Xcode Version Control - Merge Branch

一个版本比较的窗口将会出现,在那里就会有机会再合并代码前重新审查所有代码修改过的地方。如果你需要,我们可以快速浏览一下,当你准备好了,就可以再次点击合并Merge按钮。

Xcode Version Control - Merge Branch

当Xcode询问你有关该项目快照的事,点击启用按钮继续。稍等片刻,AnotherBranche分支已经合并到主分支上了。

按照同样的方法,从LastBranch分支中合并过来。你就会发现如果你没有提交本次版本修改过的文件,Xcode不会让你再次合并。所以,唯一的方法只有,先提交。提交完了再尝试从LastBranche分支上合并。在版本比较的窗口中你会发现一些红色的区域,表示这些修改的地方会在合并之后代替蓝色那部分。这意味着要合并过来的分支上的代码将会代替当前正在工作的主分支上的相同行数的代码,如下图所示:

Xcode Version Control - Merge Replace Line

如果你使用编辑窗口下的工具栏按钮,你可以很容易避免这种情况,并保持现有的和新增的代码。在选择的区域内,点击圆形上面带箭头的按钮,可以使用所有按钮来查看他们的效果。在下面的截图中,我选了第一个按钮,意味着存在在主分支上的代码将会置于从其他分支合并过来的代码的前面:

Xcode Version Control - Merge Line

仔细检查所有修改过的地方,确认任何分支里的代码都没有被排除在外。一旦你完成了,你要确认所有代码都在的,然后就点击合并Merge按钮。

恭喜!你已经成功从多个分支中合并代码到一个分支上了,现在你知道如何用Xcode处理类似的事情了。

放弃修改

这个选项是对放弃那些在工程中不想被修改的文件非常有用的,因为只需要点击一下鼠标就可以撤销所有自上次提交到目前所做的操作。当实现的方式不是预期的方向,而你想从上次提交的版本重新开始工作,这时候就非常有用。注意,放弃所有改变的操作是不可恢复的选项,因此,如果你不小心做了这个操作,你将没有任何机会恢复到你刚刚所做的工作。所以,要谨慎。

在此前,当我们讨论版本对比的时候,我们第一次看到了如何通过在编辑器窗口之间使用每个标记区域的小菜单按钮来放弃指定代码段的修改。这里,我们将看到如何在整个项目中执行放弃操作,并瞬间恢复到上次提交的版本。

测试这部分的目的,打开ViewController.h文件,添加一个公共方法声明:

@interface ViewController : UIViewController

-(void)aVeryCoolMethod;

@end

现在,在ViewController.m 文件添加一小段这个方法的实现:

-(void)aVeryCoolMethod{
    NSLog("I'm feeling that you'll discard me... Really?");
}

如果你注意到了项目导航栏,M标志旁已经添加了我们创建的两个文件。这正是我们想要的看到的,如果取消了变化,所有修改的文件将会受影响并恢复到先前的状态。

这里有个很重要的细节需要提一下:你可以放弃所有文件之前所做的修改,也可以放弃部分指定文件修改。这个决定在于你,如果你想选择性放弃,那么请首先确保选的文件是你想放弃修改的。为了让后面可以看得清楚点,如果你只选择ViewController.m文件然后打开资源控制(Source Control)菜单,你会发现在ViewController.m会有个标题为“放弃修改(Discard Changes)”的选项。同样的,如果你只选择ViewController.h文件并打开相同的菜单,你也会在ViewController.h中发现放弃修改(Discard Changes)的选项。不过,如果你想同时放弃这两个文件的修改(并假设这里面会有超过两处的修改),只需在项目浏览器(Project Navigator)选择他们并再次打开资源控制器(Source Control)菜单。相应的指令变成放弃这两个文件的修改……如下图所示:

Xcode Version Control - Discard File

在这个例子中,我们不打算使用这个选项,用放弃所有改变(Discard All Changes)来代替。通过点击它,屏幕上就会出现确认的提示窗口,这是Xcode防止你误操作的方式。

Xcode Version Control - Discard File

点击放弃所有改变(Discard All Changes...)提示框的按钮,公共方法就会恢复到原来的样子。正如你所见,放弃所有改变仅仅是点击几下就会远离你现在的状态,所以我再次提醒你,当你想放弃之前操作的时候要非常小心,尤其是你通常都使用资源控制器(Source Control)菜单。

总结

在本次教程中,我努力深入细致地把如何使用Xcode管理版本和资源控制讲述给大家。在幕后,真正工作的其实是git,一个非常流行和非常有用的版本控制系统。你或许发现文中并没有提及到GitHub或者任何有关Xcode功能的地方,但是我这么做的目的,是想完全关注在通过Xcode进行的git管理上,除此之外,如果你知道如何处理版本控制,你也可以直接使用GitHub。进一步说,正如我在教程开始时候经常提到的,如果你是团队中的一员,那么用版本控制系统来工作是强制性的。如果你是个人工作而且你之前没用这个的话,那么也强烈地建议、鼓励你现在开始使用Xcode的版本控制。这是一种保证你不会对现成的工作造成任何的损害,也不会增加工作量的方式,除此之外,当添加新的功能来拓展你的应用程序时也变得非常容易。最后,我希望前面所展示的例子是有帮助的,以及使用Xcode来管理版本是能够有效地节省工作时间的。

和往常一样,你可以随时留下评论来分享你的看法。