Merge branch 'jk/cached-textconv'
authorJunio C Hamano <gitster@pobox.com>
Sun, 9 May 2010 05:33:08 +0000 (22:33 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 9 May 2010 05:33:08 +0000 (22:33 -0700)
* jk/cached-textconv:
diff: avoid useless filespec population
diff: cache textconv output
textconv: refactor calls to run_textconv
introduce notes-cache interface
make commit_tree a library function

1  2 
Makefile
commit.h
diff.c
diff --combined Makefile
index eb1d1623c204c10c13ebc8ca60ca1a07ddef49df,24e92abe916a179bb85d4a67a6bdcf2d8ff575e3..4f7224a59a9c93b12a7878b0f692c1bc469d6daf
+++ b/Makefile
@@@ -34,7 -34,7 +34,7 @@@ all:
  # Define NO_D_INO_IN_DIRENT if you don't have d_ino in your struct dirent.
  #
  # Define NO_D_TYPE_IN_DIRENT if your platform defines DT_UNKNOWN but lacks
 -# d_type in struct dirent (latest Cygwin -- will be fixed soonish).
 +# d_type in struct dirent (Cygwin 1.5, fixed in Cygwin 1.7).
  #
  # Define NO_C99_FORMAT if your formatted IO functions (printf/scanf et.al.)
  # do not support the 'size specifiers' introduced by C99, namely ll, hh,
  # Define NO_PTHREADS if you do not have or do not want to use Pthreads.
  #
  # Define NO_PREAD if you have a problem with pread() system call (e.g.
 -# cygwin.dll before v1.5.22).
 +# cygwin1.dll before v1.5.22).
  #
  # Define NO_FAST_WORKING_DIRECTORY if accessing objects in pack files is
  # generally faster on your platform than accessing the working directory.
  # Define JSMIN to point to JavaScript minifier that functions as
  # a filter to have gitweb.js minified.
  #
 +# Define CSSMIN to point to a CSS minifier in order to generate a minified
 +# version of gitweb.css
 +#
  # Define DEFAULT_PAGER to a sensible pager command (defaults to "less") if
  # you want to use something different.  The value will be interpreted by the
  # shell at runtime when it is used.
@@@ -282,6 -279,9 +282,6 @@@ lib = li
  # DESTDIR=
  pathsep = :
  
 -# JavaScript minifier invocation that can function as filter
 -JSMIN =
 -
  export prefix bindir sharedir sysconfdir
  
  CC = gcc
@@@ -486,6 -486,7 +486,7 @@@ LIB_H += log-tree.
  LIB_H += mailmap.h
  LIB_H += merge-recursive.h
  LIB_H += notes.h
+ LIB_H += notes-cache.h
  LIB_H += object.h
  LIB_H += pack.h
  LIB_H += pack-refs.h
@@@ -575,6 -576,7 +576,7 @@@ LIB_OBJS += merge-file.
  LIB_OBJS += merge-recursive.o
  LIB_OBJS += name-hash.o
  LIB_OBJS += notes.o
+ LIB_OBJS += notes-cache.o
  LIB_OBJS += object.o
  LIB_OBJS += pack-check.o
  LIB_OBJS += pack-refs.o
@@@ -831,24 -833,22 +833,24 @@@ ifeq ($(uname_S),SunOS
        BASIC_CFLAGS += -D__EXTENSIONS__ -D__sun__ -DHAVE_ALLOCA_H
  endif
  ifeq ($(uname_O),Cygwin)
 -      NO_D_TYPE_IN_DIRENT = YesPlease
 -      NO_D_INO_IN_DIRENT = YesPlease
 -      NO_STRCASESTR = YesPlease
 -      NO_MEMMEM = YesPlease
 -      NO_MKSTEMPS = YesPlease
 -      NO_SYMLINK_HEAD = YesPlease
 +      ifeq ($(shell expr "$(uname_R)" : '1\.[1-6]\.'),4)
 +              NO_D_TYPE_IN_DIRENT = YesPlease
 +              NO_D_INO_IN_DIRENT = YesPlease
 +              NO_STRCASESTR = YesPlease
 +              NO_MEMMEM = YesPlease
 +              NO_MKSTEMPS = YesPlease
 +              NO_SYMLINK_HEAD = YesPlease
 +              NO_IPV6 = YesPlease
 +              OLD_ICONV = UnfortunatelyYes
 +      endif
        NEEDS_LIBICONV = YesPlease
        NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes
        NO_TRUSTABLE_FILEMODE = UnfortunatelyYes
 -      OLD_ICONV = UnfortunatelyYes
        NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
        # There are conflicting reports about this.
        # On some boxes NO_MMAP is needed, and not so elsewhere.
        # Try commenting this out if you suspect MMAP is more efficient
        NO_MMAP = YesPlease
 -      NO_IPV6 = YesPlease
        X = .exe
        COMPAT_OBJS += compat/cygwin.o
        UNRELIABLE_FSTAT = UnfortunatelyYes
@@@ -866,7 -866,6 +868,7 @@@ ifeq ($(uname_S),FreeBSD
                NO_UINTMAX_T = YesPlease
                NO_STRTOUMAX = YesPlease
        endif
 +      PYTHON_PATH = /usr/local/bin/python
  endif
  ifeq ($(uname_S),OpenBSD)
        NO_STRCASESTR = YesPlease
@@@ -922,6 -921,7 +924,6 @@@ ifeq ($(uname_S),IRIX
        SNPRINTF_RETURNS_BOGUS = YesPlease
        SHELL_PATH = /usr/gnu/bin/bash
        NEEDS_LIBGEN = YesPlease
 -      NEEDS_LIBICONV = YesPlease
  endif
  ifeq ($(uname_S),IRIX64)
        NO_SETENV=YesPlease
        SNPRINTF_RETURNS_BOGUS = YesPlease
        SHELL_PATH=/usr/gnu/bin/bash
        NEEDS_LIBGEN = YesPlease
 -      NEEDS_LIBICONV = YesPlease
  endif
  ifeq ($(uname_S),HP-UX)
        NO_IPV6=YesPlease
@@@ -1479,7 -1480,7 +1481,7 @@@ endi
  ifndef NO_PYTHON
        $(QUIET_SUBDIR0)git_remote_helpers $(QUIET_SUBDIR1) PYTHON_PATH='$(PYTHON_PATH_SQ)' prefix='$(prefix_SQ)' all
  endif
 -      $(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1)
 +      $(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) SHELL_PATH='$(SHELL_PATH_SQ)' PERL_PATH='$(PERL_PATH_SQ)'
  
  please_set_SHELL_PATH_to_a_more_modern_shell:
        @$$(:)
@@@ -1561,29 -1562,18 +1563,29 @@@ gitweb
        $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) all
  
  ifdef JSMIN
 -OTHER_PROGRAMS += gitweb/gitweb.cgi   gitweb/gitweb.min.js
 -gitweb/gitweb.cgi: gitweb/gitweb.perl gitweb/gitweb.min.js
 +GITWEB_PROGRAMS += gitweb/gitweb.min.js
 +GITWEB_JS = gitweb/gitweb.min.js
  else
 -OTHER_PROGRAMS += gitweb/gitweb.cgi
 -gitweb/gitweb.cgi: gitweb/gitweb.perl
 +GITWEB_JS = gitweb/gitweb.js
  endif
 +ifdef CSSMIN
 +GITWEB_PROGRAMS += gitweb/gitweb.min.css
 +GITWEB_CSS = gitweb/gitweb.min.css
 +else
 +GITWEB_CSS = gitweb/gitweb.css
 +endif
 +OTHER_PROGRAMS +=  gitweb/gitweb.cgi  $(GITWEB_PROGRAMS)
 +gitweb/gitweb.cgi: gitweb/gitweb.perl $(GITWEB_PROGRAMS)
        $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)
  
  ifdef JSMIN
  gitweb/gitweb.min.js: gitweb/gitweb.js
        $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)
  endif # JSMIN
 +ifdef CSSMIN
 +gitweb/gitweb.min.css: gitweb/gitweb.css
 +      $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)
 +endif # CSSMIN
  
  
  git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css gitweb/gitweb.js
            -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
            -e '/@@GITWEB_CGI@@/r gitweb/gitweb.cgi' \
            -e '/@@GITWEB_CGI@@/d' \
 -          -e '/@@GITWEB_CSS@@/r gitweb/gitweb.css' \
 +          -e '/@@GITWEB_CSS@@/r $(GITWEB_CSS)' \
            -e '/@@GITWEB_CSS@@/d' \
 -          -e '/@@GITWEB_JS@@/r gitweb/gitweb.js' \
 +          -e '/@@GITWEB_JS@@/r $(GITWEB_JS)' \
            -e '/@@GITWEB_JS@@/d' \
            -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
 +            -e 's|@@GITWEB_CSS_NAME@@|$(GITWEB_CSS)|' \
 +            -e 's|@@GITWEB_JS_NAME@@|$(GITWEB_JS)|' \
            $@.sh > $@+ && \
        chmod +x $@+ && \
        mv $@+ $@
