From 12e422a0562de2aebb05f5f414dbcde7caf85886 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 21 Apr 2014 16:08:05 -0700 Subject: Some doc and examples/diff.c changes I was playing with "git diff-index" and wanted to be able to emulate that behavior a little more closely with the diff example. Also, I wanted to play with running `git_diff_tree_to_workdir` directly even though core Git doesn't exactly have the equivalent, so I added a command line option for that and tweaked some other things in the example code. This changes a minor output thing in that the "raw" print helper function will no longer add ellipses (...) if the OID is not actually abbreviated. --- examples/diff.c | 151 ++++++++++++++++++++++++++++++---------------------- include/git2/diff.h | 29 ++++------ src/diff_print.c | 3 +- 3 files changed, 99 insertions(+), 84 deletions(-) diff --git a/examples/diff.c b/examples/diff.c index 6f68e8305..1dbf85f61 100644 --- a/examples/diff.c +++ b/examples/diff.c @@ -33,14 +33,26 @@ static const char *colors[] = { "\033[36m" /* cyan */ }; +enum { + OUTPUT_DIFF = 0, + OUTPUT_STAT = 1, + OUTPUT_SHORTSTAT = 2, + OUTPUT_NUMSTAT = 3 +}; + +enum { + CACHE_NORMAL = 0, + CACHE_ONLY = 1, + CACHE_NONE = 2 +}; + /** The 'opts' struct captures all the various parsed command line options. */ struct opts { git_diff_options diffopts; git_diff_find_options findopts; int color; - int cached; - int numstat; - int shortstat; + int cache; + int output; git_diff_format_t format; const char *treeish1; const char *treeish2; @@ -48,11 +60,11 @@ struct opts { }; /** These functions are implemented at the end */ +static void usage(const char *message, const char *arg); static void parse_opts(struct opts *o, int argc, char *argv[]); static int color_printer( const git_diff_delta*, const git_diff_hunk*, const git_diff_line*, void*); -static void diff_print_numstat(git_diff *diff); -static void diff_print_shortstat(git_diff *diff); +static void diff_print_stats(git_diff *diff, struct opts *o); int main(int argc, char *argv[]) { @@ -61,7 +73,7 @@ int main(int argc, char *argv[]) git_diff *diff; struct opts o = { GIT_DIFF_OPTIONS_INIT, GIT_DIFF_FIND_OPTIONS_INIT, - -1, 0, 0, 0, GIT_DIFF_FORMAT_PATCH, NULL, NULL, "." + -1, 0, 0, GIT_DIFF_FORMAT_PATCH, NULL, NULL, "." }; git_threads_init(); @@ -78,6 +90,7 @@ int main(int argc, char *argv[]) * * <sha1> --cached * * <sha1> * * --cached + * * --nocache (don't use index data in diff at all) * * nothing * * Currently ranged arguments like <sha1>..<sha2> and <sha1>...<sha2> @@ -93,20 +106,23 @@ int main(int argc, char *argv[]) check_lg2( git_diff_tree_to_tree(&diff, repo, t1, t2, &o.diffopts), "diff trees", NULL); - else if (t1 && o.cached) - check_lg2( - git_diff_tree_to_index(&diff, repo, t1, NULL, &o.diffopts), - "diff tree to index", NULL); + else if (o.cache != CACHE_NORMAL) { + if (!t1) + treeish_to_tree(&t1, repo, "HEAD"); + + if (o.cache == CACHE_NONE) + check_lg2( + git_diff_tree_to_workdir(&diff, repo, t1, &o.diffopts), + "diff tree to working directory", NULL); + else + check_lg2( + git_diff_tree_to_index(&diff, repo, t1, NULL, &o.diffopts), + "diff tree to index", NULL); + } else if (t1) check_lg2( git_diff_tree_to_workdir_with_index(&diff, repo, t1, &o.diffopts), "diff tree to working directory", NULL); - else if (o.cached) { - treeish_to_tree(&t1, repo, "HEAD"); - check_lg2( - git_diff_tree_to_index(&diff, repo, t1, NULL, &o.diffopts), - "diff tree to index", NULL); - } else check_lg2( git_diff_index_to_workdir(&diff, repo, NULL, &o.diffopts), @@ -121,11 +137,14 @@ int main(int argc, char *argv[]) /** Generate simple output using libgit2 display helper. */ - if (o.numstat == 1) - diff_print_numstat(diff); - else if (o.shortstat == 1) - diff_print_shortstat(diff); - else { + switch (o.output) { + case OUTPUT_STAT: + case OUTPUT_NUMSTAT: + case OUTPUT_SHORTSTAT: + diff_print_stats(diff, &o); + break; + + case OUTPUT_DIFF: if (o.color >= 0) fputs(colors[0], stdout); @@ -135,6 +154,10 @@ int main(int argc, char *argv[]) if (o.color >= 0) fputs(colors[0], stdout); + break; + + default: + usage("Unknown output format", "programmer error"); } /** Cleanup before exiting. */ @@ -213,13 +236,20 @@ static void parse_opts(struct opts *o, int argc, char *argv[]) !strcmp(a, "--patch")) o->format = GIT_DIFF_FORMAT_PATCH; else if (!strcmp(a, "--cached")) - o->cached = 1; - else if (!strcmp(a, "--name-only")) + o->cache = CACHE_ONLY; + else if (!strcmp(a, "--nocache")) + o->cache = CACHE_NONE; + else if (!strcmp(a, "--name-only") || !strcmp(a, "--format=name")) o->format = GIT_DIFF_FORMAT_NAME_ONLY; - else if (!strcmp(a, "--name-status")) + else if (!strcmp(a, "--name-status") || + !strcmp(a, "--format=name-status")) o->format = GIT_DIFF_FORMAT_NAME_STATUS; - else if (!strcmp(a, "--raw")) + else if (!strcmp(a, "--raw") || !strcmp(a, "--format=raw")) o->format = GIT_DIFF_FORMAT_RAW; + else if (!strcmp(a, "--format=diff-index")) { + o->format = GIT_DIFF_FORMAT_RAW; + o->diffopts.id_abbrev = 40; + } else if (!strcmp(a, "--color")) o->color = 0; else if (!strcmp(a, "--no-color")) @@ -242,10 +272,12 @@ static void parse_opts(struct opts *o, int argc, char *argv[]) o->diffopts.flags |= GIT_DIFF_PATIENCE; else if (!strcmp(a, "--minimal")) o->diffopts.flags |= GIT_DIFF_MINIMAL; + else if (!strcmp(a, "--stat")) + o->output = OUTPUT_STAT; else if (!strcmp(a, "--numstat")) - o->numstat = 1; + o->output = OUTPUT_NUMSTAT; else if (!strcmp(a, "--shortstat")) - o->shortstat = 1; + o->output = OUTPUT_SHORTSTAT; else if (match_uint16_arg( &o->findopts.rename_threshold, &args, "-M") || match_uint16_arg( @@ -267,6 +299,8 @@ static void parse_opts(struct opts *o, int argc, char *argv[]) &o->diffopts.context_lines, &args, "--unified") && !match_uint16_arg( &o->diffopts.interhunk_lines, &args, "--inter-hunk-context") && + !match_uint16_arg( + &o->diffopts.id_abbrev, &args, "--abbrev") && !match_str_arg(&o->diffopts.old_prefix, &args, "--src-prefix") && !match_str_arg(&o->diffopts.new_prefix, &args, "--dst-prefix") && !match_str_arg(&o->dir, &args, "--git-dir")) @@ -274,34 +308,8 @@ static void parse_opts(struct opts *o, int argc, char *argv[]) } } -/** Display diff output with "--numstat".*/ -static void diff_print_numstat(git_diff *diff) -{ - git_patch *patch; - const git_diff_delta *delta; - size_t d, ndeltas = git_diff_num_deltas(diff); - size_t nadditions, ndeletions; - - for (d = 0; d < ndeltas; d++){ - check_lg2( - git_patch_from_diff(&patch, diff, d), - "generating patch from diff", NULL); - - check_lg2( - git_patch_line_stats(NULL, &nadditions, &ndeletions, patch), - "generating the number of additions and deletions", NULL); - - delta = git_patch_get_delta(patch); - - printf("%ld\t%ld\t%s\n", - (long)nadditions, (long)ndeletions, delta->new_file.path); - - git_patch_free(patch); - } -} - -/** Display diff output with "--shortstat".*/ -static void diff_print_shortstat(git_diff *diff) +/** Display diff output with "--numstat" or "--shortstat" */ +static void diff_print_stats(git_diff *diff, struct opts *o) { git_patch *patch; size_t d, ndeltas = git_diff_num_deltas(diff); @@ -320,26 +328,39 @@ static void diff_print_shortstat(git_diff *diff) git_patch_line_stats(NULL, &nadditions, &ndeletions, patch), "generating the number of additions and deletions", NULL); + if (o->output == OUTPUT_NUMSTAT) { + const git_diff_delta *delta = git_patch_get_delta(patch); + printf("%ld\t%ld\t%s\n", + (long)nadditions, (long)ndeletions, delta->new_file.path); + } + else if (o->output == OUTPUT_STAT) { + const git_diff_delta *delta = git_patch_get_delta(patch); + printf(" %s\t| %ld\t(%ld+ %ld-)\n", + delta->new_file.path, (long)nadditions + (long)ndeletions, + (long)nadditions, (long)ndeletions); + } + nadditions_sum += nadditions; ndeletions_sum += ndeletions; git_patch_free(patch); } - if (ndeltas) { - - printf(" %ld ", (long)ndeltas); - printf("%s", 1==ndeltas ? "file changed" : "files changed"); + if (o->output != OUTPUT_NUMSTAT && ndeltas > 0) { + printf(" %ld %s", (long)ndeltas, + 1 == ndeltas ? "file changed" : "files changed"); - if(nadditions_sum) { - printf(", %ld ",nadditions_sum); - printf("%s", 1==nadditions_sum ? "insertion(+)" : "insertions(+)"); + if (nadditions_sum) { + printf(", %ld ",nadditions_sum); + printf("%s", 1 == nadditions_sum ? "insertion(+)" : "insertions(+)"); } - if(ndeletions_sum) { - printf(", %ld ",ndeletions_sum); - printf("%s", 1==ndeletions_sum ? "deletion(-)" : "deletions(-)"); + if (ndeletions_sum) { + printf(", %ld ",ndeletions_sum); + printf("%s", 1 == ndeletions_sum ? "deletion(-)" : "deletions(-)"); } + printf("\n"); } } + diff --git a/include/git2/diff.h b/include/git2/diff.h index a0cfbc918..c8e1ad143 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -725,24 +725,17 @@ GIT_EXTERN(int) git_diff_index_to_workdir( * The tree you provide will be used for the "old_file" side of the delta, * and the working directory will be used for the "new_file" side. * - * Please note: this is *NOT* the same as `git diff `. Running - * `git diff HEAD` or the like actually uses information from the index, - * along with the tree and working directory info. - * - * This function returns strictly the differences between the tree and the - * files contained in the working directory, regardless of the state of - * files in the index. It may come as a surprise, but there is no direct - * equivalent in core git. - * - * To emulate `git diff `, use `git_diff_tree_to_workdir_with_index` - * (or `git_diff_tree_to_index` and `git_diff_index_to_workdir`, then call - * `git_diff_merge` on the results). That will yield a `git_diff` that - * matches the git output. - * - * If this seems confusing, take the case of a file with a staged deletion - * where the file has then been put back into the working dir and modified. - * The tree-to-workdir diff for that file is 'modified', but core git would - * show status 'deleted' since there is a pending deletion in the index. + * This is not the same as `git diff ` or `git diff-index + * `. Those commands use information from the index, whereas this + * function strictly returns the differences between the tree and the files + * in the working directory, regardless of the state of the index. Use + * `git_diff_tree_to_workdir_with_index` to emulate those commands. + * + * To see difference between this and `git_diff_tree_to_workdir_with_index`, + * consider the example of a staged file deletion where the file has then + * been put back into the working dir and further modified. The + * tree-to-workdir diff for that file is 'modified', but `git diff` would + * show status 'deleted' since there is a staged delete. * * @param diff A pointer to a git_diff pointer that will be allocated. * @param repo The repository containing the tree. diff --git a/src/diff_print.c b/src/diff_print.c index a7f7b6fe8..ee5cd8dfb 100644 --- a/src/diff_print.c +++ b/src/diff_print.c @@ -175,7 +175,8 @@ static int diff_print_one_raw( git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.id); git_buf_printf( - out, ":%06o %06o %s... %s... %c", + out, (pi->oid_strlen <= GIT_OID_HEXSZ) ? + ":%06o %06o %s... %s... %c" : ":%06o %06o %s %s %c", delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code); if (delta->similarity > 0) -- cgit v1.2.1 From 8d09efa24ee01e9e4b14672978bfd1bb1ca2436a Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 22 Apr 2014 12:33:27 -0700 Subject: Use git_diff_get_stats in example/diff + refactor This takes the `--stat` and related example options in the example diff.c program and converts them to use the `git_diff_get_stats` API which nicely formats stats for you. I went to add bar-graph scaling to the stats formatter and noticed that the `git_diff_stats` structure was holding on to all of the `git_patch` objects. Unfortunately, each of these objects keeps the full text of the diff in memory, so this is very expensive. I ended up modifying `git_diff_stats` to keep just the data that it needs to keep and allowed it to release the patches. Then, I added width scaling to the output on top of that. In making the diff example program match 'git diff' output, I ended up removing an newline from the sumamry output which I then had to compensate for in the email formatting to match the expectations. Lastly, I went through and refactored the tests to use a couple of helper functions and reduce the overall amount of code there. --- examples/diff.c | 103 +++++-------- include/git2/diff.h | 4 +- src/diff.c | 3 +- src/diff_stats.c | 299 ++++++++++++++++++-------------------- tests/diff/format_email.c | 193 +++++++------------------ tests/diff/stats.c | 357 ++++++++++++++-------------------------------- 6 files changed, 340 insertions(+), 619 deletions(-) diff --git a/examples/diff.c b/examples/diff.c index 1dbf85f61..76ac2f311 100644 --- a/examples/diff.c +++ b/examples/diff.c @@ -34,10 +34,11 @@ static const char *colors[] = { }; enum { - OUTPUT_DIFF = 0, - OUTPUT_STAT = 1, - OUTPUT_SHORTSTAT = 2, - OUTPUT_NUMSTAT = 3 + OUTPUT_DIFF = (1 << 0), + OUTPUT_STAT = (1 << 1), + OUTPUT_SHORTSTAT = (1 << 2), + OUTPUT_NUMSTAT = (1 << 3), + OUTPUT_SUMMARY = (1 << 4) }; enum { @@ -137,14 +138,13 @@ int main(int argc, char *argv[]) /** Generate simple output using libgit2 display helper. */ - switch (o.output) { - case OUTPUT_STAT: - case OUTPUT_NUMSTAT: - case OUTPUT_SHORTSTAT: + if (!o.output) + o.output = OUTPUT_DIFF; + + if (o.output != OUTPUT_DIFF) diff_print_stats(diff, &o); - break; - case OUTPUT_DIFF: + if ((o.output & OUTPUT_DIFF) != 0) { if (o.color >= 0) fputs(colors[0], stdout); @@ -154,10 +154,6 @@ int main(int argc, char *argv[]) if (o.color >= 0) fputs(colors[0], stdout); - break; - - default: - usage("Unknown output format", "programmer error"); } /** Cleanup before exiting. */ @@ -233,8 +229,10 @@ static void parse_opts(struct opts *o, int argc, char *argv[]) usage("Only one or two tree identifiers can be provided", NULL); } else if (!strcmp(a, "-p") || !strcmp(a, "-u") || - !strcmp(a, "--patch")) + !strcmp(a, "--patch")) { + o->output |= OUTPUT_DIFF; o->format = GIT_DIFF_FORMAT_PATCH; + } else if (!strcmp(a, "--cached")) o->cache = CACHE_ONLY; else if (!strcmp(a, "--nocache")) @@ -273,11 +271,13 @@ static void parse_opts(struct opts *o, int argc, char *argv[]) else if (!strcmp(a, "--minimal")) o->diffopts.flags |= GIT_DIFF_MINIMAL; else if (!strcmp(a, "--stat")) - o->output = OUTPUT_STAT; + o->output |= OUTPUT_STAT; else if (!strcmp(a, "--numstat")) - o->output = OUTPUT_NUMSTAT; + o->output |= OUTPUT_NUMSTAT; else if (!strcmp(a, "--shortstat")) - o->output = OUTPUT_SHORTSTAT; + o->output |= OUTPUT_SHORTSTAT; + else if (!strcmp(a, "--summary")) + o->output |= OUTPUT_SUMMARY; else if (match_uint16_arg( &o->findopts.rename_threshold, &args, "-M") || match_uint16_arg( @@ -308,59 +308,30 @@ static void parse_opts(struct opts *o, int argc, char *argv[]) } } -/** Display diff output with "--numstat" or "--shortstat" */ +/** Display diff output with "--stat", "--numstat", or "--shortstat" */ static void diff_print_stats(git_diff *diff, struct opts *o) { - git_patch *patch; - size_t d, ndeltas = git_diff_num_deltas(diff); - size_t nadditions, ndeletions; - long nadditions_sum, ndeletions_sum; - - nadditions_sum = 0; - ndeletions_sum = 0; + git_diff_stats *stats; + git_buf b = GIT_BUF_INIT_CONST(NULL, 0); + git_diff_stats_format_t format = 0; - for (d = 0; d < ndeltas; d++){ - check_lg2( - git_patch_from_diff(&patch, diff, d), - "generating patch from diff", NULL); + check_lg2( + git_diff_get_stats(&stats, diff), "generating stats for diff", NULL); - check_lg2( - git_patch_line_stats(NULL, &nadditions, &ndeletions, patch), - "generating the number of additions and deletions", NULL); + if (o->output & OUTPUT_STAT) + format |= GIT_DIFF_STATS_FULL; + if (o->output & OUTPUT_SHORTSTAT) + format |= GIT_DIFF_STATS_SHORT; + if (o->output & OUTPUT_NUMSTAT) + format |= GIT_DIFF_STATS_NUMBER; + if (o->output & OUTPUT_SUMMARY) + format |= GIT_DIFF_STATS_INCLUDE_SUMMARY; - if (o->output == OUTPUT_NUMSTAT) { - const git_diff_delta *delta = git_patch_get_delta(patch); - printf("%ld\t%ld\t%s\n", - (long)nadditions, (long)ndeletions, delta->new_file.path); - } - else if (o->output == OUTPUT_STAT) { - const git_diff_delta *delta = git_patch_get_delta(patch); - printf(" %s\t| %ld\t(%ld+ %ld-)\n", - delta->new_file.path, (long)nadditions + (long)ndeletions, - (long)nadditions, (long)ndeletions); - } + check_lg2( + git_diff_stats_to_buf(&b, stats, format, 80), "formatting stats", NULL); - nadditions_sum += nadditions; - ndeletions_sum += ndeletions; + fputs(b.ptr, stdout); - git_patch_free(patch); - } - - if (o->output != OUTPUT_NUMSTAT && ndeltas > 0) { - printf(" %ld %s", (long)ndeltas, - 1 == ndeltas ? "file changed" : "files changed"); - - if (nadditions_sum) { - printf(", %ld ",nadditions_sum); - printf("%s", 1 == nadditions_sum ? "insertion(+)" : "insertions(+)"); - } - - if (ndeletions_sum) { - printf(", %ld ",ndeletions_sum); - printf("%s", 1 == ndeletions_sum ? "deletion(-)" : "deletions(-)"); - } - - printf("\n"); - } + git_buf_free(&b); + git_diff_stats_free(stats); } - diff --git a/include/git2/diff.h b/include/git2/diff.h index c8e1ad143..e5e641a2a 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -1136,12 +1136,14 @@ GIT_EXTERN(size_t) git_diff_stats_deletions( * @param out buffer to store the formatted diff statistics in. * @param stats A `git_diff_stats` generated by one of the above functions. * @param format Formatting option. + * @param width Target width for output (only affects GIT_DIFF_STATS_FULL) * @return 0 on success; non-zero on error */ GIT_EXTERN(int) git_diff_stats_to_buf( git_buf *out, const git_diff_stats *stats, - git_diff_stats_format_t format); + git_diff_stats_format_t format, + size_t width); /** * Deallocate a `git_diff_stats`. diff --git a/src/diff.c b/src/diff.c index 0d1aed4ad..fd881c6f6 100644 --- a/src/diff.c +++ b/src/diff.c @@ -1590,7 +1590,8 @@ int git_diff_format_email( if ((error = git_buf_puts(out, "---\n")) < 0 || (error = git_diff_get_stats(&stats, diff)) < 0 || - (error = git_diff_stats_to_buf(out, stats, format_flags)) < 0 || + (error = git_diff_stats_to_buf(out, stats, format_flags, 0)) < 0 || + (error = git_buf_putc(out, '\n')) < 0 || (error = git_diff_format_email__append_patches_tobuf(out, diff)) < 0) goto on_error; diff --git a/src/diff_stats.c b/src/diff_stats.c index bb436bf7b..8a71be894 100644 --- a/src/diff_stats.c +++ b/src/diff_stats.c @@ -8,151 +8,123 @@ #include "vector.h" #include "diff.h" #include "diff_patch.h" +#include #define DIFF_RENAME_FILE_SEPARATOR " => " +#define STATS_FULL_MIN_SCALE 7 + +typedef struct { + size_t insertions; + size_t deletions; +} diff_file_stats; struct git_diff_stats { - git_vector patches; + git_diff *diff; + diff_file_stats *filestats; size_t files_changed; size_t insertions; size_t deletions; -}; - -static size_t diff_get_filename_padding( - int has_renames, - const git_diff_stats *stats) -{ - const git_patch *patch = NULL; - size_t i, max_padding = 0; - - if (has_renames) { - git_vector_foreach(&stats->patches, i, patch) { - const git_diff_delta *delta = NULL; - size_t len; - - delta = git_patch_get_delta(patch); - if (strcmp(delta->old_file.path, delta->new_file.path) == 0) - continue; - - if ((len = strlen(delta->old_file.path) + strlen(delta->new_file.path)) > max_padding) - max_padding = len; - } - } - - git_vector_foreach(&stats->patches, i, patch) { - const git_diff_delta *delta = NULL; - size_t len; - - delta = git_patch_get_delta(patch); - len = strlen(delta->new_file.path); - - if (strcmp(delta->old_file.path, delta->new_file.path) != 0) - continue; - - if (len > max_padding) - max_padding = len; - } + size_t renames; - return max_padding; -} + size_t max_name; + size_t max_filestat; + int max_digits; +}; int git_diff_file_stats__full_to_buf( git_buf *out, - size_t max_padding, - int has_renames, - const git_patch *patch) + const git_diff_delta *delta, + const diff_file_stats *filestat, + const git_diff_stats *stats, + double scale_to) { const char *old_path = NULL, *new_path = NULL; - const git_diff_delta *delta = NULL; size_t padding, old_size, new_size; - int error; - - delta = git_patch_get_delta(patch); old_path = delta->old_file.path; new_path = delta->new_file.path; old_size = delta->old_file.size; new_size = delta->new_file.size; - if ((error = git_buf_printf(out, " %s", old_path)) < 0) + if (git_buf_printf(out, " %s", old_path) < 0) goto on_error; if (strcmp(old_path, new_path) != 0) { - padding = max_padding - strlen(old_path) - strlen(new_path); + padding = stats->max_name - strlen(old_path) - strlen(new_path); - if ((error = git_buf_printf(out, DIFF_RENAME_FILE_SEPARATOR "%s", new_path)) < 0) + if (git_buf_printf(out, DIFF_RENAME_FILE_SEPARATOR "%s", new_path) < 0) goto on_error; - } - else { - padding = max_padding - strlen(old_path); + } else { + padding = stats->max_name - strlen(old_path); - if (has_renames) + if (stats->renames > 0) padding += strlen(DIFF_RENAME_FILE_SEPARATOR); } - if ((error = git_buf_putcn(out, ' ', padding)) < 0 || - (error = git_buf_puts(out, " | ")) < 0) - goto on_error; + if (git_buf_putcn(out, ' ', padding) < 0 || + git_buf_puts(out, " | ") < 0) + goto on_error; if (delta->flags & GIT_DIFF_FLAG_BINARY) { - if ((error = git_buf_printf(out, "Bin %" PRIuZ " -> %" PRIuZ " bytes", old_size, new_size)) < 0) + if (git_buf_printf(out, + "Bin %" PRIuZ " -> %" PRIuZ " bytes", old_size, new_size) < 0) goto on_error; } else { - size_t insertions, deletions; - - if ((error = git_patch_line_stats(NULL, &insertions, &deletions, patch)) < 0) + if (git_buf_printf(out, + "%*" PRIuZ, stats->max_digits, + filestat->insertions + filestat->deletions) < 0) goto on_error; - if ((error = git_buf_printf(out, "%" PRIuZ, insertions + deletions)) < 0) - goto on_error; + if (filestat->insertions || filestat->deletions) { + if (git_buf_putc(out, ' ') < 0) + goto on_error; - if (insertions || deletions) { - if ((error = git_buf_putc(out, ' ')) < 0 || - (error = git_buf_putcn(out, '+', insertions)) < 0 || - (error = git_buf_putcn(out, '-', deletions)) < 0) + if (scale_to <= 0) { + if (git_buf_putcn(out, '+', filestat->insertions) < 0 || + git_buf_putcn(out, '-', filestat->deletions) < 0) goto on_error; + } else { + size_t total = filestat->insertions + filestat->deletions; + double full = round(total * scale_to / stats->max_filestat); + size_t plus = full * filestat->insertions / total; + size_t minus = (size_t)full - plus; + + if (git_buf_putcn(out, '+', max(plus, 1)) < 0 || + git_buf_putcn(out, '-', max(minus, 1)) < 0) + goto on_error; + } } } - error = git_buf_putc(out, '\n'); + git_buf_putc(out, '\n'); on_error: - return error; + return (git_buf_oom(out) ? -1 : 0); } int git_diff_file_stats__number_to_buf( git_buf *out, - const git_patch *patch) + const git_diff_delta *delta, + const diff_file_stats *filestats) { - const git_diff_delta *delta = NULL; - const char *path = NULL; - size_t insertions, deletions; int error; - - delta = git_patch_get_delta(patch); - path = delta->new_file.path; - - if ((error = git_patch_line_stats(NULL, &insertions, &deletions, patch)) < 0) - return error; + const char *path = delta->new_file.path; if (delta->flags & GIT_DIFF_FLAG_BINARY) error = git_buf_printf(out, "%-8c" "%-8c" "%s\n", '-', '-', path); else - error = git_buf_printf(out, "%-8" PRIuZ "%-8" PRIuZ "%s\n", insertions, deletions, path); + error = git_buf_printf(out, "%-8" PRIuZ "%-8" PRIuZ "%s\n", + filestats->insertions, filestats->deletions, path); return error; } int git_diff_file_stats__summary_to_buf( git_buf *out, - const git_patch *patch) + const git_diff_delta *delta) { - const git_diff_delta *delta = NULL; - - delta = git_patch_get_delta(patch); - if (delta->old_file.mode != delta->new_file.mode) { if (delta->old_file.mode == 0) { git_buf_printf(out, " create mode %06o %s\n", @@ -171,39 +143,6 @@ int git_diff_file_stats__summary_to_buf( return 0; } -int git_diff_stats__has_renames( - const git_diff_stats *stats) -{ - git_patch *patch = NULL; - size_t i; - - git_vector_foreach(&stats->patches, i, patch) { - const git_diff_delta *delta = git_patch_get_delta(patch); - - if (strcmp(delta->old_file.path, delta->new_file.path) != 0) { - return 1; - } - } - - return 0; -} - -int git_diff_stats__add_file_stats( - git_diff_stats *stats, - git_patch *patch) -{ - const git_diff_delta *delta = NULL; - int error = 0; - - if ((delta = git_patch_get_delta(patch)) == NULL) - return -1; - - if ((error = git_vector_insert(&stats->patches, patch)) < 0) - return error; - - return error; -} - int git_diff_get_stats( git_diff_stats **out, git_diff *diff) @@ -220,35 +159,60 @@ int git_diff_get_stats( deltas = git_diff_num_deltas(diff); - for (i = 0; i < deltas; ++i) { + stats->filestats = git__calloc(deltas, sizeof(diff_file_stats)); + if (!stats->filestats) { + git__free(stats); + return -1; + } + + stats->diff = diff; + GIT_REFCOUNT_INC(diff); + + for (i = 0; i < deltas && !error; ++i) { git_patch *patch = NULL; - size_t add, remove; + size_t add = 0, remove = 0, namelen; + const git_diff_delta *delta; if ((error = git_patch_from_diff(&patch, diff, i)) < 0) - goto on_error; + break; - if ((error = git_patch_line_stats(NULL, &add, &remove, patch)) < 0 || - (error = git_diff_stats__add_file_stats(stats, patch)) < 0) { - git_patch_free(patch); - goto on_error; + /* keep a count of renames because it will affect formatting */ + delta = git_patch_get_delta(patch); + + namelen = strlen(delta->new_file.path); + if (strcmp(delta->old_file.path, delta->new_file.path) != 0) { + namelen += strlen(delta->old_file.path); + stats->renames++; } + /* and, of course, count the line stats */ + error = git_patch_line_stats(NULL, &add, &remove, patch); + + git_patch_free(patch); + + stats->filestats[i].insertions = add; + stats->filestats[i].deletions = remove; + total_insertions += add; total_deletions += remove; + + if (stats->max_name < namelen) + stats->max_name = namelen; + if (stats->max_filestat < add + remove) + stats->max_filestat = add + remove; } stats->files_changed = deltas; stats->insertions = total_insertions; stats->deletions = total_deletions; + stats->max_digits = (int)ceil(log10(stats->max_filestat + 1)); - *out = stats; - - goto done; - -on_error: - git_diff_stats_free(stats); + if (error < 0) { + git_diff_stats_free(stats); + stats = NULL; + } -done: + *out = stats; return error; } @@ -279,48 +243,68 @@ size_t git_diff_stats_deletions( int git_diff_stats_to_buf( git_buf *out, const git_diff_stats *stats, - git_diff_stats_format_t format) + git_diff_stats_format_t format, + size_t width) { - git_patch *patch = NULL; + int error = 0; size_t i; - int has_renames = 0, error = 0; + const git_diff_delta *delta; assert(out && stats); - /* check if we have renames, it affects the padding */ - has_renames = git_diff_stats__has_renames(stats); - - git_vector_foreach(&stats->patches, i, patch) { - if (format & GIT_DIFF_STATS_FULL) { - size_t max_padding = diff_get_filename_padding(has_renames, stats); + if (format & GIT_DIFF_STATS_NUMBER) { + for (i = 0; i < stats->files_changed; ++i) { + if ((delta = git_diff_get_delta(stats->diff, i)) == NULL) + continue; - error = git_diff_file_stats__full_to_buf(out, max_padding, has_renames, patch); + error = git_diff_file_stats__number_to_buf( + out, delta, &stats->filestats[i]); + if (error < 0) + return error; } - else if (format & GIT_DIFF_STATS_NUMBER) { - error = git_diff_file_stats__number_to_buf(out, patch); + } + + if (format & GIT_DIFF_STATS_FULL) { + double scale_to = -1; + if (width > 0) { + if (width > stats->max_name + stats->max_digits + 5) + scale_to = width - (stats->max_name + stats->max_digits + 5); + if (scale_to < STATS_FULL_MIN_SCALE) + scale_to = STATS_FULL_MIN_SCALE; } - if (error < 0) - return error; + for (i = 0; i < stats->files_changed; ++i) { + if ((delta = git_diff_get_delta(stats->diff, i)) == NULL) + continue; + + error = git_diff_file_stats__full_to_buf( + out, delta, &stats->filestats[i], stats, scale_to); + if (error < 0) + return error; + } } if (format & GIT_DIFF_STATS_FULL || format & GIT_DIFF_STATS_SHORT) { - error = git_buf_printf(out, " %" PRIuZ " file%s changed, %" PRIuZ " insertions(+), %" PRIuZ " deletions(-)\n", - stats->files_changed, stats->files_changed > 1 ? "s" : "", - stats->insertions, stats->deletions); + error = git_buf_printf( + out, " %" PRIuZ " file%s changed, %" PRIuZ + " insertion%s(+), %" PRIuZ " deletion%s(-)\n", + stats->files_changed, stats->files_changed != 1 ? "s" : "", + stats->insertions, stats->insertions != 1 ? "s" : "", + stats->deletions, stats->deletions != 1 ? "s" : ""); if (error < 0) return error; } if (format & GIT_DIFF_STATS_INCLUDE_SUMMARY) { - git_vector_foreach(&stats->patches, i, patch) { - if ((error = git_diff_file_stats__summary_to_buf(out, patch)) < 0) + for (i = 0; i < stats->files_changed; ++i) { + if ((delta = git_diff_get_delta(stats->diff, i)) == NULL) + continue; + + error = git_diff_file_stats__summary_to_buf(out, delta); + if (error < 0) return error; } - - if (git_vector_length(&stats->patches) > 0) - git_buf_putc(out, '\n'); } return error; @@ -328,16 +312,11 @@ int git_diff_stats_to_buf( void git_diff_stats_free(git_diff_stats *stats) { - size_t i; - git_patch *patch; - if (stats == NULL) return; - git_vector_foreach(&stats->patches, i, patch) - git_patch_free(patch); - - git_vector_free(&stats->patches); + git_diff_free(stats->diff); /* bumped refcount in constructor */ + git__free(stats->filestats); git__free(stats); } diff --git a/tests/diff/format_email.c b/tests/diff/format_email.c index 3260fdea8..18ad99bd5 100644 --- a/tests/diff/format_email.c +++ b/tests/diff/format_email.c @@ -17,15 +17,44 @@ void test_diff_format_email__cleanup(void) cl_git_sandbox_cleanup(); } -void test_diff_format_email__simple(void) +static void assert_email_match( + const char *expected, + const char *oidstr, + git_diff_format_email_options *opts) { git_oid oid; git_commit *commit = NULL; git_diff *diff = NULL; - git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; git_buf buf = GIT_BUF_INIT; - const char *email = + git_oid_fromstr(&oid, oidstr); + + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + + opts->id = git_commit_id(commit); + opts->author = git_commit_author(commit); + if (!opts->summary) + opts->summary = git_commit_summary(commit); + + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + cl_git_pass(git_diff_format_email(&buf, diff, opts)); + + cl_assert_equal_s(expected, git_buf_cstr(&buf)); + git_buf_clear(&buf); + + cl_git_pass(git_diff_commit_as_email( + &buf, repo, commit, 1, 1, opts->flags, NULL)); + cl_assert_equal_s(expected, git_buf_cstr(&buf)); + + git_diff_free(diff); + git_commit_free(commit); + git_buf_free(&buf); +} + +void test_diff_format_email__simple(void) +{ + git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; + const char *email = "From 9264b96c6d104d0e07ae33d3007b6a48246c6f92 Mon Sep 17 00:00:00 2001\n" \ "From: Jacques Germishuys \n" \ "Date: Wed, 9 Apr 2014 20:57:01 +0200\n" \ @@ -64,25 +93,8 @@ void test_diff_format_email__simple(void) "libgit2 " LIBGIT2_VERSION "\n" \ "\n"; - git_oid_fromstr(&oid, "9264b96c6d104d0e07ae33d3007b6a48246c6f92"); - - cl_git_pass(git_commit_lookup(&commit, repo, &oid)); - - opts.id = git_commit_id(commit); - opts.author = git_commit_author(commit); - opts.summary = git_commit_summary(commit); - - cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); - cl_git_pass(git_diff_format_email(&buf, diff, &opts)); - cl_assert(strcmp(git_buf_cstr(&buf), email) == 0); - - git_buf_clear(&buf); - cl_git_pass(git_diff_commit_as_email(&buf, repo, commit, 1, 1, 0, NULL)); - cl_assert(strcmp(git_buf_cstr(&buf), email) == 0); - - git_diff_free(diff); - git_commit_free(commit); - git_buf_free(&buf); + assert_email_match( + email, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts); } void test_diff_format_email__multiple(void) @@ -90,10 +102,10 @@ void test_diff_format_email__multiple(void) git_oid oid; git_commit *commit = NULL; git_diff *diff = NULL; - git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; + git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; git_buf buf = GIT_BUF_INIT; - const char *email = + const char *email = "From 10808fe9c9be5a190c0ba68d1a002233fb363508 Mon Sep 17 00:00:00 2001\n" \ "From: Jacques Germishuys \n" \ "Date: Thu, 10 Apr 2014 19:37:05 +0200\n" \ @@ -167,6 +179,7 @@ void test_diff_format_email__multiple(void) "libgit2 " LIBGIT2_VERSION "\n" \ "\n"; + git_oid_fromstr(&oid, "10808fe9c9be5a190c0ba68d1a002233fb363508"); cl_git_pass(git_commit_lookup(&commit, repo, &oid)); @@ -196,7 +209,7 @@ void test_diff_format_email__multiple(void) cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); cl_git_pass(git_diff_format_email(&buf, diff, &opts)); - cl_assert(strcmp(git_buf_cstr(&buf), email) == 0); + cl_assert_equal_s(email, git_buf_cstr(&buf)); git_diff_free(diff); git_commit_free(commit); @@ -205,13 +218,8 @@ void test_diff_format_email__multiple(void) void test_diff_format_email__exclude_marker(void) { - git_oid oid; - git_commit *commit = NULL; - git_diff *diff = NULL; git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; - git_buf buf = GIT_BUF_INIT; - - const char *email = + const char *email = "From 9264b96c6d104d0e07ae33d3007b6a48246c6f92 Mon Sep 17 00:00:00 2001\n" \ "From: Jacques Germishuys \n" \ "Date: Wed, 9 Apr 2014 20:57:01 +0200\n" \ @@ -250,27 +258,10 @@ void test_diff_format_email__exclude_marker(void) "libgit2 " LIBGIT2_VERSION "\n" \ "\n"; - git_oid_fromstr(&oid, "9264b96c6d104d0e07ae33d3007b6a48246c6f92"); - cl_git_pass(git_commit_lookup(&commit, repo, &oid)); - - opts.id = git_commit_id(commit); - opts.author = git_commit_author(commit); - opts.summary = git_commit_summary(commit); - opts.flags |= GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER; - cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); - cl_git_pass(git_diff_format_email(&buf, diff, &opts)); - cl_assert(strcmp(git_buf_cstr(&buf), email) == 0); - - git_buf_clear(&buf); - cl_git_pass(git_diff_commit_as_email(&buf, repo, commit, 1, 1, - GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER, NULL)); - cl_assert(strcmp(git_buf_cstr(&buf), email) == 0); - - git_diff_free(diff); - git_commit_free(commit); - git_buf_free(&buf); + assert_email_match( + email, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts); } void test_diff_format_email__invalid_no(void) @@ -303,13 +294,8 @@ void test_diff_format_email__invalid_no(void) void test_diff_format_email__mode_change(void) { - git_oid oid; - git_commit *commit = NULL; - git_diff *diff = NULL; git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; - git_buf buf = GIT_BUF_INIT; - - const char *email = + const char *email = "From 7ade76dd34bba4733cf9878079f9fd4a456a9189 Mon Sep 17 00:00:00 2001\n" \ "From: Jacques Germishuys \n" \ "Date: Thu, 10 Apr 2014 10:05:03 +0200\n" \ @@ -330,36 +316,14 @@ void test_diff_format_email__mode_change(void) "libgit2 " LIBGIT2_VERSION "\n" \ "\n"; - git_oid_fromstr(&oid, "7ade76dd34bba4733cf9878079f9fd4a456a9189"); - - cl_git_pass(git_commit_lookup(&commit, repo, &oid)); - - opts.id = git_commit_id(commit); - opts.author = git_commit_author(commit); - opts.summary = git_commit_summary(commit); - - cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); - cl_git_pass(git_diff_format_email(&buf, diff, &opts)); - cl_assert(strcmp(git_buf_cstr(&buf), email) == 0); - - git_buf_clear(&buf); - cl_git_pass(git_diff_commit_as_email(&buf, repo, commit, 1, 1, 0, NULL)); - cl_assert(strcmp(git_buf_cstr(&buf), email) == 0); - - git_diff_free(diff); - git_commit_free(commit); - git_buf_free(&buf); + assert_email_match( + email, "7ade76dd34bba4733cf9878079f9fd4a456a9189", &opts); } void test_diff_format_email__rename_add_remove(void) { - git_oid oid; - git_commit *commit = NULL; - git_diff *diff = NULL; git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; - git_buf buf = GIT_BUF_INIT; - - const char *email = + const char *email = "From 6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d Mon Sep 17 00:00:00 2001\n" \ "From: Jacques Germishuys \n" \ "Date: Wed, 9 Apr 2014 21:15:56 +0200\n" \ @@ -422,35 +386,13 @@ void test_diff_format_email__rename_add_remove(void) "libgit2 " LIBGIT2_VERSION "\n" \ "\n"; - git_oid_fromstr(&oid, "6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d"); - - cl_git_pass(git_commit_lookup(&commit, repo, &oid)); - - opts.id = git_commit_id(commit); - opts.author = git_commit_author(commit); - opts.summary = git_commit_summary(commit); - - cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); - cl_git_pass(git_diff_format_email(&buf, diff, &opts)); - cl_assert(strcmp(git_buf_cstr(&buf), email) == 0); - - git_buf_clear(&buf); - cl_git_pass(git_diff_commit_as_email(&buf, repo, commit, 1, 1, 0, NULL)); - cl_assert(strcmp(git_buf_cstr(&buf), email) == 0); - - git_diff_free(diff); - git_commit_free(commit); - git_buf_free(&buf); + assert_email_match( + email, "6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d", &opts); } void test_diff_format_email__multiline_summary(void) { - git_oid oid; - git_commit *commit = NULL; - git_diff *diff = NULL; git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; - git_buf buf = GIT_BUF_INIT; - const char *email = "From 9264b96c6d104d0e07ae33d3007b6a48246c6f92 Mon Sep 17 00:00:00 2001\n" \ "From: Jacques Germishuys \n" \ @@ -490,36 +432,15 @@ void test_diff_format_email__multiline_summary(void) "libgit2 " LIBGIT2_VERSION "\n" \ "\n"; - git_oid_fromstr(&oid, "9264b96c6d104d0e07ae33d3007b6a48246c6f92"); - - cl_git_pass(git_commit_lookup(&commit, repo, &oid)); - - opts.id = git_commit_id(commit); - opts.author = git_commit_author(commit); opts.summary = "Modify some content\nSome extra stuff here"; - cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); - cl_git_pass(git_diff_format_email(&buf, diff, &opts)); - cl_assert(strcmp(git_buf_cstr(&buf), email) == 0); - - git_buf_clear(&buf); - cl_git_pass(git_diff_commit_as_email(&buf, repo, commit, 1, 1, 0, NULL)); - cl_assert(strcmp(git_buf_cstr(&buf), email) == 0); - - git_diff_free(diff); - git_commit_free(commit); - git_buf_free(&buf); + assert_email_match( + email, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts); } void test_diff_format_email__binary(void) { - git_oid oid; - git_commit *commit = NULL; - git_diff *diff = NULL; git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; - git_buf buf = GIT_BUF_INIT; - - /* TODO: Actually 0 bytes here should be 5!. Seems like we don't load the new content for binary files? */ const char *email = "From 8d7523f6fcb2404257889abe0d96f093d9f524f9 Mon Sep 17 00:00:00 2001\n" \ "From: Jacques Germishuys \n" \ @@ -536,21 +457,11 @@ void test_diff_format_email__binary(void) "--\n" \ "libgit2 " LIBGIT2_VERSION "\n" \ "\n"; + /* TODO: Actually 0 bytes here should be 5!. Seems like we don't load the new content for binary files? */ - git_oid_fromstr(&oid, "8d7523f6fcb2404257889abe0d96f093d9f524f9"); - - cl_git_pass(git_commit_lookup(&commit, repo, &oid)); - - opts.id = git_commit_id(commit); - opts.author = git_commit_author(commit); opts.summary = "Modified binary file"; - cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); - cl_git_pass(git_diff_format_email(&buf, diff, &opts)); - cl_assert(strcmp(git_buf_cstr(&buf), email) == 0); - - git_diff_free(diff); - git_commit_free(commit); - git_buf_free(&buf); + assert_email_match( + email, "8d7523f6fcb2404257889abe0d96f093d9f524f9", &opts); } diff --git a/tests/diff/stats.c b/tests/diff/stats.c index 131b7681d..055019f69 100644 --- a/tests/diff/stats.c +++ b/tests/diff/stats.c @@ -5,246 +5,173 @@ #include "commit.h" #include "diff.h" -static git_repository *repo; +static git_repository *_repo; +static git_diff_stats *_stats; void test_diff_stats__initialize(void) { - repo = cl_git_sandbox_init("diff_format_email"); + _repo = cl_git_sandbox_init("diff_format_email"); } void test_diff_stats__cleanup(void) { + git_diff_stats_free(_stats); _stats = NULL; cl_git_sandbox_cleanup(); } -void test_diff_stats__stat(void) +static void diff_stats_from_commit_oid( + git_diff_stats **stats, const char *oidstr, bool rename) { git_oid oid; - git_commit *commit = NULL; - git_diff *diff = NULL; - git_diff_stats *stats = NULL; - git_buf buf = GIT_BUF_INIT; + git_commit *commit; + git_diff *diff; + + git_oid_fromstr(&oid, oidstr); + cl_git_pass(git_commit_lookup(&commit, _repo, &oid)); + cl_git_pass(git_diff__commit(&diff, _repo, commit, NULL)); + if (rename) + cl_git_pass(git_diff_find_similar(diff, NULL)); + cl_git_pass(git_diff_get_stats(stats, diff)); + + git_diff_free(diff); + git_commit_free(commit); +} +void test_diff_stats__stat(void) +{ + git_buf buf = GIT_BUF_INIT; const char *stat = " file1.txt | 8 +++++---\n" \ " 1 file changed, 5 insertions(+), 3 deletions(-)\n"; - git_oid_fromstr(&oid, "9264b96c6d104d0e07ae33d3007b6a48246c6f92"); + diff_stats_from_commit_oid( + &_stats, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", false); - cl_git_pass(git_commit_lookup(&commit, repo, &oid)); - cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + cl_assert_equal_sz(1, git_diff_stats_files_changed(_stats)); + cl_assert_equal_sz(5, git_diff_stats_insertions(_stats)); + cl_assert_equal_sz(3, git_diff_stats_deletions(_stats)); - cl_git_pass(git_diff_get_stats(&stats, diff)); - cl_assert(git_diff_stats_files_changed(stats) == 1); - cl_assert(git_diff_stats_insertions(stats) == 5); - cl_assert(git_diff_stats_deletions(stats) == 3); - - cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL)); + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0)); cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); - - git_diff_stats_free(stats); - git_diff_free(diff); - git_commit_free(commit); git_buf_free(&buf); } void test_diff_stats__multiple_hunks(void) { - git_oid oid; - git_commit *commit = NULL; - git_diff *diff = NULL; - git_diff_stats *stats = NULL; git_buf buf = GIT_BUF_INIT; - const char *stat = " file2.txt | 5 +++--\n" \ " file3.txt | 6 ++++--\n" \ " 2 files changed, 7 insertions(+), 4 deletions(-)\n"; - git_oid_fromstr(&oid, "cd471f0d8770371e1bc78bcbb38db4c7e4106bd2"); - - cl_git_pass(git_commit_lookup(&commit, repo, &oid)); - cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + diff_stats_from_commit_oid( + &_stats, "cd471f0d8770371e1bc78bcbb38db4c7e4106bd2", false); - cl_git_pass(git_diff_get_stats(&stats, diff)); - cl_assert(git_diff_stats_files_changed(stats) == 2); - cl_assert(git_diff_stats_insertions(stats) == 7); - cl_assert(git_diff_stats_deletions(stats) == 4); + cl_assert_equal_sz(2, git_diff_stats_files_changed(_stats)); + cl_assert_equal_sz(7, git_diff_stats_insertions(_stats)); + cl_assert_equal_sz(4, git_diff_stats_deletions(_stats)); - cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL)); - cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); - - git_diff_stats_free(stats); - git_diff_free(diff); - git_commit_free(commit); + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0)); + cl_assert_equal_s(stat, git_buf_cstr(&buf)); git_buf_free(&buf); } void test_diff_stats__numstat(void) { - git_oid oid; - git_commit *commit = NULL; - git_diff *diff = NULL; - git_diff_stats *stats = NULL; git_buf buf = GIT_BUF_INIT; - const char *stat = "3 2 file2.txt\n" "4 2 file3.txt\n"; - git_oid_fromstr(&oid, "cd471f0d8770371e1bc78bcbb38db4c7e4106bd2"); - - cl_git_pass(git_commit_lookup(&commit, repo, &oid)); - cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); - - cl_git_pass(git_diff_get_stats(&stats, diff)); - - cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_NUMBER)); - cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); + diff_stats_from_commit_oid( + &_stats, "cd471f0d8770371e1bc78bcbb38db4c7e4106bd2", false); - git_diff_stats_free(stats); - git_diff_free(diff); - git_commit_free(commit); + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_NUMBER, 0)); + cl_assert_equal_s(stat, git_buf_cstr(&buf)); git_buf_free(&buf); } void test_diff_stats__shortstat(void) { - git_oid oid; - git_commit *commit = NULL; - git_diff *diff = NULL; - git_diff_stats *stats = NULL; git_buf buf = GIT_BUF_INIT; - const char *stat = " 1 file changed, 5 insertions(+), 3 deletions(-)\n"; - git_oid_fromstr(&oid, "9264b96c6d104d0e07ae33d3007b6a48246c6f92"); - - cl_git_pass(git_commit_lookup(&commit, repo, &oid)); - cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + diff_stats_from_commit_oid( + &_stats, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", false); - cl_git_pass(git_diff_get_stats(&stats, diff)); - cl_assert(git_diff_stats_files_changed(stats) == 1); - cl_assert(git_diff_stats_insertions(stats) == 5); - cl_assert(git_diff_stats_deletions(stats) == 3); + cl_assert_equal_sz(1, git_diff_stats_files_changed(_stats)); + cl_assert_equal_sz(5, git_diff_stats_insertions(_stats)); + cl_assert_equal_sz(3, git_diff_stats_deletions(_stats)); - cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_SHORT)); - cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); - - git_diff_stats_free(stats); - git_diff_free(diff); - git_commit_free(commit); + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_SHORT, 0)); + cl_assert_equal_s(stat, git_buf_cstr(&buf)); git_buf_free(&buf); } void test_diff_stats__rename(void) { - git_oid oid; - git_commit *commit = NULL; - git_diff *diff = NULL; - git_diff_stats *stats = NULL; git_buf buf = GIT_BUF_INIT; - const char *stat = " file2.txt => file2.txt.renamed | 1 +\n" " file3.txt => file3.txt.renamed | 4 +++-\n" - " 2 files changed, 4 insertions(+), 1 deletions(-)\n"; - - git_oid_fromstr(&oid, "8947a46e2097638ca6040ad4877246f4186ec3bd"); + " 2 files changed, 4 insertions(+), 1 deletion(-)\n"; - cl_git_pass(git_commit_lookup(&commit, repo, &oid)); - cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); - cl_git_pass(git_diff_find_similar(diff, NULL)); + diff_stats_from_commit_oid( + &_stats, "8947a46e2097638ca6040ad4877246f4186ec3bd", true); - cl_git_pass(git_diff_get_stats(&stats, diff)); - cl_assert(git_diff_stats_files_changed(stats) == 2); - cl_assert(git_diff_stats_insertions(stats) == 4); - cl_assert(git_diff_stats_deletions(stats) == 1); + cl_assert_equal_sz(2, git_diff_stats_files_changed(_stats)); + cl_assert_equal_sz(4, git_diff_stats_insertions(_stats)); + cl_assert_equal_sz(1, git_diff_stats_deletions(_stats)); - cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL)); - cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); - - git_diff_stats_free(stats); - git_diff_free(diff); - git_commit_free(commit); + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0)); + cl_assert_equal_s(stat, git_buf_cstr(&buf)); git_buf_free(&buf); } void test_diff_stats__rename_nochanges(void) { - git_oid oid; - git_commit *commit = NULL; - git_diff *diff = NULL; - git_diff_stats *stats = NULL; git_buf buf = GIT_BUF_INIT; - const char *stat = " file2.txt.renamed => file2.txt.renamed2 | 0\n" " file3.txt.renamed => file3.txt.renamed2 | 0\n" " 2 files changed, 0 insertions(+), 0 deletions(-)\n"; - git_oid_fromstr(&oid, "3991dce9e71a0641ca49a6a4eea6c9e7ff402ed4"); - - cl_git_pass(git_commit_lookup(&commit, repo, &oid)); - cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); - cl_git_pass(git_diff_find_similar(diff, NULL)); + diff_stats_from_commit_oid( + &_stats, "3991dce9e71a0641ca49a6a4eea6c9e7ff402ed4", true); - cl_git_pass(git_diff_get_stats(&stats, diff)); - cl_assert(git_diff_stats_files_changed(stats) == 2); - cl_assert(git_diff_stats_insertions(stats) == 0); - cl_assert(git_diff_stats_deletions(stats) == 0); + cl_assert_equal_sz(2, git_diff_stats_files_changed(_stats)); + cl_assert_equal_sz(0, git_diff_stats_insertions(_stats)); + cl_assert_equal_sz(0, git_diff_stats_deletions(_stats)); - cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL)); - cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); - - git_diff_stats_free(stats); - git_diff_free(diff); - git_commit_free(commit); + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0)); + cl_assert_equal_s(stat, git_buf_cstr(&buf)); git_buf_free(&buf); } void test_diff_stats__rename_and_modifiy(void) { - git_oid oid; - git_commit *commit = NULL; - git_diff *diff = NULL; - git_diff_stats *stats = NULL; git_buf buf = GIT_BUF_INIT; - const char *stat = " file2.txt.renamed2 | 2 +-\n" " file3.txt.renamed2 => file3.txt.renamed | 0\n" - " 2 files changed, 1 insertions(+), 1 deletions(-)\n"; + " 2 files changed, 1 insertion(+), 1 deletion(-)\n"; - git_oid_fromstr(&oid, "4ca10087e696d2ba78d07b146a118e9a7096ed4f"); + diff_stats_from_commit_oid( + &_stats, "4ca10087e696d2ba78d07b146a118e9a7096ed4f", true); - cl_git_pass(git_commit_lookup(&commit, repo, &oid)); - cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); - cl_git_pass(git_diff_find_similar(diff, NULL)); + cl_assert_equal_sz(2, git_diff_stats_files_changed(_stats)); + cl_assert_equal_sz(1, git_diff_stats_insertions(_stats)); + cl_assert_equal_sz(1, git_diff_stats_deletions(_stats)); - cl_git_pass(git_diff_get_stats(&stats, diff)); - cl_assert(git_diff_stats_files_changed(stats) == 2); - cl_assert(git_diff_stats_insertions(stats) == 1); - cl_assert(git_diff_stats_deletions(stats) == 1); - - cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL)); - cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); - - git_diff_stats_free(stats); - git_diff_free(diff); - git_commit_free(commit); + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0)); + cl_assert_equal_s(stat, git_buf_cstr(&buf)); git_buf_free(&buf); } void test_diff_stats__rename_no_find(void) { - git_oid oid; - git_commit *commit = NULL; - git_diff *diff = NULL; - git_diff_stats *stats = NULL; git_buf buf = GIT_BUF_INIT; - const char *stat = " file2.txt | 5 -----\n" " file2.txt.renamed | 6 ++++++\n" @@ -252,33 +179,21 @@ void test_diff_stats__rename_no_find(void) " file3.txt.renamed | 7 +++++++\n" " 4 files changed, 13 insertions(+), 10 deletions(-)\n"; - git_oid_fromstr(&oid, "8947a46e2097638ca6040ad4877246f4186ec3bd"); + diff_stats_from_commit_oid( + &_stats, "8947a46e2097638ca6040ad4877246f4186ec3bd", false); - cl_git_pass(git_commit_lookup(&commit, repo, &oid)); - cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + cl_assert_equal_sz(4, git_diff_stats_files_changed(_stats)); + cl_assert_equal_sz(13, git_diff_stats_insertions(_stats)); + cl_assert_equal_sz(10, git_diff_stats_deletions(_stats)); - cl_git_pass(git_diff_get_stats(&stats, diff)); - cl_assert(git_diff_stats_files_changed(stats) == 4); - cl_assert(git_diff_stats_insertions(stats) == 13); - cl_assert(git_diff_stats_deletions(stats) == 10); - - cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL)); - cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); - - git_diff_stats_free(stats); - git_diff_free(diff); - git_commit_free(commit); + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0)); + cl_assert_equal_s(stat, git_buf_cstr(&buf)); git_buf_free(&buf); } void test_diff_stats__rename_nochanges_no_find(void) { - git_oid oid; - git_commit *commit = NULL; - git_diff *diff = NULL; - git_diff_stats *stats = NULL; git_buf buf = GIT_BUF_INIT; - const char *stat = " file2.txt.renamed | 6 ------\n" " file2.txt.renamed2 | 6 ++++++\n" @@ -286,143 +201,85 @@ void test_diff_stats__rename_nochanges_no_find(void) " file3.txt.renamed2 | 7 +++++++\n" " 4 files changed, 13 insertions(+), 13 deletions(-)\n"; - git_oid_fromstr(&oid, "3991dce9e71a0641ca49a6a4eea6c9e7ff402ed4"); - - cl_git_pass(git_commit_lookup(&commit, repo, &oid)); - cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + diff_stats_from_commit_oid( + &_stats, "3991dce9e71a0641ca49a6a4eea6c9e7ff402ed4", false); - cl_git_pass(git_diff_get_stats(&stats, diff)); - cl_assert(git_diff_stats_files_changed(stats) == 4); - cl_assert(git_diff_stats_insertions(stats) == 13); - cl_assert(git_diff_stats_deletions(stats) == 13); - - cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL)); - cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); + cl_assert_equal_sz(4, git_diff_stats_files_changed(_stats)); + cl_assert_equal_sz(13, git_diff_stats_insertions(_stats)); + cl_assert_equal_sz(13, git_diff_stats_deletions(_stats)); - git_diff_stats_free(stats); - git_diff_free(diff); - git_commit_free(commit); + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0)); + cl_assert_equal_s(stat, git_buf_cstr(&buf)); git_buf_free(&buf); } void test_diff_stats__rename_and_modifiy_no_find(void) { - git_oid oid; - git_commit *commit = NULL; - git_diff *diff = NULL; - git_diff_stats *stats = NULL; git_buf buf = GIT_BUF_INIT; - const char *stat = " file2.txt.renamed2 | 2 +-\n" " file3.txt.renamed | 7 +++++++\n" " file3.txt.renamed2 | 7 -------\n" " 3 files changed, 8 insertions(+), 8 deletions(-)\n"; - git_oid_fromstr(&oid, "4ca10087e696d2ba78d07b146a118e9a7096ed4f"); - - cl_git_pass(git_commit_lookup(&commit, repo, &oid)); - cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); - - cl_git_pass(git_diff_get_stats(&stats, diff)); - cl_assert(git_diff_stats_files_changed(stats) == 3); - cl_assert(git_diff_stats_insertions(stats) == 8); - cl_assert(git_diff_stats_deletions(stats) == 8); + diff_stats_from_commit_oid( + &_stats, "4ca10087e696d2ba78d07b146a118e9a7096ed4f", false); - cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL)); - cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); + cl_assert_equal_sz(3, git_diff_stats_files_changed(_stats)); + cl_assert_equal_sz(8, git_diff_stats_insertions(_stats)); + cl_assert_equal_sz(8, git_diff_stats_deletions(_stats)); - git_diff_stats_free(stats); - git_diff_free(diff); - git_commit_free(commit); + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0)); + cl_assert_equal_s(stat, git_buf_cstr(&buf)); git_buf_free(&buf); } void test_diff_stats__binary(void) { - git_oid oid; - git_commit *commit = NULL; - git_diff *diff = NULL; - git_diff_stats *stats = NULL; git_buf buf = GIT_BUF_INIT; - - /* TODO: Actually 0 bytes here should be 5!. Seems like we don't load the new content for binary files? */ const char *stat = " binary.bin | Bin 3 -> 0 bytes\n" " 1 file changed, 0 insertions(+), 0 deletions(-)\n"; + /* TODO: Actually 0 bytes here should be 5!. Seems like we don't load the new content for binary files? */ - git_oid_fromstr(&oid, "8d7523f6fcb2404257889abe0d96f093d9f524f9"); - - cl_git_pass(git_commit_lookup(&commit, repo, &oid)); - cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); - - cl_git_pass(git_diff_get_stats(&stats, diff)); - cl_assert(git_diff_stats_files_changed(stats) == 1); - cl_assert(git_diff_stats_insertions(stats) == 0); - cl_assert(git_diff_stats_deletions(stats) == 0); + diff_stats_from_commit_oid( + &_stats, "8d7523f6fcb2404257889abe0d96f093d9f524f9", false); - cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL)); - cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); + cl_assert_equal_sz(1, git_diff_stats_files_changed(_stats)); + cl_assert_equal_sz(0, git_diff_stats_insertions(_stats)); + cl_assert_equal_sz(0, git_diff_stats_deletions(_stats)); - git_diff_stats_free(stats); - git_diff_free(diff); - git_commit_free(commit); + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0)); + cl_assert_equal_s(stat, git_buf_cstr(&buf)); git_buf_free(&buf); } void test_diff_stats__binary_numstat(void) { - git_oid oid; - git_commit *commit = NULL; - git_diff *diff = NULL; - git_diff_stats *stats = NULL; git_buf buf = GIT_BUF_INIT; - const char *stat = "- - binary.bin\n"; - git_oid_fromstr(&oid, "8d7523f6fcb2404257889abe0d96f093d9f524f9"); - - cl_git_pass(git_commit_lookup(&commit, repo, &oid)); - cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); - - cl_git_pass(git_diff_get_stats(&stats, diff)); + diff_stats_from_commit_oid( + &_stats, "8d7523f6fcb2404257889abe0d96f093d9f524f9", false); - cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_NUMBER)); - cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); - - git_diff_stats_free(stats); - git_diff_free(diff); - git_commit_free(commit); + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_NUMBER, 0)); + cl_assert_equal_s(stat, git_buf_cstr(&buf)); git_buf_free(&buf); } void test_diff_stats__mode_change(void) { - git_oid oid; - git_commit *commit = NULL; - git_diff *diff = NULL; - git_diff_stats *stats = NULL; git_buf buf = GIT_BUF_INIT; - const char *stat = " file1.txt.renamed | 0\n" \ " 1 file changed, 0 insertions(+), 0 deletions(-)\n" \ - " mode change 100644 => 100755 file1.txt.renamed\n" \ - "\n"; + " mode change 100644 => 100755 file1.txt.renamed\n"; - git_oid_fromstr(&oid, "7ade76dd34bba4733cf9878079f9fd4a456a9189"); + diff_stats_from_commit_oid( + &_stats, "7ade76dd34bba4733cf9878079f9fd4a456a9189", false); - cl_git_pass(git_commit_lookup(&commit, repo, &oid)); - cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); - - cl_git_pass(git_diff_get_stats(&stats, diff)); - - cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL | GIT_DIFF_STATS_INCLUDE_SUMMARY)); - cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); - - git_diff_stats_free(stats); - git_diff_free(diff); - git_commit_free(commit); + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL | GIT_DIFF_STATS_INCLUDE_SUMMARY, 0)); + cl_assert_equal_s(stat, git_buf_cstr(&buf)); git_buf_free(&buf); } -- cgit v1.2.1 From e60883c82f1c4d44be856e545b4cbeea27522416 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 22 Apr 2014 12:59:31 -0700 Subject: Replace math fns with simpler integer math --- src/diff_stats.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/src/diff_stats.c b/src/diff_stats.c index 8a71be894..6ad670c42 100644 --- a/src/diff_stats.c +++ b/src/diff_stats.c @@ -8,7 +8,6 @@ #include "vector.h" #include "diff.h" #include "diff_patch.h" -#include #define DIFF_RENAME_FILE_SEPARATOR " => " #define STATS_FULL_MIN_SCALE 7 @@ -32,12 +31,25 @@ struct git_diff_stats { int max_digits; }; +static int digits_for_value(size_t val) +{ + int count = 1; + size_t placevalue = 10; + + while (val >= placevalue) { + ++count; + placevalue *= 10; + } + + return count; +} + int git_diff_file_stats__full_to_buf( git_buf *out, const git_diff_delta *delta, const diff_file_stats *filestat, const git_diff_stats *stats, - double scale_to) + size_t width) { const char *old_path = NULL, *new_path = NULL; size_t padding, old_size, new_size; @@ -81,15 +93,16 @@ int git_diff_file_stats__full_to_buf( if (git_buf_putc(out, ' ') < 0) goto on_error; - if (scale_to <= 0) { + if (!width) { if (git_buf_putcn(out, '+', filestat->insertions) < 0 || git_buf_putcn(out, '-', filestat->deletions) < 0) goto on_error; } else { size_t total = filestat->insertions + filestat->deletions; - double full = round(total * scale_to / stats->max_filestat); + size_t full = (total * width + stats->max_filestat / 2) / + stats->max_filestat; size_t plus = full * filestat->insertions / total; - size_t minus = (size_t)full - plus; + size_t minus = full - plus; if (git_buf_putcn(out, '+', max(plus, 1)) < 0 || git_buf_putcn(out, '-', max(minus, 1)) < 0) @@ -205,7 +218,7 @@ int git_diff_get_stats( stats->files_changed = deltas; stats->insertions = total_insertions; stats->deletions = total_deletions; - stats->max_digits = (int)ceil(log10(stats->max_filestat + 1)); + stats->max_digits = digits_for_value(stats->max_filestat + 1); if (error < 0) { git_diff_stats_free(stats); @@ -265,12 +278,11 @@ int git_diff_stats_to_buf( } if (format & GIT_DIFF_STATS_FULL) { - double scale_to = -1; if (width > 0) { if (width > stats->max_name + stats->max_digits + 5) - scale_to = width - (stats->max_name + stats->max_digits + 5); - if (scale_to < STATS_FULL_MIN_SCALE) - scale_to = STATS_FULL_MIN_SCALE; + width -= (stats->max_name + stats->max_digits + 5); + if (width < STATS_FULL_MIN_SCALE) + width = STATS_FULL_MIN_SCALE; } for (i = 0; i < stats->files_changed; ++i) { @@ -278,7 +290,7 @@ int git_diff_stats_to_buf( continue; error = git_diff_file_stats__full_to_buf( - out, delta, &stats->filestats[i], stats, scale_to); + out, delta, &stats->filestats[i], stats, width); if (error < 0) return error; } -- cgit v1.2.1