作为一个合格的程序员必须知道Git这款版本控制工具,如果不知道那真的可以淘汰了。我们每天都在和代码打交道,每次的需求变更我们都会要去修改代码,如果没有一个好用的代码版本控制管理工具的话,我们每次就要手动备份和拷贝文件来回修改文件,如果项目过大并且参入的开发人数更多,很有可能把整个项目的源代码搞乱掉。怎么解决这些问题呢?既然我们都是程序员,为何不自己写一个文件版本控制管理功能呢?

如果年龄大一点的程序员应该知道SVN这款代码版本管理工具,我个人知道这款工具的时候是在2017年的我老师给我上课的时候一些示例代码使用SVN进行管理的,然后得知写程序还需要代码管理,当时我年龄应该是17岁左右,对代码管理一无所知,现在我用Git已经快4年时间了,所以我准备写一篇文章介绍一下Git

Apache Subversion 通常被缩写成SVN,是一个开放源代码的版本控制系统,Subversion 在 2000 年由 CollabNet Inc 开发,现在发展成为 Apache 软件基金会的一个项目;如果本地没有代码可以从远程服务器端拉取一份代码到本地,架构是一种集中式的代码管理工具,所有代码集中在中央仓库里面保存,开发者需要在自己电脑上开发,开发完成之后将代码推送到集中仓库,这样其他开发者也能同步到代码。缺点也很明显这也是分布式系统里面最大设计问题:严重的依赖服务器端,当服务器端无法使用的时候,版本控制也就无法再使用了;SVN算是一个集中代码版本控制工具了,所有代码会集中存储在中央仓库里面,开发者需要就去自取代码,缺点很明显就是没有整个代码历史版本,而只要当前的版本。所以看到这个架构联想到现在分布式架构很多也是依赖中央服务器,这也是分布式系统需要解决的问题。

什么Git

到目前为止,当今世界上使用最广泛的现代版本控制系统就是Git了,Git是一个非常成熟的项目版本控制软件,最初由是用来管理Linux 操作系统内核的代码的,也是著名创建者 Linus Torvalds 于 2005 年开发,目前几乎大部分软件公司都在使用它。

Git相比SVN是真正意义上的分布式架构,每个开发者的电脑都会存储代码仓库,在Git中每个开发人员的代码工作副本也是存储库可以包含所有更改的完整历史记录,这样也不需要依赖于远程的中央仓库负责;Git可以在任何时间点,把文档的状态作为更新记录保存起来。因此可以把编辑过的文档复原到以前的状态,也可以显示编辑前后的内容差异。而且,编辑旧文件后,试图覆盖较新的文件的时候即上传文件到服务器时,系统会发出警告,因此可以避免在无意中覆盖了他人的编辑内容,这样用户体验就非常好了。

Git架构

在Git架构中每个数据库有3种区域分别为:工作区、暂存区、.git仓库;这个3个区域也对应着3种文件状态,工作区里面是已修改文件,暂存区的文件暂存索引状态,然后就是存储状态已提交状态,如下图:

Git分为远程数据库&本地数据库: 本地数据库方便单人开发使用,所有的版本信息会存储在本地数据库中;而远程数据库是方便多人本地数据库同步使用的,建立的共享数据库。本地数据库我们会记录📝我们本地项目代码做的修改,如果这是有其他人需要同步修改的记录,我们只需要把本地数据库记录同步到远程数据库,其他人拉取这个记录即可,同理别人也可以这么做,并且远程数据库和本数据库会辅助开发者修复不同的记录冲突问题。

每次修改的文件,如果内容修改那么会生成一个版本号和快照的方式,提交修改记录会保存在本地暂存区中,提交是以时间顺序排列状态被保存到数据库中的,凭借该提交和最新的文件状态,就可以知道过去的修改记录以及内容,如果没有被修改的文件则直接被链接这样就可以不用占用多余的内存。

