summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/git2/attr.h10
-rw-r--r--include/git2/config.h50
-rw-r--r--src/attr_file.c12
-rw-r--r--src/blob.c172
-rw-r--r--src/blob.h1
-rw-r--r--src/buffer.c56
-rw-r--r--src/buffer.h11
-rw-r--r--src/config.c172
-rw-r--r--src/config_cache.c95
-rw-r--r--src/config_file.c22
-rw-r--r--src/crlf.c229
-rw-r--r--src/fileops.c74
-rw-r--r--src/fileops.h13
-rw-r--r--src/filter.c165
-rw-r--r--src/filter.h119
-rw-r--r--src/ignore.c6
-rw-r--r--src/index.c6
-rw-r--r--src/odb.c7
-rw-r--r--src/odb_loose.c26
-rw-r--r--src/reflog.c6
-rw-r--r--src/refs.c48
-rw-r--r--src/repository.c21
-rw-r--r--src/repository.h61
-rw-r--r--tests-clar/attr/attr_expect.h42
-rw-r--r--tests-clar/attr/file.c46
-rw-r--r--tests-clar/attr/lookup.c277
-rw-r--r--tests-clar/attr/repo.c161
-rw-r--r--tests-clar/clar_helpers.c12
-rw-r--r--tests-clar/clar_libgit2.h1
-rw-r--r--tests-clar/core/buffer.c12
-rw-r--r--tests-clar/core/path.c2
-rw-r--r--tests-clar/object/blob/filter.c125
-rw-r--r--tests/test_helpers.c17
33 files changed, 1555 insertions, 522 deletions
diff --git a/include/git2/attr.h b/include/git2/attr.h
index 7e8bb9fe8..81d1e517b 100644
--- a/include/git2/attr.h
+++ b/include/git2/attr.h
@@ -19,12 +19,12 @@
*/
GIT_BEGIN_DECL
-#define GIT_ATTR_TRUE git_attr__true
-#define GIT_ATTR_FALSE git_attr__false
-#define GIT_ATTR_UNSPECIFIED NULL
+#define GIT_ATTR_TRUE(attr) ((attr) == git_attr__true)
+#define GIT_ATTR_FALSE(attr) ((attr) == git_attr__false)
+#define GIT_ATTR_UNSPECIFIED(attr) ((attr) == NULL)
-GIT_EXTERN(const char *)git_attr__true;
-GIT_EXTERN(const char *)git_attr__false;
+GIT_EXTERN(const char *) git_attr__true;
+GIT_EXTERN(const char *) git_attr__false;
/**
diff --git a/include/git2/config.h b/include/git2/config.h
index 8a0f58937..acc45b018 100644
--- a/include/git2/config.h
+++ b/include/git2/config.h
@@ -37,6 +37,19 @@ struct git_config_file {
void (*free)(struct git_config_file *);
};
+typedef enum {
+ GIT_CVAR_FALSE = 0,
+ GIT_CVAR_TRUE = 1,
+ GIT_CVAR_INT32,
+ GIT_CVAR_STRING
+} git_cvar_t;
+
+typedef struct {
+ git_cvar_t cvar_type;
+ const char *str_match;
+ int map_value;
+} git_cvar_map;
+
/**
* Locate the path to the global configuration file
*
@@ -301,6 +314,43 @@ GIT_EXTERN(int) git_config_foreach(
int (*callback)(const char *var_name, const char *value, void *payload),
void *payload);
+
+/**
+ * Query the value of a config variable and return it mapped to
+ * an integer constant.
+ *
+ * This is a helper method to easily map different possible values
+ * to a variable to integer constants that easily identify them.
+ *
+ * A mapping array looks as follows:
+ *
+ * git_cvar_map autocrlf_mapping[3] = {
+ * {GIT_CVAR_FALSE, NULL, GIT_AUTO_CRLF_FALSE},
+ * {GIT_CVAR_TRUE, NULL, GIT_AUTO_CRLF_TRUE},
+ * {GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT},
+ * {GIT_CVAR_STRING, "default", GIT_AUTO_CRLF_DEFAULT}};
+ *
+ * On any "false" value for the variable (e.g. "false", "FALSE", "no"), the
+ * mapping will store `GIT_AUTO_CRLF_FALSE` in the `out` parameter.
+ *
+ * The same thing applies for any "true" value such as "true", "yes" or "1", storing
+ * the `GIT_AUTO_CRLF_TRUE` variable.
+ *
+ * Otherwise, if the value matches the string "input" (with case insensitive comparison),
+ * the given constant will be stored in `out`, and likewise for "default".
+ *
+ * If not a single match can be made to store in `out`, an error code will be
+ * returned.
+ *
+ * @param cfg config file to get the variables from
+ * @param name name of the config variable to lookup
+ * @param maps array of `git_cvar_map` objects specifying the possible mappings
+ * @param map_n number of mapping objects in `maps`
+ * @param out place to store the result of the mapping
+ * @return GIT_SUCCESS on success, error code otherwise
+ */
+GIT_EXTERN(int) git_config_get_mapped(git_config *cfg, const char *name, git_cvar_map *maps, size_t map_n, int *out);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/src/attr_file.c b/src/attr_file.c
index 7911381ea..3783b5ef3 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -111,7 +111,7 @@ int git_attr_file__from_file(
git_repository *repo, const char *path, git_attr_file *file)
{
int error = GIT_SUCCESS;
- git_fbuffer fbuf = GIT_FBUFFER_INIT;
+ git_buf fbuf = GIT_BUF_INIT;
assert(path && file);
@@ -120,9 +120,9 @@ int git_attr_file__from_file(
if (error == GIT_SUCCESS &&
(error = git_futils_readbuffer(&fbuf, path)) == GIT_SUCCESS)
- error = git_attr_file__from_buffer(repo, fbuf.data, file);
+ error = git_attr_file__from_buffer(repo, fbuf.ptr, file);
- git_futils_freebuffer(&fbuf);
+ git_buf_free(&fbuf);
if (error != GIT_SUCCESS)
git__rethrow(error, "Could not open attribute file '%s'", path);
@@ -458,12 +458,12 @@ int git_attr_assignment__parse(
}
assign->name_hash = 5381;
- assign->value = GIT_ATTR_TRUE;
+ assign->value = git_attr__true;
assign->is_allocated = 0;
/* look for magic name prefixes */
if (*scan == '-') {
- assign->value = GIT_ATTR_FALSE;
+ assign->value = git_attr__false;
scan++;
} else if (*scan == '!') {
assign->value = NULL; /* explicit unspecified state */
@@ -510,7 +510,7 @@ int git_attr_assignment__parse(
}
/* expand macros (if given a repo with a macro cache) */
- if (repo != NULL && assign->value == GIT_ATTR_TRUE) {
+ if (repo != NULL && assign->value == git_attr__true) {
git_attr_rule *macro =
git_hashtable_lookup(repo->attrcache.macros, assign->name);
diff --git a/src/blob.c b/src/blob.c
index 4065ffa12..b67f8afa5 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -11,6 +11,7 @@
#include "common.h"
#include "blob.h"
+#include "filter.h"
const void *git_blob_rawcontent(git_blob *blob)
{
@@ -24,6 +25,12 @@ size_t git_blob_rawsize(git_blob *blob)
return blob->odb_object->raw.len;
}
+int git_blob__getbuf(git_buf *buffer, git_blob *blob)
+{
+ return git_buf_set(
+ buffer, blob->odb_object->raw.data, blob->odb_object->raw.len);
+}
+
void git_blob__free(git_blob *blob)
{
git_odb_object_free(blob->odb_object);
@@ -65,15 +72,100 @@ int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *b
return GIT_SUCCESS;
}
+static int write_file_stream(git_oid *oid, git_odb *odb, const char *path, git_off_t file_size)
+{
+ int fd, error;
+ char buffer[4096];
+ git_odb_stream *stream = NULL;
+
+ if ((error = git_odb_open_wstream(&stream, odb, file_size, GIT_OBJ_BLOB)) < GIT_SUCCESS)
+ return error;
+
+ if ((fd = p_open(path, O_RDONLY)) < 0) {
+ error = git__throw(GIT_ENOTFOUND, "Failed to create blob. Could not open '%s'", path);
+ goto cleanup;
+ }
+
+ while (file_size > 0) {
+ ssize_t read_len = p_read(fd, buffer, sizeof(buffer));
+
+ if (read_len < 0) {
+ error = git__throw(GIT_EOSERR, "Failed to create blob. Can't read full file");
+ p_close(fd);
+ goto cleanup;
+ }
+
+ stream->write(stream, buffer, read_len);
+ file_size -= read_len;
+ }
+
+ p_close(fd);
+ error = stream->finalize_write(oid, stream);
+
+cleanup:
+ stream->free(stream);
+ return error;
+}
+
+static int write_file_filtered(
+ git_oid *oid,
+ git_odb *odb,
+ const char *full_path,
+ git_vector *filters)
+{
+ int error;
+ git_buf source = GIT_BUF_INIT;
+ git_buf dest = GIT_BUF_INIT;
+
+ error = git_futils_readbuffer(&source, full_path);
+ if (error < GIT_SUCCESS)
+ return error;
+
+ error = git_filters_apply(&dest, &source, filters);
+
+ /* Free the source as soon as possible. This can be big in memory,
+ * and we don't want to ODB write to choke */
+ git_buf_free(&source);
+
+ if (error == GIT_SUCCESS) {
+ /* Write the file to disk if it was properly filtered */
+ error = git_odb_write(oid, odb, dest.ptr, dest.size, GIT_OBJ_BLOB);
+ }
+
+ git_buf_free(&dest);
+ return GIT_SUCCESS;
+}
+
+static int write_symlink(git_oid *oid, git_odb *odb, const char *path, size_t link_size)
+{
+ char *link_data;
+ ssize_t read_len;
+ int error;
+
+ link_data = git__malloc(link_size);
+ if (!link_data)
+ return GIT_ENOMEM;
+
+ read_len = p_readlink(path, link_data, link_size);
+
+ if (read_len != (ssize_t)link_size) {
+ free(link_data);
+ return git__throw(GIT_EOSERR, "Failed to create blob. Can't read symlink");
+ }
+
+ error = git_odb_write(oid, odb, (void *)link_data, link_size, GIT_OBJ_BLOB);
+ free(link_data);
+ return error;
+}
+
int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path)
{
int error = GIT_SUCCESS;
git_buf full_path = GIT_BUF_INIT;
git_off_t size;
- git_odb_stream *stream = NULL;
struct stat st;
const char *workdir;
- git_odb *odb;
+ git_odb *odb = NULL;
workdir = git_repository_workdir(repo);
if (workdir == NULL)
@@ -95,63 +187,45 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat
if (error < GIT_SUCCESS)
goto cleanup;
- if ((error = git_odb_open_wstream(&stream, odb, (size_t)size, GIT_OBJ_BLOB)) < GIT_SUCCESS)
- goto cleanup;
-
if (S_ISLNK(st.st_mode)) {
- char *link_data;
- ssize_t read_len;
-
- link_data = git__malloc((size_t)size);
- if (!link_data) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
-
- read_len = p_readlink(full_path.ptr, link_data, (size_t)size);
-
- if (read_len != (ssize_t)size) {
- error = git__throw(GIT_EOSERR, "Failed to create blob. Can't read symlink");
- free(link_data);
- goto cleanup;
- }
-
- stream->write(stream, link_data, (size_t)size);
- free(link_data);
-
+ error = write_symlink(oid, odb, full_path.ptr, (size_t)size);
} else {
- int fd;
- char buffer[2048];
-
- if ((fd = p_open(full_path.ptr, O_RDONLY)) < 0) {
- error = git__throw(GIT_ENOTFOUND, "Failed to create blob. Could not open '%s'", full_path.ptr);
- goto cleanup;
- }
-
- while (size > 0) {
- ssize_t read_len = p_read(fd, buffer, sizeof(buffer));
+ git_vector write_filters = GIT_VECTOR_INIT;
+ int filter_count;
- if (read_len < 0) {
- error = git__throw(GIT_EOSERR, "Failed to create blob. Can't read full file");
- p_close(fd);
- goto cleanup;
- }
+ /* Load the filters for writing this file to the ODB */
+ filter_count = git_filters_load(&write_filters, repo, path, GIT_FILTER_TO_ODB);
- stream->write(stream, buffer, read_len);
- size -= read_len;
+ if (filter_count < 0) {
+ /* Negative value means there was a critical error */
+ error = filter_count;
+ goto cleanup;
+ } else if (filter_count == 0) {
+ /* No filters need to be applied to the document: we can stream
+ * directly from disk */
+ error = write_file_stream(oid, odb, full_path.ptr, size);
+ } else {
+ /* We need to apply one or more filters */
+ error = write_file_filtered(oid, odb, full_path.ptr, &write_filters);
}
- p_close(fd);
+ git_filters_free(&write_filters);
+
+ /*
+ * TODO: eventually support streaming filtered files, for files which are bigger
+ * than a given threshold. This is not a priority because applying a filter in
+ * streaming mode changes the final size of the blob, and without knowing its
+ * final size, the blob cannot be written in stream mode to the ODB.
+ *
+ * The plan is to do streaming writes to a tempfile on disk and then opening
+ * streaming that file to the ODB, using `write_file_stream`.
+ *
+ * CAREFULLY DESIGNED APIS YO
+ */
}
- error = stream->finalize_write(oid, stream);
-
cleanup:
- if (stream)
- stream->free(stream);
-
git_buf_free(&full_path);
-
return error;
}
diff --git a/src/blob.h b/src/blob.h
index f810b506b..0305e9473 100644
--- a/src/blob.h
+++ b/src/blob.h
@@ -19,5 +19,6 @@ struct git_blob {
void git_blob__free(git_blob *blob);
int git_blob__parse(git_blob *blob, git_odb_object *obj);
+int git_blob__getbuf(git_buf *buffer, git_blob *blob);
#endif
diff --git a/src/buffer.c b/src/buffer.c
index 183da7c5f..3098f6d68 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -7,14 +7,17 @@
#include "buffer.h"
#include "posix.h"
#include <stdarg.h>
+#include <ctype.h>
/* Used as default value for git_buf->ptr so that people can always
* assume ptr is non-NULL and zero terminated even for new git_bufs.
*/
char git_buf_initbuf[1];
+static char git_buf__oom;
+
#define ENSURE_SIZE(b, d) \
- if ((ssize_t)(d) > buf->asize && git_buf_grow(b, (d)) < GIT_SUCCESS)\
+ if ((d) > buf->asize && git_buf_grow(b, (d)) < GIT_SUCCESS)\
return GIT_ENOMEM;
@@ -31,8 +34,10 @@ void git_buf_init(git_buf *buf, size_t initial_size)
int git_buf_grow(git_buf *buf, size_t target_size)
{
int error = git_buf_try_grow(buf, target_size);
- if (error != GIT_SUCCESS)
- buf->asize = -1;
+ if (error != GIT_SUCCESS) {
+ buf->ptr = &git_buf__oom;
+ }
+
return error;
}
@@ -41,17 +46,17 @@ int git_buf_try_grow(git_buf *buf, size_t target_size)
char *new_ptr;
size_t new_size;
- if (buf->asize < 0)
+ if (buf->ptr == &git_buf__oom)
return GIT_ENOMEM;
- if (target_size <= (size_t)buf->asize)
+ if (target_size <= buf->asize)
return GIT_SUCCESS;
if (buf->asize == 0) {
new_size = target_size;
new_ptr = NULL;
} else {
- new_size = (size_t)buf->asize;
+ new_size = buf->asize;
new_ptr = buf->ptr;
}
@@ -64,7 +69,6 @@ int git_buf_try_grow(git_buf *buf, size_t target_size)
new_size = (new_size + 7) & ~7;
new_ptr = git__realloc(new_ptr, new_size);
- /* if realloc fails, return without modifying the git_buf */
if (!new_ptr)
return GIT_ENOMEM;
@@ -83,7 +87,7 @@ void git_buf_free(git_buf *buf)
{
if (!buf) return;
- if (buf->ptr != git_buf_initbuf)
+ if (buf->ptr != git_buf_initbuf && buf->ptr != &git_buf__oom)
git__free(buf->ptr);
git_buf_init(buf, 0);
@@ -98,12 +102,12 @@ void git_buf_clear(git_buf *buf)
int git_buf_oom(const git_buf *buf)
{
- return (buf->asize < 0);
+ return (buf->ptr == &git_buf__oom);
}
int git_buf_lasterror(const git_buf *buf)
{
- return (buf->asize < 0) ? GIT_ENOMEM : GIT_SUCCESS;
+ return (buf->ptr == &git_buf__oom) ? GIT_ENOMEM : GIT_SUCCESS;
}
int git_buf_set(git_buf *buf, const char *data, size_t len)
@@ -162,11 +166,12 @@ int git_buf_printf(git_buf *buf, const char *format, ...)
va_end(arglist);
if (len < 0) {
- buf->asize = -1;
+ free(buf->ptr);
+ buf->ptr = &git_buf__oom;
return GIT_ENOMEM;
}
- if (len + 1 <= buf->asize - buf->size) {
+ if ((size_t)len + 1 <= buf->asize - buf->size) {
buf->size += len;
break;
}
@@ -205,9 +210,9 @@ void git_buf_consume(git_buf *buf, const char *end)
}
}
-void git_buf_truncate(git_buf *buf, ssize_t len)
+void git_buf_truncate(git_buf *buf, size_t len)
{
- if (len >= 0 && len < buf->size) {
+ if (len < buf->size) {
buf->size = len;
buf->ptr[buf->size] = '\0';
}
@@ -230,7 +235,7 @@ char *git_buf_detach(git_buf *buf)
{
char *data = buf->ptr;
- if (buf->asize <= 0)
+ if (buf->asize == 0 || buf->ptr == &git_buf__oom)
return NULL;
git_buf_init(buf, 0);
@@ -238,7 +243,7 @@ char *git_buf_detach(git_buf *buf)
return data;
}
-void git_buf_attach(git_buf *buf, char *ptr, ssize_t asize)
+void git_buf_attach(git_buf *buf, char *ptr, size_t asize)
{
git_buf_free(buf);
@@ -372,3 +377,22 @@ int git_buf_join(
return error;
}
+
+void git_buf_rtrim(git_buf *buf)
+{
+ while (buf->size > 0) {
+ if (!isspace(buf->ptr[buf->size - 1]))
+ break;
+
+ buf->size--;
+ }
+
+ buf->ptr[buf->size] = '\0';
+}
+
+int git_buf_cmp(const git_buf *a, const git_buf *b)
+{
+ int result = memcmp(a->ptr, b->ptr, min(a->size, b->size));
+ return (result != 0) ? result :
+ (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
+}
diff --git a/src/buffer.h b/src/buffer.h
index 3969f461e..3cdd794af 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -11,7 +11,7 @@
typedef struct {
char *ptr;
- ssize_t asize, size;
+ size_t asize, size;
} git_buf;
extern char git_buf_initbuf[];
@@ -47,7 +47,7 @@ int git_buf_try_grow(git_buf *buf, size_t target_size);
void git_buf_free(git_buf *buf);
void git_buf_swap(git_buf *buf_a, git_buf *buf_b);
char *git_buf_detach(git_buf *buf);
-void git_buf_attach(git_buf *buf, char *ptr, ssize_t asize);
+void git_buf_attach(git_buf *buf, char *ptr, size_t asize);
/**
* Test if there have been any reallocation failures with this git_buf.
@@ -83,7 +83,7 @@ int git_buf_puts(git_buf *buf, const char *string);
int git_buf_printf(git_buf *buf, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
void git_buf_clear(git_buf *buf);
void git_buf_consume(git_buf *buf, const char *end);
-void git_buf_truncate(git_buf *buf, ssize_t len);
+void git_buf_truncate(git_buf *buf, size_t len);
void git_buf_rtruncate_at_char(git_buf *path, char separator);
int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...);
@@ -115,4 +115,9 @@ GIT_INLINE(int) git_buf_rfind_next(git_buf *buf, char ch)
return idx;
}
+/* Remove whitespace from the end of the buffer */
+void git_buf_rtrim(git_buf *buf);
+
+int git_buf_cmp(const git_buf *a, const git_buf *b);
+
#endif
diff --git a/src/config.c b/src/config.c
index 4ff1b2e72..912224158 100644
--- a/src/config.c
+++ b/src/config.c
@@ -209,23 +209,37 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value)
return file->set(file, name, value);
}
-/***********
- * Getters
- ***********/
+static int parse_bool(int *out, const char *value)
+{
+ /* A missing value means true */
+ if (value == NULL) {
+ *out = 1;
+ return GIT_SUCCESS;
+ }
-int git_config_get_int64(git_config *cfg, const char *name, int64_t *out)
+ if (!strcasecmp(value, "true") ||
+ !strcasecmp(value, "yes") ||
+ !strcasecmp(value, "on")) {
+ *out = 1;
+ return GIT_SUCCESS;
+ }
+ if (!strcasecmp(value, "false") ||
+ !strcasecmp(value, "no") ||
+ !strcasecmp(value, "off")) {
+ *out = 0;
+ return GIT_SUCCESS;
+ }
+
+ return GIT_EINVALIDTYPE;
+}
+
+static int parse_int64(int64_t *out, const char *value)
{
- const char *value, *num_end;
- int ret;
+ const char *num_end;
int64_t num;
- ret = git_config_get_string(cfg, name, &value);
- if (ret < GIT_SUCCESS)
- return git__rethrow(ret, "Failed to retrieve value for '%s'", name);
-
- ret = git__strtol64(&num, value, &num_end, 0);
- if (ret < GIT_SUCCESS)
- return git__rethrow(ret, "Failed to convert value for '%s'", name);
+ if (git__strtol64(&num, value, &num_end, 0) < 0)
+ return GIT_EINVALIDTYPE;
switch (*num_end) {
case 'g':
@@ -245,38 +259,112 @@ int git_config_get_int64(git_config *cfg, const char *name, int64_t *out)
/* check that that there are no more characters after the
* given modifier suffix */
if (num_end[1] != '\0')
- return git__throw(GIT_EINVALIDTYPE,
- "Failed to get value for '%s'. Invalid type suffix", name);
+ return GIT_EINVALIDTYPE;
/* fallthrough */
case '\0':
*out = num;
- return GIT_SUCCESS;
+ return 0;
default:
- return git__throw(GIT_EINVALIDTYPE,
- "Failed to get value for '%s'. Value is of invalid type", name);
+ return GIT_EINVALIDTYPE;
}
}
-int git_config_get_int32(git_config *cfg, const char *name, int32_t *out)
+static int parse_int32(int32_t *out, const char *value)
{
- int64_t tmp_long;
- int32_t tmp_int;
+ int64_t tmp;
+ int32_t truncate;
+
+ if (parse_int64(&tmp, value) < 0)
+ return GIT_EINVALIDTYPE;
+
+ truncate = tmp & 0xFFFFFFFF;
+ if (truncate != tmp)
+ return GIT_EOVERFLOW;
+
+ *out = truncate;
+ return 0;
+}
+
+/***********
+ * Getters
+ ***********/
+int git_config_get_mapped(git_config *cfg, const char *name, git_cvar_map *maps, size_t map_n, int *out)
+{
+ size_t i;
+ const char *value;
+ int error;
+
+ error = git_config_get_string(cfg, name, &value);
+ if (error < GIT_SUCCESS)
+ return error;
+
+ for (i = 0; i < map_n; ++i) {
+ git_cvar_map *m = maps + i;
+
+ switch (m->cvar_type) {
+ case GIT_CVAR_FALSE:
+ case GIT_CVAR_TRUE: {
+ int bool_val;
+
+ if (parse_bool(&bool_val, value) == 0 &&
+ bool_val == (int)m->cvar_type) {
+ *out = m->map_value;
+ return 0;
+ }
+
+ break;
+ }
+
+ case GIT_CVAR_INT32:
+ if (parse_int32(out, value) == 0)
+ return 0;
+
+ break;
+
+ case GIT_CVAR_STRING:
+ if (strcasecmp(value, m->str_match) == 0) {
+ *out = m->map_value;
+ return 0;
+ }
+ }
+ }
+
+ return git__throw(GIT_ENOTFOUND,
+ "Failed to map the '%s' config variable with a valid value", name);
+}
+
+int git_config_get_int64(git_config *cfg, const char *name, int64_t *out)
+{
+ const char *value;
int ret;
- ret = git_config_get_int64(cfg, name, &tmp_long);
+ ret = git_config_get_string(cfg, name, &value);
if (ret < GIT_SUCCESS)
- return git__rethrow(ret, "Failed to convert value for '%s'", name);
-
- tmp_int = tmp_long & 0xFFFFFFFF;
- if (tmp_int != tmp_long)
- return git__throw(GIT_EOVERFLOW, "Value for '%s' is too large", name);
+ return git__rethrow(ret, "Failed to retrieve value for '%s'", name);
- *out = tmp_int;
+ if (parse_int64(out, value) < 0)
+ return git__throw(GIT_EINVALIDTYPE, "Failed to parse '%s' as an integer", value);
- return ret;
+ return GIT_SUCCESS;
+}
+
+int git_config_get_int32(git_config *cfg, const char *name, int32_t *out)
+{
+ const char *value;
+ int error;
+
+ error = git_config_get_string(cfg, name, &value);
+ if (error < GIT_SUCCESS)
+ return git__rethrow(error, "Failed to get value for %s", name);
+
+ error = parse_int32(out, value);
+ if (error < GIT_SUCCESS)
+ return git__throw(GIT_EINVALIDTYPE, "Failed to parse '%s' as a 32-bit integer", value);
+
+ return GIT_SUCCESS;
}
int git_config_get_bool(git_config *cfg, const char *name, int *out)
@@ -288,33 +376,15 @@ int git_config_get_bool(git_config *cfg, const char *name, int *out)
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to get value for %s", name);
- /* A missing value means true */
- if (value == NULL) {
- *out = 1;
+ if (parse_bool(out, value) == 0)
return GIT_SUCCESS;
- }
- if (!strcasecmp(value, "true") ||
- !strcasecmp(value, "yes") ||
- !strcasecmp(value, "on")) {
- *out = 1;
- return GIT_SUCCESS;
- }
- if (!strcasecmp(value, "false") ||
- !strcasecmp(value, "no") ||
- !strcasecmp(value, "off")) {
- *out = 0;
+ if (parse_int32(out, value) == 0) {
+ *out = !!(*out);
return GIT_SUCCESS;
}
- /* Try to parse it as an integer */
- error = git_config_get_int32(cfg, name, out);
- if (error == GIT_SUCCESS)
- *out = !!(*out);
-
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to get value for %s", name);
- return error;
+ return git__throw(GIT_EINVALIDTYPE, "Failed to parse '%s' as a boolean value", value);
}
int git_config_get_string(git_config *cfg, const char *name, const char **out)
diff --git a/src/config_cache.c b/src/config_cache.c
new file mode 100644
index 000000000..5e20847f5
--- /dev/null
+++ b/src/config_cache.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+#include "fileops.h"
+#include "hashtable.h"
+#include "config.h"
+#include "git2/config.h"
+#include "vector.h"
+#include "filter.h"
+#include "repository.h"
+
+struct map_data {
+ const char *cvar_name;
+ git_cvar_map *maps;
+ size_t map_count;
+ int default_value;
+};
+
+/*
+ * core.eol
+ * Sets the line ending type to use in the working directory for
+ * files that have the text property set. Alternatives are lf, crlf
+ * and native, which uses the platform’s native line ending. The default
+ * value is native. See gitattributes(5) for more information on
+ * end-of-line conversion.
+ */
+static git_cvar_map _cvar_map_eol[] = {
+ {GIT_CVAR_FALSE, NULL, GIT_EOL_UNSET},
+ {GIT_CVAR_STRING, "lf", GIT_EOL_LF},
+ {GIT_CVAR_STRING, "crlf", GIT_EOL_CRLF},
+ {GIT_CVAR_STRING, "native", GIT_EOL_NATIVE}
+};
+
+/*
+ * core.autocrlf
+ * Setting this variable to "true" is almost the same as setting
+ * the text attribute to "auto" on all files except that text files are
+ * not guaranteed to be normalized: files that contain CRLF in the
+ * repository will not be touched. Use this setting if you want to have
+ * CRLF line endings in your working directory even though the repository
+ * does not have normalized line endings. This variable can be set to input,
+ * in which case no output conversion is performed.
+ */
+static git_cvar_map _cvar_map_autocrlf[] = {
+ {GIT_CVAR_FALSE, NULL, GIT_AUTO_CRLF_FALSE},
+ {GIT_CVAR_TRUE, NULL, GIT_AUTO_CRLF_TRUE},
+ {GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT}
+};
+
+static struct map_data _cvar_maps[] = {
+ {"core.autocrlf", _cvar_map_autocrlf, ARRAY_SIZE(_cvar_map_autocrlf), GIT_AUTO_CRLF_DEFAULT},
+ {"core.eol", _cvar_map_eol, ARRAY_SIZE(_cvar_map_eol), GIT_EOL_DEFAULT}
+};
+
+int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar)
+{
+ *out = repo->cvar_cache[(int)cvar];
+
+ if (*out == GIT_CVAR_NOT_CACHED) {
+ struct map_data *data = &_cvar_maps[(int)cvar];
+ git_config *config;
+ int error;
+
+ error = git_repository_config__weakptr(&config, repo);
+ if (error < GIT_SUCCESS)
+ return error;
+
+ error = git_config_get_mapped(
+ config, data->cvar_name, data->maps, data->map_count, out);
+
+ if (error == GIT_ENOTFOUND)
+ *out = data->default_value;
+
+ else if (error < GIT_SUCCESS)
+ return error;
+
+ repo->cvar_cache[(int)cvar] = *out;
+ }
+
+ return GIT_SUCCESS;
+}
+
+void git_repository__cvar_cache_clear(git_repository *repo)
+{
+ int i;
+
+ for (i = 0; i < GIT_CVAR_CACHE_MAX; ++i)
+ repo->cvar_cache[i] = GIT_CVAR_NOT_CACHED;
+}
+
diff --git a/src/config_file.c b/src/config_file.c
index c9c7d11eb..ce76493c7 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -73,7 +73,7 @@ typedef struct {
git_hashtable *values;
struct {
- git_fbuffer buffer;
+ git_buf buffer;
char *read_ptr;
int line_number;
int eof;
@@ -151,6 +151,7 @@ static int config_open(git_config_file *cfg)
if (b->values == NULL)
return GIT_ENOMEM;
+ git_buf_init(&b->reader.buffer, 0);
error = git_futils_readbuffer(&b->reader.buffer, b->file_path);
/* It's fine if the file doesn't exist */
@@ -164,14 +165,14 @@ static int config_open(git_config_file *cfg)
if (error < GIT_SUCCESS)
goto cleanup;
- git_futils_freebuffer(&b->reader.buffer);
+ git_buf_free(&b->reader.buffer);
return GIT_SUCCESS;
cleanup:
free_vars(b->values);
b->values = NULL;
- git_futils_freebuffer(&b->reader.buffer);
+ git_buf_free(&b->reader.buffer);
return git__rethrow(error, "Failed to open config");
}
@@ -765,7 +766,7 @@ static int skip_bom(diskfile_backend *cfg)
{
static const char utf8_bom[] = "\xef\xbb\xbf";
- if (cfg->reader.buffer.len < sizeof(utf8_bom))
+ if (cfg->reader.buffer.size < sizeof(utf8_bom))
return GIT_SUCCESS;
if (memcmp(cfg->reader.read_ptr, utf8_bom, sizeof(utf8_bom)) == 0)
@@ -847,7 +848,7 @@ static int config_parse(diskfile_backend *cfg_file)
git_buf buf = GIT_BUF_INIT;
/* Initialize the reading position */
- cfg_file->reader.read_ptr = cfg_file->reader.buffer.data;
+ cfg_file->reader.read_ptr = cfg_file->reader.buffer.ptr;
cfg_file->reader.eof = 0;
/* If the file is empty, there's nothing for us to do */
@@ -976,10 +977,9 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
cfg->reader.read_ptr = NULL;
cfg->reader.eof = 1;
data_start = NULL;
- cfg->reader.buffer.len = 0;
- cfg->reader.buffer.data = NULL;
+ git_buf_clear(&cfg->reader.buffer);
} else {
- cfg->reader.read_ptr = cfg->reader.buffer.data;
+ cfg->reader.read_ptr = cfg->reader.buffer.ptr;
cfg->reader.eof = 0;
data_start = cfg->reader.read_ptr;
}
@@ -1093,7 +1093,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
/* And then the write out rest of the file */
error = git_filebuf_write(&file, post_start,
- cfg->reader.buffer.len - (post_start - data_start));
+ cfg->reader.buffer.size - (post_start - data_start));
if (error < GIT_SUCCESS) {
git__rethrow(error, "Failed to write the rest of the file");
@@ -1128,7 +1128,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
goto cleanup;
}
- error = git_filebuf_write(&file, cfg->reader.buffer.data, cfg->reader.buffer.len);
+ error = git_filebuf_write(&file, cfg->reader.buffer.ptr, cfg->reader.buffer.size);
if (error < GIT_SUCCESS) {
git__rethrow(error, "Failed to write original config content");
goto cleanup;
@@ -1155,7 +1155,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
else
error = git_filebuf_commit(&file, GIT_CONFIG_FILE_MODE);
- git_futils_freebuffer(&cfg->reader.buffer);
+ git_buf_free(&cfg->reader.buffer);
return error;
}
diff --git a/src/crlf.c b/src/crlf.c
new file mode 100644
index 000000000..f0ec7b736
--- /dev/null
+++ b/src/crlf.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+#include "fileops.h"
+#include "hash.h"
+#include "filter.h"
+#include "repository.h"
+
+#include "git2/attr.h"
+
+struct crlf_attrs {
+ int crlf_action;
+ int eol;
+};
+
+struct crlf_filter {
+ git_filter f;
+ struct crlf_attrs attrs;
+};
+
+static int check_crlf(const char *value)
+{
+ if (GIT_ATTR_TRUE(value))
+ return GIT_CRLF_TEXT;
+
+ if (GIT_ATTR_FALSE(value))
+ return GIT_CRLF_BINARY;
+
+ if (GIT_ATTR_UNSPECIFIED(value))
+ return GIT_CRLF_GUESS;
+
+ if (strcmp(value, "input") == 0)
+ return GIT_CRLF_INPUT;
+
+ if (strcmp(value, "auto") == 0)
+ return GIT_CRLF_AUTO;
+
+ return GIT_CRLF_GUESS;
+}
+
+static int check_eol(const char *value)
+{
+ if (GIT_ATTR_UNSPECIFIED(value))
+ return GIT_EOL_UNSET;
+
+ if (strcmp(value, "lf") == 0)
+ return GIT_EOL_LF;
+
+ if (strcmp(value, "crlf") == 0)
+ return GIT_EOL_CRLF;
+
+ return GIT_EOL_UNSET;
+}
+
+static int crlf_input_action(struct crlf_attrs *ca)
+{
+ if (ca->crlf_action == GIT_CRLF_BINARY)
+ return GIT_CRLF_BINARY;
+
+ if (ca->eol == GIT_EOL_LF)
+ return GIT_CRLF_INPUT;
+
+ if (ca->eol == GIT_EOL_CRLF)
+ return GIT_CRLF_CRLF;
+
+ return ca->crlf_action;
+}
+
+static int crlf_load_attributes(struct crlf_attrs *ca, git_repository *repo, const char *path)
+{
+#define NUM_CONV_ATTRS 3
+
+ static const char *attr_names[NUM_CONV_ATTRS] = {
+ "crlf", "eol", "text",
+ };
+
+ const char *attr_vals[NUM_CONV_ATTRS];
+ int error;
+
+ error = git_attr_get_many(repo, path, NUM_CONV_ATTRS, attr_names, attr_vals);
+
+ if (error == GIT_ENOTFOUND) {
+ ca->crlf_action = GIT_CRLF_GUESS;
+ ca->eol = GIT_EOL_UNSET;
+ return 0;
+ }
+
+ if (error == GIT_SUCCESS) {
+ ca->crlf_action = check_crlf(attr_vals[2]); /* text */
+ if (ca->crlf_action == GIT_CRLF_GUESS)
+ ca->crlf_action = check_crlf(attr_vals[0]); /* clrf */
+
+ ca->eol = check_eol(attr_vals[1]); /* eol */
+ return 0;
+ }
+
+ return error;
+}
+
+static int drop_crlf(git_buf *dest, const git_buf *source)
+{
+ const char *scan = source->ptr, *next;
+ const char *scan_end = source->ptr + source->size;
+
+ /* Main scan loop. Find the next carriage return and copy the
+ * whole chunk up to that point to the destination buffer.
+ */
+ while ((next = memchr(scan, '\r', scan_end - scan)) != NULL) {
+ /* copy input up to \r */
+ if (next > scan)
+ git_buf_put(dest, scan, next - scan);
+
+ /* Do not drop \r unless it is followed by \n */
+ if (*(next + 1) != '\n')
+ git_buf_putc(dest, '\r');
+
+ scan = next + 1;
+ }
+
+ /* If there was no \r, then tell the library to skip this filter */
+ if (scan == source->ptr)
+ return -1;
+
+ /* Copy remaining input into dest */
+ git_buf_put(dest, scan, scan_end - scan);
+
+ return git_buf_lasterror(dest);
+}
+
+static int crlf_apply_to_odb(git_filter *self, git_buf *dest, const git_buf *source)
+{
+ struct crlf_filter *filter = (struct crlf_filter *)self;
+
+ assert(self && dest && source);
+
+ /* Empty file? Nothing to do */
+ if (source->size == 0)
+ return 0;
+
+ /* Heuristics to see if we can skip the conversion.
+ * Straight from Core Git.
+ */
+ if (filter->attrs.crlf_action == GIT_CRLF_AUTO ||
+ filter->attrs.crlf_action == GIT_CRLF_GUESS) {
+
+ git_text_stats stats;
+ git_text_gather_stats(&stats, source);
+
+ /*
+ * We're currently not going to even try to convert stuff
+ * that has bare CR characters. Does anybody do that crazy
+ * stuff?
+ */
+ if (stats.cr != stats.crlf)
+ return -1;
+
+ /*
+ * And add some heuristics for binary vs text, of course...
+ */
+ if (git_text_is_binary(&stats))
+ return -1;
+
+#if 0
+ if (crlf_action == CRLF_GUESS) {
+ /*
+ * If the file in the index has any CR in it, do not convert.
+ * This is the new safer autocrlf handling.
+ */
+ if (has_cr_in_index(path))
+ return 0;
+ }
+#endif
+
+ if (!stats.cr)
+ return -1;
+ }
+
+ /* Actually drop the carriage returns */
+ return drop_crlf(dest, source);
+}
+
+int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const char *path)
+{
+ struct crlf_attrs ca;
+ struct crlf_filter *filter;
+ int error;
+
+ /* Load gitattributes for the path */
+ if ((error = crlf_load_attributes(&ca, repo, path)) < 0)
+ return error;
+
+ /*
+ * Use the core Git logic to see if we should perform CRLF for this file
+ * based on its attributes & the value of `core.auto_crlf`
+ */
+ ca.crlf_action = crlf_input_action(&ca);
+
+ if (ca.crlf_action == GIT_CRLF_BINARY)
+ return 0;
+
+ if (ca.crlf_action == GIT_CRLF_GUESS) {
+ int auto_crlf;
+
+ if ((error = git_repository__cvar(
+ &auto_crlf, repo, GIT_CVAR_AUTO_CRLF)) < GIT_SUCCESS)
+ return error;
+
+ if (auto_crlf == GIT_AUTO_CRLF_FALSE)
+ return 0;
+ }
+
+ /* If we're good, we create a new filter object and push it
+ * into the filters array */
+ filter = git__malloc(sizeof(struct crlf_filter));
+ if (filter == NULL)
+ return GIT_ENOMEM;
+
+ filter->f.apply = &crlf_apply_to_odb;
+ filter->f.do_free = NULL;
+ memcpy(&filter->attrs, &ca, sizeof(struct crlf_attrs));
+
+ return git_vector_insert(filters, filter);
+}
+
diff --git a/src/fileops.c b/src/fileops.c
index 3241c68b1..d2b4af51e 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -97,87 +97,77 @@ mode_t git_futils_canonical_mode(mode_t raw_mode)
return 0;
}
-int git_futils_readbuffer_updated(git_fbuffer *obj, const char *path, time_t *mtime, int *updated)
+int git_futils_readbuffer_updated(git_buf *buf, const char *path, time_t *mtime, int *updated)
{
git_file fd;
size_t len;
struct stat st;
- unsigned char *buff;
- assert(obj && path && *path);
+ assert(buf && path && *path);
if (updated != NULL)
*updated = 0;
- if (p_stat(path, &st) < 0)
- return git__throw(GIT_ENOTFOUND, "Failed to stat file %s", path);
+ if ((fd = p_open(path, O_RDONLY)) < 0) {
+ return git__throw(GIT_ENOTFOUND, "Failed to read file '%s': %s", path, strerror(errno));
+ }
- if (S_ISDIR(st.st_mode))
- return git__throw(GIT_ERROR, "Can't read a dir into a buffer");
+ if (p_fstat(fd, &st) < 0 || S_ISDIR(st.st_mode) || !git__is_sizet(st.st_size+1)) {
+ close(fd);
+ return git__throw(GIT_EOSERR, "Failed to stat file '%s'", path);
+ }
/*
* If we were given a time, we only want to read the file if it
* has been modified.
*/
- if (mtime != NULL && *mtime >= st.st_mtime)
- return GIT_SUCCESS;
+ if (mtime != NULL && *mtime >= st.st_mtime) {
+ close(fd);
+ return 0;
+ }
if (mtime != NULL)
*mtime = st.st_mtime;
- if (!git__is_sizet(st.st_size+1))
- return git__throw(GIT_ERROR, "Failed to read file `%s`. An error occured while calculating its size", path);
len = (size_t) st.st_size;
- if ((fd = p_open(path, O_RDONLY)) < 0)
- return git__throw(GIT_EOSERR, "Failed to open %s for reading", path);
+ git_buf_clear(buf);
- if ((buff = git__malloc(len + 1)) == NULL) {
- p_close(fd);
+ if (git_buf_grow(buf, len + 1) < 0) {
+ close(fd);
return GIT_ENOMEM;
}
- if (p_read(fd, buff, len) < 0) {
- p_close(fd);
- git__free(buff);
- return git__throw(GIT_ERROR, "Failed to read file `%s`", path);
+ buf->ptr[len] = '\0';
+
+ while (len > 0) {
+ ssize_t read_size = p_read(fd, buf->ptr, len);
+
+ if (read_size < 0) {
+ close(fd);
+ return git__throw(GIT_EOSERR, "Failed to read from FD");
+ }
+
+ len -= read_size;
+ buf->size += read_size;
}
- buff[len] = '\0';
p_close(fd);
if (mtime != NULL)
*mtime = st.st_mtime;
+
if (updated != NULL)
*updated = 1;
- obj->data = buff;
- obj->len = len;
-
- return GIT_SUCCESS;
-}
-
-int git_futils_readbuffer(git_fbuffer *obj, const char *path)
-{
- return git_futils_readbuffer_updated(obj, path, NULL, NULL);
+ return 0;
}
-void git_futils_fbuffer_rtrim(git_fbuffer *obj)
+int git_futils_readbuffer(git_buf *buf, const char *path)
{
- unsigned char *buff = obj->data;
- while (obj->len > 0 && isspace(buff[obj->len - 1]))
- obj->len--;
- buff[obj->len] = '\0';
+ return git_futils_readbuffer_updated(buf, path, NULL, NULL);
}
-void git_futils_freebuffer(git_fbuffer *obj)
-{
- assert(obj);
- git__free(obj->data);
- obj->data = NULL;
-}
-
-
int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode)
{
if (git_futils_mkpath2file(to, dirmode) < GIT_SUCCESS)
diff --git a/src/fileops.h b/src/fileops.h
index 4c114026b..43ef21521 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -17,17 +17,8 @@
*
* Read whole files into an in-memory buffer for processing
*/
-#define GIT_FBUFFER_INIT {NULL, 0}
-
-typedef struct { /* file io buffer */
- void *data; /* data bytes */
- size_t len; /* data length */
-} git_fbuffer;
-
-extern int git_futils_readbuffer(git_fbuffer *obj, const char *path);
-extern int git_futils_readbuffer_updated(git_fbuffer *obj, const char *path, time_t *mtime, int *updated);
-extern void git_futils_freebuffer(git_fbuffer *obj);
-extern void git_futils_fbuffer_rtrim(git_fbuffer *obj);
+extern int git_futils_readbuffer(git_buf *obj, const char *path);
+extern int git_futils_readbuffer_updated(git_buf *obj, const char *path, time_t *mtime, int *updated);
/**
* File utils
diff --git a/src/filter.c b/src/filter.c
new file mode 100644
index 000000000..f0ee1ad39
--- /dev/null
+++ b/src/filter.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+#include "fileops.h"
+#include "hash.h"
+#include "filter.h"
+#include "repository.h"
+#include "git2/config.h"
+
+/* Tweaked from Core Git. I wonder what we could use this for... */
+void git_text_gather_stats(git_text_stats *stats, const git_buf *text)
+{
+ size_t i;
+
+ memset(stats, 0, sizeof(*stats));
+
+ for (i = 0; i < text->size; i++) {
+ unsigned char c = text->ptr[i];
+
+ if (c == '\r') {
+ stats->cr++;
+
+ if (i + 1 < text->size && text->ptr[i + 1] == '\n')
+ stats->crlf++;
+ }
+
+ else if (c == '\n')
+ stats->lf++;
+
+ else if (c == 0x85)
+ /* Unicode CR+LF */
+ stats->crlf++;
+
+ else if (c == 127)
+ /* DEL */
+ stats->nonprintable++;
+
+ else if (c <= 0x1F || (c >= 0x80 && c <= 0x9F)) {
+ switch (c) {
+ /* BS, HT, ESC and FF */
+ case '\b': case '\t': case '\033': case '\014':
+ stats->printable++;
+ break;
+ case 0:
+ stats->nul++;
+ /* fall through */
+ default:
+ stats->nonprintable++;
+ }
+ }
+
+ else
+ stats->printable++;
+ }
+
+ /* If file ends with EOF then don't count this EOF as non-printable. */
+ if (text->size >= 1 && text->ptr[text->size - 1] == '\032')
+ stats->nonprintable--;
+}
+
+/*
+ * Fresh from Core Git
+ */
+int git_text_is_binary(git_text_stats *stats)
+{
+ if (stats->nul)
+ return 1;
+
+ if ((stats->printable >> 7) < stats->nonprintable)
+ return 1;
+ /*
+ * Other heuristics? Average line length might be relevant,
+ * as might LF vs CR vs CRLF counts..
+ *
+ * NOTE! It might be normal to have a low ratio of CRLF to LF
+ * (somebody starts with a LF-only file and edits it with an editor
+ * that adds CRLF only to lines that are added..). But do we
+ * want to support CR-only? Probably not.
+ */
+ return 0;
+}
+
+int git_filters_load(git_vector *filters, git_repository *repo, const char *path, int mode)
+{
+ int error;
+
+ if (mode == GIT_FILTER_TO_ODB) {
+ /* Load the CRLF cleanup filter when writing to the ODB */
+ error = git_filter_add__crlf_to_odb(filters, repo, path);
+ if (error < GIT_SUCCESS)
+ return error;
+ } else {
+ return git__throw(GIT_ENOTIMPLEMENTED,
+ "Worktree filters are not implemented yet");
+ }
+
+ return (int)filters->length;
+}
+
+void git_filters_free(git_vector *filters)
+{
+ size_t i;
+ git_filter *filter;
+
+ git_vector_foreach(filters, i, filter) {
+ if (filter->do_free != NULL)
+ filter->do_free(filter);
+ else
+ free(filter);
+ }
+
+ git_vector_free(filters);
+}
+
+int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters)
+{
+ unsigned int i, src;
+ git_buf *dbuffer[2];
+
+ dbuffer[0] = source;
+ dbuffer[1] = dest;
+
+ src = 0;
+
+ if (source->size == 0) {
+ git_buf_clear(dest);
+ return GIT_SUCCESS;
+ }
+
+ /* Pre-grow the destination buffer to more or less the size
+ * we expect it to have */
+ if (git_buf_grow(dest, source->size) < 0)
+ return GIT_ENOMEM;
+
+ for (i = 0; i < filters->length; ++i) {
+ git_filter *filter = git_vector_get(filters, i);
+ unsigned int dst = 1 - src;
+
+ git_buf_clear(dbuffer[dst]);
+
+ /* Apply the filter from dbuffer[src] to the other buffer;
+ * if the filtering is canceled by the user mid-filter,
+ * we skip to the next filter without changing the source
+ * of the double buffering (so that the text goes through
+ * cleanly).
+ */
+ if (filter->apply(filter, dbuffer[dst], dbuffer[src]) == 0)
+ src = dst;
+
+ if (git_buf_oom(dbuffer[dst]))
+ return GIT_ENOMEM;
+ }
+
+ /* Ensure that the output ends up in dbuffer[1] (i.e. the dest) */
+ if (src != 1)
+ git_buf_swap(dest, source);
+
+ return GIT_SUCCESS;
+}
+
diff --git a/src/filter.h b/src/filter.h
new file mode 100644
index 000000000..5a77f25c6
--- /dev/null
+++ b/src/filter.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_filter_h__
+#define INCLUDE_filter_h__
+
+#include "common.h"
+#include "buffer.h"
+#include "git2/odb.h"
+#include "git2/repository.h"
+
+typedef struct git_filter {
+ int (*apply)(struct git_filter *self, git_buf *dest, const git_buf *source);
+ void (*do_free)(struct git_filter *self);
+} git_filter;
+
+typedef enum {
+ GIT_FILTER_TO_WORKTREE,
+ GIT_FILTER_TO_ODB
+} git_filter_mode;
+
+typedef enum {
+ GIT_CRLF_GUESS = -1,
+ GIT_CRLF_BINARY = 0,
+ GIT_CRLF_TEXT,
+ GIT_CRLF_INPUT,
+ GIT_CRLF_CRLF,
+ GIT_CRLF_AUTO,
+} git_crlf_t;
+
+typedef struct {
+ /* NUL, CR, LF and CRLF counts */
+ unsigned int nul, cr, lf, crlf;
+
+ /* These are just approximations! */
+ unsigned int printable, nonprintable;
+} git_text_stats;
+
+/*
+ * FILTER API
+ */
+
+/*
+ * For any given path in the working directory, fill the `filters`
+ * array with the relevant filters that need to be applied.
+ *
+ * Mode is either `GIT_FILTER_TO_WORKTREE` if you need to load the
+ * filters that will be used when checking out a file to the working
+ * directory, or `GIT_FILTER_TO_ODB` for the filters used when writing
+ * a file to the ODB.
+ *
+ * @param filters Vector where to store all the loaded filters
+ * @param repo Repository object that contains `path`
+ * @param path Relative path of the file to be filtered
+ * @param mode Filtering direction (WT->ODB or ODB->WT)
+ * @return the number of filters loaded for the file (0 if the file
+ * doesn't need filtering), or a negative error code
+ */
+extern int git_filters_load(git_vector *filters, git_repository *repo, const char *path, int mode);
+
+/*
+ * Apply one or more filters to a file.
+ *
+ * The file must have been loaded as a `git_buf` object. Both the `source`
+ * and `dest` buffers are owned by the caller and must be freed once
+ * they are no longer needed.
+ *
+ * NOTE: Because of the double-buffering schema, the `source` buffer that contains
+ * the original file may be tampered once the filtering is complete. Regardless,
+ * the `dest` buffer will always contain the final result of the filtering
+ *
+ * @param dest Buffer to store the result of the filtering
+ * @param source Buffer containing the document to filter
+ * @param filters A non-empty vector of filters as supplied by `git_filters_load`
+ * @return GIT_SUCCESS on success, an error code otherwise
+ */
+extern int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters);
+
+/*
+ * Free the `filters` array generated by `git_filters_load`.
+ *
+ * Note that this frees both the array and its contents. The array will
+ * be clean/reusable after this call.
+ *
+ * @param filters A filters array as supplied by `git_filters_load`
+ */
+extern void git_filters_free(git_vector *filters);
+
+/*
+ * Available filters
+ */
+
+/* Strip CRLF, from Worktree to ODB */
+extern int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const char *path);
+
+
+/*
+ * PLAINTEXT API
+ */
+
+/*
+ * Gather stats for a piece of text
+ *
+ * Fill the `stats` structure with information on the number of
+ * unreadable characters, carriage returns, etc, so it can be
+ * used in heuristics.
+ */
+extern void git_text_gather_stats(git_text_stats *stats, const git_buf *text);
+
+/*
+ * Process `git_text_stats` data generated by `git_text_stat` to see
+ * if it qualifies as a binary file
+ */
+extern int git_text_is_binary(git_text_stats *stats);
+
+#endif
diff --git a/src/ignore.c b/src/ignore.c
index 30f86b822..a3bf0a282 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -11,7 +11,7 @@ static int load_ignore_file(
git_repository *repo, const char *path, git_attr_file *ignores)
{
int error = GIT_SUCCESS;
- git_fbuffer fbuf = GIT_FBUFFER_INIT;
+ git_buf fbuf = GIT_BUF_INIT;
git_attr_fnmatch *match = NULL;
const char *scan = NULL;
char *context = NULL;
@@ -28,7 +28,7 @@ static int load_ignore_file(
if (error == GIT_SUCCESS)
error = git_futils_readbuffer(&fbuf, path);
- scan = fbuf.data;
+ scan = fbuf.ptr;
while (error == GIT_SUCCESS && *scan) {
if (!match && !(match = git__calloc(1, sizeof(git_attr_fnmatch)))) {
@@ -53,7 +53,7 @@ static int load_ignore_file(
}
}
- git_futils_freebuffer(&fbuf);
+ git_buf_free(&fbuf);
git__free(match);
git__free(context);
diff --git a/src/index.c b/src/index.c
index 4dccad527..5ac99de3e 100644
--- a/src/index.c
+++ b/src/index.c
@@ -216,7 +216,7 @@ void git_index_clear(git_index *index)
int git_index_read(git_index *index)
{
int error = GIT_SUCCESS, updated;
- git_fbuffer buffer = GIT_FBUFFER_INIT;
+ git_buf buffer = GIT_BUF_INIT;
time_t mtime;
assert(index->index_file_path);
@@ -235,12 +235,12 @@ int git_index_read(git_index *index)
if (updated) {
git_index_clear(index);
- error = parse_index(index, buffer.data, buffer.len);
+ error = parse_index(index, buffer.ptr, buffer.size);
if (error == GIT_SUCCESS)
index->last_modified = mtime;
- git_futils_freebuffer(&buffer);
+ git_buf_free(&buffer);
}
if (error < GIT_SUCCESS)
diff --git a/src/odb.c b/src/odb.c
index 4eaf289e7..81fc82ba8 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -393,8 +393,8 @@ static int add_default_backends(git_odb *db, const char *objects_dir, int as_alt
static int load_alternates(git_odb *odb, const char *objects_dir)
{
git_buf alternates_path = GIT_BUF_INIT;
+ git_buf alternates_buf = GIT_BUF_INIT;
char *buffer;
- git_fbuffer alternates_buf = GIT_FBUFFER_INIT;
const char *alternate;
int error;
@@ -412,7 +412,7 @@ static int load_alternates(git_odb *odb, const char *objects_dir)
return git__throw(GIT_EOSERR, "Failed to add backend. Can't read alternates");
}
- buffer = (char *)alternates_buf.data;
+ buffer = (char *)alternates_buf.ptr;
error = GIT_SUCCESS;
/* add each alternate as a new backend; one alternate per line */
@@ -433,7 +433,8 @@ static int load_alternates(git_odb *odb, const char *objects_dir)
}
git_buf_free(&alternates_path);
- git_futils_freebuffer(&alternates_buf);
+ git_buf_free(&alternates_buf);
+
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to load alternates");
return error;
diff --git a/src/odb_loose.c b/src/odb_loose.c
index bb2b7b5f5..f5f6e35ac 100644
--- a/src/odb_loose.c
+++ b/src/odb_loose.c
@@ -75,13 +75,13 @@ static int object_file_name(git_buf *name, const char *dir, const git_oid *id)
}
-static size_t get_binary_object_header(obj_hdr *hdr, git_fbuffer *obj)
+static size_t get_binary_object_header(obj_hdr *hdr, git_buf *obj)
{
unsigned char c;
- unsigned char *data = obj->data;
+ unsigned char *data = (unsigned char *)obj->ptr;
size_t shift, size, used = 0;
- if (obj->len == 0)
+ if (obj->size == 0)
return 0;
c = data[used++];
@@ -90,7 +90,7 @@ static size_t get_binary_object_header(obj_hdr *hdr, git_fbuffer *obj)
size = c & 15;
shift = 4;
while (c & 0x80) {
- if (obj->len <= used)
+ if (obj->size <= used)
return 0;
if (sizeof(size_t) * 8 <= shift)
return 0;
@@ -177,12 +177,12 @@ static void set_stream_output(z_stream *s, void *out, size_t len)
}
-static int start_inflate(z_stream *s, git_fbuffer *obj, void *out, size_t len)
+static int start_inflate(z_stream *s, git_buf *obj, void *out, size_t len)
{
int status;
init_stream(s, out, len);
- set_stream_input(s, obj->data, obj->len);
+ set_stream_input(s, obj->ptr, obj->size);
if ((status = inflateInit(s)) < Z_OK)
return status;
@@ -287,7 +287,7 @@ static void *inflate_tail(z_stream *s, void *hb, size_t used, obj_hdr *hdr)
* of loose object data into packs. This format is no longer used, but
* we must still read it.
*/
-static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_fbuffer *obj)
+static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_buf *obj)
{
unsigned char *in, *buf;
obj_hdr hdr;
@@ -310,8 +310,8 @@ static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_fbuffer *obj)
if (!buf)
return GIT_ENOMEM;
- in = ((unsigned char *)obj->data) + used;
- len = obj->len - used;
+ in = ((unsigned char *)obj->ptr) + used;
+ len = obj->size - used;
if (inflate_buffer(in, len, buf, hdr.size)) {
git__free(buf);
return git__throw(GIT_ERROR, "Failed to inflate loose object. Could not inflate buffer");
@@ -325,7 +325,7 @@ static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_fbuffer *obj)
return GIT_SUCCESS;
}
-static int inflate_disk_obj(git_rawobj *out, git_fbuffer *obj)
+static int inflate_disk_obj(git_rawobj *out, git_buf *obj)
{
unsigned char head[64], *buf;
z_stream zs;
@@ -335,7 +335,7 @@ static int inflate_disk_obj(git_rawobj *out, git_fbuffer *obj)
/*
* check for a pack-like loose object
*/
- if (!is_zlib_compressed_data(obj->data))
+ if (!is_zlib_compressed_data((unsigned char *)obj->ptr))
return inflate_packlike_loose_disk_obj(out, obj);
/*
@@ -383,7 +383,7 @@ static int inflate_disk_obj(git_rawobj *out, git_fbuffer *obj)
static int read_loose(git_rawobj *out, git_buf *loc)
{
int error;
- git_fbuffer obj = GIT_FBUFFER_INIT;
+ git_buf obj = GIT_BUF_INIT;
assert(out && loc);
@@ -398,7 +398,7 @@ static int read_loose(git_rawobj *out, git_buf *loc)
return git__throw(GIT_ENOTFOUND, "Failed to read loose object. File not found");
error = inflate_disk_obj(out, &obj);
- git_futils_freebuffer(&obj);
+ git_buf_free(&obj);
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to read loose object");
}
diff --git a/src/reflog.c b/src/reflog.c
index 9f5ccd322..6ca9418cf 100644
--- a/src/reflog.c
+++ b/src/reflog.c
@@ -183,7 +183,7 @@ int git_reflog_read(git_reflog **reflog, git_reference *ref)
{
int error;
git_buf log_path = GIT_BUF_INIT;
- git_fbuffer log_file = GIT_FBUFFER_INIT;
+ git_buf log_file = GIT_BUF_INIT;
git_reflog *log = NULL;
*reflog = NULL;
@@ -201,7 +201,7 @@ int git_reflog_read(git_reflog **reflog, git_reference *ref)
goto cleanup;
}
- if ((error = reflog_parse(log, log_file.data, log_file.len)) < GIT_SUCCESS)
+ if ((error = reflog_parse(log, log_file.ptr, log_file.size)) < GIT_SUCCESS)
git__rethrow(error, "Failed to read reflog");
else
*reflog = log;
@@ -209,7 +209,7 @@ int git_reflog_read(git_reflog **reflog, git_reference *ref)
cleanup:
if (error != GIT_SUCCESS && log != NULL)
git_reflog_free(log);
- git_futils_freebuffer(&log_file);
+ git_buf_free(&log_file);
git_buf_free(&log_path);
return error;
diff --git a/src/refs.c b/src/refs.c
index 8e911c1ae..2e1d92da2 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -32,15 +32,15 @@ struct packref {
static const int default_table_size = 32;
static int reference_read(
- git_fbuffer *file_content,
+ git_buf *file_content,
time_t *mtime,
const char *repo_path,
const char *ref_name,
int *updated);
/* loose refs */
-static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content);
-static int loose_parse_oid(git_oid *ref, git_fbuffer *file_content);
+static int loose_parse_symbolic(git_reference *ref, git_buf *file_content);
+static int loose_parse_oid(git_oid *ref, git_buf *file_content);
static int loose_lookup(git_reference *ref);
static int loose_lookup_to_packfile(struct packref **ref_out,
git_repository *repo, const char *name);
@@ -113,7 +113,7 @@ static int reference_alloc(
return GIT_SUCCESS;
}
-static int reference_read(git_fbuffer *file_content, time_t *mtime, const char *repo_path, const char *ref_name, int *updated)
+static int reference_read(git_buf *file_content, time_t *mtime, const char *repo_path, const char *ref_name, int *updated)
{
git_buf path = GIT_BUF_INIT;
int error = GIT_SUCCESS;
@@ -129,15 +129,15 @@ static int reference_read(git_fbuffer *file_content, time_t *mtime, const char *
return error;
}
-static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content)
+static int loose_parse_symbolic(git_reference *ref, git_buf *file_content)
{
const unsigned int header_len = strlen(GIT_SYMREF);
const char *refname_start;
char *eol;
- refname_start = (const char *)file_content->data;
+ refname_start = (const char *)file_content->ptr;
- if (file_content->len < (header_len + 1))
+ if (file_content->size < (header_len + 1))
return git__throw(GIT_EOBJCORRUPTED,
"Failed to parse loose reference. Object too short");
@@ -165,15 +165,15 @@ static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content)
return GIT_SUCCESS;
}
-static int loose_parse_oid(git_oid *oid, git_fbuffer *file_content)
+static int loose_parse_oid(git_oid *oid, git_buf *file_content)
{
int error;
char *buffer;
- buffer = (char *)file_content->data;
+ buffer = (char *)file_content->ptr;
/* File format: 40 chars (OID) + newline */
- if (file_content->len < GIT_OID_HEXSZ + 1)
+ if (file_content->size < GIT_OID_HEXSZ + 1)
return git__throw(GIT_EOBJCORRUPTED,
"Failed to parse loose reference. Reference too short");
@@ -193,26 +193,26 @@ static int loose_parse_oid(git_oid *oid, git_fbuffer *file_content)
static git_rtype loose_guess_rtype(const git_buf *full_path)
{
- git_fbuffer ref_file = GIT_FBUFFER_INIT;
+ git_buf ref_file = GIT_BUF_INIT;
git_rtype type;
type = GIT_REF_INVALID;
if (git_futils_readbuffer(&ref_file, full_path->ptr) == GIT_SUCCESS) {
- if (git__prefixcmp((const char *)(ref_file.data), GIT_SYMREF) == 0)
+ if (git__prefixcmp((const char *)(ref_file.ptr), GIT_SYMREF) == 0)
type = GIT_REF_SYMBOLIC;
else
type = GIT_REF_OID;
}
- git_futils_freebuffer(&ref_file);
+ git_buf_free(&ref_file);
return type;
}
static int loose_lookup(git_reference *ref)
{
int error = GIT_SUCCESS, updated;
- git_fbuffer ref_file = GIT_FBUFFER_INIT;
+ git_buf ref_file = GIT_BUF_INIT;
if (reference_read(&ref_file, &ref->mtime,
ref->owner->path_repository, ref->name, &updated) < GIT_SUCCESS)
@@ -228,7 +228,7 @@ static int loose_lookup(git_reference *ref)
ref->flags = 0;
- if (git__prefixcmp((const char *)(ref_file.data), GIT_SYMREF) == 0) {
+ if (git__prefixcmp((const char *)(ref_file.ptr), GIT_SYMREF) == 0) {
ref->flags |= GIT_REF_SYMBOLIC;
error = loose_parse_symbolic(ref, &ref_file);
} else {
@@ -236,7 +236,7 @@ static int loose_lookup(git_reference *ref)
error = loose_parse_oid(&ref->target.oid, &ref_file);
}
- git_futils_freebuffer(&ref_file);
+ git_buf_free(&ref_file);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to lookup loose reference");
@@ -250,7 +250,7 @@ static int loose_lookup_to_packfile(
const char *name)
{
int error = GIT_SUCCESS;
- git_fbuffer ref_file = GIT_FBUFFER_INIT;
+ git_buf ref_file = GIT_BUF_INIT;
struct packref *ref = NULL;
size_t name_len;
@@ -273,11 +273,11 @@ static int loose_lookup_to_packfile(
ref->flags = GIT_PACKREF_WAS_LOOSE;
*ref_out = ref;
- git_futils_freebuffer(&ref_file);
+ git_buf_free(&ref_file);
return GIT_SUCCESS;
cleanup:
- git_futils_freebuffer(&ref_file);
+ git_buf_free(&ref_file);
free(ref);
return git__rethrow(error, "Failed to lookup loose reference");
}
@@ -427,7 +427,7 @@ cleanup:
static int packed_load(git_repository *repo)
{
int error = GIT_SUCCESS, updated;
- git_fbuffer packfile = GIT_FBUFFER_INIT;
+ git_buf packfile = GIT_BUF_INIT;
const char *buffer_start, *buffer_end;
git_refcache *ref_cache = &repo->references;
@@ -468,8 +468,8 @@ static int packed_load(git_repository *repo)
git_hashtable_clear(ref_cache->packfile);
- buffer_start = (const char *)packfile.data;
- buffer_end = (const char *)(buffer_start) + packfile.len;
+ buffer_start = (const char *)packfile.ptr;
+ buffer_end = (const char *)(buffer_start) + packfile.size;
while (buffer_start < buffer_end && buffer_start[0] == '#') {
buffer_start = strchr(buffer_start, '\n');
@@ -500,13 +500,13 @@ static int packed_load(git_repository *repo)
}
}
- git_futils_freebuffer(&packfile);
+ git_buf_free(&packfile);
return GIT_SUCCESS;
cleanup:
git_hashtable_free(ref_cache->packfile);
ref_cache->packfile = NULL;
- git_futils_freebuffer(&packfile);
+ git_buf_free(&packfile);
return git__rethrow(error, "Failed to load packed references");
}
diff --git a/src/repository.c b/src/repository.c
index f394d06fe..1f8306991 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -43,6 +43,8 @@ static void drop_config(git_repository *repo)
git_config_free(repo->_config);
repo->_config = NULL;
}
+
+ git_repository__cvar_cache_clear(repo);
}
static void drop_index(git_repository *repo)
@@ -111,6 +113,9 @@ static git_repository *repository_alloc(void)
return NULL;
}
+ /* set all the entries in the cvar cache to `unset` */
+ git_repository__cvar_cache_clear(repo);
+
return repo;
}
@@ -467,7 +472,7 @@ static int retrieve_ceiling_directories_offset(
*/
static int read_gitfile(git_buf *path_out, const char *file_path, const char *base_path)
{
- git_fbuffer file;
+ git_buf file = GIT_BUF_INIT;
int error;
assert(path_out && file_path);
@@ -476,22 +481,22 @@ static int read_gitfile(git_buf *path_out, const char *file_path, const char *ba
if (error < GIT_SUCCESS)
return error;
- if (git__prefixcmp((char *)file.data, GIT_FILE_CONTENT_PREFIX)) {
- git_futils_freebuffer(&file);
+ if (git__prefixcmp((char *)file.ptr, GIT_FILE_CONTENT_PREFIX)) {
+ git_buf_free(&file);
return git__throw(GIT_ENOTFOUND, "Invalid gitfile format `%s`", file_path);
}
- git_futils_fbuffer_rtrim(&file);
+ git_buf_rtrim(&file);
- if (strlen(GIT_FILE_CONTENT_PREFIX) == file.len) {
- git_futils_freebuffer(&file);
+ if (strlen(GIT_FILE_CONTENT_PREFIX) == file.size) {
+ git_buf_free(&file);
return git__throw(GIT_ENOTFOUND, "No path in git file `%s`", file_path);
}
error = git_path_prettify_dir(path_out,
- ((char *)file.data) + strlen(GIT_FILE_CONTENT_PREFIX), base_path);
+ ((char *)file.ptr) + strlen(GIT_FILE_CONTENT_PREFIX), base_path);
- git_futils_freebuffer(&file);
+ git_buf_free(&file);
if (error == GIT_SUCCESS && git_path_exists(path_out->ptr) == 0)
return GIT_SUCCESS;
diff --git a/src/repository.h b/src/repository.h
index 516fd10be..b5dcc1340 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -26,6 +26,49 @@
#define GIT_DIR_MODE 0755
#define GIT_BARE_DIR_MODE 0777
+/** Cvar cache identifiers */
+typedef enum {
+ GIT_CVAR_AUTO_CRLF = 0, /* core.autocrlf */
+ GIT_CVAR_EOL, /* core.eol */
+ GIT_CVAR_CACHE_MAX
+} git_cvar_cached;
+
+/**
+ * CVAR value enumerations
+ *
+ * These are the values that are actually stored in the cvar cache, instead
+ * of their string equivalents. These values are internal and symbolic;
+ * make sure that none of them is set to `-1`, since that is the unique
+ * identifier for "not cached"
+ */
+typedef enum {
+ /* The value hasn't been loaded from the cache yet */
+ GIT_CVAR_NOT_CACHED = -1,
+
+ /* core.safecrlf: false, 'fail', 'warn' */
+ GIT_SAFE_CRLF_FALSE = 0,
+ GIT_SAFE_CRLF_FAIL = 1,
+ GIT_SAFE_CRLF_WARN = 2,
+
+ /* core.autocrlf: false, true, 'input; */
+ GIT_AUTO_CRLF_FALSE = 0,
+ GIT_AUTO_CRLF_TRUE = 1,
+ GIT_AUTO_CRLF_INPUT = 2,
+ GIT_AUTO_CRLF_DEFAULT = GIT_AUTO_CRLF_FALSE,
+
+ /* core.eol: unset, 'crlf', 'lf', 'native' */
+ GIT_EOL_UNSET = 0,
+ GIT_EOL_CRLF = 1,
+ GIT_EOL_LF = 2,
+#ifdef GIT_WIN32
+ GIT_EOL_NATIVE = GIT_EOL_CRLF,
+#else
+ GIT_EOL_NATIVE = GIT_EOL_LF,
+#endif
+ GIT_EOL_DEFAULT = GIT_EOL_NATIVE
+} git_cvar_value;
+
+/** Base git object for inheritance */
struct git_object {
git_cached_obj cached;
git_repository *repo;
@@ -46,6 +89,8 @@ struct git_repository {
unsigned is_bare:1;
unsigned int lru_counter;
+
+ git_cvar_value cvar_cache[GIT_CVAR_CACHE_MAX];
};
/* fully free the object; internal method, do not
@@ -55,8 +100,24 @@ void git_object__free(void *object);
int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end, const char *header);
void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid);
+/*
+ * Weak pointers to repository internals.
+ *
+ * The returned pointers do not need to be freed. Do not keep
+ * permanent references to these (i.e. between API calls), since they may
+ * become invalidated if the user replaces a repository internal.
+ */
int git_repository_config__weakptr(git_config **out, git_repository *repo);
int git_repository_odb__weakptr(git_odb **out, git_repository *repo);
int git_repository_index__weakptr(git_index **out, git_repository *repo);
+/*
+ * CVAR cache
+ *
+ * Efficient access to the most used config variables of a repository.
+ * The cache is cleared everytime the config backend is replaced.
+ */
+int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar);
+void git_repository__cvar_cache_clear(git_repository *repo);
+
#endif
diff --git a/tests-clar/attr/attr_expect.h b/tests-clar/attr/attr_expect.h
new file mode 100644
index 000000000..bea562457
--- /dev/null
+++ b/tests-clar/attr/attr_expect.h
@@ -0,0 +1,42 @@
+#ifndef __CLAR_TEST_ATTR_EXPECT__
+#define __CLAR_TEST_ATTR_EXPECT__
+
+enum attr_expect_t {
+ EXPECT_FALSE,
+ EXPECT_TRUE,
+ EXPECT_UNDEFINED,
+ EXPECT_STRING
+};
+
+struct attr_expected {
+ const char *path;
+ const char *attr;
+ enum attr_expect_t expected;
+ const char *expected_str;
+};
+
+static inline void attr_check_expected(
+ enum attr_expect_t expected,
+ const char *expected_str,
+ const char *value)
+{
+ switch (expected) {
+ case EXPECT_TRUE:
+ cl_assert(GIT_ATTR_TRUE(value));
+ break;
+
+ case EXPECT_FALSE:
+ cl_assert(GIT_ATTR_FALSE(value));
+ break;
+
+ case EXPECT_UNDEFINED:
+ cl_assert(GIT_ATTR_UNSPECIFIED(value));
+ break;
+
+ case EXPECT_STRING:
+ cl_assert_strequal(expected_str, value);
+ break;
+ }
+}
+
+#endif
diff --git a/tests-clar/attr/file.c b/tests-clar/attr/file.c
index af50cd38e..132b906cd 100644
--- a/tests-clar/attr/file.c
+++ b/tests-clar/attr/file.c
@@ -1,5 +1,6 @@
#include "clar_libgit2.h"
#include "attr_file.h"
+#include "attr_expect.h"
#define get_rule(X) ((git_attr_rule *)git_vector_get(&file->rules,(X)))
#define get_assign(R,Y) ((git_attr_assignment *)git_vector_get(&(R)->assigns,(Y)))
@@ -25,7 +26,7 @@ void test_attr_file__simple_read(void)
assign = get_assign(rule, 0);
cl_assert(assign != NULL);
cl_assert_strequal("binary", assign->name);
- cl_assert(assign->value == GIT_ATTR_TRUE);
+ cl_assert(GIT_ATTR_TRUE(assign->value));
cl_assert(!assign->is_allocated);
git_attr_file__free(file);
@@ -54,7 +55,7 @@ void test_attr_file__match_variants(void)
assign = get_assign(rule,0);
cl_assert_strequal("attr0", assign->name);
cl_assert(assign->name_hash == git_attr_file__name_hash(assign->name));
- cl_assert(assign->value == GIT_ATTR_TRUE);
+ cl_assert(GIT_ATTR_TRUE(assign->value));
cl_assert(!assign->is_allocated);
rule = get_rule(1);
@@ -83,7 +84,7 @@ void test_attr_file__match_variants(void)
cl_assert(rule->assigns.length == 1);
assign = get_assign(rule,0);
cl_assert_strequal("attr7", assign->name);
- cl_assert(assign->value == GIT_ATTR_TRUE);
+ cl_assert(GIT_ATTR_TRUE(assign->value));
rule = get_rule(8);
cl_assert_strequal("pat8 with spaces", rule->match.pattern);
@@ -102,8 +103,8 @@ static void check_one_assign(
int assign_idx,
const char *pattern,
const char *name,
- const char *value,
- int is_allocated)
+ enum attr_expect_t expected,
+ const char *expected_str)
{
git_attr_rule *rule = get_rule(rule_idx);
git_attr_assignment *assign = get_assign(rule, assign_idx);
@@ -112,11 +113,8 @@ static void check_one_assign(
cl_assert(rule->assigns.length == 1);
cl_assert_strequal(name, assign->name);
cl_assert(assign->name_hash == git_attr_file__name_hash(assign->name));
- cl_assert(assign->is_allocated == is_allocated);
- if (is_allocated)
- cl_assert_strequal(value, assign->value);
- else
- cl_assert(assign->value == value);
+
+ attr_check_expected(expected, expected_str, assign->value);
}
void test_attr_file__assign_variants(void)
@@ -130,14 +128,14 @@ void test_attr_file__assign_variants(void)
cl_assert_strequal(cl_fixture("attr/attr2"), file->path);
cl_assert(file->rules.length == 11);
- check_one_assign(file, 0, 0, "pat0", "simple", GIT_ATTR_TRUE, 0);
- check_one_assign(file, 1, 0, "pat1", "neg", GIT_ATTR_FALSE, 0);
- check_one_assign(file, 2, 0, "*", "notundef", GIT_ATTR_TRUE, 0);
- check_one_assign(file, 3, 0, "pat2", "notundef", NULL, 0);
- check_one_assign(file, 4, 0, "pat3", "assigned", "test-value", 1);
- check_one_assign(file, 5, 0, "pat4", "rule-with-more-chars", "value-with-more-chars", 1);
- check_one_assign(file, 6, 0, "pat5", "empty", GIT_ATTR_TRUE, 0);
- check_one_assign(file, 7, 0, "pat6", "negempty", GIT_ATTR_FALSE, 0);
+ check_one_assign(file, 0, 0, "pat0", "simple", EXPECT_TRUE, NULL);
+ check_one_assign(file, 1, 0, "pat1", "neg", EXPECT_FALSE, NULL);
+ check_one_assign(file, 2, 0, "*", "notundef", EXPECT_TRUE, NULL);
+ check_one_assign(file, 3, 0, "pat2", "notundef", EXPECT_UNDEFINED, NULL);
+ check_one_assign(file, 4, 0, "pat3", "assigned", EXPECT_STRING, "test-value");
+ check_one_assign(file, 5, 0, "pat4", "rule-with-more-chars", EXPECT_STRING, "value-with-more-chars");
+ check_one_assign(file, 6, 0, "pat5", "empty", EXPECT_TRUE, NULL);
+ check_one_assign(file, 7, 0, "pat6", "negempty", EXPECT_FALSE, NULL);
rule = get_rule(8);
cl_assert_strequal("pat7", rule->match.pattern);
@@ -148,11 +146,11 @@ void test_attr_file__assign_variants(void)
assign = git_attr_rule__lookup_assignment(rule, "multiple");
cl_assert(assign);
cl_assert_strequal("multiple", assign->name);
- cl_assert(assign->value == GIT_ATTR_TRUE);
+ cl_assert(GIT_ATTR_TRUE(assign->value));
assign = git_attr_rule__lookup_assignment(rule, "single");
cl_assert(assign);
cl_assert_strequal("single", assign->name);
- cl_assert(assign->value == GIT_ATTR_FALSE);
+ cl_assert(GIT_ATTR_FALSE(assign->value));
assign = git_attr_rule__lookup_assignment(rule, "values");
cl_assert(assign);
cl_assert_strequal("values", assign->name);
@@ -174,13 +172,13 @@ void test_attr_file__assign_variants(void)
assign = git_attr_rule__lookup_assignment(rule, "again");
cl_assert(assign);
cl_assert_strequal("again", assign->name);
- cl_assert(assign->value == GIT_ATTR_TRUE);
+ cl_assert(GIT_ATTR_TRUE(assign->value));
assign = git_attr_rule__lookup_assignment(rule, "another");
cl_assert(assign);
cl_assert_strequal("another", assign->name);
cl_assert_strequal("12321", assign->value);
- check_one_assign(file, 10, 0, "pat9", "at-eof", GIT_ATTR_FALSE, 0);
+ check_one_assign(file, 10, 0, "pat9", "at-eof", EXPECT_FALSE, NULL);
git_attr_file__free(file);
}
@@ -204,10 +202,10 @@ void test_attr_file__check_attr_examples(void)
cl_assert_strequal("java", assign->value);
assign = git_attr_rule__lookup_assignment(rule, "crlf");
cl_assert_strequal("crlf", assign->name);
- cl_assert(GIT_ATTR_FALSE == assign->value);
+ cl_assert(GIT_ATTR_FALSE(assign->value));
assign = git_attr_rule__lookup_assignment(rule, "myAttr");
cl_assert_strequal("myAttr", assign->name);
- cl_assert(GIT_ATTR_TRUE == assign->value);
+ cl_assert(GIT_ATTR_TRUE(assign->value));
assign = git_attr_rule__lookup_assignment(rule, "missing");
cl_assert(assign == NULL);
diff --git a/tests-clar/attr/lookup.c b/tests-clar/attr/lookup.c
index 9462bbe7f..19396182e 100644
--- a/tests-clar/attr/lookup.c
+++ b/tests-clar/attr/lookup.c
@@ -1,6 +1,8 @@
#include "clar_libgit2.h"
#include "attr_file.h"
+#include "attr_expect.h"
+
void test_attr_lookup__simple(void)
{
git_attr_file *file;
@@ -18,7 +20,7 @@ void test_attr_lookup__simple(void)
cl_assert(!path.is_dir);
cl_git_pass(git_attr_file__lookup_one(file,&path,"binary",&value));
- cl_assert(value == GIT_ATTR_TRUE);
+ cl_assert(GIT_ATTR_TRUE(value));
cl_git_pass(git_attr_file__lookup_one(file,&path,"missing",&value));
cl_assert(!value);
@@ -26,36 +28,23 @@ void test_attr_lookup__simple(void)
git_attr_file__free(file);
}
-typedef struct {
- const char *path;
- const char *attr;
- const char *expected;
- int use_strcmp;
- int force_dir;
-} test_case;
-
-static void run_test_cases(git_attr_file *file, test_case *cases)
+static void run_test_cases(git_attr_file *file, struct attr_expected *cases, int force_dir)
{
git_attr_path path;
const char *value = NULL;
- test_case *c;
+ struct attr_expected *c;
int error;
for (c = cases; c->path != NULL; c++) {
cl_git_pass(git_attr_path__init(&path, c->path, NULL));
- if (c->force_dir)
+ if (force_dir)
path.is_dir = 1;
error = git_attr_file__lookup_one(file,&path,c->attr,&value);
- if (error != GIT_SUCCESS)
- fprintf(stderr, "failure with %s %s %s\n", c->path, c->attr, c->expected);
cl_git_pass(error);
- if (c->use_strcmp)
- cl_assert_strequal(c->expected, value);
- else
- cl_assert(c->expected == value);
+ attr_check_expected(c->expected, c->expected_str, value);
}
}
@@ -63,74 +52,79 @@ void test_attr_lookup__match_variants(void)
{
git_attr_file *file;
git_attr_path path;
- test_case cases[] = {
+
+ struct attr_expected dir_cases[] = {
+ { "pat2", "attr2", EXPECT_TRUE, NULL },
+ { "/testing/for/pat2", "attr2", EXPECT_TRUE, NULL },
+ { "/not/pat2/yousee", "attr2", EXPECT_UNDEFINED, NULL },
+ { "/fun/fun/fun/pat4.dir", "attr4", EXPECT_TRUE, NULL },
+ { "foo.pat5", "attr5", EXPECT_TRUE, NULL },
+ { NULL, NULL, 0, NULL }
+ };
+
+ struct attr_expected cases[] = {
/* pat0 -> simple match */
- { "pat0", "attr0", GIT_ATTR_TRUE, 0, 0 },
- { "/testing/for/pat0", "attr0", GIT_ATTR_TRUE, 0, 0 },
- { "relative/to/pat0", "attr0", GIT_ATTR_TRUE, 0, 0 },
- { "this-contains-pat0-inside", "attr0", NULL, 0, 0 },
- { "this-aint-right", "attr0", NULL, 0, 0 },
- { "/this/pat0/dont/match", "attr0", NULL, 0, 0 },
+ { "pat0", "attr0", EXPECT_TRUE, NULL },
+ { "/testing/for/pat0", "attr0", EXPECT_TRUE, NULL },
+ { "relative/to/pat0", "attr0", EXPECT_TRUE, NULL },
+ { "this-contains-pat0-inside", "attr0", EXPECT_UNDEFINED, NULL },
+ { "this-aint-right", "attr0", EXPECT_UNDEFINED, NULL },
+ { "/this/pat0/dont/match", "attr0", EXPECT_UNDEFINED, NULL },
/* negative match */
- { "pat0", "attr1", GIT_ATTR_TRUE, 0, 0 },
- { "pat1", "attr1", NULL, 0, 0 },
- { "/testing/for/pat1", "attr1", NULL, 0, 0 },
- { "/testing/for/pat0", "attr1", GIT_ATTR_TRUE, 0, 0 },
- { "/testing/for/pat1/inside", "attr1", GIT_ATTR_TRUE, 0, 0 },
- { "misc", "attr1", GIT_ATTR_TRUE, 0, 0 },
+ { "pat0", "attr1", EXPECT_TRUE, NULL },
+ { "pat1", "attr1", EXPECT_UNDEFINED, NULL },
+ { "/testing/for/pat1", "attr1", EXPECT_UNDEFINED, NULL },
+ { "/testing/for/pat0", "attr1", EXPECT_TRUE, NULL },
+ { "/testing/for/pat1/inside", "attr1", EXPECT_TRUE, NULL },
+ { "misc", "attr1", EXPECT_TRUE, NULL },
/* dir match */
- { "pat2", "attr2", NULL, 0, 0 },
- { "pat2", "attr2", GIT_ATTR_TRUE, 0, 1 },
- { "/testing/for/pat2", "attr2", NULL, 0, 0 },
- { "/testing/for/pat2", "attr2", GIT_ATTR_TRUE, 0, 1 },
- { "/not/pat2/yousee", "attr2", NULL, 0, 0 },
- { "/not/pat2/yousee", "attr2", NULL, 0, 1 },
+ { "pat2", "attr2", EXPECT_UNDEFINED, NULL },
+ { "/testing/for/pat2", "attr2", EXPECT_UNDEFINED, NULL },
+ { "/not/pat2/yousee", "attr2", EXPECT_UNDEFINED, NULL },
/* path match */
- { "pat3file", "attr3", NULL, 0, 0 },
- { "/pat3dir/pat3file", "attr3", NULL, 0, 0 },
- { "pat3dir/pat3file", "attr3", GIT_ATTR_TRUE, 0, 0 },
+ { "pat3file", "attr3", EXPECT_UNDEFINED, NULL },
+ { "/pat3dir/pat3file", "attr3", EXPECT_UNDEFINED, NULL },
+ { "pat3dir/pat3file", "attr3", EXPECT_TRUE, NULL },
/* pattern* match */
- { "pat4.txt", "attr4", GIT_ATTR_TRUE, 0, 0 },
- { "/fun/fun/fun/pat4.c", "attr4", GIT_ATTR_TRUE, 0, 0 },
- { "pat4.", "attr4", GIT_ATTR_TRUE, 0, 0 },
- { "pat4", "attr4", NULL, 0, 0 },
- { "/fun/fun/fun/pat4.dir", "attr4", GIT_ATTR_TRUE, 0, 1 },
+ { "pat4.txt", "attr4", EXPECT_TRUE, NULL },
+ { "/fun/fun/fun/pat4.c", "attr4", EXPECT_TRUE, NULL },
+ { "pat4.", "attr4", EXPECT_TRUE, NULL },
+ { "pat4", "attr4", EXPECT_UNDEFINED, NULL },
/* *pattern match */
- { "foo.pat5", "attr5", GIT_ATTR_TRUE, 0, 0 },
- { "foo.pat5", "attr5", GIT_ATTR_TRUE, 0, 1 },
- { "/this/is/ok.pat5", "attr5", GIT_ATTR_TRUE, 0, 0 },
- { "/this/is/bad.pat5/yousee.txt", "attr5", NULL, 0, 0 },
- { "foo.pat5", "attr100", NULL, 0, 0 },
+ { "foo.pat5", "attr5", EXPECT_TRUE, NULL },
+ { "/this/is/ok.pat5", "attr5", EXPECT_TRUE, NULL },
+ { "/this/is/bad.pat5/yousee.txt", "attr5", EXPECT_UNDEFINED, NULL },
+ { "foo.pat5", "attr100", EXPECT_UNDEFINED, NULL },
/* glob match with slashes */
- { "foo.pat6", "attr6", NULL, 0, 0 },
- { "pat6/pat6/foobar.pat6", "attr6", GIT_ATTR_TRUE, 0, 0 },
- { "pat6/pat6/.pat6", "attr6", GIT_ATTR_TRUE, 0, 0 },
- { "pat6/pat6/extra/foobar.pat6", "attr6", NULL, 0, 0 },
- { "/prefix/pat6/pat6/foobar.pat6", "attr6", NULL, 0, 0 },
- { "/pat6/pat6/foobar.pat6", "attr6", NULL, 0, 0 },
+ { "foo.pat6", "attr6", EXPECT_UNDEFINED, NULL },
+ { "pat6/pat6/foobar.pat6", "attr6", EXPECT_TRUE, NULL },
+ { "pat6/pat6/.pat6", "attr6", EXPECT_TRUE, NULL },
+ { "pat6/pat6/extra/foobar.pat6", "attr6", EXPECT_UNDEFINED, NULL },
+ { "/prefix/pat6/pat6/foobar.pat6", "attr6", EXPECT_UNDEFINED, NULL },
+ { "/pat6/pat6/foobar.pat6", "attr6", EXPECT_UNDEFINED, NULL },
/* complex pattern */
- { "pat7a12z", "attr7", GIT_ATTR_TRUE, 0, 0 },
- { "pat7e__x", "attr7", GIT_ATTR_TRUE, 0, 0 },
- { "pat7b/1y", "attr7", NULL, 0, 0 }, /* ? does not match / */
- { "pat7e_x", "attr7", NULL, 0, 0 },
- { "pat7aaaa", "attr7", NULL, 0, 0 },
- { "pat7zzzz", "attr7", NULL, 0, 0 },
- { "/this/can/be/anything/pat7a12z", "attr7", GIT_ATTR_TRUE, 0, 0 },
- { "but/it/still/must/match/pat7aaaa", "attr7", NULL, 0, 0 },
- { "pat7aaay.fail", "attr7", NULL, 0, 0 },
+ { "pat7a12z", "attr7", EXPECT_TRUE, NULL },
+ { "pat7e__x", "attr7", EXPECT_TRUE, NULL },
+ { "pat7b/1y", "attr7", EXPECT_UNDEFINED, NULL }, /* ? does not match / */
+ { "pat7e_x", "attr7", EXPECT_UNDEFINED, NULL },
+ { "pat7aaaa", "attr7", EXPECT_UNDEFINED, NULL },
+ { "pat7zzzz", "attr7", EXPECT_UNDEFINED, NULL },
+ { "/this/can/be/anything/pat7a12z", "attr7", EXPECT_TRUE, NULL },
+ { "but/it/still/must/match/pat7aaaa", "attr7", EXPECT_UNDEFINED, NULL },
+ { "pat7aaay.fail", "attr7", EXPECT_UNDEFINED, NULL },
/* pattern with spaces */
- { "pat8 with spaces", "attr8", GIT_ATTR_TRUE, 0, 0 },
- { "/gotta love/pat8 with spaces", "attr8", GIT_ATTR_TRUE, 0, 0 },
- { "failing pat8 with spaces", "attr8", NULL, 0, 0 },
- { "spaces", "attr8", NULL, 0, 0 },
+ { "pat8 with spaces", "attr8", EXPECT_TRUE, NULL },
+ { "/gotta love/pat8 with spaces", "attr8", EXPECT_TRUE, NULL },
+ { "failing pat8 with spaces", "attr8", EXPECT_UNDEFINED, NULL },
+ { "spaces", "attr8", EXPECT_UNDEFINED, NULL },
/* pattern at eof */
- { "pat9", "attr9", GIT_ATTR_TRUE, 0, 0 },
- { "/eof/pat9", "attr9", GIT_ATTR_TRUE, 0, 0 },
- { "pat", "attr9", NULL, 0, 0 },
- { "at9", "attr9", NULL, 0, 0 },
- { "pat9.fail", "attr9", NULL, 0, 0 },
+ { "pat9", "attr9", EXPECT_TRUE, NULL },
+ { "/eof/pat9", "attr9", EXPECT_TRUE, NULL },
+ { "pat", "attr9", EXPECT_UNDEFINED, NULL },
+ { "at9", "attr9", EXPECT_UNDEFINED, NULL },
+ { "pat9.fail", "attr9", EXPECT_UNDEFINED, NULL },
/* sentinel at end */
- { NULL, NULL, NULL, 0, 0 }
+ { NULL, NULL, 0, NULL }
};
cl_git_pass(git_attr_file__new(&file));
@@ -141,7 +135,8 @@ void test_attr_lookup__match_variants(void)
cl_git_pass(git_attr_path__init(&path, "/testing/for/pat0", NULL));
cl_assert_strequal("pat0", path.basename);
- run_test_cases(file, cases);
+ run_test_cases(file, cases, 0);
+ run_test_cases(file, dir_cases, 1);
git_attr_file__free(file);
}
@@ -149,54 +144,54 @@ void test_attr_lookup__match_variants(void)
void test_attr_lookup__assign_variants(void)
{
git_attr_file *file;
- test_case cases[] = {
+ struct attr_expected cases[] = {
/* pat0 -> simple assign */
- { "pat0", "simple", GIT_ATTR_TRUE, 0, 0 },
- { "/testing/pat0", "simple", GIT_ATTR_TRUE, 0, 0 },
- { "pat0", "fail", NULL, 0, 0 },
- { "/testing/pat0", "fail", NULL, 0, 0 },
+ { "pat0", "simple", EXPECT_TRUE, NULL },
+ { "/testing/pat0", "simple", EXPECT_TRUE, NULL },
+ { "pat0", "fail", EXPECT_UNDEFINED, NULL },
+ { "/testing/pat0", "fail", EXPECT_UNDEFINED, NULL },
/* negative assign */
- { "pat1", "neg", GIT_ATTR_FALSE, 0, 0 },
- { "/testing/pat1", "neg", GIT_ATTR_FALSE, 0, 0 },
- { "pat1", "fail", NULL, 0, 0 },
- { "/testing/pat1", "fail", NULL, 0, 0 },
+ { "pat1", "neg", EXPECT_FALSE, NULL },
+ { "/testing/pat1", "neg", EXPECT_FALSE, NULL },
+ { "pat1", "fail", EXPECT_UNDEFINED, NULL },
+ { "/testing/pat1", "fail", EXPECT_UNDEFINED, NULL },
/* forced undef */
- { "pat1", "notundef", GIT_ATTR_TRUE, 0, 0 },
- { "pat2", "notundef", NULL, 0, 0 },
- { "/lead/in/pat1", "notundef", GIT_ATTR_TRUE, 0, 0 },
- { "/lead/in/pat2", "notundef", NULL, 0, 0 },
+ { "pat1", "notundef", EXPECT_TRUE, NULL },
+ { "pat2", "notundef", EXPECT_UNDEFINED, NULL },
+ { "/lead/in/pat1", "notundef", EXPECT_TRUE, NULL },
+ { "/lead/in/pat2", "notundef", EXPECT_UNDEFINED, NULL },
/* assign value */
- { "pat3", "assigned", "test-value", 1, 0 },
- { "pat3", "notassigned", NULL, 0, 0 },
+ { "pat3", "assigned", EXPECT_STRING, "test-value" },
+ { "pat3", "notassigned", EXPECT_UNDEFINED, NULL },
/* assign value */
- { "pat4", "rule-with-more-chars", "value-with-more-chars", 1, 0 },
- { "pat4", "notassigned-rule-with-more-chars", NULL, 0, 0 },
+ { "pat4", "rule-with-more-chars", EXPECT_STRING, "value-with-more-chars" },
+ { "pat4", "notassigned-rule-with-more-chars", EXPECT_UNDEFINED, NULL },
/* empty assignments */
- { "pat5", "empty", GIT_ATTR_TRUE, 0, 0 },
- { "pat6", "negempty", GIT_ATTR_FALSE, 0, 0 },
+ { "pat5", "empty", EXPECT_TRUE, NULL },
+ { "pat6", "negempty", EXPECT_FALSE, NULL },
/* multiple assignment */
- { "pat7", "multiple", GIT_ATTR_TRUE, 0, 0 },
- { "pat7", "single", GIT_ATTR_FALSE, 0, 0 },
- { "pat7", "values", "1", 1, 0 },
- { "pat7", "also", "a-really-long-value/*", 1, 0 },
- { "pat7", "happy", "yes!", 1, 0 },
- { "pat8", "again", GIT_ATTR_TRUE, 0, 0 },
- { "pat8", "another", "12321", 1, 0 },
+ { "pat7", "multiple", EXPECT_TRUE, NULL },
+ { "pat7", "single", EXPECT_FALSE, NULL },
+ { "pat7", "values", EXPECT_STRING, "1" },
+ { "pat7", "also", EXPECT_STRING, "a-really-long-value/*" },
+ { "pat7", "happy", EXPECT_STRING, "yes!" },
+ { "pat8", "again", EXPECT_TRUE, NULL },
+ { "pat8", "another", EXPECT_STRING, "12321" },
/* bad assignment */
- { "patbad0", "simple", NULL, 0, 0 },
- { "patbad0", "notundef", GIT_ATTR_TRUE, 0, 0 },
- { "patbad1", "simple", NULL, 0, 0 },
+ { "patbad0", "simple", EXPECT_UNDEFINED, NULL },
+ { "patbad0", "notundef", EXPECT_TRUE, NULL },
+ { "patbad1", "simple", EXPECT_UNDEFINED, NULL },
/* eof assignment */
- { "pat9", "at-eof", GIT_ATTR_FALSE, 0, 0 },
+ { "pat9", "at-eof", EXPECT_FALSE, NULL },
/* sentinel at end */
- { NULL, NULL, NULL, 0, 0 }
+ { NULL, NULL, 0, NULL }
};
cl_git_pass(git_attr_file__new(&file));
cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr2"), file));
cl_assert(file->rules.length == 11);
- run_test_cases(file, cases);
+ run_test_cases(file, cases, 0);
git_attr_file__free(file);
}
@@ -204,34 +199,34 @@ void test_attr_lookup__assign_variants(void)
void test_attr_lookup__check_attr_examples(void)
{
git_attr_file *file;
- test_case cases[] = {
- { "foo.java", "diff", "java", 1, 0 },
- { "foo.java", "crlf", GIT_ATTR_FALSE, 0, 0 },
- { "foo.java", "myAttr", GIT_ATTR_TRUE, 0, 0 },
- { "foo.java", "other", NULL, 0, 0 },
- { "/prefix/dir/foo.java", "diff", "java", 1, 0 },
- { "/prefix/dir/foo.java", "crlf", GIT_ATTR_FALSE, 0, 0 },
- { "/prefix/dir/foo.java", "myAttr", GIT_ATTR_TRUE, 0, 0 },
- { "/prefix/dir/foo.java", "other", NULL, 0, 0 },
- { "NoMyAttr.java", "crlf", GIT_ATTR_FALSE, 0, 0 },
- { "NoMyAttr.java", "myAttr", NULL, 0, 0 },
- { "NoMyAttr.java", "other", NULL, 0, 0 },
- { "/prefix/dir/NoMyAttr.java", "crlf", GIT_ATTR_FALSE, 0, 0 },
- { "/prefix/dir/NoMyAttr.java", "myAttr", NULL, 0, 0 },
- { "/prefix/dir/NoMyAttr.java", "other", NULL, 0, 0 },
- { "README", "caveat", "unspecified", 1, 0 },
- { "/specific/path/README", "caveat", "unspecified", 1, 0 },
- { "README", "missing", NULL, 0, 0 },
- { "/specific/path/README", "missing", NULL, 0, 0 },
+ struct attr_expected cases[] = {
+ { "foo.java", "diff", EXPECT_STRING, "java" },
+ { "foo.java", "crlf", EXPECT_FALSE, NULL },
+ { "foo.java", "myAttr", EXPECT_TRUE, NULL },
+ { "foo.java", "other", EXPECT_UNDEFINED, NULL },
+ { "/prefix/dir/foo.java", "diff", EXPECT_STRING, "java" },
+ { "/prefix/dir/foo.java", "crlf", EXPECT_FALSE, NULL },
+ { "/prefix/dir/foo.java", "myAttr", EXPECT_TRUE, NULL },
+ { "/prefix/dir/foo.java", "other", EXPECT_UNDEFINED, NULL },
+ { "NoMyAttr.java", "crlf", EXPECT_FALSE, NULL },
+ { "NoMyAttr.java", "myAttr", EXPECT_UNDEFINED, NULL },
+ { "NoMyAttr.java", "other", EXPECT_UNDEFINED, NULL },
+ { "/prefix/dir/NoMyAttr.java", "crlf", EXPECT_FALSE, NULL },
+ { "/prefix/dir/NoMyAttr.java", "myAttr", EXPECT_UNDEFINED, NULL },
+ { "/prefix/dir/NoMyAttr.java", "other", EXPECT_UNDEFINED, NULL },
+ { "README", "caveat", EXPECT_STRING, "unspecified" },
+ { "/specific/path/README", "caveat", EXPECT_STRING, "unspecified" },
+ { "README", "missing", EXPECT_UNDEFINED, NULL },
+ { "/specific/path/README", "missing", EXPECT_UNDEFINED, NULL },
/* sentinel at end */
- { NULL, NULL, NULL, 0, 0 }
+ { NULL, NULL, 0, NULL }
};
cl_git_pass(git_attr_file__new(&file));
cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr3"), file));
cl_assert(file->rules.length == 3);
- run_test_cases(file, cases);
+ run_test_cases(file, cases, 0);
git_attr_file__free(file);
}
@@ -239,24 +234,24 @@ void test_attr_lookup__check_attr_examples(void)
void test_attr_lookup__from_buffer(void)
{
git_attr_file *file;
- test_case cases[] = {
- { "abc", "foo", GIT_ATTR_TRUE, 0, 0 },
- { "abc", "bar", GIT_ATTR_TRUE, 0, 0 },
- { "abc", "baz", GIT_ATTR_TRUE, 0, 0 },
- { "aaa", "foo", GIT_ATTR_TRUE, 0, 0 },
- { "aaa", "bar", NULL, 0, 0 },
- { "aaa", "baz", GIT_ATTR_TRUE, 0, 0 },
- { "qqq", "foo", NULL, 0, 0 },
- { "qqq", "bar", NULL, 0, 0 },
- { "qqq", "baz", GIT_ATTR_TRUE, 0, 0 },
- { NULL, NULL, NULL, 0, 0 }
+ struct attr_expected cases[] = {
+ { "abc", "foo", EXPECT_TRUE, NULL },
+ { "abc", "bar", EXPECT_TRUE, NULL },
+ { "abc", "baz", EXPECT_TRUE, NULL },
+ { "aaa", "foo", EXPECT_TRUE, NULL },
+ { "aaa", "bar", EXPECT_UNDEFINED, NULL },
+ { "aaa", "baz", EXPECT_TRUE, NULL },
+ { "qqq", "foo", EXPECT_UNDEFINED, NULL },
+ { "qqq", "bar", EXPECT_UNDEFINED, NULL },
+ { "qqq", "baz", EXPECT_TRUE, NULL },
+ { NULL, NULL, 0, NULL }
};
cl_git_pass(git_attr_file__new(&file));
cl_git_pass(git_attr_file__from_buffer(NULL, "a* foo\nabc bar\n* baz", file));
cl_assert(file->rules.length == 3);
- run_test_cases(file, cases);
+ run_test_cases(file, cases, 0);
git_attr_file__free(file);
}
diff --git a/tests-clar/attr/repo.c b/tests-clar/attr/repo.c
index 7a716042a..722431579 100644
--- a/tests-clar/attr/repo.c
+++ b/tests-clar/attr/repo.c
@@ -3,6 +3,8 @@
#include "git2/attr.h"
#include "attr.h"
+#include "attr_expect.h"
+
static git_repository *g_repo = NULL;
void test_attr_repo__initialize(void)
@@ -28,67 +30,45 @@ void test_attr_repo__cleanup(void)
void test_attr_repo__get_one(void)
{
const char *value;
- struct {
- const char *file;
- const char *attr;
- const char *expected;
- } test_cases[] = {
- { "root_test1", "repoattr", GIT_ATTR_TRUE },
- { "root_test1", "rootattr", GIT_ATTR_TRUE },
- { "root_test1", "missingattr", NULL },
- { "root_test1", "subattr", NULL },
- { "root_test1", "negattr", NULL },
- { "root_test2", "repoattr", GIT_ATTR_TRUE },
- { "root_test2", "rootattr", GIT_ATTR_FALSE },
- { "root_test2", "missingattr", NULL },
- { "root_test2", "multiattr", GIT_ATTR_FALSE },
- { "root_test3", "repoattr", GIT_ATTR_TRUE },
- { "root_test3", "rootattr", NULL },
- { "root_test3", "multiattr", "3" },
- { "root_test3", "multi2", NULL },
- { "sub/subdir_test1", "repoattr", GIT_ATTR_TRUE },
- { "sub/subdir_test1", "rootattr", GIT_ATTR_TRUE },
- { "sub/subdir_test1", "missingattr", NULL },
- { "sub/subdir_test1", "subattr", "yes" },
- { "sub/subdir_test1", "negattr", GIT_ATTR_FALSE },
- { "sub/subdir_test1", "another", NULL },
- { "sub/subdir_test2.txt", "repoattr", GIT_ATTR_TRUE },
- { "sub/subdir_test2.txt", "rootattr", GIT_ATTR_TRUE },
- { "sub/subdir_test2.txt", "missingattr", NULL },
- { "sub/subdir_test2.txt", "subattr", "yes" },
- { "sub/subdir_test2.txt", "negattr", GIT_ATTR_FALSE },
- { "sub/subdir_test2.txt", "another", "zero" },
- { "sub/subdir_test2.txt", "reposub", GIT_ATTR_TRUE },
- { "sub/sub/subdir.txt", "another", "one" },
- { "sub/sub/subdir.txt", "reposubsub", GIT_ATTR_TRUE },
- { "sub/sub/subdir.txt", "reposub", NULL },
- { "does-not-exist", "foo", "yes" },
- { "sub/deep/file", "deepdeep", GIT_ATTR_TRUE },
- { NULL, NULL, NULL }
- }, *scan;
-
- for (scan = test_cases; scan->file != NULL; scan++) {
- git_buf b = GIT_BUF_INIT;
-
- git_buf_printf(&b, "%s:%s == expect %s",
- scan->file, scan->attr, scan->expected);
- cl_must_pass_(
- git_attr_get(g_repo, scan->file, scan->attr, &value) == GIT_SUCCESS,
- b.ptr);
-
- git_buf_printf(&b, ", got %s", value);
-
- if (scan->expected == NULL ||
- scan->expected == GIT_ATTR_TRUE ||
- scan->expected == GIT_ATTR_FALSE)
- {
- cl_assert_(scan->expected == value, b.ptr);
- } else {
- cl_assert_strequal(scan->expected, value);
- }
+ struct attr_expected test_cases[] = {
+ { "root_test1", "repoattr", EXPECT_TRUE, NULL },
+ { "root_test1", "rootattr", EXPECT_TRUE, NULL },
+ { "root_test1", "missingattr", EXPECT_UNDEFINED, NULL },
+ { "root_test1", "subattr", EXPECT_UNDEFINED, NULL },
+ { "root_test1", "negattr", EXPECT_UNDEFINED, NULL },
+ { "root_test2", "repoattr", EXPECT_TRUE, NULL },
+ { "root_test2", "rootattr", EXPECT_FALSE, NULL },
+ { "root_test2", "missingattr", EXPECT_UNDEFINED, NULL },
+ { "root_test2", "multiattr", EXPECT_FALSE, NULL },
+ { "root_test3", "repoattr", EXPECT_TRUE, NULL },
+ { "root_test3", "rootattr", EXPECT_UNDEFINED, NULL },
+ { "root_test3", "multiattr", EXPECT_STRING, "3" },
+ { "root_test3", "multi2", EXPECT_UNDEFINED, NULL },
+ { "sub/subdir_test1", "repoattr", EXPECT_TRUE, NULL },
+ { "sub/subdir_test1", "rootattr", EXPECT_TRUE, NULL },
+ { "sub/subdir_test1", "missingattr", EXPECT_UNDEFINED, NULL },
+ { "sub/subdir_test1", "subattr", EXPECT_STRING, "yes" },
+ { "sub/subdir_test1", "negattr", EXPECT_FALSE, NULL },
+ { "sub/subdir_test1", "another", EXPECT_UNDEFINED, NULL },
+ { "sub/subdir_test2.txt", "repoattr", EXPECT_TRUE, NULL },
+ { "sub/subdir_test2.txt", "rootattr", EXPECT_TRUE, NULL },
+ { "sub/subdir_test2.txt", "missingattr", EXPECT_UNDEFINED, NULL },
+ { "sub/subdir_test2.txt", "subattr", EXPECT_STRING, "yes" },
+ { "sub/subdir_test2.txt", "negattr", EXPECT_FALSE, NULL },
+ { "sub/subdir_test2.txt", "another", EXPECT_STRING, "zero" },
+ { "sub/subdir_test2.txt", "reposub", EXPECT_TRUE, NULL },
+ { "sub/sub/subdir.txt", "another", EXPECT_STRING, "one" },
+ { "sub/sub/subdir.txt", "reposubsub", EXPECT_TRUE, NULL },
+ { "sub/sub/subdir.txt", "reposub", EXPECT_UNDEFINED, NULL },
+ { "does-not-exist", "foo", EXPECT_STRING, "yes" },
+ { "sub/deep/file", "deepdeep", EXPECT_TRUE, NULL },
+ { NULL, NULL, 0, NULL }
+ }, *scan;
- git_buf_free(&b);
+ for (scan = test_cases; scan->path != NULL; scan++) {
+ cl_git_pass(git_attr_get(g_repo, scan->path, scan->attr, &value));
+ attr_check_expected(scan->expected, scan->expected_str, value);
}
cl_git_pass(git_attr_cache__is_cached(g_repo, ".git/info/attributes"));
@@ -103,25 +83,24 @@ void test_attr_repo__get_many(void)
cl_git_pass(git_attr_get_many(g_repo, "root_test1", 4, names, values));
- cl_assert(values[0] == GIT_ATTR_TRUE);
- cl_assert(values[1] == GIT_ATTR_TRUE);
- cl_assert(values[2] == NULL);
- cl_assert(values[3] == NULL);
+ cl_assert(GIT_ATTR_TRUE(values[0]));
+ cl_assert(GIT_ATTR_TRUE(values[1]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
cl_git_pass(git_attr_get_many(g_repo, "root_test2", 4, names, values));
- cl_assert(values[0] == GIT_ATTR_TRUE);
- cl_assert(values[1] == GIT_ATTR_FALSE);
- cl_assert(values[2] == NULL);
- cl_assert(values[3] == NULL);
+ cl_assert(GIT_ATTR_TRUE(values[0]));
+ cl_assert(GIT_ATTR_FALSE(values[1]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
cl_git_pass(git_attr_get_many(g_repo, "sub/subdir_test1", 4, names, values));
- cl_assert(values[0] == GIT_ATTR_TRUE);
- cl_assert(values[1] == GIT_ATTR_TRUE);
- cl_assert(values[2] == NULL);
+ cl_assert(GIT_ATTR_TRUE(values[0]));
+ cl_assert(GIT_ATTR_TRUE(values[1]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
cl_assert_strequal("yes", values[3]);
-
}
static int count_attrs(
@@ -161,19 +140,19 @@ void test_attr_repo__manpage_example(void)
const char *value;
cl_git_pass(git_attr_get(g_repo, "sub/abc", "foo", &value));
- cl_assert(value == GIT_ATTR_TRUE);
+ cl_assert(GIT_ATTR_TRUE(value));
cl_git_pass(git_attr_get(g_repo, "sub/abc", "bar", &value));
- cl_assert(value == NULL);
+ cl_assert(GIT_ATTR_UNSPECIFIED(value));
cl_git_pass(git_attr_get(g_repo, "sub/abc", "baz", &value));
- cl_assert(value == GIT_ATTR_FALSE);
+ cl_assert(GIT_ATTR_FALSE(value));
cl_git_pass(git_attr_get(g_repo, "sub/abc", "merge", &value));
cl_assert_strequal("filfre", value);
cl_git_pass(git_attr_get(g_repo, "sub/abc", "frotz", &value));
- cl_assert(value == NULL);
+ cl_assert(GIT_ATTR_UNSPECIFIED(value));
}
void test_attr_repo__macros(void)
@@ -185,24 +164,24 @@ void test_attr_repo__macros(void)
cl_git_pass(git_attr_get_many(g_repo, "binfile", 5, names, values));
- cl_assert(values[0] == GIT_ATTR_TRUE);
- cl_assert(values[1] == GIT_ATTR_TRUE);
- cl_assert(values[2] == GIT_ATTR_FALSE);
- cl_assert(values[3] == GIT_ATTR_FALSE);
- cl_assert(values[4] == NULL);
+ cl_assert(GIT_ATTR_TRUE(values[0]));
+ cl_assert(GIT_ATTR_TRUE(values[1]));
+ cl_assert(GIT_ATTR_FALSE(values[2]));
+ cl_assert(GIT_ATTR_FALSE(values[3]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[4]));
cl_git_pass(git_attr_get_many(g_repo, "macro_test", 5, names2, values));
- cl_assert(values[0] == GIT_ATTR_TRUE);
- cl_assert(values[1] == GIT_ATTR_TRUE);
- cl_assert(values[2] == GIT_ATTR_FALSE);
- cl_assert(values[3] == NULL);
+ cl_assert(GIT_ATTR_TRUE(values[0]));
+ cl_assert(GIT_ATTR_TRUE(values[1]));
+ cl_assert(GIT_ATTR_FALSE(values[2]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
cl_assert_strequal("77", values[4]);
cl_git_pass(git_attr_get_many(g_repo, "macro_test", 3, names3, values));
- cl_assert(values[0] == GIT_ATTR_TRUE);
- cl_assert(values[1] == GIT_ATTR_FALSE);
+ cl_assert(GIT_ATTR_TRUE(values[0]));
+ cl_assert(GIT_ATTR_FALSE(values[1]));
cl_assert_strequal("answer", values[2]);
}
@@ -215,9 +194,9 @@ void test_attr_repo__bad_macros(void)
cl_git_pass(git_attr_get_many(g_repo, "macro_bad", 6, names, values));
/* these three just confirm that the "mymacro" rule ran */
- cl_assert(values[0] == NULL);
- cl_assert(values[1] == GIT_ATTR_TRUE);
- cl_assert(values[2] == GIT_ATTR_FALSE);
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[0]));
+ cl_assert(GIT_ATTR_TRUE(values[1]));
+ cl_assert(GIT_ATTR_FALSE(values[2]));
/* file contains:
* # let's try some malicious macro defs
@@ -241,7 +220,7 @@ void test_attr_repo__bad_macros(void)
* so summary results should be:
* -firstmacro secondmacro="hahaha" thirdmacro
*/
- cl_assert(values[3] == GIT_ATTR_FALSE);
+ cl_assert(GIT_ATTR_FALSE(values[3]));
cl_assert_strequal("hahaha", values[4]);
- cl_assert(values[5] == GIT_ATTR_TRUE);
+ cl_assert(GIT_ATTR_TRUE(values[5]));
}
diff --git a/tests-clar/clar_helpers.c b/tests-clar/clar_helpers.c
index eea8bc87d..1ef5a9bf2 100644
--- a/tests-clar/clar_helpers.c
+++ b/tests-clar/clar_helpers.c
@@ -27,3 +27,15 @@ void cl_git_mkfile(const char *filename, const char *content)
cl_must_pass(p_close(fd));
}
+
+void cl_git_append2file(const char *filename, const char *new_content)
+{
+ int fd = p_open(filename, O_WRONLY | O_APPEND | O_CREAT);
+ cl_assert(fd != 0);
+ if (!new_content)
+ new_content = "\n";
+ cl_must_pass(p_write(fd, new_content, strlen(new_content)));
+ cl_must_pass(p_close(fd));
+ cl_must_pass(p_chmod(filename, 0644));
+}
+
diff --git a/tests-clar/clar_libgit2.h b/tests-clar/clar_libgit2.h
index 73ef66844..fd5c16a03 100644
--- a/tests-clar/clar_libgit2.h
+++ b/tests-clar/clar_libgit2.h
@@ -53,5 +53,6 @@ GIT_INLINE(void) cl_assert_strequal_internal(
/* Write the contents of a buffer to disk */
void cl_git_mkfile(const char *filename, const char *content);
+void cl_git_append2file(const char *filename, const char *new_content);
#endif
diff --git a/tests-clar/core/buffer.c b/tests-clar/core/buffer.c
index 740cd8578..870525b36 100644
--- a/tests-clar/core/buffer.c
+++ b/tests-clar/core/buffer.c
@@ -218,8 +218,8 @@ check_buf_append(
const char* data_a,
const char* data_b,
const char* expected_data,
- ssize_t expected_size,
- ssize_t expected_asize)
+ size_t expected_size,
+ size_t expected_asize)
{
git_buf tgt = GIT_BUF_INIT;
@@ -371,8 +371,8 @@ void test_core_buffer__7(void)
git_buf_attach(&a, b, 0);
cl_assert_strequal(fun, a.ptr);
- cl_assert(a.size == (ssize_t)strlen(fun));
- cl_assert(a.asize == (ssize_t)strlen(fun) + 1);
+ cl_assert(a.size == strlen(fun));
+ cl_assert(a.asize == strlen(fun) + 1);
git_buf_free(&a);
@@ -380,8 +380,8 @@ void test_core_buffer__7(void)
git_buf_attach(&a, b, strlen(fun) + 1);
cl_assert_strequal(fun, a.ptr);
- cl_assert(a.size == (ssize_t)strlen(fun));
- cl_assert(a.asize == (ssize_t)strlen(fun) + 1);
+ cl_assert(a.size == strlen(fun));
+ cl_assert(a.asize == strlen(fun) + 1);
git_buf_free(&a);
}
diff --git a/tests-clar/core/path.c b/tests-clar/core/path.c
index 3ff5d7daf..c07362f1d 100644
--- a/tests-clar/core/path.c
+++ b/tests-clar/core/path.c
@@ -243,7 +243,7 @@ void test_core_path__07_path_to_dir(void)
void test_core_path__08_self_join(void)
{
git_buf path = GIT_BUF_INIT;
- ssize_t asize = 0;
+ size_t asize = 0;
asize = path.asize;
cl_git_pass(git_buf_sets(&path, "/foo"));
diff --git a/tests-clar/object/blob/filter.c b/tests-clar/object/blob/filter.c
new file mode 100644
index 000000000..0b87b2b46
--- /dev/null
+++ b/tests-clar/object/blob/filter.c
@@ -0,0 +1,125 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "blob.h"
+#include "filter.h"
+
+static git_repository *g_repo = NULL;
+#define NUM_TEST_OBJECTS 6
+static git_oid g_oids[NUM_TEST_OBJECTS];
+static const char *g_raw[NUM_TEST_OBJECTS] = {
+ "",
+ "foo\nbar\n",
+ "foo\rbar\r",
+ "foo\r\nbar\r\n",
+ "foo\nbar\rboth\r\nreversed\n\ragain\nproblems\r",
+ "123\n\000\001\002\003\004abc\255\254\253\r\n"
+};
+static int g_len[NUM_TEST_OBJECTS] = { -1, -1, -1, -1, -1, 17 };
+static git_text_stats g_stats[NUM_TEST_OBJECTS] = {
+ { 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 2, 0, 6, 0 },
+ { 0, 2, 0, 0, 6, 0 },
+ { 0, 2, 2, 2, 6, 0 },
+ { 0, 4, 4, 1, 31, 0 },
+ { 1, 1, 2, 1, 9, 5 }
+};
+static git_buf g_crlf_filtered[NUM_TEST_OBJECTS] = {
+ { "", 0, 0 },
+ { "foo\nbar\n", 0, 8 },
+ { "foo\rbar\r", 0, 8 },
+ { "foo\nbar\n", 0, 8 },
+ { "foo\nbar\rboth\nreversed\n\ragain\nproblems\r", 0, 38 },
+ { "123\n\000\001\002\003\004abc\255\254\253\n", 0, 16 }
+};
+
+void test_object_blob_filter__initialize(void)
+{
+ int i;
+
+ cl_fixture_sandbox("empty_standard_repo");
+ cl_git_pass(p_rename(
+ "empty_standard_repo/.gitted", "empty_standard_repo/.git"));
+ cl_git_pass(git_repository_open(&g_repo, "empty_standard_repo"));
+
+ for (i = 0; i < NUM_TEST_OBJECTS; i++) {
+ size_t len = (g_len[i] < 0) ? strlen(g_raw[i]) : (size_t)g_len[i];
+ g_len[i] = (int)len;
+
+ cl_git_pass(
+ git_blob_create_frombuffer(&g_oids[i], g_repo, g_raw[i], len)
+ );
+ }
+}
+
+void test_object_blob_filter__cleanup(void)
+{
+ git_repository_free(g_repo);
+ g_repo = NULL;
+ cl_fixture_cleanup("empty_standard_repo");
+}
+
+void test_object_blob_filter__unfiltered(void)
+{
+ int i;
+ git_blob *blob;
+
+ for (i = 0; i < NUM_TEST_OBJECTS; i++) {
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &g_oids[i]));
+ cl_assert((size_t)g_len[i] == git_blob_rawsize(blob));
+ cl_assert(memcmp(git_blob_rawcontent(blob), g_raw[i], g_len[i]) == 0);
+ git_blob_free(blob);
+ }
+}
+
+void test_object_blob_filter__stats(void)
+{
+ int i;
+ git_blob *blob;
+ git_buf buf = GIT_BUF_INIT;
+ git_text_stats stats;
+
+ for (i = 0; i < NUM_TEST_OBJECTS; i++) {
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &g_oids[i]));
+ cl_git_pass(git_blob__getbuf(&buf, blob));
+ git_text_gather_stats(&stats, &buf);
+ cl_assert(memcmp(&g_stats[i], &stats, sizeof(stats)) == 0);
+ git_blob_free(blob);
+ }
+
+ git_buf_free(&buf);
+}
+
+void test_object_blob_filter__to_odb(void)
+{
+ git_vector filters = GIT_VECTOR_INIT;
+ git_config *cfg;
+ int i;
+ git_blob *blob;
+ git_buf orig = GIT_BUF_INIT, out = GIT_BUF_INIT;
+
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_assert(cfg);
+
+ git_attr_cache_flush(g_repo);
+ cl_git_append2file("empty_standard_repo/.gitattributes", "*.txt text\n");
+
+ cl_assert(git_filters_load(
+ &filters, g_repo, "filename.txt", GIT_FILTER_TO_ODB) > 0);
+ cl_assert(filters.length == 1);
+
+ for (i = 0; i < NUM_TEST_OBJECTS; i++) {
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &g_oids[i]));
+ cl_git_pass(git_blob__getbuf(&orig, blob));
+
+ cl_git_pass(git_filters_apply(&out, &orig, &filters));
+ cl_assert(git_buf_cmp(&out, &g_crlf_filtered[i]) == 0);
+
+ git_blob_free(blob);
+ }
+
+ git_filters_free(&filters);
+ git_buf_free(&orig);
+ git_buf_free(&out);
+ git_config_free(cfg);
+}
+
diff --git a/tests/test_helpers.c b/tests/test_helpers.c
index 42c8031cd..837358453 100644
--- a/tests/test_helpers.c
+++ b/tests/test_helpers.c
@@ -182,7 +182,7 @@ int cmp_objects(git_rawobj *o, object_data *d)
int copy_file(const char *src, const char *dst)
{
- git_fbuffer source_buf;
+ git_buf source_buf = GIT_BUF_INIT;
git_file dst_fd;
int error = GIT_ERROR;
@@ -193,10 +193,10 @@ int copy_file(const char *src, const char *dst)
if (dst_fd < 0)
goto cleanup;
- error = p_write(dst_fd, source_buf.data, source_buf.len);
+ error = p_write(dst_fd, source_buf.ptr, source_buf.size);
cleanup:
- git_futils_freebuffer(&source_buf);
+ git_buf_free(&source_buf);
p_close(dst_fd);
return error;
@@ -204,22 +204,23 @@ cleanup:
int cmp_files(const char *a, const char *b)
{
- git_fbuffer buf_a, buf_b;
+ git_buf buf_a = GIT_BUF_INIT;
+ git_buf buf_b = GIT_BUF_INIT;
int error = GIT_ERROR;
if (git_futils_readbuffer(&buf_a, a) < GIT_SUCCESS)
return GIT_ERROR;
if (git_futils_readbuffer(&buf_b, b) < GIT_SUCCESS) {
- git_futils_freebuffer(&buf_a);
+ git_buf_free(&buf_a);
return GIT_ERROR;
}
- if (buf_a.len == buf_b.len && !memcmp(buf_a.data, buf_b.data, buf_a.len))
+ if (buf_a.size == buf_b.size && !memcmp(buf_a.ptr, buf_b.ptr, buf_a.size))
error = GIT_SUCCESS;
- git_futils_freebuffer(&buf_a);
- git_futils_freebuffer(&buf_b);
+ git_buf_free(&buf_a);
+ git_buf_free(&buf_b);
return error;
}