什么是版本控制
版本控制是一种系统性的方法,用于管理和跟踪软件开发项目中的代码、文件和资源。它的核心目标是在不同时间点的开发中保持代码的一致性、可追溯性和可维护性。

为什么要做版本控制
这个问题可以从定义中的关键词可以看出端倪:不同时间点下的代码一致性、可追溯性、可维护性。这里额外补充一点:当今的互联网开发早已步入敏捷迭代,各种动态化方案层出不穷都有着顺应时代潮流的意思,因此软件开发一定会越来越多地涉及到团队开发,团队之间的协作也一定需要这样一个机制去保障稳定性。
代码一致性
版本控制系统允许开发人员有效地管理和组织项目中的代码。它跟踪每个文件的历史记录,记录了每个版本的变更,包括谁做了什么修改、何时以及为什么。
可追溯性
版本控制系统允许为重要的里程碑或发布创建标签(tag
),以便轻松识别和回滚到特定版本。
并且当某个版本的代码出现问题,版本控制系统允许开发人员轻松地回滚到之前的稳定版本,并在不破坏其余代码的情况下进行修复。
可维护性
开发人员可以创建分支(branch
),这是一个独立的代码副本,用于开发新功能、修复错误或进行实验性工作。然后,这些分支可以与主代码库合并,以实现功能集成。
团队协作
在多人协作的环境中,不同开发者可能同时修改代码。版本控制系统确保不同的修改不会互相冲突,以及如何解决冲突。
版本控制系统促进了团队之间的协作,可以通过代码审查工具进行代码审查,以确保代码质量和一致性。
版本控制工具Git
Git
的安装
WIndows
访问 Git官网,选择对应的操作系统下载即可。

这里下载独立安装包即可

点install
就行,会自动安装一个git GUI
工具,但通常不会直接用它

下载完成安装即可,输入以下命令验证安装是否成功
复制代码
git version

图形化工具
安装
这里推荐TortoiseGit这个工具,非常方便。但我觉得Git本身学习成本并不高,GUI
工具更多是为了提升效率,基本的Git
知识点还是需要掌握的。

下载本体

然后是语言包

安装时一路next
即可,安装完本体后再安装语言包。

配置好Github
的账户、邮箱、秘钥等,注意配置好了密钥也不会在下述部分显示,可以打开编辑全局进行查看


