Git08-diff

  • diff是英文differences(差异)的缩写,指的是两个事物的不同。
  • 在Linux系统和UNIX系统中,diff命令会逐行比较两个文本的差异然后显示出来。
//(1)创建initial文件
]# cat > initial << EOF
Now is the time
For all good men
To come to the aid
Of their country.
EOF

//(2)创建rewrite文件
]# cat > rewrite << EOF
Today is the time
For all good men
And women
To come to the aid
Of their country.
EOF

//(3)比较两个文件
]# diff -u initial rewrite 
--- initial	2023-03-21 14:42:40.021594181 +0800
+++ rewrite	2023-03-21 14:42:46.393647598 +0800
@@ -1,4 +1,5 @@
-Now is the time
+Today is the time
 For all good men
+And women
 To come to the aid
 Of their country.
  • diff -u的输出,原始文件被“---”符号标记起来,新文件被用“+++”标记。@@之间表示两个不同文件的上下文行号。
    • 以减号(-)开始的行表示从原始文件删除该行以得到新文件。
    • 以加号(+)开始的行表示从原始文件中添加该行以产生新文件。
    • 以空格开始的行是两个版本都有的行。
  • diff提供文件差异的摘要,提供一个文件如何转变为另一个文件的正式描述,还可以显示多个文件之间和整个目录层次结构的差异。
  • diff -r命令命令可以计算两个目录结构中所有对应的两个文件间的差异。diff -r -u会为两个目录层次结构产生一个合并格式的diff。
  • diff不提供文件改变的原因,也不会去判断初始状态和最终状态。

 

  • git diff也能产生差异摘要,也可以进行文件的比较,也可以像diff -r命令一样,遍历两个树对象,同时显示它们间的差别。但是git diff还有它自己的细微差别和针对Git用户的特殊需求定制的强大功能。
  • 提示,从技术上讲,一个树对象只代表版本库中的一个目录层级,它包含该目录下的直接文件和它的所有直接子目录的信息,但不包括所有子目录的完整内容。然而,因为树对象引用所有子目录的树对象,所以对应项目根目录的树对象实际上代表某个时刻的整个项目,因此我们可以说,git diff遍历两棵树(树对象和目录)。

