public static @NonNls @NotNull <T> String getLogString(@NotNull @NonNls String root, @NotNull Collection<? extends T> changes,
                                                         @NotNull Convertor<? super T, ? extends FilePath> beforePathGetter,
                                                         @NotNull Convertor<? super T, ? extends FilePath> afterPathGetter) {
    return StringUtil.join(changes, change -> {
      FilePath after = afterPathGetter.convert(change);
      FilePath before = beforePathGetter.convert(change);
      if (before == null) {
        return "A: " + getRelativePath(root, after);
      }
      else if (after == null) {
        return "D: " + getRelativePath(root, before);
      }
      else if (ChangesUtil.equalsCaseSensitive(before, after)) {
        return "M: " + getRelativePath(root, after);
      }
      else {
        return "R: " + getRelativePath(root, before) + " -> " + getRelativePath(root, after);
      }
    }, ", ");
  }
public static @NonNls @NotNull <T> String getLogString(@NotNull @NonNls String root, @NotNull Collection<? extends T> changes,
                                                         @NotNull Convertor<? super T, ? extends FilePath> beforePathGetter,
                                                         @NotNull Convertor<? super T, ? extends FilePath> afterPathGetter)