Skip to content

Commit

Permalink
Update 优雅的回退代码.md
Browse files Browse the repository at this point in the history
  • Loading branch information
fyhhub authored Apr 16, 2024
1 parent 7279eb9 commit 6d1c798
Showing 1 changed file with 321 additions and 23 deletions.
344 changes: 321 additions & 23 deletions src/tools/Git/优雅的回退代码.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,342 @@
# 优雅的回退代码
# 一、危险操作

[如何使用 Git Rebase 优雅回退代码](https://yorkyu.cn/how-to-use-rebase-to-gracefully-fallback-code-08cdafe3d403.html#%E4%B8%89-Rebase-%E5%9B%9E%E9%80%80%E4%BB%A3%E7%A0%81)
## 1. 拉取work、test、ut环境分支代码 ❌

## 1. 切出一个分支用于回退
禁止拉取work、test、ut环境分支,如果合并到线上,可能会导致线上代码被污染出现Bug!

## 2. 执行`git rebase -i commit_n`
使用git log,找到你想回退到哪个commit id,然后执行
```bash
git pull origin test # 错误操作
```

## 2. 回退远程代码 ⚠️

通常我们在网上看到的回退操作,有不少是通过`git reset`进行完成的,但是这个操作是比较危险的,经常会出现你明明修改了代码,但是你merge上去之后,什么也没更改。
**所以,在回退代码方面,推荐大家使用**`git revert`**操作。**

回退代码的原理,其实就是把你之前的代码修改,再进行反向修改。例如你进行了如下操作:

1. 基于master切了一个分支`feature-test`
2. 删除一行代码,然后提交产生了`Commit(C)`
3. 执行git revert xx之后,代码会自动反向修改,恢复那一行代码,产生Merge Commit(D)

此时的git树如下:
![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3f9a6c0217284b67ae7794347896c687~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1189\&h=603\&s=31847\&e=jpg\&b=ffffff)

下面来介绍回退代码的几种操作

### (1)回退某一个commit

```bash
git revert 产生新commit(推荐)
git log # 通过日志,找到你想回退代码的commitid

git revert 425e6dd10b86783 # 回退某个commit
```


![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9584d15f1a024b6091d9c81d0c1c2307~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=627&h=234&s=47074&e=png&b=25282e)

在你执行`git revert`后,会产生一个新的commitId, 并且会出现一个message编辑器, 你可以修改revert产生的commit message

### (2)回退多个commit

有如下两个commitid, `A``B`


![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2dfb30bb45714ebc8237c9750bc1ae81~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=776&h=184&s=35688&e=png&b=25282e)

可通过如下命令回退多个commit

```bash
git revert OLDER_COMMIT^..NEWER_COMMIT # 回退几个commit就产生几个新commit

git -n revert OLDER_COMMIT^..NEWER_COMMIT # 回退的commit, 合并成一个
```

在上面的例子中,我们执行如下命令

```bash
git revert -n 82e9029759976b9bbba6d47adf68f6a2eeafea88^..64b4646ec1ff9e3d608e583563fefd834a23b062
```

然后你还需要重新commit, 这样你就回退了

```bash
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不知道要做什么。merge commit是两个分支的汇合点。本质上这两个分支地位是完全相等的。例如下面的图中,master在合并后,从`Commit(B)`移动到了`Commit(Merge)`, 此时的指针是与`feature-test`重叠的。
![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/623f24b8c0914df18beb91f026498836~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1798\&h=634\&s=52297\&e=jpg\&b=ffffff)

然后继续执行如下命令:

```bash
git revert -m 1 83e2776
```

这里`-m 1`其实就是保留目标分支,回退`feature分支`上的代码。所以,一般来讲,我们都用`-m 1`即可。

## 3. 回退本地代码 ⚠️

### (1)git reset

此处有三个`commit`, 如果我想回退掉`前面两个commit`


![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/13cade351ddb48d59a2ab6197f918e88~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=733&h=412&s=55760&e=png&b=25282e)

获取最下面的`commitid`, 然后执行:

```bash
git reset --soft 3ffc7c6856e207d45ee71d4dd1fd53f215c9d008 # 先回退
```

回退完成后,你会发现你的工作区多出了被回退的代码,此时如果你想push上去,是不行的,需要强制push。

```bash
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
```

或者,你可以将其浓缩为一行命令:

```bash
git merge feature master
```

这会在 feature 分支中创建一个新的 **merge commit**,它将两个分支的历史联系在一起,请看如下所示的分支结构:

![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f76bb7ab03fb43c2ad06b30f44b10420~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1919\&h=928\&s=50526\&e=jpg\&b=ffffff)

使用 merge 是很好的方式,因为它是一种 **非破坏性的** 操作。现有分支不会以任何方式被更改。这避免了 rebase 操作所产生的潜在缺陷(下面讨论)。
另一方面,这也意味着 feature 分支每次需要合并上游更改时,它都将产生一个额外的合并提交。如果master 提交非常活跃,这可能会严重污染你的 feature 分支历史记录。尽管可以使用高级选项 git log 缓解此问题,**但它可能使其他开发人员难以理解项目的历史记录(此处仅做讨论,组内规范仍然使用merge流程)**

## 2. git rebase

可以理解为“重新设置基线”(重新设置分支比较的起点commit),并将“新基线”以后的commit拷贝到指定的分支上。所有当前分支上在“新基线”以后的commit会被copy一份存储到一个临时区域,然后按顺序应用到指定分支上

```bash
git checkout feature
git rebase master
```

![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/80295d6d477a4943950832fe379d1f64~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1818\&h=855\&s=45574\&e=jpg\&b=ffffff)

大家可能注意到了,`master`是落后于`feature`的,需要我们在`master` 上将`feature`分支合并过来。当然,你也可以使用Gitlab可视界面合并。

```bash
git checkout master

git merge feature
```

# 三、强大的git rebase

## 1. git rebase 注意事项 ⚠️

使用git rebase的注意事项,请一定遵守。

1. **公共分支不rebase**
2. **已经push的部分不rebase**

线上提交执行变基会导致什么结果:
(1)你在本地对部分线上提交进行了变基,这部分提交我们称之为a,a在变基之后commit id 发生了变化
(2)你在本地改变的这些提交有可能存在于你的同事的开发分支中,我们称之为b,他们与a的内容相同,commit id 不同
(3)如果你把变基结果强行push 到远程仓库后,你的同事在本地执行git pull 的时候会导致a 和b 发生融合,且都出现在了历史提交中,导致你的变基行为无效

## 2. 修改commit顺序

![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d90e89114f96405a850462bf7a32c4ca~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=865&h=372&s=90599&e=png&b=25272d)

如果你想处理从 commit A 到最新的commit, 你需要获取这个范围外的第一条,也就是图上的最后一条commit

```bash
git rebase -i 086cbb47628be91b3cc2407056d231fe8c75a120
```

第一条是最老的commit记录。最后一条是最新的。
![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d0b81d3873ac4d40bf4bd265a6de8584~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1358\&h=626\&s=195006\&e=png\&b=292c33)
然后交换位置后, `:wq`保存。


![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4a5252a65796498ebc4a17ab8af9f47e~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1358&h=626&s=184452&e=png&b=292c33)

可以看到,两条commit顺序变了。

![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e4dbb7b2ee03494893058d8b29c6ab30~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=861&h=233&s=53275&e=png&b=25272d)

## 3. 回退commit

注意:日常不推荐使用,会导致落后于master分支,你的更改将不会产生新的merge。

```bash
git rebase -i 086cbb47628be91b3cc2407056d231fe8c75a120
```

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f01e4cd416f34a9b87574b90e1c197bf~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=578\&h=102\&s=16391\&e=png\&b=292c33)

与上面的操作一样,你需要把`pick`改成`d`或者`drop`, 你也可以直接删除这一行,保存之后就会回退commit, 但是这条commit会被删除,请谨慎操作。

## 4. 修改commit

```bash
git rebase -i 086cbb47628be91b3cc2407056d231fe8c75a120
```

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0b286e3593fb4835bfbfa20f3890089c~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=454\&h=110\&s=15782\&e=png\&b=292c33)

`pick`改成`e``edit`。然后`:wq`保存和退出编辑。
控制台会打印如下内容:
![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6b62aaca53f740e69ea1d75af1dd2e19~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=992\&h=338\&s=68104\&e=png\&b=292c33)
此时你会进入编辑commit的状态,这时候你修改代码。
![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1aaf38473a844a20976d2fc1516aa14b~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1476\&h=204\&s=40685\&e=png\&b=2a2d34)
再执行:

```bash
git add .
git rebase --continue
```

然后会进入编辑commit message的状态, 在这一步你可以修改commit message
![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/56f23370c0504d9e953caa71bef289a9~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1212\&h=566\&s=137422\&e=png\&b=292c33)
保存后,你的commit就被修改了。
![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ea286bdfa2f84366989a90ae6b1dac4e~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1098\&h=166\&s=50062\&e=png\&b=292c33)

## 5. 合并commit

如果你想让自己的commit好看一点,你可以把你这次需求所有的commit进行合并。有如下三个commit

![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/dc77096dc16048698cf62fde35759e48~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=829&h=367&s=82704&e=png&b=25272d)

```bash
git rebase -i 086cbb47628be91b3cc2407056d231fe8c75a120
```
git rebase -i fa1b56d920e636914b6ef27988358ff122279261

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4eeefc284564417e804c8393ece79d93~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1386\&h=720\&s=226865\&e=png\&b=292c33)

修改一下:

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a99f6efffe3f4fa086d2d4c7bf61419c~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=522\&h=128\&s=22204\&e=png\&b=292c33)

`pick`改成`s``squash`, 这个意思是把commit合并到前一个commit。在上面的例子中,都被合并到了commit A
git会将三个commit 的message也进行合并, 你也可以编辑commit A的 message

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2dc2b7b592be4084820da305a86a5c53~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1190\&h=458\&s=61244\&e=png\&b=292c33)

最后只剩下一个 commit

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/63f61ede84364eb1994140ccad8b6a7b~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=823&h=194&s=34357&e=png&b=25272d)

如果你闲麻烦,想直接合并,省略掉编辑message的过程,可以这样:

```bash
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
```

编辑界面看到形如下述的 commit:
也支持同时多个cmmit

```bash
git cherry-pick A B C
```
pick 6fa5869 commit1
pick 0b84ee7 commit2
pick 986c6c8 commit3
pick 91a0dcc commit4

支持某个范围

```bash
git cherry-pick A^..B # A是最老的commit
```

## 3. 合并 commit2 ~ commit4 到最旧的`commit1`
# 五、git stash

你可能会遇到这样的场景:在`feature-A`分支上开发,突然来了另一个需求,但是我不想 commit提交我的代码,想直接进入`feature-B`的开发。正常来讲这肯定是不行的,但是我们可以用`git stash`临时将代码存放起来。

`git stash`的用法相当简单:

修改:
```bash
git stash # 先临时存起来

git checkout feature-B # 切换到另外的分支
```
pick 6fa5869 commit1
squash 0b84ee7 commit2
squash 986c6c8 commit3
squash 91a0dcc commit4

开发完成后,再恢复过来:

```bash
git checkout feature-A # 切换回来

git stash list # 可以查看stash 记录
git stash apply # 恢复代码
```

然后wq, 保存并退出
当然,你也可以用可视界面操作:


![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/27c1763cd7d74298b694e8b3e493f0db~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=827&h=228&s=68198&e=png&b=272a30)
# 六、VSCode插件推荐

## 1. Gitlen

非常推荐安装

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/315ef9a492574548a3b06897375fdd89~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=357\&h=71\&s=15852\&e=png\&b=202227)

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/db39c6b199cc4811b2d7a75676180f0b~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=366\&h=210\&s=14027\&e=png\&b=2e3139)

可以看到每一行,每一个文件的commit记录。鼠标放在代码上也可以看到最近谁更改了这一行

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/66be2ed764fc471dbc8f03ca46f08420~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=435\&h=73\&s=7956\&e=png\&b=262930)

## 2. Gitlab Workflow

## 4. 保存 rebase 结果后,再编辑 commit 信息,使这次 rebase 失效
![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/86979707c01f426db4c061d8283f1de2~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=367\&h=75\&s=11581\&e=png\&b=24262c)

此时你需要修改合并到的那个commit的message信息。
可以一键创建MR

+ 冲突处理
![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/15ac89c4bf544232a0e227c03b8c9cc8~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=153\&h=54\&s=3610\&e=png\&b=24272d)

出现冲突时,需处理冲突后,再执行 –continue。处理冲突时一般选择 Accept Current Change
并且支持在VSCode上看MR记录

## 5. git revert
![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1873adf5dc6c49a0b8b4531f3541c567~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=367\&h=256\&s=22706\&e=png\&b=202227)

0 comments on commit 6d1c798

Please sign in to comment.