|
Packit Service |
20376f |
/*
|
|
Packit Service |
20376f |
* Copyright (C) the libgit2 contributors. All rights reserved.
|
|
Packit Service |
20376f |
*
|
|
Packit Service |
20376f |
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
|
Packit Service |
20376f |
* a Linking Exception. For full terms see the included COPYING file.
|
|
Packit Service |
20376f |
*/
|
|
Packit Service |
20376f |
#include "common.h"
|
|
Packit Service |
20376f |
#include "vector.h"
|
|
Packit Service |
20376f |
#include "diff.h"
|
|
Packit Service |
20376f |
#include "patch_generate.h"
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
#define DIFF_RENAME_FILE_SEPARATOR " => "
|
|
Packit Service |
20376f |
#define STATS_FULL_MIN_SCALE 7
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
typedef struct {
|
|
Packit Service |
20376f |
size_t insertions;
|
|
Packit Service |
20376f |
size_t deletions;
|
|
Packit Service |
20376f |
} diff_file_stats;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
struct git_diff_stats {
|
|
Packit Service |
20376f |
git_diff *diff;
|
|
Packit Service |
20376f |
diff_file_stats *filestats;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
size_t files_changed;
|
|
Packit Service |
20376f |
size_t insertions;
|
|
Packit Service |
20376f |
size_t deletions;
|
|
Packit Service |
20376f |
size_t renames;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
size_t max_name;
|
|
Packit Service |
20376f |
size_t max_filestat;
|
|
Packit Service |
20376f |
int max_digits;
|
|
Packit Service |
20376f |
};
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int digits_for_value(size_t val)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
int count = 1;
|
|
Packit Service |
20376f |
size_t placevalue = 10;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
while (val >= placevalue) {
|
|
Packit Service |
20376f |
++count;
|
|
Packit Service |
20376f |
placevalue *= 10;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return count;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
int git_diff_file_stats__full_to_buf(
|
|
Packit Service |
20376f |
git_buf *out,
|
|
Packit Service |
20376f |
const git_diff_delta *delta,
|
|
Packit Service |
20376f |
const diff_file_stats *filestat,
|
|
Packit Service |
20376f |
const git_diff_stats *stats,
|
|
Packit Service |
20376f |
size_t width)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
const char *old_path = NULL, *new_path = NULL;
|
|
Packit Service |
20376f |
size_t padding, old_size, new_size;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
old_path = delta->old_file.path;
|
|
Packit Service |
20376f |
new_path = delta->new_file.path;
|
|
Packit Service |
20376f |
old_size = delta->old_file.size;
|
|
Packit Service |
20376f |
new_size = delta->new_file.size;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (git_buf_printf(out, " %s", old_path) < 0)
|
|
Packit Service |
20376f |
goto on_error;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (strcmp(old_path, new_path) != 0) {
|
|
Packit Service |
20376f |
padding = stats->max_name - strlen(old_path) - strlen(new_path);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (git_buf_printf(out, DIFF_RENAME_FILE_SEPARATOR "%s", new_path) < 0)
|
|
Packit Service |
20376f |
goto on_error;
|
|
Packit Service |
20376f |
} else {
|
|
Packit Service |
20376f |
padding = stats->max_name - strlen(old_path);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (stats->renames > 0)
|
|
Packit Service |
20376f |
padding += strlen(DIFF_RENAME_FILE_SEPARATOR);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (git_buf_putcn(out, ' ', padding) < 0 ||
|
|
Packit Service |
20376f |
git_buf_puts(out, " | ") < 0)
|
|
Packit Service |
20376f |
goto on_error;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (delta->flags & GIT_DIFF_FLAG_BINARY) {
|
|
Packit Service |
20376f |
if (git_buf_printf(out,
|
|
Packit Service |
20376f |
"Bin %" PRIuZ " -> %" PRIuZ " bytes", old_size, new_size) < 0)
|
|
Packit Service |
20376f |
goto on_error;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
else {
|
|
Packit Service |
20376f |
if (git_buf_printf(out,
|
|
Packit Service |
20376f |
"%*" PRIuZ, stats->max_digits,
|
|
Packit Service |
20376f |
filestat->insertions + filestat->deletions) < 0)
|
|
Packit Service |
20376f |
goto on_error;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (filestat->insertions || filestat->deletions) {
|
|
Packit Service |
20376f |
if (git_buf_putc(out, ' ') < 0)
|
|
Packit Service |
20376f |
goto on_error;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (!width) {
|
|
Packit Service |
20376f |
if (git_buf_putcn(out, '+', filestat->insertions) < 0 ||
|
|
Packit Service |
20376f |
git_buf_putcn(out, '-', filestat->deletions) < 0)
|
|
Packit Service |
20376f |
goto on_error;
|
|
Packit Service |
20376f |
} else {
|
|
Packit Service |
20376f |
size_t total = filestat->insertions + filestat->deletions;
|
|
Packit Service |
20376f |
size_t full = (total * width + stats->max_filestat / 2) /
|
|
Packit Service |
20376f |
stats->max_filestat;
|
|
Packit Service |
20376f |
size_t plus = full * filestat->insertions / total;
|
|
Packit Service |
20376f |
size_t minus = full - plus;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (git_buf_putcn(out, '+', max(plus, 1)) < 0 ||
|
|
Packit Service |
20376f |
git_buf_putcn(out, '-', max(minus, 1)) < 0)
|
|
Packit Service |
20376f |
goto on_error;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_buf_putc(out, '\n');
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
on_error:
|
|
Packit Service |
20376f |
return (git_buf_oom(out) ? -1 : 0);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
int git_diff_file_stats__number_to_buf(
|
|
Packit Service |
20376f |
git_buf *out,
|
|
Packit Service |
20376f |
const git_diff_delta *delta,
|
|
Packit Service |
20376f |
const diff_file_stats *filestats)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
int error;
|
|
Packit Service |
20376f |
const char *path = delta->new_file.path;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (delta->flags & GIT_DIFF_FLAG_BINARY)
|
|
Packit Service |
20376f |
error = git_buf_printf(out, "%-8c" "%-8c" "%s\n", '-', '-', path);
|
|
Packit Service |
20376f |
else
|
|
Packit Service |
20376f |
error = git_buf_printf(out, "%-8" PRIuZ "%-8" PRIuZ "%s\n",
|
|
Packit Service |
20376f |
filestats->insertions, filestats->deletions, path);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return error;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
int git_diff_file_stats__summary_to_buf(
|
|
Packit Service |
20376f |
git_buf *out,
|
|
Packit Service |
20376f |
const git_diff_delta *delta)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
if (delta->old_file.mode != delta->new_file.mode) {
|
|
Packit Service |
20376f |
if (delta->old_file.mode == 0) {
|
|
Packit Service |
20376f |
git_buf_printf(out, " create mode %06o %s\n",
|
|
Packit Service |
20376f |
delta->new_file.mode, delta->new_file.path);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
else if (delta->new_file.mode == 0) {
|
|
Packit Service |
20376f |
git_buf_printf(out, " delete mode %06o %s\n",
|
|
Packit Service |
20376f |
delta->old_file.mode, delta->old_file.path);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
else {
|
|
Packit Service |
20376f |
git_buf_printf(out, " mode change %06o => %06o %s\n",
|
|
Packit Service |
20376f |
delta->old_file.mode, delta->new_file.mode, delta->new_file.path);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
int git_diff_get_stats(
|
|
Packit Service |
20376f |
git_diff_stats **out,
|
|
Packit Service |
20376f |
git_diff *diff)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
size_t i, deltas;
|
|
Packit Service |
20376f |
size_t total_insertions = 0, total_deletions = 0;
|
|
Packit Service |
20376f |
git_diff_stats *stats = NULL;
|
|
Packit Service |
20376f |
int error = 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
assert(out && diff);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
stats = git__calloc(1, sizeof(git_diff_stats));
|
|
Packit Service |
20376f |
GITERR_CHECK_ALLOC(stats);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
deltas = git_diff_num_deltas(diff);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
stats->filestats = git__calloc(deltas, sizeof(diff_file_stats));
|
|
Packit Service |
20376f |
if (!stats->filestats) {
|
|
Packit Service |
20376f |
git__free(stats);
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
stats->diff = diff;
|
|
Packit Service |
20376f |
GIT_REFCOUNT_INC(diff);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
for (i = 0; i < deltas && !error; ++i) {
|
|
Packit Service |
20376f |
git_patch *patch = NULL;
|
|
Packit Service |
20376f |
size_t add = 0, remove = 0, namelen;
|
|
Packit Service |
20376f |
const git_diff_delta *delta;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if ((error = git_patch_from_diff(&patch, diff, i)) < 0)
|
|
Packit Service |
20376f |
break;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* keep a count of renames because it will affect formatting */
|
|
Packit Service |
20376f |
delta = patch->delta;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* TODO ugh */
|
|
Packit Service |
20376f |
namelen = strlen(delta->new_file.path);
|
|
Packit Service |
20376f |
if (strcmp(delta->old_file.path, delta->new_file.path) != 0) {
|
|
Packit Service |
20376f |
namelen += strlen(delta->old_file.path);
|
|
Packit Service |
20376f |
stats->renames++;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* and, of course, count the line stats */
|
|
Packit Service |
20376f |
error = git_patch_line_stats(NULL, &add, &remove, patch);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_patch_free(patch);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
stats->filestats[i].insertions = add;
|
|
Packit Service |
20376f |
stats->filestats[i].deletions = remove;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
total_insertions += add;
|
|
Packit Service |
20376f |
total_deletions += remove;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (stats->max_name < namelen)
|
|
Packit Service |
20376f |
stats->max_name = namelen;
|
|
Packit Service |
20376f |
if (stats->max_filestat < add + remove)
|
|
Packit Service |
20376f |
stats->max_filestat = add + remove;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
stats->files_changed = deltas;
|
|
Packit Service |
20376f |
stats->insertions = total_insertions;
|
|
Packit Service |
20376f |
stats->deletions = total_deletions;
|
|
Packit Service |
20376f |
stats->max_digits = digits_for_value(stats->max_filestat + 1);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (error < 0) {
|
|
Packit Service |
20376f |
git_diff_stats_free(stats);
|
|
Packit Service |
20376f |
stats = NULL;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
*out = stats;
|
|
Packit Service |
20376f |
return error;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
size_t git_diff_stats_files_changed(
|
|
Packit Service |
20376f |
const git_diff_stats *stats)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
assert(stats);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return stats->files_changed;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
size_t git_diff_stats_insertions(
|
|
Packit Service |
20376f |
const git_diff_stats *stats)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
assert(stats);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return stats->insertions;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
size_t git_diff_stats_deletions(
|
|
Packit Service |
20376f |
const git_diff_stats *stats)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
assert(stats);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return stats->deletions;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
int git_diff_stats_to_buf(
|
|
Packit Service |
20376f |
git_buf *out,
|
|
Packit Service |
20376f |
const git_diff_stats *stats,
|
|
Packit Service |
20376f |
git_diff_stats_format_t format,
|
|
Packit Service |
20376f |
size_t width)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
int error = 0;
|
|
Packit Service |
20376f |
size_t i;
|
|
Packit Service |
20376f |
const git_diff_delta *delta;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
assert(out && stats);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (format & GIT_DIFF_STATS_NUMBER) {
|
|
Packit Service |
20376f |
for (i = 0; i < stats->files_changed; ++i) {
|
|
Packit Service |
20376f |
if ((delta = git_diff_get_delta(stats->diff, i)) == NULL)
|
|
Packit Service |
20376f |
continue;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
error = git_diff_file_stats__number_to_buf(
|
|
Packit Service |
20376f |
out, delta, &stats->filestats[i]);
|
|
Packit Service |
20376f |
if (error < 0)
|
|
Packit Service |
20376f |
return error;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (format & GIT_DIFF_STATS_FULL) {
|
|
Packit Service |
20376f |
if (width > 0) {
|
|
Packit Service |
20376f |
if (width > stats->max_name + stats->max_digits + 5)
|
|
Packit Service |
20376f |
width -= (stats->max_name + stats->max_digits + 5);
|
|
Packit Service |
20376f |
if (width < STATS_FULL_MIN_SCALE)
|
|
Packit Service |
20376f |
width = STATS_FULL_MIN_SCALE;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
if (width > stats->max_filestat)
|
|
Packit Service |
20376f |
width = 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
for (i = 0; i < stats->files_changed; ++i) {
|
|
Packit Service |
20376f |
if ((delta = git_diff_get_delta(stats->diff, i)) == NULL)
|
|
Packit Service |
20376f |
continue;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
error = git_diff_file_stats__full_to_buf(
|
|
Packit Service |
20376f |
out, delta, &stats->filestats[i], stats, width);
|
|
Packit Service |
20376f |
if (error < 0)
|
|
Packit Service |
20376f |
return error;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (format & GIT_DIFF_STATS_FULL || format & GIT_DIFF_STATS_SHORT) {
|
|
Packit Service |
20376f |
git_buf_printf(
|
|
Packit Service |
20376f |
out, " %" PRIuZ " file%s changed",
|
|
Packit Service |
20376f |
stats->files_changed, stats->files_changed != 1 ? "s" : "");
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (stats->insertions || stats->deletions == 0)
|
|
Packit Service |
20376f |
git_buf_printf(
|
|
Packit Service |
20376f |
out, ", %" PRIuZ " insertion%s(+)",
|
|
Packit Service |
20376f |
stats->insertions, stats->insertions != 1 ? "s" : "");
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (stats->deletions || stats->insertions == 0)
|
|
Packit Service |
20376f |
git_buf_printf(
|
|
Packit Service |
20376f |
out, ", %" PRIuZ " deletion%s(-)",
|
|
Packit Service |
20376f |
stats->deletions, stats->deletions != 1 ? "s" : "");
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_buf_putc(out, '\n');
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (git_buf_oom(out))
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (format & GIT_DIFF_STATS_INCLUDE_SUMMARY) {
|
|
Packit Service |
20376f |
for (i = 0; i < stats->files_changed; ++i) {
|
|
Packit Service |
20376f |
if ((delta = git_diff_get_delta(stats->diff, i)) == NULL)
|
|
Packit Service |
20376f |
continue;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
error = git_diff_file_stats__summary_to_buf(out, delta);
|
|
Packit Service |
20376f |
if (error < 0)
|
|
Packit Service |
20376f |
return error;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return error;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
void git_diff_stats_free(git_diff_stats *stats)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
if (stats == NULL)
|
|
Packit Service |
20376f |
return;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_diff_free(stats->diff); /* bumped refcount in constructor */
|
|
Packit Service |
20376f |
git__free(stats->filestats);
|
|
Packit Service |
20376f |
git__free(stats);
|
|
Packit Service |
20376f |
}
|