diffcore-break.con commit tutorial.txt: start describing how to copy repositories (f35ca9e)
   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;
  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        delta_size = count_delta(delta, delta_size);
  65        free(delta);
  66        if (delta_size == UINT_MAX)
  67                return 0; /* error in delta computation */
  68
  69        if (base_size < delta_size)
  70                return MAX_SCORE;
  71
  72        return delta_size * MAX_SCORE / base_size; 
  73}
  74
  75void diffcore_break(int min_score)
  76{
  77        struct diff_queue_struct *q = &diff_queued_diff;
  78        struct diff_queue_struct outq;
  79        int i;
  80
  81        if (!min_score)
  82                min_score = DEFAULT_BREAK_SCORE;
  83
  84        outq.nr = outq.alloc = 0;
  85        outq.queue = NULL;
  86
  87        for (i = 0; i < q->nr; i++) {
  88                struct diff_filepair *p = q->queue[i];
  89                int score;
  90
  91                /* We deal only with in-place edit of non directory.
  92                 * We do not break anything else.
  93                 */
  94                if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two) &&
  95                    !S_ISDIR(p->one->mode) && !S_ISDIR(p->two->mode) &&
  96                    !strcmp(p->one->path, p->two->path)) {
  97                        score = very_different(p->one, p->two, min_score);
  98                        if (min_score <= score) {
  99                                /* Split this into delete and create */
 100                                struct diff_filespec *null_one, *null_two;
 101                                struct diff_filepair *dp;
 102
 103                                /* deletion of one */
 104                                null_one = alloc_filespec(p->one->path);
 105                                dp = diff_queue(&outq, p->one, null_one);
 106                                dp->score = score;
 107                                dp->broken_pair = 1;
 108
 109                                /* creation of two */
 110                                null_two = alloc_filespec(p->two->path);
 111                                dp = diff_queue(&outq, null_two, p->two);
 112                                dp->score = score;
 113                                dp->broken_pair = 1;
 114
 115                                free(p); /* not diff_free_filepair(), we are
 116                                          * reusing one and two here.
 117                                          */
 118                                continue;
 119                        }
 120                }
 121                diff_q(&outq, p);
 122        }
 123        free(q->queue);
 124        *q = outq;
 125
 126        return;
 127}