Skip to content

Commit bceafe7

Browse files
Junio C HamanoLinus Torvalds
authored andcommitted
[PATCH] Fix diff-pruning logic which was running prune too early.
For later stages to reorder patches, pruning logic and rename detection logic should not decide which delete to discard (because another entry said it will take over the file as a rename) until the very end. Also fix some tests that were assuming the earlier "last one is rename or keep everything else is copy" semantics of diff-raw format, which no longer is true. Signed-off-by: Junio C Hamano <junkio@cox.net> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
1 parent 9a4a100 commit bceafe7

File tree

8 files changed

+69
-127
lines changed

8 files changed

+69
-127
lines changed

diff-cache.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,6 @@ int main(int argc, const char **argv)
229229
ret = diff_cache(active_cache, active_nr);
230230
if (detect_rename)
231231
diffcore_rename(detect_rename, diff_score_opt);
232-
diffcore_prune();
233232
if (pickaxe)
234233
diffcore_pickaxe(pickaxe);
235234
if (2 <= argc)

diff-files.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@ int main(int argc, const char **argv)
115115
}
116116
if (detect_rename)
117117
diffcore_rename(detect_rename, diff_score_opt);
118-
diffcore_prune();
119118
if (pickaxe)
120119
diffcore_pickaxe(pickaxe);
121120
if (1 < argc)

