diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 000000000..f90540b55
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+*.c eol=lf
+*.h eol=lf
diff --git a/README.md b/README.md
index dae6a76bf..7cfde59f8 100644
--- a/README.md
+++ b/README.md
@@ -39,7 +39,7 @@ Under Unix-like systems, like Linux, *BSD and Mac OS X, libgit2 expects `pthread
they should be installed by default on all systems. Under Windows, libgit2 uses the native Windows API
for threading.
-Additionally, he following libraries may be used as replacement for built-in functionality:
+Additionally, the following libraries may be used as replacement for built-in functionality:
* LibSSL **(optional)**
@@ -88,6 +88,9 @@ The waf build system for libgit2 accepts the following flags:
--with-sqlite
Enable sqlite support.
+ --with-redis
+ Enable redis support.
+
You can run `./waf --help` to see a full list of install options and
targets.
diff --git a/include/git2.h b/include/git2.h
index 29fa98e18..0d0e54898 100644
--- a/include/git2.h
+++ b/include/git2.h
@@ -26,9 +26,9 @@
#ifndef INCLUDE_git_git_h__
#define INCLUDE_git_git_h__
-#define LIBGIT2_VERSION "0.11.0"
+#define LIBGIT2_VERSION "0.12.0"
#define LIBGIT2_VER_MAJOR 0
-#define LIBGIT2_VER_MINOR 10
+#define LIBGIT2_VER_MINOR 12
#define LIBGIT2_VER_REVISION 0
#include "git2/common.h"
@@ -52,5 +52,6 @@
#include "git2/tree.h"
#include "git2/index.h"
+#include "git2/config.h"
#endif
diff --git a/include/git2/common.h b/include/git2/common.h
index 22c7cc466..9a27ac2e5 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -84,92 +84,6 @@
* @{
*/
-/** Operation completed successfully. */
-#define GIT_SUCCESS 0
-
-/**
- * Operation failed, with unspecified reason.
- * This value also serves as the base error code; all other
- * error codes are subtracted from it such that all errors
- * are < 0, in typical POSIX C tradition.
- */
-#define GIT_ERROR -1
-
-/** Input was not a properly formatted Git object id. */
-#define GIT_ENOTOID (GIT_ERROR - 1)
-
-/** Input does not exist in the scope searched. */
-#define GIT_ENOTFOUND (GIT_ERROR - 2)
-
-/** Not enough space available. */
-#define GIT_ENOMEM (GIT_ERROR - 3)
-
-/** Consult the OS error information. */
-#define GIT_EOSERR (GIT_ERROR - 4)
-
-/** The specified object is of invalid type */
-#define GIT_EOBJTYPE (GIT_ERROR - 5)
-
-/** The specified object has its data corrupted */
-#define GIT_EOBJCORRUPTED (GIT_ERROR - 6)
-
-/** The specified repository is invalid */
-#define GIT_ENOTAREPO (GIT_ERROR - 7)
-
-/** The object type is invalid or doesn't match */
-#define GIT_EINVALIDTYPE (GIT_ERROR - 8)
-
-/** The object cannot be written because it's missing internal data */
-#define GIT_EMISSINGOBJDATA (GIT_ERROR - 9)
-
-/** The packfile for the ODB is corrupted */
-#define GIT_EPACKCORRUPTED (GIT_ERROR - 10)
-
-/** Failed to acquire or release a file lock */
-#define GIT_EFLOCKFAIL (GIT_ERROR - 11)
-
-/** The Z library failed to inflate/deflate an object's data */
-#define GIT_EZLIB (GIT_ERROR - 12)
-
-/** The queried object is currently busy */
-#define GIT_EBUSY (GIT_ERROR - 13)
-
-/** The index file is not backed up by an existing repository */
-#define GIT_EBAREINDEX (GIT_ERROR - 14)
-
-/** The name of the reference is not valid */
-#define GIT_EINVALIDREFNAME (GIT_ERROR - 15)
-
-/** The specified reference has its data corrupted */
-#define GIT_EREFCORRUPTED (GIT_ERROR - 16)
-
-/** The specified symbolic reference is too deeply nested */
-#define GIT_ETOONESTEDSYMREF (GIT_ERROR - 17)
-
-/** The pack-refs file is either corrupted or its format is not currently supported */
-#define GIT_EPACKEDREFSCORRUPTED (GIT_ERROR - 18)
-
-/** The path is invalid */
-#define GIT_EINVALIDPATH (GIT_ERROR - 19)
-
-/** The revision walker is empty; there are no more commits left to iterate */
-#define GIT_EREVWALKOVER (GIT_ERROR - 20)
-
-/** The state of the reference is not valid */
-#define GIT_EINVALIDREFSTATE (GIT_ERROR - 21)
-
-/** This feature has not been implemented yet */
-#define GIT_ENOTIMPLEMENTED (GIT_ERROR - 22)
-
-/** A reference with this name already exists */
-#define GIT_EEXISTS (GIT_ERROR - 23)
-
-/** The given integer literal is too large to be parsed */
-#define GIT_EOVERFLOW (GIT_ERROR - 24)
-
-/** The given literal is not a valid number */
-#define GIT_ENOTNUM (GIT_ERROR - 25)
-
GIT_BEGIN_DECL
typedef struct {
diff --git a/include/git2/config.h b/include/git2/config.h
new file mode 100644
index 000000000..3ebbe64de
--- /dev/null
+++ b/include/git2/config.h
@@ -0,0 +1,171 @@
+/*
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * In addition to the permissions in the GNU General Public License,
+ * the authors give you unlimited permission to link the compiled
+ * version of this file into combinations with other programs,
+ * and to distribute those combinations without any restriction
+ * coming from the use of this file. (The General Public License
+ * restrictions do apply in other respects; for example, they cover
+ * modification of the file, and distribution when not linked into
+ * a combined executable.)
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef INCLUDE_git_config_h__
+#define INCLUDE_git_config_h__
+
+#include "common.h"
+#include "types.h"
+
+/**
+ * @file git2/config.h
+ * @brief Git config management routines
+ * @defgroup git_config Git config management routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Allocate a new configuration
+ */
+GIT_EXTERN(int) git_config_new(git_config **out);
+
+/**
+ * Open a configuration file
+ *
+ * @param cfg_out pointer to the configuration data
+ * @param path where to load the confiration from
+ */
+GIT_EXTERN(int) git_config_open_bare(git_config **cfg_out, const char *path);
+
+/**
+ *
+ */
+GIT_EXTERN(int) git_config_add_backend(git_config *cfg, git_config_backend *backend, int priority);
+
+/**
+ * Free the configuration and its associated memory
+ *
+ * @param cfg the configuration to free
+ */
+GIT_EXTERN(void) git_config_free(git_config *cfg);
+
+/**
+ * Get the value of an integer config variable.
+ *
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @param out pointer to the variable where the value should be stored
+ * @return GIT_SUCCESS on success; error code otherwise
+ */
+GIT_EXTERN(int) git_config_get_int(git_config *cfg, const char *name, int *out);
+
+/**
+ * Get the value of a long integer config variable.
+ *
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @param out pointer to the variable where the value should be stored
+ * @return GIT_SUCCESS on success; error code otherwise
+ */
+GIT_EXTERN(int) git_config_get_long(git_config *cfg, const char *name, long int *out);
+
+/**
+ * Get the value of a boolean config variable.
+ *
+ * This function uses the usual C convention of 0 being false and
+ * anything else true.
+ *
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @param out pointer to the variable where the value should be stored
+ * @return GIT_SUCCESS on success; error code otherwise
+ */
+GIT_EXTERN(int) git_config_get_bool(git_config *cfg, const char *name, int *out);
+
+/**
+ * Get the value of a string config variable.
+ *
+ * The string is owned by the variable and should not be freed by the
+ * user.
+ *
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @param out pointer to the variable's value
+ * @return GIT_SUCCESS on success; error code otherwise
+ */
+GIT_EXTERN(int) git_config_get_string(git_config *cfg, const char *name, const char **out);
+
+/**
+ * Set the value of an integer config variable.
+ *
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @param out pointer to the variable where the value should be stored
+ * @return GIT_SUCCESS on success; error code otherwise
+ */
+GIT_EXTERN(int) git_config_set_int(git_config *cfg, const char *name, int value);
+
+/**
+ * Set the value of a long integer config variable.
+ *
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @param out pointer to the variable where the value should be stored
+ * @return GIT_SUCCESS on success; error code otherwise
+ */
+GIT_EXTERN(int) git_config_set_long(git_config *cfg, const char *name, long int value);
+
+/**
+ * Set the value of a boolean config variable.
+ *
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @param value the value to store
+ * @return GIT_SUCCESS on success; error code otherwise
+ */
+GIT_EXTERN(int) git_config_set_bool(git_config *cfg, const char *name, int value);
+
+/**
+ * Set the value of a string config variable.
+ *
+ * A copy of the string is made and the user is free to use it
+ * afterwards.
+ *
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @param value the string to store.
+ * @return GIT_SUCCESS on success; error code otherwise
+ */
+GIT_EXTERN(int) git_config_set_string(git_config *cfg, const char *name, const char *value);
+
+/**
+ * Perform an operation on each config variable.
+ *
+ * The callback is passed a pointer to a config variable name and the
+ * data pointer passed to this function. As soon as one of the
+ * callback functions returns something other than 0, this function
+ * returns that value.
+ *
+ * @param cfg where to get the variables from
+ * @param callback the function to call on each variable
+ * @param data the data to pass to the callback
+ * @return GIT_SUCCESS or the return value of the callback which didn't return 0
+ */
+GIT_EXTERN(int) git_config_foreach(git_config *cfg, int (*callback)(const char *, void *data), void *data);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/config_backend.h b/include/git2/config_backend.h
new file mode 100644
index 000000000..427cd95dd
--- /dev/null
+++ b/include/git2/config_backend.h
@@ -0,0 +1,57 @@
+/*
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * In addition to the permissions in the GNU General Public License,
+ * the authors give you unlimited permission to link the compiled
+ * version of this file into combinations with other programs,
+ * and to distribute those combinations without any restriction
+ * coming from the use of this file. (The General Public License
+ * restrictions do apply in other respects; for example, they cover
+ * modification of the file, and distribution when not linked into
+ * a combined executable.)
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INCLUDE_git_config_backend_h__
+#define INCLUDE_git_config_backend_h__
+
+#include "common.h"
+#include "types.h"
+#include "config.h"
+
+GIT_BEGIN_DECL
+
+struct git_config;
+
+struct git_config_backend {
+ struct git_config *cfg;
+ /* Open means open the file/database and parse if necessary */
+ int (*open)(struct git_config_backend *);
+ int (* get)(struct git_config_backend *, const char *key, const char **value);
+ int (* set)(struct git_config_backend *, const char *key, const char *value);
+ int (*foreach)(struct git_config_backend *, int (*fn)(const char *, void *), void *data);
+ void (*free)(struct git_config_backend *);
+};
+
+/**
+ * Create a file-backed configuration backend
+ *
+ * @param out the new backend
+ * @path where the config file is located
+ */
+GIT_EXTERN(int) git_config_backend_file(struct git_config_backend **out, const char *path);
+
+GIT_END_DECL
+
+#endif
diff --git a/include/git2/errors.h b/include/git2/errors.h
index 627e67c70..7e957b803 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -25,6 +25,8 @@
#ifndef INCLUDE_git_errors_h__
#define INCLUDE_git_errors_h__
+#include "common.h"
+
/**
* @file git2/errors.h
* @brief Git error handling routines and variables
@@ -33,8 +35,106 @@
*/
GIT_BEGIN_DECL
+typedef enum {
+ GIT_SUCCESS = 0,
+ GIT_ERROR = -1,
+
+ /** Input was not a properly formatted Git object id. */
+ GIT_ENOTOID = -2,
+
+ /** Input does not exist in the scope searched. */
+ GIT_ENOTFOUND = -3,
+
+ /** Not enough space available. */
+ GIT_ENOMEM = -4,
+
+ /** Consult the OS error information. */
+ GIT_EOSERR = -5,
+
+ /** The specified object is of invalid type */
+ GIT_EOBJTYPE = -6,
+
+ /** The specified repository is invalid */
+ GIT_ENOTAREPO = -7,
+
+ /** The object type is invalid or doesn't match */
+ GIT_EINVALIDTYPE = -8,
+
+ /** The object cannot be written because it's missing internal data */
+ GIT_EMISSINGOBJDATA = -9,
+
+ /** The packfile for the ODB is corrupted */
+ GIT_EPACKCORRUPTED = -10,
+
+ /** Failed to acquire or release a file lock */
+ GIT_EFLOCKFAIL = -11,
+
+ /** The Z library failed to inflate/deflate an object's data */
+ GIT_EZLIB = -12,
+
+ /** The queried object is currently busy */
+ GIT_EBUSY = -13,
+
+ /** The index file is not backed up by an existing repository */
+ GIT_EBAREINDEX = -14,
+
+ /** The name of the reference is not valid */
+ GIT_EINVALIDREFNAME = -15,
+
+ /** The specified reference has its data corrupted */
+ GIT_EREFCORRUPTED = -16,
+
+ /** The specified symbolic reference is too deeply nested */
+ GIT_ETOONESTEDSYMREF = -17,
+
+ /** The pack-refs file is either corrupted or its format is not currently supported */
+ GIT_EPACKEDREFSCORRUPTED = -18,
+
+ /** The path is invalid */
+ GIT_EINVALIDPATH = -19,
+
+ /** The revision walker is empty; there are no more commits left to iterate */
+ GIT_EREVWALKOVER = -20,
+
+ /** The state of the reference is not valid */
+ GIT_EINVALIDREFSTATE = -21,
+
+ /** This feature has not been implemented yet */
+ GIT_ENOTIMPLEMENTED = -22,
+
+ /** A reference with this name already exists */
+ GIT_EEXISTS = -23,
+
+ /** The given integer literal is too large to be parsed */
+ GIT_EOVERFLOW = -24,
+
+ /** The given literal is not a valid number */
+ GIT_ENOTNUM = -25,
+
+ /** Streaming error */
+ GIT_ESTREAM = -26,
+
+ /** invalid arguments to function */
+ GIT_EINVALIDARGS = -27,
+
+ /** The specified object has its data corrupted */
+ GIT_EOBJCORRUPTED = -28,
+} git_error;
+
+/**
+ * Return a detailed error string with the latest error
+ * that occurred in the library.
+ * @return a string explaining the error
+ */
+GIT_EXTERN(const char *) git_lasterror(void);
+
/**
* strerror() for the Git library
+ *
+ * Get a string description for a given error code.
+ * NOTE: This method will be eventually deprecated in favor
+ * of the new `git_lasterror`.
+ *
* @param num The error code to explain
* @return a string explaining the error code
*/
diff --git a/include/git2/index.h b/include/git2/index.h
index 09993a154..2e5c17bc3 100644
--- a/include/git2/index.h
+++ b/include/git2/index.h
@@ -51,39 +51,29 @@ GIT_BEGIN_DECL
*
* In-memory only flags:
*/
-#define GIT_IDXENTRY_UPDATE (1 << 16)
-#define GIT_IDXENTRY_REMOVE (1 << 17)
-#define GIT_IDXENTRY_UPTODATE (1 << 18)
-#define GIT_IDXENTRY_ADDED (1 << 19)
+#define GIT_IDXENTRY_UPDATE (1 << 0)
+#define GIT_IDXENTRY_REMOVE (1 << 1)
+#define GIT_IDXENTRY_UPTODATE (1 << 2)
+#define GIT_IDXENTRY_ADDED (1 << 3)
-#define GIT_IDXENTRY_HASHED (1 << 20)
-#define GIT_IDXENTRY_UNHASHED (1 << 21)
-#define GIT_IDXENTRY_WT_REMOVE (1 << 22) /* remove in work directory */
-#define GIT_IDXENTRY_CONFLICTED (1 << 23)
+#define GIT_IDXENTRY_HASHED (1 << 4)
+#define GIT_IDXENTRY_UNHASHED (1 << 5)
+#define GIT_IDXENTRY_WT_REMOVE (1 << 6) /* remove in work directory */
+#define GIT_IDXENTRY_CONFLICTED (1 << 7)
-#define GIT_IDXENTRY_UNPACKED (1 << 24)
-#define GIT_IDXENTRY_NEW_SKIP_WORKTREE (1 << 25)
+#define GIT_IDXENTRY_UNPACKED (1 << 8)
+#define GIT_IDXENTRY_NEW_SKIP_WORKTREE (1 << 9)
/*
* Extended on-disk flags:
*/
-#define GIT_IDXENTRY_INTENT_TO_ADD (1 << 29)
-#define GIT_IDXENTRY_SKIP_WORKTREE (1 << 30)
+#define GIT_IDXENTRY_INTENT_TO_ADD (1 << 13)
+#define GIT_IDXENTRY_SKIP_WORKTREE (1 << 14)
/* GIT_IDXENTRY_EXTENDED2 is for future extension */
-#define GIT_IDXENTRY_EXTENDED2 (1 << 31)
+#define GIT_IDXENTRY_EXTENDED2 (1 << 15)
#define GIT_IDXENTRY_EXTENDED_FLAGS (GIT_IDXENTRY_INTENT_TO_ADD | GIT_IDXENTRY_SKIP_WORKTREE)
-/*
- * Safeguard to avoid saving wrong flags:
- * - GIT_IDXENTRY_EXTENDED2 won't get saved until its semantic is known
- * - Bits in 0x0000FFFF have been saved in flags already
- * - Bits in 0x003F0000 are currently in-memory flags
- */
-#if GIT_IDXENTRY_EXTENDED_FLAGS & 0x803FFFFF
-#error "GIT_IDXENTRY_EXTENDED_FLAGS out of range"
-#endif
-
/** Time used in a git index entry */
typedef struct {
git_time_t seconds;
@@ -188,7 +178,12 @@ GIT_EXTERN(int) git_index_write(git_index *index);
GIT_EXTERN(int) git_index_find(git_index *index, const char *path);
/**
- * Add or update an index entry from a file in disk.
+ * Add or update an index entry from a file in disk
+ *
+ * The file `path` must be relative to the repository's
+ * working folder and must be readable.
+ *
+ * This method will fail in bare index instances.
*
* @param index an existing index object
* @param path filename to add
@@ -198,7 +193,55 @@ GIT_EXTERN(int) git_index_find(git_index *index, const char *path);
GIT_EXTERN(int) git_index_add(git_index *index, const char *path, int stage);
/**
- * Remove an entry from the index
+ * Add or update an index entry from an in-memory struct
+ *
+ * A full copy (including the 'path' string) of the given
+ * 'source_entry' will be inserted on the index.
+ *
+ * @param index an existing index object
+ * @param source_entry new entry object
+ * @return 0 on success, otherwise an error code
+ */
+GIT_EXTERN(int) git_index_add2(git_index *index, const git_index_entry *source_entry);
+
+/**
+ * Add (append) an index entry from a file in disk
+ *
+ * A new entry will always be inserted into the index;
+ * if the index already contains an entry for such
+ * path, the old entry will **not** be replaced.
+ *
+ * The file `path` must be relative to the repository's
+ * working folder and must be readable.
+ *
+ * This method will fail in bare index instances.
+ *
+ * @param index an existing index object
+ * @param path filename to add
+ * @param stage stage for the entry
+ * @return 0 on success, otherwise an error code
+ */
+GIT_EXTERN(int) git_index_append(git_index *index, const char *path, int stage);
+
+/**
+ * Add (append) an index entry from an in-memory struct
+ *
+ * A new entry will always be inserted into the index;
+ * if the index already contains an entry for the path
+ * in the `entry` struct, the old entry will **not** be
+ * replaced.
+ *
+ * A full copy (including the 'path' string) of the given
+ * 'source_entry' will be inserted on the index.
+ *
+ * @param index an existing index object
+ * @param source_entry new entry object
+ * @return 0 on success, otherwise an error code
+ */
+GIT_EXTERN(int) git_index_append2(git_index *index, const git_index_entry *source_entry);
+
+/**
+ * Remove an entry from the index
*
* @param index an existing index object
* @param position position of the entry to remove
@@ -206,18 +249,6 @@ GIT_EXTERN(int) git_index_add(git_index *index, const char *path, int stage);
*/
GIT_EXTERN(int) git_index_remove(git_index *index, int position);
-/**
- * Insert an entry into the index.
- * A full copy (including the 'path' string) of the given
- * 'source_entry' will be inserted on the index; if the index
- * already contains an entry for the same path, the entry
- * will be updated.
- *
- * @param index an existing index object
- * @param source_entry new entry object
- * @return 0 on success, otherwise an error code
- */
-GIT_EXTERN(int) git_index_insert(git_index *index, const git_index_entry *source_entry);
/**
* Get a pointer to one of the entries in the index
diff --git a/include/git2/thread-utils.h b/include/git2/thread-utils.h
index fb8644b93..e26876bea 100644
--- a/include/git2/thread-utils.h
+++ b/include/git2/thread-utils.h
@@ -35,6 +35,7 @@
#if defined(__APPLE__) && defined(__MACH__)
# undef GIT_TLS
+# define GIT_TLS
#elif defined(__GNUC__) || \
defined(__SUNPRO_C) || \
diff --git a/include/git2/types.h b/include/git2/types.h
index 6123abc82..ab7dc523e 100644
--- a/include/git2/types.h
+++ b/include/git2/types.h
@@ -130,6 +130,12 @@ typedef struct git_treebuilder git_treebuilder;
/** Memory representation of an index file. */
typedef struct git_index git_index;
+/** Memory representation of a config file */
+typedef struct git_config git_config;
+
+/** A specific implementation of a config backend */
+typedef struct git_config_backend git_config_backend;
+
/** Time in a signature */
typedef struct git_time {
git_time_t time; /** time in seconds from epoch */
diff --git a/src/backends/hiredis.c b/src/backends/hiredis.c
index 707412bf6..2533a16da 100644
--- a/src/backends/hiredis.c
+++ b/src/backends/hiredis.c
@@ -53,7 +53,7 @@ int hiredis_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_backe
reply = redisCommand(backend->db, "HMGET %b %s %s", oid->id, GIT_OID_RAWSZ,
"type", "size");
- if (reply->type == REDIS_REPLY_ARRAY) {
+ if (reply && reply->type == REDIS_REPLY_ARRAY) {
if (reply->element[0]->type != REDIS_REPLY_NIL &&
reply->element[0]->type != REDIS_REPLY_NIL) {
*type_p = (git_otype) atoi(reply->element[0]->str);
@@ -83,7 +83,7 @@ int hiredis_backend__read(void **data_p, size_t *len_p, git_otype *type_p, git_o
reply = redisCommand(backend->db, "HMGET %b %s %s %s", oid->id, GIT_OID_RAWSZ,
"type", "size", "data");
- if (reply->type == REDIS_REPLY_ARRAY) {
+ if (reply && reply->type == REDIS_REPLY_ARRAY) {
if (reply->element[0]->type != REDIS_REPLY_NIL &&
reply->element[1]->type != REDIS_REPLY_NIL &&
reply->element[2]->type != REDIS_REPLY_NIL) {
@@ -118,7 +118,7 @@ int hiredis_backend__exists(git_odb_backend *_backend, const git_oid *oid) {
found = 0;
reply = redisCommand(backend->db, "exists %b", oid->id, GIT_OID_RAWSZ);
- if (reply->type != REDIS_REPLY_NIL && reply->type != REDIS_REPLY_ERROR)
+ if (reply && reply->type != REDIS_REPLY_NIL && reply->type != REDIS_REPLY_ERROR)
found = 1;
@@ -144,7 +144,8 @@ int hiredis_backend__write(git_oid *id, git_odb_backend *_backend, const void *d
"size %d "
"data %b ", id->id, GIT_OID_RAWSZ,
(int) type, len, data, len);
- error = reply->type == REDIS_REPLY_ERROR ? GIT_ERROR : GIT_SUCCESS;
+
+ error = (reply == NULL || reply->type == REDIS_REPLY_ERROR) ? GIT_ERROR : GIT_SUCCESS;
freeReplyObject(reply);
return error;
diff --git a/src/blob.c b/src/blob.c
index 5e3c22fbf..987169358 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -62,7 +62,7 @@ int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *b
git_odb_stream *stream;
if ((error = git_odb_open_wstream(&stream, repo->db, len, GIT_OBJ_BLOB)) < GIT_SUCCESS)
- return error;
+ return git__rethrow(error, "Failed to create blob. Can't open write stream");
stream->write(stream, buffer, len);
@@ -81,7 +81,7 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat
git_odb_stream *stream;
if (repo->path_workdir == NULL)
- return GIT_ENOTFOUND;
+ return git__throw(GIT_ENOTFOUND, "Failed to create blob. No workdir given");
git__joinpath(full_path, repo->path_workdir, path);
@@ -106,7 +106,7 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat
if (read_len < 0) {
gitfo_close(fd);
stream->free(stream);
- return GIT_EOSERR;
+ return git__throw(GIT_EOSERR, "Failed to create blob. Can't read full file");
}
stream->write(stream, buffer, read_len);
diff --git a/src/cache.c b/src/cache.c
index fd42e2c5b..433fc3d9c 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -29,10 +29,7 @@
#include "thread-utils.h"
#include "cache.h"
-#define GIT_CACHE_OPENADR 3
-
-
-void git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr)
+int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr)
{
size_t i;
@@ -52,12 +49,15 @@ void git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_p
cache->free_obj = free_ptr;
cache->nodes = git__malloc((size + 1) * sizeof(cache_node));
+ if (cache->nodes == NULL)
+ return GIT_ENOMEM;
for (i = 0; i < (size + 1); ++i) {
git_mutex_init(&cache->nodes[i].lock);
cache->nodes[i].ptr = NULL;
- cache->nodes[i].lru = 0;
}
+
+ return GIT_SUCCESS;
}
void git_cache_free(git_cache *cache)
@@ -77,85 +77,53 @@ void git_cache_free(git_cache *cache)
void *git_cache_get(git_cache *cache, const git_oid *oid)
{
const uint32_t *hash;
- size_t i, pos, found = 0;
cache_node *node = NULL;
+ void *result = NULL;
hash = (const uint32_t *)oid->id;
+ node = &cache->nodes[hash[0] & cache->size_mask];
- for (i = 0; !found && i < GIT_CACHE_OPENADR; ++i) {
- pos = hash[i] & cache->size_mask;
- node = &cache->nodes[pos];
-
- git_mutex_lock(&node->lock);
- {
- if (node->ptr && git_cached_obj_compare(node->ptr, oid) == 0) {
- git_cached_obj_incref(node->ptr);
- node->lru = ++cache->lru_count;
- found = 1;
- }
+ git_mutex_lock(&node->lock);
+ {
+ if (node->ptr && git_cached_obj_compare(node->ptr, oid) == 0) {
+ git_cached_obj_incref(node->ptr);
+ result = node->ptr;
}
- git_mutex_unlock(&node->lock);
}
+ git_mutex_unlock(&node->lock);
-
- return found ? node->ptr : NULL;
+ return result;
}
void *git_cache_try_store(git_cache *cache, void *entry)
{
- cache_node *nodes[GIT_CACHE_OPENADR], *lru_node;
const uint32_t *hash;
const git_oid *oid;
- size_t i;
+ cache_node *node = NULL;
oid = &((git_cached_obj*)entry)->oid;
hash = (const uint32_t *)oid->id;
+ node = &cache->nodes[hash[0] & cache->size_mask];
/* increase the refcount on this object, because
* the cache now owns it */
git_cached_obj_incref(entry);
+ git_mutex_lock(&node->lock);
- for (i = 0; i < GIT_CACHE_OPENADR; ++i) {
- size_t pos = hash[i] & cache->size_mask;
-
- nodes[i] = &cache->nodes[pos];
- git_mutex_lock(&nodes[i]->lock);
- }
-
- lru_node = nodes[0];
-
- for (i = 0; i < GIT_CACHE_OPENADR; ++i) {
-
- if (nodes[i]->ptr == NULL) {
- nodes[i]->ptr = entry;
- nodes[i]->lru = ++cache->lru_count;
- break;
- } else if (git_cached_obj_compare(nodes[i]->ptr, oid) == 0) {
- git_cached_obj_decref(entry, cache->free_obj);
- entry = nodes[i]->ptr;
- nodes[i]->lru = ++cache->lru_count;
- break;
- }
-
- if (nodes[i]->lru < lru_node->lru)
- lru_node = nodes[i];
- }
-
- if (i == GIT_CACHE_OPENADR) {
- void *old_entry = lru_node->ptr;
- assert(old_entry);
-
- git_cached_obj_decref(old_entry, cache->free_obj);
- lru_node->ptr = entry;
- lru_node->lru = ++cache->lru_count;
+ if (node->ptr == NULL) {
+ node->ptr = entry;
+ } else if (git_cached_obj_compare(node->ptr, oid) == 0) {
+ git_cached_obj_decref(entry, cache->free_obj);
+ entry = node->ptr;
+ } else {
+ git_cached_obj_decref(node->ptr, cache->free_obj);
+ node->ptr = entry;
}
/* increase the refcount again, because we are
* returning it to the user */
git_cached_obj_incref(entry);
-
- for (i = 0; i < GIT_CACHE_OPENADR; ++i)
- git_mutex_unlock(&nodes[i]->lock);
+ git_mutex_unlock(&node->lock);
return entry;
}
diff --git a/src/cache.h b/src/cache.h
index 975aaff7e..4794dea3a 100644
--- a/src/cache.h
+++ b/src/cache.h
@@ -19,7 +19,6 @@ typedef struct {
typedef struct {
git_cached_obj *ptr;
git_mutex lock;
- unsigned int lru;
} cache_node;
typedef struct {
@@ -31,7 +30,7 @@ typedef struct {
} git_cache;
-void git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr);
+int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr);
void git_cache_free(git_cache *cache);
void *git_cache_try_store(git_cache *cache, void *entry);
diff --git a/src/commit.c b/src/commit.c
index 9621703c3..54d7a47fe 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -224,9 +224,18 @@ int git_commit_create(
if (error < GIT_SUCCESS)
return error;
- if (git_reference_type(head) == GIT_REF_SYMBOLIC) {
- if ((error = git_reference_resolve(&head, head)) < GIT_SUCCESS)
+ error = git_reference_resolve(&head, head);
+ if (error < GIT_SUCCESS) {
+ if (error != GIT_ENOTFOUND)
return error;
+ /*
+ * The target of the reference was not found. This can happen
+ * just after a repository has been initialized (the master
+ * branch doesn't exist yet, as it doesn't have anything to
+ * point to) or after an orphan checkout, so if the target
+ * branch doesn't exist yet, create it and return.
+ */
+ return git_reference_create_oid_f(&head, repo, git_reference_target(head), oid);
}
error = git_reference_set_oid(head, oid);
@@ -277,7 +286,7 @@ int commit_parse_buffer(git_commit *commit, const void *data, size_t len)
if (buffer < buffer_end) {
const char *line_end;
- size_t message_len = buffer_end - buffer;
+ size_t message_len;
/* Long message */
message_len = buffer_end - buffer;
diff --git a/src/common.h b/src/common.h
index 5ad878e26..f4f11fd2f 100644
--- a/src/common.h
+++ b/src/common.h
@@ -50,10 +50,14 @@ typedef SSIZE_T ssize_t;
#include "git2/common.h"
#include "git2/types.h"
-#include "util.h"
+#include "git2/errors.h"
#include "thread-utils.h"
#include "bswap.h"
#define GIT_PATH_MAX 4096
+extern int git__throw(int error, const char *, ...) GIT_FORMAT_PRINTF(2, 3);
+extern int git__rethrow(int error, const char *, ...) GIT_FORMAT_PRINTF(2, 3);
+
+#include "util.h"
#endif /* INCLUDE_common_h__ */
diff --git a/src/config.c b/src/config.c
new file mode 100644
index 000000000..cd0a73ccd
--- /dev/null
+++ b/src/config.c
@@ -0,0 +1,323 @@
+/*
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * In addition to the permissions in the GNU General Public License,
+ * the authors give you unlimited permission to link the compiled
+ * version of this file into combinations with other programs,
+ * and to distribute those combinations without any restriction
+ * coming from the use of this file. (The General Public License
+ * restrictions do apply in other respects; for example, they cover
+ * modification of the file, and distribution when not linked into
+ * a combined executable.)
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "common.h"
+#include "fileops.h"
+#include "hashtable.h"
+#include "config.h"
+#include "git2/config_backend.h"
+#include "vector.h"
+
+#include
+
+typedef struct {
+ git_config_backend *backend;
+ int priority;
+} backend_internal;
+
+int git_config_open_bare(git_config **out, const char *path)
+{
+ git_config_backend *backend = NULL;
+ git_config *cfg = NULL;
+ int error = GIT_SUCCESS;
+
+ error = git_config_new(&cfg);
+ if (error < GIT_SUCCESS)
+ goto error;
+
+ error = git_config_backend_file(&backend, path);
+ if (error < GIT_SUCCESS)
+ goto error;
+
+ error = git_config_add_backend(cfg, backend, 1);
+ if (error < GIT_SUCCESS)
+ goto error;
+
+ error = backend->open(backend);
+ if (error < GIT_SUCCESS)
+ goto error;
+
+ *out = cfg;
+
+ return error;
+
+ error:
+ if(backend)
+ backend->free(backend);
+
+ return error;
+}
+
+void git_config_free(git_config *cfg)
+{
+ unsigned int i;
+ git_config_backend *backend;
+ backend_internal *internal;
+
+ for(i = 0; i < cfg->backends.length; ++i){
+ internal = git_vector_get(&cfg->backends, i);
+ backend = internal->backend;
+ backend->free(backend);
+ free(internal);
+ }
+
+ git_vector_free(&cfg->backends);
+ free(cfg);
+}
+
+static int config_backend_cmp(const void *a, const void *b)
+{
+ const backend_internal *bk_a = *(const backend_internal **)(a);
+ const backend_internal *bk_b = *(const backend_internal **)(b);
+
+ return bk_b->priority - bk_a->priority;
+}
+
+int git_config_new(git_config **out)
+{
+ git_config *cfg;
+
+ cfg = git__malloc(sizeof(git_config));
+ if (cfg == NULL)
+ return GIT_ENOMEM;
+
+ memset(cfg, 0x0, sizeof(git_config));
+
+ if (git_vector_init(&cfg->backends, 3, config_backend_cmp) < 0) {
+ free(cfg);
+ return GIT_ENOMEM;
+ }
+
+ *out = cfg;
+
+ return GIT_SUCCESS;
+}
+
+int git_config_add_backend(git_config *cfg, git_config_backend *backend, int priority)
+{
+ backend_internal *internal;
+
+ assert(cfg && backend);
+
+ internal = git__malloc(sizeof(backend_internal));
+ if (internal == NULL)
+ return GIT_ENOMEM;
+
+ internal->backend = backend;
+ internal->priority = priority;
+
+ if (git_vector_insert(&cfg->backends, internal) < 0) {
+ free(internal);
+ return GIT_ENOMEM;
+ }
+
+ git_vector_sort(&cfg->backends);
+ internal->backend->cfg = cfg;
+
+ return GIT_SUCCESS;
+}
+
+/*
+ * Loop over all the variables
+ */
+
+int git_config_foreach(git_config *cfg, int (*fn)(const char *, void *), void *data)
+{
+ int ret = GIT_SUCCESS;
+ unsigned int i;
+ backend_internal *internal;
+ git_config_backend *backend;
+
+ for(i = 0; i < cfg->backends.length && ret == 0; ++i) {
+ internal = git_vector_get(&cfg->backends, i);
+ backend = internal->backend;
+ ret = backend->foreach(backend, fn, data);
+ }
+
+ return ret;
+}
+
+
+/**************
+ * Setters
+ **************/
+
+/*
+ * Internal function to actually set the string value of a variable
+ */
+
+int git_config_set_long(git_config *cfg, const char *name, long int value)
+{
+ char str_value[5]; /* Most numbers should fit in here */
+ int buf_len = sizeof(str_value), ret;
+ char *help_buf = NULL;
+
+ if ((ret = snprintf(str_value, buf_len, "%ld", value)) >= buf_len - 1){
+ /* The number is too large, we need to allocate more memory */
+ buf_len = ret + 1;
+ help_buf = git__malloc(buf_len);
+ snprintf(help_buf, buf_len, "%ld", value);
+ ret = git_config_set_string(cfg, name, help_buf);
+ free(help_buf);
+ } else {
+ ret = git_config_set_string(cfg, name, str_value);
+ }
+
+ return ret;
+}
+
+int git_config_set_int(git_config *cfg, const char *name, int value)
+{
+ return git_config_set_long(cfg, name, value);
+}
+
+int git_config_set_bool(git_config *cfg, const char *name, int value)
+{
+ const char *str_value;
+
+ if (value == 0)
+ str_value = "false";
+ else
+ str_value = "true";
+
+ return git_config_set_string(cfg, name, str_value);
+}
+
+int git_config_set_string(git_config *cfg, const char *name, const char *value)
+{
+ backend_internal *internal;
+ git_config_backend *backend;
+
+ assert(cfg->backends.length > 0);
+
+ internal = git_vector_get(&cfg->backends, 0);
+ backend = internal->backend;
+
+ return backend->set(backend, name, value);
+}
+
+/***********
+ * Getters
+ ***********/
+
+int git_config_get_long(git_config *cfg, const char *name, long int *out)
+{
+ const char *value, *num_end;
+ int ret;
+ long int num;
+
+ ret = git_config_get_string(cfg, name, &value);
+ if (ret < GIT_SUCCESS)
+ return ret;
+
+ ret = git__strtol32(&num, value, &num_end, 0);
+ if (ret < GIT_SUCCESS)
+ return ret;
+
+ switch (*num_end) {
+ case '\0':
+ break;
+ case 'k':
+ case 'K':
+ num *= 1024;
+ break;
+ case 'm':
+ case 'M':
+ num *= 1024 * 1024;
+ break;
+ case 'g':
+ case 'G':
+ num *= 1024 * 1024 * 1024;
+ break;
+ default:
+ return GIT_EINVALIDTYPE;
+ }
+
+ *out = num;
+
+ return GIT_SUCCESS;
+}
+
+int git_config_get_int(git_config *cfg, const char *name, int *out)
+{
+ long int tmp;
+ int ret;
+
+ ret = git_config_get_long(cfg, name, &tmp);
+
+ *out = (int) tmp;
+
+ return ret;
+}
+
+int git_config_get_bool(git_config *cfg, const char *name, int *out)
+{
+ const char *value;
+ int error = GIT_SUCCESS;
+
+ error = git_config_get_string(cfg, name, &value);
+ if (error < GIT_SUCCESS)
+ return error;
+
+ /* A missing value means true */
+ if (value == NULL) {
+ *out = 1;
+ 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;
+ return GIT_SUCCESS;
+ }
+
+ /* Try to parse it as an integer */
+ error = git_config_get_int(cfg, name, out);
+ if (error == GIT_SUCCESS)
+ *out = !!(*out);
+
+ return error;
+}
+
+int git_config_get_string(git_config *cfg, const char *name, const char **out)
+{
+ backend_internal *internal;
+ git_config_backend *backend;
+
+ assert(cfg->backends.length > 0);
+
+ internal = git_vector_get(&cfg->backends, 0);
+ backend = internal->backend;
+
+ return backend->get(backend, name, out);
+}
+
diff --git a/src/config.h b/src/config.h
new file mode 100644
index 000000000..a811fd850
--- /dev/null
+++ b/src/config.h
@@ -0,0 +1,12 @@
+#ifndef INCLUDE_config_h__
+#define INCLUDE_config_h__
+
+#include "git2.h"
+#include "git2/config.h"
+#include "vector.h"
+
+struct git_config {
+ git_vector backends;
+};
+
+#endif
diff --git a/src/config_file.c b/src/config_file.c
new file mode 100644
index 000000000..37bb2794e
--- /dev/null
+++ b/src/config_file.c
@@ -0,0 +1,1011 @@
+/*
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * In addition to the permissions in the GNU General Public License,
+ * the authors give you unlimited permission to link the compiled
+ * version of this file into combinations with other programs,
+ * and to distribute those combinations without any restriction
+ * coming from the use of this file. (The General Public License
+ * restrictions do apply in other respects; for example, they cover
+ * modification of the file, and distribution when not linked into
+ * a combined executable.)
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "common.h"
+#include "config.h"
+#include "fileops.h"
+#include "git2/config_backend.h"
+#include "git2/types.h"
+
+#include
+
+typedef struct cvar_t {
+ struct cvar_t *next;
+ char *section;
+ char *name;
+ char *value;
+} cvar_t;
+
+typedef struct {
+ struct cvar_t *head;
+ struct cvar_t *tail;
+} cvar_t_list;
+
+#define CVAR_LIST_HEAD(list) ((list)->head)
+
+#define CVAR_LIST_TAIL(list) ((list)->tail)
+
+#define CVAR_LIST_NEXT(var) ((var)->next)
+
+#define CVAR_LIST_EMPTY(list) ((list)->head == NULL)
+
+#define CVAR_LIST_APPEND(list, var) do {\
+ if (CVAR_LIST_EMPTY(list)) {\
+ CVAR_LIST_HEAD(list) = CVAR_LIST_TAIL(list) = var;\
+ } else {\
+ CVAR_LIST_NEXT(CVAR_LIST_TAIL(list)) = var;\
+ CVAR_LIST_TAIL(list) = var;\
+ }\
+} while(0)
+
+#define CVAR_LIST_REMOVE_HEAD(list) do {\
+ CVAR_LIST_HEAD(list) = CVAR_LIST_NEXT(CVAR_LIST_HEAD(list));\
+} while(0)
+
+#define CVAR_LIST_REMOVE_AFTER(var) do {\
+ CVAR_LIST_NEXT(var) = CVAR_LIST_NEXT(CVAR_LIST_NEXT(var));\
+} while(0)
+
+#define CVAR_LIST_FOREACH(list, iter)\
+ for ((iter) = CVAR_LIST_HEAD(list);\
+ (iter) != NULL;\
+ (iter) = CVAR_LIST_NEXT(iter))
+
+/*
+ * Inspired by the FreeBSD functions
+ */
+#define CVAR_LIST_FOREACH_SAFE(start, iter, tmp)\
+ for ((iter) = CVAR_LIST_HEAD(vars);\
+ (iter) && (((tmp) = CVAR_LIST_NEXT(iter) || 1));\
+ (iter) = (tmp))
+
+
+
+typedef struct {
+ git_config_backend parent;
+
+ cvar_t_list var_list;
+
+ struct {
+ gitfo_buf buffer;
+ char *read_ptr;
+ int line_number;
+ int eof;
+ } reader;
+
+ char *file_path;
+} file_backend;
+
+static int config_parse(file_backend *cfg_file);
+static int parse_variable(file_backend *cfg, char **var_name, char **var_value);
+
+static void cvar_free(cvar_t *var)
+{
+ if (var == NULL)
+ return;
+
+ free(var->section);
+ free(var->name);
+ free(var->value);
+ free(var);
+}
+
+static void cvar_list_free(cvar_t_list *list)
+{
+ cvar_t *cur;
+
+ while (!CVAR_LIST_EMPTY(list)) {
+ cur = CVAR_LIST_HEAD(list);
+ CVAR_LIST_REMOVE_HEAD(list);
+ cvar_free(cur);
+ }
+}
+
+/*
+ * Compare two strings according to the git section-subsection
+ * rules. The order of the strings is important because local is
+ * assumed to have the internal format (only the section name and with
+ * case information) and input the normalized one (only dots, no case
+ * information).
+ */
+static int cvar_match_section(const char *local, const char *input)
+{
+ char *first_dot, *last_dot;
+ char *local_sp = strchr(local, ' ');
+ int comparison_len;
+
+ /*
+ * If the local section name doesn't contain a space, then we can
+ * just do a case-insensitive compare.
+ */
+ if (local_sp == NULL)
+ return !strncasecmp(local, input, strlen(local));
+
+ /*
+ * From here onwards, there is a space diving the section and the
+ * subsection. Anything before the space in local is
+ * case-insensitive.
+ */
+ if (strncasecmp(local, input, local_sp - local))
+ return 0;
+
+ /*
+ * We compare starting from the first character after the
+ * quotation marks, which is two characters beyond the space. For
+ * the input, we start one character beyond the dot. If the names
+ * have different lengths, then we can fail early, as we know they
+ * can't be the same.
+ * The length is given by the length between the quotation marks.
+ */
+
+ first_dot = strchr(input, '.');
+ last_dot = strrchr(input, '.');
+ comparison_len = strlen(local_sp + 2) - 1;
+
+ if (last_dot == first_dot || last_dot - first_dot - 1 != comparison_len)
+ return 0;
+
+ return !strncmp(local_sp + 2, first_dot + 1, comparison_len);
+}
+
+static int cvar_match_name(const cvar_t *var, const char *str)
+{
+ const char *name_start;
+
+ if (!cvar_match_section(var->section, str)) {
+ return 0;
+ }
+ /* Early exit if the lengths are different */
+ name_start = strrchr(str, '.') + 1;
+ if (strlen(var->name) != strlen(name_start))
+ return 0;
+
+ return !strcasecmp(var->name, name_start);
+}
+
+static cvar_t *cvar_list_find(cvar_t_list *list, const char *name)
+{
+ cvar_t *iter;
+
+ CVAR_LIST_FOREACH (list, iter) {
+ if (cvar_match_name(iter, name))
+ return iter;
+ }
+
+ return NULL;
+}
+
+static int cvar_name_normalize(const char *input, char **output)
+{
+ char *input_sp = strchr(input, ' ');
+ char *quote, *str;
+ int i;
+
+ /* We need to make a copy anyway */
+ str = git__strdup(input);
+ if (str == NULL)
+ return GIT_ENOMEM;
+
+ *output = str;
+
+ /* If there aren't any spaces, we don't need to do anything */
+ if (input_sp == NULL)
+ return GIT_SUCCESS;
+
+ /*
+ * If there are spaces, we replace the space by a dot, move the
+ * variable name so that the dot before it replaces the last
+ * quotation mark and repeat so that the first quotation mark
+ * disappears.
+ */
+ str[input_sp - input] = '.';
+
+ for (i = 0; i < 2; ++i) {
+ quote = strrchr(str, '"');
+ memmove(quote, quote + 1, strlen(quote));
+ }
+
+ return GIT_SUCCESS;
+}
+
+static int config_open(git_config_backend *cfg)
+{
+ int error;
+ file_backend *b = (file_backend *)cfg;
+
+ error = gitfo_read_file(&b->reader.buffer, b->file_path);
+ if(error < GIT_SUCCESS)
+ goto cleanup;
+
+ error = config_parse(b);
+ if (error < GIT_SUCCESS)
+ goto cleanup;
+
+ gitfo_free_buf(&b->reader.buffer);
+
+ return error;
+
+ cleanup:
+ cvar_list_free(&b->var_list);
+ gitfo_free_buf(&b->reader.buffer);
+ free(cfg);
+
+ return error;
+}
+
+static void backend_free(git_config_backend *_backend)
+{
+ file_backend *backend = (file_backend *)_backend;
+
+ if (backend == NULL)
+ return;
+
+ free(backend->file_path);
+ cvar_list_free(&backend->var_list);
+
+ free(backend);
+}
+
+static int file_foreach(git_config_backend *backend, int (*fn)(const char *, void *), void *data)
+{
+ int ret = GIT_SUCCESS;
+ cvar_t *var;
+ char *normalized;
+ file_backend *b = (file_backend *)backend;
+
+ CVAR_LIST_FOREACH(&b->var_list, var) {
+ ret = cvar_name_normalize(var->name, &normalized);
+ if (ret < GIT_SUCCESS)
+ return ret;
+
+ ret = fn(normalized, data);
+ free(normalized);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int config_set(git_config_backend *cfg, const char *name, const char *value)
+{
+ cvar_t *var = NULL;
+ cvar_t *existing = NULL;
+ int error = GIT_SUCCESS;
+ const char *last_dot;
+ file_backend *b = (file_backend *)cfg;
+
+ /*
+ * If it already exists, we just need to update its value.
+ */
+ existing = cvar_list_find(&b->var_list, name);
+ if (existing != NULL) {
+ char *tmp = value ? git__strdup(value) : NULL;
+ if (tmp == NULL && value != NULL)
+ return GIT_ENOMEM;
+
+ free(existing->value);
+ existing->value = tmp;
+
+ return GIT_SUCCESS;
+ }
+
+ /*
+ * Otherwise, create it and stick it at the end of the queue.
+ */
+
+ last_dot = strrchr(name, '.');
+ if (last_dot == NULL) {
+ return git__throw(GIT_EINVALIDTYPE, "Variables without section aren't allowed");
+ }
+
+ var = git__malloc(sizeof(cvar_t));
+ if (var == NULL)
+ return GIT_ENOMEM;
+
+ memset(var, 0x0, sizeof(cvar_t));
+
+ var->section = git__strndup(name, last_dot - name);
+ if (var->section == NULL) {
+ error = GIT_ENOMEM;
+ goto out;
+ }
+
+ var->name = git__strdup(last_dot + 1);
+ if (var->name == NULL) {
+ error = GIT_ENOMEM;
+ goto out;
+ }
+
+ var->value = value ? git__strdup(value) : NULL;
+ if (var->value == NULL && value != NULL) {
+ error = GIT_ENOMEM;
+ goto out;
+ }
+
+ CVAR_LIST_APPEND(&b->var_list, var);
+
+ out:
+ if (error < GIT_SUCCESS)
+ cvar_free(var);
+
+ return error;
+}
+
+/*
+ * Internal function that actually gets the value in string form
+ */
+static int config_get(git_config_backend *cfg, const char *name, const char **out)
+{
+ cvar_t *var;
+ int error = GIT_SUCCESS;
+ file_backend *b = (file_backend *)cfg;
+
+ var = cvar_list_find(&b->var_list, name);
+
+ if (var == NULL)
+ return git__throw(GIT_ENOTFOUND, "Variable '%s' not found", name);
+
+ *out = var->value;
+
+ return error;
+}
+
+int git_config_backend_file(git_config_backend **out, const char *path)
+{
+ file_backend *backend;
+
+ backend = git__malloc(sizeof(file_backend));
+ if (backend == NULL)
+ return GIT_ENOMEM;
+
+ memset(backend, 0x0, sizeof(file_backend));
+
+ backend->file_path = git__strdup(path);
+ if (backend->file_path == NULL) {
+ free(backend);
+ return GIT_ENOMEM;
+ }
+
+ backend->parent.open = config_open;
+ backend->parent.get = config_get;
+ backend->parent.set = config_set;
+ backend->parent.foreach = file_foreach;
+ backend->parent.free = backend_free;
+
+ *out = (git_config_backend *)backend;
+
+ return GIT_SUCCESS;
+}
+
+static int cfg_getchar_raw(file_backend *cfg)
+{
+ int c;
+
+ c = *cfg->reader.read_ptr++;
+
+ /*
+ Win 32 line breaks: if we find a \r\n sequence,
+ return only the \n as a newline
+ */
+ if (c == '\r' && *cfg->reader.read_ptr == '\n') {
+ cfg->reader.read_ptr++;
+ c = '\n';
+ }
+
+ if (c == '\n')
+ cfg->reader.line_number++;
+
+ if (c == 0) {
+ cfg->reader.eof = 1;
+ c = '\n';
+ }
+
+ return c;
+}
+
+#define SKIP_WHITESPACE (1 << 1)
+#define SKIP_COMMENTS (1 << 2)
+
+static int cfg_getchar(file_backend *cfg_file, int flags)
+{
+ const int skip_whitespace = (flags & SKIP_WHITESPACE);
+ const int skip_comments = (flags & SKIP_COMMENTS);
+ int c;
+
+ assert(cfg_file->reader.read_ptr);
+
+ do c = cfg_getchar_raw(cfg_file);
+ while (skip_whitespace && isspace(c));
+
+ if (skip_comments && (c == '#' || c == ';')) {
+ do c = cfg_getchar_raw(cfg_file);
+ while (c != '\n');
+ }
+
+ return c;
+}
+
+/*
+ * Read the next char, but don't move the reading pointer.
+ */
+static int cfg_peek(file_backend *cfg, int flags)
+{
+ void *old_read_ptr;
+ int old_lineno, old_eof;
+ int ret;
+
+ assert(cfg->reader.read_ptr);
+
+ old_read_ptr = cfg->reader.read_ptr;
+ old_lineno = cfg->reader.line_number;
+ old_eof = cfg->reader.eof;
+
+ ret = cfg_getchar(cfg, flags);
+
+ cfg->reader.read_ptr = old_read_ptr;
+ cfg->reader.line_number = old_lineno;
+ cfg->reader.eof = old_eof;
+
+ return ret;
+}
+
+static const char *LINEBREAK_UNIX = "\\\n";
+static const char *LINEBREAK_WIN32 = "\\\r\n";
+
+static int is_linebreak(const char *pos)
+{
+ return memcmp(pos - 1, LINEBREAK_UNIX, sizeof(LINEBREAK_UNIX)) == 0 ||
+ memcmp(pos - 2, LINEBREAK_WIN32, sizeof(LINEBREAK_WIN32)) == 0;
+}
+
+/*
+ * Read and consume a line, returning it in newly-allocated memory.
+ */
+static char *cfg_readline(file_backend *cfg)
+{
+ char *line = NULL;
+ char *line_src, *line_end;
+ int line_len;
+
+ line_src = cfg->reader.read_ptr;
+ line_end = strchr(line_src, '\n');
+
+ /* no newline at EOF */
+ if (line_end == NULL)
+ line_end = strchr(line_src, 0);
+ else
+ while (is_linebreak(line_end))
+ line_end = strchr(line_end + 1, '\n');
+
+
+ while (line_src < line_end && isspace(*line_src))
+ line_src++;
+
+ line = (char *)git__malloc((size_t)(line_end - line_src) + 1);
+ if (line == NULL)
+ return NULL;
+
+ line_len = 0;
+ while (line_src < line_end) {
+
+ if (memcmp(line_src, LINEBREAK_UNIX, sizeof(LINEBREAK_UNIX)) == 0) {
+ line_src += sizeof(LINEBREAK_UNIX);
+ continue;
+ }
+
+ if (memcmp(line_src, LINEBREAK_WIN32, sizeof(LINEBREAK_WIN32)) == 0) {
+ line_src += sizeof(LINEBREAK_WIN32);
+ continue;
+ }
+
+ line[line_len++] = *line_src++;
+ }
+
+ line[line_len] = '\0';
+
+ while (--line_len >= 0 && isspace(line[line_len]))
+ line[line_len] = '\0';
+
+ if (*line_end == '\n')
+ line_end++;
+
+ if (*line_end == '\0')
+ cfg->reader.eof = 1;
+
+ cfg->reader.line_number++;
+ cfg->reader.read_ptr = line_end;
+
+ return line;
+}
+
+/*
+ * Consume a line, without storing it anywhere
+ */
+void cfg_consume_line(file_backend *cfg)
+{
+ char *line_start, *line_end;
+
+ line_start = cfg->reader.read_ptr;
+ line_end = strchr(line_start, '\n');
+ /* No newline at EOF */
+ if(line_end == NULL){
+ line_end = strchr(line_start, '\0');
+ }
+
+ if (*line_end == '\n')
+ line_end++;
+
+ if (*line_end == '\0')
+ cfg->reader.eof = 1;
+
+ cfg->reader.line_number++;
+ cfg->reader.read_ptr = line_end;
+}
+
+static inline int config_keychar(int c)
+{
+ return isalnum(c) || c == '-';
+}
+
+static int parse_section_header_ext(const char *line, const char *base_name, char **section_name)
+{
+ int buf_len, total_len, pos, rpos;
+ int c, ret;
+ char *subsection, *first_quote, *last_quote;
+ int error = GIT_SUCCESS;
+ int quote_marks;
+ /*
+ * base_name is what came before the space. We should be at the
+ * first quotation mark, except for now, line isn't being kept in
+ * sync so we only really use it to calculate the length.
+ */
+
+ first_quote = strchr(line, '"');
+ last_quote = strrchr(line, '"');
+
+ if (last_quote - first_quote == 0)
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse ext header. There is no final quotation mark");
+
+ buf_len = last_quote - first_quote + 2;
+
+ subsection = git__malloc(buf_len + 2);
+ if (subsection == NULL)
+ return GIT_ENOMEM;
+
+ pos = 0;
+ rpos = 0;
+ quote_marks = 0;
+
+ line = first_quote;
+ c = line[rpos++];
+
+ /*
+ * At the end of each iteration, whatever is stored in c will be
+ * added to the string. In case of error, jump to out
+ */
+ do {
+ switch (c) {
+ case '"':
+ if (quote_marks++ >= 2)
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse ext header. Too many quotes");
+ break;
+ case '\\':
+ c = line[rpos++];
+ switch (c) {
+ case '"':
+ case '\\':
+ break;
+ default:
+ error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse ext header. Unsupported escape char \\%c", c);
+ goto out;
+ }
+ default:
+ break;
+ }
+
+ subsection[pos++] = c;
+ } while ((c = line[rpos++]) != ']');
+
+ subsection[pos] = '\0';
+
+ total_len = strlen(base_name) + strlen(subsection) + 2;
+ *section_name = git__malloc(total_len);
+ if (*section_name == NULL) {
+ error = GIT_ENOMEM;
+ goto out;
+ }
+
+ ret = snprintf(*section_name, total_len, "%s %s", base_name, subsection);
+ if (ret >= total_len) {
+ /* If this fails, we've checked the length wrong */
+ error = git__throw(GIT_ERROR, "Failed to parse ext header. Wrong total lenght calculation");
+ goto out;
+ } else if (ret < 0) {
+ error = git__throw(GIT_EOSERR, "Failed to parse ext header. OS error: %s", strerror(errno));
+ goto out;
+ }
+
+ git__strntolower(*section_name, strchr(*section_name, ' ') - *section_name);
+
+ out:
+ free(subsection);
+
+ return error;
+}
+
+static int parse_section_header(file_backend *cfg, char **section_out)
+{
+ char *name, *name_end;
+ int name_length, c, pos;
+ int error = GIT_SUCCESS;
+ char *line;
+
+ line = cfg_readline(cfg);
+ if (line == NULL)
+ return GIT_ENOMEM;
+
+ /* find the end of the variable's name */
+ name_end = strchr(line, ']');
+ if (name_end == NULL)
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse header. Can't find header name end");
+
+ name = (char *)git__malloc((size_t)(name_end - line) + 1);
+ if (name == NULL)
+ return GIT_ENOMEM;
+
+ name_length = 0;
+ pos = 0;
+
+ /* Make sure we were given a section header */
+ c = line[pos++];
+ if (c != '[') {
+ error = git__throw(GIT_ERROR, "Failed to parse header. Didn't get section header. This is a bug");
+ goto error;
+ }
+
+ c = line[pos++];
+
+ do {
+ if (cfg->reader.eof){
+ error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse header. Config file ended unexpectedly");
+ goto error;
+ }
+
+ if (isspace(c)){
+ name[name_length] = '\0';
+ error = parse_section_header_ext(line, name, section_out);
+ free(line);
+ free(name);
+ return error;
+ }
+
+ if (!config_keychar(c) && c != '.') {
+ error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse header. Wrong format on header");
+ goto error;
+ }
+
+ name[name_length++] = tolower(c);
+
+ } while ((c = line[pos++]) != ']');
+
+ name[name_length] = 0;
+ free(line);
+ git__strtolower(name);
+ *section_out = name;
+ return GIT_SUCCESS;
+
+error:
+ free(line);
+ free(name);
+ return error;
+}
+
+static int skip_bom(file_backend *cfg)
+{
+ static const char *utf8_bom = "\xef\xbb\xbf";
+
+ if (memcmp(cfg->reader.read_ptr, utf8_bom, sizeof(utf8_bom)) == 0)
+ cfg->reader.read_ptr += sizeof(utf8_bom);
+
+ /* TODO: the reference implementation does pretty stupid
+ shit with the BoM
+ */
+
+ return GIT_SUCCESS;
+}
+
+/*
+ (* basic types *)
+ digit = "0".."9"
+ integer = digit { digit }
+ alphabet = "a".."z" + "A" .. "Z"
+
+ section_char = alphabet | "." | "-"
+ extension_char = (* any character except newline *)
+ any_char = (* any character *)
+ variable_char = "alphabet" | "-"
+
+
+ (* actual grammar *)
+ config = { section }
+
+ section = header { definition }
+
+ header = "[" section [subsection | subsection_ext] "]"
+
+ subsection = "." section
+ subsection_ext = "\"" extension "\""
+
+ section = section_char { section_char }
+ extension = extension_char { extension_char }
+
+ definition = variable_name ["=" variable_value] "\n"
+
+ variable_name = variable_char { variable_char }
+ variable_value = string | boolean | integer
+
+ string = quoted_string | plain_string
+ quoted_string = "\"" plain_string "\""
+ plain_string = { any_char }
+
+ boolean = boolean_true | boolean_false
+ boolean_true = "yes" | "1" | "true" | "on"
+ boolean_false = "no" | "0" | "false" | "off"
+*/
+
+static void strip_comments(char *line)
+{
+ int quote_count = 0;
+ char *ptr;
+
+ for (ptr = line; *ptr; ++ptr) {
+ if (ptr[0] == '"' && ptr > line && ptr[-1] != '\\')
+ quote_count++;
+
+ if ((ptr[0] == ';' || ptr[0] == '#') && (quote_count % 2) == 0) {
+ ptr[0] = '\0';
+ break;
+ }
+ }
+
+ if (isspace(ptr[-1])) {
+ /* TODO skip whitespace */
+ }
+}
+
+static int config_parse(file_backend *cfg_file)
+{
+ int error = GIT_SUCCESS, c;
+ char *current_section = NULL;
+ char *var_name;
+ char *var_value;
+ cvar_t *var;
+
+ /* Initialise the reading position */
+ cfg_file->reader.read_ptr = cfg_file->reader.buffer.data;
+ cfg_file->reader.eof = 0;
+
+ skip_bom(cfg_file);
+
+ while (error == GIT_SUCCESS && !cfg_file->reader.eof) {
+
+ c = cfg_peek(cfg_file, SKIP_WHITESPACE);
+
+ switch (c) {
+ case '\0': /* We've arrived at the end of the file */
+ break;
+
+ case '[': /* section header, new section begins */
+ free(current_section);
+ error = parse_section_header(cfg_file, ¤t_section);
+ break;
+
+ case ';':
+ case '#':
+ cfg_consume_line(cfg_file);
+ break;
+
+ default: /* assume variable declaration */
+ error = parse_variable(cfg_file, &var_name, &var_value);
+
+ if (error < GIT_SUCCESS)
+ break;
+
+ var = malloc(sizeof(cvar_t));
+ if (var == NULL) {
+ error = GIT_ENOMEM;
+ break;
+ }
+
+ memset(var, 0x0, sizeof(cvar_t));
+
+ var->section = git__strdup(current_section);
+ if (var->section == NULL) {
+ error = GIT_ENOMEM;
+ free(var);
+ break;
+ }
+
+ var->name = var_name;
+ var->value = var_value;
+ git__strtolower(var->name);
+
+ CVAR_LIST_APPEND(&cfg_file->var_list, var);
+
+ break;
+ }
+ }
+
+ if (current_section)
+ free(current_section);
+
+ return error;
+}
+
+static int is_multiline_var(const char *str)
+{
+ char *end = strrchr(str, '\0') - 1;
+
+ while (isspace(*end))
+ --end;
+
+ return *end == '\\';
+}
+
+static int parse_multiline_variable(file_backend *cfg, const char *first, char **out)
+{
+ char *line = NULL, *end;
+ int error = GIT_SUCCESS, len, ret;
+ char *buf;
+
+ /* Check that the next line exists */
+ line = cfg_readline(cfg);
+ if (line == NULL)
+ return GIT_ENOMEM;
+
+ /* We've reached the end of the file, there is input missing */
+ if (line[0] == '\0') {
+ error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse multiline var. File ended unexpectedly");
+ goto out;
+ }
+
+ strip_comments(line);
+
+ /* If it was just a comment, pretend it didn't exist */
+ if (line[0] == '\0') {
+ error = parse_multiline_variable(cfg, first, out);
+ goto out;
+ }
+
+ /* Find the continuation character '\' and strip the whitespace */
+ end = strrchr(first, '\\');
+ while (isspace(end[-1]))
+ --end;
+
+ *end = '\0'; /* Terminate the string here */
+
+ len = strlen(first) + strlen(line) + 2;
+ buf = git__malloc(len);
+ if (buf == NULL) {
+ error = GIT_ENOMEM;
+ goto out;
+ }
+
+ ret = snprintf(buf, len, "%s %s", first, line);
+ if (ret < 0) {
+ error = git__throw(GIT_EOSERR, "Failed to parse multiline var. Failed to put together two lines. OS err: %s", strerror(errno));
+ free(buf);
+ goto out;
+ }
+
+ /*
+ * If we need to continue reading the next line, pretend
+ * everything we've read up to now was in one line and call
+ * ourselves.
+ */
+ if (is_multiline_var(buf)) {
+ char *final_val;
+ error = parse_multiline_variable(cfg, buf, &final_val);
+ free(buf);
+ buf = final_val;
+ }
+
+ *out = buf;
+
+ out:
+ free(line);
+ return error;
+}
+
+static int parse_variable(file_backend *cfg, char **var_name, char **var_value)
+{
+ char *tmp;
+ int error = GIT_SUCCESS;
+ const char *var_end = NULL;
+ const char *value_start = NULL;
+ char *line;
+
+ line = cfg_readline(cfg);
+ if (line == NULL)
+ return GIT_ENOMEM;
+
+ strip_comments(line);
+
+ var_end = strchr(line, '=');
+
+ if (var_end == NULL)
+ var_end = strchr(line, '\0');
+ else
+ value_start = var_end + 1;
+
+ if (isspace(var_end[-1])) {
+ do var_end--;
+ while (isspace(var_end[0]));
+ }
+
+ tmp = git__strndup(line, var_end - line + 1);
+ if (tmp == NULL) {
+ error = GIT_ENOMEM;
+ goto out;
+ }
+
+ *var_name = tmp;
+
+ /*
+ * Now, let's try to parse the value
+ */
+ if (value_start != NULL) {
+
+ while (isspace(value_start[0]))
+ value_start++;
+
+ if (value_start[0] == '\0')
+ goto out;
+
+ if (is_multiline_var(value_start)) {
+ error = parse_multiline_variable(cfg, value_start, var_value);
+ if (error < GIT_SUCCESS)
+ free(*var_name);
+ goto out;
+ }
+
+ tmp = strdup(value_start);
+ if (tmp == NULL) {
+ free(*var_name);
+ error = GIT_ENOMEM;
+ goto out;
+ }
+
+ *var_value = tmp;
+ } else {
+ /* If thre is no value, boolean true is assumed */
+ *var_value = NULL;
+ }
+
+ out:
+ free(line);
+ return error;
+}
diff --git a/src/errors.c b/src/errors.c
index c3a495cc9..1fb68ee65 100644
--- a/src/errors.c
+++ b/src/errors.c
@@ -1,6 +1,35 @@
+/*
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * In addition to the permissions in the GNU General Public License,
+ * the authors give you unlimited permission to link the compiled
+ * version of this file into combinations with other programs,
+ * and to distribute those combinations without any restriction
+ * coming from the use of this file. (The General Public License
+ * restrictions do apply in other respects; for example, they cover
+ * modification of the file, and distribution when not linked into
+ * a combined executable.)
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
#include "common.h"
+#include "git2/thread-utils.h" /* for GIT_TLS */
#include "thread-utils.h" /* for GIT_TLS */
+#include
+
+static GIT_TLS char g_last_error[1024];
+
static struct {
int num;
const char *str;
@@ -13,7 +42,7 @@ static struct {
{GIT_EOBJTYPE, "The specified object is of invalid type"},
{GIT_EOBJCORRUPTED, "The specified object has its data corrupted"},
{GIT_ENOTAREPO, "The specified repository is invalid"},
- {GIT_EINVALIDTYPE, "The object type is invalid or doesn't match"},
+ {GIT_EINVALIDTYPE, "The object or config variable type is invalid or doesn't match"},
{GIT_EMISSINGOBJDATA, "The object cannot be written that because it's missing internal data"},
{GIT_EPACKCORRUPTED, "The packfile for the ODB is corrupted"},
{GIT_EFLOCKFAIL, "Failed to adquire or release a file lock"},
@@ -46,3 +75,38 @@ const char *git_strerror(int num)
return "Unknown error";
}
+
+int git__rethrow(int error, const char *msg, ...)
+{
+ char new_error[1024];
+ char *old_error = NULL;
+
+ va_list va;
+
+ va_start(va, msg);
+ vsnprintf(new_error, sizeof(new_error), msg, va);
+ va_end(va);
+
+ old_error = strdup(g_last_error);
+ snprintf(g_last_error, sizeof(g_last_error), "%s \n - %s", new_error, old_error);
+ free(old_error);
+
+ return error;
+}
+
+int git__throw(int error, const char *msg, ...)
+{
+ va_list va;
+
+ va_start(va, msg);
+ vsnprintf(g_last_error, sizeof(g_last_error), msg, va);
+ va_end(va);
+
+ return error;
+}
+
+const char *git_lasterror(void)
+{
+ return g_last_error;
+}
+
diff --git a/src/fileops.c b/src/fileops.c
index 5dd4a3806..45dd8f444 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -10,7 +10,7 @@ int gitfo_mkdir_2file(const char *file_path)
error = git__dirname_r(target_folder_path, sizeof(target_folder_path), file_path);
if (error < GIT_SUCCESS)
- return error;
+ return git__throw(GIT_EINVALIDPATH, "Failed to recursively build `%s` tree structure. Unable to parse parent folder name", file_path);
/* Does the containing folder exist? */
if (gitfo_isdir(target_folder_path)) {
@@ -19,7 +19,7 @@ int gitfo_mkdir_2file(const char *file_path)
/* Let's create the tree structure */
error = gitfo_mkdir_recurs(target_folder_path, mode);
if (error < GIT_SUCCESS)
- return error;
+ return error; /* The callee already takes care of setting the correct error message. */
}
return GIT_SUCCESS;
@@ -164,19 +164,19 @@ int gitfo_read_file(gitfo_buf *obj, const char *path)
if (((size = gitfo_size(fd)) < 0) || !git__is_sizet(size+1)) {
gitfo_close(fd);
- return GIT_ERROR;
+ return git__throw(GIT_ERROR, "Failed to read file `%s`. Either an error occured while calculating its size or the file is too large", path);
}
len = (size_t) size;
if ((buff = git__malloc(len + 1)) == NULL) {
gitfo_close(fd);
- return GIT_ERROR;
+ return GIT_ENOMEM;
}
if (gitfo_read(fd, buff, len) < 0) {
gitfo_close(fd);
free(buff);
- return GIT_ERROR;
+ return git__throw(GIT_ERROR, "Failed to read file `%s`", path);
}
buff[len] = '\0';
@@ -197,13 +197,15 @@ void gitfo_free_buf(gitfo_buf *obj)
int gitfo_mv(const char *from, const char *to)
{
+ int error;
+
#ifdef GIT_WIN32
/*
* Win32 POSIX compilance my ass. If the destination
* file exists, the `rename` call fails. This is as
* close as it gets with the Win32 API.
*/
- return MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? GIT_SUCCESS : GIT_EOSERR;
+ error = MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? GIT_SUCCESS : GIT_EOSERR;
#else
/* Don't even try this on Win32 */
if (!link(from, to)) {
@@ -214,16 +216,21 @@ int gitfo_mv(const char *from, const char *to)
if (!rename(from, to))
return GIT_SUCCESS;
- return GIT_EOSERR;
+ error = GIT_EOSERR;
#endif
+
+ if (error < GIT_SUCCESS)
+ return git__throw(error, "Failed to move file from `%s`to `%s`", from, to);
+
+ return GIT_SUCCESS;
}
int gitfo_mv_force(const char *from, const char *to)
{
if (gitfo_mkdir_2file(to) < GIT_SUCCESS)
- return GIT_EOSERR;
+ return GIT_EOSERR; /* The callee already takes care of setting the correct error message. */
- return gitfo_mv(from, to);
+ return gitfo_mv(from, to); /* The callee already takes care of setting the correct error message. */
}
int gitfo_map_ro(git_map *out, git_file fd, git_off_t begin, size_t len)
@@ -338,7 +345,7 @@ int gitfo_dirent(
struct dirent *de;
if (!wd_len || path_sz < wd_len + 2)
- return GIT_ERROR;
+ return git__throw(GIT_EINVALIDARGS, "Failed to process `%s` tree structure. Path is either empty or buffer size is too short", path);
while (path[wd_len - 1] == '/')
wd_len--;
@@ -347,7 +354,7 @@ int gitfo_dirent(
dir = opendir(path);
if (!dir)
- return GIT_EOSERR;
+ return git__throw(GIT_EOSERR, "Failed to process `%s` tree structure. An error occured while opening the directory", path);
while ((de = readdir(dir)) != NULL) {
size_t de_len;
@@ -364,14 +371,14 @@ int gitfo_dirent(
de_len = strlen(de->d_name);
if (path_sz < wd_len + de_len + 1) {
closedir(dir);
- return GIT_ERROR;
+ return git__throw(GIT_ERROR, "Failed to process `%s` tree structure. Buffer size is too short", path);
}
strcpy(path + wd_len, de->d_name);
result = fn(arg, path);
if (result < GIT_SUCCESS) {
closedir(dir);
- return result;
+ return result; /* The callee is reponsible for setting the correct error message */
}
if (result > 0) {
closedir(dir);
@@ -399,7 +406,7 @@ int retrieve_path_root_offset(const char *path)
if (*(path + offset) == '/')
return offset;
- return GIT_ERROR;
+ return -1; /* Not a real error. Rather a signal than the path is not rooted */
}
@@ -438,7 +445,11 @@ int gitfo_mkdir_recurs(const char *path, int mode)
error = gitfo_mkdir(path, mode);
free(path_copy);
- return error;
+
+ if (error < GIT_SUCCESS)
+ return git__throw(error, "Failed to recursively create `%s` tree structure", path);
+
+ return GIT_SUCCESS;
}
static int retrieve_previous_path_component_start(const char *path)
@@ -484,7 +495,7 @@ int gitfo_prettify_dir_path(char *buffer_out, size_t size, const char *path)
if (root_path_offset < 0) {
error = gitfo_getcwd(buffer_out, size);
if (error < GIT_SUCCESS)
- return error;
+ return error; /* The callee already takes care of setting the correct error message. */
len = strlen(buffer_out);
buffer_out += len;
@@ -529,7 +540,7 @@ int gitfo_prettify_dir_path(char *buffer_out, size_t size, const char *path)
/* Are we escaping out of the root dir? */
if (len < 0)
- return GIT_EINVALIDPATH;
+ return git__throw(GIT_EINVALIDPATH, "Failed to normalize path `%s`. The path escapes out of the root directory", path);
buffer_out = (char *)buffer_out_start + len;
continue;
@@ -537,7 +548,7 @@ int gitfo_prettify_dir_path(char *buffer_out, size_t size, const char *path)
/* Guard against potential multiple dot path traversal (cf http://cwe.mitre.org/data/definitions/33.html) */
if (only_dots && segment_len > 0)
- return GIT_EINVALIDPATH;
+ return git__throw(GIT_EINVALIDPATH, "Failed to normalize path `%s`. The path contains a segment with three `.` or more", path);
*buffer_out++ = '/';
len++;
@@ -557,21 +568,21 @@ int gitfo_prettify_file_path(char *buffer_out, size_t size, const char *path)
/* Let's make sure the filename isn't empty nor a dot */
if (path_len == 0 || (path_len == 1 && *path == '.'))
- return GIT_EINVALIDPATH;
+ return git__throw(GIT_EINVALIDPATH, "Failed to normalize file path `%s`. The path is either empty or equals `.`", path);
/* Let's make sure the filename doesn't end with "/", "/." or "/.." */
for (i = 1; path_len > i && i < 4; i++) {
if (!strncmp(path + path_len - i, pattern, i))
- return GIT_EINVALIDPATH;
+ return git__throw(GIT_EINVALIDPATH, "Failed to normalize file path `%s`. The path points to a folder", path);
}
error = gitfo_prettify_dir_path(buffer_out, size, path);
if (error < GIT_SUCCESS)
- return error;
+ return error; /* The callee already takes care of setting the correct error message. */
path_len = strlen(buffer_out);
- if (path_len < 2)
- return GIT_EINVALIDPATH;
+ if (path_len < 2) /* TODO: Fixme. We should also take of detecting Windows rooted path (probably through usage of retrieve_path_root_offset) */
+ return git__throw(GIT_EINVALIDPATH, "Failed to normalize file path `%s`. The path points to a folder", path);
/* Remove the trailing slash */
buffer_out[path_len - 1] = '\0';
@@ -616,11 +627,11 @@ int gitfo_getcwd(char *buffer_out, size_t size)
#ifdef GIT_WIN32
cwd_buffer = _getcwd(buffer_out, size);
#else
- cwd_buffer = getcwd(buffer_out, size); //TODO: Fixme. Ensure the required headers are correctly included
+ cwd_buffer = getcwd(buffer_out, size);
#endif
if (cwd_buffer == NULL)
- return GIT_EOSERR;
+ return git__throw(GIT_EOSERR, "Failed to retrieve current working directory");
posixify_path(buffer_out);
diff --git a/src/index.c b/src/index.c
index 94d1c9e2a..d912d90f7 100644
--- a/src/index.c
+++ b/src/index.c
@@ -103,6 +103,7 @@ static git_index_tree *read_tree_internal(const char **, const char *, git_index
static int read_unmerged_internal(git_index *, const char **, size_t buffer_size);
static int parse_index(git_index *index, const char *buffer, size_t buffer_size);
+static int is_index_extended(git_index *index);
static void sort_index(git_index *index);
static int write_index(git_index *index, git_filebuf *file);
@@ -317,56 +318,12 @@ git_index_entry *git_index_get(git_index *index, int n)
return git_vector_get(&index->entries, (unsigned int)n);
}
-int git_index_add(git_index *index, const char *rel_path, int stage)
-{
- git_index_entry entry;
- char full_path[GIT_PATH_MAX];
- struct stat st;
- int error;
-
- if (index->repository == NULL)
- return GIT_EBAREINDEX;
-
- git__joinpath(full_path, index->repository->path_workdir, rel_path);
-
- if (gitfo_exists(full_path) < 0)
- return GIT_ENOTFOUND;
-
- if (gitfo_stat(full_path, &st) < 0)
- return GIT_EOSERR;
-
- if (stage < 0 || stage > 3)
- return GIT_ERROR;
-
- memset(&entry, 0x0, sizeof(git_index_entry));
-
- entry.ctime.seconds = (git_time_t)st.st_ctime;
- entry.mtime.seconds = (git_time_t)st.st_mtime;
- /* entry.mtime.nanoseconds = st.st_mtimensec; */
- /* entry.ctime.nanoseconds = st.st_ctimensec; */
- entry.dev= st.st_rdev;
- entry.ino = st.st_ino;
- entry.mode = st.st_mode;
- entry.uid = st.st_uid;
- entry.gid = st.st_gid;
- entry.file_size = st.st_size;
-
- /* write the blob to disk and get the oid */
- if ((error = git_blob_create_fromfile(&entry.oid, index->repository, rel_path)) < GIT_SUCCESS)
- return error;
-
- entry.flags |= (stage << GIT_IDXENTRY_STAGESHIFT);
- entry.path = (char *)rel_path; /* do not duplicate; index_insert already does this */
-
- return git_index_insert(index, &entry);
-}
-
-void sort_index(git_index *index)
+static void sort_index(git_index *index)
{
git_vector_sort(&index->entries);
}
-int git_index_insert(git_index *index, const git_index_entry *source_entry)
+static int index_insert(git_index *index, const git_index_entry *source_entry, int replace)
{
git_index_entry *entry;
size_t path_length;
@@ -402,13 +359,15 @@ int git_index_insert(git_index *index, const git_index_entry *source_entry)
/* look if an entry with this path already exists */
position = git_index_find(index, source_entry->path);
- /* if no entry exists, add the entry at the end;
+ /* if no entry exists and replace is not set,
+ * add the entry at the end;
* the index is no longer sorted */
- if (position == GIT_ENOTFOUND) {
+ if (!replace || position == GIT_ENOTFOUND) {
if (git_vector_insert(&index->entries, entry) < GIT_SUCCESS)
return GIT_ENOMEM;
- /* if a previous entry exists, replace it */
+ /* if a previous entry exists and replace is set,
+ * replace it */
} else {
git_index_entry **entry_array = (git_index_entry **)index->entries.contents;
@@ -421,6 +380,81 @@ int git_index_insert(git_index *index, const git_index_entry *source_entry)
return GIT_SUCCESS;
}
+static int index_init_entry(git_index_entry *entry, git_index *index, const char *rel_path, int stage)
+{
+ char full_path[GIT_PATH_MAX];
+ struct stat st;
+ int error;
+
+ if (index->repository == NULL)
+ return GIT_EBAREINDEX;
+
+ git__joinpath(full_path, index->repository->path_workdir, rel_path);
+
+ if (gitfo_exists(full_path) < 0)
+ return GIT_ENOTFOUND;
+
+ if (gitfo_stat(full_path, &st) < 0)
+ return GIT_EOSERR;
+
+ if (stage < 0 || stage > 3)
+ return GIT_ERROR;
+
+ memset(entry, 0x0, sizeof(git_index_entry));
+
+ entry->ctime.seconds = (git_time_t)st.st_ctime;
+ entry->mtime.seconds = (git_time_t)st.st_mtime;
+ /* entry.mtime.nanoseconds = st.st_mtimensec; */
+ /* entry.ctime.nanoseconds = st.st_ctimensec; */
+ entry->dev= st.st_rdev;
+ entry->ino = st.st_ino;
+ entry->mode = st.st_mode;
+ entry->uid = st.st_uid;
+ entry->gid = st.st_gid;
+ entry->file_size = st.st_size;
+
+ /* write the blob to disk and get the oid */
+ if ((error = git_blob_create_fromfile(&entry->oid, index->repository, rel_path)) < GIT_SUCCESS)
+ return error;
+
+ entry->flags |= (stage << GIT_IDXENTRY_STAGESHIFT);
+ entry->path = (char *)rel_path; /* do not duplicate; index_insert already does this */
+ return GIT_SUCCESS;
+}
+
+int git_index_add(git_index *index, const char *path, int stage)
+{
+ int error;
+ git_index_entry entry;
+
+ if ((error = index_init_entry(&entry, index, path, stage)) < GIT_SUCCESS)
+ return error;
+
+ return index_insert(index, &entry, 1);
+}
+
+int git_index_append(git_index *index, const char *path, int stage)
+{
+ int error;
+ git_index_entry entry;
+
+ if ((error = index_init_entry(&entry, index, path, stage)) < GIT_SUCCESS)
+ return error;
+
+ return index_insert(index, &entry, 0);
+}
+
+int git_index_add2(git_index *index, const git_index_entry *source_entry)
+{
+ return index_insert(index, source_entry, 1);
+}
+
+int git_index_append2(git_index *index, const git_index_entry *source_entry)
+{
+ return index_insert(index, source_entry, 0);
+}
+
+
int git_index_remove(git_index *index, int position)
{
assert(index);
@@ -798,6 +832,24 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
return GIT_SUCCESS;
}
+static int is_index_extended(git_index *index)
+{
+ unsigned int i, extended;
+
+ extended = 0;
+
+ for (i = 0; i < index->entries.length; ++i) {
+ git_index_entry *entry;
+ entry = git_vector_get(&index->entries, i);
+ entry->flags &= ~GIT_IDXENTRY_EXTENDED;
+ if (entry->flags_extended & GIT_IDXENTRY_EXTENDED_FLAGS) {
+ extended++;
+ entry->flags |= GIT_IDXENTRY_EXTENDED;
+ }
+ }
+ return extended;
+}
+
static int write_disk_entry(git_filebuf *file, git_index_entry *entry)
{
struct entry_short *ondisk;
@@ -866,12 +918,14 @@ static int write_index(git_index *index, git_filebuf *file)
struct index_header header;
- int is_extended = 1;
+ int is_extended;
assert(index && file);
+ is_extended = is_index_extended(index);
+
header.signature = htonl(INDEX_HEADER_SIG);
- header.version = htonl(is_extended ? INDEX_VERSION_NUMBER : INDEX_VERSION_NUMBER_EXT);
+ header.version = htonl(is_extended ? INDEX_VERSION_NUMBER_EXT : INDEX_VERSION_NUMBER);
header.entry_count = htonl(index->entries.length);
git_filebuf_write(file, &header, sizeof(struct index_header));
diff --git a/src/odb.c b/src/odb.c
index 43d0ef9a4..f09b6d7e9 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -221,7 +221,7 @@ static int init_fake_wstream(git_odb_stream **stream_p, git_odb_backend *backend
*
***********************************************************/
-int backend_sort_cmp(const void *a, const void *b)
+static int backend_sort_cmp(const void *a, const void *b)
{
const backend_internal *backend_a = *(const backend_internal **)(a);
const backend_internal *backend_b = *(const backend_internal **)(b);
@@ -234,15 +234,19 @@ int backend_sort_cmp(const void *a, const void *b)
int git_odb_new(git_odb **out)
{
+ int error;
+
git_odb *db = git__calloc(1, sizeof(*db));
if (!db)
return GIT_ENOMEM;
- git_cache_init(&db->cache, GIT_DEFAULT_CACHE_SIZE, &free_odb_object);
+ error = git_cache_init(&db->cache, GIT_DEFAULT_CACHE_SIZE, &free_odb_object);
+ if (error < GIT_SUCCESS)
+ return error;
- if (git_vector_init(&db->backends, 4, backend_sort_cmp) < 0) {
+ if ((error = git_vector_init(&db->backends, 4, backend_sort_cmp)) < GIT_SUCCESS) {
free(db);
- return GIT_ENOMEM;
+ return error;
}
*out = db;
@@ -444,7 +448,7 @@ int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git
return error;
*len_p = object->raw.len;
- *type_p = object->raw.len;
+ *type_p = object->raw.type;
git_odb_object_close(object);
}
diff --git a/src/odb_loose.c b/src/odb_loose.c
index 8ee01cd2c..873dbfa0a 100644
--- a/src/odb_loose.c
+++ b/src/odb_loose.c
@@ -336,7 +336,6 @@ static int inflate_disk_obj(git_rawobj *out, gitfo_buf *obj)
{
unsigned char head[64], *buf;
z_stream zs;
- int z_status;
obj_hdr hdr;
size_t used;
@@ -350,7 +349,7 @@ static int inflate_disk_obj(git_rawobj *out, gitfo_buf *obj)
* inflate the initial part of the io buffer in order
* to parse the object header (type and size).
*/
- if ((z_status = start_inflate(&zs, obj, head, sizeof(head))) < Z_OK)
+ if (start_inflate(&zs, obj, head, sizeof(head)) < Z_OK)
return GIT_ERROR;
if ((used = get_object_header(&hdr, head)) == 0)
@@ -434,6 +433,9 @@ static int read_header_loose(git_rawobj *out, const char *loc)
if ((read_bytes = read(fd, raw_buffer, sizeof(raw_buffer))) > 0) {
set_stream_input(&zs, raw_buffer, read_bytes);
z_return = inflate(&zs, 0);
+ } else {
+ z_return = Z_STREAM_END;
+ break;
}
} while (z_return == Z_OK);
diff --git a/src/odb_pack.c b/src/odb_pack.c
index 561b32935..57ad5e34b 100644
--- a/src/odb_pack.c
+++ b/src/odb_pack.c
@@ -113,7 +113,7 @@ struct pack_backend {
git_vector packs;
struct pack_file *last_found;
char *pack_folder;
- off_t pack_folder_size;
+ time_t pack_folder_mtime;
size_t window_size; /* needs default value */
@@ -874,7 +874,7 @@ static int packfile_refresh_all(struct pack_backend *backend)
if (gitfo_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode))
return GIT_ENOTFOUND;
- if (st.st_size != backend->pack_folder_size) {
+ if (st.st_mtime != backend->pack_folder_mtime) {
char path[GIT_PATH_MAX];
strcpy(path, backend->pack_folder);
@@ -884,7 +884,7 @@ static int packfile_refresh_all(struct pack_backend *backend)
return error;
git_vector_sort(&backend->packs);
- backend->pack_folder_size = st.st_size;
+ backend->pack_folder_mtime = st.st_mtime;
}
return GIT_SUCCESS;
@@ -1385,6 +1385,7 @@ void pack_backend__free(git_odb_backend *_backend)
}
git_vector_free(&backend->packs);
+ free(backend->pack_folder);
free(backend);
}
@@ -1408,7 +1409,7 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
git__joinpath(path, objects_dir, "pack");
if (gitfo_isdir(path) == GIT_SUCCESS) {
backend->pack_folder = git__strdup(path);
- backend->pack_folder_size = -1;
+ backend->pack_folder_mtime = 0;
if (backend->pack_folder == NULL) {
free(backend);
diff --git a/src/refs.c b/src/refs.c
index 00b9ff6b2..c4d3d6ae6 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -122,7 +122,8 @@ static int reference_create(
else if (type == GIT_REF_OID)
size = sizeof(reference_oid);
else
- return GIT_EINVALIDREFSTATE;
+ return git__throw(GIT_EINVALIDARGS,
+ "Invalid reference type. Use either GIT_REF_OID or GIT_REF_SYMBOLIC as type specifier");
reference = git__malloc(size);
if (reference == NULL)
@@ -159,11 +160,9 @@ static int reference_read(gitfo_buf *file_content, time_t *mtime, const char *re
/* Determine the full path of the file */
git__joinpath(path, repo_path, ref_name);
- if (gitfo_stat(path, &st) < 0)
- return GIT_ENOTFOUND;
-
- if (S_ISDIR(st.st_mode))
- return GIT_EOBJCORRUPTED;
+ if (gitfo_stat(path, &st) < 0 || S_ISDIR(st.st_mode))
+ return git__throw(GIT_ENOTFOUND,
+ "Cannot read reference file '%s'", ref_name);
if (mtime)
*mtime = st.st_mtime;
@@ -205,7 +204,8 @@ static int loose_update(git_reference *ref)
else if (ref->type == GIT_REF_OID)
error = loose_parse_oid(ref, &ref_file);
else
- error = GIT_EINVALIDREFSTATE;
+ error = git__throw(GIT_EOBJCORRUPTED,
+ "Invalid reference type (%d) for loose reference", ref->type);
gitfo_free_buf(&ref_file);
@@ -229,7 +229,8 @@ static int loose_parse_symbolic(git_reference *ref, gitfo_buf *file_content)
ref_sym = (reference_symbolic *)ref;
if (file_content->len < (header_len + 1))
- return GIT_EREFCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED,
+ "Failed to parse loose reference. Object too short");
/*
* Assume we have already checked for the header
@@ -246,7 +247,8 @@ static int loose_parse_symbolic(git_reference *ref, gitfo_buf *file_content)
/* remove newline at the end of file */
eol = strchr(ref_sym->target, '\n');
if (eol == NULL)
- return GIT_EREFCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED,
+ "Failed to parse loose reference. Missing EOL");
*eol = '\0';
if (eol[-1] == '\r')
@@ -257,6 +259,7 @@ static int loose_parse_symbolic(git_reference *ref, gitfo_buf *file_content)
static int loose_parse_oid(git_reference *ref, gitfo_buf *file_content)
{
+ int error;
reference_oid *ref_oid;
char *buffer;
@@ -265,17 +268,19 @@ static int loose_parse_oid(git_reference *ref, gitfo_buf *file_content)
/* File format: 40 chars (OID) + newline */
if (file_content->len < GIT_OID_HEXSZ + 1)
- return GIT_EREFCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED,
+ "Failed to parse loose reference. Reference too short");
- if (git_oid_mkstr(&ref_oid->oid, buffer) < GIT_SUCCESS)
- return GIT_EREFCORRUPTED;
+ if ((error = git_oid_mkstr(&ref_oid->oid, buffer)) < GIT_SUCCESS)
+ return git__rethrow(GIT_EOBJCORRUPTED, "Failed to parse loose reference.");
buffer = buffer + GIT_OID_HEXSZ;
if (*buffer == '\r')
buffer++;
if (*buffer != '\n')
- return GIT_EREFCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED,
+ "Failed to parse loose reference. Missing EOL");
return GIT_SUCCESS;
}
@@ -387,7 +392,7 @@ static int loose_write(git_reference *ref)
strcpy(ref_contents, GIT_SYMREF);
strcat(ref_contents, ref_sym->target);
} else {
- error = GIT_EINVALIDREFSTATE;
+ error = git__throw(GIT_EOBJCORRUPTED, "Failed to write reference. Invalid reference type");
goto unlock;
}
@@ -684,7 +689,7 @@ static int packed_loadloose(git_repository *repository)
/* Remove any loose references from the cache */
{
- const void *_unused;
+ const void *GIT_UNUSED(_unused);
git_reference *reference;
GIT_HASHTABLE_FOREACH(repository->references.loose_cache, _unused, reference,
@@ -787,6 +792,8 @@ static int packed_find_peel(reference_oid *ref)
*/
}
+ git_object_close(object);
+
return GIT_SUCCESS;
}
@@ -868,7 +875,7 @@ static int packed_write(git_repository *repo)
/* Load all the packfile into a vector */
{
git_reference *reference;
- const void *_unused;
+ const void *GIT_UNUSED(_unused);
GIT_HASHTABLE_FOREACH(repo->references.packfile, _unused, reference,
git_vector_insert(&packing_list, reference); /* cannot fail: vector already has the right size */
@@ -1480,8 +1487,9 @@ int git_reference_resolve(git_reference **resolved_ref, git_reference *ref)
for (i = 0; i < MAX_NESTING_LEVEL; ++i) {
reference_symbolic *ref_sym;
+ *resolved_ref = ref;
+
if (ref->type & GIT_REF_OID) {
- *resolved_ref = ref;
return GIT_SUCCESS;
}
@@ -1518,7 +1526,7 @@ int git_reference_listcb(git_repository *repo, unsigned int list_flags, int (*ca
/* list all the packed references first */
if (list_flags & GIT_REF_PACKED) {
const char *ref_name;
- void *_unused;
+ void *GIT_UNUSED(_unused);
if ((error = packed_load(repo)) < GIT_SUCCESS)
return error;
@@ -1597,7 +1605,7 @@ int git_repository__refcache_init(git_refcache *refs)
void git_repository__refcache_free(git_refcache *refs)
{
git_reference *reference;
- const void *_unused;
+ const void *GIT_UNUSED(_unused);
assert(refs);
@@ -1692,8 +1700,9 @@ static int normalize_name(char *buffer_out, const char *name, int is_oid_ref)
}
/* Object id refname have to contain at least one slash, except
- * for HEAD in a detached state */
- if (is_oid_ref && !contains_a_slash && strcmp(name, GIT_HEAD_FILE))
+ * for HEAD in a detached state or MERGE_HEAD if we're in the
+ * middle of a merge */
+ if (is_oid_ref && !contains_a_slash && (strcmp(name, GIT_HEAD_FILE) && strcmp(name, GIT_MERGE_HEAD_FILE)))
return GIT_EINVALIDREFNAME;
/* A refname can not end with ".lock" */
diff --git a/src/refs.h b/src/refs.h
index bebb1b97d..b8f3e2f6d 100644
--- a/src/refs.h
+++ b/src/refs.h
@@ -17,6 +17,7 @@
#define MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH 100
#define GIT_HEAD_FILE "HEAD"
+#define GIT_MERGE_HEAD_FILE "MERGE_HEAD"
#define GIT_REFS_HEADS_MASTER_FILE GIT_REFS_HEADS_DIR "master"
struct git_reference {
diff --git a/src/repository.c b/src/repository.c
index c428b00af..eab56fc9a 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -58,7 +58,6 @@ static int assign_repository_dirs(
const char *git_work_tree)
{
char path_aux[GIT_PATH_MAX];
- size_t git_dir_path_len;
int error = GIT_SUCCESS;
assert(repo);
@@ -70,8 +69,6 @@ static int assign_repository_dirs(
if (error < GIT_SUCCESS)
return error;
- git_dir_path_len = strlen(path_aux);
-
/* store GIT_DIR */
repo->path_repository = git__strdup(path_aux);
if (repo->path_repository == NULL)
@@ -127,16 +124,16 @@ static int check_repository_dirs(git_repository *repo)
char path_aux[GIT_PATH_MAX];
if (gitfo_isdir(repo->path_repository) < GIT_SUCCESS)
- return GIT_ENOTAREPO;
+ return git__throw(GIT_ENOTAREPO, "`%s` is not a folder", repo->path_repository);
/* Ensure GIT_OBJECT_DIRECTORY exists */
if (gitfo_isdir(repo->path_odb) < GIT_SUCCESS)
- return GIT_ENOTAREPO;
+ return git__throw(GIT_ENOTAREPO, "`%s` does not exist", repo->path_odb);
/* Ensure HEAD file exists */
git__joinpath(path_aux, repo->path_repository, GIT_HEAD_FILE);
if (gitfo_exists(path_aux) < 0)
- return GIT_ENOTAREPO;
+ return git__throw(GIT_ENOTAREPO, "HEAD file is missing");
return GIT_SUCCESS;
}
@@ -148,12 +145,12 @@ static int guess_repository_dirs(git_repository *repo, const char *repository_pa
/* Git directory name */
if (git__basename_r(buffer, sizeof(buffer), repository_path) < 0)
- return GIT_EINVALIDPATH;
+ return git__throw(GIT_EINVALIDPATH, "Unable to parse folder name from `%s`", repository_path);
if (strcmp(buffer, DOT_GIT) == 0) {
/* Path to working dir */
if (git__dirname_r(buffer, sizeof(buffer), repository_path) < 0)
- return GIT_EINVALIDPATH;
+ return git__throw(GIT_EINVALIDPATH, "Unable to parse parent folder name from `%s`", repository_path);
path_work_tree = buffer;
}
@@ -162,13 +159,19 @@ static int guess_repository_dirs(git_repository *repo, const char *repository_pa
static git_repository *repository_alloc()
{
+ int error;
+
git_repository *repo = git__malloc(sizeof(git_repository));
if (!repo)
return NULL;
memset(repo, 0x0, sizeof(git_repository));
- git_cache_init(&repo->objects, GIT_DEFAULT_CACHE_SIZE, &git_object__free);
+ error = git_cache_init(&repo->objects, GIT_DEFAULT_CACHE_SIZE, &git_object__free);
+ if (error < GIT_SUCCESS) {
+ free(repo);
+ return NULL;
+ }
if (git_repository__refcache_init(&repo->references) < GIT_SUCCESS) {
free(repo);
@@ -180,7 +183,7 @@ static git_repository *repository_alloc()
static int init_odb(git_repository *repo)
{
- return git_odb_open(&repo->db, repo->path_odb);
+ return git_odb_open(&repo->db, repo->path_odb); /* TODO: Move odb.c to new error handling */
}
int git_repository_open3(git_repository **repo_out,
@@ -195,7 +198,7 @@ int git_repository_open3(git_repository **repo_out,
assert(repo_out);
if (object_database == NULL)
- return GIT_ERROR;
+ return git__throw(GIT_EINVALIDARGS, "Failed to open repository. `object_database` can't be null");
repo = repository_alloc();
if (repo == NULL)
@@ -221,7 +224,7 @@ int git_repository_open3(git_repository **repo_out,
cleanup:
git_repository_free(repo);
- return error;
+ return git__rethrow(error, "Failed to open repository");
}
@@ -262,7 +265,7 @@ int git_repository_open2(git_repository **repo_out,
cleanup:
git_repository_free(repo);
- return error;
+ return git__rethrow(error, "Failed to open repository");
}
int git_repository_open(git_repository **repo_out, const char *path)
@@ -293,7 +296,7 @@ int git_repository_open(git_repository **repo_out, const char *path)
cleanup:
git_repository_free(repo);
- return error;
+ return git__rethrow(error, "Failed to open repository");
}
void git_repository_free(git_repository *repo)
@@ -312,8 +315,10 @@ void git_repository_free(git_repository *repo)
if (repo->db != NULL)
git_odb_close(repo->db);
- if (repo->index != NULL)
+ if (repo->index != NULL) {
+ repo->index->repository = NULL;
git_index_free(repo->index);
+ }
free(repo);
}
@@ -325,7 +330,7 @@ int git_repository_index(git_index **index_out, git_repository *repo)
assert(index_out && repo);
if (repo->index == NULL) {
- error = git_index_open_inrepo(&repo->index, repo);
+ error = git_index_open_inrepo(&repo->index, repo); /* TODO: move index.c to new error handling */
if (error < GIT_SUCCESS)
return error;
@@ -352,7 +357,7 @@ static int repo_init_reinit(repo_init *results)
static int repo_init_createhead(git_repository *repo)
{
git_reference *head_reference;
- return git_reference_create_symbolic(&head_reference, repo, GIT_HEAD_FILE, GIT_REFS_HEADS_MASTER_FILE);
+ return git_reference_create_symbolic(&head_reference, repo, GIT_HEAD_FILE, GIT_REFS_HEADS_MASTER_FILE); /* TODO: finalize moving refs.c to new error handling */
}
static int repo_init_check_head_existence(char * repository_path)
@@ -366,6 +371,7 @@ static int repo_init_check_head_existence(char * repository_path)
static int repo_init_structure(repo_init *results)
{
const int mode = 0755; /* or 0777 ? */
+ int error;
char temp_path[GIT_PATH_MAX];
char *git_dir = results->path_repository;
@@ -375,23 +381,27 @@ static int repo_init_structure(repo_init *results)
/* Creates the '/objects/info/' directory */
git__joinpath(temp_path, git_dir, GIT_OBJECTS_INFO_DIR);
- if (gitfo_mkdir_recurs(temp_path, mode) < GIT_SUCCESS)
- return GIT_ERROR;
+ error = gitfo_mkdir_recurs(temp_path, mode);
+ if (error < GIT_SUCCESS)
+ return error;
/* Creates the '/objects/pack/' directory */
git__joinpath(temp_path, git_dir, GIT_OBJECTS_PACK_DIR);
- if (gitfo_mkdir(temp_path, mode))
- return GIT_ERROR;
+ error = gitfo_mkdir(temp_path, mode);
+ if (error < GIT_SUCCESS)
+ return git__throw(error, "Unable to create `%s` folder", temp_path);
/* Creates the '/refs/heads/' directory */
git__joinpath(temp_path, git_dir, GIT_REFS_HEADS_DIR);
- if (gitfo_mkdir_recurs(temp_path, mode))
- return GIT_ERROR;
+ error = gitfo_mkdir_recurs(temp_path, mode);
+ if (error < GIT_SUCCESS)
+ return error;
/* Creates the '/refs/tags/' directory */
git__joinpath(temp_path, git_dir, GIT_REFS_TAGS_DIR);
- if (gitfo_mkdir(temp_path, mode))
- return GIT_ERROR;
+ error = gitfo_mkdir(temp_path, mode);
+ if (error < GIT_SUCCESS)
+ return git__throw(error, "Unable to create `%s` folder", temp_path);
/* TODO: what's left? templates? */
@@ -470,7 +480,7 @@ int git_repository_init(git_repository **repo_out, const char *path, unsigned is
cleanup:
free(results.path_repository);
git_repository_free(repo);
- return error;
+ return git__rethrow(error, "Failed to (re)init the repository `%s`", path);
}
int git_repository_is_empty(git_repository *repo)
@@ -480,10 +490,10 @@ int git_repository_is_empty(git_repository *repo)
error = git_reference_lookup(&head, repo, "HEAD");
if (error < GIT_SUCCESS)
- return error;
+ return git__throw(error, "Failed to determine the emptiness of the repository. An error occured while retrieving the HEAD reference");
if (git_reference_type(head) != GIT_REF_SYMBOLIC)
- return GIT_EOBJCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to determine the emptiness of the repository. HEAD is probably in detached state");
return git_reference_resolve(&branch, head) == GIT_SUCCESS ? 0 : 1;
}
diff --git a/src/revwalk.c b/src/revwalk.c
index b62b09961..8163851a3 100644
--- a/src/revwalk.c
+++ b/src/revwalk.c
@@ -210,7 +210,7 @@ static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawo
git_oid oid;
if (git_oid_mkstr(&oid, (char *)buffer + STRLEN("parent ")) < GIT_SUCCESS)
- return GIT_EOBJCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse commit. Parent object is corrupted");
commit->parents[i] = commit_lookup(walk, &oid);
if (commit->parents[i] == NULL)
@@ -222,14 +222,14 @@ static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawo
commit->out_degree = (unsigned short)parents;
if ((buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL)
- return GIT_EOBJCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse commit. Object is corrupted");
buffer = memchr(buffer, '>', buffer_end - buffer);
if (buffer == NULL)
- return GIT_EOBJCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse commit. Can't find author");
if (git__strtol32(&commit_time, (char *)buffer + 2, NULL, 10) < GIT_SUCCESS)
- return GIT_EOBJCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse commit. Can't parse commit time");
commit->time = (time_t)commit_time;
commit->parsed = 1;
@@ -245,11 +245,11 @@ static int commit_parse(git_revwalk *walk, commit_object *commit)
return GIT_SUCCESS;
if ((error = git_odb_read(&obj, walk->repo->db, &commit->oid)) < GIT_SUCCESS)
- return error;
+ return git__rethrow(error, "Failed to parse commit. Can't read object");
if (obj->raw.type != GIT_OBJ_COMMIT) {
git_odb_object_close(obj);
- return GIT_EOBJTYPE;
+ return git__throw(GIT_EOBJTYPE, "Failed to parse commit. Object is no commit object");
}
error = commit_quick_parse(walk, commit, &obj->raw);
@@ -305,7 +305,7 @@ static int push_commit(git_revwalk *walk, const git_oid *oid, int uninteresting)
commit = commit_lookup(walk, oid);
if (commit == NULL)
- return GIT_ENOTFOUND;
+ return git__throw(GIT_ENOTFOUND, "Failed to push commit. Object not found");
commit->uninteresting = uninteresting;
@@ -483,7 +483,7 @@ int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo)
void git_revwalk_free(git_revwalk *walk)
{
unsigned int i;
- const void *_unused;
+ const void *GIT_UNUSED(_unused);
commit_object *commit;
if (walk == NULL)
@@ -558,7 +558,7 @@ int git_revwalk_next(git_oid *oid, git_revwalk *walk)
void git_revwalk_reset(git_revwalk *walk)
{
- const void *_unused;
+ const void *GIT_UNUSED(_unused);
commit_object *commit;
assert(walk);
diff --git a/src/signature.c b/src/signature.c
index e8014620a..62bd28b9a 100644
--- a/src/signature.c
+++ b/src/signature.c
@@ -115,22 +115,22 @@ static int parse_timezone_offset(const char *buffer, long *offset_out)
}
if (offset_start[0] != '-' && offset_start[0] != '+')
- return GIT_EOBJCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. It doesn't start with '+' or '-'");
if (git__strtol32(&dec_offset, offset_start + 1, &offset_end, 10) < GIT_SUCCESS)
- return GIT_EOBJCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. It isn't a number");
if (offset_end - offset_start != 5)
- return GIT_EOBJCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Invalid length");
hours = dec_offset / 100;
mins = dec_offset % 100;
if (hours > 14) // see http://www.worldtimezone.com/faq.html
- return GIT_EOBJCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Hour value too large");;
if (mins > 59)
- return GIT_EOBJCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Minute value too large");
offset = (hours * 60) + mins;
@@ -157,19 +157,19 @@ int git_signature__parse(git_signature *sig, const char **buffer_out,
line_end = memchr(buffer, '\n', buffer_end - buffer);
if (!line_end)
- return GIT_EOBJCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. No newline found");;
if (buffer + (header_len + 1) > line_end)
- return GIT_EOBJCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Signature too short");
if (memcmp(buffer, header, header_len) != 0)
- return GIT_EOBJCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Expected prefix '%s' doesn't match actual", header);
buffer += header_len;
/* Parse name */
if ((name_end = memchr(buffer, '<', buffer_end - buffer)) == NULL)
- return GIT_EOBJCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Can't find e-mail start");
name_length = name_end - buffer - 1;
sig->name = git__malloc(name_length + 1);
@@ -181,11 +181,11 @@ int git_signature__parse(git_signature *sig, const char **buffer_out,
buffer = name_end + 1;
if (buffer >= line_end)
- return GIT_EOBJCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Ended unexpectedly");
/* Parse email */
if ((email_end = memchr(buffer, '>', buffer_end - buffer)) == NULL)
- return GIT_EOBJCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Can't find e-mail end");
email_length = email_end - buffer;
sig->email = git__malloc(email_length + 1);
@@ -197,10 +197,10 @@ int git_signature__parse(git_signature *sig, const char **buffer_out,
buffer = email_end + 1;
if (buffer >= line_end)
- return GIT_EOBJCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Ended unexpectedly");
if (git__strtol32(&time, buffer, &buffer, 10) < GIT_SUCCESS)
- return GIT_EOBJCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Timestamp isn't a number");
sig->when.time = (time_t)time;
diff --git a/src/tag.c b/src/tag.c
index 148eb280c..849429b7e 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -90,13 +90,13 @@ static int parse_tag_buffer(git_tag *tag, const char *buffer, const char *buffer
int error;
if ((error = git__parse_oid(&tag->target, &buffer, buffer_end, "object ")) < 0)
- return error;
+ return git__rethrow(error, "Failed to parse tag. Object field invalid");
if (buffer + 5 >= buffer_end)
- return GIT_EOBJCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Object too short");
if (memcmp(buffer, "type ", 5) != 0)
- return GIT_EOBJCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Type field not found");
buffer += 5;
tag->type = GIT_OBJ_BAD;
@@ -105,7 +105,7 @@ static int parse_tag_buffer(git_tag *tag, const char *buffer, const char *buffer
size_t type_length = strlen(tag_types[i]);
if (buffer + type_length >= buffer_end)
- return GIT_EOBJCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Object too short");
if (memcmp(buffer, tag_types[i], type_length) == 0) {
tag->type = i;
@@ -115,18 +115,19 @@ static int parse_tag_buffer(git_tag *tag, const char *buffer, const char *buffer
}
if (tag->type == GIT_OBJ_BAD)
- return GIT_EOBJCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Invalid object type");
if (buffer + 4 >= buffer_end)
- return GIT_EOBJCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Object too short");
if (memcmp(buffer, "tag ", 4) != 0)
- return GIT_EOBJCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Tag field not found");
+
buffer += 4;
search = memchr(buffer, '\n', buffer_end - buffer);
if (search == NULL)
- return GIT_EOBJCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Object too short");
text_len = search - buffer;
@@ -218,7 +219,7 @@ static int tag_create(
}
if (!git_odb_exists(repo->db, target))
- return GIT_ENOTFOUND;
+ return git__throw(GIT_ENOTFOUND, "Failed to create tag. Object to tag doesn't exist");
/* Try to find out what the type is */
if (target_type == GIT_OBJ_ANY) {
diff --git a/src/tree.c b/src/tree.c
index 64f81d780..6f9fc8607 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -151,15 +151,15 @@ static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buf
return GIT_ENOMEM;
if (git__strtol32((long *)&entry->attr, buffer, &buffer, 8) < GIT_SUCCESS)
- return GIT_EOBJCORRUPTED;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tree. Can't parse attributes");
if (*buffer++ != ' ') {
- error = GIT_EOBJCORRUPTED;
+ error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse tree. Object it corrupted");
break;
}
if (memchr(buffer, 0, buffer_end - buffer) == NULL) {
- error = GIT_EOBJCORRUPTED;
+ error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse tree. Object it corrupted");
break;
}
@@ -274,7 +274,7 @@ int git_tree_create_fromindex(git_oid *oid, git_index *index)
int error;
if (index->repository == NULL)
- return GIT_EBAREINDEX;
+ return git__throw(GIT_EBAREINDEX, "Failed to create tree. The index file is not backed up by an existing repository");
error = write_index(oid, index, "", 0, 0, git_index_entrycount(index));
return (error < GIT_SUCCESS) ? error : GIT_SUCCESS;
@@ -343,7 +343,7 @@ int git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, con
assert(bld && id && filename);
if (!valid_attributes(attributes))
- return GIT_ERROR;
+ return git__throw(GIT_ERROR, "Failed to insert entry. Invalid atrributes");
if ((pos = git_vector_bsearch2(&bld->entries, entry_search_cmp, filename)) != GIT_ENOTFOUND) {
entry = git_vector_get(&bld->entries, pos);
@@ -400,7 +400,7 @@ int git_treebuilder_remove(git_treebuilder *bld, const char *filename)
git_tree_entry *remove_ptr = (git_tree_entry *)git_treebuilder_get(bld, filename);
if (remove_ptr == NULL || remove_ptr->removed)
- return GIT_ENOTFOUND;
+ return git__throw(GIT_ENOTFOUND, "Failed to remove entry. File isn't in the tree");
remove_ptr->removed = 1;
bld->entry_count--;
@@ -424,13 +424,14 @@ int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *b
if (entry->removed)
continue;
- size += (entry->attr > 0x7FF) ? 7 : 6;
+ snprintf(filemode, sizeof(filemode), "%o ", entry->attr);
+ size += strlen(filemode);
size += entry->filename_len + 1;
size += GIT_OID_RAWSZ;
}
if ((error = git_odb_open_wstream(&stream, git_repository_database(repo), size, GIT_OBJ_TREE)) < GIT_SUCCESS)
- return error;
+ return git__rethrow(error, "Failed to write tree. Can't open write stream");
for (i = 0; i < bld->entries.length; ++i) {
git_tree_entry *entry = bld->entries.contents[i];
diff --git a/src/util.c b/src/util.c
index 55a7ab2a9..9499ceadf 100644
--- a/src/util.c
+++ b/src/util.c
@@ -104,6 +104,20 @@ int git__fmt(char *buf, size_t buf_sz, const char *fmt, ...)
return r;
}
+void git__strntolower(char *str, int len)
+{
+ int i;
+
+ for (i = 0; i < len; ++i) {
+ str[i] = tolower(str[i]);
+ }
+}
+
+void git__strtolower(char *str)
+{
+ git__strntolower(str, strlen(str));
+}
+
int git__prefixcmp(const char *str, const char *prefix)
{
for (;;) {
diff --git a/src/util.h b/src/util.h
index 3c606493f..e5a2ebe5f 100644
--- a/src/util.h
+++ b/src/util.h
@@ -6,16 +6,60 @@
#define MSB(x, bits) ((x) & (~0ULL << (bitsizeof(x) - (bits))))
/*
- * Don't wrap malloc/calloc.
- * Use the default versions in glibc, and make
- * sure that any methods that allocate memory
- * return a GIT_ENOMEM error when allocation
- * fails.
+ * Custom memory allocation wrappers
+ * that set error code and error message
+ * on allocation failure
*/
-#define git__malloc malloc
-#define git__calloc calloc
-#define git__realloc realloc
-#define git__strdup strdup
+GIT_INLINE(void *) git__malloc(size_t len)
+{
+ void *ptr = malloc(len);
+ if (!ptr)
+ git__throw(GIT_ENOMEM, "Out of memory. Failed to allocate %d bytes.", (int)len);
+ return ptr;
+}
+
+GIT_INLINE(void *) git__calloc(size_t nelem, size_t elsize)
+{
+ void *ptr = calloc(nelem, elsize);
+ if (!ptr)
+ git__throw(GIT_ENOMEM, "Out of memory. Failed to allocate %d bytes.", (int)elsize);
+ return ptr;
+}
+
+GIT_INLINE(char *) git__strdup(const char *str)
+{
+ char *ptr = strdup(str);
+ if (!ptr)
+ git__throw(GIT_ENOMEM, "Out of memory. Failed to duplicate string");
+ return ptr;
+}
+
+GIT_INLINE(char *) git__strndup(const char *str, size_t n)
+{
+ size_t length;
+ char *ptr;
+
+ length = strlen(str);
+ if (n < length)
+ length = n;
+
+ ptr = malloc(length + 1);
+ if (!ptr)
+ git__throw(GIT_ENOMEM, "Out of memory. Failed to duplicate string");
+
+ memcpy(ptr, str, length);
+ ptr[length] = 0;
+
+ return ptr;
+}
+
+GIT_INLINE(void *) git__realloc(void *ptr, size_t size)
+{
+ void *new_ptr = realloc(ptr, size);
+ if (!new_ptr)
+ git__throw(GIT_ENOMEM, "Out of memory. Failed to allocate %d bytes.", (int)size);
+ return new_ptr;
+}
extern int git__fmt(char *, size_t, const char *, ...)
GIT_FORMAT_PRINTF(3, 4);
@@ -98,6 +142,9 @@ GIT_INLINE(int) git__is_sizet(git_off_t p)
extern char *git__strtok(char *output, char *src, char *delimit);
extern char *git__strtok_keep(char *output, char *src, char *delimit);
+extern void git__strntolower(char *str, int len);
+extern void git__strtolower(char *str);
+
#define STRLEN(str) (sizeof(str) - 1)
#define GIT_OID_LINE_LENGTH(header) (STRLEN(header) + 1 + GIT_OID_HEXSZ + 1)
diff --git a/src/vector.c b/src/vector.c
index d0b0c5c56..1ddc26e3e 100644
--- a/src/vector.c
+++ b/src/vector.c
@@ -106,7 +106,7 @@ int git_vector_bsearch2(git_vector *v, git_vector_cmp key_lookup, const void *ke
/* need comparison function to sort the vector */
if (v->_cmp == NULL)
- return GIT_ENOTFOUND;
+ return git__throw(GIT_ENOTFOUND, "Can't sort vector. No comparison function set");
git_vector_sort(v);
@@ -114,7 +114,7 @@ int git_vector_bsearch2(git_vector *v, git_vector_cmp key_lookup, const void *ke
if (find != NULL)
return (int)(find - v->contents);
- return GIT_ENOTFOUND;
+ return git__throw(GIT_ENOTFOUND, "Can't find element");
}
int git_vector_search2(git_vector *v, git_vector_cmp key_lookup, const void *key)
@@ -128,7 +128,7 @@ int git_vector_search2(git_vector *v, git_vector_cmp key_lookup, const void *key
return i;
}
- return GIT_ENOTFOUND;
+ return git__throw(GIT_ENOTFOUND, "Can't find element");
}
static int strict_comparison(const void *a, const void *b)
@@ -143,9 +143,6 @@ int git_vector_search(git_vector *v, const void *entry)
int git_vector_bsearch(git_vector *v, const void *key)
{
- if (v->_cmp == NULL)
- return GIT_ENOTFOUND;
-
return git_vector_bsearch2(v, v->_cmp, key);
}
@@ -156,7 +153,7 @@ int git_vector_remove(git_vector *v, unsigned int idx)
assert(v);
if (idx >= v->length || v->length == 0)
- return GIT_ENOTFOUND;
+ return git__throw(GIT_ENOTFOUND, "Can't remove element. Index out of bounds");
for (i = idx; i < v->length - 1; ++i)
v->contents[i] = v->contents[i + 1];
diff --git a/src/win32/pthread.c b/src/win32/pthread.c
index f47364a76..7e17b6bdf 100644
--- a/src/win32/pthread.c
+++ b/src/win32/pthread.c
@@ -48,16 +48,15 @@ int pthread_join(pthread_t thread, void **value_ptr)
int pthread_mutex_init(pthread_mutex_t *GIT_RESTRICT mutex,
const pthread_mutexattr_t *GIT_RESTRICT GIT_UNUSED(mutexattr))
{
- GIT_UNUSED_ARG(mutexattr);
+ GIT_UNUSED_ARG(mutexattr);
InitializeCriticalSection(mutex);
return 0;
}
int pthread_mutex_destroy(pthread_mutex_t *mutex)
{
- int ret;
- ret = CloseHandle(mutex);
- return -(!ret);
+ DeleteCriticalSection(mutex);
+ return 0;
}
int pthread_mutex_lock(pthread_mutex_t *mutex)
diff --git a/tests/NAMING b/tests/NAMING
index ab425e23e..df07b7d51 100644
--- a/tests/NAMING
+++ b/tests/NAMING
@@ -23,14 +23,24 @@ Categories
04__: Parsing and loading commit data
-05__: To be described
+05__: Revision walking
-06__: To be described
+06__: Index reading, writing and searching
-07__: To be described
+07__: Tests for the internal hashtable code
-08__: To be described
+08__: Tag reading and writing
-09__: To be described
+09__: Reading tree objects
-10__: Symbolic, loose and packed references reading and writing.
\ No newline at end of file
+10__: Symbolic, loose and packed references reading and writing.
+
+11__: SQLite backend
+
+12__: Repository init and opening
+
+13__: Threads, empty as of now
+
+14__: Redis backend
+
+15__: Configuration parsing
diff --git a/tests/resources/config/config0 b/tests/resources/config/config0
new file mode 100644
index 000000000..85235c501
Binary files /dev/null and b/tests/resources/config/config0 differ
diff --git a/tests/resources/config/config1 b/tests/resources/config/config1
new file mode 100644
index 000000000..211dc9e7d
Binary files /dev/null and b/tests/resources/config/config1 differ
diff --git a/tests/resources/config/config2 b/tests/resources/config/config2
new file mode 100644
index 000000000..60a389827
Binary files /dev/null and b/tests/resources/config/config2 differ
diff --git a/tests/resources/config/config3 b/tests/resources/config/config3
new file mode 100644
index 000000000..44a5e50ea
Binary files /dev/null and b/tests/resources/config/config3 differ
diff --git a/tests/resources/config/config4 b/tests/resources/config/config4
new file mode 100644
index 000000000..741fa0ffd
Binary files /dev/null and b/tests/resources/config/config4 differ
diff --git a/tests/resources/config/config5 b/tests/resources/config/config5
new file mode 100644
index 000000000..8ab60ccec
Binary files /dev/null and b/tests/resources/config/config5 differ
diff --git a/tests/t04-commit.c b/tests/t04-commit.c
index bcc0417c8..36f3e66b5 100644
--- a/tests/t04-commit.c
+++ b/tests/t04-commit.c
@@ -368,7 +368,7 @@ BEGIN_TEST(details0, "query the details on a parsed commit")
const char *message, *message_short;
git_time_t commit_time;
unsigned int parents, p;
- git_commit *parent;
+ git_commit *parent = NULL, *old_parent = NULL;
git_oid_mkstr(&id, commit_ids[i]);
@@ -390,11 +390,19 @@ BEGIN_TEST(details0, "query the details on a parsed commit")
must_be_true(commit_time > 0);
must_be_true(parents <= 2);
for (p = 0;p < parents;p++) {
+ if (old_parent != NULL)
+ git_commit_close(old_parent);
+
+ old_parent = parent;
must_pass(git_commit_parent(&parent, commit, p));
must_be_true(parent != NULL);
must_be_true(git_commit_author(parent) != NULL); // is it really a commit?
}
+ git_commit_close(old_parent);
+ git_commit_close(parent);
+
must_fail(git_commit_parent(&parent, commit, parents));
+ git_commit_close(commit);
}
git_repository_free(repo);
@@ -462,9 +470,76 @@ BEGIN_TEST(write0, "write a new commit object from memory to disk")
must_pass(remove_loose_object(REPOSITORY_FOLDER, (git_object *)commit));
+ git_commit_close(commit);
git_repository_free(repo);
END_TEST
+#define ROOT_COMMIT_MESSAGE "This is a root commit\n\
+This is a root commit and should be the only one in this branch\n"
+
+BEGIN_TEST(root0, "create a root commit")
+ git_repository *repo;
+ git_commit *commit;
+ git_oid tree_id, commit_id;
+ const git_oid *branch_oid;
+ const git_signature *author, *committer;
+ const char *branch_name = "refs/heads/root-commit-branch";
+ git_reference *head, *branch;
+ char *head_old;
+
+ must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
+
+ git_oid_mkstr(&tree_id, tree_oid);
+
+ /* create signatures */
+ committer = git_signature_new(COMMITTER_NAME, COMMITTER_EMAIL, 123456789, 60);
+ must_be_true(committer != NULL);
+
+ author = git_signature_new(COMMITTER_NAME, COMMITTER_EMAIL, 987654321, 90);
+ must_be_true(author != NULL);
+
+ /* First we need to update HEAD so it points to our non-existant branch */
+ must_pass(git_reference_lookup(&head, repo, "HEAD"));
+ must_be_true(git_reference_type(head) == GIT_REF_SYMBOLIC);
+ head_old = git__strdup(git_reference_target(head));
+ must_be_true(head_old != NULL);
+
+ must_pass(git_reference_set_target(head, branch_name));
+
+ must_pass(git_commit_create_v(
+ &commit_id, /* out id */
+ repo,
+ "HEAD",
+ author,
+ committer,
+ ROOT_COMMIT_MESSAGE,
+ &tree_id,
+ 0));
+
+ git_signature_free((git_signature *)committer);
+ git_signature_free((git_signature *)author);
+
+ /*
+ * The fact that creating a commit works has already been
+ * tested. Here we just make sure it's our commit and that it was
+ * written as a root commit.
+ */
+ must_pass(git_commit_lookup(&commit, repo, &commit_id));
+ must_be_true(git_commit_parentcount(commit) == 0);
+ must_pass(git_reference_lookup(&branch, repo, branch_name));
+ branch_oid = git_reference_oid(branch);
+ must_pass(git_oid_cmp(branch_oid, &commit_id));
+ must_be_true(!strcmp(git_commit_message(commit), ROOT_COMMIT_MESSAGE));
+
+ /* Remove the data we just added to the repo */
+ must_pass(git_reference_lookup(&head, repo, "HEAD"));
+ must_pass(git_reference_set_target(head, head_old));
+ must_pass(git_reference_delete(branch));
+ must_pass(remove_loose_object(REPOSITORY_FOLDER, (git_object *)commit));
+ free(head_old);
+ git_commit_close(commit);
+ git_repository_free(repo);
+END_TEST
BEGIN_SUITE(commit)
ADD_TEST(parse0);
@@ -474,4 +549,6 @@ BEGIN_SUITE(commit)
ADD_TEST(write0);
//ADD_TEST(write1);
+
+ ADD_TEST(root0);
END_SUITE
diff --git a/tests/t07-hashtable.c b/tests/t07-hashtable.c
index 597136965..0b362cafd 100644
--- a/tests/t07-hashtable.c
+++ b/tests/t07-hashtable.c
@@ -155,7 +155,7 @@ BEGIN_TEST(tableit0, "iterate through all the contents of the table")
const int objects_n = 32;
int i;
table_item *objects, *ob;
- const void *_unused;
+ const void *GIT_UNUSED(_unused);
git_hashtable *table = NULL;
diff --git a/tests/t09-tree.c b/tests/t09-tree.c
index bd88642fa..af992fdb3 100644
--- a/tests/t09-tree.c
+++ b/tests/t09-tree.c
@@ -32,6 +32,7 @@ static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd";
static const char *blob_oid = "fa49b077972391ad58037050f2a75f74e3671e92";
static const char *first_tree = "181037049a54a1eb5fab404658a3a250b44335d7";
static const char *second_tree = "f60079018b664e4e79329a7ef9559c8d9e0378d1";
+static const char *third_tree = "eb86d8b81d6adbd5290a935d6c9976882de98488";
#if 0
static int print_tree(git_repository *repo, const git_oid *tree_oid, int depth)
@@ -82,6 +83,7 @@ BEGIN_TEST(read0, "acces randomly the entries on a loaded tree")
must_be_true(git_tree_entry_byindex(tree, 3) == NULL);
must_be_true(git_tree_entry_byindex(tree, -1) == NULL);
+ git_tree_close(tree);
git_repository_free(repo);
END_TEST
@@ -102,7 +104,9 @@ BEGIN_TEST(read1, "read a tree from the repository")
/* GH-86: git_object_lookup() should also check the type if the object comes from the cache */
must_be_true(git_object_lookup(&obj, repo, &id, GIT_OBJ_TREE) == 0);
+ git_object_close(obj);
must_be_true(git_object_lookup(&obj, repo, &id, GIT_OBJ_BLOB) == GIT_EINVALIDTYPE);
+ git_object_close(obj);
entry = git_tree_entry_byname(tree, "README");
must_be_true(entry != NULL);
@@ -111,6 +115,8 @@ BEGIN_TEST(read1, "read a tree from the repository")
must_pass(git_tree_entry_2object(&obj, repo, entry));
+ git_object_close(obj);
+ git_tree_close(tree);
git_repository_free(repo);
END_TEST
@@ -148,9 +154,50 @@ BEGIN_TEST(write2, "write a tree from a memory")
must_pass(git_treebuilder_write(&rid,repo,builder));
must_be_true(git_oid_cmp(&rid, &id2) == 0);
+
+ git_treebuilder_free(builder);
+ git_tree_close(tree);
close_temp_repo(repo);
END_TEST
+BEGIN_TEST(write3, "write a hierarchical tree from a memory")
+ git_repository *repo;
+ git_treebuilder *builder;
+ git_tree *tree;
+ git_oid id, bid, subtree_id, id2, id3;
+ git_oid id_hiearar;
+
+ must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
+ git_oid_mkstr(&id, first_tree);
+ git_oid_mkstr(&id2, second_tree);
+ git_oid_mkstr(&id3, third_tree);
+ git_oid_mkstr(&bid, blob_oid);
+
+ //create subtree
+ must_pass(git_treebuilder_create(&builder, NULL));
+ must_pass(git_treebuilder_insert(NULL,builder,"new.txt",&bid,0100644));
+ must_pass(git_treebuilder_write(&subtree_id,repo,builder));
+ git_treebuilder_free(builder);
+
+ // create parent tree
+ must_pass(git_tree_lookup(&tree, repo, &id));
+ must_pass(git_treebuilder_create(&builder, tree));
+ must_pass(git_treebuilder_insert(NULL,builder,"new",&subtree_id,040000));
+ must_pass(git_treebuilder_write(&id_hiearar,repo,builder));
+ git_treebuilder_free(builder);
+ git_tree_close(tree);
+
+ must_be_true(git_oid_cmp(&id_hiearar, &id3) == 0);
+
+ // check data is correct
+ must_pass(git_tree_lookup(&tree, repo, &id_hiearar));
+ must_be_true(2 == git_tree_entrycount(tree));
+ git_tree_close(tree);
+
+ close_temp_repo(repo);
+
+END_TEST
+
BEGIN_SUITE(tree)
//ADD_TEST(print0);
ADD_TEST(read0);
@@ -158,5 +205,6 @@ BEGIN_SUITE(tree)
//ADD_TEST(write0);
//ADD_TEST(write1);
ADD_TEST(write2);
+ ADD_TEST(write3);
END_SUITE
diff --git a/tests/t10-refs.c b/tests/t10-refs.c
index a6a560193..ee006a8ce 100644
--- a/tests/t10-refs.c
+++ b/tests/t10-refs.c
@@ -51,6 +51,7 @@ BEGIN_TEST(readtag0, "lookup a loose tag reference")
git__joinpath(ref_name_from_tag_name, GIT_REFS_TAGS_DIR, git_tag_name((git_tag *)object));
must_be_true(strcmp(ref_name_from_tag_name, loose_tag_ref_name) == 0);
+ git_object_close(object);
git_repository_free(repo);
END_TEST
@@ -91,6 +92,7 @@ BEGIN_TEST(readsym0, "lookup a symbolic reference")
git_oid_mkstr(&id, current_master_tip);
must_be_true(git_oid_cmp(&id, git_object_id(object)) == 0);
+ git_object_close(object);
git_repository_free(repo);
END_TEST
@@ -117,6 +119,7 @@ BEGIN_TEST(readsym1, "lookup a nested symbolic reference")
git_oid_mkstr(&id, current_master_tip);
must_be_true(git_oid_cmp(&id, git_object_id(object)) == 0);
+ git_object_close(object);
git_repository_free(repo);
END_TEST
@@ -175,6 +178,7 @@ BEGIN_TEST(readpacked0, "lookup a packed reference")
must_be_true(object != NULL);
must_be_true(git_object_type(object) == GIT_OBJ_COMMIT);
+ git_object_close(object);
git_repository_free(repo);
END_TEST
@@ -194,7 +198,7 @@ END_TEST
BEGIN_TEST(create0, "create a new symbolic reference")
git_reference *new_reference, *looked_up_ref, *resolved_ref;
- git_repository *repo;
+ git_repository *repo, *repo2;
git_oid id;
char ref_path[GIT_PATH_MAX];
@@ -202,7 +206,7 @@ BEGIN_TEST(create0, "create a new symbolic reference")
git_oid_mkstr(&id, current_master_tip);
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
+ must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
/* Retrieve the physical path to the symbolic ref for further cleaning */
git__joinpath(ref_path, repo->path_repository, new_head_tracker);
@@ -226,14 +230,13 @@ BEGIN_TEST(create0, "create a new symbolic reference")
git_repository_free(repo);
/* Similar test with a fresh new repository */
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
+ must_pass(git_repository_open(&repo2, TEMP_REPO_FOLDER));
- must_pass(git_reference_lookup(&looked_up_ref, repo, new_head_tracker));
+ must_pass(git_reference_lookup(&looked_up_ref, repo2, new_head_tracker));
must_pass(git_reference_resolve(&resolved_ref, looked_up_ref));
must_be_true(git_oid_cmp(&id, git_reference_oid(resolved_ref)) == 0);
- git_reference_delete(looked_up_ref);
- git_repository_free(repo);
+ close_temp_repo(repo2);
END_TEST
BEGIN_TEST(create1, "create a deep symbolic reference")
@@ -246,7 +249,7 @@ BEGIN_TEST(create1, "create a deep symbolic reference")
git_oid_mkstr(&id, current_master_tip);
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
+ must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
git__joinpath(ref_path, repo->path_repository, new_head_tracker);
must_pass(git_reference_create_symbolic(&new_reference, repo, new_head_tracker, current_head_target));
@@ -254,13 +257,12 @@ BEGIN_TEST(create1, "create a deep symbolic reference")
must_pass(git_reference_resolve(&resolved_ref, looked_up_ref));
must_be_true(git_oid_cmp(&id, git_reference_oid(resolved_ref)) == 0);
- git_reference_delete(looked_up_ref);
- git_repository_free(repo);
+ close_temp_repo(repo);
END_TEST
BEGIN_TEST(create2, "create a new OID reference")
git_reference *new_reference, *looked_up_ref;
- git_repository *repo;
+ git_repository *repo, *repo2;
git_oid id;
char ref_path[GIT_PATH_MAX];
@@ -268,7 +270,7 @@ BEGIN_TEST(create2, "create a new OID reference")
git_oid_mkstr(&id, current_master_tip);
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
+ must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
/* Retrieve the physical path to the symbolic ref for further cleaning */
git__joinpath(ref_path, repo->path_repository, new_head);
@@ -288,13 +290,12 @@ BEGIN_TEST(create2, "create a new OID reference")
git_repository_free(repo);
/* Similar test with a fresh new repository */
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
+ must_pass(git_repository_open(&repo2, TEMP_REPO_FOLDER));
- must_pass(git_reference_lookup(&looked_up_ref, repo, new_head));
+ must_pass(git_reference_lookup(&looked_up_ref, repo2, new_head));
must_be_true(git_oid_cmp(&id, git_reference_oid(looked_up_ref)) == 0);
- git_reference_delete(looked_up_ref);
- git_repository_free(repo);
+ close_temp_repo(repo2);
END_TEST
BEGIN_TEST(create3, "Can not create a new OID reference which targets at an unknown id")
@@ -325,7 +326,7 @@ BEGIN_TEST(overwrite0, "Overwrite an existing symbolic reference")
git_reference *ref, *branch_ref;
git_repository *repo;
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
+ must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
/* The target needds to exist and we need to check the name has changed */
must_pass(git_reference_create_symbolic(&branch_ref, repo, ref_branch_name, ref_master_name));
@@ -344,9 +345,7 @@ BEGIN_TEST(overwrite0, "Overwrite an existing symbolic reference")
must_be_true(git_reference_type(ref) & GIT_REF_SYMBOLIC);
must_be_true(!strcmp(git_reference_target(ref), ref_master_name));
- must_pass(git_reference_delete(ref));
- must_pass(git_reference_delete(branch_ref));
- git_repository_free(repo);
+ close_temp_repo(repo);
END_TEST
BEGIN_TEST(overwrite1, "Overwrite an existing object id reference")
@@ -354,7 +353,7 @@ BEGIN_TEST(overwrite1, "Overwrite an existing object id reference")
git_repository *repo;
git_oid id;
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
+ must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
must_pass(git_reference_lookup(&ref, repo, ref_master_name));
must_be_true(ref->type & GIT_REF_OID);
@@ -375,8 +374,7 @@ BEGIN_TEST(overwrite1, "Overwrite an existing object id reference")
must_pass(git_reference_lookup(&ref, repo, ref_name));
must_be_true(!git_oid_cmp(&id, git_reference_oid(ref)));
- git_reference_delete(ref);
- git_repository_free(repo);
+ close_temp_repo(repo);
END_TEST
BEGIN_TEST(overwrite2, "Overwrite an existing object id reference with a symbolic one")
@@ -384,7 +382,7 @@ BEGIN_TEST(overwrite2, "Overwrite an existing object id reference with a symboli
git_repository *repo;
git_oid id;
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
+ must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
must_pass(git_reference_lookup(&ref, repo, ref_master_name));
must_be_true(ref->type & GIT_REF_OID);
@@ -399,8 +397,7 @@ BEGIN_TEST(overwrite2, "Overwrite an existing object id reference with a symboli
must_be_true(git_reference_type(ref) & GIT_REF_SYMBOLIC);
must_be_true(!strcmp(git_reference_target(ref), ref_master_name));
- git_reference_delete(ref);
- git_repository_free(repo);
+ close_temp_repo(repo);
END_TEST
BEGIN_TEST(overwrite3, "Overwrite an existing symbolic reference with an object id one")
@@ -408,7 +405,7 @@ BEGIN_TEST(overwrite3, "Overwrite an existing symbolic reference with an object
git_repository *repo;
git_oid id;
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
+ must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
must_pass(git_reference_lookup(&ref, repo, ref_master_name));
must_be_true(ref->type & GIT_REF_OID);
@@ -425,8 +422,7 @@ BEGIN_TEST(overwrite3, "Overwrite an existing symbolic reference with an object
must_be_true(git_reference_type(ref) & GIT_REF_OID);
must_be_true(!git_oid_cmp(git_reference_oid(ref), &id));
- git_reference_delete(ref);
- git_repository_free(repo);
+ close_temp_repo(repo);
END_TEST
BEGIN_TEST(pack0, "create a packfile for an empty folder")
diff --git a/tests/t15-config.c b/tests/t15-config.c
new file mode 100644
index 000000000..50d473580
--- /dev/null
+++ b/tests/t15-config.c
@@ -0,0 +1,170 @@
+/*
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * In addition to the permissions in the GNU General Public License,
+ * the authors give you unlimited permission to link the compiled
+ * version of this file into combinations with other programs,
+ * and to distribute those combinations without any restriction
+ * coming from the use of this file. (The General Public License
+ * restrictions do apply in other respects; for example, they cover
+ * modification of the file, and distribution when not linked into
+ * a combined executable.)
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#include "test_lib.h"
+#include "test_helpers.h"
+
+#include
+
+#define CONFIG_BASE TEST_RESOURCES "/config"
+
+/*
+ * This one is so we know the code isn't completely broken
+ */
+BEGIN_TEST(config0, "read a simple configuration")
+ git_config *cfg;
+ int i;
+
+ must_pass(git_config_open_bare(&cfg, CONFIG_BASE "/config0"));
+ must_pass(git_config_get_int(cfg, "core.repositoryformatversion", &i));
+ must_be_true(i == 0);
+ must_pass(git_config_get_bool(cfg, "core.filemode", &i));
+ must_be_true(i == 1);
+ must_pass(git_config_get_bool(cfg, "core.bare", &i));
+ must_be_true(i == 0);
+ must_pass(git_config_get_bool(cfg, "core.logallrefupdates", &i));
+ must_be_true(i == 1);
+
+ git_config_free(cfg);
+END_TEST
+
+/*
+ * [this "that"] and [this "That] are different namespaces. Make sure
+ * each returns the correct one.
+ */
+BEGIN_TEST(config1, "case sensitivity")
+ git_config *cfg;
+ int i;
+ const char *str;
+
+ must_pass(git_config_open_bare(&cfg, CONFIG_BASE "/config1"));
+
+ must_pass(git_config_get_string(cfg, "this.that.other", &str));
+ must_be_true(!strcmp(str, "true"));
+ must_pass(git_config_get_string(cfg, "this.That.other", &str));
+ must_be_true(!strcmp(str, "yes"));
+
+ must_pass(git_config_get_bool(cfg, "this.that.other", &i));
+ must_be_true(i == 1);
+ must_pass(git_config_get_bool(cfg, "this.That.other", &i));
+ must_be_true(i == 1);
+
+ /* This one doesn't exist */
+ must_fail(git_config_get_bool(cfg, "this.thaT.other", &i));
+
+ git_config_free(cfg);
+END_TEST
+
+/*
+ * If \ is the last non-space character on the line, we read the next
+ * one, separating each line with SP.
+ */
+BEGIN_TEST(config2, "parse a multiline value")
+ git_config *cfg;
+ const char *str;
+
+ must_pass(git_config_open_bare(&cfg, CONFIG_BASE "/config2"));
+
+ must_pass(git_config_get_string(cfg, "this.That.and", &str));
+ must_be_true(!strcmp(str, "one one one two two three three"));
+
+ git_config_free(cfg);
+END_TEST
+
+/*
+ * This kind of subsection declaration is case-insensitive
+ */
+BEGIN_TEST(config3, "parse a [section.subsection] header")
+ git_config *cfg;
+ const char *str;
+
+ must_pass(git_config_open_bare(&cfg, CONFIG_BASE "/config3"));
+
+ must_pass(git_config_get_string(cfg, "section.subsection.var", &str));
+ must_be_true(!strcmp(str, "hello"));
+
+ /* Avoid a false positive */
+ str = "nohello";
+ must_pass(git_config_get_string(cfg, "section.subSectIon.var", &str));
+ must_be_true(!strcmp(str, "hello"));
+
+ git_config_free(cfg);
+END_TEST
+
+BEGIN_TEST(config4, "a variable name on its own is valid")
+ git_config *cfg;
+const char *str;
+int i;
+
+ must_pass(git_config_open_bare(&cfg, CONFIG_BASE "/config4"));
+
+ must_pass(git_config_get_string(cfg, "some.section.variable", &str));
+ must_be_true(str == NULL);
+
+ must_pass(git_config_get_bool(cfg, "some.section.variable", &i));
+ must_be_true(i == 1);
+
+
+ git_config_free(cfg);
+END_TEST
+
+BEGIN_TEST(config5, "test number suffixes")
+ git_config *cfg;
+ long int i;
+
+ must_pass(git_config_open_bare(&cfg, CONFIG_BASE "/config5"));
+
+ must_pass(git_config_get_long(cfg, "number.simple", &i));
+ must_be_true(i == 1);
+
+ must_pass(git_config_get_long(cfg, "number.k", &i));
+ must_be_true(i == 1 * 1024);
+
+ must_pass(git_config_get_long(cfg, "number.kk", &i));
+ must_be_true(i == 1 * 1024);
+
+ must_pass(git_config_get_long(cfg, "number.m", &i));
+ must_be_true(i == 1 * 1024 * 1024);
+
+ must_pass(git_config_get_long(cfg, "number.mm", &i));
+ must_be_true(i == 1 * 1024 * 1024);
+
+ must_pass(git_config_get_long(cfg, "number.g", &i));
+ must_be_true(i == 1 * 1024 * 1024 * 1024);
+
+ must_pass(git_config_get_long(cfg, "number.gg", &i));
+ must_be_true(i == 1 * 1024 * 1024 * 1024);
+
+ git_config_free(cfg);
+END_TEST
+
+
+BEGIN_SUITE(config)
+ ADD_TEST(config0);
+ ADD_TEST(config1);
+ ADD_TEST(config2);
+ ADD_TEST(config3);
+ ADD_TEST(config4);
+ ADD_TEST(config5);
+END_SUITE
diff --git a/tests/test_lib.c b/tests/test_lib.c
index c9c6141c6..5778404c1 100755
--- a/tests/test_lib.c
+++ b/tests/test_lib.c
@@ -130,6 +130,7 @@ static void free_suite(git_testsuite *ts)
if (ts->list[n])
test_free(ts->list[n]);
+ free(ts->name);
free(ts);
}
diff --git a/tests/test_main.c b/tests/test_main.c
index 102688ce1..0f5f16a26 100644
--- a/tests/test_main.c
+++ b/tests/test_main.c
@@ -44,6 +44,7 @@ DECLARE_SUITE(sqlite);
DECLARE_SUITE(hiredis);
DECLARE_SUITE(repository);
DECLARE_SUITE(threads);
+DECLARE_SUITE(config);
static libgit2_suite suite_methods[]= {
SUITE_NAME(core),
@@ -60,7 +61,8 @@ static libgit2_suite suite_methods[]= {
SUITE_NAME(sqlite),
SUITE_NAME(repository),
SUITE_NAME(threads),
- SUITE_NAME(hiredis)
+ SUITE_NAME(hiredis),
+ SUITE_NAME(config),
};
#define GIT_SUITE_COUNT (ARRAY_SIZE(suite_methods))
diff --git a/wscript b/wscript
index d97538ab0..f4f8da989 100644
--- a/wscript
+++ b/wscript
@@ -65,6 +65,9 @@ def configure(conf):
else:
conf.env.PLATFORM = 'unix'
+ if conf.env.DEST_OS == 'sunos':
+ conf.env.DEFINES += ['NO_VIZ']
+
if conf.options.threadsafe:
if conf.env.PLATFORM == 'unix':
conf.check_cc(lib='pthread', uselib_store='pthread')