Start implementing "git-apply"
[git.git] / diffcore-rename.c
index 8aa8f84..794e5cc 100644 (file)
@@ -68,37 +68,39 @@ static int estimate_similarity(struct diff_filespec *src,
         * else.
         */
        void *delta;
-       unsigned long delta_size;
+       unsigned long delta_size, base_size;
        int score;
 
        delta_size = ((src->size < dst->size) ?
                      (dst->size - src->size) : (src->size - dst->size));
-
-       /* We would not consider rename followed by more than
-        * minimum_score/MAX_SCORE edits; that is, delta_size must be smaller
-        * than (src->size + dst->size)/2 * minimum_score/MAX_SCORE,
-        * which means...
+       base_size = ((src->size < dst->size) ? src->size : dst->size);
+
+       /* We would not consider edits that change the file size so
+        * drastically.  delta_size must be smaller than
+        * (MAX_SCORE-minimum_score)/MAX_SCORE * min(src->size, dst->size).
+        * Note that base_size == 0 case is handled here already
+        * and the final score computation below would not have a
+        * divide-by-zero issue.
         */
-
-       if ((src->size+dst->size)*minimum_score < delta_size*MAX_SCORE*2)
+       if (base_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE)
                return 0;
 
        delta = diff_delta(src->data, src->size,
                           dst->data, dst->size,
                           &delta_size);
+       /*
+        * We currently punt here, but we may later end up parsing the
+        * delta to really assess the extent of damage.  A big consecutive
+        * remove would produce small delta_size that affects quite a
+        * big portion of the file.
+        */
        free(delta);
 
-       /* This "delta" is really xdiff with adler32 and all the
-        * overheads but it is a quick and dirty approximation.
-        *
-        * Now we will give some score to it.  100% edit gets
-        * 0 points and 0% edit gets MAX_SCORE points.  That is, every
-        * 1/MAX_SCORE edit gets 1 point penalty.  The amount of penalty is:
-        *
-        * (delta_size * 2 / (src->size + dst->size)) * MAX_SCORE
-        *
+       /*
+        * Now we will give some score to it.  100% edit gets 0 points
+        * and 0% edit gets MAX_SCORE points.
         */
-       score = MAX_SCORE-(MAX_SCORE*2*delta_size/(src->size+dst->size));
+       score = MAX_SCORE - (MAX_SCORE * delta_size / base_size); 
        if (score < 0) return 0;
        if (MAX_SCORE < score) return MAX_SCORE;
        return score;
@@ -129,7 +131,7 @@ static void record_rename_pair(struct diff_queue_struct *outq,
         * To achieve this sort order, we give xform_work the number
         * above.
         */
-       struct diff_file_pair *dp = diff_queue(outq, src, dst);
+       struct diff_filepair *dp = diff_queue(outq, src, dst);
        dp->xfrm_work = (rank * 2 + 1) | (score<<RENAME_SCORE_SHIFT);
        dst->xfrm_flags |= RENAME_DST_MATCHED;
 }
@@ -140,7 +142,7 @@ static void debug_filespec(struct diff_filespec *s, int x, const char *one)
        fprintf(stderr, "queue[%d] %s (%s) %s %06o %s\n",
                x, one,
                s->path,
-               s->file_valid ? "valid" : "invalid",
+               DIFF_FILE_VALID(s) ? "valid" : "invalid",
                s->mode,
                s->sha1_valid ? sha1_to_hex(s->sha1) : "");
        fprintf(stderr, "queue[%d] %s size %lu flags %d\n",
@@ -148,7 +150,7 @@ static void debug_filespec(struct diff_filespec *s, int x, const char *one)
                s->size, s->xfrm_flags);
 }
 
-static void debug_filepair(const struct diff_file_pair *p, int i)
+static void debug_filepair(const struct diff_filepair *p, int i)
 {
        debug_filespec(p->one, i, "one");
        debug_filespec(p->two, i, "two");
@@ -165,7 +167,7 @@ static void debug_queue(const char *msg, struct diff_queue_struct *q)
                fprintf(stderr, "%s\n", msg);
        fprintf(stderr, "q->nr = %d\n", q->nr);
        for (i = 0; i < q->nr; i++) {
-               struct diff_file_pair *p = q->queue[i];
+               struct diff_filepair *p = q->queue[i];
                debug_filepair(p, i);
        }
 }
