Skip to content

Commit c04a715

Browse files
committed
diff: make --dirstat binary-file safe
Instead of counting added and removed lines (and mixing the byte size reported for binary files in the result), summarize the extent of damage the same way as we count similarity for rename detection. Signed-off-by: Junio C Hamano <[email protected]>
1 parent 2a5fe25 commit c04a715

File tree

1 file changed

+60
-23
lines changed

1 file changed

+60
-23
lines changed

diff.c

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -991,18 +991,23 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options)
991991
}
992992
}
993993

994-
struct diffstat_dir {
995-
struct diffstat_file **files;
996-
int nr, percent, cumulative;
994+
struct dirstat_file {
995+
const char *name;
996+
unsigned long changed;
997997
};
998998

999-
static long gather_dirstat(FILE *file, struct diffstat_dir *dir, unsigned long changed, const char *base, int baselen)
999+
struct dirstat_dir {
1000+
struct dirstat_file *files;
1001+
int alloc, nr, percent, cumulative;
1002+
};
1003+
1004+
static long gather_dirstat(FILE *file, struct dirstat_dir *dir, unsigned long changed, const char *base, int baselen)
10001005
{
10011006
unsigned long this_dir = 0;
10021007
unsigned int sources = 0;
10031008

10041009
while (dir->nr) {
1005-
struct diffstat_file *f = *dir->files;
1010+
struct dirstat_file *f = dir->files;
10061011
int namelen = strlen(f->name);
10071012
unsigned long this;
10081013
char *slash;
@@ -1017,10 +1022,7 @@ static long gather_dirstat(FILE *file, struct diffstat_dir *dir, unsigned long c
10171022
this = gather_dirstat(file, dir, changed, f->name, newbaselen);
10181023
sources++;
10191024
} else {
1020-
if (f->is_unmerged || f->is_binary)
1021-
this = 0;
1022-
else
1023-
this = f->added + f->deleted;
1025+
this = f->changed;
10241026
dir->files++;
10251027
dir->nr--;
10261028
sources += 2;
@@ -1048,30 +1050,65 @@ static long gather_dirstat(FILE *file, struct diffstat_dir *dir, unsigned long c
10481050
return this_dir;
10491051
}
10501052

1051-
static void show_dirstat(struct diffstat_t *data, struct diff_options *options)
1053+
static void show_dirstat(struct diff_options *options)
10521054
{
10531055
int i;
10541056
unsigned long changed;
1055-
struct diffstat_dir dir;
1057+
struct dirstat_dir dir;
1058+
struct diff_queue_struct *q = &diff_queued_diff;
1059+
1060+
dir.files = NULL;
1061+
dir.alloc = 0;
1062+
dir.nr = 0;
1063+
dir.percent = options->dirstat_percent;
1064+
dir.cumulative = options->output_format & DIFF_FORMAT_CUMULATIVE;
10561065

1057-
/* Calculate total changes */
10581066
changed = 0;
1059-
for (i = 0; i < data->nr; i++) {
1060-
if (data->files[i]->is_binary || data->files[i]->is_unmerged)
1067+
for (i = 0; i < q->nr; i++) {
1068+
struct diff_filepair *p = q->queue[i];
1069+
const char *name;
1070+
unsigned long copied, added, damage;
1071+
1072+
name = p->one->path ? p->one->path : p->two->path;
1073+
1074+
if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) {
1075+
diff_populate_filespec(p->one, 0);
1076+
diff_populate_filespec(p->two, 0);
1077+
diffcore_count_changes(p->one, p->two, NULL, NULL, 0,
1078+
&copied, &added);
1079+
diff_free_filespec_data(p->one);
1080+
diff_free_filespec_data(p->two);
1081+
} else if (DIFF_FILE_VALID(p->one)) {
1082+
diff_populate_filespec(p->one, 1);
1083+
copied = added = 0;
1084+
diff_free_filespec_data(p->one);
1085+
} else if (DIFF_FILE_VALID(p->two)) {
1086+
diff_populate_filespec(p->two, 1);
1087+
copied = 0;
1088+
added = p->two->size;
1089+
diff_free_filespec_data(p->two);
1090+
} else
10611091
continue;
1062-
changed += data->files[i]->added;
1063-
changed += data->files[i]->deleted;
1092+
1093+
/*
1094+
* Original minus copied is the removed material,
1095+
* added is the new material. They are both damages
1096+
* made to the preimage.
1097+
*/
1098+
damage = (p->one->size - copied) + added;
1099+
1100+
ALLOC_GROW(dir.files, dir.nr + 1, dir.alloc);
1101+
dir.files[dir.nr].name = name;
1102+
dir.files[dir.nr].changed = damage;
1103+
changed += damage;
1104+
dir.nr++;
10641105
}
10651106

10661107
/* This can happen even with many files, if everything was renames */
10671108
if (!changed)
10681109
return;
10691110

10701111
/* Show all directories with more than x% of the changes */
1071-
dir.files = data->files;
1072-
dir.nr = data->nr;
1073-
dir.percent = options->dirstat_percent;
1074-
dir.cumulative = options->output_format & DIFF_FORMAT_CUMULATIVE;
10751112
gather_dirstat(options->file, &dir, changed, "", 0);
10761113
}
10771114

@@ -3095,7 +3132,7 @@ void diff_flush(struct diff_options *options)
30953132
separator++;
30963133
}
30973134

3098-
if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIRSTAT)) {
3135+
if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT)) {
30993136
struct diffstat_t diffstat;
31003137

31013138
memset(&diffstat, 0, sizeof(struct diffstat_t));
@@ -3105,8 +3142,6 @@ void diff_flush(struct diff_options *options)
31053142
if (check_pair_status(p))
31063143
diff_flush_stat(p, options, &diffstat);
31073144
}
3108-
if (output_format & DIFF_FORMAT_DIRSTAT)
3109-
show_dirstat(&diffstat, options);
31103145
if (output_format & DIFF_FORMAT_NUMSTAT)
31113146
show_numstat(&diffstat, options);
31123147
if (output_format & DIFF_FORMAT_DIFFSTAT)
@@ -3116,6 +3151,8 @@ void diff_flush(struct diff_options *options)
31163151
free_diffstat_info(&diffstat);
31173152
separator++;
31183153
}
3154+
if (output_format & DIFF_FORMAT_DIRSTAT)
3155+
show_dirstat(options);
31193156

31203157
if (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) {
31213158
for (i = 0; i < q->nr; i++)

0 commit comments

Comments
 (0)