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 <gitster@pobox.com>
Junio C Hamano committed
Feb 12, 2008 at 17:06 UTC
c04a7155a03e5ef089864912769baf9001f64aa9
1 file changed
+60
-23
diff.c
+60
-23
@@ -991,18 +991,23 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options)
991
}
992
}
993
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;
997
};
998
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)
1005
{
1006
unsigned long this_dir = 0;
1007
unsigned int sources = 0;
1008
1009
while (dir->nr) {
1005
- struct diffstat_file *f = *dir->files;
1010
+ struct dirstat_file *f = dir->files;
1011
int namelen = strlen(f->name);
1012
unsigned long this;
1013
char *slash;
@@ -1017,10 +1022,7 @@ static long gather_dirstat(FILE *file, struct diffstat_dir *dir, unsigned long c
1022
this = gather_dirstat(file, dir, changed, f->name, newbaselen);
1023
sources++;
1024
} else {
1020
- if (f->is_unmerged || f->is_binary)
1021
- this = 0;
1022
- else
1023
- this = f->added + f->deleted;
1025
+ this = f->changed;
1026
dir->files++;
1027
dir->nr--;
1028
sources += 2;
@@ -1048,19 +1050,58 @@ static long gather_dirstat(FILE *file, struct diffstat_dir *dir, unsigned long c
1050
return this_dir;
1051
}
1052
1051
-static void show_dirstat(struct diffstat_t *data, struct diff_options *options)
1053
+static void show_dirstat(struct diff_options *options)
1054
{
1055
int i;
1056
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;
1065
1057
- /* Calculate total changes */
1066
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
1091
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++;
1105
}
1106
1107
/* This can happen even with many files, if everything was renames */
@@ -1068,10 +1109,6 @@ static void show_dirstat(struct diffstat_t *data, struct diff_options *options)
1109
return;
1110
1111
/* 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;
1112
gather_dirstat(options->file, &dir, changed, "", 0);
1113
}
1114
@@ -3095,7 +3132,7 @@ void diff_flush(struct diff_options *options)
3132
separator++;
3133
}
3134
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)) {
3136
struct diffstat_t diffstat;
3137
3138
memset(&diffstat, 0, sizeof(struct diffstat_t));
@@ -3105,8 +3142,6 @@ void diff_flush(struct diff_options *options)
3142
if (check_pair_status(p))
3143
diff_flush_stat(p, options, &diffstat);
3144
}
3108
- if (output_format & DIFF_FORMAT_DIRSTAT)
3109
- show_dirstat(&diffstat, options);
3145
if (output_format & DIFF_FORMAT_NUMSTAT)
3146
show_numstat(&diffstat, options);
3147
if (output_format & DIFF_FORMAT_DIFFSTAT)
@@ -3116,6 +3151,8 @@ void diff_flush(struct diff_options *options)
3151
free_diffstat_info(&diffstat);
3152
separator++;
3153
}
3154
+ if (output_format & DIFF_FORMAT_DIRSTAT)
3155
+ show_dirstat(options);
3156
3157
if (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) {
3158
for (i = 0; i < q->nr; i++)