summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt4
-rw-r--r--examples/Makefile8
-rw-r--r--examples/diff.c234
-rw-r--r--include/git2.h1
-rw-r--r--include/git2/attr.h10
-rw-r--r--include/git2/config.h50
-rw-r--r--include/git2/diff.h355
-rw-r--r--include/git2/oid.h5
-rw-r--r--include/git2/status.h2
-rw-r--r--include/git2/tree.h4
-rw-r--r--packaging/rpm/README6
-rw-r--r--packaging/rpm/libgit2.spec106
-rw-r--r--src/attr.c6
-rw-r--r--src/attr_file.c12
-rw-r--r--src/blob.c172
-rw-r--r--src/blob.h1
-rw-r--r--src/buffer.c56
-rw-r--r--src/buffer.h11
-rw-r--r--src/cc-compat.h16
-rw-r--r--src/config.c172
-rw-r--r--src/config_cache.c95
-rw-r--r--src/config_file.c25
-rw-r--r--src/crlf.c229
-rw-r--r--src/diff.c599
-rw-r--r--src/diff.h25
-rw-r--r--src/diff_output.c739
-rw-r--r--src/fileops.c90
-rw-r--r--src/fileops.h30
-rw-r--r--src/filter.c165
-rw-r--r--src/filter.h119
-rw-r--r--src/hashtable.h28
-rw-r--r--src/ignore.c6
-rw-r--r--src/index.c6
-rw-r--r--src/iterator.c97
-rw-r--r--src/iterator.h6
-rw-r--r--src/odb.c7
-rw-r--r--src/odb_loose.c26
-rw-r--r--src/odb_pack.c4
-rw-r--r--src/oid.c10
-rw-r--r--src/path.c45
-rw-r--r--src/path.h22
-rw-r--r--src/pkt.c6
-rw-r--r--src/posix.h2
-rw-r--r--src/reflog.c6
-rw-r--r--src/refs.c77
-rw-r--r--src/remote.c4
-rw-r--r--src/repository.c21
-rw-r--r--src/repository.h61
-rw-r--r--src/revwalk.c6
-rw-r--r--src/transport.c4
-rw-r--r--src/transports/local.c6
-rw-r--r--src/unix/posix.h1
-rw-r--r--src/vector.c10
-rw-r--r--src/vector.h6
-rw-r--r--src/win32/dir.c12
-rw-r--r--src/win32/dir.h6
-rw-r--r--src/win32/posix.h10
-rw-r--r--src/win32/pthread.c8
-rw-r--r--src/xdiff/xdiff.h135
-rw-r--r--src/xdiff/xdiffi.c572
-rw-r--r--src/xdiff/xdiffi.h63
-rw-r--r--src/xdiff/xemit.c253
-rw-r--r--src/xdiff/xemit.h36
-rw-r--r--src/xdiff/xhistogram.c371
-rw-r--r--src/xdiff/xinclude.h46
-rw-r--r--src/xdiff/xmacros.h54
-rw-r--r--src/xdiff/xmerge.c619
-rw-r--r--src/xdiff/xpatience.c358
-rw-r--r--src/xdiff/xprepare.c483
-rw-r--r--src/xdiff/xprepare.h34
-rw-r--r--src/xdiff/xtypes.h67
-rw-r--r--src/xdiff/xutils.c419
-rw-r--r--src/xdiff/xutils.h49
-rw-r--r--tests-clar/attr/attr_expect.h42
-rw-r--r--tests-clar/attr/file.c46
-rw-r--r--tests-clar/attr/lookup.c277
-rw-r--r--tests-clar/attr/repo.c173
-rw-r--r--tests-clar/clar_helpers.c61
-rw-r--r--tests-clar/clar_libgit2.h11
-rw-r--r--tests-clar/config/multivar.c11
-rw-r--r--tests-clar/core/buffer.c12
-rw-r--r--tests-clar/core/dirent.c6
-rw-r--r--tests-clar/core/path.c2
-rw-r--r--tests-clar/diff/blob.c91
-rw-r--r--tests-clar/diff/diff_helpers.c66
-rw-r--r--tests-clar/diff/diff_helpers.h39
-rw-r--r--tests-clar/diff/index.c92
-rw-r--r--tests-clar/diff/iterator.c60
-rw-r--r--tests-clar/diff/tree.c170
-rw-r--r--tests-clar/diff/workdir.c228
-rw-r--r--tests-clar/object/blob/filter.c125
-rw-r--r--tests-clar/status/ignore.c13
-rw-r--r--tests-clar/status/worktree.c67
-rw-r--r--tests/resources/attr/.gitted/indexbin1376 -> 1376 bytes
-rw-r--r--tests/resources/attr/.gitted/logs/HEAD3
-rw-r--r--tests/resources/attr/.gitted/logs/refs/heads/master3
-rw-r--r--tests/resources/attr/.gitted/objects/10/8bb4e7fd7b16490dc33ff7d972151e73d7166ebin0 -> 130 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/37/0fe9ec224ce33e71f9e5ec2bd1142ce9937a6abin0 -> 177 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/3a/6df026462ebafe455af9867d27eda20a9e0974bin0 -> 84 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/4d/713dc48e6b1bd75b0d61ad078ba9ca3a56745d2
-rw-r--r--tests/resources/attr/.gitted/objects/6d/968d62c89c7d9ea23a4c9a7b665d017c3d8ffdbin0 -> 422 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/71/7fc31f6b84f9d6fc3a4edbca259d7fc92beee2bin0 -> 422 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/96/089fd31ce1d3ee2afb0ba09ba063066932f027bin0 -> 422 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/a9/7cc019851d401a4f1d091cb91a15890a0dd1ba2
-rw-r--r--tests/resources/attr/.gitted/objects/c9/6bbb2c2557a8325ae1559e3ba79cdcecb230762
-rw-r--r--tests/resources/attr/.gitted/objects/f5/b0af1fb4f5c0cd7aad880711d368a07333c3072
-rw-r--r--tests/resources/attr/.gitted/objects/fe/773770c5a6cc7185580c9204b1ff18a33ff3fc1
-rw-r--r--tests/resources/attr/.gitted/refs/heads/master2
-rw-r--r--tests/resources/attr/gitattributes4
-rw-r--r--tests/resources/attr/root_test25
-rw-r--r--tests/resources/attr/root_test320
-rw-r--r--tests/resources/attr/root_test4.txt15
-rw-r--r--tests/resources/attr/sub/sub/.gitattributes3
-rw-r--r--tests/resources/status/.gitted/indexbin1160 -> 1160 bytes
-rw-r--r--tests/t00-core.c6
-rw-r--r--tests/t07-hashtable.c5
-rw-r--r--tests/t18-status.c8
-rw-r--r--tests/test_helpers.c17
-rw-r--r--tests/test_main.c6
119 files changed, 8323 insertions, 784 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 45ab12193..b46e82515 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -97,9 +97,9 @@ FILE(GLOB SRC_H include/git2/*.h)
# On Windows use specific platform sources
IF (WIN32 AND NOT CYGWIN)
ADD_DEFINITIONS(-DWIN32 -D_DEBUG)
- FILE(GLOB SRC src/*.c src/transports/*.c src/win32/*.c)
+ FILE(GLOB SRC src/*.c src/transports/*.c src/xdiff/*.c src/win32/*.c)
ELSE()
- FILE(GLOB SRC src/*.c src/transports/*.c src/unix/*.c)
+ FILE(GLOB SRC src/*.c src/transports/*.c src/xdiff/*.c src/unix/*.c)
ENDIF ()
# Compile and link libgit2
diff --git a/examples/Makefile b/examples/Makefile
index efb55547b..fe99c75cb 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -1,13 +1,15 @@
.PHONY: all
CC = gcc
-CFLAGS = -g -I../include
+CFLAGS = -g -I../include -I../src
LFLAGS = -L../build -lgit2 -lz
+APPS = general showindex diff
-all: general showindex
+all: $(APPS)
% : %.c
$(CC) -o $@ $(CFLAGS) $< $(LFLAGS)
clean:
- $(RM) general showindex
+ $(RM) $(APPS)
+ $(RM) -r *.dSYM
diff --git a/examples/diff.c b/examples/diff.c
new file mode 100644
index 000000000..f80f7029c
--- /dev/null
+++ b/examples/diff.c
@@ -0,0 +1,234 @@
+#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 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[])
+{
+ char path[GIT_PATH_MAX];
+ git_repository *repo = NULL;
+ git_tree *t1 = NULL, *t2 = NULL;
+ git_diff_options opts = {0};
+ git_diff_list *diff;
+ int i, color = -1, compact = 0, cached = 0;
+ char *a, *dir = ".", *treeish1 = NULL, *treeish2 = NULL;
+
+ /* parse arguments as copied from git-diff */
+
+ 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, "--cached"))
+ cached = 1;
+ 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 (!strcmp(a, "--ignored"))
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED;
+ else if (!strcmp(a, "--untracked"))
+ opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
+ 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);
+ }
+
+ /* open repo */
+
+ check(git_repository_discover(path, sizeof(path), dir, 0, "/"),
+ "Could not discover repository");
+ check(git_repository_open(&repo, path),
+ "Could not open repository");
+
+ if (treeish1)
+ check(resolve_to_tree(repo, treeish1, &t1), "Looking up first tree");
+ if (treeish2)
+ check(resolve_to_tree(repo, treeish2, &t2), "Looking up second tree");
+
+ /* <sha1> <sha2> */
+ /* <sha1> --cached */
+ /* <sha1> */
+ /* --cached */
+ /* nothing */
+
+ if (t1 && t2)
+ check(git_diff_tree_to_tree(repo, &opts, t1, t2, &diff), "Diff");
+ else if (t1 && cached)
+ check(git_diff_index_to_tree(repo, &opts, t1, &diff), "Diff");
+ else if (t1) {
+ git_diff_list *diff2;
+ check(git_diff_index_to_tree(repo, &opts, t1, &diff), "Diff");
+ check(git_diff_workdir_to_index(repo, &opts, &diff2), "Diff");
+ check(git_diff_merge(diff, diff2), "Merge diffs");
+ git_diff_list_free(diff2);
+ }
+ else if (cached) {
+ check(resolve_to_tree(repo, "HEAD", &t1), "looking up HEAD");
+ check(git_diff_index_to_tree(repo, &opts, t1, &diff), "Diff");
+ }
+ else
+ check(git_diff_workdir_to_index(repo, &opts, &diff), "Diff");
+
+ if (color >= 0)
+ fputs(colors[0], stdout);
+
+ if (compact)
+ check(git_diff_print_compact(diff, &color, printer), "Displaying diff");
+ else
+ check(git_diff_print_patch(diff, &color, printer), "Displaying diff");
+
+ if (color >= 0)
+ fputs(colors[0], stdout);
+
+ git_diff_list_free(diff);
+ git_tree_free(t1);
+ git_tree_free(t2);
+ git_repository_free(repo);
+
+ return 0;
+}
+
diff --git a/include/git2.h b/include/git2.h
index 5a55bb284..1711ff8be 100644
--- a/include/git2.h
+++ b/include/git2.h
@@ -30,6 +30,7 @@
#include "git2/commit.h"
#include "git2/tag.h"
#include "git2/tree.h"
+#include "git2/diff.h"
#include "git2/index.h"
#include "git2/config.h"
diff --git a/include/git2/attr.h b/include/git2/attr.h
index 7e8bb9fe8..81d1e517b 100644
--- a/include/git2/attr.h
+++ b/include/git2/attr.h
@@ -19,12 +19,12 @@
*/
GIT_BEGIN_DECL
-#define GIT_ATTR_TRUE git_attr__true
-#define GIT_ATTR_FALSE git_attr__false
-#define GIT_ATTR_UNSPECIFIED NULL
+#define GIT_ATTR_TRUE(attr) ((attr) == git_attr__true)
+#define GIT_ATTR_FALSE(attr) ((attr) == git_attr__false)
+#define GIT_ATTR_UNSPECIFIED(attr) ((attr) == NULL)
-GIT_EXTERN(const char *)git_attr__true;
-GIT_EXTERN(const char *)git_attr__false;
+GIT_EXTERN(const char *) git_attr__true;
+GIT_EXTERN(const char *) git_attr__false;
/**
diff --git a/include/git2/config.h b/include/git2/config.h
index 8a0f58937..acc45b018 100644
--- a/include/git2/config.h
+++ b/include/git2/config.h
@@ -37,6 +37,19 @@ struct git_config_file {
void (*free)(struct git_config_file *);
};
+typedef enum {
+ GIT_CVAR_FALSE = 0,
+ GIT_CVAR_TRUE = 1,
+ GIT_CVAR_INT32,
+ GIT_CVAR_STRING
+} git_cvar_t;
+
+typedef struct {
+ git_cvar_t cvar_type;
+ const char *str_match;
+ int map_value;
+} git_cvar_map;
+
/**
* Locate the path to the global configuration file
*
@@ -301,6 +314,43 @@ GIT_EXTERN(int) git_config_foreach(
int (*callback)(const char *var_name, const char *value, void *payload),
void *payload);
+
+/**
+ * Query the value of a config variable and return it mapped to
+ * an integer constant.
+ *
+ * This is a helper method to easily map different possible values
+ * to a variable to integer constants that easily identify them.
+ *
+ * A mapping array looks as follows:
+ *
+ * git_cvar_map autocrlf_mapping[3] = {
+ * {GIT_CVAR_FALSE, NULL, GIT_AUTO_CRLF_FALSE},
+ * {GIT_CVAR_TRUE, NULL, GIT_AUTO_CRLF_TRUE},
+ * {GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT},
+ * {GIT_CVAR_STRING, "default", GIT_AUTO_CRLF_DEFAULT}};
+ *
+ * On any "false" value for the variable (e.g. "false", "FALSE", "no"), the
+ * mapping will store `GIT_AUTO_CRLF_FALSE` in the `out` parameter.
+ *
+ * The same thing applies for any "true" value such as "true", "yes" or "1", storing
+ * the `GIT_AUTO_CRLF_TRUE` variable.
+ *
+ * Otherwise, if the value matches the string "input" (with case insensitive comparison),
+ * the given constant will be stored in `out`, and likewise for "default".
+ *
+ * If not a single match can be made to store in `out`, an error code will be
+ * returned.
+ *
+ * @param cfg config file to get the variables from
+ * @param name name of the config variable to lookup
+ * @param maps array of `git_cvar_map` objects specifying the possible mappings
+ * @param map_n number of mapping objects in `maps`
+ * @param out place to store the result of the mapping
+ * @return GIT_SUCCESS on success, error code otherwise
+ */
+GIT_EXTERN(int) git_config_get_mapped(git_config *cfg, const char *name, git_cvar_map *maps, size_t map_n, int *out);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/diff.h b/include/git2/diff.h
new file mode 100644
index 000000000..ba2e21c27
--- /dev/null
+++ b/include/git2/diff.h
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_diff_h__
+#define INCLUDE_git_diff_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+#include "tree.h"
+#include "refs.h"
+
+/**
+ * @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
+
+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),
+ GIT_DIFF_INCLUDE_IGNORED = (1 << 7),
+ GIT_DIFF_INCLUDE_UNTRACKED = (1 << 8)
+};
+
+/**
+ * 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.
+ */
+typedef struct {
+ uint32_t flags; /**< defaults to GIT_DIFF_NORMAL */
+ uint16_t context_lines; /**< defaults to 3 */
+ uint16_t interhunk_lines; /**< defaults to 3 */
+ char *old_prefix; /**< defaults to "a" */
+ char *new_prefix; /**< defaults to "b" */
+ git_strarray pathspec; /**< defaults to show all paths */
+} git_diff_options;
+
+/**
+ * The diff list object that contains all individual file deltas.
+ */
+typedef struct git_diff_list git_diff_list;
+
+enum {
+ GIT_DIFF_FILE_VALID_OID = (1 << 0),
+ GIT_DIFF_FILE_FREE_PATH = (1 << 1),
+ GIT_DIFF_FILE_BINARY = (1 << 2),
+ GIT_DIFF_FILE_NOT_BINARY = (1 << 3),
+ GIT_DIFF_FILE_FREE_DATA = (1 << 4),
+ GIT_DIFF_FILE_UNMAP_DATA = (1 << 5)
+};
+
+/**
+ * What type of change is described by a git_diff_delta?
+ */
+typedef enum {
+ GIT_DELTA_UNMODIFIED = 0,
+ GIT_DELTA_ADDED = 1,
+ GIT_DELTA_DELETED = 2,
+ GIT_DELTA_MODIFIED = 3,
+ GIT_DELTA_RENAMED = 4,
+ GIT_DELTA_COPIED = 5,
+ GIT_DELTA_IGNORED = 6,
+ GIT_DELTA_UNTRACKED = 7
+} git_delta_t;
+
+/**
+ * Description of one side of a diff.
+ */
+typedef struct {
+ git_oid oid;
+ char *path;
+ uint16_t mode;
+ git_off_t size;
+ unsigned int flags;
+} git_diff_file;
+
+/**
+ * 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_diff_file old_file;
+ git_diff_file new_file;
+ git_delta_t status;
+ unsigned int similarity; /**< for RENAMED and COPIED, value 0-100 */
+ int 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;
+ int new_start;
+ 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,
+ git_diff_range *range,
+ const char *header,
+ size_t header_len);
+
+/**
+ * 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 */
+ const char *content,
+ size_t content_len);
+
+/**
+ * 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);
+
+
+/** @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.
+ */
+/**@{*/
+
+/**
+ * Deallocate a diff list.
+ */
+GIT_EXTERN(void) git_diff_list_free(git_diff_list *diff);
+
+/**
+ * Compute a difference between two tree objects.
+ *
+ * @param repo The repository containing the trees.
+ * @param opts Structure with options to influence diff or NULL for defaults.
+ * @param old_tree A git_tree object to diff from.
+ * @param new_tree A git_tree object to diff to.
+ * @param diff A pointer to a git_diff_list pointer that will be allocated.
+ */
+GIT_EXTERN(int) git_diff_tree_to_tree(
+ git_repository *repo,
+ const git_diff_options *opts, /**< can be NULL for defaults */
+ git_tree *old_tree,
+ git_tree *new_tree,
+ git_diff_list **diff);
+
+/**
+ * Compute a difference between a tree and the index.
+ *
+ * @param repo The repository containing the tree and index.
+ * @param opts Structure with options to influence diff or NULL for defaults.
+ * @param old_tree A git_tree object to diff from.
+ * @param diff A pointer to a git_diff_list pointer that will be allocated.
+ */
+GIT_EXTERN(int) git_diff_index_to_tree(
+ git_repository *repo,
+ const git_diff_options *opts, /**< can be NULL for defaults */
+ git_tree *old_tree,
+ git_diff_list **diff);
+
+/**
+ * Compute a difference between the working directory and the index.
+ *
+ * @param repo The repository.
+ * @param opts Structure with options to influence diff or NULL for defaults.
+ * @param diff A pointer to a git_diff_list pointer that will be allocated.
+ */
+GIT_EXTERN(int) git_diff_workdir_to_index(
+ git_repository *repo,
+ const git_diff_options *opts, /**< can be NULL for defaults */
+ git_diff_list **diff);
+
+/**
+ * Compute a difference between the working directory and a tree.
+ *
+ * This returns strictly the differences between the tree and the
+ * files contained in the working directory, regardless of the state
+ * of files in the index. There is no direct equivalent in C git.
+ *
+ * This is *NOT* the same as 'git diff HEAD' or 'git diff <SHA>'. Those
+ * commands diff the tree, the index, and the workdir. To emulate those
+ * functions, call `git_diff_index_to_tree` and `git_diff_workdir_to_index`,
+ * then call `git_diff_merge` on the results.
+ *
+ * @param repo The repository containing the tree.
+ * @param opts Structure with options to influence diff or NULL for defaults.
+ * @param old_tree A git_tree object to diff from.
+ * @param diff A pointer to a git_diff_list pointer that will be allocated.
+ */
+GIT_EXTERN(int) git_diff_workdir_to_tree(
+ git_repository *repo,
+ const git_diff_options *opts, /**< can be NULL for defaults */
+ git_tree *old_tree,
+ git_diff_list **diff);
+
+/**
+ * Merge one diff list into another.
+ *
+ * This merges items from the "from" list into the "onto" list. The
+ * resulting diff list will have all items that appear in either list.
+ * If an item appears in both lists, then it will be "merged" to appear
+ * as if the old version was from the "onto" list and the new version
+ * is from the "from" list (with the exception that if the item has a
+ * pending DELETE in the middle, then it will show as deleted).
+ *
+ * @param onto Diff to merge into.
+ * @param from Diff to merge.
+ */
+GIT_EXTERN(int) git_diff_merge(
+ git_diff_list *onto,
+ const git_diff_list *from);
+
+/**@}*/
+
+
+/** @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,
+ git_diff_file_fn file_cb,
+ git_diff_hunk_fn hunk_cb,
+ git_diff_line_fn line_cb);
+
+/**
+ * Iterate over a diff generating text output like "git diff --name-status".
+ */
+GIT_EXTERN(int) git_diff_print_compact(
+ 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(
+ 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_blob,
+ git_blob *new_blob,
+ git_diff_options *options,
+ void *cb_data,
+ git_diff_hunk_fn hunk_cb,
+ git_diff_line_fn line_cb);
+
+GIT_END_DECL
+
+/** @} */
+
+#endif
diff --git a/include/git2/oid.h b/include/git2/oid.h
index ad7086164..712ecb2bb 100644
--- a/include/git2/oid.h
+++ b/include/git2/oid.h
@@ -160,6 +160,11 @@ GIT_EXTERN(int) git_oid_ncmp(const git_oid *a, const git_oid *b, unsigned int le
GIT_EXTERN(int) git_oid_streq(const git_oid *a, const char *str);
/**
+ * Check is an oid is all zeros.
+ */
+GIT_EXTERN(int) git_oid_iszero(const git_oid *a);
+
+/**
* OID Shortener object
*/
typedef struct git_oid_shorten git_oid_shorten;
diff --git a/include/git2/status.h b/include/git2/status.h
index 2a304b82f..5c45dae1e 100644
--- a/include/git2/status.h
+++ b/include/git2/status.h
@@ -31,7 +31,7 @@ GIT_BEGIN_DECL
#define GIT_STATUS_WT_MODIFIED (1 << 4)
#define GIT_STATUS_WT_DELETED (1 << 5)
-#define GIT_STATUS_IGNORED (1 << 6)
+#define GIT_STATUS_IGNORED (1 << 6)
/**
* Gather file statuses and run a callback for each one.
diff --git a/include/git2/tree.h b/include/git2/tree.h
index 95e0fdf94..972c3795c 100644
--- a/include/git2/tree.h
+++ b/include/git2/tree.h
@@ -313,8 +313,6 @@ 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_ADDED = 1,
GIT_STATUS_DELETED = 2,
@@ -349,5 +347,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
diff --git a/packaging/rpm/README b/packaging/rpm/README
new file mode 100644
index 000000000..1a6410b16
--- /dev/null
+++ b/packaging/rpm/README
@@ -0,0 +1,6 @@
+To build RPM pakcages for Fedora, follow these steps:
+ cp packaging/rpm/libgit2.spec ~/rpmbuild/SPECS
+ cd ~/rpmbuild/SOURCES
+ wget https://github.com/downloads/libgit2/libgit2/libgit2-0.16.0.tar.gz
+ cd ~/rpmbuild/SPECS
+ rpmbuild -ba libgit2.spec
diff --git a/packaging/rpm/libgit2.spec b/packaging/rpm/libgit2.spec
new file mode 100644
index 000000000..a6e82b241
--- /dev/null
+++ b/packaging/rpm/libgit2.spec
@@ -0,0 +1,106 @@
+#
+# spec file for package libgit2
+#
+# Copyright (c) 2012 Saleem Ansari <tuxdna@gmail.com>
+# Copyright (c) 2012 SUSE LINUX Products GmbH, Nuernberg, Germany.
+# Copyright (c) 2011, Sascha Peilicke <saschpe@gmx.de>
+#
+# All modifications and additions to the file contributed by third parties
+# remain the property of their copyright owners, unless otherwise agreed
+# upon. The license for this file, and modifications and additions to the
+# file, is the same license as for the pristine package itself (unless the
+# license for the pristine package is not an Open Source License, in which
+# case the license is the MIT License). An "Open Source License" is a
+# license that conforms to the Open Source Definition (Version 1.9)
+# published by the Open Source Initiative.
+
+# Please submit bugfixes or comments via http://bugs.opensuse.org/
+#
+Name: libgit2
+Version: 0.16.0
+Release: 1
+Summary: C git library
+License: GPL-2.0 with linking
+Group: Development/Libraries/C and C++
+Url: http://libgit2.github.com/
+Source0: https://github.com/downloads/libgit2/libgit2/libgit2-0.16.0.tar.gz
+BuildRequires: cmake
+BuildRequires: pkgconfig
+BuildRoot: %{_tmppath}/%{name}-%{version}-build
+%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos_version}
+BuildRequires: openssl-devel
+%else
+BuildRequires: libopenssl-devel
+%endif
+
+%description
+libgit2 is a portable, pure C implementation of the Git core methods
+provided as a re-entrant linkable library with a solid API, allowing
+you to write native speed custom Git applications in any language
+with bindings.
+
+%package -n %{name}-0
+Summary: C git library
+Group: System/Libraries
+
+%description -n %{name}-0
+libgit2 is a portable, pure C implementation of the Git core methods
+provided as a re-entrant linkable library with a solid API, allowing
+you to write native speed custom Git applications in any language
+with bindings.
+
+%package devel
+Summary: C git library
+Group: Development/Libraries/C and C++
+Requires: %{name}-0 >= %{version}
+
+%description devel
+This package contains all necessary include files and libraries needed
+to compile and develop applications that use libgit2.
+
+%prep
+%setup -q
+
+%build
+cmake . \
+ -DCMAKE_C_FLAGS:STRING="%{optflags}" \
+ -DCMAKE_INSTALL_PREFIX:PATH=%{_prefix} \
+ -DINSTALL_LIB:PATH=%{_libdir}
+make %{?_smp_mflags}
+
+%install
+%make_install
+
+%post -n %{name}-0 -p /sbin/ldconfig
+%postun -n %{name}-0 -p /sbin/ldconfig
+
+%files -n %{name}-0
+%defattr (-,root,root)
+%doc AUTHORS COPYING README.md
+%{_libdir}/%{name}.so.*
+
+%files devel
+%defattr (-,root,root)
+%doc CONVENTIONS examples
+%{_libdir}/%{name}.so
+%{_includedir}/git2*
+%{_libdir}/pkgconfig/libgit2.pc
+
+%changelog
+* Tue Mar 04 2012 tuxdna@gmail.com
+- Update to version 0.16.0
+* Tue Jan 31 2012 jengelh@medozas.de
+- Provide pkgconfig symbols
+* Thu Oct 27 2011 saschpe@suse.de
+- Change license to 'GPL-2.0 with linking', fixes bnc#726789
+* Wed Oct 26 2011 saschpe@suse.de
+- Update to version 0.15.0:
+ * Upstream doesn't provide changes
+- Removed outdated %%clean section
+* Tue Jan 18 2011 saschpe@gmx.de
+- Proper Requires for devel package
+* Tue Jan 18 2011 saschpe@gmx.de
+- Set BuildRequires to "openssl-devel" also for RHEL and CentOS
+* Tue Jan 18 2011 saschpe@gmx.de
+- Initial commit (0.0.1)
+- Added patch to fix shared library soname
diff --git a/src/attr.c b/src/attr.c
index a7c65f94c..603498df2 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -408,10 +408,9 @@ void git_attr_cache_flush(
return;
if (repo->attrcache.files) {
- const void *GIT_UNUSED(name);
git_attr_file *file;
- GIT_HASHTABLE_FOREACH(repo->attrcache.files, name, file,
+ GIT_HASHTABLE_FOREACH_VALUE(repo->attrcache.files, file,
git_attr_file__free(file));
git_hashtable_free(repo->attrcache.files);
@@ -419,10 +418,9 @@ void git_attr_cache_flush(
}
if (repo->attrcache.macros) {
- const void *GIT_UNUSED(name);
git_attr_rule *rule;
- GIT_HASHTABLE_FOREACH(repo->attrcache.macros, name, rule,
+ GIT_HASHTABLE_FOREACH_VALUE(repo->attrcache.macros, rule,
git_attr_rule__free(rule));
git_hashtable_free(repo->attrcache.macros);
diff --git a/src/attr_file.c b/src/attr_file.c
index 7911381ea..3783b5ef3 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -111,7 +111,7 @@ int git_attr_file__from_file(
git_repository *repo, const char *path, git_attr_file *file)
{
int error = GIT_SUCCESS;
- git_fbuffer fbuf = GIT_FBUFFER_INIT;
+ git_buf fbuf = GIT_BUF_INIT;
assert(path && file);
@@ -120,9 +120,9 @@ int git_attr_file__from_file(
if (error == GIT_SUCCESS &&
(error = git_futils_readbuffer(&fbuf, path)) == GIT_SUCCESS)
- error = git_attr_file__from_buffer(repo, fbuf.data, file);
+ error = git_attr_file__from_buffer(repo, fbuf.ptr, file);
- git_futils_freebuffer(&fbuf);
+ git_buf_free(&fbuf);
if (error != GIT_SUCCESS)
git__rethrow(error, "Could not open attribute file '%s'", path);
@@ -458,12 +458,12 @@ int git_attr_assignment__parse(
}
assign->name_hash = 5381;
- assign->value = GIT_ATTR_TRUE;
+ assign->value = git_attr__true;
assign->is_allocated = 0;
/* look for magic name prefixes */
if (*scan == '-') {
- assign->value = GIT_ATTR_FALSE;
+ assign->value = git_attr__false;
scan++;
} else if (*scan == '!') {
assign->value = NULL; /* explicit unspecified state */
@@ -510,7 +510,7 @@ int git_attr_assignment__parse(
}
/* expand macros (if given a repo with a macro cache) */
- if (repo != NULL && assign->value == GIT_ATTR_TRUE) {
+ if (repo != NULL && assign->value == git_attr__true) {
git_attr_rule *macro =
git_hashtable_lookup(repo->attrcache.macros, assign->name);
diff --git a/src/blob.c b/src/blob.c
index 4065ffa12..b67f8afa5 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -11,6 +11,7 @@
#include "common.h"
#include "blob.h"
+#include "filter.h"
const void *git_blob_rawcontent(git_blob *blob)
{
@@ -24,6 +25,12 @@ size_t git_blob_rawsize(git_blob *blob)
return blob->odb_object->raw.len;
}
+int git_blob__getbuf(git_buf *buffer, git_blob *blob)
+{
+ return git_buf_set(
+ buffer, blob->odb_object->raw.data, blob->odb_object->raw.len);
+}
+
void git_blob__free(git_blob *blob)
{
git_odb_object_free(blob->odb_object);
@@ -65,15 +72,100 @@ int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *b
return GIT_SUCCESS;
}
+static int write_file_stream(git_oid *oid, git_odb *odb, const char *path, git_off_t file_size)
+{
+ int fd, error;
+ char buffer[4096];
+ git_odb_stream *stream = NULL;
+
+ if ((error = git_odb_open_wstream(&stream, odb, file_size, GIT_OBJ_BLOB)) < GIT_SUCCESS)
+ return error;
+
+ if ((fd = p_open(path, O_RDONLY)) < 0) {
+ error = git__throw(GIT_ENOTFOUND, "Failed to create blob. Could not open '%s'", path);
+ goto cleanup;
+ }
+
+ while (file_size > 0) {
+ ssize_t read_len = p_read(fd, buffer, sizeof(buffer));
+
+ if (read_len < 0) {
+ error = git__throw(GIT_EOSERR, "Failed to create blob. Can't read full file");
+ p_close(fd);
+ goto cleanup;
+ }
+
+ stream->write(stream, buffer, read_len);
+ file_size -= read_len;
+ }
+
+ p_close(fd);
+ error = stream->finalize_write(oid, stream);
+
+cleanup:
+ stream->free(stream);
+ return error;
+}
+
+static int write_file_filtered(
+ git_oid *oid,
+ git_odb *odb,
+ const char *full_path,
+ git_vector *filters)
+{
+ int error;
+ git_buf source = GIT_BUF_INIT;
+ git_buf dest = GIT_BUF_INIT;
+
+ error = git_futils_readbuffer(&source, full_path);
+ if (error < GIT_SUCCESS)
+ return error;
+
+ error = git_filters_apply(&dest, &source, filters);
+
+ /* Free the source as soon as possible. This can be big in memory,
+ * and we don't want to ODB write to choke */
+ git_buf_free(&source);
+
+ if (error == GIT_SUCCESS) {
+ /* Write the file to disk if it was properly filtered */
+ error = git_odb_write(oid, odb, dest.ptr, dest.size, GIT_OBJ_BLOB);
+ }
+
+ git_buf_free(&dest);
+ return GIT_SUCCESS;
+}
+
+static int write_symlink(git_oid *oid, git_odb *odb, const char *path, size_t link_size)
+{
+ char *link_data;
+ ssize_t read_len;
+ int error;
+
+ link_data = git__malloc(link_size);
+ if (!link_data)
+ return GIT_ENOMEM;
+
+ read_len = p_readlink(path, link_data, link_size);
+
+ if (read_len != (ssize_t)link_size) {
+ free(link_data);
+ return git__throw(GIT_EOSERR, "Failed to create blob. Can't read symlink");
+ }
+
+ error = git_odb_write(oid, odb, (void *)link_data, link_size, GIT_OBJ_BLOB);
+ free(link_data);
+ return error;
+}
+
int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path)
{
int error = GIT_SUCCESS;
git_buf full_path = GIT_BUF_INIT;
git_off_t size;
- git_odb_stream *stream = NULL;
struct stat st;
const char *workdir;
- git_odb *odb;
+ git_odb *odb = NULL;
workdir = git_repository_workdir(repo);
if (workdir == NULL)
@@ -95,63 +187,45 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat
if (error < GIT_SUCCESS)
goto cleanup;
- if ((error = git_odb_open_wstream(&stream, odb, (size_t)size, GIT_OBJ_BLOB)) < GIT_SUCCESS)
- goto cleanup;
-
if (S_ISLNK(st.st_mode)) {
- char *link_data;
- ssize_t read_len;
-
- link_data = git__malloc((size_t)size);
- if (!link_data) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
-
- read_len = p_readlink(full_path.ptr, link_data, (size_t)size);
-
- if (read_len != (ssize_t)size) {
- error = git__throw(GIT_EOSERR, "Failed to create blob. Can't read symlink");
- free(link_data);
- goto cleanup;
- }
-
- stream->write(stream, link_data, (size_t)size);
- free(link_data);
-
+ error = write_symlink(oid, odb, full_path.ptr, (size_t)size);
} else {
- int fd;
- char buffer[2048];
-
- if ((fd = p_open(full_path.ptr, O_RDONLY)) < 0) {
- error = git__throw(GIT_ENOTFOUND, "Failed to create blob. Could not open '%s'", full_path.ptr);
- goto cleanup;
- }
-
- while (size > 0) {
- ssize_t read_len = p_read(fd, buffer, sizeof(buffer));
+ git_vector write_filters = GIT_VECTOR_INIT;
+ int filter_count;
- if (read_len < 0) {
- error = git__throw(GIT_EOSERR, "Failed to create blob. Can't read full file");
- p_close(fd);
- goto cleanup;
- }
+ /* Load the filters for writing this file to the ODB */
+ filter_count = git_filters_load(&write_filters, repo, path, GIT_FILTER_TO_ODB);
- stream->write(stream, buffer, read_len);
- size -= read_len;
+ if (filter_count < 0) {
+ /* Negative value means there was a critical error */
+ error = filter_count;
+ goto cleanup;
+ } else if (filter_count == 0) {
+ /* No filters need to be applied to the document: we can stream
+ * directly from disk */
+ error = write_file_stream(oid, odb, full_path.ptr, size);
+ } else {
+ /* We need to apply one or more filters */
+ error = write_file_filtered(oid, odb, full_path.ptr, &write_filters);
}
- p_close(fd);
+ git_filters_free(&write_filters);
+
+ /*
+ * TODO: eventually support streaming filtered files, for files which are bigger
+ * than a given threshold. This is not a priority because applying a filter in
+ * streaming mode changes the final size of the blob, and without knowing its
+ * final size, the blob cannot be written in stream mode to the ODB.
+ *
+ * The plan is to do streaming writes to a tempfile on disk and then opening
+ * streaming that file to the ODB, using `write_file_stream`.
+ *
+ * CAREFULLY DESIGNED APIS YO
+ */
}
- error = stream->finalize_write(oid, stream);
-
cleanup:
- if (stream)
- stream->free(stream);
-
git_buf_free(&full_path);
-
return error;
}
diff --git a/src/blob.h b/src/blob.h
index f810b506b..0305e9473 100644
--- a/src/blob.h
+++ b/src/blob.h
@@ -19,5 +19,6 @@ struct git_blob {
void git_blob__free(git_blob *blob);
int git_blob__parse(git_blob *blob, git_odb_object *obj);
+int git_blob__getbuf(git_buf *buffer, git_blob *blob);
#endif
diff --git a/src/buffer.c b/src/buffer.c
index 183da7c5f..3098f6d68 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -7,14 +7,17 @@
#include "buffer.h"
#include "posix.h"
#include <stdarg.h>
+#include <ctype.h>
/* Used as default value for git_buf->ptr so that people can always
* assume ptr is non-NULL and zero terminated even for new git_bufs.
*/
char git_buf_initbuf[1];
+static char git_buf__oom;
+
#define ENSURE_SIZE(b, d) \
- if ((ssize_t)(d) > buf->asize && git_buf_grow(b, (d)) < GIT_SUCCESS)\
+ if ((d) > buf->asize && git_buf_grow(b, (d)) < GIT_SUCCESS)\
return GIT_ENOMEM;
@@ -31,8 +34,10 @@ void git_buf_init(git_buf *buf, size_t initial_size)
int git_buf_grow(git_buf *buf, size_t target_size)
{
int error = git_buf_try_grow(buf, target_size);
- if (error != GIT_SUCCESS)
- buf->asize = -1;
+ if (error != GIT_SUCCESS) {
+ buf->ptr = &git_buf__oom;
+ }
+
return error;
}
@@ -41,17 +46,17 @@ int git_buf_try_grow(git_buf *buf, size_t target_size)
char *new_ptr;
size_t new_size;
- if (buf->asize < 0)
+ if (buf->ptr == &git_buf__oom)
return GIT_ENOMEM;
- if (target_size <= (size_t)buf->asize)
+ if (target_size <= buf->asize)
return GIT_SUCCESS;
if (buf->asize == 0) {
new_size = target_size;
new_ptr = NULL;
} else {
- new_size = (size_t)buf->asize;
+ new_size = buf->asize;
new_ptr = buf->ptr;
}
@@ -64,7 +69,6 @@ int git_buf_try_grow(git_buf *buf, size_t target_size)
new_size = (new_size + 7) & ~7;
new_ptr = git__realloc(new_ptr, new_size);
- /* if realloc fails, return without modifying the git_buf */
if (!new_ptr)
return GIT_ENOMEM;
@@ -83,7 +87,7 @@ void git_buf_free(git_buf *buf)
{
if (!buf) return;
- if (buf->ptr != git_buf_initbuf)
+ if (buf->ptr != git_buf_initbuf && buf->ptr != &git_buf__oom)
git__free(buf->ptr);
git_buf_init(buf, 0);
@@ -98,12 +102,12 @@ void git_buf_clear(git_buf *buf)
int git_buf_oom(const git_buf *buf)
{
- return (buf->asize < 0);
+ return (buf->ptr == &git_buf__oom);
}
int git_buf_lasterror(const git_buf *buf)
{
- return (buf->asize < 0) ? GIT_ENOMEM : GIT_SUCCESS;
+ return (buf->ptr == &git_buf__oom) ? GIT_ENOMEM : GIT_SUCCESS;
}
int git_buf_set(git_buf *buf, const char *data, size_t len)
@@ -162,11 +166,12 @@ int git_buf_printf(git_buf *buf, const char *format, ...)
va_end(arglist);
if (len < 0) {
- buf->asize = -1;
+ free(buf->ptr);
+ buf->ptr = &git_buf__oom;
return GIT_ENOMEM;
}
- if (len + 1 <= buf->asize - buf->size) {
+ if ((size_t)len + 1 <= buf->asize - buf->size) {
buf->size += len;
break;
}
@@ -205,9 +210,9 @@ void git_buf_consume(git_buf *buf, const char *end)
}
}
-void git_buf_truncate(git_buf *buf, ssize_t len)
+void git_buf_truncate(git_buf *buf, size_t len)
{
- if (len >= 0 && len < buf->size) {
+ if (len < buf->size) {
buf->size = len;
buf->ptr[buf->size] = '\0';
}
@@ -230,7 +235,7 @@ char *git_buf_detach(git_buf *buf)
{
char *data = buf->ptr;
- if (buf->asize <= 0)
+ if (buf->asize == 0 || buf->ptr == &git_buf__oom)
return NULL;
git_buf_init(buf, 0);
@@ -238,7 +243,7 @@ char *git_buf_detach(git_buf *buf)
return data;
}
-void git_buf_attach(git_buf *buf, char *ptr, ssize_t asize)
+void git_buf_attach(git_buf *buf, char *ptr, size_t asize)
{
git_buf_free(buf);
@@ -372,3 +377,22 @@ int git_buf_join(
return error;
}
+
+void git_buf_rtrim(git_buf *buf)
+{
+ while (buf->size > 0) {
+ if (!isspace(buf->ptr[buf->size - 1]))
+ break;
+
+ buf->size--;
+ }
+
+ buf->ptr[buf->size] = '\0';
+}
+
+int git_buf_cmp(const git_buf *a, const git_buf *b)
+{
+ int result = memcmp(a->ptr, b->ptr, min(a->size, b->size));
+ return (result != 0) ? result :
+ (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
+}
diff --git a/src/buffer.h b/src/buffer.h
index 3969f461e..3cdd794af 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -11,7 +11,7 @@
typedef struct {
char *ptr;
- ssize_t asize, size;
+ size_t asize, size;
} git_buf;
extern char git_buf_initbuf[];
@@ -47,7 +47,7 @@ int git_buf_try_grow(git_buf *buf, size_t target_size);
void git_buf_free(git_buf *buf);
void git_buf_swap(git_buf *buf_a, git_buf *buf_b);
char *git_buf_detach(git_buf *buf);
-void git_buf_attach(git_buf *buf, char *ptr, ssize_t asize);
+void git_buf_attach(git_buf *buf, char *ptr, size_t asize);
/**
* Test if there have been any reallocation failures with this git_buf.
@@ -83,7 +83,7 @@ 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);
void git_buf_clear(git_buf *buf);
void git_buf_consume(git_buf *buf, const char *end);
-void git_buf_truncate(git_buf *buf, ssize_t len);
+void git_buf_truncate(git_buf *buf, size_t len);
void git_buf_rtruncate_at_char(git_buf *path, char separator);
int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...);
@@ -115,4 +115,9 @@ GIT_INLINE(int) git_buf_rfind_next(git_buf *buf, char ch)
return idx;
}
+/* Remove whitespace from the end of the buffer */
+void git_buf_rtrim(git_buf *buf);
+
+int git_buf_cmp(const git_buf *a, const git_buf *b);
+
#endif
diff --git a/src/cc-compat.h b/src/cc-compat.h
index 29cc2ec6a..3df36b61f 100644
--- a/src/cc-compat.h
+++ b/src/cc-compat.h
@@ -33,21 +33,7 @@
# define GIT_TYPEOF(x)
#endif
-#ifdef __cplusplus
-# define GIT_UNUSED(x)
-#else
-# ifdef __GNUC__
-# define GIT_UNUSED(x) x __attribute__ ((__unused__))
-# else
-# define GIT_UNUSED(x) x
-# endif
-#endif
-
-#if defined(_MSC_VER)
-#define GIT_UNUSED_ARG(x) ((void)(x)); /* note trailing ; */
-#else
-#define GIT_UNUSED_ARG(x)
-#endif
+#define GIT_UNUSED(x) ((void)(x))
/* Define the printf format specifer to use for size_t output */
#if defined(_MSC_VER) || defined(__MINGW32__)
diff --git a/src/config.c b/src/config.c
index 4ff1b2e72..912224158 100644
--- a/src/config.c
+++ b/src/config.c
@@ -209,23 +209,37 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value)
return file->set(file, name, value);
}
-/***********
- * Getters
- ***********/
+static int parse_bool(int *out, const char *value)
+{
+ /* A missing value means true */
+ if (value == NULL) {
+ *out = 1;
+ return GIT_SUCCESS;
+ }
-int git_config_get_int64(git_config *cfg, const char *name, int64_t *out)
+ if (!strcasecmp(value, "true") ||
+ !strcasecmp(value, "yes") ||
+ !strcasecmp(value, "on")) {
+ *out = 1;
+ return GIT_SUCCESS;
+ }
+ if (!strcasecmp(value, "false") ||
+ !strcasecmp(value, "no") ||
+ !strcasecmp(value, "off")) {
+ *out = 0;
+ return GIT_SUCCESS;
+ }
+
+ return GIT_EINVALIDTYPE;
+}
+
+static int parse_int64(int64_t *out, const char *value)
{
- const char *value, *num_end;
- int ret;
+ const char *num_end;
int64_t num;
- ret = git_config_get_string(cfg, name, &value);
- if (ret < GIT_SUCCESS)
- return git__rethrow(ret, "Failed to retrieve value for '%s'", name);
-
- ret = git__strtol64(&num, value, &num_end, 0);
- if (ret < GIT_SUCCESS)
- return git__rethrow(ret, "Failed to convert value for '%s'", name);
+ if (git__strtol64(&num, value, &num_end, 0) < 0)
+ return GIT_EINVALIDTYPE;
switch (*num_end) {
case 'g':
@@ -245,38 +259,112 @@ int git_config_get_int64(git_config *cfg, const char *name, int64_t *out)
/* check that that there are no more characters after the
* given modifier suffix */
if (num_end[1] != '\0')
- return git__throw(GIT_EINVALIDTYPE,
- "Failed to get value for '%s'. Invalid type suffix", name);
+ return GIT_EINVALIDTYPE;
/* fallthrough */
case '\0':
*out = num;
- return GIT_SUCCESS;
+ return 0;
default:
- return git__throw(GIT_EINVALIDTYPE,
- "Failed to get value for '%s'. Value is of invalid type", name);
+ return GIT_EINVALIDTYPE;
}
}
-int git_config_get_int32(git_config *cfg, const char *name, int32_t *out)
+static int parse_int32(int32_t *out, const char *value)
{
- int64_t tmp_long;
- int32_t tmp_int;
+ int64_t tmp;
+ int32_t truncate;
+
+ if (parse_int64(&tmp, value) < 0)
+ return GIT_EINVALIDTYPE;
+
+ truncate = tmp & 0xFFFFFFFF;
+ if (truncate != tmp)
+ return GIT_EOVERFLOW;
+
+ *out = truncate;
+ return 0;
+}
+
+/***********
+ * Getters
+ ***********/
+int git_config_get_mapped(git_config *cfg, const char *name, git_cvar_map *maps, size_t map_n, int *out)
+{
+ size_t i;
+ const char *value;
+ int error;
+
+ error = git_config_get_string(cfg, name, &value);
+ if (error < GIT_SUCCESS)
+ return error;
+
+ for (i = 0; i < map_n; ++i) {
+ git_cvar_map *m = maps + i;
+
+ switch (m->cvar_type) {
+ case GIT_CVAR_FALSE:
+ case GIT_CVAR_TRUE: {
+ int bool_val;
+
+ if (parse_bool(&bool_val, value) == 0 &&
+ bool_val == (int)m->cvar_type) {
+ *out = m->map_value;
+ return 0;
+ }
+
+ break;
+ }
+
+ case GIT_CVAR_INT32:
+ if (parse_int32(out, value) == 0)
+ return 0;
+
+ break;
+
+ case GIT_CVAR_STRING:
+ if (strcasecmp(value, m->str_match) == 0) {
+ *out = m->map_value;
+ return 0;
+ }
+ }
+ }
+
+ return git__throw(GIT_ENOTFOUND,
+ "Failed to map the '%s' config variable with a valid value", name);
+}
+
+int git_config_get_int64(git_config *cfg, const char *name, int64_t *out)
+{
+ const char *value;
int ret;
- ret = git_config_get_int64(cfg, name, &tmp_long);
+ ret = git_config_get_string(cfg, name, &value);
if (ret < GIT_SUCCESS)
- return git__rethrow(ret, "Failed to convert value for '%s'", name);
-
- tmp_int = tmp_long & 0xFFFFFFFF;
- if (tmp_int != tmp_long)
- return git__throw(GIT_EOVERFLOW, "Value for '%s' is too large", name);
+ return git__rethrow(ret, "Failed to retrieve value for '%s'", name);
- *out = tmp_int;
+ if (parse_int64(out, value) < 0)
+ return git__throw(GIT_EINVALIDTYPE, "Failed to parse '%s' as an integer", value);
- return ret;
+ return GIT_SUCCESS;
+}
+
+int git_config_get_int32(git_config *cfg, const char *name, int32_t *out)
+{
+ const char *value;
+ int error;
+
+ error = git_config_get_string(cfg, name, &value);
+ if (error < GIT_SUCCESS)
+ return git__rethrow(error, "Failed to get value for %s", name);
+
+ error = parse_int32(out, value);
+ if (error < GIT_SUCCESS)
+ return git__throw(GIT_EINVALIDTYPE, "Failed to parse '%s' as a 32-bit integer", value);
+
+ return GIT_SUCCESS;
}
int git_config_get_bool(git_config *cfg, const char *name, int *out)
@@ -288,33 +376,15 @@ int git_config_get_bool(git_config *cfg, const char *name, int *out)
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to get value for %s", name);
- /* A missing value means true */
- if (value == NULL) {
- *out = 1;
+ if (parse_bool(out, value) == 0)
return GIT_SUCCESS;
- }
- if (!strcasecmp(value, "true") ||
- !strcasecmp(value, "yes") ||
- !strcasecmp(value, "on")) {
- *out = 1;
- return GIT_SUCCESS;
- }
- if (!strcasecmp(value, "false") ||
- !strcasecmp(value, "no") ||
- !strcasecmp(value, "off")) {
- *out = 0;
+ if (parse_int32(out, value) == 0) {
+ *out = !!(*out);
return GIT_SUCCESS;
}
- /* Try to parse it as an integer */
- error = git_config_get_int32(cfg, name, out);
- if (error == GIT_SUCCESS)
- *out = !!(*out);
-
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to get value for %s", name);
- return error;
+ return git__throw(GIT_EINVALIDTYPE, "Failed to parse '%s' as a boolean value", value);
}
int git_config_get_string(git_config *cfg, const char *name, const char **out)
diff --git a/src/config_cache.c b/src/config_cache.c
new file mode 100644
index 000000000..5e20847f5
--- /dev/null
+++ b/src/config_cache.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * 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 "fileops.h"
+#include "hashtable.h"
+#include "config.h"
+#include "git2/config.h"
+#include "vector.h"
+#include "filter.h"
+#include "repository.h"
+
+struct map_data {
+ const char *cvar_name;
+ git_cvar_map *maps;
+ size_t map_count;
+ int default_value;
+};
+
+/*
+ * core.eol
+ * Sets the line ending type to use in the working directory for
+ * files that have the text property set. Alternatives are lf, crlf
+ * and native, which uses the platform’s native line ending. The default
+ * value is native. See gitattributes(5) for more information on
+ * end-of-line conversion.
+ */
+static git_cvar_map _cvar_map_eol[] = {
+ {GIT_CVAR_FALSE, NULL, GIT_EOL_UNSET},
+ {GIT_CVAR_STRING, "lf", GIT_EOL_LF},
+ {GIT_CVAR_STRING, "crlf", GIT_EOL_CRLF},
+ {GIT_CVAR_STRING, "native", GIT_EOL_NATIVE}
+};
+
+/*
+ * core.autocrlf
+ * Setting this variable to "true" is almost the same as setting
+ * the text attribute to "auto" on all files except that text files are
+ * not guaranteed to be normalized: files that contain CRLF in the
+ * repository will not be touched. Use this setting if you want to have
+ * CRLF line endings in your working directory even though the repository
+ * does not have normalized line endings. This variable can be set to input,
+ * in which case no output conversion is performed.
+ */
+static git_cvar_map _cvar_map_autocrlf[] = {
+ {GIT_CVAR_FALSE, NULL, GIT_AUTO_CRLF_FALSE},
+ {GIT_CVAR_TRUE, NULL, GIT_AUTO_CRLF_TRUE},
+ {GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT}
+};
+
+static struct map_data _cvar_maps[] = {
+ {"core.autocrlf", _cvar_map_autocrlf, ARRAY_SIZE(_cvar_map_autocrlf), GIT_AUTO_CRLF_DEFAULT},
+ {"core.eol", _cvar_map_eol, ARRAY_SIZE(_cvar_map_eol), GIT_EOL_DEFAULT}
+};
+
+int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar)
+{
+ *out = repo->cvar_cache[(int)cvar];
+
+ if (*out == GIT_CVAR_NOT_CACHED) {
+ struct map_data *data = &_cvar_maps[(int)cvar];
+ git_config *config;
+ int error;
+
+ error = git_repository_config__weakptr(&config, repo);
+ if (error < GIT_SUCCESS)
+ return error;
+
+ error = git_config_get_mapped(
+ config, data->cvar_name, data->maps, data->map_count, out);
+
+ if (error == GIT_ENOTFOUND)
+ *out = data->default_value;
+
+ else if (error < GIT_SUCCESS)
+ return error;
+
+ repo->cvar_cache[(int)cvar] = *out;
+ }
+
+ return GIT_SUCCESS;
+}
+
+void git_repository__cvar_cache_clear(git_repository *repo)
+{
+ int i;
+
+ for (i = 0; i < GIT_CVAR_CACHE_MAX; ++i)
+ repo->cvar_cache[i] = GIT_CVAR_NOT_CACHED;
+}
+
diff --git a/src/config_file.c b/src/config_file.c
index c9c7d11eb..3c7c593ec 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -73,7 +73,7 @@ typedef struct {
git_hashtable *values;
struct {
- git_fbuffer buffer;
+ git_buf buffer;
char *read_ptr;
int line_number;
int eof;
@@ -125,13 +125,12 @@ static int normalize_name(const char *in, char **out)
static void free_vars(git_hashtable *values)
{
- const char *GIT_UNUSED(_unused) = NULL;
cvar_t *var = NULL;
if (values == NULL)
return;
- GIT_HASHTABLE_FOREACH(values, _unused, var,
+ GIT_HASHTABLE_FOREACH_VALUE(values, var,
do {
cvar_t *next = CVAR_LIST_NEXT(var);
cvar_free(var);
@@ -151,6 +150,7 @@ static int config_open(git_config_file *cfg)
if (b->values == NULL)
return GIT_ENOMEM;
+ git_buf_init(&b->reader.buffer, 0);
error = git_futils_readbuffer(&b->reader.buffer, b->file_path);
/* It's fine if the file doesn't exist */
@@ -164,14 +164,14 @@ static int config_open(git_config_file *cfg)
if (error < GIT_SUCCESS)
goto cleanup;
- git_futils_freebuffer(&b->reader.buffer);
+ git_buf_free(&b->reader.buffer);
return GIT_SUCCESS;
cleanup:
free_vars(b->values);
b->values = NULL;
- git_futils_freebuffer(&b->reader.buffer);
+ git_buf_free(&b->reader.buffer);
return git__rethrow(error, "Failed to open config");
}
@@ -765,7 +765,7 @@ static int skip_bom(diskfile_backend *cfg)
{
static const char utf8_bom[] = "\xef\xbb\xbf";
- if (cfg->reader.buffer.len < sizeof(utf8_bom))
+ if (cfg->reader.buffer.size < sizeof(utf8_bom))
return GIT_SUCCESS;
if (memcmp(cfg->reader.read_ptr, utf8_bom, sizeof(utf8_bom)) == 0)
@@ -847,7 +847,7 @@ static int config_parse(diskfile_backend *cfg_file)
git_buf buf = GIT_BUF_INIT;
/* Initialize the reading position */
- cfg_file->reader.read_ptr = cfg_file->reader.buffer.data;
+ cfg_file->reader.read_ptr = cfg_file->reader.buffer.ptr;
cfg_file->reader.eof = 0;
/* If the file is empty, there's nothing for us to do */
@@ -976,10 +976,9 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
cfg->reader.read_ptr = NULL;
cfg->reader.eof = 1;
data_start = NULL;
- cfg->reader.buffer.len = 0;
- cfg->reader.buffer.data = NULL;
+ git_buf_clear(&cfg->reader.buffer);
} else {
- cfg->reader.read_ptr = cfg->reader.buffer.data;
+ cfg->reader.read_ptr = cfg->reader.buffer.ptr;
cfg->reader.eof = 0;
data_start = cfg->reader.read_ptr;
}
@@ -1093,7 +1092,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
/* And then the write out rest of the file */
error = git_filebuf_write(&file, post_start,
- cfg->reader.buffer.len - (post_start - data_start));
+ cfg->reader.buffer.size - (post_start - data_start));
if (error < GIT_SUCCESS) {
git__rethrow(error, "Failed to write the rest of the file");
@@ -1128,7 +1127,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
goto cleanup;
}
- error = git_filebuf_write(&file, cfg->reader.buffer.data, cfg->reader.buffer.len);
+ error = git_filebuf_write(&file, cfg->reader.buffer.ptr, cfg->reader.buffer.size);
if (error < GIT_SUCCESS) {
git__rethrow(error, "Failed to write original config content");
goto cleanup;
@@ -1155,7 +1154,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
else
error = git_filebuf_commit(&file, GIT_CONFIG_FILE_MODE);
- git_futils_freebuffer(&cfg->reader.buffer);
+ git_buf_free(&cfg->reader.buffer);
return error;
}
diff --git a/src/crlf.c b/src/crlf.c
new file mode 100644
index 000000000..f0ec7b736
--- /dev/null
+++ b/src/crlf.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * 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 "fileops.h"
+#include "hash.h"
+#include "filter.h"
+#include "repository.h"
+
+#include "git2/attr.h"
+
+struct crlf_attrs {
+ int crlf_action;
+ int eol;
+};
+
+struct crlf_filter {
+ git_filter f;
+ struct crlf_attrs attrs;
+};
+
+static int check_crlf(const char *value)
+{
+ if (GIT_ATTR_TRUE(value))
+ return GIT_CRLF_TEXT;
+
+ if (GIT_ATTR_FALSE(value))
+ return GIT_CRLF_BINARY;
+
+ if (GIT_ATTR_UNSPECIFIED(value))
+ return GIT_CRLF_GUESS;
+
+ if (strcmp(value, "input") == 0)
+ return GIT_CRLF_INPUT;
+
+ if (strcmp(value, "auto") == 0)
+ return GIT_CRLF_AUTO;
+
+ return GIT_CRLF_GUESS;
+}
+
+static int check_eol(const char *value)
+{
+ if (GIT_ATTR_UNSPECIFIED(value))
+ return GIT_EOL_UNSET;
+
+ if (strcmp(value, "lf") == 0)
+ return GIT_EOL_LF;
+
+ if (strcmp(value, "crlf") == 0)
+ return GIT_EOL_CRLF;
+
+ return GIT_EOL_UNSET;
+}
+
+static int crlf_input_action(struct crlf_attrs *ca)
+{
+ if (ca->crlf_action == GIT_CRLF_BINARY)
+ return GIT_CRLF_BINARY;
+
+ if (ca->eol == GIT_EOL_LF)
+ return GIT_CRLF_INPUT;
+
+ if (ca->eol == GIT_EOL_CRLF)
+ return GIT_CRLF_CRLF;
+
+ return ca->crlf_action;
+}
+
+static int crlf_load_attributes(struct crlf_attrs *ca, git_repository *repo, const char *path)
+{
+#define NUM_CONV_ATTRS 3
+
+ static const char *attr_names[NUM_CONV_ATTRS] = {
+ "crlf", "eol", "text",
+ };
+
+ const char *attr_vals[NUM_CONV_ATTRS];
+ int error;
+
+ error = git_attr_get_many(repo, path, NUM_CONV_ATTRS, attr_names, attr_vals);
+
+ if (error == GIT_ENOTFOUND) {
+ ca->crlf_action = GIT_CRLF_GUESS;
+ ca->eol = GIT_EOL_UNSET;
+ return 0;
+ }
+
+ if (error == GIT_SUCCESS) {
+ ca->crlf_action = check_crlf(attr_vals[2]); /* text */
+ if (ca->crlf_action == GIT_CRLF_GUESS)
+ ca->crlf_action = check_crlf(attr_vals[0]); /* clrf */
+
+ ca->eol = check_eol(attr_vals[1]); /* eol */
+ return 0;
+ }
+
+ return error;
+}
+
+static int drop_crlf(git_buf *dest, const git_buf *source)
+{
+ const char *scan = source->ptr, *next;
+ const char *scan_end = source->ptr + source->size;
+
+ /* Main scan loop. Find the next carriage return and copy the
+ * whole chunk up to that point to the destination buffer.
+ */
+ while ((next = memchr(scan, '\r', scan_end - scan)) != NULL) {
+ /* copy input up to \r */
+ if (next > scan)
+ git_buf_put(dest, scan, next - scan);
+
+ /* Do not drop \r unless it is followed by \n */
+ if (*(next + 1) != '\n')
+ git_buf_putc(dest, '\r');
+
+ scan = next + 1;
+ }
+
+ /* If there was no \r, then tell the library to skip this filter */
+ if (scan == source->ptr)
+ return -1;
+
+ /* Copy remaining input into dest */
+ git_buf_put(dest, scan, scan_end - scan);
+
+ return git_buf_lasterror(dest);
+}
+
+static int crlf_apply_to_odb(git_filter *self, git_buf *dest, const git_buf *source)
+{
+ struct crlf_filter *filter = (struct crlf_filter *)self;
+
+ assert(self && dest && source);
+
+ /* Empty file? Nothing to do */
+ if (source->size == 0)
+ return 0;
+
+ /* Heuristics to see if we can skip the conversion.
+ * Straight from Core Git.
+ */
+ if (filter->attrs.crlf_action == GIT_CRLF_AUTO ||
+ filter->attrs.crlf_action == GIT_CRLF_GUESS) {
+
+ git_text_stats stats;
+ git_text_gather_stats(&stats, source);
+
+ /*
+ * We're currently not going to even try to convert stuff
+ * that has bare CR characters. Does anybody do that crazy
+ * stuff?
+ */
+ if (stats.cr != stats.crlf)
+ return -1;
+
+ /*
+ * And add some heuristics for binary vs text, of course...
+ */
+ if (git_text_is_binary(&stats))
+ return -1;
+
+#if 0
+ if (crlf_action == CRLF_GUESS) {
+ /*
+ * If the file in the index has any CR in it, do not convert.
+ * This is the new safer autocrlf handling.
+ */
+ if (has_cr_in_index(path))
+ return 0;
+ }
+#endif
+
+ if (!stats.cr)
+ return -1;
+ }
+
+ /* Actually drop the carriage returns */
+ return drop_crlf(dest, source);
+}
+
+int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const char *path)
+{
+ struct crlf_attrs ca;
+ struct crlf_filter *filter;
+ int error;
+
+ /* Load gitattributes for the path */
+ if ((error = crlf_load_attributes(&ca, repo, path)) < 0)
+ return error;
+
+ /*
+ * Use the core Git logic to see if we should perform CRLF for this file
+ * based on its attributes & the value of `core.auto_crlf`
+ */
+ ca.crlf_action = crlf_input_action(&ca);
+
+ if (ca.crlf_action == GIT_CRLF_BINARY)
+ return 0;
+
+ if (ca.crlf_action == GIT_CRLF_GUESS) {
+ int auto_crlf;
+
+ if ((error = git_repository__cvar(
+ &auto_crlf, repo, GIT_CVAR_AUTO_CRLF)) < GIT_SUCCESS)
+ return error;
+
+ if (auto_crlf == GIT_AUTO_CRLF_FALSE)
+ return 0;
+ }
+
+ /* If we're good, we create a new filter object and push it
+ * into the filters array */
+ filter = git__malloc(sizeof(struct crlf_filter));
+ if (filter == NULL)
+ return GIT_ENOMEM;
+
+ filter->f.apply = &crlf_apply_to_odb;
+ filter->f.do_free = NULL;
+ memcpy(&filter->attrs, &ca, sizeof(struct crlf_attrs));
+
+ return git_vector_insert(filters, filter);
+}
+
diff --git a/src/diff.c b/src/diff.c
new file mode 100644
index 000000000..db339d74e
--- /dev/null
+++ b/src/diff.c
@@ -0,0 +1,599 @@
+/*
+ * Copyright (C) 2012 the libgit2 contributors
+ *
+ * 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 "git2/diff.h"
+#include "diff.h"
+#include "fileops.h"
+
+static void diff_delta__free(git_diff_delta *delta)
+{
+ if (!delta)
+ return;
+
+ if (delta->new_file.flags & GIT_DIFF_FILE_FREE_PATH) {
+ git__free((char *)delta->new_file.path);
+ delta->new_file.path = NULL;
+ }
+
+ if (delta->old_file.flags & GIT_DIFF_FILE_FREE_PATH) {
+ git__free((char *)delta->old_file.path);
+ delta->old_file.path = NULL;
+ }
+
+ git__free(delta);
+}
+
+static git_diff_delta *diff_delta__alloc(
+ git_diff_list *diff,
+ git_delta_t status,
+ const char *path)
+{
+ git_diff_delta *delta = git__calloc(1, sizeof(git_diff_delta));
+ if (!delta)
+ return NULL;
+
+ delta->old_file.path = git__strdup(path);
+ if (delta->old_file.path == NULL) {
+ git__free(delta);
+ return NULL;
+ }
+ delta->old_file.flags |= GIT_DIFF_FILE_FREE_PATH;
+ delta->new_file.path = delta->old_file.path;
+
+ if (diff->opts.flags & GIT_DIFF_REVERSE) {
+ switch (status) {
+ case GIT_DELTA_ADDED: status = GIT_DELTA_DELETED; break;
+ case GIT_DELTA_DELETED: status = GIT_DELTA_ADDED; break;
+ default: break; /* leave other status values alone */
+ }
+ }
+ delta->status = status;
+
+ return delta;
+}
+
+static git_diff_delta *diff_delta__dup(const git_diff_delta *d)
+{
+ git_diff_delta *delta = git__malloc(sizeof(git_diff_delta));
+ if (!delta)
+ return NULL;
+
+ memcpy(delta, d, sizeof(git_diff_delta));
+
+ delta->old_file.path = git__strdup(d->old_file.path);
+ if (delta->old_file.path == NULL) {
+ git__free(delta);
+ return NULL;
+ }
+ delta->old_file.flags |= GIT_DIFF_FILE_FREE_PATH;
+
+ if (d->new_file.path != d->old_file.path) {
+ delta->new_file.path = git__strdup(d->new_file.path);
+ if (delta->new_file.path == NULL) {
+ git__free(delta->old_file.path);
+ git__free(delta);
+ return NULL;
+ }
+ delta->new_file.flags |= GIT_DIFF_FILE_FREE_PATH;
+ } else {
+ delta->new_file.path = delta->old_file.path;
+ delta->new_file.flags &= ~GIT_DIFF_FILE_FREE_PATH;
+ }
+
+ return delta;
+}
+
+static git_diff_delta *diff_delta__merge_like_cgit(
+ const git_diff_delta *a, const git_diff_delta *b)
+{
+ git_diff_delta *dup = diff_delta__dup(a);
+ if (!dup)
+ return NULL;
+
+ if (git_oid_cmp(&dup->new_file.oid, &b->new_file.oid) == 0)
+ return dup;
+
+ git_oid_cpy(&dup->new_file.oid, &b->new_file.oid);
+
+ dup->new_file.mode = b->new_file.mode;
+ dup->new_file.size = b->new_file.size;
+ dup->new_file.flags =
+ (dup->new_file.flags & GIT_DIFF_FILE_FREE_PATH) |
+ (b->new_file.flags & ~GIT_DIFF_FILE_FREE_PATH);
+
+ /* Emulate C git for merging two diffs (a la 'git diff <sha>').
+ *
+ * When C git does a diff between the work dir and a tree, it actually
+ * diffs with the index but uses the workdir contents. This emulates
+ * those choices so we can emulate the type of diff.
+ */
+ if (git_oid_cmp(&dup->old_file.oid, &dup->new_file.oid) == 0) {
+ if (dup->status == GIT_DELTA_DELETED)
+ /* preserve pending delete info */;
+ else if (b->status == GIT_DELTA_UNTRACKED ||
+ b->status == GIT_DELTA_IGNORED)
+ dup->status = b->status;
+ else
+ dup->status = GIT_DELTA_UNMODIFIED;
+ }
+ else if (dup->status == GIT_DELTA_UNMODIFIED ||
+ b->status == GIT_DELTA_DELETED)
+ dup->status = b->status;
+
+ return dup;
+}
+
+static int diff_delta__from_one(
+ git_diff_list *diff,
+ git_delta_t status,
+ const git_index_entry *entry)
+{
+ int error;
+ git_diff_delta *delta = diff_delta__alloc(diff, status, entry->path);
+ if (!delta)
+ return git__rethrow(GIT_ENOMEM, "Could not allocate diff record");
+
+ /* This fn is just for single-sided diffs */
+ assert(status != GIT_DELTA_MODIFIED);
+
+ if (delta->status == GIT_DELTA_DELETED) {
+ delta->old_file.mode = entry->mode;
+ delta->old_file.size = entry->file_size;
+ git_oid_cpy(&delta->old_file.oid, &entry->oid);
+ } else /* ADDED, IGNORED, UNTRACKED */ {
+ delta->new_file.mode = entry->mode;
+ delta->new_file.size = entry->file_size;
+ git_oid_cpy(&delta->new_file.oid, &entry->oid);
+ }
+
+ delta->old_file.flags |= GIT_DIFF_FILE_VALID_OID;
+ delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
+
+ if ((error = git_vector_insert(&diff->deltas, delta)) < GIT_SUCCESS)
+ diff_delta__free(delta);
+
+ return error;
+}
+
+static int diff_delta__from_two(
+ git_diff_list *diff,
+ git_delta_t status,
+ const git_index_entry *old_entry,
+ const git_index_entry *new_entry,
+ git_oid *new_oid)
+{
+ int error;
+ git_diff_delta *delta;
+
+ if ((diff->opts.flags & GIT_DIFF_REVERSE) != 0) {
+ const git_index_entry *temp = old_entry;
+ old_entry = new_entry;
+ new_entry = temp;
+ }
+
+ delta = diff_delta__alloc(diff, status, old_entry->path);
+ if (!delta)
+ return git__rethrow(GIT_ENOMEM, "Could not allocate diff record");
+
+ delta->old_file.mode = old_entry->mode;
+ git_oid_cpy(&delta->old_file.oid, &old_entry->oid);
+ delta->old_file.flags |= GIT_DIFF_FILE_VALID_OID;
+
+ delta->new_file.mode = new_entry->mode;
+ git_oid_cpy(&delta->new_file.oid, new_oid ? new_oid : &new_entry->oid);
+ if (new_oid || !git_oid_iszero(&new_entry->oid))
+ delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
+
+ if ((error = git_vector_insert(&diff->deltas, delta)) < GIT_SUCCESS)
+ diff_delta__free(delta);
+
+ return error;
+}
+
+#define DIFF_OLD_PREFIX_DEFAULT "a/"
+#define DIFF_NEW_PREFIX_DEFAULT "b/"
+
+static char *diff_strdup_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 int diff_delta__cmp(const void *a, const void *b)
+{
+ const git_diff_delta *da = a, *db = b;
+ int val = strcmp(da->old_file.path, db->old_file.path);
+ return val ? val : ((int)da->status - (int)db->status);
+}
+
+static git_diff_list *git_diff_list_alloc(
+ git_repository *repo, const git_diff_options *opts)
+{
+ git_diff_list *diff = git__calloc(1, sizeof(git_diff_list));
+ if (diff == NULL)
+ return NULL;
+
+ diff->repo = repo;
+
+ if (opts == NULL)
+ return diff;
+
+ memcpy(&diff->opts, opts, sizeof(git_diff_options));
+
+ diff->opts.old_prefix = diff_strdup_prefix(
+ opts->old_prefix ? opts->old_prefix : DIFF_OLD_PREFIX_DEFAULT);
+ diff->opts.new_prefix = diff_strdup_prefix(
+ opts->new_prefix ? opts->new_prefix : DIFF_NEW_PREFIX_DEFAULT);
+
+ if (!diff->opts.old_prefix || !diff->opts.new_prefix) {
+ git__free(diff);
+ return NULL;
+ }
+
+ if (diff->opts.flags & GIT_DIFF_REVERSE) {
+ char *swap = diff->opts.old_prefix;
+ diff->opts.old_prefix = diff->opts.new_prefix;
+ diff->opts.new_prefix = swap;
+ }
+
+ if (git_vector_init(&diff->deltas, 0, diff_delta__cmp) < GIT_SUCCESS) {
+ git__free(diff->opts.old_prefix);
+ git__free(diff->opts.new_prefix);
+ git__free(diff);
+ return NULL;
+ }
+
+ /* do something safe with the pathspec strarray */
+
+ return diff;
+}
+
+void git_diff_list_free(git_diff_list *diff)
+{
+ git_diff_delta *delta;
+ unsigned int i;
+
+ if (!diff)
+ return;
+
+ git_vector_foreach(&diff->deltas, i, delta) {
+ diff_delta__free(delta);
+ diff->deltas.contents[i] = NULL;
+ }
+ git_vector_free(&diff->deltas);
+ git__free(diff->opts.old_prefix);
+ git__free(diff->opts.new_prefix);
+ git__free(diff);
+}
+
+static int oid_for_workdir_item(
+ git_repository *repo,
+ const git_index_entry *item,
+ git_oid *oid)
+{
+ int error = GIT_SUCCESS;
+ git_buf full_path = GIT_BUF_INIT;
+
+ error = git_buf_joinpath(
+ &full_path, git_repository_workdir(repo), item->path);
+ if (error != GIT_SUCCESS)
+ return error;
+
+ /* otherwise calculate OID for file */
+ if (S_ISLNK(item->mode))
+ error = git_odb__hashlink(oid, full_path.ptr);
+ else if (!git__is_sizet(item->file_size))
+ error = git__throw(GIT_ERROR, "File size overflow for 32-bit systems");
+ else {
+ int fd;
+
+ if ((fd = p_open(full_path.ptr, O_RDONLY)) < 0)
+ error = git__throw(
+ GIT_EOSERR, "Could not open '%s'", item->path);
+ else {
+ error = git_odb__hashfd(
+ oid, fd, (size_t)item->file_size, GIT_OBJ_BLOB);
+ p_close(fd);
+ }
+ }
+
+ git_buf_free(&full_path);
+
+ return error;
+}
+
+static int maybe_modified(
+ git_iterator *old_iter,
+ const git_index_entry *oitem,
+ git_iterator *new_iter,
+ const git_index_entry *nitem,
+ git_diff_list *diff)
+{
+ int error = GIT_SUCCESS;
+ git_oid noid, *use_noid = NULL;
+
+ GIT_UNUSED(old_iter);
+
+ /* support "assume unchanged" & "skip worktree" bits */
+ if ((oitem->flags_extended & GIT_IDXENTRY_INTENT_TO_ADD) != 0 ||
+ (oitem->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE) != 0)
+ return GIT_SUCCESS;
+
+ if (GIT_MODE_TYPE(oitem->mode) != GIT_MODE_TYPE(nitem->mode)) {
+ error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem);
+ if (error == GIT_SUCCESS)
+ error = diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem);
+ return error;
+ }
+
+ if (git_oid_cmp(&oitem->oid, &nitem->oid) == 0 &&
+ oitem->mode == nitem->mode)
+ return GIT_SUCCESS;
+
+ if (git_oid_iszero(&nitem->oid) && new_iter->type == GIT_ITERATOR_WORKDIR) {
+ /* if they files look exactly alike, then we'll assume the same */
+ if (oitem->file_size == nitem->file_size &&
+ oitem->ctime.seconds == nitem->ctime.seconds &&
+ oitem->mtime.seconds == nitem->mtime.seconds &&
+ oitem->dev == nitem->dev &&
+ oitem->ino == nitem->ino &&
+ oitem->uid == nitem->uid &&
+ oitem->gid == nitem->gid)
+ return GIT_SUCCESS;
+
+ /* TODO: check git attributes so we will not have to read the file
+ * in if it is marked binary.
+ */
+ error = oid_for_workdir_item(diff->repo, nitem, &noid);
+ if (error != GIT_SUCCESS)
+ return error;
+
+ if (git_oid_cmp(&oitem->oid, &noid) == 0 &&
+ oitem->mode == nitem->mode)
+ return GIT_SUCCESS;
+
+ /* store calculated oid so we don't have to recalc later */
+ use_noid = &noid;
+ }
+
+ return diff_delta__from_two(
+ diff, GIT_DELTA_MODIFIED, oitem, nitem, use_noid);
+}
+
+static int diff_from_iterators(
+ git_repository *repo,
+ const git_diff_options *opts, /**< can be NULL for defaults */
+ git_iterator *old_iter,
+ git_iterator *new_iter,
+ git_diff_list **diff_ptr)
+{
+ int error;
+ const git_index_entry *oitem, *nitem;
+ char *ignore_prefix = NULL;
+ git_diff_list *diff = git_diff_list_alloc(repo, opts);
+ if (!diff) {
+ error = GIT_ENOMEM;
+ goto cleanup;
+ }
+
+ diff->old_src = old_iter->type;
+ diff->new_src = new_iter->type;
+
+ if ((error = git_iterator_current(old_iter, &oitem)) < GIT_SUCCESS ||
+ (error = git_iterator_current(new_iter, &nitem)) < GIT_SUCCESS)
+ goto cleanup;
+
+ /* run iterators building diffs */
+ while (!error && (oitem || nitem)) {
+
+ /* create DELETED records for old items not matched in new */
+ if (oitem && (!nitem || strcmp(oitem->path, nitem->path) < 0)) {
+ error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem);
+ if (error == GIT_SUCCESS)
+ error = git_iterator_advance(old_iter, &oitem);
+ continue;
+ }
+
+ /* create ADDED, TRACKED, or IGNORED records for new items not
+ * matched in old (and/or descend into directories as needed)
+ */
+ if (nitem && (!oitem || strcmp(oitem->path, nitem->path) > 0)) {
+ int is_ignored;
+ git_delta_t delta_type = GIT_DELTA_ADDED;
+
+ /* contained in ignored parent directory, so this can be skipped. */
+ if (ignore_prefix != NULL &&
+ git__prefixcmp(nitem->path, ignore_prefix) == 0)
+ {
+ error = git_iterator_advance(new_iter, &nitem);
+ continue;
+ }
+
+ is_ignored = git_iterator_current_is_ignored(new_iter);
+
+ if (S_ISDIR(nitem->mode)) {
+ if (git__prefixcmp(oitem->path, nitem->path) == 0) {
+ if (is_ignored)
+ ignore_prefix = nitem->path;
+ error = git_iterator_advance_into_directory(new_iter, &nitem);
+ continue;
+ }
+ delta_type = GIT_DELTA_UNTRACKED;
+ }
+ else if (is_ignored)
+ delta_type = GIT_DELTA_IGNORED;
+ else if (new_iter->type == GIT_ITERATOR_WORKDIR)
+ delta_type = GIT_DELTA_UNTRACKED;
+
+ error = diff_delta__from_one(diff, delta_type, nitem);
+ if (error == GIT_SUCCESS)
+ error = git_iterator_advance(new_iter, &nitem);
+ continue;
+ }
+
+ /* otherwise item paths match, so create MODIFIED record
+ * (or ADDED and DELETED pair if type changed)
+ */
+ assert(oitem && nitem && strcmp(oitem->path, nitem->path) == 0);
+
+ error = maybe_modified(old_iter, oitem, new_iter, nitem, diff);
+ if (error == GIT_SUCCESS)
+ error = git_iterator_advance(old_iter, &oitem);
+ if (error == GIT_SUCCESS)
+ error = git_iterator_advance(new_iter, &nitem);
+ }
+
+cleanup:
+ git_iterator_free(old_iter);
+ git_iterator_free(new_iter);
+
+ if (error != GIT_SUCCESS) {
+ git_diff_list_free(diff);
+ diff = NULL;
+ }
+
+ *diff_ptr = diff;
+
+ return error;
+}
+
+
+int git_diff_tree_to_tree(
+ git_repository *repo,
+ const git_diff_options *opts, /**< can be NULL for defaults */
+ git_tree *old_tree,
+ git_tree *new_tree,
+ git_diff_list **diff)
+{
+ int error;
+ git_iterator *a = NULL, *b = NULL;
+
+ assert(repo && old_tree && new_tree && diff);
+
+ if ((error = git_iterator_for_tree(repo, old_tree, &a)) < GIT_SUCCESS ||
+ (error = git_iterator_for_tree(repo, new_tree, &b)) < GIT_SUCCESS)
+ return error;
+
+ return diff_from_iterators(repo, opts, a, b, diff);
+}
+
+int git_diff_index_to_tree(
+ git_repository *repo,
+ const git_diff_options *opts,
+ git_tree *old_tree,
+ git_diff_list **diff)
+{
+ int error;
+ git_iterator *a = NULL, *b = NULL;
+
+ assert(repo && old_tree && diff);
+
+ if ((error = git_iterator_for_tree(repo, old_tree, &a)) < GIT_SUCCESS ||
+ (error = git_iterator_for_index(repo, &b)) < GIT_SUCCESS)
+ return error;
+
+ return diff_from_iterators(repo, opts, a, b, diff);
+}
+
+int git_diff_workdir_to_index(
+ git_repository *repo,
+ const git_diff_options *opts,
+ git_diff_list **diff)
+{
+ int error;
+ git_iterator *a = NULL, *b = NULL;
+
+ assert(repo && diff);
+
+ if ((error = git_iterator_for_index(repo, &a)) < GIT_SUCCESS ||
+ (error = git_iterator_for_workdir(repo, &b)) < GIT_SUCCESS)
+ return error;
+
+ return diff_from_iterators(repo, opts, a, b, diff);
+}
+
+
+int git_diff_workdir_to_tree(
+ git_repository *repo,
+ const git_diff_options *opts,
+ git_tree *old_tree,
+ git_diff_list **diff)
+{
+ int error;
+ git_iterator *a = NULL, *b = NULL;
+
+ assert(repo && old_tree && diff);
+
+ if ((error = git_iterator_for_tree(repo, old_tree, &a)) < GIT_SUCCESS ||
+ (error = git_iterator_for_workdir(repo, &b)) < GIT_SUCCESS)
+ return error;
+
+ return diff_from_iterators(repo, opts, a, b, diff);
+}
+
+int git_diff_merge(
+ git_diff_list *onto,
+ const git_diff_list *from)
+{
+ int error;
+ unsigned int i = 0, j = 0;
+ git_vector onto_new;
+ git_diff_delta *delta;
+
+ error = git_vector_init(&onto_new, onto->deltas.length, diff_delta__cmp);
+ if (error < GIT_SUCCESS)
+ return error;
+
+ while (i < onto->deltas.length || j < from->deltas.length) {
+ git_diff_delta *o = git_vector_get(&onto->deltas, i);
+ const git_diff_delta *f = git_vector_get_const(&from->deltas, j);
+ const char *opath =
+ !o ? NULL : o->old_file.path ? o->old_file.path : o->new_file.path;
+ const char *fpath =
+ !f ? NULL : f->old_file.path ? f->old_file.path : f->new_file.path;
+
+ if (opath && (!fpath || strcmp(opath, fpath) < 0)) {
+ delta = diff_delta__dup(o);
+ i++;
+ } else if (fpath && (!opath || strcmp(opath, fpath) > 0)) {
+ delta = diff_delta__dup(f);
+ j++;
+ } else {
+ delta = diff_delta__merge_like_cgit(o, f);
+ i++;
+ j++;
+ }
+
+ if (!delta)
+ error = GIT_ENOMEM;
+ else
+ error = git_vector_insert(&onto_new, delta);
+
+ if (error != GIT_SUCCESS)
+ break;
+ }
+
+ if (error == GIT_SUCCESS) {
+ git_vector_swap(&onto->deltas, &onto_new);
+ onto->new_src = from->new_src;
+ }
+
+ git_vector_foreach(&onto_new, i, delta)
+ diff_delta__free(delta);
+ git_vector_free(&onto_new);
+
+ return error;
+}
diff --git a/src/diff.h b/src/diff.h
new file mode 100644
index 000000000..7d69199ea
--- /dev/null
+++ b/src/diff.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_diff_h__
+#define INCLUDE_diff_h__
+
+#include <stdio.h>
+#include "vector.h"
+#include "buffer.h"
+#include "iterator.h"
+#include "repository.h"
+
+struct git_diff_list {
+ git_repository *repo;
+ git_diff_options opts;
+ git_vector deltas; /* vector of git_diff_file_delta */
+ git_iterator_type_t old_src;
+ git_iterator_type_t new_src;
+};
+
+#endif
+
diff --git a/src/diff_output.c b/src/diff_output.c
new file mode 100644
index 000000000..2c6bacc81
--- /dev/null
+++ b/src/diff_output.c
@@ -0,0 +1,739 @@
+/*
+ * Copyright (C) 2012 the libgit2 contributors
+ *
+ * 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 "git2/diff.h"
+#include "git2/attr.h"
+#include "git2/blob.h"
+#include "xdiff/xdiff.h"
+#include <ctype.h>
+#include "diff.h"
+#include "map.h"
+#include "fileops.h"
+#include "filter.h"
+
+typedef struct {
+ git_diff_list *diff;
+ void *cb_data;
+ git_diff_hunk_fn hunk_cb;
+ git_diff_line_fn line_cb;
+ unsigned int index;
+ git_diff_delta *delta;
+} diff_output_info;
+
+static int read_next_int(const char **str, int *value)
+{
+ const char *scan = *str;
+ int v = 0, digits = 0;
+ /* find next digit */
+ for (scan = *str; *scan && !isdigit(*scan); scan++);
+ /* parse next number */
+ for (; isdigit(*scan); scan++, digits++)
+ v = (v * 10) + (*scan - '0');
+ *str = scan;
+ *value = v;
+ return (digits > 0) ? GIT_SUCCESS : GIT_ENOTFOUND;
+}
+
+static int diff_output_cb(void *priv, mmbuffer_t *bufs, int len)
+{
+ int err = GIT_SUCCESS;
+ diff_output_info *info = priv;
+
+ if (len == 1 && info->hunk_cb) {
+ git_diff_range range = { -1, 0, -1, 0 };
+
+ /* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */
+ if (bufs[0].ptr[0] == '@') {
+ const char *scan = bufs[0].ptr;
+ if (!(err = read_next_int(&scan, &range.old_start)) && *scan == ',')
+ err = read_next_int(&scan, &range.old_lines);
+ if (!err &&
+ !(err = read_next_int(&scan, &range.new_start)) && *scan == ',')
+ err = read_next_int(&scan, &range.new_lines);
+ if (!err && range.old_start >= 0 && range.new_start >= 0)
+ err = info->hunk_cb(
+ info->cb_data, info->delta, &range, bufs[0].ptr, bufs[0].size);
+ }
+ }
+ else if ((len == 2 || len == 3) && info->line_cb) {
+ int origin;
+
+ /* expect " "/"-"/"+", then data, then maybe newline */
+ origin =
+ (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION :
+ (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION :
+ GIT_DIFF_LINE_CONTEXT;
+
+ err = info->line_cb(
+ info->cb_data, info->delta, origin, bufs[1].ptr, bufs[1].size);
+
+ /* deal with adding and removing newline at EOF */
+ if (err == GIT_SUCCESS && len == 3) {
+ if (origin == GIT_DIFF_LINE_ADDITION)
+ origin = GIT_DIFF_LINE_ADD_EOFNL;
+ else
+ origin = GIT_DIFF_LINE_DEL_EOFNL;
+
+ err = info->line_cb(
+ info->cb_data, info->delta, origin, bufs[2].ptr, bufs[2].size);
+ }
+ }
+
+ return err;
+}
+
+#define BINARY_DIFF_FLAGS (GIT_DIFF_FILE_BINARY|GIT_DIFF_FILE_NOT_BINARY)
+
+static int set_file_is_binary_by_attr(git_repository *repo, git_diff_file *file)
+{
+ const char *value;
+ int error = git_attr_get(repo, file->path, "diff", &value);
+ if (error != GIT_SUCCESS)
+ return error;
+ if (GIT_ATTR_FALSE(value))
+ file->flags |= GIT_DIFF_FILE_BINARY;
+ else if (GIT_ATTR_TRUE(value))
+ file->flags |= GIT_DIFF_FILE_NOT_BINARY;
+ /* otherwise leave file->flags alone */
+ return error;
+}
+
+static void set_delta_is_binary(git_diff_delta *delta)
+{
+ if ((delta->old_file.flags & GIT_DIFF_FILE_BINARY) != 0 ||
+ (delta->new_file.flags & GIT_DIFF_FILE_BINARY) != 0)
+ delta->binary = 1;
+ else if ((delta->old_file.flags & GIT_DIFF_FILE_NOT_BINARY) != 0 ||
+ (delta->new_file.flags & GIT_DIFF_FILE_NOT_BINARY) != 0)
+ delta->binary = 0;
+ /* otherwise leave delta->binary value untouched */
+}
+
+static int file_is_binary_by_attr(
+ git_diff_list *diff,
+ git_diff_delta *delta)
+{
+ int error, mirror_new;
+
+ delta->binary = -1;
+
+ /* make sure files are conceivably mmap-able */
+ if ((git_off_t)((size_t)delta->old_file.size) != delta->old_file.size ||
+ (git_off_t)((size_t)delta->new_file.size) != delta->new_file.size)
+ {
+ delta->old_file.flags |= GIT_DIFF_FILE_BINARY;
+ delta->new_file.flags |= GIT_DIFF_FILE_BINARY;
+ delta->binary = 1;
+ return GIT_SUCCESS;
+ }
+
+ /* check if user is forcing us to text diff these files */
+ if (diff->opts.flags & GIT_DIFF_FORCE_TEXT) {
+ delta->old_file.flags |= GIT_DIFF_FILE_NOT_BINARY;
+ delta->new_file.flags |= GIT_DIFF_FILE_NOT_BINARY;
+ delta->binary = 0;
+ return GIT_SUCCESS;
+ }
+
+ /* check diff attribute +, -, or 0 */
+ error = set_file_is_binary_by_attr(diff->repo, &delta->old_file);
+ if (error != GIT_SUCCESS)
+ return error;
+
+ mirror_new = (delta->new_file.path == delta->old_file.path ||
+ strcmp(delta->new_file.path, delta->old_file.path) == 0);
+ if (mirror_new)
+ delta->new_file.flags &= (delta->old_file.flags & BINARY_DIFF_FLAGS);
+ else
+ error = set_file_is_binary_by_attr(diff->repo, &delta->new_file);
+
+ set_delta_is_binary(delta);
+
+ return error;
+}
+
+static int file_is_binary_by_content(
+ git_diff_list *diff,
+ git_diff_delta *delta,
+ git_map *old_data,
+ git_map *new_data)
+{
+ git_buf search;
+ git_text_stats stats;
+
+ GIT_UNUSED(diff);
+
+ if ((delta->old_file.flags & BINARY_DIFF_FLAGS) == 0) {
+ search.ptr = old_data->data;
+ search.size = min(old_data->len, 4000);
+
+ git_text_gather_stats(&stats, &search);
+
+ if (git_text_is_binary(&stats))
+ delta->old_file.flags |= GIT_DIFF_FILE_BINARY;
+ else
+ delta->old_file.flags |= GIT_DIFF_FILE_NOT_BINARY;
+ }
+
+ if ((delta->new_file.flags & BINARY_DIFF_FLAGS) == 0) {
+ search.ptr = new_data->data;
+ search.size = min(new_data->len, 4000);
+
+ git_text_gather_stats(&stats, &search);
+
+ if (git_text_is_binary(&stats))
+ delta->new_file.flags |= GIT_DIFF_FILE_BINARY;
+ else
+ delta->new_file.flags |= GIT_DIFF_FILE_NOT_BINARY;
+ }
+
+ set_delta_is_binary(delta);
+
+ /* TODO: if value != NULL, implement diff drivers */
+
+ return GIT_SUCCESS;
+}
+
+static void setup_xdiff_options(
+ git_diff_options *opts, xdemitconf_t *cfg, xpparam_t *param)
+{
+ memset(cfg, 0, sizeof(xdemitconf_t));
+ memset(param, 0, sizeof(xpparam_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)
+ param->flags |= XDF_WHITESPACE_FLAGS;
+ if (opts->flags & GIT_DIFF_IGNORE_WHITESPACE_CHANGE)
+ param->flags |= XDF_IGNORE_WHITESPACE_CHANGE;
+ if (opts->flags & GIT_DIFF_IGNORE_WHITESPACE_EOL)
+ param->flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
+}
+
+static int get_blob_content(
+ git_repository *repo,
+ const git_oid *oid,
+ git_map *map,
+ git_blob **blob)
+{
+ int error;
+
+ if (git_oid_iszero(oid))
+ return GIT_SUCCESS;
+
+ if ((error = git_blob_lookup(blob, repo, oid)) == GIT_SUCCESS) {
+ map->data = (void *)git_blob_rawcontent(*blob);
+ map->len = git_blob_rawsize(*blob);
+ }
+
+ return error;
+}
+
+static int get_workdir_content(
+ git_repository *repo,
+ git_diff_file *file,
+ git_map *map)
+{
+ git_buf full_path = GIT_BUF_INIT;
+ int error = git_buf_joinpath(
+ &full_path, git_repository_workdir(repo), file->path);
+ if (error != GIT_SUCCESS)
+ return error;
+
+ if (S_ISLNK(file->mode)) {
+ file->flags |= GIT_DIFF_FILE_FREE_DATA;
+ file->flags |= GIT_DIFF_FILE_BINARY;
+
+ map->data = git__malloc((size_t)file->size + 1);
+ if (map->data == NULL)
+ error = GIT_ENOMEM;
+ else {
+ ssize_t read_len =
+ p_readlink(full_path.ptr, map->data, (size_t)file->size + 1);
+ if (read_len != (ssize_t)file->size)
+ error = git__throw(
+ GIT_EOSERR, "Failed to read symlink %s", file->path);
+ else
+ map->len = read_len;
+
+ }
+ }
+ else {
+ error = git_futils_mmap_ro_file(map, full_path.ptr);
+ file->flags |= GIT_DIFF_FILE_UNMAP_DATA;
+ }
+ git_buf_free(&full_path);
+ return error;
+}
+
+static void release_content(git_diff_file *file, git_map *map, git_blob *blob)
+{
+ if (blob != NULL)
+ git_blob_free(blob);
+
+ if (file->flags & GIT_DIFF_FILE_FREE_DATA) {
+ git__free(map->data);
+ map->data = NULL;
+ file->flags &= ~GIT_DIFF_FILE_FREE_DATA;
+ }
+ else if (file->flags & GIT_DIFF_FILE_UNMAP_DATA) {
+ git_futils_mmap_free(map);
+ map->data = NULL;
+ file->flags &= ~GIT_DIFF_FILE_UNMAP_DATA;
+ }
+}
+
+int git_diff_foreach(
+ git_diff_list *diff,
+ void *data,
+ git_diff_file_fn file_cb,
+ git_diff_hunk_fn hunk_cb,
+ git_diff_line_fn line_cb)
+{
+ int error = GIT_SUCCESS;
+ diff_output_info info;
+ git_diff_delta *delta;
+ xpparam_t xdiff_params;
+ xdemitconf_t xdiff_config;
+ xdemitcb_t xdiff_callback;
+
+ info.diff = diff;
+ info.cb_data = data;
+ info.hunk_cb = hunk_cb;
+ info.line_cb = line_cb;
+
+ setup_xdiff_options(&diff->opts, &xdiff_config, &xdiff_params);
+ memset(&xdiff_callback, 0, sizeof(xdiff_callback));
+ xdiff_callback.outf = diff_output_cb;
+ xdiff_callback.priv = &info;
+
+ git_vector_foreach(&diff->deltas, info.index, delta) {
+ git_blob *old_blob = NULL, *new_blob = NULL;
+ git_map old_data, new_data;
+ mmfile_t old_xdiff_data, new_xdiff_data;
+
+ if (delta->status == GIT_DELTA_UNMODIFIED)
+ continue;
+
+ if (delta->status == GIT_DELTA_IGNORED &&
+ (diff->opts.flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
+ continue;
+
+ if (delta->status == GIT_DELTA_UNTRACKED &&
+ (diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
+ continue;
+
+ error = file_is_binary_by_attr(diff, delta);
+ if (error < GIT_SUCCESS)
+ goto cleanup;
+
+ old_data.data = "";
+ old_data.len = 0;
+ new_data.data = "";
+ new_data.len = 0;
+
+ /* TODO: Partial blob reading to defer loading whole blob.
+ * I.e. I want a blob with just the first 4kb loaded, then
+ * later on I will read the rest of the blob if needed.
+ */
+
+ /* map files */
+ if (delta->binary != 1 &&
+ (hunk_cb || line_cb) &&
+ (delta->status == GIT_DELTA_DELETED ||
+ delta->status == GIT_DELTA_MODIFIED))
+ {
+ if (diff->old_src == GIT_ITERATOR_WORKDIR)
+ error = get_workdir_content(diff->repo, &delta->old_file, &old_data);
+ else
+ error = get_blob_content(
+ diff->repo, &delta->old_file.oid, &old_data, &old_blob);
+ if (error != GIT_SUCCESS)
+ goto cleanup;
+ }
+
+ if (delta->binary != 1 &&
+ (hunk_cb || line_cb || git_oid_iszero(&delta->new_file.oid)) &&
+ (delta->status == GIT_DELTA_ADDED ||
+ delta->status == GIT_DELTA_MODIFIED))
+ {
+ if (diff->new_src == GIT_ITERATOR_WORKDIR)
+ error = get_workdir_content(diff->repo, &delta->new_file, &new_data);
+ else
+ error = get_blob_content(
+ diff->repo, &delta->new_file.oid, &new_data, &new_blob);
+ if (error != GIT_SUCCESS)
+ goto cleanup;
+
+ if ((delta->new_file.flags | GIT_DIFF_FILE_VALID_OID) == 0) {
+ error = git_odb_hash(
+ &delta->new_file.oid, new_data.data, new_data.len, GIT_OBJ_BLOB);
+ if (error != GIT_SUCCESS)
+ goto cleanup;
+
+ /* since we did not have the definitive oid, we may have
+ * incorrect status and need to skip this item.
+ */
+ if (git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid) == 0) {
+ delta->status = GIT_DELTA_UNMODIFIED;
+ goto cleanup;
+ }
+ }
+ }
+
+ /* if we have not already decided whether file is binary,
+ * check the first 4K for nul bytes to decide...
+ */
+ if (delta->binary == -1) {
+ error = file_is_binary_by_content(
+ diff, delta, &old_data, &new_data);
+ if (error < GIT_SUCCESS)
+ goto cleanup;
+ }
+
+ /* 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) {
+ error = file_cb(data, delta, (float)info.index / diff->deltas.length);
+ if (error != GIT_SUCCESS)
+ goto cleanup;
+ }
+
+ /* don't do hunk and line diffs if file is binary */
+ if (delta->binary == 1)
+ goto cleanup;
+
+ /* nothing to do if we did not get data */
+ if (!old_data.len && !new_data.len)
+ goto cleanup;
+
+ assert(hunk_cb || line_cb);
+
+ info.delta = delta;
+ old_xdiff_data.ptr = old_data.data;
+ old_xdiff_data.size = old_data.len;
+ new_xdiff_data.ptr = new_data.data;
+ new_xdiff_data.size = new_data.len;
+
+ xdl_diff(&old_xdiff_data, &new_xdiff_data,
+ &xdiff_params, &xdiff_config, &xdiff_callback);
+
+cleanup:
+ release_content(&delta->old_file, &old_data, old_blob);
+ release_content(&delta->new_file, &new_data, new_blob);
+
+ if (error != GIT_SUCCESS)
+ break;
+ }
+
+ return error;
+}
+
+
+typedef struct {
+ git_diff_list *diff;
+ git_diff_output_fn print_cb;
+ void *cb_data;
+ git_buf *buf;
+} diff_print_info;
+
+static char pick_suffix(int mode)
+{
+ if (S_ISDIR(mode))
+ return '/';
+ else if (mode & 0100)
+ /* in git, modes are very regular, so we must have 0100755 mode */
+ return '*';
+ else
+ return ' ';
+}
+
+static int print_compact(void *data, git_diff_delta *delta, float progress)
+{
+ diff_print_info *pi = data;
+ char code, old_suffix, new_suffix;
+
+ GIT_UNUSED(progress);
+
+ switch (delta->status) {
+ case GIT_DELTA_ADDED: code = 'A'; break;
+ case GIT_DELTA_DELETED: code = 'D'; break;
+ case GIT_DELTA_MODIFIED: code = 'M'; break;
+ case GIT_DELTA_RENAMED: code = 'R'; break;
+ case GIT_DELTA_COPIED: code = 'C'; break;
+ case GIT_DELTA_IGNORED: code = 'I'; break;
+ case GIT_DELTA_UNTRACKED: code = '?'; break;
+ default: code = 0;
+ }
+
+ if (!code)
+ return GIT_SUCCESS;
+
+ old_suffix = pick_suffix(delta->old_file.mode);
+ new_suffix = pick_suffix(delta->new_file.mode);
+
+ git_buf_clear(pi->buf);
+
+ if (delta->old_file.path != delta->new_file.path &&
+ strcmp(delta->old_file.path,delta->new_file.path) != 0)
+ git_buf_printf(pi->buf, "%c\t%s%c -> %s%c\n", code,
+ delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
+ else if (delta->old_file.mode != delta->new_file.mode &&
+ delta->old_file.mode != 0 && delta->new_file.mode != 0)
+ git_buf_printf(pi->buf, "%c\t%s%c (%o -> %o)\n", code,
+ delta->old_file.path, new_suffix, delta->old_file.mode, delta->new_file.mode);
+ else if (old_suffix != ' ')
+ git_buf_printf(pi->buf, "%c\t%s%c\n", code, delta->old_file.path, old_suffix);
+ else
+ git_buf_printf(pi->buf, "%c\t%s\n", code, delta->old_file.path);
+
+ if (git_buf_lasterror(pi->buf) != GIT_SUCCESS)
+ return git_buf_lasterror(pi->buf);
+
+ return pi->print_cb(pi->cb_data, GIT_DIFF_LINE_FILE_HDR, pi->buf->ptr);
+}
+
+int git_diff_print_compact(
+ git_diff_list *diff,
+ void *cb_data,
+ git_diff_output_fn print_cb)
+{
+ int error;
+ git_buf buf = GIT_BUF_INIT;
+ diff_print_info pi;
+
+ pi.diff = diff;
+ pi.print_cb = print_cb;
+ pi.cb_data = cb_data;
+ pi.buf = &buf;
+
+ error = git_diff_foreach(diff, &pi, print_compact, NULL, NULL);
+
+ git_buf_free(&buf);
+
+ return error;
+}
+
+
+static int print_oid_range(diff_print_info *pi, git_diff_delta *delta)
+{
+ char start_oid[8], end_oid[8];
+
+ /* TODO: Determine a good actual OID range to print */
+ git_oid_to_string(start_oid, sizeof(start_oid), &delta->old_file.oid);
+ git_oid_to_string(end_oid, sizeof(end_oid), &delta->new_file.oid);
+
+ /* TODO: Match git diff more closely */
+ if (delta->old_file.mode == delta->new_file.mode) {
+ git_buf_printf(pi->buf, "index %s..%s %o\n",
+ start_oid, end_oid, delta->old_file.mode);
+ } else {
+ if (delta->old_file.mode == 0) {
+ git_buf_printf(pi->buf, "new file mode %o\n", delta->new_file.mode);
+ } else if (delta->new_file.mode == 0) {
+ git_buf_printf(pi->buf, "deleted file mode %o\n", delta->old_file.mode);
+ } else {
+ git_buf_printf(pi->buf, "old mode %o\n", delta->old_file.mode);
+ git_buf_printf(pi->buf, "new mode %o\n", delta->new_file.mode);
+ }
+ git_buf_printf(pi->buf, "index %s..%s\n", start_oid, end_oid);
+ }
+
+ return git_buf_lasterror(pi->buf);
+}
+
+static int print_patch_file(void *data, git_diff_delta *delta, float progress)
+{
+ int error;
+ diff_print_info *pi = data;
+ const char *oldpfx = pi->diff->opts.old_prefix;
+ const char *oldpath = delta->old_file.path;
+ const char *newpfx = pi->diff->opts.new_prefix;
+ const char *newpath = delta->new_file.path;
+
+ GIT_UNUSED(progress);
+
+ git_buf_clear(pi->buf);
+ git_buf_printf(pi->buf, "diff --git %s%s %s%s\n", oldpfx, delta->old_file.path, newpfx, delta->new_file.path);
+ if ((error = print_oid_range(pi, delta)) < GIT_SUCCESS)
+ return error;
+
+ if (git_oid_iszero(&delta->old_file.oid)) {
+ oldpfx = "";
+ oldpath = "/dev/null";
+ }
+ if (git_oid_iszero(&delta->new_file.oid)) {
+ oldpfx = "";
+ oldpath = "/dev/null";
+ }
+
+ if (delta->binary != 1) {
+ git_buf_printf(pi->buf, "--- %s%s\n", oldpfx, oldpath);
+ git_buf_printf(pi->buf, "+++ %s%s\n", newpfx, newpath);
+ }
+
+ if (git_buf_lasterror(pi->buf) != GIT_SUCCESS)
+ return git_buf_lasterror(pi->buf);
+
+ error = pi->print_cb(pi->cb_data, GIT_DIFF_LINE_FILE_HDR, pi->buf->ptr);
+ if (error != GIT_SUCCESS || delta->binary != 1)
+ return error;
+
+ git_buf_clear(pi->buf);
+ git_buf_printf(
+ pi->buf, "Binary files %s%s and %s%s differ\n",
+ oldpfx, oldpath, newpfx, newpath);
+ if (git_buf_lasterror(pi->buf) != GIT_SUCCESS)
+ return git_buf_lasterror(pi->buf);
+
+ return pi->print_cb(pi->cb_data, GIT_DIFF_LINE_BINARY, pi->buf->ptr);
+}
+
+static int print_patch_hunk(
+ void *data,
+ git_diff_delta *d,
+ git_diff_range *r,
+ const char *header,
+ size_t header_len)
+{
+ diff_print_info *pi = data;
+
+ GIT_UNUSED(d);
+ GIT_UNUSED(r);
+
+ git_buf_clear(pi->buf);
+
+ if (git_buf_printf(pi->buf, "%.*s", (int)header_len, header) == GIT_SUCCESS)
+ return pi->print_cb(pi->cb_data, GIT_DIFF_LINE_HUNK_HDR, pi->buf->ptr);
+ else
+ return git_buf_lasterror(pi->buf);
+}
+
+static int print_patch_line(
+ void *data,
+ git_diff_delta *delta,
+ char line_origin, /* GIT_DIFF_LINE value from above */
+ const char *content,
+ size_t content_len)
+{
+ diff_print_info *pi = data;
+
+ GIT_UNUSED(delta);
+
+ git_buf_clear(pi->buf);
+
+ if (line_origin == GIT_DIFF_LINE_ADDITION ||
+ line_origin == GIT_DIFF_LINE_DELETION ||
+ line_origin == GIT_DIFF_LINE_CONTEXT)
+ git_buf_printf(pi->buf, "%c%.*s", line_origin, (int)content_len, content);
+ else if (content_len > 0)
+ git_buf_printf(pi->buf, "%.*s", (int)content_len, content);
+
+ if (git_buf_lasterror(pi->buf) != GIT_SUCCESS)
+ return git_buf_lasterror(pi->buf);
+
+ return pi->print_cb(pi->cb_data, line_origin, pi->buf->ptr);
+}
+
+int git_diff_print_patch(
+ git_diff_list *diff,
+ void *cb_data,
+ git_diff_output_fn print_cb)
+{
+ int error;
+ git_buf buf = GIT_BUF_INIT;
+ diff_print_info pi;
+
+ pi.diff = diff;
+ pi.print_cb = print_cb;
+ pi.cb_data = cb_data;
+ pi.buf = &buf;
+
+ error = git_diff_foreach(
+ diff, &pi, print_patch_file, print_patch_hunk, print_patch_line);
+
+ git_buf_free(&buf);
+
+ return error;
+}
+
+
+int git_diff_blobs(
+ git_repository *repo,
+ git_blob *old_blob,
+ git_blob *new_blob,
+ git_diff_options *options,
+ void *cb_data,
+ git_diff_hunk_fn hunk_cb,
+ git_diff_line_fn line_cb)
+{
+ diff_output_info info;
+ git_diff_delta delta;
+ mmfile_t old_data, new_data;
+ xpparam_t xdiff_params;
+ xdemitconf_t xdiff_config;
+ xdemitcb_t xdiff_callback;
+
+ assert(repo);
+
+ if (options && (options->flags & GIT_DIFF_REVERSE)) {
+ git_blob *swap = old_blob;
+ old_blob = new_blob;
+ new_blob = swap;
+ }
+
+ if (old_blob) {
+ old_data.ptr = (char *)git_blob_rawcontent(old_blob);
+ old_data.size = git_blob_rawsize(old_blob);
+ } else {
+ old_data.ptr = "";
+ old_data.size = 0;
+ }
+
+ if (new_blob) {
+ new_data.ptr = (char *)git_blob_rawcontent(new_blob);
+ new_data.size = git_blob_rawsize(new_blob);
+ } else {
+ new_data.ptr = "";
+ new_data.size = 0;
+ }
+
+ /* populate a "fake" delta record */
+ delta.status = old_data.ptr ?
+ (new_data.ptr ? GIT_DELTA_MODIFIED : GIT_DELTA_DELETED) :
+ (new_data.ptr ? GIT_DELTA_ADDED : GIT_DELTA_UNTRACKED);
+ delta.old_file.mode = 0100644; /* can't know the truth from a blob alone */
+ delta.new_file.mode = 0100644;
+ git_oid_cpy(&delta.old_file.oid, git_object_id((const git_object *)old_blob));
+ git_oid_cpy(&delta.new_file.oid, git_object_id((const git_object *)new_blob));
+ delta.old_file.path = NULL;
+ delta.new_file.path = NULL;
+ delta.similarity = 0;
+
+ info.diff = NULL;
+ info.delta = &delta;
+ info.cb_data = cb_data;
+ info.hunk_cb = hunk_cb;
+ info.line_cb = line_cb;
+
+ setup_xdiff_options(options, &xdiff_config, &xdiff_params);
+ memset(&xdiff_callback, 0, sizeof(xdiff_callback));
+ xdiff_callback.outf = diff_output_cb;
+ xdiff_callback.priv = &info;
+
+ xdl_diff(&old_data, &new_data, &xdiff_params, &xdiff_config, &xdiff_callback);
+
+ return GIT_SUCCESS;
+}
diff --git a/src/fileops.c b/src/fileops.c
index 3241c68b1..856823afb 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -79,10 +79,6 @@ git_off_t git_futils_filesize(git_file fd)
return sb.st_size;
}
-#define GIT_MODE_PERMS_MASK 0777
-#define GIT_CANONICAL_PERMS(MODE) (((MODE) & 0100) ? 0755 : 0644)
-#define GIT_MODE_TYPE(MODE) ((MODE) & ~GIT_MODE_PERMS_MASK)
-
mode_t git_futils_canonical_mode(mode_t raw_mode)
{
if (S_ISREG(raw_mode))
@@ -97,87 +93,77 @@ mode_t git_futils_canonical_mode(mode_t raw_mode)
return 0;
}
-int git_futils_readbuffer_updated(git_fbuffer *obj, const char *path, time_t *mtime, int *updated)
+int git_futils_readbuffer_updated(git_buf *buf, const char *path, time_t *mtime, int *updated)
{
git_file fd;
size_t len;
struct stat st;
- unsigned char *buff;
- assert(obj && path && *path);
+ assert(buf && path && *path);
if (updated != NULL)
*updated = 0;
- if (p_stat(path, &st) < 0)
- return git__throw(GIT_ENOTFOUND, "Failed to stat file %s", path);
+ if ((fd = p_open(path, O_RDONLY)) < 0) {
+ return git__throw(GIT_ENOTFOUND, "Failed to read file '%s': %s", path, strerror(errno));
+ }
- if (S_ISDIR(st.st_mode))
- return git__throw(GIT_ERROR, "Can't read a dir into a buffer");
+ if (p_fstat(fd, &st) < 0 || S_ISDIR(st.st_mode) || !git__is_sizet(st.st_size+1)) {
+ close(fd);
+ return git__throw(GIT_EOSERR, "Failed to stat file '%s'", path);
+ }
/*
* If we were given a time, we only want to read the file if it
* has been modified.
*/
- if (mtime != NULL && *mtime >= st.st_mtime)
- return GIT_SUCCESS;
+ if (mtime != NULL && *mtime >= st.st_mtime) {
+ close(fd);
+ return 0;
+ }
if (mtime != NULL)
*mtime = st.st_mtime;
- if (!git__is_sizet(st.st_size+1))
- return git__throw(GIT_ERROR, "Failed to read file `%s`. An error occured while calculating its size", path);
len = (size_t) st.st_size;
- if ((fd = p_open(path, O_RDONLY)) < 0)
- return git__throw(GIT_EOSERR, "Failed to open %s for reading", path);
+ git_buf_clear(buf);
- if ((buff = git__malloc(len + 1)) == NULL) {
- p_close(fd);
+ if (git_buf_grow(buf, len + 1) < 0) {
+ close(fd);
return GIT_ENOMEM;
}
- if (p_read(fd, buff, len) < 0) {
- p_close(fd);
- git__free(buff);
- return git__throw(GIT_ERROR, "Failed to read file `%s`", path);
+ buf->ptr[len] = '\0';
+
+ while (len > 0) {
+ ssize_t read_size = p_read(fd, buf->ptr, len);
+
+ if (read_size < 0) {
+ close(fd);
+ return git__throw(GIT_EOSERR, "Failed to read from FD");
+ }
+
+ len -= read_size;
+ buf->size += read_size;
}
- buff[len] = '\0';
p_close(fd);
if (mtime != NULL)
*mtime = st.st_mtime;
+
if (updated != NULL)
*updated = 1;
- obj->data = buff;
- obj->len = len;
-
- return GIT_SUCCESS;
-}
-
-int git_futils_readbuffer(git_fbuffer *obj, const char *path)
-{
- return git_futils_readbuffer_updated(obj, path, NULL, NULL);
-}
-
-void git_futils_fbuffer_rtrim(git_fbuffer *obj)
-{
- unsigned char *buff = obj->data;
- while (obj->len > 0 && isspace(buff[obj->len - 1]))
- obj->len--;
- buff[obj->len] = '\0';
+ return 0;
}
-void git_futils_freebuffer(git_fbuffer *obj)
+int git_futils_readbuffer(git_buf *buf, const char *path)
{
- assert(obj);
- git__free(obj->data);
- obj->data = NULL;
+ return git_futils_readbuffer_updated(buf, path, NULL, NULL);
}
-
int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode)
{
if (git_futils_mkpath2file(to, dirmode) < GIT_SUCCESS)
@@ -191,6 +177,18 @@ int git_futils_mmap_ro(git_map *out, git_file fd, git_off_t begin, size_t len)
return p_mmap(out, len, GIT_PROT_READ, GIT_MAP_SHARED, fd, begin);
}
+int git_futils_mmap_ro_file(git_map *out, const char *path)
+{
+ git_file fd = p_open(path, O_RDONLY /* | O_NOATIME */);
+ git_off_t len = git_futils_filesize(fd);
+ int result;
+ if (!git__is_sizet(len))
+ return git__throw(GIT_ERROR, "File `%s` too large to mmap", path);
+ result = git_futils_mmap_ro(out, fd, 0, (size_t)len);
+ p_close(fd);
+ return result;
+}
+
void git_futils_mmap_free(git_map *out)
{
p_munmap(out);
diff --git a/src/fileops.h b/src/fileops.h
index 4c114026b..ab57b6f38 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -17,17 +17,8 @@
*
* Read whole files into an in-memory buffer for processing
*/
-#define GIT_FBUFFER_INIT {NULL, 0}
-
-typedef struct { /* file io buffer */
- void *data; /* data bytes */
- size_t len; /* data length */
-} git_fbuffer;
-
-extern int git_futils_readbuffer(git_fbuffer *obj, const char *path);
-extern int git_futils_readbuffer_updated(git_fbuffer *obj, const char *path, time_t *mtime, int *updated);
-extern void git_futils_freebuffer(git_fbuffer *obj);
-extern void git_futils_fbuffer_rtrim(git_fbuffer *obj);
+extern int git_futils_readbuffer(git_buf *obj, const char *path);
+extern int git_futils_readbuffer_updated(git_buf *obj, const char *path, time_t *mtime, int *updated);
/**
* File utils
@@ -90,6 +81,10 @@ extern int git_futils_mv_withpath(const char *from, const char *to, const mode_t
*/
extern git_off_t git_futils_filesize(git_file fd);
+#define GIT_MODE_PERMS_MASK 0777
+#define GIT_CANONICAL_PERMS(MODE) (((MODE) & 0100) ? 0755 : 0644)
+#define GIT_MODE_TYPE(MODE) ((MODE) & ~GIT_MODE_PERMS_MASK)
+
/**
* Convert a mode_t from the OS to a legal git mode_t value.
*/
@@ -118,6 +113,19 @@ extern int git_futils_mmap_ro(
size_t len);
/**
+ * Read-only map an entire file.
+ *
+ * @param out buffer to populate with the mapping information.
+ * @param path path to file to be opened.
+ * @return
+ * - GIT_SUCCESS on success;
+ * - GIT_EOSERR on an unspecified OS related error.
+ */
+extern int git_futils_mmap_ro_file(
+ git_map *out,
+ const char *path);
+
+/**
* Release the memory associated with a previous memory mapping.
* @param map the mapping description previously configured.
*/
diff --git a/src/filter.c b/src/filter.c
new file mode 100644
index 000000000..f0ee1ad39
--- /dev/null
+++ b/src/filter.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * 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 "fileops.h"
+#include "hash.h"
+#include "filter.h"
+#include "repository.h"
+#include "git2/config.h"
+
+/* Tweaked from Core Git. I wonder what we could use this for... */
+void git_text_gather_stats(git_text_stats *stats, const git_buf *text)
+{
+ size_t i;
+
+ memset(stats, 0, sizeof(*stats));
+
+ for (i = 0; i < text->size; i++) {
+ unsigned char c = text->ptr[i];
+
+ if (c == '\r') {
+ stats->cr++;
+
+ if (i + 1 < text->size && text->ptr[i + 1] == '\n')
+ stats->crlf++;
+ }
+
+ else if (c == '\n')
+ stats->lf++;
+
+ else if (c == 0x85)
+ /* Unicode CR+LF */
+ stats->crlf++;
+
+ else if (c == 127)
+ /* DEL */
+ stats->nonprintable++;
+
+ else if (c <= 0x1F || (c >= 0x80 && c <= 0x9F)) {
+ switch (c) {
+ /* BS, HT, ESC and FF */
+ case '\b': case '\t': case '\033': case '\014':
+ stats->printable++;
+ break;
+ case 0:
+ stats->nul++;
+ /* fall through */
+ default:
+ stats->nonprintable++;
+ }
+ }
+
+ else
+ stats->printable++;
+ }
+
+ /* If file ends with EOF then don't count this EOF as non-printable. */
+ if (text->size >= 1 && text->ptr[text->size - 1] == '\032')
+ stats->nonprintable--;
+}
+
+/*
+ * Fresh from Core Git
+ */
+int git_text_is_binary(git_text_stats *stats)
+{
+ if (stats->nul)
+ return 1;
+
+ if ((stats->printable >> 7) < stats->nonprintable)
+ return 1;
+ /*
+ * Other heuristics? Average line length might be relevant,
+ * as might LF vs CR vs CRLF counts..
+ *
+ * NOTE! It might be normal to have a low ratio of CRLF to LF
+ * (somebody starts with a LF-only file and edits it with an editor
+ * that adds CRLF only to lines that are added..). But do we
+ * want to support CR-only? Probably not.
+ */
+ return 0;
+}
+
+int git_filters_load(git_vector *filters, git_repository *repo, const char *path, int mode)
+{
+ int error;
+
+ if (mode == GIT_FILTER_TO_ODB) {
+ /* Load the CRLF cleanup filter when writing to the ODB */
+ error = git_filter_add__crlf_to_odb(filters, repo, path);
+ if (error < GIT_SUCCESS)
+ return error;
+ } else {
+ return git__throw(GIT_ENOTIMPLEMENTED,
+ "Worktree filters are not implemented yet");
+ }
+
+ return (int)filters->length;
+}
+
+void git_filters_free(git_vector *filters)
+{
+ size_t i;
+ git_filter *filter;
+
+ git_vector_foreach(filters, i, filter) {
+ if (filter->do_free != NULL)
+ filter->do_free(filter);
+ else
+ free(filter);
+ }
+
+ git_vector_free(filters);
+}
+
+int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters)
+{
+ unsigned int i, src;
+ git_buf *dbuffer[2];
+
+ dbuffer[0] = source;
+ dbuffer[1] = dest;
+
+ src = 0;
+
+ if (source->size == 0) {
+ git_buf_clear(dest);
+ return GIT_SUCCESS;
+ }
+
+ /* Pre-grow the destination buffer to more or less the size
+ * we expect it to have */
+ if (git_buf_grow(dest, source->size) < 0)
+ return GIT_ENOMEM;
+
+ for (i = 0; i < filters->length; ++i) {
+ git_filter *filter = git_vector_get(filters, i);
+ unsigned int dst = 1 - src;
+
+ git_buf_clear(dbuffer[dst]);
+
+ /* Apply the filter from dbuffer[src] to the other buffer;
+ * if the filtering is canceled by the user mid-filter,
+ * we skip to the next filter without changing the source
+ * of the double buffering (so that the text goes through
+ * cleanly).
+ */
+ if (filter->apply(filter, dbuffer[dst], dbuffer[src]) == 0)
+ src = dst;
+
+ if (git_buf_oom(dbuffer[dst]))
+ return GIT_ENOMEM;
+ }
+
+ /* Ensure that the output ends up in dbuffer[1] (i.e. the dest) */
+ if (src != 1)
+ git_buf_swap(dest, source);
+
+ return GIT_SUCCESS;
+}
+
diff --git a/src/filter.h b/src/filter.h
new file mode 100644
index 000000000..5a77f25c6
--- /dev/null
+++ b/src/filter.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_filter_h__
+#define INCLUDE_filter_h__
+
+#include "common.h"
+#include "buffer.h"
+#include "git2/odb.h"
+#include "git2/repository.h"
+
+typedef struct git_filter {
+ int (*apply)(struct git_filter *self, git_buf *dest, const git_buf *source);
+ void (*do_free)(struct git_filter *self);
+} git_filter;
+
+typedef enum {
+ GIT_FILTER_TO_WORKTREE,
+ GIT_FILTER_TO_ODB
+} git_filter_mode;
+
+typedef enum {
+ GIT_CRLF_GUESS = -1,
+ GIT_CRLF_BINARY = 0,
+ GIT_CRLF_TEXT,
+ GIT_CRLF_INPUT,
+ GIT_CRLF_CRLF,
+ GIT_CRLF_AUTO,
+} git_crlf_t;
+
+typedef struct {
+ /* NUL, CR, LF and CRLF counts */
+ unsigned int nul, cr, lf, crlf;
+
+ /* These are just approximations! */
+ unsigned int printable, nonprintable;
+} git_text_stats;
+
+/*
+ * FILTER API
+ */
+
+/*
+ * For any given path in the working directory, fill the `filters`
+ * array with the relevant filters that need to be applied.
+ *
+ * Mode is either `GIT_FILTER_TO_WORKTREE` if you need to load the
+ * filters that will be used when checking out a file to the working
+ * directory, or `GIT_FILTER_TO_ODB` for the filters used when writing
+ * a file to the ODB.
+ *
+ * @param filters Vector where to store all the loaded filters
+ * @param repo Repository object that contains `path`
+ * @param path Relative path of the file to be filtered
+ * @param mode Filtering direction (WT->ODB or ODB->WT)
+ * @return the number of filters loaded for the file (0 if the file
+ * doesn't need filtering), or a negative error code
+ */
+extern int git_filters_load(git_vector *filters, git_repository *repo, const char *path, int mode);
+
+/*
+ * Apply one or more filters to a file.
+ *
+ * The file must have been loaded as a `git_buf` object. Both the `source`
+ * and `dest` buffers are owned by the caller and must be freed once
+ * they are no longer needed.
+ *
+ * NOTE: Because of the double-buffering schema, the `source` buffer that contains
+ * the original file may be tampered once the filtering is complete. Regardless,
+ * the `dest` buffer will always contain the final result of the filtering
+ *
+ * @param dest Buffer to store the result of the filtering
+ * @param source Buffer containing the document to filter
+ * @param filters A non-empty vector of filters as supplied by `git_filters_load`
+ * @return GIT_SUCCESS on success, an error code otherwise
+ */
+extern int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters);
+
+/*
+ * Free the `filters` array generated by `git_filters_load`.
+ *
+ * Note that this frees both the array and its contents. The array will
+ * be clean/reusable after this call.
+ *
+ * @param filters A filters array as supplied by `git_filters_load`
+ */
+extern void git_filters_free(git_vector *filters);
+
+/*
+ * Available filters
+ */
+
+/* Strip CRLF, from Worktree to ODB */
+extern int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const char *path);
+
+
+/*
+ * PLAINTEXT API
+ */
+
+/*
+ * Gather stats for a piece of text
+ *
+ * Fill the `stats` structure with information on the number of
+ * unreadable characters, carriage returns, etc, so it can be
+ * used in heuristics.
+ */
+extern void git_text_gather_stats(git_text_stats *stats, const git_buf *text);
+
+/*
+ * Process `git_text_stats` data generated by `git_text_stat` to see
+ * if it qualifies as a binary file
+ */
+extern int git_text_is_binary(git_text_stats *stats);
+
+#endif
diff --git a/src/hashtable.h b/src/hashtable.h
index f6fbb8585..e09965965 100644
--- a/src/hashtable.h
+++ b/src/hashtable.h
@@ -65,20 +65,20 @@ GIT_INLINE(int) git_hashtable_insert(git_hashtable *h, const void *key, void *va
#define git_hashtable_node_at(nodes, pos) ((git_hashtable_node *)(&nodes[pos]))
-#define GIT_HASHTABLE_FOREACH(self, pkey, pvalue, code) {\
- git_hashtable *_self = (self);\
- git_hashtable_node *_nodes = _self->nodes;\
- unsigned int _i, _size = _self->size;\
- for (_i = 0; _i < _size; _i ++) {\
- git_hashtable_node *_node = git_hashtable_node_at(_nodes, _i);\
- if (_node->key)\
- {\
- pkey = _node->key;\
- pvalue = _node->value;\
- code;\
- }\
- }\
-}
+#define GIT_HASHTABLE__FOREACH(self,block) { \
+ unsigned int _c; \
+ git_hashtable_node *_n = (self)->nodes; \
+ for (_c = (self)->size; _c > 0; _c--, _n++) { \
+ if (!_n->key) continue; block } }
+
+#define GIT_HASHTABLE_FOREACH(self, pkey, pvalue, code)\
+ GIT_HASHTABLE__FOREACH(self,{(pkey)=_n->key;(pvalue)=_n->value;code;})
+
+#define GIT_HASHTABLE_FOREACH_KEY(self, pkey, code)\
+ GIT_HASHTABLE__FOREACH(self,{(pkey)=_n->key;code;})
+
+#define GIT_HASHTABLE_FOREACH_VALUE(self, pvalue, code)\
+ GIT_HASHTABLE__FOREACH(self,{(pvalue)=_n->value;code;})
#define GIT_HASHTABLE_FOREACH_DELETE() {\
_node->key = NULL; _node->value = NULL; _self->key_count--;\
diff --git a/src/ignore.c b/src/ignore.c
index 30f86b822..a3bf0a282 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -11,7 +11,7 @@ static int load_ignore_file(
git_repository *repo, const char *path, git_attr_file *ignores)
{
int error = GIT_SUCCESS;
- git_fbuffer fbuf = GIT_FBUFFER_INIT;
+ git_buf fbuf = GIT_BUF_INIT;
git_attr_fnmatch *match = NULL;
const char *scan = NULL;
char *context = NULL;
@@ -28,7 +28,7 @@ static int load_ignore_file(
if (error == GIT_SUCCESS)
error = git_futils_readbuffer(&fbuf, path);
- scan = fbuf.data;
+ scan = fbuf.ptr;
while (error == GIT_SUCCESS && *scan) {
if (!match && !(match = git__calloc(1, sizeof(git_attr_fnmatch)))) {
@@ -53,7 +53,7 @@ static int load_ignore_file(
}
}
- git_futils_freebuffer(&fbuf);
+ git_buf_free(&fbuf);
git__free(match);
git__free(context);
diff --git a/src/index.c b/src/index.c
index 4dccad527..5ac99de3e 100644
--- a/src/index.c
+++ b/src/index.c
@@ -216,7 +216,7 @@ void git_index_clear(git_index *index)
int git_index_read(git_index *index)
{
int error = GIT_SUCCESS, updated;
- git_fbuffer buffer = GIT_FBUFFER_INIT;
+ git_buf buffer = GIT_BUF_INIT;
time_t mtime;
assert(index->index_file_path);
@@ -235,12 +235,12 @@ int git_index_read(git_index *index)
if (updated) {
git_index_clear(index);
- error = parse_index(index, buffer.data, buffer.len);
+ error = parse_index(index, buffer.ptr, buffer.size);
if (error == GIT_SUCCESS)
index->last_modified = mtime;
- git_futils_freebuffer(&buffer);
+ git_buf_free(&buffer);
}
if (error < GIT_SUCCESS)
diff --git a/src/iterator.c b/src/iterator.c
index 8255d4c9a..c026c3c09 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -143,6 +143,16 @@ static void tree_iterator__free(git_iterator *self)
git_buf_free(&ti->path);
}
+static int tree_iterator__reset(git_iterator *self)
+{
+ tree_iterator *ti = (tree_iterator *)self;
+ while (ti->stack && ti->stack->next)
+ tree_iterator__pop_frame(ti);
+ if (ti->stack)
+ ti->stack->index = 0;
+ return tree_iterator__expand_tree(ti);
+}
+
int git_iterator_for_tree(
git_repository *repo, git_tree *tree, git_iterator **iter)
{
@@ -155,6 +165,7 @@ int git_iterator_for_tree(
ti->base.current = tree_iterator__current;
ti->base.at_end = tree_iterator__at_end;
ti->base.advance = tree_iterator__advance;
+ ti->base.reset = tree_iterator__reset;
ti->base.free = tree_iterator__free;
ti->repo = repo;
ti->stack = tree_iterator__alloc_frame(tree);
@@ -199,6 +210,13 @@ static int index_iterator__advance(
return GIT_SUCCESS;
}
+static int index_iterator__reset(git_iterator *self)
+{
+ index_iterator *ii = (index_iterator *)self;
+ ii->current = 0;
+ return GIT_SUCCESS;
+}
+
static void index_iterator__free(git_iterator *self)
{
index_iterator *ii = (index_iterator *)self;
@@ -217,6 +235,7 @@ int git_iterator_for_index(git_repository *repo, git_iterator **iter)
ii->base.current = index_iterator__current;
ii->base.at_end = index_iterator__at_end;
ii->base.advance = index_iterator__advance;
+ ii->base.reset = index_iterator__reset;
ii->base.free = index_iterator__free;
ii->current = 0;
@@ -251,7 +270,7 @@ static workdir_iterator_frame *workdir_iterator__alloc_frame(void)
workdir_iterator_frame *wf = git__calloc(1, sizeof(workdir_iterator_frame));
if (wf == NULL)
return wf;
- if (git_vector_init(&wf->entries, 0, git__strcmp_cb) != GIT_SUCCESS) {
+ if (git_vector_init(&wf->entries, 0, git_path_with_stat_cmp) != GIT_SUCCESS) {
git__free(wf);
return NULL;
}
@@ -261,7 +280,7 @@ static workdir_iterator_frame *workdir_iterator__alloc_frame(void)
static void workdir_iterator__free_frame(workdir_iterator_frame *wf)
{
unsigned int i;
- char *path;
+ git_path_with_stat *path;
git_vector_foreach(&wf->entries, i, path)
git__free(path);
@@ -278,10 +297,7 @@ static int workdir_iterator__expand_dir(workdir_iterator *wi)
if (wf == NULL)
return GIT_ENOMEM;
- /* allocate dir entries with extra byte (the "1" param) so we
- * can suffix directory names with a "/".
- */
- error = git_path_dirload(wi->path.ptr, wi->root_len, 1, &wf->entries);
+ error = git_path_dirload_with_stat(wi->path.ptr, wi->root_len, &wf->entries);
if (error < GIT_SUCCESS || wf->entries.length == 0) {
workdir_iterator__free_frame(wf);
return GIT_ENOTFOUND;
@@ -319,7 +335,7 @@ static int workdir_iterator__advance(
int error;
workdir_iterator *wi = (workdir_iterator *)self;
workdir_iterator_frame *wf;
- const char *next;
+ git_path_with_stat *next;
if (entry != NULL)
*entry = NULL;
@@ -330,7 +346,7 @@ static int workdir_iterator__advance(
while ((wf = wi->stack) != NULL) {
next = git_vector_get(&wf->entries, ++wf->index);
if (next != NULL) {
- if (strcmp(next, DOT_GIT) == 0)
+ if (strcmp(next->path, DOT_GIT "/") == 0)
continue;
/* else found a good entry */
break;
@@ -355,6 +371,20 @@ static int workdir_iterator__advance(
return error;
}
+static int workdir_iterator__reset(git_iterator *self)
+{
+ workdir_iterator *wi = (workdir_iterator *)self;
+ while (wi->stack != NULL && wi->stack->next != NULL) {
+ workdir_iterator_frame *wf = wi->stack;
+ wi->stack = wf->next;
+ workdir_iterator__free_frame(wf);
+ git_ignore__pop_dir(&wi->ignores);
+ }
+ if (wi->stack)
+ wi->stack->index = 0;
+ return GIT_SUCCESS;
+}
+
static void workdir_iterator__free(git_iterator *self)
{
workdir_iterator *wi = (workdir_iterator *)self;
@@ -372,39 +402,35 @@ static void workdir_iterator__free(git_iterator *self)
static int workdir_iterator__update_entry(workdir_iterator *wi)
{
int error;
- struct stat st;
- char *relpath = git_vector_get(&wi->stack->entries, wi->stack->index);
+ git_path_with_stat *ps = git_vector_get(&wi->stack->entries, wi->stack->index);
- error = git_buf_joinpath(
- &wi->path, git_repository_workdir(wi->repo), relpath);
+ git_buf_truncate(&wi->path, wi->root_len);
+ error = git_buf_put(&wi->path, ps->path, ps->path_len);
if (error < GIT_SUCCESS)
return error;
memset(&wi->entry, 0, sizeof(wi->entry));
- wi->entry.path = relpath;
+ wi->entry.path = ps->path;
/* skip over .git directory */
- if (strcmp(relpath, DOT_GIT) == 0)
+ if (strcmp(ps->path, DOT_GIT "/") == 0)
return workdir_iterator__advance((git_iterator *)wi, NULL);
/* if there is an error processing the entry, treat as ignored */
wi->is_ignored = 1;
- if (p_lstat(wi->path.ptr, &st) < 0)
- return GIT_SUCCESS;
-
/* TODO: remove shared code for struct stat conversion with index.c */
- wi->entry.ctime.seconds = (git_time_t)st.st_ctime;
- wi->entry.mtime.seconds = (git_time_t)st.st_mtime;
- wi->entry.dev = st.st_rdev;
- wi->entry.ino = st.st_ino;
- wi->entry.mode = git_futils_canonical_mode(st.st_mode);
- wi->entry.uid = st.st_uid;
- wi->entry.gid = st.st_gid;
- wi->entry.file_size = st.st_size;
+ wi->entry.ctime.seconds = (git_time_t)ps->st.st_ctime;
+ wi->entry.mtime.seconds = (git_time_t)ps->st.st_mtime;
+ wi->entry.dev = ps->st.st_rdev;
+ wi->entry.ino = ps->st.st_ino;
+ wi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
+ wi->entry.uid = ps->st.st_uid;
+ wi->entry.gid = ps->st.st_gid;
+ wi->entry.file_size = ps->st.st_size;
/* if this is a file type we don't handle, treat as ignored */
- if (st.st_mode == 0)
+ if (wi->entry.mode == 0)
return GIT_SUCCESS;
/* okay, we are far enough along to look up real ignore rule */
@@ -412,18 +438,10 @@ static int workdir_iterator__update_entry(workdir_iterator *wi)
if (error != GIT_SUCCESS)
return GIT_SUCCESS;
- if (S_ISDIR(st.st_mode)) {
- if (git_path_contains(&wi->path, DOT_GIT) == GIT_SUCCESS) {
- /* create submodule entry */
- wi->entry.mode = S_IFGITLINK;
- } else {
- /* create directory entry that can be advanced into as needed */
- size_t pathlen = strlen(wi->entry.path);
- wi->entry.path[pathlen] = '/';
- wi->entry.path[pathlen + 1] = '\0';
- wi->entry.mode = S_IFDIR;
- }
- }
+ /* detect submodules */
+ if (S_ISDIR(wi->entry.mode) &&
+ git_path_contains(&wi->path, DOT_GIT) == GIT_SUCCESS)
+ wi->entry.mode = S_IFGITLINK;
return GIT_SUCCESS;
}
@@ -439,11 +457,14 @@ int git_iterator_for_workdir(git_repository *repo, git_iterator **iter)
wi->base.current = workdir_iterator__current;
wi->base.at_end = workdir_iterator__at_end;
wi->base.advance = workdir_iterator__advance;
+ wi->base.reset = workdir_iterator__reset;
wi->base.free = workdir_iterator__free;
wi->repo = repo;
error = git_buf_sets(&wi->path, git_repository_workdir(repo));
if (error == GIT_SUCCESS)
+ error = git_path_to_dir(&wi->path);
+ if (error == GIT_SUCCESS)
error = git_ignore__for_path(repo, "", &wi->ignores);
if (error != GIT_SUCCESS) {
git__free(wi);
diff --git a/src/iterator.h b/src/iterator.h
index ac30b4ded..aa78c9f29 100644
--- a/src/iterator.h
+++ b/src/iterator.h
@@ -23,6 +23,7 @@ struct git_iterator {
int (*current)(git_iterator *, const git_index_entry **);
int (*at_end)(git_iterator *);
int (*advance)(git_iterator *, const git_index_entry **);
+ int (*reset)(git_iterator *);
void (*free)(git_iterator *);
};
@@ -60,6 +61,11 @@ GIT_INLINE(int) git_iterator_advance(
return iter->advance(iter, entry);
}
+GIT_INLINE(int) git_iterator_reset(git_iterator *iter)
+{
+ return iter->reset(iter);
+}
+
GIT_INLINE(void) git_iterator_free(git_iterator *iter)
{
iter->free(iter);
diff --git a/src/odb.c b/src/odb.c
index 4eaf289e7..81fc82ba8 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -393,8 +393,8 @@ static int add_default_backends(git_odb *db, const char *objects_dir, int as_alt
static int load_alternates(git_odb *odb, const char *objects_dir)
{
git_buf alternates_path = GIT_BUF_INIT;
+ git_buf alternates_buf = GIT_BUF_INIT;
char *buffer;
- git_fbuffer alternates_buf = GIT_FBUFFER_INIT;
const char *alternate;
int error;
@@ -412,7 +412,7 @@ static int load_alternates(git_odb *odb, const char *objects_dir)
return git__throw(GIT_EOSERR, "Failed to add backend. Can't read alternates");
}
- buffer = (char *)alternates_buf.data;
+ buffer = (char *)alternates_buf.ptr;
error = GIT_SUCCESS;
/* add each alternate as a new backend; one alternate per line */
@@ -433,7 +433,8 @@ static int load_alternates(git_odb *odb, const char *objects_dir)
}
git_buf_free(&alternates_path);
- git_futils_freebuffer(&alternates_buf);
+ git_buf_free(&alternates_buf);
+
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to load alternates");
return error;
diff --git a/src/odb_loose.c b/src/odb_loose.c
index bb2b7b5f5..f5f6e35ac 100644
--- a/src/odb_loose.c
+++ b/src/odb_loose.c
@@ -75,13 +75,13 @@ static int object_file_name(git_buf *name, const char *dir, const git_oid *id)
}
-static size_t get_binary_object_header(obj_hdr *hdr, git_fbuffer *obj)
+static size_t get_binary_object_header(obj_hdr *hdr, git_buf *obj)
{
unsigned char c;
- unsigned char *data = obj->data;
+ unsigned char *data = (unsigned char *)obj->ptr;
size_t shift, size, used = 0;
- if (obj->len == 0)
+ if (obj->size == 0)
return 0;
c = data[used++];
@@ -90,7 +90,7 @@ static size_t get_binary_object_header(obj_hdr *hdr, git_fbuffer *obj)
size = c & 15;
shift = 4;
while (c & 0x80) {
- if (obj->len <= used)
+ if (obj->size <= used)
return 0;
if (sizeof(size_t) * 8 <= shift)
return 0;
@@ -177,12 +177,12 @@ static void set_stream_output(z_stream *s, void *out, size_t len)
}
-static int start_inflate(z_stream *s, git_fbuffer *obj, void *out, size_t len)
+static int start_inflate(z_stream *s, git_buf *obj, void *out, size_t len)
{
int status;
init_stream(s, out, len);
- set_stream_input(s, obj->data, obj->len);
+ set_stream_input(s, obj->ptr, obj->size);
if ((status = inflateInit(s)) < Z_OK)
return status;
@@ -287,7 +287,7 @@ static void *inflate_tail(z_stream *s, void *hb, size_t used, obj_hdr *hdr)
* of loose object data into packs. This format is no longer used, but
* we must still read it.
*/
-static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_fbuffer *obj)
+static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_buf *obj)
{
unsigned char *in, *buf;
obj_hdr hdr;
@@ -310,8 +310,8 @@ static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_fbuffer *obj)
if (!buf)
return GIT_ENOMEM;
- in = ((unsigned char *)obj->data) + used;
- len = obj->len - used;
+ in = ((unsigned char *)obj->ptr) + used;
+ len = obj->size - used;
if (inflate_buffer(in, len, buf, hdr.size)) {
git__free(buf);
return git__throw(GIT_ERROR, "Failed to inflate loose object. Could not inflate buffer");
@@ -325,7 +325,7 @@ static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_fbuffer *obj)
return GIT_SUCCESS;
}
-static int inflate_disk_obj(git_rawobj *out, git_fbuffer *obj)
+static int inflate_disk_obj(git_rawobj *out, git_buf *obj)
{
unsigned char head[64], *buf;
z_stream zs;
@@ -335,7 +335,7 @@ static int inflate_disk_obj(git_rawobj *out, git_fbuffer *obj)
/*
* check for a pack-like loose object
*/
- if (!is_zlib_compressed_data(obj->data))
+ if (!is_zlib_compressed_data((unsigned char *)obj->ptr))
return inflate_packlike_loose_disk_obj(out, obj);
/*
@@ -383,7 +383,7 @@ static int inflate_disk_obj(git_rawobj *out, git_fbuffer *obj)
static int read_loose(git_rawobj *out, git_buf *loc)
{
int error;
- git_fbuffer obj = GIT_FBUFFER_INIT;
+ git_buf obj = GIT_BUF_INIT;
assert(out && loc);
@@ -398,7 +398,7 @@ static int read_loose(git_rawobj *out, git_buf *loc)
return git__throw(GIT_ENOTFOUND, "Failed to read loose object. File not found");
error = inflate_disk_obj(out, &obj);
- git_futils_freebuffer(&obj);
+ git_buf_free(&obj);
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to read loose object");
}
diff --git a/src/odb_pack.c b/src/odb_pack.c
index 9e1004eb8..249144a3a 100644
--- a/src/odb_pack.c
+++ b/src/odb_pack.c
@@ -159,9 +159,9 @@ static int pack_entry_find_prefix(struct git_pack_entry *e,
*
***********************************************************/
-GIT_INLINE(void) pack_window_free_all(struct pack_backend *GIT_UNUSED(backend), struct git_pack_file *p)
+GIT_INLINE(void) pack_window_free_all(struct pack_backend *backend, struct git_pack_file *p)
{
- GIT_UNUSED_ARG(backend);
+ GIT_UNUSED(backend);
git_mwindow_free_all(&p->mwf);
}
diff --git a/src/oid.c b/src/oid.c
index 92d8d1e89..a1f010927 100644
--- a/src/oid.c
+++ b/src/oid.c
@@ -190,6 +190,16 @@ int git_oid_streq(const git_oid *a, const char *str)
return git_oid_cmp(a, &id) == 0 ? GIT_SUCCESS : GIT_ERROR;
}
+int git_oid_iszero(const git_oid *oid_a)
+{
+ const unsigned char *a = oid_a->id;
+ unsigned int i;
+ for (i = 0; i < GIT_OID_RAWSZ; ++i, ++a)
+ if (*a != 0)
+ return 0;
+ return 1;
+}
+
typedef short node_index;
typedef union {
diff --git a/src/path.c b/src/path.c
index ec40f4b06..d2c292bf2 100644
--- a/src/path.c
+++ b/src/path.c
@@ -583,3 +583,48 @@ int git_path_dirload(
return GIT_SUCCESS;
}
+int git_path_with_stat_cmp(const void *a, const void *b)
+{
+ const git_path_with_stat *psa = a, *psb = b;
+ return git__strcmp_cb(psa->path, psb->path);
+}
+
+int git_path_dirload_with_stat(
+ const char *path,
+ size_t prefix_len,
+ git_vector *contents)
+{
+ int error;
+ unsigned int i;
+ git_path_with_stat *ps;
+ git_buf full = GIT_BUF_INIT;
+
+ if ((error = git_buf_set(&full, path, prefix_len)) != GIT_SUCCESS)
+ return error;
+
+ if ((error = git_path_dirload(path, prefix_len,
+ sizeof(git_path_with_stat) + 1, contents)) != GIT_SUCCESS) {
+ git_buf_free(&full);
+ return error;
+ }
+
+ git_vector_foreach(contents, i, ps) {
+ size_t path_len = strlen((char *)ps);
+
+ memmove(ps->path, ps, path_len + 1);
+ ps->path_len = path_len;
+
+ git_buf_joinpath(&full, full.ptr, ps->path);
+ p_lstat(full.ptr, &ps->st);
+ git_buf_truncate(&full, prefix_len);
+
+ if (S_ISDIR(ps->st.st_mode)) {
+ ps->path[path_len] = '/';
+ ps->path[path_len + 1] = '\0';
+ }
+ }
+
+ git_buf_free(&full);
+
+ return error;
+}
diff --git a/src/path.h b/src/path.h
index abe6c2217..981fdd6a4 100644
--- a/src/path.h
+++ b/src/path.h
@@ -246,4 +246,26 @@ extern int git_path_dirload(
size_t alloc_extra,
git_vector *contents);
+
+typedef struct {
+ struct stat st;
+ size_t path_len;
+ char path[GIT_FLEX_ARRAY];
+} git_path_with_stat;
+
+extern int git_path_with_stat_cmp(const void *a, const void *b);
+
+/**
+ * Load all directory entries along with stat info into a vector.
+ *
+ * This is just like git_path_dirload except that each entry in the
+ * vector is a git_path_with_stat structure that contains both the
+ * path and the stat info, plus directories will have a / suffixed
+ * to their path name.
+ */
+extern int git_path_dirload_with_stat(
+ const char *path,
+ size_t prefix_len,
+ git_vector *contents);
+
#endif
diff --git a/src/pkt.c b/src/pkt.c
index df972e72a..51da55de1 100644
--- a/src/pkt.c
+++ b/src/pkt.c
@@ -41,11 +41,11 @@ static int flush_pkt(git_pkt **out)
}
/* the rest of the line will be useful for multi_ack */
-static int ack_pkt(git_pkt **out, const char *GIT_UNUSED(line), size_t GIT_UNUSED(len))
+static int ack_pkt(git_pkt **out, const char *line, size_t len)
{
git_pkt *pkt;
- GIT_UNUSED_ARG(line);
- GIT_UNUSED_ARG(len);
+ GIT_UNUSED(line);
+ GIT_UNUSED(len);
pkt = git__malloc(sizeof(git_pkt));
if (pkt == NULL)
diff --git a/src/posix.h b/src/posix.h
index 0cce1fe34..fb17cba6c 100644
--- a/src/posix.h
+++ b/src/posix.h
@@ -66,4 +66,6 @@ extern int p_rename(const char *from, const char *to);
# include "unix/posix.h"
#endif
+#define p_readdir_r(d,e,r) readdir_r(d,e,r)
+
#endif
diff --git a/src/reflog.c b/src/reflog.c
index 9f5ccd322..6ca9418cf 100644
--- a/src/reflog.c
+++ b/src/reflog.c
@@ -183,7 +183,7 @@ int git_reflog_read(git_reflog **reflog, git_reference *ref)
{
int error;
git_buf log_path = GIT_BUF_INIT;
- git_fbuffer log_file = GIT_FBUFFER_INIT;
+ git_buf log_file = GIT_BUF_INIT;
git_reflog *log = NULL;
*reflog = NULL;
@@ -201,7 +201,7 @@ int git_reflog_read(git_reflog **reflog, git_reference *ref)
goto cleanup;
}
- if ((error = reflog_parse(log, log_file.data, log_file.len)) < GIT_SUCCESS)
+ if ((error = reflog_parse(log, log_file.ptr, log_file.size)) < GIT_SUCCESS)
git__rethrow(error, "Failed to read reflog");
else
*reflog = log;
@@ -209,7 +209,7 @@ int git_reflog_read(git_reflog **reflog, git_reference *ref)
cleanup:
if (error != GIT_SUCCESS && log != NULL)
git_reflog_free(log);
- git_futils_freebuffer(&log_file);
+ git_buf_free(&log_file);
git_buf_free(&log_path);
return error;
diff --git a/src/refs.c b/src/refs.c
index 8e911c1ae..f3388bf53 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -32,15 +32,15 @@ struct packref {
static const int default_table_size = 32;
static int reference_read(
- git_fbuffer *file_content,
+ git_buf *file_content,
time_t *mtime,
const char *repo_path,
const char *ref_name,
int *updated);
/* loose refs */
-static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content);
-static int loose_parse_oid(git_oid *ref, git_fbuffer *file_content);
+static int loose_parse_symbolic(git_reference *ref, git_buf *file_content);
+static int loose_parse_oid(git_oid *ref, git_buf *file_content);
static int loose_lookup(git_reference *ref);
static int loose_lookup_to_packfile(struct packref **ref_out,
git_repository *repo, const char *name);
@@ -105,7 +105,7 @@ static int reference_alloc(
reference->name = git__strdup(name);
if (reference->name == NULL) {
- free(reference);
+ git__free(reference);
return GIT_ENOMEM;
}
@@ -113,7 +113,7 @@ static int reference_alloc(
return GIT_SUCCESS;
}
-static int reference_read(git_fbuffer *file_content, time_t *mtime, const char *repo_path, const char *ref_name, int *updated)
+static int reference_read(git_buf *file_content, time_t *mtime, const char *repo_path, const char *ref_name, int *updated)
{
git_buf path = GIT_BUF_INIT;
int error = GIT_SUCCESS;
@@ -129,15 +129,15 @@ static int reference_read(git_fbuffer *file_content, time_t *mtime, const char *
return error;
}
-static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content)
+static int loose_parse_symbolic(git_reference *ref, git_buf *file_content)
{
const unsigned int header_len = strlen(GIT_SYMREF);
const char *refname_start;
char *eol;
- refname_start = (const char *)file_content->data;
+ refname_start = (const char *)file_content->ptr;
- if (file_content->len < (header_len + 1))
+ if (file_content->size < (header_len + 1))
return git__throw(GIT_EOBJCORRUPTED,
"Failed to parse loose reference. Object too short");
@@ -165,15 +165,15 @@ static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content)
return GIT_SUCCESS;
}
-static int loose_parse_oid(git_oid *oid, git_fbuffer *file_content)
+static int loose_parse_oid(git_oid *oid, git_buf *file_content)
{
int error;
char *buffer;
- buffer = (char *)file_content->data;
+ buffer = (char *)file_content->ptr;
/* File format: 40 chars (OID) + newline */
- if (file_content->len < GIT_OID_HEXSZ + 1)
+ if (file_content->size < GIT_OID_HEXSZ + 1)
return git__throw(GIT_EOBJCORRUPTED,
"Failed to parse loose reference. Reference too short");
@@ -193,26 +193,26 @@ static int loose_parse_oid(git_oid *oid, git_fbuffer *file_content)
static git_rtype loose_guess_rtype(const git_buf *full_path)
{
- git_fbuffer ref_file = GIT_FBUFFER_INIT;
+ git_buf ref_file = GIT_BUF_INIT;
git_rtype type;
type = GIT_REF_INVALID;
if (git_futils_readbuffer(&ref_file, full_path->ptr) == GIT_SUCCESS) {
- if (git__prefixcmp((const char *)(ref_file.data), GIT_SYMREF) == 0)
+ if (git__prefixcmp((const char *)(ref_file.ptr), GIT_SYMREF) == 0)
type = GIT_REF_SYMBOLIC;
else
type = GIT_REF_OID;
}
- git_futils_freebuffer(&ref_file);
+ git_buf_free(&ref_file);
return type;
}
static int loose_lookup(git_reference *ref)
{
int error = GIT_SUCCESS, updated;
- git_fbuffer ref_file = GIT_FBUFFER_INIT;
+ git_buf ref_file = GIT_BUF_INIT;
if (reference_read(&ref_file, &ref->mtime,
ref->owner->path_repository, ref->name, &updated) < GIT_SUCCESS)
@@ -222,13 +222,13 @@ static int loose_lookup(git_reference *ref)
return GIT_SUCCESS;
if (ref->flags & GIT_REF_SYMBOLIC) {
- free(ref->target.symbolic);
+ git__free(ref->target.symbolic);
ref->target.symbolic = NULL;
}
ref->flags = 0;
- if (git__prefixcmp((const char *)(ref_file.data), GIT_SYMREF) == 0) {
+ if (git__prefixcmp((const char *)(ref_file.ptr), GIT_SYMREF) == 0) {
ref->flags |= GIT_REF_SYMBOLIC;
error = loose_parse_symbolic(ref, &ref_file);
} else {
@@ -236,7 +236,7 @@ static int loose_lookup(git_reference *ref)
error = loose_parse_oid(&ref->target.oid, &ref_file);
}
- git_futils_freebuffer(&ref_file);
+ git_buf_free(&ref_file);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to lookup loose reference");
@@ -250,7 +250,7 @@ static int loose_lookup_to_packfile(
const char *name)
{
int error = GIT_SUCCESS;
- git_fbuffer ref_file = GIT_FBUFFER_INIT;
+ git_buf ref_file = GIT_BUF_INIT;
struct packref *ref = NULL;
size_t name_len;
@@ -273,12 +273,13 @@ static int loose_lookup_to_packfile(
ref->flags = GIT_PACKREF_WAS_LOOSE;
*ref_out = ref;
- git_futils_freebuffer(&ref_file);
+ git_buf_free(&ref_file);
return GIT_SUCCESS;
cleanup:
- git_futils_freebuffer(&ref_file);
- free(ref);
+ git_buf_free(&ref_file);
+ git__free(ref);
+
return git__rethrow(error, "Failed to lookup loose reference");
}
@@ -420,14 +421,14 @@ static int packed_parse_oid(
return GIT_SUCCESS;
cleanup:
- free(ref);
+ git__free(ref);
return git__rethrow(error, "Failed to parse OID of packed reference");
}
static int packed_load(git_repository *repo)
{
int error = GIT_SUCCESS, updated;
- git_fbuffer packfile = GIT_FBUFFER_INIT;
+ git_buf packfile = GIT_BUF_INIT;
const char *buffer_start, *buffer_end;
git_refcache *ref_cache = &repo->references;
@@ -468,8 +469,8 @@ static int packed_load(git_repository *repo)
git_hashtable_clear(ref_cache->packfile);
- buffer_start = (const char *)packfile.data;
- buffer_end = (const char *)(buffer_start) + packfile.len;
+ buffer_start = (const char *)packfile.ptr;
+ buffer_end = (const char *)(buffer_start) + packfile.size;
while (buffer_start < buffer_end && buffer_start[0] == '#') {
buffer_start = strchr(buffer_start, '\n');
@@ -495,18 +496,18 @@ static int packed_load(git_repository *repo)
error = git_hashtable_insert(ref_cache->packfile, ref->name, ref);
if (error < GIT_SUCCESS) {
- free(ref);
+ git__free(ref);
goto cleanup;
}
}
- git_futils_freebuffer(&packfile);
+ git_buf_free(&packfile);
return GIT_SUCCESS;
cleanup:
git_hashtable_free(ref_cache->packfile);
ref_cache->packfile = NULL;
- git_futils_freebuffer(&packfile);
+ git_buf_free(&packfile);
return git__rethrow(error, "Failed to load packed references");
}
@@ -560,12 +561,12 @@ static int _dirent_loose_load(void *data, git_buf *full_path)
if (git_hashtable_insert2(
repository->references.packfile,
ref->name, ref, &old_ref) < GIT_SUCCESS) {
- free(ref);
+ git__free(ref);
return GIT_ENOMEM;
}
if (old_ref != NULL)
- free(old_ref);
+ git__free(old_ref);
}
return error == GIT_SUCCESS ?
@@ -773,9 +774,8 @@ static int packed_write(git_repository *repo)
/* Load all the packfile into a vector */
{
struct packref *reference;
- const void *GIT_UNUSED(_unused);
- GIT_HASHTABLE_FOREACH(repo->references.packfile, _unused, reference,
+ GIT_HASHTABLE_FOREACH_VALUE(repo->references.packfile, reference,
/* cannot fail: vector already has the right size */
git_vector_insert(&packing_list, reference);
);
@@ -929,7 +929,7 @@ static int packed_lookup(git_reference *ref)
return GIT_SUCCESS;
if (ref->flags & GIT_REF_SYMBOLIC) {
- free(ref->target.symbolic);
+ git__free(ref->target.symbolic);
ref->target.symbolic = NULL;
}
@@ -1513,12 +1513,11 @@ int git_reference_foreach(
/* list all the packed references first */
if (list_flags & GIT_REF_PACKED) {
const char *ref_name;
- void *GIT_UNUSED(_unused);
if ((error = packed_load(repo)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to list references");
- GIT_HASHTABLE_FOREACH(repo->references.packfile, ref_name, _unused,
+ GIT_HASHTABLE_FOREACH_KEY(repo->references.packfile, ref_name,
if ((error = callback(ref_name, payload)) < GIT_SUCCESS)
return git__throw(error,
"Failed to list references. User callback failed");
@@ -1595,12 +1594,10 @@ void git_repository__refcache_free(git_refcache *refs)
assert(refs);
if (refs->packfile) {
- const void *GIT_UNUSED(_unused);
struct packref *reference;
- GIT_HASHTABLE_FOREACH(refs->packfile, _unused, reference,
- free(reference);
- );
+ GIT_HASHTABLE_FOREACH_VALUE(
+ refs->packfile, reference, git__free(reference));
git_hashtable_free(refs->packfile);
}
diff --git a/src/remote.c b/src/remote.c
index 5b442e934..52b6aacc9 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -431,13 +431,13 @@ struct cb_data {
regex_t *preg;
};
-static int remote_list_cb(const char *name, const char *GIT_UNUSED(value), void *data_)
+static int remote_list_cb(const char *name, const char *value, void *data_)
{
struct cb_data *data = (struct cb_data *)data_;
size_t nmatch = 2;
regmatch_t pmatch[2];
int error;
- GIT_UNUSED_ARG(value);
+ GIT_UNUSED(value);
if (!regexec(data->preg, name, nmatch, pmatch, 0)) {
char *remote_name = git__strndup(&name[pmatch[1].rm_so], pmatch[1].rm_eo - pmatch[1].rm_so);
diff --git a/src/repository.c b/src/repository.c
index f394d06fe..1f8306991 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -43,6 +43,8 @@ static void drop_config(git_repository *repo)
git_config_free(repo->_config);
repo->_config = NULL;
}
+
+ git_repository__cvar_cache_clear(repo);
}
static void drop_index(git_repository *repo)
@@ -111,6 +113,9 @@ static git_repository *repository_alloc(void)
return NULL;
}
+ /* set all the entries in the cvar cache to `unset` */
+ git_repository__cvar_cache_clear(repo);
+
return repo;
}
@@ -467,7 +472,7 @@ static int retrieve_ceiling_directories_offset(
*/
static int read_gitfile(git_buf *path_out, const char *file_path, const char *base_path)
{
- git_fbuffer file;
+ git_buf file = GIT_BUF_INIT;
int error;
assert(path_out && file_path);
@@ -476,22 +481,22 @@ static int read_gitfile(git_buf *path_out, const char *file_path, const char *ba
if (error < GIT_SUCCESS)
return error;
- if (git__prefixcmp((char *)file.data, GIT_FILE_CONTENT_PREFIX)) {
- git_futils_freebuffer(&file);
+ if (git__prefixcmp((char *)file.ptr, GIT_FILE_CONTENT_PREFIX)) {
+ git_buf_free(&file);
return git__throw(GIT_ENOTFOUND, "Invalid gitfile format `%s`", file_path);
}
- git_futils_fbuffer_rtrim(&file);
+ git_buf_rtrim(&file);
- if (strlen(GIT_FILE_CONTENT_PREFIX) == file.len) {
- git_futils_freebuffer(&file);
+ if (strlen(GIT_FILE_CONTENT_PREFIX) == file.size) {
+ git_buf_free(&file);
return git__throw(GIT_ENOTFOUND, "No path in git file `%s`", file_path);
}
error = git_path_prettify_dir(path_out,
- ((char *)file.data) + strlen(GIT_FILE_CONTENT_PREFIX), base_path);
+ ((char *)file.ptr) + strlen(GIT_FILE_CONTENT_PREFIX), base_path);
- git_futils_freebuffer(&file);
+ git_buf_free(&file);
if (error == GIT_SUCCESS && git_path_exists(path_out->ptr) == 0)
return GIT_SUCCESS;
diff --git a/src/repository.h b/src/repository.h
index 516fd10be..b5dcc1340 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -26,6 +26,49 @@
#define GIT_DIR_MODE 0755
#define GIT_BARE_DIR_MODE 0777
+/** Cvar cache identifiers */
+typedef enum {
+ GIT_CVAR_AUTO_CRLF = 0, /* core.autocrlf */
+ GIT_CVAR_EOL, /* core.eol */
+ GIT_CVAR_CACHE_MAX
+} git_cvar_cached;
+
+/**
+ * CVAR value enumerations
+ *
+ * These are the values that are actually stored in the cvar cache, instead
+ * of their string equivalents. These values are internal and symbolic;
+ * make sure that none of them is set to `-1`, since that is the unique
+ * identifier for "not cached"
+ */
+typedef enum {
+ /* The value hasn't been loaded from the cache yet */
+ GIT_CVAR_NOT_CACHED = -1,
+
+ /* core.safecrlf: false, 'fail', 'warn' */
+ GIT_SAFE_CRLF_FALSE = 0,
+ GIT_SAFE_CRLF_FAIL = 1,
+ GIT_SAFE_CRLF_WARN = 2,
+
+ /* core.autocrlf: false, true, 'input; */
+ GIT_AUTO_CRLF_FALSE = 0,
+ GIT_AUTO_CRLF_TRUE = 1,
+ GIT_AUTO_CRLF_INPUT = 2,
+ GIT_AUTO_CRLF_DEFAULT = GIT_AUTO_CRLF_FALSE,
+
+ /* core.eol: unset, 'crlf', 'lf', 'native' */
+ GIT_EOL_UNSET = 0,
+ GIT_EOL_CRLF = 1,
+ GIT_EOL_LF = 2,
+#ifdef GIT_WIN32
+ GIT_EOL_NATIVE = GIT_EOL_CRLF,
+#else
+ GIT_EOL_NATIVE = GIT_EOL_LF,
+#endif
+ GIT_EOL_DEFAULT = GIT_EOL_NATIVE
+} git_cvar_value;
+
+/** Base git object for inheritance */
struct git_object {
git_cached_obj cached;
git_repository *repo;
@@ -46,6 +89,8 @@ struct git_repository {
unsigned is_bare:1;
unsigned int lru_counter;
+
+ git_cvar_value cvar_cache[GIT_CVAR_CACHE_MAX];
};
/* fully free the object; internal method, do not
@@ -55,8 +100,24 @@ void git_object__free(void *object);
int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end, const char *header);
void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid);
+/*
+ * Weak pointers to repository internals.
+ *
+ * The returned pointers do not need to be freed. Do not keep
+ * permanent references to these (i.e. between API calls), since they may
+ * become invalidated if the user replaces a repository internal.
+ */
int git_repository_config__weakptr(git_config **out, git_repository *repo);
int git_repository_odb__weakptr(git_odb **out, git_repository *repo);
int git_repository_index__weakptr(git_index **out, git_repository *repo);
+/*
+ * CVAR cache
+ *
+ * Efficient access to the most used config variables of a repository.
+ * The cache is cleared everytime the config backend is replaced.
+ */
+int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar);
+void git_repository__cvar_cache_clear(git_repository *repo);
+
#endif
diff --git a/src/revwalk.c b/src/revwalk.c
index cd971b5d9..997771f08 100644
--- a/src/revwalk.c
+++ b/src/revwalk.c
@@ -590,7 +590,6 @@ int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo)
void git_revwalk_free(git_revwalk *walk)
{
unsigned int i;
- const void *GIT_UNUSED(_unused);
commit_object *commit;
if (walk == NULL)
@@ -602,7 +601,7 @@ void git_revwalk_free(git_revwalk *walk)
/* if the parent has more than PARENTS_PER_COMMIT parents,
* we had to allocate a separate array for those parents.
* make sure it's being free'd */
- GIT_HASHTABLE_FOREACH(walk->commits, _unused, commit, {
+ GIT_HASHTABLE_FOREACH_VALUE(walk->commits, commit, {
if (commit->out_degree > PARENTS_PER_COMMIT)
git__free(commit->parents);
});
@@ -669,12 +668,11 @@ int git_revwalk_next(git_oid *oid, git_revwalk *walk)
void git_revwalk_reset(git_revwalk *walk)
{
- const void *GIT_UNUSED(_unused);
commit_object *commit;
assert(walk);
- GIT_HASHTABLE_FOREACH(walk->commits, _unused, commit,
+ GIT_HASHTABLE_FOREACH_VALUE(walk->commits, commit,
commit->seen = 0;
commit->in_degree = 0;
commit->topo_delay = 0;
diff --git a/src/transport.c b/src/transport.c
index 4c486e200..4910f2433 100644
--- a/src/transport.c
+++ b/src/transport.c
@@ -51,9 +51,9 @@ static git_transport_cb transport_find_fn(const char *url)
* Public API *
**************/
-int git_transport_dummy(git_transport **GIT_UNUSED(transport))
+int git_transport_dummy(git_transport **transport)
{
- GIT_UNUSED_ARG(transport);
+ GIT_UNUSED(transport);
return git__throw(GIT_ENOTIMPLEMENTED, "This protocol isn't implemented. Sorry");
}
diff --git a/src/transports/local.c b/src/transports/local.c
index 1dfc8ed2e..eb24db0fd 100644
--- a/src/transports/local.c
+++ b/src/transports/local.c
@@ -154,7 +154,7 @@ static int local_ls(git_transport *transport, git_headlist_cb list_cb, void *pay
* Try to open the url as a git directory. The direction doesn't
* matter in this case because we're calulating the heads ourselves.
*/
-static int local_connect(git_transport *transport, int GIT_UNUSED(direction))
+static int local_connect(git_transport *transport, int direction)
{
git_repository *repo;
int error;
@@ -162,7 +162,7 @@ static int local_connect(git_transport *transport, int GIT_UNUSED(direction))
const char *path;
git_buf buf = GIT_BUF_INIT;
- GIT_UNUSED_ARG(direction);
+ GIT_UNUSED(direction);
/* The repo layer doesn't want the prefix */
if (!git__prefixcmp(transport->url, "file://")) {
@@ -194,7 +194,7 @@ static int local_connect(git_transport *transport, int GIT_UNUSED(direction))
return GIT_SUCCESS;
}
-static int local_close(git_transport *GIT_UNUSED(transport))
+static int local_close(git_transport *transport)
{
transport_local *t = (transport_local *)transport;
diff --git a/src/unix/posix.h b/src/unix/posix.h
index 2b0d85bb5..9973acf30 100644
--- a/src/unix/posix.h
+++ b/src/unix/posix.h
@@ -21,6 +21,5 @@
#define p_snprintf(b, c, f, ...) snprintf(b, c, f, __VA_ARGS__)
#define p_mkstemp(p) mkstemp(p)
#define p_setenv(n,v,o) setenv(n,v,o)
-#define p_readdir_r(d,e,r) readdir_r(d,e,r)
#endif
diff --git a/src/vector.c b/src/vector.c
index e109704ab..7513ea3f0 100644
--- a/src/vector.c
+++ b/src/vector.c
@@ -220,4 +220,14 @@ void git_vector_clear(git_vector *v)
v->sorted = 1;
}
+void git_vector_swap(git_vector *a, git_vector *b)
+{
+ git_vector t;
+
+ if (!a || !b || a == b)
+ return;
+ memcpy(&t, a, sizeof(t));
+ memcpy(a, b, sizeof(t));
+ memcpy(b, &t, sizeof(t));
+}
diff --git a/src/vector.h b/src/vector.h
index 44635ae14..180edbf7c 100644
--- a/src/vector.h
+++ b/src/vector.h
@@ -24,6 +24,7 @@ typedef struct git_vector {
int git_vector_init(git_vector *v, unsigned int initial_size, git_vector_cmp cmp);
void git_vector_free(git_vector *v);
void git_vector_clear(git_vector *v);
+void git_vector_swap(git_vector *a, git_vector *b);
int git_vector_search(git_vector *v, const void *entry);
int git_vector_search2(git_vector *v, git_vector_cmp cmp, const void *key);
@@ -38,6 +39,11 @@ GIT_INLINE(void *) git_vector_get(git_vector *v, unsigned int position)
return (position < v->length) ? v->contents[position] : NULL;
}
+GIT_INLINE(const void *) git_vector_get_const(const git_vector *v, unsigned int position)
+{
+ return (position < v->length) ? v->contents[position] : NULL;
+}
+
GIT_INLINE(void *) git_vector_last(git_vector *v)
{
return (v->length > 0) ? git_vector_get(v, v->length - 1) : NULL;
diff --git a/src/win32/dir.c b/src/win32/dir.c
index 23bc55558..035e2b685 100644
--- a/src/win32/dir.c
+++ b/src/win32/dir.c
@@ -58,8 +58,11 @@ git__DIR *git__opendir(const char *dir)
return new;
}
-int git__readdir_r(
- git__DIR *d, struct git__dirent *entry, struct git__dirent **result)
+int git__readdir_ext(
+ git__DIR *d,
+ struct git__dirent *entry,
+ struct git__dirent **result,
+ int *is_dir)
{
if (!d || !entry || !result || d->h == INVALID_HANDLE_VALUE)
return -1;
@@ -80,13 +83,16 @@ int git__readdir_r(
entry->d_name, GIT_PATH_MAX, NULL, NULL);
*result = entry;
+ if (is_dir != NULL)
+ *is_dir = ((d->f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
+
return 0;
}
struct git__dirent *git__readdir(git__DIR *d)
{
struct git__dirent *result;
- if (git__readdir_r(d, &d->entry, &result) < 0)
+ if (git__readdir_ext(d, &d->entry, &result, NULL) < 0)
return NULL;
return result;
}
diff --git a/src/win32/dir.h b/src/win32/dir.h
index fc54e2977..c816d79bb 100644
--- a/src/win32/dir.h
+++ b/src/win32/dir.h
@@ -24,7 +24,8 @@ typedef struct {
extern git__DIR *git__opendir(const char *);
extern struct git__dirent *git__readdir(git__DIR *);
-extern int git__readdir_r(git__DIR*, struct git__dirent*, struct git__dirent**);
+extern int git__readdir_ext(
+ git__DIR *, struct git__dirent *, struct git__dirent **, int *);
extern void git__rewinddir(git__DIR *);
extern int git__closedir(git__DIR *);
@@ -33,10 +34,9 @@ extern int git__closedir(git__DIR *);
# define DIR git__DIR
# define opendir git__opendir
# define readdir git__readdir
+# define readdir_r(d,e,r) git__readdir_ext((d),(e),(r),NULL)
# define rewinddir git__rewinddir
# define closedir git__closedir
# endif
-#define p_readdir_r(d,e,r) git__readdir_r(d,e,r)
-
#endif /* INCLUDE_dir_h__ */
diff --git a/src/win32/posix.h b/src/win32/posix.h
index 8f603657b..60adc9666 100644
--- a/src/win32/posix.h
+++ b/src/win32/posix.h
@@ -11,20 +11,20 @@
#include "fnmatch.h"
#include "utf-conv.h"
-GIT_INLINE(int) p_link(const char *GIT_UNUSED(old), const char *GIT_UNUSED(new))
+GIT_INLINE(int) p_link(const char *old, const char *new)
{
- GIT_UNUSED_ARG(old)
- GIT_UNUSED_ARG(new)
+ GIT_UNUSED(old);
+ GIT_UNUSED(new);
errno = ENOSYS;
return -1;
}
-GIT_INLINE(int) p_mkdir(const char *path, mode_t GIT_UNUSED(mode))
+GIT_INLINE(int) p_mkdir(const char *path, mode_t mode)
{
wchar_t* buf = gitwin_to_utf16(path);
int ret = _wmkdir(buf);
- GIT_UNUSED_ARG(mode)
+ GIT_UNUSED(mode);
git__free(buf);
return ret;
diff --git a/src/win32/pthread.c b/src/win32/pthread.c
index cbce639c0..3db536848 100644
--- a/src/win32/pthread.c
+++ b/src/win32/pthread.c
@@ -8,10 +8,10 @@
#include "pthread.h"
int pthread_create(pthread_t *GIT_RESTRICT thread,
- const pthread_attr_t *GIT_RESTRICT GIT_UNUSED(attr),
+ const pthread_attr_t *GIT_RESTRICT attr,
void *(*start_routine)(void*), void *GIT_RESTRICT arg)
{
- GIT_UNUSED_ARG(attr);
+ GIT_UNUSED(attr);
*thread = (pthread_t) CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)start_routine, arg, 0, NULL);
return *thread ? GIT_SUCCESS : git__throw(GIT_EOSERR, "Failed to create pthread");
}
@@ -26,9 +26,9 @@ int pthread_join(pthread_t thread, void **value_ptr)
}
int pthread_mutex_init(pthread_mutex_t *GIT_RESTRICT mutex,
- const pthread_mutexattr_t *GIT_RESTRICT GIT_UNUSED(mutexattr))
+ const pthread_mutexattr_t *GIT_RESTRICT mutexattr)
{
- GIT_UNUSED_ARG(mutexattr);
+ GIT_UNUSED(mutexattr);
InitializeCriticalSection(mutex);
return 0;
}
diff --git a/src/xdiff/xdiff.h b/src/xdiff/xdiff.h
new file mode 100644
index 000000000..cb8b235b5
--- /dev/null
+++ b/src/xdiff/xdiff.h
@@ -0,0 +1,135 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XDIFF_H)
+#define XDIFF_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* #ifdef __cplusplus */
+
+
+#define XDF_NEED_MINIMAL (1 << 1)
+#define XDF_IGNORE_WHITESPACE (1 << 2)
+#define XDF_IGNORE_WHITESPACE_CHANGE (1 << 3)
+#define XDF_IGNORE_WHITESPACE_AT_EOL (1 << 4)
+#define XDF_PATIENCE_DIFF (1 << 5)
+#define XDF_HISTOGRAM_DIFF (1 << 6)
+#define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | XDF_IGNORE_WHITESPACE_CHANGE | XDF_IGNORE_WHITESPACE_AT_EOL)
+
+#define XDL_PATCH_NORMAL '-'
+#define XDL_PATCH_REVERSE '+'
+#define XDL_PATCH_MODEMASK ((1 << 8) - 1)
+#define XDL_PATCH_IGNOREBSPACE (1 << 8)
+
+#define XDL_EMIT_FUNCNAMES (1 << 0)
+#define XDL_EMIT_COMMON (1 << 1)
+#define XDL_EMIT_FUNCCONTEXT (1 << 2)
+
+#define XDL_MMB_READONLY (1 << 0)
+
+#define XDL_MMF_ATOMIC (1 << 0)
+
+#define XDL_BDOP_INS 1
+#define XDL_BDOP_CPY 2
+#define XDL_BDOP_INSB 3
+
+/* merge simplification levels */
+#define XDL_MERGE_MINIMAL 0
+#define XDL_MERGE_EAGER 1
+#define XDL_MERGE_ZEALOUS 2
+#define XDL_MERGE_ZEALOUS_ALNUM 3
+
+/* merge favor modes */
+#define XDL_MERGE_FAVOR_OURS 1
+#define XDL_MERGE_FAVOR_THEIRS 2
+#define XDL_MERGE_FAVOR_UNION 3
+
+/* merge output styles */
+#define XDL_MERGE_DIFF3 1
+
+typedef struct s_mmfile {
+ char *ptr;
+ size_t size;
+} mmfile_t;
+
+typedef struct s_mmbuffer {
+ char *ptr;
+ size_t size;
+} mmbuffer_t;
+
+typedef struct s_xpparam {
+ unsigned long flags;
+} xpparam_t;
+
+typedef struct s_xdemitcb {
+ void *priv;
+ int (*outf)(void *, mmbuffer_t *, int);
+} xdemitcb_t;
+
+typedef long (*find_func_t)(const char *line, long line_len, char *buffer, long buffer_size, void *priv);
+
+typedef struct s_xdemitconf {
+ long ctxlen;
+ long interhunkctxlen;
+ unsigned long flags;
+ find_func_t find_func;
+ void *find_func_priv;
+ void (*emit_func)(void);
+} xdemitconf_t;
+
+typedef struct s_bdiffparam {
+ long bsize;
+} bdiffparam_t;
+
+
+#define xdl_malloc(x) malloc(x)
+#define xdl_free(ptr) free(ptr)
+#define xdl_realloc(ptr,x) realloc(ptr,x)
+
+void *xdl_mmfile_first(mmfile_t *mmf, long *size);
+long xdl_mmfile_size(mmfile_t *mmf);
+
+int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+ xdemitconf_t const *xecfg, xdemitcb_t *ecb);
+
+typedef struct s_xmparam {
+ xpparam_t xpp;
+ int marker_size;
+ int level;
+ int favor;
+ int style;
+ const char *ancestor; /* label for orig */
+ const char *file1; /* label for mf1 */
+ const char *file2; /* label for mf2 */
+} xmparam_t;
+
+#define DEFAULT_CONFLICT_MARKER_SIZE 7
+
+int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2,
+ xmparam_t const *xmp, mmbuffer_t *result);
+
+#ifdef __cplusplus
+}
+#endif /* #ifdef __cplusplus */
+
+#endif /* #if !defined(XDIFF_H) */
diff --git a/src/xdiff/xdiffi.c b/src/xdiff/xdiffi.c
new file mode 100644
index 000000000..75a392275
--- /dev/null
+++ b/src/xdiff/xdiffi.c
@@ -0,0 +1,572 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#include "xinclude.h"
+
+
+
+#define XDL_MAX_COST_MIN 256
+#define XDL_HEUR_MIN_COST 256
+#define XDL_LINE_MAX (long)((1UL << (CHAR_BIT * sizeof(long) - 1)) - 1)
+#define XDL_SNAKE_CNT 20
+#define XDL_K_HEUR 4
+
+
+
+typedef struct s_xdpsplit {
+ long i1, i2;
+ int min_lo, min_hi;
+} xdpsplit_t;
+
+
+
+
+static long xdl_split(unsigned long const *ha1, long off1, long lim1,
+ unsigned long const *ha2, long off2, long lim2,
+ long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl,
+ xdalgoenv_t *xenv);
+static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, long chg2);
+
+
+
+
+
+/*
+ * See "An O(ND) Difference Algorithm and its Variations", by Eugene Myers.
+ * Basically considers a "box" (off1, off2, lim1, lim2) and scan from both
+ * the forward diagonal starting from (off1, off2) and the backward diagonal
+ * starting from (lim1, lim2). If the K values on the same diagonal crosses
+ * returns the furthest point of reach. We might end up having to expensive
+ * cases using this algorithm is full, so a little bit of heuristic is needed
+ * to cut the search and to return a suboptimal point.
+ */
+static long xdl_split(unsigned long const *ha1, long off1, long lim1,
+ unsigned long const *ha2, long off2, long lim2,
+ long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl,
+ xdalgoenv_t *xenv) {
+ long dmin = off1 - lim2, dmax = lim1 - off2;
+ long fmid = off1 - off2, bmid = lim1 - lim2;
+ long odd = (fmid - bmid) & 1;
+ long fmin = fmid, fmax = fmid;
+ long bmin = bmid, bmax = bmid;
+ long ec, d, i1, i2, prev1, best, dd, v, k;
+
+ /*
+ * Set initial diagonal values for both forward and backward path.
+ */
+ kvdf[fmid] = off1;
+ kvdb[bmid] = lim1;
+
+ for (ec = 1;; ec++) {
+ int got_snake = 0;
+
+ /*
+ * We need to extent the diagonal "domain" by one. If the next
+ * values exits the box boundaries we need to change it in the
+ * opposite direction because (max - min) must be a power of two.
+ * Also we initialize the external K value to -1 so that we can
+ * avoid extra conditions check inside the core loop.
+ */
+ if (fmin > dmin)
+ kvdf[--fmin - 1] = -1;
+ else
+ ++fmin;
+ if (fmax < dmax)
+ kvdf[++fmax + 1] = -1;
+ else
+ --fmax;
+
+ for (d = fmax; d >= fmin; d -= 2) {
+ if (kvdf[d - 1] >= kvdf[d + 1])
+ i1 = kvdf[d - 1] + 1;
+ else
+ i1 = kvdf[d + 1];
+ prev1 = i1;
+ i2 = i1 - d;
+ for (; i1 < lim1 && i2 < lim2 && ha1[i1] == ha2[i2]; i1++, i2++);
+ if (i1 - prev1 > xenv->snake_cnt)
+ got_snake = 1;
+ kvdf[d] = i1;
+ if (odd && bmin <= d && d <= bmax && kvdb[d] <= i1) {
+ spl->i1 = i1;
+ spl->i2 = i2;
+ spl->min_lo = spl->min_hi = 1;
+ return ec;
+ }
+ }
+
+ /*
+ * We need to extent the diagonal "domain" by one. If the next
+ * values exits the box boundaries we need to change it in the
+ * opposite direction because (max - min) must be a power of two.
+ * Also we initialize the external K value to -1 so that we can
+ * avoid extra conditions check inside the core loop.
+ */
+ if (bmin > dmin)
+ kvdb[--bmin - 1] = XDL_LINE_MAX;
+ else
+ ++bmin;
+ if (bmax < dmax)
+ kvdb[++bmax + 1] = XDL_LINE_MAX;
+ else
+ --bmax;
+
+ for (d = bmax; d >= bmin; d -= 2) {
+ if (kvdb[d - 1] < kvdb[d + 1])
+ i1 = kvdb[d - 1];
+ else
+ i1 = kvdb[d + 1] - 1;
+ prev1 = i1;
+ i2 = i1 - d;
+ for (; i1 > off1 && i2 > off2 && ha1[i1 - 1] == ha2[i2 - 1]; i1--, i2--);
+ if (prev1 - i1 > xenv->snake_cnt)
+ got_snake = 1;
+ kvdb[d] = i1;
+ if (!odd && fmin <= d && d <= fmax && i1 <= kvdf[d]) {
+ spl->i1 = i1;
+ spl->i2 = i2;
+ spl->min_lo = spl->min_hi = 1;
+ return ec;
+ }
+ }
+
+ if (need_min)
+ continue;
+
+ /*
+ * If the edit cost is above the heuristic trigger and if
+ * we got a good snake, we sample current diagonals to see
+ * if some of the, have reached an "interesting" path. Our
+ * measure is a function of the distance from the diagonal
+ * corner (i1 + i2) penalized with the distance from the
+ * mid diagonal itself. If this value is above the current
+ * edit cost times a magic factor (XDL_K_HEUR) we consider
+ * it interesting.
+ */
+ if (got_snake && ec > xenv->heur_min) {
+ for (best = 0, d = fmax; d >= fmin; d -= 2) {
+ dd = d > fmid ? d - fmid: fmid - d;
+ i1 = kvdf[d];
+ i2 = i1 - d;
+ v = (i1 - off1) + (i2 - off2) - dd;
+
+ if (v > XDL_K_HEUR * ec && v > best &&
+ off1 + xenv->snake_cnt <= i1 && i1 < lim1 &&
+ off2 + xenv->snake_cnt <= i2 && i2 < lim2) {
+ for (k = 1; ha1[i1 - k] == ha2[i2 - k]; k++)
+ if (k == xenv->snake_cnt) {
+ best = v;
+ spl->i1 = i1;
+ spl->i2 = i2;
+ break;
+ }
+ }
+ }
+ if (best > 0) {
+ spl->min_lo = 1;
+ spl->min_hi = 0;
+ return ec;
+ }
+
+ for (best = 0, d = bmax; d >= bmin; d -= 2) {
+ dd = d > bmid ? d - bmid: bmid - d;
+ i1 = kvdb[d];
+ i2 = i1 - d;
+ v = (lim1 - i1) + (lim2 - i2) - dd;
+
+ if (v > XDL_K_HEUR * ec && v > best &&
+ off1 < i1 && i1 <= lim1 - xenv->snake_cnt &&
+ off2 < i2 && i2 <= lim2 - xenv->snake_cnt) {
+ for (k = 0; ha1[i1 + k] == ha2[i2 + k]; k++)
+ if (k == xenv->snake_cnt - 1) {
+ best = v;
+ spl->i1 = i1;
+ spl->i2 = i2;
+ break;
+ }
+ }
+ }
+ if (best > 0) {
+ spl->min_lo = 0;
+ spl->min_hi = 1;
+ return ec;
+ }
+ }
+
+ /*
+ * Enough is enough. We spent too much time here and now we collect
+ * the furthest reaching path using the (i1 + i2) measure.
+ */
+ if (ec >= xenv->mxcost) {
+ long fbest, fbest1, bbest, bbest1;
+
+ fbest = fbest1 = -1;
+ for (d = fmax; d >= fmin; d -= 2) {
+ i1 = XDL_MIN(kvdf[d], lim1);
+ i2 = i1 - d;
+ if (lim2 < i2)
+ i1 = lim2 + d, i2 = lim2;
+ if (fbest < i1 + i2) {
+ fbest = i1 + i2;
+ fbest1 = i1;
+ }
+ }
+
+ bbest = bbest1 = XDL_LINE_MAX;
+ for (d = bmax; d >= bmin; d -= 2) {
+ i1 = XDL_MAX(off1, kvdb[d]);
+ i2 = i1 - d;
+ if (i2 < off2)
+ i1 = off2 + d, i2 = off2;
+ if (i1 + i2 < bbest) {
+ bbest = i1 + i2;
+ bbest1 = i1;
+ }
+ }
+
+ if ((lim1 + lim2) - bbest < fbest - (off1 + off2)) {
+ spl->i1 = fbest1;
+ spl->i2 = fbest - fbest1;
+ spl->min_lo = 1;
+ spl->min_hi = 0;
+ } else {
+ spl->i1 = bbest1;
+ spl->i2 = bbest - bbest1;
+ spl->min_lo = 0;
+ spl->min_hi = 1;
+ }
+ return ec;
+ }
+ }
+}
+
+
+/*
+ * Rule: "Divide et Impera". Recursively split the box in sub-boxes by calling
+ * the box splitting function. Note that the real job (marking changed lines)
+ * is done in the two boundary reaching checks.
+ */
+int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,
+ diffdata_t *dd2, long off2, long lim2,
+ long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) {
+ unsigned long const *ha1 = dd1->ha, *ha2 = dd2->ha;
+
+ /*
+ * Shrink the box by walking through each diagonal snake (SW and NE).
+ */
+ for (; off1 < lim1 && off2 < lim2 && ha1[off1] == ha2[off2]; off1++, off2++);
+ for (; off1 < lim1 && off2 < lim2 && ha1[lim1 - 1] == ha2[lim2 - 1]; lim1--, lim2--);
+
+ /*
+ * If one dimension is empty, then all records on the other one must
+ * be obviously changed.
+ */
+ if (off1 == lim1) {
+ char *rchg2 = dd2->rchg;
+ long *rindex2 = dd2->rindex;
+
+ for (; off2 < lim2; off2++)
+ rchg2[rindex2[off2]] = 1;
+ } else if (off2 == lim2) {
+ char *rchg1 = dd1->rchg;
+ long *rindex1 = dd1->rindex;
+
+ for (; off1 < lim1; off1++)
+ rchg1[rindex1[off1]] = 1;
+ } else {
+ xdpsplit_t spl;
+ spl.i1 = spl.i2 = 0;
+
+ /*
+ * Divide ...
+ */
+ if (xdl_split(ha1, off1, lim1, ha2, off2, lim2, kvdf, kvdb,
+ need_min, &spl, xenv) < 0) {
+
+ return -1;
+ }
+
+ /*
+ * ... et Impera.
+ */
+ if (xdl_recs_cmp(dd1, off1, spl.i1, dd2, off2, spl.i2,
+ kvdf, kvdb, spl.min_lo, xenv) < 0 ||
+ xdl_recs_cmp(dd1, spl.i1, lim1, dd2, spl.i2, lim2,
+ kvdf, kvdb, spl.min_hi, xenv) < 0) {
+
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+ xdfenv_t *xe) {
+ long ndiags;
+ long *kvd, *kvdf, *kvdb;
+ xdalgoenv_t xenv;
+ diffdata_t dd1, dd2;
+
+ if (xpp->flags & XDF_PATIENCE_DIFF)
+ return xdl_do_patience_diff(mf1, mf2, xpp, xe);
+
+ if (xpp->flags & XDF_HISTOGRAM_DIFF)
+ return xdl_do_histogram_diff(mf1, mf2, xpp, xe);
+
+ if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0) {
+
+ return -1;
+ }
+
+ /*
+ * Allocate and setup K vectors to be used by the differential algorithm.
+ * One is to store the forward path and one to store the backward path.
+ */
+ ndiags = xe->xdf1.nreff + xe->xdf2.nreff + 3;
+ if (!(kvd = (long *) xdl_malloc((2 * ndiags + 2) * sizeof(long)))) {
+
+ xdl_free_env(xe);
+ return -1;
+ }
+ kvdf = kvd;
+ kvdb = kvdf + ndiags;
+ kvdf += xe->xdf2.nreff + 1;
+ kvdb += xe->xdf2.nreff + 1;
+
+ xenv.mxcost = xdl_bogosqrt(ndiags);
+ if (xenv.mxcost < XDL_MAX_COST_MIN)
+ xenv.mxcost = XDL_MAX_COST_MIN;
+ xenv.snake_cnt = XDL_SNAKE_CNT;
+ xenv.heur_min = XDL_HEUR_MIN_COST;
+
+ dd1.nrec = xe->xdf1.nreff;
+ dd1.ha = xe->xdf1.ha;
+ dd1.rchg = xe->xdf1.rchg;
+ dd1.rindex = xe->xdf1.rindex;
+ dd2.nrec = xe->xdf2.nreff;
+ dd2.ha = xe->xdf2.ha;
+ dd2.rchg = xe->xdf2.rchg;
+ dd2.rindex = xe->xdf2.rindex;
+
+ if (xdl_recs_cmp(&dd1, 0, dd1.nrec, &dd2, 0, dd2.nrec,
+ kvdf, kvdb, (xpp->flags & XDF_NEED_MINIMAL) != 0, &xenv) < 0) {
+
+ xdl_free(kvd);
+ xdl_free_env(xe);
+ return -1;
+ }
+
+ xdl_free(kvd);
+
+ return 0;
+}
+
+
+static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, long chg2) {
+ xdchange_t *xch;
+
+ if (!(xch = (xdchange_t *) xdl_malloc(sizeof(xdchange_t))))
+ return NULL;
+
+ xch->next = xscr;
+ xch->i1 = i1;
+ xch->i2 = i2;
+ xch->chg1 = chg1;
+ xch->chg2 = chg2;
+
+ return xch;
+}
+
+
+int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
+ long ix, ixo, ixs, ixref, grpsiz, nrec = xdf->nrec;
+ char *rchg = xdf->rchg, *rchgo = xdfo->rchg;
+ xrecord_t **recs = xdf->recs;
+
+ /*
+ * This is the same of what GNU diff does. Move back and forward
+ * change groups for a consistent and pretty diff output. This also
+ * helps in finding joinable change groups and reduce the diff size.
+ */
+ for (ix = ixo = 0;;) {
+ /*
+ * Find the first changed line in the to-be-compacted file.
+ * We need to keep track of both indexes, so if we find a
+ * changed lines group on the other file, while scanning the
+ * to-be-compacted file, we need to skip it properly. Note
+ * that loops that are testing for changed lines on rchg* do
+ * not need index bounding since the array is prepared with
+ * a zero at position -1 and N.
+ */
+ for (; ix < nrec && !rchg[ix]; ix++)
+ while (rchgo[ixo++]);
+ if (ix == nrec)
+ break;
+
+ /*
+ * Record the start of a changed-group in the to-be-compacted file
+ * and find the end of it, on both to-be-compacted and other file
+ * indexes (ix and ixo).
+ */
+ ixs = ix;
+ for (ix++; rchg[ix]; ix++);
+ for (; rchgo[ixo]; ixo++);
+
+ do {
+ grpsiz = ix - ixs;
+
+ /*
+ * If the line before the current change group, is equal to
+ * the last line of the current change group, shift backward
+ * the group.
+ */
+ while (ixs > 0 && recs[ixs - 1]->ha == recs[ix - 1]->ha &&
+ xdl_recmatch(recs[ixs - 1]->ptr, recs[ixs - 1]->size, recs[ix - 1]->ptr, recs[ix - 1]->size, flags)) {
+ rchg[--ixs] = 1;
+ rchg[--ix] = 0;
+
+ /*
+ * This change might have joined two change groups,
+ * so we try to take this scenario in account by moving
+ * the start index accordingly (and so the other-file
+ * end-of-group index).
+ */
+ for (; rchg[ixs - 1]; ixs--);
+ while (rchgo[--ixo]);
+ }
+
+ /*
+ * Record the end-of-group position in case we are matched
+ * with a group of changes in the other file (that is, the
+ * change record before the end-of-group index in the other
+ * file is set).
+ */
+ ixref = rchgo[ixo - 1] ? ix: nrec;
+
+ /*
+ * If the first line of the current change group, is equal to
+ * the line next of the current change group, shift forward
+ * the group.
+ */
+ while (ix < nrec && recs[ixs]->ha == recs[ix]->ha &&
+ xdl_recmatch(recs[ixs]->ptr, recs[ixs]->size, recs[ix]->ptr, recs[ix]->size, flags)) {
+ rchg[ixs++] = 0;
+ rchg[ix++] = 1;
+
+ /*
+ * This change might have joined two change groups,
+ * so we try to take this scenario in account by moving
+ * the start index accordingly (and so the other-file
+ * end-of-group index). Keep tracking the reference
+ * index in case we are shifting together with a
+ * corresponding group of changes in the other file.
+ */
+ for (; rchg[ix]; ix++);
+ while (rchgo[++ixo])
+ ixref = ix;
+ }
+ } while (grpsiz != ix - ixs);
+
+ /*
+ * Try to move back the possibly merged group of changes, to match
+ * the recorded postion in the other file.
+ */
+ while (ixref < ix) {
+ rchg[--ixs] = 1;
+ rchg[--ix] = 0;
+ while (rchgo[--ixo]);
+ }
+ }
+
+ return 0;
+}
+
+
+int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {
+ xdchange_t *cscr = NULL, *xch;
+ char *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg;
+ long i1, i2, l1, l2;
+
+ /*
+ * Trivial. Collects "groups" of changes and creates an edit script.
+ */
+ for (i1 = xe->xdf1.nrec, i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--, i2--)
+ if (rchg1[i1 - 1] || rchg2[i2 - 1]) {
+ for (l1 = i1; rchg1[i1 - 1]; i1--);
+ for (l2 = i2; rchg2[i2 - 1]; i2--);
+
+ if (!(xch = xdl_add_change(cscr, i1, i2, l1 - i1, l2 - i2))) {
+ xdl_free_script(cscr);
+ return -1;
+ }
+ cscr = xch;
+ }
+
+ *xscr = cscr;
+
+ return 0;
+}
+
+
+void xdl_free_script(xdchange_t *xscr) {
+ xdchange_t *xch;
+
+ while ((xch = xscr) != NULL) {
+ xscr = xscr->next;
+ xdl_free(xch);
+ }
+}
+
+
+int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+ xdemitconf_t const *xecfg, xdemitcb_t *ecb) {
+ xdchange_t *xscr;
+ xdfenv_t xe;
+ emit_func_t ef = xecfg->emit_func ?
+ (emit_func_t)xecfg->emit_func : xdl_emit_diff;
+
+ if (xdl_do_diff(mf1, mf2, xpp, &xe) < 0) {
+
+ return -1;
+ }
+ if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||
+ xdl_change_compact(&xe.xdf2, &xe.xdf1, xpp->flags) < 0 ||
+ xdl_build_script(&xe, &xscr) < 0) {
+
+ xdl_free_env(&xe);
+ return -1;
+ }
+ if (xscr) {
+ if (ef(&xe, xscr, ecb, xecfg) < 0) {
+
+ xdl_free_script(xscr);
+ xdl_free_env(&xe);
+ return -1;
+ }
+ xdl_free_script(xscr);
+ }
+ xdl_free_env(&xe);
+
+ return 0;
+}
diff --git a/src/xdiff/xdiffi.h b/src/xdiff/xdiffi.h
new file mode 100644
index 000000000..7a92ea9c4
--- /dev/null
+++ b/src/xdiff/xdiffi.h
@@ -0,0 +1,63 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XDIFFI_H)
+#define XDIFFI_H
+
+
+typedef struct s_diffdata {
+ long nrec;
+ unsigned long const *ha;
+ long *rindex;
+ char *rchg;
+} diffdata_t;
+
+typedef struct s_xdalgoenv {
+ long mxcost;
+ long snake_cnt;
+ long heur_min;
+} xdalgoenv_t;
+
+typedef struct s_xdchange {
+ struct s_xdchange *next;
+ long i1, i2;
+ long chg1, chg2;
+} xdchange_t;
+
+
+
+int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,
+ diffdata_t *dd2, long off2, long lim2,
+ long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv);
+int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+ xdfenv_t *xe);
+int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags);
+int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr);
+void xdl_free_script(xdchange_t *xscr);
+int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+ xdemitconf_t const *xecfg);
+int xdl_do_patience_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+ xdfenv_t *env);
+int xdl_do_histogram_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+ xdfenv_t *env);
+
+#endif /* #if !defined(XDIFFI_H) */
diff --git a/src/xdiff/xemit.c b/src/xdiff/xemit.c
new file mode 100644
index 000000000..8b7d417a1
--- /dev/null
+++ b/src/xdiff/xemit.c
@@ -0,0 +1,253 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#include "xinclude.h"
+
+
+
+
+static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec);
+static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb);
+
+
+
+
+static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) {
+
+ *rec = xdf->recs[ri]->ptr;
+
+ return xdf->recs[ri]->size;
+}
+
+
+static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) {
+ long size, psize = strlen(pre);
+ char const *rec;
+
+ size = xdl_get_rec(xdf, ri, &rec);
+ if (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0) {
+
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Starting at the passed change atom, find the latest change atom to be included
+ * inside the differential hunk according to the specified configuration.
+ */
+xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg) {
+ xdchange_t *xch, *xchp;
+ long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;
+
+ for (xchp = xscr, xch = xscr->next; xch; xchp = xch, xch = xch->next)
+ if (xch->i1 - (xchp->i1 + xchp->chg1) > max_common)
+ break;
+
+ return xchp;
+}
+
+
+static long def_ff(const char *rec, long len, char *buf, long sz, void *priv)
+{
+ (void)priv;
+
+ if (len > 0 &&
+ (isalpha((unsigned char)*rec) || /* identifier? */
+ *rec == '_' || /* also identifier? */
+ *rec == '$')) { /* identifiers from VMS and other esoterico */
+ if (len > sz)
+ len = sz;
+ while (0 < len && isspace((unsigned char)rec[len - 1]))
+ len--;
+ memcpy(buf, rec, len);
+ return len;
+ }
+ return -1;
+}
+
+static int xdl_emit_common(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+ xdemitconf_t const *xecfg) {
+ xdfile_t *xdf = &xe->xdf2;
+ const char *rchg = xdf->rchg;
+ long ix;
+
+ (void)xscr;
+ (void)xecfg;
+
+ for (ix = 0; ix < xdf->nrec; ix++) {
+ if (rchg[ix])
+ continue;
+ if (xdl_emit_record(xdf, ix, "", ecb))
+ return -1;
+ }
+ return 0;
+}
+
+struct func_line {
+ long len;
+ char buf[80];
+};
+
+static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,
+ struct func_line *func_line, long start, long limit)
+{
+ find_func_t ff = xecfg->find_func ? xecfg->find_func : def_ff;
+ long l, size, step = (start > limit) ? -1 : 1;
+ char *buf, dummy[1];
+
+ buf = func_line ? func_line->buf : dummy;
+ size = func_line ? sizeof(func_line->buf) : sizeof(dummy);
+
+ for (l = start; l != limit && 0 <= l && l < xe->xdf1.nrec; l += step) {
+ const char *rec;
+ long reclen = xdl_get_rec(&xe->xdf1, l, &rec);
+ long len = ff(rec, reclen, buf, size, xecfg->find_func_priv);
+ if (len >= 0) {
+ if (func_line)
+ func_line->len = len;
+ return l;
+ }
+ }
+ return -1;
+}
+
+int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+ xdemitconf_t const *xecfg) {
+ long s1, s2, e1, e2, lctx;
+ xdchange_t *xch, *xche;
+ long funclineprev = -1;
+ struct func_line func_line = { 0 };
+
+ if (xecfg->flags & XDL_EMIT_COMMON)
+ return xdl_emit_common(xe, xscr, ecb, xecfg);
+
+ for (xch = xscr; xch; xch = xche->next) {
+ xche = xdl_get_hunk(xch, xecfg);
+
+ s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
+ s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
+
+ if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) {
+ long fs1 = get_func_line(xe, xecfg, NULL, xch->i1, -1);
+ if (fs1 < 0)
+ fs1 = 0;
+ if (fs1 < s1) {
+ s2 -= s1 - fs1;
+ s1 = fs1;
+ }
+ }
+
+ again:
+ lctx = xecfg->ctxlen;
+ lctx = XDL_MIN(lctx, xe->xdf1.nrec - (xche->i1 + xche->chg1));
+ lctx = XDL_MIN(lctx, xe->xdf2.nrec - (xche->i2 + xche->chg2));
+
+ e1 = xche->i1 + xche->chg1 + lctx;
+ e2 = xche->i2 + xche->chg2 + lctx;
+
+ if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) {
+ long fe1 = get_func_line(xe, xecfg, NULL,
+ xche->i1 + xche->chg1,
+ xe->xdf1.nrec);
+ if (fe1 < 0)
+ fe1 = xe->xdf1.nrec;
+ if (fe1 > e1) {
+ e2 += fe1 - e1;
+ e1 = fe1;
+ }
+
+ /*
+ * Overlap with next change? Then include it
+ * in the current hunk and start over to find
+ * its new end.
+ */
+ if (xche->next) {
+ long l = xche->next->i1;
+ if (l <= e1 ||
+ get_func_line(xe, xecfg, NULL, l, e1) < 0) {
+ xche = xche->next;
+ goto again;
+ }
+ }
+ }
+
+ /*
+ * Emit current hunk header.
+ */
+
+ if (xecfg->flags & XDL_EMIT_FUNCNAMES) {
+ get_func_line(xe, xecfg, &func_line,
+ s1 - 1, funclineprev);
+ funclineprev = s1 - 1;
+ }
+ if (xdl_emit_hunk_hdr(s1 + 1, e1 - s1, s2 + 1, e2 - s2,
+ func_line.buf, func_line.len, ecb) < 0)
+ return -1;
+
+ /*
+ * Emit pre-context.
+ */
+ for (; s2 < xch->i2; s2++)
+ if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0)
+ return -1;
+
+ for (s1 = xch->i1, s2 = xch->i2;; xch = xch->next) {
+ /*
+ * Merge previous with current change atom.
+ */
+ for (; s1 < xch->i1 && s2 < xch->i2; s1++, s2++)
+ if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0)
+ return -1;
+
+ /*
+ * Removes lines from the first file.
+ */
+ for (s1 = xch->i1; s1 < xch->i1 + xch->chg1; s1++)
+ if (xdl_emit_record(&xe->xdf1, s1, "-", ecb) < 0)
+ return -1;
+
+ /*
+ * Adds lines from the second file.
+ */
+ for (s2 = xch->i2; s2 < xch->i2 + xch->chg2; s2++)
+ if (xdl_emit_record(&xe->xdf2, s2, "+", ecb) < 0)
+ return -1;
+
+ if (xch == xche)
+ break;
+ s1 = xch->i1 + xch->chg1;
+ s2 = xch->i2 + xch->chg2;
+ }
+
+ /*
+ * Emit post-context.
+ */
+ for (s2 = xche->i2 + xche->chg2; s2 < e2; s2++)
+ if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0)
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/xdiff/xemit.h b/src/xdiff/xemit.h
new file mode 100644
index 000000000..c2e2e8302
--- /dev/null
+++ b/src/xdiff/xemit.h
@@ -0,0 +1,36 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XEMIT_H)
+#define XEMIT_H
+
+
+typedef int (*emit_func_t)(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+ xdemitconf_t const *xecfg);
+
+xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg);
+int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+ xdemitconf_t const *xecfg);
+
+
+
+#endif /* #if !defined(XEMIT_H) */
diff --git a/src/xdiff/xhistogram.c b/src/xdiff/xhistogram.c
new file mode 100644
index 000000000..5d101754d
--- /dev/null
+++ b/src/xdiff/xhistogram.c
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in JGit's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "xinclude.h"
+#include "xtypes.h"
+#include "xdiff.h"
+
+#define MAX_PTR UINT_MAX
+#define MAX_CNT UINT_MAX
+
+#define LINE_END(n) (line##n + count##n - 1)
+#define LINE_END_PTR(n) (*line##n + *count##n - 1)
+
+struct histindex {
+ struct record {
+ unsigned int ptr, cnt;
+ struct record *next;
+ } **records, /* an ocurrence */
+ **line_map; /* map of line to record chain */
+ chastore_t rcha;
+ unsigned int *next_ptrs;
+ unsigned int table_bits,
+ records_size,
+ line_map_size;
+
+ unsigned int max_chain_length,
+ key_shift,
+ ptr_shift;
+
+ unsigned int cnt,
+ has_common;
+
+ xdfenv_t *env;
+ xpparam_t const *xpp;
+};
+
+struct region {
+ unsigned int begin1, end1;
+ unsigned int begin2, end2;
+};
+
+#define LINE_MAP(i, a) (i->line_map[(a) - i->ptr_shift])
+
+#define NEXT_PTR(index, ptr) \
+ (index->next_ptrs[(ptr) - index->ptr_shift])
+
+#define CNT(index, ptr) \
+ ((LINE_MAP(index, ptr))->cnt)
+
+#define REC(env, s, l) \
+ (env->xdf##s.recs[l - 1])
+
+static int cmp_recs(xpparam_t const *xpp,
+ xrecord_t *r1, xrecord_t *r2)
+{
+ return r1->ha == r2->ha &&
+ xdl_recmatch(r1->ptr, r1->size, r2->ptr, r2->size,
+ xpp->flags);
+}
+
+#define CMP_ENV(xpp, env, s1, l1, s2, l2) \
+ (cmp_recs(xpp, REC(env, s1, l1), REC(env, s2, l2)))
+
+#define CMP(i, s1, l1, s2, l2) \
+ (cmp_recs(i->xpp, REC(i->env, s1, l1), REC(i->env, s2, l2)))
+
+#define TABLE_HASH(index, side, line) \
+ XDL_HASHLONG((REC(index->env, side, line))->ha, index->table_bits)
+
+static int scanA(struct histindex *index, unsigned int line1, unsigned int count1)
+{
+ unsigned int ptr;
+ unsigned int tbl_idx;
+ unsigned int chain_len;
+ struct record **rec_chain, *rec;
+
+ for (ptr = LINE_END(1); line1 <= ptr; ptr--) {
+ tbl_idx = TABLE_HASH(index, 1, ptr);
+ rec_chain = index->records + tbl_idx;
+ rec = *rec_chain;
+
+ chain_len = 0;
+ while (rec) {
+ if (CMP(index, 1, rec->ptr, 1, ptr)) {
+ /*
+ * ptr is identical to another element. Insert
+ * it onto the front of the existing element
+ * chain.
+ */
+ NEXT_PTR(index, ptr) = rec->ptr;
+ rec->ptr = ptr;
+ /* cap rec->cnt at MAX_CNT */
+ rec->cnt = XDL_MIN(MAX_CNT, rec->cnt + 1);
+ LINE_MAP(index, ptr) = rec;
+ goto continue_scan;
+ }
+
+ rec = rec->next;
+ chain_len++;
+ }
+
+ if (chain_len == index->max_chain_length)
+ return -1;
+
+ /*
+ * This is the first time we have ever seen this particular
+ * element in the sequence. Construct a new chain for it.
+ */
+ if (!(rec = xdl_cha_alloc(&index->rcha)))
+ return -1;
+ rec->ptr = ptr;
+ rec->cnt = 1;
+ rec->next = *rec_chain;
+ *rec_chain = rec;
+ LINE_MAP(index, ptr) = rec;
+
+continue_scan:
+ ; /* no op */
+ }
+
+ return 0;
+}
+
+static int try_lcs(
+ struct histindex *index, struct region *lcs, unsigned int b_ptr,
+ unsigned int line1, unsigned int count1,
+ unsigned int line2, unsigned int count2)
+{
+ unsigned int b_next = b_ptr + 1;
+ struct record *rec = index->records[TABLE_HASH(index, 2, b_ptr)];
+ unsigned int as, ae, bs, be, np, rc;
+ int should_break;
+
+ for (; rec; rec = rec->next) {
+ if (rec->cnt > index->cnt) {
+ if (!index->has_common)
+ index->has_common = CMP(index, 1, rec->ptr, 2, b_ptr);
+ continue;
+ }
+
+ as = rec->ptr;
+ if (!CMP(index, 1, as, 2, b_ptr))
+ continue;
+
+ index->has_common = 1;
+ for (;;) {
+ should_break = 0;
+ np = NEXT_PTR(index, as);
+ bs = b_ptr;
+ ae = as;
+ be = bs;
+ rc = rec->cnt;
+
+ while (line1 < as && line2 < bs
+ && CMP(index, 1, as - 1, 2, bs - 1)) {
+ as--;
+ bs--;
+ if (1 < rc)
+ rc = XDL_MIN(rc, CNT(index, as));
+ }
+ while (ae < LINE_END(1) && be < LINE_END(2)
+ && CMP(index, 1, ae + 1, 2, be + 1)) {
+ ae++;
+ be++;
+ if (1 < rc)
+ rc = XDL_MIN(rc, CNT(index, ae));
+ }
+
+ if (b_next <= be)
+ b_next = be + 1;
+ if (lcs->end1 - lcs->begin1 < ae - as || rc < index->cnt) {
+ lcs->begin1 = as;
+ lcs->begin2 = bs;
+ lcs->end1 = ae;
+ lcs->end2 = be;
+ index->cnt = rc;
+ }
+
+ if (np == 0)
+ break;
+
+ while (np <= ae) {
+ np = NEXT_PTR(index, np);
+ if (np == 0) {
+ should_break = 1;
+ break;
+ }
+ }
+
+ if (should_break)
+ break;
+
+ as = np;
+ }
+ }
+ return b_next;
+}
+
+static int find_lcs(
+ struct histindex *index, struct region *lcs,
+ unsigned int line1, unsigned int count1,
+ unsigned int line2, unsigned int count2)
+{
+ unsigned int b_ptr;
+
+ if (scanA(index, line1, count1))
+ return -1;
+
+ index->cnt = index->max_chain_length + 1;
+
+ for (b_ptr = line2; b_ptr <= LINE_END(2); )
+ b_ptr = try_lcs(index, lcs, b_ptr, line1, count1, line2, count2);
+
+ return index->has_common && index->max_chain_length < index->cnt;
+}
+
+static int fall_back_to_classic_diff(struct histindex *index,
+ int line1, int count1, int line2, int count2)
+{
+ xpparam_t xpp;
+ xpp.flags = index->xpp->flags & ~XDF_HISTOGRAM_DIFF;
+
+ return xdl_fall_back_diff(index->env, &xpp,
+ line1, count1, line2, count2);
+}
+
+static int histogram_diff(
+ xpparam_t const *xpp, xdfenv_t *env,
+ unsigned int line1, unsigned int count1,
+ unsigned int line2, unsigned int count2)
+{
+ struct histindex index;
+ struct region lcs;
+ unsigned int sz;
+ int result = -1;
+
+ if (count1 <= 0 && count2 <= 0)
+ return 0;
+
+ if (LINE_END(1) >= MAX_PTR)
+ return -1;
+
+ if (!count1) {
+ while(count2--)
+ env->xdf2.rchg[line2++ - 1] = 1;
+ return 0;
+ } else if (!count2) {
+ while(count1--)
+ env->xdf1.rchg[line1++ - 1] = 1;
+ return 0;
+ }
+
+ memset(&index, 0, sizeof(index));
+
+ index.env = env;
+ index.xpp = xpp;
+
+ index.records = NULL;
+ index.line_map = NULL;
+ /* in case of early xdl_cha_free() */
+ index.rcha.head = NULL;
+
+ index.table_bits = xdl_hashbits(count1);
+ sz = index.records_size = 1 << index.table_bits;
+ sz *= sizeof(struct record *);
+ if (!(index.records = (struct record **) xdl_malloc(sz)))
+ goto cleanup;
+ memset(index.records, 0, sz);
+
+ sz = index.line_map_size = count1;
+ sz *= sizeof(struct record *);
+ if (!(index.line_map = (struct record **) xdl_malloc(sz)))
+ goto cleanup;
+ memset(index.line_map, 0, sz);
+
+ sz = index.line_map_size;
+ sz *= sizeof(unsigned int);
+ if (!(index.next_ptrs = (unsigned int *) xdl_malloc(sz)))
+ goto cleanup;
+ memset(index.next_ptrs, 0, sz);
+
+ /* lines / 4 + 1 comes from xprepare.c:xdl_prepare_ctx() */
+ if (xdl_cha_init(&index.rcha, sizeof(struct record), count1 / 4 + 1) < 0)
+ goto cleanup;
+
+ index.ptr_shift = line1;
+ index.max_chain_length = 64;
+
+ memset(&lcs, 0, sizeof(lcs));
+ if (find_lcs(&index, &lcs, line1, count1, line2, count2))
+ result = fall_back_to_classic_diff(&index, line1, count1, line2, count2);
+ else {
+ if (lcs.begin1 == 0 && lcs.begin2 == 0) {
+ while (count1--)
+ env->xdf1.rchg[line1++ - 1] = 1;
+ while (count2--)
+ env->xdf2.rchg[line2++ - 1] = 1;
+ result = 0;
+ } else {
+ result = histogram_diff(xpp, env,
+ line1, lcs.begin1 - line1,
+ line2, lcs.begin2 - line2);
+ if (result)
+ goto cleanup;
+ result = histogram_diff(xpp, env,
+ lcs.end1 + 1, LINE_END(1) - lcs.end1,
+ lcs.end2 + 1, LINE_END(2) - lcs.end2);
+ if (result)
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ xdl_free(index.records);
+ xdl_free(index.line_map);
+ xdl_free(index.next_ptrs);
+ xdl_cha_free(&index.rcha);
+
+ return result;
+}
+
+int xdl_do_histogram_diff(mmfile_t *file1, mmfile_t *file2,
+ xpparam_t const *xpp, xdfenv_t *env)
+{
+ if (xdl_prepare_env(file1, file2, xpp, env) < 0)
+ return -1;
+
+ return histogram_diff(xpp, env,
+ env->xdf1.dstart + 1, env->xdf1.dend - env->xdf1.dstart + 1,
+ env->xdf2.dstart + 1, env->xdf2.dend - env->xdf2.dstart + 1);
+}
diff --git a/src/xdiff/xinclude.h b/src/xdiff/xinclude.h
new file mode 100644
index 000000000..2928d329b
--- /dev/null
+++ b/src/xdiff/xinclude.h
@@ -0,0 +1,46 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XINCLUDE_H)
+#define XINCLUDE_H
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#ifdef WIN32
+#else
+#include <unistd.h>
+#endif
+
+#include "xmacros.h"
+#include "xdiff.h"
+#include "xtypes.h"
+#include "xutils.h"
+#include "xprepare.h"
+#include "xdiffi.h"
+#include "xemit.h"
+
+
+#endif /* #if !defined(XINCLUDE_H) */
diff --git a/src/xdiff/xmacros.h b/src/xdiff/xmacros.h
new file mode 100644
index 000000000..165a895a9
--- /dev/null
+++ b/src/xdiff/xmacros.h
@@ -0,0 +1,54 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XMACROS_H)
+#define XMACROS_H
+
+
+
+
+#define XDL_MIN(a, b) ((a) < (b) ? (a): (b))
+#define XDL_MAX(a, b) ((a) > (b) ? (a): (b))
+#define XDL_ABS(v) ((v) >= 0 ? (v): -(v))
+#define XDL_ISDIGIT(c) ((c) >= '0' && (c) <= '9')
+#define XDL_ISSPACE(c) (isspace((unsigned char)(c)))
+#define XDL_ADDBITS(v,b) ((v) + ((v) >> (b)))
+#define XDL_MASKBITS(b) ((1UL << (b)) - 1)
+#define XDL_HASHLONG(v,b) (XDL_ADDBITS((unsigned long)(v), b) & XDL_MASKBITS(b))
+#define XDL_PTRFREE(p) do { if (p) { xdl_free(p); (p) = NULL; } } while (0)
+#define XDL_LE32_PUT(p, v) \
+do { \
+ unsigned char *__p = (unsigned char *) (p); \
+ *__p++ = (unsigned char) (v); \
+ *__p++ = (unsigned char) ((v) >> 8); \
+ *__p++ = (unsigned char) ((v) >> 16); \
+ *__p = (unsigned char) ((v) >> 24); \
+} while (0)
+#define XDL_LE32_GET(p, v) \
+do { \
+ unsigned char const *__p = (unsigned char const *) (p); \
+ (v) = (unsigned long) __p[0] | ((unsigned long) __p[1]) << 8 | \
+ ((unsigned long) __p[2]) << 16 | ((unsigned long) __p[3]) << 24; \
+} while (0)
+
+
+#endif /* #if !defined(XMACROS_H) */
diff --git a/src/xdiff/xmerge.c b/src/xdiff/xmerge.c
new file mode 100644
index 000000000..9e13b25ab
--- /dev/null
+++ b/src/xdiff/xmerge.c
@@ -0,0 +1,619 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003-2006 Davide Libenzi, Johannes E. Schindelin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#include "xinclude.h"
+
+typedef struct s_xdmerge {
+ struct s_xdmerge *next;
+ /*
+ * 0 = conflict,
+ * 1 = no conflict, take first,
+ * 2 = no conflict, take second.
+ * 3 = no conflict, take both.
+ */
+ int mode;
+ /*
+ * These point at the respective postimages. E.g. <i1,chg1> is
+ * how side #1 wants to change the common ancestor; if there is no
+ * overlap, lines before i1 in the postimage of side #1 appear
+ * in the merge result as a region touched by neither side.
+ */
+ long i1, i2;
+ long chg1, chg2;
+ /*
+ * These point at the preimage; of course there is just one
+ * preimage, that is from the shared common ancestor.
+ */
+ long i0;
+ long chg0;
+} xdmerge_t;
+
+static int xdl_append_merge(xdmerge_t **merge, int mode,
+ long i0, long chg0,
+ long i1, long chg1,
+ long i2, long chg2)
+{
+ xdmerge_t *m = *merge;
+ if (m && (i1 <= m->i1 + m->chg1 || i2 <= m->i2 + m->chg2)) {
+ if (mode != m->mode)
+ m->mode = 0;
+ m->chg0 = i0 + chg0 - m->i0;
+ m->chg1 = i1 + chg1 - m->i1;
+ m->chg2 = i2 + chg2 - m->i2;
+ } else {
+ m = xdl_malloc(sizeof(xdmerge_t));
+ if (!m)
+ return -1;
+ m->next = NULL;
+ m->mode = mode;
+ m->i0 = i0;
+ m->chg0 = chg0;
+ m->i1 = i1;
+ m->chg1 = chg1;
+ m->i2 = i2;
+ m->chg2 = chg2;
+ if (*merge)
+ (*merge)->next = m;
+ *merge = m;
+ }
+ return 0;
+}
+
+static int xdl_cleanup_merge(xdmerge_t *c)
+{
+ int count = 0;
+ xdmerge_t *next_c;
+
+ /* were there conflicts? */
+ for (; c; c = next_c) {
+ if (c->mode == 0)
+ count++;
+ next_c = c->next;
+ free(c);
+ }
+ return count;
+}
+
+static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,
+ int line_count, long flags)
+{
+ int i;
+ xrecord_t **rec1 = xe1->xdf2.recs + i1;
+ xrecord_t **rec2 = xe2->xdf2.recs + i2;
+
+ for (i = 0; i < line_count; i++) {
+ int result = xdl_recmatch(rec1[i]->ptr, rec1[i]->size,
+ rec2[i]->ptr, rec2[i]->size, flags);
+ if (!result)
+ return -1;
+ }
+ return 0;
+}
+
+static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+{
+ xrecord_t **recs;
+ int size = 0;
+
+ recs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i;
+
+ if (count < 1)
+ return 0;
+
+ for (i = 0; i < count; size += recs[i++]->size)
+ if (dest)
+ memcpy(dest + size, recs[i]->ptr, recs[i]->size);
+ if (add_nl) {
+ i = recs[count - 1]->size;
+ if (i == 0 || recs[count - 1]->ptr[i - 1] != '\n') {
+ if (dest)
+ dest[size] = '\n';
+ size++;
+ }
+ }
+ return size;
+}
+
+static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+{
+ return xdl_recs_copy_0(0, xe, i, count, add_nl, dest);
+}
+
+static int xdl_orig_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+{
+ return xdl_recs_copy_0(1, xe, i, count, add_nl, dest);
+}
+
+static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
+ xdfenv_t *xe2, const char *name2,
+ const char *name3,
+ int size, int i, int style,
+ xdmerge_t *m, char *dest, int marker_size)
+{
+ int marker1_size = (name1 ? strlen(name1) + 1 : 0);
+ int marker2_size = (name2 ? strlen(name2) + 1 : 0);
+ int marker3_size = (name3 ? strlen(name3) + 1 : 0);
+
+ if (marker_size <= 0)
+ marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
+
+ /* Before conflicting part */
+ size += xdl_recs_copy(xe1, i, m->i1 - i, 0,
+ dest ? dest + size : NULL);
+
+ if (!dest) {
+ size += marker_size + 1 + marker1_size;
+ } else {
+ memset(dest + size, '<', marker_size);
+ size += marker_size;
+ if (marker1_size) {
+ dest[size] = ' ';
+ memcpy(dest + size + 1, name1, marker1_size - 1);
+ size += marker1_size;
+ }
+ dest[size++] = '\n';
+ }
+
+ /* Postimage from side #1 */
+ size += xdl_recs_copy(xe1, m->i1, m->chg1, 1,
+ dest ? dest + size : NULL);
+
+ if (style == XDL_MERGE_DIFF3) {
+ /* Shared preimage */
+ if (!dest) {
+ size += marker_size + 1 + marker3_size;
+ } else {
+ memset(dest + size, '|', marker_size);
+ size += marker_size;
+ if (marker3_size) {
+ dest[size] = ' ';
+ memcpy(dest + size + 1, name3, marker3_size - 1);
+ size += marker3_size;
+ }
+ dest[size++] = '\n';
+ }
+ size += xdl_orig_copy(xe1, m->i0, m->chg0, 1,
+ dest ? dest + size : NULL);
+ }
+
+ if (!dest) {
+ size += marker_size + 1;
+ } else {
+ memset(dest + size, '=', marker_size);
+ size += marker_size;
+ dest[size++] = '\n';
+ }
+
+ /* Postimage from side #2 */
+ size += xdl_recs_copy(xe2, m->i2, m->chg2, 1,
+ dest ? dest + size : NULL);
+ if (!dest) {
+ size += marker_size + 1 + marker2_size;
+ } else {
+ memset(dest + size, '>', marker_size);
+ size += marker_size;
+ if (marker2_size) {
+ dest[size] = ' ';
+ memcpy(dest + size + 1, name2, marker2_size - 1);
+ size += marker2_size;
+ }
+ dest[size++] = '\n';
+ }
+ return size;
+}
+
+static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
+ xdfenv_t *xe2, const char *name2,
+ const char *ancestor_name,
+ int favor,
+ xdmerge_t *m, char *dest, int style,
+ int marker_size)
+{
+ int size, i;
+
+ for (size = i = 0; m; m = m->next) {
+ if (favor && !m->mode)
+ m->mode = favor;
+
+ if (m->mode == 0)
+ size = fill_conflict_hunk(xe1, name1, xe2, name2,
+ ancestor_name,
+ size, i, style, m, dest,
+ marker_size);
+ else if (m->mode & 3) {
+ /* Before conflicting part */
+ size += xdl_recs_copy(xe1, i, m->i1 - i, 0,
+ dest ? dest + size : NULL);
+ /* Postimage from side #1 */
+ if (m->mode & 1)
+ size += xdl_recs_copy(xe1, m->i1, m->chg1, 1,
+ dest ? dest + size : NULL);
+ /* Postimage from side #2 */
+ if (m->mode & 2)
+ size += xdl_recs_copy(xe2, m->i2, m->chg2, 1,
+ dest ? dest + size : NULL);
+ } else
+ continue;
+ i = m->i1 + m->chg1;
+ }
+ size += xdl_recs_copy(xe1, i, xe1->xdf2.nrec - i, 0,
+ dest ? dest + size : NULL);
+ return size;
+}
+
+/*
+ * Sometimes, changes are not quite identical, but differ in only a few
+ * lines. Try hard to show only these few lines as conflicting.
+ */
+static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,
+ xpparam_t const *xpp)
+{
+ for (; m; m = m->next) {
+ mmfile_t t1, t2;
+ xdfenv_t xe;
+ xdchange_t *xscr, *x;
+ int i1 = m->i1, i2 = m->i2;
+
+ /* let's handle just the conflicts */
+ if (m->mode)
+ continue;
+
+ /* no sense refining a conflict when one side is empty */
+ if (m->chg1 == 0 || m->chg2 == 0)
+ continue;
+
+ /*
+ * This probably does not work outside git, since
+ * we have a very simple mmfile structure.
+ */
+ t1.ptr = (char *)xe1->xdf2.recs[m->i1]->ptr;
+ t1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1]->ptr
+ + xe1->xdf2.recs[m->i1 + m->chg1 - 1]->size - t1.ptr;
+ t2.ptr = (char *)xe2->xdf2.recs[m->i2]->ptr;
+ t2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1]->ptr
+ + xe2->xdf2.recs[m->i2 + m->chg2 - 1]->size - t2.ptr;
+ if (xdl_do_diff(&t1, &t2, xpp, &xe) < 0)
+ return -1;
+ if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||
+ xdl_change_compact(&xe.xdf2, &xe.xdf1, xpp->flags) < 0 ||
+ xdl_build_script(&xe, &xscr) < 0) {
+ xdl_free_env(&xe);
+ return -1;
+ }
+ if (!xscr) {
+ /* If this happens, the changes are identical. */
+ xdl_free_env(&xe);
+ m->mode = 4;
+ continue;
+ }
+ x = xscr;
+ m->i1 = xscr->i1 + i1;
+ m->chg1 = xscr->chg1;
+ m->i2 = xscr->i2 + i2;
+ m->chg2 = xscr->chg2;
+ while (xscr->next) {
+ xdmerge_t *m2 = xdl_malloc(sizeof(xdmerge_t));
+ if (!m2) {
+ xdl_free_env(&xe);
+ xdl_free_script(x);
+ return -1;
+ }
+ xscr = xscr->next;
+ m2->next = m->next;
+ m->next = m2;
+ m = m2;
+ m->mode = 0;
+ m->i1 = xscr->i1 + i1;
+ m->chg1 = xscr->chg1;
+ m->i2 = xscr->i2 + i2;
+ m->chg2 = xscr->chg2;
+ }
+ xdl_free_env(&xe);
+ xdl_free_script(x);
+ }
+ return 0;
+}
+
+static int line_contains_alnum(const char *ptr, long size)
+{
+ while (size--)
+ if (isalnum((unsigned char)*(ptr++)))
+ return 1;
+ return 0;
+}
+
+static int lines_contain_alnum(xdfenv_t *xe, int i, int chg)
+{
+ for (; chg; chg--, i++)
+ if (line_contains_alnum(xe->xdf2.recs[i]->ptr,
+ xe->xdf2.recs[i]->size))
+ return 1;
+ return 0;
+}
+
+/*
+ * This function merges m and m->next, marking everything between those hunks
+ * as conflicting, too.
+ */
+static void xdl_merge_two_conflicts(xdmerge_t *m)
+{
+ xdmerge_t *next_m = m->next;
+ m->chg1 = next_m->i1 + next_m->chg1 - m->i1;
+ m->chg2 = next_m->i2 + next_m->chg2 - m->i2;
+ m->next = next_m->next;
+ free(next_m);
+}
+
+/*
+ * If there are less than 3 non-conflicting lines between conflicts,
+ * it appears simpler -- because it takes up less (or as many) lines --
+ * if the lines are moved into the conflicts.
+ */
+static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m,
+ int simplify_if_no_alnum)
+{
+ int result = 0;
+
+ if (!m)
+ return result;
+ for (;;) {
+ xdmerge_t *next_m = m->next;
+ int begin, end;
+
+ if (!next_m)
+ return result;
+
+ begin = m->i1 + m->chg1;
+ end = next_m->i1;
+
+ if (m->mode != 0 || next_m->mode != 0 ||
+ (end - begin > 3 &&
+ (!simplify_if_no_alnum ||
+ lines_contain_alnum(xe1, begin, end - begin)))) {
+ m = next_m;
+ } else {
+ result++;
+ xdl_merge_two_conflicts(m);
+ }
+ }
+}
+
+/*
+ * level == 0: mark all overlapping changes as conflict
+ * level == 1: mark overlapping changes as conflict only if not identical
+ * level == 2: analyze non-identical changes for minimal conflict set
+ * level == 3: analyze non-identical changes for minimal conflict set, but
+ * treat hunks not containing any letter or number as conflicting
+ *
+ * returns < 0 on error, == 0 for no conflicts, else number of conflicts
+ */
+static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,
+ xdfenv_t *xe2, xdchange_t *xscr2,
+ xmparam_t const *xmp, mmbuffer_t *result)
+{
+ xdmerge_t *changes, *c;
+ xpparam_t const *xpp = &xmp->xpp;
+ const char *const ancestor_name = xmp->ancestor;
+ const char *const name1 = xmp->file1;
+ const char *const name2 = xmp->file2;
+ int i0, i1, i2, chg0, chg1, chg2;
+ int level = xmp->level;
+ int style = xmp->style;
+ int favor = xmp->favor;
+
+ if (style == XDL_MERGE_DIFF3) {
+ /*
+ * "diff3 -m" output does not make sense for anything
+ * more aggressive than XDL_MERGE_EAGER.
+ */
+ if (XDL_MERGE_EAGER < level)
+ level = XDL_MERGE_EAGER;
+ }
+
+ c = changes = NULL;
+
+ while (xscr1 && xscr2) {
+ if (!changes)
+ changes = c;
+ if (xscr1->i1 + xscr1->chg1 < xscr2->i1) {
+ i0 = xscr1->i1;
+ i1 = xscr1->i2;
+ i2 = xscr2->i2 - xscr2->i1 + xscr1->i1;
+ chg0 = xscr1->chg1;
+ chg1 = xscr1->chg2;
+ chg2 = xscr1->chg1;
+ if (xdl_append_merge(&c, 1,
+ i0, chg0, i1, chg1, i2, chg2)) {
+ xdl_cleanup_merge(changes);
+ return -1;
+ }
+ xscr1 = xscr1->next;
+ continue;
+ }
+ if (xscr2->i1 + xscr2->chg1 < xscr1->i1) {
+ i0 = xscr2->i1;
+ i1 = xscr1->i2 - xscr1->i1 + xscr2->i1;
+ i2 = xscr2->i2;
+ chg0 = xscr2->chg1;
+ chg1 = xscr2->chg1;
+ chg2 = xscr2->chg2;
+ if (xdl_append_merge(&c, 2,
+ i0, chg0, i1, chg1, i2, chg2)) {
+ xdl_cleanup_merge(changes);
+ return -1;
+ }
+ xscr2 = xscr2->next;
+ continue;
+ }
+ if (level == XDL_MERGE_MINIMAL || xscr1->i1 != xscr2->i1 ||
+ xscr1->chg1 != xscr2->chg1 ||
+ xscr1->chg2 != xscr2->chg2 ||
+ xdl_merge_cmp_lines(xe1, xscr1->i2,
+ xe2, xscr2->i2,
+ xscr1->chg2, xpp->flags)) {
+ /* conflict */
+ int off = xscr1->i1 - xscr2->i1;
+ int ffo = off + xscr1->chg1 - xscr2->chg1;
+
+ i0 = xscr1->i1;
+ i1 = xscr1->i2;
+ i2 = xscr2->i2;
+ if (off > 0) {
+ i0 -= off;
+ i1 -= off;
+ }
+ else
+ i2 += off;
+ chg0 = xscr1->i1 + xscr1->chg1 - i0;
+ chg1 = xscr1->i2 + xscr1->chg2 - i1;
+ chg2 = xscr2->i2 + xscr2->chg2 - i2;
+ if (ffo < 0) {
+ chg0 -= ffo;
+ chg1 -= ffo;
+ } else
+ chg2 += ffo;
+ if (xdl_append_merge(&c, 0,
+ i0, chg0, i1, chg1, i2, chg2)) {
+ xdl_cleanup_merge(changes);
+ return -1;
+ }
+ }
+
+ i1 = xscr1->i1 + xscr1->chg1;
+ i2 = xscr2->i1 + xscr2->chg1;
+
+ if (i1 >= i2)
+ xscr2 = xscr2->next;
+ if (i2 >= i1)
+ xscr1 = xscr1->next;
+ }
+ while (xscr1) {
+ if (!changes)
+ changes = c;
+ i0 = xscr1->i1;
+ i1 = xscr1->i2;
+ i2 = xscr1->i1 + xe2->xdf2.nrec - xe2->xdf1.nrec;
+ chg0 = xscr1->chg1;
+ chg1 = xscr1->chg2;
+ chg2 = xscr1->chg1;
+ if (xdl_append_merge(&c, 1,
+ i0, chg0, i1, chg1, i2, chg2)) {
+ xdl_cleanup_merge(changes);
+ return -1;
+ }
+ xscr1 = xscr1->next;
+ }
+ while (xscr2) {
+ if (!changes)
+ changes = c;
+ i0 = xscr2->i1;
+ i1 = xscr2->i1 + xe1->xdf2.nrec - xe1->xdf1.nrec;
+ i2 = xscr2->i2;
+ chg0 = xscr2->chg1;
+ chg1 = xscr2->chg1;
+ chg2 = xscr2->chg2;
+ if (xdl_append_merge(&c, 2,
+ i0, chg0, i1, chg1, i2, chg2)) {
+ xdl_cleanup_merge(changes);
+ return -1;
+ }
+ xscr2 = xscr2->next;
+ }
+ if (!changes)
+ changes = c;
+ /* refine conflicts */
+ if (XDL_MERGE_ZEALOUS <= level &&
+ (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||
+ xdl_simplify_non_conflicts(xe1, changes,
+ XDL_MERGE_ZEALOUS < level) < 0)) {
+ xdl_cleanup_merge(changes);
+ return -1;
+ }
+ /* output */
+ if (result) {
+ int marker_size = xmp->marker_size;
+ int size = xdl_fill_merge_buffer(xe1, name1, xe2, name2,
+ ancestor_name,
+ favor, changes, NULL, style,
+ marker_size);
+ result->ptr = xdl_malloc(size);
+ if (!result->ptr) {
+ xdl_cleanup_merge(changes);
+ return -1;
+ }
+ result->size = size;
+ xdl_fill_merge_buffer(xe1, name1, xe2, name2,
+ ancestor_name, favor, changes,
+ result->ptr, style, marker_size);
+ }
+ return xdl_cleanup_merge(changes);
+}
+
+int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2,
+ xmparam_t const *xmp, mmbuffer_t *result)
+{
+ xdchange_t *xscr1, *xscr2;
+ xdfenv_t xe1, xe2;
+ int status;
+ xpparam_t const *xpp = &xmp->xpp;
+
+ result->ptr = NULL;
+ result->size = 0;
+
+ if (xdl_do_diff(orig, mf1, xpp, &xe1) < 0 ||
+ xdl_do_diff(orig, mf2, xpp, &xe2) < 0) {
+ return -1;
+ }
+ if (xdl_change_compact(&xe1.xdf1, &xe1.xdf2, xpp->flags) < 0 ||
+ xdl_change_compact(&xe1.xdf2, &xe1.xdf1, xpp->flags) < 0 ||
+ xdl_build_script(&xe1, &xscr1) < 0) {
+ xdl_free_env(&xe1);
+ return -1;
+ }
+ if (xdl_change_compact(&xe2.xdf1, &xe2.xdf2, xpp->flags) < 0 ||
+ xdl_change_compact(&xe2.xdf2, &xe2.xdf1, xpp->flags) < 0 ||
+ xdl_build_script(&xe2, &xscr2) < 0) {
+ xdl_free_env(&xe2);
+ return -1;
+ }
+ status = 0;
+ if (!xscr1) {
+ result->ptr = xdl_malloc(mf2->size);
+ memcpy(result->ptr, mf2->ptr, mf2->size);
+ result->size = mf2->size;
+ } else if (!xscr2) {
+ result->ptr = xdl_malloc(mf1->size);
+ memcpy(result->ptr, mf1->ptr, mf1->size);
+ result->size = mf1->size;
+ } else {
+ status = xdl_do_merge(&xe1, xscr1,
+ &xe2, xscr2,
+ xmp, result);
+ }
+ xdl_free_script(xscr1);
+ xdl_free_script(xscr2);
+
+ xdl_free_env(&xe1);
+ xdl_free_env(&xe2);
+
+ return status;
+}
diff --git a/src/xdiff/xpatience.c b/src/xdiff/xpatience.c
new file mode 100644
index 000000000..fdd7d0263
--- /dev/null
+++ b/src/xdiff/xpatience.c
@@ -0,0 +1,358 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003-2009 Davide Libenzi, Johannes E. Schindelin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+#include "xinclude.h"
+#include "xtypes.h"
+#include "xdiff.h"
+
+/*
+ * The basic idea of patience diff is to find lines that are unique in
+ * both files. These are intuitively the ones that we want to see as
+ * common lines.
+ *
+ * The maximal ordered sequence of such line pairs (where ordered means
+ * that the order in the sequence agrees with the order of the lines in
+ * both files) naturally defines an initial set of common lines.
+ *
+ * Now, the algorithm tries to extend the set of common lines by growing
+ * the line ranges where the files have identical lines.
+ *
+ * Between those common lines, the patience diff algorithm is applied
+ * recursively, until no unique line pairs can be found; these line ranges
+ * are handled by the well-known Myers algorithm.
+ */
+
+#define NON_UNIQUE ULONG_MAX
+
+/*
+ * This is a hash mapping from line hash to line numbers in the first and
+ * second file.
+ */
+struct hashmap {
+ int nr, alloc;
+ struct entry {
+ unsigned long hash;
+ /*
+ * 0 = unused entry, 1 = first line, 2 = second, etc.
+ * line2 is NON_UNIQUE if the line is not unique
+ * in either the first or the second file.
+ */
+ unsigned long line1, line2;
+ /*
+ * "next" & "previous" are used for the longest common
+ * sequence;
+ * initially, "next" reflects only the order in file1.
+ */
+ struct entry *next, *previous;
+ } *entries, *first, *last;
+ /* were common records found? */
+ unsigned long has_matches;
+ mmfile_t *file1, *file2;
+ xdfenv_t *env;
+ xpparam_t const *xpp;
+};
+
+/* The argument "pass" is 1 for the first file, 2 for the second. */
+static void insert_record(int line, struct hashmap *map, int pass)
+{
+ xrecord_t **records = pass == 1 ?
+ map->env->xdf1.recs : map->env->xdf2.recs;
+ xrecord_t *record = records[line - 1], *other;
+ /*
+ * After xdl_prepare_env() (or more precisely, due to
+ * xdl_classify_record()), the "ha" member of the records (AKA lines)
+ * is _not_ the hash anymore, but a linearized version of it. In
+ * other words, the "ha" member is guaranteed to start with 0 and
+ * the second record's ha can only be 0 or 1, etc.
+ *
+ * So we multiply ha by 2 in the hope that the hashing was
+ * "unique enough".
+ */
+ int index = (int)((record->ha << 1) % map->alloc);
+
+ while (map->entries[index].line1) {
+ other = map->env->xdf1.recs[map->entries[index].line1 - 1];
+ if (map->entries[index].hash != record->ha ||
+ !xdl_recmatch(record->ptr, record->size,
+ other->ptr, other->size,
+ map->xpp->flags)) {
+ if (++index >= map->alloc)
+ index = 0;
+ continue;
+ }
+ if (pass == 2)
+ map->has_matches = 1;
+ if (pass == 1 || map->entries[index].line2)
+ map->entries[index].line2 = NON_UNIQUE;
+ else
+ map->entries[index].line2 = line;
+ return;
+ }
+ if (pass == 2)
+ return;
+ map->entries[index].line1 = line;
+ map->entries[index].hash = record->ha;
+ if (!map->first)
+ map->first = map->entries + index;
+ if (map->last) {
+ map->last->next = map->entries + index;
+ map->entries[index].previous = map->last;
+ }
+ map->last = map->entries + index;
+ map->nr++;
+}
+
+/*
+ * This function has to be called for each recursion into the inter-hunk
+ * parts, as previously non-unique lines can become unique when being
+ * restricted to a smaller part of the files.
+ *
+ * It is assumed that env has been prepared using xdl_prepare().
+ */
+static int fill_hashmap(mmfile_t *file1, mmfile_t *file2,
+ xpparam_t const *xpp, xdfenv_t *env,
+ struct hashmap *result,
+ int line1, int count1, int line2, int count2)
+{
+ result->file1 = file1;
+ result->file2 = file2;
+ result->xpp = xpp;
+ result->env = env;
+
+ /* We know exactly how large we want the hash map */
+ result->alloc = count1 * 2;
+ result->entries = (struct entry *)
+ xdl_malloc(result->alloc * sizeof(struct entry));
+ if (!result->entries)
+ return -1;
+ memset(result->entries, 0, result->alloc * sizeof(struct entry));
+
+ /* First, fill with entries from the first file */
+ while (count1--)
+ insert_record(line1++, result, 1);
+
+ /* Then search for matches in the second file */
+ while (count2--)
+ insert_record(line2++, result, 2);
+
+ return 0;
+}
+
+/*
+ * Find the longest sequence with a smaller last element (meaning a smaller
+ * line2, as we construct the sequence with entries ordered by line1).
+ */
+static int binary_search(struct entry **sequence, int longest,
+ struct entry *entry)
+{
+ int left = -1, right = longest;
+
+ while (left + 1 < right) {
+ int middle = (left + right) / 2;
+ /* by construction, no two entries can be equal */
+ if (sequence[middle]->line2 > entry->line2)
+ right = middle;
+ else
+ left = middle;
+ }
+ /* return the index in "sequence", _not_ the sequence length */
+ return left;
+}
+
+/*
+ * The idea is to start with the list of common unique lines sorted by
+ * the order in file1. For each of these pairs, the longest (partial)
+ * sequence whose last element's line2 is smaller is determined.
+ *
+ * For efficiency, the sequences are kept in a list containing exactly one
+ * item per sequence length: the sequence with the smallest last
+ * element (in terms of line2).
+ */
+static struct entry *find_longest_common_sequence(struct hashmap *map)
+{
+ struct entry **sequence = xdl_malloc(map->nr * sizeof(struct entry *));
+ int longest = 0, i;
+ struct entry *entry;
+
+ for (entry = map->first; entry; entry = entry->next) {
+ if (!entry->line2 || entry->line2 == NON_UNIQUE)
+ continue;
+ i = binary_search(sequence, longest, entry);
+ entry->previous = i < 0 ? NULL : sequence[i];
+ sequence[++i] = entry;
+ if (i == longest)
+ longest++;
+ }
+
+ /* No common unique lines were found */
+ if (!longest) {
+ xdl_free(sequence);
+ return NULL;
+ }
+
+ /* Iterate starting at the last element, adjusting the "next" members */
+ entry = sequence[longest - 1];
+ entry->next = NULL;
+ while (entry->previous) {
+ entry->previous->next = entry;
+ entry = entry->previous;
+ }
+ xdl_free(sequence);
+ return entry;
+}
+
+static int match(struct hashmap *map, int line1, int line2)
+{
+ xrecord_t *record1 = map->env->xdf1.recs[line1 - 1];
+ xrecord_t *record2 = map->env->xdf2.recs[line2 - 1];
+ return xdl_recmatch(record1->ptr, record1->size,
+ record2->ptr, record2->size, map->xpp->flags);
+}
+
+static int patience_diff(mmfile_t *file1, mmfile_t *file2,
+ xpparam_t const *xpp, xdfenv_t *env,
+ int line1, int count1, int line2, int count2);
+
+static int walk_common_sequence(struct hashmap *map, struct entry *first,
+ int line1, int count1, int line2, int count2)
+{
+ int end1 = line1 + count1, end2 = line2 + count2;
+ int next1, next2;
+
+ for (;;) {
+ /* Try to grow the line ranges of common lines */
+ if (first) {
+ next1 = first->line1;
+ next2 = first->line2;
+ while (next1 > line1 && next2 > line2 &&
+ match(map, next1 - 1, next2 - 1)) {
+ next1--;
+ next2--;
+ }
+ } else {
+ next1 = end1;
+ next2 = end2;
+ }
+ while (line1 < next1 && line2 < next2 &&
+ match(map, line1, line2)) {
+ line1++;
+ line2++;
+ }
+
+ /* Recurse */
+ if (next1 > line1 || next2 > line2) {
+ struct hashmap submap;
+
+ memset(&submap, 0, sizeof(submap));
+ if (patience_diff(map->file1, map->file2,
+ map->xpp, map->env,
+ line1, next1 - line1,
+ line2, next2 - line2))
+ return -1;
+ }
+
+ if (!first)
+ return 0;
+
+ while (first->next &&
+ first->next->line1 == first->line1 + 1 &&
+ first->next->line2 == first->line2 + 1)
+ first = first->next;
+
+ line1 = first->line1 + 1;
+ line2 = first->line2 + 1;
+
+ first = first->next;
+ }
+}
+
+static int fall_back_to_classic_diff(struct hashmap *map,
+ int line1, int count1, int line2, int count2)
+{
+ xpparam_t xpp;
+ xpp.flags = map->xpp->flags & ~XDF_PATIENCE_DIFF;
+
+ return xdl_fall_back_diff(map->env, &xpp,
+ line1, count1, line2, count2);
+}
+
+/*
+ * Recursively find the longest common sequence of unique lines,
+ * and if none was found, ask xdl_do_diff() to do the job.
+ *
+ * This function assumes that env was prepared with xdl_prepare_env().
+ */
+static int patience_diff(mmfile_t *file1, mmfile_t *file2,
+ xpparam_t const *xpp, xdfenv_t *env,
+ int line1, int count1, int line2, int count2)
+{
+ struct hashmap map;
+ struct entry *first;
+ int result = 0;
+
+ /* trivial case: one side is empty */
+ if (!count1) {
+ while(count2--)
+ env->xdf2.rchg[line2++ - 1] = 1;
+ return 0;
+ } else if (!count2) {
+ while(count1--)
+ env->xdf1.rchg[line1++ - 1] = 1;
+ return 0;
+ }
+
+ memset(&map, 0, sizeof(map));
+ if (fill_hashmap(file1, file2, xpp, env, &map,
+ line1, count1, line2, count2))
+ return -1;
+
+ /* are there any matching lines at all? */
+ if (!map.has_matches) {
+ while(count1--)
+ env->xdf1.rchg[line1++ - 1] = 1;
+ while(count2--)
+ env->xdf2.rchg[line2++ - 1] = 1;
+ xdl_free(map.entries);
+ return 0;
+ }
+
+ first = find_longest_common_sequence(&map);
+ if (first)
+ result = walk_common_sequence(&map, first,
+ line1, count1, line2, count2);
+ else
+ result = fall_back_to_classic_diff(&map,
+ line1, count1, line2, count2);
+
+ xdl_free(map.entries);
+ return result;
+}
+
+int xdl_do_patience_diff(mmfile_t *file1, mmfile_t *file2,
+ xpparam_t const *xpp, xdfenv_t *env)
+{
+ if (xdl_prepare_env(file1, file2, xpp, env) < 0)
+ return -1;
+
+ /* environment is cleaned up in xdl_diff() */
+ return patience_diff(file1, file2, xpp, env,
+ 1, env->xdf1.nrec, 1, env->xdf2.nrec);
+}
diff --git a/src/xdiff/xprepare.c b/src/xdiff/xprepare.c
new file mode 100644
index 000000000..e419f4f72
--- /dev/null
+++ b/src/xdiff/xprepare.c
@@ -0,0 +1,483 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#include "xinclude.h"
+
+
+#define XDL_KPDIS_RUN 4
+#define XDL_MAX_EQLIMIT 1024
+#define XDL_SIMSCAN_WINDOW 100
+#define XDL_GUESS_NLINES1 256
+#define XDL_GUESS_NLINES2 20
+
+
+typedef struct s_xdlclass {
+ struct s_xdlclass *next;
+ unsigned long ha;
+ char const *line;
+ long size;
+ long idx;
+ long len1, len2;
+} xdlclass_t;
+
+typedef struct s_xdlclassifier {
+ unsigned int hbits;
+ long hsize;
+ xdlclass_t **rchash;
+ chastore_t ncha;
+ xdlclass_t **rcrecs;
+ long alloc;
+ long count;
+ long flags;
+} xdlclassifier_t;
+
+
+
+
+static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags);
+static void xdl_free_classifier(xdlclassifier_t *cf);
+static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,
+ unsigned int hbits, xrecord_t *rec);
+static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,
+ xdlclassifier_t *cf, xdfile_t *xdf);
+static void xdl_free_ctx(xdfile_t *xdf);
+static int xdl_clean_mmatch(char const *dis, long i, long s, long e);
+static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);
+static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2);
+static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);
+
+
+
+
+static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags) {
+ cf->flags = flags;
+
+ cf->hbits = xdl_hashbits((unsigned int) size);
+ cf->hsize = 1 << cf->hbits;
+
+ if (xdl_cha_init(&cf->ncha, sizeof(xdlclass_t), size / 4 + 1) < 0) {
+
+ return -1;
+ }
+ if (!(cf->rchash = (xdlclass_t **) xdl_malloc(cf->hsize * sizeof(xdlclass_t *)))) {
+
+ xdl_cha_free(&cf->ncha);
+ return -1;
+ }
+ memset(cf->rchash, 0, cf->hsize * sizeof(xdlclass_t *));
+
+ cf->alloc = size;
+ if (!(cf->rcrecs = (xdlclass_t **) xdl_malloc(cf->alloc * sizeof(xdlclass_t *)))) {
+
+ xdl_free(cf->rchash);
+ xdl_cha_free(&cf->ncha);
+ return -1;
+ }
+
+ cf->count = 0;
+
+ return 0;
+}
+
+
+static void xdl_free_classifier(xdlclassifier_t *cf) {
+
+ xdl_free(cf->rcrecs);
+ xdl_free(cf->rchash);
+ xdl_cha_free(&cf->ncha);
+}
+
+
+static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,
+ unsigned int hbits, xrecord_t *rec) {
+ long hi;
+ char const *line;
+ xdlclass_t *rcrec;
+ xdlclass_t **rcrecs;
+
+ line = rec->ptr;
+ hi = (long) XDL_HASHLONG(rec->ha, cf->hbits);
+ for (rcrec = cf->rchash[hi]; rcrec; rcrec = rcrec->next)
+ if (rcrec->ha == rec->ha &&
+ xdl_recmatch(rcrec->line, rcrec->size,
+ rec->ptr, rec->size, cf->flags))
+ break;
+
+ if (!rcrec) {
+ if (!(rcrec = xdl_cha_alloc(&cf->ncha))) {
+
+ return -1;
+ }
+ rcrec->idx = cf->count++;
+ if (cf->count > cf->alloc) {
+ cf->alloc *= 2;
+ if (!(rcrecs = (xdlclass_t **) xdl_realloc(cf->rcrecs, cf->alloc * sizeof(xdlclass_t *)))) {
+
+ return -1;
+ }
+ cf->rcrecs = rcrecs;
+ }
+ cf->rcrecs[rcrec->idx] = rcrec;
+ rcrec->line = line;
+ rcrec->size = rec->size;
+ rcrec->ha = rec->ha;
+ rcrec->len1 = rcrec->len2 = 0;
+ rcrec->next = cf->rchash[hi];
+ cf->rchash[hi] = rcrec;
+ }
+
+ (pass == 1) ? rcrec->len1++ : rcrec->len2++;
+
+ rec->ha = (unsigned long) rcrec->idx;
+
+ hi = (long) XDL_HASHLONG(rec->ha, hbits);
+ rec->next = rhash[hi];
+ rhash[hi] = rec;
+
+ return 0;
+}
+
+
+static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,
+ xdlclassifier_t *cf, xdfile_t *xdf) {
+ unsigned int hbits;
+ long nrec, hsize, bsize;
+ unsigned long hav;
+ char const *blk, *cur, *top, *prev;
+ xrecord_t *crec;
+ xrecord_t **recs, **rrecs;
+ xrecord_t **rhash;
+ unsigned long *ha;
+ char *rchg;
+ long *rindex;
+
+ ha = NULL;
+ rindex = NULL;
+ rchg = NULL;
+ rhash = NULL;
+ recs = NULL;
+
+ if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)
+ goto abort;
+ if (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *))))
+ goto abort;
+
+ if (xpp->flags & XDF_HISTOGRAM_DIFF)
+ hbits = hsize = 0;
+ else {
+ hbits = xdl_hashbits((unsigned int) narec);
+ hsize = 1 << hbits;
+ if (!(rhash = (xrecord_t **) xdl_malloc(hsize * sizeof(xrecord_t *))))
+ goto abort;
+ memset(rhash, 0, hsize * sizeof(xrecord_t *));
+ }
+
+ nrec = 0;
+ if ((cur = blk = xdl_mmfile_first(mf, &bsize)) != NULL) {
+ for (top = blk + bsize; cur < top; ) {
+ prev = cur;
+ hav = xdl_hash_record(&cur, top, xpp->flags);
+ if (nrec >= narec) {
+ narec *= 2;
+ if (!(rrecs = (xrecord_t **) xdl_realloc(recs, narec * sizeof(xrecord_t *))))
+ goto abort;
+ recs = rrecs;
+ }
+ if (!(crec = xdl_cha_alloc(&xdf->rcha)))
+ goto abort;
+ crec->ptr = prev;
+ crec->size = (long) (cur - prev);
+ crec->ha = hav;
+ recs[nrec++] = crec;
+
+ if (!(xpp->flags & XDF_HISTOGRAM_DIFF) &&
+ xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)
+ goto abort;
+ }
+ }
+
+ if (!(rchg = (char *) xdl_malloc((nrec + 2) * sizeof(char))))
+ goto abort;
+ memset(rchg, 0, (nrec + 2) * sizeof(char));
+
+ if (!(rindex = (long *) xdl_malloc((nrec + 1) * sizeof(long))))
+ goto abort;
+ if (!(ha = (unsigned long *) xdl_malloc((nrec + 1) * sizeof(unsigned long))))
+ goto abort;
+
+ xdf->nrec = nrec;
+ xdf->recs = recs;
+ xdf->hbits = hbits;
+ xdf->rhash = rhash;
+ xdf->rchg = rchg + 1;
+ xdf->rindex = rindex;
+ xdf->nreff = 0;
+ xdf->ha = ha;
+ xdf->dstart = 0;
+ xdf->dend = nrec - 1;
+
+ return 0;
+
+abort:
+ xdl_free(ha);
+ xdl_free(rindex);
+ xdl_free(rchg);
+ xdl_free(rhash);
+ xdl_free(recs);
+ xdl_cha_free(&xdf->rcha);
+ return -1;
+}
+
+
+static void xdl_free_ctx(xdfile_t *xdf) {
+
+ xdl_free(xdf->rhash);
+ xdl_free(xdf->rindex);
+ xdl_free(xdf->rchg - 1);
+ xdl_free(xdf->ha);
+ xdl_free(xdf->recs);
+ xdl_cha_free(&xdf->rcha);
+}
+
+
+int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+ xdfenv_t *xe) {
+ long enl1, enl2, sample;
+ xdlclassifier_t cf;
+
+ memset(&cf, 0, sizeof(cf));
+
+ /*
+ * For histogram diff, we can afford a smaller sample size and
+ * thus a poorer estimate of the number of lines, as the hash
+ * table (rhash) won't be filled up/grown. The number of lines
+ * (nrecs) will be updated correctly anyway by
+ * xdl_prepare_ctx().
+ */
+ sample = xpp->flags & XDF_HISTOGRAM_DIFF ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1;
+
+ enl1 = xdl_guess_lines(mf1, sample) + 1;
+ enl2 = xdl_guess_lines(mf2, sample) + 1;
+
+ if (!(xpp->flags & XDF_HISTOGRAM_DIFF) &&
+ xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0) {
+
+ return -1;
+ }
+
+ if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {
+
+ xdl_free_classifier(&cf);
+ return -1;
+ }
+ if (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {
+
+ xdl_free_ctx(&xe->xdf1);
+ xdl_free_classifier(&cf);
+ return -1;
+ }
+
+ if (!(xpp->flags & XDF_PATIENCE_DIFF) &&
+ !(xpp->flags & XDF_HISTOGRAM_DIFF) &&
+ xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {
+
+ xdl_free_ctx(&xe->xdf2);
+ xdl_free_ctx(&xe->xdf1);
+ return -1;
+ }
+
+ if (!(xpp->flags & XDF_HISTOGRAM_DIFF))
+ xdl_free_classifier(&cf);
+
+ return 0;
+}
+
+
+void xdl_free_env(xdfenv_t *xe) {
+
+ xdl_free_ctx(&xe->xdf2);
+ xdl_free_ctx(&xe->xdf1);
+}
+
+
+static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {
+ long r, rdis0, rpdis0, rdis1, rpdis1;
+
+ /*
+ * Limits the window the is examined during the similar-lines
+ * scan. The loops below stops when dis[i - r] == 1 (line that
+ * has no match), but there are corner cases where the loop
+ * proceed all the way to the extremities by causing huge
+ * performance penalties in case of big files.
+ */
+ if (i - s > XDL_SIMSCAN_WINDOW)
+ s = i - XDL_SIMSCAN_WINDOW;
+ if (e - i > XDL_SIMSCAN_WINDOW)
+ e = i + XDL_SIMSCAN_WINDOW;
+
+ /*
+ * Scans the lines before 'i' to find a run of lines that either
+ * have no match (dis[j] == 0) or have multiple matches (dis[j] > 1).
+ * Note that we always call this function with dis[i] > 1, so the
+ * current line (i) is already a multimatch line.
+ */
+ for (r = 1, rdis0 = 0, rpdis0 = 1; (i - r) >= s; r++) {
+ if (!dis[i - r])
+ rdis0++;
+ else if (dis[i - r] == 2)
+ rpdis0++;
+ else
+ break;
+ }
+ /*
+ * If the run before the line 'i' found only multimatch lines, we
+ * return 0 and hence we don't make the current line (i) discarded.
+ * We want to discard multimatch lines only when they appear in the
+ * middle of runs with nomatch lines (dis[j] == 0).
+ */
+ if (rdis0 == 0)
+ return 0;
+ for (r = 1, rdis1 = 0, rpdis1 = 1; (i + r) <= e; r++) {
+ if (!dis[i + r])
+ rdis1++;
+ else if (dis[i + r] == 2)
+ rpdis1++;
+ else
+ break;
+ }
+ /*
+ * If the run after the line 'i' found only multimatch lines, we
+ * return 0 and hence we don't make the current line (i) discarded.
+ */
+ if (rdis1 == 0)
+ return 0;
+ rdis1 += rdis0;
+ rpdis1 += rpdis0;
+
+ return rpdis1 * XDL_KPDIS_RUN < (rpdis1 + rdis1);
+}
+
+
+/*
+ * Try to reduce the problem complexity, discard records that have no
+ * matches on the other file. Also, lines that have multiple matches
+ * might be potentially discarded if they happear in a run of discardable.
+ */
+static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {
+ long i, nm, nreff, mlim;
+ xrecord_t **recs;
+ xdlclass_t *rcrec;
+ char *dis, *dis1, *dis2;
+
+ if (!(dis = (char *) xdl_malloc(xdf1->nrec + xdf2->nrec + 2))) {
+
+ return -1;
+ }
+ memset(dis, 0, xdf1->nrec + xdf2->nrec + 2);
+ dis1 = dis;
+ dis2 = dis1 + xdf1->nrec + 1;
+
+ if ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)
+ mlim = XDL_MAX_EQLIMIT;
+ for (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {
+ rcrec = cf->rcrecs[(*recs)->ha];
+ nm = rcrec ? rcrec->len2 : 0;
+ dis1[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1;
+ }
+
+ if ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)
+ mlim = XDL_MAX_EQLIMIT;
+ for (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {
+ rcrec = cf->rcrecs[(*recs)->ha];
+ nm = rcrec ? rcrec->len1 : 0;
+ dis2[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1;
+ }
+
+ for (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];
+ i <= xdf1->dend; i++, recs++) {
+ if (dis1[i] == 1 ||
+ (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {
+ xdf1->rindex[nreff] = i;
+ xdf1->ha[nreff] = (*recs)->ha;
+ nreff++;
+ } else
+ xdf1->rchg[i] = 1;
+ }
+ xdf1->nreff = nreff;
+
+ for (nreff = 0, i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart];
+ i <= xdf2->dend; i++, recs++) {
+ if (dis2[i] == 1 ||
+ (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {
+ xdf2->rindex[nreff] = i;
+ xdf2->ha[nreff] = (*recs)->ha;
+ nreff++;
+ } else
+ xdf2->rchg[i] = 1;
+ }
+ xdf2->nreff = nreff;
+
+ xdl_free(dis);
+
+ return 0;
+}
+
+
+/*
+ * Early trim initial and terminal matching records.
+ */
+static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {
+ long i, lim;
+ xrecord_t **recs1, **recs2;
+
+ recs1 = xdf1->recs;
+ recs2 = xdf2->recs;
+ for (i = 0, lim = XDL_MIN(xdf1->nrec, xdf2->nrec); i < lim;
+ i++, recs1++, recs2++)
+ if ((*recs1)->ha != (*recs2)->ha)
+ break;
+
+ xdf1->dstart = xdf2->dstart = i;
+
+ recs1 = xdf1->recs + xdf1->nrec - 1;
+ recs2 = xdf2->recs + xdf2->nrec - 1;
+ for (lim -= i, i = 0; i < lim; i++, recs1--, recs2--)
+ if ((*recs1)->ha != (*recs2)->ha)
+ break;
+
+ xdf1->dend = xdf1->nrec - i - 1;
+ xdf2->dend = xdf2->nrec - i - 1;
+
+ return 0;
+}
+
+
+static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {
+
+ if (xdl_trim_ends(xdf1, xdf2) < 0 ||
+ xdl_cleanup_records(cf, xdf1, xdf2) < 0) {
+
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/xdiff/xprepare.h b/src/xdiff/xprepare.h
new file mode 100644
index 000000000..8fb06a537
--- /dev/null
+++ b/src/xdiff/xprepare.h
@@ -0,0 +1,34 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XPREPARE_H)
+#define XPREPARE_H
+
+
+
+int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+ xdfenv_t *xe);
+void xdl_free_env(xdfenv_t *xe);
+
+
+
+#endif /* #if !defined(XPREPARE_H) */
diff --git a/src/xdiff/xtypes.h b/src/xdiff/xtypes.h
new file mode 100644
index 000000000..2511aef8d
--- /dev/null
+++ b/src/xdiff/xtypes.h
@@ -0,0 +1,67 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XTYPES_H)
+#define XTYPES_H
+
+
+
+typedef struct s_chanode {
+ struct s_chanode *next;
+ long icurr;
+} chanode_t;
+
+typedef struct s_chastore {
+ chanode_t *head, *tail;
+ long isize, nsize;
+ chanode_t *ancur;
+ chanode_t *sncur;
+ long scurr;
+} chastore_t;
+
+typedef struct s_xrecord {
+ struct s_xrecord *next;
+ char const *ptr;
+ long size;
+ unsigned long ha;
+} xrecord_t;
+
+typedef struct s_xdfile {
+ chastore_t rcha;
+ long nrec;
+ unsigned int hbits;
+ xrecord_t **rhash;
+ long dstart, dend;
+ xrecord_t **recs;
+ char *rchg;
+ long *rindex;
+ long nreff;
+ unsigned long *ha;
+} xdfile_t;
+
+typedef struct s_xdfenv {
+ xdfile_t xdf1, xdf2;
+} xdfenv_t;
+
+
+
+#endif /* #if !defined(XTYPES_H) */
diff --git a/src/xdiff/xutils.c b/src/xdiff/xutils.c
new file mode 100644
index 000000000..9dea04bba
--- /dev/null
+++ b/src/xdiff/xutils.c
@@ -0,0 +1,419 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#include "xinclude.h"
+
+
+
+
+long xdl_bogosqrt(long n) {
+ long i;
+
+ /*
+ * Classical integer square root approximation using shifts.
+ */
+ for (i = 1; n > 0; n >>= 2)
+ i <<= 1;
+
+ return i;
+}
+
+
+int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,
+ xdemitcb_t *ecb) {
+ int i = 2;
+ mmbuffer_t mb[3];
+
+ mb[0].ptr = (char *) pre;
+ mb[0].size = psize;
+ mb[1].ptr = (char *) rec;
+ mb[1].size = size;
+ if (size > 0 && rec[size - 1] != '\n') {
+ mb[2].ptr = (char *) "\n\\ No newline at end of file\n";
+ mb[2].size = strlen(mb[2].ptr);
+ i++;
+ }
+ if (ecb->outf(ecb->priv, mb, i) < 0) {
+
+ return -1;
+ }
+
+ return 0;
+}
+
+void *xdl_mmfile_first(mmfile_t *mmf, long *size)
+{
+ *size = mmf->size;
+ return mmf->ptr;
+}
+
+
+long xdl_mmfile_size(mmfile_t *mmf)
+{
+ return mmf->size;
+}
+
+
+int xdl_cha_init(chastore_t *cha, long isize, long icount) {
+
+ cha->head = cha->tail = NULL;
+ cha->isize = isize;
+ cha->nsize = icount * isize;
+ cha->ancur = cha->sncur = NULL;
+ cha->scurr = 0;
+
+ return 0;
+}
+
+
+void xdl_cha_free(chastore_t *cha) {
+ chanode_t *cur, *tmp;
+
+ for (cur = cha->head; (tmp = cur) != NULL;) {
+ cur = cur->next;
+ xdl_free(tmp);
+ }
+}
+
+
+void *xdl_cha_alloc(chastore_t *cha) {
+ chanode_t *ancur;
+ void *data;
+
+ if (!(ancur = cha->ancur) || ancur->icurr == cha->nsize) {
+ if (!(ancur = (chanode_t *) xdl_malloc(sizeof(chanode_t) + cha->nsize))) {
+
+ return NULL;
+ }
+ ancur->icurr = 0;
+ ancur->next = NULL;
+ if (cha->tail)
+ cha->tail->next = ancur;
+ if (!cha->head)
+ cha->head = ancur;
+ cha->tail = ancur;
+ cha->ancur = ancur;
+ }
+
+ data = (char *) ancur + sizeof(chanode_t) + ancur->icurr;
+ ancur->icurr += cha->isize;
+
+ return data;
+}
+
+
+void *xdl_cha_first(chastore_t *cha) {
+ chanode_t *sncur;
+
+ if (!(cha->sncur = sncur = cha->head))
+ return NULL;
+
+ cha->scurr = 0;
+
+ return (char *) sncur + sizeof(chanode_t) + cha->scurr;
+}
+
+
+void *xdl_cha_next(chastore_t *cha) {
+ chanode_t *sncur;
+
+ if (!(sncur = cha->sncur))
+ return NULL;
+ cha->scurr += cha->isize;
+ if (cha->scurr == sncur->icurr) {
+ if (!(sncur = cha->sncur = sncur->next))
+ return NULL;
+ cha->scurr = 0;
+ }
+
+ return (char *) sncur + sizeof(chanode_t) + cha->scurr;
+}
+
+
+long xdl_guess_lines(mmfile_t *mf, long sample) {
+ long nl = 0, size, tsize = 0;
+ char const *data, *cur, *top;
+
+ if ((cur = data = xdl_mmfile_first(mf, &size)) != NULL) {
+ for (top = data + size; nl < sample && cur < top; ) {
+ nl++;
+ if (!(cur = memchr(cur, '\n', top - cur)))
+ cur = top;
+ else
+ cur++;
+ }
+ tsize += (long) (cur - data);
+ }
+
+ if (nl && tsize)
+ nl = xdl_mmfile_size(mf) / (tsize / nl);
+
+ return nl + 1;
+}
+
+int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
+{
+ int i1, i2;
+
+ if (s1 == s2 && !memcmp(l1, l2, s1))
+ return 1;
+ if (!(flags & XDF_WHITESPACE_FLAGS))
+ return 0;
+
+ i1 = 0;
+ i2 = 0;
+
+ /*
+ * -w matches everything that matches with -b, and -b in turn
+ * matches everything that matches with --ignore-space-at-eol.
+ *
+ * Each flavor of ignoring needs different logic to skip whitespaces
+ * while we have both sides to compare.
+ */
+ if (flags & XDF_IGNORE_WHITESPACE) {
+ goto skip_ws;
+ while (i1 < s1 && i2 < s2) {
+ if (l1[i1++] != l2[i2++])
+ return 0;
+ skip_ws:
+ while (i1 < s1 && XDL_ISSPACE(l1[i1]))
+ i1++;
+ while (i2 < s2 && XDL_ISSPACE(l2[i2]))
+ i2++;
+ }
+ } else if (flags & XDF_IGNORE_WHITESPACE_CHANGE) {
+ while (i1 < s1 && i2 < s2) {
+ if (XDL_ISSPACE(l1[i1]) && XDL_ISSPACE(l2[i2])) {
+ /* Skip matching spaces and try again */
+ while (i1 < s1 && XDL_ISSPACE(l1[i1]))
+ i1++;
+ while (i2 < s2 && XDL_ISSPACE(l2[i2]))
+ i2++;
+ continue;
+ }
+ if (l1[i1++] != l2[i2++])
+ return 0;
+ }
+ } else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL) {
+ while (i1 < s1 && i2 < s2 && l1[i1++] == l2[i2++])
+ ; /* keep going */
+ }
+
+ /*
+ * After running out of one side, the remaining side must have
+ * nothing but whitespace for the lines to match. Note that
+ * ignore-whitespace-at-eol case may break out of the loop
+ * while there still are characters remaining on both lines.
+ */
+ if (i1 < s1) {
+ while (i1 < s1 && XDL_ISSPACE(l1[i1]))
+ i1++;
+ if (s1 != i1)
+ return 0;
+ }
+ if (i2 < s2) {
+ while (i2 < s2 && XDL_ISSPACE(l2[i2]))
+ i2++;
+ return (s2 == i2);
+ }
+ return 1;
+}
+
+static unsigned long xdl_hash_record_with_whitespace(char const **data,
+ char const *top, long flags) {
+ unsigned long ha = 5381;
+ char const *ptr = *data;
+
+ for (; ptr < top && *ptr != '\n'; ptr++) {
+ if (XDL_ISSPACE(*ptr)) {
+ const char *ptr2 = ptr;
+ int at_eol;
+ while (ptr + 1 < top && XDL_ISSPACE(ptr[1])
+ && ptr[1] != '\n')
+ ptr++;
+ at_eol = (top <= ptr + 1 || ptr[1] == '\n');
+ if (flags & XDF_IGNORE_WHITESPACE)
+ ; /* already handled */
+ else if (flags & XDF_IGNORE_WHITESPACE_CHANGE
+ && !at_eol) {
+ ha += (ha << 5);
+ ha ^= (unsigned long) ' ';
+ }
+ else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL
+ && !at_eol) {
+ while (ptr2 != ptr + 1) {
+ ha += (ha << 5);
+ ha ^= (unsigned long) *ptr2;
+ ptr2++;
+ }
+ }
+ continue;
+ }
+ ha += (ha << 5);
+ ha ^= (unsigned long) *ptr;
+ }
+ *data = ptr < top ? ptr + 1: ptr;
+
+ return ha;
+}
+
+
+unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
+ unsigned long ha = 5381;
+ char const *ptr = *data;
+
+ if (flags & XDF_WHITESPACE_FLAGS)
+ return xdl_hash_record_with_whitespace(data, top, flags);
+
+ for (; ptr < top && *ptr != '\n'; ptr++) {
+ ha += (ha << 5);
+ ha ^= (unsigned long) *ptr;
+ }
+ *data = ptr < top ? ptr + 1: ptr;
+
+ return ha;
+}
+
+
+unsigned int xdl_hashbits(unsigned int size) {
+ unsigned int val = 1, bits = 0;
+
+ for (; val < size && bits < CHAR_BIT * sizeof(unsigned int); val <<= 1, bits++);
+ return bits ? bits: 1;
+}
+
+
+int xdl_num_out(char *out, long val) {
+ char *ptr, *str = out;
+ char buf[32];
+
+ ptr = buf + sizeof(buf) - 1;
+ *ptr = '\0';
+ if (val < 0) {
+ *--ptr = '-';
+ val = -val;
+ }
+ for (; val && ptr > buf; val /= 10)
+ *--ptr = "0123456789"[val % 10];
+ if (*ptr)
+ for (; *ptr; ptr++, str++)
+ *str = *ptr;
+ else
+ *str++ = '0';
+ *str = '\0';
+
+ return str - out;
+}
+
+
+long xdl_atol(char const *str, char const **next) {
+ long val, base;
+ char const *top;
+
+ for (top = str; XDL_ISDIGIT(*top); top++);
+ if (next)
+ *next = top;
+ for (val = 0, base = 1, top--; top >= str; top--, base *= 10)
+ val += base * (long)(*top - '0');
+ return val;
+}
+
+
+int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
+ const char *func, long funclen, xdemitcb_t *ecb) {
+ int nb = 0;
+ mmbuffer_t mb;
+ char buf[128];
+
+ memcpy(buf, "@@ -", 4);
+ nb += 4;
+
+ nb += xdl_num_out(buf + nb, c1 ? s1: s1 - 1);
+
+ if (c1 != 1) {
+ memcpy(buf + nb, ",", 1);
+ nb += 1;
+
+ nb += xdl_num_out(buf + nb, c1);
+ }
+
+ memcpy(buf + nb, " +", 2);
+ nb += 2;
+
+ nb += xdl_num_out(buf + nb, c2 ? s2: s2 - 1);
+
+ if (c2 != 1) {
+ memcpy(buf + nb, ",", 1);
+ nb += 1;
+
+ nb += xdl_num_out(buf + nb, c2);
+ }
+
+ memcpy(buf + nb, " @@", 3);
+ nb += 3;
+ if (func && funclen) {
+ buf[nb++] = ' ';
+ if (funclen > (long)sizeof(buf) - nb - 1)
+ funclen = (long)sizeof(buf) - nb - 1;
+ memcpy(buf + nb, func, funclen);
+ nb += funclen;
+ }
+ buf[nb++] = '\n';
+
+ mb.ptr = buf;
+ mb.size = nb;
+ if (ecb->outf(ecb->priv, &mb, 1) < 0)
+ return -1;
+
+ return 0;
+}
+
+int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
+ int line1, int count1, int line2, int count2)
+{
+ /*
+ * This probably does not work outside Git, since
+ * we have a very simple mmfile structure.
+ *
+ * Note: ideally, we would reuse the prepared environment, but
+ * the libxdiff interface does not (yet) allow for diffing only
+ * ranges of lines instead of the whole files.
+ */
+ mmfile_t subfile1, subfile2;
+ xdfenv_t env;
+
+ subfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1]->ptr;
+ subfile1.size = diff_env->xdf1.recs[line1 + count1 - 2]->ptr +
+ diff_env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;
+ subfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1]->ptr;
+ subfile2.size = diff_env->xdf2.recs[line2 + count2 - 2]->ptr +
+ diff_env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;
+ if (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)
+ return -1;
+
+ memcpy(diff_env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1);
+ memcpy(diff_env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2);
+
+ xdl_free_env(&env);
+
+ return 0;
+}
diff --git a/src/xdiff/xutils.h b/src/xdiff/xutils.h
new file mode 100644
index 000000000..714719a89
--- /dev/null
+++ b/src/xdiff/xutils.h
@@ -0,0 +1,49 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XUTILS_H)
+#define XUTILS_H
+
+
+
+long xdl_bogosqrt(long n);
+int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,
+ xdemitcb_t *ecb);
+int xdl_cha_init(chastore_t *cha, long isize, long icount);
+void xdl_cha_free(chastore_t *cha);
+void *xdl_cha_alloc(chastore_t *cha);
+void *xdl_cha_first(chastore_t *cha);
+void *xdl_cha_next(chastore_t *cha);
+long xdl_guess_lines(mmfile_t *mf, long sample);
+int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags);
+unsigned long xdl_hash_record(char const **data, char const *top, long flags);
+unsigned int xdl_hashbits(unsigned int size);
+int xdl_num_out(char *out, long val);
+long xdl_atol(char const *str, char const **next);
+int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
+ const char *func, long funclen, xdemitcb_t *ecb);
+int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
+ int line1, int count1, int line2, int count2);
+
+
+
+#endif /* #if !defined(XUTILS_H) */
diff --git a/tests-clar/attr/attr_expect.h b/tests-clar/attr/attr_expect.h
new file mode 100644
index 000000000..bea562457
--- /dev/null
+++ b/tests-clar/attr/attr_expect.h
@@ -0,0 +1,42 @@
+#ifndef __CLAR_TEST_ATTR_EXPECT__
+#define __CLAR_TEST_ATTR_EXPECT__
+
+enum attr_expect_t {
+ EXPECT_FALSE,
+ EXPECT_TRUE,
+ EXPECT_UNDEFINED,
+ EXPECT_STRING
+};
+
+struct attr_expected {
+ const char *path;
+ const char *attr;
+ enum attr_expect_t expected;
+ const char *expected_str;
+};
+
+static inline void attr_check_expected(
+ enum attr_expect_t expected,
+ const char *expected_str,
+ const char *value)
+{
+ switch (expected) {
+ case EXPECT_TRUE:
+ cl_assert(GIT_ATTR_TRUE(value));
+ break;
+
+ case EXPECT_FALSE:
+ cl_assert(GIT_ATTR_FALSE(value));
+ break;
+
+ case EXPECT_UNDEFINED:
+ cl_assert(GIT_ATTR_UNSPECIFIED(value));
+ break;
+
+ case EXPECT_STRING:
+ cl_assert_strequal(expected_str, value);
+ break;
+ }
+}
+
+#endif
diff --git a/tests-clar/attr/file.c b/tests-clar/attr/file.c
index af50cd38e..132b906cd 100644
--- a/tests-clar/attr/file.c
+++ b/tests-clar/attr/file.c
@@ -1,5 +1,6 @@
#include "clar_libgit2.h"
#include "attr_file.h"
+#include "attr_expect.h"
#define get_rule(X) ((git_attr_rule *)git_vector_get(&file->rules,(X)))
#define get_assign(R,Y) ((git_attr_assignment *)git_vector_get(&(R)->assigns,(Y)))
@@ -25,7 +26,7 @@ void test_attr_file__simple_read(void)
assign = get_assign(rule, 0);
cl_assert(assign != NULL);
cl_assert_strequal("binary", assign->name);
- cl_assert(assign->value == GIT_ATTR_TRUE);
+ cl_assert(GIT_ATTR_TRUE(assign->value));
cl_assert(!assign->is_allocated);
git_attr_file__free(file);
@@ -54,7 +55,7 @@ void test_attr_file__match_variants(void)
assign = get_assign(rule,0);
cl_assert_strequal("attr0", assign->name);
cl_assert(assign->name_hash == git_attr_file__name_hash(assign->name));
- cl_assert(assign->value == GIT_ATTR_TRUE);
+ cl_assert(GIT_ATTR_TRUE(assign->value));
cl_assert(!assign->is_allocated);
rule = get_rule(1);
@@ -83,7 +84,7 @@ void test_attr_file__match_variants(void)
cl_assert(rule->assigns.length == 1);
assign = get_assign(rule,0);
cl_assert_strequal("attr7", assign->name);
- cl_assert(assign->value == GIT_ATTR_TRUE);
+ cl_assert(GIT_ATTR_TRUE(assign->value));
rule = get_rule(8);
cl_assert_strequal("pat8 with spaces", rule->match.pattern);
@@ -102,8 +103,8 @@ static void check_one_assign(
int assign_idx,
const char *pattern,
const char *name,
- const char *value,
- int is_allocated)
+ enum attr_expect_t expected,
+ const char *expected_str)
{
git_attr_rule *rule = get_rule(rule_idx);
git_attr_assignment *assign = get_assign(rule, assign_idx);
@@ -112,11 +113,8 @@ static void check_one_assign(
cl_assert(rule->assigns.length == 1);
cl_assert_strequal(name, assign->name);
cl_assert(assign->name_hash == git_attr_file__name_hash(assign->name));
- cl_assert(assign->is_allocated == is_allocated);
- if (is_allocated)
- cl_assert_strequal(value, assign->value);
- else
- cl_assert(assign->value == value);
+
+ attr_check_expected(expected, expected_str, assign->value);
}
void test_attr_file__assign_variants(void)
@@ -130,14 +128,14 @@ void test_attr_file__assign_variants(void)
cl_assert_strequal(cl_fixture("attr/attr2"), file->path);
cl_assert(file->rules.length == 11);
- check_one_assign(file, 0, 0, "pat0", "simple", GIT_ATTR_TRUE, 0);
- check_one_assign(file, 1, 0, "pat1", "neg", GIT_ATTR_FALSE, 0);
- check_one_assign(file, 2, 0, "*", "notundef", GIT_ATTR_TRUE, 0);
- check_one_assign(file, 3, 0, "pat2", "notundef", NULL, 0);
- check_one_assign(file, 4, 0, "pat3", "assigned", "test-value", 1);
- check_one_assign(file, 5, 0, "pat4", "rule-with-more-chars", "value-with-more-chars", 1);
- check_one_assign(file, 6, 0, "pat5", "empty", GIT_ATTR_TRUE, 0);
- check_one_assign(file, 7, 0, "pat6", "negempty", GIT_ATTR_FALSE, 0);
+ check_one_assign(file, 0, 0, "pat0", "simple", EXPECT_TRUE, NULL);
+ check_one_assign(file, 1, 0, "pat1", "neg", EXPECT_FALSE, NULL);
+ check_one_assign(file, 2, 0, "*", "notundef", EXPECT_TRUE, NULL);
+ check_one_assign(file, 3, 0, "pat2", "notundef", EXPECT_UNDEFINED, NULL);
+ check_one_assign(file, 4, 0, "pat3", "assigned", EXPECT_STRING, "test-value");
+ check_one_assign(file, 5, 0, "pat4", "rule-with-more-chars", EXPECT_STRING, "value-with-more-chars");
+ check_one_assign(file, 6, 0, "pat5", "empty", EXPECT_TRUE, NULL);
+ check_one_assign(file, 7, 0, "pat6", "negempty", EXPECT_FALSE, NULL);
rule = get_rule(8);
cl_assert_strequal("pat7", rule->match.pattern);
@@ -148,11 +146,11 @@ void test_attr_file__assign_variants(void)
assign = git_attr_rule__lookup_assignment(rule, "multiple");
cl_assert(assign);
cl_assert_strequal("multiple", assign->name);
- cl_assert(assign->value == GIT_ATTR_TRUE);
+ cl_assert(GIT_ATTR_TRUE(assign->value));
assign = git_attr_rule__lookup_assignment(rule, "single");
cl_assert(assign);
cl_assert_strequal("single", assign->name);
- cl_assert(assign->value == GIT_ATTR_FALSE);
+ cl_assert(GIT_ATTR_FALSE(assign->value));
assign = git_attr_rule__lookup_assignment(rule, "values");
cl_assert(assign);
cl_assert_strequal("values", assign->name);
@@ -174,13 +172,13 @@ void test_attr_file__assign_variants(void)
assign = git_attr_rule__lookup_assignment(rule, "again");
cl_assert(assign);
cl_assert_strequal("again", assign->name);
- cl_assert(assign->value == GIT_ATTR_TRUE);
+ cl_assert(GIT_ATTR_TRUE(assign->value));
assign = git_attr_rule__lookup_assignment(rule, "another");
cl_assert(assign);
cl_assert_strequal("another", assign->name);
cl_assert_strequal("12321", assign->value);
- check_one_assign(file, 10, 0, "pat9", "at-eof", GIT_ATTR_FALSE, 0);
+ check_one_assign(file, 10, 0, "pat9", "at-eof", EXPECT_FALSE, NULL);
git_attr_file__free(file);
}
@@ -204,10 +202,10 @@ void test_attr_file__check_attr_examples(void)
cl_assert_strequal("java", assign->value);
assign = git_attr_rule__lookup_assignment(rule, "crlf");
cl_assert_strequal("crlf", assign->name);
- cl_assert(GIT_ATTR_FALSE == assign->value);
+ cl_assert(GIT_ATTR_FALSE(assign->value));
assign = git_attr_rule__lookup_assignment(rule, "myAttr");
cl_assert_strequal("myAttr", assign->name);
- cl_assert(GIT_ATTR_TRUE == assign->value);
+ cl_assert(GIT_ATTR_TRUE(assign->value));
assign = git_attr_rule__lookup_assignment(rule, "missing");
cl_assert(assign == NULL);
diff --git a/tests-clar/attr/lookup.c b/tests-clar/attr/lookup.c
index 9462bbe7f..19396182e 100644
--- a/tests-clar/attr/lookup.c
+++ b/tests-clar/attr/lookup.c
@@ -1,6 +1,8 @@
#include "clar_libgit2.h"
#include "attr_file.h"
+#include "attr_expect.h"
+
void test_attr_lookup__simple(void)
{
git_attr_file *file;
@@ -18,7 +20,7 @@ void test_attr_lookup__simple(void)
cl_assert(!path.is_dir);
cl_git_pass(git_attr_file__lookup_one(file,&path,"binary",&value));
- cl_assert(value == GIT_ATTR_TRUE);
+ cl_assert(GIT_ATTR_TRUE(value));
cl_git_pass(git_attr_file__lookup_one(file,&path,"missing",&value));
cl_assert(!value);
@@ -26,36 +28,23 @@ void test_attr_lookup__simple(void)
git_attr_file__free(file);
}
-typedef struct {
- const char *path;
- const char *attr;
- const char *expected;
- int use_strcmp;
- int force_dir;
-} test_case;
-
-static void run_test_cases(git_attr_file *file, test_case *cases)
+static void run_test_cases(git_attr_file *file, struct attr_expected *cases, int force_dir)
{
git_attr_path path;
const char *value = NULL;
- test_case *c;
+ struct attr_expected *c;
int error;
for (c = cases; c->path != NULL; c++) {
cl_git_pass(git_attr_path__init(&path, c->path, NULL));
- if (c->force_dir)
+ if (force_dir)
path.is_dir = 1;
error = git_attr_file__lookup_one(file,&path,c->attr,&value);
- if (error != GIT_SUCCESS)
- fprintf(stderr, "failure with %s %s %s\n", c->path, c->attr, c->expected);
cl_git_pass(error);
- if (c->use_strcmp)
- cl_assert_strequal(c->expected, value);
- else
- cl_assert(c->expected == value);
+ attr_check_expected(c->expected, c->expected_str, value);
}
}
@@ -63,74 +52,79 @@ void test_attr_lookup__match_variants(void)
{
git_attr_file *file;
git_attr_path path;
- test_case cases[] = {
+
+ struct attr_expected dir_cases[] = {
+ { "pat2", "attr2", EXPECT_TRUE, NULL },
+ { "/testing/for/pat2", "attr2", EXPECT_TRUE, NULL },
+ { "/not/pat2/yousee", "attr2", EXPECT_UNDEFINED, NULL },
+ { "/fun/fun/fun/pat4.dir", "attr4", EXPECT_TRUE, NULL },
+ { "foo.pat5", "attr5", EXPECT_TRUE, NULL },
+ { NULL, NULL, 0, NULL }
+ };
+
+ struct attr_expected cases[] = {
/* pat0 -> simple match */
- { "pat0", "attr0", GIT_ATTR_TRUE, 0, 0 },
- { "/testing/for/pat0", "attr0", GIT_ATTR_TRUE, 0, 0 },
- { "relative/to/pat0", "attr0", GIT_ATTR_TRUE, 0, 0 },
- { "this-contains-pat0-inside", "attr0", NULL, 0, 0 },
- { "this-aint-right", "attr0", NULL, 0, 0 },
- { "/this/pat0/dont/match", "attr0", NULL, 0, 0 },
+ { "pat0", "attr0", EXPECT_TRUE, NULL },
+ { "/testing/for/pat0", "attr0", EXPECT_TRUE, NULL },
+ { "relative/to/pat0", "attr0", EXPECT_TRUE, NULL },
+ { "this-contains-pat0-inside", "attr0", EXPECT_UNDEFINED, NULL },
+ { "this-aint-right", "attr0", EXPECT_UNDEFINED, NULL },
+ { "/this/pat0/dont/match", "attr0", EXPECT_UNDEFINED, NULL },
/* negative match */
- { "pat0", "attr1", GIT_ATTR_TRUE, 0, 0 },
- { "pat1", "attr1", NULL, 0, 0 },
- { "/testing/for/pat1", "attr1", NULL, 0, 0 },
- { "/testing/for/pat0", "attr1", GIT_ATTR_TRUE, 0, 0 },
- { "/testing/for/pat1/inside", "attr1", GIT_ATTR_TRUE, 0, 0 },
- { "misc", "attr1", GIT_ATTR_TRUE, 0, 0 },
+ { "pat0", "attr1", EXPECT_TRUE, NULL },
+ { "pat1", "attr1", EXPECT_UNDEFINED, NULL },
+ { "/testing/for/pat1", "attr1", EXPECT_UNDEFINED, NULL },
+ { "/testing/for/pat0", "attr1", EXPECT_TRUE, NULL },
+ { "/testing/for/pat1/inside", "attr1", EXPECT_TRUE, NULL },
+ { "misc", "attr1", EXPECT_TRUE, NULL },
/* dir match */
- { "pat2", "attr2", NULL, 0, 0 },
- { "pat2", "attr2", GIT_ATTR_TRUE, 0, 1 },
- { "/testing/for/pat2", "attr2", NULL, 0, 0 },
- { "/testing/for/pat2", "attr2", GIT_ATTR_TRUE, 0, 1 },
- { "/not/pat2/yousee", "attr2", NULL, 0, 0 },
- { "/not/pat2/yousee", "attr2", NULL, 0, 1 },
+ { "pat2", "attr2", EXPECT_UNDEFINED, NULL },
+ { "/testing/for/pat2", "attr2", EXPECT_UNDEFINED, NULL },
+ { "/not/pat2/yousee", "attr2", EXPECT_UNDEFINED, NULL },
/* path match */
- { "pat3file", "attr3", NULL, 0, 0 },
- { "/pat3dir/pat3file", "attr3", NULL, 0, 0 },
- { "pat3dir/pat3file", "attr3", GIT_ATTR_TRUE, 0, 0 },
+ { "pat3file", "attr3", EXPECT_UNDEFINED, NULL },
+ { "/pat3dir/pat3file", "attr3", EXPECT_UNDEFINED, NULL },
+ { "pat3dir/pat3file", "attr3", EXPECT_TRUE, NULL },
/* pattern* match */
- { "pat4.txt", "attr4", GIT_ATTR_TRUE, 0, 0 },
- { "/fun/fun/fun/pat4.c", "attr4", GIT_ATTR_TRUE, 0, 0 },
- { "pat4.", "attr4", GIT_ATTR_TRUE, 0, 0 },
- { "pat4", "attr4", NULL, 0, 0 },
- { "/fun/fun/fun/pat4.dir", "attr4", GIT_ATTR_TRUE, 0, 1 },
+ { "pat4.txt", "attr4", EXPECT_TRUE, NULL },
+ { "/fun/fun/fun/pat4.c", "attr4", EXPECT_TRUE, NULL },
+ { "pat4.", "attr4", EXPECT_TRUE, NULL },
+ { "pat4", "attr4", EXPECT_UNDEFINED, NULL },
/* *pattern match */
- { "foo.pat5", "attr5", GIT_ATTR_TRUE, 0, 0 },
- { "foo.pat5", "attr5", GIT_ATTR_TRUE, 0, 1 },
- { "/this/is/ok.pat5", "attr5", GIT_ATTR_TRUE, 0, 0 },
- { "/this/is/bad.pat5/yousee.txt", "attr5", NULL, 0, 0 },
- { "foo.pat5", "attr100", NULL, 0, 0 },
+ { "foo.pat5", "attr5", EXPECT_TRUE, NULL },
+ { "/this/is/ok.pat5", "attr5", EXPECT_TRUE, NULL },
+ { "/this/is/bad.pat5/yousee.txt", "attr5", EXPECT_UNDEFINED, NULL },
+ { "foo.pat5", "attr100", EXPECT_UNDEFINED, NULL },
/* glob match with slashes */
- { "foo.pat6", "attr6", NULL, 0, 0 },
- { "pat6/pat6/foobar.pat6", "attr6", GIT_ATTR_TRUE, 0, 0 },
- { "pat6/pat6/.pat6", "attr6", GIT_ATTR_TRUE, 0, 0 },
- { "pat6/pat6/extra/foobar.pat6", "attr6", NULL, 0, 0 },
- { "/prefix/pat6/pat6/foobar.pat6", "attr6", NULL, 0, 0 },
- { "/pat6/pat6/foobar.pat6", "attr6", NULL, 0, 0 },
+ { "foo.pat6", "attr6", EXPECT_UNDEFINED, NULL },
+ { "pat6/pat6/foobar.pat6", "attr6", EXPECT_TRUE, NULL },
+ { "pat6/pat6/.pat6", "attr6", EXPECT_TRUE, NULL },
+ { "pat6/pat6/extra/foobar.pat6", "attr6", EXPECT_UNDEFINED, NULL },
+ { "/prefix/pat6/pat6/foobar.pat6", "attr6", EXPECT_UNDEFINED, NULL },
+ { "/pat6/pat6/foobar.pat6", "attr6", EXPECT_UNDEFINED, NULL },
/* complex pattern */
- { "pat7a12z", "attr7", GIT_ATTR_TRUE, 0, 0 },
- { "pat7e__x", "attr7", GIT_ATTR_TRUE, 0, 0 },
- { "pat7b/1y", "attr7", NULL, 0, 0 }, /* ? does not match / */
- { "pat7e_x", "attr7", NULL, 0, 0 },
- { "pat7aaaa", "attr7", NULL, 0, 0 },
- { "pat7zzzz", "attr7", NULL, 0, 0 },
- { "/this/can/be/anything/pat7a12z", "attr7", GIT_ATTR_TRUE, 0, 0 },
- { "but/it/still/must/match/pat7aaaa", "attr7", NULL, 0, 0 },
- { "pat7aaay.fail", "attr7", NULL, 0, 0 },
+ { "pat7a12z", "attr7", EXPECT_TRUE, NULL },
+ { "pat7e__x", "attr7", EXPECT_TRUE, NULL },
+ { "pat7b/1y", "attr7", EXPECT_UNDEFINED, NULL }, /* ? does not match / */
+ { "pat7e_x", "attr7", EXPECT_UNDEFINED, NULL },
+ { "pat7aaaa", "attr7", EXPECT_UNDEFINED, NULL },
+ { "pat7zzzz", "attr7", EXPECT_UNDEFINED, NULL },
+ { "/this/can/be/anything/pat7a12z", "attr7", EXPECT_TRUE, NULL },
+ { "but/it/still/must/match/pat7aaaa", "attr7", EXPECT_UNDEFINED, NULL },
+ { "pat7aaay.fail", "attr7", EXPECT_UNDEFINED, NULL },
/* pattern with spaces */
- { "pat8 with spaces", "attr8", GIT_ATTR_TRUE, 0, 0 },
- { "/gotta love/pat8 with spaces", "attr8", GIT_ATTR_TRUE, 0, 0 },
- { "failing pat8 with spaces", "attr8", NULL, 0, 0 },
- { "spaces", "attr8", NULL, 0, 0 },
+ { "pat8 with spaces", "attr8", EXPECT_TRUE, NULL },
+ { "/gotta love/pat8 with spaces", "attr8", EXPECT_TRUE, NULL },
+ { "failing pat8 with spaces", "attr8", EXPECT_UNDEFINED, NULL },
+ { "spaces", "attr8", EXPECT_UNDEFINED, NULL },
/* pattern at eof */
- { "pat9", "attr9", GIT_ATTR_TRUE, 0, 0 },
- { "/eof/pat9", "attr9", GIT_ATTR_TRUE, 0, 0 },
- { "pat", "attr9", NULL, 0, 0 },
- { "at9", "attr9", NULL, 0, 0 },
- { "pat9.fail", "attr9", NULL, 0, 0 },
+ { "pat9", "attr9", EXPECT_TRUE, NULL },
+ { "/eof/pat9", "attr9", EXPECT_TRUE, NULL },
+ { "pat", "attr9", EXPECT_UNDEFINED, NULL },
+ { "at9", "attr9", EXPECT_UNDEFINED, NULL },
+ { "pat9.fail", "attr9", EXPECT_UNDEFINED, NULL },
/* sentinel at end */
- { NULL, NULL, NULL, 0, 0 }
+ { NULL, NULL, 0, NULL }
};
cl_git_pass(git_attr_file__new(&file));
@@ -141,7 +135,8 @@ void test_attr_lookup__match_variants(void)
cl_git_pass(git_attr_path__init(&path, "/testing/for/pat0", NULL));
cl_assert_strequal("pat0", path.basename);
- run_test_cases(file, cases);
+ run_test_cases(file, cases, 0);
+ run_test_cases(file, dir_cases, 1);
git_attr_file__free(file);
}
@@ -149,54 +144,54 @@ void test_attr_lookup__match_variants(void)
void test_attr_lookup__assign_variants(void)
{
git_attr_file *file;
- test_case cases[] = {
+ struct attr_expected cases[] = {
/* pat0 -> simple assign */
- { "pat0", "simple", GIT_ATTR_TRUE, 0, 0 },
- { "/testing/pat0", "simple", GIT_ATTR_TRUE, 0, 0 },
- { "pat0", "fail", NULL, 0, 0 },
- { "/testing/pat0", "fail", NULL, 0, 0 },
+ { "pat0", "simple", EXPECT_TRUE, NULL },
+ { "/testing/pat0", "simple", EXPECT_TRUE, NULL },
+ { "pat0", "fail", EXPECT_UNDEFINED, NULL },
+ { "/testing/pat0", "fail", EXPECT_UNDEFINED, NULL },
/* negative assign */
- { "pat1", "neg", GIT_ATTR_FALSE, 0, 0 },
- { "/testing/pat1", "neg", GIT_ATTR_FALSE, 0, 0 },
- { "pat1", "fail", NULL, 0, 0 },
- { "/testing/pat1", "fail", NULL, 0, 0 },
+ { "pat1", "neg", EXPECT_FALSE, NULL },
+ { "/testing/pat1", "neg", EXPECT_FALSE, NULL },
+ { "pat1", "fail", EXPECT_UNDEFINED, NULL },
+ { "/testing/pat1", "fail", EXPECT_UNDEFINED, NULL },
/* forced undef */
- { "pat1", "notundef", GIT_ATTR_TRUE, 0, 0 },
- { "pat2", "notundef", NULL, 0, 0 },
- { "/lead/in/pat1", "notundef", GIT_ATTR_TRUE, 0, 0 },
- { "/lead/in/pat2", "notundef", NULL, 0, 0 },
+ { "pat1", "notundef", EXPECT_TRUE, NULL },
+ { "pat2", "notundef", EXPECT_UNDEFINED, NULL },
+ { "/lead/in/pat1", "notundef", EXPECT_TRUE, NULL },
+ { "/lead/in/pat2", "notundef", EXPECT_UNDEFINED, NULL },
/* assign value */
- { "pat3", "assigned", "test-value", 1, 0 },
- { "pat3", "notassigned", NULL, 0, 0 },
+ { "pat3", "assigned", EXPECT_STRING, "test-value" },
+ { "pat3", "notassigned", EXPECT_UNDEFINED, NULL },
/* assign value */
- { "pat4", "rule-with-more-chars", "value-with-more-chars", 1, 0 },
- { "pat4", "notassigned-rule-with-more-chars", NULL, 0, 0 },
+ { "pat4", "rule-with-more-chars", EXPECT_STRING, "value-with-more-chars" },
+ { "pat4", "notassigned-rule-with-more-chars", EXPECT_UNDEFINED, NULL },
/* empty assignments */
- { "pat5", "empty", GIT_ATTR_TRUE, 0, 0 },
- { "pat6", "negempty", GIT_ATTR_FALSE, 0, 0 },
+ { "pat5", "empty", EXPECT_TRUE, NULL },
+ { "pat6", "negempty", EXPECT_FALSE, NULL },
/* multiple assignment */
- { "pat7", "multiple", GIT_ATTR_TRUE, 0, 0 },
- { "pat7", "single", GIT_ATTR_FALSE, 0, 0 },
- { "pat7", "values", "1", 1, 0 },
- { "pat7", "also", "a-really-long-value/*", 1, 0 },
- { "pat7", "happy", "yes!", 1, 0 },
- { "pat8", "again", GIT_ATTR_TRUE, 0, 0 },
- { "pat8", "another", "12321", 1, 0 },
+ { "pat7", "multiple", EXPECT_TRUE, NULL },
+ { "pat7", "single", EXPECT_FALSE, NULL },
+ { "pat7", "values", EXPECT_STRING, "1" },
+ { "pat7", "also", EXPECT_STRING, "a-really-long-value/*" },
+ { "pat7", "happy", EXPECT_STRING, "yes!" },
+ { "pat8", "again", EXPECT_TRUE, NULL },
+ { "pat8", "another", EXPECT_STRING, "12321" },
/* bad assignment */
- { "patbad0", "simple", NULL, 0, 0 },
- { "patbad0", "notundef", GIT_ATTR_TRUE, 0, 0 },
- { "patbad1", "simple", NULL, 0, 0 },
+ { "patbad0", "simple", EXPECT_UNDEFINED, NULL },
+ { "patbad0", "notundef", EXPECT_TRUE, NULL },
+ { "patbad1", "simple", EXPECT_UNDEFINED, NULL },
/* eof assignment */
- { "pat9", "at-eof", GIT_ATTR_FALSE, 0, 0 },
+ { "pat9", "at-eof", EXPECT_FALSE, NULL },
/* sentinel at end */
- { NULL, NULL, NULL, 0, 0 }
+ { NULL, NULL, 0, NULL }
};
cl_git_pass(git_attr_file__new(&file));
cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr2"), file));
cl_assert(file->rules.length == 11);
- run_test_cases(file, cases);
+ run_test_cases(file, cases, 0);
git_attr_file__free(file);
}
@@ -204,34 +199,34 @@ void test_attr_lookup__assign_variants(void)
void test_attr_lookup__check_attr_examples(void)
{
git_attr_file *file;
- test_case cases[] = {
- { "foo.java", "diff", "java", 1, 0 },
- { "foo.java", "crlf", GIT_ATTR_FALSE, 0, 0 },
- { "foo.java", "myAttr", GIT_ATTR_TRUE, 0, 0 },
- { "foo.java", "other", NULL, 0, 0 },
- { "/prefix/dir/foo.java", "diff", "java", 1, 0 },
- { "/prefix/dir/foo.java", "crlf", GIT_ATTR_FALSE, 0, 0 },
- { "/prefix/dir/foo.java", "myAttr", GIT_ATTR_TRUE, 0, 0 },
- { "/prefix/dir/foo.java", "other", NULL, 0, 0 },
- { "NoMyAttr.java", "crlf", GIT_ATTR_FALSE, 0, 0 },
- { "NoMyAttr.java", "myAttr", NULL, 0, 0 },
- { "NoMyAttr.java", "other", NULL, 0, 0 },
- { "/prefix/dir/NoMyAttr.java", "crlf", GIT_ATTR_FALSE, 0, 0 },
- { "/prefix/dir/NoMyAttr.java", "myAttr", NULL, 0, 0 },
- { "/prefix/dir/NoMyAttr.java", "other", NULL, 0, 0 },
- { "README", "caveat", "unspecified", 1, 0 },
- { "/specific/path/README", "caveat", "unspecified", 1, 0 },
- { "README", "missing", NULL, 0, 0 },
- { "/specific/path/README", "missing", NULL, 0, 0 },
+ struct attr_expected cases[] = {
+ { "foo.java", "diff", EXPECT_STRING, "java" },
+ { "foo.java", "crlf", EXPECT_FALSE, NULL },
+ { "foo.java", "myAttr", EXPECT_TRUE, NULL },
+ { "foo.java", "other", EXPECT_UNDEFINED, NULL },
+ { "/prefix/dir/foo.java", "diff", EXPECT_STRING, "java" },
+ { "/prefix/dir/foo.java", "crlf", EXPECT_FALSE, NULL },
+ { "/prefix/dir/foo.java", "myAttr", EXPECT_TRUE, NULL },
+ { "/prefix/dir/foo.java", "other", EXPECT_UNDEFINED, NULL },
+ { "NoMyAttr.java", "crlf", EXPECT_FALSE, NULL },
+ { "NoMyAttr.java", "myAttr", EXPECT_UNDEFINED, NULL },
+ { "NoMyAttr.java", "other", EXPECT_UNDEFINED, NULL },
+ { "/prefix/dir/NoMyAttr.java", "crlf", EXPECT_FALSE, NULL },
+ { "/prefix/dir/NoMyAttr.java", "myAttr", EXPECT_UNDEFINED, NULL },
+ { "/prefix/dir/NoMyAttr.java", "other", EXPECT_UNDEFINED, NULL },
+ { "README", "caveat", EXPECT_STRING, "unspecified" },
+ { "/specific/path/README", "caveat", EXPECT_STRING, "unspecified" },
+ { "README", "missing", EXPECT_UNDEFINED, NULL },
+ { "/specific/path/README", "missing", EXPECT_UNDEFINED, NULL },
/* sentinel at end */
- { NULL, NULL, NULL, 0, 0 }
+ { NULL, NULL, 0, NULL }
};
cl_git_pass(git_attr_file__new(&file));
cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr3"), file));
cl_assert(file->rules.length == 3);
- run_test_cases(file, cases);
+ run_test_cases(file, cases, 0);
git_attr_file__free(file);
}
@@ -239,24 +234,24 @@ void test_attr_lookup__check_attr_examples(void)
void test_attr_lookup__from_buffer(void)
{
git_attr_file *file;
- test_case cases[] = {
- { "abc", "foo", GIT_ATTR_TRUE, 0, 0 },
- { "abc", "bar", GIT_ATTR_TRUE, 0, 0 },
- { "abc", "baz", GIT_ATTR_TRUE, 0, 0 },
- { "aaa", "foo", GIT_ATTR_TRUE, 0, 0 },
- { "aaa", "bar", NULL, 0, 0 },
- { "aaa", "baz", GIT_ATTR_TRUE, 0, 0 },
- { "qqq", "foo", NULL, 0, 0 },
- { "qqq", "bar", NULL, 0, 0 },
- { "qqq", "baz", GIT_ATTR_TRUE, 0, 0 },
- { NULL, NULL, NULL, 0, 0 }
+ struct attr_expected cases[] = {
+ { "abc", "foo", EXPECT_TRUE, NULL },
+ { "abc", "bar", EXPECT_TRUE, NULL },
+ { "abc", "baz", EXPECT_TRUE, NULL },
+ { "aaa", "foo", EXPECT_TRUE, NULL },
+ { "aaa", "bar", EXPECT_UNDEFINED, NULL },
+ { "aaa", "baz", EXPECT_TRUE, NULL },
+ { "qqq", "foo", EXPECT_UNDEFINED, NULL },
+ { "qqq", "bar", EXPECT_UNDEFINED, NULL },
+ { "qqq", "baz", EXPECT_TRUE, NULL },
+ { NULL, NULL, 0, NULL }
};
cl_git_pass(git_attr_file__new(&file));
cl_git_pass(git_attr_file__from_buffer(NULL, "a* foo\nabc bar\n* baz", file));
cl_assert(file->rules.length == 3);
- run_test_cases(file, cases);
+ run_test_cases(file, cases, 0);
git_attr_file__free(file);
}
diff --git a/tests-clar/attr/repo.c b/tests-clar/attr/repo.c
index 7a716042a..4de4afaa7 100644
--- a/tests-clar/attr/repo.c
+++ b/tests-clar/attr/repo.c
@@ -3,6 +3,8 @@
#include "git2/attr.h"
#include "attr.h"
+#include "attr_expect.h"
+
static git_repository *g_repo = NULL;
void test_attr_repo__initialize(void)
@@ -27,68 +29,47 @@ void test_attr_repo__cleanup(void)
void test_attr_repo__get_one(void)
{
- const char *value;
- struct {
- const char *file;
- const char *attr;
- const char *expected;
- } test_cases[] = {
- { "root_test1", "repoattr", GIT_ATTR_TRUE },
- { "root_test1", "rootattr", GIT_ATTR_TRUE },
- { "root_test1", "missingattr", NULL },
- { "root_test1", "subattr", NULL },
- { "root_test1", "negattr", NULL },
- { "root_test2", "repoattr", GIT_ATTR_TRUE },
- { "root_test2", "rootattr", GIT_ATTR_FALSE },
- { "root_test2", "missingattr", NULL },
- { "root_test2", "multiattr", GIT_ATTR_FALSE },
- { "root_test3", "repoattr", GIT_ATTR_TRUE },
- { "root_test3", "rootattr", NULL },
- { "root_test3", "multiattr", "3" },
- { "root_test3", "multi2", NULL },
- { "sub/subdir_test1", "repoattr", GIT_ATTR_TRUE },
- { "sub/subdir_test1", "rootattr", GIT_ATTR_TRUE },
- { "sub/subdir_test1", "missingattr", NULL },
- { "sub/subdir_test1", "subattr", "yes" },
- { "sub/subdir_test1", "negattr", GIT_ATTR_FALSE },
- { "sub/subdir_test1", "another", NULL },
- { "sub/subdir_test2.txt", "repoattr", GIT_ATTR_TRUE },
- { "sub/subdir_test2.txt", "rootattr", GIT_ATTR_TRUE },
- { "sub/subdir_test2.txt", "missingattr", NULL },
- { "sub/subdir_test2.txt", "subattr", "yes" },
- { "sub/subdir_test2.txt", "negattr", GIT_ATTR_FALSE },
- { "sub/subdir_test2.txt", "another", "zero" },
- { "sub/subdir_test2.txt", "reposub", GIT_ATTR_TRUE },
- { "sub/sub/subdir.txt", "another", "one" },
- { "sub/sub/subdir.txt", "reposubsub", GIT_ATTR_TRUE },
- { "sub/sub/subdir.txt", "reposub", NULL },
- { "does-not-exist", "foo", "yes" },
- { "sub/deep/file", "deepdeep", GIT_ATTR_TRUE },
- { NULL, NULL, NULL }
+ struct attr_expected test_cases[] = {
+ { "root_test1", "repoattr", EXPECT_TRUE, NULL },
+ { "root_test1", "rootattr", EXPECT_TRUE, NULL },
+ { "root_test1", "missingattr", EXPECT_UNDEFINED, NULL },
+ { "root_test1", "subattr", EXPECT_UNDEFINED, NULL },
+ { "root_test1", "negattr", EXPECT_UNDEFINED, NULL },
+ { "root_test2", "repoattr", EXPECT_TRUE, NULL },
+ { "root_test2", "rootattr", EXPECT_FALSE, NULL },
+ { "root_test2", "missingattr", EXPECT_UNDEFINED, NULL },
+ { "root_test2", "multiattr", EXPECT_FALSE, NULL },
+ { "root_test3", "repoattr", EXPECT_TRUE, NULL },
+ { "root_test3", "rootattr", EXPECT_UNDEFINED, NULL },
+ { "root_test3", "multiattr", EXPECT_STRING, "3" },
+ { "root_test3", "multi2", EXPECT_UNDEFINED, NULL },
+ { "sub/subdir_test1", "repoattr", EXPECT_TRUE, NULL },
+ { "sub/subdir_test1", "rootattr", EXPECT_TRUE, NULL },
+ { "sub/subdir_test1", "missingattr", EXPECT_UNDEFINED, NULL },
+ { "sub/subdir_test1", "subattr", EXPECT_STRING, "yes" },
+ { "sub/subdir_test1", "negattr", EXPECT_FALSE, NULL },
+ { "sub/subdir_test1", "another", EXPECT_UNDEFINED, NULL },
+ { "sub/subdir_test2.txt", "repoattr", EXPECT_TRUE, NULL },
+ { "sub/subdir_test2.txt", "rootattr", EXPECT_TRUE, NULL },
+ { "sub/subdir_test2.txt", "missingattr", EXPECT_UNDEFINED, NULL },
+ { "sub/subdir_test2.txt", "subattr", EXPECT_STRING, "yes" },
+ { "sub/subdir_test2.txt", "negattr", EXPECT_FALSE, NULL },
+ { "sub/subdir_test2.txt", "another", EXPECT_STRING, "zero" },
+ { "sub/subdir_test2.txt", "reposub", EXPECT_TRUE, NULL },
+ { "sub/sub/subdir.txt", "another", EXPECT_STRING, "one" },
+ { "sub/sub/subdir.txt", "reposubsub", EXPECT_TRUE, NULL },
+ { "sub/sub/subdir.txt", "reposub", EXPECT_UNDEFINED, NULL },
+ { "does-not-exist", "foo", EXPECT_STRING, "yes" },
+ { "sub/deep/file", "deepdeep", EXPECT_TRUE, NULL },
+ { "sub/sub/d/no", "test", EXPECT_STRING, "a/b/d/*" },
+ { "sub/sub/d/yes", "test", EXPECT_UNDEFINED, NULL },
+ { NULL, NULL, 0, NULL }
}, *scan;
- for (scan = test_cases; scan->file != NULL; scan++) {
- git_buf b = GIT_BUF_INIT;
-
- git_buf_printf(&b, "%s:%s == expect %s",
- scan->file, scan->attr, scan->expected);
-
- cl_must_pass_(
- git_attr_get(g_repo, scan->file, scan->attr, &value) == GIT_SUCCESS,
- b.ptr);
-
- git_buf_printf(&b, ", got %s", value);
-
- if (scan->expected == NULL ||
- scan->expected == GIT_ATTR_TRUE ||
- scan->expected == GIT_ATTR_FALSE)
- {
- cl_assert_(scan->expected == value, b.ptr);
- } else {
- cl_assert_strequal(scan->expected, value);
- }
-
- git_buf_free(&b);
+ for (scan = test_cases; scan->path != NULL; scan++) {
+ const char *value;
+ cl_git_pass(git_attr_get(g_repo, scan->path, scan->attr, &value));
+ attr_check_expected(scan->expected, scan->expected_str, value);
}
cl_git_pass(git_attr_cache__is_cached(g_repo, ".git/info/attributes"));
@@ -103,34 +84,33 @@ void test_attr_repo__get_many(void)
cl_git_pass(git_attr_get_many(g_repo, "root_test1", 4, names, values));
- cl_assert(values[0] == GIT_ATTR_TRUE);
- cl_assert(values[1] == GIT_ATTR_TRUE);
- cl_assert(values[2] == NULL);
- cl_assert(values[3] == NULL);
+ cl_assert(GIT_ATTR_TRUE(values[0]));
+ cl_assert(GIT_ATTR_TRUE(values[1]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
cl_git_pass(git_attr_get_many(g_repo, "root_test2", 4, names, values));
- cl_assert(values[0] == GIT_ATTR_TRUE);
- cl_assert(values[1] == GIT_ATTR_FALSE);
- cl_assert(values[2] == NULL);
- cl_assert(values[3] == NULL);
+ cl_assert(GIT_ATTR_TRUE(values[0]));
+ cl_assert(GIT_ATTR_FALSE(values[1]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
cl_git_pass(git_attr_get_many(g_repo, "sub/subdir_test1", 4, names, values));
- cl_assert(values[0] == GIT_ATTR_TRUE);
- cl_assert(values[1] == GIT_ATTR_TRUE);
- cl_assert(values[2] == NULL);
+ cl_assert(GIT_ATTR_TRUE(values[0]));
+ cl_assert(GIT_ATTR_TRUE(values[1]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
cl_assert_strequal("yes", values[3]);
-
}
static int count_attrs(
- const char *GIT_UNUSED(name),
- const char *GIT_UNUSED(value),
+ const char *name,
+ const char *value,
void *payload)
{
- GIT_UNUSED_ARG(name);
- GIT_UNUSED_ARG(value);
+ GIT_UNUSED(name);
+ GIT_UNUSED(value);
*((int *)payload) += 1;
@@ -161,19 +141,19 @@ void test_attr_repo__manpage_example(void)
const char *value;
cl_git_pass(git_attr_get(g_repo, "sub/abc", "foo", &value));
- cl_assert(value == GIT_ATTR_TRUE);
+ cl_assert(GIT_ATTR_TRUE(value));
cl_git_pass(git_attr_get(g_repo, "sub/abc", "bar", &value));
- cl_assert(value == NULL);
+ cl_assert(GIT_ATTR_UNSPECIFIED(value));
cl_git_pass(git_attr_get(g_repo, "sub/abc", "baz", &value));
- cl_assert(value == GIT_ATTR_FALSE);
+ cl_assert(GIT_ATTR_FALSE(value));
cl_git_pass(git_attr_get(g_repo, "sub/abc", "merge", &value));
cl_assert_strequal("filfre", value);
cl_git_pass(git_attr_get(g_repo, "sub/abc", "frotz", &value));
- cl_assert(value == NULL);
+ cl_assert(GIT_ATTR_UNSPECIFIED(value));
}
void test_attr_repo__macros(void)
@@ -185,24 +165,24 @@ void test_attr_repo__macros(void)
cl_git_pass(git_attr_get_many(g_repo, "binfile", 5, names, values));
- cl_assert(values[0] == GIT_ATTR_TRUE);
- cl_assert(values[1] == GIT_ATTR_TRUE);
- cl_assert(values[2] == GIT_ATTR_FALSE);
- cl_assert(values[3] == GIT_ATTR_FALSE);
- cl_assert(values[4] == NULL);
+ cl_assert(GIT_ATTR_TRUE(values[0]));
+ cl_assert(GIT_ATTR_TRUE(values[1]));
+ cl_assert(GIT_ATTR_FALSE(values[2]));
+ cl_assert(GIT_ATTR_FALSE(values[3]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[4]));
cl_git_pass(git_attr_get_many(g_repo, "macro_test", 5, names2, values));
- cl_assert(values[0] == GIT_ATTR_TRUE);
- cl_assert(values[1] == GIT_ATTR_TRUE);
- cl_assert(values[2] == GIT_ATTR_FALSE);
- cl_assert(values[3] == NULL);
+ cl_assert(GIT_ATTR_TRUE(values[0]));
+ cl_assert(GIT_ATTR_TRUE(values[1]));
+ cl_assert(GIT_ATTR_FALSE(values[2]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
cl_assert_strequal("77", values[4]);
cl_git_pass(git_attr_get_many(g_repo, "macro_test", 3, names3, values));
- cl_assert(values[0] == GIT_ATTR_TRUE);
- cl_assert(values[1] == GIT_ATTR_FALSE);
+ cl_assert(GIT_ATTR_TRUE(values[0]));
+ cl_assert(GIT_ATTR_FALSE(values[1]));
cl_assert_strequal("answer", values[2]);
}
@@ -215,9 +195,9 @@ void test_attr_repo__bad_macros(void)
cl_git_pass(git_attr_get_many(g_repo, "macro_bad", 6, names, values));
/* these three just confirm that the "mymacro" rule ran */
- cl_assert(values[0] == NULL);
- cl_assert(values[1] == GIT_ATTR_TRUE);
- cl_assert(values[2] == GIT_ATTR_FALSE);
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[0]));
+ cl_assert(GIT_ATTR_TRUE(values[1]));
+ cl_assert(GIT_ATTR_FALSE(values[2]));
/* file contains:
* # let's try some malicious macro defs
@@ -241,7 +221,8 @@ void test_attr_repo__bad_macros(void)
* so summary results should be:
* -firstmacro secondmacro="hahaha" thirdmacro
*/
- cl_assert(values[3] == GIT_ATTR_FALSE);
+ cl_assert(GIT_ATTR_FALSE(values[3]));
cl_assert_strequal("hahaha", values[4]);
- cl_assert(values[5] == GIT_ATTR_TRUE);
+ cl_assert(GIT_ATTR_TRUE(values[5]));
}
+
diff --git a/tests-clar/clar_helpers.c b/tests-clar/clar_helpers.c
index eea8bc87d..22db56f0c 100644
--- a/tests-clar/clar_helpers.c
+++ b/tests-clar/clar_helpers.c
@@ -27,3 +27,64 @@ void cl_git_mkfile(const char *filename, const char *content)
cl_must_pass(p_close(fd));
}
+
+void cl_git_append2file(const char *filename, const char *new_content)
+{
+ int fd = p_open(filename, O_WRONLY | O_APPEND | O_CREAT);
+ cl_assert(fd != 0);
+ if (!new_content)
+ new_content = "\n";
+ cl_must_pass(p_write(fd, new_content, strlen(new_content)));
+ cl_must_pass(p_close(fd));
+ cl_must_pass(p_chmod(filename, 0644));
+}
+
+static const char *_cl_sandbox = NULL;
+static git_repository *_cl_repo = NULL;
+
+git_repository *cl_git_sandbox_init(const char *sandbox)
+{
+ /* Copy the whole sandbox folder from our fixtures to our test sandbox
+ * area. After this it can be accessed with `./sandbox`
+ */
+ cl_fixture_sandbox(sandbox);
+ _cl_sandbox = sandbox;
+
+ p_chdir(sandbox);
+
+ /* Rename `sandbox/.gitted` to `sandbox/.git` which must be done since
+ * we cannot store a folder named `.git` inside the fixtures folder of
+ * our libgit2 repo.
+ */
+ cl_git_pass(p_rename(".gitted", ".git"));
+
+ /* If we have `gitattributes`, rename to `.gitattributes`. This may
+ * be necessary if we don't want the attributes to be applied in the
+ * libgit2 repo, but just during testing.
+ */
+ if (p_access("gitattributes", F_OK) == 0)
+ cl_git_pass(p_rename("gitattributes", ".gitattributes"));
+
+ /* As with `gitattributes`, we may need `gitignore` just for testing. */
+ if (p_access("gitignore", F_OK) == 0)
+ cl_git_pass(p_rename("gitignore", ".gitignore"));
+
+ p_chdir("..");
+
+ /* Now open the sandbox repository and make it available for tests */
+ cl_git_pass(git_repository_open(&_cl_repo, sandbox));
+
+ return _cl_repo;
+}
+
+void cl_git_sandbox_cleanup(void)
+{
+ if (_cl_repo) {
+ git_repository_free(_cl_repo);
+ _cl_repo = NULL;
+ }
+ if (_cl_sandbox) {
+ cl_fixture_cleanup(_cl_sandbox);
+ _cl_sandbox = NULL;
+ }
+}
diff --git a/tests-clar/clar_libgit2.h b/tests-clar/clar_libgit2.h
index 73ef66844..5c034a385 100644
--- a/tests-clar/clar_libgit2.h
+++ b/tests-clar/clar_libgit2.h
@@ -38,10 +38,13 @@ GIT_INLINE(void) cl_assert_strequal_internal(
if (!match) {
char buf[4096];
snprintf(buf, 4096, "'%s' != '%s'", a, b);
- clar__assert(0, file, line, buf, err, 1);
+ clar__assert(0, file, line, err, buf, 1);
}
}
+#define cl_assert_intequal(a,b) \
+ do { if ((a) != (b)) { char buf[128]; snprintf(buf,128,"%d != %d",(a),(b)); clar__assert(0,__FILE__,__LINE__,#a " != " #b,buf,1); } } while (0)
+
/*
* Some utility macros for building long strings
*/
@@ -53,5 +56,11 @@ GIT_INLINE(void) cl_assert_strequal_internal(
/* Write the contents of a buffer to disk */
void cl_git_mkfile(const char *filename, const char *content);
+void cl_git_append2file(const char *filename, const char *new_content);
+
+/* Git sandbox setup helpers */
+
+git_repository *cl_git_sandbox_init(const char *sandbox);
+void cl_git_sandbox_cleanup(void);
#endif
diff --git a/tests-clar/config/multivar.c b/tests-clar/config/multivar.c
index bccdc1289..9a411f0df 100644
--- a/tests-clar/config/multivar.c
+++ b/tests-clar/config/multivar.c
@@ -12,10 +12,12 @@ void test_config_multivar__cleanup(void)
cl_fixture_cleanup("config");
}
-static int mv_read_cb(const char *name, const char *GIT_UNUSED(value), void *data)
+static int mv_read_cb(const char *name, const char *value, void *data)
{
int *n = (int *) data;
+ GIT_UNUSED(value);
+
if (!strcmp(name, _name))
(*n)++;
@@ -35,10 +37,12 @@ void test_config_multivar__foreach(void)
git_config_free(cfg);
}
-static int cb(const char *GIT_UNUSED(val), void *data)
+static int cb(const char *val, void *data)
{
int *n = (int *) data;
+ GIT_UNUSED(val);
+
(*n)++;
return GIT_SUCCESS;
@@ -119,6 +123,8 @@ void test_config_multivar__replace(void)
n = 0;
cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n));
cl_assert(n == 2);
+
+ git_config_free(cfg);
}
void test_config_multivar__replace_multiple(void)
@@ -141,4 +147,5 @@ void test_config_multivar__replace_multiple(void)
cl_git_pass(git_config_get_multivar(cfg, _name, "otherplace", cb, &n));
cl_assert(n == 2);
+ git_config_free(cfg);
}
diff --git a/tests-clar/core/buffer.c b/tests-clar/core/buffer.c
index 740cd8578..870525b36 100644
--- a/tests-clar/core/buffer.c
+++ b/tests-clar/core/buffer.c
@@ -218,8 +218,8 @@ check_buf_append(
const char* data_a,
const char* data_b,
const char* expected_data,
- ssize_t expected_size,
- ssize_t expected_asize)
+ size_t expected_size,
+ size_t expected_asize)
{
git_buf tgt = GIT_BUF_INIT;
@@ -371,8 +371,8 @@ void test_core_buffer__7(void)
git_buf_attach(&a, b, 0);
cl_assert_strequal(fun, a.ptr);
- cl_assert(a.size == (ssize_t)strlen(fun));
- cl_assert(a.asize == (ssize_t)strlen(fun) + 1);
+ cl_assert(a.size == strlen(fun));
+ cl_assert(a.asize == strlen(fun) + 1);
git_buf_free(&a);
@@ -380,8 +380,8 @@ void test_core_buffer__7(void)
git_buf_attach(&a, b, strlen(fun) + 1);
cl_assert_strequal(fun, a.ptr);
- cl_assert(a.size == (ssize_t)strlen(fun));
- cl_assert(a.asize == (ssize_t)strlen(fun) + 1);
+ cl_assert(a.size == strlen(fun));
+ cl_assert(a.asize == strlen(fun) + 1);
git_buf_free(&a);
}
diff --git a/tests-clar/core/dirent.c b/tests-clar/core/dirent.c
index edd04471e..9c366bf97 100644
--- a/tests-clar/core/dirent.c
+++ b/tests-clar/core/dirent.c
@@ -88,10 +88,10 @@ static int one_entry(void *state, git_buf *path)
return GIT_ERROR;
}
-static int dont_call_me(void *GIT_UNUSED(state), git_buf *GIT_UNUSED(path))
+static int dont_call_me(void *state, git_buf *path)
{
- GIT_UNUSED_ARG(state)
- GIT_UNUSED_ARG(path)
+ GIT_UNUSED(state);
+ GIT_UNUSED(path);
return GIT_ERROR;
}
diff --git a/tests-clar/core/path.c b/tests-clar/core/path.c
index 3ff5d7daf..c07362f1d 100644
--- a/tests-clar/core/path.c
+++ b/tests-clar/core/path.c
@@ -243,7 +243,7 @@ void test_core_path__07_path_to_dir(void)
void test_core_path__08_self_join(void)
{
git_buf path = GIT_BUF_INIT;
- ssize_t asize = 0;
+ size_t asize = 0;
asize = path.asize;
cl_git_pass(git_buf_sets(&path, "/foo"));
diff --git a/tests-clar/diff/blob.c b/tests-clar/diff/blob.c
new file mode 100644
index 000000000..bdfe8baaf
--- /dev/null
+++ b/tests-clar/diff/blob.c
@@ -0,0 +1,91 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_blob__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("attr");
+}
+
+void test_diff_blob__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+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 = {0};
+ 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 = 1;
+
+ 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);
+}
+
diff --git a/tests-clar/diff/diff_helpers.c b/tests-clar/diff/diff_helpers.c
index b2dbe9ee7..d8eca7d9b 100644
--- a/tests-clar/diff/diff_helpers.c
+++ b/tests-clar/diff/diff_helpers.c
@@ -20,3 +20,69 @@ 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++;
+ switch (delta->status) {
+ case GIT_DELTA_ADDED: e->file_adds++; break;
+ case GIT_DELTA_DELETED: e->file_dels++; break;
+ case GIT_DELTA_MODIFIED: e->file_mods++; break;
+ case GIT_DELTA_IGNORED: e->file_ignored++; break;
+ case GIT_DELTA_UNTRACKED: e->file_untracked++; break;
+ default: break;
+ }
+ 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:
+ case GIT_DIFF_LINE_ADD_EOFNL:
+ e->line_adds++;
+ break;
+ case GIT_DIFF_LINE_DELETION:
+ case GIT_DIFF_LINE_DEL_EOFNL:
+ e->line_dels++;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
diff --git a/tests-clar/diff/diff_helpers.h b/tests-clar/diff/diff_helpers.h
index a75dd912c..010d156fa 100644
--- a/tests-clar/diff/diff_helpers.h
+++ b/tests-clar/diff/diff_helpers.h
@@ -1,4 +1,43 @@
#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 file_ignored;
+ int file_untracked;
+
+ 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);
+
diff --git a/tests-clar/diff/index.c b/tests-clar/diff/index.c
new file mode 100644
index 000000000..171815df5
--- /dev/null
+++ b/tests-clar/diff/index.c
@@ -0,0 +1,92 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_index__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("status");
+}
+
+void test_diff_index__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+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);
+}
diff --git a/tests-clar/diff/iterator.c b/tests-clar/diff/iterator.c
index d6b548ed0..3953fd83f 100644
--- a/tests-clar/diff/iterator.c
+++ b/tests-clar/diff/iterator.c
@@ -2,37 +2,6 @@
#include "diff_helpers.h"
#include "iterator.h"
-static git_repository *g_repo = NULL;
-static const char *g_sandbox = NULL;
-
-static void setup_sandbox(const char *sandbox)
-{
- cl_fixture_sandbox(sandbox);
- g_sandbox = sandbox;
-
- p_chdir(sandbox);
- cl_git_pass(p_rename(".gitted", ".git"));
- if (p_access("gitattributes", F_OK) == 0)
- cl_git_pass(p_rename("gitattributes", ".gitattributes"));
- if (p_access("gitignore", F_OK) == 0)
- cl_git_pass(p_rename("gitignore", ".gitignore"));
- p_chdir("..");
-
- cl_git_pass(git_repository_open(&g_repo, sandbox));
-}
-
-static void cleanup_sandbox(void)
-{
- if (g_repo) {
- git_repository_free(g_repo);
- g_repo = NULL;
- }
- if (g_sandbox) {
- cl_fixture_cleanup(g_sandbox);
- g_sandbox = NULL;
- }
-}
-
void test_diff_iterator__initialize(void)
{
/* since we are doing tests with different sandboxes, defer setup
@@ -44,7 +13,7 @@ void test_diff_iterator__initialize(void)
void test_diff_iterator__cleanup(void)
{
- cleanup_sandbox();
+ cl_git_sandbox_cleanup();
}
@@ -60,11 +29,10 @@ static void tree_iterator_test(
git_iterator *i;
const git_index_entry *entry;
int count = 0;
+ git_repository *repo = cl_git_sandbox_init(sandbox);
- setup_sandbox(sandbox);
-
- cl_assert(t = resolve_commit_oid_to_tree(g_repo, treeish));
- cl_git_pass(git_iterator_for_tree(g_repo, t, &i));
+ cl_assert(t = resolve_commit_oid_to_tree(repo, treeish));
+ cl_git_pass(git_iterator_for_tree(repo, t, &i));
cl_git_pass(git_iterator_current(i, &entry));
while (entry != NULL) {
@@ -183,10 +151,9 @@ static void index_iterator_test(
git_iterator *i;
const git_index_entry *entry;
int count = 0;
+ git_repository *repo = cl_git_sandbox_init(sandbox);
- setup_sandbox(sandbox);
-
- cl_git_pass(git_iterator_for_index(g_repo, &i));
+ cl_git_pass(git_iterator_for_index(repo, &i));
cl_git_pass(git_iterator_current(i, &entry));
while (entry != NULL) {
@@ -238,9 +205,9 @@ static const char *expected_index_oids_0[] = {
"5819a185d77b03325aaf87cafc771db36f6ddca7",
"ff69f8639ce2e6010b3f33a74160aad98b48da2b",
"45141a79a77842c59a63229403220a4e4be74e3d",
- "45141a79a77842c59a63229403220a4e4be74e3d",
- "45141a79a77842c59a63229403220a4e4be74e3d",
- "fb5067b1aef3ac1ada4b379dbcb7d17255df7d78",
+ "4d713dc48e6b1bd75b0d61ad078ba9ca3a56745d",
+ "108bb4e7fd7b16490dc33ff7d972151e73d7166e",
+ "fe773770c5a6cc7185580c9204b1ff18a33ff3fc",
"99eae476896f4907224978b88e5ecaa6c5bb67a9",
"3e42ffc54a663f9401cc25843d6c0e71a33e4249",
"e563cf4758f0d646f1b14b76016aa17fa9e549a4",
@@ -303,10 +270,9 @@ static void workdir_iterator_test(
git_iterator *i;
const git_index_entry *entry;
int count = 0, count_all = 0;
+ git_repository *repo = cl_git_sandbox_init(sandbox);
- setup_sandbox(sandbox);
-
- cl_git_pass(git_iterator_for_workdir(g_repo, &i));
+ cl_git_pass(git_iterator_for_workdir(repo, &i));
cl_git_pass(git_iterator_current(i, &entry));
while (entry != NULL) {
@@ -338,7 +304,7 @@ static void workdir_iterator_test(
void test_diff_iterator__workdir_0(void)
{
- workdir_iterator_test("attr", 24, 2, NULL, "ign");
+ workdir_iterator_test("attr", 25, 2, NULL, "ign");
}
static const char *status_paths[] = {
@@ -351,10 +317,10 @@ static const char *status_paths[] = {
"staged_delete_modified_file",
"staged_new_file",
"staged_new_file_modified_file",
+ "subdir.txt",
"subdir/current_file",
"subdir/modified_file",
"subdir/new_file",
- "subdir.txt",
NULL
};
diff --git a/tests-clar/diff/tree.c b/tests-clar/diff/tree.c
new file mode 100644
index 000000000..91e1343cc
--- /dev/null
+++ b/tests-clar/diff/tree.c
@@ -0,0 +1,170 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_tree__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("attr");
+}
+
+void test_diff_tree__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+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(g_repo, a_commit);
+ git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit);
+ git_tree *c = resolve_commit_oid_to_tree(g_repo, c_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_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);
+}
+
+void test_diff_tree__options(void)
+{
+ /* grabbed a couple of commit oids from the history of the attr repo */
+ const char *a_commit = "6bab5c79cd5140d0";
+ const char *b_commit = "605812ab7fe421fdd";
+ const char *c_commit = "f5b0af1fb4f5";
+ const char *d_commit = "a97cc019851";
+
+ 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_tree *c = resolve_commit_oid_to_tree(g_repo, c_commit);
+ git_tree *d = resolve_commit_oid_to_tree(g_repo, d_commit);
+
+ git_diff_options opts = {0};
+ git_diff_list *diff = NULL;
+ diff_expects actual;
+ int test_ab_or_cd[] = { 0, 0, 0, 0, 1, 1, 1, 1, 1 };
+ git_diff_options test_options[] = {
+ /* a vs b tests */
+ { GIT_DIFF_NORMAL, 1, 1, NULL, NULL, {0} },
+ { GIT_DIFF_NORMAL, 3, 1, NULL, NULL, {0} },
+ { GIT_DIFF_REVERSE, 2, 1, NULL, NULL, {0} },
+ { GIT_DIFF_FORCE_TEXT, 2, 1, NULL, NULL, {0} },
+ /* c vs d tests */
+ { GIT_DIFF_NORMAL, 3, 1, NULL, NULL, {0} },
+ { GIT_DIFF_IGNORE_WHITESPACE, 3, 1, NULL, NULL, {0} },
+ { GIT_DIFF_IGNORE_WHITESPACE_CHANGE, 3, 1, NULL, NULL, {0} },
+ { GIT_DIFF_IGNORE_WHITESPACE_EOL, 3, 1, NULL, NULL, {0} },
+ { GIT_DIFF_IGNORE_WHITESPACE | GIT_DIFF_REVERSE, 1, 1, NULL, NULL, {0} },
+ };
+ /* to generate these values:
+ * - cd to tests/resources/attr,
+ * - mv .gitted .git
+ * - git diff [options] 6bab5c79cd5140d0 605812ab7fe421fdd
+ * - mv .git .gitted
+ */
+ diff_expects test_expects[] = {
+ /* a vs b tests */
+ { 5, 3, 0, 2, 0, 0, 4, 0, 0, 51, 2, 46, 3 },
+ { 5, 3, 0, 2, 0, 0, 4, 0, 0, 53, 4, 46, 3 },
+ { 5, 0, 3, 2, 0, 0, 4, 0, 0, 52, 3, 3, 46 },
+ { 5, 3, 0, 2, 0, 0, 5, 0, 0, 54, 3, 48, 3 },
+ /* c vs d tests */
+ { 1, 0, 0, 1, 0, 0, 1, 0, 0, 22, 9, 10, 3 },
+ { 1, 0, 0, 1, 0, 0, 1, 0, 0, 19, 12, 7, 0 },
+ { 1, 0, 0, 1, 0, 0, 1, 0, 0, 20, 11, 8, 1 },
+ { 1, 0, 0, 1, 0, 0, 1, 0, 0, 20, 11, 8, 1 },
+ { 1, 0, 0, 1, 0, 0, 1, 0, 0, 18, 11, 0, 7 },
+ { 0 },
+ };
+ diff_expects *expected;
+ int i;
+
+ cl_assert(a);
+ cl_assert(b);
+
+ for (i = 0; test_expects[i].files > 0; i++) {
+ memset(&actual, 0, sizeof(actual)); /* clear accumulator */
+ opts = test_options[i];
+
+ if (test_ab_or_cd[i] == 0)
+ cl_git_pass(git_diff_tree_to_tree(g_repo, &opts, a, b, &diff));
+ else
+ cl_git_pass(git_diff_tree_to_tree(g_repo, &opts, c, d, &diff));
+
+ cl_git_pass(git_diff_foreach(
+ diff, &actual, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ expected = &test_expects[i];
+ cl_assert_intequal(actual.files, expected->files);
+ cl_assert_intequal(actual.file_adds, expected->file_adds);
+ cl_assert_intequal(actual.file_dels, expected->file_dels);
+ cl_assert_intequal(actual.file_mods, expected->file_mods);
+ cl_assert_intequal(actual.hunks, expected->hunks);
+ cl_assert_intequal(actual.lines, expected->lines);
+ cl_assert_intequal(actual.line_ctxt, expected->line_ctxt);
+ cl_assert_intequal(actual.line_adds, expected->line_adds);
+ cl_assert_intequal(actual.line_dels, expected->line_dels);
+
+ git_diff_list_free(diff);
+ diff = NULL;
+ }
+
+ git_tree_free(a);
+ git_tree_free(b);
+ git_tree_free(c);
+ git_tree_free(d);
+}
diff --git a/tests-clar/diff/workdir.c b/tests-clar/diff/workdir.c
new file mode 100644
index 000000000..9fefdbb03
--- /dev/null
+++ b/tests-clar/diff/workdir.c
@@ -0,0 +1,228 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_workdir__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("status");
+}
+
+void test_diff_workdir__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_diff_workdir__to_index(void)
+{
+ git_diff_options opts = {0};
+ git_diff_list *diff = NULL;
+ diff_expects exp;
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &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
+ * - git diff
+ * - mv .git .gitted
+ */
+ cl_assert_intequal(12, exp.files);
+ cl_assert_intequal(0, exp.file_adds);
+ cl_assert_intequal(4, exp.file_dels);
+ cl_assert_intequal(4, exp.file_mods);
+ cl_assert_intequal(1, exp.file_ignored);
+ cl_assert_intequal(3, exp.file_untracked);
+
+ cl_assert_intequal(8, exp.hunks);
+
+ cl_assert_intequal(14, exp.lines);
+ cl_assert_intequal(5, exp.line_ctxt);
+ cl_assert_intequal(4, exp.line_adds);
+ cl_assert_intequal(5, exp.line_dels);
+
+ git_diff_list_free(diff);
+}
+
+void test_diff_workdir__to_tree(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;
+ git_diff_list *diff2 = NULL;
+ diff_expects exp;
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ memset(&exp, 0, sizeof(exp));
+
+ /* You can't really generate the equivalent of git_diff_workdir_to_tree()
+ * using C git. It really wants to interpose the index into the diff.
+ *
+ * To validate the following results with command line git, I ran the
+ * following:
+ * - git ls-tree 26a125
+ * - find . ! -path ./.git/\* -a -type f | git hash-object --stdin-paths
+ * The results are documented at the bottom of this file in the
+ * long comment entitled "PREPARATION OF TEST DATA".
+ */
+ cl_git_pass(git_diff_workdir_to_tree(g_repo, &opts, a, &diff));
+
+ cl_git_pass(git_diff_foreach(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert(exp.files == 13);
+ cl_assert(exp.file_adds == 0);
+ cl_assert(exp.file_dels == 4);
+ cl_assert(exp.file_mods == 4);
+ cl_assert(exp.file_ignored == 1);
+ cl_assert(exp.file_untracked == 4);
+
+ /* Since there is no git diff equivalent, let's just assume that the
+ * text diffs produced by git_diff_foreach are accurate here. We will
+ * do more apples-to-apples test comparison below.
+ */
+
+ git_diff_list_free(diff);
+ diff = NULL;
+ memset(&exp, 0, sizeof(exp));
+
+ /* This is a compatible emulation of "git diff <sha>" which looks like
+ * a workdir to tree diff (even though it is not really). This is what
+ * you would get from "git diff --name-status 26a125ee1bf"
+ */
+ cl_git_pass(git_diff_index_to_tree(g_repo, &opts, a, &diff));
+ cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff2));
+ cl_git_pass(git_diff_merge(diff, diff2));
+ git_diff_list_free(diff2);
+
+ cl_git_pass(git_diff_foreach(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert(exp.files == 14);
+ cl_assert(exp.file_adds == 2);
+ cl_assert(exp.file_dels == 5);
+ cl_assert(exp.file_mods == 4);
+ cl_assert(exp.file_ignored == 1);
+ cl_assert(exp.file_untracked == 2);
+
+ cl_assert(exp.hunks == 11);
+
+ cl_assert(exp.lines == 17);
+ cl_assert(exp.line_ctxt == 4);
+ cl_assert(exp.line_adds == 8);
+ cl_assert(exp.line_dels == 5);
+
+ git_diff_list_free(diff);
+ diff = NULL;
+ memset(&exp, 0, sizeof(exp));
+
+ /* Again, emulating "git diff <sha>" for testing purposes using
+ * "git diff --name-status 0017bd4ab1ec3" instead.
+ */
+ cl_git_pass(git_diff_index_to_tree(g_repo, &opts, b, &diff));
+ cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff2));
+ cl_git_pass(git_diff_merge(diff, diff2));
+ git_diff_list_free(diff2);
+
+ cl_git_pass(git_diff_foreach(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert(exp.files == 15);
+ cl_assert(exp.file_adds == 5);
+ cl_assert(exp.file_dels == 4);
+ cl_assert(exp.file_mods == 3);
+ cl_assert(exp.file_ignored == 1);
+ cl_assert(exp.file_untracked == 2);
+
+ cl_assert(exp.hunks == 12);
+
+ cl_assert(exp.lines == 19);
+ cl_assert(exp.line_ctxt == 3);
+ cl_assert(exp.line_adds == 12);
+ cl_assert(exp.line_dels == 4);
+
+ git_diff_list_free(diff);
+
+ git_tree_free(a);
+ git_tree_free(b);
+}
+
+/* PREPARATION OF TEST DATA
+ *
+ * Since there is no command line equivalent of git_diff_workdir_to_tree,
+ * it was a bit of a pain to confirm that I was getting the expected
+ * results in the first part of this tests. Here is what I ended up
+ * doing to set my expectation for the file counts and results:
+ *
+ * Running "git ls-tree 26a125" and "git ls-tree aa27a6" shows:
+ *
+ * A a0de7e0ac200c489c41c59dfa910154a70264e6e current_file
+ * B 5452d32f1dd538eb0405e8a83cc185f79e25e80f file_deleted
+ * C 452e4244b5d083ddf0460acf1ecc74db9dcfa11a modified_file
+ * D 32504b727382542f9f089e24fddac5e78533e96c staged_changes
+ * E 061d42a44cacde5726057b67558821d95db96f19 staged_changes_file_deleted
+ * F 70bd9443ada07063e7fbf0b3ff5c13f7494d89c2 staged_changes_modified_file
+ * G e9b9107f290627c04d097733a10055af941f6bca staged_delete_file_deleted
+ * H dabc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 staged_delete_modified_file
+ * I 53ace0d1cc1145a5f4fe4f78a186a60263190733 subdir/current_file
+ * J 1888c805345ba265b0ee9449b8877b6064592058 subdir/deleted_file
+ * K a6191982709b746d5650e93c2acf34ef74e11504 subdir/modified_file
+ * L e8ee89e15bbe9b20137715232387b3de5b28972e subdir.txt
+ *
+ * --------
+ *
+ * find . ! -path ./.git/\* -a -type f | git hash-object --stdin-paths
+ *
+ * A a0de7e0ac200c489c41c59dfa910154a70264e6e current_file
+ * M 6a79f808a9c6bc9531ac726c184bbcd9351ccf11 ignored_file
+ * C 0a539630525aca2e7bc84975958f92f10a64c9b6 modified_file
+ * N d4fa8600b4f37d7516bef4816ae2c64dbf029e3a new_file
+ * D 55d316c9ba708999f1918e9677d01dfcae69c6b9 staged_changes
+ * F 011c3440d5c596e21d836aa6d7b10eb581f68c49 staged_changes_modified_file
+ * H dabc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 staged_delete_modified_file
+ * O 529a16e8e762d4acb7b9636ff540a00831f9155a staged_new_file
+ * P 8b090c06d14ffa09c4e880088ebad33893f921d1 staged_new_file_modified_file
+ * I 53ace0d1cc1145a5f4fe4f78a186a60263190733 subdir/current_file
+ * K 57274b75eeb5f36fd55527806d567b2240a20c57 subdir/modified_file
+ * Q 80a86a6931b91bc01c2dbf5ca55bdd24ad1ef466 subdir/new_file
+ * L e8ee89e15bbe9b20137715232387b3de5b28972e subdir.txt
+ *
+ * --------
+ *
+ * A - current_file (UNMODIFIED) -> not in results
+ * B D file_deleted
+ * M I ignored_file (IGNORED)
+ * C M modified_file
+ * N U new_file (UNTRACKED)
+ * D M staged_changes
+ * E D staged_changes_file_deleted
+ * F M staged_changes_modified_file
+ * G D staged_delete_file_deleted
+ * H - staged_delete_modified_file (UNMODIFIED) -> not in results
+ * O U staged_new_file
+ * P U staged_new_file_modified_file
+ * I - subdir/current_file (UNMODIFIED) -> not in results
+ * J D subdir/deleted_file
+ * K M subdir/modified_file
+ * Q U subdir/new_file
+ * L - subdir.txt (UNMODIFIED) -> not in results
+ *
+ * Expect 13 files, 0 ADD, 4 DEL, 4 MOD, 1 IGN, 4 UNTR
+ */
diff --git a/tests-clar/object/blob/filter.c b/tests-clar/object/blob/filter.c
new file mode 100644
index 000000000..0b87b2b46
--- /dev/null
+++ b/tests-clar/object/blob/filter.c
@@ -0,0 +1,125 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "blob.h"
+#include "filter.h"
+
+static git_repository *g_repo = NULL;
+#define NUM_TEST_OBJECTS 6
+static git_oid g_oids[NUM_TEST_OBJECTS];
+static const char *g_raw[NUM_TEST_OBJECTS] = {
+ "",
+ "foo\nbar\n",
+ "foo\rbar\r",
+ "foo\r\nbar\r\n",
+ "foo\nbar\rboth\r\nreversed\n\ragain\nproblems\r",
+ "123\n\000\001\002\003\004abc\255\254\253\r\n"
+};
+static int g_len[NUM_TEST_OBJECTS] = { -1, -1, -1, -1, -1, 17 };
+static git_text_stats g_stats[NUM_TEST_OBJECTS] = {
+ { 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 2, 0, 6, 0 },
+ { 0, 2, 0, 0, 6, 0 },
+ { 0, 2, 2, 2, 6, 0 },
+ { 0, 4, 4, 1, 31, 0 },
+ { 1, 1, 2, 1, 9, 5 }
+};
+static git_buf g_crlf_filtered[NUM_TEST_OBJECTS] = {
+ { "", 0, 0 },
+ { "foo\nbar\n", 0, 8 },
+ { "foo\rbar\r", 0, 8 },
+ { "foo\nbar\n", 0, 8 },
+ { "foo\nbar\rboth\nreversed\n\ragain\nproblems\r", 0, 38 },
+ { "123\n\000\001\002\003\004abc\255\254\253\n", 0, 16 }
+};
+
+void test_object_blob_filter__initialize(void)
+{
+ int i;
+
+ cl_fixture_sandbox("empty_standard_repo");
+ cl_git_pass(p_rename(
+ "empty_standard_repo/.gitted", "empty_standard_repo/.git"));
+ cl_git_pass(git_repository_open(&g_repo, "empty_standard_repo"));
+
+ for (i = 0; i < NUM_TEST_OBJECTS; i++) {
+ size_t len = (g_len[i] < 0) ? strlen(g_raw[i]) : (size_t)g_len[i];
+ g_len[i] = (int)len;
+
+ cl_git_pass(
+ git_blob_create_frombuffer(&g_oids[i], g_repo, g_raw[i], len)
+ );
+ }
+}
+
+void test_object_blob_filter__cleanup(void)
+{
+ git_repository_free(g_repo);
+ g_repo = NULL;
+ cl_fixture_cleanup("empty_standard_repo");
+}
+
+void test_object_blob_filter__unfiltered(void)
+{
+ int i;
+ git_blob *blob;
+
+ for (i = 0; i < NUM_TEST_OBJECTS; i++) {
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &g_oids[i]));
+ cl_assert((size_t)g_len[i] == git_blob_rawsize(blob));
+ cl_assert(memcmp(git_blob_rawcontent(blob), g_raw[i], g_len[i]) == 0);
+ git_blob_free(blob);
+ }
+}
+
+void test_object_blob_filter__stats(void)
+{
+ int i;
+ git_blob *blob;
+ git_buf buf = GIT_BUF_INIT;
+ git_text_stats stats;
+
+ for (i = 0; i < NUM_TEST_OBJECTS; i++) {
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &g_oids[i]));
+ cl_git_pass(git_blob__getbuf(&buf, blob));
+ git_text_gather_stats(&stats, &buf);
+ cl_assert(memcmp(&g_stats[i], &stats, sizeof(stats)) == 0);
+ git_blob_free(blob);
+ }
+
+ git_buf_free(&buf);
+}
+
+void test_object_blob_filter__to_odb(void)
+{
+ git_vector filters = GIT_VECTOR_INIT;
+ git_config *cfg;
+ int i;
+ git_blob *blob;
+ git_buf orig = GIT_BUF_INIT, out = GIT_BUF_INIT;
+
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_assert(cfg);
+
+ git_attr_cache_flush(g_repo);
+ cl_git_append2file("empty_standard_repo/.gitattributes", "*.txt text\n");
+
+ cl_assert(git_filters_load(
+ &filters, g_repo, "filename.txt", GIT_FILTER_TO_ODB) > 0);
+ cl_assert(filters.length == 1);
+
+ for (i = 0; i < NUM_TEST_OBJECTS; i++) {
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &g_oids[i]));
+ cl_git_pass(git_blob__getbuf(&orig, blob));
+
+ cl_git_pass(git_filters_apply(&out, &orig, &filters));
+ cl_assert(git_buf_cmp(&out, &g_crlf_filtered[i]) == 0);
+
+ git_blob_free(blob);
+ }
+
+ git_filters_free(&filters);
+ git_buf_free(&orig);
+ git_buf_free(&out);
+ git_config_free(cfg);
+}
+
diff --git a/tests-clar/status/ignore.c b/tests-clar/status/ignore.c
index 67aecba31..99cb9e8b8 100644
--- a/tests-clar/status/ignore.c
+++ b/tests-clar/status/ignore.c
@@ -7,21 +7,12 @@ static git_repository *g_repo = NULL;
void test_status_ignore__initialize(void)
{
- /* Before each test, instantiate the attr repo from the fixtures and
- * rename the .gitted to .git so it is a repo with a working dir. Also
- * rename gitignore to .gitignore.
- */
- cl_fixture_sandbox("attr");
- cl_git_pass(p_rename("attr/.gitted", "attr/.git"));
- cl_git_pass(p_rename("attr/gitignore", "attr/.gitignore"));
- cl_git_pass(git_repository_open(&g_repo, "attr/.git"));
+ g_repo = cl_git_sandbox_init("attr");
}
void test_status_ignore__cleanup(void)
{
- git_repository_free(g_repo);
- g_repo = NULL;
- cl_fixture_cleanup("attr");
+ cl_git_sandbox_cleanup();
}
void test_status_ignore__0(void)
diff --git a/tests-clar/status/worktree.c b/tests-clar/status/worktree.c
index d8e62a94d..132ec1fc1 100644
--- a/tests-clar/status/worktree.c
+++ b/tests-clar/status/worktree.c
@@ -5,12 +5,6 @@
/**
- * Test fixtures
- */
-static git_repository *_repository = NULL;
-
-
-/**
* Auxiliary methods
*/
static int
@@ -37,48 +31,27 @@ exit:
}
static int
-cb_status__count(const char *GIT_UNUSED(p), unsigned int GIT_UNUSED(s), void *payload)
+cb_status__count(const char *p, unsigned int s, void *payload)
{
volatile int *count = (int *)payload;
- GIT_UNUSED_ARG(p);
- GIT_UNUSED_ARG(s);
+ GIT_UNUSED(p);
+ GIT_UNUSED(s);
- *count++;
+ (*count)++;
return GIT_SUCCESS;
}
-
/**
* Initializer
*
- * This method is called once before starting each
- * test, and will load the required fixtures
+ * Not all of the tests in this file use the same fixtures, so we allow each
+ * test to load their fixture at the top of the test function.
*/
void test_status_worktree__initialize(void)
{
- /*
- * Sandbox the `status/` repository from our Fixtures.
- * This will copy the whole folder to our sandbox,
- * so now it can be accessed with `./status`
- */
- cl_fixture_sandbox("status");
-
- /*
- * Rename `status/.gitted` to `status/.git`
- * We do this because we cannot store a folder named `.git`
- * inside the fixtures folder in our libgit2 repo.
- */
- cl_git_pass(
- p_rename("status/.gitted", "status/.git")
- );
-
- /*
- * Open the sandboxed "status" repository
- */
- cl_git_pass(git_repository_open(&_repository, "status/.git"));
}
/**
@@ -89,10 +62,7 @@ void test_status_worktree__initialize(void)
*/
void test_status_worktree__cleanup(void)
{
- git_repository_free(_repository);
- _repository = NULL;
-
- cl_fixture_cleanup("status");
+ cl_git_sandbox_cleanup();
}
/**
@@ -101,6 +71,7 @@ void test_status_worktree__cleanup(void)
void test_status_worktree__whole_repository(void)
{
struct status_entry_counts counts;
+ git_repository *repo = cl_git_sandbox_init("status");
memset(&counts, 0x0, sizeof(struct status_entry_counts));
counts.expected_entry_count = entry_count0;
@@ -108,7 +79,7 @@ void test_status_worktree__whole_repository(void)
counts.expected_statuses = entry_statuses0;
cl_git_pass(
- git_status_foreach(_repository, cb_status__normal, &counts)
+ git_status_foreach(repo, cb_status__normal, &counts)
);
cl_assert(counts.entry_count == counts.expected_entry_count);
@@ -119,8 +90,10 @@ void test_status_worktree__whole_repository(void)
void test_status_worktree__empty_repository(void)
{
int count = 0;
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_pass(git_status_foreach(repo, cb_status__count, &count));
- git_status_foreach(_repository, cb_status__count, &count);
cl_assert(count == 0);
}
@@ -128,10 +101,11 @@ void test_status_worktree__single_file(void)
{
int i;
unsigned int status_flags;
+ git_repository *repo = cl_git_sandbox_init("status");
for (i = 0; i < (int)entry_count0; i++) {
cl_git_pass(
- git_status_file(&status_flags, _repository, entry_paths0[i])
+ git_status_file(&status_flags, repo, entry_paths0[i])
);
cl_assert(entry_statuses0[i] == status_flags);
}
@@ -140,15 +114,22 @@ void test_status_worktree__single_file(void)
void test_status_worktree__ignores(void)
{
int i, ignored;
+ git_repository *repo = cl_git_sandbox_init("status");
for (i = 0; i < (int)entry_count0; i++) {
- cl_git_pass(git_status_should_ignore(_repository, entry_paths0[i], &ignored));
+ cl_git_pass(
+ git_status_should_ignore(repo, entry_paths0[i], &ignored)
+ );
cl_assert(ignored == (entry_statuses0[i] == GIT_STATUS_IGNORED));
}
- cl_git_pass(git_status_should_ignore(_repository, "nonexistent_file", &ignored));
+ cl_git_pass(
+ git_status_should_ignore(repo, "nonexistent_file", &ignored)
+ );
cl_assert(!ignored);
- cl_git_pass(git_status_should_ignore(_repository, "ignored_nonexistent_file", &ignored));
+ cl_git_pass(
+ git_status_should_ignore(repo, "ignored_nonexistent_file", &ignored)
+ );
cl_assert(ignored);
}
diff --git a/tests/resources/attr/.gitted/index b/tests/resources/attr/.gitted/index
index c52747e0b..19fa99d5b 100644
--- a/tests/resources/attr/.gitted/index
+++ b/tests/resources/attr/.gitted/index
Binary files differ
diff --git a/tests/resources/attr/.gitted/logs/HEAD b/tests/resources/attr/.gitted/logs/HEAD
index f518a465a..68fcff2c5 100644
--- a/tests/resources/attr/.gitted/logs/HEAD
+++ b/tests/resources/attr/.gitted/logs/HEAD
@@ -1,3 +1,6 @@
0000000000000000000000000000000000000000 6bab5c79cd5140d0f800917f550eb2a3dc32b0da Russell Belfer <arrbee@arrbee.com> 1324416995 -0800 commit (initial): initial test data
6bab5c79cd5140d0f800917f550eb2a3dc32b0da 605812ab7fe421fdd325a935d35cb06a9234a7d7 Russell Belfer <arrbee@arrbee.com> 1325143098 -0800 commit: latest test updates
605812ab7fe421fdd325a935d35cb06a9234a7d7 a5d76cad53f66f1312bd995909a5bab3c0820770 Russell Belfer <arrbee@arrbee.com> 1325281762 -0800 commit: more macro tests
+a5d76cad53f66f1312bd995909a5bab3c0820770 370fe9ec224ce33e71f9e5ec2bd1142ce9937a6a Russell Belfer <arrbee@arrbee.com> 1327611749 -0800 commit: Updating files so we can do diffs
+370fe9ec224ce33e71f9e5ec2bd1142ce9937a6a f5b0af1fb4f5c0cd7aad880711d368a07333c307 Russell Belfer <arrbee@arrbee.com> 1327621027 -0800 commit: Updating test data
+f5b0af1fb4f5c0cd7aad880711d368a07333c307 a97cc019851d401a4f1d091cb91a15890a0dd1ba Russell Belfer <arrbee@arrbee.com> 1328653313 -0800 commit: Some whitespace only changes for testing purposes
diff --git a/tests/resources/attr/.gitted/logs/refs/heads/master b/tests/resources/attr/.gitted/logs/refs/heads/master
index f518a465a..68fcff2c5 100644
--- a/tests/resources/attr/.gitted/logs/refs/heads/master
+++ b/tests/resources/attr/.gitted/logs/refs/heads/master
@@ -1,3 +1,6 @@
0000000000000000000000000000000000000000 6bab5c79cd5140d0f800917f550eb2a3dc32b0da Russell Belfer <arrbee@arrbee.com> 1324416995 -0800 commit (initial): initial test data
6bab5c79cd5140d0f800917f550eb2a3dc32b0da 605812ab7fe421fdd325a935d35cb06a9234a7d7 Russell Belfer <arrbee@arrbee.com> 1325143098 -0800 commit: latest test updates
605812ab7fe421fdd325a935d35cb06a9234a7d7 a5d76cad53f66f1312bd995909a5bab3c0820770 Russell Belfer <arrbee@arrbee.com> 1325281762 -0800 commit: more macro tests
+a5d76cad53f66f1312bd995909a5bab3c0820770 370fe9ec224ce33e71f9e5ec2bd1142ce9937a6a Russell Belfer <arrbee@arrbee.com> 1327611749 -0800 commit: Updating files so we can do diffs
+370fe9ec224ce33e71f9e5ec2bd1142ce9937a6a f5b0af1fb4f5c0cd7aad880711d368a07333c307 Russell Belfer <arrbee@arrbee.com> 1327621027 -0800 commit: Updating test data
+f5b0af1fb4f5c0cd7aad880711d368a07333c307 a97cc019851d401a4f1d091cb91a15890a0dd1ba Russell Belfer <arrbee@arrbee.com> 1328653313 -0800 commit: Some whitespace only changes for testing purposes
diff --git a/tests/resources/attr/.gitted/objects/10/8bb4e7fd7b16490dc33ff7d972151e73d7166e b/tests/resources/attr/.gitted/objects/10/8bb4e7fd7b16490dc33ff7d972151e73d7166e
new file mode 100644
index 000000000..edcf7520c
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/10/8bb4e7fd7b16490dc33ff7d972151e73d7166e
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/37/0fe9ec224ce33e71f9e5ec2bd1142ce9937a6a b/tests/resources/attr/.gitted/objects/37/0fe9ec224ce33e71f9e5ec2bd1142ce9937a6a
new file mode 100644
index 000000000..9c37c5946
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/37/0fe9ec224ce33e71f9e5ec2bd1142ce9937a6a
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/3a/6df026462ebafe455af9867d27eda20a9e0974 b/tests/resources/attr/.gitted/objects/3a/6df026462ebafe455af9867d27eda20a9e0974
new file mode 100644
index 000000000..c74add826
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/3a/6df026462ebafe455af9867d27eda20a9e0974
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/4d/713dc48e6b1bd75b0d61ad078ba9ca3a56745d b/tests/resources/attr/.gitted/objects/4d/713dc48e6b1bd75b0d61ad078ba9ca3a56745d
new file mode 100644
index 000000000..eb1e8d0c5
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/4d/713dc48e6b1bd75b0d61ad078ba9ca3a56745d
@@ -0,0 +1,2 @@
+x @WŶ
+|k 9n$}g:;51e4\k_]ރ٭hDk'~ \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/6d/968d62c89c7d9ea23a4c9a7b665d017c3d8ffd b/tests/resources/attr/.gitted/objects/6d/968d62c89c7d9ea23a4c9a7b665d017c3d8ffd
new file mode 100644
index 000000000..e832241c9
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/6d/968d62c89c7d9ea23a4c9a7b665d017c3d8ffd
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/71/7fc31f6b84f9d6fc3a4edbca259d7fc92beee2 b/tests/resources/attr/.gitted/objects/71/7fc31f6b84f9d6fc3a4edbca259d7fc92beee2
new file mode 100644
index 000000000..a80265cac
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/71/7fc31f6b84f9d6fc3a4edbca259d7fc92beee2
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/96/089fd31ce1d3ee2afb0ba09ba063066932f027 b/tests/resources/attr/.gitted/objects/96/089fd31ce1d3ee2afb0ba09ba063066932f027
new file mode 100644
index 000000000..efa62f912
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/96/089fd31ce1d3ee2afb0ba09ba063066932f027
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/a9/7cc019851d401a4f1d091cb91a15890a0dd1ba b/tests/resources/attr/.gitted/objects/a9/7cc019851d401a4f1d091cb91a15890a0dd1ba
new file mode 100644
index 000000000..1a7ec0c55
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/a9/7cc019851d401a4f1d091cb91a15890a0dd1ba
@@ -0,0 +1,2 @@
+xQj0 DS[hc;PJ( $q޾ޠ_3oIK+BtI|Lgƈ ŐR4'=qFN64
+J1FrzW[rV6-i7.eVW;X, mwl|]ṬMɢdRwC[W9sj~Wy \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/c9/6bbb2c2557a8325ae1559e3ba79cdcecb23076 b/tests/resources/attr/.gitted/objects/c9/6bbb2c2557a8325ae1559e3ba79cdcecb23076
new file mode 100644
index 000000000..589f9ad31
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/c9/6bbb2c2557a8325ae1559e3ba79cdcecb23076
@@ -0,0 +1,2 @@
+x5A
+0D]SεouJ~L0ͯ)xcfp]OOΊcB 6!뢘ó{,U<Cj[E--&#)~=;;{.e"3A \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/f5/b0af1fb4f5c0cd7aad880711d368a07333c307 b/tests/resources/attr/.gitted/objects/f5/b0af1fb4f5c0cd7aad880711d368a07333c307
new file mode 100644
index 000000000..21faeb8a2
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/f5/b0af1fb4f5c0cd7aad880711d368a07333c307
@@ -0,0 +1,2 @@
+xN[j1 ̷O 4RbPJ
+=;N A?y 1y~7Z(2be8uJanF.H" UD_HIsvZwL=0TZG_UbKo̮}cv?h<aoԵ_EK \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/fe/773770c5a6cc7185580c9204b1ff18a33ff3fc b/tests/resources/attr/.gitted/objects/fe/773770c5a6cc7185580c9204b1ff18a33ff3fc
new file mode 100644
index 000000000..e6fcbc0b3
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/fe/773770c5a6cc7185580c9204b1ff18a33ff3fc
@@ -0,0 +1 @@
+x5A0 9xBAGvդTУ坝<#1UT釛*MWlOCR2dѵC.TIlQH/mY۬UN[߬B@t8~}}R#č#kAdD_=-H \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/refs/heads/master b/tests/resources/attr/.gitted/refs/heads/master
index 0516af2d2..7f8bbe3e7 100644
--- a/tests/resources/attr/.gitted/refs/heads/master
+++ b/tests/resources/attr/.gitted/refs/heads/master
@@ -1 +1 @@
-a5d76cad53f66f1312bd995909a5bab3c0820770
+a97cc019851d401a4f1d091cb91a15890a0dd1ba
diff --git a/tests/resources/attr/gitattributes b/tests/resources/attr/gitattributes
index c0c2a56d0..e038983ec 100644
--- a/tests/resources/attr/gitattributes
+++ b/tests/resources/attr/gitattributes
@@ -23,3 +23,7 @@ macro* macro2 macro2 macro2
[attr]thirdmacro secondmacro=hahaha
macro_bad firstmacro secondmacro thirdmacro
+
+# another test that Peff found was failing
+[attr]notest !test
+
diff --git a/tests/resources/attr/root_test2 b/tests/resources/attr/root_test2
index 45141a79a..4d713dc48 100644
--- a/tests/resources/attr/root_test2
+++ b/tests/resources/attr/root_test2
@@ -1 +1,6 @@
Hello from the root
+
+Some additional lines
+
+Down here below
+
diff --git a/tests/resources/attr/root_test3 b/tests/resources/attr/root_test3
index 45141a79a..108bb4e7f 100644
--- a/tests/resources/attr/root_test3
+++ b/tests/resources/attr/root_test3
@@ -1 +1,19 @@
-Hello from the root
+Some additional lines
+
+
+ Down here below the other lines
+
+
+With even more at the end
+
+
+And lots of good stuff
+
+
+Anywhere you want
+
+
+Don't you think
+
+
+
diff --git a/tests/resources/attr/root_test4.txt b/tests/resources/attr/root_test4.txt
index fb5067b1a..fe773770c 100644
--- a/tests/resources/attr/root_test4.txt
+++ b/tests/resources/attr/root_test4.txt
@@ -1 +1,14 @@
-Hello again
+Here is some stuff at the start
+
+This should go in one hunk
+
+Some additional lines
+
+Down here below the other lines
+
+With even more at the end
+
+Followed by a second hunk of stuff
+
+That happens down here
+
diff --git a/tests/resources/attr/sub/sub/.gitattributes b/tests/resources/attr/sub/sub/.gitattributes
new file mode 100644
index 000000000..55225e4d6
--- /dev/null
+++ b/tests/resources/attr/sub/sub/.gitattributes
@@ -0,0 +1,3 @@
+d/* test=a/b/d/*
+d/yes notest
+
diff --git a/tests/resources/status/.gitted/index b/tests/resources/status/.gitted/index
index d793791c9..9a383ec0c 100644
--- a/tests/resources/status/.gitted/index
+++ b/tests/resources/status/.gitted/index
Binary files differ
diff --git a/tests/t00-core.c b/tests/t00-core.c
index 58f048af6..7f142ba21 100644
--- a/tests/t00-core.c
+++ b/tests/t00-core.c
@@ -424,10 +424,10 @@ static walk_data empty = {
GIT_BUF_INIT
};
-static int dont_call_me(void *GIT_UNUSED(state), git_buf *GIT_UNUSED(path))
+static int dont_call_me(void *state, git_buf *path)
{
- GIT_UNUSED_ARG(state)
- GIT_UNUSED_ARG(path)
+ GIT_UNUSED(state);
+ GIT_UNUSED(path);
return GIT_ERROR;
}
diff --git a/tests/t07-hashtable.c b/tests/t07-hashtable.c
index 41d52af19..6beaeac68 100644
--- a/tests/t07-hashtable.c
+++ b/tests/t07-hashtable.c
@@ -154,7 +154,6 @@ BEGIN_TEST(tableit0, "iterate through all the contents of the table")
const int objects_n = 32;
int i;
table_item *objects, *ob;
- const void *GIT_UNUSED(_unused);
git_hashtable *table = NULL;
@@ -170,9 +169,7 @@ BEGIN_TEST(tableit0, "iterate through all the contents of the table")
must_pass(git_hashtable_insert(table, &(objects[i].id), &(objects[i])));
}
- GIT_HASHTABLE_FOREACH(table, _unused, ob,
- ob->visited = 1;
- );
+ GIT_HASHTABLE_FOREACH_VALUE(table, ob, ob->visited = 1);
/* make sure all nodes have been visited */
for (i = 0; i < objects_n; ++i)
diff --git a/tests/t18-status.c b/tests/t18-status.c
index 270aa7b46..2b90ac6f4 100644
--- a/tests/t18-status.c
+++ b/tests/t18-status.c
@@ -156,14 +156,14 @@ BEGIN_TEST(statuscb0, "test retrieving status for worktree of repository")
git_futils_rmdir_r(TEMP_REPO_FOLDER, 1);
END_TEST
-static int status_cb1(const char *GIT_UNUSED(path), unsigned int GIT_UNUSED(status_flags), void *payload)
+static int status_cb1(const char *path, unsigned int status_flags, void *payload)
{
int *count = (int *)payload;;
- GIT_UNUSED_ARG(path);
- GIT_UNUSED_ARG(status_flags);
+ GIT_UNUSED(path);
+ GIT_UNUSED(status_flags);
- (void) *count++;
+ (*count)++;
return GIT_SUCCESS;
}
diff --git a/tests/test_helpers.c b/tests/test_helpers.c
index 42c8031cd..837358453 100644
--- a/tests/test_helpers.c
+++ b/tests/test_helpers.c
@@ -182,7 +182,7 @@ int cmp_objects(git_rawobj *o, object_data *d)
int copy_file(const char *src, const char *dst)
{
- git_fbuffer source_buf;
+ git_buf source_buf = GIT_BUF_INIT;
git_file dst_fd;
int error = GIT_ERROR;
@@ -193,10 +193,10 @@ int copy_file(const char *src, const char *dst)
if (dst_fd < 0)
goto cleanup;
- error = p_write(dst_fd, source_buf.data, source_buf.len);
+ error = p_write(dst_fd, source_buf.ptr, source_buf.size);
cleanup:
- git_futils_freebuffer(&source_buf);
+ git_buf_free(&source_buf);
p_close(dst_fd);
return error;
@@ -204,22 +204,23 @@ cleanup:
int cmp_files(const char *a, const char *b)
{
- git_fbuffer buf_a, buf_b;
+ git_buf buf_a = GIT_BUF_INIT;
+ git_buf buf_b = GIT_BUF_INIT;
int error = GIT_ERROR;
if (git_futils_readbuffer(&buf_a, a) < GIT_SUCCESS)
return GIT_ERROR;
if (git_futils_readbuffer(&buf_b, b) < GIT_SUCCESS) {
- git_futils_freebuffer(&buf_a);
+ git_buf_free(&buf_a);
return GIT_ERROR;
}
- if (buf_a.len == buf_b.len && !memcmp(buf_a.data, buf_b.data, buf_a.len))
+ if (buf_a.size == buf_b.size && !memcmp(buf_a.ptr, buf_b.ptr, buf_a.size))
error = GIT_SUCCESS;
- git_futils_freebuffer(&buf_a);
- git_futils_freebuffer(&buf_b);
+ git_buf_free(&buf_a);
+ git_buf_free(&buf_b);
return error;
}
diff --git a/tests/test_main.c b/tests/test_main.c
index 732d25a9d..50256e97c 100644
--- a/tests/test_main.c
+++ b/tests/test_main.c
@@ -70,12 +70,12 @@ int __cdecl
#else
int
#endif
-main(int GIT_UNUSED(argc), char *GIT_UNUSED(argv[]))
+main(int argc, char *argv[])
{
unsigned int i, failures;
- GIT_UNUSED_ARG(argc);
- GIT_UNUSED_ARG(argv);
+ GIT_UNUSED(argc);
+ GIT_UNUSED(argv);
git_threads_init();