到了Palm之后开始接触Git,逐渐发现Git的便利之处。于是觉得很有必要掌握更多的Git技巧,而不能重演SVN的历史(只懂得基本的操作:checkout checkin update info)。
Git必知必会之:Git好在哪里
个人体验,Git最好的地方在亮点:
- 离线管理(也就是通常说的“分布式”)
- 分支
先说说离线管理。
Git能做到离线管理基于Git本地仓库是对“服务器”仓库的完全拷贝,本地拥有版本管理所需要的任何信息:变更历史、分支、标签等等。
有了离线管理,大部分的工作都不需要依赖网络了。虽然说现在的网速是越来越快,但网络的速度总是比不上本地的速度。(虽然版本历史多了之后,Git查看日志的速度也不算很快 :( )。
至于为什么Git要实现离线管理的功能,应该跟Git的背景有关:开发Linux的多数是些自愿者,他们崇尚开源、自由和免费,并且分布在世界各地,能依赖一个中央版本仓库来管理源代码并不方便。
分支
在SVN里,分支其实就是一个目录,比如通常的项目目录结构如下:
+trunk
+tags
+branches
--production
--emergency
...
在SVN的世界里,一个分支其实就是一个目录,production对应:http://server/project/branches/production; emergency对应http://server/project/branches/emergency。
而Git则完全不同,不同的分支是不同的时空,分支之间除了可以merge之外没有任何联系。比如http://server/porject.git
这个项目,本地的工作空间是/home/user/project/,在该目录内同时只能有一个branch的内容,即/home/user/project/readme.txt在另外一个branch的目录也是/home/user/project/readme.txt
Git必知必会之:基本Git命令
对一个版本控制系统来说,总少不了这样的命令:
- 添加文件进入版本控制系统
- 提交修改过的内容进入版本系统
- 将文件从版本系统中删除
- 查看某文件(或全部)的修改历史
- 比较两个文件的差异
下面分别介绍对应的Git命令。在这之前,需要在本地搭建一个git仓库:
git init
输出:
Initialized empty Git repository in /home/gavin/project/git/blog/.git/
执行之后在该目录下多了个.git的隐藏目录,就算初始化成功了。这个目录里包含了git的全部信息,关于里面的文件结构以后可以继续研究。
添加文件进入版本控制系统
init之后,仓库还是空的,看不到任何log。(如果运行git log 将会出现这样的错误:fatal: bad default revision 'HEAD')。现在将文件readme.txt添加进工作区(工作区的概念在下一节)
git add readme.txt
这样readme.txt就已经在工作区里了(但还未提交),即此文件以后就归git管理了。
提交文件修改
git commit -m "Jira-0001: first commit"
也可以加另外一个参数:-a 表示直接提交所有文件(而不是只在工作区中的文件,从未add过的文件除外),这样就可以不执行git add命令了。
查看日志
git log readme.txt
如果已经习惯SVN的日志输出格式,还可以加一些参数限制输出格式:
git log --pretty=format:'%h %an %cd %s' readme.txt
其中,
%h 表示此次提交的唯一标识;
%an 表示提交人 (author name)
%cd 表示提交日企 (commit date)
%s 表示提交时输入的消息 (即通过-m参数指定的字符串)
在linux下还有些图形化的日志工具:gitg 和gitk
git log的更多用法可以参见:http://gitref.org/inspect/#log
从仓库中删除指定文件
将文件添加进git可以用git add和git commit,那么从库中删除指定文件,即不再需要将某个文件纳入版本管理的命令是:
git rm --cached readme.txt
git commit -m "delete readme.txt"
注意,如果不加--cached参数,则会将文件从磁盘删除。
放弃本地修改
偶尔会出现修改代码,却越改越乱的情况,这时候就想reset到原始状态。
git reset HEAD -- readme.txt
也可以通过checkout:
git checkout readme.txt
checkout 和 reset的区别在于:reset是将当前“指针”指向到某处(默认为最新版本),而自某处之后的版本将被丢弃(通过低级命令可以找回); 而checkout只是将指定版本的内容签出到当前版本。
比较两个文件
git diff
将显示所有未“git add”的修改内容。而:
git diff --cached
将显示所有已经经过“git add”但未经过“git commit”的变动。
如果想比较同一个文件的不同历史版本可以:
git diff 6a5a54f:readme.txt 91d2ea9:readme.txt
当然如果只是想比较两个版本的不同,不需要特别指定某个文件,则只需将版本后的“:readme.txt”去掉即可。
创建分支
前面提到过分支的强大,我通常为每个任务创建一个分支:
git branch jira-0001
这样就创建了一个名为jira-0001的分支,可以通过:
git checkout jira-0001
切换到该分支。想查看所有已经创建的分支:
git branch -l
合并分支
在分支上的工作上结束之后,需要将修改合并到主分支上,比如需要将jira-0001的工作合并到master中,则:
git merge jira-0001
如果一切顺利,则工作就算完成了,如果不走运(很遗憾,这经常发生),则会出现这样的错误:
CONFLICT (content): Merge conflict in readme.txt
Recorded preimage for 'readme.txt'
Automatic merge failed; fix conflicts and then commit the result.
则需要手动修改文件,提交后工作才算完成。git还为此提供了图形化界面,可以通过:
git mergetool
来进入图形化界面 (前提是在git 配置过mergetool)。
后续内容
经过一次草稿丢失(草稿隔了夜,早上新加的几段没保存)后,发现git有太多的内容需要介绍:
git log
git 配置
git remote、fetch、pull
这些内容不是一篇博文能承载的,留在后面补充吧。