有趣的git
Git
这一篇文章,单独讲讲Git
。因为回去我问了一下小伙伴,大家都是GUI
操作GIT
,虽然并不影响开发,但一方面我们需要去知道GUI
到底做了什么事情,另外一方面就是你需要更细致灵活和自由的去操纵GIT
这是GUI
提供不了的能力,随心所欲的写GIT
真的是一件很帅的事情。
基础概念
在这之前还得说一下fork
和clone
,我们平时开发业务项目都是自己clone
把,但这是在给你了开发权限的情况。
而fork
是这种没有权限的情况,具体说一下这个流程把(昨天正好有人问到):当我们 fork
后就多了一个同名的项目,在自己的账号下,然后clone
到本地,去关联上游仓库git remote add upstream xxx
,当开发时,需要增加一个分支(如同名上游分支叫feat:xxx),你需要建立一个分支并设置追踪关系(这里你要确定一下你是rebase还是merage),git fetch upstream
、git upstream/xx
、git push origin xxx
,然后就开发改文件push到自己的分支,提PR或者MR,然后叫人做CR(Code review)就合了。
1. git remote
对于一个想了解Git的开发者来说,是一定知道本地仓库和远程仓库的。
而本地仓库与远程仓库的一系列联系,通常是通过git remote
,例如:git remote add
来添加当前本地仓库的远程仓库, 在这之后你的本地仓库
已经和远程仓库
建立了联系。
就比如说:你fork
了一个分支,本地就新建了一个master
分支去追踪(track)远程的 master
分支,此时你的master
和 远程的 master
是同名的,就会可以直接git push
2. git branch
git
的分支有很多的理论知识,但它们的本质都是为了将branch
从对应的主线分支分离出来,去做进一步的开发而不影响原始主线。而branch系列命令
是为了我们去将各分支进行一些管理和建立联系
,而Git
分支,其实本质上仅仅是指向提交对象的可变指针
。
同样branch也存在本地分支和远程分支,我们git branch -a
可以看到所有的包含远程分支和本地的分支,而git branch --set-upstream origin/branch
是与上游分支去建立追踪关系,此时可以直接git push
。
3.git commit
在每次本地工作完成后,提交代码后,都会做一个git commit
操作来保存当前工作到本地仓库(repository), 此时会产生一个commit-id
,这是一个能唯一标识一个版本的序列号。你可以对于这个版本号做很多的操作比如回退,反转等(有个重要的地方是你只需要前4位就可以完成操作
)。在使用git push后,这个序列号还会同步到远程仓库。
4.pr or mr
Pr和Mr
,都是在集成一个版本前的缓冲余地,他们的好处就是代码合并之前能做一个拦截,去做你任何想做的事情(比如审查,打回去等)。
git的结构
适合多人合作开发不同的功能模块,此时如果每个人都在其各自的分支上开发一个相对独立的模块的话,在每次release
制作时都需先将各成员的模块做一个合并操作,用于合并各成员的工作成果,完成集成。
5.工作区域
上图我们可以看到,git的工作区域分为:工作区,暂存区,本地仓库区,远程仓库区。
工作区-workspace:即我们项目的根目录,也是我们存放代码的地方。
暂存区-index/stage:在我们项目的.git目录下有一个index文件,是一个包含文件索引的目录树用于记录文件的变化。当我们代码有改动但不想push时,我们可以选择add到暂存区。我们工作区的代码在提交push/回滚reset时,也会优先选择暂存区的内容进行操作。
本地仓库区-repository:也叫本地版本库是.git目录存在的位置。当我们执行commit命令后,暂存区的目录树会写到版本库中(最终存储在分支branch)。这里安全的存放你提交到所有版本的数据,其中HEAD指向最新放入仓库的版本。
远程仓库区-remote:托管代码的服务器,可以简单的认为是你项目组中的一台电脑用于远程数据交换。
基础实战
好了每次感觉讲基础能力的时候都觉得自己其实理解不够。唔这里推荐个基础的练习网站。
说句后话,那些专门想玩的git的伙伴们,可以去了解一下其他指针,比如FERCH_HEAD
,MERGE_HEAD
,CHERRY_PICK_HEAD
等,他们分别作用于类似解决冲突,记录拉取,cheery-pick之类的事情,但我们这讲的是基础嘛,再加上我也没去了解过了,就不写了。
HEAD指针实战
HEAD
指针,用于记录当前工作的位置,可以指向commit
和branch
,但实际上是指向当前正在操作的commit
。
我们工作在某一个分支上,比如 main
分支。当指向main
时commit
提交后,main
指针和 HEAD
指针一起前进的,每做一次提交,这两个指针就会一起向前挪一步。
我们以checkout
来说,实际上我们的checkout
这个命令控制的是HEAD
指针.
我们平时git checkout feature/xxx
是切到当前feature/xxx
这个分支,此时的指针是这样的HEAD -> feature/xxx -> commit id(提交版本号)
以下图为例:
git checkout overHere
HEAD -> overHere -> C1
可以看到此时的
overHere指针和HEAD
指针分离惹。
git checkout c1
HEAD -> C1 <- overHere
如果要切回去就git checkout overHere
切回去后我们试试提交,此时branch
指针和head
指针,会同时移动
git commit
唔?那有啥用那?
当 HEAD
指针直接指向提交时,就会导致 detached HEAD
状态。在这个状态下,如果创建了新提交,新提交不属于任何分支。相对应的,现存的所有分支也不会受 detached HEAD
状态提交的影响。
简单的说我们有时候在排查复杂的问题的时候,我们可以直接checkout
到你觉得可能有问题的版本号去修改,detached HEAD
会保护你现有分支不受影响,测试完了不想保存直接 checkout
到其他地方,可以放弃修改。想保存修改,可以创建一个 git checkout -b <new-branch-name>
新分支保存。
还有在你回滚一系列操作的时候你可以HEAD
、HEAD^
、HEAD~4
,分别代表的是当前版本,上一个版本,上4个版本。比如:git reset HAED^
表示的就是回滚到上个版本。
暂存区实战
怎么讲那,上面简单提到了暂存区但那个概念的东西有点虚无,但我们可能平时对于它的了解只在于git add,存到暂存区然后push,然后mr。但其实实际上我们平时在一些GUI工具
中,对代码的比对就跟它息息相关,这里我只简单讲一下,可能是因为我觉得在现在一些GUI
中做得更好把。
比如我们现在:git add
了一下文件到暂存区中。我们就可以通过下面的命令去对暂存区做一些比对和修改。
命令 | 作用 |
---|---|
git diff | 工作区 vs 暂存区 |
git diff head | 工作区 vs 版本库 |
git diff --cached | 暂存区 vs 版本库 |
而对于一些暂存区回滚操作来说:
命令 | 作用 |
---|---|
git reset --soft | 暂存区->工作区 |
git reset --mixed | 版本库->暂存区 |
git reset --hard | 版本库->暂存区->工作区 |
回滚
git reset --hard <commit-id>
这种模式大家熟悉把,但它会将暂存区工作区版本库全部都回滚到你指定的版本。
而reset其实有三种模式
- –hard 回滚三大区
- –mixed 回滚版本库和暂存区(默认模式)
- –soft 只回滚版本库
说说我经常会遇到的一个场景
假设我提交了一个文件,然后我突然发现有bug,我这时候觉得就改一句话的事情再提代码一次很恶心(显得自己提交记录很多)。就可以(HEAD^表示指向上个版本,HEAD~5表示指向上面5个版本,也可以git reset --soft <commit-id>前四位数就行
)
git reset --soft HEAD^
git add.
git commit -m "修改bug"
反做
它在什么场景用?比如,我们commit了三个版本(版本一、版本二、 版本三),突然发现版本二不行(如:有bug),想要撤销版本二,但又不想影响撤销版本三的提交,就可以用 git revert 命令来反做版本二,生成新的版本四,这个版本四里会保留版本三的东西,但撤销了版本二的东西。
恢复
reflog,可以分为两个单词,Reference log
,引用日志。当本地仓库中的引用发生移动时,reflog
都会记录下这个移动的行为。
在这先放个很丑陋的log,说说含义,前缀移动后的commit哈希
,HEAD@(23)
移动的原因,移动的原因
等。
在上面那副图中,引用指的就是 HEAD 指针;引用的移动就是 HEAD 指针的移动;通过记录操作的命令,操作的次序,操作的内容,操作后的提交信息,来记录这整个操作引起的指针移动行为。
看看实战:
我当时出于某种原因,删除了feature/flow-test
这个分支,属于这个分支的指针都不见了,但我发现我在上一行从这个分支离开切到了另外一个分支。
此时我从引用日志里找到了,就可以恢复这个分支的内容(我还得删一次!!!)
莫名其妙的提交丢失
有时候,我们会在一些误操作后,发现我们的提交竟然不见了,也许它变成一个"dangling objects(孤立提交)"
,指的就是没有任何分支指针或者头指针指向他,等待git去回收,这种"孤立提交"
。一般来说并不会吗说回收,我们可以通过
它应该是我们找回git提交的最后途径了,就像这样,当然我们也可以过分一点直接git fsck gc
直接给它清理了,这样后面的人就什么都找不到了(开玩笑的,本来git gc也会在超过数量后回收)。
git fsck
总结
总算把这些基础的东西,写完了后面应该会写一些git flow三种流,以及CI/CD,以及一些git小技巧。
题外话,今年面试确实有点难约,可能大伙需要再等个1个月看看,学习学习准备准备,不会有人觉得讲git也算八股文把面试也不考这个是不是自己不懂的都叫八股文啊。
转载自:https://juejin.cn/post/7195854941440639036