@@@ -1625,8 -1613,9 +1627,8 @@@ $(patsubst %.py,%,$(SCRIPT_PYTHON)): % 
            -e '}' \
            -e 's|^import sys.*|&; \\\
                   import os; \\\
 -                 sys.path[0] = os.environ.has_key("GITPYTHONLIB") and \\\
 -                               os.environ["GITPYTHONLIB"] or \\\
 -                               "@@INSTLIBDIR@@"|' \
 +                 sys.path.insert(0, os.getenv("GITPYTHONLIB",\
 +                                              "@@INSTLIBDIR@@"));|' \
            -e 's|@@INSTLIBDIR@@|'"$$INSTLIBDIR"'|g' \
            $@.py >$@+ && \
        chmod +x $@+ && \
@@@ -2098,7 -2087,7 +2100,7 @@@ clean
        $(RM) $(htmldocs).tar.gz $(manpages).tar.gz
        $(MAKE) -C Documentation/ clean
  ifndef NO_PERL
 -      $(RM) gitweb/gitweb.cgi
 +      $(MAKE) -C gitweb clean
        $(MAKE) -C perl clean
  endif
  ifndef NO_PYTHON
diff --combined commit.h
index 26ec8c0d1cebcf5e79564be690517cd2eb9c6413,2b7fd89dfd047968aa3e7c927a6c20e506a976de..6ef88dcf45d6c5b2cd0f26397db2fc7b8859d581
+++ b/commit.h
@@@ -74,16 -74,11 +74,16 @@@ struct pretty_print_contex
        struct reflog_walk_info *reflog_info;
  };
  
 +struct userformat_want {
 +      unsigned notes:1;
 +};
 +
  extern int has_non_ascii(const char *text);
  struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
  extern char *reencode_commit_message(const struct commit *commit,
                                     const char **encoding_p);
  extern void get_commit_format(const char *arg, struct rev_info *);
 +extern void userformat_find_requirements(const char *fmt, struct userformat_want *w);
  extern void format_commit_message(const struct commit *commit,
                                  const char *format, struct strbuf *sb,
                                  const struct pretty_print_context *context);
