集中化版本控制系统
集中化版本控制系统(Centralized Version Control Systems,简称CVCS),用于记录某个时间点对项目做了哪些修改,包括增加、删除等。如CVS、SVN等,都是集中化版本控制系统。集中化版本控制系统有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的开发者通过客户端连接到该服务器,并且可以从该服务器获取数据,修改记录,或者提交更新。集中化版本控制系统实现效果如下:
屏幕快照 2018-11-26 下午3.53.45.png
集中化版本控制系统使用比较方便,但是其缺点也是比较明显的。如果集中管理的服务器故障,那么在服务器故障期间内,任何人都无法提交更新,也就无法协同工作。如果服务器硬盘发生损坏,又没有做恰当的备份,虽然发生的几率非常小,但是一旦发生,项目的所有数据都将丢失,包括项目源数据和变更历史。为了解决集中化版本控制系统的缺点,于是就有了分布式版本控制系统。
分布式版本控制系统
分布式版本控制系统(Distributed Version Control System,简称DVCS),最出名的就是Git。在分布式版本控制系统中,客户端从服务器并不只是提取最新版本的文件快照,而是把代码仓库完整的镜像下来,包括整个项目的提交历史。这样一来,即使服务器发生故障,使用任何一个镜像都可以快速的恢复。分布式版本控制系统的实现效果如下:
image
Git分支模型
版本控制系统通常都会支持分支。分支可以使开发者从主线版本脱离出来,来做一些其他尝试性的工作。在部分版本控制系统中,比如SVN,新建分支需要完全创建一个源代码目录的副本。这在一些大型项目中,该过程会耗费几分钟,甚至更久的时间。
Git可以很快的创建一个分支,时间通常在几秒钟之内,这也是Git为何如此流行的一个原因。这里再介绍一个Git和其他版本控制系统的区别。很多版本控制系统记录的是文件的变化,比如两次提交之间,SVN就是记录了两次提交的不同,也就是文件变化。Git记录的不是文件变化,而是一些列不同时刻的文件快照。
Git新建一个分支,其实就是新建了一个指针,指针指向了某一时刻的提交。比如使用git branch新建一个分支:
git branch testing
实际上是新建了一个指针,该指针指向当前所在的提交对象,如下图:
image
新建了testing指针,testing指针和master指针指向了同一个提交对象 f30ab。
之后加入切换到了testing分支,然后对testing分支做了修改和提交,testing指针会随之向后移动,但是master指针是不会改变的,如下图:
image
testing指针已经改变,但是master指针并没有随之移动。这正是分支的意义所在。
上面图片中出现了HEAD指针,简单介绍下HEAD指针。Git中的HEAD指针指向的是当前的分支。拿上图举例,如果当前在master分支,那么HEAD指向的就是master,如果当前在testing分支,那么HEAD指向的就是testing。
Git命令
配置用户信息
使用Git之前需要首先配置个人信息,包括个人的用户名和电子邮件地址。每次Git提交时,都会引用用户名和电子邮件,说明是谁提交了更新。配置用户信息的命令:
// 配置用户名git config --global user.name "Test"// 配置用户电子邮箱git config --global user.email 123456@qq.com
文本编辑器
当Git需要用户输入一些额外的信息时,会自动调用一个外部的编辑器给用户使用。默认会使用操作系统指定的默认编辑器,比如说Vi或者Vim。如果想设置成其他的编辑器,可以通过git config来设置,命令如下:
// 配置编辑器为emacsgit config --global core.editor emacs
当然,在设置之前也可以查看当前的编辑器,命令如下:
// 查看所有的config配置git config --list// 查看user.name的配置git config user.name// 查看当前的编辑器git config core.editor
差异分析工具
在提交代码、合并代码、解决冲突时,需要用到差异分析工具。差异分析工具也是可以设置的,命令如下:
// 配置差异分析工具为vimdiffgit config --global merge.tool vimdiff
Git可以理解vimdiff、gvimdiff、opendiff等工具的输出信息。
新建git仓库
Git中有仓库(repository)的概念,所有的文件应该都在仓库中。可以通过两种方式新建仓库:在本地新建仓库和从远程服务器clone一个仓库。
在本地新建仓库
在本地新建仓库非常容易,只需要在对应的目录下使用git init命令即可。命令如下:
// 新建一个git仓库git init
新建仓库之后,后续就是向仓库中添加文件,并提交。添加、提交的命令之后再介绍。
从远程克隆一个仓库
从远程克隆仓库使用的是git clone命令,比如:
// 从远程服务器clone一个git仓库,会在当前目录下新建Blogs文件夹git clone https://github.com/acBool/Blogs.git
当然,clone时也可以指定本地仓库的名字,命令如下:
// 本地仓库会被命名为myBloggit clone https://github.com/acBool/Blogs.git myBlog
查看当前文件状态
Git仓库下的文件有已修改、未修改、已暂存、未跟踪几种状态,使用status命令可以查看当前文件的状态,命令如下:
// 查看当前文件状态git status
git status命令输出的信息可能有些冗余,可以使用-s参数得到一个更为简介的信息:
// 查看当前文件状态,信息更为简洁git status -s
添加文件到git仓库
新建仓库后,可以向仓库中添加文件。但是添加的文件处于未被跟踪的状态,如果要改变文件为跟踪状态,需要使用git add命令,如下:
// 将hello.c的状态改为跟踪状态git add hello.c
如果add之后的参数是文件夹,会递归的跟踪该目录下的所有文件:
// 会递归的将Tempdir目录下的所有文件改为跟踪状态git add Tempdir
忽略文件
通常情况下,总会有一些文件没必要让Git来管理,也不希望这些文件总是出现在未跟踪文件列表,比如说一些日志文件,或者变异过程中生成的临时文件。这种情况下,可以创建.gitignore文件,在.gitignore文件中列出要忽略的文件即可。比如:
// 忽略所有以.a或者.o结尾的文件*.[oa]// 忽略所有以~结尾的文件*~// 忽略所有以.a结尾的文件*.a// 除了lib.a文件,使用!取反!lib.a// 忽略TODO文件,而不是TODO文件夹/TODO// 忽略build文件夹下的所有文件build/
查看文件做了哪些修改
使用git diff命令可以查看文件做了哪些修改,实际上就是和原来的文件做对比,命令如下:
// 查看未暂存的文件做了哪些更新git diff// 查看已暂存的文件做了哪些更新git diff --cached// 查看已暂存的文件做了哪些更新git diff --staged
其中,git diff --staged和git diff --cached的功能是一样的。
提交更新
提交的命令是 git commit,命令如下:
// 如果只输入git commit,会弹出文版编辑器让输入这次提交的信息git commit// 加上-m参数,直接输入此次提交的信息git commit -m 'add file'
移除文件
从Git中移除某个文件,使用的命令是git rm,命令如下:
// 移除a.test文件git rm a.test
需要注意的是,移除之后,还需要使用commit命令来此次的操作提交。另外,移除后,本地磁盘上的a.test文件也会被删除。
如果只想移除仓库中的a.test文件,而保留本地磁盘上的a.test文件,该如何操作呢?实际上,这样的应用场景是存在的,比如说忘记加.gitignore文件,将一些不必要的文件加入到仓库中,这时候就会有这样的问题。我们想把仓库中没用的文件删除,但是本地还想要保留,git对这种情况是支持的。命令如下:
// 从仓库中移除a.test,但保留在本地磁盘git rm --cached a.test
重命名文件
Git中重命名文件可以使用mv命令,如下:
// 将a.test文件重命名为b.testgit mv a.test b.test
查看提交日志
使用git log命令可以查看一个仓库的提交日志,命令如下:
// 默认不带参数,会按照提交时间列出所有的更新,包括提交人昵称,邮箱,最新的提交在最上面git log// 显示每次提交的差异,即具体更新了哪些内容git log -p// 显示最近两次提交的差异,限制了日志数量git log -p -2// 显示每次提交简略的统计信息git log --stat
撤消对文件的修改
如果不小心改了一个文件,但是不想修改该文件,也不想把修改过的文件放入暂存区,提交,Git提供了撤消修改文件的命令:
// 撤消修改文件.DS_Storegit checkout -- .Ds_Store
将文件从暂存区移除
当想将一个文件从暂存区移除时,可以使用reset命令,如下:
// 将.DS_Store文件从暂存区移除git reset HEAD .DS_Store
目前为止,我们所介绍的命令都是和本地仓库相关,提交、添加等,操作的都是本地仓库,那么,如何和远程仓库交互呢?如何将本地仓库的修改推送到远程仓库呢?
从远程仓库拉取
从远程仓库中获取数据,可以使用git fetch或者git pull命令,如下:
// 从远程仓库test拉取数据,需要注意的是,使用这种方式拉取的数据,并不会合并到本地仓库 // 还需要手动add、commit之后,才会合并到本地仓库 git fetch test// 从远程仓库test拉取数据,这种方式拉取的数据,会尝试合并到本地仓库 git pull test
推送到远程仓库
推送到远程仓库使用git push命令即可,如下:
// 推送到远程仓库testgit push test
运行完这条命令后,可能会让输入用户名和密码,以验证是否有推送的权限。
添加远程仓库
添加远程仓库使用的命令是git remote add命令,如下:
// 添加一个远程仓库,远程仓库的url是https://github.com/***/***,远程仓库的名称是testgit remote add test https://github.com/***/***
移除远程仓库
移除远程仓库使用git remote remove命令即可:
// 移除远程仓库testgit remote remove test
重命名远程仓库
重命名远程仓库使用rename命令,命令如下:
// 把远程仓库a重命名成bgit remote rename a b
查看Git标签
Git提供了标签的功能,可以在某些重要的节点增加标签,比如版本上线,可以打上标签。查看已有标签的命令是:
// 会列出所有的标签git tag// 只列出v1.1.0的标签git tag -l 'v1.1.0'
创建Git标签
创建Git标签使用-a命令,如下:
// 创建标签,标签名为v1.4,标签信息为 version 1.4git tag -a v1.4 -m 'version 1.4'
创建Git分支
Git中默认的分支名是master。Git中的master分支并不是一个特殊的分支,master分支和其他的分支没有什么区别。之所以大多数仓库都有master分支,是因为git init命令默认创建的分支是master。
Git创建分支的命令很简单,如下:
// 新建一个testing分支git branch testing
注意,这种方式只会新建一个testing分支,但是并不会切换到testing分支下。
另外介绍一下Git中的HEAD指针,HEAD指针指向当前所在的本地分支,可以将HEAD指针理解成当前分支的别名。默认是master分支,则HEAD指向的是master分支;如果切换到testing分支下,则HEAD指向的是testing分支。
分支切换
Git中分支切换使用checkout命令,如下:
// 切换到testing目录下git checkout testing
此时HEAD指针也指向了testing分支。另外需要注意的是,切换分支时,工作目录也会对应的改变。
另外一种切换分支的方式是:
// 这种方式适用于不存在testing分支的情况git checkout -b testing
上述命令实际上是两条命令的简写:
// 新建testing分支git branch testing// 切换到testing分支git checkout testing
分支合并
分支合并使用的是git merge命令,假设现在的工作目录是master,想要合并testing分支,则命令如下:
// 将testing分支合并到master分支git merge testing
分支删除
在合并完testing分支之后,可能testing分支对我们来说已经没有用了,这时可以将testing分支删除,删除命令如下:
// 删除testing分支git branch -d testing
冲突标示
在合并分支时,不可避免的会出现冲突,出现冲突后git会标示出来,大概如下:
<<<<<<< HEAD:index.html <div id="footer">contact : email.support@github.com</div> ======= <div id="footer"> please contact us at support@github.com </div> >>>>>>> iss53:index.html
======号上面是HEAD分支,也就是当前分支的内容,======号下面是合并分支,这里是iss53分支的内容。出现冲突的原因是两个分支对同一个文件的同一行做了修改,这种情况下需要解决手动解决冲突。
分支管理
查看当前分支列表
使用git branch命令可以查看当前的分支列表,如下:
// 查看当前分支列表git branch
列出的分支列表中,如果某个分支名前有*号,表示该分之是目前所处的分支,也就是HEAD所指向的分支。
查看每个分支最后的一条提交信息
使用git branch -v命令,能够看到每个分支最后的提交信息,如下:
git branch -v
查看已合并到当前分支的分支
使用git branch --merged命令,可以查看当前有哪些分支已经合并到当前分支了,如下:
git branch --merged
查看未合并到当前分支的分支
使用git branch --no-merged,可以查看当前有哪些分支没有合并到当前分支,如下:
git branch --no-merged
推送本地分支
可以使用git push (remote) (branch) 来将本地分支推送到远程仓库分支,命令如下:
// 推送本地的testing分支到远程origin仓库的testing分支git push origin testing// 推送本地的testing分支到远程origin仓库的somebranch分支git push origin testing:somebranch
跟踪远程仓库分支
当clone一个仓库时,Git通常会在本地自动创建一个master分支,该master分之跟踪的是origin/master,即远程仓库origin的master分支。当然,也可以跟踪其他的分支,命令格式是:git checkout -b [branch] [remotename]/[branch],针对该命令,git提供了--track的快捷方式,命令如下:
// 在本地新建一个serverfix分支,该分支跟踪的是远程仓库origin的serverfix分支git checkout --track origin/serverfix// 该命令和上面所表达的含义一样git checkout -b serverfix origin/serverfix
当然,也可以将本地分支的名字和远程分支的名字设置成不一样,命令如下:
// 在本地新建一个sf分支,该分之跟踪的是远程仓库origin的serverfix分支git checkout -b sf origin/serverfix
可以设置本地分支跟踪某一个远程分支,也可以修改本地分支正在跟踪的远程分支,使用的参数是-u或者--set-upstream-to,命令如下:
// 设置当前本地分支跟踪远程仓库origin的serverfix分支git branch -u origin/serverfix// 设置当前分支跟踪远程仓库origin的serverfix分支git branch --set-upstream-to origin/serverfix
查看所有本地分支正在跟踪的远程分支
使用git branch -vv命令,可以查看所有本地分支正在跟踪的远程分支,而且会列出本地分支是否领先,或者落后远程分支,命令如下:
// 查看本地分支正在跟踪的远程分支git branch -vv
拉取远程分支
拉取远程分支可以使用git fetch命令或者git pull命令。两者的区别是:git fetch命令拉取下来的数据,不会修改工作目录中的内容,需要用户自己合并,也就是使用git merge命令。而git pull相当于将这两个命令合并成一个命令,先git fetch,然后git merge。
删除远程分支
删除远程分支使用的命令如下:
// 删除远程仓库origin的serverfix分支git push origin --delete serverfix
变基
在Git中整合分支除了merge之外,还有一种方法就是变基(rebase)。看下面的例子:
image
该分之从C2开始产生了分叉,目前有master分支和experiment分支,使用git merge命令,当然可以将experiment分支和master分支合并。前面介绍过,使用merge命令,实际上是将两个分支的最新快照C3、C4以及二者的最近祖先C2进行了三方合并,合并结果进行了一次新的提交,效果如下:
作者:acBool
链接:https://www.jianshu.com/p/31537b9580c5