Appearance
一、危险操作
1. 拉取work、test、ut环境分支代码 ❌
禁止拉取work、test、ut环境分支,如果合并到线上,可能会导致线上代码被污染出现Bug!
bash
git pull origin test # 错误操作
git pull origin test # 错误操作
2. 回退远程代码 ⚠️
通常我们在网上看到的回退操作,有不少是通过git reset
进行完成的,但是这个操作是比较危险的,经常会出现你明明修改了代码,但是你merge上去之后,什么也没更改。 所以,在回退代码方面,推荐大家使用git revert
操作。
回退代码的原理,其实就是把你之前的代码修改,再进行反向修改。例如你进行了如下操作:
- 基于master切了一个分支
feature-test
- 删除一行代码,然后提交产生了
Commit(C)
- 执行git revert xx之后,代码会自动反向修改,恢复那一行代码,产生Merge Commit(D)
此时的git树如下:
下面来介绍回退代码的几种操作
(1)回退某一个commit
bash
git revert 产生新commit(推荐)
git log # 通过日志,找到你想回退代码的commitid
git revert 425e6dd10b86783 # 回退某个commit
git revert 产生新commit(推荐)
git log # 通过日志,找到你想回退代码的commitid
git revert 425e6dd10b86783 # 回退某个commit
在你执行git revert
后,会产生一个新的commitId, 并且会出现一个message编辑器, 你可以修改revert产生的commit message
(2)回退多个commit
有如下两个commitid, A
和B
可通过如下命令回退多个commit
bash
git revert OLDER_COMMIT^..NEWER_COMMIT # 回退几个commit就产生几个新commit
git -n revert OLDER_COMMIT^..NEWER_COMMIT # 回退的commit, 合并成一个
git revert OLDER_COMMIT^..NEWER_COMMIT # 回退几个commit就产生几个新commit
git -n revert OLDER_COMMIT^..NEWER_COMMIT # 回退的commit, 合并成一个
在上面的例子中,我们执行如下命令
bash
git revert -n 82e9029759976b9bbba6d47adf68f6a2eeafea88^..64b4646ec1ff9e3d608e583563fefd834a23b062
git revert -n 82e9029759976b9bbba6d47adf68f6a2eeafea88^..64b4646ec1ff9e3d608e583563fefd834a23b062
然后你还需要重新commit, 这样你就回退了
bash
git commit -m "revert: A, B"
git commit -m "revert: A, B"
(3)回退merge
如果你按照上面学习到的,git revert xxx
回退一个merge, 那么一定会出现如下问题:
bash
git revert 83e2776
error: commit 83e2776adb7a47617fbd181228906e52ada396ac is a merge but no -m option was given.
fatal: revert failed
git revert 83e2776
error: commit 83e2776adb7a47617fbd181228906e52ada396ac is a merge but no -m option was given.
fatal: revert failed
为什么呢?因为此时git不知道要做什么。merge commit是两个分支的汇合点。本质上这两个分支地位是完全相等的。例如下面的图中,master在合并后,从Commit(B)
移动到了Commit(Merge)
, 此时的指针是与feature-test
重叠的。
然后继续执行如下命令:
bash
git revert -m 1 83e2776
git revert -m 1 83e2776
这里-m 1
其实就是保留目标分支,回退feature分支
上的代码。所以,一般来讲,我们都用-m 1
即可。
3. 回退本地代码 ⚠️
(1)git reset
此处有三个commit
, 如果我想回退掉前面两个commit
获取最下面的commitid
, 然后执行:
bash
git reset --soft 3ffc7c6856e207d45ee71d4dd1fd53f215c9d008 # 先回退
git reset --soft 3ffc7c6856e207d45ee71d4dd1fd53f215c9d008 # 先回退
回退完成后,你会发现你的工作区多出了被回退的代码,此时如果你想push上去,是不行的,需要强制push。
bash
git push --force
git push --force
如果你后悔了刚才的回退操作, 就需要重新commit工作区的代码。
(2)git rebase 方式
请参考后面 git rebase
的相关用法
二、git rebase VS git merge
不知怎么,git rebase
一直被认为初学者不应该学习它,但它实际上可以让开发团队在使用时更加轻松。我们将 git rebase
与 git merge
命令进行比较。在 Git 工作流中,说明所有可以使用 rebase 的场景
1. git merge
注意:一般我们合并代码,直接使用Gitlab可视界面即可。 最简单的方式是通过以下命令将 master 分支合并到 feature 分支中:
bash
git checkout feature
git merge master
git checkout feature
git merge master
或者,你可以将其浓缩为一行命令:
bash
git merge feature master
git merge feature master
这会在 feature 分支中创建一个新的 merge commit,它将两个分支的历史联系在一起,请看如下所示的分支结构:
使用 merge 是很好的方式,因为它是一种 非破坏性的 操作。现有分支不会以任何方式被更改。这避免了 rebase 操作所产生的潜在缺陷(下面讨论)。 另一方面,这也意味着 feature 分支每次需要合并上游更改时,它都将产生一个额外的合并提交。如果master 提交非常活跃,这可能会严重污染你的 feature 分支历史记录。尽管可以使用高级选项 git log 缓解此问题,但它可能使其他开发人员难以理解项目的历史记录(此处仅做讨论,组内规范仍然使用merge流程)
2. git rebase
可以理解为“重新设置基线”(重新设置分支比较的起点commit),并将“新基线”以后的commit拷贝到指定的分支上。所有当前分支上在“新基线”以后的commit会被copy一份存储到一个临时区域,然后按顺序应用到指定分支上
bash
git checkout feature
git rebase master
git checkout feature
git rebase master
大家可能注意到了,master
是落后于feature
的,需要我们在master
上将feature
分支合并过来。当然,你也可以使用Gitlab可视界面合并。
bash
git checkout master
git merge feature
git checkout master
git merge feature
三、强大的git rebase
1. git rebase 注意事项 ⚠️
使用git rebase的注意事项,请一定遵守。
- 公共分支不rebase
- 已经push的部分不rebase
线上提交执行变基会导致什么结果: (1)你在本地对部分线上提交进行了变基,这部分提交我们称之为a,a在变基之后commit id 发生了变化 (2)你在本地改变的这些提交有可能存在于你的同事的开发分支中,我们称之为b,他们与a的内容相同,commit id 不同 (3)如果你把变基结果强行push 到远程仓库后,你的同事在本地执行git pull 的时候会导致a 和b 发生融合,且都出现在了历史提交中,导致你的变基行为无效
2. 修改commit顺序
如果你想处理从 commit A 到最新的commit, 你需要获取这个范围外的第一条,也就是图上的最后一条commit
bash
git rebase -i 086cbb47628be91b3cc2407056d231fe8c75a120
git rebase -i 086cbb47628be91b3cc2407056d231fe8c75a120
第一条是最老的commit记录。最后一条是最新的。 然后交换位置后, :wq
保存。
可以看到,两条commit顺序变了。
3. 回退commit
注意:日常不推荐使用,会导致落后于master分支,你的更改将不会产生新的merge。
bash
git rebase -i 086cbb47628be91b3cc2407056d231fe8c75a120
git rebase -i 086cbb47628be91b3cc2407056d231fe8c75a120
与上面的操作一样,你需要把pick
改成d
或者drop
, 你也可以直接删除这一行,保存之后就会回退commit, 但是这条commit会被删除,请谨慎操作。
4. 修改commit
bash
git rebase -i 086cbb47628be91b3cc2407056d231fe8c75a120
git rebase -i 086cbb47628be91b3cc2407056d231fe8c75a120
将pick
改成e
或edit
。然后:wq
保存和退出编辑。 控制台会打印如下内容: 此时你会进入编辑commit的状态,这时候你修改代码。 再执行:
bash
git add .
git rebase --continue
git add .
git rebase --continue
然后会进入编辑commit message的状态, 在这一步你可以修改commit message 保存后,你的commit就被修改了。
5. 合并commit
如果你想让自己的commit好看一点,你可以把你这次需求所有的commit进行合并。有如下三个commit
bash
git rebase -i 086cbb47628be91b3cc2407056d231fe8c75a120
git rebase -i 086cbb47628be91b3cc2407056d231fe8c75a120
修改一下:
将pick
改成s
或squash
, 这个意思是把commit合并到前一个commit。在上面的例子中,都被合并到了commit A git会将三个commit 的message也进行合并, 你也可以编辑commit A的 message
最后只剩下一个 commit
如果你闲麻烦,想直接合并,省略掉编辑message的过程,可以这样:
bash
pick xxx feat: A
# 改成
f xxx feat: A
pick xxx feat: A
# 改成
f xxx feat: A
四、git cherry-pick
git cherry-pick
命令的作用,就是将指定的提交(commit)应用于其他分支。 比如,你的feature-A
分支上产生了一个commit (086cbb47628be91b3cc2407056d231fe8c75a120) 另一位同学feature-B
也想用你的这份代码,但是其他的代码并不想要,可以这么操作
bash
git checkout feature-B
git cherry-pick 086cbb47628be91b3cc2407056d231fe8c75a120
git checkout feature-B
git cherry-pick 086cbb47628be91b3cc2407056d231fe8c75a120
也支持同时多个cmmit
bash
git cherry-pick A B C
git cherry-pick A B C
支持某个范围
bash
git cherry-pick A^..B # A是最老的commit
git cherry-pick A^..B # A是最老的commit
五、git stash
你可能会遇到这样的场景:在feature-A
分支上开发,突然来了另一个需求,但是我不想 commit提交我的代码,想直接进入feature-B
的开发。正常来讲这肯定是不行的,但是我们可以用git stash
临时将代码存放起来。
git stash
的用法相当简单:
bash
git stash # 先临时存起来
git checkout feature-B # 切换到另外的分支
git stash # 先临时存起来
git checkout feature-B # 切换到另外的分支
开发完成后,再恢复过来:
bash
git checkout feature-A # 切换回来
git stash list # 可以查看stash 记录
git stash apply # 恢复代码
git checkout feature-A # 切换回来
git stash list # 可以查看stash 记录
git stash apply # 恢复代码
当然,你也可以用可视界面操作:
六、VSCode插件推荐
1. Gitlen
非常推荐安装
可以看到每一行,每一个文件的commit记录。鼠标放在代码上也可以看到最近谁更改了这一行
2. Gitlab Workflow
可以一键创建MR
并且支持在VSCode上看MR记录