tiodot / tiodot.github.io

总结归纳--做的东西不是每一件都留到现在 但是能通过实践,收获了如何构建一套系统,一套工具的方法论
https://xchb.work
8 stars 0 forks source link

git diff结果解析 #25

Open tiodot opened 7 years ago

tiodot commented 7 years ago

在使用fecs检测代码是否符合规范时,其检测都是针对整个文件,而预期的是只针对文件修改处做检测。而要想对某一个文件修改处做检测,无非可以:

  1. fecs提供ReadableStream流的检测,可以只检测修改部门;
  2. 检测整个文件,然后筛选修改出的检测结果;

对应第一种方式,由于改的代码可能都是某一个函数中某几行,由于没有上下文,检测结果会包含需要无用的提示,例如变量未定义,缩进问题等。 所以只能尝试第二种方案了,要实现这种方案,前提是文件处于一个git仓库中,关键问题是如何解析文件修改部分。具体思路是:fecs检测结果中会包含不规范代码的位置信息,然后依据修改代码的位置过滤出相关结果。

1. 获取文件修改

git diff命令提供了对比两个文件,输出其差异化的功能。 image

使用git diff输出的是一个合并格式,相关说明请查看git-diff文件格式解析 。一眼看上去可以很清楚的知道改动点,但是却无法直观的知道改动的具体对应文件哪行?

2. 解析diff结果

对应git diff的结果:

@@ -4,6 +4,8 @@
  */

 function parseParams(search) {
+
+    console.log('dd解决');
     let ret = {},
         seg = search.replace(/^\?/, '').split('&');
     for (let i of seg) {

头部@@ -4,6 +4,8 @@显示了文件修改的大致范围,而具体的修改位置则在代码片段中+标识,而目标就是解析这些别修改代码的具体位置。

  1. 使用正则解析代码修改的起始位置,以及对应的代码片段;
  2. 通过解析代码片段,结合起始位置,精确定位修改的具体位置;
  3. 合并所有修改代码的位置信息。

代码实现为:

// 匹配代码修改正则
const unifiedDiffRegex = /^@@ -([\d]+),([\d]+) [+]([\d]+),([\d]+) @@([\s\S]*?)(?=^@@)/gm;

module.exports = function (str) {
    const chunks = parse(str);
    // 解析每一个代码修改片段,然后合并所有位置信息
    // chunks.map(parseChunk)  => [[8, 9], [18]]
    // [[0, 1], [2, 3], [4, 5]].reduce((a, b) => a.concat(b)) => [0, 1, 2, 3, 4, 5]
    return chunks.map(parseChunk).reduce((result, chunkLine) => result.concat(chunkLine));
};

// 通过正则解析git diff出来的代码修改的地方
function parse(data) {
    let match;
    let chunks = [];
    do {
        match = unifiedDiffRegex.exec(`${data}\n@@`);
        if (match === null)
            break;
        // Stops excessive memory usage
        // https://bugs.chromium.org/p/v8/issues/detail?id=2869
        let chunk = (' ' + match[5]).substr(1);
        let currentStart = parseInt(match[3], 10);
        chunks.push({
            chunk,
            start: currentStart,
            end: currentStart + parseInt(match[4], 10)
        })
    } while (match !== null);
    return chunks;
}
// 解析每一个代码修改片段的具体修改位置
function parseChunk(chunkData) {
    const lines = chunkData.chunk.split('\n');
    lines.shift(); // 代码修改片段以\n开头,所以移除
    const start = chunkData.start;
    const chunkLines = [];
    let offset = 0;
    for (const l of lines) {
        switch (l[0]) {
            case '+':
                chunkLines.push(start + offset);
                break;
            case '-':
                offset--;
                break;
            default:
                break;
        }
        offset++;
    }
    return chunkLines;
}

应用到上述中的git diff结果中, 其输出结果为[ 7, 8, 17 ],然后就可以过滤出fecs中检测结果。 未过滤之前的结果是: image

过滤之后的结果: image

参考

  1. git-diff文件格式解析
  2. gitLens的git diff parser
  3. 上述代码github地址