diff-tree.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,10 +266,8 @@ static int call_diff_flush(void)
266266
{
267267
if (detect_rename)
268268
diffcore_rename(detect_rename, diff_score_opt);
269-
diffcore_prune();
270269
if (pickaxe)
271270
diffcore_pickaxe(pickaxe);
272-
273271
if (diff_queue_is_empty()) {
274272
diff_flush(DIFF_FORMAT_NO_OUTPUT, 0);
275273
return 0;

diff.c

Lines changed: 64 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@ static const char *diff_opts = "-pu";
1212
static unsigned char null_sha1[20] = { 0, };
1313

1414
static int reverse_diff;
15-
static int generate_patch;
16-
static int line_termination = '\n';
17-
static int inter_name_termination = '\t';
1815

1916
static const char *external_diff(void)
2017
{
@@ -502,7 +499,9 @@ struct diff_filepair *diff_queue(struct diff_queue_struct *queue,
502499
return dp;
503500
}
504501

505-
static void diff_flush_raw(struct diff_filepair *p)
502+
static void diff_flush_raw(struct diff_filepair *p,
503+
int line_termination,
504+
int inter_name_termination)
506505
{
507506
int two_paths;
508507
char status[10];
@@ -566,10 +565,6 @@ static void diff_flush_patch(struct diff_filepair *p)
566565
const char *name, *other;
567566
char msg_[PATH_MAX*2+200], *msg;
568567

569-
/* diffcore_prune() keeps "stay" entries for diff-raw
570-
* copy/rename detection, but when we are generating
571-
* patches we do not need them.
572-
*/
573568
if (diff_unmodified_pair(p))
574569
return;
575570

@@ -585,7 +580,7 @@ static void diff_flush_patch(struct diff_filepair *p)
585580
"similarity index %d%%\n"
586581
"copy from %s\n"
587582
"copy to %s\n",
588-
(int)(0.5 + p->score * 100/MAX_SCORE),
583+
(int)(0.5 + p->score * 100.0/MAX_SCORE),
589584
p->one->path, p->two->path);
590585
msg = msg_;
591586
break;
@@ -594,7 +589,7 @@ static void diff_flush_patch(struct diff_filepair *p)
594589
"similarity index %d%%\n"
595590
"rename old %s\n"
596591
"rename new %s\n",
597-
(int)(0.5 + p->score * 100/MAX_SCORE),
592+
(int)(0.5 + p->score * 100.0/MAX_SCORE),
598593
p->one->path, p->two->path);
599594
msg = msg_;
600595
break;
@@ -630,105 +625,82 @@ int diff_needs_to_stay(struct diff_queue_struct *q, int i,
630625
return 0;
631626
}
632627

633-
static int diff_used_as_source(struct diff_queue_struct *q, int lim,
634-
struct diff_filespec *it)
628+
int diff_queue_is_empty(void)
635629
{
636-
int i;
637-
for (i = 0; i < lim; i++) {
638-
struct diff_filepair *p = q->queue[i++];
639-
if (!strcmp(p->one->path, it->path))
640-
return 1;
641-
}
642-
return 0;
630+
struct diff_queue_struct *q = &diff_queued_diff;
631+
return q->nr == 0;
643632
}
644633

645-
void diffcore_prune(void)
634+
static void diff_resolve_rename_copy(void)
646635
{
647-
/*
648-
* Although rename/copy detection wants to have "no-change"
649-
* entries fed into them, the downstream do not need to see
650-
* them, unless we had rename/copy for the same path earlier.
651-
* This function removes such entries.
652-
*
653-
* The applications that use rename/copy should:
654-
*
655-
* (1) feed change and "no-change" entries via diff_queue().
656-
* (2) call diffcore_rename, and any other future diffcore_xxx
657-
* that would benefit by still having "no-change" entries.
658-
* (3) call diffcore_prune
659-
* (4) call other diffcore_xxx that do not need to see
660-
* "no-change" entries.
661-
* (5) call diff_flush().
662-
*/
663-
struct diff_queue_struct *q = &diff_queued_diff;
664-
struct diff_queue_struct outq;
665636
int i;
666-
667-
outq.queue = NULL;
668-
outq.nr = outq.alloc = 0;
669-
637+
struct diff_queue_struct *q = &diff_queued_diff;
670638
for (i = 0; i < q->nr; i++) {
671639
struct diff_filepair *p = q->queue[i];
672-
if (!diff_unmodified_pair(p) ||
673-
diff_used_as_source(q, i, p->one))
674-
diff_q(&outq, p);
675-
else
676-
free(p);
640+
p->status = 0;
641+
if (DIFF_PAIR_UNMERGED(p))
642+
p->status = 'U';
643+
else if (!DIFF_FILE_VALID((p)->one))
644+
p->status = 'N';
645+
else if (!DIFF_FILE_VALID((p)->two)) {
646+
/* maybe earlier one said 'R', meaning
647+
* it will take it, in which case we do
648+
* not need to keep 'D'.
649+
*/
650+
int j;
651+
for (j = 0; j < i; j++) {
652+
struct diff_filepair *pp = q->queue[j];
653+
if (pp->status == 'R' &&
654+
!strcmp(pp->one->path, p->one->path))
655+
break;
656+
}
657+
if (j < i)
658+
continue;
659+
p->status = 'D';
660+
}
661+
else if (strcmp(p->one->path, p->two->path)) {
662+
/* This is rename or copy. Which one is it? */
663+
if (diff_needs_to_stay(q, i+1, p->one))
664+
p->status = 'C';
665+
else
666+
p->status = 'R';
667+
}
668+
else if (memcmp(p->one->sha1, p->two->sha1, 20))
669+
p->status = 'M';
670+
else {
671+
/* we do not need this one */
672+
p->status = 0;
673+
}
677674
}
678-
free(q->queue);
679-
*q = outq;
680-
return;
681-
}
682-
683-
int diff_queue_is_empty(void)
684-
{
685-
struct diff_queue_struct *q = &diff_queued_diff;
686-
return q->nr == 0;
687675
}
688676

689677
void diff_flush(int diff_output_style, int resolve_rename_copy)
690678
{
691679
struct diff_queue_struct *q = &diff_queued_diff;
692680
int i;
681+
int line_termination = '\n';
682+
int inter_name_termination = '\t';
693683

694-
generate_patch = 0;
695-
switch (diff_output_style) {
696-
case DIFF_FORMAT_HUMAN:
697-
line_termination = '\n';
698-
inter_name_termination = '\t';
699-
break;
700-
case DIFF_FORMAT_MACHINE:
684+
if (diff_output_style == DIFF_FORMAT_MACHINE)
701685
line_termination = inter_name_termination = 0;
702-
break;
703-
case DIFF_FORMAT_PATCH:
704-
generate_patch = 1;
705-
break;
706-
}
686+
if (resolve_rename_copy)
687+
diff_resolve_rename_copy();
688+
707689
for (i = 0; i < q->nr; i++) {
708690
struct diff_filepair *p = q->queue[i];
709-
if (resolve_rename_copy) {
710-
if (DIFF_PAIR_UNMERGED(p))
711-
p->status = 'U';
712-
else if (!DIFF_FILE_VALID((p)->one))
713-
p->status = 'N';
714-
else if (!DIFF_FILE_VALID((p)->two))
715-
p->status = 'D';
716-
else if (strcmp(p->one->path, p->two->path)) {
717-
/* This is rename or copy. Which one is it? */
718-
if (diff_needs_to_stay(q, i+1, p->one))
719-
p->status = 'C';
720-
else
721-
p->status = 'R';
722-
}
723-
else
724-
p->status = 'M';
725-
}
726-
if (generate_patch)
691+
if (p->status == 0)
692+
continue;
693+
switch (diff_output_style) {
694+
case DIFF_FORMAT_PATCH:
727695
diff_flush_patch(p);
728-
else
729-
diff_flush_raw(p);
696+
break;
697+
case DIFF_FORMAT_HUMAN:
698+
case DIFF_FORMAT_MACHINE:
699+
diff_flush_raw(p, line_termination,
700+
inter_name_termination);
701+
break;
702+
}
730703
}
731-
732704
for (i = 0; i < q->nr; i++) {
733705
struct diff_filepair *p = q->queue[i];
734706
diff_free_filespec_data(p->one);
@@ -755,9 +727,9 @@ void diff_addremove(int addremove, unsigned mode,
755727
* with something like '=' or '*' (I haven't decided
756728
* which but should not make any difference).
757729
* Feeding the same new and old to diff_change()
758-
* also has the same effect. diffcore_prune() should
759-
* be used to filter uninteresting ones out before the
760-
* final output happens.
730+
* also has the same effect.
731+
* Before the final output happens, they are pruned after
732+
* merged into rename/copy pairs as appropriate.
761733
*/
762734
if (reverse_diff)
763735
addremove = (addremove == '+' ? '-' :

diff.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ extern void diff_setup(int reverse);
3939

4040
extern void diffcore_rename(int rename_copy, int minimum_score);
4141

42-
extern void diffcore_prune(void);
43-
4442
extern void diffcore_pickaxe(const char *needle);
4543
extern void diffcore_pathspec(const char **pathspec);
4644

diffcore-rename.c

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,7 @@ static void record_rename_pair(struct diff_queue_struct *outq,
133133
* The downstream diffcore transformers are free to reorder
134134
* the entries as long as they keep file pairs that has the
135135
* same p->one->path in earlier rename_rank to appear before
136-
* later ones. This ordering is used by the diff_flush()
137-
* logic to tell renames from copies, and also used by the
138-
* diffcore_prune() logic to omit unnecessary
139-
* "no-modification" entries.
136+
* later ones.
140137
*
141138
* To the final output routine, and in the diff-raw format
142139
* output, a rename/copy that is based on a path that has a
@@ -271,14 +268,8 @@ void diffcore_rename(int detect_rename, int minimum_score)
271268

272269
/* We really want to cull the candidates list early
273270
* with cheap tests in order to avoid doing deltas.
274-
*
275-
* With the current callers, we should not have already
276-
* matched entries at this point, but it is nonetheless
277-
* checked for sanity.
278271
*/
279272
for (i = 0; i < created.nr; i++) {
280-
if (created.s[i]->xfrm_flags & RENAME_DST_MATCHED)
281-
continue; /* we have matched exactly already */
282273
for (h = 0; h < sizeof(srcs)/sizeof(srcs[0]); h++) {
283274
struct diff_rename_pool *p = srcs[h];
284275
for (j = 0; j < p->nr; j++) {
@@ -386,25 +377,13 @@ void diffcore_rename(int detect_rename, int minimum_score)
386377
}
387378
else if (!DIFF_FILE_VALID(p->two)) {
388379
/* deleted */
389-
if (p->one->xfrm_flags & RENAME_SRC_GONE)
390-
; /* rename/copy deleted it already */
391-
else
392-
diff_queue(q, p->one, p->two);
380+
diff_queue(q, p->one, p->two);
393381
}
394382
else if (strcmp(p->one->path, p->two->path)) {
395383
/* rename or copy */
396384
struct diff_filepair *dp =
397385
diff_queue(q, p->one, p->two);
398386
dp->score = p->score;
399-
400-
/* if we have a later entry that is a rename/copy
401-
* that depends on p->one, then we copy here.
402-
* otherwise we rename it.
403-
*/
404-
if (!diff_needs_to_stay(&outq, i+1, p->one))
405-
/* this is the last one, so mark it as gone.
406-
*/
407-
p->one->xfrm_flags |= RENAME_SRC_GONE;
408387
}
409388
else
410389
/* otherwise it is a modified (or "stay") entry */

diffcore.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
#define DEFAULT_MINIMUM_SCORE 5000
1313

1414
#define RENAME_DST_MATCHED 01
15-
#define RENAME_SRC_GONE 02
16-
#define RENAME_SCORE_SHIFT 8
1715

1816
struct diff_filespec {
1917
unsigned char sha1[20];

t/t4005-diff-rename-2.sh

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,14 +147,13 @@ test_expect_success \
147147
################################################################
148148

149149
# tree has COPYING and rezrov. work tree has the same COPYING and
150-
# copy-edited COPYING.1, and unchanged rezrov. We should see
151-
# unmodified COPYING in the output, so that downstream diff-helper can
152-
# notice. We should not say anything about rezrov.
150+
# copy-edited COPYING.1, and unchanged rezrov. We should not say
151+
# anything about rezrov nor COPYING, since the revised again diff-raw
152+
# nows how to say Copy.
153153

154154
git-diff-cache -C $tree >current
155155
cat >expected <<\EOF
156156
:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234 COPYING COPYING.1
157-
:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 M COPYING
158157
EOF
159158

160159
test_expect_success \

0 commit comments

Comments
 (0)