GIT——一个我一直也没有仔细学过的软件

笔记基于Git - Book,只记录原理,不会事无巨细的记录每一个命令的语法格式。

基础#

版本控制#

版本控制所做的就是记录代码随时间推移的每个版本,然后,我们可以在需要的时候退回到某个版本(比如新版本中的某些修改导致了安全风险),对比版本间的差异等等。

构建代码仓库#

mkdir gitlearn && cd gitlearn
git init
Initialized empty Git repository in /home/yudoge/tmp/gitlearn/.git/

untrackted 未跟踪文件#

Git会记录文件是否被改变,不过前提是你得指定该文件被GIT所跟踪,当你在一个Git项目下创建一个文件时,Git并不会记录该文件的任何信息,比如它是否被修改等。git status会列除这些未被跟踪的文件,并告诉你git add命令可以开始跟踪它们。

再运行status,这个文件已经不是未跟踪的状态了

to be comiited 待提交文件#

当你使用git add跟踪一个文件,代表这个文件将被git纳入版本控制的范畴。实际上,它存在于一个暂存区中。为什么要有一个暂存区?

想象我们正在写一个发送邮件的功能,我们会为实现这一功能创建很多个文件,并且为了将它们纳入GIT的控制范围以获得一些GIT独有的能力(比如代码写乱了可以复原成开始跟踪它之初的样子),我们会使用git add将这些文件放入暂存区(也就是开始跟踪)。但我们并不希望GIT将我们的每一次add都记录成为一个代码版本,而是这个功能开发完成才记录一个版本。所以暂存区就出现了。

git add会将我们的文件放到暂存区中,当我们觉得暂存区中累计的开发进度已经可以使得代码推进到下一个版本了,那么才将它们通过git commit提交成一个版本。

暂存区文件操作#

暂存区可以看作是距离上次提交的版本之间所做的累计修改,并且它们会被提交到下个版本中。那么使用暂存区可以获得GIT的哪方面加持呢?

修改已经添加到暂存区的文件,添加一个新行:

vim hello.py
print("Hello, GIT project!")
print("NEW LINE!")

查看仓库状态,我们可以看到,hello.py同时存在于改动待提交(Changes to be commited)和改动未暂存的(Changes not staged for commit)两个栏目中。因为暂存区里有之前的hello.py,它可以被提交,而现在GIT又跟踪到暂存区外的hello.py被改动了,和暂存区不一样,如果你现在执行git commit那么新的改动不会被提交,所以它在两个栏目中。

使用git diff还可以查看改动的位置

如果你希望提交的版本包含NEW LINE那行修改,那么可以再次git add hello.py,将这次改动存储到暂存区中。这里使用了git status -s查看精简的状态信息,A代表暂存区中有该文件,M代表当前文件和暂存区内的有差异。

可以通过git reset HEAD <file>来取消暂存的文件(将文件从暂存区移除)。

可以通过git checkout -- <file>取消该文件上次暂存到现在所做的修改,即还原到暂存区中的样子。实际上这个命令有些hack,它是通过切换分支会丢失未提交文件的原理实现的。后面会说到工作区。

提交#

使用git commit命令提交暂存区中的修改,该命令会推进代码库的版本。-m是此次提交的说明,用于说明该版本主要做了什么工作,比如修复了上个版本的哪些bug,添加了什么功能。

使用git log命令可以看到当前代码库的版本。

分支#

分支是版本控制工具的精髓。

我们日常的代码仓库中至少应该有两条分支,第一条是master主分支,它是项目能够稳定运行的版本,不应该轻易改动,而再有一个就是development分支,添加新功能在这个分支中添加,当新功能测试通过准备上线时,将development分支合并到master分支,可以理解为让进度被甩在后面的master分支追上development

下图中的testing你可以理解为development,它在master的前面开发新功能,等到它完毕,master会请求与它合并并追上它的进度。

分支基础——Git如何管理文件#

GIT会将每一个暂存的待提交文件存储为一个blob对象,该对象除了存储文件本身外,还存储了该文件的校验和(SHA1)。下图是一个README文件的blob对象

暂存区中只存储文件的校验和

当GIT进行提交时,先会计算每个子目录和子文件的校验和,然后保存为一个树对象。如下是包含三个文件的一个树对象

提交时会产生一个代码库的版本,这个版本被记录在一个提交对象中,这个提交对象指向了上面所创建的树对象,它具有自己的校验和和提交详情。

