Commit c5cacc4e by Vicent Marti

Merge pull request #2261 from jacquesg/format-patch

Support for format-patch
parents 3b2d14a7 39206ca2
......@@ -1072,6 +1072,179 @@ GIT_EXTERN(int) git_diff_buffers(
git_diff_line_cb line_cb,
void *payload);
/**
* This is an opaque structure which is allocated by `git_diff_get_stats`.
* You are responsible for releasing the object memory when done, using the
* `git_diff_stats_free()` function.
*/
typedef struct git_diff_stats git_diff_stats;
/**
* Formatting options for diff stats
*/
typedef enum {
/** No stats*/
GIT_DIFF_STATS_NONE = 0,
/** Full statistics, equivalent of `--stat` */
GIT_DIFF_STATS_FULL = (1u << 0),
/** Short statistics, equivalent of `--shortstat` */
GIT_DIFF_STATS_SHORT = (1u << 1),
/** Number statistics, equivalent of `--numstat` */
GIT_DIFF_STATS_NUMBER = (1u << 2),
/** Extended header information such as creations, renames and mode changes, equivalent of `--summary` */
GIT_DIFF_STATS_INCLUDE_SUMMARY = (1u << 3),
} git_diff_stats_format_t;
/**
* Accumlate diff statistics for all patches.
*
* @param out Structure containg the diff statistics.
* @param diff A git_diff generated by one of the above functions.
* @return 0 on success; non-zero on error
*/
GIT_EXTERN(int) git_diff_get_stats(
git_diff_stats **out,
git_diff *diff);
/**
* Get the total number of files changed in a diff
*
* @param stats A `git_diff_stats` generated by one of the above functions.
* @return total number of files changed in the diff
*/
GIT_EXTERN(size_t) git_diff_stats_files_changed(
const git_diff_stats *stats);
/**
* Get the total number of insertions in a diff
*
* @param stats A `git_diff_stats` generated by one of the above functions.
* @return total number of insertions in the diff
*/
GIT_EXTERN(size_t) git_diff_stats_insertions(
const git_diff_stats *stats);
/**
* Get the total number of deletions in a diff
*
* @param stats A `git_diff_stats` generated by one of the above functions.
* @return total number of deletions in the diff
*/
GIT_EXTERN(size_t) git_diff_stats_deletions(
const git_diff_stats *stats);
/**
* Print diff statistics to a `git_buf`.
*
* @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.
* @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);
/**
* Deallocate a `git_diff_stats`.
*
* @param stats The previously created statistics object;
* cannot be used after free.
*/
GIT_EXTERN(void) git_diff_stats_free(git_diff_stats *stats);
/**
* Formatting options for diff e-mail generation
*/
typedef enum {
/** Normal patch, the default */
GIT_DIFF_FORMAT_EMAIL_NONE = 0,
/** Don't insert "[PATCH]" in the subject header*/
GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER = (1 << 0),
} git_diff_format_email_flags_t;
/**
* Options for controlling the formatting of the generated e-mail.
*/
typedef struct {
unsigned int version;
git_diff_format_email_flags_t flags;
/** This patch number */
size_t patch_no;
/** Total number of patches in this series */
size_t total_patches;
/** id to use for the commit */
const git_oid *id;
/** Summary of the change */
const char *summary;
/** Author of the change */
const git_signature *author;
} git_diff_format_email_options;
#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION 1
#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT {GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION, 0, 1, 1, NULL, NULL, NULL}
/**
* Create an e-mail ready patch from a diff.
*
* @param out buffer to store the e-mail patch in
* @param diff containing the commit
* @param opts structure with options to influence content and formatting.
* @return 0 or an error code
*/
GIT_EXTERN(int) git_diff_format_email(
git_buf *out,
git_diff *diff,
const git_diff_format_email_options *opts);
/**
* Create an e-mail ready patch for a commit.
*
* Does not support creating patches for merge commits (yet).
*
* @param out buffer to store the e-mail patch in
* @param repo containing the commit
* @param commit pointer to up commit
* @param patch_no patch number of the commit
* @param total_patches total number of patches in the patch set
* @param flags determines the formatting of the e-mail
* @param diff_opts structure with options to influence diff or NULL for defaults.
* @return 0 or an error code
*/
GIT_EXTERN(int) git_diff_commit_as_email(
git_buf *out,
git_repository *repo,
git_commit *commit,
size_t patch_no,
size_t total_patches,
git_diff_format_email_flags_t flags,
const git_diff_options *diff_opts);
/**
* Initializes a `git_diff_format_email_options` with default values. Equivalent to
* creating an instance with GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT.
*
* @param opts the `git_diff_format_email_options` instance to initialize.
* @param version the version of the struct; you should pass
* `GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION` here.
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_diff_format_email_init_options(
git_diff_format_email_options *opts,
int version);
GIT_END_DECL
......
......@@ -141,12 +141,12 @@ GIT_EXTERN(void) git_patch_free(git_patch *patch);
/**
* Get the delta associated with a patch
*/
GIT_EXTERN(const git_diff_delta *) git_patch_get_delta(git_patch *patch);
GIT_EXTERN(const git_diff_delta *) git_patch_get_delta(const git_patch *patch);
/**
* Get the number of hunks in a patch
*/
GIT_EXTERN(size_t) git_patch_num_hunks(git_patch *patch);
GIT_EXTERN(size_t) git_patch_num_hunks(const git_patch *patch);
/**
* Get line counts of each type in a patch.
......@@ -197,7 +197,7 @@ GIT_EXTERN(int) git_patch_get_hunk(
* @return Number of lines in hunk or -1 if invalid hunk index
*/
GIT_EXTERN(int) git_patch_num_lines_in_hunk(
git_patch *patch,
const git_patch *patch,
size_t hunk_idx);
/**
......
......@@ -148,6 +148,15 @@ int git_buf_putc(git_buf *buf, char c)
return 0;
}
int git_buf_putcn(git_buf *buf, char c, size_t len)
{
ENSURE_SIZE(buf, buf->size + len + 1);
memset(buf->ptr + buf->size, c, len);
buf->size += len;
buf->ptr[buf->size] = '\0';
return 0;
}
int git_buf_put(git_buf *buf, const char *data, size_t len)
{
ENSURE_SIZE(buf, buf->size + len + 1);
......
......@@ -88,6 +88,7 @@ GIT_INLINE(bool) git_buf_oom(const git_buf *buf)
*/
int git_buf_sets(git_buf *buf, const char *string);
int git_buf_putc(git_buf *buf, char c);
int git_buf_putcn(git_buf *buf, char c, size_t len);
int git_buf_put(git_buf *buf, const char *data, size_t len);
int git_buf_puts(git_buf *buf, const char *string);
int git_buf_printf(git_buf *buf, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
......
......@@ -874,3 +874,31 @@ int git__date_parse(git_time_t *out, const char *date)
*out = approxidate_str(date, time_sec, &error_ret);
return error_ret;
}
int git__date_rfc2822_fmt(char *out, size_t len, const git_time *date)
{
int written;
struct tm gmt;
time_t t;
assert(out && date);
t = (time_t) (date->time + date->offset * 60);
if (p_gmtime_r (&t, &gmt) == NULL)
return -1;
written = p_snprintf(out, len, "%.3s, %u %.3s %.4u %02u:%02u:%02u %+03d%02d",
weekday_names[gmt.tm_wday],
gmt.tm_mday,
month_names[gmt.tm_mon],
gmt.tm_year + 1900,
gmt.tm_hour, gmt.tm_min, gmt.tm_sec,
date->offset / 60, date->offset % 60);
if (written < 0 || (written > (int) len - 1))
return -1;
return 0;
}
......@@ -1427,6 +1427,216 @@ int git_diff__paired_foreach(
return error;
}
int git_diff__commit(
git_diff **diff,
git_repository *repo,
const git_commit *commit,
const git_diff_options *opts)
{
git_commit *parent = NULL;
git_diff *commit_diff = NULL;
git_tree *old_tree = NULL, *new_tree = NULL;
size_t parents;
int error = 0;
if ((parents = git_commit_parentcount(commit)) > 1) {
char commit_oidstr[GIT_OID_HEXSZ + 1];
error = -1;
giterr_set(GITERR_INVALID, "Commit %s is a merge commit",
git_oid_tostr(commit_oidstr, GIT_OID_HEXSZ + 1, git_commit_id(commit)));
goto on_error;
}
if (parents > 0)
if ((error = git_commit_parent(&parent, commit, 0)) < 0 ||
(error = git_commit_tree(&old_tree, parent)) < 0)
goto on_error;
if ((error = git_commit_tree(&new_tree, commit)) < 0 ||
(error = git_diff_tree_to_tree(&commit_diff, repo, old_tree, new_tree, opts)) < 0)
goto on_error;
*diff = commit_diff;
on_error:
git_tree_free(new_tree);
git_tree_free(old_tree);
git_commit_free(parent);
return error;
}
int git_diff_format_email__append_header_tobuf(
git_buf *out,
const git_oid *id,
const git_signature *author,
const char *summary,
size_t patch_no,
size_t total_patches,
bool exclude_patchno_marker)
{
char idstr[GIT_OID_HEXSZ + 1];
char date_str[GIT_DATE_RFC2822_SZ];
int error = 0;
git_oid_fmt(idstr, id);
idstr[GIT_OID_HEXSZ] = '\0';
if ((error = git__date_rfc2822_fmt(date_str, sizeof(date_str), &author->when)) < 0)
return error;
error = git_buf_printf(out,
"From %s Mon Sep 17 00:00:00 2001\n" \
"From: %s <%s>\n" \
"Date: %s\n" \
"Subject: ",
idstr,
author->name, author->email,
date_str);
if (error < 0)
return error;
if (!exclude_patchno_marker) {
if (total_patches == 1) {
error = git_buf_puts(out, "[PATCH] ");
} else {
error = git_buf_printf(out, "[PATCH %"PRIuZ"/%"PRIuZ"] ", patch_no, total_patches);
}
if (error < 0)
return error;
}
error = git_buf_printf(out, "%s\n\n", summary);
return error;
}
int git_diff_format_email__append_patches_tobuf(
git_buf *out,
git_diff *diff)
{
size_t i, deltas;
int error = 0;
deltas = git_diff_num_deltas(diff);
for (i = 0; i < deltas; ++i) {
git_patch *patch = NULL;
if ((error = git_patch_from_diff(&patch, diff, i)) >= 0)
error = git_patch_to_buf(out, patch);
git_patch_free(patch);
if (error < 0)
break;
}
return error;
}
int git_diff_format_email(
git_buf *out,
git_diff *diff,
const git_diff_format_email_options *opts)
{
git_diff_stats *stats = NULL;
char *summary = NULL, *loc = NULL;
bool ignore_marker;
unsigned int format_flags = 0;
int error;
assert(out && diff && opts);
assert(opts->summary && opts->id && opts->author);
GITERR_CHECK_VERSION(opts, GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION, "git_format_email_options");
if ((ignore_marker = opts->flags & GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER) == false) {
if (opts->patch_no > opts->total_patches) {
giterr_set(GITERR_INVALID, "patch %"PRIuZ" out of range. max %"PRIuZ, opts->patch_no, opts->total_patches);
return -1;
}
if (opts->patch_no == 0) {
giterr_set(GITERR_INVALID, "invalid patch no %"PRIuZ". should be >0", opts->patch_no);
return -1;
}
}
/* the summary we receive may not be clean.
* it could potentially contain new line characters
* or not be set, sanitize, */
if ((loc = strpbrk(opts->summary, "\r\n")) != NULL) {
size_t offset = 0;
if ((offset = (loc - opts->summary)) == 0) {
giterr_set(GITERR_INVALID, "summary is empty");
error = -1;
}
summary = git__calloc(offset + 1, sizeof(char));
GITERR_CHECK_ALLOC(summary);
strncpy(summary, opts->summary, offset);
}
error = git_diff_format_email__append_header_tobuf(out,
opts->id, opts->author, summary == NULL ? opts->summary : summary,
opts->patch_no, opts->total_patches, ignore_marker);
if (error < 0)
goto on_error;
format_flags = GIT_DIFF_STATS_FULL | GIT_DIFF_STATS_INCLUDE_SUMMARY;
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_format_email__append_patches_tobuf(out, diff)) < 0)
goto on_error;
error = git_buf_puts(out, "--\nlibgit2 " LIBGIT2_VERSION "\n\n");
on_error:
git__free(summary);
git_diff_stats_free(stats);
return error;
}
int git_diff_commit_as_email(
git_buf *out,
git_repository *repo,
git_commit *commit,
size_t patch_no,
size_t total_patches,
git_diff_format_email_flags_t flags,
const git_diff_options *diff_opts)
{
git_diff *diff = NULL;
git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
int error;
assert (out && repo && commit);
opts.flags = flags;
opts.patch_no = patch_no;
opts.total_patches = total_patches;
opts.id = git_commit_id(commit);
opts.summary = git_commit_summary(commit);
opts.author = git_commit_author(commit);
if ((error = git_diff__commit(&diff, repo, commit, diff_opts)) < 0)
return error;
error = git_diff_format_email(out, diff, &opts);
git_diff_free(diff);
return error;
}
int git_diff_init_options(git_diff_options* opts, int version)
{
if (version != GIT_DIFF_OPTIONS_VERSION) {
......@@ -1450,3 +1660,15 @@ int git_diff_find_init_options(git_diff_find_options* opts, int version)
return 0;
}
}
int git_diff_format_email_init_options(git_diff_format_email_options* opts, int version)
{
if (version != GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION) {
giterr_set(GITERR_INVALID, "Invalid version %d for git_diff_format_email_options", version);
return -1;
} else {
git_diff_format_email_options o = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
memcpy(opts, &o, sizeof(o));
return 0;
}
}
......@@ -116,6 +116,9 @@ extern void git_diff_find_similar__hashsig_free(void *sig, void *payload);
extern int git_diff_find_similar__calc_similarity(
int *score, void *siga, void *sigb, void *payload);
extern int git_diff__commit(
git_diff **diff, git_repository *repo, const git_commit *commit, const git_diff_options *opts);
/*
* Sometimes a git_diff_file will have a zero size; this attempts to
* fill in the size without loading the blob if possible. If that is
......
......@@ -631,13 +631,13 @@ void git_patch_free(git_patch *patch)
GIT_REFCOUNT_DEC(patch, diff_patch_free);
}
const git_diff_delta *git_patch_get_delta(git_patch *patch)
const git_diff_delta *git_patch_get_delta(const git_patch *patch)
{
assert(patch);
return patch->delta;
}
size_t git_patch_num_hunks(git_patch *patch)
size_t git_patch_num_hunks(const git_patch *patch)
{
assert(patch);
return git_array_size(patch->hunks);
......@@ -708,7 +708,7 @@ int git_patch_get_hunk(
return 0;
}
int git_patch_num_lines_in_hunk(git_patch *patch, size_t hunk_idx)
int git_patch_num_lines_in_hunk(const git_patch *patch, size_t hunk_idx)
{
diff_patch_hunk *hunk;
assert(patch);
......
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "common.h"
#include "vector.h"
#include "diff.h"
#include "diff_patch.h"
#define DIFF_RENAME_FILE_SEPARATOR " => "
struct git_diff_stats {
git_vector patches;
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;
}
return max_padding;
}
int git_diff_file_stats__full_to_buf(
git_buf *out,
size_t max_padding,
int has_renames,
const git_patch *patch)
{
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)
goto on_error;
if (strcmp(old_path, new_path) != 0) {
padding = max_padding - strlen(old_path) - strlen(new_path);
if ((error = git_buf_printf(out, DIFF_RENAME_FILE_SEPARATOR "%s", new_path)) < 0)
goto on_error;
}
else {
padding = max_padding - strlen(old_path);
if (has_renames)
padding += strlen(DIFF_RENAME_FILE_SEPARATOR);
}
if ((error = git_buf_putcn(out, ' ', padding)) < 0 ||
(error = 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)
goto on_error;
}
else {
size_t insertions, deletions;
if ((error = git_patch_line_stats(NULL, &insertions, &deletions, patch)) < 0)
goto on_error;
if ((error = git_buf_printf(out, "%" PRIuZ, insertions + deletions)) < 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)
goto on_error;
}
}
error = git_buf_putc(out, '\n');
on_error:
return error;
}
int git_diff_file_stats__number_to_buf(
git_buf *out,
const git_patch *patch)
{
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;
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);
return error;
}
int git_diff_file_stats__summary_to_buf(
git_buf *out,
const git_patch *patch)
{
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",
delta->new_file.mode, delta->new_file.path);
}
else if (delta->new_file.mode == 0) {
git_buf_printf(out, " delete mode %06o %s\n",
delta->old_file.mode, delta->old_file.path);
}
else {
git_buf_printf(out, " mode change %06o => %06o %s\n",
delta->old_file.mode, delta->new_file.mode, delta->new_file.path);
}
}
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)
{
size_t i, deltas;
size_t total_insertions = 0, total_deletions = 0;
git_diff_stats *stats = NULL;
int error = 0;
assert(out && diff);
stats = git__calloc(1, sizeof(git_diff_stats));
GITERR_CHECK_ALLOC(stats);
deltas = git_diff_num_deltas(diff);
for (i = 0; i < deltas; ++i) {
git_patch *patch = NULL;
size_t add, remove;
if ((error = git_patch_from_diff(&patch, diff, i)) < 0)
goto on_error;
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;
}
total_insertions += add;
total_deletions += remove;
}
stats->files_changed = deltas;
stats->insertions = total_insertions;
stats->deletions = total_deletions;
*out = stats;
goto done;
on_error:
git_diff_stats_free(stats);
done:
return error;
}
size_t git_diff_stats_files_changed(
const git_diff_stats *stats)
{
assert(stats);
return stats->files_changed;
}
size_t git_diff_stats_insertions(
const git_diff_stats *stats)
{
assert(stats);
return stats->insertions;
}
size_t git_diff_stats_deletions(
const git_diff_stats *stats)
{
assert(stats);
return stats->deletions;
}
int git_diff_stats_to_buf(
git_buf *out,
const git_diff_stats *stats,
git_diff_stats_format_t format)
{
git_patch *patch = NULL;
size_t i;
int has_renames = 0, error = 0;
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);
error = git_diff_file_stats__full_to_buf(out, max_padding, has_renames, patch);
}
else if (format & GIT_DIFF_STATS_NUMBER) {
error = git_diff_file_stats__number_to_buf(out, patch);
}
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);
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)
return error;
}
if (git_vector_length(&stats->patches) > 0)
git_buf_putc(out, '\n');
}
return error;
}
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__free(stats);
}
......@@ -20,6 +20,8 @@
# define max(a,b) ((a) > (b) ? (a) : (b))
#endif
#define GIT_DATE_RFC2822_SZ 32
/*
* Custom memory allocation wrappers
* that set error code and error message
......@@ -329,6 +331,16 @@ extern int git__parse_bool(int *out, const char *value);
extern int git__date_parse(git_time_t *out, const char *date);
/*
* Format a git_time as a RFC2822 string
*
* @param out buffer to store formatted date; a '\\0' terminator will automatically be added.
* @param len size of the buffer; should be atleast `GIT_DATE_RFC2822_SZ` in size;
* @param date the date to be formatted
* @return 0 if successful; -1 on error
*/
extern int git__date_rfc2822_fmt(char *out, size_t len, const git_time *date);
/*
* Unescapes a string in-place.
*
* Edge cases behavior:
......
#include "clar_libgit2.h"
#include "util.h"
void test_date_rfc2822__format_rfc2822_no_offset(void)
{
git_time t = {1397031663, 0};
char buf[GIT_DATE_RFC2822_SZ];
cl_git_pass(git__date_rfc2822_fmt(buf, sizeof(buf), &t));
cl_assert(strcmp(buf, "Wed, 9 Apr 2014 08:21:03 +0000") == 0);
}
void test_date_rfc2822__format_rfc2822_positive_offset(void)
{
git_time t = {1397031663, 120};
char buf[GIT_DATE_RFC2822_SZ];
cl_git_pass(git__date_rfc2822_fmt(buf, sizeof(buf), &t));
cl_assert(strcmp(buf, "Wed, 9 Apr 2014 10:21:03 +0200") == 0);
}
void test_date_rfc2822__format_rfc2822_negative_offset(void)
{
git_time t = {1397031663, -120};
char buf[GIT_DATE_RFC2822_SZ];
cl_git_pass(git__date_rfc2822_fmt(buf, sizeof(buf), &t));
cl_assert(strcmp(buf, "Wed, 9 Apr 2014 06:21:03 -0200") == 0);
}
void test_date_rfc2822__format_rfc2822_buffer_too_small(void)
{
// "Wed, 10 Apr 2014 08:21:03 +0000"
git_time t = {1397031663 + 86400, 0};
char buf[GIT_DATE_RFC2822_SZ-1];
cl_git_fail(git__date_rfc2822_fmt(buf, sizeof(buf), &t));
}
#include "clar.h"
#include "clar_libgit2.h"
#include "buffer.h"
#include "commit.h"
#include "diff.h"
static git_repository *repo;
void test_diff_format_email__initialize(void)
{
repo = cl_git_sandbox_init("diff_format_email");
}
void test_diff_format_email__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_diff_format_email__simple(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 <jacquesg@striata.com>\n" \
"Date: Wed, 9 Apr 2014 20:57:01 +0200\n" \
"Subject: [PATCH] Modify some content\n" \
"\n" \
"---\n" \
" file1.txt | 8 +++++---\n" \
" 1 file changed, 5 insertions(+), 3 deletions(-)\n" \
"\n" \
"diff --git a/file1.txt b/file1.txt\n" \
"index 94aaae8..af8f41d 100644\n" \
"--- a/file1.txt\n" \
"+++ b/file1.txt\n" \
"@@ -1,15 +1,17 @@\n" \
" file1.txt\n" \
" file1.txt\n" \
"+_file1.txt_\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
"+\n" \
"+\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"+_file1.txt_\n" \
"+_file1.txt_\n" \
" file1.txt\n" \
"--\n" \
"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);
}
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_buf buf = GIT_BUF_INIT;
const char *email =
"From 10808fe9c9be5a190c0ba68d1a002233fb363508 Mon Sep 17 00:00:00 2001\n" \
"From: Jacques Germishuys <jacquesg@striata.com>\n" \
"Date: Thu, 10 Apr 2014 19:37:05 +0200\n" \
"Subject: [PATCH 1/2] Added file2.txt file3.txt\n" \
"\n" \
"---\n" \
" file2.txt | 5 +++++\n" \
" file3.txt | 5 +++++\n" \
" 2 files changed, 10 insertions(+), 0 deletions(-)\n" \
" create mode 100644 file2.txt\n" \
" create mode 100644 file3.txt\n" \
"\n" \
"diff --git a/file2.txt b/file2.txt\n" \
"new file mode 100644\n" \
"index 0000000..e909123\n" \
"--- /dev/null\n" \
"+++ b/file2.txt\n" \
"@@ -0,0 +1,5 @@\n" \
"+file2\n" \
"+file2\n" \
"+file2\n" \
"+file2\n" \
"+file2\n" \
"diff --git a/file3.txt b/file3.txt\n" \
"new file mode 100644\n" \
"index 0000000..9435022\n" \
"--- /dev/null\n" \
"+++ b/file3.txt\n" \
"@@ -0,0 +1,5 @@\n" \
"+file3\n" \
"+file3\n" \
"+file3\n" \
"+file3\n" \
"+file3\n" \
"--\n" \
"libgit2 " LIBGIT2_VERSION "\n" \
"\n" \
"From 873806f6f27e631eb0b23e4b56bea2bfac14a373 Mon Sep 17 00:00:00 2001\n" \
"From: Jacques Germishuys <jacquesg@striata.com>\n" \
"Date: Thu, 10 Apr 2014 19:37:36 +0200\n" \
"Subject: [PATCH 2/2] Modified file2.txt, file3.txt\n" \
"\n" \
"---\n" \
" file2.txt | 2 +-\n" \
" file3.txt | 2 +-\n" \
" 2 files changed, 2 insertions(+), 2 deletions(-)\n" \
"\n" \
"diff --git a/file2.txt b/file2.txt\n" \
"index e909123..7aff11d 100644\n" \
"--- a/file2.txt\n" \
"+++ b/file2.txt\n" \
"@@ -1,5 +1,5 @@\n" \
" file2\n" \
" file2\n" \
" file2\n" \
"-file2\n" \
"+file2!\n" \
" file2\n" \
"diff --git a/file3.txt b/file3.txt\n" \
"index 9435022..9a2d780 100644\n" \
"--- a/file3.txt\n" \
"+++ b/file3.txt\n" \
"@@ -1,5 +1,5 @@\n" \
" file3\n" \
"-file3\n" \
"+file3!\n" \
" file3\n" \
" file3\n" \
" file3\n" \
"--\n" \
"libgit2 " LIBGIT2_VERSION "\n" \
"\n";
git_oid_fromstr(&oid, "10808fe9c9be5a190c0ba68d1a002233fb363508");
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.patch_no = 1;
opts.total_patches = 2;
cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
cl_git_pass(git_diff_format_email(&buf, diff, &opts));
git_diff_free(diff);
git_commit_free(commit);
diff = NULL;
commit = NULL;
git_oid_fromstr(&oid, "873806f6f27e631eb0b23e4b56bea2bfac14a373");
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.patch_no = 2;
opts.total_patches = 2;
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);
}
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 =
"From 9264b96c6d104d0e07ae33d3007b6a48246c6f92 Mon Sep 17 00:00:00 2001\n" \
"From: Jacques Germishuys <jacquesg@striata.com>\n" \
"Date: Wed, 9 Apr 2014 20:57:01 +0200\n" \
"Subject: Modify some content\n" \
"\n" \
"---\n" \
" file1.txt | 8 +++++---\n" \
" 1 file changed, 5 insertions(+), 3 deletions(-)\n" \
"\n" \
"diff --git a/file1.txt b/file1.txt\n" \
"index 94aaae8..af8f41d 100644\n" \
"--- a/file1.txt\n" \
"+++ b/file1.txt\n" \
"@@ -1,15 +1,17 @@\n" \
" file1.txt\n" \
" file1.txt\n" \
"+_file1.txt_\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
"+\n" \
"+\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"+_file1.txt_\n" \
"+_file1.txt_\n" \
" file1.txt\n" \
"--\n" \
"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);
}
void test_diff_format_email__invalid_no(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;
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.patch_no = 2;
opts.total_patches = 1;
cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
cl_git_fail(git_diff_format_email(&buf, diff, &opts));
cl_git_fail(git_diff_commit_as_email(&buf, repo, commit, 2, 1, 0, NULL));
cl_git_fail(git_diff_commit_as_email(&buf, repo, commit, 0, 0, 0, NULL));
git_diff_free(diff);
git_commit_free(commit);
git_buf_free(&buf);
}
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 =
"From 7ade76dd34bba4733cf9878079f9fd4a456a9189 Mon Sep 17 00:00:00 2001\n" \
"From: Jacques Germishuys <jacquesg@striata.com>\n" \
"Date: Thu, 10 Apr 2014 10:05:03 +0200\n" \
"Subject: [PATCH] Update permissions\n" \
"\n" \
"---\n" \
" file1.txt.renamed | 0\n" \
" 1 file changed, 0 insertions(+), 0 deletions(-)\n" \
" mode change 100644 => 100755 file1.txt.renamed\n" \
"\n" \
"diff --git a/file1.txt.renamed b/file1.txt.renamed\n" \
"old mode 100644\n" \
"new mode 100755\n" \
"index a97157a..a97157a\n" \
"--- a/file1.txt.renamed\n" \
"+++ b/file1.txt.renamed\n" \
"--\n" \
"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);
}
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 =
"From 6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d Mon Sep 17 00:00:00 2001\n" \
"From: Jacques Germishuys <jacquesg@striata.com>\n" \
"Date: Wed, 9 Apr 2014 21:15:56 +0200\n" \
"Subject: [PATCH] Renamed file1.txt -> file1.txt.renamed\n" \
"\n" \
"---\n" \
" file1.txt | 17 -----------------\n" \
" file1.txt.renamed | 17 +++++++++++++++++\n" \
" 2 files changed, 17 insertions(+), 17 deletions(-)\n" \
" delete mode 100644 file1.txt\n" \
" create mode 100644 file1.txt.renamed\n" \
"\n" \
"diff --git a/file1.txt b/file1.txt\n" \
"deleted file mode 100644\n" \
"index af8f41d..0000000\n" \
"--- a/file1.txt\n" \
"+++ /dev/null\n" \
"@@ -1,17 +0,0 @@\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"-_file1.txt_\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"-\n" \
"-\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"-_file1.txt_\n" \
"-_file1.txt_\n" \
"-file1.txt\n" \
"diff --git a/file1.txt.renamed b/file1.txt.renamed\n" \
"new file mode 100644\n" \
"index 0000000..a97157a\n" \
"--- /dev/null\n" \
"+++ b/file1.txt.renamed\n" \
"@@ -0,0 +1,17 @@\n" \
"+file1.txt\n" \
"+file1.txt\n" \
"+_file1.txt_\n" \
"+file1.txt\n" \
"+file1.txt\n" \
"+file1.txt_renamed\n" \
"+file1.txt\n" \
"+\n" \
"+\n" \
"+file1.txt\n" \
"+file1.txt\n" \
"+file1.txt_renamed\n" \
"+file1.txt\n" \
"+file1.txt\n" \
"+_file1.txt_\n" \
"+_file1.txt_\n" \
"+file1.txt\n" \
"--\n" \
"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);
}
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 <jacquesg@striata.com>\n" \
"Date: Wed, 9 Apr 2014 20:57:01 +0200\n" \
"Subject: [PATCH] Modify some content\n" \
"\n" \
"---\n" \
" file1.txt | 8 +++++---\n" \
" 1 file changed, 5 insertions(+), 3 deletions(-)\n" \
"\n" \
"diff --git a/file1.txt b/file1.txt\n" \
"index 94aaae8..af8f41d 100644\n" \
"--- a/file1.txt\n" \
"+++ b/file1.txt\n" \
"@@ -1,15 +1,17 @@\n" \
" file1.txt\n" \
" file1.txt\n" \
"+_file1.txt_\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
"+\n" \
"+\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
" file1.txt\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"-file1.txt\n" \
"+_file1.txt_\n" \
"+_file1.txt_\n" \
" file1.txt\n" \
"--\n" \
"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);
}
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 <jacquesg@striata.com>\n" \
"Date: Sun, 13 Apr 2014 18:10:18 +0200\n" \
"Subject: [PATCH] Modified binary file\n" \
"\n" \
"---\n" \
" binary.bin | Bin 3 -> 0 bytes\n" \
" 1 file changed, 0 insertions(+), 0 deletions(-)\n" \
"\n" \
"diff --git a/binary.bin b/binary.bin\n" \
"index bd474b2..9ac35ff 100644\n" \
"Binary files a/binary.bin and b/binary.bin differ\n" \
"--\n" \
"libgit2 " LIBGIT2_VERSION "\n" \
"\n";
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);
}
#include "clar.h"
#include "clar_libgit2.h"
#include "buffer.h"
#include "commit.h"
#include "diff.h"
static git_repository *repo;
void test_diff_stats__initialize(void)
{
repo = cl_git_sandbox_init("diff_format_email");
}
void test_diff_stats__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_diff_stats__stat(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 | 8 +++++---\n" \
" 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));
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_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));
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_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);
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);
git_diff_stats_free(stats);
git_diff_free(diff);
git_commit_free(commit);
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));
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_SHORT));
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__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");
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_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_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);
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));
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_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);
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";
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_find_similar(diff, NULL));
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);
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"
" file3.txt | 5 -----\n"
" file3.txt.renamed | 7 +++++++\n"
" 4 files changed, 13 insertions(+), 10 deletions(-)\n";
git_oid_fromstr(&oid, "8947a46e2097638ca6040ad4877246f4186ec3bd");
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) == 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);
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"
" file3.txt.renamed | 7 -------\n"
" 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));
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);
git_diff_stats_free(stats);
git_diff_free(diff);
git_commit_free(commit);
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);
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);
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";
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);
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);
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));
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);
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";
git_oid_fromstr(&oid, "7ade76dd34bba4733cf9878079f9fd4a456a9189");
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);
git_buf_free(&buf);
}
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~
file1.txt
file1.txt
_file1.txt_
file1.txt
file1.txt
file1.txt_renamed
file1.txt
file1.txt
file1.txt
file1.txt_renamed
file1.txt
file1.txt
_file1.txt_
_file1.txt_
file1.txt
file2
file2
file2
file2!
file2
file3
file3!
file3
file3
file3
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment