Git 保存的不是文件差异或者变化量,而只是一系列文件快照。
在 Git 中提交时,12bet,会保存一个提交(commit)对象,12bet,包含一个指向暂存内容快照的指针,作者和相关附属信息,以及一定数量(也可能没有)指向该提交对象直接祖先的指针:第一次提交是没有直接祖先的,12bet,普通提交有一个祖先,由两个或多个分支合并产生的提交则有多个祖先。
假设在工作目录中有三个文件,准备将它们暂存后提交。暂存操作(git add [blob]
)会对每一个文件计算校验和(SHA-1 哈希字串),然后把当前版本的文件快照保存到 Git 仓库中(Git 使用 blob 类型的对象存储这些快照),并将校验和加入暂存区域
当使用 git commit 新建一个提交对象前,Git 会先计算每一个子目录的校验和,然后在 Git 仓库中将这些目录保存为树(tree)对象。之后 Git 创建的提交对象,12博体育,除了包含相关提交信息以外,还包含着指向这个树对象(项目根目录)的指针,如此它就可以在将来需要的时候,重现此次快照的内容了。
作些修改后再次提交,那么这次的提交对象会包含一个指向上次提交对象的指针
Git 中的分支,其实本质上仅仅是个指向 commit 对象的可变指针。Git 会使用 master 作为分支的默认名字。
新建一个 testing 分支,12博体育,可以使用 git branch
命令:
$ git branch testing
这会在当前 commit 对象上新建一个分支指针
Git保存着一个名为 HEAD 的特别指针,它是一个指向你正在工作中的本地分支的指针。分支的切换实际上就是修改HEAD指针的指向
要切换到其他分支,可以执行 git checkout
命令。我们现在转换到新建的 testing 分支:
$ git checkout testing
这条命令做了两件事。它把 HEAD 指针移到 testing 分支,并把工作目录中的文件换成了 testing 分支所指向的快照内容。
分支切换之前:
分支切换之后:
提交会在创建commit对象之后,会将HEAD指针自动移动到新雄黄见的commit对象
提交之前:
提交之后:
由于 Git 中的分支实际上仅是一个包含所指对象校验和(40 个字符长度 SHA-1 字串)的文件,所以创建和销毁一个分支就变得非常廉价。
要新建并切换到该分支,运行 git checkout
并加上 -b
参数
$ git checkout -b iss53
相当于下面这两条命令:
$ git branch iss53
$ git checkout iss53
用 git merge
命令来进行合并。如果顺着一个分支走下去可以到达另一个分支,那么 Git 在合并两者时,只会简单地把指针前移,因为没有什么分歧需要解决,所以这个过程叫做快进(Fast forward)。
使用 git branch
的 -d
选项表示删除:
$ git branch -d hotfix
如果合并时一个分支不是在另一个分支的下游,Git 没有简单地把分支指针右移,而是对三方合并的结果作一新的快照,并自动创建一个指向它的 commit,这时候就可能会发生冲突
要看看哪些文件在合并时发生冲突,可以用 git status
查阅,任何包含未解决冲突的文件都会以未合并(unmerged)状态列出。在解决了所有文件里的所有冲突后,运行 git add
将把它们标记为已解决(resolved)。因为一旦暂存,就表示冲突已经解决。
如果觉得满意了,并且确认所有冲突都已解决,也就是进入了缓存区,就可以用 git commit
来完成这次合并提交
git branch
命令不仅仅能创建和删除分支,如果不加任何参数,它会给出当前所有分支的清单
若要查看各个分支最后一次 commit 信息,运行 git branch -v
要从该清单中筛选出你已经(或尚未)与当前分支合并的分支,可以用 --merge
和 --no-merged
选项(Git 1.5.6 以上版本)。
通过--merge
能够查看当前分支合并了哪几个分支。一般来说,列表中没有 * 的分支通常都可以用 git branch -d
来删掉。原因很简单,既然已经把它们所包含的工作整合到了其他分支,删掉也不会损失什么。
可以用 git branch --no-merged
查看尚未合并的工作,若分支包含未合并的工作,用 git branch -d
删除该分支会导致失败。如果你坚信你要删除它,可以用大写的删除选项 -D
强制执行,例如 git branch -D testing
。
可以同时拥有多个开放的分支,每个分支用于完成特定的任务,随着开发的推进,你可以随时把某个特性分支的成果并到其他分支中。许多使用 Git 的开发者都喜欢以这种方式来开展工作,比如仅在 master 分支中保留完全稳定的代码,即已经发布或即将发布的代码。与此同时,他们还有一个名为 develop 或 next 的平行分支,专门用于后续的开发,或仅用于稳定性测试。某些大项目还会有个 proposed(建议)或 pu(proposed updates,建议更新)分支,它包含着那些可能还没有成熟到进入 next 或 master 的内容
一个特性分支是指一个短期的,用来实现单一特性或与其相关工作的分支。该技术允许你迅速且完全的进行语境切换
远程分支(remote branch)是对远程仓库状态的索引。它们是一些无法移动的本地分支;只有在进行 Git 的网络活动时才会更新。远程分支就像是书签,提醒着你上次连接远程仓库时上面各分支的位置。
你的提交历史会开始朝不同的方向发展。不过只要你不和服务器通讯,你的 origin/master
指针不会移动
可以运行 git fetch origin
来和服务器通信来同步
对于无意分享的,你尽可以保留为私人分支,而只推送那些协同工作的特性分支。可以运行 git push (远程仓库名) (分支名)
你可以把本地分支推送到某个命名不同的远程分支:若想把远程分支叫作 awesomebranch,可以用 git push origin serverfix:awesomebranch
来推送数据
从远程分支检出的本地分支,称为跟踪分支(tracking branch)。跟踪分支是一种和远程分支有直接联系的本地分支。在跟踪分支里输入 git push,Git 会自行推断应该向哪个服务器的哪个分支推送数据。反过来,在这些分支里运行 git pull 会获取所有远程索引,并把它们的数据都合并到本地分支中来。
你可以随心所欲地设定为其它跟踪分支,git checkout -b [分支名] [远程名]/[分支名]
。如果你有 1.6.2 以上版本的 Git,还可以用 --track
选项简化
$ git checkout --track origin/serverfix
要为本地分支设定不同于远程分支的名字,只需在前个版本的命令里换个名字:
$ git checkout -b sf origin/serverfix
可以用这个非常无厘头的语法来删除它:git push [远程名] :[分支名]
。如果想在服务器上删除 serverfix 分支,运行下面的命令:
$ git push origin :serverfix
把一个分支整合到另一个分支的办法有两种:merge(合并) 和 rebase(衍合)。
衍合的原理是回到两个分支(你所在的分支和你想要衍合进去的分支)的共同祖先,提取你所在分支每次提交时产生的差异(diff),把这些差异分别保存到临时文件里,然后从当前分支转换到你需要衍合入的分支,依序施用每一个差异补丁文件。得到的结果没有任何区别,但衍合能产生一个更为整洁的提交历史。
衍合通过命令git rebase
来实现
通过git rebase
加上--onto
选项来重演
永远不要衍合那些已经推送到公共仓库的更新,如果把衍合当成一种在推送之前清理提交历史的手段,而且仅仅衍合那些永远不会公开的 commit,那就不会有任何问题。