每次针对修改文件还可以备注填写一些消息,方便后面查阅版本之间的差异性,也是所称的commit信息;每次提交记录系统会根据修改的内容计算出没有重复的40位英文及数字来给提交版本记录号。

创建数据库

这里我把存储代码的仓库叫作数据库,后面我也会这么称呼;如果需要创建一个数据库我们可以使用一下命令创建:

git init

这样我们就可以把本地一个文件夹📂创建成为一个数据库,另外一种方式就是通过已经现有的数据库拉取到本地来,如下:

git clone https://github.com/auula/owl

这样我们就可以从远程的数据库拉取到对应的代码,然后我们就可以在本地使用这个数据库了。如果出现无法拉取远程数据库情况,说明可能你本地Git工具没有配置所有者信息需要执行下面命令:

git config --global user.email "[email protected]"
git config --global user.name "Leon Ding"

如果需要和远程仓库进行免密访问的话还需要添加ssh公钥,我们需要执行下面命令生成:

ssh-keygen -t rsa -b 4096 -C "[email protected]"

然后一路回车即可生成得到公钥和私钥,这时去查看cat ~/.ssh/id_rsa.pub即可到公钥信息并且保存的远程数据库的配置中,然后使用ssh -T [email protected]可以测试和远程数据库是否能正常通信。

操作暂存区

暂存区都是索引着我们修改并且需要提交的文件,当我们在本地数据库操作一个文件例如下命令:

touch readme.md
echo "Hello Git" >> readme.md

这是我创建一个新的文件并且向里面写入了一段内容,这时我需要提交修改的话使用的git add readme.md即可把文件创建快照保存到暂存区,使用git status可以查看文件状态信息,如果需要提交这个文件可以使用git commit -m "commit message"就能给本次提交创建一个提交版本信息保存到本地仓库中,使用git push即可将本次提交到远程仓库中保存,流程如下图:

远程仓库

当前我们的提交是存储在本地仓库中,如需要将提交记录同步到远程仓库中,我们就需要一个远程统一的仓库地址,这里可以是自己创建私有远程代码仓库数据库,也可以是Github这种公共免费的代码托管平台,使用如下命令就可以设置远程仓库地址:

# 设置远程仓库 origin相当于一个别名
git remote add origin https://github.com/auula/kalasa.git
# 推送本地版本库到远程
git push origin main

如果想看有多少远程的仓库链接,可以使用git remote -v查看远程的仓库列表,更多详细信息可以查看:https://www.atlassian.com/git/tutorials/syncing

上面介绍如何把本地数据库同步远程数据库上,下面开始介绍如何把远程仓库的修改记录同步到本地仓库中,同步到本地仓库中很简单,只需要拉取对应的远程仓库即可:

# 拉取指定远程链接的指定分支
git pull origin master

git pull命令首先运行git fetch,它从指定的远程存储库下载内容,然后git merge执行a以将远程内容 refshead合并到一个新的本地合并提交中,这里我们大多数使用的git pull命令比较多,git fetch只会同步远程的记录,拿取到本地中,不会执行更多的操作。

分支操作

分支是版本控制中很重要的一个概念,假设我们开发一个项目的过程中,有很多新的需求,修复bug,添加一个新功能,一个主体功能,这就和装修房子一样可以粉刷墙壁也可以铺地板,都是同时进行的,如下图:

在Git中也有分支概念方便开发者去管理项目,在Git的设计中有一个很重要的概念叫head指针,一般都指向当前分支的最新一次更新记录,移动这个指针就可以实现不同的分支和版本切换了操作了,创建一个分支命令很简单使用git branch feature1这样就可以从当前分支创建一个名字为feature1的另一只分支,切换分支也很简单使用git checkout feature1就可以切换到指定的分支了。

如果在新的分支里面添加了内容,再切换到原来的分支,然后想直接删除新分支的话,会触发一个错误,错误就是不能直接删除修改过的分支,需要将其合并到当前分支才可以删除,或者强制删除,如下图:

