git 基本原理 与 常见case 处理方法

git 基本原理 与 常见case 处理方法

Alt text

现在的项目开发中,基本都是多人参与,这就需要版本控制系统来管理我们的项目了。和大多数事物一样,git 诞生于一个极富纷争和大举创新的年代。

0. 简史

Linux 内核开源项目有着为数众广的参与者。 绝大多数的 Linux 内核维护工作都花在了提交补丁和保存归档的繁琐事务上(1991-2002年间)。 到 2002 年,整个项目组开始启用一个专有的分布式版本控制系统 BitKeeper 来管理和维护代码。 
到了 2005 年,开发 BitKeeper 的商业公司同 Linux 内核开源社区的合作关系结束,他们收回了 Linux 内核社区免费使用 BitKeeper 的权力。 这就迫使 Linux 开源社区(特别是 Linux 的缔造者 Linus Torvalds)基于使用 BitKeeper 时的经验教训,开发出自己的版本系统。 他们对新的系统制订了若干目标:

  • 速度
  • 简单的设计
  • 对非线性开发模式的强力支持(允许成千上万个并行开发的分支)
  • 完全分布式
  • 有能力高效管理类似 Linux 内核一样的超大规模项目(速度和数据量)

自诞生于 2005 年以来,Git 日臻成熟完善,在高度易用的同时,仍然保留着初期设定的目标。 它的速度飞快,极其适合管理大项目,有着令人难以置信的非线性分支管理系统(令人窒息的分支管理)。

1. 基本原理

1.1 直接文件快照概念

Git 和其它版本控制系统的主要差别在于,Git 只关心文件数据的整体是否发生变化,而大多数其它系统则只关心文件内容的具体差异。

Git 通过指纹字符串来时刻保持数据的完整性。指纹字符串是 Git 使用 SHA-1 算法,通过对文件的内容和目录的结构计算出的一个 SHA-1 哈希值。这个字符串是由 40 个十六进制字符串组成,如下,我们经常看到: 
696503be5f0630b43fce06cca01a9426c683bfb9,就是我们的 commitId。

1.2 文件的三种状态

git 在管理项目时,项目中的文件存在如下三种状态:

  • 已提交(committed)
  • 已修改(modified)
  • 已暂存(staged)

Alt text

我们项目的文件都处于 working directory,当文件进行了修改,文件的状态就会变为 modified。通过 git add . 的命令,文件就会被提交到 staging area,变为 staged 状态。最后通过 git commit 命令,文件就会被提交到 git directory,文件状态就更新为 committed 。以上,就是 git 操作一个最基本的流程,以及这个过程中的状态变化。

1.3 提交

例如,项目的根目录下有三个文件,进行第一次 commit ,git 仓库中会有这样的结构产生:

Alt text

扫描二维码关注公众号,回复: 17736 查看本文章

我们会看到上图这样一个对象的链式结构:它们各自都代表什么呢?三个 blob 对象是三个文件的快照块,tree 对象记录目录树及其内容,最后有一个包含了指向 tree 对象的索引和其他提交信息元数据的 commit 对象。

我们提交几次代码后,就会出现这样的链:

Alt text

由 commit 对象构成的链式结构,每个 commit 对象指向了项目文件的快照。那么,千呼万唤始出来,这个链其实就是我们 《1.4》要分析的分支了。

1.4 分支

分支到底是什么呢?其实,本质上,Git 中的分支仅仅是个指向 commit 对象的可变指针。默认使用 master 作为分支的默认名字。每一次提交,这个指针都会向前移动,指向最后一次提交对象,如下图:

Alt text

创建分支 
那么,到现在我们就应该知道,git 创建一个新的分支,其实就是创建了一个新的分支指针。例如,我们新建 testing,如下图:

Alt text

切换分支

Alt text

如上图,我们会看到除了 master 和 testing 还有另外一个指针,那就是 HEADHEAD 指针的指向,代表了我们当前所在的分支。例如上图,HEAD 现在指向 master,那么当前的分支就是 master 分支。切换分支,就是改变 HEAD 指针的指向,例如下图:

Alt text

现在,我们就把分支切换到 testing 分支了。 
切换分支之后,我们可以继续提交更新文件,就会出现下图这样结果:

Alt text

看到这个图,大家就应该知道我们后面会讲什么了,那就是:—–

分支合并 
我们来看一下图这样的情况:

Alt text

如图,我们项目的 git 管理情况,存在三个分支,分别是:masterhotfix 和 iss53。这个对应怎样的一种场景呢?例如:master 是我们项目的主分支,我们现在接到一个新的需要要开发,就切了分支 iss53,并且进行了一次 commit 。突然,发现一个紧急的线上问题,我们要修复,所以我们切回了 master 分支,然后又切出了一个 hotfix 分支,切到 hotfix ,修复完问题之后,我们进行了一次 commit。要提交上线了,那么我们就要合并分支了~~ 
操作:

1.$ git checkout master
2. $ git merge hotfix

结果:

Alt text

hotfix 与 mater 进行了合并了。这是最简单的分支合并情况,下面来个复杂一些的,我们回到 iss53 继续开发。。。。。。。。项目变成了下面这样:

