Commit 050e8877 by Jakob Pfender

Merge branch 'development' into unmerged

parents e3c7786b c9662061
*.c eol=lf
*.h eol=lf
......@@ -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)** <http://www.openssl.org/>
......@@ -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.
......
......@@ -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
......@@ -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 {
......
/*
* 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
/*
* 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
......@@ -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
*/
......
......@@ -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,26 +193,62 @@ 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 position position of the entry to remove
* @param source_entry new entry object
* @return 0 on success, otherwise an error code
*/
GIT_EXTERN(int) git_index_remove(git_index *index, int position);
GIT_EXTERN(int) git_index_add2(git_index *index, const git_index_entry *source_entry);
/**
* Insert an entry into the index.
* 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; if the index
* already contains an entry for the same path, the entry
* will be updated.
* '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_insert(git_index *index, const git_index_entry *source_entry);
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
* @return 0 on success, otherwise an error code
*/
GIT_EXTERN(int) git_index_remove(git_index *index, int position);
/**
* Get a pointer to one of the entries in the index
......
......@@ -35,6 +35,7 @@
#if defined(__APPLE__) && defined(__MACH__)
# undef GIT_TLS
# define GIT_TLS
#elif defined(__GNUC__) || \
defined(__SUNPRO_C) || \
......
......@@ -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 */
......
......@@ -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;
......
......@@ -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);
......
......@@ -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);
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;
git_mutex_lock(&node->lock);
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;
}
......@@ -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);
......
......@@ -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;
......
......@@ -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__ */
/*
* 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 <ctype.h>
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);
}
#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
/*
* 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 <stdarg.h>
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;
}
......@@ -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);
......
......@@ -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));
......
......@@ -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);
}
......
......@@ -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);
......
......@@ -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);
......
......@@ -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" */
......
......@@ -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 {
......
......@@ -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;
}
......
......@@ -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);
......
......@@ -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;
......
......@@ -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) {
......
......@@ -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];
......
......@@ -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 (;;) {
......
......@@ -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)
......
......@@ -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];
......
......@@ -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)
......
......@@ -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
# This is a test
; of different comments
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
\ No newline at end of file
# This one checks for case sensitivity
[this "that"]
other = true
[this "That"]
other = yes
; This one tests for multiline values
[this "That"]
and = one one one \
two two \
three three
\ No newline at end of file
# A [section.subsection] header is case-insensitive
[section.SuBsection]
var = hello
# A variable name on its own is valid
[some.section]
variable
# Test for number suffixes
[number]
simple = 1
k = 1k
kk = 1K
m = 1m
mm = 1M
g = 1g
gg = 1G
......@@ -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
......@@ -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;
......
......@@ -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
......@@ -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")
......
/*
* 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 <git2.h>
#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
......@@ -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);
}
......
......@@ -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))
......
......@@ -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')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment