git 检测您的 Java 项目中的重命名/移动效果不佳 - 该怎么办?

Posted

技术标签:

【中文标题】git 检测您的 Java 项目中的重命名/移动效果不佳 - 该怎么办?【英文标题】:git detects renames/moves in our Java project poorly - what to do? 【发布时间】:2013-06-13 04:46:48 【问题描述】:

我已经阅读了很多关于 git 如何巧妙地检测文件何时被重命名或移动的信息,并且您不应该(或不能)做任何事情来帮助它完成此类任务。不幸的是,我们的项目中有很多问题,在移动文件后,git 历史记录将由一行组成。这是我最近看到的一个例子中的一个模式:

原课:

package a.b.c.d.e.f.X;

[four imports]

public interface A extends B 
  void f(String id, int index) throws MyException;

  @Annotation
  class AImpl extends C implements A 
    @OtherAnnotation
    public void f(String id, int index)
      throws MyException 
        Map<String, Object> inputs = new HashMap<String, Object>();
        inputs.put("a", id);
        inputs.put("b", index);
        execute(inputs);
    

    @Override
    public String g() 
      return "xxxx.yyyy";
    
  

搬家后的班级:

package a.b.c.d.e.f.Y;

[six imports, two new compared to original]

public interface A extends B 
  void f(MyIdObject idobject, int index) throws MyException;

  @Annotation
  class AImpl extends D implements A 
    @OtherAnnotation
    public void f(@ParameterAnnotation([some stuff]) MyIdObject idobject, int index) throws MyException 
        Map<String, Object> inputs = new HashMap<>();
        inputs.put("c", idobject.getId());
        inputs.put("b", index);
        execute(inputs);
    

    @Override
    public String g() 
      return "yyyy";
    
  

总结一下:类文件被移动到另一个目录/包(不同一级 - 见包名),添加了导入,在此处声明和实现的方法中更改了一个参数,删除了一个换行符方法头,方法内部更改了一个字符串,HashMap 实例化更改为使用菱形表示法,g() 中的返回值已更改。

大多数标识符和字符串已更改以保护无辜者 - 它们通常约为 10-20 个字符。我还没有编译这个,如果有任何错误,很抱歉 - 我希望你能明白。

由于这是一些框架重构的一部分,在同一次提交中,有一堆类按照这种模式进行了更改,不同之处在于字符串内容、放入哈希图中的字符串数量以及“f”方法名称.

所以我想我可以看到 git 在这里遇到了什么问题 - 尽管这个特定的重命名本身很明显,但当它与具有相似内容的文件的其他重命名混合时,git 无法确定哪个是哪个?文件名(未更改的)不应该足够提示吗?

我的印象是,既然 git 应该可以完美地处理这个问题,那么没有办法调整 git 以更好地处理这个问题吗?当我们重构以使其更好地工作时,我们可以做些什么吗?我们现在有什么可以做的吗?

特别是:我希望能够在每次提交中查看文件的差异,包括完成重命名的文件。我习惯于在 IntelliJ 中加载 git 历史记录,他双击一个提交,这将显示在该提交中完成了更改。我知道在 IntelliJ 中可能无法获得我想要的东西,但是对于一个历史被这样中断的文件,我将如何在命令行或其他地方做到这一点?

我们可能不想做的事情:1) 每次重命名时提交一次,2) 重命名时提交一次 + 内容更改时提交另一次。

如果您对 Java 设计感到好奇,请给我发私信——让我们继续讨论 git。 :)

【问题讨论】:

如果我可以问,为什么你需要跟踪重命名/删除“这么多”? 我希望能够在 IntelliJ 中调出文件的历史记录并查看发生了什么,例如一年前 / 5 次提交。 嗯,我明白了...我也使用 IDEA,但不幸的是,它没有 git log --follow 的等价物。 【参考方案1】:

尝试使用这些额外的git log --follow &lt;filename&gt; options:

-M[]

--查找重命名[=]

如果生成差异,检测并报告每次提交的重命名。有关在遍历历史记录时跨重命名跟踪文件,请参阅 --follow。如果指定了 n,则它是相似性指数的阈值(即添加/删除的数量与文件大小相比)。例如,-M90% 表示如果 90% 以上的文件没有更改,git 应该将删除/添加对视为重命名。如果没有 % 符号,则该数字将被读取为分数,前面有一个小数点。即,-M5 变为 0.5,因此与 -M50% 相同。同样,-M05 与 -M5% 相同。要将检测限制为精确重命名,请使用 -M100%。

-C[]

--查找副本[=]

检测副本以及重命名。另请参见 --find-copies-harder。如果指定了 n,则与 -M 的含义相同。

--find-copy-harder

出于性能原因,默认情况下,-C 选项仅在副本的原始文件在同一变更集中进行了修改时才查找副本。此标志使命令检查未修改的文件作为副本源的候选者。对于大型项目,这是一项非常昂贵的操作,因此请谨慎使用。提供多个 -C 选项具有相同的效果。

【讨论】:

编辑:抱歉,我没有看到你在这里谈论差异。 @TV'sFrank,您似乎编辑了您的评论(我的 SO 收件箱显示了不同的文本),因此很难理解您对建议的方法有什么困难。 是的,对不起,我先编辑,然后发现我在说垃圾,所以我再次编辑。我看不出你的方法有问题 - 我还没有彻底检查它

以上是关于git 检测您的 Java 项目中的重命名/移动效果不佳 - 该怎么办?的主要内容,如果未能解决你的问题,请参考以下文章

提交后修复Mercurial存储库中的重命名

Git:重命名分支中的目录

git:重命名文件并更改文件内容

想不到一个简单的重命名,在git中也有这么多学问

想不到一个简单的重命名,在git中也有这么多学问

SHELL:多文件的重命名和移动