认识工作区、暂存区、版本库
⼯作区:是在电脑上你要写代码或⽂件的⽬录。暂存区:英⽂叫 stage 或 index。⼀般存放在 .git ⽬录下的 index ⽂件(.git/index)中,我们把暂存区有时也叫作索引(index)。版本库:⼜名仓库,英⽂名 repository 。⼯作区有⼀个隐藏⽬录 .git ,它不算⼯作区,⽽是 Git 的版本库。这个版本库⾥⾯的所有⽂件都可以被 Git 管理起来,每个⽂件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。
图中左侧为⼯作区,右侧为版本库。Git 的版本库⾥存了很多东西,其中最重要的就是暂存区。
在创建 Git 版本库时,Git 会为我们⾃动创建⼀个唯⼀的 master 分⽀,以及指向 master 的⼀个指
针叫 HEAD。
当对⼯作区修改(或新增)的⽂件执⾏ git add 命令时,暂存区⽬录树的⽂件索引会被更新。
当执⾏提交操作 git commit 时,master 分⽀会做相应的更新,可以简单理解为暂存区的⽬录
树才会被真正写到版本库中。
必须要通过使用 git add 和 git commit 命令才能将⽂件添加到仓库中 进行管理!
1. index 就是我们的暂存区,add 后的内容都是添加到这⾥的。
2. HEAD 就是我们的默认指向 master 分⽀的指针
3. objects 为 Git 的对象库,⾥⾯包含了创建的各种版本库对象及内容。当执⾏ git add 命令
时,暂存区的⽬录树被更新,同时⼯作区修改(或新增)的⽂件内容被写⼊到对象库中的⼀个新的
对象中,就位于 ".git/objects" ⽬录下,让我们来看看这些对象有何⽤处:
查找 object 时要将 commit id 分成2部分,其前2位是⽂件夹名称,后38位是⽂件名称。
找到这个⽂件之后,⼀般不能直接看到⾥⾯是什么,该类⽂件是经过 (安全哈希算法)加密过的
⽂件,好在我们可以使⽤ git cat-file 命令来查看版本库对象的内容。
总结⼀下,在本地的 git 仓库中,有⼏个⽂件或者⽬录很特殊
- index: 暂存区, git add 后会更新该内容。
- HEAD: 默认指向 master 分⽀的⼀个指针。
- refs/heads/master: ⽂件⾥保存当前 master 分⽀的最新 commit id 。
- objects: 包含了创建的各种版本库对象及内容,可以简单理解为放了 git 维护的所有修改。
修改文件
Git 比其他版本控制系统设计得优秀,因为 Git 跟踪并管理的是修改,而非文件。
git diff [file] 命令用来显示暂存区和⼯作区⽂件的差异,显示的格式正是Unix通⽤的diff格
式。也可以使⽤ git diff HEAD -- [file] 命令来查看版本库和⼯作区⽂件的区别。
知道了对 ReadMe 做了什么修改后,再把它提交到本地仓库就放心了。
版本回退
git reset 命令语法格式为: git reset [--soft | --mixed | --hard] [HEAD]
--mixed 为默认选项,使⽤时可以不⽤带该参数。该参数将暂存区的内容退回为指定提交版本内容,⼯作区⽂件保持不变。--soft参数对于⼯作区和暂存区的内容都不变,只是将版本库回退到某个指定版本。--hard参数将暂存区与⼯作区都退回到指定版本。切记⼯作区有未提交的代码时不要⽤这个命令,因为⼯作区会回滚,你没有提交的代码就再也找不回了,所以使⽤该参数前⼀定要慎重。
HEAD 说明:
可直接写成 commit id,表示指定退回的版本HEAD 表示当前版本HEAD^ 上⼀个版本 或HEAD^1HEAD^^ 上上⼀个版本 或HEAD^2以此类推
version3 被用 git reset --hard 变成 version2
git log 并不能打印出 version 3 的 commit id ,运⽓好的话我们可以从终端上去找找之前的记录,运⽓不好的话 commit id 已经被我们搞丢了Git 还提供了⼀个 git reflogGit 版本回退的时候,也可以使⽤部分 commit id 来代表⽬标版本
Git 的版本回退速度⾮常快,因为 Git 在内部有个指向当前分支(此处是master)的
HEAD 指针, refs/heads/master 文件里保存当前 master 分⽀的最新 commit id 。当我们
在回退版本的时候,Git 仅仅是给 refs/heads/master 中存储⼀个特定的version,可以简单理解
成如下示意图

