使用Git diff检测代码移动+如何使用diff选项

时间:2012-09-25 20:58:24

标签: git git-diff

考虑一个文件(1.c)包含作者M和J所做的三个函数和更改。如果有人运行git blame 1.c,他将获得以下输出:

^869c699 (M 2012-09-25 14:05:31 -0600  1) 
de24af82 (J 2012-09-25 14:23:52 -0600  2) 
de24af82 (J 2012-09-25 14:23:52 -0600  3) 
de24af82 (J 2012-09-25 14:23:52 -0600  4) public int add(int x, int y)  {
de24af82 (J 2012-09-25 14:23:52 -0600  5)    int z = x+y;
de24af82 (J 2012-09-25 14:23:52 -0600  6)    return z;
de24af82 (J 2012-09-25 14:23:52 -0600  7) }  
de24af82 (J 2012-09-25 14:23:52 -0600  8) 
^869c699 (M 2012-09-25 14:05:31 -0600  9) public int multiplication(int y, int z){
^869c699 (M 2012-09-25 14:05:31 -0600 10)    int result = y*z;
^869c699 (M 2012-09-25 14:05:31 -0600 11)    return temp;
^869c699 (M 2012-09-25 14:05:31 -0600 12) }
^869c699 (M 2012-09-25 14:05:31 -0600 13) 
^869c699 (M 2012-09-25 14:05:31 -0600 14) public void main(){
de24af82 (J 2012-09-25 14:23:52 -0600 15)    //this is a comment
de24af82 (J 2012-09-25 14:23:52 -0600 16) }

现在,如果作者A更改multiplication()add()函数的位置并提交更改,git blame可以检测代码移动。请参阅以下输出:

$ git blame  -C -M e4672cf82 1.c
^869c699 (M 2012-09-25 14:05:31 -0600  1) 
de24af82 (J 2012-09-25 14:23:52 -0600  2) 
de24af82 (J 2012-09-25 14:23:52 -0600  3) 
e4672cf8 (M 2012-09-25 14:26:39 -0600  4) 
de24af82 (J 2012-09-25 14:23:52 -0600  5) 
^869c699 (M 2012-09-25 14:05:31 -0600  6) public int multiplication(int y, int z){
^869c699 (M 2012-09-25 14:05:31 -0600  7)    int result = y*z;
^869c699 (M 2012-09-25 14:05:31 -0600  8)    return temp;
^869c699 (M 2012-09-25 14:05:31 -0600  9) }
^869c699 (M 2012-09-25 14:05:31 -0600 10) 
^869c699 (M 2012-09-25 14:05:31 -0600 11) public void main(){
de24af82 (J 2012-09-25 14:23:52 -0600 12)    //this is a comment
e4672cf8 (M 2012-09-25 14:26:39 -0600 13) }
de24af82 (J 2012-09-25 14:23:52 -0600 14) public int add(int x, int y){
de24af82 (J 2012-09-25 14:23:52 -0600 15)    int z = x+y;
de24af82 (J 2012-09-25 14:23:52 -0600 16)    return z;
e4672cf8 (M 2012-09-25 14:26:39 -0600 17) }

但是,如果我尝试在这两个版本之间运行git diff,则无法检测到函数是否更改了它们的位置并提供了以下输出:

$ git diff -C -M de24af8..e4672cf82 1.c

diff --git a/1.c b/1.c
index 5b1fcba..56b4430 100644
--- a/1.c
+++ b/1.c
@@ -1,10 +1,7 @@



-public int add(int x, int y){
-       int z = x+y;
-       return z;
-}      
+

public int multiplication(int y, int z){
    int result = y*z;
@@ -13,4 +10,8 @@ public int multiplication(int y, int z){

 public void main(){
    //this is a comment
-}
\ No newline at end of file
+}
+public int add(int x, int y){
+       int z = x+y;
+       return z;
+}      
\ No newline at end of file

我的问题是:

  1. 如何在获取diff输出时强制检测代码移动?它甚至可能吗?

  2. Git diff可以应用多个选项。例如--minimal--patience。我如何在这里应用这些选项?我试过一个,但得到以下错误:

    $ git diff --minimal de24af8..e4672cf82 1.c
    usage: git diff <options> <rev>{0,2} -- <path>*
    
  3. 有人可以建议/举例说明如何正确添加这些选项吗?

3 个答案:

答案 0 :(得分:28)

Git 2.15, git diff now supports开始,使用--color-moved选项检测移动的行。它甚至可以检测文件之间的移动

显然,它适用于彩色终端输出。据我所知,没有选项以纯文本补丁格式指示移动,但这是有道理的。

对于默认行为,请尝试

git diff --color-moved

该命令还包含选项,目前为nodefaultplainzebradimmed_zebra(使用git help diff获取最新的选项和他们的描述)。例如:

git diff --color-moved=zebra

答案 1 :(得分:19)

你在这里遇到的是Git很大程度上不像这样的高级差异。 Git允许配置外部差异和合并工具是有原因的:如果没有他们的帮助,你会疯了。超越比较和Araxis Merge都会抓住这一运动,作为一个例子。

您要解决的一般问题类别是“结构化合并”:Structural Diff of two java source files

在这种情况下,git-format-patch可能比使用git-diff更幸运,因为前者提供了更多的提交信息,包括作者和提交消息,并且还为每个提交生成一个补丁文件。您指定的范围。资料来源:What is the difference between 'git format-patch and 'git diff'?

如果您正在寻找有关检测代码移动的提示,那么有趣的是,检测代码移动明确是全能镐的目标。看到这个有趣的交流:http://gitster.livejournal.com/35628.html

如果您想要检测谁交换了订单,似乎您唯一的选择就是执行以下操作:

 git log -S'public int multiplication(int y, int z){
    int result = y*z;
    return temp;
 }

 public void main(){
    //this is a comment
 }
 public int add(int x, int y)  {
    int z = x+y;
    return z;
 }'

您正在寻找的是git blame -M<num> -n,它的功能与您提出的要求非常相似:

-M|<num>|
       Detect moved or copied lines within a file. When a commit moves or
       copies a block of lines (e.g. the original file has A and then B,
       and the commit changes it to B and then A), the traditional blame
       algorithm notices only half of the movement and typically blames
       the lines that were moved up (i.e. B) to the parent and assigns
       blame to the lines that were moved down (i.e. A) to the child
       commit. With this option, both groups of lines are blamed on the
       parent by running extra passes of inspection.

       <num> is optional but it is the lower bound on the number of
       alphanumeric characters that git must detect as moving/copying
       within a file for it to associate those lines with the parent
       commit. The default value is 20.

-n, --show-number
       Show the line number in the original commit (Default: off).

答案 2 :(得分:2)

在这种特殊情况下,我认为git diff并不关心检测代码移动;相反,它只是创建一个可以应用于将旧文件转换为新文件的补丁,这是git diff输出明显显示的内容 - 该函数正在从一个位置删除并插入另一个位置。可能有更简洁的方法来输出一系列将代码从一个位置移动到另一个位置的编辑命令,但我认为git可能在这里可移植性方面犯了错误 - 无法保证最终用户总是使用{ {1}}或git apply,因此补丁的格式可以使用普通git am