Git权威指南
版本控制是管理数据变更的艺术,无论数据变更是来自同一个人,还是来自不同的人(一个团队)。版本控制系统不但要忠实地记录数据的每一次变更,还要能够帮助还原任何历史改动,以及实现团队的协同工作等。Git就是版本控制系统中的佼佼者。
我对版本控制系统的兴趣源自于我在个人知识管理中的实践,其核心就是撰写可维护的文档,并保存于版本控制系统中。可维护文档的格式可以是DocBook、FreeMind、reStructuredText等。我甚至还对FreeMind加以改造以便让其文档格式更适合于版本控制系统,这就是我的第一个开源实践:托管于SourceForge上的FreeMind-MMX项目。文档的书写格式问题解决之后,就是文档的存储问题了。通过版本控制系统,很自然地就可以实现对文档历史版本的保存,但是如何避免因为版本控制系统瘫痪而导致数据的丢失呢?Git用其崭新的分布式的版本控制设计提供了最好的解决方案。使用Git,我的知识库不再只有唯一的版本库与之对应,而是可以通过克隆操作分发到不同的磁盘或主机上,克隆的版本库之间通过推送(PUSH)和拉回(PULL)等操作进行同步,数据安全得到了极大的提升。在版本控制系统的忠实呵护下,我的知识库中关于Git的FreeMind脑图日积月累变得越来越详实,越来越清晰,最终成为这本书的雏形。
版本控制还能决定项目的成败,甚至是公司的生死,此言不虚。我在推广开源项目管理工具和为企业提供咨询服务的过程中,看到过太多的团队因为版本控制系统管理的混乱,导致项目延期、修正的Bug重现、客户的问题不能在代码中定位,无论他们使用的是什么版本控制系统,开源的或是商业的。这是因为传统的集中式版本控制系统不能有效地管理分支和进行分支间合并。集中管理的版本库只有唯一的分支命名空间,需要专人管理从而造成分支创建的不自由。分支间的合并要么因为缺乏追踪导致重复合并、引发严重冲突,要么因为蹩脚的设计导致分支合并时缺乏效率和陷阱重重。Git用其灵活的设计让项目摆脱分支管理的梦魇。
我的公司也经历过代码管理的生死考验。因为公司的开发模式主要是基于开源软件的二次开发,最早在使用SVN(Subversion)做版本控制时,很自然地使用了SVN卖主分支模型来管理代码。随着增加和修改的代码越来越多,和开源软件上游的偏离也越来越远,当上游有新版本发布时,最早可能只用几个小时就可以将改动迁移过去,但是如果对上游的改动多达几十甚至上百处时,迁移的过程就会异常痛苦,基本上和重新做一遍差不多。那时似乎只有一个选择:不再和上游合并,不再追踪上游的改动,而这与公司的价值观“发动全球智慧为客户创造价值”相违背。迷茫之中,分布式版本控制系统飘然而至,原来版本控制还可以这么做。
我最先尝试的分布式版本控制系统是Hg(Mercurial),尤其是当发现Hg和MQ(Hg的一个插件)这一对儿宝贝的时候,我如获至宝。逐渐地,公司的版本库都迁移到了Hg上。但随着新的开发人员的加入,问题就出现了,即一个人使用Hg和MQ都很好,但多个人使用则会出现难以协同的问题。于是我们大胆地采用了Git,并在实践中结合Topgit等工具进行代码的管理。再一次,也许是最后一次,我们的代码库迁移到了Git。
最早认识分布式版本控制,源自于我们看到了众多开源项目的版本控制系统大迁移,这场迁移还在进行中。
MoinMoin是我们关注的一个开源的维基软件,2006年,它的代码库从SVN迁移到Hg。 Mailman同样是我们关注的一个开源邮件列表软件。2007年,它的代码库从SVN迁移到Bazaar。 Linux采用Git做版本控制系统(一点都不奇怪,因为Git就是Linus Torvalds开发的)。 Android是最为流行的开源项目之一,因为潜在的市场巨大,已经吸引越来越多的开发者进入这个市场,而Android就是用Git维护的。 当开源软件纷纷倒向分布式版本控制系统大旗(尤其是Git)的时候,各个公司也在行动了,尤其是涉及异地团队协同,以及Android核心代码定制开发的公司。对于一些因保守而不敢向Git靠拢的公司,Git也可以派上用场,这是因为Git可以和现在大多数公司部署的SVN很好地协同,即公司的服务器是SVN,开发者的客户端则使用Git。相信随着Git的普及,以及公司代码管理观念上的改进,会有更多的公司拥抱Git。
目录
- 1. 初识Git
- 1.1. 版本控制的前世和今生
- 1.1.1. 黑暗的史前时代
- 1.1.2. CVS——开启版本控制大爆发
- 1.1.3. SVN——集中式版本控制集大成者
- 1.1.4. Git——Linus的第二个伟大作品
- 1.2. 爱上Git的理由
- 1.2.1. 每日的工作备份
- 1.2.2. 异地协同工作
- 1.2.3. 现场版本控制
- 1.2.4. 避免引入辅助目录
- 1.2.5. 重写提交说明
- 1.2.6. 想吃后悔药
- 1.2.7. 更好用的提交列表
- 1.2.8. 更好的差异比较
- 1.2.9. 工作进度保存
- 1.2.10. 代理SVN提交实现移动式办公
- 1.2.11. 无处不在的分页器
- 1.2.12. 快
- 1.3. 安装Git
- 1.3.1. Linux下安装和使用Git
- 1.3.2. Mac OS X下安装和使用Git
- 1.3.3. Windows下安装和使用Git(Cygwin篇)
- 1.3.4. Windows下安装和使用Git(msysGit篇)
- 2. Git独奏
- 2.1. Git初始化
- 2.1.1. 创建版本库及第一次提交
- 2.1.2. 思考:为什么工作区下有一个.git目录?
- 2.1.3. 思考:git config命令参数的区别?
- 2.1.4. 思考:是谁完成的提交?
- 2.1.5. 思考:随意设置提交者姓名,是否太不安全?
- 2.1.6. 思考:命令别名是干什么的?
- 2.1.7. 备份本章的工作成果
- 2.2. Git暂存区
- 2.2.1. 修改不能直接提交?
- 2.2.2. 理解 Git 暂存区(stage)
- 2.2.3. Git Diff魔法
- 2.2.4. 不要使用git commit -a
- 2.2.5. 搁置问题,暂存状态
- 2.3. Git对象
- 2.3.1. Git对象库探秘
- 2.3.2. 问题:SHA1哈希值到底是什么,如何生成的?
- 2.3.3. 问题:为什么不用顺序的数字来表示提交?
- 2.4. Git重置
- 2.4.1. 分支游标master的探秘
- 2.4.2. 用reflog挽救错误的重置
- 2.4.3. 深入了解git reset命令
- 2.5. Git检出
- 2.5.1. HEAD的重置即检出
- 2.5.2. 挽救分离头指针
- 2.5.3. 深入了解git checkout命令
- 2.6. 恢复进度
- 2.6.1. 继续暂存区未完成的实践
- 2.6.2. 使用git stash
- 2.6.3. 探秘git stash
- 2.7. Git基本操作
- 2.7.1. 先来合个影
- 2.7.2. 删除文件
- 2.7.3. 恢复删除的文件
- 2.7.4. 移动文件
- 2.7.5. 一个显示版本号的Hello World
- 2.7.6. 使用git add -i选择性添加
- 2.7.7. Hello world引发的新问题
- 2.7.8. 文件忽略
- 2.7.9. 文件归档
- 2.8. 历史穿梭
- 2.8.1. 图形工具:gitk
- 2.8.2. 图形工具:gitg
- 2.8.3. 图形工具:qgit
- 2.8.4. 命令行工具
- 2.9. 改变历史
- 2.9.1. 悔棋
- 2.9.2. 多步悔棋
- 2.9.3. 回到未来
- 2.9.4. 丢弃历史
- 2.9.5. 反转提交
- 2.10. Git克隆
- 2.10.1. 鸡蛋不装在一个篮子里
- 2.10.2. 对等工作区
- 2.10.3. 克隆生成裸版本库
- 2.10.4. 创建生成裸版本库
- 2.11. Git库管理
- 2.11.1. 对象和引用哪里去了?
- 2.11.2. 暂存区操作引入的临时对象
- 2.11.3. 重置操作引入的对象
- 2.11.4. Git管家:git gc
- 2.11.5. Git管家的自动执行
- 3. Git和声
- 3.1. Git协议与工作协同
- 3.1.1. Git支持的协议
- 3.1.2. 多用户协同的本地模拟
- 3.1.3. 强制非快进式推送
- 3.1.4. 合并后推送
- 3.1.5. 禁止非快进式推送
- 3.2. 冲突解决
- 3.2.1. 拉回操作中的合并
- 3.2.2. 合并一:自动合并
- 3.2.3. 合并二:逻辑冲突
- 3.2.4. 合并三:冲突解决
- 3.2.5. 合并四:树冲突
- 3.2.6. 合并策略
- 3.2.7. 合并相关的设置
- 3.3. Git里程碑
- 3.3.1. 显示里程碑
- 3.3.2. 创建里程碑
- 3.3.3. 删除里程碑
- 3.3.4. 不要随意更改里程碑
- 3.3.5. 共享里程碑
- 3.3.6. 删除远程版本库的里程碑
- 3.3.7. 里程碑命名规范
- 3.4. Git分支
- 3.4.1. 代码管理之殇
- 3.4.2. 分支命令概述
- 3.4.3. Hello World开发计划
- 3.4.4. 基于特性分支的开发
- 3.4.5. 基于发布分支的开发
- 3.4.6. 分支变基
- 3.5. 远程版本库
- 3.5.1. 远程分支
- 3.5.2. 分支追踪
- 3.5.3. 远程版本库
- 3.5.4. PUSH和PULL操作与远程版本库
- 3.5.5. 里程碑和远程版本库
- 3.5.6. 分支和里程碑的安全性
- 3.6. 补丁文件交互
- 3.6.1. 创建补丁
- 3.6.2. 应用补丁
- 3.6.3. StGit和Quilt
- 4. Git协同模型
- 4.1. 经典Git协同模型
- 4.1.1. 集中式协同模型
- 4.2. 金字塔式协同模型
- 4.2.1. 贡献者开放只读版本库
- 4.2.2. 以补丁方式贡献代码
- 4.3. Topgit协同模型
- 4.3.1. 作者版本控制系统三个里程碑
- 4.3.2. Topgit原理
- 4.3.3. Topgit的安装
- 4.3.4. Topgit的使用
- 4.3.4.1. tg help命令
- 4.3.4.2. tg create命令
- 4.3.4.3. tg info命令
- 4.3.4.4. tg update命令
- 4.3.4.5. tg summary命令
- 4.3.4.6. tg remote命令
- 4.3.4.7. tg push命令
- 4.3.4.8. tg depend命令
- 4.3.4.9. tg base命令
- 4.3.4.10. tg delete命令
- 4.3.4.11. tg patch命令
- 4.3.4.12. tg export命令
- 4.3.4.13. tg import命令
- 4.3.4.14. tg log命令
- 4.3.4.15. tg mail命令
- 4.3.4.16. tg graph命令
- 4.3.5. Topgit hacks
- 4.3.6. Topgit使用中的注意事项
- 4.4. 子模组协同模型
- 4.4.1. 创建子模组
- 4.4.2. 克隆带子模组的版本库
- 4.4.3. 在子模组中修改和子模组的更新
- 4.4.4. 隐性子模组
- 4.4.5. 子模组的管理问题
- 4.5. 子树合并
- 4.5.1. 引入外部版本库
- 4.5.2. 子目录方式合并外部版本库
- 4.5.3. 利用子树合并跟踪上游改动
- 4.5.4. 子树拆分
- 4.5.5. git subtree插件
- 4.6. Android式多版本库协同
- 4.6.1. 关于repo
- 4.6.2. 安装repo
- 4.6.3. repo和清单库的初始化
- 4.6.4. 清单库和清单文件
- 4.6.5. 同步项目
- 4.6.6. 建立Android代码库本地镜像
- 4.6.7. Repo的命令集
- 4.6.7.1. repo init命令
- 4.6.7.2. repo sync命令
- 4.6.7.3. repo start命令
- 4.6.7.4. repo status命令
- 4.6.7.5. repo checkout命令
- 4.6.7.6. repo branches命令
- 4.6.7.7. repo diff命令
- 4.6.7.8. repo stage命令
- 4.6.7.9. repo upload命令
- 4.6.7.10. repo download命令
- 4.6.7.11. repo rebase命令
- 4.6.7.12. repo prune命令
- 4.6.7.13. repo abandon命令
- 4.6.7.14. 其他命令
- 4.6.8. Repo命令的工作流
- 4.6.9. 好东西不能Android独享
- 4.7. Git和SVN协同模型
- 4.7.1. 使用git-svn的一般流程
- 4.7.2. git-svn的奥秘
- 4.7.3. 多样的git-svn克隆模式
- 4.7.4. 共享git-svn的克隆库
- 4.7.5. git-svn的局限
- 5. 搭建Git服务器
- 5.1. 使用HTTP协议
- 5.1.1. 哑传输协议
- 5.1.2. 智能HTTP协议
- 5.1.3. Gitweb服务器
- 5.2. 使用Git协议
- 5.2.1. Git协议语法格式
- 5.2.2. Git服务软件
- 5.2.3. 以inetd方式配置运行
- 5.2.4. 以runit方式配置运行
- 5.3. 使用SSH协议
- 5.3.1. SSH协议语法格式
- 5.3.2. 服务架设方式比较
- 5.3.3. 关于SSH公钥认证
- 5.3.4. 关于SSH主机别名
- 5.4. Gitolite服务架设
- 5.4.1. 安装Gitolite
- 5.4.2. 管理Gitolite
- 5.4.3. Gitolite授权详解
- 5.4.4. 版本库授权案例
- 5.4.5. 创建和导入版本库
- 5.4.6. 对Gitolite的改进
- 5.4.7. Gitolite功能拓展
- 5.5. Gitosis服务架设
- 5.5.1. 安装Gitosis
- 5.5.2. 管理Gitosis
- 5.5.3. Gitosis授权详解
- 5.5.4. 创建新版本库
- 5.5.5. 轻量级管理的Git服务
- 5.6. Gerrit代码审核服务器
- 5.6.1. Gerrit的实现原理
- 5.6.2. 架设Gerrit的服务器
- 5.6.3. Gerrit的配置文件
- 5.6.4. Gerrit的数据库访问
- 5.6.5. 立即注册为Gerrit管理员
- 5.6.6. 管理员访问SSH的管理接口
- 5.6.7. 创建新项目
- 5.6.8. 从已有Git库创建项目
- 5.6.9. 定义评审工作流
- 5.6.10. Gerrit评审工作流实战
- 5.6.11. 更多Gerrit参考
- 5.7. Git版本库托管
- 5.7.1. Github
- 5.7.2. Gitorious
- 6. 迁移到Git
- 7. Git的其它应用
- 8. Git杂谈
- 9. 附录
- 9.1. A. Git命令索引
- 9.1.1. A.1 常用的Git命令
- 9.1.2. A.2 对象库操作相关命令
- 9.1.3. A.3 引用操作相关命令
- 9.1.4. A.4 版本库管理相关命令
- 9.1.5. A.5 数据传输相关命令
- 9.1.6. A.6 邮件相关命令
- 9.1.7. A.7 协议相关命令
- 9.1.8. A.8 版本库转换和交互相关命令
- 9.1.9. A.9 合并相关的辅助命令
- 9.1.10. A.10 杂项
- 9.2. Git与CVS面对面
- 9.2.1. 面对面访谈录
- 9.2.2. CVS和Git命令对照
- 9.3. Git和SVN面对面
- 9.3.1. 面对面访谈录
- 9.3.2. SVN和Git命令对照
- 9.4. Git和Hg面对面
- 9.4.1. 面对面访谈录
- 9.4.2. Hg和Git命令对照