在软件开发中有一些是通用技能,无论从事什么项目,C、Java、前段、后台,都必须掌握的(甚至产品设计与UI设计都需要):
这些技能应当优先熟练掌握,今天聊一聊版本控制工具Git。之前一直使用的是SVN,也仅仅停留在工作中使用,甚至不会使用svn命令,只能使用SVN Tortoise,也就是拉代码,解决冲突,提交代码这三个步骤,偶尔拉一下分支(甚至都没有亲自合并过分支。。)。
对于Git使用,说来已久,从一开始在Github上面copy代码(clone),到背git命令,了解git flow开发模型,都是理论大于实践,以至于最近工作的代码使用了Git后,一脸的蒙逼。本质来说没有理解Git的核心思想。
老规矩,下面是学习的一些资料,这次没有找英文的原始资料,因为这方面国内很多资料,就不折腾翻译了:
这是Git中最最重要的一个概念,也是与SVN等其他工具最核心的不同。
在Git的开发中,分支是一个轻量级的概念,因此新建分支,合并分支,删除分支在开发中十分常见,我们开发时应当遵循这样的基于分支的开发流程:
本质上,他们都是分支!
本地分支中的默认分支master
也是分支,它是以origin/master分支
初始化的一个普通分支。
分支本质是n个commit的list。如果本地master
与origin/master
同步(不在本地master上commit,修改可以,不要提交),更新之后,那么master与origin/master就是一样的。
一个新的项目clone后有哪些分支:
master
当前的默认分支,第一次取下来后与origin/master
指向相同,但是后续可以在上面开发
origin/master
远程分支,内容是最后一次从远程origin获取的所有commit的分支,无法修改数据,本地数据,但含义是
remotes/origin/master
和origin/master
的指向是相同的
master
is a local branchorigin/master
is a remote branch (which is a local copy of the branch named "master" on the remote named "origin")
哪些指针:
HEAD
FETCH_HEAD
origin/master/HEAD
指向origin/master
的提交,与HEAD
区分这个可以直接理解成普通的文件夹,文件, 这几个角度:
工作区全局只有一个,没有多个备份,它不会保存完整的状态。
不commit就切换分支会污染工作目录!(不commit,且有冲突的情况下,Git不会让你切换的,如果没有冲突,则会合并!)
error: Your local changes to the following files would be overwritten by checkout:
建议:切换分支前先commit,养成commit的习惯
从其他分支合并,或者从网络fetch后merge(pull),都是合入工作区。因此要注意在此之前add/commit操作。
泛化的理解:Git的所有操作都是先针对工作区修改,因此在执行例如合并之类的操作必须要add或者commit
暂存区是Git维护的,用户手动添加,暂存区只对当前分支有效,
分支本质是n个commit的list。
本地仓库中含有n个分支。包括一个默认的本地master(映射到origin/maser)和其他本地分支(可能没有映射,也可能映射到远程对于的分支)。
切换分支时,只是指针切换,仓库内容不受影响,只会更新工作目录(文件的删除添加)与暂存区(一般是清空,如果暂存区非空,且不冲突也会保留)
合并操作,会修复被合入的分支内容(某个commit的提交内容,或者commit的list)
已提交(committed):该文件被安全地保存在了本地数据库
已暂存(staged):把已修改的文件放下下次保存的清单中
已修改(modified):修改了某个文件,但还没有保存,此外还有从没有add过的新文件,未追踪untracked
参考下图学习:
几个关注点:
Changes to be committed:
下的new file
表示,暂存区与仓库对比,他们是新文件(如果有modified
表示,暂存区与仓库对比是修改的)Changes not staged for commit:
下的modified
表示,工作区与暂存区/仓库对比是修改的Untracked files:
下的是没有加入过暂存区的内容(在暂存区/仓库找不到对比对象)HEAD
是一个指针,指向的是本地仓库中当前分支的一个commit记录!该commit是当前正在工作的commit。
什么时机HEAD会移动:
注意:它指向的内容与工作目录和暂存区可能都不同,因此可以用HEAD来执行恢复操作。
快进模式 一个常用概念,指的是谁forward呢?是HEAD的快速向前移动。
出现的情况:
merge完成时,被合入分支的最后一个commit在待合入分支之中,移动HEAD到最后一个commit。如下图所示:
rebase完成后,commit记录合入后,HEAD指针直接向前移动多个commit。
总之,当前分支是master,执行git merge branch1
。master分支最好的commit是目标分支branch1的祖先commit节点时,会发生Fast-forward的merge。
有时为了时每一次merge都有记录,要禁用fast-forward,需要使用
git merge —no-ff
命令合并。
rebase:一般只用在本地分支上,把远程分支rebase过来。
merge:一般用在远程分支上,把本地分支merge到远程主干上。
cherry-pick:从其他分支拿(pick)一个commit到某个分支
顺序,先rebase orgin 再切换到主分支 merge 过去,前向合并(fast-forward merge,分支是目标分支的祖先commit节点时,会发生Fast-forward的merge)
参考这篇文章理解:http://pinkyjie.com/2014/08/10/git-notes-part-3/
这两个都是后悔药,区别如下
reset:使用撤销commit的方式恢复,使用在本地分支的恢复上(推送之前)。
revert:使用添加commit的方式恢复,用在远程分支的恢复上。原因参考『场景分析』
git stash
:存储git stash apply
: 还原两种方案:
git stash
命令可以满足这个需求,相当于一个存储箱。
cherry-pick:
一种常见的场景就是,比如我在A分支做了几次commit以后,发现其实我并不应该在A分支上工作,应该在B分支上工作,这就需要将这些commit从A分支复制到B分支去了,这时候就需要cherry-pick命令了。
参考:http://pinkyjie.com/2014/08/10/git-notes-part-3/
下面三种恢复的越来越多。
git reset —soft xx
: 仅仅取消commit&&移动HEAD指针到xx,不修改工作目录和暂存区。这个模式的效果是,自从以来的所有改变都会显示在git status的**"Changes to be committed"**中。使用场景:取消commit,一直到某个commit(xx),但是这些修改恢复到暂存区中。
git reset xx
:取消commit&&移动HEAD指针到xx,并且恢复暂存区,但是恢复工作目录。这个模式是默认模式,这个模式的效果是,工作目录中的文件的修改都会被保留,不会丢弃,但是也不会被标记成"Changes to be committed"。使用场景:取消commit,一直到某个commit(xx),但是这些修改恢复到工作目录中。
对比上一个就是少了git add 的过程
git reset —hard xx
: 取消commit&&移动HEAD指针到xx,恢复工作目录&&暂存区。使用场景:恢复到xx commit,丢弃从xx以来的所有修改。
这个使用revert命令,不要使用reset取消commit方式恢复,而是应当使用revert添加commit的方式恢复。
原因如下
我们使用的很多命令都是省略了一下参数的,而使用了默认值,有些情况我们也要认识他们,知道含义。
git push :如我们常用:
与push命令类似,但是注意:**冒号前表示remote repository下 branch的名字,冒号后表示local branch的名字。**上面第一个命令是第二个命令不是fetch到本地master分支,而是FETCH_HEAD上
git pull :参考:
重难点理解