活用 Git 撤销,提升工程质量(文末招聘)

followWinter

0x001 概述

一个优秀的前端工程师,必先是个优秀的工程师;一个优秀的工程师,要为自己写的每一行代码负责,要为自己提交的每一个 commit 负责。话虽这么说,但是不想写一篇十分大而全的文章,就想写一些小东西。

0x002 撤销 git add

我们知道,要提交一个 commit,必须要先 add,那如果把一些不想要 add 的东西 add 进去了,该咋办?下面提供几个场景和解决方案:

场景1:添加某个误创建文件,这个文件是不想要的

  • 环境:误创建了一个 new.txt,并使用 add
$ echo new > new.txt
$ git add .
$ git status

On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    new file:   new.txt # 👈
  • 解决方案:直接删除就好了
$ rm new.txt
$ git add .
$ git status

On branch master
nothing to commit, working tree clean # 👈

场景 2:新建了一个组件,但是不希望在这次 commit 中一起提交

  • 环境:创建了一个 Product 组件,但是这个组件不在当前使用,本次的修改只应该包含一个 index.js
$ git status

 On branch master
 Changes to be committed:
     (use "git restore --staged <file>..." to unstage)
    new file:   Product/index.css # 👈 多余
    new file:   Product/index.js # 👈 多余
    new file:   index.js
  • 解决方案 1:使用git rm移除,-r表示文件夹递归,--cache表示从index移除,working tree还保留着
$ git rm -r --cache Product/
rm 'Product/index.css'
rm 'Product/index.js'

$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    new file:   index.js  # 👈 index 只剩下一个文件了

Untracked files:
  (use "git add <file>..." to include in what will be committed)
    Product/ # 👈但是 working tree 还有
$ git commit -m 'feat: index'
\[master 3bf1398\] feat: index
 1 file changed, 1 insertion(+)
 create mode 100644 index.js
  • 解决方案 2:使用git stashProduct组件先暂存,提交index.js,再放出Product
  1. 暂存Product
$ git stash -- Product
Saved working directory and index state WIP on master: b426e7e feat: initial
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    new file:   index.js  # 👈 只剩下一个文件了
  1. 提交 index.js
$ git commit -m 'feat: index'
\[master 3bf1398\] feat: index
 1 file changed, 1 insertion(+)
 create mode 100644 index.js
  1. 取出 stash 中的Product
$ git stash pop
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    new file:   Product/index.css  # 👈 又拿出来了
    new file:   Product/index.js  # 👈 又拿出来了

Dropped refs/stash@{0} (28675d4accc329da3e54b0a839967471f21c7102)
  1. 作为独立commit提交
$ git commit -m "feat: product"
\[master de42d60\] feat: product
 2 files changed, 2 insertions(+)
 create mode 100644 Product/index.css
 create mode 100644 Product/index.js

0x003 撤销不想要的修改

场景1:修改了某个文件,但是又不想要其中一些或者全部

  • 环境
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
  modified:   Product/index.js  # 👈 不想要
    modified:   index.js  # 👈 不想要
  • 解决方案1:git stash,然后就不用管它了,适用于全部抛弃或者部分
$ git stash -- Product/index.js
Saved working directory and index state WIP on master: de42d60 feat: product
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   index.js   # 👈 只剩下这个了
no changes added to commit (use "git add" and/or "git commit -a")
  • 解决方案2:git reset,适用于当前全部修改都不要了
$ git reset --hard HEAD
HEAD is now at de42d60 feat: product

$ git status
On branch master
nothing to commit, working tree clean   # 👈 全没了
  • 解决方案3:git checkout,适用于部分抛弃
$ git checkout  -- Product/index.js
$ git status
 On branch master
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
   (use "git restore <file>..." to discard changes in working directory)
    modified:   index.js   # 👈 只剩下这个了
 
 no changes added to commit (use "git add" and/or "git commit -a")