@@@ -163,4 -158,8 +163,8 @@@ static inline int single_parent(struct 
  
  struct commit_list *reduce_heads(struct commit_list *heads);
  
+ extern int commit_tree(const char *msg, unsigned char *tree,
+               struct commit_list *parents, unsigned char *ret,
+               const char *author);
  #endif /* COMMIT_H */
diff --combined diff.c
index e40c1271da3f2ea433b55f770408e8d3a0f8a521,4cb6d9a9e8e80bc834e14f15c07e67069e449a89..e49f14a92442117a8e8424bd7a750dc4fda5cab2
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -14,7 -14,6 +14,7 @@@
  #include "userdiff.h"
  #include "sigchain.h"
  #include "submodule.h"
 +#include "ll-merge.h"
  
  #ifdef NO_FAST_WORKING_DIRECTORY
  #define FAST_WORKING_DIRECTORY 0
@@@ -44,7 -43,8 +44,8 @@@ static char diff_colors[][COLOR_MAXLEN
  };
  
  static void diff_filespec_load_driver(struct diff_filespec *one);
- static char *run_textconv(const char *, struct diff_filespec *, size_t *);
+ static size_t fill_textconv(struct userdiff_driver *driver,
+                           struct diff_filespec *df, char **outbuf);
  
  static int parse_diff_color_slot(const char *var, int ofs)
  {
@@@ -466,8 -466,8 +467,8 @@@ static void emit_rewrite_diff(const cha
                              const char *name_b,
                              struct diff_filespec *one,
                              struct diff_filespec *two,
-                             const char *textconv_one,
-                             const char *textconv_two,
+                             struct userdiff_driver *textconv_one,
+                             struct userdiff_driver *textconv_two,
                              struct diff_options *o)
  {
        int lc_a, lc_b;
        const char *reset = diff_get_color(color_diff, DIFF_RESET);
        static struct strbuf a_name = STRBUF_INIT, b_name = STRBUF_INIT;
        const char *a_prefix, *b_prefix;
-       const char *data_one, *data_two;
+       char *data_one, *data_two;
        size_t size_one, size_two;
        struct emit_callback ecbdata;
  
        quote_two_c_style(&a_name, a_prefix, name_a, 0);
        quote_two_c_style(&b_name, b_prefix, name_b, 0);
  
-       diff_populate_filespec(one, 0);
-       diff_populate_filespec(two, 0);
-       if (textconv_one) {
-               data_one = run_textconv(textconv_one, one, &size_one);
-               if (!data_one)
-                       die("unable to read files to diff");
-       }
-       else {
-               data_one = one->data;
-               size_one = one->size;
-       }
-       if (textconv_two) {
-               data_two = run_textconv(textconv_two, two, &size_two);
-               if (!data_two)
-                       die("unable to read files to diff");
-       }
-       else {
-               data_two = two->data;
-               size_two = two->size;
-       }
+       size_one = fill_textconv(textconv_one, one, &data_one);
+       size_two = fill_textconv(textconv_two, two, &data_two);
  
        memset(&ecbdata, 0, sizeof(ecbdata));
        ecbdata.color_diff = color_diff;
        if (lc_b)
                emit_rewrite_lines(&ecbdata, '+', data_two, size_two);
        if (textconv_one)
 -              free(data_one);
 +              free((char *)data_one);
        if (textconv_two)
 -              free(data_two);
 +              free((char *)data_two);
  }
  
  struct diff_words_buffer {
@@@ -700,6 -682,7 +683,6 @@@ static void diff_words_show(struct diff
  {
        xpparam_t xpp;
        xdemitconf_t xecfg;
 -      xdemitcb_t ecb;
        mmfile_t minus, plus;
  
        /* special case: only removal */
        /* as only the hunk header will be parsed, we need a 0-context */
        xecfg.ctxlen = 0;
        xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words,
 -                    &xpp, &xecfg, &ecb);
 +                    &xpp, &xecfg);
        free(minus.ptr);
        free(plus.ptr);
        if (diff_words->current_plus != diff_words->plus.text.ptr +
@@@ -952,7 -935,7 +935,7 @@@ struct diffstat_t 
                unsigned is_unmerged:1;
                unsigned is_binary:1;
                unsigned is_renamed:1;
 -              unsigned int added, deleted;
 +              uintmax_t added, deleted;
        } **files;
  };
  
@@@ -1044,7 -1027,7 +1027,7 @@@ static void fill_print_name(struct diff
  static void show_stats(struct diffstat_t *data, struct diff_options *options)
  {
        int i, len, add, del, adds = 0, dels = 0;
 -      int max_change = 0, max_len = 0;
 +      uintmax_t max_change = 0, max_len = 0;
        int total_files = data->nr;
        int width, name_width;
        const char *reset, *set, *add_c, *del_c;
  
        for (i = 0; i < data->nr; i++) {
                struct diffstat_file *file = data->files[i];
 -              int change = file->added + file->deleted;
 +              uintmax_t change = file->added + file->deleted;
                fill_print_name(file);
                len = strlen(file->print_name);
                if (max_len < len)
        for (i = 0; i < data->nr; i++) {
                const char *prefix = "";
                char *name = data->files[i]->print_name;
 -              int added = data->files[i]->added;
 -              int deleted = data->files[i]->deleted;
 +              uintmax_t added = data->files[i]->added;
 +              uintmax_t deleted = data->files[i]->deleted;
                int name_len;
  
                /*
                if (data->files[i]->is_binary) {
                        show_name(options->file, prefix, name, len);
                        fprintf(options->file, "  Bin ");
 -                      fprintf(options->file, "%s%d%s", del_c, deleted, reset);
 +                      fprintf(options->file, "%s%"PRIuMAX"%s",
 +                              del_c, deleted, reset);
                        fprintf(options->file, " -> ");
 -                      fprintf(options->file, "%s%d%s", add_c, added, reset);
 +                      fprintf(options->file, "%s%"PRIuMAX"%s",
 +                              add_c, added, reset);
                        fprintf(options->file, " bytes");
                        fprintf(options->file, "\n");
                        continue;
                        del = scale_linear(del, width, max_change);
                }
                show_name(options->file, prefix, name, len);
 -              fprintf(options->file, "%5d%s", added + deleted,
 +              fprintf(options->file, "%5"PRIuMAX"%s", added + deleted,
                                added + deleted ? " " : "");
                show_graph(options->file, '+', add, add_c, reset);
                show_graph(options->file, '-', del, del_c, reset);
@@@ -1206,8 -1187,7 +1189,8 @@@ static void show_numstat(struct diffsta
                        fprintf(options->file, "-\t-\t");
                else
                        fprintf(options->file,
 -                              "%d\t%d\t", file->added, file->deleted);
 +                              "%"PRIuMAX"\t%"PRIuMAX"\t",
 +                              file->added, file->deleted);
                if (options->line_termination) {
                        fill_print_name(file);
                        if (!file->is_renamed)
@@@ -1377,32 -1357,37 +1360,32 @@@ static void free_diffstat_info(struct d
  struct checkdiff_t {
        const char *filename;
        int lineno;
 +      int conflict_marker_size;
        struct diff_options *o;
        unsigned ws_rule;
        unsigned status;
  };
  
 -static int is_conflict_marker(const char *line, unsigned long len)
 +static int is_conflict_marker(const char *line, int marker_size, unsigned long len)
  {
        char firstchar;
        int cnt;
  
 -      if (len < 8)
 +      if (len < marker_size + 1)
                return 0;
        firstchar = line[0];
        switch (firstchar) {
 -      case '=': case '>': case '<':
 +      case '=': case '>': case '<': case '|':
                break;
        default:
                return 0;
        }
 -      for (cnt = 1; cnt < 7; cnt++)
 +      for (cnt = 1; cnt < marker_size; cnt++)
                if (line[cnt] != firstchar)
                        return 0;
 -      /* line[0] thru line[6] are same as firstchar */
 -      if (firstchar == '=') {
 -              /* divider between ours and theirs? */
 -              if (len != 8 || line[7] != '\n')
 -                      return 0;
 -      } else if (len < 8 || !isspace(line[7])) {
 -              /* not divider before ours nor after theirs */
 +      /* line[1] thru line[marker_size-1] are same as firstchar */
 +      if (len < marker_size + 1 || !isspace(line[marker_size]))
                return 0;
 -      }
        return 1;
  }
  
@@@ -1410,7 -1395,6 +1393,7 @@@ static void checkdiff_consume(void *pri
  {
        struct checkdiff_t *data = priv;
        int color_diff = DIFF_OPT_TST(data->o, COLOR_DIFF);
 +      int marker_size = data->conflict_marker_size;
        const char *ws = diff_get_color(color_diff, DIFF_WHITESPACE);
        const char *reset = diff_get_color(color_diff, DIFF_RESET);
        const char *set = diff_get_color(color_diff, DIFF_FILE_NEW);
        if (line[0] == '+') {
                unsigned bad;
                data->lineno++;
 -              if (is_conflict_marker(line + 1, len - 1)) {
 +              if (is_conflict_marker(line + 1, marker_size, len - 1)) {
                        data->status |= 1;
                        fprintf(data->o->file,
                                "%s:%d: leftover conflict marker\n",
@@@ -1585,14 -1569,26 +1568,26 @@@ void diff_set_mnemonic_prefix(struct di
                options->b_prefix = b;
  }
  
- static const char *get_textconv(struct diff_filespec *one)
+ static struct userdiff_driver *get_textconv(struct diff_filespec *one)
  {
        if (!DIFF_FILE_VALID(one))
                return NULL;
        if (!S_ISREG(one->mode))
                return NULL;
        diff_filespec_load_driver(one);
-       return one->driver->textconv;
+       if (!one->driver->textconv)
+               return NULL;
+       if (one->driver->textconv_want_cache && !one->driver->textconv_cache) {
+               struct notes_cache *c = xmalloc(sizeof(*c));
+               struct strbuf name = STRBUF_INIT;
+               strbuf_addf(&name, "textconv/%s", one->driver->name);
+               notes_cache_init(c, name.buf, one->driver->textconv);
+               one->driver->textconv_cache = c;
+       }
+       return one->driver;
  }
  
  static void builtin_diff(const char *name_a,
        const char *set = diff_get_color_opt(o, DIFF_METAINFO);
        const char *reset = diff_get_color_opt(o, DIFF_RESET);
        const char *a_prefix, *b_prefix;
-       const char *textconv_one = NULL, *textconv_two = NULL;
+       struct userdiff_driver *textconv_one = NULL;
+       struct userdiff_driver *textconv_two = NULL;
        struct strbuf header = STRBUF_INIT;
  
        if (DIFF_OPT_TST(o, SUBMODULE_LOG) &&
                }
        }
  
-       if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
-               die("unable to read files to diff");
        if (!DIFF_OPT_TST(o, TEXT) &&
-           ( (diff_filespec_is_binary(one) && !textconv_one) ||
-             (diff_filespec_is_binary(two) && !textconv_two) )) {
+           ( (!textconv_one && diff_filespec_is_binary(one)) ||
+             (!textconv_two && diff_filespec_is_binary(two)) )) {
+               if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
+                       die("unable to read files to diff");
                /* Quite common confusing case */
                if (mf1.size == mf2.size &&
                    !memcmp(mf1.ptr, mf2.ptr, mf1.size))
                const char *diffopts = getenv("GIT_DIFF_OPTS");
                xpparam_t xpp;
                xdemitconf_t xecfg;
 -              xdemitcb_t ecb;
                struct emit_callback ecbdata;
                const struct userdiff_funcname *pe;
  
                        strbuf_reset(&header);
                }
  
-               if (textconv_one) {
-                       size_t size;
-                       mf1.ptr = run_textconv(textconv_one, one, &size);
-                       if (!mf1.ptr)
-                               die("unable to read files to diff");
-                       mf1.size = size;
-               }
-               if (textconv_two) {
-                       size_t size;
-                       mf2.ptr = run_textconv(textconv_two, two, &size);
-                       if (!mf2.ptr)
-                               die("unable to read files to diff");
-                       mf2.size = size;
-               }
+               mf1.size = fill_textconv(textconv_one, one, &mf1.ptr);
+               mf2.size = fill_textconv(textconv_two, two, &mf2.ptr);
  
                pe = diff_funcname_pattern(one);
                if (!pe)
                        }
                }
                xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata,
 -                            &xpp, &xecfg, &ecb);
 +                            &xpp, &xecfg);
                if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS))
                        free_diff_words_data(&ecbdata);
                if (textconv_one)
@@@ -1831,12 -1816,13 +1814,12 @@@ static void builtin_diffstat(const cha
                /* Crazy xdl interfaces.. */
                xpparam_t xpp;
                xdemitconf_t xecfg;
 -              xdemitcb_t ecb;
  
                memset(&xpp, 0, sizeof(xpp));
                memset(&xecfg, 0, sizeof(xecfg));
                xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
                xdi_diff_outf(&mf1, &mf2, diffstat_consume, diffstat,
 -                            &xpp, &xecfg, &ecb);
 +                            &xpp, &xecfg);
        }
  
   free_and_return:
@@@ -1861,7 -1847,6 +1844,7 @@@ static void builtin_checkdiff(const cha
        data.lineno = 0;
        data.o = o;
        data.ws_rule = whitespace_rule(attr_path);
 +      data.conflict_marker_size = ll_merge_marker_size(attr_path);
  
        if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
                die("unable to read files to diff");
                /* Crazy xdl interfaces.. */
                xpparam_t xpp;
                xdemitconf_t xecfg;
 -              xdemitcb_t ecb;
  
                memset(&xpp, 0, sizeof(xpp));
                memset(&xecfg, 0, sizeof(xecfg));
                xecfg.ctxlen = 1; /* at least one context line */
                xpp.flags = XDF_NEED_MINIMAL;
                xdi_diff_outf(&mf1, &mf2, checkdiff_consume, &data,
 -                            &xpp, &xecfg, &ecb);
 +                            &xpp, &xecfg);
  
                if (data.ws_rule & WS_BLANK_AT_EOF) {
                        struct emit_callback ecbdata;
@@@ -3379,6 -3365,7 +3362,6 @@@ static int diff_get_patch_id(struct dif
        for (i = 0; i < q->nr; i++) {
                xpparam_t xpp;
                xdemitconf_t xecfg;
 -              xdemitcb_t ecb;
                mmfile_t mf1, mf2;
                struct diff_filepair *p = q->queue[i];
                int len1, len2;
                xecfg.ctxlen = 3;
                xecfg.flags = XDL_EMIT_FUNCNAMES;
                xdi_diff_outf(&mf1, &mf2, patch_id_consume, &data,
 -                            &xpp, &xecfg, &ecb);
 +                            &xpp, &xecfg);
        }
  
        git_SHA1_Final(sha1, &ctx);
@@@ -3912,3 -3899,47 +3895,47 @@@ static char *run_textconv(const char *p
  
        return strbuf_detach(&buf, outsize);
  }
+ static size_t fill_textconv(struct userdiff_driver *driver,
+                           struct diff_filespec *df,
+                           char **outbuf)
+ {
+       size_t size;
+       if (!driver || !driver->textconv) {
+               if (!DIFF_FILE_VALID(df)) {
+                       *outbuf = "";
+                       return 0;
+               }
+               if (diff_populate_filespec(df, 0))
+                       die("unable to read files to diff");
+               *outbuf = df->data;
+               return df->size;
+       }
+       if (driver->textconv_cache) {
+               *outbuf = notes_cache_get(driver->textconv_cache, df->sha1,
+                                         &size);
+               if (*outbuf)
+                       return size;
+       }
+       *outbuf = run_textconv(driver->textconv, df, &size);
+       if (!*outbuf)
+               die("unable to read files to diff");
+       if (driver->textconv_cache) {
+               /* ignore errors, as we might be in a readonly repository */
+               notes_cache_put(driver->textconv_cache, df->sha1, *outbuf,
+                               size);
+               /*
+                * we could save up changes and flush them all at the end,
+                * but we would need an extra call after all diffing is done.
+                * Since generating a cache entry is the slow path anyway,
+                * this extra overhead probably isn't a big deal.
+                */
+               notes_cache_write(driver->textconv_cache);
+       }
+       return size;
+ }