前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【优化】记一次通过工具减少 Git 冲突

【优化】记一次通过工具减少 Git 冲突

作者头像
GopalFeng
发布2020-11-25 15:18:03
9790
发布2020-11-25 15:18:03
举报
文章被收录于专栏:前端杂货铺-Gopal

起因

当我们的项目越来越大的时候,Git 冲突是团队协作中令人非常苦恼的事情,不仅仅浪费了我们时间,而且很容易解决冲突的时候出现问题。

我能想到的就是模块细分化,每个人都负责自己相关的模块,这样开发者之间的代码就不会相互影响,也就不会有代码的冲突。但多人协作项目中公共的代码模块必不可少,比如我们常见的公共变量文件 constant.js,并且大部分的冲突来源于此。

基于此,Leader 提出了另外一个解决方案——控制书写代码的顺序

为什么顺序那么重要

对于这个问题,我们要先清楚,为什么会有代码冲突?

因为我们改了同一个文件中同一行的代码

举个例子,比如我们常量定义中有如下:

代码语言:javascript
复制
export const Employees = {
  Andy: 'I can sing',
  Oliver: 'I can run',
  Ivan: 'I can rap'
};

同事 A 和我都要改到 Employees 这个常量,他添加 Brad: 'I am Brad' , 我添加了 Patrick: 'I am Patrick',我们都加到了该变量的后面,就会导致冲突,类似如下

代码语言:javascript
复制
@@@ -1,4 -1,4 +1,8 @@@
  Andy: 'I can sing',
  Oliver: 'I can run',
  Ivan: 'I can rap',
  <<<<<<< HEAD
 +Brad: 'I am Brad',
  =======
+ Patrick: I am Patrick,
  >>>>>>>

但假如我们两个都是有顺序的会变成怎样呢?

首先调整之后文件的顺序如下:

代码语言:javascript
复制
export const Employees = {
  Andy: 'I can sing',
  Ivan: 'I can rap',
  Oliver: 'I can run'
};

同事 A 添加代码如下:

代码语言:javascript
复制
export const Employees = {
  Andy: 'I can sing',
+ Brad: 'I am Brad',
  Ivan: 'I can rap',
  Oliver: 'I can run'
};

我添加代码如下:

代码语言:javascript
复制
export const Employees = {
  Andy: 'I can sing',
  Ivan: 'I can rap',
  Oliver: 'I can run',
+ Patrick: 'I am Patrick'
};

这个时候,当我们合代码的时候就没有冲突了

通过工具去排序

知道了顺序对于我们避免解决冲突的重要性,那么接下来就是要执行了,如果要求同事们都时刻遵循写代码的顺序,显然不太合理,我们打算用工具去执行。

具体步骤如下:

接下来,我用 vue-cli 演示下

时机——pre-commit:在 git commit 之前,可以使用 git hooks 做到

在 package.json 中加入如下代码,意思是在 git commit 之前会去执行 node ./auto-fix/index.js 以及 git add 命令

代码语言:javascript
复制
  "lint-staged": {
    "*.{js}": [
      "node ./auto-fix/index.js",
      "git add"
    ]
  },
  "gitHooks": {
    "pre-commit": "lint-staged"
  },

如果发现没有触发的话,可以看下是不是没有这两个包 lint-stagedyorkie。可以安装一下,如下所示:

代码语言:javascript
复制
npm i lint-staged --save-dev
npm i yorkie --save-dev  

其中 yorkie[1] 是 尤大大 folk husky[2],它俩功能是一样的,都是生成一些 git hooks 文件,读取项目中package.json 的相关配置项去执行一些命令,区别是尤大做了一些逻辑和配置上的改动

读取和排序:寻找指定目录下的文件(下面示例为 src/constant 目录下),匹配出文件中的对象,针对对象排序

这里排序的策略是先针对 value 值进行排序,如果 value 值相同,再针对 key 值排序

写入:将排好序的文件写回原文件

提示:成功或者失败的提示

具体做法以及代码,新建文件 auto-fix/index.js, 代码如下:

代码语言:javascript
复制
const fs = require('fs');
const path = require('path');
const chalk = require('chalk');

function resolve(p) {
  return path.join(__dirname, '..', p);
}

const objRegex = /\{[^}\/\/]*\}/g;

