
- 描 述
- diff 命令比较文本文件
- 用途语法
- 比较两个文件的内容
- 标 志
- 将每行末尾当作单个新行标记处理
- diff 命令
- 只有当输入为文本文件时才有效
- 外文名
- diff
-
diff
2019-07-29 11:32:39diff是Unix系统的一个很重要的工具程序。 它用来比较两个文本文件的差异,是代码版本管理的基石之一。你在命令行下,输入: $ diff <变动前的文件> <变动后的文件> diff就会告诉你,这两个文件有何差异...diff是Unix系统的一个很重要的工具程序。
它用来比较两个文本文件的差异,是代码版本管理的基石之一。你在命令行下,输入:
$ diff <变动前的文件> <变动后的文件>
diff就会告诉你,这两个文件有何差异。它的显示结果不太好懂,下面我就来说明,如何读懂diff。
一、diff的三种格式
由于历史原因,diff有三种格式:
* 正常格式(normal diff) * 上下文格式(context diff) * 合并格式(unified diff)
我们依次来看。
二、示例文件
为了便于讲解,先新建两个示例文件。
第一个文件叫做f1,内容是每行一个a,一共7行。
a a a a a a a
第二个文件叫做f2,修改f1而成,第4行变成b,其他不变。
a a a b a a a
三、正常格式的diff
现在对f1和f2进行比较:
$ diff f1 f2
这时,diff就会显示正常格式的结果:
4c4 < a --- > b
第一行是一个提示,用来说明变动位置。
4c4
它分成三个部分:前面的"4",表示f1的第4行有变化;中间的"c"表示变动的模式是内容改变(change),其他模式还有"增加"(a,代表addition)和"删除"(d,代表deletion);后面的"4",表示变动后变成f2的第4行。
第二行分成两个部分。
< a
前面的小于号,表示要从f1当中去除该行(也就是第4行),后面的"a"表示该行的内容。
第三行用来分割f1和f2。
---
第四行,类似于第二行。
> b
前面的大于号表示f2增加了该行,后面的"b"表示该行的内容。
最早的Unix(即AT&T版本的Unix),使用的就是这种格式的diff。
四、上下文格式的diff
上个世纪80年代初,加州大学伯克利分校推出BSD版本的Unix时,觉得diff的显示结果太简单,最好加入上下文,便于了解发生的变动。因此,推出了上下文格式的diff。
它的使用方法是加入c参数(代表context)。
$ diff -c f1 f2
显示结果如下:
*** f1 2012-08-29 16:45:41.000000000 +0800 --- f2 2012-08-29 16:45:51.000000000 +0800 *************** *** 1,7 **** a a a !a a a a --- 1,7 ---- a a a !b a a a
这个结果分成四个部分。
第一部分的两行,显示两个文件的基本情况:文件名和时间信息。
*** f1 2012-08-29 16:45:41.000000000 +0800 --- f2 2012-08-29 16:45:51.000000000 +0800
"***“表示变动前的文件,”—"表示变动后的文件。
第二部分是15个星号,将文件的基本情况与变动内容分割开。
***************
第三部分显示变动前的文件,即f1。
*** 1,7 **** a a a !a a a a
这时不仅显示发生变化的第4行,还显示第4行的前面三行和后面三行,因此一共显示7行。所以,前面的"*** 1,7 ****"就表示,从第1行开始连续7行。
另外,文件内容的每一行最前面,还有一个标记位。如果为空,表示该行无变化;如果是感叹号(!),表示该行有改动;如果是减号(-),表示该行被删除;如果是加号(+),表示该行为新增。
第四部分显示变动后的文件,即f2。
--- 1,7 ---- a a a !b a a a
除了变动行(第4行)以外,也是上下文各显示三行,总共显示7行。
五、合并格式的diff
如果两个文件相似度很高,那么上下文格式的diff,将显示大量重复的内容,很浪费空间。1990年,GNU diff率先推出了"合并格式"的diff,将f1和f2的上下文合并在一起显示。
它的使用方法是加入u参数(代表unified)。
$ diff -u f1 f2
显示结果如下:
--- f1 2012-08-29 16:45:41.000000000 +0800 +++ f2 2012-08-29 16:45:51.000000000 +0800 @@ -1,7 +1,7 @@ a a a -a +b a a a
它的第一部分,也是文件的基本信息。
--- f1 2012-08-29 16:45:41.000000000 +0800 +++ f2 2012-08-29 16:45:51.000000000 +0800
"—“表示变动前的文件,”+++"表示变动后的文件。
第二部分,变动的位置用两个@作为起首和结束。
@@ -1,7 +1,7 @@
前面的"-1,7"分成三个部分:减号表示第一个文件(即f1),"1"表示第1行,“7"表示连续7行。合在一起,就表示下面是第一个文件从第1行开始的连续7行。同样的,”+1,7"表示变动后,成为第二个文件从第1行开始的连续7行。
第三部分是变动的具体内容。
a a a -a +b a a a
除了有变动的那些行以外,也是上下文各显示3行。它将两个文件的上下文,合并显示在一起,所以叫做"合并格式"。每一行最前面的标志位,空表示无变动,减号表示第一个文件删除的行,加号表示第二个文件新增的行。
六、git格式的diff
版本管理系统git,使用的是合并格式diff的变体。
$ git diff
显示结果如下:
diff --git a/f1 b/f1 index 6f8a38c..449b072 100644 --- a/f1 +++ b/f1 @@ -1,7 +1,7 @@ a a a -a +b a a a
第一行表示结果为git格式的diff。
diff --git a/f1 b/f1
进行比较的是,a版本的f1(即变动前)和b版本的f1(即变动后)。
第二行表示两个版本的git哈希值(index区域的6f8a38c对象,与工作目录区域的449b072对象进行比较),最后的六位数字是对象的模式(普通文件,644权限)。
index 6f8a38c..449b072 100644
第三行表示进行比较的两个文件。
--- a/f1 +++ b/f1
"—“表示变动前的版本,”+++"表示变动后的版本。
后面的行都与官方的合并格式diff相同。
@@ -1,7 +1,7 @@ a a a -a +b a a a
七、阅读材料
* diff - Wikipedia
* How to read a patch or diff
* How to work with diff representation in git
(完)
-
Diff
2016-05-05 23:29:031 "git diff" 与暂存区相比,对未存档的文件做哪些修改; 2 --staged, HEAD 2.1 已存档的文件,查看暂存区和最近提交的历史文件的区别 "git diff --stages" 2.2 查看文件修改与最新提交的历史文件的区别 "git ...1 "git diff"
与暂存区相比,对未存档的文件做哪些修改;
2 --staged, HEAD
2.1 已存档的文件,查看暂存区和最近提交的历史文件的区别
"git diff --stages"
2.2 查看文件修改与最新提交的历史文件的区别
"git diff HEAD"
其中,HEAD表示最后一次提交的别名;HEAD^表示前一次提交的别名;HEAD~n标识前n次提交的别名;
3 --color-words, --word-diff
针对长行小改动,使用如下命令查看区别
"git diff --color-words"
"git diff --word-diff"
4 --stat
"git diff-stat" 只查看哪个文件修改
-
diff参数
2020-08-24 14:36:40diff(选项)(参数) 选项 -<行数>:指定要显示多少行的文本。此参数必须与-c或-u参数一并使用; -a或——text:diff预设只会逐行比较文本文件; -b或--ignore-space-change:不检查空格字符的不同; -B或--...语法
diff(选项)(参数)
选项
-<行数>:指定要显示多少行的文本。此参数必须与-c或-u参数一并使用;
-a或——text:diff预设只会逐行比较文本文件;
-b或--ignore-space-change:不检查空格字符的不同;
-B或--ignore-blank-lines:不检查空白行;
-c:显示全部内容,并标出不同之处;
-C<行数>或--context<行数>:与执行“-c-<行数>”指令相同;
-d或——minimal:使用不同的演算法,以小的单位来做比较;
-D<巨集名称>或ifdef<巨集名称>:此参数的输出格式可用于前置处理器巨集;
-e或——ed:此参数的输出格式可用于ed的script文件;
-f或-forward-ed:输出的格式类似ed的script文件,但按照原来文件的顺序来显示不同处;
-H或--speed-large-files:比较大文件时,可加快速度;
-l<字符或字符串>或--ignore-matching-lines<字符或字符串>:若两个文件在某几行有所不同,而之际航同时都包含了选项中指定的字符或字符串,则不显示这两个文件的差异;
-i或--ignore-case:不检查大小写的不同;
-l或——paginate:将结果交由pr程序来分页;
-n或——rcs:将比较结果以RCS的格式来显示;
-N或--new-file:在比较目录时,若文件A仅出现在某个目录中,预设会显示:Only in目录,文件A 若使用-N参数,则diff会将文件A 与一个空白的文件比较;
-p:若比较的文件为C语言的程序码文件时,显示差异所在的函数名称;
-P或--unidirectional-new-file:与-N类似,但只有当第二个目录包含了第一个目录所没有的文件时,才会将这个文件与空白的文件做比较;
-q或--brief:仅显示有无差异,不显示详细的信息;
-r或——recursive:比较子目录中的文件;
-s或--report-identical-files:若没有发现任何差异,仍然显示信息;
-S<文件>或--starting-file<文件>:在比较目录时,从指定的文件开始比较;
-t或--expand-tabs:在输出时,将tab字符展开;
-T或--initial-tab:在每行前面加上tab字符以便对齐;
-u,-U<列数>或--unified=<列数>:以合并的方式来显示文件内容的不同;
-v或——version:显示版本信息;
-w或--ignore-all-space:忽略全部的空格字符;
-W<宽度>或--width<宽度>:在使用-y参数时,指定栏宽;
-x<文件名或目录>或--exclude<文件名或目录>:不比较选项中所指定的文件或目录;
-X<文件>或--exclude-from<文件>;您可以将文件或目录类型存成文本文件,然后在=<文件>中指定此文本文件;
-y或--side-by-side:以并列的方式显示文件的异同之处;
--help:显示帮助;
--left-column:在使用-y参数时,若两个文件某一行内容相同,则仅在左侧的栏位显示该行内容;
--suppress-common-lines:在使用-y参数时,仅显示不同之处。
还有个colordiff命令,用颜色标识不同的地方。需要先安装 -
Diff算法
2019-06-12 08:38:53Diff算法 什么是Diff算法? diff算法作为Virtual DOM的加速器,其算法的改进优化是React整个界面渲染的基础和性能的保障,同时也是React源码中最神秘的,最不可思议的部分 传统Diff: 计算一棵树形结构转换为另一棵...Diff算法
什么是Diff算法?
diff算法作为Virtual DOM的加速器,其算法的改进优化是React整个界面渲染的基础和性能的保障,同时也是React源码中最神秘的,最不可思议的部分
传统Diff:
计算一棵树形结构转换为另一棵树形结构需要最少步骤,如果使用传统的diff算法通过循环递归遍历节点进行对比,其复杂度要达到O(n^3),其中n是节点总数,效率十分低下,假设我们要展示1000个节点,那么我们就要依次执行上十亿次的比较。
下面附上一则简单的传统diff算法:
let result = []; // 比较叶子节点 const diffLeafs = function (beforeLeaf, afterLeaf) { // 获取较大节点树的长度 let count = Math.max(beforeLeaf.children.length, afterLeaf.children.length); // 循环遍历 for (let i = 0; i < count; i++) { const beforeTag = beforeLeaf.children[i]; const afterTag = afterLeaf.children[i]; // 添加 afterTag 节点 if (beforeTag === undefined) { result.push({ type: "add", element: afterTag }); // 删除 beforeTag 节点 } else if (afterTag === undefined) { result.push({ type: "remove", element: beforeTag }); // 节点名改变时,删除 beforeTag 节点,添加 afterTag 节点 } else if (beforeTag.tagName !== afterTag.tagName) { result.push({ type: "remove", element: beforeTag }); result.push({ type: "add", element: afterTag }); // 节点不变而内容改变时,改变节点 } else if (beforeTag.innerHTML !== afterTag.innerHTML) { if (beforeTag.children.length === 0) { result.push({ type: "changed", beforeElement: beforeTag, afterElement: afterTag, html: afterTag.innerHTML }); } else { // 递归比较 diffLeafs(beforeTag, afterTag); } } } return result; }
React Diff算法优化策略图
- React更新阶段会对ReactElement类型判断而进行不同的操作;ReactElement类型包含三种即:文本、Dom、组件;
- 每个类型的元素更新处理方式:
- 自定义元素的更新,主要是更新render出的节点,做甩手掌柜交给render出的节点的对应component去管理更新。
- text节点的更新很简单,直接更新文案。
- 浏览器基本元素的更新,分为两块:
- 更新属性,对比出前后属性的不同,局部更新。并且处理特殊属性,比如事件绑定。
- 子节点的更新,子节点更新主要是找出差异对象,找差异对象的时候也会使用上面的shouldUpdateReactComponent来判断,如果是可以直接更新的就会递归调用子节点的更新,这样也会递归查找差异对象。不可直接更新的删除之前的对象或添加新的对象。之后根据差异对象操作dom元素(位置变动,删除,添加等)
React中Diff算法的实现
React Diff:
之前说过,React采用虚拟DOM技术实现对真实DOM的映射,即React Diff算法的差异查找实质是对两个JavaScript对象的差异查找;
基于三个策略:
1.Web UI中DOM节点跨层级的移动操作特别少,可以忽略不计。(tree diff) 2.拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的树形结。(component diff) 3.对于同一层级的一组子节点,它们可以通过唯一ID进行区分。(element diff)
对于以上三个策略,react分别对tree diff,component diff,element diff进行算法优化。
1.tree diff
基于策略一,WebUI中DOM节点跨层级的移动操作少的可以忽略不计,React对Virtual DOM树进行层级控制,只会对相同层级的DOM节点进行比较,即同一个父元素下的所有子节点,当发现节点已经不存在了,则会删除掉该节点下所有的子节点,不会再进行比较。这样只需要对DOM树进行一次遍历,就可以完成整个树的比较。复杂度变为O(n);
疑问:当我们的DOM节点进行跨层级操作时,diff会有怎么样的表现呢?
如下图所示,A节点及其子节点被整个移动到D节点下面去,
由于React只会简单的考虑同级节点的位置变换,而对于不同层级的节点,只有创建和删除操作,
所以当根节点发现A节点消失了,就会删除A节点及其子节点,当D发现多了一个子节点A,就会创建新的A作为其子节点。
此时,diff的执行情况是:createA-->createB-->createC-->deleteA
Tree DIFF是对树的每一层进行遍历,如果某组件不存在了,则会直接销毁。如图所示,左边是旧属,右边是新属,第一层是R组件,一模一样,不会发生变化;第二层进入Component DIFF,同一类型组件继续比较下去,发现A组件没有,所以直接删掉A、B、C组件;继续第三层,重新创建A、B、C组件。
由此可以发现,当出现节点跨层级移动时,并不会出现想象中的移动操作,而是会进行删除,重新创建的动作,这是一种很影响React性能的操作。因此官方也不建议进行DOM节点跨层级的操作。
2.componnet diff
React是基于组件构建应用的,对于组件间的比较所采用的策略也是非常简洁和高效的。
-
如果是同一个类型的组件,则按照原策略进行Virtual DOM比较。
-
如果不是同一类型的组件,则将其判断为dirty component,从而替换整个组价下的所有子节点。
-
如果是同一个类型的组件,有可能经过一轮Virtual DOM比较下来,并没有发生变化。如果我们能够提前确切知道这一点,那么就可以省下大量的diff运算时间。因此,React允许用户通过shouldComponentUpdate()来判断该组件是否需要进行diff算法分析。
如下图所示,当组件D变为组件G时,即使这两个组件结构相似,
一旦React判断D和G是不用类型的组件,就不会比较两者的结构,而是直接删除组件D,重新创建组件G及其子节点。
虽然当两个组件是不同类型但结构相似时,进行diff算法分析会影响性能,但是毕竟不同类型的组件存在相似DOM树的情况在实际开发过程中很少出现,因此这种极端因素很难在实际开发过程中造成重大影响。3.element diff
当节点属于同一层级时,diff提供了3种节点操作,分别为INSERT_MARKUP(插入),MOVE_EXISTING(移动),REMOVE_NODE(删除)。
- INSERT_MARKUP:新的组件类型不在旧集合中,即全新的节点,需要对新节点进行插入操作。
- MOVE_EXISTING:旧集合中有新组件类型,且element是可更新的类型,这时候就需要做移动操作,可以复用以前的DOM节点。
- REMOVE_NODE:旧组件类型,在新集合里也有,但对应的element不同则不能直接复用和更新,需要执行删除操作,或者旧组件不在新集合里的,也需要执行删除操作。
Element DIFF紧接着以上统一类型组件继续比较下去,常见类型就是列表。同一个列表由旧变新有三种行为,插入、移动和删除,它的比较策略是对于每一个列表指定key,
先将所有列表遍历一遍,确定要新增和删除的,再确定需要移动的。
如图所示,第一步将D删掉,第二步增加E,再次执行时A和B只需要移动位置即可。React中Diff算法实现的代码
_updateChildren: function(nextNestedChildrenElements, transaction, context) { var prevChildren = this._renderedChildren; var removedNodes = {}; var mountImages = []; // 获取新的子元素数组 var nextChildren = this._reconcilerUpdateChildren( prevChildren, nextNestedChildrenElements, mountImages, removedNodes, transaction, context ); if (!nextChildren && !prevChildren) { return; } var updates = null; var name; var nextIndex = 0; var lastIndex = 0; var nextMountIndex = 0; var lastPlacedNode = null; for (name in nextChildren) { if (!nextChildren.hasOwnProperty(name)) { continue; } var prevChild = prevChildren && prevChildren[name]; var nextChild = nextChildren[name]; if (prevChild === nextChild) { // 同一个引用,说明是使用的同一个component,所以我们需要做移动的操作 // 移动已有的子节点 // NOTICE:这里根据nextIndex, lastIndex决定是否移动 updates = enqueue( updates, this.moveChild(prevChild, lastPlacedNode, nextIndex, lastIndex) ); // 更新lastIndex lastIndex = Math.max(prevChild._mountIndex, lastIndex); // 更新component的.mountIndex属性 prevChild._mountIndex = nextIndex; } else { if (prevChild) { // 更新lastIndex lastIndex = Math.max(prevChild._mountIndex, lastIndex); } // 添加新的子节点在指定的位置上 updates = enqueue( updates, this._mountChildAtIndex( nextChild, mountImages[nextMountIndex], lastPlacedNode, nextIndex, transaction, context ) ); nextMountIndex++; } // 更新nextIndex nextIndex++; lastPlacedNode = ReactReconciler.getHostNode(nextChild); } // 移除掉不存在的旧子节点,和旧子节点和新子节点不同的旧子节点 for (name in removedNodes) { if (removedNodes.hasOwnProperty(name)) { updates = enqueue( updates, this._unmountChild(prevChildren[name], removedNodes[name]) ); } } }
基于中Diff的开发建议
基于tree diff:
- 开发组件时,注意保持DOM结构的稳定;即,尽可能少地动态操作DOM结构,尤其是移动操作。
- 当节点数过大或者页面更新次数过多时,页面卡顿的现象会比较明显。
- 这时可以通过 CSS 隐藏或显示节点,而不是真的移除或添加 DOM 节点。
基于component diff:
- 注意使用 shouldComponentUpdate() 来减少组件不必要的更新。
- 对于类似的结构应该尽量封装成组件,既减少代码量,又能减少component diff的性能消耗。
基于element diff:
- 对于列表结构,尽量减少类似将最后一个节点移动到列表首部的操作,当节点数量过大或更新操作过于频繁时,在一定程度上会影响 React 的渲染性能。
-
diff算法
2020-11-23 16:04:311.tree diff2.componnet diff3.element diff基于中Diff的开发建议基于tree diff基于component diff基于element diff 一直都没有好好的看一下diff算法 今天搞一下 什么是Diff算法? diff算法作为Virt -
excel diff
2015-03-17 22:06:51能够diff excel,实现svn diff的功能,作为插件使用 -
一分钟区分git diff、git diff --cached、git diff HEAD
2020-10-16 14:14:27对比git diff命令之前,我们先新建一个test.txt文本,先后执行如下命令 # 第一次编辑文本,新增第一行内容 first content. # 执行git命令-提交到版本库 $ git add test.txt $ git commit -m "first submit" # 第二次... -
diff diff3 命令
2017-10-30 13:43:39diff 命令在最简单的情况下,比较给定的两个文件的不同.如果使用'-'代替'文件'参数,则要比较的内容将来自标准输入.diff 命令是以逐行的方式,比较文本文件的异同处.如果该命令指定进行目录的比较,则将会... -
git diff/difftool
2019-07-30 20:27:00参考好文:使用命令和P4Merge...git difftool 即可弹出比较工具的界面 哈哈 =========================== 本地区域 git diff/difftool filepath 不带任何命令参数,工作区(本地文件)与暂存区比较,后面不带... -
Git diff
2020-02-18 19:41:27git diff显示提交,提交和工作树等之间的变化 此命令比较的是工作目录中当前文件和暂存区域快照之间的差异,也就是修改之后还没有暂存起来的变化内容。 常用命令: 比较版本库中任意两个分支的差异。 git diff <... -
svn diff
2020-08-06 07:31:00svn diff 文件名 svn diff 文件名 会查看本地版本库中所作的修改。 cd 到文件所属的目录下使用 svn diff 文件名 svn diff xx.cs 或者直接 svn diff 文件路径/文件名 svn diff xxx/xxx/xx.cs svn diff -r r... -
git diff的最全最详细的4大主流用法
2019-03-25 16:40:021.git diff用来比较文件之间的不同,其基本用法如下: (1)git diff:当工作区有改动,临时区为空,diff的对比是“工作区与最后一次commit提交的仓库的共同文件”;当工作区有改动,临时区不为空,diff对比的是... -
git diff
2019-10-01 17:01:32git diff 显示工作目录与索引区文件之间的差异git diff --cached显示索引区与git仓库之间的差异git diff HEAD 显示工作目录与git仓库之间的差异 git diff HEAD^ 比较上次提交 git diff HEAD~2 比较上2次提交--diff-... -
java-diff-utils:Diff Utils库是一个开放源代码库,用于在文本或某种数据之间执行比较diff操作:计算diff...
2021-02-03 20:32:55java-diff-utils 状态 介绍 Diff Utils库是一个开放源代码库,用于执行文本之间的比较操作:计算差异,应用补丁,生成统一的差异或对其进行解析,生成差异输出以方便将来显示(如并排视图)等等。 构建该库的主要... -
diff git_git diff长行
2020-07-27 18:38:08diff gitI find git diff output is not easy to read when the text file contains long lines that is long enough to exceeds the screen size. 我发现当文本文件包含的行足够长以超过屏幕尺寸时, git diff输出... -
git diff的生成方式:myers diff算法
2020-09-20 08:21:43每个开发者多少都接触过git的diff功能,如下图: 该功能能够展示出某个文件的改动信息,非常便于开发者的日常开发。 本文将探讨下此功能实现的原理。 myers diff 做了啥 引用个例子 a = ABCABBA b = CBABAC 现在... -
Linux diff
2019-04-30 11:45:58git diff 格式 git diff commitA commitB 可以是commit的hash值 也可使HEAD,HEAD~3等 git diff branch1 branch2 比较不同分支差异 git diff fileName 比较暂存区和工作区 只有通过 git add 跟踪过的文件... -
diff命令
2020-07-17 21:29:22diff命令 diff命令能够比较给定的两个文件的不同,如果使用-代替文件参数,则要比较的内容将来自标准输入,diff命令是以逐行的方式比较文本文件的异同处,如果该命令指定进行目录的比较,则将会比较该目录中具有相同... -
yapf-diff:`yapf`只有你的`diff`-源码
2021-01-29 19:51:20yapf_diff 仅更改已更改的内容 安装 与pipenv pipenv install git+https://github.com/luxresearch/yapf-diff.git@master#egg=yapf-diff 带点pip install git+... -
mysqldiff
2019-08-08 21:01:43本文介绍mysqldiff工具来比较数据表结构,并生成差异SQL语句。 mysqldiff类似Linux下的diff命令,用来比较对象的定义是否相同,并显示不同的地方。 如果要比较数据库是否一致,可以用另外一个工具:mysqldbcompare... -
linux 比较两个文件夹diff不同 (diff命令, md5列表)
2020-09-15 04:19:36主要介绍了linux 比较两个文件夹diff不同 (diff命令, md5列表),比较文件夹diff,可以直接使用diff命令,也可以比较文件md5列表,下面通过实例给大家介绍下,感兴趣的朋友跟随脚本之家小编一起学习吧