LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 6358|回复: 7

git 之五分钟教程

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

使用道具 举报

发表于 2006-11-15 20:00:54 | 显示全部楼层
看来用 git 的人不多,好文也无人问津。。。
回复 支持 反对

使用道具 举报

发表于 2008-8-1 03:18:28 | 显示全部楼层
目录中产生了一些无用文件,请问要怎么清除?
回复 支持 反对

使用道具 举报

发表于 2008-8-5 02:26:13 | 显示全部楼层
是否写完了?
回复 支持 反对

使用道具 举报

发表于 2008-8-5 13:38:08 | 显示全部楼层
易用性:git > svn > cvs
回复 支持 反对

使用道具 举报

发表于 2008-11-19 12:26:33 | 显示全部楼层
非常非常好的文章,
感谢dieken_qfz
回复 支持 反对

使用道具 举报

发表于 2008-11-19 12:56:06 | 显示全部楼层
挺好的文章~~ 要是能根据新的 git 更新一下就好了~
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表