Skip to content

Commit 15d061b

Browse files
Junio C HamanoLinus Torvalds
authored andcommitted
[PATCH] Fix the way diffcore-rename records unremoved source.
Earier version of diffcore-rename used to keep unmodified filepair in its output so that the last stage of the processing that tells renames from copies can make all of rename/copy to copies. However this had a bad interaction with other diffcore filters that wanted to run after diffcore-rename, in that such unmodified filepair must be retained for proper distinction between renames and copies to happen. This patch fixes the problem by changing the way diffcore-rename records the information needed to distinguish "all are copies" case and "the last one is a rename" case. Signed-off-by: Junio C Hamano <junkio@cox.net> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
1 parent 367cec1 commit 15d061b

File tree

4 files changed

+169
-80
lines changed

4 files changed

+169
-80
lines changed

diff.c

Lines changed: 37 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,7 @@ struct diff_filepair *diff_queue(struct diff_queue_struct *queue,
518518
dp->one = one;
519519
dp->two = two;
520520
dp->score = 0;
521+
dp->source_stays = 0;
521522
diff_q(queue, dp);
522523
return dp;
523524
}
@@ -675,8 +676,8 @@ void diff_debug_filepair(const struct diff_filepair *p, int i)
675676
{
676677
diff_debug_filespec(p->one, i, "one");
677678
diff_debug_filespec(p->two, i, "two");
678-
fprintf(stderr, "score %d, status %c\n",
679-
p->score, p->status ? : '?');
679+
fprintf(stderr, "score %d, status %c source_stays %d\n",
680+
p->score, p->status ? : '?', p->source_stays);
680681
}
681682

682683
void diff_debug_queue(const char *msg, struct diff_queue_struct *q)
@@ -698,32 +699,35 @@ static void diff_resolve_rename_copy(void)
698699
struct diff_filepair *p, *pp;
699700
struct diff_queue_struct *q = &diff_queued_diff;
700701

701-
/* This should not depend on the ordering of things. */
702-
703702
diff_debug_queue("resolve-rename-copy", q);
704703