@@ -180,8 +182,8 @@ static void debug_queue(const char *msg, struct diff_queue_struct *q)
  */
 static int rank_compare(const void *a_, const void *b_)
 {
-       const struct diff_file_pair *a = *(const struct diff_file_pair **)a_;
-       const struct diff_file_pair *b = *(const struct diff_file_pair **)b_;
+       const struct diff_filepair *a = *(const struct diff_filepair **)a_;
+       const struct diff_filepair *b = *(const struct diff_filepair **)b_;
        int a_rank = a->xfrm_work & ((1<<RENAME_SCORE_SHIFT) - 1);
        int b_rank = b->xfrm_work & ((1<<RENAME_SCORE_SHIFT) - 1);
 
@@ -207,8 +209,8 @@ static int needs_to_stay(struct diff_queue_struct *q, int i,
         * as the source of rename/copy), we need to copy, not rename.
         */
        while (i < q->nr) {
-               struct diff_file_pair *p = q->queue[i++];
-               if (!p->two->file_valid)
+               struct diff_filepair *p = q->queue[i++];
+               if (!DIFF_FILE_VALID(p->two))
                        continue; /* removed is fine */
                if (strcmp(p->one->path, it->path))
                        continue; /* not relevant */
@@ -222,10 +224,27 @@ static int needs_to_stay(struct diff_queue_struct *q, int i,
        return 0;
 }
 
-void diff_detect_rename(struct diff_queue_struct *q,
-                       int detect_rename,
-                       int minimum_score)
+int diff_scoreopt_parse(const char *opt)
 {
+       int diglen, num, scale, i;
+       if (opt[0] != '-' || (opt[1] != 'M' && opt[1] != 'C'))
+               return -1; /* that is not a -M nor -C option */
+       diglen = strspn(opt+2, "0123456789");
+       if (diglen == 0 || strlen(opt+2) != diglen)
+               return 0; /* use default */
+       sscanf(opt+2, "%d", &num);
+       for (i = 0, scale = 1; i < diglen; i++)
+               scale *= 10;
+
+       /* user says num divided by scale and we say internally that
+        * is MAX_SCORE * num / scale.
+        */
+       return MAX_SCORE * num / scale;
+}
+
+void diffcore_rename(int detect_rename, int minimum_score)
+{
+       struct diff_queue_struct *q = &diff_queued_diff;
        struct diff_queue_struct outq;
        struct diff_rename_pool created, deleted, stay;
        struct diff_rename_pool *(srcs[2]);
@@ -233,6 +252,8 @@ void diff_detect_rename(struct diff_queue_struct *q,
        int h, i, j;
        int num_create, num_src, dst_cnt, src_cnt;
 
+       if (!minimum_score)
+               minimum_score = DEFAULT_MINIMUM_SCORE;
        outq.queue = NULL;
        outq.nr = outq.alloc = 0;
 
@@ -243,21 +264,14 @@ void diff_detect_rename(struct diff_queue_struct *q,
        srcs[0] = &deleted;
        srcs[1] = &stay;
 
-       /* NEEDSWORK:
-        * (1) make sure we properly ignore but pass trees.
-        *
-        * (2) make sure we do right thing on the same path deleted
-        *     and created in the same patch.
-        */
-
        for (i = 0; i < q->nr; i++) {
-               struct diff_file_pair *p = q->queue[i];
-               if (!p->one->file_valid)
-                       if (!p->two->file_valid)
+               struct diff_filepair *p = q->queue[i];
+               if (!DIFF_FILE_VALID(p->one))
+                       if (!DIFF_FILE_VALID(p->two))
                                continue; /* ignore nonsense */
                        else
                                diff_rename_pool_add(&created, p->two);
-               else if (!p->two->file_valid)
+               else if (!DIFF_FILE_VALID(p->two))
                        diff_rename_pool_add(&deleted, p->one);
                else if (1 < detect_rename) /* find copy, too */
                        diff_rename_pool_add(&stay, p->one);
@@ -321,7 +335,7 @@ void diff_detect_rename(struct diff_queue_struct *q,
                if (mx[i].dst->xfrm_flags & RENAME_DST_MATCHED)
                        continue; /* alreayd done, either exact or fuzzy. */
                if (mx[i].score < minimum_score)
-                       continue;
+                       break; /* there is not any more diffs applicable. */
                record_rename_pair(&outq,
                                  mx[i].src, mx[i].dst, mx[i].rank,
                                  mx[i].score);
@@ -340,20 +354,20 @@ void diff_detect_rename(struct diff_queue_struct *q,
         * See comments at the top of record_rename_pair for numbers used
         * to assign xfrm_work.
         *
-        * Note that we have not annotated the diff_file_pair with any comment
+        * Note that we have not annotated the diff_filepair with any comment
         * so there is nothing other than p to free.
         */
        for (i = 0; i < q->nr; i++) {
-               struct diff_file_pair *dp, *p = q->queue[i];
-               if (!p->one->file_valid) {
-                       if (p->two->file_valid) {
+               struct diff_filepair *dp, *p = q->queue[i];
+               if (!DIFF_FILE_VALID(p->one)) {
+                       if (DIFF_FILE_VALID(p->two)) {
                                /* creation */
                                dp = diff_queue(&outq, p->one, p->two);
                                dp->xfrm_work = 4;
                        }
                        /* otherwise it is a nonsense; just ignore it */
                }
-               else if (!p->two->file_valid) {
+               else if (!DIFF_FILE_VALID(p->two)) {
                        /* deletion */
                        dp = diff_queue(&outq, p->one, p->two);
                        dp->xfrm_work = 2;
@@ -378,15 +392,15 @@ void diff_detect_rename(struct diff_queue_struct *q,
 
        /* Copy it out to q, removing duplicates. */
        for (i = 0; i < outq.nr; i++) {
-               struct diff_file_pair *p = outq.queue[i];
-               if (!p->one->file_valid) {
+               struct diff_filepair *p = outq.queue[i];
+               if (!DIFF_FILE_VALID(p->one)) {
                        /* created */
                        if (p->two->xfrm_flags & RENAME_DST_MATCHED)
                                ; /* rename/copy created it already */
                        else
                                diff_queue(q, p->one, p->two);
                }
-               else if (!p->two->file_valid) {
+               else if (!DIFF_FILE_VALID(p->two)) {
                        /* deleted */
                        if (p->one->xfrm_flags & RENAME_SRC_GONE)
                                ; /* rename/copy deleted it already */
@@ -395,7 +409,7 @@ void diff_detect_rename(struct diff_queue_struct *q,
                }
                else if (strcmp(p->one->path, p->two->path)) {
                        /* rename or copy */
-                       struct diff_file_pair *dp =
+                       struct diff_filepair *dp =
                                diff_queue(q, p->one, p->two);
                        int msglen = (strlen(p->one->path) +
                                      strlen(p->two->path) + 100);