Git 的诞生 很多人都知道, Linus 在 1991 年创建了开源的 Linux,从此, Linux 系统不断发展,已经成为最大的服务器系统软件了。Linus虽然创建了Linux,但Linux的壮大是靠全世界热心的志愿者参与的,这么多人在世界各地为Linux编写代码,那 Linux 的代码是如何管理的呢? 事实是,在 2002 年以前,世界各地的志愿者把源代码文件通过 diff 的方式发给 Linus,然后由 Linus本人通过手工方式合并代码! 你也许会想,为什么 Linus 不把 Linux 代码放到版本控制系统里呢?不是有 CVS、 SVN 这些免费的版本控制系统吗?因为 Linus 坚定地反对 CVS 和 SVN,这些集中式的版本控制系统不但速度慢,而且必须联网才能使用。有一些商用的版本控制系统,虽然比 CVS、 SVN 好用,但那是付费的,和 Linux 的开源精神不符。 不过,到了 2002 年, Linux 系统已经发展了十年了,代码库之大让 Linus 很难继续通过手工方式管理了,社区的弟兄们也对这种方式表达了强烈不满,于是 Linus 选择了一个商业的版本控制系统 BitKeeper, BitKeeper 的东家 BitMover 公司出于人道主义精神,授权 Linux 社区免费使用这个版本控制系统。安定团结的大好局面在 2005 年就被打破了,原因是 Linux 社区牛人聚集,不免沾染了一些梁山好汉的江湖习气。开发 Samba 的 Andrew 试图破解 BitKeeper 的协议(这么干的其实也不只他一个),被BitMover 公司发现了(监控工作做得不错!),于是 BitMover 公司怒了,要收回 Linux 社区的免费使 用权。 Linus 可以向 BitMover 公司道个歉,保证以后严格管教弟兄们,嗯,这是不可能的。实际情况是这样的:Linus 花了两周时间自己用 C 写了一个分布式版本控制系统,这就是 Git!一个月之内, Linux 系统的 源码已经由 Git 管理了!牛是怎么定义的呢?大家可以体会一下。Git 迅速成为最流行的分布式版本控制系统,尤其是 2008 年, GitHub 网站上线了,它为开源项目免费提供 Git 存储,无数开源项目开始迁移至 GitHub,包括 jQuery, PHP, Ruby 等等。 历史就是这么偶然,如果不是当年 BitMover 公司威胁 Linux 社区,可能现在我们就没有免费而超级好用的 Git 了。 集中式 vs 分布式 Linus 一直痛恨的 CVS 及 SVN 都是集中式的版本控制系统,而 Git 是分布式版本控制系统,集中式和分布式版本控制系统有什么区别呢? 先说集中式版本控制系统,版本库是集中存放在中央服务器的,而干活的时候,用的都是自己的电脑,所以要先从中央服务器取得最新的版本,然后开始干活,干完活了,再把自己的活推送给中央服务器。中央服务器就好比是一个图书馆,你要改一本书,必须先从图书馆借出来,然后回到家自己改,改完了,再放回图书馆
集中式版本控制系统最大的毛病就是必须联网才能工作,如果在局域网内还好,带宽够大,速度够快,可如果在互联网上,遇到网速慢的话,可能提交一个 10M 的文件就需要 5 分钟,这还不得把人给憋死啊。 那分布式版本控制系统与集中式版本控制系统有何不同呢?首先,分布式版本控制系统根本没有“中央服务器”,每个人的电脑上都是一个完整的版本库,这样,你工作的时候,就不需要联网了,因为版本库就在你自己的电脑上。既然每个人电脑上都有一个完整的版本库,那多个人如何协作呢?比方说你在自己电脑上改了文件 A,你的同事也在他的电脑上改了文件 A,这时,你们俩之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。和集中式版本控制系统相比,分布式版本控制系统的安全性要高很多,因为每个人电脑里都有完整的版本库,某一个人的电脑坏掉了不要紧,随便从其他人那里复制一个就可以了。而集中式版本控制系统的中央服务器要是出了问题,所有人都没法干活了。 在实际使用分布式版本控制系统的时候,其实很少在两人之间的电脑上推送版本库的修改,因为可能你们俩不在一个局域网内,两台电脑互相访问不了,也可能今天你的同事病了,他的电脑压根没有开机。因此,分布式版本控制系统通常也有一台充当“中央服务器”的电脑,但这个务器的作用仅仅是用来方便“交换”大家的修改, 没有它大家也一样干活,只是交换修改不方便而已。
当然, Git 的优势不单是不必联网这么简单,后面我们还会看到 Git 极其强大的分支管理,把 SVN 等 远远抛在了后面。 CVS 作为最早的开源而且免费的集中式版本控制系统,直到现在还有不少人在用。由于 CVS 自身设 计的问题,会造成提交文件不完整,版本库莫名其妙损坏的情况。同样是开源而且免费的 SVN 修正了 CVS 的一些稳定性问题,是目前用得最多的集中式版本库控制系统。 除了免费的外,还有收费的集中式版本控制系统,比如 IBM 的 ClearCase(以前是 Rational 公司的, 被 IBM 收购了),特点是安装比 Windows 还大,运行比蜗牛还慢,能用 ClearCase 的一般是世界 500 强,他们有个共同的特点是财大气粗,或者人傻钱多。 微软自己也有一个集中式版本控制系统叫 VSS,集成在 Visual Studio 中。由于其反人类的设计,连微 软自己都不好意思用了。 分布式版本控制系统除了 Git 以及促使 Git 诞生的 BitKeeper 外,还有类似 Git 的 Mercurial 和 Bazaar 等。这些分布式版本控制系统各有特点,但最快、最简单也最流行的依然是 Git!
安装git
最早 Git 是在 Linux 上开发的,很长一段时间内, Git 也只能在 Linux 和 Unix 系统上跑。不过,慢慢地有人把它移植 到了 Windows 上。现在, Git 可以在 Linux、 Unix、 Mac 和 Windows 这几大平台上正常运行了。 在 Linux 上安装 Git yum -y install git
一.创建版本库
20 mkdir /xgp 创建所需目录
21 git init git初始化
创建用户名邮箱
26 git config --global user.name "admin"
27 git config --global user.email "admin@admin"
把文件添加到版本库 现在我们编写三个xgp.txt 文件,内容如下
23 vim xgp1.txt #xgp1
24 vim xgp2.txt #xgp2
25 vim xgp3.txt #xgp3
第一步,用命令 git add 告诉 Git,把文件添加到仓库: 28 git add xgp1.txt xgp2.txt xgp3.txt
第二步,用命令 git commit 告诉 Git,把文件提交到仓库: 29 git commit -m "第一次提交" 查看提交信息
52 cd ./git
53 ls -a
57 cat logs/refs/heads/master
58 cat HEAD
小结 现在总结一下今天学的两点内容: 初始化一个 Git 仓库,使用 git init 命令。 添加文件到 Git 仓库,分两步: • 第一步,使用命令 git add <file>,注意,可反复多次使用,添加多个文件; • 第二步,使用命令 git commit,完成。
二.时光机穿梭
我们已经成功地添加并提交了一个 readme.txt 文件,现在,是时候继续工作了,于是,我们继续修改 xgp.txt 文件,改成如下内容: xgp1 回退测试 现在,运行 git status 命令看看结果: git status 命令可以让我们时刻掌握仓库当前的状态,上面的命令告诉我们, readme.txt 被修改过了,但还没有准备提交的修改
虽然 Git 告诉我们 xgp1.txt 被修改了,但如果能看看具体修改了什么内容,自然是很好的。比如你休假两周从国外回来,第一天上班时,已经记不清上次怎么修改的 readme.txt,所以,需要用 git diff 这个命令看看: git diff 顾名思义就是查看 difference,显示的格式正是 Unix 通用的 diff 格式,可以从上面的命令输出看到,我们第一行添加了一个”回退测试”
知道了对 readme.txt 作了什么修改后,再把它提交到仓库就放心多了,提交修改和提交新文件是一样的两步,第一步是 git add: git add xgp1.txt 同样没有任何输出。在执行第二步 git commit 之前,我们再运行 git status 看看当前仓库的状态: git status 告诉我们,将要被提交的修改包括 xgp.txt,下一步,就可以放心地提交了: git commit -m "第二次提交"
提交后,我们再用 git status 命令看看仓库的当前状态: 小结 要随时掌握工作区的状态,使用 git status 命令。 如果 git status 告诉你有文件被修改过,用 git diff 可以查看修改内容。
四。管理修改
为什么说 Git 管理的是修改,而不是文件呢?我们还是做实验。第一步,对 xgp1.txt 做一个修改,比如加一行内容:
[root@localhost xgp]# cat xgp1.txt
xgp1
回退测试
xgp666
xgpniubi
然后,添加:
93 git add xgp1.txt
94 git status
然后,再修改 readme.txt:
[root@localhost xgp]# cat xgp1.txt
xgp1
回退测试
xgp666
xgpniubi
1
提交:
[root@localhost xgp]# git commit -m "第四次提交"
[master 31cb090] 第四次提交
1 file changed, 1 insertion(+)
提交后,再看看状态:
咦,怎么第二次的修改没有被提交? 别激动,我们回顾一下操作过程: 第一次修改 -> git add -> 第二次修改 -> git commit 你看,我们前面讲了, Git 管理的是修改,当你用 git add 命令后,在工作区的第一次修改被放入暂存区, 准备提交, 但是,在工作区的第二次修改并没有放入暂存区,所以, git commit 只负责把暂存区的修改提交了,也就是第一次的 修改被提交了,第二次的修改不会被提交。 提交后,用 git diff HEAD -- readme.txt 命令可以查看工作区和版本库里面最新版本的区别: 可见,第二次修改确实没有被提交。 那怎么提交第二次修改呢?你可以继续 git add 再 git commit,也可以别着急提交第一次修改,先 git add 第二次修 改,再 git commit,就相当于把两次修改合并后一块提交了: 第一次修改 -> git add -> 第二次修改 -> git add -> git commit 好,现在,把第二次修改提交了,然后开始小结。 小结 现在,你又理解了 Git 是如何跟踪修改的,每次修改,如果不 add 到暂存区,那就不会加入到 commit 中。
五。撤销修改
自然,你是不会犯错的。不过现在是凌晨两点,你正在赶一份工作报告,你在 readme.txt 中添加了一行:
[root@localhost xgp]# cat xgp1.txt
xgp1
回退测试
xgp666
xgpniubi
1
22222
在你准备提交前,一杯咖啡起了作用,你猛然发现了“stupid boss”可能会让你丢掉这个月的奖金!既然错误发现得很及时,就可以很容易地纠正它。你可以删掉最后一行,手动把文件恢复到上一个版本的状态。如果 用 git status 查看一下: 你可以发现, Git 会告诉你, git checkout -- file 可以丢弃工作区的修改:
git checkout -- xgp1.txt
[root@localhost xgp]# cat xgp1.txt
xgp1
回退测试
xgp666
xgpniubi
1
庆幸的是,在 commit 之前,你发现了这个问题。用 git status 查看一下,修改只是添加到了暂存区,还没有提交: Git 同样告诉我们,用命令 git reset HEAD file 可以把暂存区的修改撤销掉(unstage),重新放回工作区: [root@localhost xgp]# git reset HEAD xgp1.txt 重置后撤出暂存区的变更: M xgp1.txt git reset 命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用 HEAD 时,表示最新的版本。 再用 git status 查看一下,现在暂存区是干净的,工作区有修改:
还记得如何丢弃工作区的修改吗? 整个世界终于清静了! 现在,假设你不但改错了东西,还从暂存区提交到了版本库,怎么办呢?还记得版本回退一节吗? 可以回退到上一个版本。不过,这是有条件的,就是你还没有把自己的本地版本库推送到远程。还记得 Git 是分布式版本控制系统吗?我们后面会讲到远程版本库,一旦你把“stupid boss”提交推送到远程版本库,你就真的惨了……
小结 又到了小结时间。 场景 1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令 git checkout -- file。 场景 2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令 git reset HEAD file,就回到了场景 1, 第二步按场景 1 操作。 场景 3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程 库。
六。删除文件
在 Git 中,删除也是一个修改操作,我们实战一下,先添加一个新文件 test.txt 到 Git 并且提交:
[root@localhost xgp]# vim test.txt
[root@localhost xgp]# cat test.txt
qqqqqqqqqqqq
[root@localhost xgp]# git add test.txt
[root@localhost xgp]# git commit -m "第一次"
[master f9688d7] 第一次
1 file changed, 1 insertion(+)
create mode 100644 test.txt
一般情况下,你通常直接在文件管理器中把没用的文件删了,或者用 rm 命令删了: 这个时候, Git 知道你删除了文件,因此,工作区和版本库就不一致了, git status 命令会立刻告诉你哪些文件被删除了: 现在你有两个选择,一是确实要从版本库中删除该文件,那就用命令 git rm 删掉,并且 git commit: 现在,文件就从版本库中被删除了。 另一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本: $ git checkout -- test.txt git checkout 其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。
小结 命令 git rm 用于删除一个文件。如果一个文件已经被提交到版本库,那么你永远不用担心误删,但是要小心,你只 能恢复文件到最新版本,你会丢失最近一次提交后你修改的内容。
七。Git远程仓库
第1步:创建SSH Key。在用户主目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有 id_rsa 和 id_rsa.pub 这两个文件, 如果已经有了,可直接跳到下一步。如果没有,打开 Shell (Windows 下打开 Git Bash),创建 SSH Key: ssh-keygen -t rsa -C 2877364346@qq.com 你需要把邮件地址换成你自己的邮件地址,然后一路回车,使用默认值即可,由于这个 Key 也不是用于军事目的, 所以也无需设置密码。 如果一切顺利的话,可以在用户主目录里找到.ssh 目录,里面有 id_rsa 和 id_rsa.pub 两个文件,这两个就是 SSH Key 的秘钥对, id_rsa 是私钥,不能泄露出去, id_rsa.pub 是公钥,可以放心地告诉任何人。
第 2 步:登陆 GitHub,打开“Account settings”, “SSH Keys”页面: 然后,点“Add SSH Key”,填上任意 Title,在 Key 文本框里粘贴 id_rsa.pub 文件的内容:
为什么 GitHub 需要 SSH Key 呢?因为 GitHub 需要识别出你推送的提交确实是你推送的,而不是别人冒充的,而Git 支持 SSH 协议,所以, GitHub 只要知道了你的公钥,就可以确认只有你自己才能推送。
从远程库克隆 现在的情景是,你已经在本地创建了一个 Git 仓库后,又想在 GitHub 创建一个 Git 仓库,并且让这两个仓库进行远程同步,这样, GitHub 上的仓库既可以作为备份,又可以让其他人通过该仓库来协作,真是一举多得。 首先,登陆 GitHub,然后,在右上角找到“Create a new repo”按钮,创建一个新的仓库 目前,在 GitHub 上的这个 learngit 仓库还是空的, GitHub 告诉我们,可以从这个仓库克隆出新的仓库,也可以把一个已有的本地仓库与之关联,然后,把本地仓库的内容推送到 GitHub 仓库。
现在,远程库已经准备好了,下一步是用命令 git clone 克隆一个本地库: git clone git@github.com:xgp666/xgp.git
注意把 Git 库的地址换成你自己的,然后进入 gitskills 目录看看,已经有 README.md 文件了。 如果有多个人协作开发,那么每个人各自从远程克隆一份就可以了。 你也许还注意到, GitHub 给出的地址不止一个,还可以用 https://github.com/michaelliao/gitskills.git 这样的地 址。实际上, Git 支持多种协议,默认的 git://使用 ssh,但也可以使用 https 等其他协议。 使用 https 除了速度慢以外,还有个最大的麻烦是每次推送都必须输入口令,但是在某些只开放 http 端口的公司内 部就无法使用 ssh 协议而只能用 https。 测试:在远程库创建一个文件
[root@localhost xgp]# vim index.html
[root@localhost xgp]# cat index.html
Qqqqq
[root@localhost xgp]# git add index.html
[root@localhost xgp]# git commit -m "123"
[master 65e2ec7] 123
1 file changed, 1 insertion(+)
create mode 100644 index.html
[root@localhost xgp]# git push origin master (默认是master)
浏览器查看 小结 要克隆一个仓库,首先必须知道仓库的地址,然后使用 git clone 命令克隆。 Git 支持多种协议,包括 https,但通过 ssh 支持的原生 git 协议速度最快。
八。将本地链接远程库
现在的情景是,你已经在本地创建了一个 Git 仓库后,又想在 GitHub 创建一个 Git 仓库,并且让这两个仓库进行远 程同步,这样, GitHub 上的仓库既可以作为备份,又可以让其他人通过该仓库来协作,真是一举多得。
目前,在 GitHub 上的这个 learngit 仓库还是空的, GitHub 告诉我们,可以从这个仓库克隆出新的仓库,也可以把一个已有的本地仓库与之关联,然后,把本地仓库的内容推送到 GitHub 仓库。 现在,我们根据 GitHub 的提示,在本地的 learngit 仓库下运行命令: git remote add origin git@github.com:xgp666/xgp.git
下一步,就可以把本地库的所有内容推送到远程库上: 1.git branch --set-upstream-to=origin/master master 2.git fetch 3.再次执行git branch --set-upstream-to=origin/master master 4.git push origin master 5.git pull 6.
[root@localhost xhp]# vim aa.txt
[root@localhost xhp]# git add *
[root@localhost xhp]# git commit -m "4"
[master 2f77003] 4
[root@localhost xhp]# git status
! 位于分支 master
! 您的分支领先 'origin/master' 共 3 个提交。
! (使用 "git push" 来发布您的本地提交)
!
无文件要提交,干净的工作区
7.git push origin master 浏览器查看 从现在起,只要本地作了提交,就可以通过命令: $ git push origin master 把本地 master 分支的最新修改推送至 GitHub,现在,你就拥有了真正的分布式版本库!