Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/libgit2.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/diff-internals.md89
-rw-r--r--src/blob.c1
-rw-r--r--src/checkout.c1
-rw-r--r--src/clone.c1
-rw-r--r--src/crlf.c6
-rw-r--r--src/diff.c72
-rw-r--r--src/diff.h11
-rw-r--r--src/diff_driver.c160
-rw-r--r--src/diff_driver.h34
-rw-r--r--src/diff_file.c442
-rw-r--r--src/diff_file.h54
-rw-r--r--src/diff_output.c1526
-rw-r--r--src/diff_output.h93
-rw-r--r--src/diff_patch.c723
-rw-r--r--src/diff_patch.h75
-rw-r--r--src/diff_print.c10
-rw-r--r--src/diff_tform.c6
-rw-r--r--src/diff_xdiff.c161
-rw-r--r--src/diff_xdiff.h28
-rw-r--r--src/fetch.c2
-rw-r--r--src/iterator.c1
-rw-r--r--src/merge.c2
-rw-r--r--src/refdb_fs.c1
-rw-r--r--src/refs.c1
-rw-r--r--src/remote.h2
-rw-r--r--src/repository.c4
-rw-r--r--src/repository.h5
-rw-r--r--src/signature.c1
-rw-r--r--src/stash.c1
-rw-r--r--src/status.c2
-rw-r--r--src/submodule.c2
-rw-r--r--src/thread-utils.h2
-rw-r--r--src/tree.c3
-rw-r--r--src/util.h2
-rw-r--r--tests-clar/checkout/index.c1
-rw-r--r--tests-clar/clar.c36
-rw-r--r--tests-clar/clone/nonetwork.c3
-rw-r--r--tests-clar/diff/patch.c2
-rw-r--r--tests-clar/diff/rename.c2
-rw-r--r--tests-clar/diff/submodules.c1
-rw-r--r--tests-clar/fetchhead/nonetwork.c2
-rw-r--r--tests-clar/merge/merge_helpers.c2
-rw-r--r--tests-clar/odb/alternates.c2
-rw-r--r--tests-clar/online/clone.c3
-rw-r--r--tests-clar/online/fetchhead.c2
-rw-r--r--tests-clar/refs/delete.c5
-rw-r--r--tests-clar/refs/pack.c10
-rw-r--r--tests-clar/refs/reflog/reflog.c2
-rw-r--r--tests-clar/refs/rename.c13
-rw-r--r--tests-clar/repo/discover.c2
-rw-r--r--tests-clar/status/ignore.c2
-rw-r--r--tests-clar/status/worktree.c2
52 files changed, 1951 insertions, 1665 deletions
diff --git a/docs/diff-internals.md b/docs/diff-internals.md
new file mode 100644
index 000000000..1983b7939
--- /dev/null
+++ b/docs/diff-internals.md
@@ -0,0 +1,89 @@
+Diff is broken into four phases:
+
+1. Building a list of things that have changed. These changes are called
+ deltas (git_diff_delta objects) and are grouped into a git_diff_list.
+2. Applying file similarity measurement for rename and copy detection (and
+ to potentially split files that have changed radically). This step is
+ optional.
+3. Computing the textual diff for each delta. Not all deltas have a
+ meaningful textual diff. For those that do, the textual diff can
+ either be generated on the fly and passed to output callbacks or can be
+ turned into a git_diff_patch object.
+4. Formatting the diff and/or patch into standard text formats (such as
+ patches, raw lists, etc).
+
+In the source code, step 1 is implemented in `src/diff.c`, step 2 in
+`src/diff_tform.c`, step 3 in `src/diff_patch.c`, and step 4 in
+`src/diff_print.c`. Additionally, when it comes to accessing file
+content, everything goes through diff drivers that are implemented in
+`src/diff_driver.c`.
+
+External Objects
+----------------
+
+* `git_diff_options` repesents user choices about how a diff should be
+ performed and is passed to most diff generating functions.
+* `git_diff_file` represents an item on one side of a possible delta
+* `git_diff_delta` represents a pair of items that have changed in some
+ way - it contains two `git_diff_file` plus a status and other stuff.
+* `git_diff_list` is a list of deltas along with information about how
+ those particular deltas were found.
+* `git_diff_patch` represents the actual diff between a pair of items. In
+ some cases, a delta may not have a corresponding patch, if the objects
+ are binary, for example. The content of a patch will be a set of hunks
+ and lines.
+* A `hunk` is range of lines described by a `git_diff_range` (i.e. "lines
+ 10-20 in the old file became lines 12-23 in the new"). It will have a
+ header that compactly represents that information, and it will have a
+ number of lines of context surrounding added and deleted lines.
+* A `line` is simple a line of data along with a `git_diff_line_t` value
+ that tells how the data should be interpretted (e.g. context or added).
+
+Internal Objects
+----------------
+
+* `git_diff_file_content` is an internal structure that represents the
+ data on one side of an item to be diffed; it is an augmented
+ `git_diff_file` with more flags and the actual file data.
+** it is created from a repository plus a) a git_diff_file, b) a git_blob,
+ or c) raw data and size
+** there are three main operations on git_diff_file_content:
+*** _initialization_ sets up the data structure and does what it can up to,
+ but not including loading and looking at the actual data
+*** _loading_ loads the data, preprocesses it (i.e. applies filters) and
+ potentially analyzes it (to decide if binary)
+*** _free_ releases loaded data and frees any allocated memory
+
+* The internal structure of a `git_diff_patch` stores the actual diff
+ between a pair of `git_diff_file_content` items
+** it may be "unset" if the items are not diffable
+** "empty" if the items are the same
+** otherwise it will consist of a set of hunks each of which covers some
+ number of lines of context, additions and deletions
+** a patch is created from two git_diff_file_content items
+** a patch is fully instantiated in three phases:
+*** initial creation and initialization
+*** loading of data and preliminary data examination
+*** diffing of data and optional storage of diffs
+** (TBD) if a patch is asked to store the diffs and the size of the diff
+ is significantly smaller than the raw data of the two sides, then the
+ patch may be flattened using a pool of string data
+
+* `git_diff_output` is an internal structure that represents an output
+ target for a `git_diff_patch`
+** It consists of file, hunk, and line callbacks, plus a payload
+** There is a standard flattened output that can be used for plain text output
+** Typically we use a `git_xdiff_output` which drives the callbacks via the
+ xdiff code taken from core Git.
+
+* `git_diff_driver` is an internal structure that encapsulates the logic
+ for a given type of file
+** a driver is looked up based on the name and mode of a file.
+** the driver can then be used to:
+*** determine if a file is binary (by attributes, by git_diff_options
+ settings, or by examining the content)
+*** give you a function pointer that is used to evaluate function context
+ for hunk headers
+** At some point, the logic for getting a filtered version of file content
+ or calculating the OID of a file may be moved into the driver.
+
diff --git a/src/blob.c b/src/blob.c
index a68c4cc3e..2e4d5f479 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -11,6 +11,7 @@
#include "git2/odb_backend.h"
#include "common.h"
+#include "filebuf.h"
#include "blob.h"
#include "filter.h"
#include "buf_text.h"
diff --git a/src/checkout.c b/src/checkout.c
index 7a2e68300..ede0be8e8 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -20,6 +20,7 @@
#include "refs.h"
#include "repository.h"
+#include "index.h"
#include "filter.h"
#include "blob.h"
#include "diff.h"
diff --git a/src/clone.c b/src/clone.c
index af3298fd0..5b6c6f77d 100644
--- a/src/clone.c
+++ b/src/clone.c
@@ -21,6 +21,7 @@
#include "fileops.h"
#include "refs.h"
#include "path.h"
+#include "repository.h"
static int create_branch(
git_reference **branch,
diff --git a/src/crlf.c b/src/crlf.c
index 81268da83..65039f9cc 100644
--- a/src/crlf.c
+++ b/src/crlf.c
@@ -5,14 +5,16 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "git2/attr.h"
+#include "git2/blob.h"
+#include "git2/index.h"
+
#include "common.h"
#include "fileops.h"
#include "hash.h"
#include "filter.h"
#include "buf_text.h"
#include "repository.h"
-#include "git2/attr.h"
-#include "git2/blob.h"
struct crlf_attrs {
int crlf_action;
diff --git a/src/diff.c b/src/diff.c
index 05ef4f16b..97ccb3cbd 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -11,6 +11,8 @@
#include "attr_file.h"
#include "filter.h"
#include "pathspec.h"
+#include "index.h"
+#include "odb.h"
#define DIFF_FLAG_IS_SET(DIFF,FLAG) (((DIFF)->opts.flags & (FLAG)) != 0)
#define DIFF_FLAG_ISNT_SET(DIFF,FLAG) (((DIFF)->opts.flags & (FLAG)) == 0)
@@ -1170,3 +1172,73 @@ int git_diff_tree_to_workdir(
return error;
}
+
+size_t git_diff_num_deltas(git_diff_list *diff)
+{
+ assert(diff);
+ return (size_t)diff->deltas.length;
+}
+
+size_t git_diff_num_deltas_of_type(git_diff_list *diff, git_delta_t type)
+{
+ size_t i, count = 0;
+ git_diff_delta *delta;
+
+ assert(diff);
+
+ git_vector_foreach(&diff->deltas, i, delta) {
+ count += (delta->status == type);
+ }
+
+ return count;
+}
+
+int git_diff__paired_foreach(
+ git_diff_list *idx2head,
+ git_diff_list *wd2idx,
+ int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload),
+ void *payload)
+{
+ int cmp;
+ git_diff_delta *i2h, *w2i;
+ size_t i, j, i_max, j_max;
+ int (*strcomp)(const char *, const char *);
+
+ i_max = idx2head ? idx2head->deltas.length : 0;
+ j_max = wd2idx ? wd2idx->deltas.length : 0;
+
+ /* Get appropriate strcmp function */
+ strcomp = idx2head ? idx2head->strcomp : wd2idx ? wd2idx->strcomp : NULL;
+
+ /* Assert both iterators use matching ignore-case. If this function ever
+ * supports merging diffs that are not sorted by the same function, then
+ * it will need to spool and sort on one of the results before merging
+ */
+ if (idx2head && wd2idx) {
+ assert(idx2head->strcomp == wd2idx->strcomp);
+ }
+
+ for (i = 0, j = 0; i < i_max || j < j_max; ) {
+ i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL;
+ w2i = wd2idx ? GIT_VECTOR_GET(&wd2idx->deltas,j) : NULL;
+
+ cmp = !w2i ? -1 : !i2h ? 1 :
+ strcomp(i2h->old_file.path, w2i->old_file.path);
+
+ if (cmp < 0) {
+ if (cb(i2h, NULL, payload))
+ return GIT_EUSER;
+ i++;
+ } else if (cmp > 0) {
+ if (cb(NULL, w2i, payload))
+ return GIT_EUSER;
+ j++;
+ } else {
+ if (cb(i2h, w2i, payload))
+ return GIT_EUSER;
+ i++; j++;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/diff.h b/src/diff.h
index ac8ab2aed..ad12e7731 100644
--- a/src/diff.h
+++ b/src/diff.h
@@ -29,11 +29,16 @@ enum {
GIT_DIFFCAPS_TRUST_NANOSECS = (1 << 5), /* use stat time nanoseconds */
};
+#define DIFF_FLAGS_KNOWN_BINARY (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY)
+#define DIFF_FLAGS_NOT_BINARY (GIT_DIFF_FLAG_NOT_BINARY|GIT_DIFF_FLAG__NO_DATA)
+
enum {
GIT_DIFF_FLAG__FREE_PATH = (1 << 7), /* `path` is allocated memory */
GIT_DIFF_FLAG__FREE_DATA = (1 << 8), /* internal file data is allocated */
GIT_DIFF_FLAG__UNMAP_DATA = (1 << 9), /* internal file data is mmap'ed */
GIT_DIFF_FLAG__NO_DATA = (1 << 10), /* file data should not be loaded */
+ GIT_DIFF_FLAG__FREE_BLOB = (1 << 11), /* release the blob when done */
+ GIT_DIFF_FLAG__LOADED = (1 << 12), /* file data has been loaded */
GIT_DIFF_FLAG__TO_DELETE = (1 << 16), /* delete entry during rename det. */
GIT_DIFF_FLAG__TO_SPLIT = (1 << 17), /* split entry during rename det. */
@@ -83,6 +88,12 @@ extern int git_diff__from_iterators(
git_iterator *new_iter,
const git_diff_options *opts);
+extern int git_diff__paired_foreach(
+ git_diff_list *idx2head,
+ git_diff_list *wd2idx,
+ int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload),
+ void *payload);
+
int git_diff_find_similar__hashsig_for_file(
void **out, const git_diff_file *f, const char *path, void *p);
diff --git a/src/diff_driver.c b/src/diff_driver.c
new file mode 100644
index 000000000..5438afc67
--- /dev/null
+++ b/src/diff_driver.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#include "common.h"
+
+#include "git2/attr.h"
+
+#include "diff.h"
+#include "diff_patch.h"
+#include "diff_driver.h"
+#include "strmap.h"
+#include "pool.h"
+#include "map.h"
+#include "buf_text.h"
+
+typedef enum {
+ DIFF_DRIVER_AUTO = 0,
+ DIFF_DRIVER_FALSE = 1,
+ DIFF_DRIVER_TRUE = 2,
+ DIFF_DRIVER_NAMED = 3,
+} git_diff_driver_t;
+
+enum {
+ DIFF_CONTEXT_FIND_NORMAL = 0,
+ DIFF_CONTEXT_FIND_ICASE = (1 << 0),
+ DIFF_CONTEXT_FIND_EXT = (1 << 1),
+};
+
+/* data for finding function context for a given file type */
+struct git_diff_driver {
+ git_diff_driver_t type;
+ git_strarray fn_patterns;
+ int binary;
+};
+
+struct git_diff_driver_registry {
+ git_strmap *drivers;
+ git_pool strings;
+};
+
+static git_diff_driver global_drivers[3] = {
+ { DIFF_DRIVER_AUTO, { NULL, 0 }, -1 },
+ { DIFF_DRIVER_FALSE, { NULL, 0 }, 1 },
+ { DIFF_DRIVER_TRUE, { NULL, 0 }, 0 },
+};
+
+git_diff_driver_registry *git_diff_driver_registry_new()
+{
+ return git__calloc(1, sizeof(git_diff_driver_registry));
+}
+
+void git_diff_driver_registry_free(git_diff_driver_registry *reg)
+{
+ git__free(reg);
+}
+
+int git_diff_driver_lookup(
+ git_diff_driver **out, git_repository *repo, const char *path)
+{
+ const char *value;
+
+ assert(out);
+
+ if (!repo || !path || !strlen(path))
+ goto use_auto;
+
+ if (git_attr_get(&value, repo, 0, path, "diff") < 0)
+ return -1;
+
+ if (GIT_ATTR_FALSE(value)) {
+ *out = &global_drivers[DIFF_DRIVER_FALSE];
+ return 0;
+ }
+
+ else if (GIT_ATTR_TRUE(value)) {
+ *out = &global_drivers[DIFF_DRIVER_TRUE];
+ return 0;
+ }
+
+ /* otherwise look for driver information in config and build driver */
+
+use_auto:
+ *out = &global_drivers[DIFF_DRIVER_AUTO];
+ return 0;
+}
+
+void git_diff_driver_free(git_diff_driver *driver)
+{
+ GIT_UNUSED(driver);
+ /* do nothing for now */
+}
+
+int git_diff_driver_is_binary(git_diff_driver *driver)
+{
+ return driver ? driver->binary : -1;
+}
+
+int git_diff_driver_content_is_binary(
+ git_diff_driver *driver, const char *content, size_t content_len)
+{
+ const git_buf search = { (char *)content, 0, min(content_len, 4000) };
+
+ GIT_UNUSED(driver);
+
+ /* TODO: provide encoding / binary detection callbacks that can
+ * be UTF-8 aware, etc. For now, instead of trying to be smart,
+ * let's just use the simple NUL-byte detection that core git uses.
+ */
+
+ /* previously was: if (git_buf_text_is_binary(&search)) */
+ if (git_buf_text_contains_nul(&search))
+ return 1;
+
+ return 0;
+}
+
+static long diff_context_find(
+ const char *line,
+ long line_len,
+ char *out,
+ long out_size,
+ void *payload)
+{
+ git_diff_driver *driver = payload;
+ const char *scan;
+
+ GIT_UNUSED(driver);
+
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line_len--;
+ if (line_len > 0 && line[line_len - 1] == '\r')
+ line_len--;
+ if (!line_len)
+ return -1;
+
+ if (!git__isalpha(*line) && *line != '_' && *line != '$')
+ return -1;
+
+ for (scan = &line[line_len-1]; scan > line && git__isspace(*scan); --scan)
+ /* search backward for non-space */;
+ line_len = scan - line;
+
+ if (line_len >= out_size)
+ line_len = out_size - 1;
+
+ memcpy(out, line, line_len);
+ out[line_len] = '\0';
+
+ return line_len;
+}
+
+git_diff_find_context_fn git_diff_driver_find_content_fn(git_diff_driver *driver)
+{
+ GIT_UNUSED(driver);
+ return diff_context_find;
+}
+
diff --git a/src/diff_driver.h b/src/diff_driver.h
new file mode 100644
index 000000000..b9881a7ed
--- /dev/null
+++ b/src/diff_driver.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_diff_driver_h__
+#define INCLUDE_diff_driver_h__
+
+#include "common.h"
+
+typedef struct git_diff_driver_registry git_diff_driver_registry;
+
+git_diff_driver_registry *git_diff_driver_registry_new();
+void git_diff_driver_registry_free(git_diff_driver_registry *);
+
+typedef struct git_diff_driver git_diff_driver;
+
+int git_diff_driver_lookup(git_diff_driver **, git_repository *, const char *);
+void git_diff_driver_free(git_diff_driver *);
+
+/* returns -1 meaning "unknown", 0 meaning not binary, 1 meaning binary */
+int git_diff_driver_is_binary(git_diff_driver *);
+
+/* returns -1 meaning "unknown", 0 meaning not binary, 1 meaning binary */
+int git_diff_driver_content_is_binary(
+ git_diff_driver *, const char *content, size_t content_len);
+
+typedef long (*git_diff_find_context_fn)(
+ const char *, long, char *, long, void *);
+
+git_diff_find_context_fn git_diff_driver_find_content_fn(git_diff_driver *);
+
+#endif
diff --git a/src/diff_file.c b/src/diff_file.c
new file mode 100644
index 000000000..e4f8ca1e8
--- /dev/null
+++ b/src/diff_file.c
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#include "common.h"
+#include "git2/blob.h"
+#include "git2/submodule.h"
+#include "diff.h"
+#include "diff_file.h"
+#include "odb.h"
+#include "fileops.h"
+#include "filter.h"
+
+#define DIFF_MAX_FILESIZE 0x20000000
+
+static bool diff_file_content_binary_by_size(git_diff_file_content *fc)
+{
+ /* if we have diff opts, check max_size vs file size */
+ if ((fc->file.flags & DIFF_FLAGS_KNOWN_BINARY) == 0 &&
+ fc->opts && fc->opts->max_size >= 0)
+ {
+ git_off_t threshold = DIFF_MAX_FILESIZE;
+ if (fc->opts->max_size > 0)
+ threshold = fc->opts->max_size;
+ if (fc->file.size > threshold)
+ fc->file.flags |= GIT_DIFF_FLAG_BINARY;
+ }
+
+ return ((fc->file.flags & GIT_DIFF_FLAG_BINARY) != 0);
+}
+
+static void diff_file_content_binary_by_content(git_diff_file_content *fc)
+{
+ if ((fc->file.flags & DIFF_FLAGS_KNOWN_BINARY) != 0)
+ return;
+
+ switch (git_diff_driver_content_is_binary(
+ fc->driver, fc->map.data, fc->map.len)) {
+ case 0: fc->file.flags |= GIT_DIFF_FLAG_NOT_BINARY; break;
+ case 1: fc->file.flags |= GIT_DIFF_FLAG_BINARY; break;
+ default: break;
+ }
+}
+
+static int diff_file_content_init_common(git_diff_file_content *fc)
+{
+ uint32_t flags = fc->opts ? fc->opts->flags : GIT_DIFF_NORMAL;
+
+ if (!fc->driver) {
+ if (git_diff_driver_lookup(&fc->driver, fc->repo, "") < 0)
+ return -1;
+ fc->src = GIT_ITERATOR_TYPE_TREE;
+ }
+
+ /* make sure file is conceivable mmap-able */
+ if ((git_off_t)((size_t)fc->file.size) != fc->file.size)
+ fc->file.flags |= GIT_DIFF_FLAG_BINARY;
+
+ /* check if user is forcing is to text diff the file */
+ else if (flags & GIT_DIFF_FORCE_TEXT)
+ fc->file.flags |= GIT_DIFF_FLAG_NOT_BINARY;
+
+ /* otherwise see if diff driver forces a behavior */
+ else switch (git_diff_driver_is_binary(fc->driver)) {
+ case 0: fc->file.flags |= GIT_DIFF_FLAG_NOT_BINARY; break;
+ case 1: fc->file.flags |= GIT_DIFF_FLAG_BINARY; break;
+ default: break;
+ }
+
+ diff_file_content_binary_by_size(fc);
+
+ if ((fc->file.flags & GIT_DIFF_FLAG__NO_DATA) != 0) {
+ fc->file.flags |= GIT_DIFF_FLAG__LOADED;
+ fc->map.len = 0;
+ fc->map.data = "";
+ }
+
+ if ((fc->file.flags & GIT_DIFF_FLAG__LOADED) != 0)
+ diff_file_content_binary_by_content(fc);
+
+ return 0;
+}
+
+int diff_file_content_init_from_diff(
+ git_diff_file_content *fc,
+ git_diff_list *diff,
+ size_t delta_index,
+ bool use_old)
+{
+ git_diff_delta *delta = git_vector_get(&diff->deltas, delta_index);
+ git_diff_file *file = use_old ? &delta->old_file : &delta->new_file;
+ bool has_data = true;
+
+ memset(fc, 0, sizeof(*fc));
+ fc->repo = diff->repo;
+ fc->opts = &diff->opts;
+ fc->src = use_old ? diff->old_src : diff->new_src;
+ memcpy(&fc->file, file, sizeof(fc->file));
+
+ if (git_diff_driver_lookup(&fc->driver, fc->repo, file->path) < 0)
+ return -1;
+
+ switch (delta->status) {
+ case GIT_DELTA_ADDED:
+ has_data = !use_old; break;
+ case GIT_DELTA_DELETED:
+ has_data = use_old; break;
+ case GIT_DELTA_UNTRACKED:
+ has_data = !use_old &&
+ (diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) != 0;
+ break;
+ case GIT_DELTA_MODIFIED:
+ case GIT_DELTA_COPIED:
+ case GIT_DELTA_RENAMED:
+ break;
+ default:
+ has_data = false;
+ break;
+ }
+
+ if (!has_data)
+ fc->file.flags |= GIT_DIFF_FLAG__NO_DATA;
+
+ return diff_file_content_init_common(fc);
+}
+
+int diff_file_content_init_from_blob(
+ git_diff_file_content *fc,
+ git_repository *repo,
+ const git_diff_options *opts,
+ const git_blob *blob)
+{
+ memset(fc, 0, sizeof(*fc));
+ fc->repo = repo;
+ fc->opts = opts;
+ fc->blob = blob;
+
+ if (!blob) {
+ fc->file.flags |= GIT_DIFF_FLAG__NO_DATA;
+ } else {
+ fc->file.flags |= GIT_DIFF_FLAG__LOADED | GIT_DIFF_FLAG_VALID_OID;
+ fc->file.size = git_blob_rawsize(blob);
+ fc->file.mode = 0644;
+ git_oid_cpy(&fc->file.oid, git_blob_id(blob));
+
+ fc->map.len = (size_t)fc->file.size;
+ fc->map.data = (char *)git_blob_rawcontent(blob);
+ }
+
+ return diff_file_content_init_common(fc);
+}
+
+int diff_file_content_init_from_raw(
+ git_diff_file_content *fc,
+ git_repository *repo,
+ const git_diff_options *opts,
+ const char *buf,
+ size_t buflen)
+{
+ memset(fc, 0, sizeof(*fc));
+ fc->repo = repo;
+ fc->opts = opts;
+
+ if (!buf) {
+ fc->file.flags |= GIT_DIFF_FLAG__NO_DATA;
+ } else {
+ fc->file.flags |= GIT_DIFF_FLAG__LOADED | GIT_DIFF_FLAG_VALID_OID;
+ fc->file.size = buflen;
+ fc->file.mode = 0644;
+ git_odb_hash(&fc->file.oid, buf, buflen, GIT_OBJ_BLOB);
+
+ fc->map.len = buflen;
+ fc->map.data = (char *)buf;
+ }
+
+ return diff_file_content_init_common(fc);
+}
+
+static int diff_file_content_commit_to_str(
+ git_diff_file_content *fc, bool check_status)
+{
+ char oid[GIT_OID_HEXSZ+1];
+ git_buf content = GIT_BUF_INIT;
+ const char *status = "";
+
+ if (check_status) {
+ int error = 0;
+ git_submodule *sm = NULL;
+ unsigned int sm_status = 0;
+ const git_oid *sm_head;
+
+ if ((error = git_submodule_lookup(&sm, fc->repo, fc->file.path)) < 0 ||
+ (error = git_submodule_status(&sm_status, sm)) < 0) {
+ /* GIT_EEXISTS means a "submodule" that has not been git added */
+ if (error == GIT_EEXISTS)
+ error = 0;
+ return error;
+ }
+
+ /* update OID if we didn't have it previously */
+ if ((fc->file.flags & GIT_DIFF_FLAG_VALID_OID) == 0 &&
+ ((sm_head = git_submodule_wd_id(sm)) != NULL ||
+ (sm_head = git_submodule_head_id(sm)) != NULL))
+ {
+ git_oid_cpy(&fc->file.oid, sm_head);
+ fc->file.flags |= GIT_DIFF_FLAG_VALID_OID;
+ }
+
+ if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status))
+ status = "-dirty";
+ }
+
+ git_oid_tostr(oid, sizeof(oid), &fc->file.oid);
+ if (git_buf_printf(&content, "Subproject commit %s%s\n", oid, status) < 0)
+ return -1;
+
+ fc->map.len = git_buf_len(&content);
+ fc->map.data = git_buf_detach(&content);
+ fc->file.flags |= GIT_DIFF_FLAG__FREE_DATA;
+
+ return 0;
+}
+
+static int diff_file_content_load_blob(git_diff_file_content *fc)
+{
+ int error = 0;
+ git_odb_object *odb_obj = NULL;
+
+ if (git_oid_iszero(&fc->file.oid))
+ return 0;
+
+ if (fc->file.mode == GIT_FILEMODE_COMMIT)
+ return diff_file_content_commit_to_str(fc, false);
+
+ /* if we don't know size, try to peek at object header first */
+ if (!fc->file.size) {
+ git_odb *odb;
+ size_t len;
+ git_otype type;
+
+ if (!(error = git_repository_odb__weakptr(&odb, fc->repo))) {
+ error = git_odb__read_header_or_object(
+ &odb_obj, &len, &type, odb, &fc->file.oid);
+ git_odb_free(odb);
+ }
+ if (error)
+ return error;
+
+ fc->file.size = len;
+ }
+
+ if (diff_file_content_binary_by_size(fc))
+ return 0;
+
+ if (odb_obj != NULL) {
+ error = git_object__from_odb_object(
+ (git_object **)&fc->blob, fc->repo, odb_obj, GIT_OBJ_BLOB);
+ git_odb_object_free(odb_obj);
+ } else {
+ error = git_blob_lookup(
+ (git_blob **)&fc->blob, fc->repo, &fc->file.oid);
+ }
+
+ if (!error) {
+ fc->file.flags |= GIT_DIFF_FLAG__FREE_BLOB;
+ fc->map.data = (void *)git_blob_rawcontent(fc->blob);
+ fc->map.len = (size_t)git_blob_rawsize(fc->blob);
+ }
+
+ return error;
+}
+
+static int diff_file_content_load_workdir_symlink(
+ git_diff_file_content *fc, git_buf *path)
+{
+ ssize_t alloc_len, read_len;
+
+ /* link path on disk could be UTF-16, so prepare a buffer that is
+ * big enough to handle some UTF-8 data expansion
+ */
+ alloc_len = (ssize_t)(fc->file.size * 2) + 1;
+
+ fc->map.data = git__calloc(alloc_len, sizeof(char));
+ GITERR_CHECK_ALLOC(fc->map.data);
+
+ fc->file.flags |= GIT_DIFF_FLAG__FREE_DATA;
+
+ read_len = p_readlink(git_buf_cstr(path), fc->map.data, alloc_len);
+ if (read_len < 0) {
+ giterr_set(GITERR_OS, "Failed to read symlink '%s'", fc->file.path);
+ return -1;
+ }
+
+ fc->map.len = read_len;
+ return 0;
+}
+
+static int diff_file_content_load_workdir_file(
+ git_diff_file_content *fc, git_buf *path)
+{
+ int error = 0;
+ git_vector filters = GIT_VECTOR_INIT;
+ git_buf raw = GIT_BUF_INIT, filtered = GIT_BUF_INIT;
+ git_file fd = git_futils_open_ro(git_buf_cstr(path));
+
+ if (fd < 0)
+ return fd;
+
+ if (!fc->file.size &&
+ !(fc->file.size = git_futils_filesize(fd)))
+ goto cleanup;
+
+ if (diff_file_content_binary_by_size(fc))
+ goto cleanup;
+
+ if ((error = git_filters_load(
+ &filters, fc->repo, fc->file.path, GIT_FILTER_TO_ODB)) < 0)
+ goto cleanup;
+ /* error >= is a filter count */
+
+ if (error == 0) {
+ if (!(error = git_futils_mmap_ro(
+ &fc->map, fd, 0, (size_t)fc->file.size)))
+ fc->file.flags |= GIT_DIFF_FLAG__UNMAP_DATA;
+ else /* fall through to try readbuffer below */
+ giterr_clear();
+ }
+
+ if (error != 0) {
+ error = git_futils_readbuffer_fd(&raw, fd, (size_t)fc->file.size);
+ if (error < 0)
+ goto cleanup;
+
+ if (!filters.length)
+ git_buf_swap(&filtered, &raw);
+ else
+ error = git_filters_apply(&filtered, &raw, &filters);
+
+ if (!error) {
+ fc->map.len = git_buf_len(&filtered);
+ fc->map.data = git_buf_detach(&filtered);
+ fc->file.flags |= GIT_DIFF_FLAG__FREE_DATA;
+ }
+
+ git_buf_free(&raw);
+ git_buf_free(&filtered);
+ }
+
+cleanup:
+ git_filters_free(&filters);
+ p_close(fd);
+
+ return error;
+}
+
+static int diff_file_content_load_workdir(git_diff_file_content *fc)
+{
+ int error = 0;
+ git_buf path = GIT_BUF_INIT;
+
+ if (fc->file.mode == GIT_FILEMODE_COMMIT)
+ return diff_file_content_commit_to_str(fc, true);
+
+ if (fc->file.mode == GIT_FILEMODE_TREE)
+ return 0;
+
+ if (git_buf_joinpath(
+ &path, git_repository_workdir(fc->repo), fc->file.path) < 0)
+ return -1;
+
+ if (S_ISLNK(fc->file.mode))
+ error = diff_file_content_load_workdir_symlink(fc, &path);
+ else
+ error = diff_file_content_load_workdir_file(fc, &path);
+
+ /* once data is loaded, update OID if we didn't have it previously */
+ if (!error && (fc->file.flags & GIT_DIFF_FLAG_VALID_OID) == 0) {
+ error = git_odb_hash(
+ &fc->file.oid, fc->map.data, fc->map.len, GIT_OBJ_BLOB);
+ fc->file.flags |= GIT_DIFF_FLAG_VALID_OID;
+ }
+
+ git_buf_free(&path);
+ return error;
+}
+
+int diff_file_content_load(git_diff_file_content *fc)
+{
+ int error = 0;
+
+ if ((fc->file.flags & GIT_DIFF_FLAG__LOADED) != 0)
+ return 0;
+
+ if (fc->file.flags & GIT_DIFF_FLAG_BINARY)
+ return 0;
+
+ if (fc->src == GIT_ITERATOR_TYPE_WORKDIR)
+ error = diff_file_content_load_workdir(fc);
+ else
+ error = diff_file_content_load_blob(fc);
+ if (error)
+ return error;
+
+ fc->file.flags |= GIT_DIFF_FLAG__LOADED;
+
+ diff_file_content_binary_by_content(fc);
+
+ return 0;
+}
+
+void diff_file_content_unload(git_diff_file_content *fc)
+{
+ if (fc->file.flags & GIT_DIFF_FLAG__FREE_DATA) {
+ git__free(fc->map.data);
+ fc->map.data = "";
+ fc->map.len = 0;
+ fc->file.flags &= ~GIT_DIFF_FLAG__FREE_DATA;
+ }
+ else if (fc->file.flags & GIT_DIFF_FLAG__UNMAP_DATA) {
+ git_futils_mmap_free(&fc->map);
+ fc->map.data = "";
+ fc->map.len = 0;
+ fc->file.flags &= ~GIT_DIFF_FLAG__UNMAP_DATA;
+ }
+
+ if (fc->file.flags & GIT_DIFF_FLAG__FREE_BLOB) {
+ git_blob_free((git_blob *)fc->blob);
+ fc->blob = NULL;
+ fc->file.flags &= ~GIT_DIFF_FLAG__FREE_BLOB;
+ }
+
+ fc->file.flags &= ~GIT_DIFF_FLAG__LOADED;
+}
+
+void diff_file_content_clear(git_diff_file_content *fc)
+{
+ diff_file_content_unload(fc);
+
+ /* for now, nothing else to do */
+}
diff --git a/src/diff_file.h b/src/diff_file.h
new file mode 100644
index 000000000..51c6878a9
--- /dev/null
+++ b/src/diff_file.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_diff_file_h__
+#define INCLUDE_diff_file_h__
+
+#include "common.h"
+#include "diff.h"
+#include "diff_driver.h"
+#include "map.h"
+
+/* expanded information for one side of a delta */
+typedef struct {
+ git_repository *repo;
+ const git_diff_options *opts;
+ git_diff_file file;
+ git_diff_driver *driver;
+ git_iterator_type_t src;
+ const git_blob *blob;
+ git_map map;
+} git_diff_file_content;
+
+extern int diff_file_content_init_from_diff(
+ git_diff_file_content *fc,
+ git_diff_list *diff,
+ size_t delta_index,
+ bool use_old);
+
+extern int diff_file_content_init_from_blob(
+ git_diff_file_content *fc,
+ git_repository *repo,
+ const git_diff_options *opts,
+ const git_blob *blob);
+
+extern int diff_file_content_init_from_raw(
+ git_diff_file_content *fc,
+ git_repository *repo,
+ const git_diff_options *opts,
+ const char *buf,
+ size_t buflen);
+
+/* this loads the blob/file-on-disk as needed */
+extern int diff_file_content_load(git_diff_file_content *fc);
+
+/* this releases the blob/file-in-memory */
+extern void diff_file_content_unload(git_diff_file_content *fc);
+
+/* this unloads and also releases any other resources */
+extern void diff_file_content_clear(git_diff_file_content *fc);
+
+#endif
diff --git a/src/diff_output.c b/src/diff_output.c
deleted file mode 100644
index 9a5be2a10..000000000
--- a/src/diff_output.c
+++ /dev/null
@@ -1,1526 +0,0 @@
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "common.h"
-#include "git2/attr.h"
-#include "git2/oid.h"
-#include "git2/submodule.h"
-#include "diff_output.h"
-#include <ctype.h>
-#include "fileops.h"
-#include "filter.h"
-#include "buf_text.h"
-
-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) ? 0 : -1;
-}
-
-static int parse_hunk_header(git_diff_range *range, const char *header)
-{
- /* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */
- if (*header != '@')
- return -1;
- if (read_next_int(&header, &range->old_start) < 0)
- return -1;
- if (*header == ',') {
- if (read_next_int(&header, &range->old_lines) < 0)
- return -1;
- } else
- range->old_lines = 1;
- if (read_next_int(&header, &range->new_start) < 0)
- return -1;
- if (*header == ',') {
- if (read_next_int(&header, &range->new_lines) < 0)
- return -1;
- } else
- range->new_lines = 1;
- if (range->old_start < 0 || range->new_start < 0)
- return -1;
-
- return 0;
-}
-
-#define KNOWN_BINARY_FLAGS (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY)
-#define NOT_BINARY_FLAGS (GIT_DIFF_FLAG_NOT_BINARY|GIT_DIFF_FLAG__NO_DATA)
-
-static int update_file_is_binary_by_attr(
- git_repository *repo, git_diff_file *file)
-{
- const char *value;
-
- /* because of blob diffs, cannot assume path is set */
- if (!file->path || !strlen(file->path))
- return 0;
-
- if (git_attr_get(&value, repo, 0, file->path, "diff") < 0)
- return -1;
-
- if (GIT_ATTR_FALSE(value))
- file->flags |= GIT_DIFF_FLAG_BINARY;
- else if (GIT_ATTR_TRUE(value))
- file->flags |= GIT_DIFF_FLAG_NOT_BINARY;
- /* otherwise leave file->flags alone */
-
- return 0;
-}
-
-static void update_delta_is_binary(git_diff_delta *delta)
-{
- if ((delta->old_file.flags & GIT_DIFF_FLAG_BINARY) != 0 ||
- (delta->new_file.flags & GIT_DIFF_FLAG_BINARY) != 0)
- delta->flags |= GIT_DIFF_FLAG_BINARY;
-
- else if ((delta->old_file.flags & NOT_BINARY_FLAGS) != 0 &&
- (delta->new_file.flags & NOT_BINARY_FLAGS) != 0)
- delta->flags |= GIT_DIFF_FLAG_NOT_BINARY;
-
- /* otherwise leave delta->flags binary value untouched */
-}
-
-/* returns if we forced binary setting (and no further checks needed) */
-static bool diff_delta_is_binary_forced(
- diff_context *ctxt,
- git_diff_delta *delta)
-{
- /* return true if binary-ness has already been settled */
- if ((delta->flags & KNOWN_BINARY_FLAGS) != 0)
- return true;
-
- /* 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_FLAG_BINARY;
- delta->new_file.flags |= GIT_DIFF_FLAG_BINARY;
- delta->flags |= GIT_DIFF_FLAG_BINARY;
- return true;
- }
-
- /* check if user is forcing us to text diff these files */
- if (ctxt->opts && (ctxt->opts->flags & GIT_DIFF_FORCE_TEXT) != 0) {
- delta->old_file.flags |= GIT_DIFF_FLAG_NOT_BINARY;
- delta->new_file.flags |= GIT_DIFF_FLAG_NOT_BINARY;
- delta->flags |= GIT_DIFF_FLAG_NOT_BINARY;
- return true;
- }
-
- return false;
-}
-
-static int diff_delta_is_binary_by_attr(
- diff_context *ctxt, git_diff_patch *patch)
-{
- int error = 0, mirror_new;
- git_diff_delta *delta = patch->delta;
-
- if (diff_delta_is_binary_forced(ctxt, delta))
- return 0;
-
- /* check diff attribute +, -, or 0 */
- if (update_file_is_binary_by_attr(ctxt->repo, &delta->old_file) < 0)
- return -1;
-
- mirror_new = (delta->new_file.path == delta->old_file.path ||
- ctxt->diff->strcomp(delta->new_file.path, delta->old_file.path) == 0);
- if (mirror_new)
- delta->new_file.flags |= (delta->old_file.flags & KNOWN_BINARY_FLAGS);
- else
- error = update_file_is_binary_by_attr(ctxt->repo, &delta->new_file);
-
- update_delta_is_binary(delta);
-
- return error;
-}
-
-static int diff_delta_is_binary_by_content(
- diff_context *ctxt,
- git_diff_delta *delta,
- git_diff_file *file,
- const git_map *map)
-{
- const git_buf search = { map->data, 0, min(map->len, 4000) };
-
- if (diff_delta_is_binary_forced(ctxt, delta))
- return 0;
-
- /* TODO: provide encoding / binary detection callbacks that can
- * be UTF-8 aware, etc. For now, instead of trying to be smart,
- * let's just use the simple NUL-byte detection that core git uses.
- */
-
- /* previously was: if (git_buf_text_is_binary(&search)) */
- if (git_buf_text_contains_nul(&search))
- file->flags |= GIT_DIFF_FLAG_BINARY;
- else
- file->flags |= GIT_DIFF_FLAG_NOT_BINARY;
-
- update_delta_is_binary(delta);
-
- return 0;
-}
-
-static int diff_delta_is_binary_by_size(
- diff_context *ctxt, git_diff_delta *delta, git_diff_file *file)
-{
- git_off_t threshold = MAX_DIFF_FILESIZE;
-
- if ((file->flags & KNOWN_BINARY_FLAGS) != 0)
- return 0;
-
- if (ctxt && ctxt->opts) {
- if (ctxt->opts->max_size < 0)
- return 0;
-
- if (ctxt->opts->max_size > 0)
- threshold = ctxt->opts->max_size;
- }
-
- if (file->size > threshold)
- file->flags |= GIT_DIFF_FLAG_BINARY;
-
- update_delta_is_binary(delta);
-
- return 0;
-}
-
-static long diff_context_find(
- const char *line,
- long line_len,
- char *out,
- long out_size,
- void *payload)
-{
- diff_context *ctxt = payload;
- const char *scan;
- bool found_paren = false;
-
- GIT_UNUSED(ctxt);
-
- if (line_len > 0 && line[line_len - 1] == '\n')
- line_len--;
- if (line_len > 0 && line[line_len - 1] == '\r')
- line_len--;
- if (!line_len)
- return -1;
-
- if (!isalpha(*line))
- return -1;
-
- for (scan = &line[line_len - 1]; scan > line && *scan != '('; --scan)
- /* search backward for ( */;
- if (scan != line) {
- found_paren = true;
- line_len = scan - line;
-
- for (--scan; scan > line && !isalpha(*scan); --scan)
- --line_len;
- }
-
- if (!line_len)
- return -1;
-
- if (out_size > line_len) {
- memcpy(out, line, line_len);
-
- if (found_paren)
- out[line_len++] = '(';
- out[line_len] = '\0';
- } else {
- memcpy(out, line, out_size);
- line_len = out_size;
- }
-
- return line_len;
-}
-
-static void setup_xdiff_options(diff_context *ctxt)
-{
- memset(&ctxt->xdiff_config, 0, sizeof(ctxt->xdiff_config));
- memset(&ctxt->xdiff_params, 0, sizeof(ctxt->xdiff_params));
-
- ctxt->xdiff_config.ctxlen =
- (!ctxt->opts) ? 3 : ctxt->opts->context_lines;
- ctxt->xdiff_config.interhunkctxlen =
- (!ctxt->opts) ? 0 : ctxt->opts->interhunk_lines;
-
- ctxt->xdiff_config.flags = XDL_EMIT_FUNCNAMES;
- ctxt->xdiff_config.find_func = diff_context_find;
- ctxt->xdiff_config.find_func_priv = ctxt;
-
- if (!ctxt->opts)
- return;
-
- if (ctxt->opts->flags & GIT_DIFF_IGNORE_WHITESPACE)
- ctxt->xdiff_params.flags |= XDF_WHITESPACE_FLAGS;
- if (ctxt->opts->flags & GIT_DIFF_IGNORE_WHITESPACE_CHANGE)
- ctxt->xdiff_params.flags |= XDF_IGNORE_WHITESPACE_CHANGE;
- if (ctxt->opts->flags & GIT_DIFF_IGNORE_WHITESPACE_EOL)
- ctxt->xdiff_params.flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
-}
-
-
-static int get_blob_content(
- diff_context *ctxt,
- git_diff_delta *delta,
- git_diff_file *file,
- git_map *map,
- git_blob **blob)
-{
- int error;
- git_odb_object *odb_obj = NULL;
-
- if (git_oid_iszero(&file->oid))
- return 0;
-
- if (file->mode == GIT_FILEMODE_COMMIT) {
- char oidstr[GIT_OID_HEXSZ+1];
- git_buf content = GIT_BUF_INIT;
-
- git_oid_tostr(oidstr, sizeof(oidstr), &file->oid);
- git_buf_printf(&content, "Subproject commit %s\n", oidstr);
-
- map->data = git_buf_detach(&content);
- map->len = strlen(map->data);
-
- file->flags |= GIT_DIFF_FLAG__FREE_DATA;
- return 0;
- }
-
- if (!file->size) {
- git_odb *odb;
- size_t len;
- git_otype type;
-
- /* peek at object header to avoid loading if too large */
- if ((error = git_repository_odb__weakptr(&odb, ctxt->repo)) < 0 ||
- (error = git_odb__read_header_or_object(
- &odb_obj, &len, &type, odb, &file->oid)) < 0)
- return error;
-
- assert(type == GIT_OBJ_BLOB);
-
- file->size = len;
- }
-
- /* if blob is too large to diff, mark as binary */
- if ((error = diff_delta_is_binary_by_size(ctxt, delta, file)) < 0)
- return error;
- if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
- return 0;
-
- if (odb_obj != NULL) {
- error = git_object__from_odb_object(
- (git_object **)blob, ctxt->repo, odb_obj, GIT_OBJ_BLOB);
- git_odb_object_free(odb_obj);
- } else
- error = git_blob_lookup(blob, ctxt->repo, &file->oid);
-
- if (error)
- return error;
-
- map->data = (void *)git_blob_rawcontent(*blob);
- map->len = (size_t)git_blob_rawsize(*blob);
-
- return diff_delta_is_binary_by_content(ctxt, delta, file, map);
-}
-
-static int get_workdir_sm_content(
- diff_context *ctxt,
- git_diff_file *file,
- git_map *map)
-{
- int error = 0;
- git_buf content = GIT_BUF_INIT;
- git_submodule* sm = NULL;
- unsigned int sm_status = 0;
- const char* sm_status_text = "";
- char oidstr[GIT_OID_HEXSZ+1];
-
- if ((error = git_submodule_lookup(&sm, ctxt->repo, file->path)) < 0 ||
- (error = git_submodule_status(&sm_status, sm)) < 0) {
-
- /* GIT_EEXISTS means a "submodule" that has not been git added */
- if (error == GIT_EEXISTS)
- error = 0;
- return error;
- }
-
- /* update OID if we didn't have it previously */
- if ((file->flags & GIT_DIFF_FLAG_VALID_OID) == 0) {
- const git_oid* sm_head;
-
- if ((sm_head = git_submodule_wd_id(sm)) != NULL ||
- (sm_head = git_submodule_head_id(sm)) != NULL) {
-
- git_oid_cpy(&file->oid, sm_head);
- file->flags |= GIT_DIFF_FLAG_VALID_OID;
- }
- }
-
- git_oid_tostr(oidstr, sizeof(oidstr), &file->oid);
-
- if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status))
- sm_status_text = "-dirty";
-
- git_buf_printf(
- &content, "Subproject commit %s%s\n", oidstr, sm_status_text);
-
- map->data = git_buf_detach(&content);
- map->len = strlen(map->data);
-
- file->flags |= GIT_DIFF_FLAG__FREE_DATA;
-
- return 0;
-}
-
-static int get_filtered(
- git_map *map, git_file fd, git_diff_file *file, git_vector *filters)
-{
- int error;
- git_buf raw = GIT_BUF_INIT, filtered = GIT_BUF_INIT;
-
- if ((error = git_futils_readbuffer_fd(&raw, fd, (size_t)file->size)) < 0)
- return error;
-
- if (!filters->length)
- git_buf_swap(&filtered, &raw);
- else
- error = git_filters_apply(&filtered, &raw, filters);
-
- if (!error) {
- map->len = git_buf_len(&filtered);
- map->data = git_buf_detach(&filtered);
-
- file->flags |= GIT_DIFF_FLAG__FREE_DATA;
- }
-
- git_buf_free(&raw);
- git_buf_free(&filtered);
-
- return error;
-}
-
-static int get_workdir_content(
- diff_context *ctxt,
- git_diff_delta *delta,
- git_diff_file *file,
- git_map *map)
-{
- int error = 0;
- git_buf path = GIT_BUF_INIT;
- const char *wd = git_repository_workdir(ctxt->repo);
-
- if (S_ISGITLINK(file->mode))
- return get_workdir_sm_content(ctxt, file, map);
-
- if (S_ISDIR(file->mode))
- return 0;
-
- if (git_buf_joinpath(&path, wd, file->path) < 0)
- return -1;
-
- if (S_ISLNK(file->mode)) {
- ssize_t alloc_len, read_len;
-
- file->flags |= GIT_DIFF_FLAG__FREE_DATA;
- file->flags |= GIT_DIFF_FLAG_BINARY;
-
- /* link path on disk could be UTF-16, so prepare a buffer that is
- * big enough to handle some UTF-8 data expansion
- */
- alloc_len = (ssize_t)(file->size * 2) + 1;
-
- map->data = git__malloc(alloc_len);
- GITERR_CHECK_ALLOC(map->data);
-
- read_len = p_readlink(path.ptr, map->data, alloc_len);
- if (read_len < 0) {
- giterr_set(GITERR_OS, "Failed to read symlink '%s'", file->path);
- error = -1;
- goto cleanup;
- }
-
- map->len = read_len;
- }
- else {
- git_file fd = git_futils_open_ro(path.ptr);
- git_vector filters = GIT_VECTOR_INIT;
-
- if (fd < 0) {
- error = fd;
- goto cleanup;
- }
-
- if (!file->size && !(file->size = git_futils_filesize(fd)))
- goto close_and_cleanup;
-
- if ((error = diff_delta_is_binary_by_size(ctxt, delta, file)) < 0 ||
- (delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
- goto close_and_cleanup;
-
- error = git_filters_load(
- &filters, ctxt->repo, file->path, GIT_FILTER_TO_ODB);
- if (error < 0)
- goto close_and_cleanup;
-
- if (error == 0) { /* note: git_filters_load returns filter count */
- error = git_futils_mmap_ro(map, fd, 0, (size_t)file->size);
- if (!error)
- file->flags |= GIT_DIFF_FLAG__UNMAP_DATA;
- }
- if (error != 0)
- error = get_filtered(map, fd, file, &filters);
-
-close_and_cleanup:
- git_filters_free(&filters);
- p_close(fd);
- }
-
- /* once data is loaded, update OID if we didn't have it previously */
- if (!error && (file->flags & GIT_DIFF_FLAG_VALID_OID) == 0) {
- error = git_odb_hash(
- &file->oid, map->data, map->len, GIT_OBJ_BLOB);
- if (!error)
- file->flags |= GIT_DIFF_FLAG_VALID_OID;
- }
-
- if (!error)
- error = diff_delta_is_binary_by_content(ctxt, delta, file, map);
-
-cleanup:
- git_buf_free(&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_FLAG__FREE_DATA) {
- git__free(map->data);
- map->data = "";
- map->len = 0;
- file->flags &= ~GIT_DIFF_FLAG__FREE_DATA;
- }
- else if (file->flags & GIT_DIFF_FLAG__UNMAP_DATA) {
- git_futils_mmap_free(map);
- map->data = "";
- map->len = 0;
- file->flags &= ~GIT_DIFF_FLAG__UNMAP_DATA;
- }
-}
-
-
-static int diff_context_init(
- diff_context *ctxt,
- git_diff_list *diff,
- git_repository *repo,
- const git_diff_options *opts,
- git_diff_file_cb file_cb,
- git_diff_hunk_cb hunk_cb,
- git_diff_data_cb data_cb,
- void *payload)
-{
- memset(ctxt, 0, sizeof(diff_context));
-
- if (!repo && diff)
- repo = diff->repo;
-
- if (!opts && diff)
- opts = &diff->opts;
-
- ctxt->repo = repo;
- ctxt->diff = diff;
- ctxt->opts = opts;
- ctxt->file_cb = file_cb;
- ctxt->hunk_cb = hunk_cb;
- ctxt->data_cb = data_cb;
- ctxt->payload = payload;
- ctxt->error = 0;
-
- setup_xdiff_options(ctxt);
-
- return 0;
-}
-
-static int diff_delta_file_callback(
- diff_context *ctxt, git_diff_delta *delta, size_t idx)
-{
- float progress;
-
- if (!ctxt->file_cb)
- return 0;
-
- progress = ctxt->diff ? ((float)idx / ctxt->diff->deltas.length) : 1.0f;
-
- if (ctxt->file_cb(delta, progress, ctxt->payload) != 0)
- ctxt->error = GIT_EUSER;
-
- return ctxt->error;
-}
-
-static void diff_patch_init(
- diff_context *ctxt, git_diff_patch *patch)
-{
- memset(patch, 0, sizeof(git_diff_patch));
-
- patch->diff = ctxt->diff;
- patch->ctxt = ctxt;
-
- if (patch->diff) {
- patch->old_src = patch->diff->old_src;
- patch->new_src = patch->diff->new_src;
- } else {
- patch->old_src = patch->new_src = GIT_ITERATOR_TYPE_TREE;
- }
-}
-
-static git_diff_patch *diff_patch_alloc(
- diff_context *ctxt, git_diff_delta *delta)
-{
- git_diff_patch *patch = git__malloc(sizeof(git_diff_patch));
- if (!patch)
- return NULL;
-
- diff_patch_init(ctxt, patch);
-
- git_diff_list_addref(patch->diff);
-
- GIT_REFCOUNT_INC(patch);
-
- patch->delta = delta;
- patch->flags = GIT_DIFF_PATCH_ALLOCATED;
-
- return patch;
-}
-
-static int diff_patch_load(
- diff_context *ctxt, git_diff_patch *patch)
-{
- int error = 0;
- git_diff_delta *delta = patch->delta;
- bool check_if_unmodified = false;
-
- if ((patch->flags & GIT_DIFF_PATCH_LOADED) != 0)
- return 0;
-
- error = diff_delta_is_binary_by_attr(ctxt, patch);
-
- patch->old_data.data = "";
- patch->old_data.len = 0;
- patch->old_blob = NULL;
-
- patch->new_data.data = "";
- patch->new_data.len = 0;
- patch->new_blob = NULL;
-
- if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
- goto cleanup;
-
- if (!ctxt->hunk_cb &&
- !ctxt->data_cb &&
- (ctxt->opts->flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0)
- goto cleanup;
-
- switch (delta->status) {
- case GIT_DELTA_ADDED:
- delta->old_file.flags |= GIT_DIFF_FLAG__NO_DATA;
- break;
- case GIT_DELTA_DELETED:
- delta->new_file.flags |= GIT_DIFF_FLAG__NO_DATA;
- break;
- case GIT_DELTA_MODIFIED:
- case GIT_DELTA_COPIED:
- case GIT_DELTA_RENAMED:
- break;
- case GIT_DELTA_UNTRACKED:
- delta->old_file.flags |= GIT_DIFF_FLAG__NO_DATA;
- if ((ctxt->opts->flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) == 0)
- delta->new_file.flags |= GIT_DIFF_FLAG__NO_DATA;
- break;
- default:
- delta->new_file.flags |= GIT_DIFF_FLAG__NO_DATA;
- delta->old_file.flags |= GIT_DIFF_FLAG__NO_DATA;
- break;
- }
-
-#define CHECK_UNMODIFIED (GIT_DIFF_FLAG__NO_DATA | GIT_DIFF_FLAG_VALID_OID)
-
- check_if_unmodified =
- (delta->old_file.flags & CHECK_UNMODIFIED) == 0 &&
- (delta->new_file.flags & CHECK_UNMODIFIED) == 0;
-
- /* Always try to load workdir content first, since it may need to be
- * filtered (and hence use 2x memory) and we want to minimize the max
- * memory footprint during diff.
- */
-
- if ((delta->old_file.flags & GIT_DIFF_FLAG__NO_DATA) == 0 &&
- patch->old_src == GIT_ITERATOR_TYPE_WORKDIR) {
- if ((error = get_workdir_content(
- ctxt, delta, &delta->old_file, &patch->old_data)) < 0)
- goto cleanup;
- if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
- goto cleanup;
- }
-
- if ((delta->new_file.flags & GIT_DIFF_FLAG__NO_DATA) == 0 &&
- patch->new_src == GIT_ITERATOR_TYPE_WORKDIR) {
- if ((error = get_workdir_content(
- ctxt, delta, &delta->new_file, &patch->new_data)) < 0)
- goto cleanup;
- if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
- goto cleanup;
- }
-
- if ((delta->old_file.flags & GIT_DIFF_FLAG__NO_DATA) == 0 &&
- patch->old_src != GIT_ITERATOR_TYPE_WORKDIR) {
- if ((error = get_blob_content(
- ctxt, delta, &delta->old_file,
- &patch->old_data, &patch->old_blob)) < 0)
- goto cleanup;
- if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
- goto cleanup;
- }
-
- if ((delta->new_file.flags & GIT_DIFF_FLAG__NO_DATA) == 0 &&
- patch->new_src != GIT_ITERATOR_TYPE_WORKDIR) {
- if ((error = get_blob_content(
- ctxt, delta, &delta->new_file,
- &patch->new_data, &patch->new_blob)) < 0)
- goto cleanup;
- if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
- goto cleanup;
- }
-
- /* if we did not previously have the definitive oid, we may have
- * incorrect status and need to switch this to UNMODIFIED.
- */
- if (check_if_unmodified &&
- delta->old_file.mode == delta->new_file.mode &&
- !git_oid__cmp(&delta->old_file.oid, &delta->new_file.oid)) {
-
- delta->status = GIT_DELTA_UNMODIFIED;
-
- if ((ctxt->opts->flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
- goto cleanup;
- }
-
-cleanup:
- if ((delta->flags & KNOWN_BINARY_FLAGS) == 0)
- update_delta_is_binary(delta);
-
- if (!error) {
- patch->flags |= GIT_DIFF_PATCH_LOADED;
-
- /* patch is diffable only for non-binary, modified files where at
- * least one side has data and there is actual change in the data
- */
- if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0 &&
- delta->status != GIT_DELTA_UNMODIFIED &&
- (patch->old_data.len || patch->new_data.len) &&
- (patch->old_data.len != patch->new_data.len ||
- !git_oid_equal(&delta->old_file.oid, &delta->new_file.oid)))
- patch->flags |= GIT_DIFF_PATCH_DIFFABLE;
- }
-
- return error;
-}
-
-static int diff_patch_cb(void *priv, mmbuffer_t *bufs, int len)
-{
- git_diff_patch *patch = priv;
- diff_context *ctxt = patch->ctxt;
-
- if (len == 1) {
- ctxt->error = parse_hunk_header(&ctxt->range, bufs[0].ptr);
- if (ctxt->error < 0)
- return ctxt->error;
-
- if (ctxt->hunk_cb != NULL &&
- ctxt->hunk_cb(patch->delta, &ctxt->range,
- bufs[0].ptr, bufs[0].size, ctxt->payload))
- ctxt->error = GIT_EUSER;
- }
-
- if (len == 2 || len == 3) {
- /* expect " "/"-"/"+", then data */
- char origin =
- (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION :
- (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION :
- GIT_DIFF_LINE_CONTEXT;
-
- if (ctxt->data_cb != NULL &&
- ctxt->data_cb(patch->delta, &ctxt->range,
- origin, bufs[1].ptr, bufs[1].size, ctxt->payload))
- ctxt->error = GIT_EUSER;
- }
-
- if (len == 3 && !ctxt->error) {
- /* If we have a '+' and a third buf, then we have added a line
- * without a newline and the old code had one, so DEL_EOFNL.
- * If we have a '-' and a third buf, then we have removed a line
- * with out a newline but added a blank line, so ADD_EOFNL.
- */
- char origin =
- (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_DEL_EOFNL :
- (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_ADD_EOFNL :
- GIT_DIFF_LINE_CONTEXT_EOFNL;
-
- if (ctxt->data_cb != NULL &&
- ctxt->data_cb(patch->delta, &ctxt->range,
- origin, bufs[2].ptr, bufs[2].size, ctxt->payload))
- ctxt->error = GIT_EUSER;
- }
-
- return ctxt->error;
-}
-
-static int diff_patch_generate(
- diff_context *ctxt, git_diff_patch *patch)
-{
- int error = 0;
- xdemitcb_t xdiff_callback;
- mmfile_t old_xdiff_data, new_xdiff_data;
-
- if ((patch->flags & GIT_DIFF_PATCH_DIFFED) != 0)
- return 0;
-
- if ((patch->flags & GIT_DIFF_PATCH_LOADED) == 0)
- if ((error = diff_patch_load(ctxt, patch)) < 0)
- return error;
-
- if ((patch->flags & GIT_DIFF_PATCH_DIFFABLE) == 0)
- return 0;
-
- if (!ctxt->file_cb && !ctxt->hunk_cb)
- return 0;
-
- patch->ctxt = ctxt;
-
- memset(&xdiff_callback, 0, sizeof(xdiff_callback));
- xdiff_callback.outf = diff_patch_cb;
- xdiff_callback.priv = patch;
-
- old_xdiff_data.ptr = patch->old_data.data;
- old_xdiff_data.size = patch->old_data.len;
- new_xdiff_data.ptr = patch->new_data.data;
- new_xdiff_data.size = patch->new_data.len;
-
- xdl_diff(&old_xdiff_data, &new_xdiff_data,
- &ctxt->xdiff_params, &ctxt->xdiff_config, &xdiff_callback);
-
- error = ctxt->error;
-
- if (!error)
- patch->flags |= GIT_DIFF_PATCH_DIFFED;
-
- return error;
-}
-
-static void diff_patch_unload(git_diff_patch *patch)
-{
- if ((patch->flags & GIT_DIFF_PATCH_DIFFED) != 0) {
- patch->flags = (patch->flags & ~GIT_DIFF_PATCH_DIFFED);
-
- patch->hunks_size = 0;
- patch->lines_size = 0;
- }
-
- if ((patch->flags & GIT_DIFF_PATCH_LOADED) != 0) {
- patch->flags = (patch->flags & ~GIT_DIFF_PATCH_LOADED);
-
- release_content(
- &patch->delta->old_file, &patch->old_data, patch->old_blob);
- release_content(
- &patch->delta->new_file, &patch->new_data, patch->new_blob);
- }
-}
-
-static void diff_patch_free(git_diff_patch *patch)
-{
- diff_patch_unload(patch);
-
- git__free(patch->lines);
- patch->lines = NULL;
- patch->lines_asize = 0;
-
- git__free(patch->hunks);
- patch->hunks = NULL;
- patch->hunks_asize = 0;
-
- if (!(patch->flags & GIT_DIFF_PATCH_ALLOCATED))
- return;
-
- patch->flags = 0;
-
- git_diff_list_free(patch->diff); /* decrements refcount */
-
- git__free(patch);
-}
-
-#define MAX_HUNK_STEP 128
-#define MIN_HUNK_STEP 8
-#define MAX_LINE_STEP 256
-#define MIN_LINE_STEP 8
-
-static int diff_patch_hunk_cb(
- const git_diff_delta *delta,
- const git_diff_range *range,
- const char *header,
- size_t header_len,
- void *payload)
-{
- git_diff_patch *patch = payload;
- diff_patch_hunk *hunk;
-
- GIT_UNUSED(delta);
-
- if (patch->hunks_size >= patch->hunks_asize) {
- size_t new_size;
- diff_patch_hunk *new_hunks;
-
- if (patch->hunks_asize > MAX_HUNK_STEP)
- new_size = patch->hunks_asize + MAX_HUNK_STEP;
- else
- new_size = patch->hunks_asize * 2;
- if (new_size < MIN_HUNK_STEP)
- new_size = MIN_HUNK_STEP;
-
- new_hunks = git__realloc(
- patch->hunks, new_size * sizeof(diff_patch_hunk));
- if (!new_hunks)
- return -1;
-
- patch->hunks = new_hunks;
- patch->hunks_asize = new_size;
- }
-
- hunk = &patch->hunks[patch->hunks_size++];
-
- memcpy(&hunk->range, range, sizeof(hunk->range));
-
- assert(header_len + 1 < sizeof(hunk->header));
- memcpy(&hunk->header, header, header_len);
- hunk->header[header_len] = '\0';
- hunk->header_len = header_len;
-
- hunk->line_start = patch->lines_size;
- hunk->line_count = 0;
-
- patch->oldno = range->old_start;
- patch->newno = range->new_start;
-
- return 0;
-}
-
-static int diff_patch_line_cb(
- const git_diff_delta *delta,
- const git_diff_range *range,
- char line_origin,
- const char *content,
- size_t content_len,
- void *payload)
-{
- git_diff_patch *patch = payload;
- diff_patch_hunk *hunk;
- diff_patch_line *line;
-
- GIT_UNUSED(delta);
- GIT_UNUSED(range);
-
- assert(patch->hunks_size > 0);
- assert(patch->hunks != NULL);
-
- hunk = &patch->hunks[patch->hunks_size - 1];
-
- if (patch->lines_size >= patch->lines_asize) {
- size_t new_size;
- diff_patch_line *new_lines;
-
- if (patch->lines_asize > MAX_LINE_STEP)
- new_size = patch->lines_asize + MAX_LINE_STEP;
- else
- new_size = patch->lines_asize * 2;
- if (new_size < MIN_LINE_STEP)
- new_size = MIN_LINE_STEP;
-
- new_lines = git__realloc(
- patch->lines, new_size * sizeof(diff_patch_line));
- if (!new_lines)
- return -1;
-
- patch->lines = new_lines;
- patch->lines_asize = new_size;
- }
-
- line = &patch->lines[patch->lines_size++];
-
- line->ptr = content;
- line->len = content_len;
- line->origin = line_origin;
-
- /* do some bookkeeping so we can provide old/new line numbers */
-
- for (line->lines = 0; content_len > 0; --content_len) {
- if (*content++ == '\n')
- ++line->lines;
- }
-
- switch (line_origin) {
- case GIT_DIFF_LINE_ADDITION:
- case GIT_DIFF_LINE_DEL_EOFNL:
- line->oldno = -1;
- line->newno = patch->newno;
- patch->newno += line->lines;
- break;
- case GIT_DIFF_LINE_DELETION:
- case GIT_DIFF_LINE_ADD_EOFNL:
- line->oldno = patch->oldno;
- line->newno = -1;
- patch->oldno += line->lines;
- break;
- default:
- line->oldno = patch->oldno;
- line->newno = patch->newno;
- patch->oldno += line->lines;
- patch->newno += line->lines;
- break;
- }
-
- hunk->line_count++;
-
- return 0;
-}
-
-static int diff_required(git_diff_list *diff, const char *action)
-{
- if (!diff) {
- giterr_set(GITERR_INVALID, "Must provide valid diff to %s", action);
- return -1;
- }
-
- return 0;
-}
-
-int git_diff_foreach(
- git_diff_list *diff,
- git_diff_file_cb file_cb,
- git_diff_hunk_cb hunk_cb,
- git_diff_data_cb data_cb,
- void *payload)
-{
- int error = 0;
- diff_context ctxt;
- size_t idx;
- git_diff_patch patch;
-
- if (diff_required(diff, "git_diff_foreach") < 0)
- return -1;
-
- if (diff_context_init(
- &ctxt, diff, NULL, NULL, file_cb, hunk_cb, data_cb, payload) < 0)
- return -1;
-
- diff_patch_init(&ctxt, &patch);
-
- git_vector_foreach(&diff->deltas, idx, patch.delta) {
-
- /* check flags against patch status */
- if (git_diff_delta__should_skip(ctxt.opts, patch.delta))
- continue;
-
- if (!(error = diff_patch_load(&ctxt, &patch))) {
-
- /* invoke file callback */
- error = diff_delta_file_callback(&ctxt, patch.delta, idx);
-
- /* generate diffs and invoke hunk and line callbacks */
- if (!error)
- error = diff_patch_generate(&ctxt, &patch);
-
- diff_patch_unload(&patch);
- }
-
- if (error < 0)
- break;
- }
-
- if (error == GIT_EUSER)
- giterr_clear(); /* don't let error message leak */
-
- return error;
-}
-
-static void set_data_from_blob(
- const git_blob *blob, git_map *map, git_diff_file *file)
-{
- if (blob) {
- file->size = git_blob_rawsize(blob);
- git_oid_cpy(&file->oid, git_object_id((const git_object *)blob));
- file->mode = 0644;
-
- map->len = (size_t)file->size;
- map->data = (char *)git_blob_rawcontent(blob);
- } else {
- file->size = 0;
- file->flags |= GIT_DIFF_FLAG__NO_DATA;
-
- map->len = 0;
- map->data = "";
- }
-}
-
-static void set_data_from_buffer(
- const char *buffer, size_t buffer_len, git_map *map, git_diff_file *file)
-{
- file->size = (git_off_t)buffer_len;
- file->mode = 0644;
- map->len = buffer_len;
-
- if (!buffer) {
- file->flags |= GIT_DIFF_FLAG__NO_DATA;
- map->data = NULL;
- } else {
- map->data = (char *)buffer;
- git_odb_hash(&file->oid, buffer, buffer_len, GIT_OBJ_BLOB);
- }
-}
-
-typedef struct {
- diff_context ctxt;
- git_diff_delta delta;
- git_diff_patch patch;
-} diff_single_data;
-
-static int diff_single_init(
- diff_single_data *data,
- git_repository *repo,
- const git_diff_options *opts,
- git_diff_file_cb file_cb,
- git_diff_hunk_cb hunk_cb,
- git_diff_data_cb data_cb,
- void *payload)
-{
- GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options");
-
- memset(data, 0, sizeof(*data));
-
- if (diff_context_init(
- &data->ctxt, NULL, repo, opts,
- file_cb, hunk_cb, data_cb, payload) < 0)
- return -1;
-
- diff_patch_init(&data->ctxt, &data->patch);
-
- return 0;
-}
-
-static int diff_single_apply(diff_single_data *data)
-{
- int error;
- git_diff_delta *delta = &data->delta;
- bool has_old = ((delta->old_file.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
- bool has_new = ((delta->new_file.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
-
- /* finish setting up fake git_diff_delta record and loaded data */
-
- data->patch.delta = delta;
- delta->flags = delta->flags & ~KNOWN_BINARY_FLAGS;
-
- delta->status = has_new ?
- (has_old ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) :
- (has_old ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED);
-
- if (git_oid__cmp(&delta->new_file.oid, &delta->old_file.oid) == 0)
- delta->status = GIT_DELTA_UNMODIFIED;
-
- if ((error = diff_delta_is_binary_by_content(
- &data->ctxt, delta, &delta->old_file, &data->patch.old_data)) < 0 ||
- (error = diff_delta_is_binary_by_content(
- &data->ctxt, delta, &delta->new_file, &data->patch.new_data)) < 0)
- goto cleanup;
-
- data->patch.flags |= GIT_DIFF_PATCH_LOADED;
-
- if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0 &&
- delta->status != GIT_DELTA_UNMODIFIED)
- data->patch.flags |= GIT_DIFF_PATCH_DIFFABLE;
-
- /* do diffs */
-
- if (!(error = diff_delta_file_callback(&data->ctxt, delta, 1)))
- error = diff_patch_generate(&data->ctxt, &data->patch);
-
-cleanup:
- if (error == GIT_EUSER)
- giterr_clear();
-
- diff_patch_unload(&data->patch);
-
- return error;
-}
-
-int git_diff_blobs(
- const git_blob *old_blob,
- const git_blob *new_blob,
- const git_diff_options *options,
- git_diff_file_cb file_cb,
- git_diff_hunk_cb hunk_cb,
- git_diff_data_cb data_cb,
- void *payload)
-{
- int error;
- diff_single_data d;
- git_repository *repo =
- new_blob ? git_object_owner((const git_object *)new_blob) :
- old_blob ? git_object_owner((const git_object *)old_blob) : NULL;
-
- if (!repo) /* Hmm, given two NULL blobs, silently do no callbacks? */
- return 0;
-
- if ((error = diff_single_init(
- &d, repo, options, file_cb, hunk_cb, data_cb, payload)) < 0)
- return error;
-
- if (options && (options->flags & GIT_DIFF_REVERSE) != 0) {
- const git_blob *swap = old_blob;
- old_blob = new_blob;
- new_blob = swap;
- }
-
- set_data_from_blob(old_blob, &d.patch.old_data, &d.delta.old_file);
- set_data_from_blob(new_blob, &d.patch.new_data, &d.delta.new_file);
-
- return diff_single_apply(&d);
-}
-
-int git_diff_blob_to_buffer(
- const git_blob *old_blob,
- const char *buf,
- size_t buflen,
- const git_diff_options *options,
- git_diff_file_cb file_cb,
- git_diff_hunk_cb hunk_cb,
- git_diff_data_cb data_cb,
- void *payload)
-{
- int error;
- diff_single_data d;
- git_repository *repo =
- old_blob ? git_object_owner((const git_object *)old_blob) : NULL;
-
- if (!repo && !buf) /* Hmm, given NULLs, silently do no callbacks? */
- return 0;
-
- if ((error = diff_single_init(
- &d, repo, options, file_cb, hunk_cb, data_cb, payload)) < 0)
- return error;
-
- if (options && (options->flags & GIT_DIFF_REVERSE) != 0) {
- set_data_from_buffer(buf, buflen, &d.patch.old_data, &d.delta.old_file);
- set_data_from_blob(old_blob, &d.patch.new_data, &d.delta.new_file);
- } else {
- set_data_from_blob(old_blob, &d.patch.old_data, &d.delta.old_file);
- set_data_from_buffer(buf, buflen, &d.patch.new_data, &d.delta.new_file);
- }
-
- return diff_single_apply(&d);
-}
-
-size_t git_diff_num_deltas(git_diff_list *diff)
-{
- assert(diff);
- return (size_t)diff->deltas.length;
-}
-
-size_t git_diff_num_deltas_of_type(git_diff_list *diff, git_delta_t type)
-{
- size_t i, count = 0;
- git_diff_delta *delta;
-
- assert(diff);
-
- git_vector_foreach(&diff->deltas, i, delta) {
- count += (delta->status == type);
- }
-
- return count;
-}
-
-int git_diff_get_patch(
- git_diff_patch **patch_ptr,
- const git_diff_delta **delta_ptr,
- git_diff_list *diff,
- size_t idx)
-{
- int error;
- diff_context ctxt;
- git_diff_delta *delta;
- git_diff_patch *patch;
-
- if (patch_ptr)
- *patch_ptr = NULL;
- if (delta_ptr)
- *delta_ptr = NULL;
-
- if (diff_required(diff, "git_diff_get_patch") < 0)
- return -1;
-
- if (diff_context_init(
- &ctxt, diff, NULL, NULL,
- NULL, diff_patch_hunk_cb, diff_patch_line_cb, NULL) < 0)
- return -1;
-
- delta = git_vector_get(&diff->deltas, idx);
- if (!delta) {
- giterr_set(GITERR_INVALID, "Index out of range for delta in diff");
- return GIT_ENOTFOUND;
- }
-
- if (delta_ptr)
- *delta_ptr = delta;
-
- if (!patch_ptr &&
- ((delta->flags & KNOWN_BINARY_FLAGS) != 0 ||
- (diff->opts.flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0))
- return 0;
-
- if (git_diff_delta__should_skip(ctxt.opts, delta))
- return 0;
-
- /* Don't load the patch if the user doesn't want it */
- if (!patch_ptr)
- return 0;
-
- patch = diff_patch_alloc(&ctxt, delta);
- if (!patch)
- return -1;
-
- if (!(error = diff_patch_load(&ctxt, patch))) {
- ctxt.payload = patch;
-
- error = diff_patch_generate(&ctxt, patch);
-
- if (error == GIT_EUSER)
- error = ctxt.error;
- }
-
- if (error)
- git_diff_patch_free(patch);
- else if (patch_ptr)
- *patch_ptr = patch;
-
- return error;
-}
-
-void git_diff_patch_free(git_diff_patch *patch)
-{
- if (patch)
- GIT_REFCOUNT_DEC(patch, diff_patch_free);
-}
-
-const git_diff_delta *git_diff_patch_delta(git_diff_patch *patch)
-{
- assert(patch);
- return patch->delta;
-}
-
-size_t git_diff_patch_num_hunks(git_diff_patch *patch)
-{
- assert(patch);
- return patch->hunks_size;
-}
-
-int git_diff_patch_line_stats(
- size_t *total_ctxt,
- size_t *total_adds,
- size_t *total_dels,
- const git_diff_patch *patch)
-{
- size_t totals[3], idx;
-
- memset(totals, 0, sizeof(totals));
-
- for (idx = 0; idx < patch->lines_size; ++idx) {
- switch (patch->lines[idx].origin) {
- case GIT_DIFF_LINE_CONTEXT: totals[0]++; break;
- case GIT_DIFF_LINE_ADDITION: totals[1]++; break;
- case GIT_DIFF_LINE_DELETION: totals[2]++; break;
- default:
- /* diff --stat and --numstat don't count EOFNL marks because
- * they will always be paired with a ADDITION or DELETION line.
- */
- break;
- }
- }
-
- if (total_ctxt)
- *total_ctxt = totals[0];
- if (total_adds)
- *total_adds = totals[1];
- if (total_dels)
- *total_dels = totals[2];
-
- return 0;
-}
-
-static int diff_error_outofrange(const char *thing)
-{
- giterr_set(GITERR_INVALID, "Diff patch %s index out of range", thing);
- return GIT_ENOTFOUND;
-}
-
-int git_diff_patch_get_hunk(
- const git_diff_range **range,
- const char **header,
- size_t *header_len,
- size_t *lines_in_hunk,
- git_diff_patch *patch,
- size_t hunk_idx)
-{
- diff_patch_hunk *hunk;
-
- assert(patch);
-
- if (hunk_idx >= patch->hunks_size) {
- if (range) *range = NULL;
- if (header) *header = NULL;
- if (header_len) *header_len = 0;
- if (lines_in_hunk) *lines_in_hunk = 0;
-
- return diff_error_outofrange("hunk");
- }
-
- hunk = &patch->hunks[hunk_idx];
-
- if (range) *range = &hunk->range;
- if (header) *header = hunk->header;
- if (header_len) *header_len = hunk->header_len;
- if (lines_in_hunk) *lines_in_hunk = hunk->line_count;
-
- return 0;
-}
-
-int git_diff_patch_num_lines_in_hunk(
- git_diff_patch *patch,
- size_t hunk_idx)
-{
- assert(patch);
-
- if (hunk_idx >= patch->hunks_size)
- return diff_error_outofrange("hunk");
- else
- return (int)patch->hunks[hunk_idx].line_count;
-}
-
-int git_diff_patch_get_line_in_hunk(
- char *line_origin,
- const char **content,
- size_t *content_len,
- int *old_lineno,
- int *new_lineno,
- git_diff_patch *patch,
- size_t hunk_idx,
- size_t line_of_hunk)
-{
- diff_patch_hunk *hunk;
- diff_patch_line *line;
- const char *thing;
-
- assert(patch);
-
- if (hunk_idx >= patch->hunks_size) {
- thing = "hunk";
- goto notfound;
- }
- hunk = &patch->hunks[hunk_idx];
-
- if (line_of_hunk >= hunk->line_count) {
- thing = "link";
- goto notfound;
- }
-
- line = &patch->lines[hunk->line_start + line_of_hunk];
-
- if (line_origin) *line_origin = line->origin;
- if (content) *content = line->ptr;
- if (content_len) *content_len = line->len;
- if (old_lineno) *old_lineno = (int)line->oldno;
- if (new_lineno) *new_lineno = (int)line->newno;
-
- return 0;
-
-notfound:
- if (line_origin) *line_origin = GIT_DIFF_LINE_CONTEXT;
- if (content) *content = NULL;
- if (content_len) *content_len = 0;
- if (old_lineno) *old_lineno = -1;
- if (new_lineno) *new_lineno = -1;
-
- return diff_error_outofrange(thing);
-}
-
-int git_diff__paired_foreach(
- git_diff_list *idx2head,
- git_diff_list *wd2idx,
- int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload),
- void *payload)
-{
- int cmp;
- git_diff_delta *i2h, *w2i;
- size_t i, j, i_max, j_max;
- int (*strcomp)(const char *, const char *);
-
- i_max = idx2head ? idx2head->deltas.length : 0;
- j_max = wd2idx ? wd2idx->deltas.length : 0;
-
- /* Get appropriate strcmp function */
- strcomp = idx2head ? idx2head->strcomp : wd2idx ? wd2idx->strcomp : NULL;
-
- /* Assert both iterators use matching ignore-case. If this function ever
- * supports merging diffs that are not sorted by the same function, then
- * it will need to spool and sort on one of the results before merging
- */
- if (idx2head && wd2idx) {
- assert(idx2head->strcomp == wd2idx->strcomp);
- }
-
- for (i = 0, j = 0; i < i_max || j < j_max; ) {
- i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL;
- w2i = wd2idx ? GIT_VECTOR_GET(&wd2idx->deltas,j) : NULL;
-
- cmp = !w2i ? -1 : !i2h ? 1 :
- strcomp(i2h->old_file.path, w2i->old_file.path);
-
- if (cmp < 0) {
- if (cb(i2h, NULL, payload))
- return GIT_EUSER;
- i++;
- } else if (cmp > 0) {
- if (cb(NULL, w2i, payload))
- return GIT_EUSER;
- j++;
- } else {
- if (cb(i2h, w2i, payload))
- return GIT_EUSER;
- i++; j++;
- }
- }
-
- return 0;
-}
diff --git a/src/diff_output.h b/src/diff_output.h
deleted file mode 100644
index 083355676..000000000
--- a/src/diff_output.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_diff_output_h__
-#define INCLUDE_diff_output_h__
-
-#include "git2/blob.h"
-#include "diff.h"
-#include "map.h"
-#include "xdiff/xdiff.h"
-
-#define MAX_DIFF_FILESIZE 0x20000000
-
-enum {
- GIT_DIFF_PATCH_ALLOCATED = (1 << 0),
- GIT_DIFF_PATCH_PREPPED = (1 << 1),
- GIT_DIFF_PATCH_LOADED = (1 << 2),
- GIT_DIFF_PATCH_DIFFABLE = (1 << 3),
- GIT_DIFF_PATCH_DIFFED = (1 << 4),
-};
-
-/* context for performing diffs */
-typedef struct {
- git_repository *repo;
- git_diff_list *diff;
- const git_diff_options *opts;
- git_diff_file_cb file_cb;
- git_diff_hunk_cb hunk_cb;
- git_diff_data_cb data_cb;
- void *payload;
- int error;
- git_diff_range range;
- xdemitconf_t xdiff_config;
- xpparam_t xdiff_params;
-} diff_context;
-
-/* cached information about a single span in a diff */
-typedef struct diff_patch_line diff_patch_line;
-struct diff_patch_line {
- const char *ptr;
- size_t len;
- size_t lines, oldno, newno;
- char origin;
-};
-
-/* cached information about a hunk in a diff */
-typedef struct diff_patch_hunk diff_patch_hunk;
-struct diff_patch_hunk {
- git_diff_range range;
- char header[128];
- size_t header_len;
- size_t line_start;
- size_t line_count;
-};
-
-struct git_diff_patch {
- git_refcount rc;
- git_diff_list *diff; /* for refcount purposes, maybe NULL for blob diffs */
- git_diff_delta *delta;
- diff_context *ctxt; /* only valid while generating patch */
- git_iterator_type_t old_src;
- git_iterator_type_t new_src;
- git_blob *old_blob;
- git_blob *new_blob;
- git_map old_data;
- git_map new_data;
- uint32_t flags;
- diff_patch_hunk *hunks;
- size_t hunks_asize, hunks_size;
- diff_patch_line *lines;
- size_t lines_asize, lines_size;
- size_t oldno, newno;
-};
-
-/* context for performing diff on a single delta */
-typedef struct {
- git_diff_patch *patch;
- uint32_t prepped : 1;
- uint32_t loaded : 1;
- uint32_t diffable : 1;
- uint32_t diffed : 1;
-} diff_delta_context;
-
-extern int git_diff__paired_foreach(
- git_diff_list *idx2head,
- git_diff_list *wd2idx,
- int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload),
- void *payload);
-
-#endif
diff --git a/src/diff_patch.c b/src/diff_patch.c
new file mode 100644
index 000000000..d7eb69db6
--- /dev/null
+++ b/src/diff_patch.c
@@ -0,0 +1,723 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#include "common.h"
+#include "diff.h"
+#include "diff_file.h"
+#include "diff_driver.h"
+#include "diff_patch.h"
+#include "diff_xdiff.h"
+
+static void diff_output_init(git_diff_output*, const git_diff_options*,
+ git_diff_file_cb, git_diff_hunk_cb, git_diff_data_cb, void*);
+
+static void diff_output_to_patch(git_diff_output *, git_diff_patch *);
+
+static void diff_patch_update_binary(git_diff_patch *patch)
+{
+ if ((patch->delta->flags & DIFF_FLAGS_KNOWN_BINARY) != 0)
+ return;
+
+ if ((patch->ofile.file.flags & GIT_DIFF_FLAG_BINARY) != 0 ||
+ (patch->nfile.file.flags & GIT_DIFF_FLAG_BINARY) != 0)
+ patch->delta->flags |= GIT_DIFF_FLAG_BINARY;
+
+ else if ((patch->ofile.file.flags & DIFF_FLAGS_NOT_BINARY) != 0 &&
+ (patch->nfile.file.flags & DIFF_FLAGS_NOT_BINARY) != 0)
+ patch->delta->flags |= GIT_DIFF_FLAG_NOT_BINARY;
+}
+
+static void diff_patch_init_common(git_diff_patch *patch)
+{
+ diff_patch_update_binary(patch);
+
+ if ((patch->delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
+ patch->flags |= GIT_DIFF_PATCH_LOADED; /* set LOADED but not DIFFABLE */
+
+ patch->flags |= GIT_DIFF_PATCH_INITIALIZED;
+
+ if (patch->diff)
+ git_diff_list_addref(patch->diff);
+}
+
+static int diff_patch_init_from_diff(
+ git_diff_patch *patch, git_diff_list *diff, size_t delta_index)
+{
+ int error = 0;
+
+ memset(patch, 0, sizeof(*patch));
+ patch->diff = diff;
+ patch->delta = git_vector_get(&diff->deltas, delta_index);
+ patch->delta_index = delta_index;
+
+ if ((error = diff_file_content_init_from_diff(
+ &patch->ofile, diff, delta_index, true)) < 0 ||
+ (error = diff_file_content_init_from_diff(
+ &patch->nfile, diff, delta_index, false)) < 0)
+ return error;
+
+ diff_patch_init_common(patch);
+
+ return 0;
+}
+
+static int diff_patch_alloc_from_diff(
+ git_diff_patch **out,
+ git_diff_list *diff,
+ size_t delta_index)
+{
+ int error;
+ git_diff_patch *patch = git__calloc(1, sizeof(git_diff_patch));
+ GITERR_CHECK_ALLOC(patch);
+
+ if (!(error = diff_patch_init_from_diff(patch, diff, delta_index))) {
+ patch->flags |= GIT_DIFF_PATCH_ALLOCATED;
+ GIT_REFCOUNT_INC(patch);
+ } else {
+ git__free(patch);
+ patch = NULL;
+ }
+
+ *out = patch;
+ return error;
+}
+
+static int diff_patch_load(git_diff_patch *patch, git_diff_output *output)
+{
+ int error = 0;
+ bool incomplete_data;
+
+ if ((patch->flags & GIT_DIFF_PATCH_LOADED) != 0)
+ return 0;
+
+ /* if no hunk and data callbacks and user doesn't care if data looks
+ * binary, then there is no need to actually load the data
+ */
+ if (patch->ofile.opts &&
+ (patch->ofile.opts->flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0 &&
+ output && !output->hunk_cb && !output->data_cb)
+ return 0;
+
+#define DIFF_FLAGS_KNOWN_DATA (GIT_DIFF_FLAG__NO_DATA|GIT_DIFF_FLAG_VALID_OID)
+
+ incomplete_data =
+ ((patch->ofile.file.flags & DIFF_FLAGS_KNOWN_DATA) != 0 &&
+ (patch->nfile.file.flags & DIFF_FLAGS_KNOWN_DATA) != 0);
+
+ /* always try to load workdir content first because filtering may
+ * need 2x data size and this minimizes peak memory footprint
+ */
+ if (patch->ofile.src == GIT_ITERATOR_TYPE_WORKDIR) {
+ if ((error = diff_file_content_load(&patch->ofile)) < 0 ||
+ (patch->ofile.file.flags & GIT_DIFF_FLAG_BINARY) != 0)
+ goto cleanup;
+ }
+ if (patch->nfile.src == GIT_ITERATOR_TYPE_WORKDIR) {
+ if ((error = diff_file_content_load(&patch->nfile)) < 0 ||
+ (patch->nfile.file.flags & GIT_DIFF_FLAG_BINARY) != 0)
+ goto cleanup;
+ }
+
+ /* once workdir has been tried, load other data as needed */
+ if (patch->ofile.src != GIT_ITERATOR_TYPE_WORKDIR) {
+ if ((error = diff_file_content_load(&patch->ofile)) < 0 ||
+ (patch->ofile.file.flags & GIT_DIFF_FLAG_BINARY) != 0)
+ goto cleanup;
+ }
+ if (patch->nfile.src != GIT_ITERATOR_TYPE_WORKDIR) {
+ if ((error = diff_file_content_load(&patch->nfile)) < 0 ||
+ (patch->nfile.file.flags & GIT_DIFF_FLAG_BINARY) != 0)
+ goto cleanup;
+ }
+
+ /* if we were previously missing an oid, reassess UNMODIFIED state */
+ if (incomplete_data &&
+ patch->ofile.file.mode == patch->nfile.file.mode &&
+ git_oid_equal(&patch->ofile.file.oid, &patch->nfile.file.oid))
+ patch->delta->status = GIT_DELTA_UNMODIFIED;
+
+cleanup:
+ diff_patch_update_binary(patch);
+
+ if (!error) {
+ /* patch is diffable only for non-binary, modified files where
+ * at least one side has data and the data actually changed
+ */
+ if ((patch->delta->flags & GIT_DIFF_FLAG_BINARY) == 0 &&
+ patch->delta->status != GIT_DELTA_UNMODIFIED &&
+ (patch->ofile.map.len || patch->nfile.map.len) &&
+ (patch->ofile.map.len != patch->nfile.map.len ||
+ !git_oid_equal(&patch->ofile.file.oid, &patch->nfile.file.oid)))
+ patch->flags |= GIT_DIFF_PATCH_DIFFABLE;
+
+ patch->flags |= GIT_DIFF_PATCH_LOADED;
+ }
+
+ return error;
+}
+
+static int diff_patch_file_callback(
+ git_diff_patch *patch, git_diff_output *output)
+{
+ float progress;
+
+ if (!output->file_cb)
+ return 0;
+
+ progress = patch->diff ?
+ ((float)patch->delta_index / patch->diff->deltas.length) : 1.0f;
+
+ if (output->file_cb(patch->delta, progress, output->payload) != 0)
+ output->error = GIT_EUSER;
+
+ return output->error;
+}
+
+static int diff_patch_generate(git_diff_patch *patch, git_diff_output *output)
+{
+ int error = 0;
+
+ if ((patch->flags & GIT_DIFF_PATCH_DIFFED) != 0)
+ return 0;
+
+ if ((patch->flags & GIT_DIFF_PATCH_LOADED) == 0 &&
+ (error = diff_patch_load(patch, output)) < 0)
+ return error;
+
+ if ((patch->flags & GIT_DIFF_PATCH_DIFFABLE) == 0)
+ return 0;
+
+ if (output->diff_cb != NULL &&
+ !(error = output->diff_cb(output, patch)))
+ patch->flags |= GIT_DIFF_PATCH_DIFFED;
+
+ return error;
+}
+
+static void diff_patch_free(git_diff_patch *patch)
+{
+ diff_file_content_clear(&patch->ofile);
+ diff_file_content_clear(&patch->nfile);
+
+ git_array_clear(patch->lines);
+ git_array_clear(patch->hunks);
+
+ git_diff_list_free(patch->diff); /* decrements refcount */
+ patch->diff = NULL;
+
+ git_pool_clear(&patch->flattened);
+
+ if (patch->flags & GIT_DIFF_PATCH_ALLOCATED)
+ git__free(patch);
+}
+
+static int diff_required(git_diff_list *diff, const char *action)
+{
+ if (diff)
+ return 0;
+ giterr_set(GITERR_INVALID, "Must provide valid diff to %s", action);
+ return -1;
+}
+
+int git_diff_foreach(
+ git_diff_list *diff,
+ git_diff_file_cb file_cb,
+ git_diff_hunk_cb hunk_cb,
+ git_diff_data_cb data_cb,
+ void *payload)
+{
+ int error = 0;
+ git_xdiff_output xo;
+ size_t idx;
+ git_diff_patch patch;
+
+ if (diff_required(diff, "git_diff_foreach") < 0)
+ return -1;
+
+ diff_output_init((git_diff_output *)&xo,
+ &diff->opts, file_cb, hunk_cb, data_cb, payload);
+ git_xdiff_init(&xo, &diff->opts);
+
+ git_vector_foreach(&diff->deltas, idx, patch.delta) {
+ /* check flags against patch status */
+ if (git_diff_delta__should_skip(&diff->opts, patch.delta))
+ continue;
+
+ if (!(error = diff_patch_init_from_diff(&patch, diff, idx))) {
+
+ error = diff_patch_file_callback(&patch, (git_diff_output *)&xo);
+
+ if (!error)
+ error = diff_patch_generate(&patch, (git_diff_output *)&xo);
+
+ git_diff_patch_free(&patch);
+ }
+
+ if (error < 0)
+ break;
+ }
+
+ if (error == GIT_EUSER)
+ giterr_clear(); /* don't leave error message set invalidly */
+ return error;
+}
+
+typedef struct {
+ git_xdiff_output xo;
+ git_diff_patch patch;
+ git_diff_delta delta;
+} diff_single_info;
+
+static int diff_single_generate(diff_single_info *info)
+{
+ int error = 0;
+ git_diff_patch *patch = &info->patch;
+ bool has_old = ((patch->ofile.file.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
+ bool has_new = ((patch->nfile.file.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
+
+ info->delta.status = has_new ?
+ (has_old ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) :
+ (has_old ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED);
+
+ if (git_oid_equal(&patch->nfile.file.oid, &patch->ofile.file.oid))
+ info->delta.status = GIT_DELTA_UNMODIFIED;
+
+ patch->delta = &info->delta;
+
+ diff_patch_init_common(patch);
+
+ error = diff_patch_file_callback(patch, (git_diff_output *)&info->xo);
+
+ if (!error)
+ error = diff_patch_generate(patch, (git_diff_output *)&info->xo);
+
+ if (error == GIT_EUSER)
+ giterr_clear(); /* don't leave error message set invalidly */
+
+ return error;
+}
+
+int git_diff_blobs(
+ const git_blob *old_blob,
+ const git_blob *new_blob,
+ const git_diff_options *opts,
+ git_diff_file_cb file_cb,
+ git_diff_hunk_cb hunk_cb,
+ git_diff_data_cb data_cb,
+ void *payload)
+{
+ int error = 0;
+ diff_single_info info;
+ git_repository *repo =
+ new_blob ? git_object_owner((const git_object *)new_blob) :
+ old_blob ? git_object_owner((const git_object *)old_blob) : NULL;
+
+ GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options");
+
+ if (!repo) /* Hmm, given two NULL blobs, silently do no callbacks? */
+ return 0;
+
+ if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) {
+ const git_blob *swap = old_blob;
+ old_blob = new_blob;
+ new_blob = swap;
+ }
+
+ memset(&info, 0, sizeof(info));
+
+ diff_output_init((git_diff_output *)&info.xo,
+ opts, file_cb, hunk_cb, data_cb, payload);
+ git_xdiff_init(&info.xo, opts);
+
+ if (!(error = diff_file_content_init_from_blob(
+ &info.patch.ofile, repo, opts, old_blob)) &&
+ !(error = diff_file_content_init_from_blob(
+ &info.patch.nfile, repo, opts, new_blob)))
+ error = diff_single_generate(&info);
+
+ git_diff_patch_free(&info.patch);
+
+ return error;
+}
+
+int git_diff_blob_to_buffer(
+ const git_blob *old_blob,
+ const char *buf,
+ size_t buflen,
+ const git_diff_options *opts,
+ git_diff_file_cb file_cb,
+ git_diff_hunk_cb hunk_cb,
+ git_diff_data_cb data_cb,
+ void *payload)
+{
+ int error = 0;
+ diff_single_info info;
+ git_repository *repo =
+ old_blob ? git_object_owner((const git_object *)old_blob) : NULL;
+
+ GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options");
+
+ if (!repo && !buf) /* Hmm, given NULLs, silently do no callbacks? */
+ return 0;
+
+ memset(&info, 0, sizeof(info));
+
+ diff_output_init((git_diff_output *)&info.xo,
+ opts, file_cb, hunk_cb, data_cb, payload);
+ git_xdiff_init(&info.xo, opts);
+
+ if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) {
+ if (!(error = diff_file_content_init_from_raw(
+ &info.patch.ofile, repo, opts, buf, buflen)))
+ error = diff_file_content_init_from_blob(
+ &info.patch.nfile, repo, opts, old_blob);
+ } else {
+ if (!(error = diff_file_content_init_from_blob(
+ &info.patch.ofile, repo, opts, old_blob)))
+ error = diff_file_content_init_from_raw(
+ &info.patch.nfile, repo, opts, buf, buflen);
+ }
+
+ error = diff_single_generate(&info);
+
+ git_diff_patch_free(&info.patch);
+
+ return error;
+}
+
+int git_diff_get_patch(
+ git_diff_patch **patch_ptr,
+ const git_diff_delta **delta_ptr,
+ git_diff_list *diff,
+ size_t idx)
+{
+ int error = 0;
+ git_xdiff_output xo;
+ git_diff_delta *delta = NULL;
+ git_diff_patch *patch = NULL;
+
+ if (patch_ptr) *patch_ptr = NULL;
+ if (delta_ptr) *delta_ptr = NULL;
+
+ if (diff_required(diff, "git_diff_get_patch") < 0)
+ return -1;
+
+ delta = git_vector_get(&diff->deltas, idx);
+ if (!delta) {
+ giterr_set(GITERR_INVALID, "Index out of range for delta in diff");
+ return GIT_ENOTFOUND;
+ }
+
+ if (delta_ptr)
+ *delta_ptr = delta;
+
+ if (git_diff_delta__should_skip(&diff->opts, delta))
+ return 0;
+
+ /* don't load the patch data unless we need it for binary check */
+ if (!patch_ptr &&
+ ((delta->flags & DIFF_FLAGS_KNOWN_BINARY) != 0 ||
+ (diff->opts.flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0))
+ return 0;
+
+ if ((error = diff_patch_alloc_from_diff(&patch, diff, idx)) < 0)
+ return error;
+
+ diff_output_to_patch((git_diff_output *)&xo, patch);
+ git_xdiff_init(&xo, &diff->opts);
+
+ error = diff_patch_file_callback(patch, (git_diff_output *)&xo);
+
+ if (!error)
+ error = diff_patch_generate(patch, (git_diff_output *)&xo);
+
+ if (!error) {
+ /* if cumulative diff size is < 0.5 total size, flatten the patch */
+ /* unload the file content */
+ }
+
+ if (error || !patch_ptr)
+ git_diff_patch_free(patch);
+ else
+ *patch_ptr = patch;
+
+ if (error == GIT_EUSER)
+ giterr_clear(); /* don't leave error message set invalidly */
+ return error;
+}
+
+void git_diff_patch_free(git_diff_patch *patch)
+{
+ if (patch)
+ GIT_REFCOUNT_DEC(patch, diff_patch_free);
+}
+
+const git_diff_delta *git_diff_patch_delta(git_diff_patch *patch)
+{
+ assert(patch);
+ return patch->delta;
+}
+
+size_t git_diff_patch_num_hunks(git_diff_patch *patch)
+{
+ assert(patch);
+ return git_array_size(patch->hunks);
+}
+
+int git_diff_patch_line_stats(
+ size_t *total_ctxt,
+ size_t *total_adds,
+ size_t *total_dels,
+ const git_diff_patch *patch)
+{
+ size_t totals[3], idx;
+
+ memset(totals, 0, sizeof(totals));
+
+ for (idx = 0; idx < git_array_size(patch->lines); ++idx) {
+ diff_patch_line *line = git_array_get(patch->lines, idx);
+ if (!line)
+ continue;
+
+ switch (line->origin) {
+ case GIT_DIFF_LINE_CONTEXT: totals[0]++; break;
+ case GIT_DIFF_LINE_ADDITION: totals[1]++; break;
+ case GIT_DIFF_LINE_DELETION: totals[2]++; break;
+ default:
+ /* diff --stat and --numstat don't count EOFNL marks because
+ * they will always be paired with a ADDITION or DELETION line.
+ */
+ break;
+ }
+ }
+
+ if (total_ctxt)
+ *total_ctxt = totals[0];
+ if (total_adds)
+ *total_adds = totals[1];
+ if (total_dels)
+ *total_dels = totals[2];
+
+ return 0;
+}
+
+static int diff_error_outofrange(const char *thing)
+{
+ giterr_set(GITERR_INVALID, "Diff patch %s index out of range", thing);
+ return GIT_ENOTFOUND;
+}
+
+int git_diff_patch_get_hunk(
+ const git_diff_range **range,
+ const char **header,
+ size_t *header_len,
+ size_t *lines_in_hunk,
+ git_diff_patch *patch,
+ size_t hunk_idx)
+{
+ diff_patch_hunk *hunk;
+ assert(patch);
+
+ hunk = git_array_get(patch->hunks, hunk_idx);
+
+ if (!hunk) {
+ if (range) *range = NULL;
+ if (header) *header = NULL;
+ if (header_len) *header_len = 0;
+ if (lines_in_hunk) *lines_in_hunk = 0;
+ return diff_error_outofrange("hunk");
+ }
+
+ if (range) *range = &hunk->range;
+ if (header) *header = hunk->header;
+ if (header_len) *header_len = hunk->header_len;
+ if (lines_in_hunk) *lines_in_hunk = hunk->line_count;
+ return 0;
+}
+
+int git_diff_patch_num_lines_in_hunk(git_diff_patch *patch, size_t hunk_idx)
+{
+ diff_patch_hunk *hunk;
+ assert(patch);
+
+ if (!(hunk = git_array_get(patch->hunks, hunk_idx)))
+ return diff_error_outofrange("hunk");
+ return (int)hunk->line_count;
+}
+
+int git_diff_patch_get_line_in_hunk(
+ char *line_origin,
+ const char **content,
+ size_t *content_len,
+ int *old_lineno,
+ int *new_lineno,
+ git_diff_patch *patch,
+ size_t hunk_idx,
+ size_t line_of_hunk)
+{
+ diff_patch_hunk *hunk;
+ diff_patch_line *line;
+ const char *thing;
+
+ assert(patch);
+
+ if (!(hunk = git_array_get(patch->hunks, hunk_idx))) {
+ thing = "hunk";
+ goto notfound;
+ }
+
+ if (line_of_hunk >= hunk->line_count ||
+ !(line = git_array_get(
+ patch->lines, hunk->line_start + line_of_hunk))) {
+ thing = "line";
+ goto notfound;
+ }
+
+ if (line_origin) *line_origin = line->origin;
+ if (content) *content = line->ptr;
+ if (content_len) *content_len = line->len;
+ if (old_lineno) *old_lineno = (int)line->oldno;
+ if (new_lineno) *new_lineno = (int)line->newno;
+
+ return 0;
+
+notfound:
+ if (line_origin) *line_origin = GIT_DIFF_LINE_CONTEXT;
+ if (content) *content = NULL;
+ if (content_len) *content_len = 0;
+ if (old_lineno) *old_lineno = -1;
+ if (new_lineno) *new_lineno = -1;
+
+ return diff_error_outofrange(thing);
+}
+
+
+static int diff_patch_file_cb(
+ const git_diff_delta *delta,
+ float progress,
+ void *payload)
+{
+ GIT_UNUSED(delta);
+ GIT_UNUSED(progress);
+ GIT_UNUSED(payload);
+ return 0;
+}
+
+static int diff_patch_hunk_cb(
+ const git_diff_delta *delta,
+ const git_diff_range *range,
+ const char *header,
+ size_t header_len,
+ void *payload)
+{
+ git_diff_patch *patch = payload;
+ diff_patch_hunk *hunk;
+
+ GIT_UNUSED(delta);
+
+ git_array_alloc(patch->hunks, hunk);
+ GITERR_CHECK_ALLOC(hunk);
+
+ memcpy(&hunk->range, range, sizeof(hunk->range));
+
+ assert(header_len + 1 < sizeof(hunk->header));
+ memcpy(&hunk->header, header, header_len);
+ hunk->header[header_len] = '\0';
+ hunk->header_len = header_len;
+
+ hunk->line_start = git_array_size(patch->lines);
+ hunk->line_count = 0;
+
+ patch->oldno = range->old_start;
+ patch->newno = range->new_start;
+
+ return 0;
+}
+
+static int diff_patch_line_cb(
+ const git_diff_delta *delta,
+ const git_diff_range *range,
+ char line_origin,
+ const char *content,
+ size_t content_len,
+ void *payload)
+{
+ git_diff_patch *patch = payload;
+ diff_patch_hunk *hunk;
+ diff_patch_line *line;
+
+ GIT_UNUSED(delta);
+ GIT_UNUSED(range);
+
+ hunk = git_array_last(patch->hunks);
+ GITERR_CHECK_ALLOC(hunk);
+
+ git_array_alloc(patch->lines, line);
+ GITERR_CHECK_ALLOC(line);
+
+ line->ptr = content;
+ line->len = content_len;
+ line->origin = line_origin;
+
+ patch->content_size += content_len;
+
+ /* do some bookkeeping so we can provide old/new line numbers */
+
+ for (line->lines = 0; content_len > 0; --content_len) {
+ if (*content++ == '\n')
+ ++line->lines;
+ }
+
+ switch (line_origin) {
+ case GIT_DIFF_LINE_ADDITION:
+ case GIT_DIFF_LINE_DEL_EOFNL:
+ line->oldno = -1;
+ line->newno = patch->newno;
+ patch->newno += line->lines;
+ break;
+ case GIT_DIFF_LINE_DELETION:
+ case GIT_DIFF_LINE_ADD_EOFNL:
+ line->oldno = patch->oldno;
+ line->newno = -1;
+ patch->oldno += line->lines;
+ break;
+ default:
+ line->oldno = patch->oldno;
+ line->newno = patch->newno;
+ patch->oldno += line->lines;
+ patch->newno += line->lines;
+ break;
+ }
+
+ hunk->line_count++;
+
+ return 0;
+}
+
+static void diff_output_init(
+ git_diff_output *out,
+ const git_diff_options *opts,
+ git_diff_file_cb file_cb,
+ git_diff_hunk_cb hunk_cb,
+ git_diff_data_cb data_cb,
+ void *payload)
+{
+ GIT_UNUSED(opts);
+
+ memset(out, 0, sizeof(*out));
+
+ out->file_cb = file_cb;
+ out->hunk_cb = hunk_cb;
+ out->data_cb = data_cb;
+ out->payload = payload;
+}
+
+static void diff_output_to_patch(git_diff_output *out, git_diff_patch *patch)
+{
+ diff_output_init(
+ out, patch->ofile.opts,
+ diff_patch_file_cb, diff_patch_hunk_cb, diff_patch_line_cb, patch);
+}
diff --git a/src/diff_patch.h b/src/diff_patch.h
new file mode 100644
index 000000000..7de6e1e5b
--- /dev/null
+++ b/src/diff_patch.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_diff_patch_h__
+#define INCLUDE_diff_patch_h__
+
+#include "common.h"
+#include "diff.h"
+#include "diff_file.h"
+#include "array.h"
+
+/* cached information about a single span in a diff */
+typedef struct diff_patch_line diff_patch_line;
+struct diff_patch_line {
+ const char *ptr;
+ size_t len;
+ size_t lines, oldno, newno;
+ char origin;
+};
+
+/* cached information about a hunk in a diff */
+typedef struct diff_patch_hunk diff_patch_hunk;
+struct diff_patch_hunk {
+ git_diff_range range;
+ char header[128];
+ size_t header_len;
+ size_t line_start;
+ size_t line_count;
+};
+
+struct git_diff_patch {
+ git_refcount rc;
+ git_diff_list *diff; /* for refcount purposes, maybe NULL for blob diffs */
+ git_diff_delta *delta;
+ size_t delta_index;
+ git_diff_file_content ofile;
+ git_diff_file_content nfile;
+ uint32_t flags;
+ git_array_t(diff_patch_hunk) hunks;
+ git_array_t(diff_patch_line) lines;
+ size_t oldno, newno;
+ size_t content_size;
+ git_pool flattened;
+};
+
+enum {
+ GIT_DIFF_PATCH_ALLOCATED = (1 << 0),
+ GIT_DIFF_PATCH_INITIALIZED = (1 << 1),
+ GIT_DIFF_PATCH_LOADED = (1 << 2),
+ GIT_DIFF_PATCH_DIFFABLE = (1 << 3),
+ GIT_DIFF_PATCH_DIFFED = (1 << 4),
+ GIT_DIFF_PATCH_FLATTENED = (1 << 5),
+};
+
+typedef struct git_diff_output git_diff_output;
+struct git_diff_output {
+ /* these callbacks are issued with the diff data */
+ git_diff_file_cb file_cb;
+ git_diff_hunk_cb hunk_cb;
+ git_diff_data_cb data_cb;
+ void *payload;
+
+ /* this records the actual error in cases where it may be obscured */
+ int error;
+
+ /* this callback is used to do the diff and drive the other callbacks.
+ * see diff_xdiff.h for how to use this in practice for now.
+ */
+ int (*diff_cb)(git_diff_output *output, git_diff_patch *patch);
+};
+
+#endif
diff --git a/src/diff_print.c b/src/diff_print.c
index b6fbec829..860876531 100644
--- a/src/diff_print.c
+++ b/src/diff_print.c
@@ -6,7 +6,8 @@
*/
#include "common.h"
#include "diff.h"
-#include "diff_output.h"
+#include "diff_patch.h"
+#include "buffer.h"
typedef struct {
git_diff_list *diff;
@@ -390,14 +391,15 @@ int git_diff_patch_print(
&pi, &temp, patch->diff, print_cb, payload)))
error = print_patch_file(patch->delta, 0, &pi);
- for (h = 0; h < patch->hunks_size && !error; ++h) {
- diff_patch_hunk *hunk = &patch->hunks[h];
+ for (h = 0; h < git_array_size(patch->hunks) && !error; ++h) {
+ diff_patch_hunk *hunk = git_array_get(patch->hunks, h);
error = print_patch_hunk(
patch->delta, &hunk->range, hunk->header, hunk->header_len, &pi);
for (l = 0; l < hunk->line_count && !error; ++l) {
- diff_patch_line *line = &patch->lines[hunk->line_start + l];
+ diff_patch_line *line =
+ git_array_get(patch->lines, hunk->line_start + l);
error = print_patch_line(
patch->delta, &hunk->range,
diff --git a/src/diff_tform.c b/src/diff_tform.c
index bc3acae1d..597c240ae 100644
--- a/src/diff_tform.c
+++ b/src/diff_tform.c
@@ -5,10 +5,14 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "common.h"
-#include "diff.h"
+
#include "git2/config.h"
#include "git2/blob.h"
+
+#include "diff.h"
#include "hashsig.h"
+#include "path.h"
+#include "fileops.h"
static git_diff_delta *diff_delta__dup(
const git_diff_delta *d, git_pool *pool)
diff --git a/src/diff_xdiff.c b/src/diff_xdiff.c
new file mode 100644
index 000000000..1d1c2d54c
--- /dev/null
+++ b/src/diff_xdiff.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#include "common.h"
+#include "diff.h"
+#include "diff_driver.h"
+#include "diff_patch.h"
+#include "diff_xdiff.h"
+
+static int git_xdiff_scan_int(const char **str, int *value)
+{
+ const char *scan = *str;
+ int v = 0, digits = 0;
+ /* find next digit */
+ for (scan = *str; *scan && !git__isdigit(*scan); scan++);
+ /* parse next number */
+ for (; git__isdigit(*scan); scan++, digits++)
+ v = (v * 10) + (*scan - '0');
+ *str = scan;
+ *value = v;
+ return (digits > 0) ? 0 : -1;
+}
+
+static int git_xdiff_parse_hunk(git_diff_range *range, const char *header)
+{
+ /* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */
+ if (*header != '@')
+ return -1;
+ if (git_xdiff_scan_int(&header, &range->old_start) < 0)
+ return -1;
+ if (*header == ',') {
+ if (git_xdiff_scan_int(&header, &range->old_lines) < 0)
+ return -1;
+ } else
+ range->old_lines = 1;
+ if (git_xdiff_scan_int(&header, &range->new_start) < 0)
+ return -1;
+ if (*header == ',') {
+ if (git_xdiff_scan_int(&header, &range->new_lines) < 0)
+ return -1;
+ } else
+ range->new_lines = 1;
+ if (range->old_start < 0 || range->new_start < 0)
+ return -1;
+
+ return 0;
+}
+
+typedef struct {
+ git_xdiff_output *xo;
+ git_diff_patch *patch;
+ git_diff_range range;
+} git_xdiff_info;
+
+static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len)
+{
+ git_xdiff_info *info = priv;
+ git_diff_patch *patch = info->patch;
+ git_diff_output *output = &info->xo->output;
+
+ if (len == 1) {
+ output->error = git_xdiff_parse_hunk(&info->range, bufs[0].ptr);
+ if (output->error < 0)
+ return output->error;
+
+ if (output->hunk_cb != NULL &&
+ output->hunk_cb(patch->delta, &info->range,
+ bufs[0].ptr, bufs[0].size, output->payload))
+ output->error = GIT_EUSER;
+ }
+
+ if (len == 2 || len == 3) {
+ /* expect " "/"-"/"+", then data */
+ char origin =
+ (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION :
+ (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION :
+ GIT_DIFF_LINE_CONTEXT;
+
+ if (output->data_cb != NULL &&
+ output->data_cb(patch->delta, &info->range,
+ origin, bufs[1].ptr, bufs[1].size, output->payload))
+ output->error = GIT_EUSER;
+ }
+
+ if (len == 3 && !output->error) {
+ /* If we have a '+' and a third buf, then we have added a line
+ * without a newline and the old code had one, so DEL_EOFNL.
+ * If we have a '-' and a third buf, then we have removed a line
+ * with out a newline but added a blank line, so ADD_EOFNL.
+ */
+ char origin =
+ (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_DEL_EOFNL :
+ (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_ADD_EOFNL :
+ GIT_DIFF_LINE_CONTEXT_EOFNL;
+
+ if (output->data_cb != NULL &&
+ output->data_cb(patch->delta, &info->range,
+ origin, bufs[2].ptr, bufs[2].size, output->payload))
+ output->error = GIT_EUSER;
+ }
+
+ return output->error;
+}
+
+static int git_xdiff(git_diff_output *output, git_diff_patch *patch)
+{
+ git_xdiff_output *xo = (git_xdiff_output *)output;
+ git_xdiff_info info;
+ mmfile_t old_xdiff_data, new_xdiff_data;
+
+ memset(&info, 0, sizeof(info));
+ info.patch = patch;
+ info.xo = xo;
+
+ xo->callback.priv = &info;
+
+ xo->config.find_func_priv = patch->ofile.driver;
+ xo->config.find_func = patch->ofile.driver ?
+ git_diff_driver_find_content_fn(patch->ofile.driver) : NULL;
+
+ if (xo->config.find_func != NULL)
+ xo->config.flags |= XDL_EMIT_FUNCNAMES;
+ else
+ xo->config.flags &= ~XDL_EMIT_FUNCNAMES;
+
+
+ old_xdiff_data.ptr = patch->ofile.map.data;
+ old_xdiff_data.size = patch->ofile.map.len;
+ new_xdiff_data.ptr = patch->nfile.map.data;
+ new_xdiff_data.size = patch->nfile.map.len;
+
+ xdl_diff(&old_xdiff_data, &new_xdiff_data,
+ &xo->params, &xo->config, &xo->callback);
+
+ return xo->output.error;
+}
+
+void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts)
+{
+ uint32_t flags = opts ? opts->flags : GIT_DIFF_NORMAL;
+
+ xo->output.diff_cb = git_xdiff;
+
+ memset(&xo->config, 0, sizeof(xo->config));
+ xo->config.ctxlen = opts ? opts->context_lines : 3;
+ xo->config.interhunkctxlen = opts ? opts->interhunk_lines : 0;
+
+ memset(&xo->params, 0, sizeof(xo->params));
+ if (flags & GIT_DIFF_IGNORE_WHITESPACE)
+ xo->params.flags |= XDF_WHITESPACE_FLAGS;
+ if (flags & GIT_DIFF_IGNORE_WHITESPACE_CHANGE)
+ xo->params.flags |= XDF_IGNORE_WHITESPACE_CHANGE;
+ if (flags & GIT_DIFF_IGNORE_WHITESPACE_EOL)
+ xo->params.flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
+
+ memset(&xo->callback, 0, sizeof(xo->callback));
+ xo->callback.outf = git_xdiff_cb;
+}
diff --git a/src/diff_xdiff.h b/src/diff_xdiff.h
new file mode 100644
index 000000000..c547b00cf
--- /dev/null
+++ b/src/diff_xdiff.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_diff_xdiff_h__
+#define INCLUDE_diff_xdiff_h__
+
+#include "diff.h"
+#include "diff_patch.h"
+#include "xdiff/xdiff.h"
+
+/* A git_xdiff_output is a git_diff_output with extra fields necessary
+ * to use libxdiff. Calling git_xdiff_init() will set the diff_cb field
+ * of the output to use xdiff to generate the diffs.
+ */
+typedef struct {
+ git_diff_output output;
+
+ xdemitconf_t config;
+ xpparam_t params;
+ xdemitcb_t callback;
+} git_xdiff_output;
+
+void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts);
+
+#endif
diff --git a/src/fetch.c b/src/fetch.c
index b5ec69777..03fad5fec 100644
--- a/src/fetch.c
+++ b/src/fetch.c
@@ -16,6 +16,8 @@
#include "pack.h"
#include "fetch.h"
#include "netops.h"
+#include "repository.h"
+#include "refs.h"
struct filter_payload {
git_remote *remote;
diff --git a/src/iterator.c b/src/iterator.c
index 4360b99ad..76b0e41d0 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -7,6 +7,7 @@
#include "iterator.h"
#include "tree.h"
+#include "index.h"
#include "ignore.h"
#include "buffer.h"
#include "git2/submodule.h"
diff --git a/src/merge.c b/src/merge.c
index 047d96013..82d2e6f37 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -24,6 +24,8 @@
#include "blob.h"
#include "hashsig.h"
#include "oid.h"
+#include "index.h"
+#include "filebuf.h"
#include "git2/types.h"
#include "git2/repository.h"
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index 4083ba9e5..b9e283ac5 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -9,6 +9,7 @@
#include "hash.h"
#include "repository.h"
#include "fileops.h"
+#include "filebuf.h"
#include "pack.h"
#include "reflog.h"
#include "refdb.h"
diff --git a/src/refs.c b/src/refs.c
index 2b545954d..c0e460cc3 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -9,6 +9,7 @@
#include "hash.h"
#include "repository.h"
#include "fileops.h"
+#include "filebuf.h"
#include "pack.h"
#include "reflog.h"
#include "refdb.h"
diff --git a/src/remote.h b/src/remote.h
index c9c26b77d..dce4803ed 100644
--- a/src/remote.h
+++ b/src/remote.h
@@ -11,7 +11,7 @@
#include "git2/transport.h"
#include "refspec.h"
-#include "repository.h"
+#include "vector.h"
#define GIT_REMOTE_ORIGIN "origin"
diff --git a/src/repository.c b/src/repository.c
index 2e7a334c9..4514fee23 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -17,12 +17,15 @@
#include "tag.h"
#include "blob.h"
#include "fileops.h"
+#include "filebuf.h"
+#include "index.h"
#include "config.h"
#include "refs.h"
#include "filter.h"
#include "odb.h"
#include "remote.h"
#include "merge.h"
+#include "diff_driver.h"
#define GIT_FILE_CONTENT_PREFIX "gitdir:"
@@ -108,6 +111,7 @@ void git_repository_free(git_repository *repo)
git_cache_free(&repo->objects);
git_submodule_config_free(repo);
+ git_diff_driver_registry_free(repo->diff_drivers);
git__free(repo->path_repository);
git__free(repo->workdir);
diff --git a/src/repository.h b/src/repository.h
index bd5f63dac..12dc50d51 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -14,15 +14,13 @@
#include "git2/object.h"
#include "git2/config.h"
-#include "index.h"
#include "cache.h"
#include "refs.h"
#include "buffer.h"
-#include "odb.h"
#include "object.h"
#include "attrcache.h"
#include "strmap.h"
-#include "refdb.h"
+#include "diff_driver.h"
#define DOT_GIT ".git"
#define GIT_DIR DOT_GIT "/"
@@ -108,6 +106,7 @@ struct git_repository {
git_cache objects;
git_attr_cache attrcache;
git_strmap *submodules;
+ git_diff_driver_registry *diff_drivers;
char *path_repository;
char *workdir;
diff --git a/src/signature.c b/src/signature.c
index cd6167fb4..4b8f03a21 100644
--- a/src/signature.c
+++ b/src/signature.c
@@ -9,6 +9,7 @@
#include "signature.h"
#include "repository.h"
#include "git2/common.h"
+#include "posix.h"
void git_signature_free(git_signature *sig)
{
diff --git a/src/stash.c b/src/stash.c
index 19b29be77..1222634d5 100644
--- a/src/stash.c
+++ b/src/stash.c
@@ -14,6 +14,7 @@
#include "git2/stash.h"
#include "git2/status.h"
#include "git2/checkout.h"
+#include "git2/index.h"
#include "signature.h"
static int create_error(int error, const char *msg)
diff --git a/src/status.c b/src/status.c
index 89f3eedb5..712e0d515 100644
--- a/src/status.c
+++ b/src/status.c
@@ -14,10 +14,10 @@
#include "git2/status.h"
#include "repository.h"
#include "ignore.h"
+#include "index.h"
#include "git2/diff.h"
#include "diff.h"
-#include "diff_output.h"
static unsigned int index_delta2status(git_delta_t index_status)
{
diff --git a/src/submodule.c b/src/submodule.c
index 16114d8ac..af488b7f3 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -22,6 +22,8 @@
#include "submodule.h"
#include "tree.h"
#include "iterator.h"
+#include "path.h"
+#include "index.h"
#define GIT_MODULES_FILE ".gitmodules"
diff --git a/src/thread-utils.h b/src/thread-utils.h
index 49b5f3b5e..f56f61b50 100644
--- a/src/thread-utils.h
+++ b/src/thread-utils.h
@@ -7,8 +7,6 @@
#ifndef INCLUDE_thread_utils_h__
#define INCLUDE_thread_utils_h__
-#include "common.h"
-
/* Common operations even if threading has been disabled */
typedef struct {
#if defined(GIT_WIN32)
diff --git a/src/tree.c b/src/tree.c
index 10d131438..65d01b4d5 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -10,6 +10,9 @@
#include "tree.h"
#include "git2/repository.h"
#include "git2/object.h"
+#include "path.h"
+#include "tree-cache.h"
+#include "index.h"
#define DEFAULT_TREE_SIZE 16
#define MAX_FILEMODE_BYTES 6
diff --git a/src/util.h b/src/util.h
index 5ae87ac10..43ba79240 100644
--- a/src/util.h
+++ b/src/util.h
@@ -194,6 +194,8 @@ extern int git__strcasecmp(const char *a, const char *b);
extern int git__strncmp(const char *a, const char *b, size_t sz);
extern int git__strncasecmp(const char *a, const char *b, size_t sz);
+#include "thread-utils.h"
+
typedef struct {
git_atomic refcount;
void *owner;
diff --git a/tests-clar/checkout/index.c b/tests-clar/checkout/index.c
index 78ff5ac62..a3a0f8fda 100644
--- a/tests-clar/checkout/index.c
+++ b/tests-clar/checkout/index.c
@@ -2,6 +2,7 @@
#include "checkout_helpers.h"
#include "git2/checkout.h"
+#include "fileops.h"
#include "repository.h"
static git_repository *g_repo;
diff --git a/tests-clar/clar.c b/tests-clar/clar.c
index fed87c30d..0eae81bf5 100644
--- a/tests-clar/clar.c
+++ b/tests-clar/clar.c
@@ -183,10 +183,10 @@ clar_run_test(
}
static void
-clar_run_suite(const struct clar_suite *suite)
+clar_run_suite(const struct clar_suite *suite, const char *name)
{
const struct clar_func *test = suite->tests;
- size_t i;
+ size_t i, namelen;
if (!suite->enabled)
return;
@@ -200,7 +200,23 @@ clar_run_suite(const struct clar_suite *suite)
_clar.active_suite = suite->name;
_clar.suite_errors = 0;
+ if (name) {
+ size_t suitelen = strlen(suite->name);
+ namelen = strlen(name);
+ if (namelen <= suitelen) {
+ name = NULL;
+ } else {
+ name += suitelen;
+ while (*name == ':')
+ ++name;
+ namelen = strlen(name);
+ }
+ }
+
for (i = 0; i < suite->test_count; ++i) {
+ if (name && strncmp(test[i].name, name, namelen))
+ continue;
+
_clar.active_test = test[i].name;
clar_run_test(&test[i], &suite->initialize, &suite->cleanup);
@@ -240,7 +256,7 @@ clar_parse_args(int argc, char **argv)
case 'x': { /* given suite name */
int offset = (argument[2] == '=') ? 3 : 2, found = 0;
char action = argument[1];
- size_t j, len;
+ size_t j, len, cmplen;
argument += offset;
len = strlen(argument);
@@ -249,7 +265,11 @@ clar_parse_args(int argc, char **argv)
clar_usage(argv[0]);
for (j = 0; j < _clar_suite_count; ++j) {
- if (strncmp(argument, _clar_suites[j].name, len) == 0) {
+ cmplen = strlen(_clar_suites[j].name);
+ if (cmplen > len)
+ cmplen = len;
+
+ if (strncmp(argument, _clar_suites[j].name, cmplen) == 0) {
int exact = !strcmp(argument, _clar_suites[j].name);
++found;
@@ -258,9 +278,9 @@ clar_parse_args(int argc, char **argv)
_clar.report_suite_names = 1;
switch (action) {
- case 's': clar_run_suite(&_clar_suites[j]); break;
- case 'i': _clar_suites[j].enabled = 1; break;
- case 'x': _clar_suites[j].enabled = 0; break;
+ case 's': clar_run_suite(&_clar_suites[j], argument); break;
+ case 'i': _clar_suites[j].enabled = 1; break;
+ case 'x': _clar_suites[j].enabled = 0; break;
}
if (exact)
@@ -318,7 +338,7 @@ clar_test(int argc, char **argv)
if (!_clar.suites_ran) {
size_t i;
for (i = 0; i < _clar_suite_count; ++i)
- clar_run_suite(&_clar_suites[i]);
+ clar_run_suite(&_clar_suites[i], NULL);
}
clar_print_shutdown(
diff --git a/tests-clar/clone/nonetwork.c b/tests-clar/clone/nonetwork.c
index 8aae1fb52..339b1e70d 100644
--- a/tests-clar/clone/nonetwork.c
+++ b/tests-clar/clone/nonetwork.c
@@ -1,8 +1,9 @@
#include "clar_libgit2.h"
#include "git2/clone.h"
-#include "repository.h"
#include "remote.h"
+#include "fileops.h"
+#include "repository.h"
#define LIVE_REPO_URL "git://github.com/libgit2/TestGitRepository"
diff --git a/tests-clar/diff/patch.c b/tests-clar/diff/patch.c
index f9e913a74..6390957c9 100644
--- a/tests-clar/diff/patch.c
+++ b/tests-clar/diff/patch.c
@@ -543,7 +543,7 @@ void test_diff_patch__line_counts_with_eofnl(void)
"index 378a7d9..3d0154e 100644\n"
"--- a/songof7cities.txt\n"
"+++ b/songof7cities.txt\n"
- "@@ -42,7 +42,7 @@\n"
+ "@@ -42,7 +42,7 @@ With peoples undefeated of the dark, enduring blood\n"
" \n"
" To the sound of trumpets shall their seed restore my Cities\n"
" Wealthy and well-weaponed, that once more may I behold\n"
diff --git a/tests-clar/diff/rename.c b/tests-clar/diff/rename.c
index 8bff96cf2..a9f1b4e20 100644
--- a/tests-clar/diff/rename.c
+++ b/tests-clar/diff/rename.c
@@ -558,7 +558,7 @@ void test_diff_rename__patch(void)
git_diff_patch *patch;
const git_diff_delta *delta;
char *text;
- const char *expected = "diff --git a/sixserving.txt b/ikeepsix.txt\nindex ad0a8e5..36020db 100644\n--- a/sixserving.txt\n+++ b/ikeepsix.txt\n@@ -1,3 +1,6 @@\n+I Keep Six Honest Serving-Men\n+=============================\n+\n I KEEP six honest serving-men\n (They taught me all I knew);\n Their names are What and Why and When\n@@ -21,4 +24,4 @@\n One million Hows, two million Wheres,\n And seven million Whys!\n \n- -- Rudyard Kipling\n+ -- Rudyard Kipling\n";
+ const char *expected = "diff --git a/sixserving.txt b/ikeepsix.txt\nindex ad0a8e5..36020db 100644\n--- a/sixserving.txt\n+++ b/ikeepsix.txt\n@@ -1,3 +1,6 @@\n+I Keep Six Honest Serving-Men\n+=============================\n+\n I KEEP six honest serving-men\n (They taught me all I knew);\n Their names are What and Why and When\n@@ -21,4 +24,4 @@ She sends'em abroad on her own affairs\n One million Hows, two million Wheres,\n And seven million Whys!\n \n- -- Rudyard Kipling\n+ -- Rudyard Kipling\n";
old_tree = resolve_commit_oid_to_tree(g_repo, sha0);
new_tree = resolve_commit_oid_to_tree(g_repo, sha1);
diff --git a/tests-clar/diff/submodules.c b/tests-clar/diff/submodules.c
index f152af46f..6e52a6319 100644
--- a/tests-clar/diff/submodules.c
+++ b/tests-clar/diff/submodules.c
@@ -1,5 +1,6 @@
#include "clar_libgit2.h"
#include "repository.h"
+#include "posix.h"
#include "../submodule/submodule_helpers.h"
static git_repository *g_repo = NULL;
diff --git a/tests-clar/fetchhead/nonetwork.c b/tests-clar/fetchhead/nonetwork.c
index ef30679f9..a68ebb0b7 100644
--- a/tests-clar/fetchhead/nonetwork.c
+++ b/tests-clar/fetchhead/nonetwork.c
@@ -1,6 +1,6 @@
#include "clar_libgit2.h"
-#include "repository.h"
+#include "fileops.h"
#include "fetchhead.h"
#include "fetchhead_data.h"
diff --git a/tests-clar/merge/merge_helpers.c b/tests-clar/merge/merge_helpers.c
index bc31b1f44..e4092787c 100644
--- a/tests-clar/merge/merge_helpers.c
+++ b/tests-clar/merge/merge_helpers.c
@@ -1,5 +1,5 @@
#include "clar_libgit2.h"
-#include "buffer.h"
+#include "fileops.h"
#include "refs.h"
#include "tree.h"
#include "merge_helpers.h"
diff --git a/tests-clar/odb/alternates.c b/tests-clar/odb/alternates.c
index be7bfa9cd..4e876c2b3 100644
--- a/tests-clar/odb/alternates.c
+++ b/tests-clar/odb/alternates.c
@@ -1,6 +1,6 @@
#include "clar_libgit2.h"
#include "odb.h"
-#include "repository.h"
+#include "filebuf.h"
static git_buf destpath, filepath;
static const char *paths[] = {
diff --git a/tests-clar/online/clone.c b/tests-clar/online/clone.c
index aa12e47c9..bc4285a00 100644
--- a/tests-clar/online/clone.c
+++ b/tests-clar/online/clone.c
@@ -2,8 +2,9 @@
#include "git2/clone.h"
#include "git2/cred_helpers.h"
-#include "repository.h"
#include "remote.h"
+#include "fileops.h"
+#include "refs.h"
#define LIVE_REPO_URL "http://github.com/libgit2/TestGitRepository"
#define LIVE_EMPTYREPO_URL "http://github.com/libgit2/TestEmptyRepository"
diff --git a/tests-clar/online/fetchhead.c b/tests-clar/online/fetchhead.c
index e14ae0926..58717eef8 100644
--- a/tests-clar/online/fetchhead.c
+++ b/tests-clar/online/fetchhead.c
@@ -1,6 +1,6 @@
#include "clar_libgit2.h"
-#include "repository.h"
+#include "fileops.h"
#include "fetchhead.h"
#include "../fetchhead/fetchhead_data.h"
#include "git2/clone.h"
diff --git a/tests-clar/refs/delete.c b/tests-clar/refs/delete.c
index 053f41229..973768aeb 100644
--- a/tests-clar/refs/delete.c
+++ b/tests-clar/refs/delete.c
@@ -1,7 +1,8 @@
#include "clar_libgit2.h"
-#include "repository.h"
+#include "fileops.h"
#include "git2/reflog.h"
+#include "git2/refdb.h"
#include "reflog.h"
#include "ref_helpers.h"
@@ -31,7 +32,7 @@ void test_refs_delete__packed_loose(void)
git_buf temp_path = GIT_BUF_INIT;
/* Ensure the loose reference exists on the file system */
- cl_git_pass(git_buf_joinpath(&temp_path, g_repo->path_repository, packed_test_head_name));
+ cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), packed_test_head_name));
cl_assert(git_path_exists(temp_path.ptr));
/* Lookup the reference */
diff --git a/tests-clar/refs/pack.c b/tests-clar/refs/pack.c
index 412c4c5fd..d8d5cc6d0 100644
--- a/tests-clar/refs/pack.c
+++ b/tests-clar/refs/pack.c
@@ -1,8 +1,10 @@
#include "clar_libgit2.h"
-#include "repository.h"
+#include "fileops.h"
#include "git2/reflog.h"
+#include "git2/refdb.h"
#include "reflog.h"
+#include "refs.h"
#include "ref_helpers.h"
static const char *loose_tag_ref_name = "refs/tags/e90810b";
@@ -33,7 +35,7 @@ void test_refs_pack__empty(void)
// create a packfile for an empty folder
git_buf temp_path = GIT_BUF_INIT;
- cl_git_pass(git_buf_join_n(&temp_path, '/', 3, g_repo->path_repository, GIT_REFS_HEADS_DIR, "empty_dir"));
+ cl_git_pass(git_buf_join_n(&temp_path, '/', 3, git_repository_path(g_repo), GIT_REFS_HEADS_DIR, "empty_dir"));
cl_git_pass(git_futils_mkdir_r(temp_path.ptr, NULL, GIT_REFS_DIR_MODE));
git_buf_free(&temp_path);
@@ -60,7 +62,7 @@ void test_refs_pack__loose(void)
packall();
/* Ensure the packed-refs file exists */
- cl_git_pass(git_buf_joinpath(&temp_path, g_repo->path_repository, GIT_PACKEDREFS_FILE));
+ cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), GIT_PACKEDREFS_FILE));
cl_assert(git_path_exists(temp_path.ptr));
/* Ensure the known ref can still be looked up but is now packed */
@@ -69,7 +71,7 @@ void test_refs_pack__loose(void)
cl_assert_equal_s(reference->name, loose_tag_ref_name);
/* Ensure the known ref has been removed from the loose folder structure */
- cl_git_pass(git_buf_joinpath(&temp_path, g_repo->path_repository, loose_tag_ref_name));
+ cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), loose_tag_ref_name));
cl_assert(!git_path_exists(temp_path.ptr));
git_reference_free(reference);
diff --git a/tests-clar/refs/reflog/reflog.c b/tests-clar/refs/reflog/reflog.c
index 1cd0ddd92..095cabf04 100644
--- a/tests-clar/refs/reflog/reflog.c
+++ b/tests-clar/refs/reflog/reflog.c
@@ -1,6 +1,6 @@
#include "clar_libgit2.h"
-#include "repository.h"
+#include "fileops.h"
#include "git2/reflog.h"
#include "reflog.h"
diff --git a/tests-clar/refs/rename.c b/tests-clar/refs/rename.c
index 5ab84c48e..543bc4d62 100644
--- a/tests-clar/refs/rename.c
+++ b/tests-clar/refs/rename.c
@@ -1,8 +1,9 @@
#include "clar_libgit2.h"
-#include "repository.h"
+#include "fileops.h"
#include "git2/reflog.h"
#include "reflog.h"
+#include "refs.h"
#include "ref_helpers.h"
static const char *loose_tag_ref_name = "refs/tags/e90810b";
@@ -38,7 +39,7 @@ void test_refs_rename__loose(void)
const char *new_name = "refs/tags/Nemo/knows/refs.kung-fu";
/* Ensure the ref doesn't exist on the file system */
- cl_git_pass(git_buf_joinpath(&temp_path, g_repo->path_repository, new_name));
+ cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), new_name));
cl_assert(!git_path_exists(temp_path.ptr));
/* Retrieval of the reference to rename */
@@ -64,7 +65,7 @@ void test_refs_rename__loose(void)
cl_assert(reference_is_packed(new_ref) == 0);
/* ...and the ref can be found in the file system */
- cl_git_pass(git_buf_joinpath(&temp_path, g_repo->path_repository, new_name));
+ cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), new_name));
cl_assert(git_path_exists(temp_path.ptr));
git_reference_free(new_ref);
@@ -80,7 +81,7 @@ void test_refs_rename__packed(void)
const char *brand_new_name = "refs/heads/brand_new_name";
/* Ensure the ref doesn't exist on the file system */
- cl_git_pass(git_buf_joinpath(&temp_path, g_repo->path_repository, packed_head_name));
+ cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), packed_head_name));
cl_assert(!git_path_exists(temp_path.ptr));
/* The reference can however be looked-up... */
@@ -106,7 +107,7 @@ void test_refs_rename__packed(void)
cl_assert(reference_is_packed(new_ref) == 0);
/* ...and the ref now happily lives in the file system */
- cl_git_pass(git_buf_joinpath(&temp_path, g_repo->path_repository, brand_new_name));
+ cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), brand_new_name));
cl_assert(git_path_exists(temp_path.ptr));
git_reference_free(new_ref);
@@ -122,7 +123,7 @@ void test_refs_rename__packed_doesnt_pack_others(void)
const char *brand_new_name = "refs/heads/brand_new_name";
/* Ensure the other reference exists on the file system */
- cl_git_pass(git_buf_joinpath(&temp_path, g_repo->path_repository, packed_test_head_name));
+ cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), packed_test_head_name));
cl_assert(git_path_exists(temp_path.ptr));
/* Lookup the other reference */
diff --git a/tests-clar/repo/discover.c b/tests-clar/repo/discover.c
index 3d9aeedd7..f93ff2462 100644
--- a/tests-clar/repo/discover.c
+++ b/tests-clar/repo/discover.c
@@ -1,9 +1,9 @@
#include "clar_libgit2.h"
#include "odb.h"
+#include "fileops.h"
#include "repository.h"
-
#define TEMP_REPO_FOLDER "temprepo/"
#define DISCOVER_FOLDER TEMP_REPO_FOLDER "discover.git"
diff --git a/tests-clar/status/ignore.c b/tests-clar/status/ignore.c
index 2d3898ba4..4f6879cfc 100644
--- a/tests-clar/status/ignore.c
+++ b/tests-clar/status/ignore.c
@@ -169,7 +169,7 @@ void test_status_ignore__ignore_pattern_ignorecase(void)
cl_git_mkfile("empty_standard_repo/A.txt", "Differs in case");
cl_git_pass(git_repository_index(&index, g_repo));
- ignore_case = index->ignore_case;
+ ignore_case = (git_index_caps(index) & GIT_INDEXCAP_IGNORE_CASE) != 0;
git_index_free(index);
cl_git_pass(git_status_file(&flags, g_repo, "A.txt"));
diff --git a/tests-clar/status/worktree.c b/tests-clar/status/worktree.c
index 062a09aeb..13335843b 100644
--- a/tests-clar/status/worktree.c
+++ b/tests-clar/status/worktree.c
@@ -105,7 +105,7 @@ void test_status_worktree__swap_subdir_and_file(void)
bool ignore_case;
cl_git_pass(git_repository_index(&index, repo));
- ignore_case = index->ignore_case;
+ ignore_case = (git_index_caps(index) & GIT_INDEXCAP_IGNORE_CASE) != 0;
git_index_free(index);
/* first alter the contents of the worktree */