705704
for (i = 0; i < q->nr; i++) {
706705
p = q->queue[i];
707706
p->status = 0; /* undecided */
708707
if (DIFF_PAIR_UNMERGED(p))
709708
p->status = 'U';
710-
else if (!DIFF_FILE_VALID((p)->one))
709+
else if (!DIFF_FILE_VALID(p->one))
711710
p->status = 'N';
712-
else if (!DIFF_FILE_VALID((p)->two)) {
713-
/* Deletion record should be omitted if there
714-
* are rename/copy entries using this one as
715-
* the source. Then we can say one of them
716-
* is a rename and the rest are copies.
711+
else if (!DIFF_FILE_VALID(p->two)) {
712+
/* Deleted entry may have been picked up by
713+
* another rename-copy entry. So we scan the
714+
* queue and if we find one that uses us as the
715+
* source we do not say delete for this entry.
717716
*/
718-
p->status = 'D';
719717
for (j = 0; j < q->nr; j++) {
720718
pp = q->queue[j];
721-
if (!strcmp(pp->one->path, p->one->path) &&
722-
strcmp(pp->one->path, pp->two->path)) {
719+
if (!strcmp(p->one->path, pp->one->path) &&
720+
pp->score) {
721+
/* rename/copy are always valid
722+
* so we do not say DIFF_FILE_VALID()
723+
* on pp->one and pp->two.
724+
*/
723725
p->status = 'X';
724726
break;
725727
}
726728
}
729+
if (!p->status)
730+
p->status = 'D';
727731
}
728732
else if (DIFF_PAIR_TYPE_CHANGED(p))
729733
p->status = 'T';
@@ -732,33 +736,24 @@ static void diff_resolve_rename_copy(void)
732736
* whose both sides are valid and of the same type, i.e.
733737
* either in-place edit or rename/copy edit.
734738
*/
735-
else if (strcmp(p->one->path, p->two->path)) {
736-
/* See if there is somebody else anywhere that
737-
* will keep the path (either modified or
738-
* unmodified). If so, we have to be a copy,
739-
* not a rename. In addition, if there is
740-
* some other rename or copy that comes later
741-
* than us that uses the same source, we
742-
* have to be a copy, not a rename.
739+
else if (p->score) {
740+
if (p->source_stays) {
741+
p->status = 'C';
742+
continue;
743+
}
744+
/* See if there is some other filepair that
745+
* copies from the same source as us. If so
746+
* we are a copy. Otherwise we are a rename.
743747
*/
744-
for (j = 0; j < q->nr; j++) {
748+
for (j = i + 1; j < q->nr; j++) {
745749
pp = q->queue[j];
746750
if (strcmp(pp->one->path, p->one->path))
747-
continue;
748-
if (!strcmp(pp->one->path, pp->two->path)) {
749-
if (DIFF_FILE_VALID(pp->two)) {
750-
/* non-delete */
751-
p->status = 'C';
752-
break;
753-
}
754-
continue;
755-
}
756-
/* pp is a rename/copy ... */
757-
if (i < j) {
758-
/* ... and comes later than us */
759-
p->status = 'C';
760-
break;
761-
}
751+
continue; /* not us */
752+
if (!pp->score)
753+
continue; /* not a rename/copy */
754+
/* pp is a rename/copy from the same source */
755+
p->status = 'C';
756+
break;
762757
}
763758
if (!p->status)
764759
p->status = 'R';
@@ -767,8 +762,11 @@ static void diff_resolve_rename_copy(void)
767762
p->one->mode != p->two->mode)
768763
p->status = 'M';
769764
else
770-
/* this is a "no-change" entry */
771-
p->status = 'X';
765+
/* this is a "no-change" entry.
766+
* should not happen anymore.
767+
* p->status = 'X';
768+
*/
769+
die("internal error in diffcore: unmodified entry remains");
772770
}
773771
diff_debug_queue("resolve-rename-copy done", q);
774772
}

diffcore-rename.c

Lines changed: 24 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,15 @@ static struct diff_rename_dst *locate_rename_dst(struct diff_filespec *two,
5252
return &(rename_dst[first]);
5353
}
5454

55+
/* Table of rename/copy src files */
5556
static struct diff_rename_src {
5657
struct diff_filespec *one;
57-
unsigned src_used : 1;
58+
unsigned src_stays : 1;
5859
} *rename_src;
5960
static int rename_src_nr, rename_src_alloc;
6061

61-
static struct diff_rename_src *locate_rename_src(struct diff_filespec *one,
62-
int insert_ok)
62+
static struct diff_rename_src *register_rename_src(struct diff_filespec *one,
63+
int src_stays)
6364
{
6465
int first, last;
6566

@@ -77,9 +78,7 @@ static struct diff_rename_src *locate_rename_src(struct diff_filespec *one,
7778
}
7879
first = next+1;
7980
}
80-
/* not found */
81-
if (!insert_ok)
82-
return NULL;
81+
8382
/* insert to make it at "first" */
8483
if (rename_src_alloc <= rename_src_nr) {
8584
rename_src_alloc = alloc_nr(rename_src_alloc);
@@ -91,7 +90,7 @@ static struct diff_rename_src *locate_rename_src(struct diff_filespec *one,
9190
memmove(rename_src + first + 1, rename_src + first,
9291
(rename_src_nr - first - 1) * sizeof(*rename_src));
9392
rename_src[first].one = one;
94-
rename_src[first].src_used = 0;
93+
rename_src[first].src_stays = src_stays;
9594
return &(rename_src[first]);
9695
}
9796

@@ -199,15 +198,14 @@ static void record_rename_pair(struct diff_queue_struct *renq,
199198
fill_filespec(two, dst->sha1, dst->mode);
200199

201200
dp = diff_queue(renq, one, two);
202-
dp->score = score;
203-
204-
rename_src[src_index].src_used = 1;
201+
dp->score = score ? : 1; /* make sure it is at least 1 */
202+
dp->source_stays = rename_src[src_index].src_stays;
205203
rename_dst[dst_index].pair = dp;
206204
}
207205

208206
/*
209207
* We sort the rename similarity matrix with the score, in descending
210-
* order (more similar first).
208+
* order (the most similar first).
211209
*/
212210
static int score_compare(const void *a_, const void *b_)
213211
{
@@ -254,9 +252,9 @@ void diffcore_rename(int detect_rename, int minimum_score)
254252
else
255253
locate_rename_dst(p->two, 1);
256254
else if (!DIFF_FILE_VALID(p->two))
257-
locate_rename_src(p->one, 1);
258-
else if (1 < detect_rename) /* find copy, too */
259-
locate_rename_src(p->one, 1);
255+
register_rename_src(p->one, 0);
256+
else if (detect_rename == DIFF_DETECT_COPY)
257+
register_rename_src(p->one, 1);
260258
}
261259
if (rename_dst_nr == 0)
262260
goto cleanup; /* nothing to do */
@@ -280,7 +278,7 @@ void diffcore_rename(int detect_rename, int minimum_score)
280278
* doing the delta matrix altogether.
281279
*/
282280
if (renq.nr == rename_dst_nr)
283-
goto flush_rest;
281+
goto cleanup;
284282

285283
num_create = (rename_dst_nr - renq.nr);
286284
num_src = rename_src_nr;
@@ -307,37 +305,30 @@ void diffcore_rename(int detect_rename, int minimum_score)
307305
if (dst->pair)
308306
continue; /* already done, either exact or fuzzy. */
309307
if (mx[i].score < minimum_score)
310-
break; /* there is not any more diffs applicable. */
308+
break; /* there is no more usable pair. */
311309
record_rename_pair(&renq, mx[i].dst, mx[i].src, mx[i].score);
312310
}
313311
free(mx);
314312
diff_debug_queue("done detecting fuzzy", &renq);
315313

316-
flush_rest:
314+
cleanup:
317315
/* At this point, we have found some renames and copies and they
318316
* are kept in renq. The original list is still in *q.
319-
*
320-
* Scan the original list and move them into the outq; we will sort
321-
* outq and swap it into the queue supplied to pass that to
322-
* downstream, so we assign the sort keys in this loop.
323-
*
324-
* See comments at the top of record_rename_pair for numbers used
325-
* to assign rename_rank.
326317
*/
327318
outq.queue = NULL;
328319
outq.nr = outq.alloc = 0;
329320
for (i = 0; i < q->nr; i++) {
330321
struct diff_filepair *p = q->queue[i];
331-
struct diff_rename_src *src = locate_rename_src(p->one, 0);
332322
struct diff_rename_dst *dst = locate_rename_dst(p->two, 0);
333323
struct diff_filepair *pair_to_free = NULL;
334324

335325
if (dst) {
336326
/* creation */
337327
if (dst->pair) {
338-
/* renq has rename/copy already to produce
339-
* this file, so we do not emit the creation
340-
* record in the output.
328+
/* renq has rename/copy to produce
329+
* this file already, so we do not
330+
* emit the creation record in the
331+
* output.
341332
*/
342333
diff_q(&outq, dst->pair);
343334
pair_to_free = p;
@@ -349,17 +340,12 @@ void diffcore_rename(int detect_rename, int minimum_score)
349340
diff_q(&outq, p);
350341
}
351342
else if (!diff_unmodified_pair(p))
352-
/* all the other cases need to be recorded as is */
343+
/* all the usual ones need to be kept */
353344
diff_q(&outq, p);
354-
else {
355-
/* unmodified pair needs to be recorded only if
356-
* it is used as the source of rename/copy
357-
*/
358-
if (src && src->src_used)
359-
diff_q(&outq, p);
360-
else
361-
pair_to_free = p;
362-
}
345+
else
346+
/* no need to keep unmodified pairs */
347+
pair_to_free = p;
348+
363349
if (pair_to_free)
364350
diff_free_filepair(pair_to_free);
365351
}
@@ -370,7 +356,6 @@ void diffcore_rename(int detect_rename, int minimum_score)
370356
*q = outq;
371357
diff_debug_queue("done collapsing", q);
372358

373-
cleanup:
374359
free(rename_dst);
375360
rename_dst = NULL;
376361
rename_dst_nr = rename_dst_alloc = 0;

diffcore.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,11 @@ extern void diff_free_filespec_data(struct diff_filespec *);
3939
struct diff_filepair {
4040
struct diff_filespec *one;
4141
struct diff_filespec *two;
42-
int score; /* only valid when one and two are different paths */
43-
int status; /* M C R N D U (see Documentation/diff-format.txt) */
42+
unsigned short int score; /* only valid when one and two are
43+
* different paths
44+
*/
45+
char source_stays; /* all of R/C are copies */
46+
char status; /* M C R N D U (see Documentation/diff-format.txt) */
4447
};
4548
#define DIFF_PAIR_UNMERGED(p) \
4649
(!DIFF_FILE_VALID((p)->one) && !DIFF_FILE_VALID((p)->two))

0 commit comments

Comments
 (0)