git 5分钟教程

来源:互联网 发布:黑魂34g内存优化 编辑:程序博客网 时间:2024/06/11 18:40
  1. git 之五分钟教程  
  2.         yubao.liu at gmail.com  
  3.         dieken at newsmth  
  4.   
  5. 我懒,估计读者也是懒的,所以写点小文供大家了解一下 git。  
  6.   
  7. git 是一个分布式版本管理工具,关于它大家应该都有所耳闻了,  
  8. 貌似 Linus 说是 2005 年 4 月 17 日公开的,如今已经 1.4.3.4  
  9. 了,开发社区非常活跃,一如 Linux (Linus 英明神武,sigh),  
  10. git 现在的维护者是 Junio C Hamano,主页在 [url]http://git.or.cz[/url] 。  
  11.   
  12. 关于分布式版本管理工具的定义,我土,感受最深的是每个人都有  
  13. 自己的代码库,这个跟 SVN 不同,这个区别带来的好处是跟踪本地  
  14. 的修改过程非常方便,SVN 里头不同代码库之间不能 switch,麻烦。  
  15. 但是 SVN 的用户界面实在是比 git 友好得多,特别是 SVN 建立  
  16. 在大家普遍比较熟悉的 CVS 模型上。  
  17.   
  18. 闲话少说,切入正题。  
  19.   
  20. * git 的四种对象  
  21.   
  22. 在 git 中最基本的四种对象为 blob,tree,commit,tag。  
  23.   blob   - 即文件,注意只包含内容,没有名字,权限等属性(例外的是  
  24.            包含大小)  
  25.   tree   - 所有文件名字以及其属性的列表,这些属性只是基本属性,比如  
  26.            权限,是否符号链接。git 没有像 svn 那样丰富的 property 用。  
  27.   commit - 表示修改历史,描述一个个 tree 之间如何联系起来的,每一个  
  28.            commit 对应有一个 tree —— commit 的修改结果。commit 包含  
  29.            作者、提交者、parent commit、tree、log、修改时间、提交时间,  
  30.            注意 git 明确区分了 author 和 committer 这两个角色。  
  31.   tag    - 标签,它可以指向 blob, tree, commit 并包含签名,最常见的是  
  32.            指向 commit 的 GPG 签名的标签。  
  33.   
  34. blob,tree,commit 都是用其存储内容的 SHA-1 值命名的(不是简单的对  
  35. 整个文件取 SHA-1 值),tag 自然使用的是普通名字。  
  36.   
  37. git 的想法就是 blob 和 tree 中包含的只能是最公共的最基本的信息,  
  38. 所以它不会在 blob 和 tree 里头存放 svn 那么多的自定义属性。  
  39.   
  40. git 也只是定位在 content tracker,这个术语跟 VCS 是有微妙的差别的,  
  41. 第一是体现在它对 blob 和 tree 的“纯数据”的要求上,第二是对分支  
  42. 合并的处理上,如下所示(a, b, c 表示三个 commit):  
  43.   
  44.    -- a ----> master  
  45.        \  
  46.         b ----- c -> test  
  47.   
  48. test 分支从 master 的 a 点上分出来,做了许多修改,然后当 master  
  49. 分支和 test 分支合并会怎么样呢?按照通常的 CVS/SVN 的做法,是创建  
  50. 一个新的 commit,继承自 a 和 c,但是 git 则是直接修改 master  
  51. 使其指向 c,在 git 这称为 fast forward,整个版本图变成这样:  
  52.   
  53.    -- a -- b -- c --> master  
  54.                  \  
  55.                   `--> test  
  56.   
  57. 从图中看 master 分支和 test 分支分不出主次,这里头的含义很值得玩味。  
  58.   
  59.   
  60. * 选择对象  
  61.   
  62. blob, tree, commit 都是用其 SHA-1 命名的,如果得到一个 SHA-1  
  63. 值,但不知道是什么类型,可以用  
  64.         git cat-file -t SHA-1  
  65. 命令识别,它会输出 blob, tree, commit (如果给它一个 tag 名字,  
  66. 它也能输出 tag) 这样的字符串,然后就可以用  
  67.         git cat-file type SHA-1  
  68. 查看其内容了,这里 type 是 blob, tree, commit, tag 中的一个。  
  69. 注意对于 tree 对象它输出的是 tree 对象存储时的样子,看起来像是  
  70. 乱码,所以对于 tree 要用  
  71.         git ls-tree SHA-1  
  72.   
  73. 另外 git 也可以在需要 tree 对象的地方给它一个 commit 对象  
  74. 或者一个 tag 对象,这时就是用 commit 或 tag 指向的 tree,  
  75. 在 git 的手册中可以看到 tree-ish 这样的写法,意思就是可以  
  76. 从 tree-ish 这个参数得到一个 tree,同样的,也有 commit-ish.  
  77. 比如  
  78.     git-ls-tree [-d] [-r] [-t] [-z] [--name-only]   
  79.         [--name-status] [--full-name] [--abbrev=[<n>]]  
  80.         <tree-ish> [paths…]  
  81.   
  82. 总结一下,可以用 SHA-1 值指定是哪一个 blob/tree/commit 对象(无需  
  83. 指出类型),可以用 commit 或者 tag 指定一个 tree,可以用 tag 指定  
  84. 一个 commit(一般 tag 都是用来指向 commit,虽然它可以指向其它类型  
  85. 对象)。  
  86.   
  87. 在 git 中很频繁提到 ref 或者 reference,它就是一个名字,包含了  
  88. 一个 commit。分支名字,tag 其实都是 reference,这可以从它们都  
  89. 位于 .git/refs 下看出来,区别在于分支名字(就是指代各个分支的  
  90. HEAD)指向的目标可以改变,而 tag 不能。  
  91.   
  92. 指代一个 tree 中的某个文件可以用  
  93.      tree-ish:path/to/file  
  94. 的格式,注意 path 前面没有“/”。  
  95.   
  96. * commit 记法  
  97.   
  98. commit 因为有继承关系(ancestry),它的选择方式更特殊些,比如怎么  
  99. 选择它的 parent commit,以及 parent commit 的 parent commit,  
  100. 虽然这个信息可以逐步通过 git cat-file commit commit-ish 获得,  
  101. 但显然需要一个简便的记法来指定,类似于 SVN 的 HEAD、PREV、COMMITTED。  
  102.   
  103. 这种 git 独有的记法在 git-rev-parse(1) 有详细描述,简要的说,  
  104. commit-ish^n    表示 commit 的第 n 个 直接 parent commit,n 从 1 算起。  
  105.                 只有在一个 commit 是合并的结果时这个 commit 才可能有  
  106.                 commit^2, commit^3。  
  107.                 当 n 等于 1 时可以省略,只写 ^。  
  108.                 当 n 等于 0 时就指 commit 本身,这可以用来将一个 tag  
  109.                 转成 commit。  
  110.   
  111. 这种 ^n 记法可以连续写,比如 HEAD^2^2 表示 HEAD 的第二个 parent commit  
  112. 的第二个 parent commit。  
  113.   
  114. commit-ish~n    这种记法是 commit-ish^^^...^ 总共 n 个 ^ 的简写,这样在  
  115.                 直线历史上能够方便的说倒数第 (n+1) 次提交(commit-ish^1  
  116.                 是倒数第二次)。  
  117.                 当 n 等于 1 时自然也是可以不写 1 的。  
  118.   
  119. 在接收 commit 集合的命令中,比如 git log,commit 的记法有特殊含义:  
  120. git log HEAD      
  121.                 表示按照 parent 关系从 HEAD 能够到达的所有 commit,  
  122.                 包含 HEAD。  
  123.   
  124. git log ^HEAD~2 HEAD  
  125.                 表示从 HEAD  能够到达的所有 commit(包含 HEAD),排除  
  126.                 能从 HEAD~2 到达的所有 commit(包含 HEAD~2),这样计算  
  127.                 的结果就是 HEAD~1 和 HEAD.  
  128.   
  129. 这样的集合记法可以写多个,比如  
  130. git log commit-1 commit-2 ^commit-3 ^commit-4  
  131.   
  132. 特殊的,commitA..commitB 是 ^commitA commitB 的简写。  
  133.   
  134. 这种集合记法在比较两个分支的区别时很有效,比如想看 test 分支从 master  
  135. 分支分出来后做了什么修改,可以用  
  136. git log ^master test  
  137. 这里不需要从分支点排除,因为按照这种集合相减关系,得到的就是 test 自  
  138. master 分出来后所创建的所有 commit。  
  139.   
  140. 由于 git 的 fast forward 形式的合并,HEAD^1 并不总是合并前所在的  
  141. 工作分支,这一点是很容易迷糊人的,如下图(a,b,c 代表 commit):  
  142.   
  143.    --- a ---> master  
  144.         \  
  145.          b ----- c ---> test  
  146.   
  147. 现在 master 从 test 上合并,按照 git 的做法,因为 a 是 c 祖先,而且  
  148. master 分支上自 a 之后没有新的修改,所以 git 直接把 master 指向 c,  
  149. 而不创建新的 commit:  
  150.   
  151.    --- a --- b ----- c ---->master, test  
  152.   
  153. 现在 master 和 test 指向同一个 commit,master^1 是 b,而 b 并不在  
  154. *原先的* master 分支上。合并后,master 和 test 的历史是一致的,没有  
  155. 区别,这个 CVS 和 SVN 的做法是不同的,按照 CVS 或者 SVN 的做法,  
  156. 合并后 show log master 看到的应该是 a 和 d(d 的 parent commit 是 a 和 c),  
  157. 但是 git log master 看到的是 a b c。  
  158.   
  159. git 的这个 fast forward 形式的 merge 很有意思。  
  160.   
  161. * .git 和 index  
  162. CVS/SVN 中,代码库和工作拷贝都是分开的,工作拷贝的版本信息放在每一层  
  163. 目录中,这给在工作拷贝中 grep 代码不便。git 将代码库和工作拷贝的版本  
  164. 信息统一放在工作拷贝的顶层目录下的 .git/ 中(可以配置使用其它目录)。  
  165.   
  166. .git/  
  167.   ├─branches  
  168.   ├─hooks  
  169.   ├─info  
  170.   ├─objects  
  171.   │  ├─info  
  172.   │  └─pack  
  173.   ├─refs  
  174.   │  ├─heads  
  175.   │  └─tags  
  176.   └─remotes  
  177.   
  178. objects 下面是存放 blob, tree, commit 对象的地方,取 SHA-1的十六进制  
  179. 前两个字符做目录名,余下的做文件名, 注意一个对象的 SHA-1 值不是直接  
  180. 对这里面的文件计算 SHA-1 值得到的。  
  181.   
  182. 用 SHA-1 引用对象时,一般取其前六个字符,只要能够唯一识别一个对象即可,  
  183.   
  184. .git 下可以存在 config 文件,这个是一个库的配置文件,用户可以在自己  
  185. 的 HOME 目录下有 .gitconfig 文件,两者格式是一样的,参考 git-repo-config(1)。  
  186.   
  187. .git 下可能还存在一个 logs 目录,由于 git 的 fast forward 形式的合并  
  188. 做法,导致不能通过 git log 来获取一个分支的头曾经指向哪些 commit,这个  
  189. logs 目录下的文件就是用来记录这个信息的,要想使用它需要在 .git/config  
  190. 或者 ~/.gitconfig 里头设置选项。这些 log 就称为 reflog,叫 ref 是因为  
  191. 分支名和标签都是 commit 的引用,都放在 .git/refs 目录下。  
  192.   
  193. .git 下还有 index 文件(对于刚创建的空库是没有的),这个文件就起到 CVS  
  194. 的 .cvs 目录或者 SVN 的 .svn 目录的作用,它记录了工作拷贝在哪个 commit  
  195. 上,以及工作拷贝中被 git 管理的文件的状态。在 SVN 中,.svn 里头保存了  
  196. 一份代码,称为 BASE,index 里头跟它作用类似,不过它只是记录了文件名  
  197. 和其 SHA-1 值、mtime、ctime、所在的设备号、权限位、uid、gid、size,  
  198. 真正的文件内容都在 .git/objects 里头。  
  199.   
  200. index 跟 tree 很类似,除了包含一些信息用于跟踪工作拷贝中的文件状态,  
  201. 粗略的讲,使用 git 最常打交道的有三个 tree:HEAD 对应的 tree,index  
  202. 对应的 tree,工作拷贝中的目录树。  
  203.   
  204. git 和 svn 对于处理 index 和 BASE 版本的方式不大一样,SVN 是以工作拷贝为  
  205. 中心,BASE 在提交前是不会变的,git 则以 index 为中心,在提交前 index 是  
  206. 可以变的,这一点也很迷糊人,比如,如果你在工作拷贝中修改了文件,用 git  
  207. commit 却告诉你没有修改,因为 git commit 是把 index 代表的状态提交到库里  
  208. 头,只有你用git-update-index your_file 将工作目录中的修改反映到 index 里,  
  209. git commit 才会跟直觉的提交行为一致,不过文件被提交到库里的时机是在调用  
  210. git-update-index 的时候,而非 git commit 的时候,git commit只是依照  
  211. index 的状态创建一个 tree 放入 .git/ojbects 里头。可以用 git commit -a  
  212. 达到 svn commit 的直观效果。  
  213.   
  214. index 也被称为 cache,这是早期的叫法。  
  215.   
  216. 一份 .gitconfig:  
  217. [core]  
  218.         fileMode = false  
  219.         logAllRefUpdates = true  
  220.     compression = 9  
  221.   
  222. [diff]  
  223.     color = auto  
  224.   
  225. [pack]  
  226.     window = 64  
  227.   
  228. [user]  
  229.     email = your_email  
  230.     name = your_name  
  231.   
  232. [merge]  
  233.         summary = true  
  234.   
  235.   
  236.   
  237. * 入门操作  
  238. git 的命令分成两类,低级命令(称为“plumbing”)和高级命令(称为“porcelain”),  
  239. 不算开头的 git 前缀,低级命令的名字 *一般* 是两个单词的,高级命令则 *一般* 是  
  240. 一个单词的。  
  241.   
  242. 低级命令都是一些 C 写的小程序,直接操作 git 核心的对象如 tree,commit 以  
  243. 及 index 等,这些是提供给编写 git 包装程序比如 cogito 的人使用的,高级命  
  244. 令是面向最终用户的,这些命令要么是对 git 命令的包装,要么是提供一些方便的  
  245. 功能比如统计 log。  
  246.   
  247. 一个 git 命令可以写成 "git-cmd" 或者 "git cmd",前者在 PATH 中寻找 "git-cmd"  
  248. 这个命令并执行之,后者则是执行 git 命令,传入后面的 cmd 等参数,git 这个  
  249. 程序在 exec-path 中寻找 git-cmd 并执行之,这个 exec-path 可以通过命令行  
  250. 选项 --exec-path=XXX 或者 GIT_EXEC_PATH 环境变量设置,在安装 git 时它内部  
  251. 也会保存一个缺省的 exec-path。这个路径列表的语法跟 PATH 一致。git 的这种  
  252. exec-path 机制能够让它很方便的加入更多的命令。  
  253.   
  254. 获取一个 git 命令的帮助可以用 man git-cmd 或者 git --help cmd 或者 git help cmd  
  255. 或者 git cmd --help,这四种命令效果跟第一条一样。  
  256.   
  257. (a)  
  258. git init-db  
  259. 在当前目录下创建 git 库(.git)  
  260.   
  261. (b)  
  262. echo hello > xxx  
  263. git add xxx  
  264. 递归将 xxx 加入 git 控制中,执行完后文件已经被加入库中,index 也已更新,  
  265. 这时 git diff 因为 index 跟 工作拷贝一致,所以没有差别。  
  266. // 其实就是用的 git-update-index,这个命令在更新 index 时也会立即把  
  267. // 文件写入库中,这点跟 SVN 的 add 不一样。  
  268.   
  269. (c)  
  270. git commit  
  271. 将 index 实例化为一个 tree,创建一个 commit,这个时候看.git/objects 下面  
  272. 就有三个对象:blob xxx,一个 tree 和一个commit。  
  273.   
  274. (d)  
  275. echo world >> xxx  
  276. git diff  
  277. 比较 index 和 工作拷贝  
  278. git diff --cached  
  279. 比较 index 和前一次提交对应的 tree,这就是 git commit 要提交的东西。  
  280. git diff HEAD  
  281. 比较工作拷贝和前一次提交对应的 tree,这就是 git commit -a 要提交的东西。  
  282.   
  283. (e)  
  284. git commit  
  285. 因为 index 没有改变,而 commit 只是从 index 创建 tree,所以没有需要提交  
  286. 的,提示用 git-update-index.  
  287. git update-index xxx  
  288. git commit  
  289. 更新了 index,这下可以提交了, 如果是想提交 index 中所有修改过和删除了的  
  290. 文件,这两步可以用 git commit -a(这个命令类似于在顶层目录执行 svn commit)。  
  291.   
  292. (f)  
  293. git log -p  
  294. 查看 log,-p 表示显示每一次修改的 diff。  
  295.   
  296. (g)  
  297. git branch  
  298. 显示所有分支,当前分支前面有一个 *,默认分支叫 master。  
  299. git branch test  
  300. 建立一个分支 test  
  301. git checkout test  
  302. 切换到 test 分支,这两步可以合并为 git checkout -b test。  
  303.   
  304. (h)  
  305. 在 test 分支做了有些修改后提交;  
  306. git checkout master  
  307. 回到主分支  
  308. git log master..test  
  309. 查看 test 分支上修改记录(参考 “commit 记法” 一节)。  
  310.   
  311. git merge --no-commit "msg" HEAD test  
  312. 这个命令的参数语法比较怪异。--no-commit 是让 git 不要马上  
  313. 提交,这样可以检查一下合并结果是否正确,由于这段时间主干上  
  314. 没有修改,所以这个合并其实就是 fast forward,直接将  
  315. master 指向 test 分支的 HEAD,不会创建新的 commit。  
  316. // git pull 的行为有点古怪,不推荐使用。  
  317.   
  318. 如果不是 fast forward,可以用 git diff 检查一下然后再  
  319. git commit;如果合并后有冲突,git 会把冲突标记写入文件,  
  320. 这个时候打开这个文件编辑之,解决冲突后用 git-update-index  
  321. 更新 index,然后 git commit。  
  322.   
  323. (i)  
  324. gitk --all  
  325. 查看版本图。  
  326.   
  327. (j)  
  328. git branch -D test  
  329. 删除 test 分支。  
  330.   
  331. (k)  
  332. git clone git://git.kernel.org/pub/scm/git/git.git  
  333. 把 git 的开发库镜像到本地的 git 目录中。  
  334.   
  335. (l)  
  336. cd git && git fetch  
  337. 与 git 的开发库同步。不建议用 git pull,可以用 git fetch  
  338. 和 git merge 的组合代替 git pull。  
  339.   
  340. (m)  
  341. git status  
  342. 查看工作拷贝中的修改情况  
  343.   
  344. (n)  
  345. git show commit-ish  
  346. 查看一个 commit。  
  347.   
  348. (o)  
  349. git reset commit-ish  
  350. 将当前 HEAD 指向 commit-ish 代表的 commit。  
  351. 这个命令有三个选项:  
  352. --mixed     默认选项,调整 HEAD 并重置 index,保留工作拷贝中的修改,  
  353.             修改过的文件需要再次 git-update-index 以更新 index。  
  354. --soft      仅修改 HEAD。  
  355. --hard      修改 HEAD,并将 index 和 工作拷贝的状态对应到 commit-ish  
  356.             相应的 tree 上,这会丢失工作拷贝中的修改!  
  357.   
  358. 在 git-reset(1) 中有一些这个命令的使用场合说明,可以看看,常用的可能  
  359. 是第一条:  
  360. git reset --soft HEAD^  
  361. 撤销最近一次提交。  
  362.   
  363. // 类似于 svn revert 的是 git checkout。  
  364.   
  365. (p)  
  366. git revert commit-ish  
  367. 撤销库里的某次提交,注意跟 svn revert 撤销工作拷贝的修改不一样。  
  368. 如果是撤销最近的提交,最好用 git reset。  
  369.   
  370. (q)  
  371. 有点点类似于 svn info 的命令是:  
  372. cat .git/remotes/origin  
  373.   
  374. 还有一个 git-push(1), 比较的复杂,以及其它的许多许多命令,  
  375. 比如格式化 patch,将 patch 通过邮件发送,从 mbox 中提取  
  376. patch,等等,这篇小文就不罗嗦了(我也不大会-_-b),可以参考手册。  
  377.   
  378.   
  379. * 参考资料  
  380. [url]http://www.kernel.org/pub/software/scm/git/docs/[/url]  
  381.     这个上面的 tutorial 和 Everyday Git 指向两篇很好的教程,此页的  
  382. FURTHER DOCUMENTATION 中提到的 Discussion 以及 Core tutorial 对了解  
  383. git 的内部机制很有帮助。  
  384.   
  385. [url]http://git.or.cz/gitwiki/GitLinks[/url]  
  386.     很多 git 相关资料的链接。  
  387.   
  388. [url]http://linux.yyz.us/git-howto.html[/url]  
  389.     Kernel Hackers' Guide to git, 比较老的一篇教程了。  
  390.   
  391. [url]http://www.gelato.unsw.edu.au/archives/git/0512/13748.html[/url]  
  392. [url]http://marc.theaimsgroup.com/?l=git&m=113402372012587&w=4[/url]  
  393.     git for the confused, 虽然有点老了,仍然强烈推荐。  

0 0
原创粉丝点击