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)。
- git diff
- 命令常用的选项:
- -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。
- -M[<n>], --find-renames[=<n>:检测重命名。如果指定相似性指数阈值n(即与文件大小相比的添加/删除量)。例如,-M90%意味着如果超过90%的文件没有更改,Git应该将删除/添加的文件对视为重命名。
- 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
# #