32

考虑一个文件 (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()andadd()函数的位置并提交了更改,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. 如何在获取差异输出时强制检测代码移动?甚至可能吗?

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

    $ git diff --minimal de24af8..e4672cf82 1.c
    usage: git diff <options> <rev>{0,2} -- <path>*
    

谁能建议/举例说明如何正确添加这些选项?

4

3 回答 3

51

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

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

对于默认行为,请尝试

git diff --color-moved

该命令还接受选项,当前为nodefaultplain和(用于获取最新选项及其描述)zebra。例如:dimmed_zebragit help diff

git diff --color-moved=zebra
于 2017-11-09T02:24:54.207 回答
24

这是写它时的最佳答案,但它不再准确。2017 年,Git 2.15 对其进行了升级diff以进行移动检测。正如现在投票最多的答案中所解释的那样,使用git diff --color-moved

原答案:

你在这里遇到的是 Git 基本上没有像这样的高级差异。Git 允许配置外部差异和合并工具是有原因的:如果没有他们的帮助,你会发疯的。例如,Beyond Compare 和 Araxis Merge 都会捕捉到这种运动。

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

您可能git-format-patchgit-diff在这种情况下更幸运,因为前者提供了更多的提交信息,包括作者和提交消息,并且还为您指定的范围内的每个提交生成一个补丁文件。资料来源:“git format-patch”和“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).
于 2012-10-09T17:38:02.823 回答
2

在这种特殊情况下,我认为不必git diff担心检测代码移动。相反,它只是创建一个可用于将旧文件转换为新文件的补丁,这就是您的git diff输出清楚地显示的内容 - 该函数正在从一个位置删除并插入另一个位置。可能有更简洁的方法来输出一系列编辑命令,这些命令将代码从一个位置移动到另一个位置,但我认为git这里的可移植性可能会出错 - 不能保证最终用户总是使用git applyor git am,所以补丁是以一种即使与 plain 也可以使用的格式生成patch

于 2012-10-09T19:01:10.770 回答