撤销修改
如果我们在我们的⼯作区写了很⻓时间代码,需求变更之类的情况
情况⼀:对于⼯作区的代码,还没有 add
可以使用 git checkout -- [file] 命令让⼯作区的⽂件回到最近⼀次 add 或 commit 时的状态。 要注意 git checkout -- [file] 命令中的-- 很重要,切记不要省略,⼀旦省略,该命令就变为其他意思了。
情况⼆:已经 add ,但没有 commit
add 后还是保存到了暂存区呢?怎么撤销呢?
git reset 回退命令,该命令如果使⽤ --mixed 参数,可以将暂存区的内容退回为指定的版本内容,但⼯作区⽂件保持不变。那我们就可以只回退下暂存区的内容了
git status 查看⼀下,发现现在暂存区是干净的,工作区有修改。
这时候就回到情况一了。
情况三:已经 add ,并且也 commit 了
不要担⼼,我们可以 git reset --hard HEAD^ 回退到上⼀个版本!不过,这是有条件的,就是
你还没有把⾃⼰的本地版本库推送到远程。还记得Git是分布式版本控制系统吗?我们后⾯会讲到远程
版本库,⼀旦你推送到远程版本库,你就真的惨了……
删除文件
工作区和版本库就不⼀致了,要删⽂件,目前除了要删⼯作区的⽂件,还要清除版本库的文件
确实要从版本库中删除该⽂件不小心删错了
分支管理
每次提交,Git都把代码串成⼀条时间线,这条时间线就可以理解为是⼀个分⽀。
再来理解⼀下HEAD,HEAD 严格来说不是指向提交,⽽是指向master,master才是指向提交的,所 以,HEAD 指向的就是当前分支。每次提交,master分⽀都会向前移动⼀步,这样,随着你不断提交,master分⽀的线也越来越长,而HEAD只要⼀直指向master分支即可指向当前分支
创建分支
Git ⽀持我们查看或创建其他分⽀,在这⾥我们来创建第⼀个⾃⼰的分⽀ dev ,对应的命令为
git branch dev
当我们创建新的分⽀后,Git 新建了⼀个指针叫 dev, * 表示当前 HEAD 指向的分⽀是 master 分
⽀。另外,可以通过⽬录结构发现,新的 dev 分⽀
发现⽬前 dev 和 master 指向同⼀个修改。并且也可以验证下 HEAD ⽬前是指向 master 的。
cat .git/HEAD
切换分支
git checkout dev
切换到分支dev提交代码后,当再切换到 master 分⽀之时,HEAD 就指向了 master,当然看不到提交了
合并分支
git merge
为了在 master 主分⽀上能看到新的提交,就需要合并分支
git merge dev # 合并 dev 分⽀Updating 16623e1..3740dceFast-forwardReadMe | 1 +1 file changed, 1 insertion(+)
git merge 命令⽤于合并指定分⽀到当前分⽀。合并后,master 就能看到 dev 分⽀提交的内容
了。此时的状态如图如下所⽰。
git merge 命令⽤于合并指定分⽀到当前分⽀。合并后,master 就能看到 dev 分⽀提交的内容
了。此时的状态如图如下所显示。
Fast-forward 代表“快进模式”,也就是直接把master指向dev的当前提交,所以合并速度⾮常快。
当然,也不是每次合并都能 Fast-forward,我们后⾯会讲其他方式的合并。

这种情况下,Git 只能试图把各⾃的修改合并起来,但这种合并就可能会有冲突
发现 ⽂件有冲突后,可以 cat ReadMe 直接查看⽂件内容,要说的是 Git 会⽤ <<<<<<<,=======,>>>>>>> 来标记出不同分⽀的冲突内容⼿动调整冲突代码,并需要再次提交修正后的结果
结果如下

记 dev1 分⽀使⽤完毕后就可以删除了git log可以看分支状态
这样的好处是,从分⽀历史上就可以看出分⽀信息。例如我们现在已经删除了在合并冲突部分创建的 dev1 分⽀,但依旧能看到 master 其实是由其他分⽀合并
--no-ff 参数,表⽰禁⽤ Fast forward 模式。禁⽤ Fast forward 模式后合并会创建
⼀个新的 commit ,所以加上 -m 参数,把描述写进去。
git merge --no-ff -m "merge with no-ff" dev2
所以在合并分⽀时,加上 --no-ff 参数就可以⽤普通模式合并,合并后的历史有分⽀,能看出来曾
经做过合并,⽽ fast forward 合并就看不出来曾经做过合并。

删除分支
git branch -d dev
合并完成后, dev 分⽀对于我们来说就没用了, 那么dev分⽀就可以被删除掉,注意 如果当前正处于某分支下,就不能删除当前分⽀
因为创建、合并和删除分⽀非常快,所以Git⿎励你使⽤分⽀完成某个任务,合并后再删掉分⽀,这和直接在master分⽀上⼯作效果是⼀样的,但过程更安全
分支管理策略
bug 分⽀
假如我们现在正在 dev2 分⽀上进⾏开发,开发到⼀半,突然发现 master 分⽀上⾯有 bug,需要
解决。在Git中,每个 bug 都可以通过⼀个新的临时分⽀来修复,修复后,合并分⽀,然后将临时分⽀ 删除。