什么是Git?
Git是一个开源的分布式版本控制系统,可以有效、高速的处理从很小到非常大的项目版本管理。简而言之,就是一个控制代码版本的插件。
本文主要介绍在Xcode中从Git安装到使用的一系列操作。最终并不会产生一个demo,只是在过程中逐步展示Git的各项功能,将重心放在IDE上而不是代码本身。
一、创建一个带有版本控制的项目
在Xcode中创建工程的时候,Xcode是有一个增加Git的选项的,勾选这个选项以后,项目中就会增加Git源。然而很多没有用过Git的开发者往往会忽略这个选项。
让我们来看一下这个选项在哪里。
打开Xcode,创建一个新工程。
这里我选择iOS区的Single View Application,选择其他的模板使用方式同理。
选择Next,在项目名中输入Git_Demo。为方便演示,下面的Devices我选择iPhone。
然后再点击下一步。
我们很容易看到,在窗口的下方有一个提示:
没错。只要勾选上这个选项,我们的项目就可以在自己的Mac上使用Git来进行版本控制了。
这个选项默认是勾选的,当然如果我们不需要,我们可以把这个选项勾掉。
本教程中我们需要将它勾选上,然后点击Create按钮创建工程。
创建完项目后,我们打开Finder,找到刚刚创建的项目存储目录。我们会发现在项目目录中,有一个.git的子目录,这事Xcode为存储git源相关数据为我们创建的。
这就是本地git源的保存位置。只要我们选择了相应的选项,这个目录就会被创建。
如果看不到这个目录,那么需要设置让隐藏的文件可见。
打开Terminal终端窗口,输入如下命令:
对于OS X 10.9以后的版本:
defaults write com.apple.finder AppleShowAllFiles Yes
对于以前的OS X版本:
defaults write com.apple.Finder AppleShowAllFiles Yes
然后重启Finder应用,输入:
killall Finder
显然,在Xcode中创建git源毫不费力。那么问题来了。如果我创建项目时没有勾选这个选项,现在我想要加上这个功能,可以做到吗?
答案是肯定的。
打开Terminal终端,进入项目的目录位置。
cd 项目位置
例如,我将项目保存在了系统默认的文稿文件夹,于是我的命令就是:
cd documents/Git_Demo
然后初始化git源:
git init
这时候.git文件夹已经被创建了,但是还是一个空的源,我们要将项目现有的内容加入其中:
git add .
切记这不要忽略了add后面的“.”
最后输入:
git commit -m 'Initial commit'
然后完全退出Xcode(图标上点击右键>退出),这样我们的项目就可以使用git源了。
打开我们的项目,在Source Control菜单,我们发现所有的选项都可以使用了,就像一开始勾选了创建git源的选项一样~
二、提交更改(Commit)
提交更改指备份一个包含所有更改的新版本。通常情况下,我们完成了一些比较有意义的工作,并且项目处于一个稳定的状态时,我们就可以提交一次更改。
具体什么时候提交更改并没有硬性的规定。我的建议是,从上次提交更改到现在为止,如果我们花费了大量的时间精力做新工作,并且新工作一旦被删除或者损坏很难恢复,那么我们就应该提交更改了。
默认情况下,Xcode会在我们创建项目的时候提交一次更改作为初始版本。这个工作会在创建项目的时候在后台完成。如果我们的项目本来没有添加git源,但是后来我们手动添加了,也会在我们添加的时候创建一个初始版本(git commit -m 'Initial commit')。
点击Source Control>History...,我们会看到我们的初始版本。
之后我们每次提交更改,这里都会显示记录。
接下来我们小小修改一点我们的项目。
点击ViewController.swift,我们小小修改一下我们的viewDidLoad方法:
overridefuncviewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
NSLog("Git测试")
}
可以看到,在我们的ViewController.swift文件的右边出现了一个M,如图所示:
这代表着这个文件相比上一次的提交有所变化。一般来说只要改变文件,名称右边就会出现一个M字母,来提示我们有未提交的更改。
点击Source Control>Commit...,下面的窗口就会出现:
让我们来看看这些区域分别显示了什么:
- 被更改的文件列表,列出了所有被更改的文件。在文件的左侧有一个选择框,默认是勾选的。如果取消勾选,那么这个文件的更改就不会被提交。
- 两个区域中,左边的是当前版本,右边的是上次提交的版本。
- 左边的蓝色区域显示了我们更改的内容。
-
标签上显示的是更改的标号,如果有多项更改就会显示1、2、3...等。点击这个标签右边的小箭头,会弹出一个菜单,我们可以选择Don't Commit(不提交更改)或者Discard Change(忽略更改)。
如果我们选择Don't Commit,小对勾就会被一个停止标记替代,并且标签转化为灰色。这项更改就不会被保存到源中。
如果我们选择Discard Change,会弹出一个窗口,提示我们所做的更改将被恢复,并且这个操作无法撤销。
- 备注区域,可以自己写一些文字用于记录本次更改的信息。如果留空,系统会提示我们书写一些备注。
如果我们仔细观察上面这个提交窗口,我们会看到我们所做的所有修改都会被Xcode看做改变,即使是一个空行。实际上空行相当于回车,只不过在屏幕上是不可见的,因此作为改变也是理所当然的。
看完窗口信息,我们来完成我们的第一次提交。窗口的右下角,有一个按钮,上面写着Commit 1 file。
这个按钮会显示需要提交的文件总数。
点击这个按钮,我们的第一次提交就完成了。
点击Source Control>History...,我们发现我们的提交已经显示在历史记录里了。(因为是Demo,我随便写了一下提交信息,请各位不要介意(吐舌)
从图中可以看出,我们编写的信息以及更改的文件数量会被显示出来。
Xcode执行初始提交,所有文件都会被提交一下,而这次只有我们修改的那个文件被提交。
另外,我们会发现ViewController.swift旁边的M符号已经消失了。
现在我们进行第二次提交。
点击File>New>File,选择source中的Swift File,命名为Test.swift。
我们发现,在新增加的Test.swift的右边,有一个A。
这个A字母代表了这个文件是新增加的。
同时我们修改ViewController.swift文件中的viewDidLoad方法:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
NSLog("Git测试")
NSLog("Git测试2")
}
点击Source Control>Commit...将改变提交。
这次我们有3个文件需要提交。因为我们增加了新的Test.swift,所以对应的工程文件也被改变了。
点击Test.swift,我们看到,因为是新增加的文件,右侧并没有旧版本,只是提示File was added。
点击Commit 3 files提交。点击Source Control>History...查看提交的记录。
三、版本之间的比较(Comparing Versions)
当我们提交了同一工程的不同版本之后,在他们之间比较,追踪修改信息就会非常方便。当新添加的代码出现故障时,当前版本与之前版本进行比较就非常重要了,我们可以看出新版本相比上个稳定版有了哪些更改。
要比较同一个文件的两个版本,我们可以使用View>Version Editor>Show version editor,或是点击工具栏上的Version Editor按钮:
点击以后,编辑器会变成两栏。左边显示当前的版本,右边显示最新提交的稳定版本。
点击编辑器下方的时钟,可以选择之前的版本,方便对不同的版本进行对比。
这里我推荐点击右边编辑器的时钟按钮,因为通常情况下,左侧的编辑器显示的都是当前的版本。
点击之后,两个版本的区别会在编辑器中显示出来。蓝色高亮的区域显示了被更改的代码,因此比较代码的变化非常容易。继续选择任何此前的版本,并观察两栏的区别。
在两个编辑器中间,还有在提交窗口看到的小标签。点击向下的按钮可以跳出让我们Discard Change(忽略更改)的选项。如果我们点击了Discard Change,Xcode会提示我们是否同意。如果我们同意忽略,这些被忽略的代码将会永远消失,无法再找回来。所以要注意不要无意中忽略任何代码。
除了上面说到的方法,还有一种我们回到之前版本的方法。如果我们仔细观察两个编辑器下面的工具栏,在中间有个带箭头的时钟图标:
点击它之后,两个面板之间的纵列内容就发生了改变,变成了一系列表示之前更改的时间戳。注意并不是所有的都代表实际提交。代表先前版本的圆角矩形的数量取决于提交的次数。
在这一列的下面,有两个箭头。左边的那个属于左边的面板,右边的箭头属于右边的面板。将箭头移动到任意之前的版本,我们会看到在相应面板中的改变。如果我们想比较当前版本和之前任意版本的区别,让一个箭头指向local行,然后移动第二个箭头。时间戳从底部到顶部代表了从新到旧的代码。在base行,我们会看到上一次提交的内容。
四、查看不同作者的提交
除了比较文件的版本外,Xcode还可以让我们追踪文件的提交者,以及是谁改变了哪一部分代码。在多人的团队中,这个功能非常有用。
要使用这个功能,点击View>Version Editor>Show Blame View菜单,或是右键点击工具栏的Version editor按钮上,选择Blame选项。
点击以后将出现这样一个窗口:
当前文件依据不同的提交被水平线分成几段,每个代码段的作者,以及提交信息和其他信息显示在窗口右边的一个特殊面板中。因为我是使用的本地提交,因此只有我一个作者。
点击blame面板上每一行右侧的i标志,将会显示修改的一些其他信息。当指针停在提交段上时,一个带图片的小按钮就会出现在它的右边。点击选中该段代码,就会弹出一个附带提交信息窗口。在这个窗口中,我们还可以跳转到特定的位置:
- 特定提交的修改文件
- 比较窗口
除了比较视图和blame试图,其实还有一个日志视图(Log view)。我们可以通过View>Version Editor>Show Log View,或者右键点击工具栏的Version editor按钮上,选择Log选项。
这里就不再详细说它了,因为这个功能并不复杂。
五、分支(Branches)
如果我们现在有一个即将发布的稳定版,或者已经发布的版本,我们突然想添加一些新的东西,如何防止新的代码让项目陷入瘫痪呢?
答案是:我们可以使用分支。
如何简单的理解分支呢?我们可以把项目想象成一棵树,稳定版本就是树的主干。任何添加新功能的版本都必须是树干的一部分。分支,就像是树的枝干,它从树干生长出来,向不同的方向生长。在git中,我们可以通过创建分支来为我们的代码设置一个新的路径来实现新特性,而不用担心在开发中破坏主干。
实际上,在git中默认都会有一个分支,叫做master。Xcode自动执行的第一次提交中就发生在这个分支中。通常,单独的开发者只在master这个分支开发,这其实不是一个好习惯。无论我们是单打独斗还是组团合作,在对项目作出重大改变或添加重大功能时,使用分支是十分重要的,它会为我们避免很多麻烦。当然,在团队项目中,为我们自己负责部分的代码搞一个分支几乎是必须的。
- 提交到App Store或客户的最终产品必须是项目中的master分支项目。
- 任何在第二分支中实现的代码或者功能最终都必须合并到master分支,这样正式发布的应用程序才是完整的。(以后再讲这一点)
当我们开始一个新的分支时,我们实际上是以当前的工作状态作为起点,即食有未提交的更改。从这个时候开始,所有的改变都存在于分支中。
要创建一个分支,点击Source Control > Git_Demo-master > New Brance…,然后会弹出如下菜单:
我们需要给这个分支起名,为方便使用,我将其起名为AnotherBranch好了。
点击Create按钮,一个新的分支就会被创建,所有的代码也会复制到新的分支中。
打开Source Control菜单,我们可以很清楚的看到分支的名称,就在项目的名字边:
现在我们来做一次分支提交。
首先我们来添加一些代码。
打开Test.swift,将import Foundation
删除,然后将下面代码复制进去:
import UIKit
class Test: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
NSLog("Git_Branch测试")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
点击Source Control>Commit...菜单,版本比较窗口将会出现,我们会看到只有一个被修改过的文件——Test.swift,新添加的部分会被高亮显示。
输入下一个提交信息:分支第一次提交,然后点击commit 1 file按钮。现在AnotherBrance分支的改变就会被提交了。
点击工具栏的Version editor按钮上,选择Comparison选项,我们会看到被选中的分支是AnotherBranch,点击它,我们会看到这个分支和master分支同时出现,从master分支中选择任意版本,Xcode都会高亮显示两者之间的区别。通过这样,我们可以方便地跟踪所有分支间代码的改变。
最后,切换到另一个分支,或是master分支,我们可以点击Source Control >Git_Demo AnotherBranch>Switch to Branch…菜单。
从弹出的窗口我们可以选择跳转到现有的其他任意分支。这里我们跳转回master分支。
选择它并点击Switch按钮,master分支就会成为当然分支。我们会发现在AnotherBranch中做出的改变并没有出现在master分支。着说明,我们在管理工程改变代码的同时,却没有修改稳定版本。
六、合并分支(Merging Branches)
在分支中进行开发是一种好习惯,然而,如果代码改变要体现在发行版中,那么分支就必须被合并到master分支中。在Xcode里,将两个分支合并成一个非常简单。
从刚刚的状态中,我们有两个分支:master和AnotherBranch,我们要两个不同的分支合并成一个,有两种选择:
- Merging from Branches(从分支合并):与我们选择的分支相关的任何改变都会被合并到现在分支中。
- Merging into Branches(合并到分支):当前分支的任何改变都会被合并到我们选择的分支中。
这两种方式我们都可以在Source Control>Git_Demo菜单中找到。注意当我们的活动分支是master分支时,第二个选项是不可选的。
为显示效果,我们在master分支的Test.swift中做一些改变。
将import Foundation
删除,然后将下面代码复制进去:
import UIKit
class Test: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func test(){
}
}
通过对比不难看出,现在两个分支中有很大的差别:
提交我们主分支的更改,下面我们来合并分支。
- 确保活动分支是master分支。
- 打开Source Control>Git_Demo – master>Merge From Branch…菜单,选择AnotherBranch然后点击Merge按钮。
我们发现,提交的时候出现了两种颜色。蓝色代表的是从AnotherBranch添加进来的代码,红色的表示将会遵从AnotherBranch被删掉的代码。
我们发现在窗口的最下面有4个按钮。我们将红色的部分选为第二个,意思是用master中的代码代替AnotherBranch中的。另外两个按钮中,第一个表示master分支的代码会被放在上面,另一个分支的代码会跟在它后面;第四个表示另一个分支的代码会放在上面,master分支的代码会跟在它后面。
点击Merge,我们看到我们的代码已经被成功合并了~别忘了提交我们的代码到一个稳定的版本。
七、忽略更改(Discarding Changes)
放弃不想要的代码更改功能非常有用,只需轻轻一点,自从上一次提交之后的更改都会被放弃。当我们再开发时遇到了难以处理的问题,想要恢复一个稳定的版本时,就可以使用这个功能。
当我们在讨论版本比较时,我们学会了如何忽略某一部分更改的方法,而Discarding Changes将会帮助我们忽略自从上一次提交之后的所有更改。
为了测试这个功能,我们先对刚刚提交的稳定版做一些更改。
打开ViewController.swift,在类中添加一个新的方法:
func discardTest(){
}
我们注意到,ViewController.swift名字边的M已经出现了。现在我们忽略所有更改,看看能不能返回之前的状态。
点击Source Control>Discard All Changes...,会弹出一个提示框。这是Xcode防止你误删代码的唯一措施。
点击Discard All Changes...,我们刚刚增加的discardTest方法就永远属于过去了。
需要注意的一点是,我们可以选择忽略所有文件的更改,也可以选择忽略单个文件的更改,这完全取决于我们自己。刚才的Discard All Changes...将会恢复整个工程所有的更改。如果我想要忽略单个文件的更改,在左边的文件列表选择要忽略的文件,点击Source Control>Discard Changes in "XXX"...就可以忽略单个的文件而不会影响到其他的文件。