0x004 撤销/修改 git commit

场景1:commit 已经提交,但是 commit message 有错误

  • 场景展示
$ git add .
$ git commit -m 'feat: 错误的 message'
\[master ed31f58\] feat: 错误的 message # 👈 
 2 files changed, 2 insertions(+), 2 deletions(-)
  • 解决方案:git commit --amend
$ git commit --amend -m 'feat: 正确的 message'
\[master 325e290\] feat: 正确的 message # 👈 
 Date: Tue May 26 12:39:07 2020 +0800
 2 files changed, 2 insertions(+), 2 deletions(-)

场景2:commit 已经提交,但是有文件遗漏了

场景展示:

  1. 查看状态,当前有两个文件被修改
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   Product/index.js  # 👈 修改
    modified:   index.js  # 👈 修改
  1. 只提交一个文件
$ git add Product/index.js  # 👈 提交一个
$ git commit -m 'feat: 完成产品功能'
\[master 8f3f605\] feat: 完成产品功能
 1 file changed, 1 insertion(+), 1 deletion(-)
  1. 查看状态,还有一个文件还没提交
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   index.js  # 👈 剩余一个

解决方案1:git commit --amend

$ git add .
$ git commit --amend -m 'feat: 完成 产品功能'   # 👈 提交
\[master 1ae5752\] feat: 完成 产品功能
 Date: Tue May 26 12:49:00 2020 +0800
 2 files changed, 2 insertions(+), 2 deletions(-)
 
$ git status
On branch master
nothing to commit, working tree clean  # 👈 全提交了

解决方案2:git reset --soft HEAD^

$ git reset head^
Unstaged changes after reset:
M   Product/index.js  # 👈 上次提交的又回来了

$ git add .
$ git commit -m 'feat: 完成 产品功能'  # 👈 再次提交
\[master 1ae5752\] feat: 完成 产品功能
 Date: Tue May 26 12:49:00 2020 +0800
 2 files changed, 2 insertions(+), 2 deletions(-)

场景3:commit 已经提交了,但是多了一些文件

  • 场景展示:
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    new file:   Product/index.css
    new file:   Product/index.js
    modified:   index.js

# 原本只想提交 Product 相关文件,但是误把 index.js 提交了
$ git add .
$ git commit -m 'feat: 完成 Product 组件'
\[master 6c0f3c8\] feat: 完成 Product 组件
 3 files changed, 3 insertions(+), 1 deletion(-)
 create mode 100644 Product/index.css
 create mode 100644 Product/index.js

解决方案1: git reset head^ 之后重新提交

$ git reset head^
Unstaged changes after reset:
M   index.js

$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   index.js  # 👈 提交的又回来了

Untracked files:
  (use "git add <file>..." to include in what will be committed)
    Product/  # 👈 提交的又回来了

no changes added to commit (use "git add" and/or "git commit -a")

$ git add Product/\*   # 👈 只提交 Product
$ git commit -m 'feat: 完成 Product 组件'
\[master 9b9d085\] feat: 完成 Product 组件
 2 files changed, 2 insertions(+)
 create mode 100644 Product/index.css
 create mode 100644 Product/index.js

0x005 删除一个 commit(最新的一个)

有时候我们不想要一个 commit 了怎么办,这里提供几个解决方案

  • 场景展示:
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    modified:   index.js

$ git commit -m 'feat: 不想要的提交'
\[master aa47ff2\] feat: 不想要的提交
 1 file changed, 1 insertion(+)
 
$ git log --pretty=oneline
aa47ff2541fa64e6d44735e90dedcee60d5fb0a3 (HEAD -> master) feat: 不想要的提交   # 👈 这个提交不想要
0dc03f1970ed670b97bccffc92ab5598b5049bf8 feat: 完成首页
9b9d085b932043a2d9250b864a1cbb1535f82184 feat: 完成 Product 组件
...
  • 解决方案1:reset
