那些你该了解的Git常用操作,如何优雅的回退代码?

一、完整的Git Workflow

1. ssh登录设置

设置用户名和邮箱

bash 复制代码
git config --global user.name "xxx"
git config --global user.email "xxxx@xx.xx"

生成密钥

bash 复制代码
ssh-keygen -t rsa -C "xxx@xxx.cn"

找到~/.ssh/id_rsa.pub并复制内容。 gitlab上创建ssh将刚才复制的内容粘贴上去,title随便取个名字。

2. git clone

有如下几种克隆代码的形式:

  1. 全量拉取所有代码和commit
markdown 复制代码
git clone git@xxx.git
  1. 我们只克隆下包含最近一次commit的一个分支,这样这个项目文件就不会很大。

使用场景:你只是想clone最新版本来使用或学习,而不是参与整个项目的开发工作

markdown 复制代码
git clone --depth 1 git@xxxxx.git
  1. 可以选择只拉取某个分支
markdown 复制代码
git clone -b feature-xxx  git@xxxxx.git

3. 创建并切换分支

创建 + 切换分支

bash 复制代码
git checkout -b feature-xxx

切换分支

bash 复制代码
git checkout feature-xxx

分支命名规范

  • 新需求和特性: feature-模块名-姓名缩写
  • bug修复(待确定): fix-模块名-姓名缩写

4. git add

bash 复制代码
git add 1.txt # 添加1.txt到暂存区

git add . # 添加所有文件到暂存区

5. git commit -m "xxx"

bash 复制代码
git commit -m "xxx" # 提交commit

6. git log

bash 复制代码
git log # 查看log日志

git log --stat # 查看大致改动,可以看到修改的文件

7. git show

bash 复制代码
git show 70f68aa8fcfcea1054fb790ab9879f48c2598aa4 # 查看commit详情

6. git push

bash 复制代码
git push # 上传commit

git push --force # 强制上传commit

7. git pull

bash 复制代码
git pull # 拉取当前分支的远程代码

git pull origin master # 拉取master的远程代码

8. 创建merge request

9. 解决冲突

(1)假设当前处于 feature-xxx 的分支, 你即将合并test分支 (2)创建merge request后发现有merge conflict (3)首先你需要 创建并切换 或 直接切换test-conflict这个解决冲突的分支

bash 复制代码
git checkout test-conflict

# 如果没有这个分支,就创建并切换过去
git checkout -b test-conflict

(4)当前处于test-conflict分支, 你需要如下操作

bash 复制代码
git pull # 拉取当前最新远程代码

git pull origin test # 拉取 test 的代码

git pull origin feature-xxx # 拉取你自己的代码

git add .

git commit -m "merge: reolve conflict" # 创建commit

git push

(5)创建Merge request, 此时是从test-conflict合并到test

相信到这里你已经明白如何处理了,其他环境同理。 简单理解下来,其实解决分支冲突,核心在于需要你为目标分支再切换出来一个分支,避免污染分支。

10. 删除分支

在开发完成后,可以考虑删除当前的分支

bash 复制代码
git checkout xxx # 先切换到别的分支,再删除
git push origin --delete [branch_name] # 删除远程
git branch -D [branch_name] # 删除本地分支

二、危险操作

1. 拉取work、test、ut环境分支代码 ❌

禁止拉取work、test、ut环境分支,如果合并到线上,可能会导致线上代码被污染出现Bug!

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树如下:

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

(1)回退某一个commit

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

git revert 425e6dd10b86783 # 回退某个commit

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

(2)回退多个commit

有如下两个commitid, AB

可通过如下命令回退多个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重叠的。

然后继续执行如下命令:

bash 复制代码
git revert -m 1 83e2776

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

3. 回退本地代码 ⚠️

(1)git reset

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

获取最下面的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 rebasegit merge 命令进行比较。在 Git 工作流中,说明所有可以使用 rebase 的场景

1. git merge

注意:一般我们合并代码,直接使用Gitlab可视界面即可。 最简单的方式是通过以下命令将 master 分支合并到 feature 分支中:

bash 复制代码
git checkout feature
git merge master

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

bash 复制代码
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

大家可能注意到了,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顺序

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

bash 复制代码
git rebase -i 086cbb47628be91b3cc2407056d231fe8c75a120

第一条是最老的commit记录。最后一条是最新的。 然后交换位置后, :wq保存。

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

3. 回退commit

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

bash 复制代码
git rebase -i 086cbb47628be91b3cc2407056d231fe8c75a120

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

4. 修改commit

bash 复制代码
git rebase -i 086cbb47628be91b3cc2407056d231fe8c75a120

pick改成eedit。然后:wq保存和退出编辑。 控制台会打印如下内容: 此时你会进入编辑commit的状态,这时候你修改代码。 再执行:

bash 复制代码
git add .
git rebase --continue

然后会进入编辑commit message的状态, 在这一步你可以修改commit message 保存后,你的commit就被修改了。

5. 合并commit

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

bash 复制代码
git rebase -i 086cbb47628be91b3cc2407056d231fe8c75a120

修改一下: pick改成ssquash, 这个意思是把commit合并到前一个commit。在上面的例子中,都被合并到了commit A git会将三个commit 的message也进行合并, 你也可以编辑commit A的 message 最后只剩下一个 commit

如果你闲麻烦,想直接合并,省略掉编辑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

也支持同时多个cmmit

bash 复制代码
git cherry-pick A B C

支持某个范围

bash 复制代码
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 # 切换到另外的分支

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

bash 复制代码
git checkout feature-A # 切换回来

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

当然,你也可以用可视界面操作:

七、VSCode插件推荐

1. Gitlen

非常推荐安装

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

2. Gitlab Workflow

可以一键创建MR

并且支持在VSCode上看MR记录

相关推荐
学习使我快乐013 小时前
JS进阶 3——深入面向对象、原型
开发语言·前端·javascript
bobostudio19953 小时前
TypeScript 设计模式之【策略模式】
前端·javascript·设计模式·typescript·策略模式
黄尚圈圈4 小时前
Vue 中引入 ECharts 的详细步骤与示例
前端·vue.js·echarts
浮华似水5 小时前
简洁之道 - React Hook Form
前端
正小安7 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch9 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光9 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   9 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   9 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d