1、git diff命令的基本用法

  • 如果选择两个不同的根级树对象进行比较,git diff将会得到这两个项目状态的所有不同
  • 可以用git diff将一个项目从一个状态转换到另外一个状态。例如,如果你和你的同事共同开发一个项目,一个根级别的git diff就可以有效地将两个版本库进行同步。
  • git diff命令的4种基本用法:
    • git diff
      • 比较索引和工作目录,会显示它们之间的差异。会显示工作目录里哪些文件是“脏的”,可以将这个“脏”文件作为下次暂存的候选。不会显示索引中的和永久存在版本库中的文件的不同(更不必说可能相关的远程版本库)。
    • git diff [<commit>]
      • 比较指定提交和工作目录,会显示它们之间的差异。常见的一种用法是用HEAD或者一个特定的分支名作为commit。
    • git diff --cached [<commit>]
      • 比较指定提交和索引,会显它们之间的差异。<commit>默认是HEAD。使用HEAD,该命令会显示下次提交会如何修改当前分支。--cached和--staged等价,--stage在Git 1.6.1及后续版本是可用的。
    • git diff <commit> <commit>
      • 比较两个指定提交,会显示它们之间的差异。这条命令会忽略索引和工作目录,它是比较任意对象库中两个树对象的方法(workhorse)。
  • 命令常用的选项:
    • -M[<n>], --find-renames[=<n>:检测重命名。如果指定相似性指数阈值n(即与文件大小相比的添加/删除量)。例如,-M90%意味着如果超过90%的文件没有更改,Git应该将删除/添加的文件对视为重命名。
      • 如果没有%符号,该数字将被读取为一个分数,其前面有一个小数点。例如,即-M5变为0.5,与-M50%相同。同理,-M05等同于-M5%。
      • 要将检测限制为精确的重命名,请使用-M100%。
      • 默认相似性指数为50%。
    • -w, --ignore-all-space:行比较时忽略空格。即使一行有空格而另一行没有空格,这也会忽略差异。
    • --stat[=<width>[,<name-width>[,<count>]]]:显示两个树状态之间差异的统计数据。显示改变了多少行,添加了多少行,删除了多少行。
    • --color[=<when>]:在输出时使用多种颜色,一种颜色显示一种变化。
    • -S"string":选项用来在版本库中搜索包含string的变更。
    • 警告,-a选项对于git diff命令没有意义,这和-a选项对于git commit完全不同。要显示暂存的和未暂存的差异,需要使用git diff HEAD。
  • git diff命令的数据来源
    • 整个提交图中的任意树对象。
    • 工作目录。
    • 索引。
  • 通常,git diff命令进行树的比较时可以通过提交名、分支名或者标签名。并且,工作目录的文件和目录结构还有在索引中暂存文件的完整结构,都可以被看做树。

2、git diff和提交范围

2.1、提交范围

  • git diff命令支持两点语法来显示两个提交之间的不同。因此,下面两条命令是等价的:
    • git diff <commit> <commit>
    • git diff <commit>..<commit>
  • 比较git dif和git log:
    • git diff不关心比较的文件的历史,也不关心分支。
    • git log特别关心一个文件是如何变成另外一个的。比如,当产生分支时,在每个分支上发生了什么。
    • 这两个命令执行完全不同的操作。git log操作一系列提交,而git dif操作两个不同的节点。

2.2、对称差

  • 要比较两个分支上的开发线,可以使用对称差。
//对称差:会显示共同祖先(或者合并基础[merge base]之间的差异。
git diff <commit>...<commit>
  • 如图8-1所示,比较两个分支的开发线。

//(范围)显示5个单独的提交:V, W, X, Y, Z。
git log master..maint

//(范围)显示H和Z处的树之间的差异,累计有11个提交:C, D, ..., H和V, W, .., Z。
git diff master..maint

//(对称差)显示11个单独的提交:C, D, ..., H和V, W, .., Z。
git log master...maint

//(对称差)组合V, W, .., Z中的变更。
git diff master...maint

3、git diff示例

  • 如图8-2所示,一个包含两个文件的项目目录。file1文件已经在工作目录中更改(从foo改为quux)。

示例1-1:

//(1)添加用户配置
]# git config --global user.email "hengha@123.com"
]# git config --global user.name "heng ha"

//(2)初始化一个新的版本库
]# mkdir diff-example
]# cd diff-example/
]# git init

//(3)创建一个提交
]# echo "foo" > file1
]# echo "bar" > file2
]# git add file1 file2
]# git commit -m "Add file1 and file2"

3.1、工作目录、索引和提交的相互比较

3.1.1、未暂存file1

示例1-2:

  • file1未修改前,工作目录、索引和HEAD的状态是一致的
  • file1在工作目录中已经修改,但是还没有暂存(即工作目录变了,索引和HEAD没有变化)。和图8-2的状态不一致,但是仍然可以进行比较操作。
//(1)修改file1
]# echo "quux" > file1

//(2)比较工作目录和索引
]# git diff
diff --git a/file1 b/file1
index 257cc56..d90bda0 100644
--- a/file1
+++ b/file1
@@ -1 +1 @@
-foo
+quux

//(3)比较工作目录和指定提交
]# git diff HEAD
diff --git a/file1 b/file1
index 257cc56..d90bda0 100644
--- a/file1
+++ b/file1
@@ -1 +1 @@
-foo
+quux

//(4)比较索引和指定提交(默认HEAD)
]# git diff --cached

3.1.2、暂存file1

示例1-3:

  • file1在工作目录中已经修改,并进行了暂存(即工作目录和索引变了,HEAD还没有变化)。和图8-2的状态一致了。
//(1)暂存file1
]# git add file1

//(2)比较工作目录和索引
]# git diff

//(3)比较工作目录和指定提交
]# git diff HEAD
diff --git a/file1 b/file1
index 257cc56..d90bda0 100644
--- a/file1
+++ b/file1
@@ -1 +1 @@
-foo
+quux