$ git reset --hard HEAD^
HEAD is now at 0dc03f1 feat: 完成首页

$ git log --pretty=oneline
0dc03f1970ed670b97bccffc92ab5598b5049bf8 (HEAD -> master) feat: 完成首页 # 👈 没了
9b9d085b932043a2d9250b864a1cbb1535f82184 feat: 完成 Product 组件
  • 执行以下命令
$ git rebase 7064a1652cda00d4a57d24d3661b018771a49387 # 倒二个
  • 此时会进入 vim
pick 5acd6e2 feat(project): 添加 version 脚本

# Rebase 7064a16..5acd6e2 onto 7064a16 (1 command)
  • 修改为以下内容并保存
drop 5acd6e2 feat(project): 添加 version 脚本
  • 查看结果
Successfully rebased and updated refs/heads/feat/A.

$  git log --pretty=oneline
7064a1652cda00d4a57d24d3661b018771a49387 (HEAD -> feat/A) feat(changeLost): 添加 changeLog  # 第一个没啦
c45cbb73e79d460473dc941ab062ff7e11d099fa (tag: v1.0.3) 1.0.3
671757d69b98e3d6cae6e4dce5717fac4f77a3a7 feat(project): 添加 ChangeLog
fd369e89072279a7057e236ba1707f2a19709b03 (tag: v1.0.2) 1.0.2
4e9f32cc30525a70c07f3c544594f105c48d8d0f (tag: v1.0.1) 1.0.1
  • 解决方案2:rebase
$ git log --pretty=oneline
5acd6e27a85b7e728454374b9fe516387e2e7450 (HEAD -> feat/A) feat(project): 添加 version 脚本
7064a1652cda00d4a57d24d3661b018771a49387 feat(changeLost): 添加 changeLog
c45cbb73e79d460473dc941ab062ff7e11d099fa (tag: v1.0.3) 1.0.3
671757d69b98e3d6cae6e4dce5717fac4f77a3a7 feat(project): 添加 ChangeLog
fd369e89072279a7057e236ba1707f2a19709b03 (tag: v1.0.2) 1.0.2
4e9f32cc30525a70c07f3c544594f105c48d8d0f (tag: v1.0.1) 1.0.1
  • 解决方案3: revert
$ git revert aa47ff2541fa64e6d44735e90dedcee60d5fb0a3 --no-edit
\[master ae7eae8\] Revert "feat: 不想要的提交"
 Date: Tue May 26 13:45:07 2020 +0800
 1 file changed, 1 deletion(-)

$ git log --pretty=oneline
ae7eae86691a90e81b68b85e0f3cb7ebaee4312c (HEAD -> master) Revert "feat: 不想要的提交"   # 👈 没了
aa47ff2541fa64e6d44735e90dedcee60d5fb0a3 feat: 不想要的提交
0dc03f1970ed670b97bccffc92ab5598b5049bf8 feat: 完成首页
9b9d085b932043a2d9250b864a1cbb1535f82184 feat: 完成 Product 组件
  • 解决方案4:reflog + reset
$ git reflog
6fa3df5 (HEAD -> master) HEAD@{0}: commit: feat: 不想要的提交
0dc03f1 HEAD@{1}: reset: moving to HEAD^   # 👈 这个
aa47ff2 HEAD@{2}: reset: moving to HEAD
aa47ff2 HEAD@{3}: reset: moving to HEAD@{6}

$ git reset --hard HEAD@{1}
HEAD is now at 0dc03f1 feat: 完成首页

0x006 取消一次 merge

  • 场景展示
有 feat/A 和 feat/B 分支合并到 master,如下图现在想撤销 feat/B 到 master 的合并

