Commit 48ecd122 by Vicent Martí

Merge pull request #659 from libgit2/development-merge

New-error-handling
parents 2218fd57 4ef14af9
......@@ -43,6 +43,7 @@ Ramsay Jones
Robert G. Jakabosky
Romain Geissler
Romain Muller
Russell Belfer
Sakari Jokinen
Sam
Sarath Lakshman
......
Error reporting in libgit2
==========================
Error reporting is performed on an explicit `git_error **` argument, which appears at the end of all API calls that can return an error. Yes, this does clutter the API.
When a function fails, an error is set on the error variable **and** returns one of the generic error codes.
~~~c
int git_repository_open(git_repository **repository, const char *path, git_error **error)
{
// perform some opening
if (p_exists(path) < 0) {
giterr_set(error, GITERR_REPOSITORY, "The path '%s' doesn't exist", path);
return GIT_ENOTFOUND;
}
...
if (try_to_parse(path, error) < 0)
return GIT_ERROR;
...
}
~~~
The simple error API
--------------------
- `void giterr_set(git_error **, int, const char *, ...)`: the main function used to set an error. It allocates a new error object and stores it in the passed error pointer. It has no return value. The arguments for `giterr_set` are as follows:
- `git_error **error_ptr`: the pointer where the error will be created.
- `int error_class`: the class for the error. This is **not** an error code: this is an speficic enum that specifies the error family. The point is to map these families 1-1 with Exception types on higher level languages (e.g. GitRepositoryException)
- `const char *error_str, ...`: the error string, with optional formatting arguments
- `void giterr_free(git_error *)`: takes an error and frees it. This function is available in the external API.
- `void giterr_clear(git_error **)`: clears an error previously set in an error pointer, setting it to NULL and calling `giterr_free` on it.
- `void giterr_propagate(git_error **, git_error *)`: moves an error to a given error pointer, handling the case when the error pointer is NULL (in that case the error gets freed, because it cannot be propagated).
The new error code return values
--------------------------------
We are doing this the POSIX way: one error code for each "expected failure", and a generic error code for all the critical failures.
For instance: A reference lookup can have an expected failure (which is when the reference cannot be found), and a critical failure (which could be any of a long list of things that could go wrong, such as the refs packfile being corrupted, a loose ref being written with the wrong permissions, etc). We cannot have distinct error codes for every single error in the library, hence `git_reference_lookup` would return GIT_SUCCESS if the operation was successful, GIT_ENOTFOUND when the reference doesn't exist, and GIT_ERROR when an error happens -- **the error is then detailed in the `git_error` parameter**.
Please be smart when returning error codes. Functions have max two "expected errors", and in most cases only one.
Writing error messages
----------------------
Here are some guidelines when writing error messages:
- Use proper English, and an impersonal or past tenses: *The given path does not exist*, *Failed to lookup object in ODB*
- Use short, direct and objective messages. **One line, max**. libgit2 is a low level library: think that all the messages reported will be thrown as Ruby or Python exceptions. Think how long are common exception messages in those languages.
- **Do not add redundant information to the error message**, specially information that can be infered from the context.
E.g. in `git_repository_open`, do not report a message like "Failed to open repository: path not found". Somebody is
calling that function. If it fails, he already knows that the repository failed to open!
General guidelines for error reporting
--------------------------------------
- We never handle programming errors with these functions. Programming errors are `assert`ed, and when their source is internal, fixed as soon as possible. This is C, people.
Example of programming errors that would **not** be handled: passing NULL to a function that expects a valid pointer; passing a `git_tree` to a function that expects a `git_commit`. All these cases need to be identified with `assert` and fixed asap.
Example of a runtime error: failing to parse a `git_tree` because it contains invalid data. Failing to open a file because it doesn't exist on disk. These errors would be handled, and a `git_error` would be set.
- The `git_error **` argument is always the last in the signature of all API calls. No exceptions.
- When the programmer (or us, internally) doesn't need error handling, he can pass `NULL` to the `git_error **` param. This means that the errors won't be *reported*, but obviously they still will be handled (i.e. the failing function will interrupt and return cleanly). This is transparently handled by `giterr_set`
- `git_error *` **must be initialized to `NULL` before passing its value to a function!!**
~~~c
git_error *err;
git_error *good_error = NULL;
git_foo_func(arg1, arg2, &error); // invalid: `error` is not initialized
git_foo_func2(arg1, arg2, &good_error); // OK!
git_foo_func3(arg1, arg2, NULL); // OK! But no error reporting!
~~~
- Piling up errors is an error! Don't do this! Errors must always be free'd when a function returns.
~~~c
git_error *error = NULL;
git_foo_func1(arg1, &error);
git_foo_func2(arg2, &error); // WRONG! What if func1 failed? `error` would leak!
~~~
- Likewise: do not rethrow errors internally!
~~~c
int git_commit_create(..., git_error **error)
{
if (git_reference_exists("HEAD", error) < 0) {
/* HEAD does not exist; create it so we can commit... */
if (git_reference_create("HEAD", error) < 0) {
/* error could be rethrown */
}
}
- Remember that errors are now allocated, and hence they need to be free'd after they've been used. Failure to do so internally (e.g. in the already seen examples of error piling) will be reported by Valgrind, so we can easily find where are we rethrowing errors.
- Remember that any function that fails **will set an error object**, and that object will be freed.
......@@ -2,7 +2,7 @@ default: all
CC = gcc
CFLAGS += -g
CFLAGS += -I../../include -L../../ -lgit2
CFLAGS += -I../../include -L../../ -lgit2 -lpthread
OBJECTS = \
git2.o \
......
......@@ -3,95 +3,111 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
static int rename_packfile(char *packname, git_indexer *idx)
struct dl_data {
git_remote *remote;
git_off_t *bytes;
git_indexer_stats *stats;
int ret;
int finished;
};
static void *download(void *ptr)
{
char path[GIT_PATH_MAX], oid[GIT_OID_HEXSZ + 1], *slash;
int ret;
strcpy(path, packname);
slash = strrchr(path, '/');
if (!slash)
return GIT_EINVALIDARGS;
memset(oid, 0x0, sizeof(oid));
// The name of the packfile is given by it's hash which you can get
// with git_indexer_hash after the index has been written out to
// disk. Rename the packfile to its "real" name in the same
// directory as it was originally (libgit2 stores it in the folder
// where the packs go, so a rename in place is the right thing to do here
git_oid_fmt(oid, git_indexer_hash(idx));
ret = sprintf(slash + 1, "pack-%s.pack", oid);
if(ret < 0)
return GIT_EOSERR;
printf("Renaming pack to %s\n", path);
return rename(packname, path);
struct dl_data *data = (struct dl_data *)ptr;
// Connect to the remote end specifying that we want to fetch
// information from it.
if (git_remote_connect(data->remote, GIT_DIR_FETCH) < 0) {
data->ret = -1;
goto exit;
}
// Download the packfile and index it. This function updates the
// amount of received data and the indexer stats which lets you
// inform the user about progress.
if (git_remote_download(data->remote, data->bytes, data->stats) < 0) {
data->ret = -1;
goto exit;
}
data->ret = 0;
exit:
data->finished = 1;
pthread_exit(&data->ret);
}
int update_cb(const char *refname, const git_oid *a, const git_oid *b)
{
const char *action;
char a_str[GIT_OID_HEXSZ+1], b_str[GIT_OID_HEXSZ+1];
git_oid_fmt(b_str, b);
b_str[GIT_OID_HEXSZ] = '\0';
if (git_oid_iszero(a)) {
printf("[new] %.20s %s\n", b_str, refname);
} else {
git_oid_fmt(a_str, a);
a_str[GIT_OID_HEXSZ] = '\0';
printf("[updated] %.10s..%.10s %s\n", a_str, b_str, refname);
}
return 0;
}
int fetch(git_repository *repo, int argc, char **argv)
{
git_remote *remote = NULL;
git_indexer *idx = NULL;
git_off_t bytes = 0;
git_indexer_stats stats;
int error;
char *packname = NULL;
pthread_t worker;
struct dl_data data;
// Get the remote and connect to it
// Figure out whether it's a named remote or a URL
printf("Fetching %s\n", argv[1]);
error = git_remote_new(&remote, repo, argv[1], NULL);
if (error < GIT_SUCCESS)
return error;
error = git_remote_connect(remote, GIT_DIR_FETCH);
if (error < GIT_SUCCESS)
return error;
// Download the packfile from the server. As we don't know its hash
// yet, it will get a temporary filename
error = git_remote_download(&packname, remote);
if (error < GIT_SUCCESS)
return error;
// No error and a NULL packname means no packfile was needed
if (packname != NULL) {
printf("The packname is %s\n", packname);
// Create a new instance indexer
error = git_indexer_new(&idx, packname);
if (error < GIT_SUCCESS)
return error;
// This should be run in paralel, but it'd be too complicated for the example
error = git_indexer_run(idx, &stats);
if (error < GIT_SUCCESS)
return error;
printf("Received %d objects\n", stats.total);
// Write the index file. The index will be stored with the
// correct filename
error = git_indexer_write(idx);
if (error < GIT_SUCCESS)
return error;
error = rename_packfile(packname, idx);
if (error < GIT_SUCCESS)
return error;
if (git_remote_load(&remote, repo, argv[1]) < 0) {
if (git_remote_new(&remote, repo, argv[1], NULL) < 0)
return -1;
}
// Set up the information for the background worker thread
data.remote = remote;
data.bytes = &bytes;
data.stats = &stats;
data.ret = 0;
data.finished = 0;
memset(&stats, 0, sizeof(stats));
pthread_create(&worker, NULL, download, &data);
// Loop while the worker thread is still running. Here we show processed
// and total objects in the pack and the amount of received
// data. Most frontends will probably want to show a percentage and
// the download rate.
do {
usleep(10000);
printf("\rReceived %d/%d objects in %d bytes", stats.processed, stats.total, bytes);
} while (!data.finished);
printf("\rReceived %d/%d objects in %d bytes\n", stats.processed, stats.total, bytes);
// Disconnect the underlying connection to prevent from idling.
git_remote_disconnect(remote);
// Update the references in the remote's namespace to point to the
// right commits. This may be needed even if there was no packfile
// to download, which can happen e.g. when the branches have been
// changed but all the neede objects are available locally.
error = git_remote_update_tips(remote);
if (error < GIT_SUCCESS)
return error;
if (git_remote_update_tips(remote, update_cb) < 0)
return -1;
free(packname);
git_indexer_free(idx);
git_remote_free(remote);
return GIT_SUCCESS;
return 0;
on_error:
git_remote_free(remote);
return -1;
}
......@@ -31,7 +31,7 @@ int run_command(git_cb fn, int argc, char **argv)
// Run the command. If something goes wrong, print the error message to stderr
error = fn(repo, argc, argv);
if (error < GIT_SUCCESS)
fprintf(stderr, "Bad news:\n %s\n", git_lasterror());
fprintf(stderr, "Bad news:\n %s\n", git_error_last()->message);
if(repo)
git_repository_free(repo);
......
......@@ -13,6 +13,61 @@ int index_cb(const git_indexer_stats *stats, void *data)
int index_pack(git_repository *repo, int argc, char **argv)
{
git_indexer_stream *idx;
git_indexer_stats stats = {0, 0};
int error, fd;
char hash[GIT_OID_HEXSZ + 1] = {0};
ssize_t read_bytes;
char buf[512];
if (argc < 2) {
fprintf(stderr, "I need a packfile\n");
return EXIT_FAILURE;
}
if (git_indexer_stream_new(&idx, ".git") < 0) {
puts("bad idx");
return -1;
}
if ((fd = open(argv[1], 0)) < 0) {
perror("open");
return -1;
}
do {
read_bytes = read(fd, buf, sizeof(buf));
if (read_bytes < 0)
break;
if ((error = git_indexer_stream_add(idx, buf, read_bytes, &stats)) < 0)
goto cleanup;
printf("\rIndexing %d of %d", stats.processed, stats.total);
} while (read_bytes > 0);
if (read_bytes < 0) {
error = -1;
perror("failed reading");
goto cleanup;
}
if ((error = git_indexer_stream_finalize(idx, &stats)) < 0)
goto cleanup;
printf("\rIndexing %d of %d\n", stats.processed, stats.total);
git_oid_fmt(hash, git_indexer_stream_hash(idx));
puts(hash);
cleanup:
close(fd);
git_indexer_stream_free(idx);
return error;
}
int index_pack_old(git_repository *repo, int argc, char **argv)
{
git_indexer *indexer;
git_indexer_stats stats;
int error;
......
......@@ -22,6 +22,7 @@
#include "git2/repository.h"
#include "git2/revwalk.h"
#include "git2/merge.h"
#include "git2/refs.h"
#include "git2/reflog.h"
......@@ -40,7 +41,7 @@
#include "git2/net.h"
#include "git2/status.h"
#include "git2/indexer.h"
#include "git2/submodule.h"
#include "git2/notes.h"
#endif
......@@ -19,42 +19,186 @@
*/
GIT_BEGIN_DECL
/**
* GIT_ATTR_TRUE checks if an attribute is set on. In core git
* parlance, this the value for "Set" attributes.
*
* For example, if the attribute file contains:
*
* *.c foo
*
* Then for file `xyz.c` looking up attribute "foo" gives a value for
* which `GIT_ATTR_TRUE(value)` is true.
*/
#define GIT_ATTR_TRUE(attr) ((attr) == git_attr__true)
/**
* GIT_ATTR_FALSE checks if an attribute is set off. In core git
* parlance, this is the value for attributes that are "Unset" (not to
* be confused with values that a "Unspecified").
*
* For example, if the attribute file contains:
*
* *.h -foo
*
* Then for file `zyx.h` looking up attribute "foo" gives a value for
* which `GIT_ATTR_FALSE(value)` is true.
*/
#define GIT_ATTR_FALSE(attr) ((attr) == git_attr__false)
#define GIT_ATTR_UNSPECIFIED(attr) ((attr) == NULL)
/**
* GIT_ATTR_UNSPECIFIED checks if an attribute is unspecified. This
* may be due to the attribute not being mentioned at all or because
* the attribute was explicitly set unspecified via the `!` operator.
*
* For example, if the attribute file contains:
*
* *.c foo
* *.h -foo
* onefile.c !foo
*
* Then for `onefile.c` looking up attribute "foo" yields a value with
* `GIT_ATTR_UNSPECIFIED(value)` of true. Also, looking up "foo" on
* file `onefile.rb` or looking up "bar" on any file will all give
* `GIT_ATTR_UNSPECIFIED(value)` of true.
*/
#define GIT_ATTR_UNSPECIFIED(attr) (!(attr) || (attr) == git_attr__unset)
/**
* GIT_ATTR_SET_TO_VALUE checks if an attribute is set to a value (as
* opposied to TRUE, FALSE or UNSPECIFIED). This would be the case if
* for a file with something like:
*
* *.txt eol=lf
*
* Given this, looking up "eol" for `onefile.txt` will give back the
* string "lf" and `GIT_ATTR_SET_TO_VALUE(attr)` will return true.
*/
#define GIT_ATTR_SET_TO_VALUE(attr) \
((attr) && (attr) != git_attr__unset && \
(attr) != git_attr__true && (attr) != git_attr__false)
GIT_EXTERN(const char *) git_attr__true;
GIT_EXTERN(const char *) git_attr__false;
GIT_EXTERN(const char *) git_attr__unset;
/**
* Check attribute flags: Reading values from index and working directory.
*
* When checking attributes, it is possible to check attribute files
* in both the working directory (if there is one) and the index (if
* there is one). You can explicitly choose where to check and in
* which order using the following flags.
*
* Core git usually checks the working directory then the index,
* except during a checkout when it checks the index first. It will
* use index only for creating archives or for a bare repo (if an
* index has been specified for the bare repo).
*/
#define GIT_ATTR_CHECK_FILE_THEN_INDEX 0
#define GIT_ATTR_CHECK_INDEX_THEN_FILE 1
#define GIT_ATTR_CHECK_INDEX_ONLY 2
/**
* Check attribute flags: Using the system attributes file.
*
* Normally, attribute checks include looking in the /etc (or system
* equivalent) directory for a `gitattributes` file. Passing this
* flag will cause attribute checks to ignore that file.
*/
#define GIT_ATTR_CHECK_NO_SYSTEM (1 << 2)
/**
* Lookup attribute for path returning string caller must free
* Look up the value of one git attribute for path.
*
* @param repo The repository containing the path.
* @param flags A combination of GIT_ATTR_CHECK... flags.
* @param path The path to check for attributes. Relative paths are
* interpreted relative to the repo root. The file does
* not have to exist, but if it does not, then it will be
* treated as a plain file (not a directory).
* @param name The name of the attribute to look up.
* @param value Output of the value of the attribute. Use the GIT_ATTR_...
* macros to test for TRUE, FALSE, UNSPECIFIED, etc. or just
* use the string value for attributes set to a value. You
* should NOT modify or free this value.
*/
GIT_EXTERN(int) git_attr_get(
git_repository *repo, const char *path, const char *name,
git_repository *repo,
uint32_t flags,
const char *path,
const char *name,
const char **value);
/**
* Lookup list of attributes for path, populating array of strings
* Look up a list of git attributes for path.
*
* Use this if you have a known list of attributes that you want to
* look up in a single call. This is somewhat more efficient than
* calling `git_attr_get()` multiple times.
*
* For example, you might write:
*
* const char *attrs[] = { "crlf", "diff", "foo" };
* const char **values[3];
* git_attr_get_many(repo, 0, "my/fun/file.c", 3, attrs, values);
*
* Then you could loop through the 3 values to get the settings for
* the three attributes you asked about.
*
* @param repo The repository containing the path.
* @param flags A combination of GIT_ATTR_CHECK... flags.
* @param path The path inside the repo to check attributes. This
* does not have to exist, but if it does not, then
* it will be treated as a plain file (i.e. not a directory).
* @param num_attr The number of attributes being looked up
* @param names An array of num_attr strings containing attribute names.
* @param values An array of num_attr entries that will have string
* pointers written into it for the values of the attributes.
* You should not modify or free the values that are written
* into this array (although of course, you should free the
* array itself if you allocated it).
*/
GIT_EXTERN(int) git_attr_get_many(
git_repository *repo, const char *path,
size_t num_attr, const char **names,
git_repository *repo,
uint32_t flags,
const char *path,
size_t num_attr,
const char **names,
const char **values);
/**
* Perform an operation on each attribute of a path.
* Loop over all the git attributes for a path.
*
* @param repo The repository containing the path.
* @param flags A combination of GIT_ATTR_CHECK... flags.
* @param path The path inside the repo to check attributes. This
* does not have to exist, but if it does not, then
* it will be treated as a plain file (i.e. not a directory).
* @param callback The function that will be invoked on each attribute
* and attribute value. The name parameter will be the name
* of the attribute and the value will be the value it is
* set to, including possibly NULL if the attribute is
* explicitly set to UNSPECIFIED using the ! sign. This
* will be invoked only once per attribute name, even if
* there are multiple rules for a given file. The highest
* priority rule will be used.
* @param payload Passed on as extra parameter to callback function.
*/
GIT_EXTERN(int) git_attr_foreach(
git_repository *repo, const char *path,
git_repository *repo,
uint32_t flags,
const char *path,
int (*callback)(const char *name, const char *value, void *payload),
void *payload);
/**
* Flush the gitattributes cache.
*
* Call this if you have reason to believe that the attributes files
* on disk no longer match the cached contents of memory.
* Call this if you have reason to believe that the attributes files on
* disk no longer match the cached contents of memory. This will cause
* the attributes files to be reloaded the next time that an attribute
* access function is called.
*/
GIT_EXTERN(void) git_attr_cache_flush(
git_repository *repo);
......@@ -62,7 +206,7 @@ GIT_EXTERN(void) git_attr_cache_flush(
/**
* Add a macro definition.
*
* Macros will automatically be loaded from the top level .gitattributes
* Macros will automatically be loaded from the top level `.gitattributes`
* file of the repository (plus the build-in "binary" macro). This
* function allows you to add others. For example, to add the default
* macro, you would call:
......
......@@ -4,12 +4,119 @@
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_branch_h__
#define INCLUDE_branch_h__
#ifndef INCLUDE_git_branch_h__
#define INCLUDE_git_branch_h__
struct git_branch {
char *remote; /* TODO: Make this a git_remote */
char *merge;
};
#include "common.h"
#include "types.h"
/**
* @file git2/branch.h
* @brief Git branch parsing routines
* @defgroup git_branch Git branch management
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
/**
* Create a new branch pointing at a target commit
*
* A new direct reference will be created pointing to
* this target commit. If `force` is true and a reference
* already exists with the given name, it'll be replaced.
*
* @param oid_out Pointer where to store the OID of the target commit.
*
* @param repo Repository where to store the branch.
*
* @param branch_name Name for the branch; this name is
* validated for consistency. It should also not conflict with
* an already existing branch name.
*
* @param target Object to which this branch should point. This object
* must belong to the given `repo` and can either be a git_commit or a
* git_tag. When a git_tag is being passed, it should be dereferencable
* to a git_commit which oid will be used as the target of the branch.
*
* @param force Overwrite existing branch.
*
* @return GIT_SUCCESS or an error code.
* A proper reference is written in the refs/heads namespace
* pointing to the provided target commit.
*/
GIT_EXTERN(int) git_branch_create(
git_oid *oid_out,
git_repository *repo,
const char *branch_name,
const git_object *target,
int force);
/**
* Delete an existing branch reference.
*
* @param repo Repository where lives the branch.
*
* @param branch_name Name of the branch to be deleted;
* this name is validated for consistency.
*
* @param branch_type Type of the considered branch. This should
* be valued with either GIT_BRANCH_LOCAL or GIT_BRANCH_REMOTE.
*
* @return GIT_SUCCESS on success, GIT_ENOTFOUND if the branch
* doesn't exist or an error code.
*/
GIT_EXTERN(int) git_branch_delete(
git_repository *repo,
const char *branch_name,
git_branch_type branch_type);
/**
* Fill a list with all the branches in the Repository
*
* The string array will be filled with the names of the
* matching branches; these values are owned by the user and
* should be free'd manually when no longer needed, using
* `git_strarray_free`.
*
* @param branch_names Pointer to a git_strarray structure
* where the branch names will be stored.
*
* @param repo Repository where to find the branches.
*
* @param list_flags Filtering flags for the branch
* listing. Valid values are GIT_BRANCH_LOCAL, GIT_BRANCH_REMOTE
* or a combination of the two.
*
* @return GIT_SUCCESS or an error code.
*/
GIT_EXTERN(int) git_branch_list(
git_strarray *branch_names,
git_repository *repo,
unsigned int list_flags);
/**
* Move/rename an existing branch reference.
*
* @param repo Repository where lives the branch.
*
* @param old_branch_name Current name of the branch to be moved;
* this name is validated for consistency.
*
* @param new_branch_name Target name of the branch once the move
* is performed; this name is validated for consistency.
*
* @param force Overwrite existing branch.
*
* @return GIT_SUCCESS on success, GIT_ENOTFOUND if the branch
* doesn't exist or an error code.
*/
GIT_EXTERN(int) git_branch_move(
git_repository *repo,
const char *old_branch_name,
const char *new_branch_name,
int force);
/** @} */
GIT_END_DECL
#endif
......@@ -87,6 +87,7 @@ typedef struct {
} git_strarray;
GIT_EXTERN(void) git_strarray_free(git_strarray *array);
GIT_EXTERN(int) git_strarray_copy(git_strarray *tgt, const git_strarray *src);
/**
* Return the version of the libgit2 library
......
......@@ -39,7 +39,9 @@ enum {
GIT_DIFF_IGNORE_SUBMODULES = (1 << 5),
GIT_DIFF_PATIENCE = (1 << 6),
GIT_DIFF_INCLUDE_IGNORED = (1 << 7),
GIT_DIFF_INCLUDE_UNTRACKED = (1 << 8)
GIT_DIFF_INCLUDE_UNTRACKED = (1 << 8),
GIT_DIFF_INCLUDE_UNMODIFIED = (1 << 9),
GIT_DIFF_RECURSE_UNTRACKED_DIRS = (1 << 10),
};
/**
......@@ -152,19 +154,22 @@ typedef int (*git_diff_hunk_fn)(
* Line origin constants.
*
* These values describe where a line came from and will be passed to
* the git_diff_line_fn when iterating over a diff. There are some
* the git_diff_data_fn when iterating over a diff. There are some
* special origin contants at the end that are used for the text
* output callbacks to demarcate lines that are actually part of
* the file or hunk headers.
*/
enum {
/* these values will be sent to `git_diff_line_fn` along with the line */
/* these values will be sent to `git_diff_data_fn` along with the line */
GIT_DIFF_LINE_CONTEXT = ' ',
GIT_DIFF_LINE_ADDITION = '+',
GIT_DIFF_LINE_DELETION = '-',
GIT_DIFF_LINE_ADD_EOFNL = '\n', /**< LF was added at end of file */
GIT_DIFF_LINE_DEL_EOFNL = '\0', /**< LF was removed at end of file */
/* these values will only be sent to a `git_diff_output_fn` */
/* these values will only be sent to a `git_diff_data_fn` when the content
* of a diff is being formatted (eg. through git_diff_print_patch() or
* git_diff_print_compact(), for instance).
*/
GIT_DIFF_LINE_FILE_HDR = 'F',
GIT_DIFF_LINE_HUNK_HDR = 'H',
GIT_DIFF_LINE_BINARY = 'B'
......@@ -172,25 +177,19 @@ enum {
/**
* When iterating over a diff, callback that will be made per text diff
* line.
*/
typedef int (*git_diff_line_fn)(
void *cb_data,
git_diff_delta *delta,
char line_origin, /**< GIT_DIFF_LINE_... value from above */
const char *content,
size_t content_len);
/**
* line. In this context, the provided range will be NULL.
*
* When printing a diff, callback that will be made to output each line
* of text. This uses some extra GIT_DIFF_LINE_... constants for output
* of lines of file and hunk headers.
*/
typedef int (*git_diff_output_fn)(
typedef int (*git_diff_data_fn)(
void *cb_data,
git_diff_delta *delta,
git_diff_range *range,
char line_origin, /**< GIT_DIFF_LINE_... value from above */
const char *formatted_output);
const char *content,
size_t content_len);
/** @name Diff List Generator Functions
*
......@@ -309,7 +308,7 @@ GIT_EXTERN(int) git_diff_foreach(
void *cb_data,
git_diff_file_fn file_cb,
git_diff_hunk_fn hunk_cb,
git_diff_line_fn line_cb);
git_diff_data_fn line_cb);
/**
* Iterate over a diff generating text output like "git diff --name-status".
......@@ -317,7 +316,7 @@ GIT_EXTERN(int) git_diff_foreach(
GIT_EXTERN(int) git_diff_print_compact(
git_diff_list *diff,
void *cb_data,
git_diff_output_fn print_cb);
git_diff_data_fn print_cb);
/**
* Iterate over a diff generating text output like "git diff".
......@@ -327,7 +326,7 @@ GIT_EXTERN(int) git_diff_print_compact(
GIT_EXTERN(int) git_diff_print_patch(
git_diff_list *diff,
void *cb_data,
git_diff_output_fn print_cb);
git_diff_data_fn print_cb);
/**@}*/
......@@ -345,7 +344,7 @@ GIT_EXTERN(int) git_diff_blobs(
git_diff_options *options,
void *cb_data,
git_diff_hunk_fn hunk_cb,
git_diff_line_fn line_cb);
git_diff_data_fn line_cb);
GIT_END_DECL
......
......@@ -21,123 +21,61 @@ 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,
/** The given short oid is ambiguous */
GIT_EAMBIGUOUSOIDPREFIX = -29,
GIT_EAMBIGUOUS = -29,
/** Skip and passthrough the given ODB backend */
GIT_EPASSTHROUGH = -30,
/** The path pattern and string did not match */
GIT_ENOMATCH = -31,
/** The buffer is too short to satisfy the request */
GIT_ESHORTBUFFER = -32,
GIT_EREVWALKOVER = -33,
} git_error_t;
typedef struct {
char *message;
int klass;
} 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);
typedef enum {
GITERR_NOMEMORY,
GITERR_OS,
GITERR_INVALID,
GITERR_REFERENCE,
GITERR_ZLIB,
GITERR_REPOSITORY,
GITERR_CONFIG,
GITERR_REGEX,
GITERR_ODB,
GITERR_INDEX,
GITERR_OBJECT,
GITERR_NET,
GITERR_TAG,
GITERR_TREE,
} git_error_class;
/**
* 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`.
* Return the last `git_error` object that was generated for the
* current thread or NULL if no error has occurred.
*
* @param num The error code to explain
* @return a string explaining the error code
* @return A git_error object.
*/
GIT_EXTERN(const char *) git_strerror(int num);
GIT_EXTERN(const git_error *) giterr_last(void);
/**
* Clear the latest library error
* Clear the last library error that occurred for this thread.
*/
GIT_EXTERN(void) git_clearerror(void);
GIT_EXTERN(void) giterr_clear(void);
/** @} */
GIT_END_DECL
......
......@@ -23,6 +23,51 @@ typedef struct git_indexer_stats {
typedef struct git_indexer git_indexer;
typedef struct git_indexer_stream git_indexer_stream;
/**
* Create a new streaming indexer instance
*
* @param out where to store the inexer instance
* @param path to the gitdir (metadata directory)
*/
GIT_EXTERN(int) git_indexer_stream_new(git_indexer_stream **out, const char *gitdir);
/**
* Add data to the indexer
*
* @param idx the indexer
* @param data the data to add
* @param size the size of the data
* @param stats stat storage
*/
GIT_EXTERN(int) git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t size, git_indexer_stats *stats);
/**
* Finalize the pack and index
*
* Resolve any pending deltas and write out the index file
*
* @param idx the indexer
*/
GIT_EXTERN(int) git_indexer_stream_finalize(git_indexer_stream *idx, git_indexer_stats *stats);
/**
* Get the packfile's hash
*
* A packfile's name is derived from the sorted hashing of all object
* names. This is only correct after the index has been finalized.
*
* @param idx the indexer instance
*/
GIT_EXTERN(const git_oid *) git_indexer_stream_hash(git_indexer_stream *idx);
/**
* Free the indexer and its resources
*
* @param idx the indexer to free
*/
GIT_EXTERN(void) git_indexer_stream_free(git_indexer_stream *idx);
/**
* Create a new indexer instance
......
/*
* Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_git_merge_h__
#define INCLUDE_git_merge_h__
#include "common.h"
#include "types.h"
#include "oid.h"
/**
* @file git2/merge.h
* @brief Git merge-base routines
* @defgroup git_revwalk Git merge-base routines
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
/**
* Find a merge base between two commits
*
* @param out the OID of a merge base between 'one' and 'two'
* @param repo the repository where the commits exist
* @param one one of the commits
* @param two the other commit
*/
GIT_EXTERN(int) git_merge_base(git_oid *out, git_repository *repo, git_oid *one, git_oid *two);
/** @} */
GIT_END_DECL
#endif
......@@ -92,6 +92,16 @@ GIT_EXTERN(int) git_note_remove(git_repository *repo, const char *notes_ref,
*/
GIT_EXTERN(void) git_note_free(git_note *note);
/**
* Get the default notes reference for a repository
*
* @param out Pointer to the default notes reference
* @param repo The Git repository
*
* @return GIT_SUCCESS or an error code
*/
GIT_EXTERN(int) git_note_default_ref(const char **out, git_repository *repo);
/** @} */
GIT_END_DECL
#endif
......@@ -33,6 +33,17 @@ GIT_BEGIN_DECL
GIT_EXTERN(int) git_reference_lookup(git_reference **reference_out, git_repository *repo, const char *name);
/**
* Lookup a reference by name and resolve immediately to OID.
*
* @param oid Pointer to oid to be filled in
* @param repo The repository in which to look up the reference
* @param name The long name for the reference
* @return 0 on success, -1 if name could not be resolved
*/
GIT_EXTERN(int) git_reference_name_to_oid(
git_oid *out, git_repository *repo, const char *name);
/**
* Create a new symbolic reference.
*
* The reference will be created in the repository and written
......@@ -304,6 +315,15 @@ GIT_EXTERN(int) git_reference_reload(git_reference *ref);
*/
GIT_EXTERN(void) git_reference_free(git_reference *ref);
/**
* Compare two references.
*
* @param ref1 The first git_reference
* @param ref2 The second git_reference
* @return GIT_SUCCESS if the same, else a stable but meaningless ordering.
*/
GIT_EXTERN(int) git_reference_cmp(git_reference *ref1, git_reference *ref2);
/** @} */
GIT_END_DECL
#endif
......@@ -7,6 +7,7 @@
#ifndef INCLUDE_git_refspec_h__
#define INCLUDE_git_refspec_h__
#include "common.h"
#include "types.h"
/**
......@@ -35,14 +36,13 @@ const char *git_refspec_src(const git_refspec *refspec);
const char *git_refspec_dst(const git_refspec *refspec);
/**
* Match a refspec's source descriptor with a reference name
* Check if a refspec's source descriptor matches a reference
*
* @param refspec the refspec
* @param refname the name of the reference to check
* @return GIT_SUCCESS on successful match; GIT_ENOMACH on match
* failure or an error code on other failure
* @return 1 if the refspec matches, 0 otherwise
*/
int git_refspec_src_match(const git_refspec *refspec, const char *refname);
int git_refspec_src_matches(const git_refspec *refspec, const char *refname);
/**
* Transform a reference to its target following the refspec's rules
......
......@@ -11,6 +11,7 @@
#include "repository.h"
#include "refspec.h"
#include "net.h"
#include "indexer.h"
/**
* @file git2/remote.h
......@@ -150,7 +151,7 @@ GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void
* @param filename where to store the temproray filename
* @return GIT_SUCCESS or an error code
*/
GIT_EXTERN(int) git_remote_download(char **filename, git_remote *remote);
GIT_EXTERN(int) git_remote_download(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats);
/**
* Check whether the remote is connected
......@@ -175,6 +176,9 @@ GIT_EXTERN(void) git_remote_disconnect(git_remote *remote);
/**
* Free the memory associated with a remote
*
* This also disconnects from the remote, if the connection
* has not been closed yet (using git_remote_disconnect).
*
* @param remote the remote to free
*/
GIT_EXTERN(void) git_remote_free(git_remote *remote);
......@@ -182,12 +186,10 @@ GIT_EXTERN(void) git_remote_free(git_remote *remote);
/**
* Update the tips to the new state
*
* Make sure that you only call this once you've successfully indexed
* or expanded the packfile.
*
* @param remote the remote to update
* @param cb callback to run on each ref update. 'a' is the old value, 'b' is then new value
*/
GIT_EXTERN(int) git_remote_update_tips(git_remote *remote);
GIT_EXTERN(int) git_remote_update_tips(git_remote *remote, int (*cb)(const char *refname, const git_oid *a, const git_oid *b));
/**
* Return whether a string is a valid remote URL
......
......@@ -70,6 +70,20 @@ GIT_EXTERN(int) git_repository_discover(
int across_fs,
const char *ceiling_dirs);
enum {
GIT_REPOSITORY_OPEN_NO_SEARCH = (1 << 0),
GIT_REPOSITORY_OPEN_CROSS_FS = (1 << 1),
};
/**
* Find and open a repository with extended controls.
*/
GIT_EXTERN(int) git_repository_open_ext(
git_repository **repo,
const char *start_path,
uint32_t flags,
const char *ceiling_dirs);
/**
* Free a previously allocated repository
*
......
......@@ -164,6 +164,28 @@ GIT_EXTERN(int) git_revwalk_hide_glob(git_revwalk *walk, const char *glob);
GIT_EXTERN(int) git_revwalk_hide_head(git_revwalk *walk);
/**
* Push the OID pointed to by a reference
*
* The reference must point to a commit.
*
* @param walk the walker being used for the traversal
* @param refname the referece to push
* @return GIT_SUCCESS or an error code
*/
GIT_EXTERN(int) git_revwalk_push_ref(git_revwalk *walk, const char *refname);
/**
* Hide the OID pointed to by a reference
*
* The reference must point to a commit.
*
* @param walk the walker being used for the traversal
* @param refname the referece to hide
* @return GIT_SUCCESS or an error code
*/
GIT_EXTERN(int) git_revwalk_hide_ref(git_revwalk *walk, const char *refname);
/**
* Get the next commit from the revision walk.
*
* The initial call to this method is *not* blocking when
......
......@@ -47,6 +47,79 @@ GIT_BEGIN_DECL
GIT_EXTERN(int) git_status_foreach(git_repository *repo, int (*callback)(const char *, unsigned int, void *), void *payload);
/**
* Select the files on which to report status.
*
* - GIT_STATUS_SHOW_INDEX_AND_WORKDIR is the default. This is the
* rough equivalent of `git status --porcelain` where each file
* will receive a callback indicating its status in the index and
* in the workdir.
* - GIT_STATUS_SHOW_INDEX_ONLY will only make callbacks for index
* side of status. The status of the index contents relative to
* the HEAD will be given.
* - GIT_STATUS_SHOW_WORKDIR_ONLY will only make callbacks for the
* workdir side of status, reporting the status of workdir content
* relative to the index.
* - GIT_STATUS_SHOW_INDEX_THEN_WORKDIR behaves like index-only
* followed by workdir-only, causing two callbacks to be issued
* per file (first index then workdir). This is slightly more
* efficient than making separate calls. This makes it easier to
* emulate the output of a plain `git status`.
*/
typedef enum {
GIT_STATUS_SHOW_INDEX_AND_WORKDIR = 0,
GIT_STATUS_SHOW_INDEX_ONLY = 1,
GIT_STATUS_SHOW_WORKDIR_ONLY = 2,
GIT_STATUS_SHOW_INDEX_THEN_WORKDIR = 3,
} git_status_show_t;
/**
* Flags to control status callbacks
*
* - GIT_STATUS_OPT_INCLUDE_UNTRACKED says that callbacks should
* be made on untracked files. These will only be made if the
* workdir files are included in the status "show" option.
* - GIT_STATUS_OPT_INCLUDE_IGNORED says that ignored files should
* get callbacks. Again, these callbacks will only be made if
* the workdir files are included in the status "show" option.
* Right now, there is no option to include all files in
* directories that are ignored completely.
* - GIT_STATUS_OPT_INCLUDE_UNMODIFIED indicates that callback
* should be made even on unmodified files.
* - GIT_STATUS_OPT_EXCLUDE_SUBMODULES indicates that directories
* which appear to be submodules should just be skipped over.
* - GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS indicates that the
* contents of untracked directories should be included in the
* status. Normally if an entire directory is new, then just
* the top-level directory will be included (with a trailing
* slash on the entry name). Given this flag, the directory
* itself will not be included, but all the files in it will.
*/
#define GIT_STATUS_OPT_INCLUDE_UNTRACKED (1 << 0)
#define GIT_STATUS_OPT_INCLUDE_IGNORED (1 << 1)
#define GIT_STATUS_OPT_INCLUDE_UNMODIFIED (1 << 2)
#define GIT_STATUS_OPT_EXCLUDE_SUBMODULES (1 << 3)
#define GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS (1 << 4)
/**
* Options to control how callbacks will be made by
* `git_status_foreach_ext()`.
*/
typedef struct {
git_status_show_t show;
unsigned int flags;
git_strarray pathspec;
} git_status_options;
/**
* Gather file status information and run callbacks as requested.
*/
GIT_EXTERN(int) git_status_foreach_ext(
git_repository *repo,
git_status_options *opts,
int (*callback)(const char *, unsigned int, void *),
void *payload);
/**
* Get file status for a single file
*
* @param status_flags the status value
......
/*
* Copyright (C) 2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_git_submodule_h__
#define INCLUDE_git_submodule_h__
#include "common.h"
#include "types.h"
#include "oid.h"
/**
* @file git2/submodule.h
* @brief Git submodule management utilities
* @defgroup git_submodule Git submodule management routines
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
typedef enum {
GIT_SUBMODULE_UPDATE_CHECKOUT = 0,
GIT_SUBMODULE_UPDATE_REBASE = 1,
GIT_SUBMODULE_UPDATE_MERGE = 2
} git_submodule_update_t;
typedef enum {
GIT_SUBMODULE_IGNORE_ALL = 0, /* never dirty */
GIT_SUBMODULE_IGNORE_DIRTY = 1, /* only dirty if HEAD moved */
GIT_SUBMODULE_IGNORE_UNTRACKED = 2, /* dirty if tracked files change */
GIT_SUBMODULE_IGNORE_NONE = 3 /* any change or untracked == dirty */
} git_submodule_ignore_t;
/**
* Description of submodule
*
* This record describes a submodule found in a repository. There
* should be an entry for every submodule found in the HEAD and for
* every submodule described in .gitmodules. The fields are as follows:
*
* - `name` is the name of the submodule from .gitmodules.
* - `path` is the path to the submodule from the repo working directory.
* It is almost always the same as `name`.
* - `url` is the url for the submodule.
* - `oid` is the HEAD SHA1 for the submodule.
* - `update` is a value from above - see gitmodules(5) update.
* - `ignore` is a value from above - see gitmodules(5) ignore.
* - `fetch_recurse` is 0 or 1 - see gitmodules(5) fetchRecurseSubmodules.
* - `refcount` is for internal use.
*
* If the submodule has been added to .gitmodules but not yet git added,
* then the `oid` will be zero. If the submodule has been deleted, but
* the delete has not been committed yet, then the `oid` will be set, but
* the `url` will be NULL.
*/
typedef struct {
char *name;
char *path;
char *url;
git_oid oid; /* sha1 of submodule HEAD ref or zero if not committed */
git_submodule_update_t update;
git_submodule_ignore_t ignore;
int fetch_recurse;
int refcount;
} git_submodule;
/**
* Iterate over all submodules of a repository.
*
* @param repo The repository
* @param callback Function to be called with the name of each submodule.
* Return a non-zero value to terminate the iteration.
* @param payload Extra data to pass to callback
* @return 0 on success, -1 on error, or non-zero return value of callback
*/
GIT_EXTERN(int) git_submodule_foreach(
git_repository *repo,
int (*callback)(const char *name, void *payload),
void *payload);
/**
* Lookup submodule information by name or path.
*
* Given either the submodule name or path (they are ususally the same),
* this returns a structure describing the submodule. If the submodule
* does not exist, this will return GIT_ENOTFOUND and set the submodule
* pointer to NULL.
*
* @param submodule Pointer to submodule description object pointer..
* @param repo The repository.
* @param name The name of the submodule. Trailing slashes will be ignored.
* @return 0 on success, GIT_ENOTFOUND if submodule does not exist, -1 on error
*/
GIT_EXTERN(int) git_submodule_lookup(
git_submodule **submodule,
git_repository *repo,
const char *name);
/** @} */
GIT_END_DECL
#endif
......@@ -274,6 +274,21 @@ GIT_EXTERN(int) git_tag_list_match(
const char *pattern,
git_repository *repo);
/**
* Recursively peel a tag until a non tag git_object
* is met
*
* The retrieved `tag_target` object is owned by the repository
* and should be closed with the `git_object_free` method.
*
* @param tag_target Pointer to the peeled git_object
* @param tag The tag to be processed
* @return GIT_SUCCESS or an error code
*/
GIT_EXTERN(int) git_tag_peel(
git_object **tag_target,
git_tag *tag);
/** @} */
GIT_END_DECL
#endif
......@@ -160,6 +160,11 @@ typedef enum {
GIT_REF_LISTALL = GIT_REF_OID|GIT_REF_SYMBOLIC|GIT_REF_PACKED,
} git_rtype;
/** Basic type of any Git branch. */
typedef enum {
GIT_BRANCH_LOCAL = 1,
GIT_BRANCH_REMOTE = 2,
} git_branch_type;
typedef struct git_refspec git_refspec;
typedef struct git_remote git_remote;
......
......@@ -8,33 +8,49 @@
#define INCLUDE_attr_h__
#include "attr_file.h"
#include "strmap.h"
#define GIT_ATTR_CONFIG "core.attributesfile"
#define GIT_IGNORE_CONFIG "core.excludesfile"
typedef struct {
int initialized;
git_hashtable *files; /* hash path to git_attr_file of rules */
git_hashtable *macros; /* hash name to vector<git_attr_assignment> */
git_pool pool;
git_strmap *files; /* hash path to git_attr_file of rules */
git_strmap *macros; /* hash name to vector<git_attr_assignment> */
const char *cfg_attr_file; /* cached value of core.attributesfile */
const char *cfg_excl_file; /* cached value of core.excludesfile */
} git_attr_cache;
typedef int (*git_attr_file_parser)(
git_repository *, const char *, git_attr_file *);
extern int git_attr_cache__init(git_repository *repo);
extern int git_attr_cache__insert_macro(
git_repository *repo, git_attr_rule *macro);
extern int git_attr_cache__lookup_or_create_file(
extern git_attr_rule *git_attr_cache__lookup_macro(
git_repository *repo, const char *name);
extern int git_attr_cache__push_file(
git_repository *repo,
const char *key,
const char *base,
const char *filename,
int (*loader)(git_repository *, const char *, git_attr_file *),
git_attr_file **file_ptr);
git_attr_file_source source,
git_attr_file_parser parse,
git_vector *stack);
extern int git_attr_cache__push_file(
extern int git_attr_cache__internal_file(
git_repository *repo,
git_vector *stack,
const char *base,
const char *filename,
int (*loader)(git_repository *, const char *, git_attr_file *));
const char *key,
git_attr_file **file_ptr);
/* returns true if path is in cache */
extern bool git_attr_cache__is_cached(
git_repository *repo, git_attr_file_source source, const char *path);
/* returns GIT_SUCCESS if path is in cache */
extern int git_attr_cache__is_cached(git_repository *repo, const char *path);
extern int git_attr_cache__decide_sources(
uint32_t flags, bool has_wd, bool has_index, git_attr_file_source *srcs);
#endif
......@@ -9,18 +9,19 @@
#include "git2/attr.h"
#include "vector.h"
#include "hashtable.h"
#include "pool.h"
#include "buffer.h"
#define GIT_ATTR_FILE ".gitattributes"
#define GIT_ATTR_FILE_INREPO "info/attributes"
#define GIT_ATTR_FILE_SYSTEM "gitattributes"
#define GIT_ATTR_CONFIG "core.attributesfile"
#define GIT_ATTR_FNMATCH_NEGATIVE (1U << 0)
#define GIT_ATTR_FNMATCH_DIRECTORY (1U << 1)
#define GIT_ATTR_FNMATCH_FULLPATH (1U << 2)
#define GIT_ATTR_FNMATCH_MACRO (1U << 3)
#define GIT_ATTR_FNMATCH_IGNORE (1U << 4)
#define GIT_ATTR_FNMATCH_HASWILD (1U << 5)
typedef struct {
char *pattern;
......@@ -36,42 +37,49 @@ typedef struct {
typedef struct {
git_refcount unused;
const char *name;
unsigned long name_hash;
uint32_t name_hash;
} git_attr_name;
typedef struct {
git_refcount rc; /* for macros */
char *name;
unsigned long name_hash;
uint32_t name_hash;
const char *value;
int is_allocated;
} git_attr_assignment;
typedef struct {
char *path; /* cache the path this was loaded from */
char *key; /* cache "source#path" this was loaded from */
git_vector rules; /* vector of <rule*> or <fnmatch*> */
git_pool *pool;
bool pool_is_allocated;
} git_attr_file;
typedef struct {
git_buf full;
const char *path;
const char *basename;
int is_dir;
int is_dir;
} git_attr_path;
typedef enum {
GIT_ATTR_FILE_FROM_FILE = 0,
GIT_ATTR_FILE_FROM_INDEX = 1
} git_attr_file_source;
/*
* git_attr_file API
*/
extern int git_attr_file__new(git_attr_file **attrs_ptr);
extern int git_attr_file__new(
git_attr_file **attrs_ptr, git_attr_file_source src, const char *path, git_pool *pool);
extern int git_attr_file__new_and_load(
git_attr_file **attrs_ptr, const char *path);
extern void git_attr_file__free(git_attr_file *file);
extern int git_attr_file__from_buffer(
extern int git_attr_file__parse_buffer(
git_repository *repo, const char *buf, git_attr_file *file);
extern int git_attr_file__from_file(
git_repository *repo, const char *path, git_attr_file *file);
extern int git_attr_file__set_path(
git_repository *repo, const char *path, git_attr_file *file);
extern int git_attr_file__lookup_one(
git_attr_file *file,
......@@ -82,9 +90,9 @@ extern int git_attr_file__lookup_one(
/* loop over rules in file from bottom to top */
#define git_attr_file__foreach_matching_rule(file, path, iter, rule) \
git_vector_rforeach(&(file)->rules, (iter), (rule)) \
if (git_attr_rule__match((rule), (path)) == GIT_SUCCESS)
if (git_attr_rule__match((rule), (path)))
extern unsigned long git_attr_file__name_hash(const char *name);
extern uint32_t git_attr_file__name_hash(const char *name);
/*
......@@ -93,16 +101,17 @@ extern unsigned long git_attr_file__name_hash(const char *name);
extern int git_attr_fnmatch__parse(
git_attr_fnmatch *spec,
git_pool *pool,
const char *source,
const char **base);
extern int git_attr_fnmatch__match(
extern bool git_attr_fnmatch__match(
git_attr_fnmatch *rule,
const git_attr_path *path);
extern void git_attr_rule__free(git_attr_rule *rule);
extern int git_attr_rule__match(
extern bool git_attr_rule__match(
git_attr_rule *rule,
const git_attr_path *path);
......@@ -112,8 +121,11 @@ extern git_attr_assignment *git_attr_rule__lookup_assignment(
extern int git_attr_path__init(
git_attr_path *info, const char *path, const char *base);
extern void git_attr_path__free(git_attr_path *info);
extern int git_attr_assignment__parse(
git_repository *repo, /* needed to expand macros */
git_pool *pool,
git_vector *assigns,
const char **scan);
......
......@@ -42,7 +42,7 @@ int git_blob__parse(git_blob *blob, git_odb_object *odb_obj)
assert(blob);
git_cached_obj_incref((git_cached_obj *)odb_obj);
blob->odb_object = odb_obj;
return GIT_SUCCESS;
return 0;
}
int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *buffer, size_t len)
......@@ -51,58 +51,50 @@ int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *b
git_odb *odb;
git_odb_stream *stream;
error = git_repository_odb__weakptr(&odb, repo);
if (error < GIT_SUCCESS)
if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
(error = git_odb_open_wstream(&stream, odb, len, GIT_OBJ_BLOB)) < 0)
return error;
if ((error = git_odb_open_wstream(&stream, odb, len, GIT_OBJ_BLOB)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to create blob");
if ((error = stream->write(stream, buffer, len)) == 0)
error = stream->finalize_write(oid, stream);
if ((error = stream->write(stream, buffer, len)) < GIT_SUCCESS) {
stream->free(stream);
return error;
}
error = stream->finalize_write(oid, stream);
stream->free(stream);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to create blob");
return GIT_SUCCESS;
return error;
}
static int write_file_stream(git_oid *oid, git_odb *odb, const char *path, git_off_t file_size)
static int write_file_stream(
git_oid *oid, git_odb *odb, const char *path, git_off_t file_size)
{
int fd, error;
char buffer[4096];
git_odb_stream *stream = NULL;
if ((error = git_odb_open_wstream(&stream, odb, file_size, GIT_OBJ_BLOB)) < GIT_SUCCESS)
if ((error = git_odb_open_wstream(
&stream, odb, (size_t)file_size, GIT_OBJ_BLOB)) < 0)
return error;
if ((fd = p_open(path, O_RDONLY)) < 0) {
error = git__throw(GIT_ENOTFOUND, "Failed to create blob. Could not open '%s'", path);
goto cleanup;
if ((fd = git_futils_open_ro(path)) < 0) {
stream->free(stream);
return -1;
}
while (file_size > 0) {
while (!error && file_size > 0) {
ssize_t read_len = p_read(fd, buffer, sizeof(buffer));
if (read_len < 0) {
error = git__throw(GIT_EOSERR, "Failed to create blob. Can't read full file");
p_close(fd);
goto cleanup;
giterr_set(
GITERR_OS, "Failed to create blob. Can't read whole file");
error = -1;
}
stream->write(stream, buffer, read_len);
file_size -= read_len;
else if (!(error = stream->write(stream, buffer, read_len)))
file_size -= read_len;
}
p_close(fd);
error = stream->finalize_write(oid, stream);
cleanup:
if (!error)
error = stream->finalize_write(oid, stream);
stream->free(stream);
return error;
}
......@@ -117,8 +109,7 @@ static int write_file_filtered(
git_buf source = GIT_BUF_INIT;
git_buf dest = GIT_BUF_INIT;
error = git_futils_readbuffer(&source, full_path);
if (error < GIT_SUCCESS)
if ((error = git_futils_readbuffer(&source, full_path)) < 0)
return error;
error = git_filters_apply(&dest, &source, filters);
......@@ -127,40 +118,39 @@ static int write_file_filtered(
* and we don't want to ODB write to choke */
git_buf_free(&source);
if (error == GIT_SUCCESS) {
/* Write the file to disk if it was properly filtered */
/* Write the file to disk if it was properly filtered */
if (!error)
error = git_odb_write(oid, odb, dest.ptr, dest.size, GIT_OBJ_BLOB);
}
git_buf_free(&dest);
return GIT_SUCCESS;
return error;
}
static int write_symlink(git_oid *oid, git_odb *odb, const char *path, size_t link_size)
static int write_symlink(
git_oid *oid, git_odb *odb, const char *path, size_t link_size)
{
char *link_data;
ssize_t read_len;
int error;
link_data = git__malloc(link_size);
if (!link_data)
return GIT_ENOMEM;
GITERR_CHECK_ALLOC(link_data);
read_len = p_readlink(path, link_data, link_size);
if (read_len != (ssize_t)link_size) {
free(link_data);
return git__throw(GIT_EOSERR, "Failed to create blob. Can't read symlink");
giterr_set(GITERR_OS, "Failed to create blob. Can't read symlink '%s'", path);
git__free(link_data);
return -1;
}
error = git_odb_write(oid, odb, (void *)link_data, link_size, GIT_OBJ_BLOB);
free(link_data);
git__free(link_data);
return error;
}
int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path)
{
int error = GIT_SUCCESS;
int error;
git_buf full_path = GIT_BUF_INIT;
git_off_t size;
struct stat st;
......@@ -168,25 +158,18 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat
git_odb *odb = NULL;
workdir = git_repository_workdir(repo);
if (workdir == NULL)
return git__throw(GIT_ENOTFOUND, "Failed to create blob. (No working directory found)");
assert(workdir); /* error to call this on bare repo */
error = git_buf_joinpath(&full_path, workdir, path);
if (error < GIT_SUCCESS)
if ((error = git_buf_joinpath(&full_path, workdir, path)) < 0 ||
(error = git_path_lstat(full_path.ptr, &st)) < 0 ||
(error = git_repository_odb__weakptr(&odb, repo)) < 0)
{
git_buf_free(&full_path);
return error;
error = p_lstat(full_path.ptr, &st);
if (error < 0) {
error = git__throw(GIT_EOSERR, "Failed to stat blob. %s", strerror(errno));
goto cleanup;
}
size = st.st_size;
error = git_repository_odb__weakptr(&odb, repo);
if (error < GIT_SUCCESS)
goto cleanup;
if (S_ISLNK(st.st_mode)) {
error = write_symlink(oid, odb, full_path.ptr, (size_t)size);
} else {
......@@ -194,12 +177,12 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat
int filter_count;
/* Load the filters for writing this file to the ODB */
filter_count = git_filters_load(&write_filters, repo, path, GIT_FILTER_TO_ODB);
filter_count = git_filters_load(
&write_filters, repo, path, GIT_FILTER_TO_ODB);
if (filter_count < 0) {
/* Negative value means there was a critical error */
error = filter_count;
goto cleanup;
} else if (filter_count == 0) {
/* No filters need to be applied to the document: we can stream
* directly from disk */
......@@ -212,19 +195,20 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat
git_filters_free(&write_filters);
/*
* TODO: eventually support streaming filtered files, for files which are bigger
* than a given threshold. This is not a priority because applying a filter in
* streaming mode changes the final size of the blob, and without knowing its
* final size, the blob cannot be written in stream mode to the ODB.
* TODO: eventually support streaming filtered files, for files
* which are bigger than a given threshold. This is not a priority
* because applying a filter in streaming mode changes the final
* size of the blob, and without knowing its final size, the blob
* cannot be written in stream mode to the ODB.
*
* The plan is to do streaming writes to a tempfile on disk and then opening
* streaming that file to the ODB, using `write_file_stream`.
* The plan is to do streaming writes to a tempfile on disk and then
* opening streaming that file to the ODB, using
* `write_file_stream`.
*
* CAREFULLY DESIGNED APIS YO
*/
}
cleanup:
git_buf_free(&full_path);
return error;
}
......
/*
* Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "common.h"
#include "commit.h"
#include "branch.h"
#include "tag.h"
static int retrieve_branch_reference(
git_reference **branch_reference_out,
git_repository *repo,
const char *branch_name,
int is_remote)
{
git_reference *branch;
int error = -1;
char *prefix;
git_buf ref_name = GIT_BUF_INIT;
*branch_reference_out = NULL;
prefix = is_remote ? GIT_REFS_REMOTES_DIR : GIT_REFS_HEADS_DIR;
if (git_buf_joinpath(&ref_name, prefix, branch_name) < 0)
goto cleanup;
if ((error = git_reference_lookup(&branch, repo, ref_name.ptr)) < 0) {
giterr_set(GITERR_REFERENCE,
"Cannot locate %s branch '%s'.", is_remote ? "remote-tracking" : "local", branch_name);
goto cleanup;
}
*branch_reference_out = branch;
cleanup:
git_buf_free(&ref_name);
return error;
}
static int create_error_invalid(const char *msg)
{
giterr_set(GITERR_INVALID, "Cannot create branch - %s", msg);
return -1;
}
int git_branch_create(
git_oid *oid_out,
git_repository *repo,
const char *branch_name,
const git_object *target,
int force)
{
git_otype target_type = GIT_OBJ_BAD;
git_object *commit = NULL;
git_reference *branch = NULL;
git_buf canonical_branch_name = GIT_BUF_INIT;
int error = -1;
assert(repo && branch_name && target && oid_out);
if (git_object_owner(target) != repo)
return create_error_invalid("The given target does not belong to this repository");
target_type = git_object_type(target);
switch (target_type)
{
case GIT_OBJ_TAG:
if (git_tag_peel(&commit, (git_tag *)target) < 0)
goto cleanup;
if (git_object_type(commit) != GIT_OBJ_COMMIT) {
create_error_invalid("The given target does not resolve to a commit");
goto cleanup;
}
break;
case GIT_OBJ_COMMIT:
commit = (git_object *)target;
break;
default:
return create_error_invalid("Only git_tag and git_commit objects are valid targets.");
}
if (git_buf_joinpath(&canonical_branch_name, GIT_REFS_HEADS_DIR, branch_name) < 0)
goto cleanup;
if (git_reference_create_oid(&branch, repo, git_buf_cstr(&canonical_branch_name), git_object_id(commit), force) < 0)
goto cleanup;
git_oid_cpy(oid_out, git_reference_oid(branch));
error = 0;
cleanup:
if (target_type == GIT_OBJ_TAG)
git_object_free(commit);
git_reference_free(branch);
git_buf_free(&canonical_branch_name);
return error;
}
int git_branch_delete(git_repository *repo, const char *branch_name, git_branch_type branch_type)
{
git_reference *branch = NULL;
git_reference *head = NULL;
int error;
assert((branch_type == GIT_BRANCH_LOCAL) || (branch_type == GIT_BRANCH_REMOTE));
if ((error = retrieve_branch_reference(&branch, repo, branch_name, branch_type == GIT_BRANCH_REMOTE)) < 0)
goto on_error;
if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0) {
giterr_set(GITERR_REFERENCE, "Cannot locate HEAD.");
goto on_error;
}
if ((git_reference_type(head) == GIT_REF_SYMBOLIC)
&& (strcmp(git_reference_target(head), git_reference_name(branch)) == 0)) {
giterr_set(GITERR_REFERENCE,
"Cannot delete branch '%s' as it is the current HEAD of the repository.", branch_name);
goto on_error;
}
if (git_reference_delete(branch) < 0)
goto on_error;
git_reference_free(head);
return 0;
on_error:
git_reference_free(head);
git_reference_free(branch);
return -1;
}
typedef struct {
git_vector *branchlist;
unsigned int branch_type;
} branch_filter_data;
static int branch_list_cb(const char *branch_name, void *payload)
{
branch_filter_data *filter = (branch_filter_data *)payload;
if ((filter->branch_type & GIT_BRANCH_LOCAL && git__prefixcmp(branch_name, GIT_REFS_HEADS_DIR) == 0)
|| (filter->branch_type & GIT_BRANCH_REMOTE && git__prefixcmp(branch_name, GIT_REFS_REMOTES_DIR) == 0))
return git_vector_insert(filter->branchlist, git__strdup(branch_name));
return 0;
}
int git_branch_list(git_strarray *branch_names, git_repository *repo, unsigned int list_flags)
{
int error;
branch_filter_data filter;
git_vector branchlist;
assert(branch_names && repo);
if (git_vector_init(&branchlist, 8, NULL) < 0)
return -1;
filter.branchlist = &branchlist;
filter.branch_type = list_flags;
error = git_reference_foreach(repo, GIT_REF_OID|GIT_REF_PACKED, &branch_list_cb, (void *)&filter);
if (error < 0) {
git_vector_free(&branchlist);
return -1;
}
branch_names->strings = (char **)branchlist.contents;
branch_names->count = branchlist.length;
return 0;
}
int git_branch_move(git_repository *repo, const char *old_branch_name, const char *new_branch_name, int force)
{
git_reference *reference = NULL;
git_buf old_reference_name = GIT_BUF_INIT, new_reference_name = GIT_BUF_INIT;
int error = 0;
if ((error = git_buf_joinpath(&old_reference_name, GIT_REFS_HEADS_DIR, old_branch_name)) < 0)
goto cleanup;
/* We need to be able to return GIT_ENOTFOUND */
if ((error = git_reference_lookup(&reference, repo, git_buf_cstr(&old_reference_name))) < 0)
goto cleanup;
if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0)
goto cleanup;
error = git_reference_rename(reference, git_buf_cstr(&new_reference_name), force);
cleanup:
git_reference_free(reference);
git_buf_free(&old_reference_name);
git_buf_free(&new_reference_name);
return error;
}
/*
* Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_branch_h__
#define INCLUDE_branch_h__
#include "git2/branch.h"
struct git_branch {
char *remote; /* TODO: Make this a git_remote */
char *merge;
};
#endif
......@@ -17,8 +17,8 @@ char git_buf_initbuf[1];
static char git_buf__oom;
#define ENSURE_SIZE(b, d) \
if ((d) > buf->asize && git_buf_grow(b, (d)) < GIT_SUCCESS)\
return GIT_ENOMEM;
if ((d) > buf->asize && git_buf_grow(b, (d)) < 0)\
return -1;
void git_buf_init(git_buf *buf, size_t initial_size)
......@@ -34,10 +34,8 @@ void git_buf_init(git_buf *buf, size_t initial_size)
int git_buf_grow(git_buf *buf, size_t target_size)
{
int error = git_buf_try_grow(buf, target_size);
if (error != GIT_SUCCESS) {
if (error != 0)
buf->ptr = &git_buf__oom;
}
return error;
}
......@@ -47,10 +45,10 @@ int git_buf_try_grow(git_buf *buf, size_t target_size)
size_t new_size;
if (buf->ptr == &git_buf__oom)
return GIT_ENOMEM;
return -1;
if (target_size <= buf->asize)
return GIT_SUCCESS;
return 0;
if (buf->asize == 0) {
new_size = target_size;
......@@ -70,7 +68,7 @@ int git_buf_try_grow(git_buf *buf, size_t target_size)
new_ptr = git__realloc(new_ptr, new_size);
if (!new_ptr)
return GIT_ENOMEM;
return -1;
buf->asize = new_size;
buf->ptr = new_ptr;
......@@ -80,7 +78,7 @@ int git_buf_try_grow(git_buf *buf, size_t target_size)
buf->size = buf->asize - 1;
buf->ptr[buf->size] = '\0';
return GIT_SUCCESS;
return 0;
}
void git_buf_free(git_buf *buf)
......@@ -100,16 +98,11 @@ void git_buf_clear(git_buf *buf)
buf->ptr[0] = '\0';
}
int git_buf_oom(const git_buf *buf)
bool git_buf_oom(const git_buf *buf)
{
return (buf->ptr == &git_buf__oom);
}
int git_buf_lasterror(const git_buf *buf)
{
return (buf->ptr == &git_buf__oom) ? GIT_ENOMEM : GIT_SUCCESS;
}
int git_buf_set(git_buf *buf, const char *data, size_t len)
{
if (len == 0 || data == NULL) {
......@@ -122,7 +115,7 @@ int git_buf_set(git_buf *buf, const char *data, size_t len)
buf->size = len;
buf->ptr[buf->size] = '\0';
}
return GIT_SUCCESS;
return 0;
}
int git_buf_sets(git_buf *buf, const char *string)
......@@ -135,7 +128,7 @@ int git_buf_putc(git_buf *buf, char c)
ENSURE_SIZE(buf, buf->size + 2);
buf->ptr[buf->size++] = c;
buf->ptr[buf->size] = '\0';
return GIT_SUCCESS;
return 0;
}
int git_buf_put(git_buf *buf, const char *data, size_t len)
......@@ -144,7 +137,7 @@ int git_buf_put(git_buf *buf, const char *data, size_t len)
memmove(buf->ptr + buf->size, data, len);
buf->size += len;
buf->ptr[buf->size] = '\0';
return GIT_SUCCESS;
return 0;
}
int git_buf_puts(git_buf *buf, const char *string)
......@@ -153,22 +146,26 @@ int git_buf_puts(git_buf *buf, const char *string)
return git_buf_put(buf, string, strlen(string));
}
int git_buf_printf(git_buf *buf, const char *format, ...)
int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
{
int len;
va_list arglist;
ENSURE_SIZE(buf, buf->size + 1);
ENSURE_SIZE(buf, buf->size + (strlen(format) * 2));
while (1) {
va_start(arglist, format);
len = p_vsnprintf(buf->ptr + buf->size, buf->asize - buf->size, format, arglist);
va_end(arglist);
va_list args;
va_copy(args, ap);
len = p_vsnprintf(
buf->ptr + buf->size,
buf->asize - buf->size,
format, args
);
if (len < 0) {
free(buf->ptr);
git__free(buf->ptr);
buf->ptr = &git_buf__oom;
return GIT_ENOMEM;
return -1;
}
if ((size_t)len + 1 <= buf->asize - buf->size) {
......@@ -179,7 +176,19 @@ int git_buf_printf(git_buf *buf, const char *format, ...)
ENSURE_SIZE(buf, buf->size + len + 1);
}
return GIT_SUCCESS;
return 0;
}
int git_buf_printf(git_buf *buf, const char *format, ...)
{
int r;
va_list ap;
va_start(ap, format);
r = git_buf_vprintf(buf, format, ap);
va_end(ap);
return r;
}
void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf)
......@@ -220,8 +229,8 @@ void git_buf_truncate(git_buf *buf, size_t len)
void git_buf_rtruncate_at_char(git_buf *buf, char separator)
{
int idx = git_buf_rfind_next(buf, separator);
git_buf_truncate(buf, idx < 0 ? 0 : idx);
ssize_t idx = git_buf_rfind_next(buf, separator);
git_buf_truncate(buf, idx < 0 ? 0 : (size_t)idx);
}
void git_buf_swap(git_buf *buf_a, git_buf *buf_b)
......@@ -262,9 +271,9 @@ void git_buf_attach(git_buf *buf, char *ptr, size_t asize)
int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
{
va_list ap;
int i, error = GIT_SUCCESS;
size_t total_size = 0;
char *out;
int i;
size_t total_size = 0, original_size = buf->size;
char *out, *original = buf->ptr;
if (buf->size > 0 && buf->ptr[buf->size - 1] != separator)
++total_size; /* space for initial separator */
......@@ -288,9 +297,10 @@ int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
va_end(ap);
/* expand buffer if needed */
if (total_size > 0 &&
(error = git_buf_grow(buf, buf->size + total_size + 1)) < GIT_SUCCESS)
return error;
if (total_size == 0)
return 0;
if (git_buf_grow(buf, buf->size + total_size + 1) < 0)
return -1;
out = buf->ptr + buf->size;
......@@ -307,12 +317,23 @@ int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
if (!segment)
continue;
/* deal with join that references buffer's original content */
if (segment >= original && segment < original + original_size) {
size_t offset = (segment - original);
segment = buf->ptr + offset;
segment_len = original_size - offset;
} else {
segment_len = strlen(segment);
}
/* skip leading separators */
if (out > buf->ptr && out[-1] == separator)
while (*segment == separator) segment++;
while (segment_len > 0 && *segment == separator) {
segment++;
segment_len--;
}
/* copy over next buffer */
segment_len = strlen(segment);
if (segment_len > 0) {
memmove(out, segment, segment_len);
out += segment_len;
......@@ -328,7 +349,7 @@ int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
buf->size = out - buf->ptr;
buf->ptr[buf->size] = '\0';
return error;
return 0;
}
int git_buf_join(
......@@ -337,7 +358,6 @@ int git_buf_join(
const char *str_a,
const char *str_b)
{
int error = GIT_SUCCESS;
size_t strlen_a = str_a ? strlen(str_a) : 0;
size_t strlen_b = strlen(str_b);
int need_sep = 0;
......@@ -357,9 +377,8 @@ int git_buf_join(
if (str_a >= buf->ptr && str_a < buf->ptr + buf->size)
offset_a = str_a - buf->ptr;
error = git_buf_grow(buf, strlen_a + strlen_b + need_sep + 1);
if (error < GIT_SUCCESS)
return error;
if (git_buf_grow(buf, strlen_a + strlen_b + need_sep + 1) < 0)
return -1;
/* fix up internal pointers */
if (offset_a >= 0)
......@@ -375,7 +394,7 @@ int git_buf_join(
buf->size = strlen_a + strlen_b + need_sep;
buf->ptr[buf->size] = '\0';
return error;
return 0;
}
void git_buf_rtrim(git_buf *buf)
......
......@@ -32,7 +32,7 @@ void git_buf_init(git_buf *buf, size_t initial_size);
* If the allocation fails, this will return an error and the buffer
* will be marked as invalid for future operations. The existing
* contents of the buffer will be preserved however.
* @return GIT_SUCCESS or GIT_ENOMEM on failure
* @return 0 on success or -1 on failure
*/
int git_buf_grow(git_buf *buf, size_t target_size);
......@@ -57,23 +57,18 @@ void git_buf_attach(git_buf *buf, char *ptr, size_t asize);
* further calls to modify the buffer will fail. Check git_buf_oom() at the
* end of your sequence and it will be true if you ran out of memory at any
* point with that buffer.
* @return 0 if no error, 1 if allocation error.
*/
int git_buf_oom(const git_buf *buf);
/**
* Just like git_buf_oom, except returns appropriate error code.
* @return GIT_ENOMEM if allocation error, GIT_SUCCESS if not.
*
* @return false if no error, true if allocation error
*/
int git_buf_lasterror(const git_buf *buf);
bool git_buf_oom(const git_buf *buf);
/*
* The functions below that return int values, will return GIT_ENOMEM
* if they fail to expand the git_buf when they are called, otherwise
* GIT_SUCCESS. Passing a git_buf that has failed an allocation will
* automatically return GIT_ENOMEM for all further calls. As a result,
* you can ignore the return code of these functions and call them in a
* series then just call git_buf_lasterror at the end.
* Functions below that return int value error codes will return 0 on
* success or -1 on failure (which generally means an allocation failed).
* Using a git_buf where the allocation has failed with result in -1 from
* all further calls using that buffer. As a result, you can ignore the
* return code of these functions and call them in a series then just call
* git_buf_oom at the end.
*/
int git_buf_set(git_buf *buf, const char *data, size_t len);
int git_buf_sets(git_buf *buf, const char *string);
......@@ -81,6 +76,7 @@ int git_buf_putc(git_buf *buf, char c);
int git_buf_put(git_buf *buf, const char *data, size_t len);
int git_buf_puts(git_buf *buf, const char *string);
int git_buf_printf(git_buf *buf, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
int git_buf_vprintf(git_buf *buf, const char *format, va_list ap);
void git_buf_clear(git_buf *buf);
void git_buf_consume(git_buf *buf, const char *end);
void git_buf_truncate(git_buf *buf, size_t len);
......@@ -91,25 +87,30 @@ int git_buf_join(git_buf *buf, char separator, const char *str_a, const char *st
/**
* Join two strings as paths, inserting a slash between as needed.
* @return error code or GIT_SUCCESS
* @return 0 on success, -1 on failure
*/
GIT_INLINE(int) git_buf_joinpath(git_buf *buf, const char *a, const char *b)
{
return git_buf_join(buf, '/', a, b);
}
GIT_INLINE(const char *) git_buf_cstr(git_buf *buf)
GIT_INLINE(const char *) git_buf_cstr(const git_buf *buf)
{
return buf->ptr;
}
GIT_INLINE(size_t) git_buf_len(const git_buf *buf)
{
return buf->size;
}
void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf);
#define git_buf_PUTS(buf, str) git_buf_put(buf, str, sizeof(str) - 1)
GIT_INLINE(int) git_buf_rfind_next(git_buf *buf, char ch)
GIT_INLINE(ssize_t) git_buf_rfind_next(git_buf *buf, char ch)
{
int idx = buf->size - 1;
ssize_t idx = (ssize_t)buf->size - 1;
while (idx >= 0 && buf->ptr[idx] == ch) idx--;
while (idx >= 0 && buf->ptr[idx] != ch) idx--;
return idx;
......
......@@ -9,21 +9,14 @@
#include "repository.h"
#include "commit.h"
#include "thread-utils.h"
#include "util.h"
#include "cache.h"
int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr)
{
if (size < 8)
size = 8;
/* round up size to closest power of 2 */
size--;
size |= size >> 1;
size |= size >> 2;
size |= size >> 4;
size |= size >> 8;
size |= size >> 16;
size++;
size = git__size_t_powerof2(size);
cache->size_mask = size - 1;
cache->lru_count = 0;
......@@ -32,11 +25,10 @@ int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_pt
git_mutex_init(&cache->lock);
cache->nodes = git__malloc(size * sizeof(git_cached_obj *));
if (cache->nodes == NULL)
return GIT_ENOMEM;
GITERR_CHECK_ALLOC(cache->nodes);
memset(cache->nodes, 0x0, size * sizeof(git_cached_obj *));
return GIT_SUCCESS;
return 0;
}
void git_cache_free(git_cache *cache)
......
......@@ -50,4 +50,20 @@
# pragma warning ( disable : 4127 )
#endif
#if defined (_MSC_VER)
typedef unsigned char bool;
# define true 1
# define false 0
#else
# include <stdbool.h>
#endif
#ifndef va_copy
# ifdef __va_copy
# define va_copy(dst, src) __va_copy(dst, src)
# else
# define va_copy(dst, src) ((dst) = (src))
# endif
#endif
#endif /* INCLUDE_compat_h__ */
......@@ -69,24 +69,84 @@ int git_commit_create_v(
...)
{
va_list ap;
int i, error;
int i, res;
const git_commit **parents;
parents = git__malloc(parent_count * sizeof(git_commit *));
GITERR_CHECK_ALLOC(parents);
va_start(ap, parent_count);
for (i = 0; i < parent_count; ++i)
parents[i] = va_arg(ap, const git_commit *);
va_end(ap);
error = git_commit_create(
res = git_commit_create(
oid, repo, update_ref, author, committer,
message_encoding, message,
tree, parent_count, parents);
git__free((void *)parents);
return res;
}
/* Update the reference named `ref_name` so it points to `oid` */
static int update_reference(git_repository *repo, git_oid *oid, const char *ref_name)
{
git_reference *ref;
int res;
res = git_reference_lookup(&ref, repo, ref_name);
/* If we haven't found the reference at all, we assume we need to create
* a new reference and that's it */
if (res == GIT_ENOTFOUND) {
giterr_clear();
return git_reference_create_oid(NULL, repo, ref_name, oid, 1);
}
if (res < 0)
return -1;
/* If we have found a reference, but it's symbolic, we need to update
* the direct reference it points to */
if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
git_reference *aux;
const char *sym_target;
/* The target pointed at by this reference */
sym_target = git_reference_target(ref);
/* resolve the reference to the target it points to */
res = git_reference_resolve(&aux, ref);
return error;
/*
* if the symbolic reference pointed to an inexisting ref,
* this is means we're creating a new branch, for example.
* We need to create a new direct reference with that name
*/
if (res == GIT_ENOTFOUND) {
giterr_clear();
res = git_reference_create_oid(NULL, repo, sym_target, oid, 1);
git_reference_free(ref);
return res;
}
/* free the original symbolic reference now; not before because
* we're using the `sym_target` pointer */
git_reference_free(ref);
if (res < 0)
return -1;
/* store the newly found direct reference in its place */
ref = aux;
}
/* ref is made to point to `oid`: ref is either the original reference,
* or the target of the symbolic reference we've looked up */
res = git_reference_set_oid(ref, oid);
git_reference_free(ref);
return res;
}
int git_commit_create(
......@@ -102,20 +162,15 @@ int git_commit_create(
const git_commit *parents[])
{
git_buf commit = GIT_BUF_INIT;
int error, i;
int i;
git_odb *odb;
if (git_object_owner((const git_object *)tree) != repo)
return git__throw(GIT_EINVALIDARGS, "The given tree does not belong to this repository");
assert(git_object_owner((const git_object *)tree) == repo);
git_oid__writebuf(&commit, "tree ", git_object_id((const git_object *)tree));
for (i = 0; i < parent_count; ++i) {
if (git_object_owner((const git_object *)parents[i]) != repo) {
error = git__throw(GIT_EINVALIDARGS, "The given parent does not belong to this repository");
goto cleanup;
}
assert(git_object_owner((const git_object *)parents[i]) == repo);
git_oid__writebuf(&commit, "parent ", git_object_id((const git_object *)parents[i]));
}
......@@ -128,67 +183,25 @@ int git_commit_create(
git_buf_putc(&commit, '\n');
git_buf_puts(&commit, message);
if (git_buf_oom(&commit)) {
error = git__throw(git_buf_lasterror(&commit),
"Not enough memory to build the commit data");
goto cleanup;
}
error = git_repository_odb__weakptr(&odb, repo);
if (error < GIT_SUCCESS)
goto cleanup;
error = git_odb_write(oid, odb, commit.ptr, commit.size, GIT_OBJ_COMMIT);
git_buf_free(&commit);
if (error == GIT_SUCCESS && update_ref != NULL) {
git_reference *head;
git_reference *target;
error = git_reference_lookup(&head, repo, update_ref);
if (error < GIT_SUCCESS && error != GIT_ENOTFOUND)
return git__rethrow(error, "Failed to create commit");
if (error != GIT_ENOTFOUND) {
update_ref = git_reference_target(head);
error = git_reference_resolve(&target, head);
}
if (error < GIT_SUCCESS) {
if (error != GIT_ENOTFOUND) {
git_reference_free(head);
return git__rethrow(error, "Failed to create commit");
}
/*
* 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.
*/
error = git_reference_create_oid(&target, repo, update_ref, oid, 1);
git_reference_free(head);
if (error == GIT_SUCCESS)
git_reference_free(target);
if (git_buf_oom(&commit))
goto on_error;
return error;
}
if (git_repository_odb__weakptr(&odb, repo) < 0)
goto on_error;
error = git_reference_set_oid(target, oid);
if (git_odb_write(oid, odb, commit.ptr, commit.size, GIT_OBJ_COMMIT) < 0)
goto on_error;
git_reference_free(head);
git_reference_free(target);
}
git_buf_free(&commit);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to create commit");
if (update_ref != NULL)
return update_reference(repo, oid, update_ref);
return GIT_SUCCESS;
return 0;
cleanup:
on_error:
git_buf_free(&commit);
return error;
return -1;
}
int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len)
......@@ -197,35 +210,40 @@ int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len)
const char *buffer_end = (const char *)data + len;
git_oid parent_oid;
int error;
git_vector_init(&commit->parent_oids, 4, NULL);
if ((error = git_oid__parse(&commit->tree_oid, &buffer, buffer_end, "tree ")) < GIT_SUCCESS)
return git__rethrow(error, "Failed to parse buffer");
if (git_oid__parse(&commit->tree_oid, &buffer, buffer_end, "tree ") < 0)
goto bad_buffer;
/*
* TODO: commit grafts!
*/
while (git_oid__parse(&parent_oid, &buffer, buffer_end, "parent ") == GIT_SUCCESS) {
while (git_oid__parse(&parent_oid, &buffer, buffer_end, "parent ") == 0) {
git_oid *new_oid;
new_oid = git__malloc(sizeof(git_oid));
GITERR_CHECK_ALLOC(new_oid);
git_oid_cpy(new_oid, &parent_oid);
if (git_vector_insert(&commit->parent_oids, new_oid) < GIT_SUCCESS)
return GIT_ENOMEM;
if (git_vector_insert(&commit->parent_oids, new_oid) < 0)
return -1;
}
commit->author = git__malloc(sizeof(git_signature));
if ((error = git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n')) < GIT_SUCCESS)
return git__rethrow(error, "Failed to parse commit");
GITERR_CHECK_ALLOC(commit->author);
if (git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n') < 0)
return -1;
/* Always parse the committer; we need the commit time */
commit->committer = git__malloc(sizeof(git_signature));
if ((error = git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n')) < GIT_SUCCESS)
return git__rethrow(error, "Failed to parse commit");
GITERR_CHECK_ALLOC(commit->committer);
if (git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n') < 0)
return -1;
if (git__prefixcmp(buffer, "encoding ") == 0) {
const char *encoding_end;
......@@ -236,8 +254,7 @@ int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len)
encoding_end++;
commit->message_encoding = git__strndup(buffer, encoding_end - buffer);
if (!commit->message_encoding)
return GIT_ENOMEM;
GITERR_CHECK_ALLOC(commit->message_encoding);
buffer = encoding_end;
}
......@@ -248,11 +265,14 @@ int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len)
if (buffer <= buffer_end) {
commit->message = git__strndup(buffer, buffer_end - buffer);
if (!commit->message)
return GIT_ENOMEM;
GITERR_CHECK_ALLOC(commit->message);
}
return GIT_SUCCESS;
return 0;
bad_buffer:
giterr_set(GITERR_OBJECT, "Failed to parse bad commit object");
return -1;
}
int git_commit__parse(git_commit *commit, git_odb_object *obj)
......@@ -290,8 +310,10 @@ int git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n)
assert(commit);
parent_oid = git_vector_get(&commit->parent_oids, n);
if (parent_oid == NULL)
return git__throw(GIT_ENOTFOUND, "Parent %u does not exist", n);
if (parent_oid == NULL) {
giterr_set(GITERR_INVALID, "Parent %u does not exist", n);
return GIT_ENOTFOUND;
}
return git_commit_lookup(parent, commit->object.repo, parent_oid);
}
......
......@@ -46,6 +46,8 @@
#include "thread-utils.h"
#include "bswap.h"
#include <regex.h>
extern void git___throw(const char *, ...) GIT_FORMAT_PRINTF(1, 2);
#define git__throw(error, ...) \
(git___throw(__VA_ARGS__), error)
......@@ -54,6 +56,16 @@ extern void git___rethrow(const char *, ...) GIT_FORMAT_PRINTF(1, 2);
#define git__rethrow(error, ...) \
(git___rethrow(__VA_ARGS__), error)
#define GITERR_CHECK_ALLOC(ptr) if (ptr == NULL) { return -1; }
void giterr_set_oom(void);
void giterr_set(int error_class, const char *string, ...);
void giterr_clear(void);
void giterr_set_str(int error_class, const char *string);
void giterr_set_regex(const regex_t *regex, int error_code);
#include "util.h"
......
......@@ -25,4 +25,9 @@ struct git_config {
extern int git_config_find_global_r(git_buf *global_config_path);
extern int git_config_find_system_r(git_buf *system_config_path);
extern int git_config_parse_bool(int *out, const char *bool_string);
extern int git_config_lookup_map_value(
git_cvar_map *maps, size_t map_n, const char *value, int *out);
#endif
......@@ -7,7 +7,6 @@
#include "common.h"
#include "fileops.h"
#include "hashtable.h"
#include "config.h"
#include "git2/config.h"
#include "vector.h"
......
/*
* Copyright (C) 2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_config_file_h__
#define INCLUDE_config_file_h__
#include "git2/config.h"
GIT_INLINE(int) git_config_file_open(git_config_file *cfg)
{
return cfg->open(cfg);
}
GIT_INLINE(void) git_config_file_free(git_config_file *cfg)
{
cfg->free(cfg);
}
GIT_INLINE(int) git_config_file_foreach(
git_config_file *cfg,
int (*fn)(const char *key, const char *value, void *data),
void *data)
{
return cfg->foreach(cfg, fn, data);
}
#endif
......@@ -82,7 +82,8 @@ static int crlf_load_attributes(struct crlf_attrs *ca, git_repository *repo, con
const char *attr_vals[NUM_CONV_ATTRS];
int error;
error = git_attr_get_many(repo, path, NUM_CONV_ATTRS, attr_names, attr_vals);
error = git_attr_get_many(
repo, 0, path, NUM_CONV_ATTRS, attr_names, attr_vals);
if (error == GIT_ENOTFOUND) {
ca->crlf_action = GIT_CRLF_GUESS;
......@@ -105,7 +106,7 @@ static int crlf_load_attributes(struct crlf_attrs *ca, git_repository *repo, con
static int drop_crlf(git_buf *dest, const git_buf *source)
{
const char *scan = source->ptr, *next;
const char *scan_end = source->ptr + source->size;
const char *scan_end = git_buf_cstr(source) + git_buf_len(source);
/* Main scan loop. Find the next carriage return and copy the
* whole chunk up to that point to the destination buffer.
......@@ -128,8 +129,7 @@ static int drop_crlf(git_buf *dest, const git_buf *source)
/* Copy remaining input into dest */
git_buf_put(dest, scan, scan_end - scan);
return git_buf_lasterror(dest);
return 0;
}
static int crlf_apply_to_odb(git_filter *self, git_buf *dest, const git_buf *source)
......@@ -139,7 +139,7 @@ static int crlf_apply_to_odb(git_filter *self, git_buf *dest, const git_buf *sou
assert(self && dest && source);
/* Empty file? Nothing to do */
if (source->size == 0)
if (git_buf_len(source) == 0)
return 0;
/* Heuristics to see if we can skip the conversion.
......@@ -217,8 +217,7 @@ int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const
/* If we're good, we create a new filter object and push it
* into the filters array */
filter = git__malloc(sizeof(struct crlf_filter));
if (filter == NULL)
return GIT_ENOMEM;
GITERR_CHECK_ALLOC(filter);
filter->f.apply = &crlf_apply_to_odb;
filter->f.do_free = NULL;
......
......@@ -51,14 +51,19 @@ int git__delta_apply(
* if not we would underflow while accessing data from the
* base object, resulting in data corruption or segfault.
*/
if ((hdr_sz(&base_sz, &delta, delta_end) < 0) || (base_sz != base_len))
return git__throw(GIT_ERROR, "Failed to apply delta. Base size does not match given data");
if ((hdr_sz(&base_sz, &delta, delta_end) < 0) || (base_sz != base_len)) {
giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data");
return -1;
}
if (hdr_sz(&res_sz, &delta, delta_end) < 0) {
giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data");
return -1;
}
if (hdr_sz(&res_sz, &delta, delta_end) < 0)
return git__throw(GIT_ERROR, "Failed to apply delta. Base size does not match given data");
res_dp = git__malloc(res_sz + 1);
GITERR_CHECK_ALLOC(res_dp);
if ((res_dp = git__malloc(res_sz + 1)) == NULL)
return GIT_ENOMEM;
res_dp[res_sz] = '\0';
out->data = res_dp;
out->len = res_sz;
......@@ -111,5 +116,6 @@ int git__delta_apply(
fail:
git__free(out->data);
out->data = NULL;
return git__throw(GIT_ERROR, "Failed to apply delta");
giterr_set(GITERR_INVALID, "Failed to apply delta");
return -1;
}
......@@ -12,13 +12,28 @@
#include "buffer.h"
#include "iterator.h"
#include "repository.h"
#include "pool.h"
#define DIFF_OLD_PREFIX_DEFAULT "a/"
#define DIFF_NEW_PREFIX_DEFAULT "b/"
enum {
GIT_DIFFCAPS_HAS_SYMLINKS = (1 << 0), /* symlinks on platform? */
GIT_DIFFCAPS_ASSUME_UNCHANGED = (1 << 1), /* use stat? */
GIT_DIFFCAPS_TRUST_EXEC_BIT = (1 << 2), /* use st_mode exec bit? */
GIT_DIFFCAPS_TRUST_CTIME = (1 << 3), /* use st_ctime? */
GIT_DIFFCAPS_USE_DEV = (1 << 4), /* use st_dev? */
};
struct git_diff_list {
git_repository *repo;
git_diff_options opts;
git_vector pathspec;
git_vector deltas; /* vector of git_diff_file_delta */
git_pool pool;
git_iterator_type_t old_src;
git_iterator_type_t new_src;
uint32_t diffcaps;
};
#endif
......
......@@ -6,99 +6,114 @@
*/
#include "common.h"
#include "global.h"
#include "posix.h"
#include "buffer.h"
#include <stdarg.h>
static struct {
int num;
const char *str;
} error_codes[] = {
{GIT_ERROR, "Unspecified error"},
{GIT_ENOTOID, "Input was not a properly formatted Git object id."},
{GIT_ENOTFOUND, "Object does not exist in the scope searched."},
{GIT_ENOMEM, "Not enough space available."},
{GIT_EOSERR, "Consult the OS error information."},
{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 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"},
{GIT_EZLIB, "The Z library failed to inflate/deflate an object's data"},
{GIT_EBUSY, "The queried object is currently busy"},
{GIT_EINVALIDPATH, "The path is invalid"},
{GIT_EBAREINDEX, "The index file is not backed up by an existing repository"},
{GIT_EINVALIDREFNAME, "The name of the reference is not valid"},
{GIT_EREFCORRUPTED, "The specified reference has its data corrupted"},
{GIT_ETOONESTEDSYMREF, "The specified symbolic reference is too deeply nested"},
{GIT_EPACKEDREFSCORRUPTED, "The pack-refs file is either corrupted of its format is not currently supported"},
{GIT_EINVALIDPATH, "The path is invalid" },
{GIT_EREVWALKOVER, "The revision walker is empty; there are no more commits left to iterate"},
{GIT_EINVALIDREFSTATE, "The state of the reference is not valid"},
{GIT_ENOTIMPLEMENTED, "This feature has not been implemented yet"},
{GIT_EEXISTS, "A reference with this name already exists"},
{GIT_EOVERFLOW, "The given integer literal is too large to be parsed"},
{GIT_ENOTNUM, "The given literal is not a valid number"},
{GIT_EAMBIGUOUSOIDPREFIX, "The given oid prefix is ambiguous"},
/********************************************
* New error handling
********************************************/
static git_error g_git_oom_error = {
"Out of memory",
GITERR_NOMEMORY
};
const char *git_strerror(int num)
static void set_error(int error_class, char *string)
{
size_t i;
git_error *error = &GIT_GLOBAL->error_t;
if (num == GIT_EOSERR)
return strerror(errno);
for (i = 0; i < ARRAY_SIZE(error_codes); i++)
if (num == error_codes[i].num)
return error_codes[i].str;
git__free(error->message);
return "Unknown error";
}
error->message = string;
error->klass = error_class;
#define ERROR_MAX_LEN 1024
GIT_GLOBAL->last_error = error;
}
void git___rethrow(const char *msg, ...)
void giterr_set_oom(void)
{
char new_error[ERROR_MAX_LEN];
char *last_error;
char *old_error = NULL;
va_list va;
last_error = GIT_GLOBAL->error.last;
va_start(va, msg);
vsnprintf(new_error, ERROR_MAX_LEN, msg, va);
va_end(va);
old_error = git__strdup(last_error);
snprintf(last_error, ERROR_MAX_LEN, "%s \n - %s", new_error, old_error);
GIT_GLOBAL->last_error = &g_git_oom_error;
}
git__free(old_error);
void giterr_set(int error_class, const char *string, ...)
{
git_buf buf = GIT_BUF_INIT;
va_list arglist;
int unix_error_code = 0;
#ifdef GIT_WIN32
DWORD win32_error_code = 0;
#endif
if (error_class == GITERR_OS) {
unix_error_code = errno;
errno = 0;
#ifdef GIT_WIN32
win32_error_code = GetLastError();
SetLastError(0);
#endif
}
va_start(arglist, string);
git_buf_vprintf(&buf, string, arglist);
va_end(arglist);
/* automatically suffix strerror(errno) for GITERR_OS errors */
if (error_class == GITERR_OS) {
if (unix_error_code != 0) {
git_buf_PUTS(&buf, ": ");
git_buf_puts(&buf, strerror(unix_error_code));
}
#ifdef GIT_WIN32
else if (win32_error_code != 0) {
LPVOID lpMsgBuf = NULL;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, win32_error_code, 0, (LPTSTR) &lpMsgBuf, 0, NULL);
if (lpMsgBuf) {
git_buf_PUTS(&buf, ": ");
git_buf_puts(&buf, lpMsgBuf);
LocalFree(lpMsgBuf);
}
}
#endif
}
if (!git_buf_oom(&buf))
set_error(error_class, git_buf_detach(&buf));
}
void git___throw(const char *msg, ...)
void giterr_set_str(int error_class, const char *string)
{
va_list va;
char *message = git__strdup(string);
va_start(va, msg);
vsnprintf(GIT_GLOBAL->error.last, ERROR_MAX_LEN, msg, va);
va_end(va);
if (message)
set_error(error_class, message);
}
const char *git_lasterror(void)
void giterr_set_regex(const regex_t *regex, int error_code)
{
char *last_error = GIT_GLOBAL->error.last;
if (!last_error[0])
return NULL;
char error_buf[1024];
regerror(error_code, regex, error_buf, sizeof(error_buf));
giterr_set_str(GITERR_REGEX, error_buf);
}
return last_error;
void giterr_clear(void)
{
GIT_GLOBAL->last_error = NULL;
}
void git_clearerror(void)
const git_error *giterr_last(void)
{
char *last_error = GIT_GLOBAL->error.last;
last_error[0] = '\0';
return GIT_GLOBAL->last_error;
}
......@@ -9,6 +9,7 @@
#include "git2/oid.h"
#include "git2/refs.h"
#include "git2/revwalk.h"
#include "git2/indexer.h"
#include "common.h"
#include "transport.h"
......@@ -28,19 +29,13 @@ struct filter_payload {
static int filter_ref__cb(git_remote_head *head, void *payload)
{
struct filter_payload *p = payload;
int error;
if (!p->found_head && strcmp(head->name, GIT_HEAD_FILE) == 0) {
p->found_head = 1;
} else {
/* If it doesn't match the refpec, we don't want it */
error = git_refspec_src_match(p->spec, head->name);
if (error == GIT_ENOMATCH)
return GIT_SUCCESS;
if (error < GIT_SUCCESS)
return git__rethrow(error, "Error matching remote ref name");
if (!git_refspec_src_matches(p->spec, head->name))
return 0;
}
/* If we have the object, mark it so we don't ask for it */
......@@ -54,7 +49,6 @@ static int filter_ref__cb(git_remote_head *head, void *payload)
static int filter_wants(git_remote *remote)
{
int error;
struct filter_payload p;
git_vector_clear(&remote->refs);
......@@ -69,9 +63,8 @@ static int filter_wants(git_remote *remote)
p.found_head = 0;
p.remote = remote;
error = git_repository_odb__weakptr(&p.odb, remote->repo);
if (error < GIT_SUCCESS)
return error;
if (git_repository_odb__weakptr(&p.odb, remote->repo) < 0)
return -1;
return remote->transport->ls(remote->transport, &filter_ref__cb, &p);
}
......@@ -83,19 +76,16 @@ static int filter_wants(git_remote *remote)
*/
int git_fetch_negotiate(git_remote *remote)
{
int error;
git_transport *t = remote->transport;
error = filter_wants(remote);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to filter the reference list for wants");
if (filter_wants(remote) < 0) {
giterr_set(GITERR_NET, "Failed to filter the reference list for wants");
return -1;
}
/* Don't try to negotiate when we don't want anything */
if (remote->refs.length == 0)
return GIT_SUCCESS;
if (!remote->need_pack)
return GIT_SUCCESS;
if (remote->refs.length == 0 || !remote->need_pack)
return 0;
/*
* Now we have everything set up so we can start tell the server
......@@ -104,75 +94,103 @@ int git_fetch_negotiate(git_remote *remote)
return t->negotiate_fetch(t, remote->repo, &remote->refs);
}
int git_fetch_download_pack(char **out, git_remote *remote)
int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats)
{
if(!remote->need_pack) {
*out = NULL;
return GIT_SUCCESS;
}
if(!remote->need_pack)
return 0;
return remote->transport->download_pack(out, remote->transport, remote->repo);
return remote->transport->download_pack(remote->transport, remote->repo, bytes, stats);
}
/* Receiving data from a socket and storing it is pretty much the same for git and HTTP */
int git_fetch__download_pack(
char **out,
const char *buffered,
size_t buffered_size,
GIT_SOCKET fd,
git_repository *repo)
git_repository *repo,
git_off_t *bytes,
git_indexer_stats *stats)
{
git_filebuf file = GIT_FILEBUF_INIT;
int error;
int recvd;
char buff[1024];
git_buf path = GIT_BUF_INIT;
static const char suff[] = "/objects/pack/pack-received";
gitno_buffer buf;
git_indexer_stream *idx;
gitno_buffer_setup(&buf, buff, sizeof(buff), fd);
if (memcmp(buffered, "PACK", strlen("PACK"))) {
return git__throw(GIT_ERROR, "The pack doesn't start with the signature");
giterr_set(GITERR_NET, "The pack doesn't start with the signature");
return -1;
}
error = git_buf_joinpath(&path, repo->path_repository, suff);
if (error < GIT_SUCCESS)
goto cleanup;
if (git_indexer_stream_new(&idx, git_repository_path(repo)) < 0)
return -1;
error = git_filebuf_open(&file, path.ptr, GIT_FILEBUF_TEMPORARY);
if (error < GIT_SUCCESS)
goto cleanup;
memset(stats, 0, sizeof(git_indexer_stats));
if (git_indexer_stream_add(idx, buffered, buffered_size, stats) < 0)
goto on_error;
/* Part of the packfile has been received, don't loose it */
error = git_filebuf_write(&file, buffered, buffered_size);
if (error < GIT_SUCCESS)
goto cleanup;
*bytes = buffered_size;
while (1) {
error = git_filebuf_write(&file, buf.data, buf.offset);
if (error < GIT_SUCCESS)
goto cleanup;
do {
if (git_indexer_stream_add(idx, buf.data, buf.offset, stats) < 0)
goto on_error;
gitno_consume_n(&buf, buf.offset);
error = gitno_recv(&buf);
if (error < GIT_SUCCESS)
goto cleanup;
if (error == 0) /* Orderly shutdown */
break;
}
if ((recvd = gitno_recv(&buf)) < 0)
goto on_error;
*bytes += recvd;
} while(recvd > 0);
if (git_indexer_stream_finalize(idx, stats))
goto on_error;
git_indexer_stream_free(idx);
return 0;
on_error:
git_indexer_stream_free(idx);
return -1;
}
int git_fetch_setup_walk(git_revwalk **out, git_repository *repo)
{
git_revwalk *walk;
git_strarray refs;
unsigned int i;
git_reference *ref;
if (git_reference_listall(&refs, repo, GIT_REF_LISTALL) < 0)
return -1;
if (git_revwalk_new(&walk, repo) < 0)
return -1;
git_revwalk_sorting(walk, GIT_SORT_TIME);
for (i = 0; i < refs.count; ++i) {
/* No tags */
if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR))
continue;
if (git_reference_lookup(&ref, repo, refs.strings[i]) < 0)
goto on_error;
if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
continue;
if (git_revwalk_push(walk, git_reference_oid(ref)) < 0)
goto on_error;
*out = git__strdup(file.path_lock);
if (*out == NULL) {
error = GIT_ENOMEM;
goto cleanup;
git_reference_free(ref);
}
/* A bit dodgy, but we need to keep the pack at the temporary path */
error = git_filebuf_commit_at(&file, file.path_lock, GIT_PACK_FILE_MODE);
cleanup:
if (error < GIT_SUCCESS)
git_filebuf_cleanup(&file);
git_buf_free(&path);
git_strarray_free(&refs);
*out = walk;
return 0;
return error;
on_error:
git_reference_free(ref);
git_strarray_free(&refs);
return -1;
}
......@@ -10,9 +10,10 @@
#include "netops.h"
int git_fetch_negotiate(git_remote *remote);
int git_fetch_download_pack(char **out, git_remote *remote);
int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats);
int git_fetch__download_pack(char **out, const char *buffered, size_t buffered_size,
GIT_SOCKET fd, git_repository *repo);
int git_fetch__download_pack(const char *buffered, size_t buffered_size, GIT_SOCKET fd,
git_repository *repo, git_off_t *bytes, git_indexer_stats *stats);
int git_fetch_setup_walk(git_revwalk **out, git_repository *repo);
#endif
......@@ -19,7 +19,8 @@
#define GIT_FILEBUF_APPEND (1 << 2)
#define GIT_FILEBUF_FORCE (1 << 3)
#define GIT_FILEBUF_TEMPORARY (1 << 4)
#define GIT_FILEBUF_DEFLATE_SHIFT (5)
#define GIT_FILEBUF_DO_NOT_BUFFER (1 << 5)
#define GIT_FILEBUF_DEFLATE_SHIFT (6)
#define GIT_FILELOCK_EXTENSION ".lock\0"
#define GIT_FILELOCK_EXTLENGTH 6
......@@ -40,25 +41,37 @@ struct git_filebuf {
size_t buf_size, buf_pos;
git_file fd;
bool fd_is_open;
bool do_not_buffer;
int last_error;
};
typedef struct git_filebuf git_filebuf;
#define GIT_FILEBUF_INIT {0}
/* The git_filebuf object lifecycle is:
/*
* The git_filebuf object lifecycle is:
* - Allocate git_filebuf, preferably using GIT_FILEBUF_INIT.
*
* - Call git_filebuf_open() to initialize the filebuf for use.
*
* - Make as many calls to git_filebuf_write(), git_filebuf_printf(),
* git_filebuf_reserve() as you like.
* git_filebuf_reserve() as you like. The error codes for these
* functions don't need to be checked. They are stored internally
* by the file buffer.
*
* - While you are writing, you may call git_filebuf_hash() to get
* the hash of all you have written so far.
* the hash of all you have written so far. This function will
* fail if any of the previous writes to the buffer failed.
*
* - To close the git_filebuf, you may call git_filebuf_commit() or
* git_filebuf_commit_at() to save the file, or
* git_filebuf_cleanup() to abandon the file. All of these will
* clear the git_filebuf object.
* free the git_filebuf object. Likewise, all of these will fail
* if any of the previous writes to the buffer failed, and set
* an error code accordingly.
*/
int git_filebuf_write(git_filebuf *lock, const void *buff, size_t len);
int git_filebuf_reserve(git_filebuf *file, void **buff, size_t len);
int git_filebuf_printf(git_filebuf *file, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
......
......@@ -26,7 +26,7 @@ extern int git_futils_readbuffer_updated(git_buf *obj, const char *path, time_t
* These are custom filesystem-related helper methods. They are
* rather high level, and wrap the underlying POSIX methods
*
* All these methods return GIT_SUCCESS on success,
* All these methods return 0 on success,
* or an error code on failure and an error message is set.
*/
......@@ -58,10 +58,25 @@ extern int git_futils_mkdir_r(const char *path, const char *base, const mode_t m
*/
extern int git_futils_mkpath2file(const char *path, const mode_t mode);
typedef enum {
GIT_DIRREMOVAL_EMPTY_HIERARCHY = 0,
GIT_DIRREMOVAL_FILES_AND_DIRS = 1,
GIT_DIRREMOVAL_ONLY_EMPTY_DIRS = 2,
} git_directory_removal_type;
/**
* Remove path and any files and directories beneath it.
*
* @param path Path to to top level directory to process.
*
* @param removal_type GIT_DIRREMOVAL_EMPTY_HIERARCHY to remove a hierarchy
* of empty directories (will fail if any file is found), GIT_DIRREMOVAL_FILES_AND_DIRS
* to remove a hierarchy of files and folders, GIT_DIRREMOVAL_ONLY_EMPTY_DIRS to only remove
* empty directories (no failure on file encounter).
*
* @return 0 on success; -1 on error.
*/
extern int git_futils_rmdir_r(const char *path, int force);
extern int git_futils_rmdir_r(const char *path, git_directory_removal_type removal_type);
/**
* Create and open a temporary file with a `_git2_` suffix.
......@@ -77,6 +92,11 @@ extern int git_futils_mktmp(git_buf *path_out, const char *filename);
extern int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode);
/**
* Open a file readonly and set error if needed.
*/
extern int git_futils_open_ro(const char *path);
/**
* Get the filesize in bytes of a file
*/
extern git_off_t git_futils_filesize(git_file fd);
......@@ -103,8 +123,8 @@ extern mode_t git_futils_canonical_mode(mode_t raw_mode);
* @param begin first byte to map, this should be page aligned.
* @param end number of bytes to map.
* @return
* - GIT_SUCCESS on success;
* - GIT_EOSERR on an unspecified OS related error.
* - 0 on success;
* - -1 on error.
*/
extern int git_futils_mmap_ro(
git_map *out,
......@@ -118,8 +138,9 @@ extern int git_futils_mmap_ro(
* @param out buffer to populate with the mapping information.
* @param path path to file to be opened.
* @return
* - GIT_SUCCESS on success;
* - GIT_EOSERR on an unspecified OS related error.
* - 0 on success;
* - GIT_ENOTFOUND if not found;
* - -1 on an unspecified OS related error.
*/
extern int git_futils_mmap_ro_file(
git_map *out,
......@@ -137,9 +158,9 @@ extern void git_futils_mmap_free(git_map *map);
* @param pathbuf buffer to write the full path into
* @param filename name of file to find in the home directory
* @return
* - GIT_SUCCESS if found;
* - 0 if found;
* - GIT_ENOTFOUND if not found;
* - GIT_EOSERR on an unspecified OS related error.
* - -1 on an unspecified OS related error.
*/
extern int git_futils_find_global_file(git_buf *path, const char *filename);
......@@ -149,9 +170,9 @@ extern int git_futils_find_global_file(git_buf *path, const char *filename);
* @param pathbuf buffer to write the full path into
* @param filename name of file to find in the home directory
* @return
* - GIT_SUCCESS if found;
* - 0 if found;
* - GIT_ENOTFOUND if not found;
* - GIT_EOSERR on an unspecified OS related error.
* - -1 on an unspecified OS related error.
*/
extern int git_futils_find_system_file(git_buf *path, const char *filename);
......
......@@ -19,13 +19,13 @@ void git_text_gather_stats(git_text_stats *stats, const git_buf *text)
memset(stats, 0, sizeof(*stats));
for (i = 0; i < text->size; i++) {
for (i = 0; i < git_buf_len(text); i++) {
unsigned char c = text->ptr[i];
if (c == '\r') {
stats->cr++;
if (i + 1 < text->size && text->ptr[i + 1] == '\n')
if (i + 1 < git_buf_len(text) && text->ptr[i + 1] == '\n')
stats->crlf++;
}
......@@ -59,7 +59,7 @@ void git_text_gather_stats(git_text_stats *stats, const git_buf *text)
}
/* If file ends with EOF then don't count this EOF as non-printable. */
if (text->size >= 1 && text->ptr[text->size - 1] == '\032')
if (git_buf_len(text) >= 1 && text->ptr[text->size - 1] == '\032')
stats->nonprintable--;
}
......@@ -92,11 +92,11 @@ int git_filters_load(git_vector *filters, git_repository *repo, const char *path
if (mode == GIT_FILTER_TO_ODB) {
/* Load the CRLF cleanup filter when writing to the ODB */
error = git_filter_add__crlf_to_odb(filters, repo, path);
if (error < GIT_SUCCESS)
if (error < 0)
return error;
} else {
return git__throw(GIT_ENOTIMPLEMENTED,
"Worktree filters are not implemented yet");
giterr_set(GITERR_INVALID, "Worktree filters are not implemented yet");
return -1;
}
return (int)filters->length;
......@@ -111,7 +111,7 @@ void git_filters_free(git_vector *filters)
if (filter->do_free != NULL)
filter->do_free(filter);
else
free(filter);
git__free(filter);
}
git_vector_free(filters);
......@@ -127,15 +127,15 @@ int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters)
src = 0;
if (source->size == 0) {
if (git_buf_len(source) == 0) {
git_buf_clear(dest);
return GIT_SUCCESS;
}
/* Pre-grow the destination buffer to more or less the size
* we expect it to have */
if (git_buf_grow(dest, source->size) < 0)
return GIT_ENOMEM;
if (git_buf_grow(dest, git_buf_len(source)) < 0)
return -1;
for (i = 0; i < filters->length; ++i) {
git_filter *filter = git_vector_get(filters, i);
......@@ -153,7 +153,7 @@ int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters)
src = dst;
if (git_buf_oom(dbuffer[dst]))
return GIT_ENOMEM;
return -1;
}
/* Ensure that the output ends up in dbuffer[1] (i.e. the dest) */
......
......@@ -62,7 +62,7 @@ git_global_st *git__global_state(void)
if ((ptr = TlsGetValue(_tls_index)) != NULL)
return ptr;
ptr = malloc(sizeof(git_global_st));
ptr = git__malloc(sizeof(git_global_st));
if (!ptr)
return NULL;
......@@ -78,7 +78,7 @@ static int _tls_init = 0;
static void cb__free_status(void *st)
{
free(st);
git__free(st);
}
void git_threads_init(void)
......@@ -103,7 +103,7 @@ git_global_st *git__global_state(void)
if ((ptr = pthread_getspecific(_tls_key)) != NULL)
return ptr;
ptr = malloc(sizeof(git_global_st));
ptr = git__malloc(sizeof(git_global_st));
if (!ptr)
return NULL;
......
......@@ -14,6 +14,9 @@ typedef struct {
char last[1024];
} error;
git_error *last_error;
git_error error_t;
git_mwindow_ctl mem_ctl;
} git_global_st;
......
/*
* Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "common.h"
#include "repository.h"
#include "commit.h"
#define MAX_LOOPS 5
static const double max_load_factor = 0.65;
static int resize_to(git_hashtable *self, size_t new_size);
static int set_size(git_hashtable *self, size_t new_size);
static git_hashtable_node *node_with_hash(git_hashtable *self, const void *key, int hash_id);
static void node_swap_with(git_hashtable_node *self, git_hashtable_node *other);
static int node_insert(git_hashtable *self, git_hashtable_node *new_node);
static int insert_nodes(git_hashtable *self, git_hashtable_node *old_nodes, size_t old_size);
static int resize_to(git_hashtable *self, size_t new_size)
{
git_hashtable_node *old_nodes = self->nodes;
size_t old_size = self->size;
self->is_resizing = 1;
do {
self->size = new_size;
self->size_mask = new_size - 1;
self->key_count = 0;
self->nodes = git__calloc(1, sizeof(git_hashtable_node) * self->size);
if (self->nodes == NULL)
return GIT_ENOMEM;
if (insert_nodes(self, old_nodes, old_size) == 0)
self->is_resizing = 0;
else {
new_size *= 2;
git__free(self->nodes);
}
} while(self->is_resizing);
git__free(old_nodes);
return GIT_SUCCESS;
}
static int set_size(git_hashtable *self, size_t new_size)
{
self->nodes = git__realloc(self->nodes, new_size * sizeof(git_hashtable_node));
if (self->nodes == NULL)
return GIT_ENOMEM;
if (new_size > self->size) {
memset(&self->nodes[self->size], 0x0, (new_size - self->size) * sizeof(git_hashtable_node));
}
self->size = new_size;
self->size_mask = new_size - 1;
return GIT_SUCCESS;
}
static git_hashtable_node *node_with_hash(git_hashtable *self, const void *key, int hash_id)
{
size_t pos = self->hash(key, hash_id) & self->size_mask;
return git_hashtable_node_at(self->nodes, pos);
}
static void node_swap_with(git_hashtable_node *self, git_hashtable_node *other)
{
git_hashtable_node tmp = *self;
*self = *other;
*other = tmp;
}
static int node_insert(git_hashtable *self, git_hashtable_node *new_node)
{
int iteration, hash_id;
for (iteration = 0; iteration < MAX_LOOPS; iteration++) {
for (hash_id = 0; hash_id < GIT_HASHTABLE_HASHES; ++hash_id) {
git_hashtable_node *node;
node = node_with_hash(self, new_node->key, hash_id);
node_swap_with(new_node, node);
if(new_node->key == 0x0){
self->key_count++;
return GIT_SUCCESS;
}
}
}
if (self->is_resizing)
return git__throw(GIT_EBUSY, "Failed to insert node. Hashtable is currently resizing");
resize_to(self, self->size * 2);
git_hashtable_insert(self, new_node->key, new_node->value);
return GIT_SUCCESS;
}
static int insert_nodes(git_hashtable *self, git_hashtable_node *old_nodes, size_t old_size)
{
size_t i;
for (i = 0; i < old_size; ++i) {
git_hashtable_node *node = git_hashtable_node_at(old_nodes, i);
if (node->key && git_hashtable_insert(self, node->key, node->value) < GIT_SUCCESS)
return GIT_ENOMEM;
}
return GIT_SUCCESS;
}
git_hashtable *git_hashtable_alloc(size_t min_size,
git_hash_ptr hash,
git_hash_keyeq_ptr key_eq)
{
git_hashtable *table;
assert(hash && key_eq);
if ((table = git__malloc(sizeof(git_hashtable))) == NULL)
return NULL;
memset(table, 0x0, sizeof(git_hashtable));
if (min_size < 8)
min_size = 8;
/* round up size to closest power of 2 */
min_size--;
min_size |= min_size >> 1;
min_size |= min_size >> 2;
min_size |= min_size >> 4;
min_size |= min_size >> 8;
min_size |= min_size >> 16;
table->hash = hash;
table->key_equal = key_eq;
set_size(table, min_size + 1);
return table;
}
void git_hashtable_clear(git_hashtable *self)
{
assert(self);
memset(self->nodes, 0x0, sizeof(git_hashtable_node) * self->size);
self->key_count = 0;
}
void git_hashtable_free(git_hashtable *self)
{
assert(self);
git__free(self->nodes);
git__free(self);
}
int git_hashtable_insert2(git_hashtable *self, const void *key, void *value, void **old_value)
{
int hash_id;
git_hashtable_node *node;
assert(self && self->nodes);
*old_value = NULL;
for (hash_id = 0; hash_id < GIT_HASHTABLE_HASHES; ++hash_id) {
node = node_with_hash(self, key, hash_id);
if (!node->key) {
node->key = key;
node->value = value;
self->key_count++;
return GIT_SUCCESS;
}
if (key == node->key || self->key_equal(key, node->key) == 0) {
*old_value = node->value;
node->key = key;
node->value = value;
return GIT_SUCCESS;
}
}
/* no space in table; must do cuckoo dance */
{
git_hashtable_node x;
x.key = key;
x.value = value;
return node_insert(self, &x);
}
}
void *git_hashtable_lookup(git_hashtable *self, const void *key)
{
int hash_id;
git_hashtable_node *node;
assert(self && self->nodes);
for (hash_id = 0; hash_id < GIT_HASHTABLE_HASHES; ++hash_id) {
node = node_with_hash(self, key, hash_id);
if (node->key && self->key_equal(key, node->key) == 0)
return node->value;
}
return NULL;
}
int git_hashtable_remove2(git_hashtable *self, const void *key, void **old_value)
{
int hash_id;
git_hashtable_node *node;
assert(self && self->nodes);
for (hash_id = 0; hash_id < GIT_HASHTABLE_HASHES; ++hash_id) {
node = node_with_hash(self, key, hash_id);
if (node->key && self->key_equal(key, node->key) == 0) {
*old_value = node->value;
node->key = NULL;
node->value = NULL;
self->key_count--;
return GIT_SUCCESS;
}
}
return git__throw(GIT_ENOTFOUND, "Entry not found in hash table");
}
int git_hashtable_merge(git_hashtable *self, git_hashtable *other)
{
if (resize_to(self, (self->size + other->size) * 2) < GIT_SUCCESS)
return GIT_ENOMEM;
return insert_nodes(self, other->nodes, other->key_count);
}
/**
* Standard string
*/
uint32_t git_hash__strhash_cb(const void *key, int hash_id)
{
static uint32_t hash_seeds[GIT_HASHTABLE_HASHES] = {
2147483647,
0x5d20bb23,
0x7daaab3c
};
return git__hash(key, strlen((const char *)key), hash_seeds[hash_id]);
}
/*
* Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_hashtable_h__
#define INCLUDE_hashtable_h__
#include "git2/common.h"
#include "git2/oid.h"
#include "git2/odb.h"
#include "common.h"
#define GIT_HASHTABLE_HASHES 3
typedef uint32_t (*git_hash_ptr)(const void *, int hash_id);
typedef int (*git_hash_keyeq_ptr)(const void *key_a, const void *key_b);
struct git_hashtable_node {
const void *key;
void *value;
};
struct git_hashtable {
struct git_hashtable_node *nodes;
size_t size_mask;
size_t size;
size_t key_count;
int is_resizing;
git_hash_ptr hash;
git_hash_keyeq_ptr key_equal;
};
typedef struct git_hashtable_node git_hashtable_node;
typedef struct git_hashtable git_hashtable;
git_hashtable *git_hashtable_alloc(size_t min_size,
git_hash_ptr hash,
git_hash_keyeq_ptr key_eq);
void *git_hashtable_lookup(git_hashtable *h, const void *key);
int git_hashtable_remove2(git_hashtable *table, const void *key, void **old_value);
GIT_INLINE(int) git_hashtable_remove(git_hashtable *table, const void *key)
{
void *_unused;
return git_hashtable_remove2(table, key, &_unused);
}
void git_hashtable_free(git_hashtable *h);
void git_hashtable_clear(git_hashtable *h);
int git_hashtable_merge(git_hashtable *self, git_hashtable *other);
int git_hashtable_insert2(git_hashtable *h, const void *key, void *value, void **old_value);
GIT_INLINE(int) git_hashtable_insert(git_hashtable *h, const void *key, void *value)
{
void *_unused;
return git_hashtable_insert2(h, key, value, &_unused);
}
#define git_hashtable_node_at(nodes, pos) ((git_hashtable_node *)(&nodes[pos]))
#define GIT_HASHTABLE__FOREACH(self,block) { \
unsigned int _c; \
git_hashtable_node *_n = (self)->nodes; \
for (_c = (self)->size; _c > 0; _c--, _n++) { \
if (!_n->key) continue; block } }
#define GIT_HASHTABLE_FOREACH(self, pkey, pvalue, code)\
GIT_HASHTABLE__FOREACH(self,{(pkey)=_n->key;(pvalue)=_n->value;code;})
#define GIT_HASHTABLE_FOREACH_KEY(self, pkey, code)\
GIT_HASHTABLE__FOREACH(self,{(pkey)=_n->key;code;})
#define GIT_HASHTABLE_FOREACH_VALUE(self, pvalue, code)\
GIT_HASHTABLE__FOREACH(self,{(pvalue)=_n->value;code;})
#define GIT_HASHTABLE_FOREACH_DELETE() {\
_node->key = NULL; _node->value = NULL; _self->key_count--;\
}
/*
* If you want a hashtable with standard string keys, you can
* just pass git_hash__strcmp_cb and git_hash__strhash_cb to
* git_hashtable_alloc.
*/
#define git_hash__strcmp_cb git__strcmp_cb
extern uint32_t git_hash__strhash_cb(const void *key, int hash_id);
#endif
#include "ignore.h"
#include "path.h"
#include "git2/config.h"
#define GIT_IGNORE_INTERNAL "[internal]exclude"
#define GIT_IGNORE_FILE_INREPO "info/exclude"
#define GIT_IGNORE_FILE ".gitignore"
#define GIT_IGNORE_CONFIG "core.excludesfile"
static int load_ignore_file(
git_repository *repo, const char *path, git_attr_file *ignores)
static int parse_ignore_file(
git_repository *repo, const char *buffer, git_attr_file *ignores)
{
int error = GIT_SUCCESS;
git_buf fbuf = GIT_BUF_INIT;
int error = 0;
git_attr_fnmatch *match = NULL;
const char *scan = NULL;
char *context = NULL;
if (ignores->path == NULL)
error = git_attr_file__set_path(repo, path, ignores);
GIT_UNUSED(repo);
if (git__suffixcmp(ignores->path, GIT_IGNORE_FILE) == 0) {
context = git__strndup(ignores->path,
strlen(ignores->path) - strlen(GIT_IGNORE_FILE));
if (!context) error = GIT_ENOMEM;
if (ignores->key && git__suffixcmp(ignores->key, "/" GIT_IGNORE_FILE) == 0) {
context = ignores->key + 2;
context[strlen(context) - strlen(GIT_IGNORE_FILE)] = '\0';
}
if (error == GIT_SUCCESS)
error = git_futils_readbuffer(&fbuf, path);
scan = buffer;
scan = fbuf.ptr;
while (error == GIT_SUCCESS && *scan) {
if (!match && !(match = git__calloc(1, sizeof(git_attr_fnmatch)))) {
error = GIT_ENOMEM;
break;
while (!error && *scan) {
if (!match) {
match = git__calloc(1, sizeof(*match));
GITERR_CHECK_ALLOC(match);
}
if (!(error = git_attr_fnmatch__parse(match, context, &scan))) {
if (!(error = git_attr_fnmatch__parse(
match, ignores->pool, context, &scan)))
{
match->flags = match->flags | GIT_ATTR_FNMATCH_IGNORE;
scan = git__next_line(scan);
error = git_vector_insert(&ignores->rules, match);
}
if (error != GIT_SUCCESS) {
if (error != 0) {
git__free(match->pattern);
match->pattern = NULL;
if (error == GIT_ENOTFOUND)
error = GIT_SUCCESS;
error = 0;
} else {
match = NULL; /* vector now "owns" the match */
}
}
git_buf_free(&fbuf);
git__free(match);
git__free(context);
if (error != GIT_SUCCESS)
git__rethrow(error, "Could not open ignore file '%s'", path);
/* restore file path used for context */
if (context)
context[strlen(context)] = '.'; /* first char of GIT_IGNORE_FILE */
return error;
}
#define push_ignore(R,S,B,F) \
git_attr_cache__push_file((R),(S),(B),(F),load_ignore_file)
#define push_ignore_file(R,S,B,F) \
git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,parse_ignore_file,(S))
static int push_one_ignore(void *ref, git_buf *path)
{
git_ignores *ign = (git_ignores *)ref;
return push_ignore(ign->repo, &ign->ign_path, path->ptr, GIT_IGNORE_FILE);
return push_ignore_file(ign->repo, &ign->ign_path, path->ptr, GIT_IGNORE_FILE);
}
int git_ignore__for_path(git_repository *repo, const char *path, git_ignores *ignores)
int git_ignore__for_path(
git_repository *repo,
const char *path,
git_ignores *ignores)
{
int error = GIT_SUCCESS;
git_config *cfg;
int error = 0;
const char *workdir = git_repository_workdir(repo);
assert(ignores);
......@@ -83,74 +77,70 @@ int git_ignore__for_path(git_repository *repo, const char *path, git_ignores *ig
ignores->repo = repo;
git_buf_init(&ignores->dir, 0);
ignores->ign_internal = NULL;
git_vector_init(&ignores->ign_path, 8, NULL);
git_vector_init(&ignores->ign_global, 2, NULL);
if ((error = git_attr_cache__init(repo)) < GIT_SUCCESS)
if ((error = git_vector_init(&ignores->ign_path, 8, NULL)) < 0 ||
(error = git_vector_init(&ignores->ign_global, 2, NULL)) < 0 ||
(error = git_attr_cache__init(repo)) < 0)
goto cleanup;
if ((error = git_path_find_dir(&ignores->dir, path, workdir)) < GIT_SUCCESS)
/* given a unrooted path in a non-bare repo, resolve it */
if (workdir && git_path_root(path) < 0)
error = git_path_find_dir(&ignores->dir, path, workdir);
else
error = git_buf_sets(&ignores->dir, path);
if (error < 0)
goto cleanup;
/* set up internals */
error = git_attr_cache__lookup_or_create_file(
repo, GIT_IGNORE_INTERNAL, NULL, NULL, &ignores->ign_internal);
if (error < GIT_SUCCESS)
error = git_attr_cache__internal_file(
repo, GIT_IGNORE_INTERNAL, &ignores->ign_internal);
if (error < 0)
goto cleanup;
/* load .gitignore up the path */
error = git_path_walk_up(&ignores->dir, workdir, push_one_ignore, ignores);
if (error < GIT_SUCCESS)
goto cleanup;
if (workdir != NULL) {
error = git_path_walk_up(
&ignores->dir, workdir, push_one_ignore, ignores);
if (error < 0)
goto cleanup;
}
/* load .git/info/exclude */
error = push_ignore(repo, &ignores->ign_global,
repo->path_repository, GIT_IGNORE_FILE_INREPO);
if (error < GIT_SUCCESS)
error = push_ignore_file(repo, &ignores->ign_global,
git_repository_path(repo), GIT_IGNORE_FILE_INREPO);
if (error < 0)
goto cleanup;
/* load core.excludesfile */
if ((error = git_repository_config(&cfg, repo)) == GIT_SUCCESS) {
const char *core_ignore;
error = git_config_get_string(cfg, GIT_IGNORE_CONFIG, &core_ignore);
if (error == GIT_SUCCESS && core_ignore != NULL)
error = push_ignore(repo, &ignores->ign_global, NULL, core_ignore);
else {
error = GIT_SUCCESS;
git_clearerror(); /* don't care if attributesfile is not set */
}
git_config_free(cfg);
}
if (git_repository_attr_cache(repo)->cfg_excl_file != NULL)
error = push_ignore_file(repo, &ignores->ign_global, NULL,
git_repository_attr_cache(repo)->cfg_excl_file);
cleanup:
if (error < GIT_SUCCESS) {
if (error < 0)
git_ignore__free(ignores);
git__rethrow(error, "Could not get ignore files for '%s'", path);
}
return error;
}
int git_ignore__push_dir(git_ignores *ign, const char *dir)
{
int error = git_buf_joinpath(&ign->dir, ign->dir.ptr, dir);
if (error == GIT_SUCCESS)
error = push_ignore(
if (git_buf_joinpath(&ign->dir, ign->dir.ptr, dir) < 0)
return -1;
else
return push_ignore_file(
ign->repo, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE);
return error;
}
int git_ignore__pop_dir(git_ignores *ign)
{
if (ign->ign_path.length > 0) {
git_attr_file *file = git_vector_last(&ign->ign_path);
if (git__suffixcmp(ign->dir.ptr, file->path) == 0)
if (git__suffixcmp(ign->dir.ptr, file->key + 2) == 0)
git_vector_pop(&ign->ign_path);
git_buf_rtruncate_at_char(&ign->dir, '/');
}
return GIT_SUCCESS;
return 0;
}
void git_ignore__free(git_ignores *ignores)
......@@ -161,54 +151,53 @@ void git_ignore__free(git_ignores *ignores)
git_buf_free(&ignores->dir);
}
static int ignore_lookup_in_rules(
static bool ignore_lookup_in_rules(
git_vector *rules, git_attr_path *path, int *ignored)
{
unsigned int j;
git_attr_fnmatch *match;
git_vector_rforeach(rules, j, match) {
if (git_attr_fnmatch__match(match, path) == GIT_SUCCESS) {
if (git_attr_fnmatch__match(match, path)) {
*ignored = ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0);
return GIT_SUCCESS;
return true;
}
}
return GIT_ENOTFOUND;
return false;
}
int git_ignore__lookup(git_ignores *ignores, const char *pathname, int *ignored)
int git_ignore__lookup(
git_ignores *ignores, const char *pathname, int *ignored)
{
int error;
unsigned int i;
git_attr_file *file;
git_attr_path path;
if ((error = git_attr_path__init(
&path, pathname, git_repository_workdir(ignores->repo))) < GIT_SUCCESS)
return git__rethrow(error, "Could not get attribute for '%s'", pathname);
if (git_attr_path__init(
&path, pathname, git_repository_workdir(ignores->repo)) < 0)
return -1;
/* first process builtins */
error = ignore_lookup_in_rules(
&ignores->ign_internal->rules, &path, ignored);
if (error == GIT_SUCCESS)
return error;
/* first process builtins - success means path was found */
if (ignore_lookup_in_rules(
&ignores->ign_internal->rules, &path, ignored))
goto cleanup;
/* next process files in the path */
git_vector_foreach(&ignores->ign_path, i, file) {
error = ignore_lookup_in_rules(&file->rules, &path, ignored);
if (error == GIT_SUCCESS)
return error;
if (ignore_lookup_in_rules(&file->rules, &path, ignored))
goto cleanup;
}
/* last process global ignores */
git_vector_foreach(&ignores->ign_global, i, file) {
error = ignore_lookup_in_rules(&file->rules, &path, ignored);
if (error == GIT_SUCCESS)
return error;
if (ignore_lookup_in_rules(&file->rules, &path, ignored))
goto cleanup;
}
*ignored = 0;
return GIT_SUCCESS;
cleanup:
git_attr_path__free(&path);
return 0;
}
......@@ -25,13 +25,14 @@ typedef struct {
git_vector ign_global;
} git_ignores;
extern int git_ignore__for_path(
git_repository *repo, const char *path, git_ignores *ign);
extern int git_ignore__for_path(git_repository *repo, const char *path, git_ignores *ign);
extern int git_ignore__push_dir(git_ignores *ign, const char *dir);
extern int git_ignore__pop_dir(git_ignores *ign);
extern void git_ignore__free(git_ignores *ign);
extern int git_ignore__lookup(git_ignores *ign, const char *path, int *ignored);
#endif
......@@ -31,4 +31,6 @@ struct git_index {
git_vector unmerged;
};
extern void git_index__init_entry_from_stat(struct stat *st, git_index_entry *entry);
#endif
This diff is collapsed. Click to expand it.
......@@ -31,6 +31,11 @@ typedef struct { /* memory mapped buffer */
#endif
} git_map;
#define GIT_MMAP_VALIDATE(out, len, prot, flags) do { \
assert(out != NULL && len > 0); \
assert((prot & GIT_PROT_WRITE) || (prot & GIT_PROT_READ)); \
assert((flags & GIT_MAP_FIXED) == 0); } while (0)
extern int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset);
extern int p_munmap(git_map *map);
......
......@@ -89,6 +89,7 @@ void git_mwindow_scan_lru(
{
git_mwindow *w, *w_l;
puts("LRU");
for (w_l = NULL, w = mwf->windows; w; w = w->next) {
if (!w->inuse_cnt) {
/*
......@@ -115,7 +116,7 @@ static int git_mwindow_close_lru(git_mwindow_file *mwf)
unsigned int i;
git_mwindow *lru_w = NULL, *lru_l = NULL, **list = &mwf->windows;
/* FIMXE: Does this give us any advantage? */
/* FIXME: Does this give us any advantage? */
if(mwf->windows)
git_mwindow_scan_lru(mwf, &lru_w, &lru_l);
......@@ -127,22 +128,23 @@ static int git_mwindow_close_lru(git_mwindow_file *mwf)
list = &cur->windows;
}
if (lru_w) {
ctl->mapped -= lru_w->window_map.len;
git_futils_mmap_free(&lru_w->window_map);
if (!lru_w) {
giterr_set(GITERR_OS, "Failed to close memory window. Couldn't find LRU");
return -1;
}
if (lru_l)
lru_l->next = lru_w->next;
else
*list = lru_w->next;
ctl->mapped -= lru_w->window_map.len;
git_futils_mmap_free(&lru_w->window_map);
git__free(lru_w);
ctl->open_windows--;
if (lru_l)
lru_l->next = lru_w->next;
else
*list = lru_w->next;
return GIT_SUCCESS;
}
git__free(lru_w);
ctl->open_windows--;
return git__throw(GIT_ERROR, "Failed to close memory window. Couln't find LRU");
return 0;
}
static git_mwindow *new_window(
......@@ -158,7 +160,7 @@ static git_mwindow *new_window(
w = git__malloc(sizeof(*w));
if (w == NULL)
return w;
return NULL;
memset(w, 0x0, sizeof(*w));
w->offset = (offset / walign) * walign;
......@@ -170,7 +172,7 @@ static git_mwindow *new_window(
ctl->mapped += (size_t)len;
while (_mw_options.mapped_limit < ctl->mapped &&
git_mwindow_close_lru(mwf) == GIT_SUCCESS) /* nop */;
git_mwindow_close_lru(mwf) == 0) /* nop */;
/*
* We treat _mw_options.mapped_limit as a soft limit. If we can't find a
......@@ -178,8 +180,10 @@ static git_mwindow *new_window(
* window.
*/
if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < GIT_SUCCESS)
goto cleanup;
if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < 0) {
git__free(w);
return NULL;
}
ctl->mmap_calls++;
ctl->open_windows++;
......@@ -191,10 +195,6 @@ static git_mwindow *new_window(
ctl->peak_open_windows = ctl->open_windows;
return w;
cleanup:
git__free(w);
return NULL;
}
/*
......@@ -205,21 +205,20 @@ unsigned char *git_mwindow_open(
git_mwindow_file *mwf,
git_mwindow **cursor,
git_off_t offset,
int extra,
size_t extra,
unsigned int *left)
{
git_mwindow_ctl *ctl = &GIT_GLOBAL->mem_ctl;
git_mwindow *w = *cursor;
if (!w || !(git_mwindow_contains(w, offset) &&
git_mwindow_contains(w, offset + extra))) {
if (!w || !(git_mwindow_contains(w, offset) && git_mwindow_contains(w, offset + extra))) {
if (w) {
w->inuse_cnt--;
}
for (w = mwf->windows; w; w = w->next) {
if (git_mwindow_contains(w, offset) &&
git_mwindow_contains(w, offset + extra))
git_mwindow_contains(w, offset + extra))
break;
}
......@@ -248,17 +247,17 @@ unsigned char *git_mwindow_open(
if (left)
*left = (unsigned int)(w->window_map.len - offset);
fflush(stdout);
return (unsigned char *) w->window_map.data + offset;
}
int git_mwindow_file_register(git_mwindow_file *mwf)
{
git_mwindow_ctl *ctl = &GIT_GLOBAL->mem_ctl;
int error;
if (ctl->windowfiles.length == 0 &&
(error = git_vector_init(&ctl->windowfiles, 8, NULL)) < GIT_SUCCESS)
return error;
git_vector_init(&ctl->windowfiles, 8, NULL) < 0)
return -1;
return git_vector_insert(&ctl->windowfiles, mwf);
}
......
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
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