cab91a201acd9e3bb2ef09a810f13dc84185333b
   1/*
   2 * Copyright (C) 2005 Junio C Hamano
   3 */
   4#include "cache.h"
   5#include "diff.h"
   6#include "diffcore.h"
   7#include "delta.h"
   8#include "count-delta.h"
   9
  10static int very_different(struct diff_filespec *src,
  11                          struct diff_filespec *dst,
  12                          int min_score)
  13{
  14        /* dst is recorded as a modification of src.  Are they so
  15         * different that we are better off recording this as a pair
  16         * of delete and create?  min_score is the minimum amount of
  17         * new material that must exist in the dst and not in src for
  18         * the pair to be considered a complete rewrite, and recommended
  19         * to be set to a very high value, 99% or so.
  20         *
  21         * The value we return represents the amount of new material
  22         * that is in dst and not in src.  We return 0 when we do not
  23         * want to get the filepair broken.
  24         */
  25        void *delta;
  26        unsigned long delta_size, base_size, src_copied, literal_added;
  27
  28        if (!S_ISREG(src->mode) || !S_ISREG(dst->mode))
  29                return 0; /* leave symlink rename alone */
  30
  31        if (diff_populate_filespec(src, 1) || diff_populate_filespec(dst, 1))
  32                return 0; /* error but caught downstream */
  33
  34        delta_size = ((src->size < dst->size) ?
  35                      (dst->size - src->size) : (src->size - dst->size));
  36
  37        /* Notice that we use max of src and dst as the base size,
  38         * unlike rename similarity detection.  This is so that we do
  39         * not mistake a large addition as a complete rewrite.
  40         */
  41        base_size = ((src->size < dst->size) ? dst->size : src->size);
  42
  43        /*
  44         * If file size difference is too big compared to the
  45         * base_size, we declare this a complete rewrite.
  46         */
  47        if (base_size * min_score < delta_size * MAX_SCORE)
  48                return MAX_SCORE;
  49
  50        if (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))
  51                return 0; /* error but caught downstream */
  52
  53        delta = diff_delta(src->data, src->size,
  54                           dst->data, dst->size,
  55                           &delta_size);
  56
  57        /* A delta that has a lot of literal additions would have
  58         * big delta_size no matter what else it does.
  59         */
  60        if (base_size * min_score < delta_size * MAX_SCORE)
  61                return MAX_SCORE;
  62
  63        /* Estimate the edit size by interpreting delta. */
  64        if (count_delta(delta, delta_size, &src_copied, &literal_added)) {
  65                free(delta);
  66                return 0;
  67        }
  68        free(delta);
  69
  70        /* Extent of damage */
  71        if (src->size + literal_added < src_copied)
  72                delta_size = 0;
  73        else
  74                delta_size = (src->size - src_copied) + literal_added;
  75
  76        if (base_size < delta_size)
  77                return MAX_SCORE;
  78
  79        return delta_size * MAX_SCORE / base_size; 
  80}
  81
  82void diffcore_break(int min_score)
  83{
  84        struct diff_queue_struct *q = &diff_queued_diff;
  85        struct diff_queue_struct outq;
  86        int i;
  87
  88        if (!min_score)
  89                min_score = DEFAULT_BREAK_SCORE;
  90
  91        outq.nr = outq.alloc = 0;
  92        outq.queue = NULL;
  93
  94        for (i = 0; i < q->nr; i++) {
  95                struct diff_filepair *p = q->queue[i];
  96                int score;
  97
  98                /* We deal only with in-place edit of non directory.
  99                 * We do not break anything else.
 100                 */
 101                if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two) &&
 102                    !S_ISDIR(p->one->mode) && !S_ISDIR(p->two->mode) &&
 103                    !strcmp(p->one->path, p->two->path)) {
 104                        score = very_different(p->one, p->two, min_score);
 105                        if (min_score <= score) {
 106                                /* Split this into delete and create */
 107                                struct diff_filespec *null_one, *null_two;
 108                                struct diff_filepair *dp;
 109
 110                                /* deletion of one */
 111                                null_one = alloc_filespec(p->one->path);
 112                                dp = diff_queue(&outq, p->one, null_one);
 113                                dp->score = score;
 114                                dp->broken_pair = 1;
 115
 116                                /* creation of two */
 117                                null_two = alloc_filespec(p->two->path);
 118                                dp = diff_queue(&outq, null_two, p->two);
 119                                dp->score = score;
 120                                dp->broken_pair = 1;
 121
 122                                free(p); /* not diff_free_filepair(), we are
 123                                          * reusing one and two here.
 124                                          */
 125                                continue;
 126                        }
 127                }
 128                diff_q(&outq, p);
 129        }
 130        free(q->queue);
 131        *q = outq;
 132
 133        return;
 134}