最后,整个提交过程产生了这样的结构:

所以,提交对象指向提交时创建的树对象,树对象指向暂存时创建的blob对象。

由于GIT需要记录代码库的提交历史,所以每一次提交(除了首次提交)都有一个父提交对象,父提交对象就是该次提交的上一次提交,如果你没有其它分支,那么所有提交对象会连成一个链表。

可以清楚的知道版本之间的演进记录。

那分支是啥啊#

上面我们了解了提交历史在GIT中怎么记录,那分支怎么记录??假设现在我们已经提交了两次,有了两个版本,当你使用git log时,你就会发现第二个版本上有(HEAD -> master)这个标记

先来解释master,这是创建git仓库的默认分支,分支指向所有版本中的一个,代表当前该分支所处的开发进度,从上面的讲解中大家也知道了分支就是为了在一个项目中提供几个不同的开发进度的能力(比如稳定版和开发版)。

HEAD则是一个指针,它的作用很简单,指向你现在处于哪个分支中

当你使用git branch testing创建了一个名为testing的分支后,GIT中具有这样的结构:

mastertesting在一个版本上,但当前还在master分支,并没自动切换到testing

推进testing分支!#

使用git checkout切换分支,可以看到现在HEAD已经指向testing了,我们再做的任何提交操作都是testing在提交,master不会向前推进。

git checkout testing
Switched to branch 'testing'
git log
commit e3d35b798297c91991caed4c9873702d30fe1d74 (HEAD -> testing, master)
Author: yhn <1355265122@qq.com>
Date:   Fri Apr 29 11:11:32 2022 +0800

    SECOND COMMIT

commit 11579eabf1a5e34d33ba6b6258c0889d314474af
Author: yhn <1355265122@qq.com>
Date:   Fri Apr 29 11:11:10 2022 +0800

    FIRST COMMIT
~

git checkout -b <branchname>可以切换到一个分支,如果分支不存在就创建。

使用testing分支提交一个新版本,可以看到testing分支的进度已经超过了master分支:

让master追上进度#

假设testing中在做一些新功能,这次提交后,它做完了这个功能并且测试显示一切都很稳定,那么是时侯让master追上testing的进度了。git merge命令可以合并分支,将testing中的修改合并到master

现在,master已经跟上了testing的进度

多分支操作#

假设你依旧在testing上做一些新功能,使用git log --oneline --decorate --graph查看当前的分支情况。

这时来了个突发状况,你必须去处理当前稳定版本中紧急发生的BUG。你需要先切换到稳定版本,并从稳定版本的代码位置切出一个新的分支hotfix来处理这个BUG。

testing分支的角度来看,现在仓库中的分支情况如下,testing做了新的改动,但还没与master合并,hotfixmaster在同等位置,并且一会儿它要修改之前的bug。

hotfix修改了之前的BUG并提交了一个自己的版本,现在版本库中不是一个简单的链表了,已经产生了两路分叉。master较为靠后,testinghotfix较为靠前并且它们都是从master衍生的,但它们的新增或修改的代码之间并没有什么联系。如下图,假设最下面的C3是testing分支。

现在,master可以轻易的与两个分支中的任何一个合并,因为这只需要将master指针移动即可,因为这两个分支都是由master衍生出来的。这种合并叫快进(fastforward)。

现在我们决定先将master快进到hotfix来解决燃眉之急,你也可以反着来。

现在通过向git log中添加--all选项,能看到整个版本链的情况,否则你看不到分支之间的情况。可以看到master被快进到了hotfix分支,但目前的testing分支已经不是由现在的master直接衍生的了,显然它们之间的合并要麻烦点。

由于hotfix已经没用,先删除。

我们尝试运行一下git merge testing,看看会出现什么情况。第一次是发生了冲突,这是因为之前的hotfix和我们的testing都改动了hello.py的同一行,GIT不知道应该留哪个。

但在正常情况下,hotfixtesting所改动的不太可能一样。

当你打开hello.py时,你会看到git帮你标注了哪里冲突了

这里我们决定将Add in testing branch这句删除,然后git add hello.pygit commit

已经成功,这次的合并不是简单的快进,而是一种三方合并,GIT会尝试将两个分支的内容进行整合,如果遇到冲突就提示你修复冲突。

现在我们可以把testing分支删除了

git branch -d testing
posted @   yudoge  阅读(41)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· winform 绘制太阳,地球,月球 运作规律
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示
主题色彩