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