//(4)比较索引和指定提交(默认HEAD)
]# git diff --cached
diff --git a/file1 b/file1
index 257cc56..d90bda0 100644
--- a/file1
+++ b/file1
@@ -1 +1 @@
-foo
+quux

3.1.3、提交file1

示例1-4:

  • file1在工作目录中已经修改,并进行了暂存和提交(即工作目录、索引和HEAD的状态一致了)
//(1)提交file1
]# git commit -m "quux uber alles"

//(2)比较工作目录和索引
]# git diff

//(3)比较工作目录和指定提交
]# git diff HEAD

//(4)比较索引和指定提交(默认HEAD)
]# git diff --cached

//(5)比较两个指定提交
]# git diff HEAD  HEAD^
diff --git a/file1 b/file1
index d90bda0..257cc56 100644
--- a/file1
+++ b/file1
@@ -1 +1 @@
-quux
+foo

3.2、路径限制

  • git diff在默认情况下,会对比从给定树对象的根开始的整个目录结构。但可以使用路径限制(path limiting )的方法,仅比较版本库中的一个子集(可以是一个文件,也可以是一个目录)。

示例1-5:

//(1)修改文件
]# echo "hh" > file1
]# echo "hengha" > file2

//(2)不进行路径限制
]# git diff
diff --git a/file1 b/file1
index d90bda0..e8689b2 100644
--- a/file1
+++ b/file1
@@ -1 +1 @@
-quux
+hh
diff --git a/file2 b/file2
index 5716ca5..e86bf34 100644
--- a/file2
+++ b/file2
@@ -1 +1 @@
-bar
+hengha

//(3)进行路径限制
]# git diff -- file1
diff --git a/file1 b/file1
index d90bda0..e8689b2 100644
--- a/file1
+++ b/file1
@@ -1 +1 @@
-quux
+hh

3.3、搜索字符串

  • git diff -S"string":用来在版本库中搜索包含string的变更。

示例1-6:

//在master分支中搜索包含"hengha"的变更
]# git diff -S"hengha" 
diff --git a/file2 b/file2
index 5716ca5..e86bf34 100644
--- a/file2
+++ b/file2
@@ -1 +1 @@
-bar
+hengha

4、比较SVN和Git如何产生diff

1、SVN产生diff

  • 大多数系统(比如CVS或者SVN)会跟踪一系列修订版本,并只存储文件间的差异。这个策略是为了节省空间和开销。
  • 在内部,这些系统会花很多时间去思考这样的问题,“在A文件和B文件中之间有什么差异”。例如,当你从中心版本库更新文件的时候,SVN会记着你上次更新时是版本r1095,但这次更新时版本库已经到了版本r1123。因此,服务器必须把r1095和r1123之间的所有diff发送给你。一旦你的SVN客户端有了这些diff,它就可以把这些diff合并到你的工作副本中,从而产生版本r1123(这就是SVN如何避免在每次你更新的时候发送所有文件的全部内容的)。
  • 为了节省磁盘空间,SVN也在服务器上把其版本库另存为一系列diff。当你要查看r1095和r1123之间的diff时,SVN会查看这两个版本间所有单独的diff,然后将它们合并成一个大的diff,最后把结果发送给你。

2、git产生diff

  • 在Git中,每个提交都包含一棵树,也就是该提交包含的文件列表。每个树都是跟其他树独立的。
  • 在Git中,diff和patch是导出的数据,而不是基本数据
    • diff是SVN和CVS中的基本数据,如果进入.gir目录查看,你不会找到一个单独的diff文件;而如果你进SVN版本库中查看,那里会有大量diff文件。
  • 像SVN可以导出版本r1095和版本rl123之间的完整差异一样,Git可以检索和生成任意两个状态之间的差异。在这个过程中,SVN要查看版本r1095和版本r1123间的所有版本,而Git则不关心这些中间步骤。
  • 每个修订版本有一棵自己的树,但Git不需要它们来生成diff。Git可以直接操作两个版本的完整状态的快照。存储系统中这个简单的差异是Git比其他RCS速度快得多的最重要原因之一。

1

#                                                                                                                         #
posted @ 2023-03-22 17:44  麦恒  阅读(61)  评论(0编辑  收藏  举报