function fileDisplay(filePath) {
  let file = resolve(filePath);
  let fileList = fs.readdirSync(file, 'utf8');

  function compare() {
    return function ([key1, value1],[key2, value2]) {
      key1 = Number.isNaN(Number(key1)) ? key1.toLowerCase() : key1;
      key2 = Number.isNaN(Number(key2)) ? key2.toLowerCase() : key2;
      value1 = Number.isNaN(Number(value1)) ? value1.toLowerCase() : value1;
      value2 = Number.isNaN(Number(value2)) ? value2.toLowerCase() : value2;
      if (value1 === value2) {
        return key1 < key2 ? -1 : key1 > key2 ? 1 : 0; // 升序
      } else {
        return value1 < value2 ? -1 : value1 > value2 ? 1 : 0;
      }
    }
  }

  // 读取文件
  fileList.forEach(filename => {
    let fileDir = path.join(filePath, filename);
    console.log(chalk.cyan(`Auto fix ${fileDir}`))
    let fileContents = fs.readFileSync(fileDir, 'utf8');

    function sortObj(item) {
      item = item.replace(/(\S+):/g,"\"$1\":")
      .replace(/'/g, '"');

      let arr = [];
      item = JSON.parse(item);
      arr = Object.entries(item);
      // 排序
      arr = arr.sort(compare());
      let tempObj = {};
      // 将排序好的数组拼接成对象
      tempObj = arr.reduce((_sortedObj, [key, val]) => ({
        ..._sortedObj,
        [key]: val
      }), {});
      let tempStr = JSON.stringify(tempObj, null, 2);
      // 去掉 key 值的双引号
      tempStr = tempStr.replace(/"/g, "").replace(/\: /g,"\: \'").replace(/\,/g,"\'\,").replace(/\n\}/g,"\'\n\}");
      return tempStr;
    }

    // 匹配对象并排序并替换
    fileContents = fileContents.replace(objRegex, function(match) {
      return sortObj(match)
    });

    // 输出到文件中
    fs.writeFileSync(fileDir, fileContents, 'utf8');
  });

}
try {
  fileDisplay('src/constant');
  console.log(chalk.green(`Auto fix complete`))
} catch (e) {
  console.log(e);
  console.log(chalk.red(`Auto fix Error, You can check the the problem in the auto-fix/index.js directory`))
  throw new Error(`Auto fix Error, You can check the the problem in the auto-fix/index.js directory`)
}

结果

Gif 演示一下,可以看到在 git commit 的时候,就会自动排序

探索:对象属性遍历有顺序么?

我们常说,数组遍历是有顺序的,也经常说对对象遍历是无序的。

但实际上我理解这个“无序”指的只是不会按照属性排列前后的顺序而已,对象属性遍历本身是有自己的一套规则的。

可以实验一下

代码语言:javascript
复制
let obj = { [Symbol()]:0, b:0, 10:0, 2:0, a:0 };
// for...in循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)
// 输出 2 10 b a
for (let key in obj) {
  console.log(key)
}
// Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。
// [ '2', '10', 'b', 'a' ]
console.log(Object.keys(obj));

// Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。
// [ '2', '10', 'b', 'a' ]
console.log(Object.getOwnPropertyNames(obj));

// Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有 Symbol 属性的键名。
// [ Symbol() ]
console.log(Object.getOwnPropertySymbols(obj));

// Reflect.ownKeys返回一个数组,包含对象自身的(不含继承的)所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。
// [ '2', '10', 'b', 'a', Symbol() ]
console.log(Reflect.ownKeys(obj));

从上面的实验来看,总结如下:

  • 首先遍历所有数值键,按照数值升序排列
  • 其次遍历所有字符串键,按照加入时间升序排列
  • 最后遍历所有 Symbol 键,按照加入时间升序排列

回顾实现——也存在类似问题

上面的实现中,我是先根据属性的 value 值排序,如果 value 值相同再根据 key 值排序。排序后得到一个有顺序的二维数组,类似如下:

代码语言:javascript
复制
[["Andy","I am Andy"],["Gopal","I am Gopal"],["Ivan","I am Ivan"],["Oliver","I am Oliver"],["Patrick","I am Patrick"]]

然后遍历数组依次写入对象中,这样看起来似乎是没有问题,结果也是没有问题!但假如 key 值是 number 类型呢?比如类似如下:

代码语言:javascript
复制
export const Employees = {
  Andy: 'I am Andy',
  Gopal: 'I am Gopal',
  Ivan: 'I am Ivan',
  1: 'Z',
  2: 'A',
  3: 'J',
  Oliver: 'I am Oliver',
  Patrick: 'I am Patrick'
};

按照预期,按照 value 排序,应该得到

代码语言:javascript
复制
export const Employees = {
  2: 'A',
  Andy: 'I am Andy',
  Gopal: 'I am Gopal',
  Ivan: 'I am Ivan',
  Oliver: 'I am Oliver',
  Patrick: 'I am Patrick'
  3: 'J',
  1: 'Z'
};

但事实上,我们得到了类似如下的结果:

代码语言:javascript
复制
export const Employees = {
  1: 'Z',
  2: 'A',
  3: 'J',
  Andy: 'I am Andy',
  Gopal: 'I am Gopal',
  Ivan: 'I am Ivan',
  Oliver: 'I am Oliver',
  Patrick: 'I am Patrick'
};

这个实际上就是跟我们上面提到的属性遍历规则有关,因为首先遍历所有数值键,按照数值升序排列。其次遍历所有字符串键,按照加入时间升序排列。

缺点

除了上面所说的属性顺序问题【这个其实还好,是按照一定的规则去排序的】,如果你细读上面的代码,其实是会发现一些问题,也算是一些 TODO 项,如果有朋友解决了,欢迎给我提个 pr

功能上:

  • 目前只对简单的对象有用,嵌套的对象无效
  • 对象中如果书写注释,将导致对象无法匹配到

其他:

  • 对开发者的代码进行了更改,有可能开发者会有疑惑(这一点,我尽量使用提示去说明)
  • 能避免大部分冲突,但实际上不能 100%

总结

本文记录了一次通过利用 git hooks 在代码提交之前给相关的代码排序,从而减少合代码时候的冲突,也探讨了一下 Js 对象属性遍历属性的问题,希望对大家有所启发。

涉及到的 demo 已放 Github[3],有更好的想法可以提 PR

原创不易,欢迎点赞评论~

往期优秀文章推荐

  • 【webpack 性能优化】编译速度从 50S 到 7S[4]
  • 一个合格的中级前端工程师应该掌握的 20 个 Vue 技巧[5]
  • 【Vue进阶】——如何实现组件属性透传?[6]
  • 前端应该知道的 HTTP 知识【金九银十必备】[7]
  • 最强大的 CSS 布局 —— Grid 布局[8]
  • 如何用 Typescript 写一个完整的 Vue 应用程序[9]
  • 前端应该知道的web调试工具——whistle[10]

参考

  • How to avoid git conflicts in a team?[11]
  • vue-cli创建的项目中的gitHooks原理解析[12]

参考资料

[1]

yorkie: https://github.com/yyx990803/yorkie

[2]

husky: https://github.com/typicode/husky

[3]

Github: https://github.com/GpingFeng/my-auto-fix

[4]

【webpack 性能优化】编译速度从 50S 到 7S: https://juejin.im/post/6887863430510968839

[5]

一个合格的中级前端工程师应该掌握的 20 个 Vue 技巧: https://juejin.im/post/6872128694639394830

[6]

【Vue进阶】——如何实现组件属性透传?: https://juejin.im/post/6865451649817640968

[7]

前端应该知道的 HTTP 知识【金九银十必备】: https://juejin.im/post/6864119706500988935

[8]

最强大的 CSS 布局 —— Grid 布局: https://juejin.im/post/6854573220306255880

[9]

如何用 Typescript 写一个完整的 Vue 应用程序: https://juejin.im/post/6860703641037340686

[10]

前端应该知道的web调试工具——whistle: https://juejin.im/post/6861882596927504392

[11]

How to avoid git conflicts in a team?: https://stackoverflow.com/questions/16490873/how-to-avoid-git-conflicts-in-a-team

[12]

vue-cli创建的项目中的gitHooks原理解析: https://juejin.im/post/6844904063969001480

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-11-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端杂货铺 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 起因
  • 为什么顺序那么重要
  • 通过工具去排序
  • 结果
  • 探索:对象属性遍历有顺序么?
  • 回顾实现——也存在类似问题
  • 缺点
  • 总结
  • 往期优秀文章推荐
  • 参考
    • 参考资料
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档