Commit 41dd999d by Ben Straub

Merge branch 'development' into blame

parents f7db1b6f ac316e74
...@@ -31,7 +31,7 @@ CC:=$(PREFIX)$(CC) ...@@ -31,7 +31,7 @@ CC:=$(PREFIX)$(CC)
INCLUDES= -I. -Isrc -Iinclude -Ideps/http-parser -Ideps/zlib INCLUDES= -I. -Isrc -Iinclude -Ideps/http-parser -Ideps/zlib
DEFINES= $(INCLUDES) -DNO_VIZ -DSTDC -DNO_GZIP -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE $(EXTRA_DEFINES) DEFINES= $(INCLUDES) -DNO_VIZ -DSTDC -DNO_GZIP -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE $(EXTRA_DEFINES)
CFLAGS= -g $(DEFINES) -Wall -Wextra -O2 $(EXTRA_CFLAGS) CFLAGS= -g $(DEFINES) -Wall -Wextra -Wno-missing-field-initializers -O2 $(EXTRA_CFLAGS)
SRCS = $(wildcard src/*.c) $(wildcard src/transports/*.c) $(wildcard src/xdiff/*.c) $(wildcard deps/http-parser/*.c) $(wildcard deps/zlib/*.c) src/hash/hash_generic.c SRCS = $(wildcard src/*.c) $(wildcard src/transports/*.c) $(wildcard src/xdiff/*.c) $(wildcard deps/http-parser/*.c) $(wildcard deps/zlib/*.c) src/hash/hash_generic.c
......
...@@ -45,44 +45,48 @@ Internal Objects ...@@ -45,44 +45,48 @@ Internal Objects
* `git_diff_file_content` is an internal structure that represents the * `git_diff_file_content` is an internal structure that represents the
data on one side of an item to be diffed; it is an augmented data on one side of an item to be diffed; it is an augmented
`git_diff_file` with more flags and the actual file data. `git_diff_file` with more flags and the actual file data.
** it is created from a repository plus a) a git_diff_file, b) a git_blob,
* it is created from a repository plus a) a git_diff_file, b) a git_blob,
or c) raw data and size or c) raw data and size
** there are three main operations on git_diff_file_content: * there are three main operations on git_diff_file_content:
*** _initialization_ sets up the data structure and does what it can up to,
but not including loading and looking at the actual data * _initialization_ sets up the data structure and does what it can up to,
*** _loading_ loads the data, preprocesses it (i.e. applies filters) and but not including loading and looking at the actual data
potentially analyzes it (to decide if binary) * _loading_ loads the data, preprocesses it (i.e. applies filters) and
*** _free_ releases loaded data and frees any allocated memory potentially analyzes it (to decide if binary)
* _free_ releases loaded data and frees any allocated memory
* The internal structure of a `git_diff_patch` stores the actual diff * The internal structure of a `git_diff_patch` stores the actual diff
between a pair of `git_diff_file_content` items between a pair of `git_diff_file_content` items
** it may be "unset" if the items are not diffable
** "empty" if the items are the same * it may be "unset" if the items are not diffable
** otherwise it will consist of a set of hunks each of which covers some * "empty" if the items are the same
number of lines of context, additions and deletions * otherwise it will consist of a set of hunks each of which covers some
** a patch is created from two git_diff_file_content items number of lines of context, additions and deletions
** a patch is fully instantiated in three phases: * a patch is created from two git_diff_file_content items
*** initial creation and initialization * a patch is fully instantiated in three phases:
*** loading of data and preliminary data examination
*** diffing of data and optional storage of diffs * initial creation and initialization
** (TBD) if a patch is asked to store the diffs and the size of the diff * loading of data and preliminary data examination
is significantly smaller than the raw data of the two sides, then the * diffing of data and optional storage of diffs
patch may be flattened using a pool of string data * (TBD) if a patch is asked to store the diffs and the size of the diff
is significantly smaller than the raw data of the two sides, then the
patch may be flattened using a pool of string data
* `git_diff_output` is an internal structure that represents an output * `git_diff_output` is an internal structure that represents an output
target for a `git_diff_patch` target for a `git_diff_patch`
** It consists of file, hunk, and line callbacks, plus a payload * It consists of file, hunk, and line callbacks, plus a payload
** There is a standard flattened output that can be used for plain text output * There is a standard flattened output that can be used for plain text output
** Typically we use a `git_xdiff_output` which drives the callbacks via the * Typically we use a `git_xdiff_output` which drives the callbacks via the
xdiff code taken from core Git. xdiff code taken from core Git.
* `git_diff_driver` is an internal structure that encapsulates the logic * `git_diff_driver` is an internal structure that encapsulates the logic
for a given type of file for a given type of file
** a driver is looked up based on the name and mode of a file. * a driver is looked up based on the name and mode of a file.
** the driver can then be used to: * the driver can then be used to:
*** determine if a file is binary (by attributes, by git_diff_options * determine if a file is binary (by attributes, by git_diff_options
settings, or by examining the content) settings, or by examining the content)
*** give you a function pointer that is used to evaluate function context * give you a function pointer that is used to evaluate function context
for hunk headers for hunk headers
** At some point, the logic for getting a filtered version of file content * At some point, the logic for getting a filtered version of file content
or calculating the OID of a file may be moved into the driver. or calculating the OID of a file may be moved into the driver.
...@@ -12,6 +12,8 @@ int main (int argc, char** argv) ...@@ -12,6 +12,8 @@ int main (int argc, char** argv)
char out[41]; char out[41];
out[40] = '\0'; out[40] = '\0';
git_threads_init();
if (argc > 1) if (argc > 1)
dir = argv[1]; dir = argv[1];
if (!dir || argc > 2) { if (!dir || argc > 2) {
...@@ -62,6 +64,8 @@ int main (int argc, char** argv) ...@@ -62,6 +64,8 @@ int main (int argc, char** argv)
git_index_free(index); git_index_free(index);
git_repository_free(repo); git_repository_free(repo);
git_threads_shutdown();
return 0; return 0;
} }
...@@ -71,7 +71,7 @@ static void show_branch(git_repository *repo, int format) ...@@ -71,7 +71,7 @@ static void show_branch(git_repository *repo, int format)
error = git_repository_head(&head, repo); error = git_repository_head(&head, repo);
if (error == GIT_EORPHANEDHEAD || error == GIT_ENOTFOUND) if (error == GIT_EUNBORNBRANCH || error == GIT_ENOTFOUND)
branch = NULL; branch = NULL;
else if (!error) { else if (!error) {
branch = git_reference_name(head); branch = git_reference_name(head);
......
...@@ -59,4 +59,7 @@ ...@@ -59,4 +59,7 @@
#include "git2/pathspec.h" #include "git2/pathspec.h"
#include "git2/blame.h" #include "git2/blame.h"
#include "git2/buffer.h"
#include "git2/filter.h"
#endif #endif
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "types.h" #include "types.h"
#include "oid.h" #include "oid.h"
#include "object.h" #include "object.h"
#include "buffer.h"
/** /**
* @file git2/blob.h * @file git2/blob.h
...@@ -96,6 +97,37 @@ GIT_EXTERN(const void *) git_blob_rawcontent(const git_blob *blob); ...@@ -96,6 +97,37 @@ GIT_EXTERN(const void *) git_blob_rawcontent(const git_blob *blob);
GIT_EXTERN(git_off_t) git_blob_rawsize(const git_blob *blob); GIT_EXTERN(git_off_t) git_blob_rawsize(const git_blob *blob);
/** /**
* Get a buffer with the filtered content of a blob.
*
* This applies filters as if the blob was being checked out to the
* working directory under the specified filename. This may apply
* CRLF filtering or other types of changes depending on the file
* attributes set for the blob and the content detected in it.
*
* The output is written into a `git_buf` which the caller must free
* when done (via `git_buf_free`).
*
* If no filters need to be applied, then the `out` buffer will just be
* populated with a pointer to the raw content of the blob. In that case,
* be careful to *not* free the blob until done with the buffer. To keep
* the data detached from the blob, call `git_buf_grow` on the buffer
* with a `want_size` of 0 and the buffer will be reallocated to be
* detached from the blob.
*
* @param out The git_buf to be filled in
* @param blob Pointer to the blob
* @param as_path Path used for file attribute lookups, etc.
* @param check_for_binary_data Should this test if blob content contains
* NUL bytes / looks like binary data before applying filters?
* @return 0 on success or an error code
*/
GIT_EXTERN(int) git_blob_filtered_content(
git_buf *out,
git_blob *blob,
const char *as_path,
int check_for_binary_data);
/**
* Read a file from the working folder of a repository * Read a file from the working folder of a repository
* and write it to the Object Database as a loose blob * and write it to the Object Database as a loose blob
* *
......
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_git_buf_h__
#define INCLUDE_git_buf_h__
#include "common.h"
/**
* @file git2/buffer.h
* @brief Buffer export structure
*
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
/**
* A data buffer for exporting data from libgit2
*
* Sometimes libgit2 wants to return an allocated data buffer to the
* caller and have the caller take responsibility for freeing that memory.
* This can be awkward if the caller does not have easy access to the same
* allocation functions that libgit2 is using. In those cases, libgit2
* will fill in a `git_buf` and the caller can use `git_buf_free()` to
* release it when they are done.
*
* A `git_buf` may also be used for the caller to pass in a reference to
* a block of memory they hold. In this case, libgit2 will not resize or
* free the memory, but will read from it as needed.
*
* A `git_buf` is a public structure with three fields:
*
* - `ptr` points to the start of the allocated memory. If it is NULL,
* then the `git_buf` is considered empty and libgit2 will feel free
* to overwrite it with new data.
*
* - `size` holds the size (in bytes) of the data that is actually used.
*
* - `asize` holds the known total amount of allocated memory if the `ptr`
* was allocated by libgit2. It may be larger than `size`. If `ptr`
* was not allocated by libgit2 and should not be resized and/or freed,
* then `asize` will be set to zero.
*
* Some APIs may occasionally do something slightly unusual with a buffer,
* such as setting `ptr` to a value that was passed in by the user. In
* those cases, the behavior will be clearly documented by the API.
*/
typedef struct {
char *ptr;
size_t asize, size;
} git_buf;
/**
* Static initializer for git_buf from static buffer
*/
#define GIT_BUF_INIT_CONST(STR,LEN) { (char *)(STR), 0, (size_t)(LEN) }
/**
* Free the memory referred to by the git_buf.
*
* Note that this does not free the `git_buf` itself, just the memory
* pointed to by `buffer->ptr`. This will not free the memory if it looks
* like it was not allocated internally, but it will clear the buffer back
* to the empty state.
*
* @param buffer The buffer to deallocate
*/
GIT_EXTERN(void) git_buf_free(git_buf *buffer);
/**
* Resize the buffer allocation to make more space.
*
* This will attempt to grow the buffer to accomodate the target size.
*
* If the buffer refers to memory that was not allocated by libgit2 (i.e.
* the `asize` field is zero), then `ptr` will be replaced with a newly
* allocated block of data. Be careful so that memory allocated by the
* caller is not lost. As a special variant, if you pass `target_size` as
* 0 and the memory is not allocated by libgit2, this will allocate a new
* buffer of size `size` and copy the external data into it.
*
* Currently, this will never shrink a buffer, only expand it.
*
* If the allocation fails, this will return an error and the buffer will be
* marked as invalid for future operations, invaliding the contents.
*
* @param buffer The buffer to be resized; may or may not be allocated yet
* @param target_size The desired available size
* @return 0 on success, -1 on allocation failure
*/
GIT_EXTERN(int) git_buf_grow(git_buf *buffer, size_t target_size);
/**
* Set buffer to a copy of some raw data.
*
* @param buffer The buffer to set
* @param data The data to copy into the buffer
* @param datalen The length of the data to copy into the buffer
* @return 0 on success, -1 on allocation failure
*/
GIT_EXTERN(int) git_buf_set(
git_buf *buffer, const void *data, size_t datalen);
GIT_END_DECL
/** @} */
#endif
...@@ -249,7 +249,7 @@ typedef struct git_checkout_opts { ...@@ -249,7 +249,7 @@ typedef struct git_checkout_opts {
* *
* @param repo repository to check out (must be non-bare) * @param repo repository to check out (must be non-bare)
* @param opts specifies checkout options (may be NULL) * @param opts specifies checkout options (may be NULL)
* @return 0 on success, GIT_EORPHANEDHEAD when HEAD points to a non existing * @return 0 on success, GIT_EUNBORNBRANCH when HEAD points to a non existing
* branch, GIT_ERROR otherwise (use giterr_last for information * branch, GIT_ERROR otherwise (use giterr_last for information
* about the error) * about the error)
*/ */
......
...@@ -136,7 +136,9 @@ typedef enum { ...@@ -136,7 +136,9 @@ typedef enum {
GIT_OPT_SET_CACHE_OBJECT_LIMIT, GIT_OPT_SET_CACHE_OBJECT_LIMIT,
GIT_OPT_SET_CACHE_MAX_SIZE, GIT_OPT_SET_CACHE_MAX_SIZE,
GIT_OPT_ENABLE_CACHING, GIT_OPT_ENABLE_CACHING,
GIT_OPT_GET_CACHED_MEMORY GIT_OPT_GET_CACHED_MEMORY,
GIT_OPT_GET_TEMPLATE_PATH,
GIT_OPT_SET_TEMPLATE_PATH
} git_libgit2_opt_t; } git_libgit2_opt_t;
/** /**
...@@ -210,6 +212,18 @@ typedef enum { ...@@ -210,6 +212,18 @@ typedef enum {
* > Get the current bytes in cache and the maximum that would be * > Get the current bytes in cache and the maximum that would be
* > allowed in the cache. * > allowed in the cache.
* *
* * opts(GIT_OPT_GET_TEMPLATE_PATH, char *out, size_t len)
*
* > Get the default template path.
* > The path is written to the `out`
* > buffer up to size `len`. Returns GIT_EBUFS if buffer is too small.
*
* * opts(GIT_OPT_SET_TEMPLATE_PATH, const char *path)
*
* > Set the default template path.
* >
* > - `path` directory of template.
*
* @param option Option key * @param option Option key
* @param ... value to set the option * @param ... value to set the option
* @return 0 on success, <0 on failure * @return 0 on success, <0 on failure
......
...@@ -27,7 +27,7 @@ typedef enum { ...@@ -27,7 +27,7 @@ typedef enum {
GIT_EBUFS = -6, GIT_EBUFS = -6,
GIT_EUSER = -7, GIT_EUSER = -7,
GIT_EBAREREPO = -8, GIT_EBAREREPO = -8,
GIT_EORPHANEDHEAD = -9, GIT_EUNBORNBRANCH = -9,
GIT_EUNMERGED = -10, GIT_EUNMERGED = -10,
GIT_ENONFASTFORWARD = -11, GIT_ENONFASTFORWARD = -11,
GIT_EINVALIDSPEC = -12, GIT_EINVALIDSPEC = -12,
...@@ -67,6 +67,8 @@ typedef enum { ...@@ -67,6 +67,8 @@ typedef enum {
GITERR_CHECKOUT, GITERR_CHECKOUT,
GITERR_FETCHHEAD, GITERR_FETCHHEAD,
GITERR_MERGE, GITERR_MERGE,
GITERR_SSH,
GITERR_FILTER,
} git_error_t; } git_error_t;
/** /**
......
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_git_filter_h__
#define INCLUDE_git_filter_h__
#include "common.h"
#include "types.h"
#include "oid.h"
#include "buffer.h"
/**
* @file git2/filter.h
* @brief Git filter APIs
*
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
/**
* Filters are applied in one of two directions: smudging - which is
* exporting a file from the Git object database to the working directory,
* and cleaning - which is importing a file from the working directory to
* the Git object database. These values control which direction of
* change is being applied.
*/
typedef enum {
GIT_FILTER_TO_WORKTREE = 0,
GIT_FILTER_SMUDGE = GIT_FILTER_TO_WORKTREE,
GIT_FILTER_TO_ODB = 1,
GIT_FILTER_CLEAN = GIT_FILTER_TO_ODB,
} git_filter_mode_t;
/**
* A filter that can transform file data
*
* This represents a filter that can be used to transform or even replace
* file data. Libgit2 includes one built in filter and it is possible to
* write your own (see git2/sys/filter.h for information on that).
*
* The two builtin filters are:
*
* * "crlf" which uses the complex rules with the "text", "eol", and
* "crlf" file attributes to decide how to convert between LF and CRLF
* line endings
* * "ident" which replaces "$Id$" in a blob with "$Id: <blob OID>$" upon
* checkout and replaced "$Id: <anything>$" with "$Id$" on checkin.
*/
typedef struct git_filter git_filter;
/**
* List of filters to be applied
*
* This represents a list of filters to be applied to a file / blob. You
* can build the list with one call, apply it with another, and dispose it
* with a third. In typical usage, there are not many occasions where a
* git_filter_list is needed directly since the library will generally
* handle conversions for you, but it can be convenient to be able to
* build and apply the list sometimes.
*/
typedef struct git_filter_list git_filter_list;
/**
* Load the filter list for a given path.
*
* This will return 0 (success) but set the output git_filter_list to NULL
* if no filters are requested for the given file.
*
* @param filters Output newly created git_filter_list (or NULL)
* @param repo Repository object that contains `path`
* @param blob The blob to which the filter will be applied (if known)
* @param path Relative path of the file to be filtered
* @param mode Filtering direction (WT->ODB or ODB->WT)
* @return 0 on success (which could still return NULL if no filters are
* needed for the requested file), <0 on error
*/
GIT_EXTERN(int) git_filter_list_load(
git_filter_list **filters,
git_repository *repo,
git_blob *blob, /* can be NULL */
const char *path,
git_filter_mode_t mode);
/**
* Apply filter list to a data buffer.
*
* See `git2/buffer.h` for background on `git_buf` objects.
*
* If the `in` buffer holds data allocated by libgit2 (i.e. `in->asize` is
* not zero), then it will be overwritten when applying the filters. If
* not, then it will be left untouched.
*
* If there are no filters to apply (or `filters` is NULL), then the `out`
* buffer will reference the `in` buffer data (with `asize` set to zero)
* instead of allocating data. This keeps allocations to a minimum, but
* it means you have to be careful about freeing the `in` data since `out`
* may be pointing to it!
*
* @param out Buffer to store the result of the filtering
* @param filters A loaded git_filter_list (or NULL)
* @param in Buffer containing the data to filter
* @return 0 on success, an error code otherwise
*/
GIT_EXTERN(int) git_filter_list_apply_to_data(
git_buf *out,
git_filter_list *filters,
git_buf *in);
/**
* Apply filter list to the contents of a file on disk
*/
GIT_EXTERN(int) git_filter_list_apply_to_file(
git_buf *out,
git_filter_list *filters,
git_repository *repo,
const char *path);
/**
* Apply filter list to the contents of a blob
*/
GIT_EXTERN(int) git_filter_list_apply_to_blob(
git_buf *out,
git_filter_list *filters,
git_blob *blob);
/**
* Free a git_filter_list
*
* @param filters A git_filter_list created by `git_filter_list_load`
*/
GIT_EXTERN(void) git_filter_list_free(git_filter_list *filters);
GIT_END_DECL
/** @} */
#endif
...@@ -120,9 +120,9 @@ typedef struct git_index_entry { ...@@ -120,9 +120,9 @@ typedef struct git_index_entry {
/** Capabilities of system that affect index actions. */ /** Capabilities of system that affect index actions. */
typedef enum { typedef enum {
GIT_INDEXCAP_IGNORE_CASE = 1, GIT_INDEXCAP_IGNORE_CASE = 1u,
GIT_INDEXCAP_NO_FILEMODE = 2, GIT_INDEXCAP_NO_FILEMODE = 2u,
GIT_INDEXCAP_NO_SYMLINKS = 4, GIT_INDEXCAP_NO_SYMLINKS = 4u,
GIT_INDEXCAP_FROM_OWNER = ~0u GIT_INDEXCAP_FROM_OWNER = ~0u
} git_indexcap_t; } git_indexcap_t;
......
...@@ -85,15 +85,15 @@ GIT_EXTERN(int) git_merge_base( ...@@ -85,15 +85,15 @@ GIT_EXTERN(int) git_merge_base(
* *
* @param out the OID of a merge base considering all the commits * @param out the OID of a merge base considering all the commits
* @param repo the repository where the commits exist * @param repo the repository where the commits exist
* @param input_array oids of the commits
* @param length The number of commits in the provided `input_array` * @param length The number of commits in the provided `input_array`
* @param input_array oids of the commits
* @return Zero on success; GIT_ENOTFOUND or -1 on failure. * @return Zero on success; GIT_ENOTFOUND or -1 on failure.
*/ */
GIT_EXTERN(int) git_merge_base_many( GIT_EXTERN(int) git_merge_base_many(
git_oid *out, git_oid *out,
git_repository *repo, git_repository *repo,
const git_oid input_array[], size_t length,
size_t length); const git_oid input_array[]);
/** /**
* Creates a `git_merge_head` from the given reference * Creates a `git_merge_head` from the given reference
......
...@@ -297,7 +297,7 @@ GIT_EXTERN(int) git_repository_init_ext( ...@@ -297,7 +297,7 @@ GIT_EXTERN(int) git_repository_init_ext(
* @param out pointer to the reference which will be retrieved * @param out pointer to the reference which will be retrieved
* @param repo a repository object * @param repo a repository object
* *
* @return 0 on success, GIT_EORPHANEDHEAD when HEAD points to a non existing * @return 0 on success, GIT_EUNBORNBRANCH when HEAD points to a non existing
* branch, GIT_ENOTFOUND when HEAD is missing; an error code otherwise * branch, GIT_ENOTFOUND when HEAD is missing; an error code otherwise
*/ */
GIT_EXTERN(int) git_repository_head(git_reference **out, git_repository *repo); GIT_EXTERN(int) git_repository_head(git_reference **out, git_repository *repo);
...@@ -315,16 +315,16 @@ GIT_EXTERN(int) git_repository_head(git_reference **out, git_repository *repo); ...@@ -315,16 +315,16 @@ GIT_EXTERN(int) git_repository_head(git_reference **out, git_repository *repo);
GIT_EXTERN(int) git_repository_head_detached(git_repository *repo); GIT_EXTERN(int) git_repository_head_detached(git_repository *repo);
/** /**
* Check if the current branch is an orphan * Check if the current branch is unborn
* *
* An orphan branch is one named from HEAD but which doesn't exist in * An unborn branch is one named from HEAD but which doesn't exist in
* the refs namespace, because it doesn't have any commit to point to. * the refs namespace, because it doesn't have any commit to point to.
* *
* @param repo Repo to test * @param repo Repo to test
* @return 1 if the current branch is an orphan, 0 if it's not; error * @return 1 if the current branch is unborn, 0 if it's not; error
* code if there was an error * code if there was an error
*/ */
GIT_EXTERN(int) git_repository_head_orphan(git_repository *repo); GIT_EXTERN(int) git_repository_head_unborn(git_repository *repo);
/** /**
* Check if a repository is empty * Check if a repository is empty
...@@ -611,7 +611,7 @@ GIT_EXTERN(int) git_repository_set_head_detached( ...@@ -611,7 +611,7 @@ GIT_EXTERN(int) git_repository_set_head_detached(
* Otherwise, the HEAD will be detached and point to the peeled Commit. * Otherwise, the HEAD will be detached and point to the peeled Commit.
* *
* @param repo Repository pointer * @param repo Repository pointer
* @return 0 on success, GIT_EORPHANEDHEAD when HEAD points to a non existing * @return 0 on success, GIT_EUNBORNBRANCH when HEAD points to a non existing
* branch or an error code * branch or an error code
*/ */
GIT_EXTERN(int) git_repository_detach_head( GIT_EXTERN(int) git_repository_detach_head(
......
...@@ -103,7 +103,7 @@ struct git_refdb_backend { ...@@ -103,7 +103,7 @@ struct git_refdb_backend {
* Deletes the given reference from the refdb. A refdb implementation * Deletes the given reference from the refdb. A refdb implementation
* must provide this function. * must provide this function.
*/ */
int (*delete)(git_refdb_backend *backend, const char *ref_name); int (*del)(git_refdb_backend *backend, const char *ref_name);
/** /**
* Suggests that the given refdb compress or optimize its references. * Suggests that the given refdb compress or optimize its references.
...@@ -121,8 +121,8 @@ struct git_refdb_backend { ...@@ -121,8 +121,8 @@ struct git_refdb_backend {
void (*free)(git_refdb_backend *backend); void (*free)(git_refdb_backend *backend);
}; };
#define GIT_ODB_BACKEND_VERSION 1 #define GIT_REFDB_BACKEND_VERSION 1
#define GIT_ODB_BACKEND_INIT {GIT_ODB_BACKEND_VERSION} #define GIT_REFDB_BACKEND_INIT {GIT_REFDB_BACKEND_VERSION}
/** /**
* Constructors for default filesystem-based refdb backend * Constructors for default filesystem-based refdb backend
......
...@@ -59,7 +59,7 @@ GIT_INLINE(void *) git_array_grow(void *_a, size_t item_size) ...@@ -59,7 +59,7 @@ GIT_INLINE(void *) git_array_grow(void *_a, size_t item_size)
#define git_array_alloc(a) \ #define git_array_alloc(a) \
((a).size >= (a).asize) ? \ ((a).size >= (a).asize) ? \
git_array_grow(&(a), sizeof(*(a).ptr)) : \ git_array_grow(&(a), sizeof(*(a).ptr)) : \
(a).ptr ? &(a).ptr[(a).size++] : NULL ((a).ptr ? &(a).ptr[(a).size++] : NULL)
#define git_array_last(a) ((a).size ? &(a).ptr[(a).size - 1] : NULL) #define git_array_last(a) ((a).size ? &(a).ptr[(a).size - 1] : NULL)
......
...@@ -26,7 +26,6 @@ git_attr_t git_attr_value(const char *attr) ...@@ -26,7 +26,6 @@ git_attr_t git_attr_value(const char *attr)
return GIT_ATTR_VALUE_T; return GIT_ATTR_VALUE_T;
} }
static int collect_attr_files( static int collect_attr_files(
git_repository *repo, git_repository *repo,
uint32_t flags, uint32_t flags,
...@@ -103,8 +102,6 @@ int git_attr_get_many( ...@@ -103,8 +102,6 @@ int git_attr_get_many(
attr_get_many_info *info = NULL; attr_get_many_info *info = NULL;
size_t num_found = 0; size_t num_found = 0;
memset((void *)values, 0, sizeof(const char *) * num_attr);
if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0) if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)
return -1; return -1;
...@@ -141,6 +138,11 @@ int git_attr_get_many( ...@@ -141,6 +138,11 @@ int git_attr_get_many(
} }
} }
for (k = 0; k < num_attr; k++) {
if (!info[k].found)
values[k] = NULL;
}
cleanup: cleanup:
git_vector_free(&files); git_vector_free(&files);
git_attr_path__free(&path); git_attr_path__free(&path);
......
...@@ -39,7 +39,7 @@ int git_attr_file__new( ...@@ -39,7 +39,7 @@ int git_attr_file__new(
attrs->key = git_pool_malloc(attrs->pool, (uint32_t)len + 3); attrs->key = git_pool_malloc(attrs->pool, (uint32_t)len + 3);
GITERR_CHECK_ALLOC(attrs->key); GITERR_CHECK_ALLOC(attrs->key);
attrs->key[0] = '0' + from; attrs->key[0] = '0' + (char)from;
attrs->key[1] = '#'; attrs->key[1] = '#';
memcpy(&attrs->key[2], path, len); memcpy(&attrs->key[2], path, len);
attrs->key[len + 2] = '\0'; attrs->key[len + 2] = '\0';
......
...@@ -108,29 +108,21 @@ static int write_file_filtered( ...@@ -108,29 +108,21 @@ static int write_file_filtered(
git_off_t *size, git_off_t *size,
git_odb *odb, git_odb *odb,
const char *full_path, const char *full_path,
git_vector *filters) git_filter_list *fl)
{ {
int error; int error;
git_buf source = GIT_BUF_INIT; git_buf tgt = GIT_BUF_INIT;
git_buf dest = GIT_BUF_INIT;
if ((error = git_futils_readbuffer(&source, full_path)) < 0) error = git_filter_list_apply_to_file(&tgt, fl, NULL, full_path);
return error;
error = git_filters_apply(&dest, &source, filters);
/* Free the source as soon as possible. This can be big in memory,
* and we don't want to ODB write to choke */
git_buf_free(&source);
/* Write the file to disk if it was properly filtered */ /* Write the file to disk if it was properly filtered */
if (!error) { if (!error) {
*size = dest.size; *size = tgt.size;
error = git_odb_write(oid, odb, dest.ptr, dest.size, GIT_OBJ_BLOB); error = git_odb_write(oid, odb, tgt.ptr, tgt.size, GIT_OBJ_BLOB);
} }
git_buf_free(&dest); git_buf_free(&tgt);
return error; return error;
} }
...@@ -198,29 +190,25 @@ int git_blob__create_from_paths( ...@@ -198,29 +190,25 @@ int git_blob__create_from_paths(
if (S_ISLNK(mode)) { if (S_ISLNK(mode)) {
error = write_symlink(oid, odb, content_path, (size_t)size); error = write_symlink(oid, odb, content_path, (size_t)size);
} else { } else {
git_vector write_filters = GIT_VECTOR_INIT; git_filter_list *fl = NULL;
int filter_count = 0;
if (try_load_filters) { if (try_load_filters)
/* Load the filters for writing this file to the ODB */ /* Load the filters for writing this file to the ODB */
filter_count = git_filters_load( error = git_filter_list_load(
&write_filters, repo, hint_path, GIT_FILTER_TO_ODB); &fl, repo, NULL, hint_path, GIT_FILTER_TO_ODB);
}
if (filter_count < 0) { if (error < 0)
/* Negative value means there was a critical error */ /* well, that didn't work */;
error = filter_count; else if (fl == NULL)
} else if (filter_count == 0) {
/* No filters need to be applied to the document: we can stream /* No filters need to be applied to the document: we can stream
* directly from disk */ * directly from disk */
error = write_file_stream(oid, odb, content_path, size); error = write_file_stream(oid, odb, content_path, size);
} else { else {
/* We need to apply one or more filters */ /* We need to apply one or more filters */
error = write_file_filtered( error = write_file_filtered(oid, &size, odb, content_path, fl);
oid, &size, odb, content_path, &write_filters);
}
git_filters_free(&write_filters); git_filter_list_free(fl);
}
/* /*
* TODO: eventually support streaming filtered files, for files * TODO: eventually support streaming filtered files, for files
...@@ -333,8 +321,34 @@ int git_blob_is_binary(git_blob *blob) ...@@ -333,8 +321,34 @@ int git_blob_is_binary(git_blob *blob)
assert(blob); assert(blob);
content.ptr = blob->odb_object->buffer; content.ptr = blob->odb_object->buffer;
content.size = min(blob->odb_object->cached.size, 4000); content.size = min(blob->odb_object->cached.size, 4000);
content.asize = 0;
return git_buf_text_is_binary(&content); return git_buf_text_is_binary(&content);
} }
int git_blob_filtered_content(
git_buf *out,
git_blob *blob,
const char *path,
int check_for_binary_data)
{
int error = 0;
git_filter_list *fl = NULL;
assert(blob && path && out);
if (check_for_binary_data && git_blob_is_binary(blob))
return 0;
if (!(error = git_filter_list_load(
&fl, git_blob_owner(blob), blob, path, GIT_FILTER_TO_WORKTREE))) {
error = git_filter_list_apply_to_blob(out, fl, blob);
git_filter_list_free(fl);
}
return error;
}
...@@ -585,7 +585,7 @@ int git_branch_is_head( ...@@ -585,7 +585,7 @@ int git_branch_is_head(
error = git_repository_head(&head, git_reference_owner(branch)); error = git_repository_head(&head, git_reference_owner(branch));
if (error == GIT_EORPHANEDHEAD || error == GIT_ENOTFOUND) if (error == GIT_EUNBORNBRANCH || error == GIT_ENOTFOUND)
return false; return false;
if (error < 0) if (error < 0)
......
...@@ -70,10 +70,10 @@ int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src) ...@@ -70,10 +70,10 @@ int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src)
assert(tgt != src); assert(tgt != src);
if (!next) if (!next)
return GIT_ENOTFOUND; return git_buf_set(tgt, src->ptr, src->size);
/* reduce reallocs while in the loop */ /* reduce reallocs while in the loop */
if (git_buf_grow(tgt, src->size) < 0) if (git_buf_grow(tgt, src->size + 1) < 0)
return -1; return -1;
out = tgt->ptr; out = tgt->ptr;
tgt->size = 0; tgt->size = 0;
...@@ -81,20 +81,25 @@ int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src) ...@@ -81,20 +81,25 @@ int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src)
/* Find the next \r and copy whole chunk up to there to tgt */ /* Find the next \r and copy whole chunk up to there to tgt */
for (; next; scan = next + 1, next = memchr(scan, '\r', scan_end - scan)) { for (; next; scan = next + 1, next = memchr(scan, '\r', scan_end - scan)) {
if (next > scan) { if (next > scan) {
size_t copylen = next - scan; size_t copylen = (size_t)(next - scan);
memcpy(out, scan, copylen); memcpy(out, scan, copylen);
out += copylen; out += copylen;
} }
/* Do not drop \r unless it is followed by \n */ /* Do not drop \r unless it is followed by \n */
if (next[1] != '\n') if (next + 1 == scan_end || next[1] != '\n')
*out++ = '\r'; *out++ = '\r';
} }
/* Copy remaining input into dest */ /* Copy remaining input into dest */
memcpy(out, scan, scan_end - scan + 1); /* +1 for NUL byte */ if (scan < scan_end) {
out += (scan_end - scan); size_t remaining = (size_t)(scan_end - scan);
tgt->size = out - tgt->ptr; memcpy(out, scan, remaining);
out += remaining;
}
tgt->size = (size_t)(out - tgt->ptr);
tgt->ptr[tgt->size] = '\0';
return 0; return 0;
} }
...@@ -109,7 +114,7 @@ int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src) ...@@ -109,7 +114,7 @@ int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src)
assert(tgt != src); assert(tgt != src);
if (!next) if (!next)
return GIT_ENOTFOUND; return git_buf_set(tgt, src->ptr, src->size);
/* attempt to reduce reallocs while in the loop */ /* attempt to reduce reallocs while in the loop */
if (git_buf_grow(tgt, src->size + (src->size >> 4) + 1) < 0) if (git_buf_grow(tgt, src->size + (src->size >> 4) + 1) < 0)
......
...@@ -56,16 +56,16 @@ GIT_INLINE(int) git_buf_text_puts_escape_regex(git_buf *buf, const char *string) ...@@ -56,16 +56,16 @@ GIT_INLINE(int) git_buf_text_puts_escape_regex(git_buf *buf, const char *string)
extern void git_buf_text_unescape(git_buf *buf); extern void git_buf_text_unescape(git_buf *buf);
/** /**
* Replace all \r\n with \n (or do nothing if no \r\n are found) * Replace all \r\n with \n. Does not modify \r without trailing \n.
* *
* @return 0 on success, GIT_ENOTFOUND if no \r\n, -1 on memory error * @return 0 on success, -1 on memory error
*/ */
extern int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src); extern int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src);
/** /**
* Replace all \n with \r\n (or do nothing if no \n are found) * Replace all \n with \r\n. Does not modify existing \r\n.
* *
* @return 0 on success, GIT_ENOTFOUND if no \n, -1 on memory error * @return 0 on success, -1 on memory error
*/ */
extern int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src); extern int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src);
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
*/ */
#include "buffer.h" #include "buffer.h"
#include "posix.h" #include "posix.h"
#include "git2/buffer.h"
#include <stdarg.h> #include <stdarg.h>
#include <ctype.h> #include <ctype.h>
...@@ -31,7 +32,8 @@ void git_buf_init(git_buf *buf, size_t initial_size) ...@@ -31,7 +32,8 @@ void git_buf_init(git_buf *buf, size_t initial_size)
git_buf_grow(buf, initial_size); git_buf_grow(buf, initial_size);
} }
int git_buf_try_grow(git_buf *buf, size_t target_size, bool mark_oom) int git_buf_try_grow(
git_buf *buf, size_t target_size, bool mark_oom, bool preserve_external)
{ {
char *new_ptr; char *new_ptr;
size_t new_size; size_t new_size;
...@@ -39,6 +41,9 @@ int git_buf_try_grow(git_buf *buf, size_t target_size, bool mark_oom) ...@@ -39,6 +41,9 @@ int git_buf_try_grow(git_buf *buf, size_t target_size, bool mark_oom)
if (buf->ptr == git_buf__oom) if (buf->ptr == git_buf__oom)
return -1; return -1;
if (!target_size)
target_size = buf->size;
if (target_size <= buf->asize) if (target_size <= buf->asize)
return 0; return 0;
...@@ -66,6 +71,9 @@ int git_buf_try_grow(git_buf *buf, size_t target_size, bool mark_oom) ...@@ -66,6 +71,9 @@ int git_buf_try_grow(git_buf *buf, size_t target_size, bool mark_oom)
return -1; return -1;
} }
if (preserve_external && !buf->asize && buf->ptr != NULL && buf->size > 0)
memcpy(new_ptr, buf->ptr, min(buf->size, new_size));
buf->asize = new_size; buf->asize = new_size;
buf->ptr = new_ptr; buf->ptr = new_ptr;
...@@ -77,11 +85,16 @@ int git_buf_try_grow(git_buf *buf, size_t target_size, bool mark_oom) ...@@ -77,11 +85,16 @@ int git_buf_try_grow(git_buf *buf, size_t target_size, bool mark_oom)
return 0; return 0;
} }
int git_buf_grow(git_buf *buffer, size_t target_size)
{
return git_buf_try_grow(buffer, target_size, true, true);
}
void git_buf_free(git_buf *buf) void git_buf_free(git_buf *buf)
{ {
if (!buf) return; if (!buf) return;
if (buf->ptr != git_buf__initbuf && buf->ptr != git_buf__oom) if (buf->asize > 0 && buf->ptr != NULL && buf->ptr != git_buf__oom)
git__free(buf->ptr); git__free(buf->ptr);
git_buf_init(buf, 0); git_buf_init(buf, 0);
...@@ -90,11 +103,15 @@ void git_buf_free(git_buf *buf) ...@@ -90,11 +103,15 @@ void git_buf_free(git_buf *buf)
void git_buf_clear(git_buf *buf) void git_buf_clear(git_buf *buf)
{ {
buf->size = 0; buf->size = 0;
if (!buf->ptr)
buf->ptr = git_buf__initbuf;
if (buf->asize > 0) if (buf->asize > 0)
buf->ptr[0] = '\0'; buf->ptr[0] = '\0';
} }
int git_buf_set(git_buf *buf, const char *data, size_t len) int git_buf_set(git_buf *buf, const void *data, size_t len)
{ {
if (len == 0 || data == NULL) { if (len == 0 || data == NULL) {
git_buf_clear(buf); git_buf_clear(buf);
...@@ -137,7 +154,7 @@ int git_buf_puts(git_buf *buf, const char *string) ...@@ -137,7 +154,7 @@ int git_buf_puts(git_buf *buf, const char *string)
return git_buf_put(buf, string, strlen(string)); return git_buf_put(buf, string, strlen(string));
} }
static const char b64str[64] = static const char b64str[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int git_buf_put_base64(git_buf *buf, const char *data, size_t len) int git_buf_put_base64(git_buf *buf, const char *data, size_t len)
...@@ -194,6 +211,8 @@ int git_buf_vprintf(git_buf *buf, const char *format, va_list ap) ...@@ -194,6 +211,8 @@ int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
format, args format, args
); );
va_end(args);
if (len < 0) { if (len < 0) {
git__free(buf->ptr); git__free(buf->ptr);
buf->ptr = git_buf__oom; buf->ptr = git_buf__oom;
......
...@@ -9,18 +9,26 @@ ...@@ -9,18 +9,26 @@
#include "common.h" #include "common.h"
#include "git2/strarray.h" #include "git2/strarray.h"
#include "git2/buffer.h"
#include <stdarg.h> #include <stdarg.h>
typedef struct { /* typedef struct {
char *ptr; * char *ptr;
size_t asize, size; * size_t asize, size;
} git_buf; * } git_buf;
*/
extern char git_buf__initbuf[]; extern char git_buf__initbuf[];
extern char git_buf__oom[]; extern char git_buf__oom[];
/* Use to initialize buffer structure when git_buf is on stack */
#define GIT_BUF_INIT { git_buf__initbuf, 0, 0 } #define GIT_BUF_INIT { git_buf__initbuf, 0, 0 }
GIT_INLINE(bool) git_buf_is_allocated(const git_buf *buf)
{
return (buf->ptr != NULL && buf->asize > 0);
}
/** /**
* Initialize a git_buf structure. * Initialize a git_buf structure.
* *
...@@ -32,27 +40,16 @@ extern void git_buf_init(git_buf *buf, size_t initial_size); ...@@ -32,27 +40,16 @@ extern void git_buf_init(git_buf *buf, size_t initial_size);
/** /**
* Attempt to grow the buffer to hold at least `target_size` bytes. * Attempt to grow the buffer to hold at least `target_size` bytes.
* *
* If the allocation fails, this will return an error. If mark_oom is true, * If the allocation fails, this will return an error. If `mark_oom` is true,
* this will mark the buffer as invalid for future operations; if false, * this will mark the buffer as invalid for future operations; if false,
* existing buffer content will be preserved, but calling code must handle * existing buffer content will be preserved, but calling code must handle
* that buffer was not expanded. * that buffer was not expanded. If `preserve_external` is true, then any
* existing data pointed to be `ptr` even if `asize` is zero will be copied
* into the newly allocated buffer.
*/ */
extern int git_buf_try_grow(git_buf *buf, size_t target_size, bool mark_oom); extern int git_buf_try_grow(
git_buf *buf, size_t target_size, bool mark_oom, bool preserve_external);
/**
* Grow the buffer to hold at least `target_size` bytes.
*
* If the allocation fails, this will return an error and the buffer will be
* marked as invalid for future operations, invaliding contents.
*
* @return 0 on success or -1 on failure
*/
GIT_INLINE(int) git_buf_grow(git_buf *buf, size_t target_size)
{
return git_buf_try_grow(buf, target_size, true);
}
extern void git_buf_free(git_buf *buf);
extern void git_buf_swap(git_buf *buf_a, git_buf *buf_b); extern void git_buf_swap(git_buf *buf_a, git_buf *buf_b);
extern char *git_buf_detach(git_buf *buf); extern char *git_buf_detach(git_buf *buf);
extern void git_buf_attach(git_buf *buf, char *ptr, size_t asize); extern void git_buf_attach(git_buf *buf, char *ptr, size_t asize);
...@@ -81,7 +78,6 @@ GIT_INLINE(bool) git_buf_oom(const git_buf *buf) ...@@ -81,7 +78,6 @@ GIT_INLINE(bool) git_buf_oom(const git_buf *buf)
* return code of these functions and call them in a series then just call * return code of these functions and call them in a series then just call
* git_buf_oom at the end. * 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); int git_buf_sets(git_buf *buf, const char *string);
int git_buf_putc(git_buf *buf, char c); 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_put(git_buf *buf, const char *data, size_t len);
......
...@@ -678,7 +678,7 @@ fail: ...@@ -678,7 +678,7 @@ fail:
static int buffer_to_file( static int buffer_to_file(
struct stat *st, struct stat *st,
git_buf *buffer, git_buf *buf,
const char *path, const char *path,
mode_t dir_mode, mode_t dir_mode,
int file_open_flags, int file_open_flags,
...@@ -690,7 +690,7 @@ static int buffer_to_file( ...@@ -690,7 +690,7 @@ static int buffer_to_file(
return error; return error;
if ((error = git_futils_writebuffer( if ((error = git_futils_writebuffer(
buffer, path, file_open_flags, file_mode)) < 0) buf, path, file_open_flags, file_mode)) < 0)
return error; return error;
if (st != NULL && (error = p_stat(path, st)) < 0) if (st != NULL && (error = p_stat(path, st)) < 0)
...@@ -710,57 +710,28 @@ static int blob_content_to_file( ...@@ -710,57 +710,28 @@ static int blob_content_to_file(
mode_t entry_filemode, mode_t entry_filemode,
git_checkout_opts *opts) git_checkout_opts *opts)
{ {
int error = -1, nb_filters = 0; int error = 0;
mode_t file_mode = opts->file_mode; mode_t file_mode = opts->file_mode ? opts->file_mode : entry_filemode;
bool dont_free_filtered; git_buf out = GIT_BUF_INIT;
git_buf unfiltered = GIT_BUF_INIT, filtered = GIT_BUF_INIT; git_filter_list *fl = NULL;
git_vector filters = GIT_VECTOR_INIT;
/* Create a fake git_buf from the blob raw data... */
filtered.ptr = (void *)git_blob_rawcontent(blob);
filtered.size = (size_t)git_blob_rawsize(blob);
/* ... and make sure it doesn't get unexpectedly freed */
dont_free_filtered = true;
if (!opts->disable_filters &&
!git_buf_text_is_binary(&filtered) &&
(nb_filters = git_filters_load(
&filters,
git_object_owner((git_object *)blob),
path,
GIT_FILTER_TO_WORKTREE)) > 0)
{
/* reset 'filtered' so it can be a filter target */
git_buf_init(&filtered, 0);
dont_free_filtered = false;
}
if (nb_filters < 0)
return nb_filters;
if (nb_filters > 0) { if (!opts->disable_filters)
if ((error = git_blob__getbuf(&unfiltered, blob)) < 0) error = git_filter_list_load(
goto cleanup; &fl, git_blob_owner(blob), blob, path, GIT_FILTER_TO_WORKTREE);
if ((error = git_filters_apply(&filtered, &unfiltered, &filters)) < 0) if (!error)
goto cleanup; error = git_filter_list_apply_to_blob(&out, fl, blob);
}
/* Allow overriding of file mode */ git_filter_list_free(fl);
if (!file_mode)
file_mode = entry_filemode;
error = buffer_to_file( if (!error) {
st, &filtered, path, opts->dir_mode, opts->file_open_flags, file_mode); error = buffer_to_file(
st, &out, path, opts->dir_mode, opts->file_open_flags, file_mode);
if (!error)
st->st_mode = entry_filemode; st->st_mode = entry_filemode;
cleanup: git_buf_free(&out);
git_filters_free(&filters); }
git_buf_free(&unfiltered);
if (!dont_free_filtered)
git_buf_free(&filtered);
return error; return error;
} }
...@@ -1232,7 +1203,7 @@ static int checkout_data_init( ...@@ -1232,7 +1203,7 @@ static int checkout_data_init(
error = checkout_lookup_head_tree(&data->opts.baseline, repo); error = checkout_lookup_head_tree(&data->opts.baseline, repo);
if (error == GIT_EORPHANEDHEAD) { if (error == GIT_EUNBORNBRANCH) {
error = 0; error = 0;
giterr_clear(); giterr_clear();
} }
......
...@@ -415,7 +415,7 @@ static bool should_checkout( ...@@ -415,7 +415,7 @@ static bool should_checkout(
if (opts->checkout_strategy == GIT_CHECKOUT_NONE) if (opts->checkout_strategy == GIT_CHECKOUT_NONE)
return false; return false;
return !git_repository_head_orphan(repo); return !git_repository_head_unborn(repo);
} }
static void normalize_options(git_clone_options *dst, const git_clone_options *src, git_repository_init_options *initOptions) static void normalize_options(git_clone_options *dst, const git_clone_options *src, git_repository_init_options *initOptions)
...@@ -427,8 +427,7 @@ static void normalize_options(git_clone_options *dst, const git_clone_options *s ...@@ -427,8 +427,7 @@ static void normalize_options(git_clone_options *dst, const git_clone_options *s
/* Provide defaults for null pointers */ /* Provide defaults for null pointers */
if (!dst->remote_name) dst->remote_name = "origin"; if (!dst->remote_name) dst->remote_name = "origin";
if (!dst->init_options) if (!dst->init_options) {
{
dst->init_options = initOptions; dst->init_options = initOptions;
initOptions->flags = GIT_REPOSITORY_INIT_MKPATH; initOptions->flags = GIT_REPOSITORY_INIT_MKPATH;
if (dst->bare) if (dst->bare)
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "git2/attr.h" #include "git2/attr.h"
#include "git2/blob.h" #include "git2/blob.h"
#include "git2/index.h" #include "git2/index.h"
#include "git2/sys/filter.h"
#include "common.h" #include "common.h"
#include "fileops.h" #include "fileops.h"
...@@ -19,13 +20,11 @@ ...@@ -19,13 +20,11 @@
struct crlf_attrs { struct crlf_attrs {
int crlf_action; int crlf_action;
int eol; int eol;
int auto_crlf;
}; };
struct crlf_filter { struct crlf_filter {
git_filter f; git_filter f;
struct crlf_attrs attrs;
git_repository *repo;
char path[GIT_FLEX_ARRAY];
}; };
static int check_crlf(const char *value) static int check_crlf(const char *value)
...@@ -76,41 +75,10 @@ static int crlf_input_action(struct crlf_attrs *ca) ...@@ -76,41 +75,10 @@ static int crlf_input_action(struct crlf_attrs *ca)
return ca->crlf_action; return ca->crlf_action;
} }
static int crlf_load_attributes(struct crlf_attrs *ca, git_repository *repo, const char *path) static int has_cr_in_index(const git_filter_source *src)
{ {
#define NUM_CONV_ATTRS 3 git_repository *repo = git_filter_source_repo(src);
const char *path = git_filter_source_path(src);
static const char *attr_names[NUM_CONV_ATTRS] = {
"crlf", "eol", "text",
};
const char *attr_vals[NUM_CONV_ATTRS];
int error;
error = git_attr_get_many(attr_vals,
repo, 0, path, NUM_CONV_ATTRS, attr_names);
if (error == GIT_ENOTFOUND) {
ca->crlf_action = GIT_CRLF_GUESS;
ca->eol = GIT_EOL_UNSET;
return 0;
}
if (error == 0) {
ca->crlf_action = check_crlf(attr_vals[2]); /* text */
if (ca->crlf_action == GIT_CRLF_GUESS)
ca->crlf_action = check_crlf(attr_vals[0]); /* clrf */
ca->eol = check_eol(attr_vals[1]); /* eol */
return 0;
}
return -1;
}
static int has_cr_in_index(git_filter *self)
{
struct crlf_filter *filter = (struct crlf_filter *)self;
git_index *index; git_index *index;
const git_index_entry *entry; const git_index_entry *entry;
git_blob *blob; git_blob *blob;
...@@ -118,19 +86,22 @@ static int has_cr_in_index(git_filter *self) ...@@ -118,19 +86,22 @@ static int has_cr_in_index(git_filter *self)
git_off_t blobsize; git_off_t blobsize;
bool found_cr; bool found_cr;
if (git_repository_index__weakptr(&index, filter->repo) < 0) { if (!path)
return false;
if (git_repository_index__weakptr(&index, repo) < 0) {
giterr_clear(); giterr_clear();
return false; return false;
} }
if (!(entry = git_index_get_bypath(index, filter->path, 0)) && if (!(entry = git_index_get_bypath(index, path, 0)) &&
!(entry = git_index_get_bypath(index, filter->path, 1))) !(entry = git_index_get_bypath(index, path, 1)))
return false; return false;
if (!S_ISREG(entry->mode)) /* don't crlf filter non-blobs */ if (!S_ISREG(entry->mode)) /* don't crlf filter non-blobs */
return true; return true;
if (git_blob_lookup(&blob, filter->repo, &entry->oid) < 0) if (git_blob_lookup(&blob, repo, &entry->oid) < 0)
return false; return false;
blobcontent = git_blob_rawcontent(blob); blobcontent = git_blob_rawcontent(blob);
...@@ -147,27 +118,24 @@ static int has_cr_in_index(git_filter *self) ...@@ -147,27 +118,24 @@ static int has_cr_in_index(git_filter *self)
} }
static int crlf_apply_to_odb( static int crlf_apply_to_odb(
git_filter *self, git_buf *dest, const git_buf *source) struct crlf_attrs *ca,
git_buf *to,
const git_buf *from,
const git_filter_source *src)
{ {
struct crlf_filter *filter = (struct crlf_filter *)self;
assert(self && dest && source);
/* Empty file? Nothing to do */ /* Empty file? Nothing to do */
if (git_buf_len(source) == 0) if (!git_buf_len(from))
return 0; return 0;
/* Heuristics to see if we can skip the conversion. /* Heuristics to see if we can skip the conversion.
* Straight from Core Git. * Straight from Core Git.
*/ */
if (filter->attrs.crlf_action == GIT_CRLF_AUTO || if (ca->crlf_action == GIT_CRLF_AUTO || ca->crlf_action == GIT_CRLF_GUESS) {
filter->attrs.crlf_action == GIT_CRLF_GUESS) {
git_buf_text_stats stats; git_buf_text_stats stats;
/* Check heuristics for binary vs text... */ /* Check heuristics for binary vs text - returns true if binary */
if (git_buf_text_gather_stats(&stats, source, false)) if (git_buf_text_gather_stats(&stats, from, false))
return -1; return GIT_PASSTHROUGH;
/* /*
* We're currently not going to even try to convert stuff * We're currently not going to even try to convert stuff
...@@ -175,28 +143,28 @@ static int crlf_apply_to_odb( ...@@ -175,28 +143,28 @@ static int crlf_apply_to_odb(
* stuff? * stuff?
*/ */
if (stats.cr != stats.crlf) if (stats.cr != stats.crlf)
return -1; return GIT_PASSTHROUGH;
if (filter->attrs.crlf_action == GIT_CRLF_GUESS) { if (ca->crlf_action == GIT_CRLF_GUESS) {
/* /*
* If the file in the index has any CR in it, do not convert. * If the file in the index has any CR in it, do not convert.
* This is the new safer autocrlf handling. * This is the new safer autocrlf handling.
*/ */
if (has_cr_in_index(self)) if (has_cr_in_index(src))
return -1; return GIT_PASSTHROUGH;
} }
if (!stats.cr) if (!stats.cr)
return -1; return GIT_PASSTHROUGH;
} }
/* Actually drop the carriage returns */ /* Actually drop the carriage returns */
return git_buf_text_crlf_to_lf(dest, source); return git_buf_text_crlf_to_lf(to, from);
} }
static const char *line_ending(struct crlf_filter *filter) static const char *line_ending(struct crlf_attrs *ca)
{ {
switch (filter->attrs.crlf_action) { switch (ca->crlf_action) {
case GIT_CRLF_BINARY: case GIT_CRLF_BINARY:
case GIT_CRLF_INPUT: case GIT_CRLF_INPUT:
return "\n"; return "\n";
...@@ -213,11 +181,9 @@ static const char *line_ending(struct crlf_filter *filter) ...@@ -213,11 +181,9 @@ static const char *line_ending(struct crlf_filter *filter)
goto line_ending_error; goto line_ending_error;
} }
switch (filter->attrs.eol) { switch (ca->eol) {
case GIT_EOL_UNSET: case GIT_EOL_UNSET:
return GIT_EOL_NATIVE == GIT_EOL_CRLF return GIT_EOL_NATIVE == GIT_EOL_CRLF ? "\r\n" : "\n";
? "\r\n"
: "\n";
case GIT_EOL_CRLF: case GIT_EOL_CRLF:
return "\r\n"; return "\r\n";
...@@ -235,41 +201,64 @@ line_ending_error: ...@@ -235,41 +201,64 @@ line_ending_error:
} }
static int crlf_apply_to_workdir( static int crlf_apply_to_workdir(
git_filter *self, git_buf *dest, const git_buf *source) struct crlf_attrs *ca, git_buf *to, const git_buf *from)
{ {
struct crlf_filter *filter = (struct crlf_filter *)self;
const char *workdir_ending = NULL; const char *workdir_ending = NULL;
assert(self && dest && source);
/* Empty file? Nothing to do. */ /* Empty file? Nothing to do. */
if (git_buf_len(source) == 0) if (git_buf_len(from) == 0)
return -1; return 0;
/* Don't filter binary files */
if (git_buf_text_is_binary(from))
return GIT_PASSTHROUGH;
/* Determine proper line ending */ /* Determine proper line ending */
workdir_ending = line_ending(filter); workdir_ending = line_ending(ca);
if (!workdir_ending) if (!workdir_ending)
return -1; return -1;
if (!strcmp("\n", workdir_ending)) /* do nothing for \n ending */
return -1;
/* for now, only lf->crlf conversion is supported here */ if (!strcmp("\n", workdir_ending)) {
assert(!strcmp("\r\n", workdir_ending)); if (ca->crlf_action == GIT_CRLF_GUESS && ca->auto_crlf)
return git_buf_text_lf_to_crlf(dest, source); return GIT_PASSTHROUGH;
if (git_buf_find(from, '\r') < 0)
return GIT_PASSTHROUGH;
if (git_buf_text_crlf_to_lf(to, from) < 0)
return -1;
} else {
/* only other supported option is lf->crlf conversion */
assert(!strcmp("\r\n", workdir_ending));
if (git_buf_text_lf_to_crlf(to, from) < 0)
return -1;
}
return 0;
} }
static int find_and_add_filter( static int crlf_check(
git_vector *filters, git_repository *repo, const char *path, git_filter *self,
int (*apply)(struct git_filter *self, git_buf *dest, const git_buf *source)) void **payload, /* points to NULL ptr on entry, may be set */
const git_filter_source *src,
const char **attr_values)
{ {
struct crlf_attrs ca;
struct crlf_filter *filter;
size_t pathlen;
int error; int error;
struct crlf_attrs ca;
GIT_UNUSED(self);
/* Load gitattributes for the path */ if (!attr_values) {
if ((error = crlf_load_attributes(&ca, repo, path)) < 0) ca.crlf_action = GIT_CRLF_GUESS;
return error; ca.eol = GIT_EOL_UNSET;
} else {
ca.crlf_action = check_crlf(attr_values[2]); /* text */
if (ca.crlf_action == GIT_CRLF_GUESS)
ca.crlf_action = check_crlf(attr_values[0]); /* clrf */
ca.eol = check_eol(attr_values[1]); /* eol */
}
ca.auto_crlf = GIT_AUTO_CRLF_DEFAULT;
/* /*
* Use the core Git logic to see if we should perform CRLF for this file * Use the core Git logic to see if we should perform CRLF for this file
...@@ -278,41 +267,64 @@ static int find_and_add_filter( ...@@ -278,41 +267,64 @@ static int find_and_add_filter(
ca.crlf_action = crlf_input_action(&ca); ca.crlf_action = crlf_input_action(&ca);
if (ca.crlf_action == GIT_CRLF_BINARY) if (ca.crlf_action == GIT_CRLF_BINARY)
return 0; return GIT_PASSTHROUGH;
if (ca.crlf_action == GIT_CRLF_GUESS) { if (ca.crlf_action == GIT_CRLF_GUESS) {
int auto_crlf; error = git_repository__cvar(
&ca.auto_crlf, git_filter_source_repo(src), GIT_CVAR_AUTO_CRLF);
if ((error = git_repository__cvar(&auto_crlf, repo, GIT_CVAR_AUTO_CRLF)) < 0) if (error < 0)
return error; return error;
if (auto_crlf == GIT_AUTO_CRLF_FALSE) if (ca.auto_crlf == GIT_AUTO_CRLF_FALSE)
return 0; return GIT_PASSTHROUGH;
} }
/* If we're good, we create a new filter object and push it *payload = git__malloc(sizeof(ca));
* into the filters array */ GITERR_CHECK_ALLOC(*payload);
pathlen = strlen(path); memcpy(*payload, &ca, sizeof(ca));
filter = git__malloc(sizeof(struct crlf_filter) + pathlen + 1);
GITERR_CHECK_ALLOC(filter); return 0;
}
filter->f.apply = apply; static int crlf_apply(
filter->f.do_free = NULL; git_filter *self,
memcpy(&filter->attrs, &ca, sizeof(struct crlf_attrs)); void **payload, /* may be read and/or set */
filter->repo = repo; git_buf *to,
memcpy(filter->path, path, pathlen + 1); const git_buf *from,
const git_filter_source *src)
{
/* initialize payload in case `check` was bypassed */
if (!*payload) {
int error = crlf_check(self, payload, src, NULL);
if (error < 0 && error != GIT_PASSTHROUGH)
return error;
}
return git_vector_insert(filters, filter); if (git_filter_source_mode(src) == GIT_FILTER_SMUDGE)
return crlf_apply_to_workdir(*payload, to, from);
else
return crlf_apply_to_odb(*payload, to, from, src);
} }
int git_filter_add__crlf_to_odb( static void crlf_cleanup(
git_vector *filters, git_repository *repo, const char *path) git_filter *self,
void *payload)
{ {
return find_and_add_filter(filters, repo, path, &crlf_apply_to_odb); GIT_UNUSED(self);
git__free(payload);
} }
int git_filter_add__crlf_to_workdir( git_filter *git_crlf_filter_new(void)
git_vector *filters, git_repository *repo, const char *path)
{ {
return find_and_add_filter(filters, repo, path, &crlf_apply_to_workdir); struct crlf_filter *f = git__calloc(1, sizeof(struct crlf_filter));
f->f.version = GIT_FILTER_VERSION;
f->f.attributes = "crlf eol text";
f->f.initialize = NULL;
f->f.shutdown = git_filter_free;
f->f.check = crlf_check;
f->f.apply = crlf_apply;
f->f.cleanup = crlf_cleanup;
return (git_filter *)f;
} }
...@@ -440,7 +440,8 @@ static int diff_list_apply_options( ...@@ -440,7 +440,8 @@ static int diff_list_apply_options(
/* If not given explicit `opts`, check `diff.xyz` configs */ /* If not given explicit `opts`, check `diff.xyz` configs */
if (!opts) { if (!opts) {
diff->opts.context_lines = config_int(cfg, "diff.context", 3); int context = config_int(cfg, "diff.context", 3);
diff->opts.context_lines = context >= 0 ? (uint16_t)context : 3;
/* add other defaults here */ /* add other defaults here */
} }
...@@ -568,21 +569,21 @@ int git_diff__oid_for_file( ...@@ -568,21 +569,21 @@ int git_diff__oid_for_file(
giterr_set(GITERR_OS, "File size overflow (for 32-bits) on '%s'", path); giterr_set(GITERR_OS, "File size overflow (for 32-bits) on '%s'", path);
result = -1; result = -1;
} else { } else {
git_vector filters = GIT_VECTOR_INIT; git_filter_list *fl = NULL;
result = git_filters_load(&filters, repo, path, GIT_FILTER_TO_ODB); result = git_filter_list_load(&fl, repo, NULL, path, GIT_FILTER_TO_ODB);
if (result >= 0) { if (!result) {
int fd = git_futils_open_ro(full_path.ptr); int fd = git_futils_open_ro(full_path.ptr);
if (fd < 0) if (fd < 0)
result = fd; result = fd;
else { else {
result = git_odb__hashfd_filtered( result = git_odb__hashfd_filtered(
oid, fd, (size_t)size, GIT_OBJ_BLOB, &filters); oid, fd, (size_t)size, GIT_OBJ_BLOB, fl);
p_close(fd); p_close(fd);
} }
}
git_filters_free(&filters); git_filter_list_free(fl);
}
} }
cleanup: cleanup:
......
...@@ -296,9 +296,9 @@ static int diff_file_content_load_workdir_file( ...@@ -296,9 +296,9 @@ static int diff_file_content_load_workdir_file(
git_diff_file_content *fc, git_buf *path) git_diff_file_content *fc, git_buf *path)
{ {
int error = 0; int error = 0;
git_vector filters = GIT_VECTOR_INIT; git_filter_list *fl = NULL;
git_buf raw = GIT_BUF_INIT, filtered = GIT_BUF_INIT;
git_file fd = git_futils_open_ro(git_buf_cstr(path)); git_file fd = git_futils_open_ro(git_buf_cstr(path));
git_buf raw = GIT_BUF_INIT;
if (fd < 0) if (fd < 0)
return fd; return fd;
...@@ -310,41 +310,38 @@ static int diff_file_content_load_workdir_file( ...@@ -310,41 +310,38 @@ static int diff_file_content_load_workdir_file(
if (diff_file_content_binary_by_size(fc)) if (diff_file_content_binary_by_size(fc))
goto cleanup; goto cleanup;
if ((error = git_filters_load( if ((error = git_filter_list_load(
&filters, fc->repo, fc->file->path, GIT_FILTER_TO_ODB)) < 0) &fl, fc->repo, NULL, fc->file->path, GIT_FILTER_TO_ODB)) < 0)
goto cleanup; goto cleanup;
/* error >= is a filter count */
if (error == 0) { /* if there are no filters, try to mmap the file */
if (fl == NULL) {
if (!(error = git_futils_mmap_ro( if (!(error = git_futils_mmap_ro(
&fc->map, fd, 0, (size_t)fc->file->size))) &fc->map, fd, 0, (size_t)fc->file->size))) {
fc->flags |= GIT_DIFF_FLAG__UNMAP_DATA; fc->flags |= GIT_DIFF_FLAG__UNMAP_DATA;
else /* fall through to try readbuffer below */ goto cleanup;
giterr_clear(); }
/* if mmap failed, fall through to try readbuffer below */
giterr_clear();
} }
if (error != 0) { if (!(error = git_futils_readbuffer_fd(&raw, fd, (size_t)fc->file->size))) {
error = git_futils_readbuffer_fd(&raw, fd, (size_t)fc->file->size); git_buf out = GIT_BUF_INIT;
if (error < 0)
goto cleanup;
if (!filters.length) error = git_filter_list_apply_to_data(&out, fl, &raw);
git_buf_swap(&filtered, &raw);
else git_buf_free(&raw);
error = git_filters_apply(&filtered, &raw, &filters);
if (!error) { if (!error) {
fc->map.len = git_buf_len(&filtered); fc->map.len = out.size;
fc->map.data = git_buf_detach(&filtered); fc->map.data = out.ptr;
fc->flags |= GIT_DIFF_FLAG__FREE_DATA; fc->flags |= GIT_DIFF_FLAG__FREE_DATA;
} }
git_buf_free(&raw);
git_buf_free(&filtered);
} }
cleanup: cleanup:
git_filters_free(&filters); git_filter_list_free(fl);
p_close(fd); p_close(fd);
return error; return error;
......
...@@ -336,7 +336,7 @@ static int diff_print_patch_hunk( ...@@ -336,7 +336,7 @@ static int diff_print_patch_hunk(
return 0; return 0;
git_buf_clear(pi->buf); git_buf_clear(pi->buf);
if (git_buf_printf(pi->buf, "%.*s", (int)header_len, header) < 0) if (git_buf_put(pi->buf, header, header_len) < 0)
return -1; return -1;
if (pi->print_cb(d, r, GIT_DIFF_LINE_HUNK_HDR, if (pi->print_cb(d, r, GIT_DIFF_LINE_HUNK_HDR,
...@@ -360,13 +360,14 @@ static int diff_print_patch_line( ...@@ -360,13 +360,14 @@ static int diff_print_patch_line(
return 0; return 0;
git_buf_clear(pi->buf); git_buf_clear(pi->buf);
git_buf_grow(pi->buf, content_len + 2);
if (line_origin == GIT_DIFF_LINE_ADDITION || if (line_origin == GIT_DIFF_LINE_ADDITION ||
line_origin == GIT_DIFF_LINE_DELETION || line_origin == GIT_DIFF_LINE_DELETION ||
line_origin == GIT_DIFF_LINE_CONTEXT) line_origin == GIT_DIFF_LINE_CONTEXT)
git_buf_printf(pi->buf, "%c%.*s", line_origin, (int)content_len, content); git_buf_putc(pi->buf, line_origin);
else if (content_len > 0)
git_buf_printf(pi->buf, "%.*s", (int)content_len, content); git_buf_put(pi->buf, content, content_len);
if (git_buf_oom(pi->buf)) if (git_buf_oom(pi->buf))
return -1; return -1;
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
*/ */
#include "common.h" #include "common.h"
#include "fileops.h" #include "fileops.h"
#include "global.h"
#include <ctype.h> #include <ctype.h>
#if GIT_WIN32 #if GIT_WIN32
#include "win32/findfile.h" #include "win32/findfile.h"
...@@ -55,18 +56,8 @@ int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode ...@@ -55,18 +56,8 @@ int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode
int git_futils_creat_locked(const char *path, const mode_t mode) int git_futils_creat_locked(const char *path, const mode_t mode)
{ {
int fd; int fd = p_open(path, O_WRONLY | O_CREAT | O_TRUNC |
#ifdef GIT_WIN32
git_win32_path buf;
git_win32_path_from_c(buf, path);
fd = _wopen(buf, O_WRONLY | O_CREAT | O_TRUNC |
O_EXCL | O_BINARY | O_CLOEXEC, mode); O_EXCL | O_BINARY | O_CLOEXEC, mode);
#else
fd = open(path, O_WRONLY | O_CREAT | O_TRUNC |
O_EXCL | O_BINARY | O_CLOEXEC, mode);
#endif
if (fd < 0) { if (fd < 0) {
giterr_set(GITERR_OS, "Failed to create locked file '%s'", path); giterr_set(GITERR_OS, "Failed to create locked file '%s'", path);
...@@ -592,7 +583,7 @@ clean_up: ...@@ -592,7 +583,7 @@ clean_up:
static int git_futils_guess_system_dirs(git_buf *out) static int git_futils_guess_system_dirs(git_buf *out)
{ {
#ifdef GIT_WIN32 #ifdef GIT_WIN32
return git_win32__find_system_dirs(out); return git_win32__find_system_dirs(out, L"etc\\");
#else #else
return git_buf_sets(out, "/etc"); return git_buf_sets(out, "/etc");
#endif #endif
...@@ -624,17 +615,34 @@ static int git_futils_guess_xdg_dirs(git_buf *out) ...@@ -624,17 +615,34 @@ static int git_futils_guess_xdg_dirs(git_buf *out)
#endif #endif
} }
static int git_futils_guess_template_dirs(git_buf *out)
{
#ifdef GIT_WIN32
return git_win32__find_system_dirs(out, L"share\\git-core\\templates");
#else
return git_buf_sets(out, "/usr/share/git-core/templates");
#endif
}
typedef int (*git_futils_dirs_guess_cb)(git_buf *out); typedef int (*git_futils_dirs_guess_cb)(git_buf *out);
static git_buf git_futils__dirs[GIT_FUTILS_DIR__MAX] = static git_buf git_futils__dirs[GIT_FUTILS_DIR__MAX] =
{ GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT }; { GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT };
static git_futils_dirs_guess_cb git_futils__dir_guess[GIT_FUTILS_DIR__MAX] = { static git_futils_dirs_guess_cb git_futils__dir_guess[GIT_FUTILS_DIR__MAX] = {
git_futils_guess_system_dirs, git_futils_guess_system_dirs,
git_futils_guess_global_dirs, git_futils_guess_global_dirs,
git_futils_guess_xdg_dirs, git_futils_guess_xdg_dirs,
git_futils_guess_template_dirs,
}; };
static void git_futils_dirs_global_shutdown(void)
{
int i;
for (i = 0; i < GIT_FUTILS_DIR__MAX; ++i)
git_buf_free(&git_futils__dirs[i]);
}
int git_futils_dirs_global_init(void) int git_futils_dirs_global_init(void)
{ {
git_futils_dir_t i; git_futils_dir_t i;
...@@ -644,6 +652,8 @@ int git_futils_dirs_global_init(void) ...@@ -644,6 +652,8 @@ int git_futils_dirs_global_init(void)
for (i = 0; !error && i < GIT_FUTILS_DIR__MAX; i++) for (i = 0; !error && i < GIT_FUTILS_DIR__MAX; i++)
error = git_futils_dirs_get(&path, i); error = git_futils_dirs_get(&path, i);
git__on_shutdown(git_futils_dirs_global_shutdown);
return error; return error;
} }
...@@ -726,13 +736,6 @@ int git_futils_dirs_set(git_futils_dir_t which, const char *search_path) ...@@ -726,13 +736,6 @@ int git_futils_dirs_set(git_futils_dir_t which, const char *search_path)
return git_buf_oom(&git_futils__dirs[which]) ? -1 : 0; return git_buf_oom(&git_futils__dirs[which]) ? -1 : 0;
} }
void git_futils_dirs_free(void)
{
int i;
for (i = 0; i < GIT_FUTILS_DIR__MAX; ++i)
git_buf_free(&git_futils__dirs[i]);
}
static int git_futils_find_in_dirlist( static int git_futils_find_in_dirlist(
git_buf *path, const char *name, git_futils_dir_t which, const char *label) git_buf *path, const char *name, git_futils_dir_t which, const char *label)
{ {
...@@ -753,7 +756,8 @@ static int git_futils_find_in_dirlist( ...@@ -753,7 +756,8 @@ static int git_futils_find_in_dirlist(
continue; continue;
GITERR_CHECK_ERROR(git_buf_set(path, scan, len)); GITERR_CHECK_ERROR(git_buf_set(path, scan, len));
GITERR_CHECK_ERROR(git_buf_joinpath(path, path->ptr, name)); if (name)
GITERR_CHECK_ERROR(git_buf_joinpath(path, path->ptr, name));
if (git_path_exists(path->ptr)) if (git_path_exists(path->ptr))
return 0; return 0;
...@@ -782,6 +786,12 @@ int git_futils_find_xdg_file(git_buf *path, const char *filename) ...@@ -782,6 +786,12 @@ int git_futils_find_xdg_file(git_buf *path, const char *filename)
path, filename, GIT_FUTILS_DIR_XDG, "global/xdg"); path, filename, GIT_FUTILS_DIR_XDG, "global/xdg");
} }
int git_futils_find_template_dir(git_buf *path)
{
return git_futils_find_in_dirlist(
path, NULL, GIT_FUTILS_DIR_TEMPLATE, "template");
}
int git_futils_fake_symlink(const char *old, const char *new) int git_futils_fake_symlink(const char *old, const char *new)
{ {
int retcode = GIT_ERROR; int retcode = GIT_ERROR;
......
...@@ -306,11 +306,20 @@ extern int git_futils_find_xdg_file(git_buf *path, const char *filename); ...@@ -306,11 +306,20 @@ extern int git_futils_find_xdg_file(git_buf *path, const char *filename);
*/ */
extern int git_futils_find_system_file(git_buf *path, const char *filename); extern int git_futils_find_system_file(git_buf *path, const char *filename);
/**
* Find template directory.
*
* @param path buffer to write the full path into
* @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
*/
extern int git_futils_find_template_dir(git_buf *path);
typedef enum { typedef enum {
GIT_FUTILS_DIR_SYSTEM = 0, GIT_FUTILS_DIR_SYSTEM = 0,
GIT_FUTILS_DIR_GLOBAL = 1, GIT_FUTILS_DIR_GLOBAL = 1,
GIT_FUTILS_DIR_XDG = 2, GIT_FUTILS_DIR_XDG = 2,
GIT_FUTILS_DIR__MAX = 3, GIT_FUTILS_DIR_TEMPLATE = 3,
GIT_FUTILS_DIR__MAX = 4,
} git_futils_dir_t; } git_futils_dir_t;
/** /**
...@@ -354,11 +363,6 @@ extern int git_futils_dirs_get_str( ...@@ -354,11 +363,6 @@ extern int git_futils_dirs_get_str(
extern int git_futils_dirs_set(git_futils_dir_t which, const char *paths); extern int git_futils_dirs_set(git_futils_dir_t which, const char *paths);
/** /**
* Release / reset all search paths
*/
extern void git_futils_dirs_free(void);
/**
* Create a "fake" symlink (text file containing the target path). * Create a "fake" symlink (text file containing the target path).
* *
* @param new symlink file to be created * @param new symlink file to be created
......
...@@ -8,19 +8,7 @@ ...@@ -8,19 +8,7 @@
#define INCLUDE_filter_h__ #define INCLUDE_filter_h__
#include "common.h" #include "common.h"
#include "buffer.h" #include "git2/filter.h"
#include "git2/odb.h"
#include "git2/repository.h"
typedef struct git_filter {
int (*apply)(struct git_filter *self, git_buf *dest, const git_buf *source);
void (*do_free)(struct git_filter *self);
} git_filter;
typedef enum {
GIT_FILTER_TO_WORKTREE,
GIT_FILTER_TO_ODB
} git_filter_mode;
typedef enum { typedef enum {
GIT_CRLF_GUESS = -1, GIT_CRLF_GUESS = -1,
...@@ -31,64 +19,13 @@ typedef enum { ...@@ -31,64 +19,13 @@ typedef enum {
GIT_CRLF_AUTO, GIT_CRLF_AUTO,
} git_crlf_t; } git_crlf_t;
/* extern void git_filter_free(git_filter *filter);
* FILTER API
*/
/*
* For any given path in the working directory, fill the `filters`
* array with the relevant filters that need to be applied.
*
* Mode is either `GIT_FILTER_TO_WORKTREE` if you need to load the
* filters that will be used when checking out a file to the working
* directory, or `GIT_FILTER_TO_ODB` for the filters used when writing
* a file to the ODB.
*
* @param filters Vector where to store all the loaded filters
* @param repo Repository object that contains `path`
* @param path Relative path of the file to be filtered
* @param mode Filtering direction (WT->ODB or ODB->WT)
* @return the number of filters loaded for the file (0 if the file
* doesn't need filtering), or a negative error code
*/
extern int git_filters_load(git_vector *filters, git_repository *repo, const char *path, int mode);
/*
* Apply one or more filters to a file.
*
* The file must have been loaded as a `git_buf` object. Both the `source`
* and `dest` buffers are owned by the caller and must be freed once
* they are no longer needed.
*
* NOTE: Because of the double-buffering schema, the `source` buffer that contains
* the original file may be tampered once the filtering is complete. Regardless,
* the `dest` buffer will always contain the final result of the filtering
*
* @param dest Buffer to store the result of the filtering
* @param source Buffer containing the document to filter
* @param filters A non-empty vector of filters as supplied by `git_filters_load`
* @return 0 on success, an error code otherwise
*/
extern int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters);
/*
* Free the `filters` array generated by `git_filters_load`.
*
* Note that this frees both the array and its contents. The array will
* be clean/reusable after this call.
*
* @param filters A filters array as supplied by `git_filters_load`
*/
extern void git_filters_free(git_vector *filters);
/* /*
* Available filters * Available filters
*/ */
/* Strip CRLF, from Worktree to ODB */ extern git_filter *git_crlf_filter_new(void);
extern int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const char *path); extern git_filter *git_ident_filter_new(void);
/* Add CRLF, from ODB to worktree */
extern int git_filter_add__crlf_to_workdir(git_vector *filters, git_repository *repo, const char *path);
#endif #endif
...@@ -14,6 +14,28 @@ ...@@ -14,6 +14,28 @@
git_mutex git__mwindow_mutex; git_mutex git__mwindow_mutex;
#define MAX_SHUTDOWN_CB 8
git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB];
git_atomic git__n_shutdown_callbacks;
void git__on_shutdown(git_global_shutdown_fn callback)
{
int count = git_atomic_inc(&git__n_shutdown_callbacks);
assert(count <= MAX_SHUTDOWN_CB);
git__shutdown_callbacks[count - 1] = callback;
}
static void git__shutdown(void)
{
int pos;
while ((pos = git_atomic_dec(&git__n_shutdown_callbacks)) >= 0) {
if (git__shutdown_callbacks[pos])
git__shutdown_callbacks[pos]();
}
}
/** /**
* Handle the global state with TLS * Handle the global state with TLS
* *
...@@ -79,9 +101,7 @@ int git_threads_init(void) ...@@ -79,9 +101,7 @@ int git_threads_init(void)
void git_threads_shutdown(void) void git_threads_shutdown(void)
{ {
/* Shut down any subsystems that have global state */ /* Shut down any subsystems that have global state */
win32_pthread_shutdown(); git__shutdown();
git_futils_dirs_free();
git_hash_global_shutdown();
TlsFree(_tls_index); TlsFree(_tls_index);
_tls_init = 0; _tls_init = 0;
...@@ -140,6 +160,9 @@ int git_threads_init(void) ...@@ -140,6 +160,9 @@ int git_threads_init(void)
void git_threads_shutdown(void) void git_threads_shutdown(void)
{ {
/* Shut down any subsystems that have global state */
git__shutdown();
if (_tls_init) { if (_tls_init) {
void *ptr = pthread_getspecific(_tls_key); void *ptr = pthread_getspecific(_tls_key);
pthread_setspecific(_tls_key, NULL); pthread_setspecific(_tls_key, NULL);
...@@ -149,10 +172,6 @@ void git_threads_shutdown(void) ...@@ -149,10 +172,6 @@ void git_threads_shutdown(void)
pthread_key_delete(_tls_key); pthread_key_delete(_tls_key);
_tls_init = 0; _tls_init = 0;
git_mutex_free(&git__mwindow_mutex); git_mutex_free(&git__mwindow_mutex);
/* Shut down any subsystems that have global state */
git_hash_global_shutdown();
git_futils_dirs_free();
} }
git_global_st *git__global_state(void) git_global_st *git__global_state(void)
...@@ -179,15 +198,14 @@ static git_global_st __state; ...@@ -179,15 +198,14 @@ static git_global_st __state;
int git_threads_init(void) int git_threads_init(void)
{ {
/* noop */ /* noop */
return 0; return 0;
} }
void git_threads_shutdown(void) void git_threads_shutdown(void)
{ {
/* Shut down any subsystems that have global state */ /* Shut down any subsystems that have global state */
git_hash_global_shutdown(); git__shutdown();
git_futils_dirs_free();
} }
git_global_st *git__global_state(void) git_global_st *git__global_state(void)
......
...@@ -21,4 +21,8 @@ extern git_mutex git__mwindow_mutex; ...@@ -21,4 +21,8 @@ extern git_mutex git__mwindow_mutex;
#define GIT_GLOBAL (git__global_state()) #define GIT_GLOBAL (git__global_state())
typedef void (*git_global_shutdown_fn)(void);
extern void git__on_shutdown(git_global_shutdown_fn callback);
#endif #endif
...@@ -13,8 +13,6 @@ typedef struct git_hash_prov git_hash_prov; ...@@ -13,8 +13,6 @@ typedef struct git_hash_prov git_hash_prov;
typedef struct git_hash_ctx git_hash_ctx; typedef struct git_hash_ctx git_hash_ctx;
int git_hash_global_init(void); int git_hash_global_init(void);
void git_hash_global_shutdown(void);
int git_hash_ctx_init(git_hash_ctx *ctx); int git_hash_ctx_init(git_hash_ctx *ctx);
void git_hash_ctx_cleanup(git_hash_ctx *ctx); void git_hash_ctx_cleanup(git_hash_ctx *ctx);
......
...@@ -17,7 +17,6 @@ struct git_hash_ctx { ...@@ -17,7 +17,6 @@ struct git_hash_ctx {
}; };
#define git_hash_global_init() 0 #define git_hash_global_init() 0
#define git_hash_global_shutdown() /* noop */
#define git_hash_ctx_init(ctx) git_hash_init(ctx) #define git_hash_ctx_init(ctx) git_hash_init(ctx)
#define git_hash_ctx_cleanup(ctx) #define git_hash_ctx_cleanup(ctx)
......
...@@ -17,7 +17,6 @@ struct git_hash_ctx { ...@@ -17,7 +17,6 @@ struct git_hash_ctx {
}; };
#define git_hash_global_init() 0 #define git_hash_global_init() 0
#define git_hash_global_shutdown() /* noop */
#define git_hash_ctx_init(ctx) git_hash_init(ctx) #define git_hash_ctx_init(ctx) git_hash_init(ctx)
#define git_hash_ctx_cleanup(ctx) #define git_hash_ctx_cleanup(ctx)
......
...@@ -89,7 +89,15 @@ GIT_INLINE(void) hash_cryptoapi_prov_shutdown(void) ...@@ -89,7 +89,15 @@ GIT_INLINE(void) hash_cryptoapi_prov_shutdown(void)
hash_prov.type = INVALID; hash_prov.type = INVALID;
} }
int git_hash_global_init() static void git_hash_global_shutdown(void)
{
if (hash_prov.type == CNG)
hash_cng_prov_shutdown();
else if(hash_prov.type == CRYPTOAPI)
hash_cryptoapi_prov_shutdown();
}
int git_hash_global_init(void)
{ {
int error = 0; int error = 0;
...@@ -99,15 +107,9 @@ int git_hash_global_init() ...@@ -99,15 +107,9 @@ int git_hash_global_init()
if ((error = hash_cng_prov_init()) < 0) if ((error = hash_cng_prov_init()) < 0)
error = hash_cryptoapi_prov_init(); error = hash_cryptoapi_prov_init();
return error; git__on_shutdown(git_hash_global_shutdown);
}
void git_hash_global_shutdown() return error;
{
if (hash_prov.type == CNG)
hash_cng_prov_shutdown();
else if(hash_prov.type == CRYPTOAPI)
hash_cryptoapi_prov_shutdown();
} }
/* CryptoAPI: available in Windows XP and newer */ /* CryptoAPI: available in Windows XP and newer */
......
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "git2/sys/filter.h"
#include "filter.h"
#include "buffer.h"
#include "buf_text.h"
static int ident_find_id(
const char **id_start, const char **id_end, const char *start, size_t len)
{
const char *end = start + len, *found = NULL;
while (len > 3 && (found = memchr(start, '$', len)) != NULL) {
size_t remaining = (size_t)(end - found) - 1;
if (remaining < 3)
return GIT_ENOTFOUND;
start = found + 1;
len = remaining;
if (start[0] == 'I' && start[1] == 'd')
break;
}
if (len < 3 || !found)
return GIT_ENOTFOUND;
*id_start = found;
if ((found = memchr(start + 2, '$', len - 2)) == NULL)
return GIT_ENOTFOUND;
*id_end = found + 1;
return 0;
}
static int ident_insert_id(
git_buf *to, const git_buf *from, const git_filter_source *src)
{
char oid[GIT_OID_HEXSZ+1];
const char *id_start, *id_end, *from_end = from->ptr + from->size;
size_t need_size;
/* replace $Id$ with blob id */
if (!git_filter_source_id(src))
return GIT_PASSTHROUGH;
git_oid_tostr(oid, sizeof(oid), git_filter_source_id(src));
if (ident_find_id(&id_start, &id_end, from->ptr, from->size) < 0)
return GIT_PASSTHROUGH;
need_size = (size_t)(id_start - from->ptr) +
5 /* "$Id: " */ + GIT_OID_HEXSZ + 1 /* "$" */ +
(size_t)(from_end - id_end);
if (git_buf_grow(to, need_size) < 0)
return -1;
git_buf_set(to, from->ptr, (size_t)(id_start - from->ptr));
git_buf_put(to, "$Id: ", 5);
git_buf_put(to, oid, GIT_OID_HEXSZ);
git_buf_putc(to, '$');
git_buf_put(to, id_end, (size_t)(from_end - id_end));
return git_buf_oom(to) ? -1 : 0;
}
static int ident_remove_id(
git_buf *to, const git_buf *from)
{
const char *id_start, *id_end, *from_end = from->ptr + from->size;
size_t need_size;
if (ident_find_id(&id_start, &id_end, from->ptr, from->size) < 0)
return GIT_PASSTHROUGH;
need_size = (size_t)(id_start - from->ptr) +
4 /* "$Id$" */ + (size_t)(from_end - id_end);
if (git_buf_grow(to, need_size) < 0)
return -1;
git_buf_set(to, from->ptr, (size_t)(id_start - from->ptr));
git_buf_put(to, "$Id$", 4);
git_buf_put(to, id_end, (size_t)(from_end - id_end));
return git_buf_oom(to) ? -1 : 0;
}
static int ident_apply(
git_filter *self,
void **payload,
git_buf *to,
const git_buf *from,
const git_filter_source *src)
{
GIT_UNUSED(self); GIT_UNUSED(payload);
/* Don't filter binary files */
if (git_buf_text_is_binary(from))
return GIT_PASSTHROUGH;
if (git_filter_source_mode(src) == GIT_FILTER_SMUDGE)
return ident_insert_id(to, from, src);
else
return ident_remove_id(to, from);
}
git_filter *git_ident_filter_new(void)
{
git_filter *f = git__calloc(1, sizeof(git_filter));
f->version = GIT_FILTER_VERSION;
f->attributes = "+ident"; /* apply to files with ident attribute set */
f->shutdown = git_filter_free;
f->apply = ident_apply;
return f;
}
...@@ -410,7 +410,7 @@ static int create_index_error(int error, const char *msg) ...@@ -410,7 +410,7 @@ static int create_index_error(int error, const char *msg)
int git_index_set_caps(git_index *index, unsigned int caps) int git_index_set_caps(git_index *index, unsigned int caps)
{ {
int old_ignore_case; unsigned int old_ignore_case;
assert(index); assert(index);
...@@ -438,7 +438,7 @@ int git_index_set_caps(git_index *index, unsigned int caps) ...@@ -438,7 +438,7 @@ int git_index_set_caps(git_index *index, unsigned int caps)
} }
if (old_ignore_case != index->ignore_case) { if (old_ignore_case != index->ignore_case) {
git_index__set_ignore_case(index, index->ignore_case); git_index__set_ignore_case(index, (bool)index->ignore_case);
} }
return 0; return 0;
...@@ -2092,7 +2092,7 @@ int git_index_add_all( ...@@ -2092,7 +2092,7 @@ int git_index_add_all(
/* check if path actually matches */ /* check if path actually matches */
if (!git_pathspec__match( if (!git_pathspec__match(
&ps.pathspec, wd->path, no_fnmatch, ignorecase, &match, NULL)) &ps.pathspec, wd->path, no_fnmatch, (bool)ignorecase, &match, NULL))
continue; continue;
/* skip ignored items that are not already in the index */ /* skip ignored items that are not already in the index */
...@@ -2184,7 +2184,7 @@ static int index_apply_to_all( ...@@ -2184,7 +2184,7 @@ static int index_apply_to_all(
/* check if path actually matches */ /* check if path actually matches */
if (!git_pathspec__match( if (!git_pathspec__match(
&ps.pathspec, entry->path, false, index->ignore_case, &ps.pathspec, entry->path, false, (bool)index->ignore_case,
&match, NULL)) &match, NULL))
continue; continue;
......
...@@ -47,6 +47,11 @@ struct git_indexer_stream { ...@@ -47,6 +47,11 @@ struct git_indexer_stream {
git_transfer_progress_callback progress_cb; git_transfer_progress_callback progress_cb;
void *progress_payload; void *progress_payload;
char objbuf[8*1024]; char objbuf[8*1024];
/* Fields for calculating the packfile trailer (hash of everything before it) */
char inbuf[GIT_OID_RAWSZ];
int inbuf_len;
git_hash_ctx trailer;
}; };
struct delta_info { struct delta_info {
...@@ -121,6 +126,7 @@ int git_indexer_stream_new( ...@@ -121,6 +126,7 @@ int git_indexer_stream_new(
GITERR_CHECK_ALLOC(idx); GITERR_CHECK_ALLOC(idx);
idx->progress_cb = progress_cb; idx->progress_cb = progress_cb;
idx->progress_payload = progress_payload; idx->progress_payload = progress_payload;
git_hash_ctx_init(&idx->trailer);
error = git_buf_joinpath(&path, prefix, suff); error = git_buf_joinpath(&path, prefix, suff);
if (error < 0) if (error < 0)
...@@ -322,7 +328,6 @@ static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t ent ...@@ -322,7 +328,6 @@ static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t ent
entry->offset = (uint32_t)entry_start; entry->offset = (uint32_t)entry_start;
} }
/* FIXME: Parse the object instead of hashing it */
if (git_odb__hashobj(&oid, obj) < 0) { if (git_odb__hashobj(&oid, obj) < 0) {
giterr_set(GITERR_INDEXER, "Failed to hash object"); giterr_set(GITERR_INDEXER, "Failed to hash object");
goto on_error; goto on_error;
...@@ -370,6 +375,43 @@ static int do_progress_callback(git_indexer_stream *idx, git_transfer_progress * ...@@ -370,6 +375,43 @@ static int do_progress_callback(git_indexer_stream *idx, git_transfer_progress *
return idx->progress_cb(stats, idx->progress_payload); return idx->progress_cb(stats, idx->progress_payload);
} }
/* Hash everything but the last 20B of input */
static void hash_partially(git_indexer_stream *idx, const uint8_t *data, size_t size)
{
int to_expell, to_keep;
if (size == 0)
return;
/* Easy case, dump the buffer and the data minus the last 20 bytes */
if (size >= 20) {
git_hash_update(&idx->trailer, idx->inbuf, idx->inbuf_len);
git_hash_update(&idx->trailer, data, size - GIT_OID_RAWSZ);
data += size - GIT_OID_RAWSZ;
memcpy(idx->inbuf, data, GIT_OID_RAWSZ);
idx->inbuf_len = GIT_OID_RAWSZ;
return;
}
/* We can just append */
if (idx->inbuf_len + size <= GIT_OID_RAWSZ) {
memcpy(idx->inbuf + idx->inbuf_len, data, size);
idx->inbuf_len += size;
return;
}
/* We need to partially drain the buffer and then append */
to_expell = abs(size - (GIT_OID_RAWSZ - idx->inbuf_len));
to_keep = abs(idx->inbuf_len - to_expell);
git_hash_update(&idx->trailer, idx->inbuf, to_expell);
memmove(idx->inbuf, idx->inbuf + to_expell, to_keep);
memcpy(idx->inbuf + to_keep, data, size);
idx->inbuf_len += size - to_expell;
}
int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t size, git_transfer_progress *stats) int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t size, git_transfer_progress *stats)
{ {
int error = -1; int error = -1;
...@@ -384,6 +426,8 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz ...@@ -384,6 +426,8 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
if (git_filebuf_write(&idx->pack_file, data, size) < 0) if (git_filebuf_write(&idx->pack_file, data, size) < 0)
return -1; return -1;
hash_partially(idx, data, size);
/* Make sure we set the new size of the pack */ /* Make sure we set the new size of the pack */
if (idx->opened_pack) { if (idx->opened_pack) {
idx->pack->mwf.size += size; idx->pack->mwf.size += size;
...@@ -576,17 +620,33 @@ int git_indexer_stream_finalize(git_indexer_stream *idx, git_transfer_progress * ...@@ -576,17 +620,33 @@ int git_indexer_stream_finalize(git_indexer_stream *idx, git_transfer_progress *
struct git_pack_idx_header hdr; struct git_pack_idx_header hdr;
git_buf filename = GIT_BUF_INIT; git_buf filename = GIT_BUF_INIT;
struct entry *entry; struct entry *entry;
void *packfile_hash; git_oid trailer_hash, file_hash;
git_oid file_hash;
git_hash_ctx ctx; git_hash_ctx ctx;
git_filebuf index_file = {0}; git_filebuf index_file = {0};
void *packfile_trailer;
if (git_hash_ctx_init(&ctx) < 0) if (git_hash_ctx_init(&ctx) < 0)
return -1; return -1;
/* Test for this before resolve_deltas(), as it plays with idx->off */ /* Test for this before resolve_deltas(), as it plays with idx->off */
if (idx->off < idx->pack->mwf.size - GIT_OID_RAWSZ) { if (idx->off < idx->pack->mwf.size - 20) {
giterr_set(GITERR_INDEXER, "Indexing error: unexpected data at the end of the pack"); giterr_set(GITERR_INDEXER, "unexpected data at the end of the pack");
return -1;
}
packfile_trailer = git_mwindow_open(&idx->pack->mwf, &w, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ, &left);
if (packfile_trailer == NULL) {
git_mwindow_close(&w);
goto on_error;
}
/* Compare the packfile trailer as it was sent to us and what we calculated */
git_oid_fromraw(&file_hash, packfile_trailer);
git_mwindow_close(&w);
git_hash_final(&trailer_hash, &idx->trailer);
if (git_oid_cmp(&file_hash, &trailer_hash)) {
giterr_set(GITERR_INDEXER, "packfile trailer mismatch");
return -1; return -1;
} }
...@@ -595,7 +655,7 @@ int git_indexer_stream_finalize(git_indexer_stream *idx, git_transfer_progress * ...@@ -595,7 +655,7 @@ int git_indexer_stream_finalize(git_indexer_stream *idx, git_transfer_progress *
return -1; return -1;
if (stats->indexed_objects != stats->total_objects) { if (stats->indexed_objects != stats->total_objects) {
giterr_set(GITERR_INDEXER, "Indexing error: early EOF"); giterr_set(GITERR_INDEXER, "early EOF");
return -1; return -1;
} }
...@@ -658,23 +718,15 @@ int git_indexer_stream_finalize(git_indexer_stream *idx, git_transfer_progress * ...@@ -658,23 +718,15 @@ int git_indexer_stream_finalize(git_indexer_stream *idx, git_transfer_progress *
git_filebuf_write(&index_file, &split, sizeof(uint32_t) * 2); git_filebuf_write(&index_file, &split, sizeof(uint32_t) * 2);
} }
/* Write out the packfile trailer */ /* Write out the packfile trailer to the index */
packfile_hash = git_mwindow_open(&idx->pack->mwf, &w, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ, &left); if (git_filebuf_write(&index_file, &trailer_hash, GIT_OID_RAWSZ) < 0)
if (packfile_hash == NULL) {
git_mwindow_close(&w);
goto on_error; goto on_error;
}
memcpy(&file_hash, packfile_hash, GIT_OID_RAWSZ);
git_mwindow_close(&w);
git_filebuf_write(&index_file, &file_hash, sizeof(git_oid));
/* Write out the packfile trailer to the idx file as well */ /* Write out the hash of the idx */
if (git_filebuf_hash(&file_hash, &index_file) < 0) if (git_filebuf_hash(&trailer_hash, &index_file) < 0)
goto on_error; goto on_error;
git_filebuf_write(&index_file, &file_hash, sizeof(git_oid)); git_filebuf_write(&index_file, &trailer_hash, sizeof(git_oid));
/* Figure out what the final name should be */ /* Figure out what the final name should be */
if (index_path_stream(&filename, idx, ".idx") < 0) if (index_path_stream(&filename, idx, ".idx") < 0)
......
...@@ -58,7 +58,7 @@ struct merge_diff_df_data { ...@@ -58,7 +58,7 @@ struct merge_diff_df_data {
/* Merge base computation */ /* Merge base computation */
int git_merge_base_many(git_oid *out, git_repository *repo, const git_oid input_array[], size_t length) int git_merge_base_many(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[])
{ {
git_revwalk *walk; git_revwalk *walk;
git_vector list; git_vector list;
......
...@@ -585,7 +585,6 @@ int gitno_extract_url_parts( ...@@ -585,7 +585,6 @@ int gitno_extract_url_parts(
const char *start; const char *start;
/* /*
*
* ==> [user[:pass]@]hostname.tld[:port]/resource * ==> [user[:pass]@]hostname.tld[:port]/resource
*/ */
......
...@@ -168,7 +168,6 @@ int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type) ...@@ -168,7 +168,6 @@ int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type)
error = -1; error = -1;
goto done; goto done;
return -1;
} }
error = git_hash_final(out, &ctx); error = git_hash_final(out, &ctx);
...@@ -179,28 +178,30 @@ done: ...@@ -179,28 +178,30 @@ done:
} }
int git_odb__hashfd_filtered( int git_odb__hashfd_filtered(
git_oid *out, git_file fd, size_t size, git_otype type, git_vector *filters) git_oid *out, git_file fd, size_t size, git_otype type, git_filter_list *fl)
{ {
int error; int error;
git_buf raw = GIT_BUF_INIT; git_buf raw = GIT_BUF_INIT;
git_buf filtered = GIT_BUF_INIT;
if (!filters || !filters->length) if (!fl)
return git_odb__hashfd(out, fd, size, type); return git_odb__hashfd(out, fd, size, type);
/* size of data is used in header, so we have to read the whole file /* size of data is used in header, so we have to read the whole file
* into memory to apply filters before beginning to calculate the hash * into memory to apply filters before beginning to calculate the hash
*/ */
if (!(error = git_futils_readbuffer_fd(&raw, fd, size))) if (!(error = git_futils_readbuffer_fd(&raw, fd, size))) {
error = git_filters_apply(&filtered, &raw, filters); git_buf post = GIT_BUF_INIT;
error = git_filter_list_apply_to_data(&post, fl, &raw);
git_buf_free(&raw); git_buf_free(&raw);
if (!error) if (!error)
error = git_odb_hash(out, filtered.ptr, filtered.size, type); error = git_odb_hash(out, post.ptr, post.size, type);
git_buf_free(&filtered); git_buf_free(&post);
}
return error; return error;
} }
...@@ -621,7 +622,7 @@ int git_odb_exists(git_odb *db, const git_oid *id) ...@@ -621,7 +622,7 @@ int git_odb_exists(git_odb *db, const git_oid *id)
git_odb_backend *b = internal->backend; git_odb_backend *b = internal->backend;
if (b->exists != NULL) if (b->exists != NULL)
found = b->exists(b, id); found = (bool)b->exists(b, id);
} }
return (int)found; return (int)found;
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "vector.h" #include "vector.h"
#include "cache.h" #include "cache.h"
#include "posix.h" #include "posix.h"
#include "filter.h"
#define GIT_OBJECTS_DIR "objects/" #define GIT_OBJECTS_DIR "objects/"
#define GIT_OBJECT_DIR_MODE 0777 #define GIT_OBJECT_DIR_MODE 0777
...@@ -66,7 +67,7 @@ int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type); ...@@ -66,7 +67,7 @@ int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type);
* Acts just like git_odb__hashfd with the addition of filters... * Acts just like git_odb__hashfd with the addition of filters...
*/ */
int git_odb__hashfd_filtered( int git_odb__hashfd_filtered(
git_oid *out, git_file fd, size_t len, git_otype type, git_vector *filters); git_oid *out, git_file fd, size_t len, git_otype type, git_filter_list *fl);
/* /*
* Hash a `path`, assuming it could be a POSIX symlink: if the path is a * Hash a `path`, assuming it could be a POSIX symlink: if the path is a
......
...@@ -211,7 +211,7 @@ int git_oid_strcmp(const git_oid *oid_a, const char *str) ...@@ -211,7 +211,7 @@ int git_oid_strcmp(const git_oid *oid_a, const char *str)
for (a = oid_a->id; *str && (a - oid_a->id) < GIT_OID_RAWSZ; ++a) { for (a = oid_a->id; *str && (a - oid_a->id) < GIT_OID_RAWSZ; ++a) {
if ((hexval = git__fromhex(*str++)) < 0) if ((hexval = git__fromhex(*str++)) < 0)
return -1; return -1;
strval = hexval << 4; strval = (unsigned char)(hexval << 4);
if (*str) { if (*str) {
if ((hexval = git__fromhex(*str++)) < 0) if ((hexval = git__fromhex(*str++)) < 0)
return -1; return -1;
......
...@@ -565,7 +565,7 @@ static bool _check_dir_contents( ...@@ -565,7 +565,7 @@ static bool _check_dir_contents(
size_t sub_size = strlen(sub); size_t sub_size = strlen(sub);
/* leave base valid even if we could not make space for subdir */ /* leave base valid even if we could not make space for subdir */
if (git_buf_try_grow(dir, dir_size + sub_size + 2, false) < 0) if (git_buf_try_grow(dir, dir_size + sub_size + 2, false, false) < 0)
return false; return false;
/* save excursion */ /* save excursion */
...@@ -902,8 +902,16 @@ int git_path_dirload_with_stat( ...@@ -902,8 +902,16 @@ int git_path_dirload_with_stat(
git_buf_truncate(&full, prefix_len); git_buf_truncate(&full, prefix_len);
if ((error = git_buf_joinpath(&full, full.ptr, ps->path)) < 0 || if ((error = git_buf_joinpath(&full, full.ptr, ps->path)) < 0 ||
(error = git_path_lstat(full.ptr, &ps->st)) < 0) (error = git_path_lstat(full.ptr, &ps->st)) < 0) {
if (error == GIT_ENOTFOUND) {
giterr_clear();
error = 0;
git_vector_remove(contents, i--);
continue;
}
break; break;
}
if (S_ISDIR(ps->st.st_mode)) { if (S_ISDIR(ps->st.st_mode)) {
if ((error = git_buf_joinpath(&full, full.ptr, ".git")) < 0) if ((error = git_buf_joinpath(&full, full.ptr, ".git")) < 0)
......
...@@ -39,7 +39,7 @@ typedef int git_file; ...@@ -39,7 +39,7 @@ typedef int git_file;
* *
* Some of the methods are slightly wrapped to provide * Some of the methods are slightly wrapped to provide
* saner defaults. Some of these methods are emulated * saner defaults. Some of these methods are emulated
* in Windows platforns. * in Windows platforms.
* *
* Use your manpages to check the docs on these. * Use your manpages to check the docs on these.
*/ */
......
...@@ -201,5 +201,5 @@ int git_refdb_rename( ...@@ -201,5 +201,5 @@ int git_refdb_rename(
int git_refdb_delete(struct git_refdb *db, const char *ref_name) int git_refdb_delete(struct git_refdb *db, const char *ref_name)
{ {
assert(db && db->backend); assert(db && db->backend);
return db->backend->delete(db->backend, ref_name); return db->backend->del(db->backend, ref_name);
} }
...@@ -1096,7 +1096,7 @@ int git_refdb_backend_fs( ...@@ -1096,7 +1096,7 @@ int git_refdb_backend_fs(
backend->parent.lookup = &refdb_fs_backend__lookup; backend->parent.lookup = &refdb_fs_backend__lookup;
backend->parent.iterator = &refdb_fs_backend__iterator; backend->parent.iterator = &refdb_fs_backend__iterator;
backend->parent.write = &refdb_fs_backend__write; backend->parent.write = &refdb_fs_backend__write;
backend->parent.delete = &refdb_fs_backend__delete; backend->parent.del = &refdb_fs_backend__delete;
backend->parent.rename = &refdb_fs_backend__rename; backend->parent.rename = &refdb_fs_backend__rename;
backend->parent.compress = &refdb_fs_backend__compress; backend->parent.compress = &refdb_fs_backend__compress;
backend->parent.free = &refdb_fs_backend__free; backend->parent.free = &refdb_fs_backend__free;
......
...@@ -18,8 +18,6 @@ ...@@ -18,8 +18,6 @@
#include "refspec.h" #include "refspec.h"
#include "fetchhead.h" #include "fetchhead.h"
#include <regex.h>
static int add_refspec(git_remote *remote, const char *string, bool is_fetch) static int add_refspec(git_remote *remote, const char *string, bool is_fetch)
{ {
git_refspec *spec; git_refspec *spec;
...@@ -362,7 +360,7 @@ cleanup: ...@@ -362,7 +360,7 @@ cleanup:
static int update_config_refspec(const git_remote *remote, git_config *config, int direction) static int update_config_refspec(const git_remote *remote, git_config *config, int direction)
{ {
git_buf name = GIT_BUF_INIT; git_buf name = GIT_BUF_INIT;
int push; unsigned int push;
const char *dir; const char *dir;
size_t i; size_t i;
int error = 0; int error = 0;
...@@ -806,7 +804,7 @@ static int remote_head_for_ref(git_remote_head **out, git_refspec *spec, git_vec ...@@ -806,7 +804,7 @@ static int remote_head_for_ref(git_remote_head **out, git_refspec *spec, git_vec
(!git_reference_is_branch(resolved_ref)) || (!git_reference_is_branch(resolved_ref)) ||
(error = git_branch_upstream(&tracking_ref, resolved_ref)) < 0 || (error = git_branch_upstream(&tracking_ref, resolved_ref)) < 0 ||
(error = git_refspec_transform_l(&remote_name, spec, git_reference_name(tracking_ref))) < 0) { (error = git_refspec_transform_l(&remote_name, spec, git_reference_name(tracking_ref))) < 0) {
/* Not an error if HEAD is orphaned or no tracking branch */ /* Not an error if HEAD is unborn or no tracking branch */
if (error == GIT_ENOTFOUND) if (error == GIT_ENOTFOUND)
error = 0; error = 0;
...@@ -1075,35 +1073,28 @@ void git_remote_free(git_remote *remote) ...@@ -1075,35 +1073,28 @@ void git_remote_free(git_remote *remote)
git__free(remote); git__free(remote);
} }
struct cb_data { static int remote_list_cb(const git_config_entry *entry, void *payload)
git_vector *list;
regex_t *preg;
};
static int remote_list_cb(const git_config_entry *entry, void *data_)
{ {
struct cb_data *data = (struct cb_data *)data_; git_vector *list = payload;
size_t nmatch = 2; const char *name = entry->name + strlen("remote.");
regmatch_t pmatch[2]; size_t namelen = strlen(name);
const char *name = entry->name; char *remote_name;
if (!regexec(data->preg, name, nmatch, pmatch, 0)) { /* we know name matches "remote.<stuff>.(push)?url" */
char *remote_name = git__strndup(&name[pmatch[1].rm_so], pmatch[1].rm_eo - pmatch[1].rm_so);
GITERR_CHECK_ALLOC(remote_name);
if (git_vector_insert(data->list, remote_name) < 0) if (!strcmp(&name[namelen - 4], ".url"))
return -1; remote_name = git__strndup(name, namelen - 4); /* strip ".url" */
} else
remote_name = git__strndup(name, namelen - 8); /* strip ".pushurl" */
GITERR_CHECK_ALLOC(remote_name);
return 0; return git_vector_insert(list, remote_name);
} }
int git_remote_list(git_strarray *remotes_list, git_repository *repo) int git_remote_list(git_strarray *remotes_list, git_repository *repo)
{ {
git_config *cfg; git_config *cfg;
git_vector list; git_vector list;
regex_t preg;
struct cb_data data;
int error; int error;
if (git_repository_config__weakptr(&cfg, repo) < 0) if (git_repository_config__weakptr(&cfg, repo) < 0)
...@@ -1112,18 +1103,13 @@ int git_remote_list(git_strarray *remotes_list, git_repository *repo) ...@@ -1112,18 +1103,13 @@ int git_remote_list(git_strarray *remotes_list, git_repository *repo)
if (git_vector_init(&list, 4, git__strcmp_cb) < 0) if (git_vector_init(&list, 4, git__strcmp_cb) < 0)
return -1; return -1;
if (regcomp(&preg, "^remote\\.(.*)\\.(push)?url$", REG_EXTENDED) < 0) { error = git_config_foreach_match(
giterr_set(GITERR_OS, "Remote catch regex failed to compile"); cfg, "^remote\\..*\\.(push)?url$", remote_list_cb, &list);
return -1;
}
data.list = &list;
data.preg = &preg;
error = git_config_foreach(cfg, remote_list_cb, &data);
regfree(&preg);
if (error < 0) { if (error < 0) {
size_t i; size_t i;
char *elem; char *elem;
git_vector_foreach(&list, i, elem) { git_vector_foreach(&list, i, elem) {
git__free(elem); git__free(elem);
} }
...@@ -1549,7 +1535,7 @@ int git_remote_add_push(git_remote *remote, const char *refspec) ...@@ -1549,7 +1535,7 @@ int git_remote_add_push(git_remote *remote, const char *refspec)
return add_refspec(remote, refspec, false); return add_refspec(remote, refspec, false);
} }
static int copy_refspecs(git_strarray *array, git_remote *remote, int push) static int copy_refspecs(git_strarray *array, git_remote *remote, unsigned int push)
{ {
size_t i; size_t i;
git_vector refspecs; git_vector refspecs;
......
...@@ -33,8 +33,6 @@ ...@@ -33,8 +33,6 @@
#define GIT_REPO_VERSION 0 #define GIT_REPO_VERSION 0
#define GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
static void set_odb(git_repository *repo, git_odb *odb) static void set_odb(git_repository *repo, git_odb *odb)
{ {
if (odb) { if (odb) {
...@@ -1134,31 +1132,34 @@ static int repo_init_structure( ...@@ -1134,31 +1132,34 @@ static int repo_init_structure(
/* Copy external template if requested */ /* Copy external template if requested */
if (external_tpl) { if (external_tpl) {
git_config *cfg; git_config *cfg = NULL;
const char *tdir; const char *tdir = NULL;
bool default_template = false;
git_buf template_buf = GIT_BUF_INIT;
if (opts->template_path) if (opts->template_path)
tdir = opts->template_path; tdir = opts->template_path;
else if ((error = git_config_open_default(&cfg)) < 0) else if ((error = git_config_open_default(&cfg)) >= 0) {
return error;
else {
error = git_config_get_string(&tdir, cfg, "init.templatedir"); error = git_config_get_string(&tdir, cfg, "init.templatedir");
git_config_free(cfg);
if (error && error != GIT_ENOTFOUND)
return error;
giterr_clear(); giterr_clear();
tdir = GIT_TEMPLATE_DIR;
} }
error = git_futils_cp_r(tdir, repo_dir, if (!tdir) {
GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_CHMOD_DIRS | if (!(error = git_futils_find_template_dir(&template_buf)))
GIT_CPDIR_SIMPLE_TO_MODE, dmode); tdir = template_buf.ptr;
default_template = true;
}
if (tdir)
error = git_futils_cp_r(tdir, repo_dir,
GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_CHMOD_DIRS |
GIT_CPDIR_SIMPLE_TO_MODE, dmode);
git_buf_free(&template_buf);
git_config_free(cfg);
if (error < 0) { if (error < 0) {
if (strcmp(tdir, GIT_TEMPLATE_DIR) != 0) if (!default_template)
return error; return error;
/* if template was default, ignore error and use internal */ /* if template was default, ignore error and use internal */
...@@ -1451,10 +1452,10 @@ int git_repository_head(git_reference **head_out, git_repository *repo) ...@@ -1451,10 +1452,10 @@ int git_repository_head(git_reference **head_out, git_repository *repo)
error = git_reference_lookup_resolved(head_out, repo, git_reference_symbolic_target(head), -1); error = git_reference_lookup_resolved(head_out, repo, git_reference_symbolic_target(head), -1);
git_reference_free(head); git_reference_free(head);
return error == GIT_ENOTFOUND ? GIT_EORPHANEDHEAD : error; return error == GIT_ENOTFOUND ? GIT_EUNBORNBRANCH : error;
} }
int git_repository_head_orphan(git_repository *repo) int git_repository_head_unborn(git_repository *repo)
{ {
git_reference *ref = NULL; git_reference *ref = NULL;
int error; int error;
...@@ -1462,7 +1463,7 @@ int git_repository_head_orphan(git_repository *repo) ...@@ -1462,7 +1463,7 @@ int git_repository_head_orphan(git_repository *repo)
error = git_repository_head(&ref, repo); error = git_repository_head(&ref, repo);
git_reference_free(ref); git_reference_free(ref);
if (error == GIT_EORPHANEDHEAD) if (error == GIT_EUNBORNBRANCH)
return 1; return 1;
if (error < 0) if (error < 0)
...@@ -1649,7 +1650,7 @@ int git_repository_hashfile( ...@@ -1649,7 +1650,7 @@ int git_repository_hashfile(
const char *as_path) const char *as_path)
{ {
int error; int error;
git_vector filters = GIT_VECTOR_INIT; git_filter_list *fl = NULL;
git_file fd = -1; git_file fd = -1;
git_off_t len; git_off_t len;
git_buf full_path = GIT_BUF_INIT; git_buf full_path = GIT_BUF_INIT;
...@@ -1671,7 +1672,8 @@ int git_repository_hashfile( ...@@ -1671,7 +1672,8 @@ int git_repository_hashfile(
/* passing empty string for "as_path" indicated --no-filters */ /* passing empty string for "as_path" indicated --no-filters */
if (strlen(as_path) > 0) { if (strlen(as_path) > 0) {
error = git_filters_load(&filters, repo, as_path, GIT_FILTER_TO_ODB); error = git_filter_list_load(
&fl, repo, NULL, as_path, GIT_FILTER_TO_ODB);
if (error < 0) if (error < 0)
return error; return error;
} else { } else {
...@@ -1698,12 +1700,12 @@ int git_repository_hashfile( ...@@ -1698,12 +1700,12 @@ int git_repository_hashfile(
goto cleanup; goto cleanup;
} }
error = git_odb__hashfd_filtered(out, fd, (size_t)len, type, &filters); error = git_odb__hashfd_filtered(out, fd, (size_t)len, type, fl);
cleanup: cleanup:
if (fd >= 0) if (fd >= 0)
p_close(fd); p_close(fd);
git_filters_free(&filters); git_filter_list_free(fl);
git_buf_free(&full_path); git_buf_free(&full_path);
return error; return error;
......
...@@ -14,8 +14,6 @@ ...@@ -14,8 +14,6 @@
#include "git2/revparse.h" #include "git2/revparse.h"
#include "merge.h" #include "merge.h"
#include <regex.h>
git_commit_list_node *git_revwalk__commit_lookup( git_commit_list_node *git_revwalk__commit_lookup(
git_revwalk *walk, const git_oid *oid) git_revwalk *walk, const git_oid *oid)
{ {
...@@ -181,48 +179,35 @@ static int push_glob_cb(const char *refname, void *data_) ...@@ -181,48 +179,35 @@ static int push_glob_cb(const char *refname, void *data_)
static int push_glob(git_revwalk *walk, const char *glob, int hide) static int push_glob(git_revwalk *walk, const char *glob, int hide)
{ {
int error = 0;
git_buf buf = GIT_BUF_INIT; git_buf buf = GIT_BUF_INIT;
struct push_cb_data data; struct push_cb_data data;
regex_t preg; size_t wildcard;
assert(walk && glob); assert(walk && glob);
/* refs/ is implied if not given in the glob */ /* refs/ is implied if not given in the glob */
if (strncmp(glob, GIT_REFS_DIR, strlen(GIT_REFS_DIR))) { if (git__prefixcmp(glob, GIT_REFS_DIR) != 0)
git_buf_printf(&buf, GIT_REFS_DIR "%s", glob); git_buf_joinpath(&buf, GIT_REFS_DIR, glob);
} else { else
git_buf_puts(&buf, glob); git_buf_puts(&buf, glob);
}
/* If no '?', '*' or '[' exist, we append '/ *' to the glob */ /* If no '?', '*' or '[' exist, we append '/ *' to the glob */
memset(&preg, 0x0, sizeof(regex_t)); wildcard = strcspn(glob, "?*[");
if (regcomp(&preg, "[?*[]", REG_EXTENDED)) { if (!glob[wildcard])
giterr_set(GITERR_OS, "Regex failed to compile"); git_buf_put(&buf, "/*", 2);
git_buf_free(&buf);
return -1;
}
if (regexec(&preg, glob, 0, NULL, 0))
git_buf_puts(&buf, "/*");
if (git_buf_oom(&buf))
goto on_error;
data.walk = walk; data.walk = walk;
data.hide = hide; data.hide = hide;
if (git_reference_foreach_glob( if (git_buf_oom(&buf))
walk->repo, git_buf_cstr(&buf), push_glob_cb, &data) < 0) error = -1;
goto on_error; else
error = git_reference_foreach_glob(
regfree(&preg); walk->repo, git_buf_cstr(&buf), push_glob_cb, &data);
git_buf_free(&buf);
return 0;
on_error:
regfree(&preg);
git_buf_free(&buf); git_buf_free(&buf);
return -1; return error;
} }
int git_revwalk_push_glob(git_revwalk *walk, const char *glob) int git_revwalk_push_glob(git_revwalk *walk, const char *glob)
......
...@@ -27,7 +27,7 @@ static int retrieve_head(git_reference **out, git_repository *repo) ...@@ -27,7 +27,7 @@ static int retrieve_head(git_reference **out, git_repository *repo)
{ {
int error = git_repository_head(out, repo); int error = git_repository_head(out, repo);
if (error == GIT_EORPHANEDHEAD) if (error == GIT_EUNBORNBRANCH)
return create_error(error, "You do not have the initial commit yet."); return create_error(error, "You do not have the initial commit yet.");
return error; return error;
......
...@@ -252,7 +252,7 @@ int git_status_list_new( ...@@ -252,7 +252,7 @@ int git_status_list_new(
/* if there is no HEAD, that's okay - we'll make an empty iterator */ /* if there is no HEAD, that's okay - we'll make an empty iterator */
if (((error = git_repository_head_tree(&head, repo)) < 0) && if (((error = git_repository_head_tree(&head, repo)) < 0) &&
error != GIT_ENOTFOUND && error != GIT_EORPHANEDHEAD) { error != GIT_ENOTFOUND && error != GIT_EUNBORNBRANCH) {
git_index_free(index); /* release index */ git_index_free(index); /* release index */
return error; return error;
} }
......
...@@ -1557,7 +1557,7 @@ static void submodule_get_wd_status( ...@@ -1557,7 +1557,7 @@ static void submodule_get_wd_status(
if (ign == GIT_SUBMODULE_IGNORE_NONE) if (ign == GIT_SUBMODULE_IGNORE_NONE)
opt.flags |= GIT_DIFF_INCLUDE_UNTRACKED; opt.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
/* if we don't have an orphaned head, check diff with index */ /* if we don't have an unborn head, check diff with index */
if (git_repository_head_tree(&sm_head, sm_repo) < 0) if (git_repository_head_tree(&sm_head, sm_repo) < 0)
giterr_clear(); giterr_clear();
else { else {
......
...@@ -59,7 +59,7 @@ typedef struct { ...@@ -59,7 +59,7 @@ typedef struct {
git_smart_subtransport parent; git_smart_subtransport parent;
transport_smart *owner; transport_smart *owner;
gitno_socket socket; gitno_socket socket;
const char *path; char *path;
char *host; char *host;
char *port; char *port;
char *user_from_url; char *user_from_url;
...@@ -125,15 +125,9 @@ static int gen_request( ...@@ -125,15 +125,9 @@ static int gen_request(
size_t content_length) size_t content_length)
{ {
http_subtransport *t = OWNING_SUBTRANSPORT(s); http_subtransport *t = OWNING_SUBTRANSPORT(s);
const char *path = t->path ? t->path : "/";
if (!t->path) git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, path, s->service_url);
t->path = "/";
/* If we were redirected, make sure to respect that here */
if (s->redirect_url)
git_buf_printf(buf, "%s %s HTTP/1.1\r\n", s->verb, s->redirect_url);
else
git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, t->path, s->service_url);
git_buf_puts(buf, "User-Agent: git/1.0 (libgit2 " LIBGIT2_VERSION ")\r\n"); git_buf_puts(buf, "User-Agent: git/1.0 (libgit2 " LIBGIT2_VERSION ")\r\n");
git_buf_printf(buf, "Host: %s\r\n", t->host); git_buf_printf(buf, "Host: %s\r\n", t->host);
...@@ -209,7 +203,7 @@ static int on_header_ready(http_subtransport *t) ...@@ -209,7 +203,7 @@ static int on_header_ready(http_subtransport *t)
} }
else if (!strcasecmp("Location", git_buf_cstr(name))) { else if (!strcasecmp("Location", git_buf_cstr(name))) {
if (!t->location) { if (!t->location) {
t->location= git__strdup(git_buf_cstr(value)); t->location = git__strdup(git_buf_cstr(value));
GITERR_CHECK_ALLOC(t->location); GITERR_CHECK_ALLOC(t->location);
} }
} }
...@@ -255,6 +249,98 @@ static int on_header_value(http_parser *parser, const char *str, size_t len) ...@@ -255,6 +249,98 @@ static int on_header_value(http_parser *parser, const char *str, size_t len)
return 0; return 0;
} }
static void free_connection_data(http_subtransport *t)
{
if (t->host) {
git__free(t->host);
t->host = NULL;
}
if (t->port) {
git__free(t->port);
t->port = NULL;
}
if (t->user_from_url) {
git__free(t->user_from_url);
t->user_from_url = NULL;
}
if (t->pass_from_url) {
git__free(t->pass_from_url);
t->pass_from_url = NULL;
}
if (t->path) {
git__free(t->path);
t->path = NULL;
}
}
static int set_connection_data_from_url(
http_subtransport *t, const char *url, const char *service_suffix)
{
int error = 0;
const char *default_port = NULL;
char *original_host = NULL;
if (!git__prefixcmp(url, prefix_http)) {
url = url + strlen(prefix_http);
default_port = "80";
if (t->use_ssl) {
giterr_set(GITERR_NET, "Redirect from HTTPS to HTTP not allowed");
return -1;
}
}
if (!git__prefixcmp(url, prefix_https)) {
url += strlen(prefix_https);
default_port = "443";
t->use_ssl = 1;
}
if (!default_port) {
giterr_set(GITERR_NET, "Unrecognized URL prefix");
return -1;
}
/* preserve original host name for checking */
original_host = t->host;
t->host = NULL;
free_connection_data(t);
error = gitno_extract_url_parts(
&t->host, &t->port, &t->user_from_url, &t->pass_from_url,
url, default_port);
if (!error) {
const char *path = strchr(url, '/');
size_t pathlen = strlen(path);
size_t suffixlen = service_suffix ? strlen(service_suffix) : 0;
if (suffixlen &&
!memcmp(path + pathlen - suffixlen, service_suffix, suffixlen))
t->path = git__strndup(path, pathlen - suffixlen);
else
t->path = git__strdup(path);
/* Allow '/'-led urls, or a change of protocol */
if (original_host != NULL) {
if (strcmp(original_host, t->host) && t->location[0] != '/') {
giterr_set(GITERR_NET, "Cross host redirect not allowed");
error = -1;
}
git__free(original_host);
}
}
return error;
}
static int on_headers_complete(http_parser *parser) static int on_headers_complete(http_parser *parser)
{ {
parser_context *ctx = (parser_context *) parser->data; parser_context *ctx = (parser_context *) parser->data;
...@@ -308,10 +394,8 @@ static int on_headers_complete(http_parser *parser) ...@@ -308,10 +394,8 @@ static int on_headers_complete(http_parser *parser)
return t->parse_error = PARSE_ERROR_GENERIC; return t->parse_error = PARSE_ERROR_GENERIC;
} }
if (t->location[0] != '/') { if (set_connection_data_from_url(t, t->location, s->service_url) < 0)
giterr_set(GITERR_NET, "Only relative redirects are supported");
return t->parse_error = PARSE_ERROR_GENERIC; return t->parse_error = PARSE_ERROR_GENERIC;
}
/* Set the redirect URL on the stream. This is a transfer of /* Set the redirect URL on the stream. This is a transfer of
* ownership of the memory. */ * ownership of the memory. */
...@@ -822,50 +906,31 @@ static int http_action( ...@@ -822,50 +906,31 @@ static int http_action(
git_smart_service_t action) git_smart_service_t action)
{ {
http_subtransport *t = (http_subtransport *)subtransport; http_subtransport *t = (http_subtransport *)subtransport;
const char *default_port = NULL;
int ret; int ret;
if (!stream) if (!stream)
return -1; return -1;
if (!t->host || !t->port || !t->path) { if (!t->host || !t->port || !t->path) {
if (!git__prefixcmp(url, prefix_http)) { if ((ret = set_connection_data_from_url(t, url, NULL)) < 0)
url = url + strlen(prefix_http);
default_port = "80";
}
if (!git__prefixcmp(url, prefix_https)) {
url += strlen(prefix_https);
default_port = "443";
t->use_ssl = 1;
}
if (!default_port)
return -1;
if ((ret = gitno_extract_url_parts(&t->host, &t->port,
&t->user_from_url, &t->pass_from_url, url, default_port)) < 0)
return ret; return ret;
t->path = strchr(url, '/');
} }
if (http_connect(t) < 0) if (http_connect(t) < 0)
return -1; return -1;
switch (action) switch (action) {
{ case GIT_SERVICE_UPLOADPACK_LS:
case GIT_SERVICE_UPLOADPACK_LS: return http_uploadpack_ls(t, stream);
return http_uploadpack_ls(t, stream);
case GIT_SERVICE_UPLOADPACK: case GIT_SERVICE_UPLOADPACK:
return http_uploadpack(t, stream); return http_uploadpack(t, stream);
case GIT_SERVICE_RECEIVEPACK_LS: case GIT_SERVICE_RECEIVEPACK_LS:
return http_receivepack_ls(t, stream); return http_receivepack_ls(t, stream);
case GIT_SERVICE_RECEIVEPACK: case GIT_SERVICE_RECEIVEPACK:
return http_receivepack(t, stream); return http_receivepack(t, stream);
} }
*stream = NULL; *stream = NULL;
...@@ -893,25 +958,7 @@ static int http_close(git_smart_subtransport *subtransport) ...@@ -893,25 +958,7 @@ static int http_close(git_smart_subtransport *subtransport)
t->url_cred = NULL; t->url_cred = NULL;
} }
if (t->host) { free_connection_data(t);
git__free(t->host);
t->host = NULL;
}
if (t->port) {
git__free(t->port);
t->port = NULL;
}
if (t->user_from_url) {
git__free(t->user_from_url);
t->user_from_url = NULL;
}
if (t->pass_from_url) {
git__free(t->pass_from_url);
t->pass_from_url = NULL;
}
return 0; return 0;
} }
......
...@@ -37,6 +37,14 @@ typedef struct { ...@@ -37,6 +37,14 @@ typedef struct {
git_cred *cred; git_cred *cred;
} ssh_subtransport; } ssh_subtransport;
static void ssh_error(LIBSSH2_SESSION *session, const char *errmsg)
{
char *ssherr;
libssh2_session_last_error(session, &ssherr, NULL, 0);
giterr_set(GITERR_SSH, "%s: %s", errmsg, ssherr);
}
/* /*
* Create a git protocol request. * Create a git protocol request.
* *
...@@ -81,8 +89,8 @@ static int send_command(ssh_stream *s) ...@@ -81,8 +89,8 @@ static int send_command(ssh_stream *s)
goto cleanup; goto cleanup;
error = libssh2_channel_exec(s->channel, request.ptr); error = libssh2_channel_exec(s->channel, request.ptr);
if (error < 0) { if (error < LIBSSH2_ERROR_NONE) {
giterr_set(GITERR_NET, "SSH could not execute request"); ssh_error(s->session, "SSH could not execute request");
goto cleanup; goto cleanup;
} }
...@@ -107,8 +115,8 @@ static int ssh_stream_read( ...@@ -107,8 +115,8 @@ static int ssh_stream_read(
if (!s->sent_command && send_command(s) < 0) if (!s->sent_command && send_command(s) < 0)
return -1; return -1;
if ((rc = libssh2_channel_read(s->channel, buffer, buf_size)) < 0) { if ((rc = libssh2_channel_read(s->channel, buffer, buf_size)) < LIBSSH2_ERROR_NONE) {
giterr_set(GITERR_NET, "SSH could not read data"); ssh_error(s->session, "SSH could not read data");;
return -1; return -1;
} }
...@@ -127,8 +135,8 @@ static int ssh_stream_write( ...@@ -127,8 +135,8 @@ static int ssh_stream_write(
if (!s->sent_command && send_command(s) < 0) if (!s->sent_command && send_command(s) < 0)
return -1; return -1;
if (libssh2_channel_write(s->channel, buffer, len) < 0) { if (libssh2_channel_write(s->channel, buffer, len) < LIBSSH2_ERROR_NONE) {
giterr_set(GITERR_NET, "SSH could not write data"); ssh_error(s->session, "SSH could not write data");
return -1; return -1;
} }
...@@ -212,7 +220,7 @@ static int git_ssh_extract_url_parts( ...@@ -212,7 +220,7 @@ static int git_ssh_extract_url_parts(
at = strchr(url, '@'); at = strchr(url, '@');
if (at) { if (at) {
start = at+1; start = at + 1;
*username = git__substrdup(url, at - url); *username = git__substrdup(url, at - url);
GITERR_CHECK_ALLOC(*username); GITERR_CHECK_ALLOC(*username);
} else { } else {
...@@ -262,8 +270,8 @@ static int _git_ssh_authenticate_session( ...@@ -262,8 +270,8 @@ static int _git_ssh_authenticate_session(
} }
} while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc); } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
if (rc != 0) { if (rc != LIBSSH2_ERROR_NONE) {
giterr_set(GITERR_NET, "Failed to authenticate SSH session"); ssh_error(session, "Failed to authenticate SSH session");
return -1; return -1;
} }
...@@ -289,9 +297,9 @@ static int _git_ssh_session_create( ...@@ -289,9 +297,9 @@ static int _git_ssh_session_create(
rc = libssh2_session_startup(s, socket.socket); rc = libssh2_session_startup(s, socket.socket);
} while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc); } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
if (0 != rc) { if (rc != LIBSSH2_ERROR_NONE) {
ssh_error(s, "Failed to start SSH session");
libssh2_session_free(s); libssh2_session_free(s);
giterr_set(GITERR_NET, "Failed to start SSH session");
return -1; return -1;
} }
...@@ -346,11 +354,11 @@ static int _git_ssh_setup_conn( ...@@ -346,11 +354,11 @@ static int _git_ssh_setup_conn(
goto on_error; goto on_error;
if (!t->cred) { if (!t->cred) {
giterr_set(GITERR_NET, "Callback failed to initialize SSH credentials"); giterr_set(GITERR_SSH, "Callback failed to initialize SSH credentials");
goto on_error; goto on_error;
} }
} else { } else {
giterr_set(GITERR_NET, "Cannot set up SSH connection without credentials"); giterr_set(GITERR_SSH, "Cannot set up SSH connection without credentials");
goto on_error; goto on_error;
} }
assert(t->cred); assert(t->cred);
...@@ -368,7 +376,7 @@ static int _git_ssh_setup_conn( ...@@ -368,7 +376,7 @@ static int _git_ssh_setup_conn(
channel = libssh2_channel_open_session(session); channel = libssh2_channel_open_session(session);
if (!channel) { if (!channel) {
giterr_set(GITERR_NET, "Failed to open SSH channel"); ssh_error(session, "Failed to open SSH channel");
goto on_error; goto on_error;
} }
......
...@@ -73,7 +73,7 @@ typedef struct { ...@@ -73,7 +73,7 @@ typedef struct {
typedef struct { typedef struct {
git_smart_subtransport parent; git_smart_subtransport parent;
transport_smart *owner; transport_smart *owner;
const char *path; char *path;
char *host; char *host;
char *port; char *port;
char *user_from_url; char *user_from_url;
...@@ -152,6 +152,7 @@ static int winhttp_stream_connect(winhttp_stream *s) ...@@ -152,6 +152,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
wchar_t *types[] = { L"*/*", NULL }; wchar_t *types[] = { L"*/*", NULL };
BOOL peerdist = FALSE; BOOL peerdist = FALSE;
int error = -1, wide_len; int error = -1, wide_len;
unsigned long disable_redirects = WINHTTP_DISABLE_REDIRECTS;
/* Prepare URL */ /* Prepare URL */
git_buf_printf(&buf, "%s%s", t->path, s->service_url); git_buf_printf(&buf, "%s%s", t->path, s->service_url);
...@@ -195,7 +196,7 @@ static int winhttp_stream_connect(winhttp_stream *s) ...@@ -195,7 +196,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
} }
/* Set proxy if necessary */ /* Set proxy if necessary */
if (git_remote__get_http_proxy(t->owner->owner, t->use_ssl, &proxy_url) < 0) if (git_remote__get_http_proxy(t->owner->owner, !!t->use_ssl, &proxy_url) < 0)
goto on_error; goto on_error;
if (proxy_url) { if (proxy_url) {
...@@ -244,6 +245,17 @@ static int winhttp_stream_connect(winhttp_stream *s) ...@@ -244,6 +245,17 @@ static int winhttp_stream_connect(winhttp_stream *s)
git__free(proxy_wide); git__free(proxy_wide);
} }
/* Disable WinHTTP redirects so we can handle them manually. Why, you ask?
* http://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/b2ff8879-ab9f-4218-8f09-16d25dff87ae
*/
if (!WinHttpSetOption(s->request,
WINHTTP_OPTION_DISABLE_FEATURE,
&disable_redirects,
sizeof(disable_redirects))) {
giterr_set(GITERR_OS, "Failed to disable redirects");
goto on_error;
}
/* Strip unwanted headers (X-P2P-PeerDist, X-P2P-PeerDistEx) that WinHTTP /* Strip unwanted headers (X-P2P-PeerDist, X-P2P-PeerDistEx) that WinHTTP
* adds itself. This option may not be supported by the underlying * adds itself. This option may not be supported by the underlying
* platform, so we do not error-check it */ * platform, so we do not error-check it */
...@@ -380,6 +392,142 @@ static int write_chunk(HINTERNET request, const char *buffer, size_t len) ...@@ -380,6 +392,142 @@ static int write_chunk(HINTERNET request, const char *buffer, size_t len)
return 0; return 0;
} }
static void free_connection_data(winhttp_subtransport *t)
{
if (t->host) {
git__free(t->host);
t->host = NULL;
}
if (t->port) {
git__free(t->port);
t->port = NULL;
}
if (t->user_from_url) {
git__free(t->user_from_url);
t->user_from_url = NULL;
}
if (t->pass_from_url) {
git__free(t->pass_from_url);
t->pass_from_url = NULL;
}
if (t->path) {
git__free(t->path);
t->path = NULL;
}
}
static int set_connection_data_from_url(
winhttp_subtransport *t, const char *url, const char *service_suffix)
{
int error = 0;
const char *default_port = NULL;
char *original_host = NULL;
const char *original_url = url;
if (!git__prefixcmp(url, prefix_http)) {
url += strlen(prefix_http);
default_port = "80";
if (t->use_ssl) {
giterr_set(GITERR_NET, "Redirect from HTTPS to HTTP not allowed");
return -1;
}
}
if (!git__prefixcmp(url, prefix_https)) {
url += strlen(prefix_https);
default_port = "443";
t->use_ssl = 1;
}
if (!default_port) {
giterr_set(GITERR_NET, "Unrecognized URL prefix");
return -1;
}
/* preserve original host name for checking */
original_host = t->host;
t->host = NULL;
free_connection_data(t);
error = gitno_extract_url_parts(
&t->host, &t->port, &t->user_from_url, &t->pass_from_url,
url, default_port);
if (!error) {
const char *path = strchr(url, '/');
size_t pathlen = strlen(path);
size_t suffixlen = service_suffix ? strlen(service_suffix) : 0;
if (suffixlen &&
!memcmp(path + pathlen - suffixlen, service_suffix, suffixlen))
t->path = git__strndup(path, pathlen - suffixlen);
else
t->path = git__strdup(path);
/* Allow '/'-led urls, or a change of protocol */
if (original_host != NULL) {
if (strcmp(original_host, t->host) && original_url[0] != '/') {
giterr_set(GITERR_NET, "Cross host redirect not allowed");
error = -1;
}
git__free(original_host);
}
}
return error;
}
static int winhttp_connect(
winhttp_subtransport *t,
const char *url)
{
wchar_t *ua = L"git/1.0 (libgit2 " WIDEN(LIBGIT2_VERSION) L")";
git_win32_path host;
int32_t port;
const char *default_port = "80";
/* Prepare port */
if (git__strtol32(&port, t->port, NULL, 10) < 0)
return -1;
/* Prepare host */
git_win32_path_from_c(host, t->host);
/* Establish session */
t->session = WinHttpOpen(
ua,
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS,
0);
if (!t->session) {
giterr_set(GITERR_OS, "Failed to init WinHTTP");
return -1;
}
/* Establish connection */
t->connection = WinHttpConnect(
t->session,
host,
(INTERNET_PORT) port,
0);
if (!t->connection) {
giterr_set(GITERR_OS, "Failed to connect to host");
return -1;
}
return 0;
}
static int winhttp_stream_read( static int winhttp_stream_read(
git_smart_subtransport_stream *stream, git_smart_subtransport_stream *stream,
char *buffer, char *buffer,
...@@ -511,50 +659,52 @@ replay: ...@@ -511,50 +659,52 @@ replay:
/* Check for Windows 7. This workaround is only necessary on /* Check for Windows 7. This workaround is only necessary on
* Windows Vista and earlier. Windows 7 is version 6.1. */ * Windows Vista and earlier. Windows 7 is version 6.1. */
if (!git_has_win32_version(6, 1, 0)) { wchar_t *location;
wchar_t *location; DWORD location_length;
DWORD location_length; char *location8;
int redirect_cmp;
/* OK, fetch the Location header from the redirect. */
/* OK, fetch the Location header from the redirect. */ if (WinHttpQueryHeaders(s->request,
if (WinHttpQueryHeaders(s->request, WINHTTP_QUERY_LOCATION,
WINHTTP_QUERY_LOCATION, WINHTTP_HEADER_NAME_BY_INDEX,
WINHTTP_HEADER_NAME_BY_INDEX, WINHTTP_NO_OUTPUT_BUFFER,
WINHTTP_NO_OUTPUT_BUFFER, &location_length,
&location_length, WINHTTP_NO_HEADER_INDEX) ||
WINHTTP_NO_HEADER_INDEX) || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
GetLastError() != ERROR_INSUFFICIENT_BUFFER) { giterr_set(GITERR_OS, "Failed to read Location header");
giterr_set(GITERR_OS, "Failed to read Location header"); return -1;
return -1; }
}
location = git__malloc(location_length);
GITERR_CHECK_ALLOC(location);
if (!WinHttpQueryHeaders(s->request,
WINHTTP_QUERY_LOCATION,
WINHTTP_HEADER_NAME_BY_INDEX,
location,
&location_length,
WINHTTP_NO_HEADER_INDEX)) {
giterr_set(GITERR_OS, "Failed to read Location header");
git__free(location);
return -1;
}
/* Compare the Location header with the request URI */ location = git__malloc(location_length);
redirect_cmp = wcscmp(location, s->request_uri); location8 = git__malloc(location_length);
GITERR_CHECK_ALLOC(location);
if (!WinHttpQueryHeaders(s->request,
WINHTTP_QUERY_LOCATION,
WINHTTP_HEADER_NAME_BY_INDEX,
location,
&location_length,
WINHTTP_NO_HEADER_INDEX)) {
giterr_set(GITERR_OS, "Failed to read Location header");
git__free(location); git__free(location);
return -1;
if (!redirect_cmp) { }
/* Replay the request */ git__utf16_to_8(location8, location_length, location);
WinHttpCloseHandle(s->request); git__free(location);
s->request = NULL;
s->sent_request = 0; /* Replay the request */
WinHttpCloseHandle(s->request);
goto replay; s->request = NULL;
} s->sent_request = 0;
if (!git__prefixcmp_icase(location8, prefix_https)) {
/* Upgrade to secure connection; disconnect and start over */
set_connection_data_from_url(t, location8, s->service_url);
winhttp_connect(t, location8);
} }
git__free(location8);
goto replay;
} }
/* Handle authentication failures */ /* Handle authentication failures */
...@@ -888,68 +1038,6 @@ static int winhttp_stream_alloc(winhttp_subtransport *t, winhttp_stream **stream ...@@ -888,68 +1038,6 @@ static int winhttp_stream_alloc(winhttp_subtransport *t, winhttp_stream **stream
return 0; return 0;
} }
static int winhttp_connect(
winhttp_subtransport *t,
const char *url)
{
wchar_t *ua = L"git/1.0 (libgit2 " WIDEN(LIBGIT2_VERSION) L")";
git_win32_path host;
int32_t port;
const char *default_port = "80";
int ret;
if (!git__prefixcmp(url, prefix_http)) {
url = url + strlen(prefix_http);
default_port = "80";
}
if (!git__prefixcmp(url, prefix_https)) {
url += strlen(prefix_https);
default_port = "443";
t->use_ssl = 1;
}
if ((ret = gitno_extract_url_parts(&t->host, &t->port, &t->user_from_url,
&t->pass_from_url, url, default_port)) < 0)
return ret;
t->path = strchr(url, '/');
/* Prepare port */
if (git__strtol32(&port, t->port, NULL, 10) < 0)
return -1;
/* Prepare host */
git_win32_path_from_c(host, t->host);
/* Establish session */
t->session = WinHttpOpen(
ua,
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS,
0);
if (!t->session) {
giterr_set(GITERR_OS, "Failed to init WinHTTP");
return -1;
}
/* Establish connection */
t->connection = WinHttpConnect(
t->session,
host,
port,
0);
if (!t->connection) {
giterr_set(GITERR_OS, "Failed to connect to host");
return -1;
}
return 0;
}
static int winhttp_uploadpack_ls( static int winhttp_uploadpack_ls(
winhttp_subtransport *t, winhttp_subtransport *t,
winhttp_stream *s) winhttp_stream *s)
...@@ -1014,7 +1102,8 @@ static int winhttp_action( ...@@ -1014,7 +1102,8 @@ static int winhttp_action(
int ret = -1; int ret = -1;
if (!t->connection && if (!t->connection &&
winhttp_connect(t, url) < 0) (set_connection_data_from_url(t, url, NULL) < 0 ||
winhttp_connect(t, url) < 0))
return -1; return -1;
if (winhttp_stream_alloc(t, &s) < 0) if (winhttp_stream_alloc(t, &s) < 0)
...@@ -1056,25 +1145,7 @@ static int winhttp_close(git_smart_subtransport *subtransport) ...@@ -1056,25 +1145,7 @@ static int winhttp_close(git_smart_subtransport *subtransport)
winhttp_subtransport *t = (winhttp_subtransport *)subtransport; winhttp_subtransport *t = (winhttp_subtransport *)subtransport;
int ret = 0; int ret = 0;
if (t->host) { free_connection_data(t);
git__free(t->host);
t->host = NULL;
}
if (t->port) {
git__free(t->port);
t->port = NULL;
}
if (t->user_from_url) {
git__free(t->user_from_url);
t->user_from_url = NULL;
}
if (t->pass_from_url) {
git__free(t->pass_from_url);
t->pass_from_url = NULL;
}
if (t->cred) { if (t->cred) {
t->cred->free(t->cred); t->cred->free(t->cred);
......
...@@ -117,6 +117,19 @@ int git_libgit2_opts(int key, ...) ...@@ -117,6 +117,19 @@ int git_libgit2_opts(int key, ...)
*(va_arg(ap, ssize_t *)) = git_cache__current_storage.val; *(va_arg(ap, ssize_t *)) = git_cache__current_storage.val;
*(va_arg(ap, ssize_t *)) = git_cache__max_storage; *(va_arg(ap, ssize_t *)) = git_cache__max_storage;
break; break;
case GIT_OPT_GET_TEMPLATE_PATH:
{
char *out = va_arg(ap, char *);
size_t outlen = va_arg(ap, size_t);
error = git_futils_dirs_get_str(out, outlen, GIT_FUTILS_DIR_TEMPLATE);
}
break;
case GIT_OPT_SET_TEMPLATE_PATH:
error = git_futils_dirs_set(GIT_FUTILS_DIR_TEMPLATE, va_arg(ap, const char *));
break;
} }
va_end(ap); va_end(ap);
...@@ -679,6 +692,9 @@ size_t git__unescape(char *str) ...@@ -679,6 +692,9 @@ size_t git__unescape(char *str)
{ {
char *scan, *pos = str; char *scan, *pos = str;
if (!str)
return 0;
for (scan = str; *scan; pos++, scan++) { for (scan = str; *scan; pos++, scan++) {
if (*scan == '\\' && *(scan + 1) != '\0') if (*scan == '\\' && *(scan + 1) != '\0')
scan++; /* skip '\' but include next char */ scan++; /* skip '\' but include next char */
......
...@@ -86,7 +86,7 @@ static wchar_t* win32_walkpath(wchar_t *path, wchar_t *buf, size_t buflen) ...@@ -86,7 +86,7 @@ static wchar_t* win32_walkpath(wchar_t *path, wchar_t *buf, size_t buflen)
return (path != base) ? path : NULL; return (path != base) ? path : NULL;
} }
static int win32_find_git_in_path(git_buf *buf, const wchar_t *gitexe) static int win32_find_git_in_path(git_buf *buf, const wchar_t *gitexe, const wchar_t *subdir)
{ {
wchar_t *env = _wgetenv(L"PATH"), lastch; wchar_t *env = _wgetenv(L"PATH"), lastch;
struct git_win32__path root; struct git_win32__path root;
...@@ -110,8 +110,8 @@ static int win32_find_git_in_path(git_buf *buf, const wchar_t *gitexe) ...@@ -110,8 +110,8 @@ static int win32_find_git_in_path(git_buf *buf, const wchar_t *gitexe)
wcscpy(&root.path[root.len], gitexe); wcscpy(&root.path[root.len], gitexe);
if (_waccess(root.path, F_OK) == 0 && root.len > 5) { if (_waccess(root.path, F_OK) == 0 && root.len > 5) {
/* replace "bin\\" or "cmd\\" with "etc\\" */ /* replace "bin\\" or "cmd\\" with subdir */
wcscpy(&root.path[root.len - 4], L"etc\\"); wcscpy(&root.path[root.len - 4], subdir);
win32_path_to_8(buf, root.path); win32_path_to_8(buf, root.path);
return 0; return 0;
...@@ -122,7 +122,7 @@ static int win32_find_git_in_path(git_buf *buf, const wchar_t *gitexe) ...@@ -122,7 +122,7 @@ static int win32_find_git_in_path(git_buf *buf, const wchar_t *gitexe)
} }
static int win32_find_git_in_registry( static int win32_find_git_in_registry(
git_buf *buf, const HKEY hieve, const wchar_t *key) git_buf *buf, const HKEY hieve, const wchar_t *key, const wchar_t *subdir)
{ {
HKEY hKey; HKEY hKey;
DWORD dwType = REG_SZ; DWORD dwType = REG_SZ;
...@@ -130,9 +130,9 @@ static int win32_find_git_in_registry( ...@@ -130,9 +130,9 @@ static int win32_find_git_in_registry(
assert(buf); assert(buf);
path16.len = 0; path16.len = MAX_PATH;
if (RegOpenKeyExW(hieve, key, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS) { if (RegOpenKeyExW(hieve, key, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
if (RegQueryValueExW(hKey, L"InstallLocation", NULL, &dwType, if (RegQueryValueExW(hKey, L"InstallLocation", NULL, &dwType,
(LPBYTE)&path16.path, &path16.len) == ERROR_SUCCESS) (LPBYTE)&path16.path, &path16.len) == ERROR_SUCCESS)
{ {
...@@ -143,7 +143,7 @@ static int win32_find_git_in_registry( ...@@ -143,7 +143,7 @@ static int win32_find_git_in_registry(
return -1; return -1;
} }
wcscat(path16.path, L"etc\\"); wcscat(path16.path, subdir);
path16.len += 4; path16.len += 4;
win32_path_to_8(buf, path16.path); win32_path_to_8(buf, path16.path);
...@@ -180,26 +180,26 @@ static int win32_find_existing_dirs( ...@@ -180,26 +180,26 @@ static int win32_find_existing_dirs(
return (git_buf_oom(out) ? -1 : 0); return (git_buf_oom(out) ? -1 : 0);
} }
int git_win32__find_system_dirs(git_buf *out) int git_win32__find_system_dirs(git_buf *out, const wchar_t *subdir)
{ {
git_buf buf = GIT_BUF_INIT; git_buf buf = GIT_BUF_INIT;
/* directories where git.exe & git.cmd are found */ /* directories where git.exe & git.cmd are found */
if (!win32_find_git_in_path(&buf, L"git.exe") && buf.size) if (!win32_find_git_in_path(&buf, L"git.exe", subdir) && buf.size)
git_buf_set(out, buf.ptr, buf.size); git_buf_set(out, buf.ptr, buf.size);
else else
git_buf_clear(out); git_buf_clear(out);
if (!win32_find_git_in_path(&buf, L"git.cmd") && buf.size) if (!win32_find_git_in_path(&buf, L"git.cmd", subdir) && buf.size)
git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr); git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
/* directories where git is installed according to registry */ /* directories where git is installed according to registry */
if (!win32_find_git_in_registry( if (!win32_find_git_in_registry(
&buf, HKEY_CURRENT_USER, REG_MSYSGIT_INSTALL_LOCAL) && buf.size) &buf, HKEY_CURRENT_USER, REG_MSYSGIT_INSTALL_LOCAL, subdir) && buf.size)
git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr); git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
if (!win32_find_git_in_registry( if (!win32_find_git_in_registry(
&buf, HKEY_LOCAL_MACHINE, REG_MSYSGIT_INSTALL) && buf.size) &buf, HKEY_LOCAL_MACHINE, REG_MSYSGIT_INSTALL, subdir) && buf.size)
git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr); git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
git_buf_free(&buf); git_buf_free(&buf);
......
...@@ -19,7 +19,7 @@ extern int git_win32__expand_path( ...@@ -19,7 +19,7 @@ extern int git_win32__expand_path(
extern int git_win32__find_file( extern int git_win32__find_file(
git_buf *path, const struct git_win32__path *root, const char *filename); git_buf *path, const struct git_win32__path *root, const char *filename);
extern int git_win32__find_system_dirs(git_buf *out); extern int git_win32__find_system_dirs(git_buf *out, const wchar_t *subpath);
extern int git_win32__find_global_dirs(git_buf *out); extern int git_win32__find_global_dirs(git_buf *out);
extern int git_win32__find_xdg_dirs(git_buf *out); extern int git_win32__find_xdg_dirs(git_buf *out);
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
*/ */
#include "pthread.h" #include "pthread.h"
#include "../global.h"
int pthread_create( int pthread_create(
pthread_t *GIT_RESTRICT thread, pthread_t *GIT_RESTRICT thread,
...@@ -217,6 +218,14 @@ int pthread_rwlock_destroy(pthread_rwlock_t *lock) ...@@ -217,6 +218,14 @@ int pthread_rwlock_destroy(pthread_rwlock_t *lock)
} }
static void win32_pthread_shutdown(void)
{
if (win32_kernel32_dll) {
FreeLibrary(win32_kernel32_dll);
win32_kernel32_dll = NULL;
}
}
int win32_pthread_initialize(void) int win32_pthread_initialize(void)
{ {
if (win32_kernel32_dll) if (win32_kernel32_dll)
...@@ -239,15 +248,7 @@ int win32_pthread_initialize(void) ...@@ -239,15 +248,7 @@ int win32_pthread_initialize(void)
win32_srwlock_release_exclusive = (win32_srwlock_fn) win32_srwlock_release_exclusive = (win32_srwlock_fn)
GetProcAddress(win32_kernel32_dll, "ReleaseSRWLockExclusive"); GetProcAddress(win32_kernel32_dll, "ReleaseSRWLockExclusive");
return 0; git__on_shutdown(win32_pthread_shutdown);
}
int win32_pthread_shutdown(void)
{
if (win32_kernel32_dll) {
FreeLibrary(win32_kernel32_dll);
win32_kernel32_dll = NULL;
}
return 0; return 0;
} }
...@@ -69,6 +69,5 @@ int pthread_rwlock_wrunlock(pthread_rwlock_t *); ...@@ -69,6 +69,5 @@ int pthread_rwlock_wrunlock(pthread_rwlock_t *);
int pthread_rwlock_destroy(pthread_rwlock_t *); int pthread_rwlock_destroy(pthread_rwlock_t *);
extern int win32_pthread_initialize(void); extern int win32_pthread_initialize(void);
extern int win32_pthread_shutdown(void);
#endif #endif
...@@ -100,6 +100,22 @@ void test_attr_repo__get_many(void) ...@@ -100,6 +100,22 @@ void test_attr_repo__get_many(void)
cl_assert_equal_s("yes", values[3]); cl_assert_equal_s("yes", values[3]);
} }
void test_attr_repo__get_many_in_place(void)
{
const char *vals[4] = { "repoattr", "rootattr", "missingattr", "subattr" };
/* it should be legal to look up values into the same array that has
* the attribute names, overwriting each name as the value is found.
*/
cl_git_pass(git_attr_get_many(vals, g_repo, 0, "sub/subdir_test1", 4, vals));
cl_assert(GIT_ATTR_TRUE(vals[0]));
cl_assert(GIT_ATTR_TRUE(vals[1]));
cl_assert(GIT_ATTR_UNSPECIFIED(vals[2]));
cl_assert_equal_s("yes", vals[3]);
}
static int count_attrs( static int count_attrs(
const char *name, const char *name,
const char *value, const char *value,
......
...@@ -3,22 +3,6 @@ ...@@ -3,22 +3,6 @@
#include "refs.h" #include "refs.h"
#include "fileops.h" #include "fileops.h"
/* this is essentially the code from git__unescape modified slightly */
void strip_cr_from_buf(git_buf *buf)
{
char *scan, *pos = buf->ptr, *end = pos + buf->size;
for (scan = pos; scan < end; pos++, scan++) {
if (*scan == '\r')
scan++; /* skip '\r' */
if (pos != scan)
*pos = *scan;
}
*pos = '\0';
buf->size = (pos - buf->ptr);
}
void assert_on_branch(git_repository *repo, const char *branch) void assert_on_branch(git_repository *repo, const char *branch)
{ {
git_reference *head; git_reference *head;
...@@ -50,48 +34,6 @@ void reset_index_to_treeish(git_object *treeish) ...@@ -50,48 +34,6 @@ void reset_index_to_treeish(git_object *treeish)
git_index_free(index); git_index_free(index);
} }
static void check_file_contents_internal(
const char *path,
const char *expected_content,
bool strip_cr,
const char *file,
int line,
const char *msg)
{
int fd;
char data[1024] = {0};
git_buf buf = GIT_BUF_INIT;
size_t expected_len = expected_content ? strlen(expected_content) : 0;
fd = p_open(path, O_RDONLY);
cl_assert(fd >= 0);
buf.ptr = data;
buf.size = p_read(fd, buf.ptr, sizeof(data));
cl_git_pass(p_close(fd));
if (strip_cr)
strip_cr_from_buf(&buf);
clar__assert_equal(file, line, "strlen(expected_content) != strlen(actual_content)", 1, PRIuZ, expected_len, (size_t)buf.size);
clar__assert_equal(file, line, msg, 1, "%s", expected_content, buf.ptr);
}
void check_file_contents_at_line(
const char *path, const char *expected,
const char *file, int line, const char *msg)
{
check_file_contents_internal(path, expected, false, file, line, msg);
}
void check_file_contents_nocr_at_line(
const char *path, const char *expected,
const char *file, int line, const char *msg)
{
check_file_contents_internal(path, expected, true, file, line, msg);
}
int checkout_count_callback( int checkout_count_callback(
git_checkout_notify_t why, git_checkout_notify_t why,
const char *path, const char *path,
......
...@@ -2,23 +2,14 @@ ...@@ -2,23 +2,14 @@
#include "git2/object.h" #include "git2/object.h"
#include "git2/repository.h" #include "git2/repository.h"
extern void strip_cr_from_buf(git_buf *buf);
extern void assert_on_branch(git_repository *repo, const char *branch); extern void assert_on_branch(git_repository *repo, const char *branch);
extern void reset_index_to_treeish(git_object *treeish); extern void reset_index_to_treeish(git_object *treeish);
extern void check_file_contents_at_line(
const char *path, const char *expected,
const char *file, int line, const char *msg);
extern void check_file_contents_nocr_at_line(
const char *path, const char *expected,
const char *file, int line, const char *msg);
#define check_file_contents(PATH,EXP) \ #define check_file_contents(PATH,EXP) \
check_file_contents_at_line(PATH,EXP,__FILE__,__LINE__,"String mismatch: " #EXP " != " #PATH) cl_assert_equal_file(EXP,0,PATH)
#define check_file_contents_nocr(PATH,EXP) \ #define check_file_contents_nocr(PATH,EXP) \
check_file_contents_nocr_at_line(PATH,EXP,__FILE__,__LINE__,"String mismatch: " #EXP " != " #PATH) cl_assert_equal_file_ignore_cr(EXP,0,PATH)
typedef struct { typedef struct {
int n_conflicts; int n_conflicts;
......
#include "clar_libgit2.h" #include "clar_libgit2.h"
#include "checkout_helpers.h" #include "checkout_helpers.h"
#include "../filter/crlf.h"
#include "git2/checkout.h" #include "git2/checkout.h"
#include "repository.h" #include "repository.h"
#include "posix.h"
#define UTF8_BOM "\xEF\xBB\xBF"
#define ALL_CRLF_TEXT_RAW "crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n"
#define ALL_LF_TEXT_RAW "lf\nlf\nlf\nlf\nlf\n"
#define MORE_CRLF_TEXT_RAW "crlf\r\ncrlf\r\nlf\ncrlf\r\ncrlf\r\n"
#define MORE_LF_TEXT_RAW "lf\nlf\ncrlf\r\nlf\nlf\n"
#define ALL_LF_TEXT_AS_CRLF "lf\r\nlf\r\nlf\r\nlf\r\nlf\r\n"
#define MORE_CRLF_TEXT_AS_CRLF "crlf\r\ncrlf\r\nlf\r\ncrlf\r\ncrlf\r\n"
#define MORE_LF_TEXT_AS_CRLF "lf\r\nlf\r\ncrlf\r\nlf\r\nlf\r\n"
static git_repository *g_repo; static git_repository *g_repo;
...@@ -145,3 +137,95 @@ void test_checkout_crlf__autocrlf_true_index_size_is_filtered_size(void) ...@@ -145,3 +137,95 @@ void test_checkout_crlf__autocrlf_true_index_size_is_filtered_size(void)
git_index_free(index); git_index_free(index);
} }
void test_checkout_crlf__with_ident(void)
{
git_index *index;
git_blob *blob;
git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
cl_git_mkfile("crlf/.gitattributes",
"*.txt text\n*.bin binary\n"
"*.crlf text eol=crlf\n"
"*.lf text eol=lf\n"
"*.ident text ident\n"
"*.identcrlf ident text eol=crlf\n"
"*.identlf ident text eol=lf\n");
cl_repo_set_bool(g_repo, "core.autocrlf", true);
/* add files with $Id$ */
cl_git_mkfile("crlf/lf.ident", ALL_LF_TEXT_RAW "\n$Id: initial content$\n");
cl_git_mkfile("crlf/crlf.ident", ALL_CRLF_TEXT_RAW "\r\n$Id$\r\n\r\n");
cl_git_mkfile("crlf/more1.identlf", "$Id$\n" MORE_LF_TEXT_RAW);
cl_git_mkfile("crlf/more2.identcrlf", "\r\n$Id: $\r\n" MORE_CRLF_TEXT_RAW);
cl_git_pass(git_repository_index(&index, g_repo));
cl_git_pass(git_index_add_bypath(index, "lf.ident"));
cl_git_pass(git_index_add_bypath(index, "crlf.ident"));
cl_git_pass(git_index_add_bypath(index, "more1.identlf"));
cl_git_pass(git_index_add_bypath(index, "more2.identcrlf"));
cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "Some ident files\n");
git_checkout_head(g_repo, &opts);
/* check that blobs have $Id$ */
cl_git_pass(git_blob_lookup(&blob, g_repo,
& git_index_get_bypath(index, "lf.ident", 0)->oid));
cl_assert_equal_s(
ALL_LF_TEXT_RAW "\n$Id$\n", git_blob_rawcontent(blob));
git_blob_free(blob);
cl_git_pass(git_blob_lookup(&blob, g_repo,
& git_index_get_bypath(index, "more2.identcrlf", 0)->oid));
cl_assert_equal_s(
"\n$Id$\n" MORE_CRLF_TEXT_AS_LF, git_blob_rawcontent(blob));
git_blob_free(blob);
/* check that filesystem is initially untouched - matching core Git */
cl_assert_equal_file(
ALL_LF_TEXT_RAW "\n$Id: initial content$\n", 0, "crlf/lf.ident");
/* check that forced checkout rewrites correctly */
p_unlink("crlf/lf.ident");
p_unlink("crlf/crlf.ident");
p_unlink("crlf/more1.identlf");
p_unlink("crlf/more2.identcrlf");
git_checkout_head(g_repo, &opts);
if (GIT_EOL_NATIVE == GIT_EOL_LF) {
cl_assert_equal_file(
ALL_LF_TEXT_RAW
"\n$Id: fcf6d4d9c212dc66563b1171b1cd99953c756467$\n",
0, "crlf/lf.ident");
cl_assert_equal_file(
ALL_CRLF_TEXT_AS_LF
"\n$Id: f2c66ad9b2b5a734d9bf00d5000cc10a62b8a857$\n\n",
0, "crlf/crlf.ident");
} else {
cl_assert_equal_file(
ALL_LF_TEXT_AS_CRLF
"\r\n$Id: fcf6d4d9c212dc66563b1171b1cd99953c756467$\r\n",
0, "crlf/lf.ident");
cl_assert_equal_file(
ALL_CRLF_TEXT_RAW
"\r\n$Id: f2c66ad9b2b5a734d9bf00d5000cc10a62b8a857$\r\n\r\n",
0, "crlf/crlf.ident");
}
cl_assert_equal_file(
"$Id: f7830382dac1f1583422be5530fdfbd26289431b$\n"
MORE_LF_TEXT_AS_LF, 0, "crlf/more1.identlf");
cl_assert_equal_file(
"\r\n$Id: 74677a68413012ce8d7e7cfc3f12603df3a3eac4$\r\n"
MORE_CRLF_TEXT_AS_CRLF, 0, "crlf/more2.identcrlf");
git_index_free(index);
}
...@@ -16,11 +16,11 @@ void test_checkout_head__cleanup(void) ...@@ -16,11 +16,11 @@ void test_checkout_head__cleanup(void)
cl_git_sandbox_cleanup(); cl_git_sandbox_cleanup();
} }
void test_checkout_head__orphaned_head_returns_GIT_EORPHANEDHEAD(void) void test_checkout_head__unborn_head_returns_GIT_EUNBORNBRANCH(void)
{ {
make_head_orphaned(g_repo, NON_EXISTING_HEAD); make_head_unborn(g_repo, NON_EXISTING_HEAD);
cl_assert_equal_i(GIT_EORPHANEDHEAD, git_checkout_head(g_repo, NULL)); cl_assert_equal_i(GIT_EUNBORNBRANCH, git_checkout_head(g_repo, NULL));
} }
void test_checkout_head__with_index_only_tree(void) void test_checkout_head__with_index_only_tree(void)
......
...@@ -68,7 +68,6 @@ void cl_fixture_cleanup(const char *fixture_name); ...@@ -68,7 +68,6 @@ void cl_fixture_cleanup(const char *fixture_name);
#define cl_assert_equal_p(p1,p2) clar__assert_equal(__FILE__,__LINE__,"Pointer mismatch: " #p1 " != " #p2, 1, "%p", (p1), (p2)) #define cl_assert_equal_p(p1,p2) clar__assert_equal(__FILE__,__LINE__,"Pointer mismatch: " #p1 " != " #p2, 1, "%p", (p1), (p2))
#define cl_assert_equal_sz(sz1,sz2) clar__assert_equal(__FILE__,__LINE__,#sz1 " != " #sz2, 1, "%"PRIuZ, (size_t)(sz1), (size_t)(sz2))
void clar__fail( void clar__fail(
const char *file, const char *file,
......
...@@ -30,24 +30,26 @@ void cl_git_mkfile(const char *filename, const char *content) ...@@ -30,24 +30,26 @@ void cl_git_mkfile(const char *filename, const char *content)
} }
void cl_git_write2file( void cl_git_write2file(
const char *filename, const char *new_content, int flags, unsigned int mode) const char *path, const char *content, size_t content_len,
int flags, unsigned int mode)
{ {
int fd = p_open(filename, flags, mode); int fd;
cl_assert(fd >= 0); cl_assert(path && content);
if (!new_content) cl_assert((fd = p_open(path, flags, mode)) >= 0);
new_content = "\n"; if (!content_len)
cl_must_pass(p_write(fd, new_content, strlen(new_content))); content_len = strlen(content);
cl_must_pass(p_write(fd, content, content_len));
cl_must_pass(p_close(fd)); cl_must_pass(p_close(fd));
} }
void cl_git_append2file(const char *filename, const char *new_content) void cl_git_append2file(const char *path, const char *content)
{ {
cl_git_write2file(filename, new_content, O_WRONLY | O_CREAT | O_APPEND, 0644); cl_git_write2file(path, content, 0, O_WRONLY | O_CREAT | O_APPEND, 0644);
} }
void cl_git_rewritefile(const char *filename, const char *new_content) void cl_git_rewritefile(const char *path, const char *content)
{ {
cl_git_write2file(filename, new_content, O_WRONLY | O_CREAT | O_TRUNC, 0644); cl_git_write2file(path, content, 0, O_WRONLY | O_CREAT | O_TRUNC, 0644);
} }
#ifdef GIT_WIN32 #ifdef GIT_WIN32
...@@ -337,6 +339,65 @@ int cl_git_remove_placeholders(const char *directory_path, const char *filename) ...@@ -337,6 +339,65 @@ int cl_git_remove_placeholders(const char *directory_path, const char *filename)
return error; return error;
} }
#define CL_COMMIT_NAME "Libgit2 Tester"
#define CL_COMMIT_EMAIL "libgit2-test@github.com"
#define CL_COMMIT_MSG "Test commit of tree "
void cl_repo_commit_from_index(
git_oid *out,
git_repository *repo,
git_signature *sig,
git_time_t time,
const char *msg)
{
git_index *index;
git_oid commit_id, tree_id;
git_object *parent = NULL;
git_reference *ref = NULL;
git_tree *tree = NULL;
char buf[128];
int free_sig = (sig == NULL);
/* it is fine if looking up HEAD fails - we make this the first commit */
git_revparse_ext(&parent, &ref, repo, "HEAD");
/* write the index content as a tree */
cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_index_write_tree(&tree_id, index));
cl_git_pass(git_index_write(index));
git_index_free(index);
cl_git_pass(git_tree_lookup(&tree, repo, &tree_id));
if (sig)
cl_assert(sig->name && sig->email);
else if (!time)
cl_git_pass(git_signature_now(&sig, CL_COMMIT_NAME, CL_COMMIT_EMAIL));
else
cl_git_pass(git_signature_new(
&sig, CL_COMMIT_NAME, CL_COMMIT_EMAIL, time, 0));
if (!msg) {
strcpy(buf, CL_COMMIT_MSG);
git_oid_tostr(buf + strlen(CL_COMMIT_MSG),
sizeof(buf) - strlen(CL_COMMIT_MSG), &tree_id);
msg = buf;
}
cl_git_pass(git_commit_create_v(
&commit_id, repo, ref ? git_reference_name(ref) : "HEAD",
sig, sig, NULL, msg, tree, parent ? 1 : 0, parent));
if (out)
git_oid_cpy(out, &commit_id);
git_object_free(parent);
git_reference_free(ref);
if (free_sig)
git_signature_free(sig);
git_tree_free(tree);
}
void cl_repo_set_bool(git_repository *repo, const char *cfg, int value) void cl_repo_set_bool(git_repository *repo, const char *cfg, int value)
{ {
git_config *config; git_config *config;
...@@ -354,3 +415,65 @@ int cl_repo_get_bool(git_repository *repo, const char *cfg) ...@@ -354,3 +415,65 @@ int cl_repo_get_bool(git_repository *repo, const char *cfg)
git_config_free(config); git_config_free(config);
return val; return val;
} }
/* this is essentially the code from git__unescape modified slightly */
static size_t strip_cr_from_buf(char *start, size_t len)
{
char *scan, *trail, *end = start + len;
for (scan = trail = start; scan < end; trail++, scan++) {
while (*scan == '\r')
scan++; /* skip '\r' */
if (trail != scan)
*trail = *scan;
}
*trail = '\0';
return (trail - start);
}
void clar__assert_equal_file(
const char *expected_data,
size_t expected_bytes,
int ignore_cr,
const char *path,
const char *file,
size_t line)
{
char buf[4000];
ssize_t bytes, total_bytes = 0;
int fd = p_open(path, O_RDONLY | O_BINARY);
cl_assert(fd >= 0);
if (expected_data && !expected_bytes)
expected_bytes = strlen(expected_data);
while ((bytes = p_read(fd, buf, sizeof(buf))) != 0) {
clar__assert(
bytes > 0, file, line, "error reading from file", path, 1);
if (ignore_cr)
bytes = strip_cr_from_buf(buf, bytes);
if (memcmp(expected_data, buf, bytes) != 0) {
int pos;
for (pos = 0; pos < bytes && expected_data[pos] == buf[pos]; ++pos)
/* find differing byte offset */;
p_snprintf(
buf, sizeof(buf), "file content mismatch at byte %d",
(int)(total_bytes + pos));
clar__fail(file, line, buf, path, 1);
}
expected_data += bytes;
total_bytes += bytes;
}
p_close(fd);
clar__assert(!bytes, file, line, "error reading from file", path, 1);
clar__assert_equal(file, line, "mismatched file length", 1, "%"PRIuZ,
(size_t)expected_bytes, (size_t)total_bytes);
}
...@@ -43,9 +43,28 @@ GIT_INLINE(void) clar__assert_in_range( ...@@ -43,9 +43,28 @@ GIT_INLINE(void) clar__assert_in_range(
} }
} }
#define cl_assert_equal_sz(sz1,sz2) do { \
size_t __sz1 = (sz1), __sz2 = (sz2); \
clar__assert_equal(__FILE__,__LINE__,#sz1 " != " #sz2, 1, "%"PRIuZ, __sz1, __sz2); \
} while (0)
#define cl_assert_in_range(L,V,H) \ #define cl_assert_in_range(L,V,H) \
clar__assert_in_range((L),(V),(H),__FILE__,__LINE__,"Range check: " #V " in [" #L "," #H "]", 1) clar__assert_in_range((L),(V),(H),__FILE__,__LINE__,"Range check: " #V " in [" #L "," #H "]", 1)
#define cl_assert_equal_file(DATA,SIZE,PATH) \
clar__assert_equal_file(DATA,SIZE,0,PATH,__FILE__,__LINE__)
#define cl_assert_equal_file_ignore_cr(DATA,SIZE,PATH) \
clar__assert_equal_file(DATA,SIZE,1,PATH,__FILE__,__LINE__)
void clar__assert_equal_file(
const char *expected_data,
size_t expected_size,
int ignore_cr,
const char *path,
const char *file,
size_t line);
/* /*
* Some utility macros for building long strings * Some utility macros for building long strings
*/ */
...@@ -59,7 +78,8 @@ GIT_INLINE(void) clar__assert_in_range( ...@@ -59,7 +78,8 @@ GIT_INLINE(void) clar__assert_in_range(
void cl_git_mkfile(const char *filename, const char *content); void cl_git_mkfile(const char *filename, const char *content);
void cl_git_append2file(const char *filename, const char *new_content); void cl_git_append2file(const char *filename, const char *new_content);
void cl_git_rewritefile(const char *filename, const char *new_content); void cl_git_rewritefile(const char *filename, const char *new_content);
void cl_git_write2file(const char *filename, const char *new_content, int flags, unsigned int mode); void cl_git_write2file(const char *path, const char *data,
size_t datalen, int flags, unsigned int mode);
bool cl_toggle_filemode(const char *filename); bool cl_toggle_filemode(const char *filename);
bool cl_is_chmod_supported(void); bool cl_is_chmod_supported(void);
...@@ -84,6 +104,14 @@ const char* cl_git_path_url(const char *path); ...@@ -84,6 +104,14 @@ const char* cl_git_path_url(const char *path);
/* Test repository cleaner */ /* Test repository cleaner */
int cl_git_remove_placeholders(const char *directory_path, const char *filename); int cl_git_remove_placeholders(const char *directory_path, const char *filename);
/* commit creation helpers */
void cl_repo_commit_from_index(
git_oid *out,
git_repository *repo,
git_signature *sig,
git_time_t time,
const char *msg);
/* config setting helpers */ /* config setting helpers */
void cl_repo_set_bool(git_repository *repo, const char *cfg, int value); void cl_repo_set_bool(git_repository *repo, const char *cfg, int value);
int cl_repo_get_bool(git_repository *repo, const char *cfg); int cl_repo_get_bool(git_repository *repo, const char *cfg);
......
...@@ -44,7 +44,7 @@ void test_clone_empty__can_clone_an_empty_local_repo_barely(void) ...@@ -44,7 +44,7 @@ void test_clone_empty__can_clone_an_empty_local_repo_barely(void)
g_options.bare = true; g_options.bare = true;
cl_git_pass(git_clone(&g_repo_cloned, "./empty_bare.git", "./empty", &g_options)); cl_git_pass(git_clone(&g_repo_cloned, "./empty_bare.git", "./empty", &g_options));
/* Although the HEAD is orphaned... */ /* Although the HEAD is unborn... */
cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo_cloned, local_name)); cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo_cloned, local_name));
/* ...one can still retrieve the name of the remote tracking reference */ /* ...one can still retrieve the name of the remote tracking reference */
...@@ -59,7 +59,7 @@ void test_clone_empty__can_clone_an_empty_local_repo_barely(void) ...@@ -59,7 +59,7 @@ void test_clone_empty__can_clone_an_empty_local_repo_barely(void)
cl_assert_equal_s(expected_remote_name, buffer); cl_assert_equal_s(expected_remote_name, buffer);
/* ...even when the remote HEAD is orphaned as well */ /* ...even when the remote HEAD is unborn as well */
cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo_cloned, cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo_cloned,
expected_tracked_branch_name)); expected_tracked_branch_name));
} }
......
...@@ -228,7 +228,7 @@ void test_clone_nonetwork__can_checkout_given_branch(void) ...@@ -228,7 +228,7 @@ void test_clone_nonetwork__can_checkout_given_branch(void)
g_options.checkout_branch = "test"; g_options.checkout_branch = "test";
cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options)); cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
cl_assert_equal_i(0, git_repository_head_orphan(g_repo)); cl_assert_equal_i(0, git_repository_head_unborn(g_repo));
cl_git_pass(git_repository_head(&g_ref, g_repo)); cl_git_pass(git_repository_head(&g_ref, g_repo));
cl_assert_equal_s(git_reference_name(g_ref), "refs/heads/test"); cl_assert_equal_s(git_reference_name(g_ref), "refs/heads/test");
......
...@@ -6,18 +6,21 @@ void test_config_global__initialize(void) ...@@ -6,18 +6,21 @@ void test_config_global__initialize(void)
{ {
git_buf path = GIT_BUF_INIT; git_buf path = GIT_BUF_INIT;
cl_must_pass(p_mkdir("home", 0777)); cl_assert_equal_i(0, p_mkdir("home", 0777));
cl_git_pass(git_path_prettify(&path, "home", NULL)); cl_git_pass(git_path_prettify(&path, "home", NULL));
cl_git_pass(git_libgit2_opts( cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr)); GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
cl_must_pass(p_mkdir("xdg", 0777)); cl_assert_equal_i(0, p_mkdir("xdg", 0777));
cl_git_pass(git_path_prettify(&path, "xdg", NULL)); cl_assert_equal_i(0, p_mkdir("xdg/git", 0777));
cl_git_pass(git_path_prettify(&path, "xdg/git", NULL));
cl_git_pass(git_libgit2_opts( cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr)); GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr));
cl_assert_equal_i(0, p_mkdir("etc", 0777));
cl_git_pass(git_path_prettify(&path, "etc", NULL));
cl_git_pass(git_libgit2_opts( cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, NULL)); GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr));
git_buf_free(&path); git_buf_free(&path);
} }
...@@ -26,6 +29,11 @@ void test_config_global__cleanup(void) ...@@ -26,6 +29,11 @@ void test_config_global__cleanup(void)
{ {
cl_git_pass(git_futils_rmdir_r("home", NULL, GIT_RMDIR_REMOVE_FILES)); cl_git_pass(git_futils_rmdir_r("home", NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_pass(git_futils_rmdir_r("xdg", NULL, GIT_RMDIR_REMOVE_FILES)); cl_git_pass(git_futils_rmdir_r("xdg", NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_pass(git_futils_rmdir_r("etc", NULL, GIT_RMDIR_REMOVE_FILES));
git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, NULL);
git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, NULL);
git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, NULL);
} }
void test_config_global__open_global(void) void test_config_global__open_global(void)
...@@ -48,10 +56,7 @@ void test_config_global__open_xdg(void) ...@@ -48,10 +56,7 @@ void test_config_global__open_xdg(void)
const char *val, *str = "teststring"; const char *val, *str = "teststring";
const char *key = "this.variable"; const char *key = "this.variable";
p_setenv("XDG_CONFIG_HOME", "xdg", 1); cl_git_mkfile("xdg/git/config", "# XDG config\n[core]\n test = 1\n");
cl_must_pass(p_mkdir("xdg/git/", 0777));
cl_git_mkfile("xdg/git/config", "");
cl_git_pass(git_config_open_default(&cfg)); cl_git_pass(git_config_open_default(&cfg));
cl_git_pass(git_config_open_level(&xdg, cfg, GIT_CONFIG_LEVEL_XDG)); cl_git_pass(git_config_open_level(&xdg, cfg, GIT_CONFIG_LEVEL_XDG));
......
#include "clar_libgit2.h"
#include "buffer.h"
#include "fileops.h"
void test_config_include__relative(void)
{
git_config *cfg;
const char *str;
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config-include")));
cl_git_pass(git_config_get_string(&str, cfg, "foo.bar.baz"));
cl_assert_equal_s(str, "huzzah");
git_config_free(cfg);
}
void test_config_include__absolute(void)
{
git_config *cfg;
const char *str;
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git_buf_printf(&buf, "[include]\npath = %s/config-included", cl_fixture("config")));
cl_git_mkfile("config-include-absolute", git_buf_cstr(&buf));
git_buf_free(&buf);
cl_git_pass(git_config_open_ondisk(&cfg, "config-include-absolute"));
cl_git_pass(git_config_get_string(&str, cfg, "foo.bar.baz"));
cl_assert_equal_s(str, "huzzah");
git_config_free(cfg);
}
void test_config_include__homedir(void)
{
git_config *cfg;
const char *str;
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, cl_fixture("config")));
cl_git_mkfile("config-include-homedir", "[include]\npath = ~/config-included");
cl_git_pass(git_config_open_ondisk(&cfg, "config-include-homedir"));
cl_git_pass(git_config_get_string(&str, cfg, "foo.bar.baz"));
cl_assert_equal_s(str, "huzzah");
git_config_free(cfg);
}
void test_config_include__refresh(void)
{
git_config *cfg;
const char *str;
cl_fixture_sandbox("config");
cl_git_pass(git_config_open_ondisk(&cfg, "config/config-include"));
cl_git_pass(git_config_get_string(&str, cfg, "foo.bar.baz"));
cl_assert_equal_s(str, "huzzah");
/* Change the included file and see if we refresh */
cl_git_mkfile("config/config-included", "[foo \"bar\"]\nbaz = hurrah");
cl_git_pass(git_config_refresh(cfg));
cl_git_pass(git_config_get_string(&str, cfg, "foo.bar.baz"));
cl_assert_equal_s(str, "hurrah");
git_config_free(cfg);
cl_fixture_cleanup("config");
}
/* We need to pretend that the variables were defined where the file was included */
void test_config_include__ordering(void)
{
git_config *cfg;
const char *str;
cl_git_mkfile("included", "[foo \"bar\"]\nbaz = hurrah\nfrotz = hiya");
cl_git_mkfile("including",
"[foo \"bar\"]\nfrotz = hello\n"
"[include]\npath = included\n"
"[foo \"bar\"]\nbaz = huzzah\n");
cl_git_pass(git_config_open_ondisk(&cfg, "including"));
cl_git_pass(git_config_get_string(&str, cfg, "foo.bar.frotz"));
cl_assert_equal_s(str, "hiya");
cl_git_pass(git_config_get_string(&str, cfg, "foo.bar.baz"));
cl_assert_equal_s(str, "huzzah");
git_config_free(cfg);
}
/* We need to pretend that the variables were defined where the file was included */
void test_config_include__depth(void)
{
git_config *cfg;
cl_git_mkfile("a", "[include]\npath = b");
cl_git_mkfile("b", "[include]\npath = a");
cl_git_fail(git_config_open_ondisk(&cfg, "a"));
unlink("a");
unlink("b");
}
...@@ -523,3 +523,18 @@ void test_config_read__corrupt_header(void) ...@@ -523,3 +523,18 @@ void test_config_read__corrupt_header(void)
git_config_free(cfg); git_config_free(cfg);
} }
void test_config_read__override_variable(void)
{
git_config *cfg;
const char *str;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "[some] var = one\nvar = two");
cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
cl_git_pass(git_config_get_string(&str, cfg, "some.var"));
cl_assert_equal_s(str, "two");
git_config_free(cfg);
}
...@@ -919,6 +919,8 @@ void test_core_buffer__similarity_metric_whitespace(void) ...@@ -919,6 +919,8 @@ void test_core_buffer__similarity_metric_whitespace(void)
git_buf_free(&buf); git_buf_free(&buf);
} }
#include "../filter/crlf.h"
#define check_buf(expected,buf) do { \ #define check_buf(expected,buf) do { \
cl_assert_equal_s(expected, buf.ptr); \ cl_assert_equal_s(expected, buf.ptr); \
cl_assert_equal_sz(strlen(expected), buf.size); } while (0) cl_assert_equal_sz(strlen(expected), buf.size); } while (0)
...@@ -934,16 +936,16 @@ void test_core_buffer__lf_and_crlf_conversions(void) ...@@ -934,16 +936,16 @@ void test_core_buffer__lf_and_crlf_conversions(void)
cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src)); cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
check_buf("lf\r\nlf\r\nlf\r\nlf\r\n", tgt); check_buf("lf\r\nlf\r\nlf\r\nlf\r\n", tgt);
cl_assert_equal_i(GIT_ENOTFOUND, git_buf_text_crlf_to_lf(&tgt, &src)); cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
/* no conversion needed if all LFs already */ check_buf(src.ptr, tgt);
git_buf_sets(&src, "\nlf\nlf\nlf\nlf\nlf"); git_buf_sets(&src, "\nlf\nlf\nlf\nlf\nlf");
cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src)); cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
check_buf("\r\nlf\r\nlf\r\nlf\r\nlf\r\nlf", tgt); check_buf("\r\nlf\r\nlf\r\nlf\r\nlf\r\nlf", tgt);
cl_assert_equal_i(GIT_ENOTFOUND, git_buf_text_crlf_to_lf(&tgt, &src)); cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
/* no conversion needed if all LFs already */ check_buf(src.ptr, tgt);
/* CRLF source */ /* CRLF source */
...@@ -993,10 +995,45 @@ void test_core_buffer__lf_and_crlf_conversions(void) ...@@ -993,10 +995,45 @@ void test_core_buffer__lf_and_crlf_conversions(void)
check_buf("\rcrlf\nlf\nlf\ncr\rcrlf\nlf\ncr\r", tgt); check_buf("\rcrlf\nlf\nlf\ncr\rcrlf\nlf\ncr\r", tgt);
git_buf_sets(&src, "\rcr\r"); git_buf_sets(&src, "\rcr\r");
cl_assert_equal_i(GIT_ENOTFOUND, git_buf_text_lf_to_crlf(&tgt, &src)); cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
check_buf(src.ptr, tgt);
cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src)); cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
check_buf("\rcr\r", tgt); check_buf("\rcr\r", tgt);
git_buf_free(&src); git_buf_free(&src);
git_buf_free(&tgt); git_buf_free(&tgt);
/* blob correspondence tests */
git_buf_sets(&src, ALL_CRLF_TEXT_RAW);
cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
check_buf(ALL_CRLF_TEXT_AS_CRLF, tgt);
cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
check_buf(ALL_CRLF_TEXT_AS_LF, tgt);
git_buf_free(&src);
git_buf_free(&tgt);
git_buf_sets(&src, ALL_LF_TEXT_RAW);
cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
check_buf(ALL_LF_TEXT_AS_CRLF, tgt);
cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
check_buf(ALL_LF_TEXT_AS_LF, tgt);
git_buf_free(&src);
git_buf_free(&tgt);
git_buf_sets(&src, MORE_CRLF_TEXT_RAW);
cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
check_buf(MORE_CRLF_TEXT_AS_CRLF, tgt);
cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
check_buf(MORE_CRLF_TEXT_AS_LF, tgt);
git_buf_free(&src);
git_buf_free(&tgt);
git_buf_sets(&src, MORE_LF_TEXT_RAW);
cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
check_buf(MORE_LF_TEXT_AS_CRLF, tgt);
cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
check_buf(MORE_LF_TEXT_AS_LF, tgt);
git_buf_free(&src);
git_buf_free(&tgt);
} }
...@@ -24,18 +24,16 @@ void test_core_filebuf__0(void) ...@@ -24,18 +24,16 @@ void test_core_filebuf__0(void)
void test_core_filebuf__1(void) void test_core_filebuf__1(void)
{ {
git_filebuf file = GIT_FILEBUF_INIT; git_filebuf file = GIT_FILEBUF_INIT;
int fd;
char test[] = "test"; char test[] = "test";
fd = p_creat(test, 0666); //-V536 cl_git_mkfile(test, "libgit2 rocks\n");
cl_must_pass(fd);
cl_must_pass(p_write(fd, "libgit2 rocks\n", 14));
cl_must_pass(p_close(fd));
cl_git_pass(git_filebuf_open(&file, test, GIT_FILEBUF_APPEND)); cl_git_pass(git_filebuf_open(&file, test, GIT_FILEBUF_APPEND));
cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks")); cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
cl_git_pass(git_filebuf_commit(&file, 0666)); cl_git_pass(git_filebuf_commit(&file, 0666));
cl_assert_equal_file("libgit2 rocks\nlibgit2 rocks\n", 0, test);
cl_must_pass(p_unlink(test)); cl_must_pass(p_unlink(test));
} }
...@@ -53,6 +51,8 @@ void test_core_filebuf__2(void) ...@@ -53,6 +51,8 @@ void test_core_filebuf__2(void)
cl_git_pass(git_filebuf_write(&file, buf, sizeof(buf))); cl_git_pass(git_filebuf_write(&file, buf, sizeof(buf)));
cl_git_pass(git_filebuf_commit(&file, 0666)); cl_git_pass(git_filebuf_commit(&file, 0666));
cl_assert_equal_file((char *)buf, sizeof(buf), test);
cl_must_pass(p_unlink(test)); cl_must_pass(p_unlink(test));
} }
......
...@@ -7,6 +7,8 @@ static git_repository *g_repo = NULL; ...@@ -7,6 +7,8 @@ static git_repository *g_repo = NULL;
void test_diff_rename__initialize(void) void test_diff_rename__initialize(void)
{ {
g_repo = cl_git_sandbox_init("renames"); g_repo = cl_git_sandbox_init("renames");
cl_repo_set_bool(g_repo, "core.autocrlf", false);
} }
void test_diff_rename__cleanup(void) void test_diff_rename__cleanup(void)
......
...@@ -11,7 +11,6 @@ void test_diff_submodules__initialize(void) ...@@ -11,7 +11,6 @@ void test_diff_submodules__initialize(void)
void test_diff_submodules__cleanup(void) void test_diff_submodules__cleanup(void)
{ {
cleanup_fixture_submodules();
} }
static void check_diff_patches_at_line( static void check_diff_patches_at_line(
...@@ -229,11 +228,11 @@ void test_diff_submodules__invalid_cache(void) ...@@ -229,11 +228,11 @@ void test_diff_submodules__invalid_cache(void)
"<END>" "<END>"
}; };
static const char *expected_moved[] = { static const char *expected_moved[] = {
"diff --git a/sm_changed_head b/sm_changed_head\nindex 3d9386c..0910a13 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n+Subproject commit 0910a13dfa2210496f6c590d75bc360dd11b2a1b\n", "diff --git a/sm_changed_head b/sm_changed_head\nindex 3d9386c..7002348 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n+Subproject commit 700234833f6ccc20d744b238612646be071acaae\n",
"<END>" "<END>"
}; };
static const char *expected_moved_dirty[] = { static const char *expected_moved_dirty[] = {
"diff --git a/sm_changed_head b/sm_changed_head\nindex 3d9386c..0910a13 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n+Subproject commit 0910a13dfa2210496f6c590d75bc360dd11b2a1b-dirty\n", "diff --git a/sm_changed_head b/sm_changed_head\nindex 3d9386c..7002348 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n+Subproject commit 700234833f6ccc20d744b238612646be071acaae-dirty\n",
"<END>" "<END>"
}; };
...@@ -310,26 +309,7 @@ void test_diff_submodules__invalid_cache(void) ...@@ -310,26 +309,7 @@ void test_diff_submodules__invalid_cache(void)
git_diff_list_free(diff); git_diff_list_free(diff);
/* commit changed index of submodule */ /* commit changed index of submodule */
{ cl_repo_commit_from_index(NULL, smrepo, NULL, 1372350000, "Move it");
git_object *parent;
git_oid tree_id, commit_id;
git_tree *tree;
git_signature *sig;
git_reference *ref;
cl_git_pass(git_revparse_ext(&parent, &ref, smrepo, "HEAD"));
cl_git_pass(git_index_write_tree(&tree_id, smindex));
cl_git_pass(git_index_write(smindex));
cl_git_pass(git_tree_lookup(&tree, smrepo, &tree_id));
cl_git_pass(git_signature_new(&sig, "Sm Test", "sm@tester.test", 1372350000, 480));
cl_git_pass(git_commit_create_v(
&commit_id, smrepo, git_reference_name(ref), sig, sig,
NULL, "Move it", tree, 1, parent));
git_object_free(parent);
git_tree_free(tree);
git_reference_free(ref);
git_signature_free(sig);
}
git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_DIRTY); git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_DIRTY);
......
...@@ -1266,3 +1266,28 @@ void test_diff_workdir__untracked_directory_comes_last(void) ...@@ -1266,3 +1266,28 @@ void test_diff_workdir__untracked_directory_comes_last(void)
git_diff_list_free(diff); git_diff_list_free(diff);
} }
void test_diff_workdir__untracked_with_bom(void)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff_list *diff = NULL;
const git_diff_delta *delta;
g_repo = cl_git_sandbox_init("empty_standard_repo");
cl_repo_set_bool(g_repo, "core.autocrlf", true);
cl_git_write2file("empty_standard_repo/bom.txt",
"\xFF\xFE\x31\x00\x32\x00\x33\x00\x34\x00", 10, O_WRONLY|O_CREAT, 0664);
opts.flags =
GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_INCLUDE_UNTRACKED_CONTENT;
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
cl_assert_equal_i(1, git_diff_num_deltas(diff));
cl_git_pass(git_diff_get_patch(NULL, &delta, diff, 0));
cl_assert_equal_i(GIT_DELTA_UNTRACKED, delta->status);
cl_assert((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
git_diff_list_free(diff);
}
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