两个不错的教程,
这一个作简介
http://zh-cn.whygitisbetterthanx.com/#cheap-local-branching
这一个详细
http://progit.org/book/zh/ch1-0.html
上面这个图是基本命令所做的工作。
除了push fetch 还有pull (pull 是fetch和merge 两个命令的结果 ) 这三个命令涉及到与远程服务器连接,其他的基本可以说是在本地进行操作,
这就是git 的不同,它是个分布式的 即便断网也可以工作。。 我们把remote repo fetch到本地库的时候,是fetch 了库中的所有版本,而不仅仅是最新版。 而commit也仅仅是commit到本地库,这个与其他的版本管理工具不同。我们在local repo有小成的时候,再提交到remote repo .
staging aero 是个中间过程,只有真正commit 后,才进库。(有时我们修改一个文件,之后直接commit 没不起作用,好像是先add ,后commit才可以。或者commit 时加-a 参数 )
其实remote repo 和你的local repo 本质上没什么差别。只是人为规定的。(分布式,意味着每个节点相同,只是我们人为的规定把经理的库作为大家的公共库,我们通过这个库共享信息。而两个普通程序员之间也可以直接交换代码进行push pull 操作,如果他们觉得这一部分代码没有必要经经理的库,或者暂时没有必要)
git commit -a 直接跳过staging area 中间过程commit -a ==add+commit
============================================================================
Subversion 式的工作流
集成管理员工作流
另一种常见工作流是有一名集成管理员专门负责向 'blessed' 库提交,其他开发者复制该库,push 到他们自己独立的库,随后请求集成管理员 pull 他们的修改。 这是开源工程或 GitHub 库中很常见的一种开发模
()
- 项目维护者可以推送数据到公共仓库 blessed repository。 2. 贡献者克隆此仓库,修订或编写新代码。
- 贡献者推送数据到自己的公共仓库 developer public。 4. 贡献者给维护者发送邮件,请求拉取 自己的最新修订。
- 维护者在自己本地的 integration manger 仓库中,将贡献者的仓库加为远程仓库,合并更新并做测试。
- 维护者将合并后的更新推送到主仓库 blessed repository。
/
司令官与副官工作流
这其实是上一种工作流的变体。一般超大型的项目才会用到这样的工作方式,像是拥有数百协作开发者的 Linux 内核项目就是如此。各个集成管理员分别负责集成项目中的特定部分,所以称为副官(lieutenant)。而所有这些集成管理员头上还有一位负责统筹的总 集成管理员,称为司令官(dictator)。司令官维护的仓库用于提供所有协作者拉取最新集成的项目代码。整个流程看起来如图 5-3 所示:
- 一般的开发者在自己的特性分支上工作,并不定期地根据主干分支(dectator 上的 master)衍合。
- 副官(lieutenant)将普通开发者的特性分支合并到自己的 master 分支中。
- 司令官(dictator)将所有副官的 master 分支并入自己的 master 分支。
- 司令官(dictator)将集成后的 master 分支推送到共享仓库 blessed repository 中,以便所有其他开发者以此为基础进行衍合。
- 这种工作流程并不常用,只有当项目极为庞杂,或者需要多级别管理时,才会体现出优势。
================================================================================
关于分支
git 中的分支跟svn 里的不同,
svn 里的一个分支是从主干复制一份代码,然后在这个基础上独立发展出一一去,
而git 中的分支只是一个指针,git 创建一个分支的代码只是多一个指针而已(与svn 多一个代码相比,高效快速许多)
git 的存储方式是这样的 :可以认为一个文件在git 中存储储为一个blob对象 我们在一次操作中git add file1 file2 file3
添加3个blob对象,然后一个tree 对象创建并指向这三个blob ,然后一个commit 对象指向这个tree . 于是git commit 时
就将commit tree 及3个blob 添加到库中了
toutch a.java b.java c.java
git add a.java b.java c.java
git commit -m "add a b c tree files "
然后我们多次的commit 操作会一边串的产生多个commit 对象,如下图 是每一次 commit 之后
依次创建98ca9 341c2 f30ab 三个commit 对象,
我们用一个 指针 (默认是master) 指向最后一次commit 时生成的 f30ab commit对象,也就是最后一次代码所处的状态。
假如我们想回滚到34ac2 ,只需将master 指向34ac2即可()
jixiuf@jf repos $ git branch
* master ----------------------->默认只有master 一个分支,前面的*表示当前处在master分支上
而git所谓的创建一个分支,也就是建立一个指针如testing 指向其中一个commit 对象,然后,master ,testing 分支各自展开一个分支,
比如此处master ,testing 分支都指向f30ab ,而后,master 可能指向名为aaaaa 的commit对象,testing 指向名为bbbbb的commit 对象,而aaaaa ,bbbbb都指向f30ab
jixiuf@jf repos $ git branch testing <-------------------创建testing 分支
jixiuf@jf repos $ git branch <---------------------查看当前有哪些分支,列出master ,testing
* master
testing
而分支只是一个指针,而其中会出现不只一个分支,我们如何知道当前是在哪个分支上工作的呢,那就是HEAD 指向谁,谁就是当前的分支,
分支的切换也就是让HEAD 在master ,testing 之间切换而已。
jixiuf@jf repos $ cat .git/HEAD <----------------我们只需查看.git/HEAD文件就可以知道git 是通过什么记录当前处在哪个分支上
ref: refs/heads/master
我们现在切换到testing 分支上
jixiuf@jf repos $ git checkout testing
Switched to branch 'testing'
jixiuf@jf repos $ git branch
master
* testing
jixiuf@jf repos $ cat .git/HEAD
ref: refs/heads/testing
我们在testing 分支上进行一次操作,如添加或者删除一个文件
jixiuf@jf repos $ touch z.java
jixiuf@jf repos $ git add z.java
jixiuf@jf repos $ git commit -m "add z.java "
[testing b9255a8] add z.java
0 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 z.java
然后就会变成如下图
我们重新切回到master 分支上
jixiuf@jf repos $ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.
然后在主分支上进行一次操作就变成如下
由于 Git 中的分支实际上仅是一个包含所指对象校验和(40 个字符长度 SHA-1 字串)的文件,所以创建和销毁一个分支就变得非常廉价。说白了,新建一个分支就是向一个文件写入 41 个字节(外加一个换行符)那么简单,当然也就很快了。
这和大多数版本控制系统形成了鲜明对比,它们管理分支大多采取备份所有项目文件到特定目录的方式,所以根据项目文件数量和大小不同,可能花费的时间 也会有相当大的差别,快则几秒,慢则数分钟。而 Git 的实现与项目复杂度无关,它永远可以在几毫秒的时间内完成分支的创建和切换。同时,因为每次提交时都记录了祖先信息(译注:即 parent 对象),所以以后要合并分支时,寻找恰当的合并基础(译注:即共同祖先)的工作其实已经完成了一大半,实现起来非常容易。Git 鼓励开发者频繁使用分支,正是因为有着这些特性作保障。
接下来看看,我们为什么应该频繁使用分支。