使用
在使用之前先提前补充一个点(后续会详细介绍),Git
有本地仓库和远端仓库的概念,通常支持HTTPS
和SSH
两种协议进行关联,两种方式优缺点也十分明显:
HTTPS
:无需额外配置,但是在每次连接远端仓库时需要输入账号密码进行校验。SSH
:生成一组密钥对,需要本地进行配置,好处是配置好后每次连接远端仓库时会自动比较密钥对,无需额外校验。关于SSH
详细可以参考这篇文章ssh-远程登录协议
为了一劳永逸,我们这里配置下SSH
,首先打开Git bash
ruby
ruby
复制代码
# 输入下列命令,最后一个参数是您的邮箱
$ 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
bash
bash
复制代码
# 常用的分支命令,多的不用记,用到了再查就行
# 创建一个本地分支
git checkout -b branchName
# 拉取远端分支信息
git fetch
# 切换分支
git checkout newBranch
# 查看所有本地分支
git branch
# 查看远端所有分支,这里的-r指的是origin,也就是远端
git branch -r
提交(commit
)
前文在实践过程中已经提到过部分commit
的知识点了,我们再来温习一下。
- 提交操作相当于是一个快照,将本次更改的文件和提交信息存储到了暂存区。
- 提交是代码历史的基本单位,可以有效地追踪和回溯项目的演变。
- 提交时需要附带一条有意义的提交信息,以便更好理解每次更改的目的。
perl
perl
复制代码
# 常用命令
# 提交代码到暂存区
git commit -m "本次提交干了啥"
# 提交代码到暂存区并跳过检查(不是很推荐跳过git hook的检查,但我就喜欢这样干,最后再统一修正)
git commit -m '本次干了啥' --no-verify
标签
- 标签是
Git
中用于标记特定提交的方式,通常用于标记项目的重要里程碑。注意:在我们使用一些热门库时经常会看见诸如@latest、@release、@beta-0.1
这样紧跟在npm
包后面的小东西,这也就是标签,但不是git
的标签而是npm
包的标签,我们会在后续包管理的文章里再提到这一点。 - 不同于分支。标签是静态的,不会随着新的提交而发生移动,除非你手动再次进行标记。
- 标签所带来的语义能够让你很方便地找到和回滚到特定版本。
bash
bash
复制代码
# 常用命令
# 创建标签
git tag 'release'
# 查看所有便签
git tag
# 查看特定标签的提交信息
git show tagName
如何运用Git
创建一个工作区
这个小节其实已经在GUI
工具哪里浅浅地操作了一遍了,我们再复习一下~
克隆代码,将代码从远端拉到本地
perl
perl
复制代码
# 参数是仓库的ssh或https链接
git clone git@xxxxx
# clone后会自动关联远端,当然也可以通过下面命令进行关联
git remote add origin git@xxxxx
创建分支
bash
bash
复制代码
# 创建分支
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 merge
后形成了一条commit
,合并HEAD
指针指向了新的提交,并且仍然存在两条时间线。而git rebase
会在提交后销毁自身的世界线,融入主干的世界线,并且不会引起HEAD
指针的变化。实际使用合理使用git rebase
能有效减少世界线的条数,降低合并的复杂度。更多详情我推荐这篇文章面试官:说说你对Git Rebase 和 Git Merge的理解?区别?
解决冲突属于一个重要的部分,我们在后面一个场景小节单独说下。
提交代码
perl
perl
复制代码
# 创建分支后首次提交,需要关联下远端
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
:reset
:
revert
用于撤销一个或多个提交,并用一个新的提交来反转这些更改。这意味着原有的提交仍然存在,只是添加了一个新的提交来撤销之前的更改。在能用revert
解决的情况下,我不建议使用reset
,但revert
无法回撤merge
时形成的commit
,此时就需要借助reset
了。
bash
bash
复制代码
# 查看 commit 信息
git log
# 回撤对应的commit
git revert commitId

reset
用于移动分支的指针,以便于指向不同的提交,模式上包含软重置和硬重置。在回撤操作时通常使用硬重置,特别需要注意的是硬重置是强制将分支指针移动到特定commit
,会丢失部分历史记录,使用时需要特别小心。
perl
perl
复制代码
# 查看 commit 信息
git log
# 回撤对应的commit
git reset --hard commitId
使用旧功能进行新功能开发,如何将这些变更迁移到新分支
分三步走:
- 将旧分支的
commit
记录提交到远端,使用git log
查看commitId
并手动记录下来 - 切换新分支
- 执行
git cherry-pick commitId
更多cherry-pick
相关知识,参考git cherry-pick 教程
你可能并不需要这些命令
看到不少博主都推荐使用git stash
来处理并发更改的问题,我个人使用下来并不是很推荐这样做,原因很简单,经常会忘记,忘记保存过,忘记该恢复到哪个版本,恢复时误操作把代码给删了...
简单介绍一下git stash
perl
perl
复制代码
# 临时存储
git stash
# 查看存储记录
git stash list
# 恢复并且不删除stash内容
git stash apply
# 恢复并且删除stash内容
git stash pop
那么有没有什么办法规避呢,很简单嘛,把当前代码先提交一下;或者重新创建一个工作区拉取代码都能解决。并且想要某个文件在某一时段的变化,直接用VS code
的时间轴功能不香吗?

结语
这是前端工程化系列的第一篇文章,预计后续还有 10
篇的样子,个人精力有限,尽量周更。还是老规矩,如果对你有帮助的话点个👍和收藏吧❤️
参考文献
www.cnblogs.com/anayigeren/...
面试官:说说你对Git Rebase 和 Git Merge的理解?区别?
作者:lemon橙汁
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。