Commit 3a437590 by Russell Belfer

Clean up diff implementation for review

This fixes several bugs, updates tests and docs, eliminates the
FILE* assumption in favor of printing callbacks for the diff patch
formatter helpers, and adds a "diff" example function that can
perform a diff from the command line.
parent 65b09b1d
.PHONY: all
CC = gcc
CFLAGS = -g -I../include
CFLAGS = -g -I../include -I../src
LFLAGS = -L../build -lgit2 -lz
all: general showindex
all: general showindex diff
% : %.c
$(CC) -o $@ $(CFLAGS) $< $(LFLAGS)
clean:
$(RM) general showindex
$(RM) general showindex diff
$(RM) -r *.dSYM
#include <stdio.h>
#include <git2.h>
#include <stdlib.h>
#include <string.h>
void check(int error, const char *message)
{
if (error) {
fprintf(stderr, "%s (%d)\n", message, error);
exit(1);
}
}
int resolve_to_tree(git_repository *repo, const char *identifier, git_tree **tree)
{
int err = 0;
size_t len = strlen(identifier);
git_oid oid;
git_object *obj = NULL;
/* try to resolve as OID */
if (git_oid_fromstrn(&oid, identifier, len) == 0)
git_object_lookup_prefix(&obj, repo, &oid, len, GIT_OBJ_ANY);
/* try to resolve as reference */
if (obj == NULL) {
git_reference *ref, *resolved;
if (git_reference_lookup(&ref, repo, identifier) == 0) {
git_reference_resolve(&resolved, ref);
git_reference_free(ref);
if (resolved) {
git_object_lookup(&obj, repo, git_reference_oid(resolved), GIT_OBJ_ANY);
git_reference_free(resolved);
}
}
}
if (obj == NULL)
return GIT_ENOTFOUND;
switch (git_object_type(obj)) {
case GIT_OBJ_TREE:
*tree = (git_tree *)obj;
break;
case GIT_OBJ_COMMIT:
err = git_commit_tree(tree, (git_commit *)obj);
git_object_free(obj);
break;
default:
err = GIT_ENOTFOUND;
}
return err;
}
char *colors[] = {
"\033[m", /* reset */
"\033[1m", /* bold */
"\033[31m", /* red */
"\033[32m", /* green */
"\033[36m" /* cyan */
};
int printer(void *data, char usage, const char *line)
{
int *last_color = data, color = 0;
if (*last_color >= 0) {
switch (usage) {
case GIT_DIFF_LINE_ADDITION: color = 3; break;
case GIT_DIFF_LINE_DELETION: color = 2; break;
case GIT_DIFF_LINE_ADD_EOFNL: color = 3; break;
case GIT_DIFF_LINE_DEL_EOFNL: color = 2; break;
case GIT_DIFF_LINE_FILE_HDR: color = 1; break;
case GIT_DIFF_LINE_HUNK_HDR: color = 4; break;
default: color = 0;
}
if (color != *last_color) {
if (*last_color == 1 || color == 1)
fputs(colors[0], stdout);
fputs(colors[color], stdout);
*last_color = color;
}
}
fputs(line, stdout);
return 0;
}
int main(int argc, char *argv[])
{
char path[GIT_PATH_MAX];
git_repository *repo = NULL;
git_tree *a, *b;
git_diff_options opts = {0};
git_diff_list *diff;
char *dir = ".";
int color = -1;
if (argc != 3) {
fprintf(stderr, "usage: diff <tree-oid> <tree-oid>\n");
exit(1);
}
check(git_repository_discover(path, sizeof(path), dir, 0, "/"),
"Could not discover repository");
check(git_repository_open(&repo, path),
"Could not open repository");
check(resolve_to_tree(repo, argv[1], &a), "Looking up first tree");
check(resolve_to_tree(repo, argv[2], &b), "Looking up second tree");
check(git_diff_tree_to_tree(repo, &opts, a, b, &diff), "Generating diff");
fputs(colors[0], stdout);
check(git_diff_print_compact(diff, &color, printer), "Displaying diff summary");
fprintf(stdout, "--\n");
color = 0;
check(git_diff_print_patch(diff, &color, printer), "Displaying diff");
fputs(colors[0], stdout);
git_diff_list_free(diff);
git_tree_free(a);
git_tree_free(b);
git_repository_free(repo);
return 0;
}
......@@ -16,21 +16,52 @@
/**
* @file git2/diff.h
* @brief Git tree and file differencing routines.
*
* Calculating diffs is generally done in two phases: building a diff list
* then traversing the diff list. This makes is easier to share logic
* across the various types of diffs (tree vs tree, workdir vs index, etc.),
* and also allows you to insert optional diff list post-processing phases,
* such as rename detected, in between the steps. When you are done with a
* diff list object, it must be freed.
*
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
/**
* Structure describing options about how the diff should be executed.
*
* @todo Most of the parameters here are not actually supported at this time.
*/
typedef struct {
int context_lines;
int interhunk_lines;
int ignore_whitespace;
int force_text;
int force_text; /**< generate text diffs even for binaries */
git_strarray pathspec;
} git_diff_options;
/**
* The diff list object that contains all individual file deltas.
*/
typedef struct git_diff_list git_diff_list;
/**
* Description of changes to one file.
*
* When iterating over a diff list object, this will generally be passed to
* most callback functions and you can use the contents to understand
* exactly what has changed.
*
* Under some circumstances, not all fields will be filled in, but the code
* generally tries to fill in as much as possible. One example is that the
* "binary" field will not actually look at file contents if you do not
* pass in hunk and/or line callbacks to the diff foreach iteration function.
* It will just use the git attributes for those files.
*/
typedef struct {
git_status_t status; /* value from tree.h */
git_status_t status; /**< value from tree.h */
unsigned int old_attr;
unsigned int new_attr;
git_oid old_oid;
......@@ -38,16 +69,22 @@ typedef struct {
git_blob *old_blob;
git_blob *new_blob;
const char *path;
const char *new_path; /* NULL unless status is RENAMED or COPIED */
int similarity; /* value from 0 to 100 */
int binary; /* diff as binary? */
const char *new_path; /**< NULL unless status is RENAMED or COPIED */
int similarity; /**< for RENAMED and COPIED, value from 0 to 100 */
int binary; /**< files in diff are binary? */
} git_diff_delta;
/**
* When iterating over a diff, callback that will be made per file.
*/
typedef int (*git_diff_file_fn)(
void *cb_data,
git_diff_delta *delta,
float progress);
/**
* Structure describing a hunk of a diff.
*/
typedef struct {
int old_start;
int old_lines;
......@@ -55,6 +92,9 @@ typedef struct {
int new_lines;
} git_diff_range;
/**
* When iterating over a diff, callback that will be made per hunk.
*/
typedef int (*git_diff_hunk_fn)(
void *cb_data,
git_diff_delta *delta,
......@@ -62,25 +102,60 @@ typedef int (*git_diff_hunk_fn)(
const char *header,
size_t header_len);
#define GIT_DIFF_LINE_CONTEXT ' '
#define GIT_DIFF_LINE_ADDITION '+'
#define GIT_DIFF_LINE_DELETION '-'
#define GIT_DIFF_LINE_ADD_EOFNL '\n'
#define GIT_DIFF_LINE_DEL_EOFNL '\0'
/**
* Line origin constants.
*
* These values describe where a line came from and will be passed to
* the git_diff_line_fn when iterating over a diff. There are some
* special origin contants at the end that are used for the text
* output callbacks to demarcate lines that are actually part of
* the file or hunk headers.
*/
enum {
/* these values will be sent to `git_diff_line_fn` along with the line */
GIT_DIFF_LINE_CONTEXT = ' ',
GIT_DIFF_LINE_ADDITION = '+',
GIT_DIFF_LINE_DELETION = '-',
GIT_DIFF_LINE_ADD_EOFNL = '\n', /**< LF was added at end of file */
GIT_DIFF_LINE_DEL_EOFNL = '\0', /**< LF was removed at end of file */
/* these values will only be sent to a `git_diff_output_fn` */
GIT_DIFF_LINE_FILE_HDR = 'F',
GIT_DIFF_LINE_HUNK_HDR = 'H',
GIT_DIFF_LINE_BINARY = 'B'
};
/**
* When iterating over a diff, callback that will be made per text diff
* line.
*/
typedef int (*git_diff_line_fn)(
void *cb_data,
git_diff_delta *delta,
char line_origin, /* GIT_DIFF_LINE value from above */
char line_origin, /**< GIT_DIFF_LINE_... value from above */
const char *content,
size_t content_len);
typedef struct git_diff_list git_diff_list;
/**
* When printing a diff, callback that will be made to output each line
* of text. This uses some extra GIT_DIFF_LINE_... constants for output
* of lines of file and hunk headers.
*/
typedef int (*git_diff_output_fn)(
void *cb_data,
char line_origin, /**< GIT_DIFF_LINE_... value from above */
const char *formatted_output);
/*
* Generate diff lists
/** @name Diff List Generator Functions
*
* These are the functions you would use to create (or destroy) a
* git_diff_list from various objects in a repository.
*/
/**@{*/
/**
* Compute a difference between two tree objects.
*/
GIT_EXTERN(int) git_diff_tree_to_tree(
git_repository *repo,
const git_diff_options *opts,
......@@ -88,29 +163,58 @@ GIT_EXTERN(int) git_diff_tree_to_tree(
git_tree *new,
git_diff_list **diff);
/**
* Compute a difference between a tree and the index.
* @todo NOT IMPLEMENTED
*/
GIT_EXTERN(int) git_diff_index_to_tree(
git_repository *repo,
const git_diff_options *opts,
git_tree *old,
git_diff_list **diff);
/**
* Compute a difference between the working directory and a tree.
* @todo NOT IMPLEMENTED
*/
GIT_EXTERN(int) git_diff_workdir_to_tree(
git_repository *repo,
const git_diff_options *opts,
git_tree *old,
git_diff_list **diff);
/**
* Compute a difference between the working directory and the index.
* @todo NOT IMPLEMENTED
*/
GIT_EXTERN(int) git_diff_workdir_to_index(
git_repository *repo,
const git_diff_options *opts,
git_diff_list **diff);
/**
* Deallocate a diff list.
*/
GIT_EXTERN(void) git_diff_list_free(git_diff_list *diff);
/*
* Process diff lists
/**@}*/
/** @name Diff List Processor Functions
*
* These are the functions you apply to a diff list to process it
* or read it in some way.
*/
/**@{*/
/**
* Iterate over a diff list issuing callbacks.
*
* If the hunk and/or line callbacks are not NULL, then this will calculate
* text diffs for all files it thinks are not binary. If those are both
* NULL, then this will not bother with the text diffs, so it can be
* efficient.
*/
GIT_EXTERN(int) git_diff_foreach(
git_diff_list *diff,
void *cb_data,
......@@ -118,20 +222,34 @@ GIT_EXTERN(int) git_diff_foreach(
git_diff_hunk_fn hunk_cb,
git_diff_line_fn line_cb);
#ifndef _STDIO_H_
#include <stdio.h>
#endif
/**
* Iterate over a diff generating text output like "git diff --name-status".
*/
GIT_EXTERN(int) git_diff_print_compact(
FILE *fp, git_diff_list *diff);
git_diff_list *diff,
void *cb_data,
git_diff_output_fn print_cb);
/**
* Iterate over a diff generating text output like "git diff".
*
* This is a super easy way to generate a patch from a diff.
*/
GIT_EXTERN(int) git_diff_print_patch(
FILE *fp, git_diff_list *diff);
git_diff_list *diff,
void *cb_data,
git_diff_output_fn print_cb);
/**@}*/
/*
* Misc
*/
/**
* Directly run a text diff on two blobs.
*/
GIT_EXTERN(int) git_diff_blobs(
git_repository *repo,
git_blob *old,
......
......@@ -313,15 +313,14 @@ enum git_treewalk_mode {
*/
GIT_EXTERN(int) git_tree_walk(git_tree *tree, git_treewalk_cb callback, int mode, void *payload);
/** @} */
typedef enum {
GIT_STATUS_UNMODIFIED = 0,
GIT_STATUS_ADDED = 1,
GIT_STATUS_DELETED = 2,
GIT_STATUS_MODIFIED = 3,
GIT_STATUS_RENAMED = 4,
GIT_STATUS_COPIED = 5,
/* the following will only be generated by git_diff functions */
GIT_STATUS_RENAMED = 4,
GIT_STATUS_COPIED = 5,
GIT_STATUS_IGNORED = 6,
GIT_STATUS_UNTRACKED = 7
} git_status_t;
......@@ -354,5 +353,7 @@ int git_tree_diff(git_tree *old, git_tree *newer, git_tree_diff_cb cb, void *dat
int git_tree_diff_index_recursive(git_tree *tree, git_index *index, git_tree_diff_cb cb, void *data);
/** @} */
GIT_END_DECL
#endif
#include "clar_libgit2.h"
#include "diff_helpers.h"
static git_repository *g_repo = NULL;
void test_diff_blob__initialize(void)
{
cl_fixture_sandbox("attr");
cl_git_pass(p_rename("attr/.gitted", "attr/.git"));
cl_git_pass(p_rename("attr/gitattributes", "attr/.gitattributes"));
cl_git_pass(git_repository_open(&g_repo, "attr/.git"));
}
void test_diff_blob__cleanup(void)
{
git_repository_free(g_repo);
g_repo = NULL;
cl_fixture_cleanup("attr");
}
void test_diff_blob__0(void)
{
git_blob *a, *b, *c, *d;
git_oid a_oid, b_oid, c_oid, d_oid;
git_diff_options opts;
diff_expects exp;
/* tests/resources/attr/root_test1 */
cl_git_pass(git_oid_fromstrn(&a_oid, "45141a79", 8));
cl_git_pass(git_blob_lookup_prefix(&a, g_repo, &a_oid, 4));
/* tests/resources/attr/root_test2 */
cl_git_pass(git_oid_fromstrn(&b_oid, "4d713dc4", 8));
cl_git_pass(git_blob_lookup_prefix(&b, g_repo, &b_oid, 4));
/* tests/resources/attr/root_test3 */
cl_git_pass(git_oid_fromstrn(&c_oid, "c96bbb2c2557a832", 16));
cl_git_pass(git_blob_lookup_prefix(&c, g_repo, &c_oid, 8));
/* tests/resources/attr/root_test4.txt */
cl_git_pass(git_oid_fromstrn(&d_oid, "fe773770c5a6", 12));
cl_git_pass(git_blob_lookup_prefix(&d, g_repo, &d_oid, 6));
/* Doing the equivalent of a `git diff -U1` on these files */
opts.context_lines = 1;
opts.interhunk_lines = 0;
opts.ignore_whitespace = 0;
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_blobs(
g_repo, a, b, &opts, &exp, diff_hunk_fn, diff_line_fn));
cl_assert(exp.hunks == 1);
cl_assert(exp.lines == 6);
cl_assert(exp.line_ctxt == 1);
cl_assert(exp.line_adds == 5);
cl_assert(exp.line_dels == 0);
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_blobs(
g_repo, b, c, &opts, &exp, diff_hunk_fn, diff_line_fn));
cl_assert(exp.hunks == 1);
cl_assert(exp.lines == 15);
cl_assert(exp.line_ctxt == 3);
cl_assert(exp.line_adds == 9);
cl_assert(exp.line_dels == 3);
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_blobs(
g_repo, a, c, &opts, &exp, diff_hunk_fn, diff_line_fn));
cl_assert(exp.hunks == 1);
cl_assert(exp.lines == 13);
cl_assert(exp.line_ctxt == 0);
cl_assert(exp.line_adds == 12);
cl_assert(exp.line_dels == 1);
opts.context_lines = 1;
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_blobs(
g_repo, c, d, &opts, &exp, diff_hunk_fn, diff_line_fn));
cl_assert(exp.hunks == 2);
cl_assert(exp.lines == 14);
cl_assert(exp.line_ctxt == 4);
cl_assert(exp.line_adds == 6);
cl_assert(exp.line_dels == 4);
git_blob_free(a);
git_blob_free(b);
git_blob_free(c);
git_blob_free(d);
}
......@@ -20,3 +20,65 @@ git_tree *resolve_commit_oid_to_tree(
git_object_free(obj);
return tree;
}
int diff_file_fn(
void *cb_data,
git_diff_delta *delta,
float progress)
{
diff_expects *e = cb_data;
(void)progress;
e->files++;
if (delta->old_attr == 0)
e->file_adds++;
else if (delta->new_attr == 0)
e->file_dels++;
else
e->file_mods++;
return 0;
}
int diff_hunk_fn(
void *cb_data,
git_diff_delta *delta,
git_diff_range *range,
const char *header,
size_t header_len)
{
diff_expects *e = cb_data;
(void)delta;
(void)header;
(void)header_len;
e->hunks++;
e->hunk_old_lines += range->old_lines;
e->hunk_new_lines += range->new_lines;
return 0;
}
int diff_line_fn(
void *cb_data,
git_diff_delta *delta,
char line_origin,
const char *content,
size_t content_len)
{
diff_expects *e = cb_data;
(void)delta;
(void)content;
(void)content_len;
e->lines++;
switch (line_origin) {
case GIT_DIFF_LINE_CONTEXT:
e->line_ctxt++;
break;
case GIT_DIFF_LINE_ADDITION:
e->line_adds++;
break;
case GIT_DIFF_LINE_DELETION:
e->line_dels++;
break;
default:
break;
}
return 0;
}
#include "fileops.h"
#include "git2/diff.h"
extern git_tree *resolve_commit_oid_to_tree(
git_repository *repo, const char *partial_oid);
typedef struct {
int files;
int file_adds;
int file_dels;
int file_mods;
int hunks;
int hunk_new_lines;
int hunk_old_lines;
int lines;
int line_ctxt;
int line_adds;
int line_dels;
} diff_expects;
extern int diff_file_fn(
void *cb_data,
git_diff_delta *delta,
float progress);
extern int diff_hunk_fn(
void *cb_data,
git_diff_delta *delta,
git_diff_range *range,
const char *header,
size_t header_len);
extern int diff_line_fn(
void *cb_data,
git_diff_delta *delta,
char line_origin,
const char *content,
size_t content_len);
#include "clar_libgit2.h"
#include "diff_helpers.h"
static git_repository *g_repo = NULL;
void test_diff_tree__initialize(void)
{
cl_fixture_sandbox("attr");
cl_git_pass(p_rename("attr/.gitted", "attr/.git"));
cl_git_pass(p_rename("attr/gitattributes", "attr/.gitattributes"));
cl_git_pass(git_repository_open(&g_repo, "attr/.git"));
}
void test_diff_tree__cleanup(void)
{
git_repository_free(g_repo);
g_repo = NULL;
cl_fixture_cleanup("attr");
}
static git_tree *resolve_commit_oid_to_tree(const char *partial_oid)
{
size_t len = strlen(partial_oid);
git_oid oid;
git_object *obj;
git_tree *tree;
if (git_oid_fromstrn(&oid, partial_oid, len) == 0)
git_object_lookup_prefix(&obj, g_repo, &oid, len, GIT_OBJ_ANY);
cl_assert(obj);
if (git_object_type(obj) == GIT_OBJ_TREE)
return (git_tree *)obj;
cl_assert(git_object_type(obj) == GIT_OBJ_COMMIT);
cl_git_pass(git_commit_tree(&tree, (git_commit *)obj));
git_object_free(obj);
return tree;
}
void test_diff_tree__0(void)
{
/* grabbed a couple of commit oids from the history of the attr repo */
const char *a_commit = "605812a";
const char *b_commit = "370fe9ec22";
const char *c_commit = "f5b0af1fb4f5c";
git_tree *a = resolve_commit_oid_to_tree(a_commit);
git_tree *b = resolve_commit_oid_to_tree(b_commit);
git_tree *c = resolve_commit_oid_to_tree(c_commit);
git_diff_options opts;
git_diff_list *diff = NULL;
diff_expects exp;
cl_assert(a);
cl_assert(b);
opts.context_lines = 1;
opts.interhunk_lines = 0;
opts.ignore_whitespace = 0;
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_tree_to_tree(g_repo, &opts, a, b, &diff));
cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert(exp.files == 5);
cl_assert(exp.file_adds == 2);
cl_assert(exp.file_dels == 1);
cl_assert(exp.file_mods == 2);
cl_assert(exp.hunks == 5);
cl_assert(exp.lines == 7 + 24 + 1 + 6 + 6);
cl_assert(exp.line_ctxt == 1);
cl_assert(exp.line_adds == 24 + 1 + 5 + 5);
cl_assert(exp.line_dels == 7 + 1);
git_diff_list_free(diff);
diff = NULL;
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_tree_to_tree(g_repo, &opts, c, b, &diff));
cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert(exp.files == 2);
cl_assert(exp.file_adds == 0);
cl_assert(exp.file_dels == 0);
cl_assert(exp.file_mods == 2);
cl_assert(exp.hunks == 2);
cl_assert(exp.lines == 8 + 15);
cl_assert(exp.line_ctxt == 1);
cl_assert(exp.line_adds == 1);
cl_assert(exp.line_dels == 7 + 14);
git_diff_list_free(diff);
git_tree_free(a);
git_tree_free(b);
git_tree_free(c);
}
#include "clay_libgit2.h"
#include "fileops.h"
#include "git2/diff.h"
static git_repository *g_repo = NULL;
void test_diff_blob__initialize(void)
{
cl_fixture_sandbox("attr");
cl_git_pass(p_rename("attr/.gitted", "attr/.git"));
cl_git_pass(p_rename("attr/gitattributes", "attr/.gitattributes"));
cl_git_pass(git_repository_open(&g_repo, "attr/.git"));
}
void test_diff_blob__cleanup(void)
{
git_repository_free(g_repo);
g_repo = NULL;
cl_fixture_cleanup("attr");
}
typedef struct {
int files;
int hunks;
int hunk_new_lines;
int hunk_old_lines;
int lines;
int line_ctxt;
int line_adds;
int line_dels;
} diff_expects;
static void log(const char *str, int n)
{
FILE *fp = fopen("/Users/rb/tmp/diff.log", "a");
if (n > 0)
fprintf(fp, "%.*s", n, str);
else
fputs(str, fp);
fclose(fp);
}
static int diff_file_fn(
void *cb_data,
const git_oid *old,
const char *old_path,
int old_mode,
const git_oid *new,
const char *new_path,
int new_mode)
{
diff_expects *e = cb_data;
e->files++;
log("-- file --\n", 0);
return 0;
}
static int diff_hunk_fn(
void *cb_data,
int old_start,
int old_lines,
int new_start,
int new_lines)
{
diff_expects *e = cb_data;
e->hunks++;
e->hunk_old_lines += old_lines;
e->hunk_new_lines += new_lines;
log("-- hunk --\n", 0);
return 0;
}
static int diff_line_fn(
void *cb_data,
int origin,
const char *content,
size_t content_len)
{
diff_expects *e = cb_data;
e->lines++;
switch (origin) {
case GIT_DIFF_LINE_CONTEXT:
log("[ ]", 3);
e->line_ctxt++;
break;
case GIT_DIFF_LINE_ADDITION:
log("[+]", 3);
e->line_adds++;
break;
case GIT_DIFF_LINE_DELETION:
log("[-]", 3);
e->line_dels++;
break;
default:
cl_assert("Unknown diff line origin" == 0);
}
log(content, content_len);
return 0;
}
void test_diff_blob__0(void)
{
int err;
git_blob *a, *b, *c, *d;
git_oid a_oid, b_oid, c_oid, d_oid;
git_diff_opts opts;
diff_expects exp;
/* tests/resources/attr/root_test1 */
cl_git_pass(git_oid_fromstrn(&a_oid, "45141a79", 8));
cl_git_pass(git_blob_lookup_prefix(&a, g_repo, &a_oid, 4));
/* tests/resources/attr/root_test2 */
cl_git_pass(git_oid_fromstrn(&b_oid, "4d713dc4", 8));
cl_git_pass(git_blob_lookup_prefix(&b, g_repo, &b_oid, 4));
/* tests/resources/attr/root_test3 */
cl_git_pass(git_oid_fromstrn(&c_oid, "c96bbb2c2557a832", 16));
cl_git_pass(git_blob_lookup_prefix(&c, g_repo, &c_oid, 8));
/* tests/resources/attr/root_test4.txt */
cl_git_pass(git_oid_fromstrn(&d_oid, "fe773770c5a6", 12));
cl_git_pass(git_blob_lookup_prefix(&d, g_repo, &d_oid, 6));
/* Doing the equivalent of a `diff -U 2` on these files */
opts.context_lines = 2;
opts.interhunk_lines = 0;
opts.ignore_whitespace = 0;
opts.file_cb = diff_file_fn;
opts.hunk_cb = diff_hunk_fn;
opts.line_cb = diff_line_fn;
opts.cb_data = &exp;
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_blobs(g_repo, a, b, &opts));
cl_assert(exp.files == 1);
cl_assert(exp.hunks == 1);
cl_assert(exp.lines == 6);
cl_assert(exp.line_ctxt == 1);
cl_assert(exp.line_adds == 5);
cl_assert(exp.line_dels == 0);
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_blobs(g_repo, b, c, &opts));
cl_assert(exp.files == 1);
cl_assert(exp.hunks == 1);
cl_assert(exp.lines == 15);
cl_assert(exp.line_ctxt == 3);
cl_assert(exp.line_adds == 9);
cl_assert(exp.line_dels == 3);
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_blobs(g_repo, a, c, &opts));
cl_assert(exp.files == 1);
cl_assert(exp.hunks == 1);
cl_assert(exp.lines == 13);
cl_assert(exp.line_ctxt == 0);
cl_assert(exp.line_adds == 12);
cl_assert(exp.line_dels == 1);
opts.context_lines = 2;
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_blobs(g_repo, c, d, &opts));
cl_assert(exp.files == 1);
cl_assert(exp.hunks == 2);
cl_assert(exp.lines == 16);
cl_assert(exp.line_ctxt == 6);
cl_assert(exp.line_adds == 6);
cl_assert(exp.line_dels == 4);
git_blob_free(a);
git_blob_free(b);
git_blob_free(c);
}
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