Alt text

现在分支的合并就不会像上次 hotfix 那样直接合并移动指针那么简单了。 
因为你的开发历史在更早地方就开始分叉了。看下图,master 分支的当前提交对象(C4)并不是 iss53 分支的直接祖先,git 需要做额外的处理。git 会用到两个分支的末端(C4 和 C5)以及它们的共同祖先 (C2)进行一次简单的三方合并计算。如下图红框标出的要进行合并的三个提交对象:

Alt text

合并后的结果是重新生成一个新的快照,并自动创建一个新的提交对象 C6 指向这个快照。这个提交对象会有两个祖先(C4 和 C5)。

Alt text

这样,我们的分支就算合并完成了。分支的改动到合并到了 master,iss53 如果不在使用,就可以删除掉了。

1.$ git branch -d iss53

以上是 git 的一些基本原理,下面我们会讲一个好用但有时又让人害怕的命令git reset。 
git reset命令充分利用到了上述的 git 基本原理,下面我们通过学习分析git reset的用法与原理,来加深一下对 git 基本原理的理解~~

2. git reset 的原理

2.1 名词解释

image

  • working directory(git 的工作目录)

  • staging area(暂存区域)

  • git directory(repository)(本地仓库)

  • HEAD (当前分支头指针位置)

2.2 git reset 常用来干什么呢

  • git reset 之前

Alt text

  • git reset 之后

Alt text

可以修改当前 Head 指针的位置。

我们常用 git reset commitId 回退到之前的 commitId 的状态,就是将当前分支的 HEAD 指针进行了移动,但是,git reset 一般常用的参数会有三种,这三种参数产生的效果是不同的:

(1)--mixed(default)

Alt text 
上图是 --mixed 的效果。图示,使用 --mixed 回到某个 commit(例如CommitId2)的时候,HEAD Repository 与 Staging Area 的状态是一致,与当前这个 CommitId2 的状态保持一致,但是,Working Direcotry 还是之前 CommitId3 的状态。

(2)--soft

Alt text

上图是 --soft 的效果。图示,使用 --soft 回到某个 commit(例如CommitId2) 的时候,只有 HEAD Repository 回到了CommitId2的状态, Staging Area 和 Working Direcotry 会与 CommitId3 的状态一致。

(3)--hard

Alt text

上图是 --hard 的效果。图示,使用 --hard 回到某个 commit(例如CommitId2) 的时候,HEAD Repository 、 Staging Area 和 Working Direcotry 的状态是一致,都与 CommitId2 的状态一样。也就是说,刚刚的一些修改操作,一些保存到 Staging Area 的修改,都会随着你的 --hard而消失的。

3. 常见 case 处理

3.1 删除远程仓库的某次错误提交

case one: 
假设你有 3 个 commit 如下:

1.commit 3
2.commit 2
3.commit 1

其中最后一次 commit 3 是错误的,那么可以执行:

1.git reset --hard HEAD~1

此时,HEAD is now at commit 2

然后可以使用 git push --force 将本次变更强行推送至服务器。这样,在服务器上的最后一次错误提价也彻底消失了。

Pay Attention:此类操作存在风险因素,例如:在你的 commit 3 之后,其它小伙伴又提交了新的 commit 4,那么你强制推送之后,小伙伴的 commit 4 也会跟着一起消失了。

3.2 修改上次提交的代码,做一次更完美的 commit

case two: 
假如你上次提交的代码有些问题,不够完美,希望做一次更完美的 commit,且此时有很多代码你是出于修改状态的,你可以按如下流程做: 
(1) git reset commitId (注: 不要带 –hard) 到上一个版本 
(2) git stash,暂存修改 
(3) git push --force,强制 push,远程的最新的一次 commit 被删除 
(4) git stash pop,释放暂存的修改,开始继续修改代码 
(5) git add . -> git commit -> git push 完成新的commit提交

4. 常用 git 技巧

(1)修改最后一次提交 
有时候我们刚提交(git commit)完,会发现漏掉了几个文件没有添加,又或者是 commit 的信息填写有误。想要撤销刚才的提交操作,可以使用下面的命令: 
git commit --amend 
完整的例子:

1.$ git commit -m 'initial commit'
2.$ git add forgotten_file
3.$ git commit --amend -m "new commit des"

(2)别名的使用 
习惯使用 git 命令后,往往会有 “偷懒” 的想法,别名的使用可以让你少敲几个字母,提高效率。(当然,前提是你记得自己的别名,并且熟练使用它~~),如下:

1.$ git config -- global alias .co checkout
2.$ git config -- global alias .br branch
3.$ git config -- global alias .ci commit
4.$ git config -- global alias .st status

这样配置之后,以后的 git co 就是 git checkout 了,以此类推,等等。。。 
别名的使用不限于上面的命令,你可以根据自己的习惯,去配置更多的别名~~

End

饮水不忘挖井人~~膜拜一下大神 Linus

Alt text

ps:推荐大家看一下他的公开课,网易应该有,大神的思想还是非常与众不同的~~

猜你喜欢

转载自blog.csdn.net/tangxiaolang101/article/details/79929208
今日推荐