一、版本控制工具应该具备的功能
- 协同修改:多人并行不悖的修改服务器端的同一个文件;
- 数据备份:不仅保存目录和文件的当前状态,还能够保存每一个提交过的历史状态;
- 版本管理:在保存每一个版本的文件信息的时候要做到不保存重复数据,以节约存储空间,提高运行效率。这方面 SVN 采用的是增量式管理的方式,而 Git 采取了文件系统快照的方式;
- 权限控制:对团队中参与开发的人员进行权限控制,对团队外开发者贡献的代码进行审核(Git 独有);
- 历史记录:查看修改人、修改时间、修改内容、日志信息,将本地文件恢复到某一个历史状态;
- 分支管理:允许开发团队在工作过程中多条生产线同时推进任务,进一步提高效率;
二、版本控制工具
1、集中式版本控制工具:CVS、SVN、VSS……
2、分布式版本控制工具:Git、Mercurial、Bazaar、Darcs……
三、Git 命令行操作
1、本地库初始化
(1)命令:git init
(2)效果:创建了一个名为.git的隐藏目录。.git 目录中存放的是与本地库相关的子目录和文件,不要删除也不要胡乱修改。
2、设置签名
(1)形式:用户名、Email地址
(2)作用:区分不同开发人员的身份
(3)辨析:这里设置的签名和登录远程库(代码托管中心)的账号、密码没有任何关系。
(4)命令:
①项目级别/仓库级别:仅在当前本地库范围内有效
信息保存位置:./.git/config 文件
②系统用户级别:登录当前操作系统的用户范围
信息保存位置:~/.gitconfig 文件
(5)级别优先级:项目级别优先于系统用户级别,二者都有时采用项目级别的签名,都没有时不允许。如果只有系统用户级别的签名,则使用系统用户级别的签名。如果需要修改签名,只需要使用上面的命令再设置一次即可。
3、基本操作
(1)状态查看
git status:查看工作区、暂存区状态。
(2)添加
git add <file>:将工作区的“新建/修改”添加到暂存区。
(3)提交
git commit -m "提交信息" <file>:将暂存区的内容提交到本地库。
(4)将文件从暂存区恢复到原来的状态。
git rm --cached <file>
(5)查看历史记录
- git log (多屏显示时控制方式:空格向下翻页,b向上翻页,q退出)
- git log --pretty=oneline(只显示当前版本之前的记录)
- git log --oneline(只显示当前版本之前的记录)
- git reflog HEAD@{移动到当前版本需要多少步}
(6)前进后退(本质:就是移动HEAD指针)
- 基于索引值操作(推荐使用):git reset --hard [局部索引值]
如果通过该命令前进或后退了历史纪录,通过git log --oneline或git log --pretty=oneline的方式查看历史纪录的话,只能显示当前版本之前的记录,如:
- 使用^符号:只能后退
git reset --hard HEAD^:一个^表示后退一步,n 个表示后退 n 步。
- 使用~符号:只能后退
git reset --hard HEAD~n:表示后退 n 步。
(7)reset 命令的三个参数对比
- --soft参数:仅仅在本地库移动 HEAD 指针,不改变工作区和暂存区的状态。
执行命令前:本地库是最新版本,暂存区没有要提交的文件,工作区也是干净的。
执行命令后:本地库是最新版本,暂存区没有要提交的文件,工作区也是干净的。
- --mixed参数:移动本地库 HEAD 指针,重置暂存区,工作区不发生变化。
- --hard 参数:本地库移动 HEAD 指针,重置暂存区,重置工作区
备注:我们可以通过git help reset来查看reset命令的帮助文档, git reset --hard HEAD 可以使本地库,暂存区,工作区保持一致。
(8)删除文件并找回(以下说明的两种情况,前提是删除的文件往本地库提交过)
- 删除操作已经提交到本地:通过移动指针,使指针位置指向历史记录即可。
① 删除文件:
② 本地库该文件已被删除
③ 找回文件:
- 删除操作尚未提交到本地库:重置指针位置即可。
① 删除文件,并找回,HEAD指针指的就是当前指针指向的历史版本。
(9)比较文件差异
- 将工作区中的文件和暂存区进行比较:git diff <file>
- 将工作区中的文件和本地库历史记录比较:git diff [本地库中历史版本] [文件名]
- 不带文件名比较多个文件
四、分支管理
1、什么是分支?
在版本控制过程中,使用多条线同时推进多个任务。在初始化本地库的时候,默认会创建master分支,也叫主干。
2、使用分支的好处?
同时并行推进多个功能开发,提高开发效率。各个分支在开发过程中,如果某一个分支开发失败,不会对其他分支有任何影响。失败的分支删除重新开始即可。
3、分支操作
(1)查看分支:git branch -a (显示所有本地及远端分支名)/ -v (则只显示本地的所有分支名)
(2)创建分支:git branch <分支名>
(3)切换分支 :git checkout <分支名>
git checkout -b develop 创建并切换到develop分支,前提是该分支不存在,如果存在的话会报错。
(4)合并分支:
① 第一步:切换到需要合并到的那个分支上 ,git checkout <目标分支名>
② 第二步:执行 merge 命令:git merge <源分支名>
(5)解决冲突
① 冲突的表现:假设我们在不同的分支上,修改了同一个文件的同一个位置,这个时候在合并分支的时候就会产生冲突。比如:程序员A在hot_fix分支上修改了good.txt文件的第三行:
程序员B在master分支上也修改了good.txt文件的第三行:
此时如果将hot_fix分支上的内容合并到master分支上就会产生冲突。
②冲突的解决
■ 第一步:编辑文件,删除特殊符号
■ 第二步:把文件修改到满意的程度,保存退出
■ 第三步:git add <文件名>
■ 第四步:git commit -m "日志信息" , 此时commit时不能后缀文件名,否则会报错。
五、Git 基本原理
1、哈希
哈希是一个系列的加密算法,各个不同的哈希算法(例如:MD5,SHA-1,CRC32等)虽然加密强度不同,但是有以下几个共同点:
- 不管输入数据的数据量有多大,输入同一个哈希算法,得到的加密结果长度固定。
- 哈希算法确定,输入数据确定,输出数据能够保证不变。
- 哈希算法确定,输入数据有变化,输出数据一定有变化,而且通常变化很大。
- 哈希算法不可逆,指的是我们不能通过密文来反推明文。
Git 底层采用的是 SHA-1 算法。哈希算法可以被用来验证文件。原理如下图所示:
2、Git保存版本的机制
(1)集中式版本控制工具的文件管理机制
以文件变更列表的方式存储信息。这类系统将它们保存的信息看作是一组基本文件和每个文件随时间逐步累积的差异。 也就是只保存文件变化的部分,在回退历史版本的时候,它会把当前文件的修改和历史的修改以及最初的文件拼好,作为后退历史版本的文件,所以这种做法会很节省内存的消耗。
(2)Git的文件管理机制
Git把数据看作是小型文件系统的一组快照。每次提交更新时Git都会对当前的全部文件制作一个快照并保存这个快照的索引。为了高效,如果文件没有修改,Git不再重新存储该文件,而是只保留一个链接指向之前存储的文件。所以Git的工作方式可以称之为快照流。
(3)Git 文件管理机制细节
Git 的“提交对象” :每一个文件会生成一个哈希值,工作区会生成一个哈希值,整个提交会生成一个哈希值。
3、Git分支管理机制
(1)分支的创建:本质就是创建一个指针指向这个版本。
(2)分支的切换:本质就是移动HEAD指针。
六、GitHub
1、账号信息
GitHub 官网:https://github.com/
这里我们需要到GitHub的官网去创建一个远程仓库。
2、创建远程库地址别名
第一步:查看当前所有远程地址别名:git remote -v (首次创建时,不会显示任何信息)
第二步:创建远程库地址别名:git remote add [别名] [远程仓库地址]
创建完成之后可以通过查看指令进行确认。
这里说明一下,当我们GitHub官网上创建一个远程仓库的时候,上面的6.2可以不去执行,直接执行6.3的操作即可。
3、克隆
(1)命令
① 默认克隆远程master分支上的代码:git clone [远程仓库地址]
② 克隆远程仓库指定分支上的代码:git clone -b [分支名] [远程仓库地址]
注意:克隆的时候,要指定远程仓库的地址,不能使用别名,否则报错:fatal: repository 'origin' does not exist
(2)效果
◾ 完整的把远程库下载到本地
◾ 创建 origin 远程地址别名
◾ 初始化本地库
4、推送代码到远程仓库的指定分支:git push [别名] [分支名]
提示:git push时如果不再弹出用户和密码的输入提示框该怎么办?
执行该命令:git credential-manager uninstall
5、团队成员邀请
指的是我们再推送代码到远程的时候输入的用户名和密码,该用户如果没有操作这个远程仓库的权限的话,那么推送的时候就会报出下面的错误。注意这里的用户名和前面配置的签名并不是同一个账户,签名指的是谁做了操作,这里的账户才是真正操作远程的人。
如果在推送的时候报403的错误很可能就是因为该成员没有访问的权限。解决办法就是:管理员邀请该成员加入到这个仓库,该成员接收邀请,便可以重新推送了。
点击接受邀请即可。
6、拉取
场景:假设成员B对远程库中某一分支的test.txt文件做了修改并推送到了远程库,此时远程库中该文件内容为最新的提交。成员A是这个仓库的管理员,这个时候成员A本地仓库的test.txt文件还是之前的内容(文件上面会有一个红色的感叹号,因为该文件已经被修改了)。
通过:git fetch [远程库地址别名] [远程分支名],将远程文件下载到本地,但是不会改变本地工作区文件的内容,即:
如果我们想查看下载下来的文件内容,需要切换到origin/master分支上面,即:
我们再切换到本地的master分支上,通过:git merge [远程库地址别名/远程分支名] ,将远程master分支上的内容合并到本地master分支上,即:
当我们对文件所做的修改比较简单,而且不太会产生冲突的时候,我们可以通过:git pull [远程库地址别名] [远程分支名] ,将远程代码直接抓取到本地。即:
所以:pull=fetch+merge,前面是先fetch,后merge,这样做的好处就是:当我们所做的操作比较复杂的时候,我们可以先不merge本地的文件,等我们确定好从远程库下载下来的文件之后再做合并。
7、解决冲突
要点:如果不是基于 GitHub 远程库的最新版所做的修改,就不能推送,必须先拉取最新的代码。
(1)制造冲突:假设成员A对test.txt文件的第二行做了修改,并推送到了远程库。
此时成员B也修改了test.txt文件的第二行并提交到了本地库,这个时候再推送到远程库的时候就会产生冲突而导致推送失败。
(2)解决冲突:就是将冲突文件中的内容修改为我们想要的内容即可。
将上图中的冲突文件修改好之后,再添加到暂存区,提交的时候只需要加上message即可,不需要再后缀文件名,因为前面已经提交过一次了,即:
8、跨团队协作
(1)第一步以东方不败的身份登录之后,访问岳不群的远程仓库,并点击fork(复刻)。
(2)执行Fork操作。
(3)Fork结束之后,界面发生了变化:
(4)将复刻之后的仓库克隆到本地并做修改:
(5)推送到远程库:
(6)Pull Requests(发起合并申请):
第一步:点击New pull request
第二步:创建合并请求
第三步:发起合并请求
第四步:查看请求(岳不群账户登录):
第五步:进行对话1:
对话2:
第六步:审核代码
第七步:审核通过,合并代码
第八步:确认合并
第九步:将远程库修改拉取到本地
七、SSH登录
1、进入当前用户的家目录
$ cd ~
2、删除.ssh 目录
$ rm -r .ssh/
3、运行命令生成.ssh 密钥目录
$ ssh-keygen -t rsa -C [Email地址]
4、进入.ssh 目录查看文件列表
$ cd .ssh/
$ ls -lF
5、查看 id_rsa.pub 文件内容
$ cat id_rsa.pub
6、复制 id_rsa.pub 文件内容,登录 GitHub,点击用户头像→Settings→SSH and GPG keys→New SSH Key,输入复制的密钥信息,点击添加SSH Key
7、回到 Git bash 创建远程地址别名
git remote add origin_ssh git@github.com:atguigu2018ybuq/huashan.git
8、推送文件进行测试