mirror of
https://git.proxmox.com/git/libgit2
synced 2025-08-09 15:48:53 +00:00
Continue implementation of git-diff
* Implemented git_diff_index_to_tree * Reworked git_diff_options structure to handle more options * Made most of the options in git_diff_options actually work * Reorganized code a bit to remove some redundancy * Added option parsing to examples/diff.c to test most options
This commit is contained in:
parent
5a2f097fdc
commit
a2e895be82
@ -3,12 +3,13 @@
|
|||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -g -I../include -I../src
|
CFLAGS = -g -I../include -I../src
|
||||||
LFLAGS = -L../build -lgit2 -lz
|
LFLAGS = -L../build -lgit2 -lz
|
||||||
|
APPS = general showindex diff
|
||||||
|
|
||||||
all: general showindex diff
|
all: $(APPS)
|
||||||
|
|
||||||
% : %.c
|
% : %.c
|
||||||
$(CC) -o $@ $(CFLAGS) $< $(LFLAGS)
|
$(CC) -o $@ $(CFLAGS) $< $(LFLAGS)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
$(RM) general showindex diff
|
$(RM) $(APPS)
|
||||||
$(RM) -r *.dSYM
|
$(RM) -r *.dSYM
|
||||||
|
117
examples/diff.c
117
examples/diff.c
@ -87,46 +87,123 @@ int printer(void *data, char usage, const char *line)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int check_uint16_param(const char *arg, const char *pattern, uint16_t *val)
|
||||||
|
{
|
||||||
|
size_t len = strlen(pattern);
|
||||||
|
uint16_t strval;
|
||||||
|
char *endptr = NULL;
|
||||||
|
if (strncmp(arg, pattern, len))
|
||||||
|
return 0;
|
||||||
|
strval = strtoul(arg + len, &endptr, 0);
|
||||||
|
if (endptr == arg)
|
||||||
|
return 0;
|
||||||
|
*val = strval;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int check_str_param(const char *arg, const char *pattern, char **val)
|
||||||
|
{
|
||||||
|
size_t len = strlen(pattern);
|
||||||
|
if (strncmp(arg, pattern, len))
|
||||||
|
return 0;
|
||||||
|
*val = (char *)(arg + len);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void usage(const char *message, const char *arg)
|
||||||
|
{
|
||||||
|
if (message && arg)
|
||||||
|
fprintf(stderr, "%s: %s\n", message, arg);
|
||||||
|
else if (message)
|
||||||
|
fprintf(stderr, "%s\n", message);
|
||||||
|
fprintf(stderr, "usage: diff <tree-oid> <tree-oid>\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
char path[GIT_PATH_MAX];
|
char path[GIT_PATH_MAX];
|
||||||
git_repository *repo = NULL;
|
git_repository *repo = NULL;
|
||||||
git_tree *a, *b;
|
git_tree *t1 = NULL, *t2 = NULL;
|
||||||
git_diff_options opts = {0};
|
git_diff_options opts = {0};
|
||||||
git_diff_list *diff;
|
git_diff_list *diff;
|
||||||
char *dir = ".";
|
int i, color = -1, compact = 0;
|
||||||
int color = -1;
|
char *a, *dir = ".", *treeish1 = NULL, *treeish2 = NULL;
|
||||||
|
|
||||||
if (argc != 3) {
|
/* parse arguments as copied from git-diff */
|
||||||
fprintf(stderr, "usage: diff <tree-oid> <tree-oid>\n");
|
|
||||||
exit(1);
|
for (i = 1; i < argc; ++i) {
|
||||||
|
a = argv[i];
|
||||||
|
|
||||||
|
if (a[0] != '-') {
|
||||||
|
if (treeish1 == NULL)
|
||||||
|
treeish1 = a;
|
||||||
|
else if (treeish2 == NULL)
|
||||||
|
treeish2 = a;
|
||||||
|
else
|
||||||
|
usage("Only one or two tree identifiers can be provided", NULL);
|
||||||
|
}
|
||||||
|
else if (!strcmp(a, "-p") || !strcmp(a, "-u") ||
|
||||||
|
!strcmp(a, "--patch"))
|
||||||
|
compact = 0;
|
||||||
|
else if (!strcmp(a, "--name-status"))
|
||||||
|
compact = 1;
|
||||||
|
else if (!strcmp(a, "--color"))
|
||||||
|
color = 0;
|
||||||
|
else if (!strcmp(a, "--no-color"))
|
||||||
|
color = -1;
|
||||||
|
else if (!strcmp(a, "-R"))
|
||||||
|
opts.flags |= GIT_DIFF_REVERSE;
|
||||||
|
else if (!strcmp(a, "-a") || !strcmp(a, "--text"))
|
||||||
|
opts.flags |= GIT_DIFF_FORCE_TEXT;
|
||||||
|
else if (!strcmp(a, "--ignore-space-at-eol"))
|
||||||
|
opts.flags |= GIT_DIFF_IGNORE_WHITESPACE_EOL;
|
||||||
|
else if (!strcmp(a, "-b") || !strcmp(a, "--ignore-space-change"))
|
||||||
|
opts.flags |= GIT_DIFF_IGNORE_WHITESPACE_CHANGE;
|
||||||
|
else if (!strcmp(a, "-w") || !strcmp(a, "--ignore-all-space"))
|
||||||
|
opts.flags |= GIT_DIFF_IGNORE_WHITESPACE;
|
||||||
|
else if (!check_uint16_param(a, "-U", &opts.context_lines) &&
|
||||||
|
!check_uint16_param(a, "--unified=", &opts.context_lines) &&
|
||||||
|
!check_uint16_param(a, "--inter-hunk-context=",
|
||||||
|
&opts.interhunk_lines) &&
|
||||||
|
!check_str_param(a, "--src-prefix=", &opts.src_prefix) &&
|
||||||
|
!check_str_param(a, "--dst-prefix=", &opts.dst_prefix))
|
||||||
|
usage("Unknown arg", a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!treeish1)
|
||||||
|
usage("Must provide at least one tree identifier (for now)", NULL);
|
||||||
|
|
||||||
|
/* open repo */
|
||||||
|
|
||||||
check(git_repository_discover(path, sizeof(path), dir, 0, "/"),
|
check(git_repository_discover(path, sizeof(path), dir, 0, "/"),
|
||||||
"Could not discover repository");
|
"Could not discover repository");
|
||||||
check(git_repository_open(&repo, path),
|
check(git_repository_open(&repo, path),
|
||||||
"Could not open repository");
|
"Could not open repository");
|
||||||
|
|
||||||
check(resolve_to_tree(repo, argv[1], &a), "Looking up first tree");
|
check(resolve_to_tree(repo, treeish1, &t1), "Looking up first tree");
|
||||||
check(resolve_to_tree(repo, argv[2], &b), "Looking up second tree");
|
if (treeish2)
|
||||||
|
check(resolve_to_tree(repo, treeish2, &t2), "Looking up second tree");
|
||||||
|
|
||||||
check(git_diff_tree_to_tree(repo, &opts, a, b, &diff), "Generating diff");
|
if (!treeish2)
|
||||||
|
check(git_diff_index_to_tree(repo, &opts, t1, &diff), "Generating diff");
|
||||||
|
else
|
||||||
|
check(git_diff_tree_to_tree(repo, &opts, t1, t2, &diff), "Generating diff");
|
||||||
|
|
||||||
fputs(colors[0], stdout);
|
if (color >= 0)
|
||||||
|
fputs(colors[0], stdout);
|
||||||
|
|
||||||
check(git_diff_print_compact(diff, &color, printer), "Displaying diff summary");
|
if (compact)
|
||||||
|
check(git_diff_print_compact(diff, &color, printer), "Displaying diff summary");
|
||||||
|
else
|
||||||
|
check(git_diff_print_patch(diff, &color, printer), "Displaying diff");
|
||||||
|
|
||||||
fprintf(stdout, "--\n");
|
if (color >= 0)
|
||||||
|
fputs(colors[0], stdout);
|
||||||
color = 0;
|
|
||||||
|
|
||||||
check(git_diff_print_patch(diff, &color, printer), "Displaying diff");
|
|
||||||
|
|
||||||
fputs(colors[0], stdout);
|
|
||||||
|
|
||||||
git_diff_list_free(diff);
|
git_diff_list_free(diff);
|
||||||
git_tree_free(a);
|
git_tree_free(t1);
|
||||||
git_tree_free(b);
|
git_tree_free(t2);
|
||||||
git_repository_free(repo);
|
git_repository_free(repo);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -29,17 +29,33 @@
|
|||||||
*/
|
*/
|
||||||
GIT_BEGIN_DECL
|
GIT_BEGIN_DECL
|
||||||
|
|
||||||
|
enum {
|
||||||
|
GIT_DIFF_NORMAL = 0,
|
||||||
|
GIT_DIFF_REVERSE = (1 << 0),
|
||||||
|
GIT_DIFF_FORCE_TEXT = (1 << 1),
|
||||||
|
GIT_DIFF_IGNORE_WHITESPACE = (1 << 2),
|
||||||
|
GIT_DIFF_IGNORE_WHITESPACE_CHANGE = (1 << 3),
|
||||||
|
GIT_DIFF_IGNORE_WHITESPACE_EOL = (1 << 4),
|
||||||
|
GIT_DIFF_IGNORE_SUBMODULES = (1 << 5),
|
||||||
|
GIT_DIFF_PATIENCE = (1 << 6)
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Structure describing options about how the diff should be executed.
|
* Structure describing options about how the diff should be executed.
|
||||||
*
|
*
|
||||||
|
* Setting all values of the structure to zero will yield the default
|
||||||
|
* values. Similarly, passing NULL for the options structure will
|
||||||
|
* give the defaults. The default values are marked below.
|
||||||
|
*
|
||||||
* @todo Most of the parameters here are not actually supported at this time.
|
* @todo Most of the parameters here are not actually supported at this time.
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int context_lines;
|
uint32_t flags; /**< defaults to GIT_DIFF_NORMAL */
|
||||||
int interhunk_lines;
|
uint16_t context_lines; /**< defaults to 3 */
|
||||||
int ignore_whitespace;
|
uint16_t interhunk_lines; /**< defaults to 3 */
|
||||||
int force_text; /**< generate text diffs even for binaries */
|
char *src_prefix; /**< defaults to "a" */
|
||||||
git_strarray pathspec;
|
char *dst_prefix; /**< defaults to "b" */
|
||||||
|
git_strarray pathspec; /**< defaults to show all paths */
|
||||||
} git_diff_options;
|
} git_diff_options;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -158,7 +174,7 @@ typedef int (*git_diff_output_fn)(
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_diff_tree_to_tree(
|
GIT_EXTERN(int) git_diff_tree_to_tree(
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
const git_diff_options *opts,
|
const git_diff_options *opts, /**< can be NULL for defaults */
|
||||||
git_tree *old,
|
git_tree *old,
|
||||||
git_tree *new,
|
git_tree *new,
|
||||||
git_diff_list **diff);
|
git_diff_list **diff);
|
||||||
@ -169,7 +185,7 @@ GIT_EXTERN(int) git_diff_tree_to_tree(
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_diff_index_to_tree(
|
GIT_EXTERN(int) git_diff_index_to_tree(
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
const git_diff_options *opts,
|
const git_diff_options *opts, /**< can be NULL for defaults */
|
||||||
git_tree *old,
|
git_tree *old,
|
||||||
git_diff_list **diff);
|
git_diff_list **diff);
|
||||||
|
|
||||||
@ -179,7 +195,7 @@ GIT_EXTERN(int) git_diff_index_to_tree(
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_diff_workdir_to_tree(
|
GIT_EXTERN(int) git_diff_workdir_to_tree(
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
const git_diff_options *opts,
|
const git_diff_options *opts, /**< can be NULL for defaults */
|
||||||
git_tree *old,
|
git_tree *old,
|
||||||
git_diff_list **diff);
|
git_diff_list **diff);
|
||||||
|
|
||||||
@ -189,7 +205,7 @@ GIT_EXTERN(int) git_diff_workdir_to_tree(
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_diff_workdir_to_index(
|
GIT_EXTERN(int) git_diff_workdir_to_index(
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
const git_diff_options *opts,
|
const git_diff_options *opts, /**< can be NULL for defaults */
|
||||||
git_diff_list **diff);
|
git_diff_list **diff);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
494
src/diff.c
494
src/diff.c
@ -12,33 +12,6 @@
|
|||||||
#include "blob.h"
|
#include "blob.h"
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
static git_diff_delta *file_delta_new(
|
|
||||||
git_diff_list *diff,
|
|
||||||
const git_tree_diff_data *tdiff)
|
|
||||||
{
|
|
||||||
git_diff_delta *delta = git__calloc(1, sizeof(git_diff_delta));
|
|
||||||
|
|
||||||
if (!delta) {
|
|
||||||
git__rethrow(GIT_ENOMEM, "Could not allocate diff record");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* copy shared fields */
|
|
||||||
delta->status = tdiff->status;
|
|
||||||
delta->old_attr = tdiff->old_attr;
|
|
||||||
delta->new_attr = tdiff->new_attr;
|
|
||||||
delta->old_oid = tdiff->old_oid;
|
|
||||||
delta->new_oid = tdiff->new_oid;
|
|
||||||
delta->path = git__strdup(diff->pfx.ptr);
|
|
||||||
if (delta->path == NULL) {
|
|
||||||
git__free(delta);
|
|
||||||
git__rethrow(GIT_ENOMEM, "Could not allocate diff record path");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return delta;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void file_delta_free(git_diff_delta *delta)
|
static void file_delta_free(git_diff_delta *delta)
|
||||||
{
|
{
|
||||||
if (!delta)
|
if (!delta)
|
||||||
@ -55,77 +28,119 @@ static void file_delta_free(git_diff_delta *delta)
|
|||||||
git__free(delta);
|
git__free(delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tree_add_cb(const char *root, git_tree_entry *entry, void *data)
|
static int file_delta_new__from_one(
|
||||||
|
git_diff_list *diff,
|
||||||
|
git_status_t status,
|
||||||
|
unsigned int attr,
|
||||||
|
const git_oid *oid,
|
||||||
|
const char *path)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
git_diff_delta *delta = git__calloc(1, sizeof(git_diff_delta));
|
||||||
|
|
||||||
|
/* This fn is just for single-sided diffs */
|
||||||
|
assert(status == GIT_STATUS_ADDED || status == GIT_STATUS_DELETED);
|
||||||
|
|
||||||
|
if (!delta)
|
||||||
|
return git__rethrow(GIT_ENOMEM, "Could not allocate diff record");
|
||||||
|
|
||||||
|
if ((delta->path = git__strdup(path)) == NULL) {
|
||||||
|
git__free(delta);
|
||||||
|
return git__rethrow(GIT_ENOMEM, "Could not allocate diff record path");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diff->opts.flags & GIT_DIFF_REVERSE)
|
||||||
|
status = (status == GIT_STATUS_ADDED) ?
|
||||||
|
GIT_STATUS_DELETED : GIT_STATUS_ADDED;
|
||||||
|
|
||||||
|
delta->status = status;
|
||||||
|
|
||||||
|
if (status == GIT_STATUS_ADDED) {
|
||||||
|
delta->new_attr = attr;
|
||||||
|
git_oid_cpy(&delta->new_oid, oid);
|
||||||
|
} else {
|
||||||
|
delta->old_attr = attr;
|
||||||
|
git_oid_cpy(&delta->old_oid, oid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((error = git_vector_insert(&diff->files, delta)) < GIT_SUCCESS)
|
||||||
|
file_delta_free(delta);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int file_delta_new__from_tree_diff(
|
||||||
|
git_diff_list *diff,
|
||||||
|
const git_tree_diff_data *tdiff)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
git_diff_delta *delta = git__calloc(1, sizeof(git_diff_delta));
|
||||||
|
|
||||||
|
if (!delta)
|
||||||
|
return git__rethrow(GIT_ENOMEM, "Could not allocate diff record");
|
||||||
|
|
||||||
|
if ((diff->opts.flags & GIT_DIFF_REVERSE) == 0) {
|
||||||
|
delta->status = tdiff->status;
|
||||||
|
delta->old_attr = tdiff->old_attr;
|
||||||
|
delta->new_attr = tdiff->new_attr;
|
||||||
|
delta->old_oid = tdiff->old_oid;
|
||||||
|
delta->new_oid = tdiff->new_oid;
|
||||||
|
} else {
|
||||||
|
/* reverse the polarity of the neutron flow */
|
||||||
|
switch (tdiff->status) {
|
||||||
|
case GIT_STATUS_ADDED: delta->status = GIT_STATUS_DELETED; break;
|
||||||
|
case GIT_STATUS_DELETED: delta->status = GIT_STATUS_ADDED; break;
|
||||||
|
default: delta->status = tdiff->status;
|
||||||
|
}
|
||||||
|
delta->old_attr = tdiff->new_attr;
|
||||||
|
delta->new_attr = tdiff->old_attr;
|
||||||
|
delta->old_oid = tdiff->new_oid;
|
||||||
|
delta->new_oid = tdiff->old_oid;
|
||||||
|
}
|
||||||
|
|
||||||
|
delta->path = git__strdup(diff->pfx.ptr);
|
||||||
|
if (delta->path == NULL) {
|
||||||
|
git__free(delta);
|
||||||
|
return git__rethrow(GIT_ENOMEM, "Could not allocate diff record path");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((error = git_vector_insert(&diff->files, delta)) < GIT_SUCCESS)
|
||||||
|
file_delta_free(delta);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tree_walk_cb(const char *root, git_tree_entry *entry, void *data)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
git_diff_list *diff = data;
|
git_diff_list *diff = data;
|
||||||
ssize_t pfx_len = diff->pfx.size;
|
ssize_t pfx_len = diff->pfx.size;
|
||||||
git_tree_diff_data tdiff;
|
|
||||||
git_diff_delta *delta;
|
|
||||||
|
|
||||||
memset(&tdiff, 0, sizeof(tdiff));
|
if (S_ISDIR(git_tree_entry_attributes(entry)))
|
||||||
tdiff.new_attr = git_tree_entry_attributes(entry);
|
|
||||||
if (S_ISDIR(tdiff.new_attr))
|
|
||||||
return GIT_SUCCESS;
|
return GIT_SUCCESS;
|
||||||
|
|
||||||
git_oid_cpy(&tdiff.new_oid, git_tree_entry_id(entry));
|
/* join pfx, root, and entry->filename into one */
|
||||||
tdiff.status = GIT_STATUS_ADDED;
|
|
||||||
tdiff.path = git_tree_entry_name(entry);
|
|
||||||
|
|
||||||
if ((error = git_buf_joinpath(&diff->pfx, diff->pfx.ptr, root)) ||
|
if ((error = git_buf_joinpath(&diff->pfx, diff->pfx.ptr, root)) ||
|
||||||
(error = git_buf_joinpath(&diff->pfx, diff->pfx.ptr, tdiff.path)))
|
(error = git_buf_joinpath(
|
||||||
|
&diff->pfx, diff->pfx.ptr, git_tree_entry_name(entry))))
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
delta = file_delta_new(diff, &tdiff);
|
error = file_delta_new__from_one(
|
||||||
if (delta == NULL)
|
diff, diff->mode, git_tree_entry_attributes(entry),
|
||||||
error = GIT_ENOMEM;
|
git_tree_entry_id(entry), diff->pfx.ptr);
|
||||||
else if ((error = git_vector_insert(&diff->files, delta)) < GIT_SUCCESS)
|
|
||||||
file_delta_free(delta);
|
|
||||||
|
|
||||||
git_buf_truncate(&diff->pfx, pfx_len);
|
git_buf_truncate(&diff->pfx, pfx_len);
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tree_del_cb(const char *root, git_tree_entry *entry, void *data)
|
static int tree_diff_cb(const git_tree_diff_data *tdiff, void *data)
|
||||||
{
|
|
||||||
int error;
|
|
||||||
git_diff_list *diff = data;
|
|
||||||
ssize_t pfx_len = diff->pfx.size;
|
|
||||||
git_tree_diff_data tdiff;
|
|
||||||
git_diff_delta *delta;
|
|
||||||
|
|
||||||
memset(&tdiff, 0, sizeof(tdiff));
|
|
||||||
tdiff.old_attr = git_tree_entry_attributes(entry);
|
|
||||||
if (S_ISDIR(tdiff.old_attr))
|
|
||||||
return GIT_SUCCESS;
|
|
||||||
|
|
||||||
git_oid_cpy(&tdiff.old_oid, git_tree_entry_id(entry));
|
|
||||||
tdiff.status = GIT_STATUS_DELETED;
|
|
||||||
tdiff.path = git_tree_entry_name(entry);
|
|
||||||
|
|
||||||
if ((error = git_buf_joinpath(&diff->pfx, diff->pfx.ptr, root)) ||
|
|
||||||
(error = git_buf_joinpath(&diff->pfx, diff->pfx.ptr, tdiff.path)))
|
|
||||||
return error;
|
|
||||||
|
|
||||||
delta = file_delta_new(diff, &tdiff);
|
|
||||||
if (delta == NULL)
|
|
||||||
error = GIT_ENOMEM;
|
|
||||||
else if ((error = git_vector_insert(&diff->files, delta)) < GIT_SUCCESS)
|
|
||||||
file_delta_free(delta);
|
|
||||||
|
|
||||||
git_buf_truncate(&diff->pfx, pfx_len);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int tree_diff_cb(const git_tree_diff_data *ptr, void *data)
|
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
git_diff_list *diff = data;
|
git_diff_list *diff = data;
|
||||||
ssize_t pfx_len = diff->pfx.size;
|
ssize_t pfx_len = diff->pfx.size;
|
||||||
|
|
||||||
error = git_buf_joinpath(&diff->pfx, diff->pfx.ptr, ptr->path);
|
error = git_buf_joinpath(&diff->pfx, diff->pfx.ptr, tdiff->path);
|
||||||
if (error < GIT_SUCCESS)
|
if (error < GIT_SUCCESS)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@ -138,56 +153,83 @@ static int tree_diff_cb(const git_tree_diff_data *ptr, void *data)
|
|||||||
* and a deletion (thank you, git_tree_diff!)
|
* and a deletion (thank you, git_tree_diff!)
|
||||||
* otherwise, this is a blob-to-blob diff
|
* otherwise, this is a blob-to-blob diff
|
||||||
*/
|
*/
|
||||||
if (S_ISDIR(ptr->old_attr) && S_ISDIR(ptr->new_attr)) {
|
if (S_ISDIR(tdiff->old_attr) && S_ISDIR(tdiff->new_attr)) {
|
||||||
git_tree *old = NULL, *new = NULL;
|
git_tree *old = NULL, *new = NULL;
|
||||||
|
|
||||||
if (!(error = git_tree_lookup(&old, diff->repo, &ptr->old_oid)) &&
|
if (!(error = git_tree_lookup(&old, diff->repo, &tdiff->old_oid)) &&
|
||||||
!(error = git_tree_lookup(&new, diff->repo, &ptr->new_oid)))
|
!(error = git_tree_lookup(&new, diff->repo, &tdiff->new_oid)))
|
||||||
{
|
|
||||||
error = git_tree_diff(old, new, tree_diff_cb, diff);
|
error = git_tree_diff(old, new, tree_diff_cb, diff);
|
||||||
}
|
|
||||||
|
|
||||||
git_tree_free(old);
|
git_tree_free(old);
|
||||||
git_tree_free(new);
|
git_tree_free(new);
|
||||||
} else if (S_ISDIR(ptr->old_attr) && ptr->new_attr == 0) {
|
} else if (S_ISDIR(tdiff->old_attr) || S_ISDIR(tdiff->new_attr)) {
|
||||||
/* deleted a whole tree */
|
git_tree *tree = NULL;
|
||||||
git_tree *old = NULL;
|
int added_dir = S_ISDIR(tdiff->new_attr);
|
||||||
if (!(error = git_tree_lookup(&old, diff->repo, &ptr->old_oid))) {
|
const git_oid *oid = added_dir ? &tdiff->new_oid : &tdiff->old_oid;
|
||||||
error = git_tree_walk(old, tree_del_cb, GIT_TREEWALK_POST, diff);
|
diff->mode = added_dir ? GIT_STATUS_ADDED : GIT_STATUS_DELETED;
|
||||||
git_tree_free(old);
|
|
||||||
}
|
if (!(error = git_tree_lookup(&tree, diff->repo, oid)))
|
||||||
} else if (S_ISDIR(ptr->new_attr) && ptr->old_attr == 0) {
|
error = git_tree_walk(tree, tree_walk_cb, GIT_TREEWALK_POST, diff);
|
||||||
/* added a whole tree */
|
git_tree_free(tree);
|
||||||
git_tree *new = NULL;
|
} else
|
||||||
if (!(error = git_tree_lookup(&new, diff->repo, &ptr->new_oid))) {
|
error = file_delta_new__from_tree_diff(diff, tdiff);
|
||||||
error = git_tree_walk(new, tree_add_cb, GIT_TREEWALK_POST, diff);
|
|
||||||
git_tree_free(new);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
git_diff_delta *delta = file_delta_new(diff, ptr);
|
|
||||||
if (delta == NULL)
|
|
||||||
error = GIT_ENOMEM;
|
|
||||||
else if ((error = git_vector_insert(&diff->files, delta)) < GIT_SUCCESS)
|
|
||||||
file_delta_free(delta);
|
|
||||||
}
|
|
||||||
|
|
||||||
git_buf_truncate(&diff->pfx, pfx_len);
|
git_buf_truncate(&diff->pfx, pfx_len);
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *git_diff_src_prefix_default = "a/";
|
||||||
|
static char *git_diff_dst_prefix_default = "b/";
|
||||||
|
#define PREFIX_IS_DEFAULT(A) \
|
||||||
|
((A) == git_diff_src_prefix_default || (A) == git_diff_dst_prefix_default)
|
||||||
|
|
||||||
|
static char *copy_prefix(const char *prefix)
|
||||||
|
{
|
||||||
|
size_t len = strlen(prefix);
|
||||||
|
char *str = git__malloc(len + 2);
|
||||||
|
if (str != NULL) {
|
||||||
|
memcpy(str, prefix, len + 1);
|
||||||
|
/* append '/' at end if needed */
|
||||||
|
if (len > 0 && str[len - 1] != '/') {
|
||||||
|
str[len] = '/';
|
||||||
|
str[len + 1] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
static git_diff_list *git_diff_list_alloc(
|
static git_diff_list *git_diff_list_alloc(
|
||||||
git_repository *repo, const git_diff_options *opts)
|
git_repository *repo, const git_diff_options *opts)
|
||||||
{
|
{
|
||||||
git_diff_list *diff = git__calloc(1, sizeof(git_diff_list));
|
git_diff_list *diff = git__calloc(1, sizeof(git_diff_list));
|
||||||
if (diff != NULL) {
|
if (diff == NULL)
|
||||||
if (opts != NULL) {
|
return NULL;
|
||||||
memcpy(&diff->opts, opts, sizeof(git_diff_options));
|
|
||||||
/* do something safer with the pathspec strarray */
|
diff->repo = repo;
|
||||||
}
|
git_buf_init(&diff->pfx, 0);
|
||||||
diff->repo = repo;
|
|
||||||
git_buf_init(&diff->pfx, 0);
|
if (opts == NULL)
|
||||||
|
return diff;
|
||||||
|
|
||||||
|
memcpy(&diff->opts, opts, sizeof(git_diff_options));
|
||||||
|
|
||||||
|
diff->opts.src_prefix = (opts->src_prefix == NULL) ?
|
||||||
|
git_diff_src_prefix_default : copy_prefix(opts->src_prefix);
|
||||||
|
diff->opts.dst_prefix = (opts->dst_prefix == NULL) ?
|
||||||
|
git_diff_dst_prefix_default : copy_prefix(opts->dst_prefix);
|
||||||
|
if (!diff->opts.src_prefix || !diff->opts.dst_prefix) {
|
||||||
|
git__free(diff);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (diff->opts.flags & GIT_DIFF_REVERSE) {
|
||||||
|
char *swap = diff->opts.src_prefix;
|
||||||
|
diff->opts.src_prefix = diff->opts.dst_prefix;
|
||||||
|
diff->opts.dst_prefix = swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* do something safe with the pathspec strarray */
|
||||||
|
|
||||||
return diff;
|
return diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,6 +247,14 @@ void git_diff_list_free(git_diff_list *diff)
|
|||||||
diff->files.contents[i] = NULL;
|
diff->files.contents[i] = NULL;
|
||||||
}
|
}
|
||||||
git_vector_free(&diff->files);
|
git_vector_free(&diff->files);
|
||||||
|
if (!PREFIX_IS_DEFAULT(diff->opts.src_prefix)) {
|
||||||
|
git__free(diff->opts.src_prefix);
|
||||||
|
diff->opts.src_prefix = NULL;
|
||||||
|
}
|
||||||
|
if (!PREFIX_IS_DEFAULT(diff->opts.dst_prefix)) {
|
||||||
|
git__free(diff->opts.dst_prefix);
|
||||||
|
diff->opts.dst_prefix = NULL;
|
||||||
|
}
|
||||||
git__free(diff);
|
git__free(diff);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,6 +279,111 @@ int git_diff_tree_to_tree(
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
git_diff_list *diff;
|
||||||
|
git_index *index;
|
||||||
|
unsigned int index_pos;
|
||||||
|
} index_to_tree_info;
|
||||||
|
|
||||||
|
static int add_new_index_deltas(
|
||||||
|
index_to_tree_info *info,
|
||||||
|
const char *stop_path)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
git_index_entry *idx_entry = git_index_get(info->index, info->index_pos);
|
||||||
|
|
||||||
|
while (idx_entry != NULL &&
|
||||||
|
(stop_path == NULL || strcmp(idx_entry->path, stop_path) < 0))
|
||||||
|
{
|
||||||
|
error = file_delta_new__from_one(
|
||||||
|
info->diff, GIT_STATUS_ADDED, idx_entry->mode,
|
||||||
|
&idx_entry->oid, idx_entry->path);
|
||||||
|
if (error < GIT_SUCCESS)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
idx_entry = git_index_get(info->index, ++info->index_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_index_to_tree_cb(const char *root, git_tree_entry *tree_entry, void *data)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
index_to_tree_info *info = data;
|
||||||
|
git_index_entry *idx_entry;
|
||||||
|
|
||||||
|
/* TODO: submodule support for GIT_OBJ_COMMITs in tree */
|
||||||
|
if (git_tree_entry_type(tree_entry) != GIT_OBJ_BLOB)
|
||||||
|
return GIT_SUCCESS;
|
||||||
|
|
||||||
|
error = git_buf_joinpath(&info->diff->pfx, root, git_tree_entry_name(tree_entry));
|
||||||
|
if (error < GIT_SUCCESS)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
/* create add deltas for index entries that are not in the tree */
|
||||||
|
error = add_new_index_deltas(info, info->diff->pfx.ptr);
|
||||||
|
if (error < GIT_SUCCESS)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
/* create delete delta for tree entries that are not in the index */
|
||||||
|
idx_entry = git_index_get(info->index, info->index_pos);
|
||||||
|
if (idx_entry == NULL || strcmp(idx_entry->path, info->diff->pfx.ptr) > 0) {
|
||||||
|
return file_delta_new__from_one(
|
||||||
|
info->diff, GIT_STATUS_DELETED, git_tree_entry_attributes(tree_entry),
|
||||||
|
git_tree_entry_id(tree_entry), info->diff->pfx.ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* create modified delta for non-matching tree & index entries */
|
||||||
|
info->index_pos++;
|
||||||
|
|
||||||
|
if (git_oid_cmp(&idx_entry->oid, git_tree_entry_id(tree_entry)) ||
|
||||||
|
idx_entry->mode != git_tree_entry_attributes(tree_entry))
|
||||||
|
{
|
||||||
|
git_tree_diff_data tdiff;
|
||||||
|
tdiff.old_attr = git_tree_entry_attributes(tree_entry);
|
||||||
|
tdiff.new_attr = idx_entry->mode;
|
||||||
|
tdiff.status = GIT_STATUS_MODIFIED;
|
||||||
|
tdiff.path = idx_entry->path;
|
||||||
|
git_oid_cpy(&tdiff.old_oid, git_tree_entry_id(tree_entry));
|
||||||
|
git_oid_cpy(&tdiff.new_oid, &idx_entry->oid);
|
||||||
|
|
||||||
|
error = file_delta_new__from_tree_diff(info->diff, &tdiff);
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_diff_index_to_tree(
|
||||||
|
git_repository *repo,
|
||||||
|
const git_diff_options *opts,
|
||||||
|
git_tree *old,
|
||||||
|
git_diff_list **diff_ptr)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
index_to_tree_info info = {0};
|
||||||
|
|
||||||
|
if ((info.diff = git_diff_list_alloc(repo, opts)) == NULL)
|
||||||
|
return GIT_ENOMEM;
|
||||||
|
|
||||||
|
if ((error = git_repository_index(&info.index, repo)) == GIT_SUCCESS) {
|
||||||
|
error = git_tree_walk(
|
||||||
|
old, diff_index_to_tree_cb, GIT_TREEWALK_POST, &info);
|
||||||
|
if (error == GIT_SUCCESS)
|
||||||
|
error = add_new_index_deltas(&info, NULL);
|
||||||
|
git_index_free(info.index);
|
||||||
|
}
|
||||||
|
git_buf_free(&info.diff->pfx);
|
||||||
|
|
||||||
|
if (error != GIT_SUCCESS)
|
||||||
|
git_diff_list_free(info.diff);
|
||||||
|
else
|
||||||
|
*diff_ptr = info.diff;
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
git_diff_list *diff;
|
git_diff_list *diff;
|
||||||
void *cb_data;
|
void *cb_data;
|
||||||
@ -331,6 +486,26 @@ static int set_file_is_binary(
|
|||||||
return GIT_SUCCESS;
|
return GIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void setup_xdiff_config(git_diff_options *opts, xdemitconf_t *cfg)
|
||||||
|
{
|
||||||
|
memset(cfg, 0, sizeof(xdemitconf_t));
|
||||||
|
|
||||||
|
cfg->ctxlen =
|
||||||
|
(!opts || !opts->context_lines) ? 3 : opts->context_lines;
|
||||||
|
cfg->interhunkctxlen =
|
||||||
|
(!opts || !opts->interhunk_lines) ? 3 : opts->interhunk_lines;
|
||||||
|
|
||||||
|
if (!opts)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (opts->flags & GIT_DIFF_IGNORE_WHITESPACE)
|
||||||
|
cfg->flags |= XDF_WHITESPACE_FLAGS;
|
||||||
|
if (opts->flags & GIT_DIFF_IGNORE_WHITESPACE_CHANGE)
|
||||||
|
cfg->flags |= XDF_IGNORE_WHITESPACE_CHANGE;
|
||||||
|
if (opts->flags & GIT_DIFF_IGNORE_WHITESPACE_EOL)
|
||||||
|
cfg->flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
|
||||||
|
}
|
||||||
|
|
||||||
int git_diff_foreach(
|
int git_diff_foreach(
|
||||||
git_diff_list *diff,
|
git_diff_list *diff,
|
||||||
void *data,
|
void *data,
|
||||||
@ -341,17 +516,23 @@ int git_diff_foreach(
|
|||||||
int error = GIT_SUCCESS;
|
int error = GIT_SUCCESS;
|
||||||
diff_info di;
|
diff_info di;
|
||||||
git_diff_delta *delta;
|
git_diff_delta *delta;
|
||||||
|
xpparam_t xdiff_params;
|
||||||
|
xdemitconf_t xdiff_config;
|
||||||
|
xdemitcb_t xdiff_callback;
|
||||||
|
|
||||||
di.diff = diff;
|
di.diff = diff;
|
||||||
di.cb_data = data;
|
di.cb_data = data;
|
||||||
di.hunk_cb = hunk_cb;
|
di.hunk_cb = hunk_cb;
|
||||||
di.line_cb = line_cb;
|
di.line_cb = line_cb;
|
||||||
|
|
||||||
|
memset(&xdiff_params, 0, sizeof(xdiff_params));
|
||||||
|
setup_xdiff_config(&diff->opts, &xdiff_config);
|
||||||
|
memset(&xdiff_callback, 0, sizeof(xdiff_callback));
|
||||||
|
xdiff_callback.outf = diff_output_cb;
|
||||||
|
xdiff_callback.priv = &di;
|
||||||
|
|
||||||
git_vector_foreach(&diff->files, di.index, delta) {
|
git_vector_foreach(&diff->files, di.index, delta) {
|
||||||
mmfile_t old, new;
|
mmfile_t old_data, new_data;
|
||||||
xpparam_t params;
|
|
||||||
xdemitconf_t cfg;
|
|
||||||
xdemitcb_t callback;
|
|
||||||
|
|
||||||
/* map files */
|
/* map files */
|
||||||
if (hunk_cb || line_cb) {
|
if (hunk_cb || line_cb) {
|
||||||
@ -365,12 +546,12 @@ int git_diff_foreach(
|
|||||||
{
|
{
|
||||||
error = git_blob_lookup(
|
error = git_blob_lookup(
|
||||||
&delta->old_blob, diff->repo, &delta->old_oid);
|
&delta->old_blob, diff->repo, &delta->old_oid);
|
||||||
old.ptr = (char *)git_blob_rawcontent(delta->old_blob);
|
old_data.ptr = (char *)git_blob_rawcontent(delta->old_blob);
|
||||||
old.size = git_blob_rawsize(delta->old_blob);
|
old_data.size = git_blob_rawsize(delta->old_blob);
|
||||||
} else {
|
} else {
|
||||||
delta->old_blob = NULL;
|
delta->old_blob = NULL;
|
||||||
old.ptr = "";
|
old_data.ptr = "";
|
||||||
old.size = 0;
|
old_data.size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (delta->status == GIT_STATUS_ADDED ||
|
if (delta->status == GIT_STATUS_ADDED ||
|
||||||
@ -378,21 +559,25 @@ int git_diff_foreach(
|
|||||||
{
|
{
|
||||||
error = git_blob_lookup(
|
error = git_blob_lookup(
|
||||||
&delta->new_blob, diff->repo, &delta->new_oid);
|
&delta->new_blob, diff->repo, &delta->new_oid);
|
||||||
new.ptr = (char *)git_blob_rawcontent(delta->new_blob);
|
new_data.ptr = (char *)git_blob_rawcontent(delta->new_blob);
|
||||||
new.size = git_blob_rawsize(delta->new_blob);
|
new_data.size = git_blob_rawsize(delta->new_blob);
|
||||||
} else {
|
} else {
|
||||||
delta->new_blob = NULL;
|
delta->new_blob = NULL;
|
||||||
new.ptr = "";
|
new_data.ptr = "";
|
||||||
new.size = 0;
|
new_data.size = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (diff->opts.force_text)
|
if (diff->opts.flags & GIT_DIFF_FORCE_TEXT)
|
||||||
delta->binary = 0;
|
delta->binary = 0;
|
||||||
else if ((error = set_file_is_binary(
|
else if ((error = set_file_is_binary(
|
||||||
diff->repo, delta, &old, &new)) < GIT_SUCCESS)
|
diff->repo, delta, &old_data, &new_data)) < GIT_SUCCESS)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
/* TODO: if ignore_whitespace is set, then we *must* do text
|
||||||
|
* diffs to tell if a file has really been changed.
|
||||||
|
*/
|
||||||
|
|
||||||
if (file_cb != NULL) {
|
if (file_cb != NULL) {
|
||||||
error = file_cb(data, delta, (float)di.index / diff->files.length);
|
error = file_cb(data, delta, (float)di.index / diff->files.length);
|
||||||
if (error != GIT_SUCCESS)
|
if (error != GIT_SUCCESS)
|
||||||
@ -411,19 +596,8 @@ int git_diff_foreach(
|
|||||||
|
|
||||||
di.delta = delta;
|
di.delta = delta;
|
||||||
|
|
||||||
memset(¶ms, 0, sizeof(params));
|
xdl_diff(&old_data, &new_data,
|
||||||
|
&xdiff_params, &xdiff_config, &xdiff_callback);
|
||||||
memset(&cfg, 0, sizeof(cfg));
|
|
||||||
cfg.ctxlen = diff->opts.context_lines || 3;
|
|
||||||
cfg.interhunkctxlen = diff->opts.interhunk_lines || 3;
|
|
||||||
if (diff->opts.ignore_whitespace)
|
|
||||||
cfg.flags |= XDF_WHITESPACE_FLAGS;
|
|
||||||
|
|
||||||
memset(&callback, 0, sizeof(callback));
|
|
||||||
callback.outf = diff_output_cb;
|
|
||||||
callback.priv = &di;
|
|
||||||
|
|
||||||
xdl_diff(&old, &new, ¶ms, &cfg, &callback);
|
|
||||||
|
|
||||||
git_blob_free(delta->old_blob);
|
git_blob_free(delta->old_blob);
|
||||||
delta->old_blob = NULL;
|
delta->old_blob = NULL;
|
||||||
@ -436,6 +610,7 @@ int git_diff_foreach(
|
|||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
git_diff_list *diff;
|
||||||
git_diff_output_fn print_cb;
|
git_diff_output_fn print_cb;
|
||||||
void *cb_data;
|
void *cb_data;
|
||||||
git_buf *buf;
|
git_buf *buf;
|
||||||
@ -483,7 +658,8 @@ static int print_compact(void *data, git_diff_delta *delta, float progress)
|
|||||||
if (delta->new_path != NULL)
|
if (delta->new_path != NULL)
|
||||||
git_buf_printf(pi->buf, "%c\t%s%c -> %s%c\n", code,
|
git_buf_printf(pi->buf, "%c\t%s%c -> %s%c\n", code,
|
||||||
delta->path, old_suffix, delta->new_path, new_suffix);
|
delta->path, old_suffix, delta->new_path, new_suffix);
|
||||||
else if (delta->old_attr != delta->new_attr)
|
else if (delta->old_attr != delta->new_attr &&
|
||||||
|
delta->old_attr != 0 && delta->new_attr != 0)
|
||||||
git_buf_printf(pi->buf, "%c\t%s%c (%o -> %o)\n", code,
|
git_buf_printf(pi->buf, "%c\t%s%c (%o -> %o)\n", code,
|
||||||
delta->path, new_suffix, delta->old_attr, delta->new_attr);
|
delta->path, new_suffix, delta->old_attr, delta->new_attr);
|
||||||
else if (old_suffix != ' ')
|
else if (old_suffix != ' ')
|
||||||
@ -506,6 +682,7 @@ int git_diff_print_compact(
|
|||||||
git_buf buf = GIT_BUF_INIT;
|
git_buf buf = GIT_BUF_INIT;
|
||||||
print_info pi;
|
print_info pi;
|
||||||
|
|
||||||
|
pi.diff = diff;
|
||||||
pi.print_cb = print_cb;
|
pi.print_cb = print_cb;
|
||||||
pi.cb_data = cb_data;
|
pi.cb_data = cb_data;
|
||||||
pi.buf = &buf;
|
pi.buf = &buf;
|
||||||
@ -549,15 +726,15 @@ static int print_patch_file(void *data, git_diff_delta *delta, float progress)
|
|||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
print_info *pi = data;
|
print_info *pi = data;
|
||||||
const char *oldpfx = "a/";
|
const char *oldpfx = pi->diff->opts.src_prefix;
|
||||||
const char *oldpath = delta->path;
|
const char *oldpath = delta->path;
|
||||||
const char *newpfx = "b/";
|
const char *newpfx = pi->diff->opts.dst_prefix;
|
||||||
const char *newpath = delta->new_path ? delta->new_path : delta->path;
|
const char *newpath = delta->new_path ? delta->new_path : delta->path;
|
||||||
|
|
||||||
GIT_UNUSED_ARG(progress);
|
GIT_UNUSED_ARG(progress);
|
||||||
|
|
||||||
git_buf_clear(pi->buf);
|
git_buf_clear(pi->buf);
|
||||||
git_buf_printf(pi->buf, "diff --git a/%s b/%s\n", delta->path, newpath);
|
git_buf_printf(pi->buf, "diff --git %s%s %s%s\n", oldpfx, delta->path, newpfx, newpath);
|
||||||
if ((error = print_oid_range(pi, delta)) < GIT_SUCCESS)
|
if ((error = print_oid_range(pi, delta)) < GIT_SUCCESS)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@ -647,6 +824,7 @@ int git_diff_print_patch(
|
|||||||
git_buf buf = GIT_BUF_INIT;
|
git_buf buf = GIT_BUF_INIT;
|
||||||
print_info pi;
|
print_info pi;
|
||||||
|
|
||||||
|
pi.diff = diff;
|
||||||
pi.print_cb = print_cb;
|
pi.print_cb = print_cb;
|
||||||
pi.cb_data = cb_data;
|
pi.cb_data = cb_data;
|
||||||
pi.buf = &buf;
|
pi.buf = &buf;
|
||||||
@ -671,11 +849,17 @@ int git_diff_blobs(
|
|||||||
diff_info di;
|
diff_info di;
|
||||||
git_diff_delta delta;
|
git_diff_delta delta;
|
||||||
mmfile_t old, new;
|
mmfile_t old, new;
|
||||||
xpparam_t params;
|
xpparam_t xdiff_params;
|
||||||
xdemitconf_t cfg;
|
xdemitconf_t xdiff_config;
|
||||||
xdemitcb_t callback;
|
xdemitcb_t xdiff_callback;
|
||||||
|
|
||||||
assert(repo && options);
|
assert(repo);
|
||||||
|
|
||||||
|
if (options && (options->flags & GIT_DIFF_REVERSE)) {
|
||||||
|
git_blob *swap = old_blob;
|
||||||
|
old_blob = new_blob;
|
||||||
|
new_blob = swap;
|
||||||
|
}
|
||||||
|
|
||||||
if (old_blob) {
|
if (old_blob) {
|
||||||
old.ptr = (char *)git_blob_rawcontent(old_blob);
|
old.ptr = (char *)git_blob_rawcontent(old_blob);
|
||||||
@ -712,21 +896,15 @@ int git_diff_blobs(
|
|||||||
di.delta = δ
|
di.delta = δ
|
||||||
di.cb_data = cb_data;
|
di.cb_data = cb_data;
|
||||||
di.hunk_cb = hunk_cb;
|
di.hunk_cb = hunk_cb;
|
||||||
di.line_cb = line_cb;
|
di.line_cb = line_cb;
|
||||||
|
|
||||||
memset(¶ms, 0, sizeof(params));
|
memset(&xdiff_params, 0, sizeof(xdiff_params));
|
||||||
|
setup_xdiff_config(options, &xdiff_config);
|
||||||
|
memset(&xdiff_callback, 0, sizeof(xdiff_callback));
|
||||||
|
xdiff_callback.outf = diff_output_cb;
|
||||||
|
xdiff_callback.priv = &di;
|
||||||
|
|
||||||
memset(&cfg, 0, sizeof(cfg));
|
xdl_diff(&old, &new, &xdiff_params, &xdiff_config, &xdiff_callback);
|
||||||
cfg.ctxlen = options->context_lines || 3;
|
|
||||||
cfg.interhunkctxlen = options->interhunk_lines || 3;
|
|
||||||
if (options->ignore_whitespace)
|
|
||||||
cfg.flags |= XDF_WHITESPACE_FLAGS;
|
|
||||||
|
|
||||||
memset(&callback, 0, sizeof(callback));
|
|
||||||
callback.outf = diff_output_cb;
|
|
||||||
callback.priv = &di;
|
|
||||||
|
|
||||||
xdl_diff(&old, &new, ¶ms, &cfg, &callback);
|
|
||||||
|
|
||||||
return GIT_SUCCESS;
|
return GIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,11 @@
|
|||||||
struct git_diff_list {
|
struct git_diff_list {
|
||||||
git_repository *repo;
|
git_repository *repo;
|
||||||
git_diff_options opts;
|
git_diff_options opts;
|
||||||
git_buf pfx;
|
|
||||||
git_vector files; /* vector of git_diff_file_delta */
|
git_vector files; /* vector of git_diff_file_delta */
|
||||||
|
|
||||||
|
/* the following are just used while processing the diff list */
|
||||||
|
git_buf pfx;
|
||||||
|
git_status_t mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -22,7 +22,7 @@ void test_diff_blob__0(void)
|
|||||||
{
|
{
|
||||||
git_blob *a, *b, *c, *d;
|
git_blob *a, *b, *c, *d;
|
||||||
git_oid a_oid, b_oid, c_oid, d_oid;
|
git_oid a_oid, b_oid, c_oid, d_oid;
|
||||||
git_diff_options opts;
|
git_diff_options opts = {0};
|
||||||
diff_expects exp;
|
diff_expects exp;
|
||||||
|
|
||||||
/* tests/resources/attr/root_test1 */
|
/* tests/resources/attr/root_test1 */
|
||||||
@ -44,8 +44,7 @@ void test_diff_blob__0(void)
|
|||||||
/* Doing the equivalent of a `git diff -U1` on these files */
|
/* Doing the equivalent of a `git diff -U1` on these files */
|
||||||
|
|
||||||
opts.context_lines = 1;
|
opts.context_lines = 1;
|
||||||
opts.interhunk_lines = 0;
|
opts.interhunk_lines = 1;
|
||||||
opts.ignore_whitespace = 0;
|
|
||||||
|
|
||||||
memset(&exp, 0, sizeof(exp));
|
memset(&exp, 0, sizeof(exp));
|
||||||
cl_git_pass(git_diff_blobs(
|
cl_git_pass(git_diff_blobs(
|
||||||
|
@ -82,3 +82,23 @@ int diff_line_fn(
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
git_tree *resolve_commit_oid_to_tree(
|
||||||
|
git_repository *repo,
|
||||||
|
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, 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;
|
||||||
|
}
|
||||||
|
@ -38,3 +38,4 @@ extern int diff_line_fn(
|
|||||||
char line_origin,
|
char line_origin,
|
||||||
const char *content,
|
const char *content,
|
||||||
size_t content_len);
|
size_t content_len);
|
||||||
|
|
||||||
|
96
tests-clar/diff/index.c
Normal file
96
tests-clar/diff/index.c
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
#include "clar_libgit2.h"
|
||||||
|
#include "diff_helpers.h"
|
||||||
|
|
||||||
|
static git_repository *g_repo = NULL;
|
||||||
|
|
||||||
|
void test_diff_index__initialize(void)
|
||||||
|
{
|
||||||
|
cl_fixture_sandbox("status");
|
||||||
|
cl_git_pass(p_rename("status/.gitted", "status/.git"));
|
||||||
|
cl_git_pass(git_repository_open(&g_repo, "status/.git"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_diff_index__cleanup(void)
|
||||||
|
{
|
||||||
|
git_repository_free(g_repo);
|
||||||
|
g_repo = NULL;
|
||||||
|
cl_fixture_cleanup("status");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_diff_index__0(void)
|
||||||
|
{
|
||||||
|
/* grabbed a couple of commit oids from the history of the attr repo */
|
||||||
|
const char *a_commit = "26a125ee1bf"; /* the current HEAD */
|
||||||
|
const char *b_commit = "0017bd4ab1ec3"; /* the start */
|
||||||
|
git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
|
||||||
|
git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit);
|
||||||
|
git_diff_options opts = {0};
|
||||||
|
git_diff_list *diff = NULL;
|
||||||
|
diff_expects exp;
|
||||||
|
|
||||||
|
cl_assert(a);
|
||||||
|
cl_assert(b);
|
||||||
|
|
||||||
|
opts.context_lines = 1;
|
||||||
|
opts.interhunk_lines = 1;
|
||||||
|
|
||||||
|
memset(&exp, 0, sizeof(exp));
|
||||||
|
|
||||||
|
cl_git_pass(git_diff_index_to_tree(g_repo, &opts, a, &diff));
|
||||||
|
|
||||||
|
cl_git_pass(git_diff_foreach(
|
||||||
|
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
|
||||||
|
|
||||||
|
/* to generate these values:
|
||||||
|
* - cd to tests/resources/status,
|
||||||
|
* - mv .gitted .git
|
||||||
|
* - git diff --name-status --cached 26a125ee1bf
|
||||||
|
* - git diff -U1 --cached 26a125ee1bf
|
||||||
|
* - mv .git .gitted
|
||||||
|
*/
|
||||||
|
cl_assert(exp.files == 8);
|
||||||
|
cl_assert(exp.file_adds == 3);
|
||||||
|
cl_assert(exp.file_dels == 2);
|
||||||
|
cl_assert(exp.file_mods == 3);
|
||||||
|
|
||||||
|
cl_assert(exp.hunks == 8);
|
||||||
|
|
||||||
|
cl_assert(exp.lines == 11);
|
||||||
|
cl_assert(exp.line_ctxt == 3);
|
||||||
|
cl_assert(exp.line_adds == 6);
|
||||||
|
cl_assert(exp.line_dels == 2);
|
||||||
|
|
||||||
|
git_diff_list_free(diff);
|
||||||
|
diff = NULL;
|
||||||
|
memset(&exp, 0, sizeof(exp));
|
||||||
|
|
||||||
|
cl_git_pass(git_diff_index_to_tree(g_repo, &opts, b, &diff));
|
||||||
|
|
||||||
|
cl_git_pass(git_diff_foreach(
|
||||||
|
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
|
||||||
|
|
||||||
|
/* to generate these values:
|
||||||
|
* - cd to tests/resources/status,
|
||||||
|
* - mv .gitted .git
|
||||||
|
* - git diff --name-status --cached 0017bd4ab1ec3
|
||||||
|
* - git diff -U1 --cached 0017bd4ab1ec3
|
||||||
|
* - mv .git .gitted
|
||||||
|
*/
|
||||||
|
cl_assert(exp.files == 12);
|
||||||
|
cl_assert(exp.file_adds == 7);
|
||||||
|
cl_assert(exp.file_dels == 2);
|
||||||
|
cl_assert(exp.file_mods == 3);
|
||||||
|
|
||||||
|
cl_assert(exp.hunks == 12);
|
||||||
|
|
||||||
|
cl_assert(exp.lines == 16);
|
||||||
|
cl_assert(exp.line_ctxt == 3);
|
||||||
|
cl_assert(exp.line_adds == 11);
|
||||||
|
cl_assert(exp.line_dels == 2);
|
||||||
|
|
||||||
|
git_diff_list_free(diff);
|
||||||
|
diff = NULL;
|
||||||
|
|
||||||
|
git_tree_free(a);
|
||||||
|
git_tree_free(b);
|
||||||
|
}
|
@ -18,34 +18,16 @@ void test_diff_tree__cleanup(void)
|
|||||||
cl_fixture_cleanup("attr");
|
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)
|
void test_diff_tree__0(void)
|
||||||
{
|
{
|
||||||
/* grabbed a couple of commit oids from the history of the attr repo */
|
/* grabbed a couple of commit oids from the history of the attr repo */
|
||||||
const char *a_commit = "605812a";
|
const char *a_commit = "605812a";
|
||||||
const char *b_commit = "370fe9ec22";
|
const char *b_commit = "370fe9ec22";
|
||||||
const char *c_commit = "f5b0af1fb4f5c";
|
const char *c_commit = "f5b0af1fb4f5c";
|
||||||
git_tree *a = resolve_commit_oid_to_tree(a_commit);
|
git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
|
||||||
git_tree *b = resolve_commit_oid_to_tree(b_commit);
|
git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit);
|
||||||
git_tree *c = resolve_commit_oid_to_tree(c_commit);
|
git_tree *c = resolve_commit_oid_to_tree(g_repo, c_commit);
|
||||||
git_diff_options opts;
|
git_diff_options opts = {0};
|
||||||
git_diff_list *diff = NULL;
|
git_diff_list *diff = NULL;
|
||||||
diff_expects exp;
|
diff_expects exp;
|
||||||
|
|
||||||
@ -53,8 +35,7 @@ void test_diff_tree__0(void)
|
|||||||
cl_assert(b);
|
cl_assert(b);
|
||||||
|
|
||||||
opts.context_lines = 1;
|
opts.context_lines = 1;
|
||||||
opts.interhunk_lines = 0;
|
opts.interhunk_lines = 1;
|
||||||
opts.ignore_whitespace = 0;
|
|
||||||
|
|
||||||
memset(&exp, 0, sizeof(exp));
|
memset(&exp, 0, sizeof(exp));
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user