git 工作区
git系统在工作时有多个区域。在IDE写好代码,什么都不做,代码位于工作区
,使用add命令将代码保存到暂存区
,使用commit命令将暂存区代码提交到本地仓库
,本地仓库的代码可以使用push命令将代码推送到远端仓库
。这是一个git系统保存代码的完整流程。在这个流程中,存在的多个区域如下:
常用命令
- branch 分支管理
- add 保存工作区
- commit 保存暂存区
- checkout/switch 切换分支
- merge 合并分支
- rebase 变基
- log 提交记录
- reflog 所有操作记录
- checkout/restore 丢弃工作去修改
- reset/restore 丢弃暂存区修改
- stash 暂存工作区内容
- tag 标签
- rm 删除分支
branch 分支管理
git 分支管理
列出分支: git branch
列出所有分支:git branch -a
删除分支: git branch -d dev_branch
add 保存工作区
将修改添加到暂存区
git add file
git add -U 中所有跟踪文件中被修改过或已删除文件的信息添加到索引库
git add -A 表示把中所有跟踪文件中被修改过或已删除文件和所有未跟踪的文件信息添加到索引库
commit 保存暂存区
将暂存区的内容添加到本地仓库
git commit -m message
message 是一句简短的关于该提交的介绍
切换分支
切换分支
:
git checkout branch_name
git switch branch_name
新建分支
:
git checkout -b new_branch_name
git switch -c new_branch_name
查看提交记录
查看当前分支的提交历史
git log
git log 会按时间先后顺序列出所有的提交,最近的更新排在最上面。提交记录可能是来自不同的分支,git log是按照时间前后来排列,并不能直观列出各分支的提交。下面可以一些辅助参数可以让提交按分支直观展示。
# 图形化显示当前分支的提交日志
git log --graph --oneline
# 图形化显示当前分支的提交日志及每次提交的变更内容
git log --graph --patch
# 图形化显示所有分支的提交日志
git log --graph --oneline --all
# 图形化显示所有分支的提交日志及每次提交的变更内容
git log --graph --patch --all
git log --all --decorate --oneline --graph
合并
用于将另一个分支合并到当前分支。
从两个分支的同一个公共节点开始,一直到最新,全部都合并到当前分支。
git merge branch_name # 将branch_name 分支合并到当前分支
merge 合并特征
merge不会破坏合并的两个分支的提交记录,并且会新建一个commit用来记录当前合并这个动作
merge合并会将另一分支上所有的commit都合并到当前记录中,并按照时间前后排序。使用git log查看可以看到两个分支所有的log。
merge合并分为两种:
- 快进合并
- 非快进合并
快进合并:
当前的分支和另一个分支没有内容上的差异,当前分支的每一个提交(commit)都已经存在另一个分支里了,git 就会执行一个“快速向前”(fast forward)操作
git不创建任何新的提交(commit),只是将当前分支指向合并进来的分支
➜ git_test git:(master) git merge test_two
Updating 8a85b5c..635a041
Fast-forward
test_two.txt | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 test_two.txt
头结点指向master,master和test_two都是最新的节点。
commit c54e51c592d96461ae05f4fdba60656b1b6df7df (HEAD -> master, test_two)
Author: “liinui” <“jinkui.li@aabgng.com”>
Date: Thu Aug 19 08:56:22 2021 +0800
test_two.one
非快进合并:
两个分支在同一个节点的基础上都做了修改,合并之后会产生一个新的节点(新的commit,并且自动生成一个名字),两个分支都合到了一起。
git merge test_one 非快速合产生一个新的节点,将master和test_one内容合在一起。非快进合并就普通的合并。
* 8d2f27f (HEAD -> master) Merge branch 'test_one'
|
| * 490bd8f (test_one) two
* | 4858021 master:3
|/
* 19970a8 fix conflict
不想要快速合并,那么我们可以强制指定为非快速合并,只需加上--no-ff参数
git merge –no-ff test
优缺点:
快速合并:
优点:速度快,没有文件的操作,仅仅是指针操作
缺点:合并记录不清晰
非快速合并:
优点:非快进式合并会生成新的提交,可以让提交历史更加的清晰
缺点:速度慢
rebase 变基合并
rebase 合并分支
git rebase master # 将master的代码合并到当前分支
rebase和merge都是合并分支的一种方法,但是和merge的动作行为却不一样。rebase是待合并分支的提交记录在本分支上重新创建一遍,完成两个分支的合并。这种动作是有破坏性的。
优点
:可以获得更清晰的项目历史,会产生完美线性的项目历史记录,你可以在 feature分支上没有任何分叉的情况下一直追寻到项目的初始提交。
缺点
: 对分支的原状态具有破坏性,被合并分支会丢失提交的上下文。
rebase的使用需要遵循 Rebase黄金法则
: 永远不要在公共分支上使用它
rebase和merge比较:
变基是将一系列提交按照原有次序依次应用到另一分支上,而合并是把最终结果合在一起。
变基:将两个分支的提交变成一条线性节点
合并:保留两个分支的提交记录,增加新的节点
查看记录记录
查看 git 所有操作命令
git reflog 所有分支的操作
git reflog master master分支的操作
丢弃工作区修改
丢弃工作去的修改。对暂存区和版本库都不起作用
git checkout -- file
git restore file
丢弃暂存区修改
将暂存区的内容返回到工作区
git restore --staged <file_name> 将暂存区的修改重新放回工作区(包括对文件自身的操作,如添加文件、删除文件)。此时工作区显示有文件未被追踪。
暂存区的修改撤销到工作区。
对于使用git add -A 从工作区提交到暂存区的命令可以再次reset到工作区。此时工作区显示有文件未被追踪。
git reset HEAD file
删除分支
从git分支上删除文件,取消git管理。该命令会同时删除文件。如果想不删除文件而只是取消git对文件的跟踪,使用.gitinore 忽略该文件。
git rm file
git 标签
git 标签
Git 可以给仓库历史中的某一个提交打上标签,以示重要。 比较有代表性的是人们会使用这个功能来标记发布结点( v1.0 、 v2.0 等等)
列出 标签 git tag
创建标签 附属标签 git tag -a -m 'v1.4'
轻量级标签 git tag -m 'v1.4'
stash 暂存修改
git 暂存修改
在A分支上做的修改,没有保存切换到B分支。这是A分支的修改也会带到B分支,如果在B分支提交了修改,那么A分支的修改清空,B分支会有一个commit记录。
将内容暂存,包括工作区和暂存区都可以:
git stash git stash push
将暂存的内容恢复
git stash apply
查看暂存过的记录
git stash list
强推
如果远程主机的版本比本地版本更新,推送时Git会报错,要求先在本地做git pull合并差异,然后再推送到远程主机。这时,如果你一定要推送,可以使用--force选项。
git push --force origin branch_name
git的后悔药 reset
git 撤销命令简介
git 撤销命令 reset
git reset 可以回退git的版本,将git的记录回退到某一次commit之后的状态。
git reset [--soft | --mixed | --hard] [HEAD]
git reset 有三个参数,不加参数默认使用 --mixed
hard 版本回退到commit之后的状态,在重置HEAD 和 branch 的同时,重置暂存区和工作区的内容,也就是丢弃掉暂存区和工作的修改。
soft 不会重置工作区,把版本回退带来的改变放在暂存区,比如版本新增的代码,减少的代码都在暂存区。直接commit还能恢复到reset之前。
mixed 将暂存区修改回退到工作区,将reset导致的改变也放在暂存区。
使用模式区别
reset三种模式区别
区别:
--hard:重置位置的同时,直接将 working Tree工作目录、 index 暂存区及 repository 都重置成目标Reset节点的內容,所以效果看起来等同于清空暂存区和工作区。
--soft:重置位置的同时,保留working Tree工作目录和index暂存区的内容,只让repository中的内容和 reset 目标节点保持一致,因此原节点和reset节点之间的【差异变更集】会放入index暂存区中(Staged files)。所以效果看起来就是工作目录的内容不变,暂存区原有的内容也不变,只是原节点和Reset节点之间的所有差异都会放到暂存区中。
--mixed(默认):重置位置的同时,只保留Working Tree工作目录的內容,但会将 Index暂存区 和 Repository 中的內容更改和reset目标节点一致,因此原节点和Reset节点之间的【差异变更集】会放入Working Tree工作目录中。所以效果看起来就是原节点和Reset节点之间的所有差异都会放到工作目录中。
参考链接:https://www.jianshu.com/p/c2ec5f06cf1a
使用场景
使用场景:
--hard:
(1) 要放弃目前本地的所有改变時,即去掉所有add到暂存区的文件和工作区的文件,可以执行 git reset -hard HEAD 来强制恢复git管理的文件夹的內容及状态;
(2) 真的想抛弃目标节点后的所有commit(可能觉得目标节点到原节点之间的commit提交都是错了,之前所有的commit有问题)。
--soft:
合并多个commit节点为一个commit,减少commit,让分支更清晰
节点和reset节点之间的【差异变更集】会放入index暂存区中(Staged files),所以假如我们之前工作目录没有改过任何文件,也没add到暂存区,那么使用reset --soft后,我们可以直接执行 git commit 將 index暂存区中的內容提交至 repository 中。为什么要这样呢?这样做的使用场景是:假如我们想合并「当前节点」与「reset目标节点」之间不具太大意义的 commit 记录(可能是阶段性地频繁提交,就是开发一个功能的时候,改或者增加一个文件的时候就commit,这样做导致一个完整的功能可能会好多个commit点,这时假如你需要把这些commit整合成一个commit的时候)時,可以考虑使用reset --soft来让 commit 演进线图较为清晰。总而言之,可以使用--soft合并commit节点。
--mixed(默认):
(1)使用完reset --mixed后,我們可以直接执行 git add 将這些改变果的文件內容加入 index 暂存区中,再执行 git commit 将 Index暂存区 中的內容提交至Repository中,这样一样可以达到合并commit节点的效果(与上面--soft合并commit节点差不多,只是多了git add添加到暂存区的操作);
(2)移除所有Index暂存区中准备要提交的文件(Staged files),我们可以执行 git reset HEAD 来 Unstage 所有已列入 Index暂存区 的待提交的文件。(有时候发现add错文件到暂存区,就可以使用命令)。
(3)commit提交部分错误代码,或者没有必要的文件也被commit上去,不想再修改错误再commit(因为会留下一个错误commit点),可以回退到正确的commit点上,然后所有原节点和reset节点之间差异会返回工作目录,假如有个没必要的文件的话就可以直接删除了,再commit上去就OK了。
merge 冲突
首先来看一下冲突的产生过程,看仔细咯
原始环境
在git管理的目录下有一个hello_git.txt,里面有内容:
write you branch name:
当前在master分支。
➜ git_test git:(master) ✗ git branch
br_dev
* master
- 从master分支创建一个叫 br_dev 的新分支
➜ git_test git:(master) git switch -c br_dev
Switched to a new branch 'br_dev'
➜ git_test git:(br_dev)
- 修改hello_git.txt,并保存
write you branch name: br_dev
➜ git_test git:(br_dev) vim hello_git.txt
➜ git_test git:(br_dev) ✗ git add -A
➜ git_test git:(br_dev) ✗ git commit -m 'br_dev:one'
[br_dev 5d4e531] br_dev:one
1 file changed, 1 insertion(+), 1 deletion(-)
- 切换到master分支,修改hello_git.txt
write you branch name: master
➜ git_test git:(br_dev) git switch master
Switched to branch 'master'
➜ git_test git:(master) vim hello_git.txt
➜ git_test git:(master) ✗ git add -A
➜ git_test git:(master) ✗ git commit -m 'master:one'
[master 8b384b7] master:one
1 file changed, 1 insertion(+), 1 deletion(-)
- 在master分支merge br_dev 分支,产生冲突
➜ git_test git:(master) git merge br_dev
Auto-merging hello_git.txt
CONFLICT (content): Merge conflict in hello_git.txt
Automatic merge failed; fix conflicts and then commit the result.
总结一下上面的操作:
- 在 master 分支切换出一个新分支 br_dev
- 在 br_dev 分支修改第一行
- 在 master 分支修改第一行
- master 分支 merge br_dev分支,产生冲突。
那么为什么会产生冲突呢,读者是否发现其中规律?
简单来说:两个分支有一个相同的节点,当两个分支在这个共同节点的基础上都做了各自的修改,并且是对同一行代码修改,这时一个分支merge另一个分支时,就会产生冲突。
深层次原因
:git是记录的是每次修改,一个分支只会有一份记录。当一次记录时,如果两个分支对同一个行代码做了修改,就相当于两个新增记录,这时git无法判断记录哪一个,所以就会有冲突。
git merge的冲突判定机制如下:先寻找两个commit的公共祖先,比较同一个文件分别在ours和theirs下对于公共祖先的差异,然后合并这两组差异。如果双方同时修改了一处地方且修改内容不同,就判定为合并冲突,依次输出双方修改的内容。
解决办法
:不要擅自决定保存什么代码,要找到两个分支的提供者,共同决定。