课件
关于Git
版本控制对于大中型软件系统的开发非常重要。
Linux 在 1991 年被创造出来,吸引了来自全世界的很多志愿者参与它的代码编写。但人多手杂,不可避免地,代码的管理也变得非常的困难。由于种种原因,CVS、SVN 之类的集中式版本控制系统没有被采用,商用免费授权给它们用的也由于一些版权原因被收回授权了,于是,Linux 它的作者花了两周时间,自己用 C 写了个分布式的版本控制系统,这就是 Git。很快 Git 成为最流行的分布式版本控制系统之一。
Git 这个单词在英文里是“饭桶”的意思,总有点自嘲的意思。
Git 最关键的特点是它的分布式的特点。
大家也可访问下面链接 https://try.github.io/levels/1/challenges/1 来自学,一步一步的跟着做。
Git 的基本概念
版本库
Git 作为版本控制系统,控制单位就是版本库。而它作为分布式的版本控制系统,就有远端版本库和本地版本库。
远端版本库可以认为是在非本机上的,比如我们可以放在服务器上,或者放在第三方托管主机上。而本地版本库就是在你的电脑上的。
接下来要解决的注意问题就是如何在分布式这个大前提下,做到版本库之间的同步操作。
版本库的操作
创建与提交
Git 版本库的创建与提交变更需要使用 Git 的命令行,这些命令行在 Unix 系列比较新的系统里一般都是自带的,如果没有,则需要先安装:
- Linux 在 apt-get 可以安装;
- OSX 可以 port 安装或在 http://sourceforge.net/projects/git-osx-installer/ 下载安装;
- Windows 没有自带 git,msysGit 项目提供了安装包,可以在 https://git-for-windows.github.io/ 下载安装;
假设已经安装好 Git 命令行,首先在需要创建版本库的地方打开命令行,创建一个空的文件夹,进入该文件夹后,使用 git init
命令就可以把该空的文件夹变成一个空的版本库(说白了,所谓的版本库,在你本机磁盘上就是一个有着特殊结构的文件夹):
在该版本库的文件夹里创建了一个文件后,通过 git status
命令则会列出当前版本库中尚未提交的文件改动。这些改动我们称之为「未跟踪的文件」。
假设我们现在未跟踪的文件为 file.txt。接着,通过 git add file.txt
命令则可以把这个未跟踪的文件进行标记,这样,这个文件就被 git 版本库获知了,或者说它已经被跟踪了。我们创建的版本库就知道这个文件是需要提交的。
接着,通过 git commit -m "msg"
命令,则会把版本库中已经被跟踪的所有文件一同提交到版本库中。这样,就完成了一次提交。这些文件的改动都会被版本库记录下来。
接下来,假如我们又创建了两个文件,分别是 file2.txt、file3.txt。继续用 git status
命令,就能同样发现这两个未跟踪的文件。然后与上文给文件标记一样,使用 git add file2.txt
和 git add file3.txt
命令 ,把这两个文件进行标记,也可以使用 git add *.txt
来一次性把后缀名是 txt 的文件都进行跟踪。最后,同样通过 git commit -m "msg2"
命令来完成提交。
这样,我们进行了2次提交,经过2次提交,版本库中新增了3个新的文件。
克隆到本地
上面的例子是我们在本机创建的版本库,如果使用同样的命令操作,在服务器上创建一个 Git 版本库,则这个版本库就成为了远端版本库。
为了更好的进行协作,我们经常采用一个远端版本库以及若干个分布在不同机器上的本地版本库这样的模式,任何本地版本库有提交,都会把提交同步到远端版本库中,这样所有的本地版本库就可以进行同步。
很多时候,我们是先创建好远端版本库,这样我们就需要把它克隆到本地,通过 git clone
带上版本库的地址(如 git clone username@domain: path )命令,就可以完成远端版本库到本地版本库的克隆。这里版本库的地址如果在 Github 上一般是“git@github.com:用户名/版本库名”,这样格式熟悉了你会发现其实是一个 ssh 格式的 URL,当然也可以用其他的格式,比如 Githubt 也提供了 https 格式的版本库地址。
从远端拉取
现在假设我们已经把远端版本库在本地的若干台机器上完成了克隆,接下来讲如何把文件的变动从远端版本库同步到本地版本库,这种操作叫做从远端版本库拉取 comit 到本地。
具体地使用命令就是 git pull
,比如说我们远端版本库上最新的提交它把 file4.txt 这个文件进行了变动,那我们在本地版本库就只需要运行一下 git pull
命令,它就会自动地从远端拉取 commiit ,这样变动的文件就同步到本地了。对于另一个本地版本库2也是一样的操作。
提交到远端
上面说的是从远端拉取变动,同步到本地,那么那远端版本库的变动是怎么来的呢?我们当然不会在远端版本库上去改动文件然后提交,其实我们可以在本地版本库进行文件的变动(如 git add file4.txt
),然后我们把这些变动提交到本地版本库去(如 git commit -m "msg3"
),最后再把本地版本库同步到远端去就可以了。
而我们要把本地版本库同步到远端,就需要通过 git push
这条命令,它就会自动地把远端版本库上还没有的文件变动,从本地同步过去。
需要注意的是,就是 git push
命令要求你的本地版本库已经拥有了远端版本库的所有 commit,所以我们一般会先 git pull
,然后再进行文件变动并提交后再 git push
。
撤销变动
我们对 file4.txt 进行了改动,然后让版本库标记,这个文件的改动是已经跟踪了。接下来如果我们想撤销这一改动,可以通过 git reset
命令来实现,这样它只会把 file4.txt 的改动恢复成未跟踪的状态,而它的改动是仍然存在的。
而如果我们想彻底抛弃这个文件改动,让它恢复成上次 commit 时候的样子,就可以通过 git checkout
命令,并指定这个文件。
大家在用的时候一定要小心,因为这条命令它会丢失 file4 自从上次 commit 之后的所有改动。比较危险,因为它会丢失你对文件的修改,除非你真的想这么做,否则慎用。
修改提交
我们对一些文件进行了改动,然后想提交的话,可以用 git commit -a 来自动的把修改的文件先 add 再进行 commit,带上提交消息的做法和之前一样就是用-m 的参数,这样我们就让版本库中增加一次新的 commit,提交的消息为 msg4:
假如我们再把它 push 到远端之前突然间灵光一闪,想到有个地方忘了修改了,于是我们打开 file4 来进行改动,那接着就要把这个改动标记为已跟踪,然后我们可以增加一次新的 commit,或者也可以直接把它加到上ー次 commit 中去,比如刚刚提交的这个 msg4,具体做法就是直接 git commit --amend
就可以了。这样 file4 里就要会提交到上一次的 commit 中去
分支
说完了 Git 版本库的操作,其实版本库里面还有个很重要的概念就是分支。我们前面也是在同一个分支去进行文件的变动和提交,一般这个默认分支叫做 master。我们在远端版本库和本地版本库都存在这个分支,它们之间可以互相同步。为了部署的需要,也常常会在远端版本库创建一个 release 分支,保持这个分支随时都是可部署的状态,这样部署服务器上会始终从这个分支去同步文件。
除此之外,我们可以通过 Git 的命令行很方便地创建新的分支。比如,我们按星期进行开发的迭代,第一周的迭代可以创建一个 week1 分支,通过 git checkout -b
带上新分支的名称即可,这样就会创建一个名为 week1 的分支,同时当前的工作分支也自动切换到这个 week1 分支去。于是我们在第一周迭代中新增加的功能,都可以在这个新分支上去开发。而如果在第一周迭代的过程中发现已经发布的系统存在致命的 bug,就可以很方便地切换回,这样就相当于给我们的开发开了一个支线,当这个支线开发完成之后,再把它合并回主线即可,这样不仅不会影响新功能的开发,也不会耽误现有系统 bug 的修复。
刚刚创建的 week1 分支,如果我们在这个分支上进行一些新的提交,就可以把这个分支同步到远端版本库中去,而如果远端版本库不存在这个分支,就通过给 git push 的命令增加-u 的参数以及带上 origin 和新分支的名称即可(如 git push -u origin week1)。
Git 分支的操作
合并
举个例子,我们首先切换到 week1 分支,然后我们进行了文件的改动并提交,这样 file4 就在 week1 分支的最新提交中被改动了。当我们想把 week1 分支的最新的提交合并到 master 分支中,就要切换到 master 分支,然后通过 git merge 带上另一个分支的名称,就可以把那个分支合并到当前的工作分支。这样我们刚才在 week1 分支对 file4 的改动就被合并到 master 分支中了。
冲突处理
合并的时候还有一种特殊的情况,即可能会出现冲突。比如我们在 master 的分支上,file4 它有一些 week1 分支上没有的改动,而且它们的改动出现了冲突,例如它们改动了文件的同一个位置,这样在 merge 之后 Git 的工具就会提示我们 file4 发生了冲突。file4 它的其中部分内容就会在屏幕上显示下面的信息:
它标明了其中的部分哪些来自当前的分支,哪些来自 week1 的分支。这时,我们就需要手工将它们合并为期望的结果。完成后保存,然后通过 git add 就可以让版本库知道这个冲突已经解决了。接着就是 git commit 来提交。
删除
我们已经对 week1 这个分支完成了开发工作,并且已经把它合并到了主分支上,接下来我们就可以把这个分支删掉,方法是,首先要确认当前的工作分支不是要删除的分支,我们要删除 week1,那就要先切换到另一个分支比如说主分支,然后我们通过 git branch -d 参数带上要删除的分支的名称,这样分支在本地就被删除了。我们也可以把删除分支这个操作同步到远端版本库中去,通过 git push origin —delete 带上分支的名称即可。
工具
这么多命令记不住?我们可以使用一些带图形界面的 git 管理工具。SourceTree就是一款方便好用的免费 Git 管理工具,支持 Windows 和 macOS,下载地址https://www.sourcetreeapp.com/。
Git 代码托管开发实践举例
这里介绍 Git 协作开发的方式。在远端版本库上有 master 分支作为开发的主分支,还有 release 分支作为部署用的分支,在程序员的本地版本库中有 master 分支。当他要开发一个新功能的时候,就会在本地的版本库中创建一个新的分支,比如说 feature1,当它完成新功能的开发之后,就会把新的这个分支合并到主分支中,并把它同步到远端。而对于任意一个程序员的本地版本库也是这么做的,这样就可以保证 master 分支始终都是可运行、可测试的版本,而不会是某个功能只完成一半的半成品。
具体来说,我们的远端版本库上有 master 分支和一个起始的 commit,在两个程序员的本地版本库中都已经同步了主分支的最新提交,两位程序员进行了分工,要开发不同的功能,他们分别创建了新功能的本地分支。
第一个程序员他在 feature1 分支上进行了一些开发有了一些提交,而第二个程序员则在 feature2 分支上有了一些提交。接着他们在主分支上发现了 bug,于是第二个程序员切换回主分支,对 bug 进行了修复并立刻同步到了远端版本库中,而第一个程序员则继续把 feature1 给完成了。这时,他会发现主分支有新的提交,所以他首先要从远端把新的提交拉取到本地,接着他把主分支的最新提交和 featur1 的最新提交合并在主分支上,并把这些提交都同步到远端中,于是远端就在没有创建 feature1 分支的情况下得到了 feature1 分支的所有提交。接着 feature2 分支也有了一些新的动作,第一个程序员又在主分支上解决了一个 bug,他进行了一次新提交并且推送到了远端。这时第二个程序员已经完成了对 feature2 的开发,他想把成果合并到主分支中,那他也要先从远端拉取最新的 master 分支,然后再进行合并,最后他再把合并后的结果同步到远端,这样远端就又得到了 feature2 的所有提交。当然第一个程序员的本地版本库也可以从远端拉取最新的 naster 分支。
就这样继续下去,这个版本库它就可以井井有条地进行开发工作,而且保证了主分支始终都是可运行、可测试的版本。
更多
更详细的 git 命令介绍:
https://git-scm.com/book/zh/v2
SourceTreef 使用介绍:
https://www.sourcetreeapp.com/faq/