前端工程化——版本控制⚙️
什么是版本控制
版本控制是一种系统性的方法,用于管理和跟踪软件开发项目中的代码、文件和资源。它的核心目标是在不同时间点的开发中保持代码的一致性、可追溯性和可维护性。
为什么要做版本控制
这个问题可以从定义中的关键词可以看出端倪:不同时间点下的代码一致性、可追溯性、可维护性。这里额外补充一点:当今的互联网开发早已步入敏捷迭代,各种动态化方案层出不穷都有着顺应时代潮流的意思,因此软件开发一定会越来越多地涉及到团队开发,团队之间的协作也一定需要这样一个机制去保障稳定性。
代码一致性
版本控制系统允许开发人员有效地管理和组织项目中的代码。它跟踪每个文件的历史记录,记录了每个版本的变更,包括谁做了什么修改、何时以及为什么。
可追溯性
版本控制系统允许为重要的里程碑或发布创建标签(tag
),以便轻松识别和回滚到特定版本。
并且当某个版本的代码出现问题,版本控制系统允许开发人员轻松地回滚到之前的稳定版本,并在不破坏其余代码的情况下进行修复。
可维护性
开发人员可以创建分支(branch
),这是一个独立的代码副本,用于开发新功能、修复错误或进行实验性工作。然后,这些分支可以与主代码库合并,以实现功能集成。
团队协作
在多人协作的环境中,不同开发者可能同时修改代码。版本控制系统确保不同的修改不会互相冲突,以及如何解决冲突。
版本控制系统促进了团队之间的协作,可以通过代码审查工具进行代码审查,以确保代码质量和一致性。
版本控制工具Git
Git
的安装
WIndows
这里下载独立安装包即可
点install
就行,会自动安装一个git GUI
工具,但通常不会直接用它
下载完成安装即可,输入以下命令验证安装是否成功
git version
图形化工具
安装
下载本体
然后是语言包
安装时一路next
即可,安装完本体后再安装语言包。
配置好Github
的账户、邮箱、秘钥等,注意配置好了密钥也不会在下述部分显示,可以打开编辑全局进行查看
使用
在使用之前先提前补充一个点(后续会详细介绍),Git
有本地仓库和远端仓库的概念,通常支持HTTPS
和SSH
两种协议进行关联,两种方式优缺点也十分明显:
为了一劳永逸,我们这里配置下SSH
,首先打开Git bash
# 输入下列命令,最后一个参数是您的邮箱
$ ssh-keygen -t rsa -C yourEmail.com
可以看到密钥对已经生成了
进入上述目录,注意.ssh
文件夹默认是隐藏的,直接访问路径就行
以Github
为例,配置公钥
创建一个Demo
项目,平时学习的话就创建公有项目就行
复制一下我们的项目链接
选取一个你喜欢的目录作为本地工作区,然后按鼠标右键
然后喜提报错,但没事是因为我们缺失一个配置,导致TortoiseGit
和Git
冲突了
首先我们找到安装Git的目录,拷贝一下ssh.exe
的路径,比如D:\software\Git\usr\bin\ssh.exe
Here We Go
我们进到这个目录,创建一个README.md
,随便写点什么保存
将代码提交到本地暂存区
提交完成后会有一个推送按钮,作用是将暂存区里的代码推送到远端
当然又可以鼠标右键,进行推送
然后我们重新起一个目录验证一下,代码是否被推送到了远端,和前面一样手动Clone
一下
到此为止,GUI
工具就先简易把玩到这里,你可能会疑惑拉取、同步按钮这些不是还没讲吗?别着急,我们先从Git
的机制入手,看看GUI
工具背后都做了哪些工作,到时候不用我说你也会细细把玩了~
VS Code插件推荐
这里主要推荐官方自带的Git
工具和两个补充插件
官方自带+Gitlen
Gitlen
是非常全面的git
插件,这里就结合官方自带能力演示一下常规用到的一些功能点。
查看某行代码的提交信息
可以很直观地看到本地和远端代码的版本差异
将代码添加到缓存中
将代码提交到本地暂存区
将代码推送到远端
查看刚刚提交的信息
Git Graph
一个优美的Git
提交可视化插件,在团队开发时可以很直观地看出各版本之前的差异。
本地仓库、暂存区、远端仓库
相信通过前面的实操你已经对这三个概念有了基本的认识,接下来我们详细了解一下他们之前的区别把。
本地仓库
- 本地仓库是物理存储在您电脑上的
Git
仓库副本 - 它包含了完整的项目记录和文件版本信息
- 您可以在本地仓库中进行增删改查等所有你想对代码干的事儿
暂存区
- 暂存区是本地仓库的一部分,可以理解为本地仓库变更的一个快照,用于准备要提交的更改
- 当你完成文件粒度的代码变更后,需要将这些文件提交到暂存区,以便一次性提交一个或多个更改。这使得你可以有选择地进行提交更改,而不是一次性提交所有更改
- 暂存区和当前正在开发的本地仓库是一一对应的,也就是说当代码已经保存到暂存区后,不能再进行诸如分支切换等变更本地仓库文件版本的操作
远程仓库
- 远程仓库是位于网络上的
Git
仓库副本,通常托管在代码托管服务上。(如GitHub、Gitlab
等) - 它允许多个开发者协同工作,共享项目代码。
- 具备私有和公有属性,由开发者决定是否将项目开源
- 具备特定分支保护性,通常将
master
(主干)分支作为受保护的分支,不允许开发者在主干分支上进行开发,亦不允许开发者将本地分支代码直接推送到主干。那么怎么把分支同步到主干呢?一般托管平台会提供一个PR
(pull request
)能力,即一种向其他分支提交合并代码请求的能力。开发者创建PR
,由项目的主要负责人和团队中的成员进行代码的审核后,合并到master
分支 - 当然你可以从远程仓库拉取最新代码以及将本地更改推送远端,并解决版本不一致带来的冲突
分支、提交、标签
这三个概念都是用作Git
的版本控制的,可以细分出下面这些差异。
分支(branch
)
- 分支是
Git
中用于并行开发和代码管理的核心机制,它允许你创建一个独立的开发线而不影响主线代码。(可以类比与世界线进行理解) - 每个分支都有自己的代码历史,你可以在分支上就行修改、提交和合并操作。
- 常见的用途包含新增特性(
feature
分支)、修复代码(bugfix
分支)、发布功能(release
分支)、热修复(hotfix
分支,一般用在native
代码中)。这些分支命名是一种约定俗成的规范,我们在开发过程中也请尽量遵循这样的规范以减少沟通理解带来的gap
# 常用的分支命令,多的不用记,用到了再查就行
# 创建一个本地分支
git checkout -b branchName
# 拉取远端分支信息
git fetch
# 切换分支
git checkout newBranch
# 查看所有本地分支
git branch
# 查看远端所有分支,这里的-r指的是origin,也就是远端
git branch -r
提交(commit
)
前文在实践过程中已经提到过部分commit
的知识点了,我们再来温习一下。
- 提交操作相当于是一个快照,将本次更改的文件和提交信息存储到了暂存区。
- 提交是代码历史的基本单位,可以有效地追踪和回溯项目的演变。
- 提交时需要附带一条有意义的提交信息,以便更好理解每次更改的目的。
# 常用命令
# 提交代码到暂存区
git commit -m "本次提交干了啥"
# 提交代码到暂存区并跳过检查(不是很推荐跳过git hook的检查,但我就喜欢这样干,最后再统一修正)
git commit -m '本次干了啥' --no-verify
标签
- 标签是
Git
中用于标记特定提交的方式,通常用于标记项目的重要里程碑。注意:在我们使用一些热门库时经常会看见诸如@latest、@release、@beta-0.1
这样紧跟在npm
包后面的小东西,这也就是标签,但不是git
的标签而是npm
包的标签,我们会在后续包管理的文章里再提到这一点。 - 不同于分支。标签是静态的,不会随着新的提交而发生移动,除非你手动再次进行标记。
- 标签所带来的语义能够让你很方便地找到和回滚到特定版本。
# 常用命令
# 创建标签
git tag 'release'
# 查看所有便签
git tag
# 查看特定标签的提交信息
git show tagName
如何运用Git
创建一个工作区
这个小节其实已经在GUI
工具哪里浅浅地操作了一遍了,我们再复习一下~
克隆代码,将代码从远端拉到本地
# 参数是仓库的ssh或https链接
git clone git@xxxxx
# clone后会自动关联远端,当然也可以通过下面命令进行关联
git remote add origin git@xxxxx
创建分支
# 创建分支
git checkout -b feature/git-demo
拉取代码
这一步通常会有git fetch
和git pull
两种方式,几乎所有的教程都会告诉你git pull
是git fetch + git merge
的命令语法糖,但并不会告诉你怎么用git fetch
,因为我们在git fetch
后无论是ide
还是命令窗口都不会像git pull
把diff
的有效信息展示到我们眼前,所以接下来我们来仔细讲讲这个问题吧!
git fetch
执行git fetch
不止会把远端代码变更拉取下来,还能将分支等信息也一并拉取下来,我们可以用前面的命令来进行检索git brach -a
那么我们怎么看,拉取的远端代码有哪些变更呢?结合我们之前安装的Vs code
插件可以进行预览(二选一即可)
合并代码
检索完代码变更后我们如何合并呢?这里有git merge
和git rebase
两种方式,并且均会在有冲突时被打断,因此不用担心将代码冲突也一并合并了
merge VS rebase
一图胜千言,这是merge
合并后的git graph
这是rebase
后的git graph
解决冲突属于一个重要的部分,我们在后面一个场景小节单独说下。
提交代码
# 创建分支后首次提交,需要关联下远端
git push --set-upstream origin feature/git-demo
# 后续提交无需再关联
git push
提PR
在别人审核通过后,merge
按钮会变绿,我们就可以合并到主干啦
特殊场景处理
合并冲突
接着上面的例子,我们创建了一个新的分支并修改了同一个文件,然后推送到远端
这时我们直接提PR
会直接被打回,提示我们需要在合并前解决冲突。
ok接下来,我们来解决冲突。
首先是拉取并合并主干代码,这时我们发现会有传入更改和当前更改两段代码。
这里我提供一个个人解决冲突常用的思路:优先接收传入的变更,因为我对于自己的变更更熟悉,所以我通常会先备份自己的代码变更,然后接受传入的变更,最后再融入个人的代码变更,这对于我来说是一个高效的解决思路,但仍有一些细节需要处理。涉及到package.json、yarn.lcok
等变更时,在解决冲突记得重新yarn
一下,防止npm
包的冲突;涉及到核心业务逻辑和细粒度很高的地方(比如修改了同一个函数并且是不兼容变更)时需要和同事一起确认正确的逻辑后再进行冲突合并,否则很容易因解决冲突而发生代码丢失的情况。当然这种冲突本身是应该尽量避免的,避免两个甚至多个同学对同一个模块进行并行开发。
回到案例中,这两个冲突属于兼容性冲突,我们按上面的策略先拷贝我的变更,再接受传入的变更,最后将拷贝的变更合入,(PS:我当然知道可以直接点保留双方更改,但我不推荐这样做,这个操作往往会带来一些麻烦)最后我们提交一条新的commit
用于解决冲突。
然后我们刷新PR
页面会发现此时已经可以合并到主干了
回撤代码
当我们一步小心错误地合并代码了或者想要回退一些变更时,通常有两个做法,分别是revert
和reset
,下面是他们的一些特征对比:
revert
用于撤销一个或多个提交,并用一个新的提交来反转这些更改。这意味着原有的提交仍然存在,只是添加了一个新的提交来撤销之前的更改。在能用revert
解决的情况下,我不建议使用reset
,但revert
无法回撤merge
时形成的commit
,此时就需要借助reset
了。
# 查看 commit 信息
git log
# 回撤对应的commit
git revert commitId
reset
用于移动分支的指针,以便于指向不同的提交,模式上包含软重置和硬重置。在回撤操作时通常使用硬重置,特别需要注意的是硬重置是强制将分支指针移动到特定commit
,会丢失部分历史记录,使用时需要特别小心。
# 查看 commit 信息
git log
# 回撤对应的commit
git reset --hard commitId
使用旧功能进行新功能开发,如何将这些变更迁移到新分支
分三步走:
- 将旧分支的
commit
记录提交到远端,使用git log
查看commitId
并手动记录下来 - 切换新分支
- 执行
git cherry-pick commitId
你可能并不需要这些命令
看到不少博主都推荐使用git stash
来处理并发更改的问题,我个人使用下来并不是很推荐这样做,原因很简单,经常会忘记,忘记保存过,忘记该恢复到哪个版本,恢复时误操作把代码给删了...
简单介绍一下git stash
# 临时存储
git stash
# 查看存储记录
git stash list
# 恢复并且不删除stash内容
git stash apply
# 恢复并且删除stash内容
git stash pop
那么有没有什么办法规避呢,很简单嘛,把当前代码先提交一下;或者重新创建一个工作区拉取代码都能解决。并且想要某个文件在某一时段的变化,直接用VS code
的时间轴功能不香吗?
结语
这是前端工程化系列的第一篇文章,预计后续还有 10
篇的样子,个人精力有限,尽量周更。还是老规矩,如果对你有帮助的话点个👍和收藏吧❤️
参考文献
转载自:https://juejin.cn/post/7280056184793956410