python实现web svn diff
最近想做一个在线codereivew的功能,需要在web页面上分两栏(side-by-side)展示新老代码的差异(diff)。
在github参考了一些项目,例如:coderev、cdiff;经过一定的尝试,最终选择了使用Python来实现这个功能,因为python内置的difflib标准库里有一个计算diff的函数是专门用来生成两栏(side-by-side)视图用的,而这个算法是挺复杂的,网上并没有资料讲解,所以拿来即用吧。
项目地址:https://github.com/owenliang/side-by-side-diff
项目效果:https://owenliang.github.io/side-by-side-diff/sample/side-by-side-view.html
思路
生成patch
首先,需要生成diff内容,也就是patch。这种patch文件的格式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
Index: bgm/application_zdm/views/mgc_detail.php =================================================================== --- bgm/application_zdm/views/mgc_detail.php (revision 172736) +++ bgm/application_zdm/views/mgc_detail.php (working copy) @@ -189,7 +189,7 @@ </div> </div> <div class="button-group pull-right"> - <a class="btn btn-primary btn-lg btn-submit" name="publish"><?= $status != 2 ? '发布榜单' : '保存榜单' ?></a> + <a class="btn btn-primary btn-lg btn-submit" name="publish"><?= $status == 2 ? '保存榜单' : '发布榜单' ?></a> <a class="btn btn-success btn-lg btn-submit" name="draft"><?= $status != 2 ? '保存为草稿' : '榜单已发布,退回为草稿' ?></a> </div> </form> @@ -236,27 +236,6 @@ var uiLi3=[{n:"请选择",k:0}]; $(this).find("ul:first").children().each(function(){ uiLi3.push({ - n:$(this).find("a:first").html(), - k:$(this).find("input:first").val() - }); - }); - uiLi2.push({ - s:$(this).find("a:first").html(), - k:$(this).find("input:first").val(), - r:uiLi3 - }); - }); - uiLi1.push({ - n:$(this).find("a:first").html(), - k:$(this).find("input:first").val(), - a:uiLi2 - }); - }); - cateList.push({ - p:$(this).find("a:first").html(), - k:$(this).find("input:first").val(), - c:uiLi1 - }); }); cateJson = {dataList:cateList}; return cateJson; @@ -285,42 +264,19 @@ } }; - var toolBox={ - cateSelected: function (curSelected) { - //初始化商品分类json数据 - handleCategory.getCateJson(function(data){ - var curSelectedArr=[]; - var catSelectConf={ - jsonData:data, - first:0,//电脑数码 - second:0,//手机通讯 - third:0,//非智能手机 - four:0, - nodata:"none" + var toolBox={vwregergweartvwertgreqgewr }; if(curSelected){//商品分类id匹配成功 curSelectedArr=curSelected.split(","); if(curSelectedArr.length>0){ $.each(curSelectedArr,function(index,cid){ - if(index==0){ - catSelectConf.first=cid; - }else if(index==1){ - catSelectConf.second=cid; - }else if(index==2){ - catSelectConf.third=cid; - }else if(index==3){ - catSelectConf.four=cid; - }else{ + if(index=sfawdv + fdd + fvweq + fa + wefawre + fgvwref - } - }) - } - } - handleCategory.cateInit(catSelectConf);//选中的商品分类填充到select - }); - }, - }; - toolBox.cateSelected("<?= implode(',', $categories) ?>"); </script> @@ -341,38 +297,7 @@ $('#tag-selector').select2({ ajax: { - url: '/tool/tag_search_by_title', - dataType: 'json', - data: function (params) { - return { - term: params.term, - page: params.page - }; - }, - processResults: function (data) { - return { - results: data.data, - pagination: { - more: data.data.length > 0 - } - }; - } - }, - placeholder: '键入关键词开始搜索...', - minimumInputLength: 1 - }); - - $('.btn-submit').click(function (event) { - event.preventDefault(); - - var formData = $(this).closest('form').serializeArray(); - - switch (this.name) { - case 'draft': - formData.push({name: 'status', value: 1}); - formData.push({name: 'is_deleted', value: 0}); - break; - case 'publish': + formData.push({name: 'status', value: 2}); formData.push({name: 'is_deleted', value: 0}); break; |
一般我们通过svn diff或者git diff就可以生成,或者通过diff -u filename1 filename2也可以生成。
解析patch
我们需要解析patch文件,这个过程并不复杂。
类似于这种段落,表示是某个文件发生了变更:
1 2 3 4 |
Index: bgm/application_zdm/views/mgc_detail.php =================================================================== --- bgm/application_zdm/views/mgc_detail.php (revision 172736) +++ bgm/application_zdm/views/mgc_detail.php (working copy) |
类似于这种段落,表示文件内的某一段发生了变化:
1 2 3 4 5 6 7 8 9 |
@@ -189,7 +189,7 @@ </div> </div> <div class="button-group pull-right"> - <a class="btn btn-primary btn-lg btn-submit" name="publish"><?= $status != 2 ? '发布榜单' : '保存榜单' ?></a> + <a class="btn btn-primary btn-lg btn-submit" name="publish"><?= $status == 2 ? '保存榜单' : '发布榜单' ?></a> <a class="btn btn-success btn-lg btn-submit" name="draft"><?= $status != 2 ? '保存为草稿' : '榜单已发布,退回为草稿' ?></a> </div> </form> |
可见,上述段落删掉了一行,又增加了一行,相当于替换了原先的行。
而没有-号与+号的行,称为上下文(context)行,表示新老文件都拥有且没有变化过的行。
还原老与新段落
对于上述每个段落,我们需要还原出老文件和新文件。
context和-的行是老文件拥有的,而context和+的行是新文件拥有的,通过这样划分就可以得到每个差异段落的老内容和新内容。
调用python重算diff
现在可以将每个段落的老内容和新内容,作为一个pair传递给python的difflib._mdiff函数,这个函数会计算返回一个数组,每一个元素代表了双栏(side-by-side)视图中的一行。
我们需要根据_mdiff的返回值,对新老的行号进行重新标记。
在我的diff.py程序中,最终输出的结果在这里查看:https://raw.githubusercontent.com/owenliang/side-by-side-diff/master/sample/diff.json。
可以观察到,最外层数组是若干的index,即发生变化的文件。
而index.segments里的每一个元素则是一个发生差异的段落。
段落内的rows记录了差异的行,我们只需要顺序的展示为两栏即可;左侧是老文件的行内容,右侧是新文件的行内容,红色标识变化前的行,绿色标识变化后的行。
如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~
