Git 版本控制
说在前面
这一次讲课内容分成了腾讯会议和直播,参加腾讯会议都是属于我的团队内成员或者归属于我管理下的团队成员,其他可以理解为旁听。
本次讲课为了方便你么能够快速学习,如何进行团队开发,大部分为实践内容,会大致说明理论内容,用的网站是 https://git.x-lf.cn/
由于 GitHub 基本是英文加上你们访问速度比较慢
学开发我看谁只听不用电脑实操
本次的软件使用的是 git
以及 Jetbrains PhpStorm
请各位自行下载
对于
Jetbrains PhpStorm
请自行想办法下载或者找我激活(仅限腾讯会议跟我开会过的团队内成员)
什么是版本控制
其实版本控制在我们的生活中无处不在,比如你的期末考试或者是毕业答辩论文,由于你写的不规范或者老师不满意,你的老师可能会让你改了又改,于是就会出现下面的这种情况
我们手里的论文可能会经过多次版本的迭代,最终我们会选取一个最好的版本作为最终提交的论文。使用版本控制不仅仅是为了去记录版本迭代历史,更是为了能够随时回退到之前的版本,实现时间回溯。同时,可能我们的论文是多个人一同完成的,那么多个人如何去实现同步,如何保证每个人的更改该都能够正常汇总,如何解决冲突,这些问题都需要一个优秀的版本控制系统来解决。
走进Git
- 工作目录:存放我们正在写的代码 (当我们新版本开发完成之后,就可以进行新的版本提交)
- 暂存区:暂时保存我们待提交的内容 (新版本提交后会存放到本地仓库)
- 本地仓库:位于我们电脑上的一个版本控制仓库 (存放的就是当前项目各个版本代码的增删信息)
- 远程仓库:位于服务器上的版本控制仓库 (服务器上的版本信息可以由本地仓库推送上去,也可以从服务器抓取到本地仓库)
Git是一个分布式的控制系统,因此一般情况下,我们每个人的电脑上都会有一个独立仓库,由大家共同向远程仓库去推送版本的迭代信息。
通过这一系列的操作,我们就可以实现每开发完一个版本或者一个功能,就提交一次新的版本,这样,我们就可以很好的控制项目的版本迭代,像回退到之前的版本随时回退,先查看新版本或者添加或者是删除了什么代码,随时都可以查看。
安装Git
网站下载
首先前往git官网进行下载 https://git-csm.com/download/
我在下面会手把手教你们如何安装Git环境
设定区分用户
安装完成之后,需要设定用户名和邮箱来区分不同的用户
1 | git config --global user.name "YourName" |
基本命令介绍
创建本地仓库
我们可以在任意一个文件夹作为一个本地的仓库,在你想要当作本地仓库的文件夹目录下输入:
1 | git init |
完成之后,如果用户没有开启可见,那么你是看不到 .git
文件夹(这一个目录是隐藏目录,而当前的文件夹就是我们的工作目录)
下面是可见参考例子:
创建成功之后,我们可以输入查看一下当前的一个状态
1 | git status |
如果已经设置成功配置为本地仓库了,那么在一个除了 .git
目录外,没有任何其他东西,输入之后可以看到:
(表示我们还没有向仓库中提交任何内容,也就是一个空的状态)
1 | On branch master |
添加和提交
添加一个文件之后,例如 hello.txt
文件,随便输入内容,例如
1 | Hello World! |
那么,再输入
1 | git status |
就会给出下面的提示内容:
1 | On branch master |
其中
Untracked files
是未追踪的文件,也就是说如果一个文件处于未追踪的状态,那么 git 是不会记录他的变化的,始终当作一个新的文件进行处理,这里将其添加到暂存区,那么他就会标记成追踪状态
1 | git add hello.txt |
然后我们可以再次详细查看一下当前的状态:
1 | On branch master |
文件名称就由原来的未追踪状态变成了追踪状态,也就是由 Untracked files
到 Change to be commmited
下。因此我们的 hello.txt
文件就已经添加到了暂存区了。
接着我们尝试将提交 git 的本地仓库中,注意需要输入提交的描述以便后续查看,例如这次提交修改了或者新增了什么内容:
1 | 提交到 本地仓库 其提交描述为: Initial file |
1 | [master (root-commit) ea6e81c] Initial file |
接着我们就可以看到我们的提交记录
1 | git log |
我们就可以看到一条提交记录
1 | commit ea6e81c2769bf279f73cfe786ce22a07a39f994c (HEAD -> master) |
其中 commit
后面的到 (HEAD -> master)
一串内容是 hash 值,在一个本地仓库里是独一无二的
Author
作者名字,尖括号为邮箱,其余不做介绍
下面截图是添加参数 --graph
之后的截图例子,由于我们版本分支比较少,介绍不了什么
修改
我们继续打开 hello.txt
文件,将里面的内容修改(新增一行内容,例如:My name is XiaoLFeng),那么现在文件内容一共内容是:
1 | Hello World! |
那么输出后就是
1 | On branch master |
在这里,内容就被归类到 Changes not staged for commit
暂存区未提交的更改
其中发现修改的内容就是 modified
后面接着文件名字
那么我们再次提交到 git 本地仓库输入
1 | 将文件添加到暂存区 |
现在我们通过历史版本可以看到
1 | commit 470daec52ae862b48cfa6f6c8d98f9aab882fd43 (HEAD -> master) |
内容已经有两个,但是内容消息太长了,我们压缩一下只让他显示一行,参数 --oneline
1 | git log --oneline |
现在他的显示内容如下,显示了hash值和当前分支所在位置以及添加概要
1 | 470daec (HEAD -> master) Add line about my name |
以上我们查看了一个文件的修改时间和修改历史,对于版本管理,还可以直接查看某一个文件或者全部文件的修改行和修改历史,具体对某些行做出了什么修改以及其他相关的修改。
1 | git show [加上 commitID<Hash> 值,查看指定的 commit 提交记录] |
可以反馈出内容是:
1 | commit 470daec52ae862b48cfa6f6c8d98f9aab882fd43 (HEAD -> master) |
当我们提交完毕再次查看状态,就会说没有任何的修改,因为都是空的:
1 | On branch master |
.gitignore
接着我们可以创建一个 .gitignroe
文件来确定一个文件忽略列表,如果忽略列表中的文件存在且不是被追踪状态的,那么 git 不会对其进行任何的检查:
1 | 匹配全部的 doc 结尾的文件 |
1 | 排除一整个文件夹 |
1 | 目录中所有以 doc 结尾的文件,但是不包括子目录 |
1 | 目录中所有以 doc 结尾的文件,但是包括子目录 |
创建之后我们来看看是否还会受到忽略文件的影响
回滚
当我们想回退到某一个版本的时候,就可以执行回滚操作,执行之后,可以将问价你内容恢复到指定提交的状态
1 | git reset --hard commitID<Hash> |
执行之后,会直接重置为那个时候的状态。再次查看提交日志,我们发现之后修改的日志全部消失了
首先,这是原来的最新状态
1 | 9562b35 (HEAD -> master) Initial .gitignore |
接着进行回退到版本为 470daec
部分
1 | git reset --hard 470daec |
显示的历史记录的内容就变成了
1 | 470daec (HEAD -> master) Add line about my name |
如果我们只是想会看历史记录,随后我想回去怎么办?
我们可以通过查看所有分支的所有操作记录
1 | git reflog |
会显示
1 | 470daec (HEAD -> master) HEAD@{1}: reset: moving to 470daec |
那么,第二行就是我们的操作记录所在的 commitID<Hash>
那么接着输入
1 | git reset --hard 9562b35 |
就可以进入到最新的版本
分支创建与合并
分支就像我们树上的一个树枝一样,它们可能开始的时候是同一根数值,但是长着长着就开始分叉开了,这就是分支。
我们的代码就是这样,可能一开始写的时候是单个分支,但是后面随着开发的迭代,我们需要更多功能来维护我们的代码就会分出很多个分支,例如做前端的分支和做后端发的分支,这两个分支可以完全没有任何关系。
但是在团队开发中,以及我们后续需要的开发中,前后端部分在我这边我一般会分为两个仓库,所以在分支方面不会做两个分支做前端和后端。分支更倾向于开发合作方面。
例如,有一个主分支,这个分支是全部代码的集合点,假设我们有一个项目,做一个学生信息管理系统,现在已经完成了基础开发,而你需要完成一个新的功能,那么我可以从主分支 master(或者main,也有其他一般是这两个)
新建一个 feature(特点)
分支,完成一项特定的功能后进行 Pull Request(请求合并)
等操作。
因此,我们可以在一个主干分支上分出许多许多分支,分别对多个分支的代码进行维护。
创建分支
我们可以通过以下命令来查看当前仓库中存在的分支
1 | git branch |
我们发现,默认情况下只有一个 master
分支的,并且我们目前使用的也是 master
分支,一般情况下 master
分支都是作为正式版本的更新,而其他分支一般都是开发中才才会进行的频繁更新的。我们接着来基于当前分支创建一个新的分支:
1 | git branch feature |
现在我们修改一下文本内容,进行提交
1 | git commit -a -m 'branch master commit' |
在这一个操作中,我们将修改的文件修改只作用在了 master
分支部分,而新创建的分支没有发生任何的修改。
1 | de2c242 (HEAD -> master) branch master commit |
所以,我们想要对分支进行操作,我们就要将一个分支签出,切换到另外一个分支
1 | git checkout feature |
现在,我们在分支里面提交一份新的修改
现在,我们查看一下提交历史
1 | --online 输出单行 |
我们就可以得到一张分支图
1 | * 382ec04 (HEAD -> feature) branch freature commit |
合并分支
我们也可以将两个分支更新的内容最终合并到同一个分支上,按照上面的举例就是说,master
为主分支,而 feature
为开发分支,当你完成一个新的开发任务(完成了一个新的功能点),可以选择分支进行合并。
1 | git checkout master |
接着使用合并分支命令
1 | git merge feature |
会得到如下提示内容
1 | Auto-merging hello.txt |
接着我们可以看到合并产生了冲突的地方
1 | git diff |
就会告诉我们出现冲突的地方是什么地方
1 | diff --cc hello.txt |
或者你也可以直接打开冲突部分的文件
对于冲突部分直接进行修改,例如我要保留 feature
分支的 This is commit for feature branch
内容,那么我就保留这一个内容,其余部分删除
接着保存,进行提交
1 | git commit -a -m "Merge 'feature' Modify master" |
我们就可以得到如下的历史构图
1 | git log --all --graph --oneline |
变基分支
除了直接合并分支之外,我们还可以进行变基的操作,他跟合并的思维方式不一样,但是也是合并的一种方式。
合并是分支回到主干的一个过程,而变基是直接修改分支的开始位置,比如,我们希望将 feature
分支变基到 master
分支上,那么 feature
会将分支的起点移动到 master
最后一次提交到的位置。
1 | git rebase master |
变基后,相当于 feature
分支相当于同步了此前 master
的全部提交
如果是合并分支,是两个分支的提交内容进行合并
1 | 合并分支 |
如果是变基分支,是两个分支将 rebase
的 分支名
变基到当前分支,将 分支名
的全部提交内容提交到 当前分支
里面
1 | 变基分支 |
优选
我们还可以选择将其他分支的某一次提交作用到当前分支上,这一种操作成为 cherry-pick
1 | git cherry-pick <commitId[Hash]> |
这里,我们在在 master
分支上创建一个新的文件,提交此次更新之后,接着通过 cherry-pick
将此次更新的内容作用在 feature
分支上
使用IDE进行版本控制
在这里,我们使用 Intellij PhpStorm
进行演示.
<hr/
虽然前面我们基本讲解了 git
的命令行的使用方法,但是没有一个图形化界面,始终会感觉很抽象,所以我们这里使用 IDE
来演示.
IDE
内部集成了 git
模块*(但是并不代表你不需要下载git-csm)*,它可以让我们的 git
进行图形化显示.
创建版本控制
打开 PhpStorm
之后,找到版本控制模块,点击 创建 git 仓库
创建一个文件,我们发现,项目中的文件全部变成了红色,也就是处于未追踪的一个状态,接着我们进行第一次初始化的提交,提交之后我们可以在下面看到所有本地仓库的提交记录
接着我们进行一次初始化提交,提交之后我们可以在本地仓库的提交历史查看到所有的提交记录信息
现在简单编辑内容,随后再次更新
接着我们演示一下分支的创建和分支的管理.
远程仓库
远程仓库实际上就是位于服务器上的一个仓库,它能在远端保存我们的版本历史,并且可以实现多人同时合作编写项目,每个人都能够同步他人的版本,能够看到其他人的版本提交信,相当于我们的代码放在服务器上进行托管.
远程仓库分为 公有仓库
和 私有仓库
公有的远程仓库有 Github
,码云(Gitee)
,Coding
等,他们都是对外开放的,我们注册账号之后就可以呃使用远程仓库进行版本控制,其中最大的就是 Github
,但是由于服务器在国外,所以相对访问起来速度还是比较慢的,国内链接可能需要某些手段(**)才能够访问的比较正常.私有的一般是 Gitlab
,Gitea
等这种自主搭建的远程仓库私服,在公司中比较常见,它只对公司内部开放,不对外不开放.
而我们现在使用的 https://git.x-lf.cn/ 属于半仓库私服,属于自行搭建,不对外部开放,但是允许外部注册用户进行
Issue
,PullRequest
等内容进行提交
远程仓库的认证
用户名/密码登录
我这里只讲我的私服操作,对于我这边的私服,只需要进行在提交的时候登录用户和密码就可以了,其中用户名是你在私服中注册的账户与密码
由于我的电脑已经登陆过了,所以暂时没有办法进行演示,直接按需求提交你们电脑上面就会自动提示的
SSH密钥
对于我的私服来说,账户密码登录不需要每一次进行验证,在几年前的 Github
与 Gitee
网站进行提交的时候如果使用账户密码登录,在每一次 push
代码的时候都会需要账户密码验证,就会显得很麻烦.
不过好在,现在 Github
也可以通过 Access Token
进行登录,也不需要每一次都需要验证,这一步你们可以选择跳过
1 | ssh-keygen -t rsa |
然后打开对应文件夹,用开发软件或者文本软件打开 id_rsa.pub
文件,将里面的
1 | ssh-rsa xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
内容全选 Ctrl+A
加上 Ctrl+C
复制,打开到网站(我的私服或者 Github
)配置ssh密钥
点击 新增密钥
即可,在您进行项目提交的时候,选择使用密钥进行连接,而不是选择用户名密码进行登录连接就可以了.
远程项目的推送
在你完成了上述基本的开发,下面想要讲代码开源或者将代码推送到远程仓库,就需要进行远程项目的推送.
远程项目的推送,首先要将远程连接与本地仓库进行 remote
连接
1 | git remote add <别名> <远程仓库地址> |
接着我们就可以推送代码
1 | git push origin master |
接着就会显示如下内容,表示上传成功并且新建立一个 master
分支
1 | Enumerating objects: 111, done. |
下面对 IDE
进行项目推送的演示
远程仓库的项目克隆
有的时候,我们并不是自己有项目需要做,而是你在一个开源网站 Github
找到了一个好的项目,或者说你找到了一个别人的项目,但是有BUG,然后你进行维护,那么就需要克隆下来到本地仓库才能够继续在本地进行开发编辑.
1 | git clone <远程项目地址> |
然后出现下面内容就是成功克隆下来
1 | Cloning into 'DataStructure_ArrayQueue_C'... |
团队协作
团队成员可以通过克隆仓库、创建分支、提交更改、推送和拉取更新等步骤进行协作开发。Git保留每个成员的独立工作空间,允许并行开发,解决冲突,进行代码审查和发布版本。团队成员可以高效地管理代码的版本和变更,提高开发效率和代码质量。
多人项目在同一分支的上传
在同一个远程仓库中,若多个人在同一个远程仓库拥有编辑该分支,例如 master
分支的权限,那么可以由多个开发者共同编辑同一个分支
下面直接作为演示
多人项目进行分支开发
上面说到,多个人可以操作一个分支,那么多个人也可以控制多个分支
下面直接作为演示
提交工单
在
Git
中,工单(Issue)
是用于跟踪和管理软件项目中的问题、任务或建议的功能。工单提供了一种组织、讨论和解决项目中的问题的方式。团队成员可以创建工单,指派给特定成员,设置优先级和标签,并在工单中进行讨论和更新。工单的作用包括提供问题追踪、任务管理和协作的平台,促进团队成员之间的交流,改进项目的开发和维护效率。
开源代码的工单
开源代码的工单一般都是有公有仓库 Github
,码云(Gitee)
等网站,开放出来的由多个人共同维护或发现的.
假设你在 Github
找到了一个很好的内容,对于里面的内容想要自定义或者发现问题,而并不是作为修改,可以提交问题工单给开发者,那么在开源代码的工单中,一般来自于五湖四海的人发布内容.
团队合作的工单
团队合作的工单是一种在团队协作环境中使用的工具,用于跟踪、分配和管理任务、问题或需求。以下是团队合作中使用工单的关键点:
- 创建工单:团队成员可以创建工单来记录任务、问题或需求。工单应包含清晰的描述、关联的信息和必要的上下文。
- 分配责任:通过指派功能,将工单分配给适当的团队成员。这确保了每个工单都有负责人负责跟进和解决。
- 设置优先级和截止日期:为工单设置优先级和截止日期,以便团队成员可以根据重要性和紧急程度合理安排工作。
- 讨论和更新:工单通常具有评论功能,团队成员可以在工单上进行讨论、提供意见或更新进展。这有助于团队内部的沟通和协作。
- 关联相关资源:在工单中关联相关的文档、代码、设计或其他资源,以提供更全面的上下文和支持。
- 跟踪和更新状态:团队成员应及时更新工单的状态,如进行中、已解决或需要反馈等。这有助于其他成员了解工单的状态和进展。
- 关闭和总结:当工单完成时,应及时关闭,并提供总结或反馈。这有助于团队回顾和改进工作流程。
提交合并请求
合并请求 Pull Request
简称 PR
在团队合作中有着重要的作用,可以为了更好在团队内部进行代码审查管理,方便项目经理等进行代码审查,检查代码是否合格,最终由项目审查组
和项目开发组组长
等人进行允许合并.
为什么又合并请求,在正规的团队开发中,为了保持代码的良好稳定开发,一般在仓库中会设置主分支 master
分支不允许推送(包含强制推送)或不允许普通开发者进行推送,以免低端代码或未经审核代码造成项目延期,危险代码的出现,在合并请求中,可以检查出冲突部分避免强制合并导致整体项目出现不可挽回的错误.
代码提交
在提交合并请求的时候,至少有两个分支,意思与本地分支直接分支合并一样的原理,但是多了 请求
,表示需要经过同意之后才允许合并.
创建一个分支
1 | git branch feature |
在分支中写入需要修改的内容,进行提交到推送
1 | 提交到本地仓库 |
随后在网站中创建 合并请求
接着,你就可以在合并请求中找到自己的发送的请求合并栏目
最后等待审查组
与项目组长
进行代码审查和批准
在上述的合并请求操作中为一次性最佳通过效果,在实际开发中往往由于开发等造成的问题需要进行代码调整,审查组
与项目组长
会对内容进行审查,如果审查不通过,一般会告诉你不通过的原因,需要您重新进行代码修改提交,提交完成后需要评审员
重新对项目代码进行评审,直至代码审核通过至允许合并(或者你自己取消了合并请求)
注意: 合并请求可以关联工单
代码持续维护
在您创建的 Pull Request
中,不一定要完成全部内容才允许合并,其中设置 WIP
也是一个不错的选择,这一部分对每一个远程仓库都不大一样,在我的私服中,需要在标题栏写入 WIP:
字样,可以避免项目直接被审查与批准操作,对于审查组
与项目组长
遇到该字样,会选择暂时保留,而等到您去除 WIP:
字样后,才会走入正式评审流程.
在此期间,你可以继续维护您的代码,继续对代码进行编辑操作,正常进行代码的推送,所有的代码推送都会在 Pull Request
里面显示出来,同时允许拥有该分支权限的开发者共同编辑此项目
代码审查
代码审查是由审查组
与项目组长
进行的,其他开发者一般只需要维护代码即可,不需要对代码进行检查和批准.
在审查组
内对你的代码提出问题的时候,通常你也可以从审查页面说明该段代码的作用是什么
而您可以直接回复或者说明内容已经解决.
对于审查组
与项目组长
一般在 文件变动
页面会开始进行代码审查,对代码审查通过一般会直接执行通过而不会做出过多的解释
而对于代码出现问题,需要进行修改的地方,审查组
会对出现问题的地方进行评审(问题说明)
个人项目PR
在一个个人项目中,如果仓库管理员没有给予您开发权限,那么你可以通过 派生(fork)
进行代码拷贝,拷贝成自己的仓库进行操作
而派生后的仓库属于你自己的仓库,你可以克隆下来自己进行编辑,当你编辑完毕之后如果想与原仓库(派生前)的分支进行合并,那么可以直接在原仓库申请合并请求,在合并请求中,可以在列表中查找到你自己派生的仓库下的分支内容进行合并提交.
Actions
版本控制的Actions(操作)是指在版本控制系统中进行的一系列操作或行为。这些Actions包括但不限于以下几个方面:
- 提交(Commit):将代码或文件的更改记录到版本控制系统中,创建一个新的版本。
- 更新(Update):从版本控制系统中获取最新的代码或文件,并将其应用到本地工作环境中。
- 比较(Diff):对比两个版本之间的差异,查看哪些部分发生了变化。
- 合并(Merge):将不同分支或版本的代码合并在一起,将它们的更改整合到一个统一的版本中。
- 分支(Branch):创建新的代码分支,使团队成员可以在独立的环境中进行开发,而不会干扰主要的代码线路。
- 回滚(Rollback):将代码或文件恢复到之前的某个版本,以撤销不必要的更改或修复错误。
- 标签(Tag):为特定的版本打上标签,以便于在需要时快速识别和检索。
这些Actions帮助团队成员协同工作,跟踪代码更改,管理版本历史,并确保代码的稳定性和可维护性。