![image.png](https://intranetproxy.alipay.com/skylark/lark/0/2020/png/281707/1590472368645-536b45c9-ebc2-4f6e-858a-f906614ba5cb.png)  
  • 解决方案1:reset
$ git log --pretty=oneline
aeca903ffad7140b2d1c854c936290c006542f05 (HEAD -> master) Merge branch 'feat/B'
7c8f46806ffb2ee5961e24b4a435d68f893c78c4 (feat/A) feat: 添加功能a   # 👈 回退到这个就行了
1b2dd75b7193a9b37e20a269f5490971adfe4b50 (feat/B) feat: 添加功能b
0dc03f1970ed670b97bccffc92ab5598b5049bf8 feat: 完成首页
9b9d085b932043a2d9250b864a1cbb1535f82184 feat: 完成 Product 组件
$ git reset --hard 7c8f46806ffb2ee5961e24b4a435d68f893c78c4
HEAD is now at 7c8f468 feat: 添加功能a

看看结果

![image.png](https://intranetproxy.alipay.com/skylark/lark/0/2020/png/281707/1590472911298-e89e3453-14ff-4ad6-bf32-3c28e4e26699.png?x-oss-process=image%2Fresize%2Cw_1500 "image.png")
  • 解决方案2:revert
$ git log --pretty=oneline
aeca903ffad7140b2d1c854c936290c006542f05 (HEAD -> master) Merge branch 'feat/B'
7c8f46806ffb2ee5961e24b4a435d68f893c78c4 (feat/A) feat: 添加功能a  # 👈 回退到这个就行了
1b2dd75b7193a9b37e20a269f5490971adfe4b50 (feat/B) feat: 添加功能b
0dc03f1970ed670b97bccffc92ab5598b5049bf8 feat: 完成首页
9b9d085b932043a2d9250b864a1cbb1535f82184 feat: 完成 Product 组件

$ git revert aeca903ffad7140b2d1c854c936290c006542f05 -m 1   # 👈 1是只分支序号
\[master a26fa95\] Revert "Merge branch 'feat/B'"
 1 file changed, 1 deletion(-)
 delete mode 100644 b.txt
  • 解决方案3:reflog + reset
$ git reflog
aeca903 (HEAD -> master) HEAD@{0}: merge feat/B: Merge made by the 'recursive' strategy.
7c8f468 (feat/A) HEAD@{1}: merge feat/A: Fast-forward   # 👈 回退到这个就行了
0dc03f1 HEAD@{2}: checkout: moving from feat/A to master
7c8f468 (feat/A) HEAD@{3}: commit: feat: 添加功能a
0dc03f1 HEAD@{4}: checkout: moving from feat/B to feat/A
1b2dd75 (feat/B) HEAD@{5}: commit: feat: 添加功能b
0dc03f1 HEAD@{6}: checkout: moving from feat/A to feat/B
0dc03f1 HEAD@{7}: checkout: moving from master to feat/A

$ git reset --hard HEAD@{1}
HEAD is now at 7c8f468 feat: 添加功能a

结果:

image.png

0x007 删除提交历史中的一个大文件/敏感文件

这个功能在我看来有两个用处:

  1. 过去提交了敏感文件,比如密钥,期望把它从提交历史中移除
  2. 提交了大文件,导致整个项目变大,虽然后期把它删除了,但是历史中还是存在的
  • 场景展示
\# 不小心把一个安装包给提交进来了
$ git add .
$ git commit -m 'feat: 完成首页'
\[master aa23c6a\] feat: 完成首页
 2 files changed, 1 insertion(+), 1 deletion(-)
 create mode 100644 XMind-for-macOS-10.1.0-202003221812.dmg    # 👈不小心添加的大文件
 
 # 尝试删除 安装包
 $ rm XMind-for-macOS-10.1.0-202003221812.dmg
 $ git add .
 $ git commit -m 'feat: 删除安装包'
\[master 68a3be8\] feat: 删除安装包
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 XMind-for-macOS-10.1.0-202003221812.dmg  # 👈删除

明面上看是删除了,但是看看提交历史,这个文件依旧是存在的,如果是密钥文件,那不免有泄露风险。

image.png

并且文件特别大,看看这时候我们项目有多大吧:

image.png

解决方案1:filter-branch

$ git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch XMind-for-macOS-10.1.0-202003221812.dmg' --prune-empty --tag-name-filter cat -- --all
Rewrite b426e7e5fe1633d4321403e36be5656a99ba4ad8 (1/5) (0 seconds passed, remainRewrite 3bf1398f9010f2358ddb9bb6c29669c11a2fea01 (2/5) (0 seconds passed, remainRewrite 9b9d085b932043a2d9250b864a1cbb1535f82184 (3/5) (0 seconds passed, remainRewrite aa23c6ad093baa8a90264e5d8c761d8cc7b062f8 (4/5) (0 seconds passed, remaining 0 predicted)    rm 'XMind-for-macOS-10.1.0-202003221812.dmg'
Rewrite 68a3be8e878eaba59af800c63856d488de3f3df3 (5/5) (0 seconds passed, remaining 0 predicted)
Ref 'refs/heads/master' was rewritten

再看看历史:

image.png

再看看大小,还是没变啊,咋回事

image.png

继续往下:

$ git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin
$ git reflog expire --expire=now --all
$ git gc --prune=now
Enumerating objects: 16, done.
Counting objects: 100% (16/16), done.
Delta compression using up to 8 threads
Compressing objects: 100% (6/6), done.
Writing objects: 100% (16/16), done.
Total 16 (delta 3), reused 14 (delta 3)

image.png

注意:git filter-branch 有很多陷阱,不再推荐使用它来重写历史。 请考虑使用 git-filter-repo,它是一个 Python 脚本,相比大多数使用 filter-branch 的应用来说,它做得要更好。它的文档和源码可访问https://github.com/newren/git-filter-repo获取。

0x008 招聘

新零售技术事业群-CCO技术部的前端团队,我们致力于Web前端技术的研究,同时也重视全栈的能力、产品的推动。Glue即为胶水,我们粘合了设计与技术、用户和产品的关系,用最酷的技术提升客户服务的体验和小二工作的幸福感。FGT也可看成是Fighting的缩写。我们在听得到炮火的地方战斗,我们要把好阿里用户体验的最后一关!

我们负责的产品线非常丰富,PC、无线、内部CRM、Native等等,无论你是什么技术特质,总能找到合适的应用场景。

我们的业务也很有挑战,整个BU致力于通过产品技术创新驱动服务变革,改变人力密集型的传统服务模式。通过打造一站式服务平台,为企业提供全套客户服务解决方案,提升服务体验的同时极大降低服务成本。 通过输出阿里平台沉淀,构建服务生态圈,为电商服务体系贡献水电煤。

如果你来到我们团队,你将负责: 

  • 各业务前端开发 - 平台易用性与用户体验的持续改进 
  • Web前沿技术研究和新技术调研  

职位要求:  

  • 精通各种前端技术,同时具备 PC/无线 端的开发能力,有复杂产品的开发经验。  
  • 掌握常见性能优化的方法,以及衡量产品性能的方法。  
  • 对 MVC/MVVM 等模式有一定的理解,熟悉 React/Vue 等热门框架,有实际项目经验。  
  • 对前端工程化有一定理解,熟练掌握 Webpack/Grunt/Gulp 等构建工具的使用和配置。 ‘
  • 熟悉 ES6/Node.js 并且具备一定的开发能力。

简历投递地址:

  • cangxiu.lyx@alibaba-inc.com

微信(不投简历交个朋友也行哦):

IMG_2725.JPG

阅读 408

漫漫填坑路,十里长安响码声。
哎,好像不能申请多个专栏呢,原本这个专栏只放前端文章,现在看来不行了!就都放吧!

暂时没有

1.5k 声望
54 粉丝
0 条评论

暂时没有

1.5k 声望
54 粉丝
宣传栏