首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件开发 >

git怎么知晓文件差异

2013-02-19 
git如何知晓文件差异  求两版本之间的差异是一个动态规划问题  git 能发现任何的改动,但它是怎么发现的呢?

git如何知晓文件差异

  求两版本之间的差异是一个动态规划问题

  git 能发现任何的改动,但它是怎么发现的呢?难道它监控了我们对文件的读写操作? git 才没这么鸡冻……它是通过比较新旧版本,掐指一算算出来的O(∩_∩)O。

  首先假设我们只能通过以下3个操作将旧版本演化为新版本:

copy —— 复制旧版本当前行到新版本insert —— 在新版本中添加一行delete —— 跳过旧版本当前行

  那么,如下旧版本(左)到新版本(右):

122333
1333

 

可通过

方案一:copy、delete、copy方案二:delete、delete、delete、insert、insert

演化而来。

  旧版本可通过这3个操作演化成任意一个新版本,即使新版本已经面目全非:
  假设旧版本有n行,新版本有m行。不管它们每行的内容是什么,总是可以通过 n次delete 和 m次insert 演化出新版本。

  但是,由于 1个copy 能顶替 1个insert+1个delete,所以方案一比方案二少两个步骤。而且我们实际进行的操作就是步骤最少的方案一(呵呵,人类是最聪明的O(∩_∩)O)。

  现在我们有目标了:怎么得到一个步骤最少的演化方案?为了更清晰地描述演化过程,我们定义两个下标: i(旧版本行号)、j(新版本行号)。演化过程中会改变这两个下标:

copy : ++i,++jinsert : ++jdelete : ++i

  定义 minPrice[i][j] 为从位置 (i,j) 演化到(n,m) 的最少步骤数(i,j从0开始);那么,minPrice[0][0] 就是从旧版本演化到新版本的最少步骤数。

  求 minPrice 的递归式很容易得出:

git怎么知晓文件差异

  如果照这个递归式写一个递归算法,那么递归算法会有很多重叠子问题,例如:

  minPrice[0][0]、minPrice[0][1]、minPrice[1][0] 都需要计算 minPrice[1][1]。

  因此适合采用动态规划逆向求解。下面我用 java 实现了动态规划版的 Diff:

E:\temp>java Diff src.txt dest.txt   1    1   1   2      - 22   3    2   333E:\temp>

  Diff 的全部源码如下:

import java.io.*;import java.util.*;public class Diff {protected enum Operation {COPY(1), INSERT(1), DELETE(1);/** * 操作的代价 */private int price;private Operation(int price) {this.price = price;}public int getPrice() {return price;}}/** * 旧版本 */protected List<String> src;/** * 新版本 */protected List<String> dest;/** * 最小代价 */protected int[][] minPrice;/** * 获得最小代价所采取的操作 */protected Operation[][] ops;/** * 读文本文件为一行一行的字符串 */protected List<String> readLines(String path) {List<String> ans = new ArrayList<String>();try {BufferedReader in = new BufferedReader(new FileReader(path));String line;while ((line = in.readLine()) != null)ans.add(line);in.close();} catch (FileNotFoundException e) {ans = null;} catch (IOException e) {ans = null;}return ans;}/** * 把新旧版本读入 */public void readFiles(String srcPath, String destPath) {src = this.readLines(srcPath);dest = this.readLines(destPath);}/** * 判断旧版本第i行和新版本第j行是否相等,<b>利用了哈希值减少不必要的字符串全比较</b> */protected boolean lineEqual(int i, int j) {String s = src.get(i);String d = dest.get(j);if (s.hashCode() != d.hashCode())return false;return s.equals(d);}/** * 计算最小代价修改方案(动态规划) */public void minModifyPrice() {int n = src.size();int m = dest.size();int i, j;// 新旧版本下标minPrice = new int[n + 1][m + 1];ops = new Operation[n + 1][m + 1];// 初始化全添加、全删除的行列for (j = m; j >= 0; j--) {ops[n][j] = Operation.INSERT;minPrice[n][j] = (m - j) * Operation.INSERT.getPrice();}for (i = n - 1; i >= 0; i--) {ops[i][m] = Operation.DELETE;minPrice[i][m] = (n - i) * Operation.DELETE.getPrice();}for (i = n - 1; i >= 0; i--)for (j = m - 1; j >= 0; j--) {int insertPrice = Operation.INSERT.getPrice()+ minPrice[i][j + 1];int deletePrice = Operation.DELETE.getPrice()+ minPrice[i + 1][j];int copyPrice = Operation.COPY.getPrice()+ minPrice[i + 1][j + 1];int lower;if (insertPrice < deletePrice) {ops[i][j] = Operation.INSERT;lower = minPrice[i][j] = insertPrice;} else {ops[i][j] = Operation.DELETE;lower = minPrice[i][j] = deletePrice;}if (copyPrice < lower && this.lineEqual(i, j)) {ops[i][j] = Operation.COPY;minPrice[i][j] = copyPrice;}}}/** * 打印结果 */public void printResult() {int n = src.size();int m = dest.size();int i = 0, j = 0;// 新旧版本下标while (i != n || j != m) {switch (ops[i][j]) {case INSERT:System.out.printf("     %4d + %s\n", j + 1, dest.get(j));j++;break;case DELETE:System.out.printf("%4d      - %s\n", i + 1, src.get(i));i++;break;case COPY:System.out.printf("%4d %4d   %s\n", i + 1, j + 1, dest.get(j));i++;j++;break;}}}/** * @param args *            args[0] 是旧版本路径,<br> *            args[1] 是新版本路径 */public static void main(String[] args) {Diff d = new Diff();d.readFiles(args[0], args[1]);d.minModifyPrice();d.printResult();}}

  采用动态规划后,求 minPrice 的复杂度为 O(n^2),一般源文件就几百行的样子,因此,几个毫秒就能算出步骤数最少的修改方案。

  git 只要把最佳修改方案中的 insert、delete 操作存储起来,做为被管理的软件大夏的一块砖。但实际上git怎么存储各个版本的,还没研究过o(╯□╰)o

热点排行