合并只需要回到上一个主分支执行git merge feature1操作即可合并完成,如果有冲突需要手动合并代码块,没有则会自动重定向head指针到feature1上并且设置为主分支。

如果想把新建的分支推送到远程数据库中,使用git push origin feature1即可,想重命名也可以直接在末尾加入git push origin feature1:f1远程仓库分支就重命名为f1了。

在开发我可能会多次操作分支并且提交记录,这时很难通过当前的分支名来判断自己处于那条记录,这时Git就提供日志形式查看记录可以使用git reflog来查看具体提交修改记录日志,如果想查看详情可以使用git show d871cc3来查看详情。

上面提到git merge会将指定的分支合并到当前分支上并且保存修改记录,这样我们可以跟踪代码状态和工作流,对项目可以有可跟踪性,能观察到历史发生了什么;与git merge有相同合并功能命令就是git rebase,但是主要区别就是rebase会把当前分支的修改复制到目标分支最后一次提交上,而是直接显示一条直线的形式;merge更关注的历史提交记录,rebase关注的开发的过程上面。

⚠️绝对不要🙅🏻‍♀️:在公共主分支上使用rebase!这样会带来无法观察历史修改记录的情况!!!

在实际开发中肯定会遇到不同的开发者在不同的分支修改代码的情况,然后需要协同工作这时就需要去合并另外一个分支的代码,但是这种情况会出现同一个文件出现了文件内容不一致的情况,就需要手动去修改合并了,一般会用到图形化比较工具git mergetool来查看文件之间的差异,当然现在也有很多工具可以做对比,这里就不细说了。

历史回滚

实际开发过程中需求是不断变化的,例如新加的特性测试了一下觉得不好,又要回退到老的版本,这时Git的回滚功能就派上用场了,可以在日志找到对应日志编号就可以通过之前我写的head指针重置到某个位置上,从而到达版本回滚的效果,如下图:

如果想要回滚到上传的历史提交可以使用如下命令:

git reset master^

上述命令就是要求在主分支向前回滚一次提交,这里的^符号表示要向前多少次。但是如果要回滚到之前很久的版本这样写就很麻烦,如果想回滚之前指定次数可以使用git reset master~5表示回滚到前5次的版本,Git还提供另外一种方式就是通过版本号来回滚,使用git reset b9f3396回滚到指定的版本。

当然git reset还可以指定其他参数,参数--sort回到之前的版本并且会保存当前暂存区的提交的内容,应用场景如果需要取消提交并且把commit放到暂存区就用这个;参数--mixed就是默认的模式,会把暂存区的文件丢弃掉但是工作区目录这些文件还存在;参数--head模式会丢弃暂存区并且丢弃工作区文件,两个地方都会清空。

上面介绍的git reset模式是一种移动head指针的方式来操作变更记录的方式进行的,

git reset命令相对于的是git revert命令,这个revert命令不会直接回到之前提交记录上,而将记录放到当前工作区上,并且创建一次新的提交,也就是这次回滚操作也被默认创建一次commit记录,建议在公共的主分支使用revert操作。

小 结

Git 是一个内容寻址文件系统,听起来很酷。但这是什么意思呢? 这意味着,Git 的核心部分是一个简单的键值对数据库(key-value data store)。 你可以向 Git 仓库中插入任意类型的内容,它会返回一个唯一的键,通过该键可以在任意时刻再次取回该内容。

上面是一个Git在官方介绍里面说是一段话,也可以看成一个分布式的KV存储数据库,如果想要研究分布式数据库的可以看看Git内存的设计与实现;本文我只写一些我个人使用的到一些命令和功能介绍,想把Git玩精通需要时间的陪伴。

便宜 VPS vultr
最后修改:2023 年 07 月 05 日
如果觉得我的文章对你有用,请随意赞赏 🌹 谢谢 !