前言
一本秘籍闯宇宙,那么这边秘籍是什么呢?那就是git秘籍
,初学者对于git
只知道git add .
、 git commit -m 'commit message'
、git push origin main
这三个招式吧!其实git的招式还有很多,足够让你去探索一番!让我们掌握这本秘籍闯宇宙去吧!
先送大家一张git命令大全吧!
git通用流程图
git秘籍核心--多人协作冲突
1.什么是Git多人协同开发
当开发一个大型项目时通常都是许多人进行合作,每个人负责项目的一部分,Git是实现多人协同开发最常见的工具,但是对于刚接触Git的人来说,多人协同开发会遇见很多问题,这篇博客的内容主要就是为了给出多人协同开发的正确步骤,并对这个过程中遇到的问题进行解释和解决。
那么多人协同开发时如何避免代码冲突呢?
我们先了解一下多人协作开发的流程吧!
2.多人协作开发的准备工作
- 新建一个仓库
- 创建主分支,即上传项目的初始内容到master分支
- 团队内成员进行分工(各个成员之间负责的内容用尽量不冲突)
3.开发阶段
- 每个成员将远程仓库主(master)分支的内容clone下来
- 按照分工进行自己负责的工作
4.提交阶段(冲突产生阶段)
(1)、每个成员在完成自己的工作后,首先需要注意远程仓库的变化
方式一:拉取远程分支(git pull指令)
git pull指令
可以理解为两个步骤:
- 获取远程分支
- 将获取的远程分支与本地分支合并
方式二:获取远程分支(git fetch指令)
git fetch指令
的理解:
- 获取远端指定分支的最新版到本地(即在本地创建一个新分支内容为远端指定分支的最新版)
- 获取分支后就可以比较、查看远程分支的内容,随后若想push,可选择与获取的分支进行merge(合并)再push。 (2)、获取远程仓库的最新版本与本地进行合并
合并时会产生冲突问题:合并冲突
产生情况(3种):
- 两个人对同一项目的不同文件进行了修改
- 两个人对同一项目的同一文件的不同区域进行了修改
- 对同一项目的同一文件的同一区域进行了不同修改
思考:那么上面3种情况中哪种会产生冲突呢?
下面将揭晓答案:
前面两种都是可以由git进行自动合并的,而第三种情况是无法自动合并的,需要手动合并(第三种会产生冲突);
问题1:什么是自动合并呢?
合并本质上可以理解为将两个人(分支)对项目的基础的修改整合到一块,注意是对项目的修改。上述三种情况的前两种是两个人对项目的不同区域进行修改,互不干扰,所以Git可以自动的将两个人对项目的修改整合到一起
问题2:什么是手动合并呢?
当Git不知道该保留两个修改中的哪一个时,就需要人来进行这个决策,可以选择保留两个修改中的任意一个,或是选择将两个修改全部保留。完成上述决策就是手动合并的目的。
问题3:为什么需要手动合并?
当可以自动合并时,说明两个人的修改不会冲突,但是当两个人对同一文件的同一区域进行了修改那么这两个修改就会产生冲突,Git将无法整合这两个修改,因为Git不知道它该保留两个修改中的哪一个(或是要一并保存),这是就需要人工进行手动合并了。
(3)、合并过后,就可以上传(git push)到远程仓库自己的分支
5.审核阶段
管理员审核代码,没有发现问题后就可以将其与主分支进行merge,管理员应尽快完成这个过程,从而确保成员拉取到的都是最新版本。
管理员合并过程中可能也会发生冲突,需要管理员联系成员了解情况后进行手动合并。
管理员的作用:
维护远程仓库的master分支,包括以下:
- 检查各个成员分支的代码有无问题
- 将成员分支的代码合并到master分支
- 合并发生冲突时,进行手动合并
6.冲突解决
1.冲突的类型
逻辑冲突
git自动处理(合并/应用补丁)成功,但是逻辑上是有问题的。
比如另外一个人修改了文件名,但我还使用老的文件名,这种情况下自动处理是能成功的,但实际上是有问题的。
又比如,函数返回值含义变化,但我还使用老的含义,这种情况自动处理成功,但可能隐藏着重大BUG.
这种问题,主要通过自动化测试来保障。所以最好是能够写出比较完备的自动化测试用例。
这种冲突的解决,就是做一次BUG修正。不是真正解决git报告的冲突。
内容冲突
两个用户修改了同一个文件的同一块区域,git会报告内容冲突。我们常见的都是这种,后面的解决办法也主要针对这种冲突。
树冲突
文件名修改造成的冲突,称为树冲突。 比如,a用户把文件改名为a.c,b用户把同一个文件改名为b.c,那么b将这两个commit合并时,会产生冲突。 如果最终确定用b.c,那么解决办法如下:
git rm a.c git rm origin-name.c git add b.c git commit
执行前面两个git rm时,会告警“file-name : needs merge”,可以不必理会。
合并时使用git rebase 和 git merge的区别
merge 操作遇到冲突的时候,当前merge不能继续进行下去。手动修改冲突内容后,add 修改,commit 就可以了。 而rebase 操作的话,会中断rebase,同时会提示去解决冲突。 解决冲突后,将修改add后执行git rebase –continue继续操作,或者git rebase –skip忽略冲突
2.变基(rebase)
变基要在自己本地仓库中拉出来的分支使用,不要对本地仓库外有副本的分支执行变基 git pull 与 git pull --rebase
-
git pull = git fetch + git merge
-
git pull --rebase = git fetch + git rebase
git pull --rebase,这里表示把你的本地当前分支里的每个提交(commit)取消掉,并且把它们临时 保存为补丁(patch)(这些补丁放到".git/rebase"目录中),然后把本地当前分支更新 为最新的"origin"分支,最后把保存的这些补丁应用到本地当前分支上
冲突1
当你
commit
以后,在执行git pull –rebase
的时候出现冲突,请按如下步骤解决:
- 1 先找到冲突文件,解决冲突
- 2 执行git add xxx(xxx为冲突文件全路径)
- 3 执行git rebase –continue(合并冲突)
- 4 执行git pull –rebase
- 5 执行git push
冲突2
当你本地有修改的时候,你执行了git stash,然后又从服务器上pull了最新代码(git pull –rebase),出现了冲突,请按如下方式解决:
- 1 找到冲突文件,解决冲突
- 2 执行git add xxx(xxx为冲突文件全路径)
- 3 git commit
- 4 git pull –rebase
- 5 git push
多人基于同一个远程分支开发的时候,如果想要顺利 push 又不自动生成 merge commit,建议在每次提交都按照如下顺序操作:
//git stash 能够将所有未提交的修改(工作区和暂存区)保存至堆栈中,用于后续恢复当前工作目录。
git stash
git pull --rebase
git push
//git stash pop 从Git栈中读取最近一次保存的内容,恢复工作区的相关内容。由于可能存在多个Stash的内容,所以用栈来管理,pop会从最近的一个stash中读取内容并恢复。将暂存文件取出,与pull的文件对比
git stash pop
3.回滚(reset与revert)
git reset(重置)
git reset --hard 和 git reset --soft区别
git reset –-soft:回退到某个版本(
已经git add的
),只回退了commit的信息,不会恢复到index file一级。如果还要提交,直接commit即可;git reset -–hard:彻底回退到某个版本,本地的源码也会变为上一个版本的内容,撤销的commit中所包含的更改被冲掉; 其中:
A
和B
是正常提交,而C
和D
是错误提交。现在,我们想把C
和D
回退掉。而此时,HEAD
指针指向D
提交(5lk4er)
。我们只需将HEAD
指针移动到B
提交(a0fvf8)
,就可以达到目的。
那么命令是什么呢?那就是git reset
命令
1.git reset --hard
//git reset --hard <commit_id>
git reset --hard a0fvf8
命令运行之后,HEAD
指针就会移动到B
提交下
而这个时候,远程仓库的
HEAD
指针依然不变,仍在 D
提交上。所以,如果直接使用 git push
命令的话,将无法将更改推到远程仓库。此时,只能使用 -f
选项将提交强制推到远程仓库
git push -f
采用这种方式回退代码的弊端显而易见,那就是会使 HEAD
指针往回移动,从而会失去之后的提交信息。将来如果突然发现,C
和 D
是多么绝妙的想法,可它们已经早就消失在历史的长河里了。
2.git reset –-soft
//git reset --soft <commit_id>
git reset --soft a0fvf8
例子:
原理: git reset的作用是修改HEAD的位置,即将HEAD指向的位置改变为之前存在的某个版本,如下图所示,假设我们要回退到版本一:
适用场景: 如果想恢复到之前某个提交的版本,且那个版本之后提交的版本我们都不要了,就可以用这种方法。
git revert(撤销)
git revert
的作用通过反做创建一个新的版本,这个版本的内容与我们要回退到的目标版本一样,但是HEAD
指针是指向这个新生成的版本,而不是目标版本。
使用 git revert
命令来实现上述例子的话,我们可以这样做:先 revert D
,再 revert C
(有多个提交需要回退的话需要由新到旧进行 revert
):
// 1 revert D
git revert 5lk4er
// 2 revert C
git revert 76sdeb
那么就会生成两个新的提交:D'
和C'
这里只有两个提交需要
revert
,我们可以一个个回退。但如果有几十个呢?一个个回退肯定效率太低而且容易出错。我们可以使用以下方法进行批量回退:
git revert OLDER_COMMIT^..NEWER_COMMIT
这时,错误的提交 C
和 D
依然保留,将来进行甩锅的时候也有依可循。而且,这样操作的话HEAD
指针是往后移动的,可以直接使用 git push
命令推送到远程仓库里。而这种做法,正是企业所鼓励的。
例子 原理: git revert是用于“反做”某一个版本,以达到撤销该版本的修改的目的。比如,我们commit了三个版本(版本一、版本二、 版本三),突然发现版本二不行(如:有bug),想要撤销版本二,但又不想影响撤销版本三的提交,就可以用 git revert 命令来反做版本二,生成新的版本四,这个版本四里会保留版本三的东西,但撤销了版本二的东西。如下图所示:
适用场景: 如果我们想撤销之前的某一版本,但是又想保留该目标版本后面的版本,记录下这整个版本变动流程,就可以用这种方法。
文章链接:Git恢复之前版本的两种方法reset、revert(图文详解)
git指令练习网站
可以让我了解每个命令执行后发生了什么的网站(感兴趣的可以去玩一玩) Learn Git Branching,练习git命令的网站
网站页面显示
比如:练习一道题
介绍git rebase
未执行 git rebase main
执行 git rebase main
未执行 git rebase bugFix
执行 git rebase bugFix
题目问题操作提示
答题界面介绍
答案
通过输入命令我们可以观察到右边的树状图都会发生改变,可以让我们更好的理解git命令
下面的大家可以根据答案去输入git命令去尝试,我就不一一为大家解答啦!有疑问可以在评论区留言!
//新建并切换到bugFix分支
git checkout -b bugFix
//提交一次
git commit
//切换到main分支
git checkout main
//提交一次
git commit
//再次切换到bugFix分支
git checkout bugFix
//rebase 到 main
git rebase main
总结
git多人协作也是第一次去了解,毕竟还是学生没有在工作中真正的遇到这些问题。如果有问题的地方,麻烦大佬们帮忙指出,我及时修改,在此先提前感谢各位大佬们!
上面有些图是盗来的,如果有侵权请联系我!
本人大三正在学习前端!