]> git.ipfire.org Git - thirdparty/git.git/blobdiff - diff.c
Merge branch 'jt/v2-fetch-nego-fix'
[thirdparty/git.git] / diff.c
diff --git a/diff.c b/diff.c
index efe42b341ae15ebceb1e216518ab3a542be361dc..d1ad6a3c4ad0bc5049d1af26ca0ea33fc9f68a1e 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -25,7 +25,7 @@
 #include "packfile.h"
 #include "parse-options.h"
 #include "help.h"
-#include "fetch-object.h"
+#include "promisor-remote.h"
 
 #ifdef NO_FAST_WORKING_DIRECTORY
 #define FAST_WORKING_DIRECTORY 0
@@ -414,14 +414,6 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       if (!strcmp(var, "diff.wserrorhighlight")) {
-               int val = parse_ws_error_highlight(value);
-               if (val < 0)
-                       return -1;
-               ws_error_highlight_default = val;
-               return 0;
-       }
-
        if (git_color_config(var, value, cb) < 0)
                return -1;
 
@@ -450,6 +442,14 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
                return color_parse(value, diff_colors[slot]);
        }
 
+       if (!strcmp(var, "diff.wserrorhighlight")) {
+               int val = parse_ws_error_highlight(value);
+               if (val < 0)
+                       return -1;
+               ws_error_highlight_default = val;
+               return 0;
+       }
+
        /* like GNU diff's --suppress-blank-empty option  */
        if (!strcmp(var, "diff.suppressblankempty") ||
                        /* for backwards compatibility */
@@ -573,7 +573,7 @@ static int fill_mmfile(struct repository *r, mmfile_t *mf,
                mf->size = 0;
                return 0;
        }
-       else if (diff_populate_filespec(r, one, 0))
+       else if (diff_populate_filespec(r, one, NULL))
                return -1;
 
        mf->ptr = one->data;
@@ -585,9 +585,13 @@ static int fill_mmfile(struct repository *r, mmfile_t *mf,
 static unsigned long diff_filespec_size(struct repository *r,
                                        struct diff_filespec *one)
 {
+       struct diff_populate_filespec_options dpf_options = {
+               .check_size_only = 1,
+       };
+
        if (!DIFF_FILE_VALID(one))
                return 0;
-       diff_populate_filespec(r, one, CHECK_SIZE_ONLY);
+       diff_populate_filespec(r, one, &dpf_options);
        return one->size;
 }
 
@@ -933,16 +937,18 @@ static int cmp_in_block_with_wsd(const struct diff_options *o,
 }
 
 static int moved_entry_cmp(const void *hashmap_cmp_fn_data,
-                          const void *entry,
-                          const void *entry_or_key,
+                          const struct hashmap_entry *eptr,
+                          const struct hashmap_entry *entry_or_key,
                           const void *keydata)
 {
        const struct diff_options *diffopt = hashmap_cmp_fn_data;
-       const struct moved_entry *a = entry;
-       const struct moved_entry *b = entry_or_key;
+       const struct moved_entry *a, *b;
        unsigned flags = diffopt->color_moved_ws_handling
                         & XDF_WHITESPACE_FLAGS;
 
+       a = container_of(eptr, const struct moved_entry, ent);
+       b = container_of(entry_or_key, const struct moved_entry, ent);
+
        if (diffopt->color_moved_ws_handling &
            COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
                /*
@@ -964,8 +970,9 @@ static struct moved_entry *prepare_entry(struct diff_options *o,
        struct moved_entry *ret = xmalloc(sizeof(*ret));
        struct emitted_diff_symbol *l = &o->emitted_symbols->buf[line_no];
        unsigned flags = o->color_moved_ws_handling & XDF_WHITESPACE_FLAGS;
+       unsigned int hash = xdiff_hash_string(l->line, l->len, flags);
 
-       ret->ent.hash = xdiff_hash_string(l->line, l->len, flags);
+       hashmap_entry_init(&ret->ent, hash);
        ret->es = l;
        ret->next_line = NULL;
 
@@ -1002,7 +1009,7 @@ static void add_lines_to_move_detection(struct diff_options *o,
                if (prev_line && prev_line->es->s == o->emitted_symbols->buf[n].s)
                        prev_line->next_line = key;
 
-               hashmap_add(hm, key);
+               hashmap_add(hm, &key->ent);
                prev_line = key;
        }
 }
@@ -1018,7 +1025,7 @@ static void pmb_advance_or_null(struct diff_options *o,
                struct moved_entry *prev = pmb[i].match;
                struct moved_entry *cur = (prev && prev->next_line) ?
                                prev->next_line : NULL;
-               if (cur && !hm->cmpfn(o, cur, match, NULL)) {
+               if (cur && !hm->cmpfn(o, &cur->ent, &match->ent, NULL)) {
                        pmb[i].match = cur;
                } else {
                        pmb[i].match = NULL;
@@ -1035,7 +1042,7 @@ static void pmb_advance_or_null_multi_match(struct diff_options *o,
        int i;
        char *got_match = xcalloc(1, pmb_nr);
 
-       for (; match; match = hashmap_get_next(hm, match)) {
+       hashmap_for_each_entry_from(hm, match, ent) {
                for (i = 0; i < pmb_nr; i++) {
                        struct moved_entry *prev = pmb[i].match;
                        struct moved_entry *cur = (prev && prev->next_line) ?
@@ -1143,13 +1150,13 @@ static void mark_color_as_moved(struct diff_options *o,
                case DIFF_SYMBOL_PLUS:
                        hm = del_lines;
                        key = prepare_entry(o, n);
-                       match = hashmap_get(hm, key, NULL);
+                       match = hashmap_get_entry(hm, key, ent, NULL);
                        free(key);
                        break;
                case DIFF_SYMBOL_MINUS:
                        hm = add_lines;
                        key = prepare_entry(o, n);
-                       match = hashmap_get(hm, key, NULL);
+                       match = hashmap_get_entry(hm, key, ent, NULL);
                        free(key);
                        break;
                default:
@@ -1188,7 +1195,7 @@ static void mark_color_as_moved(struct diff_options *o,
                         * The current line is the start of a new block.
                         * Setup the set of potential blocks.
                         */
-                       for (; match; match = hashmap_get_next(hm, match)) {
+                       hashmap_for_each_entry_from(hm, match, ent) {
                                ALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc);
                                if (o->color_moved_ws_handling &
                                    COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) {
@@ -2492,22 +2499,6 @@ static void pprint_rename(struct strbuf *name, const char *a, const char *b)
        }
 }
 
-struct diffstat_t {
-       int nr;
-       int alloc;
-       struct diffstat_file {
-               char *from_name;
-               char *name;
-               char *print_name;
-               const char *comments;
-               unsigned is_unmerged:1;
-               unsigned is_binary:1;
-               unsigned is_renamed:1;
-               unsigned is_interesting:1;
-               uintmax_t added, deleted;
-       } **files;
-};
-
 static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,
                                          const char *name_a,
                                          const char *name_b)
@@ -2548,7 +2539,7 @@ static int scale_linear(int it, int width, int max_change)
        /*
         * make sure that at least one '-' or '+' is printed if
         * there is any change to this path. The easiest way is to
-        * scale linearly as if the alloted width is one column shorter
+        * scale linearly as if the allotted width is one column shorter
         * than it is, and then add 1 to the result.
         */
        return 1 + (it * (width - 1) / max_change);
@@ -3033,6 +3024,9 @@ static void show_dirstat(struct diff_options *options)
                struct diff_filepair *p = q->queue[i];
                const char *name;
                unsigned long copied, added, damage;
+               struct diff_populate_filespec_options dpf_options = {
+                       .check_size_only = 1,
+               };
 
                name = p->two->path ? p->two->path : p->one->path;
 
@@ -3060,19 +3054,19 @@ static void show_dirstat(struct diff_options *options)
                }
 
                if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) {
-                       diff_populate_filespec(options->repo, p->one, 0);
-                       diff_populate_filespec(options->repo, p->two, 0);
+                       diff_populate_filespec(options->repo, p->one, NULL);
+                       diff_populate_filespec(options->repo, p->two, NULL);
                        diffcore_count_changes(options->repo,
                                               p->one, p->two, NULL, NULL,
                                               &copied, &added);
                        diff_free_filespec_data(p->one);
                        diff_free_filespec_data(p->two);
                } else if (DIFF_FILE_VALID(p->one)) {
-                       diff_populate_filespec(options->repo, p->one, CHECK_SIZE_ONLY);
+                       diff_populate_filespec(options->repo, p->one, &dpf_options);
                        copied = added = 0;
                        diff_free_filespec_data(p->one);
                } else if (DIFF_FILE_VALID(p->two)) {
-                       diff_populate_filespec(options->repo, p->two, CHECK_SIZE_ONLY);
+                       diff_populate_filespec(options->repo, p->two, &dpf_options);
                        copied = 0;
                        added = p->two->size;
                        diff_free_filespec_data(p->two);
@@ -3154,7 +3148,7 @@ static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *o
        gather_dirstat(options, &dir, changed, "", 0);
 }
 
-static void free_diffstat_info(struct diffstat_t *diffstat)
+void free_diffstat_info(struct diffstat_t *diffstat)
 {
        int i;
        for (i = 0; i < diffstat->nr; i++) {
@@ -3193,7 +3187,7 @@ static int is_conflict_marker(const char *line, int marker_size, unsigned long l
        for (cnt = 1; cnt < marker_size; cnt++)
                if (line[cnt] != firstchar)
                        return 0;
-       /* line[1] thru line[marker_size-1] are same as firstchar */
+       /* line[1] through line[marker_size-1] are same as firstchar */
        if (len < marker_size + 1 || !isspace(line[marker_size]))
                return 0;
        return 1;
@@ -3352,13 +3346,17 @@ static void emit_binary_diff(struct diff_options *o,
 int diff_filespec_is_binary(struct repository *r,
                            struct diff_filespec *one)
 {
+       struct diff_populate_filespec_options dpf_options = {
+               .check_binary = 1,
+       };
+
        if (one->is_binary == -1) {
                diff_filespec_load_driver(one, r->index);
                if (one->driver->binary != -1)
                        one->is_binary = one->driver->binary;
                else {
                        if (!one->data && DIFF_FILE_VALID(one))
-                               diff_populate_filespec(r, one, CHECK_BINARY);
+                               diff_populate_filespec(r, one, &dpf_options);
                        if (one->is_binary == -1 && one->data)
                                one->is_binary = buffer_is_binary(one->data,
                                                one->size);
@@ -3690,8 +3688,8 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
        }
 
        else if (complete_rewrite) {
-               diff_populate_filespec(o->repo, one, 0);
-               diff_populate_filespec(o->repo, two, 0);
+               diff_populate_filespec(o->repo, one, NULL);
+               diff_populate_filespec(o->repo, two, NULL);
                data->deleted = count_lines(one->data, one->size);
                data->added = count_lines(two->data, two->size);
        }
@@ -3927,9 +3925,10 @@ static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
  */
 int diff_populate_filespec(struct repository *r,
                           struct diff_filespec *s,
-                          unsigned int flags)
+                          const struct diff_populate_filespec_options *options)
 {
-       int size_only = flags & CHECK_SIZE_ONLY;
+       int size_only = options ? options->check_size_only : 0;
+       int check_binary = options ? options->check_binary : 0;
        int err = 0;
        int conv_flags = global_conv_flags_eol;
        /*
@@ -3999,7 +3998,7 @@ int diff_populate_filespec(struct repository *r,
                 * opening the file and inspecting the contents, this
                 * is probably fine.
                 */
-               if ((flags & CHECK_BINARY) &&
+               if (check_binary &&
                    s->size > big_file_threshold && s->is_binary == -1) {
                        s->is_binary = 1;
                        return 0;
@@ -4024,12 +4023,30 @@ int diff_populate_filespec(struct repository *r,
                }
        }
        else {
-               enum object_type type;
-               if (size_only || (flags & CHECK_BINARY)) {
-                       type = oid_object_info(r, &s->oid, &s->size);
-                       if (type < 0)
-                               die("unable to read %s",
-                                   oid_to_hex(&s->oid));
+               struct object_info info = {
+                       .sizep = &s->size
+               };
+
+               if (!(size_only || check_binary))
+                       /*
+                        * Set contentp, since there is no chance that merely
+                        * the size is sufficient.
+                        */
+                       info.contentp = &s->data;
+
+               if (options && options->missing_object_cb) {
+                       if (!oid_object_info_extended(r, &s->oid, &info,
+                                                     OBJECT_INFO_LOOKUP_REPLACE |
+                                                     OBJECT_INFO_SKIP_FETCH_OBJECT))
+                               goto object_read;
+                       options->missing_object_cb(options->missing_object_data);
+               }
+               if (oid_object_info_extended(r, &s->oid, &info,
+                                            OBJECT_INFO_LOOKUP_REPLACE))
+                       die("unable to read %s", oid_to_hex(&s->oid));
+
+object_read:
+               if (size_only || check_binary) {
                        if (size_only)
                                return 0;
                        if (s->size > big_file_threshold && s->is_binary == -1) {
@@ -4037,9 +4054,12 @@ int diff_populate_filespec(struct repository *r,
                                return 0;
                        }
                }
-               s->data = read_object_file(&s->oid, &type, &s->size);
-               if (!s->data)
-                       die("unable to read %s", oid_to_hex(&s->oid));
+               if (!info.contentp) {
+                       info.contentp = &s->data;
+                       if (oid_object_info_extended(r, &s->oid, &info,
+                                                    OBJECT_INFO_LOOKUP_REPLACE))
+                               die("unable to read %s", oid_to_hex(&s->oid));
+               }
                s->should_free = 1;
        }
        return 0;
@@ -4075,6 +4095,9 @@ static void prep_temp_blob(struct index_state *istate,
        struct strbuf tempfile = STRBUF_INIT;
        char *path_dup = xstrdup(path);
        const char *base = basename(path_dup);
+       struct checkout_metadata meta;
+
+       init_checkout_metadata(&meta, NULL, NULL, oid);
 
        /* Generate "XXXXXX_basename.ext" */
        strbuf_addstr(&tempfile, "XXXXXX_");
@@ -4084,7 +4107,7 @@ static void prep_temp_blob(struct index_state *istate,
        if (!temp->tempfile)
                die_errno("unable to create temp-file");
        if (convert_to_working_tree(istate, path,
-                       (const char *)blob, (size_t)size, &buf)) {
+                       (const char *)blob, (size_t)size, &buf, &meta)) {
                blob = buf.buf;
                size = buf.len;
        }
@@ -4154,7 +4177,7 @@ static struct diff_tempfile *prepare_temp_file(struct repository *r,
                return temp;
        }
        else {
-               if (diff_populate_filespec(r, one, 0))
+               if (diff_populate_filespec(r, one, NULL))
                        die("cannot read data blob for %s", one->path);
                prep_temp_blob(r->index, name, temp,
                               one->data, one->size,
@@ -5978,7 +6001,7 @@ static void diff_summary(struct diff_options *opt, struct diff_filepair *p)
 }
 
 struct patch_id_t {
-       git_SHA_CTX *ctx;
+       git_hash_ctx *ctx;
        int patchlen;
 };
 
@@ -5995,16 +6018,16 @@ static int remove_space(char *line, int len)
        return dst - line;
 }
 
-void flush_one_hunk(struct object_id *result, git_SHA_CTX *ctx)
+void flush_one_hunk(struct object_id *result, git_hash_ctx *ctx)
 {
        unsigned char hash[GIT_MAX_RAWSZ];
        unsigned short carry = 0;
        int i;
 
-       git_SHA1_Final(hash, ctx);
-       git_SHA1_Init(ctx);
+       the_hash_algo->final_fn(hash, ctx);
+       the_hash_algo->init_fn(ctx);
        /* 20-byte sum, with carry */
-       for (i = 0; i < GIT_SHA1_RAWSZ; ++i) {
+       for (i = 0; i < the_hash_algo->rawsz; ++i) {
                carry += result->hash[i] + hash[i];
                result->hash[i] = carry;
                carry >>= 8;
@@ -6018,21 +6041,21 @@ static void patch_id_consume(void *priv, char *line, unsigned long len)
 
        new_len = remove_space(line, len);
 
-       git_SHA1_Update(data->ctx, line, new_len);
+       the_hash_algo->update_fn(data->ctx, line, new_len);
        data->patchlen += new_len;
 }
 
-static void patch_id_add_string(git_SHA_CTX *ctx, const char *str)
+static void patch_id_add_string(git_hash_ctx *ctx, const char *str)
 {
-       git_SHA1_Update(ctx, str, strlen(str));
+       the_hash_algo->update_fn(ctx, str, strlen(str));
 }
 
-static void patch_id_add_mode(git_SHA_CTX *ctx, unsigned mode)
+static void patch_id_add_mode(git_hash_ctx *ctx, unsigned mode)
 {
        /* large enough for 2^32 in octal */
        char buf[12];
        int len = xsnprintf(buf, sizeof(buf), "%06o", mode);
-       git_SHA1_Update(ctx, buf, len);
+       the_hash_algo->update_fn(ctx, buf, len);
 }
 
 /* returns 0 upon success, and writes result into oid */
@@ -6040,10 +6063,10 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid
 {
        struct diff_queue_struct *q = &diff_queued_diff;
        int i;
-       git_SHA_CTX ctx;
+       git_hash_ctx ctx;
        struct patch_id_t data;
 
-       git_SHA1_Init(&ctx);
+       the_hash_algo->init_fn(&ctx);
        memset(&data, 0, sizeof(struct patch_id_t));
        data.ctx = &ctx;
        oidclr(oid);
@@ -6076,27 +6099,27 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid
                len2 = remove_space(p->two->path, strlen(p->two->path));
                patch_id_add_string(&ctx, "diff--git");
                patch_id_add_string(&ctx, "a/");
-               git_SHA1_Update(&ctx, p->one->path, len1);
+               the_hash_algo->update_fn(&ctx, p->one->path, len1);
                patch_id_add_string(&ctx, "b/");
-               git_SHA1_Update(&ctx, p->two->path, len2);
+               the_hash_algo->update_fn(&ctx, p->two->path, len2);
 
                if (p->one->mode == 0) {
                        patch_id_add_string(&ctx, "newfilemode");
                        patch_id_add_mode(&ctx, p->two->mode);
                        patch_id_add_string(&ctx, "---/dev/null");
                        patch_id_add_string(&ctx, "+++b/");
-                       git_SHA1_Update(&ctx, p->two->path, len2);
+                       the_hash_algo->update_fn(&ctx, p->two->path, len2);
                } else if (p->two->mode == 0) {
                        patch_id_add_string(&ctx, "deletedfilemode");
                        patch_id_add_mode(&ctx, p->one->mode);
                        patch_id_add_string(&ctx, "---a/");
-                       git_SHA1_Update(&ctx, p->one->path, len1);
+                       the_hash_algo->update_fn(&ctx, p->one->path, len1);
                        patch_id_add_string(&ctx, "+++/dev/null");
                } else {
                        patch_id_add_string(&ctx, "---a/");
-                       git_SHA1_Update(&ctx, p->one->path, len1);
+                       the_hash_algo->update_fn(&ctx, p->one->path, len1);
                        patch_id_add_string(&ctx, "+++b/");
-                       git_SHA1_Update(&ctx, p->two->path, len2);
+                       the_hash_algo->update_fn(&ctx, p->two->path, len2);
                }
 
                if (diff_header_only)
@@ -6108,10 +6131,10 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid
 
                if (diff_filespec_is_binary(options->repo, p->one) ||
                    diff_filespec_is_binary(options->repo, p->two)) {
-                       git_SHA1_Update(&ctx, oid_to_hex(&p->one->oid),
-                                       GIT_SHA1_HEXSZ);
-                       git_SHA1_Update(&ctx, oid_to_hex(&p->two->oid),
-                                       GIT_SHA1_HEXSZ);
+                       the_hash_algo->update_fn(&ctx, oid_to_hex(&p->one->oid),
+                                       the_hash_algo->hexsz);
+                       the_hash_algo->update_fn(&ctx, oid_to_hex(&p->two->oid),
+                                       the_hash_algo->hexsz);
                        continue;
                }
 
@@ -6128,7 +6151,7 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid
        }
 
        if (!stable)
-               git_SHA1_Final(oid->hash, &ctx);
+               the_hash_algo->final_fn(oid->hash, &ctx);
 
        return 0;
 }
@@ -6230,8 +6253,10 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o)
                        if (o->color_moved == COLOR_MOVED_ZEBRA_DIM)
                                dim_moved_lines(o);
 
-                       hashmap_free(&add_lines, 1);
-                       hashmap_free(&del_lines, 1);
+                       hashmap_free_entries(&add_lines, struct moved_entry,
+                                               ent);
+                       hashmap_free_entries(&del_lines, struct moved_entry,
+                                               ent);
                }
 
                for (i = 0; i < esm.nr; i++)
@@ -6278,12 +6303,7 @@ void diff_flush(struct diff_options *options)
            dirstat_by_line) {
                struct diffstat_t diffstat;
 
-               memset(&diffstat, 0, sizeof(struct diffstat_t));
-               for (i = 0; i < q->nr; i++) {
-                       struct diff_filepair *p = q->queue[i];
-                       if (check_pair_status(p))
-                               diff_flush_stat(p, options, &diffstat);
-               }
+               compute_diffstat(options, &diffstat, q);
                if (output_format & DIFF_FORMAT_NUMSTAT)
                        show_numstat(&diffstat, options);
                if (output_format & DIFF_FORMAT_DIFFSTAT)
@@ -6423,9 +6443,9 @@ static int diff_filespec_is_identical(struct repository *r,
 {
        if (S_ISGITLINK(one->mode))
                return 0;
-       if (diff_populate_filespec(r, one, 0))
+       if (diff_populate_filespec(r, one, NULL))
                return 0;
-       if (diff_populate_filespec(r, two, 0))
+       if (diff_populate_filespec(r, two, NULL))
                return 0;
        return !memcmp(one->data, two->data, one->size);
 }
@@ -6433,6 +6453,12 @@ static int diff_filespec_is_identical(struct repository *r,
 static int diff_filespec_check_stat_unmatch(struct repository *r,
                                            struct diff_filepair *p)
 {
+       struct diff_populate_filespec_options dpf_options = {
+               .check_size_only = 1,
+               .missing_object_cb = diff_queued_diff_prefetch,
+               .missing_object_data = r,
+       };
+
        if (p->done_skip_stat_unmatch)
                return p->skip_stat_unmatch_result;
 
@@ -6455,8 +6481,8 @@ static int diff_filespec_check_stat_unmatch(struct repository *r,
            !DIFF_FILE_VALID(p->two) ||
            (p->one->oid_valid && p->two->oid_valid) ||
            (p->one->mode != p->two->mode) ||
-           diff_populate_filespec(r, p->one, CHECK_SIZE_ONLY) ||
-           diff_populate_filespec(r, p->two, CHECK_SIZE_ONLY) ||
+           diff_populate_filespec(r, p->one, &dpf_options) ||
+           diff_populate_filespec(r, p->two, &dpf_options) ||
            (p->one->size != p->two->size) ||
            !diff_filespec_is_identical(r, p->one, p->two)) /* (2) */
                p->skip_stat_unmatch_result = 1;
@@ -6507,41 +6533,59 @@ void diffcore_fix_diff_index(void)
        QSORT(q->queue, q->nr, diffnamecmp);
 }
 
-static void add_if_missing(struct repository *r,
-                          struct oid_array *to_fetch,
-                          const struct diff_filespec *filespec)
+void diff_add_if_missing(struct repository *r,
+                        struct oid_array *to_fetch,
+                        const struct diff_filespec *filespec)
 {
        if (filespec && filespec->oid_valid &&
+           !S_ISGITLINK(filespec->mode) &&
            oid_object_info_extended(r, &filespec->oid, NULL,
                                     OBJECT_INFO_FOR_PREFETCH))
                oid_array_append(to_fetch, &filespec->oid);
 }
 
-void diffcore_std(struct diff_options *options)
+void diff_queued_diff_prefetch(void *repository)
 {
-       if (options->repo == the_repository &&
-           repository_format_partial_clone) {
-               /*
-                * Prefetch the diff pairs that are about to be flushed.
-                */
-               int i;
-               struct diff_queue_struct *q = &diff_queued_diff;
-               struct oid_array to_fetch = OID_ARRAY_INIT;
+       struct repository *repo = repository;
+       int i;
+       struct diff_queue_struct *q = &diff_queued_diff;
+       struct oid_array to_fetch = OID_ARRAY_INIT;
 
-               for (i = 0; i < q->nr; i++) {
-                       struct diff_filepair *p = q->queue[i];
-                       add_if_missing(options->repo, &to_fetch, p->one);
-                       add_if_missing(options->repo, &to_fetch, p->two);
-               }
-               if (to_fetch.nr)
-                       /*
-                        * NEEDSWORK: Consider deduplicating the OIDs sent.
-                        */
-                       fetch_objects(repository_format_partial_clone,
-                                     to_fetch.oid, to_fetch.nr);
-               oid_array_clear(&to_fetch);
+       for (i = 0; i < q->nr; i++) {
+               struct diff_filepair *p = q->queue[i];
+               diff_add_if_missing(repo, &to_fetch, p->one);
+               diff_add_if_missing(repo, &to_fetch, p->two);
        }
 
+       /*
+        * NEEDSWORK: Consider deduplicating the OIDs sent.
+        */
+       promisor_remote_get_direct(repo, to_fetch.oid, to_fetch.nr);
+
+       oid_array_clear(&to_fetch);
+}
+
+void diffcore_std(struct diff_options *options)
+{
+       int output_formats_to_prefetch = DIFF_FORMAT_DIFFSTAT |
+               DIFF_FORMAT_NUMSTAT |
+               DIFF_FORMAT_PATCH |
+               DIFF_FORMAT_SHORTSTAT |
+               DIFF_FORMAT_DIRSTAT;
+
+       /*
+        * Check if the user requested a blob-data-requiring diff output and/or
+        * break-rewrite detection (which requires blob data). If yes, prefetch
+        * the diff pairs.
+        *
+        * If no prefetching occurs, diffcore_rename() will prefetch if it
+        * decides that it needs inexact rename detection.
+        */
+       if (options->repo == the_repository && has_promisor_remote() &&
+           (options->output_format & output_formats_to_prefetch ||
+            options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK))
+               diff_queued_diff_prefetch(options->repo);
+
        /* NOTE please keep the following in sync with diff_tree_combined() */
        if (options->skip_stat_unmatch)
                diffcore_skip_stat_unmatch(options);
@@ -6616,6 +6660,20 @@ static int is_submodule_ignored(const char *path, struct diff_options *options)
        return ignored;
 }
 
+void compute_diffstat(struct diff_options *options,
+                     struct diffstat_t *diffstat,
+                     struct diff_queue_struct *q)
+{
+       int i;
+
+       memset(diffstat, 0, sizeof(struct diffstat_t));
+       for (i = 0; i < q->nr; i++) {
+               struct diff_filepair *p = q->queue[i];
+               if (check_pair_status(p))
+                       diff_flush_stat(p, options, diffstat);
+       }
+}
+
 void diff_addremove(struct diff_options *options,
                    int addremove, unsigned mode,
                    const struct object_id *oid,
@@ -6773,7 +6831,7 @@ size_t fill_textconv(struct repository *r,
                        *outbuf = "";
                        return 0;
                }
-               if (diff_populate_filespec(r, df, 0))
+               if (diff_populate_filespec(r, df, NULL))
                        die("unable to read files to diff");
                *outbuf = df->data;
                return df->size;