学习目标
版本控制
版本控制是一种记录若干文件内容变化,以便将来查阅特定版本修订情况的系统. 简单讲就是备份和记录. 接下来我们要了解三种不同版本控制的发展历程.。
本地版本控制系统
人们把项目拷贝到本地磁盘上进行备份, 然后以命名方式来区分. 这种做法好处是简单, 但坏处也不少比如备份比较多或许就会混淆不同版本之间的区别. 那为了解决这个问题, 有人就开发了一个本地版本的管理系统, 它的结构图如下:
本地版本管理就是把版本号存入数据库来记录文件的历次更新差异。
集中化版本控制系统
本地版本控制系统能够将不同版本的文档保存下来并且借助版本记录可以很方便定位相关文件但又引入了新的问题,如何让在不同系统上的开发者协同工作?于是,集中化的版本控制系统( Centralized Version Control Systems,简称 CVCS )应运而生。这类系统,诸如 CVS,Subversion 以及 Perforce 等,都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。多年以来,这已成为版本控制系统的标准做法 。
这样做的好处是解决了人们开发协同的问题, 但是把所有的代码提交到同一台服务器上有一个很明显的问题就是单点故障, 如果这台服务器宕机了, 那所有人都不能提交代码, 还有如果这台服务器如果磁盘发生故障,碰巧没做备份,或者备份不够及时,就还是会有丢失数据的风险。最坏的情况是彻底丢失整个项目的所有历史更改记录,而被客户端提取出来的某些快照数据除外,但这样的话依然是个问题,你不能保证所有的数据都已经有人事先完整提取出来过。本地版本控制系统也存在类似问题,只要整个项目的历史记录被保存在单一位置,就有丢失所有历史更新记录的风险。
分布式版本控制系统
为了解决集中化版本管理所带来的问题分布式版本管理控制系统(Distributed Version Control System,简称 DVCS)就应运而生了. 在这类系统中,像 Git,Mercurial,Bazaar 以及 Darcs 等, 客户端不只是提取出最新版的文件快照, 而是把最原始的代码仓库镜像到本地. 这样一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。因为每一次的提取操作,实际上都是一次对代码仓库的完整备份。
所以综上来看的集中化版本控制系统是对本地版本控制系统的一次升级, 因为它加入了协同操作, 分布式版本控制系统是对集中化控制系统的一次补充, 使之更加完善。
Windows上安装Git
最早Git是在Linux上开发的,很长一段时间内,Git也只能在Linux和Unix系统上跑。不过,慢慢地有人把它移植到了Windows上。现在,Git可以在Linux、Unix、Mac和Windows这几大平台上正常运行了。
在Windows上使用Git,先从Git官网直接下载安装程序,选择指定系统下载,然后按默认选项安装即可。安装完成后,在开始菜单里找到“Git”->“Git Bash”,显示出类似命令行的窗口,说明Git安装成功!
在窗口内输入 git --version 查看git 版本信息如下:
在使用用Git工作之前,我们需要做个一次性的配置。方便后续Git能跟踪到谁做了修改,我们需要设置对应的用户名与邮箱地址。
git config --global user.name "your_username"
git config --global user.email your_email@domain.com
git config --list 查看所有配置
注意git config
命令的--global
参数,用了这个参数,表示你这台机器上所有的Git仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和Email地址。
理解Git文件的三种状态与工作模式
使用Git 操作文件时,文件的状态有以下三种:
状态 | 描述 |
---|---|
已提交(committed) | 已提交表示数据已经安全的保存在本地数据库中。 |
已修改(modified) | 已修改表示修改了文件,但还没保存到数据库中。 |
已暂存(staged) | 已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。 |
针对Git 文件的三种状态,这里需要了解Git项目的三个工作区域:工作区、暂存区和Git仓库。
分类 | 描述 |
---|---|
工作区 | 简单的理解为在电脑里能看到的目录,比如自己创建的本地项目目录 |
暂存区 | Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git自动创建的第一个分支master,以及指向master的一个指针叫HEAD。 |
Git仓库 | 工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。 |
基本的Git 工作流程描述如下:
- 在工作区中修改某些文件。
- 对修改后的文件进行快照,然后添加到暂存区。
- 提交更新,将保存在暂存区域的文件快照永久转储到 Git 仓库中。
流程图如下:
创建版本库并提交文件
版本库又名仓库,可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。理解了Git 文件状态与三种工作区域之后,通过一个例子来体验Git对于文件的基本操作。
编写一个文本文件并将文件提交到git仓库
- 初始化git 本地仓库
通过执行
git init
命令在本地初始化一个本地仓库,执行该命令后会在本地初始化一个没有任何文件的空仓库。
- 新建文本文件 git01.txt 并添加到暂存区
文本内容如下:
在 .git 同级目录下添加git01.txt 文件后,使用 git status
查看工作目录与暂存区文件状态
git status 命令用于显示工作目录和暂存区的状态。使用此命令能看到那些修改被暂存到了, 哪些没有, 哪些文件没有被Git tracked到。
执行git add
命令添加文件到暂存区
git add path 通常是通过git add <path>的形式把<path>添加到索引库中,<path>可以是文件也可以是目录。
git不仅能判断出<path>中,修改(不包括已删除)的文件,还能判断出新添的文件,并把它们的信息添加到索引库中。
此时可以看到有一个git 已tracked 到新文件git01.txt,文件被成功存放到暂存区
- 提交文件到本地版本库
文件被添加到暂存区后,执行git commit 命令提交暂存区文件到本地版本库中。
git commit 命令用于将更改记录(提交)到存储库。将索引的当前内容与描述更改的用户和日志消息一起存储在新的提交中。通常在执行提交时 在 git commit 命令后跟上 -m 属性 加入本次提交的记录说明 方便后续查看提交或改动记录。
git log
:命令用于显示提交日志信息。 (比较常用,后续讲到时光穿梭时会经常使用该命令)。
时光穿梭机
企业中在多人的项目开发环境下,使用Git 版本控制工具对项目版本进行管理时,通常会对项目不同版本的文件进行查看,项目历史版本,未来版本的切换操作,对于一个项目开发人员,此时对于Git 的这些基本命令操作就成为了一项基本技能。
修改文件与文件提交
修改后内容如下:
此时当文件修改后 使用 git status
命令可以看到git 检测到文件被修改,git 版本库给出的下一步操作是添加修改的文件到暂存区 此时执行添加操作命令
执行提交
git log
命令查看操作日志记录
修改需要注意的问题
下面再次修改git01.txt 然后执行提交操作
执行提交操作
此时 执行 git diff HEAD -- git01.txt
与版本库内容进行比较结果如下:
差异比较说明---
:表示变动前的文件+++
:表示变动后的文件
变动的位置用两个@作为起首和结束
@@ -1,2 +1,3 @@:减号表示第一个文件,"1"表示第1行,"2"表示连续2行。同样的,"+1,3"表示变动后,成为第二个文件从第1行开始的连续3行。。
可以看出:文本中第三行内容并没有提交到版本库中 原因在于修改后的git01.txt 并没有添加到暂存区,所有执行提交操作并不会发生改变。
暂存区文件提交与撤销
当发现因失误而将文件添加到暂存区时,git 支持文件的撤销操作 执行命令 git reset HEAD 文件
操作如下:
查看版本库状态并执行撤销操作
再次查看版本库状态 test.txt 成为未追踪文件
版本回退
当文件修改后被提交的次数很多时,对于版本库中存放的文件就会出现不同的版本,在多人开发的项目环境中,通常会对不同版本文件进行查看甚至回退的情况(比如某些游戏中所提供的状态保存功能,能够在某一时刻保存整个游戏场景状态以方便后续继续在该状态下进行游戏进行而不是从头开始),值得庆幸的是 Git 也提供了同样的功能,能够让开发者在不同版本的项目中进行切换,达到时空穿梭自如的目的!
对于上面操作的git01.txt 文件已有几个版本,对于历史版本的查看 使用git log
命令:
git log:命令用于显示提交日志信息
列表显示的结果按提交时间倒叙排序,其中第一条中 HEAD -> master
代表当前指针指向Git 版本库中master 主干分支,每次提交 Git 内部均会生成一个唯一的SHA 算法构建的字符串来唯一标识当前版本
此时如果想要执行版本版本回退操作使用命令 git reset
git reset 命令用于将当前HEAD复位到指定状态。一般用于撤消之前的一些操作(如:git add,git commit等)。
回滚前在执行两次提交操作 方便文件不同版本间的切换
第三次提交 修改后的git01.txt 内容如下:
第四次提交 修改后的git01.txt 内容如下:
git log
查看提交历史记录如下
当然,如果提交历史记录较多 可以加入数字控制显示的版本记录数 并且使用--pretty=oneline
简化输出 如下:
显示最近三次提交
切换版本前 git01.txt 内容如下:
回退到上一版本
执行 git reset --hard HEAD^
- HEAD^:将指针指向上一个版本,如果是上上一个就是 HEAD^^,上上上一个HEAD^^^,但这样记就比较麻烦,如果回退版本较多,简写为 HEAD~100 往前回退100个版本 ~后跟数字即可
查看回退后的git01.txt内容如下:
回退操作已经完成,但此时如果想要回到未来的版本即最新的版本怎么办呢? 其实这里也比较简单,前面说到针对提交后的版本库,每个版本均会有一个唯一标识,这里找到对应版本标识即可完成回到未来版本的操作 如下
执行git reset 命令如下:
如果此时回到某一个版本后直接关闭了当前git 命令窗口 怎么样才能回到未来版本呢?因为此时未来版本的唯一标识id 在窗口中看不到了!
值得庆幸的是,Git早已为你想到了这种情况,Git提供了一个命令git reflog
用来记录用户操作的每一次命令,效果如下:
git reflog:查看记录在本地的HEAD和分支引用在过去指向的位置。
文件删除
在Git中,删除文件同样是一个修改操作,即在Git世界中,Git仅仅关注文件是否被修改(文件添加,更新,删除)
在工作区添加新文件git02.txt 内容如下:
将文件添加到版本库
文件提交到版本库后,在工作目录执行手动删除操作后执行git status
命令可以看到Git 能够追踪到文件被删除情况,注意此时版本库中文件并没有被删除,只是工作目录中文件被删除!
如果此时发现文件是被误删除呢,不用担心,这里可以将误删除的文件重新从版本库中检出,执行命令:
git checkout -- 文件名
如果确定是要执行删除操作 执行git rm 命令即可
远程仓库
Git是一个分布式版本控制系统,同一个Git仓库,可以分布到不同的机器上。截止目前,并没有看到分布式的环境,因为以上的操作都在在本地发生的,对于Git,除了前面提到的本地版本库外,Git 支持远程仓库的托管服务即使用者可以将本地版本库中的文件托管到远程服务器进行存储,这样就极大的方便开发,无论你走到哪,只要你的机器能够联网,就可以通过远程的仓库地址得到一份相同的项目库文件,并且下载到本地的文件版本记录与远程文件版本保持一致,并且可以很方便的实现多人协同开发操作。
对于Git 远程仓库,GitHub(https://github.com/ )是比较知名的一个,目前已被微软收购,而国内比较知名的当属码云(https://gitee.com/ )了,当然除了这些远程仓库外,在公司,有的公司处于安全考虑,可能会自己搭建一套Git服务区来自Git的远程仓库,对于内部仓库会有专门人员来进行维护操作。这里以当下比较流行的GitHub仓库来介绍Git 远程仓库基本操作与使用。
克隆远程项目到本地
初次接触GitHub的话,在没有账号的情况下,也可以很容易得到一些比较好的开源项目,即通过克隆的方式将远程项目下载到本地 比如现在java12 都已经发布,想要找一些java12 相关的项目入门,这里就可以借助GitHub来发现你想要的相关项目
克隆项目到本地执行 git clone 命令
在本地磁盘指定目录下右键 git bash here
操作
此时在本地就得到了远程的相关项目。
将本地库推送到远程
- 创建本地版本库并提交文件到本地库(这里以前面创建版本库为主,这里不再赘述)
- 使用GitHub创建远程库git01 如下
推送本地Git版本库文件到远程仓库git01
推送本地库文件到远程有两种方式
- [x] 使用Https
- [x] 使用SSH
使用Https 比较简单,使用SSH 加密方式是Git 建议的一种推送,时间上与响应上效率都更高,这里介绍SSH推送方式配置
- 使用本地Git客户端生成SSH公钥与私钥 执行命令
ssh-keygen -t rsa -C "GitHub账户邮箱"
GitHub 公钥配置
设置公钥标题与公钥串内容
- 检查测试链接 执行命令
ssh -T git@github.com
使用SSH执行远程推送操作
根据GitHub 说明 执行SSH 推送 首先绑定远程地址到本地执行命令
git remote add origin git@github.com:zsyue/git01.git
执行远程推送操作 将本地库推送到远程master 主分支 执行命令git push -u origin master
查看远程仓库 到此,使用SSH推送本地版本库文件到GitHub远程库操作成功完成!
Git 分支操作
开发企业项目中在使用Git 或者其他类似版本控制软件对项目版本进行管理时,多人合作的项目在开发时通常不会直接在主干master 上进行操作,而是重新开辟新的分支,在新的分支上进行开发 调试 等操作,当项目调试通过时才会将分支项目的代码合并到主干中,这是在实战中比较好的一种策略,特别是多人协同开发一个项目的情况下尤其明显。Git 对于分支操作提供了一下基本命令:
命令 | 描述 |
---|---|
git checkout branch | 切换到指定分支 |
git checkout -b new_branch | 新建分支并切换到新建分支 |
git branch -d branch | 删除指定分支 |
git branch | 查看所有分支, 并且*号标记当前所在分支 |
git merge branch | 合并分支 |
git branch -m or -M oldbranch newbranch | 重命名分支,如果newbranch名字分支已经存在,则需要使用-M强制重命名,否则,使用-m进行重命名。 |
本地分支创建、合并、重命名与删除
以前面git版本库为例。
- 创建本地分支、并查看分支
默认Git 版本库所在分支为master 通常称为项目的主干,开发中通常会在主干上创建新的分支类进行本地开发工作 使用命令
git checkout -b new_branch
创建分支分支创建完毕 该命令会自动切换到新建分支上 如上图
使用命令
git branch
查看分支列表 * 号标记为当前git 所在分支
- 分支上添加文件leaf01.txt 添加操作同主干添加命令 leaf01.txt 文件内容如下:
- 切换到主干master 并执行合并操作
- 重命名分支leaf01->leaf02
- 删除分支(不能再待删除的分支上执行删除当前分支操作!!!)
分支Push与Pull操作
相关命令操作
命令 | 描述 |
---|---|
git branch -a | 查看本地与远程分支 |
git push origin branch_name | 推送本地分支到远程 |
git push origin :remote_branch | 删除远程分支(本地分支还在保留) |
git checkout -b local_branch origin/remote_branch | 拉取远程指定分支并在本地创建分支 |
- 查看远程仓库 此时远程仓库只有主干master
- 新建分支leaf01 并执行分支推送操作
远程查看 此时本地分支远程推送完成(注:推送远程分支名称可改)。
- 远程创建dev分支并拉取分支到本地
GitHub支持远程分支在线创建,这里dev开发分支创建后分支文件内容与主干master一致
本地执行克隆 并拉取dev 分支到本地
执行git clone
命令查看本地项目并执行命令
git checkout -b dev origin/dev
拉取dev 开发分支
分支操作冲突出现与解决
开发中对不同分支下同一文件进行修改后执行合并时就会出现文件修改冲突情况,这里说明一种比较常见的冲突问题 以master 和leaf01 两个分支进行演示说明。
本地分支操作冲突
- 修改master与leaf01分支前git01.txt 文件内容状态
master 主干文件内容:
leaf01 分支文件内容:
- 分支leaf01 下修改给git01.txt 并执行提交操作 效果如下
- 主干master 下修改git01.txt 并执行提交操作
- 执行合并操作 此时发现git 在合并中产生冲突
执行cat git01.txt
可以看出冲突文件内容
Git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容
<<<<<<< HEAD 当前git 指向分支 这里指的为master
======= 分离不同分支修改的内容
>>>>>>> leaf01 leaf01 与 master 在git01.txt 同一行同时出现了修改操作 这里git 是不允许发生的
此时出现冲突后 这里对git01.txt 内容进行修改 (实际开发视情况而定 这里将内容合并为一行)
修改完毕master下执行提交即可 执行命令查看分支合并图 git log --graph --pretty=oneline
多人协同操作冲突
拉取远程库dev 并在本地创建dev开发库,执行命令 git checkout -b dev origin/dev
这里以同台机器不同窗口来模拟两个用户操作同一分支同一文件(实际开发时多人操作统一文件冲突情况比较常见)
这里两个客户端以c1与c2来描述
c1 客户端本地修改dev 分支git01.txt 文件并在本地执行提交操作 效果如下
执行远程推送 将本地C1客户端提交的git01.txt 推送到远程dev分支
而此时C2 客户端在本地同样修改了dev分支git01.txt 文件并在本地执行提交
执行推送操作 此时冲突出现 原因是另外一个用户推送的文件与当前客户端推送内容存在冲突:
此时解决方式Git 已有对应提示 Push 之前先执行Pull 操作 将远程文件拉取到本地 解决完冲突后再次执行Push 操作
冲突解决
先执行Pull 拉取操作
查看冲突文件内容
这里在本地先处理冲突 将文本进行合并 然后提交 在 push 操作即可
远程仓库内容如下:
标签管理
标签操作基本命令 git tag
命令 | 描述 |
---|---|
git tag tag_name | 新建标签 默认为HEAD |
git tag -a tag_name -m 'xxx' | 添加标签并指定标签描述信息 |
git tag | 查看所有标签 |
git tag -d tag_name | 删除一个本地标签 |
git push origin tag_name | 推送本地标签到远程 |
git push origin --tags | 推送全部未推送过的本地标签到远程 |
git push origin :refs/tags/tag_name | 删除一个远程标签 |
同大多数 VCS 一样,Git 也可以对某一时间点上的版本打上标签。开发中在发布某个软件版本(比如 v1.0 等等)的时候,通常使用版本库软件命令来对某一版本打上一个标签,以方便标识。
- 添加本地标签
标签默认会打到最近的一次提交记录上
也可以对某个指定的提交标识进行标记
打标签时给标签添加说明信息 git tag -a v0.0.1 -m '软件基础版本' a4a44dc
推送本地标签到远程
远程查看
- 删除本地标签
- 删除远程标签
Idea下Git基本操作
环境集成配置
全局setting 下 git 环境指定
指定git.exe 文件路径
远程GitHub 配置
克隆远程仓库到本地
或者在Idea 打开情况下
设置远程url 地址(这里使用ssh) 并指定本地项目路径
克隆到本地 idea 打开效果:
### Idea 下分支拉取与远程推送
分支拉取
默认情况下,使用Idea检出远程仓库master文件到本地,这里点在idea中可以方便查看(与使用命令方式一致)
或者 idea 右下角 查看如下:
检出远程库dev 分支到本地 这里可以使用前面的命令 git checkout -b local_branch origin/remote_branch
或者使用图形化界面进行操作 这里介绍第二种 图形化操作 在idea 右下角拉取远程dev 到本地
检出成功后提示信息如下:
命令查看分支情况
远程推送
在以上拉取到本地的dev 分支下修改文件git01.txt 然后执行推送操作
修改为文件添加到暂存区
执行本地提交操作
此时提交出现提交窗口
这里发现 仅仅修改了git01.txt 文件 为什么会出现这么多新增的文件? 这些文件也不是从远程拉取到本地的,没有必要进行提交操作 如果提交会影响他人本地环境!!! 此时的解决方案比较简单,将暂存区的无用文件转移到工作区即可 执行命令 git reset HEAD 文件名
即可达到效果 (取消提交操作,这里需要借助git 命令进行操作 后续会介绍一种简单办法)
执行命令 git reset HEAD .idea/*
git reset HEAD git01.iml
将暂存区无需提交文件转移到工作区
执行 git status
查看暂存区文件 即将提交到本地版本库文件 发现待提交的文件只有git01.txt
执行提交操作
执行远程推送操作
或者
提交分支确认
远程查看
ignore插件集成
前面讲到dev 分支拉取与文件修改推送操作,遇到一个小的问题-在修改完git01.txt 文件后添加文件到暂存区后会将idea 本地环境相关配置文件一并提交,而这些文件没有必要提交 如果提交对于另外一方更新到本地后反而会影响项目运行 前面解决的方式是借助命令方式将暂存区文件转移到工作区来解决。
命令方式虽然能够解决,但操作麻烦,每次提交不可避免会遇到同样问题,这里介绍一个插件-gitignore,使用gitignore 插件可以在执行文件添加前将没有必要提交的文件让git给自动忽略掉这样开发中就只需要关注修改的文件即可,想想是不是很美!
1) 安装插件ignore
settings->plugins->Marketplace 搜索.ignore插件 如果之前没有使用过该插件 点击install按钮,idea会自动下载并执行安装操作,当插件安装成功后重启idea即可。
2) .ignore忽略文件使用
插件安装成功重启idea后,ignore插件即生效,接下来就可以使用插件来忽略待添加的相关文件。
新建.ignore忽略文件
点击generate 新建文件即可
文件创建成功后会放在项目根目录下 这里将新创建的.ignore文件加入暂存区(会有相应提示 点击add to git 即可)
配置忽略文件
.ignore 忽略文件创建成功后,即可配置待忽略文件,配置方式相当简单 在待忽略的文件上右键 添加到忽略文件即可 如下:
自动产生的文本内容如下:
修改git01.txt 文件后执行添加并提交 此时未受版本控制的文件列表已消失 执行提交即可(也可以提交同时进行推送一步到位!)。
分支操作与冲突处理
idea 下对于分支的操作使用起来相比命令操作是比较简单的,开发环境下idea分支操作比较常见
本地分支创建与切换
设置本地分支名称leaf01
idea下分支切换比较简单 Git->Repository->Branches 选择指定分支 执行checkout即可。
冲突出现与解决
多用户操作同一文件同一行内容提交冲突
多用户同时修改某个文件同一行内容,并能够提交,此时便会出现冲突,对同一版本文件不能出现不同内容的文件
文件提交前状态模拟
用户:张三
用户:王五
此时 假如张三抢先一步进行提交操作
此时,对于张三来说,本地提交并推送没有问题,回到王五这端,此时假如王五在不知道张三对同一文件进行修改的情况下也进行了提交操作,此时,王五这端提交结果如下:
此时,在对文件进行提交时,系统提示 建议先进行合并操作,意思就是将服务器端最新内容先合并到本地后再进行提交操作。
执行merge合并操作
保留两者文件内容 执行合并
最后提交 王五这端内容。
张三 这端执行拉取操作
分支操作内容冲突
在使用git 对文件进行版本控制时,在不同分支同一文件同一行进行修改的情况下,此时对分支文件进行合并时,同样会有出现冲突的可能。
在master 下创建新的分支leaf01
分支下修改文本第三行内容:
此时在分支leaf01 上执行提交与push 操作 此时是没有问题的(假如其它用户没有在当前分支对应文本同一行修改与提交操作)
回到主干上 修改主干文本第三行内容
主干上执行提交与push 操作此时主干提交没有问题(假如其它用户没有在当前主干对应文本同一行修改与提交操作) 然后在主干上执行合并操作 选择待合并的leaf01 分支:
此时出现冲突场景
冲突之所以出现的原因在于:主干与分支在统一位置同时进行了修改操作,此时执行合并操作是不被允许的 因为git 此时再合并时不能区分是执行替换操作 还是执行追加新内容到主干此时需要用户来进行冲突解决(合并分支内容到主干或者进行替换处理)这里执行追加处理(注意:在企业中具体场景具体分析 添加还是做替换操作)
追加完毕后主干上执行提交与push 操作即可 此时冲突解决。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。