某天完成了项目的一次开发后,准备开始第二次开发,但是如果直接在一次开发的基础上更改,我们的文件就是1+2,管理起来相当不方便。
前面*代表当前所处的分支
创建出来的分支,并不会改变我们当前所在的位置。
git branch branch1
git branch
git是如何判断我们所处当前哪个分支上呢?
(HEAD -> master, branch1) git是通过HEAD指向获取当前分支的。
将HEAD指向切换的分支
将工作目录恢复成当前分支的快照
简写命令:git checkout -b 分支名
之后提交一个文件,之后看日志,发现HEAD已经指向branch1了。再往后仔细看标注,就是master分支的日志了。
除此之外,如果是branch1分支,切换过去,只会显示该分支所有的文件。
切换master分支,则是它所有的文件。
在branch1分支下删除所有文件
git rm * -r
再切回去:
文件都在!因此各个分支是互不影响的!
首先有一次c1的提交,在c1的基础上还有c2的提交,这些操作都是在默认的master分支上进行的,在c2的提交上又创建新分支branch1。git如何判断在当前在哪个分支上,就是通过HEAD指针。当前HEAD指向master。
我们切换分支后,HEAD就指向branch1了。
我们再提交c3,此时branch1向前移动,并带着HEAD一块移动了。此时branch1的路线就是c1->c2->c3。
此时我们想操作master分支,就需要切换过去了。
之后再进行c4提交。此时master的路线就是c1->c2->c4。
假如我们开始开发,基础功能是c1提交和c2提交之间的内容,处理的是第一个版本的内容。
为了便于后面的开发,我们将其分成两个分支,一个master,一个branch1。
紧接着我们在分支branch1上开始开发,然后进行功能提升,进行第二个版本开发,提交c3、c5。
现在又出现了问题,第一个版本开发的时候,有一些小的bug。这个时候需要提交一些补丁,这可怎么呢?首先需要回退到c2上,这上面有第一个版本开发的时候所有的记录,这个时候我们实际不用回退。直接切换到master的分支,在这个分支修改bug并提交c4。这个时候就可以将c1->c2->c4的代码进行打包,替换线上的代码了。
第二个版本开发完成至c5提交。唯独缺少了c4的补丁,你可能会想到再创建一个提交修改bug。明显是不科学的,这样重复处理,是非常耽误时间和工作效率的。因此git就提供了一个很好用的功能,即合并分支。
模拟一下以上操作,新建一个文件夹。
c1提交
c2提交
git checkout -b branch1 创建分支并使HEAD指向新分支,-b就代表 branch
完成c3提交
切换回master打补丁,c4提交
--decorate 参数用来显示一些相关的信息(带有分支信息),如HEAD、分支名、tag名等
--oneline 校验和(哈希)
查看分支指向+哈希
以上显示不太明显,我们可以该命令
切换回branch1看看
我们继续提交c5
查看命令的完美组合 —> 包含了所有分支的提交,以树形图形形式展现。
--all 所有分支信息
--graph 打印出树形图
注意:在我们日常开发中最好必须有一个主分支,有一个主分支就利用我们对分支进行管理。比如平时打游戏,就会有一个主线任务和一个支线任务。然后支线任务也不会影响主线,实际上项目中主线就是项目的版本。默认情形下,大家都把默认分支master作为主分支。
git merge 目标分支
将目标分支的内容合并到当前分支
git merge branch1 将branch1分支合并到主分支master上来。
此时完成了合并,解决了第二个版本中第一个版本的bug,我们想让第二个版本上线,就打包c6代码即可。
其实最终合并关注3个点,一个是必须有共同父节点c2,其次是master末尾节点c4和brach末尾节点c5,将c4、c5实际整合成了c6,c6就完美解决了,第二个开发版本c2->c3->c5中需要修复第一个开发版本bug的问题了。
当master和branch没有形成分叉,依旧是处于一条路径线,当HEAD落后于所要合并的分支,将会形成快速前移
先初始化目录
进行两次提交
创建分支branch1,并切换过去,做第三次提交
git checkout -b branch1
git log --oneline --decorate --all --graph
切换到master,并与branch1合并
git log --oneline --decorate --all --graph
我们发现了奇怪的问题,master和branch1都跑到c3了。为啥成这样了, 因为快速前移,我们仔细观察上图,就有提示:Fast-forward
仔细观察上图发现合并前并没形成分叉,branch1只是在master的上开了一个分支。
master:c1->c2
branch1:c1->c2->c3
master上的提交都包括在branch1里了,这时执行的合并就和之前不一样了,不会出现新的提交,而是将master的指针往前移动了,同样HEAD会跟随master往前移。我们把这个过程称为“快速前移”。
实际快速前移在合并操作里是不友好的,我们打印log,它这没有任何信息(有关合并的信息)可以表示出来。
那如何解决呢?
禁止快速前移(可以commit记录描述为合并操作)
我们回退回去!
git merge --no-ff -m '这是一次合并操作' branch1 (要加描述)
初始化git仓库
创建并切换一个分支
修改a文件,并提交
切换成master主线,a.txt内容为空,因为提交也分先后,在master中只是新建了a.txt,在branch1里才修改了内容。然后切换回master,会重置工作目录。我们添加1.txt的内容。
提交它!
我们把branch1合并过来,发现冲突报错了。
我们打开a.txt
<<<<<<< HEAD master添加的内容,截止到分隔符
======= 分隔符
>>>>>>> branch1 从分隔符到当前,是branch1添加的内容
<<<<<<< HEAD 1 2 3 4 5 第二次修改 ======= a b c 第1次修改 >>>>>>> branch1想要解决冲突,打开文本,可将内容合并或删除后不需要的内容,再提交即可,这里我们只保留master添加的内容。
有的时候修改,不会产生冲突。
我们新建并提交一个b文件
添加内容,并提交
再开一个分支branch2,对b进行修改!再提交。
切换到master再合并
提示Fast-forward(快速前移),并没有产生冲突!
如上master第1次提交:新建文件并添加内容
再创建分支,修改文件中的内容
最后切换到master合并分支,合并之前,git会判断两个分支是什么关系?如果是祖先级关系,master和branch2处于同一条commit路径上(直接级祖先关系),就进行了快速前移。
✔️ master和branch2直接进行合并,不会引起冲突
如上上面的操作是,master第1次提交:新建文件并创建并切换分支,添加文件中的内容
再切换回master,修改文件中的内容,提交之后最后进行合并。这时候出现了分叉,则不会进行快速平移了,git进行如下的分支判断:
❌
分析master和branch1中的修改是否一致,如果一致合并将会成为一次空合并(因为内容完全一致,没有合并的需要)如果不一致,是否修改的同一个文件内容,如果是,产生冲突解决分支冲突:
① 手动解决冲突部分
② 解决完成后再次提交,会以这次提交内容为准
合并完成后,分支就没有任何的后期用途了,这时候我们需要手动删除分支,以防止自己词穷的时候出现重复命名分支的情况。
git branch -d 分支名称 -d -> delete
注意:HEAD所指向的分支,无法删除
还有一种情形也无法删除:
如果你的分支,从未合并:git branch -D 分支名称
切换回master,再删除分支,都失败了。提示从来没合并过的分支,git会提示你是否真的要删除一个分支。它防止我们删错分支,导致分支上的内容丢失。
这个时候就需要强制删除分支命令了。
git branch -D branch3
git merge --abort
修改文件内容
切换回master,再修改内容,提交合并
合并必然失败
可以取消合并!
git merge --abort
日志也不会提示进行过合并操作。
我们参考上面提示说明:git rm --cached <file> 将暂存区文件变为未追踪状态(删除暂存区或分支上的文件, 但本地又需要使用),这其实就是一种撤销
修改a.txt的内容
我们再把文件中的内容清空
从先从缓存区中拉取版本还原,如果没有再到版本库中拉取还原。参考廖雪峰老师的官网如下:
修改文件内容。
git reset HEAD <file>
修改a.txt
本想分开提交,但是一不小心全部放到了暂存区
如果提交的话,两个文件都得提交
那我们尝试 git rm --cached a.txt 删除暂存区或分支上的文件, 但将本地文件留存
我们发现,也无法提交,这里git会帮我们重命名了,完全不符合我们的需求。我们需求是仅仅提交b.txt。
我们还是回到原来的状态,把a.txt添加回暂存区
git reset HEAD a.txt
注意:当一个文件第一次进入暂存区实际上无需用reset命令的,直接remove就行,因为暂存区没有其之前的信息。
假设我们当前提交的记录写错了怎么整?请看下面的内容。
git commit -m '新建一个文件b,a文件待提交' --amend
注意:上次commit提交错后,不能再执行任何操作,必须紧接着它执行我们上面的修改提交信息命令才行。
我们再修改b文件
我们本想将a、b文件一起提交,结果只提交了a文件
我们把b文件也加入暂存区,再修正合并上次提交
我们可以补充记录!
撤销我们提交最新的一次提交
git reset HEAD^
如果撤销最新的两次提交
git reset HEAD^^
如果撤销最新的三次提交
git reset HEAD^^^
git reset HEAD^
可能有的编译器,会提示More ,是因为windows将其认为是换行符了,所有加上"…"即可
解决:
回归正题,看下日志
提交内容
撤销两次提交
注意:该提交并未丢失,可以通过哈希找回。
git reset HEAD~n n代表最近删除的条数
重置工作目录,丢失暂存: git reset --hard (尽量避免使用)
修改了a.txt
git reset HEAD^ --hard
并且发现一个问题,对a.txt的修改没有了。
因此尽量避免使用--hard,丢失的暂存,是不能找回的。
git reset --soft
保留工作目录,与原分支差异将放到暂存区
撤销回去
我们打开a.txt,也很容易发现,之前里写的内容,是不存在的,因为丢失的内容,是找不回的,因此建议尽量避免使用--hard。
git reset HEAD^ --soft
并且发现暂存区文件内容还在!因此它不会造成文件的丢失。
git reset --mixed(默认) 这是默认的,平时直接reset就是这种命令了。
保留工作目录,并且清空暂存区
git reset cc12a6c13af1f5b135f531617b0ce346bb8d46ca
但是如果没有打印日志,不知道哈希,如何回退呢?
git reflog
我们回到三次提交的日志。
git reset 4180e8e
可以用来撤销commit,但实质行为上并不是撤销操作,而是移动HEAD并且带上所指向的分支,重置HEAD及分支
即在HEAD之后的提交,因为当前不在任何分支上,就不会出现在工作目录中,起到撤销效果。
假设进行c1、c2、c3次提交,从c3回退到c2,实际上就是HEAD带上master一块移动到c2,此时c3就不在任何分支上了。因此打印log,就看不到c3了。如果想找回的话,即可通过reset重置回c3,即HEAD带上master一块移动到c3。
撤销除了reset,checkout也与它类似,但是两者也是不一样的,之前我们学习发现。checkout一般撤销的是对文件的修改,它前提是没有加入暂存区或者没有提交的内容。
如果我们用checkout完成上述分析,从c3回退到c2,实际HEAD指向了c2,而master没有变化,打印log的时候,一切照旧。因此我们才用checkout切换分支,因为checkout不会偏移master(分支)指针。
所以,签出指定commit,只会改变HEAD指向,并不会影响分支指向。
(后续待补充)