diff options
91 files changed, 1188 insertions, 395 deletions
diff --git a/.travis.yml b/.travis.yml index 256227589..f1660b25e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,3 +46,8 @@ notifications: - irc.freenode.net#libgit2 on_success: change on_failure: always + campfire: + on_success: always + on_failure: always + rooms: + - secure: "sH0dpPWMirbEe7AvLddZ2yOp8rzHalGmv0bYL/LIhVw3JDI589HCYckeLMSB\n3e/FeXw4bn0EqXWEXijVa4ijbilVY6d8oprdqMdWHEodng4KvY5vID3iZSGT\nxylhahO1XHmRynKQLOAvxlc93IlpVW38vQfby8giIY1nkpspb2w=" diff --git a/CONVENTIONS.md b/CONVENTIONS.md index ea5e40ee6..10d9c8644 100644 --- a/CONVENTIONS.md +++ b/CONVENTIONS.md @@ -123,8 +123,7 @@ GIT_EXTERN(int) git_foo_id( int b); ``` -Public headers are indented with spaces, three to a tab. Internal code is -indented with tabs; set your editor's tab width to 3 for best effect. +Indentation is done with tabs; set your editor's tab width to 3 for best effect. ## Documentation diff --git a/examples/diff.c b/examples/diff.c index a465182ba..b81a8682a 100644 --- a/examples/diff.c +++ b/examples/diff.c @@ -133,7 +133,7 @@ int main(int argc, char *argv[]) { git_repository *repo = NULL; git_tree *t1 = NULL, *t2 = NULL; - git_diff_options opts; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff; int i, color = -1, compact = 0, cached = 0; char *a, *dir = ".", *treeish1 = NULL, *treeish2 = NULL; diff --git a/examples/network/clone.c b/examples/network/clone.c index a718f3084..7596523c2 100644 --- a/examples/network/clone.c +++ b/examples/network/clone.c @@ -51,7 +51,7 @@ int do_clone(git_repository *repo, int argc, char **argv) { progress_data pd; git_repository *cloned_repo = NULL; - git_checkout_opts checkout_opts; + git_checkout_opts checkout_opts = GIT_CHECKOUT_OPTS_INIT; const char *url = argv[1]; const char *path = argv[2]; int error; diff --git a/examples/network/fetch.c b/examples/network/fetch.c index e341d2d6c..8048fd67a 100644 --- a/examples/network/fetch.c +++ b/examples/network/fetch.c @@ -70,7 +70,7 @@ int fetch(git_repository *repo, int argc, char **argv) const git_transfer_progress *stats; pthread_t worker; struct dl_data data; - git_remote_callbacks callbacks; + git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; argc = argc; // Figure out whether it's a named remote or a URL @@ -81,7 +81,6 @@ int fetch(git_repository *repo, int argc, char **argv) } // Set up the callbacks (only update_tips for now) - memset(&callbacks, 0, sizeof(callbacks)); callbacks.update_tips = &update_cb; callbacks.progress = &progress_cb; git_remote_set_callbacks(remote, &callbacks); diff --git a/include/git2/branch.h b/include/git2/branch.h index c9ae9cc5d..f55903cd6 100644 --- a/include/git2/branch.h +++ b/include/git2/branch.h @@ -29,6 +29,9 @@ GIT_BEGIN_DECL * * The returned reference must be freed by the user. * + * The branch name will be checked for validity. + * See `git_tag_create()` for rules about valid names. + * * @param out Pointer where to store the underlying reference. * * @param branch_name Name for the branch; this name is @@ -42,7 +45,7 @@ GIT_BEGIN_DECL * * @param force Overwrite existing branch. * - * @return 0 or an error code. + * @return 0, GIT_EINVALIDSPEC or an error code. * A proper reference is written in the refs/heads namespace * pointing to the provided target commit. */ @@ -94,6 +97,9 @@ GIT_EXTERN(int) git_branch_foreach( /** * Move/rename an existing local branch reference. * + * The new branch name will be checked for validity. + * See `git_tag_create()` for rules about valid names. + * * @param branch Current underlying reference of the branch. * * @param new_branch_name Target name of the branch once the move @@ -101,7 +107,7 @@ GIT_EXTERN(int) git_branch_foreach( * * @param force Overwrite existing branch. * - * @return 0 on success, or an error code. + * @return 0 on success, GIT_EINVALIDSPEC or an error code. */ GIT_EXTERN(int) git_branch_move( git_reference *branch, @@ -113,6 +119,9 @@ GIT_EXTERN(int) git_branch_move( * * The generated reference must be freed by the user. * + * The branch name will be checked for validity. + * See `git_tag_create()` for rules about valid names. + * * @param out pointer to the looked-up branch reference * * @param repo the repository to look up the branch @@ -124,7 +133,7 @@ GIT_EXTERN(int) git_branch_move( * be valued with either GIT_BRANCH_LOCAL or GIT_BRANCH_REMOTE. * * @return 0 on success; GIT_ENOTFOUND when no matching branch - * exists, otherwise an error code. + * exists, GIT_EINVALIDSPEC, otherwise an error code. */ GIT_EXTERN(int) git_branch_lookup( git_reference **out, diff --git a/include/git2/checkout.h b/include/git2/checkout.h index bd988db2c..c36e2a41b 100644 --- a/include/git2/checkout.h +++ b/include/git2/checkout.h @@ -186,7 +186,8 @@ typedef struct git_checkout_opts { git_strarray paths; } git_checkout_opts; -#define GIT_CHECKOUT_OPTS_INIT {1, 0} +#define GIT_CHECKOUT_OPTS_VERSION 1 +#define GIT_CHECKOUT_OPTS_INIT {GIT_CHECKOUT_OPTS_VERSION} /** * Updates files in the index and the working tree to match the content of the diff --git a/include/git2/config.h b/include/git2/config.h index af4d54044..b186e70da 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -49,6 +49,7 @@ typedef int (*git_config_foreach_cb)(const git_config_entry *, void *); * access a configuration file */ struct git_config_backend { + unsigned int version; struct git_config *cfg; /* Open means open the file/database and parse if necessary */ @@ -62,6 +63,8 @@ struct git_config_backend { int (*refresh)(struct git_config_backend *); void (*free)(struct git_config_backend *); }; +#define GIT_CONFIG_BACKEND_VERSION 1 +#define GIT_CONFIG_BACKEND_INIT {GIT_CONFIG_BACKEND_VERSION} typedef enum { GIT_CVAR_FALSE = 0, diff --git a/include/git2/diff.h b/include/git2/diff.h index fd00378af..49f781ddd 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -17,6 +17,9 @@ * @file git2/diff.h * @brief Git tree and file differencing routines. * + * Overview + * -------- + * * Calculating diffs is generally done in two phases: building a diff list * then traversing the diff list. This makes is easier to share logic * across the various types of diffs (tree vs tree, workdir vs index, etc.), @@ -24,6 +27,35 @@ * such as rename detected, in between the steps. When you are done with a * diff list object, it must be freed. * + * Terminology + * ----------- + * + * To understand the diff APIs, you should know the following terms: + * + * - A `diff` or `diff list` represents the cumulative list of differences + * between two snapshots of a repository (possibly filtered by a set of + * file name patterns). This is the `git_diff_list` object. + * - A `delta` is a file pair with an old and new revision. The old version + * may be absent if the file was just created and the new version may be + * absent if the file was deleted. A diff is mostly just a list of deltas. + * - A `binary` file / delta is a file (or pair) for which no text diffs + * should be generated. A diff list can contain delta entries that are + * binary, but no diff content will be output for those files. There is + * a base heuristic for binary detection and you can further tune the + * behavior with git attributes or diff flags and option settings. + * - A `hunk` is a span of modified lines in a delta along with some stable + * surrounding context. You can configure the amount of context and other + * properties of how hunks are generated. Each hunk also comes with a + * header that described where it starts and ends in both the old and new + * versions in the delta. + * - A `line` is a range of characters inside a hunk. It could be a context + * line (i.e. in both old and new versions), an added line (i.e. only in + * the new version), or a removed line (i.e. only in the old version). + * Unfortunately, we don't know anything about the encoding of data in the + * file being diffed, so we cannot tell you much about the line content. + * Line data will not be NUL-byte terminated, however, because it will be + * just a span of bytes inside the larger file. + * * @ingroup Git * @{ */ @@ -97,25 +129,32 @@ typedef enum { * values. Similarly, passing NULL for the options structure will * give the defaults. The default values are marked below. * - * - flags: a combination of the git_diff_option_t values above - * - context_lines: number of lines of context to show around diffs - * - interhunk_lines: min lines between diff hunks to merge them - * - old_prefix: "directory" to prefix to old file names (default "a") - * - new_prefix: "directory" to prefix to new file names (default "b") - * - pathspec: array of paths / patterns to constrain diff - * - max_size: maximum blob size to diff, above this treated as binary + * - `flags` is a combination of the `git_diff_option_t` values above + * - `context_lines` is the number of unchanged lines that define the + * boundary of a hunk (and to display before and after) + * - `interhunk_lines` is the maximum number of unchanged lines between + * hunk boundaries before the hunks will be merged into a one. + * - `old_prefix` is the virtual "directory" to prefix to old file names + * in hunk headers (default "a") + * - `new_prefix` is the virtual "directory" to prefix to new file names + * in hunk headers (default "b") + * - `pathspec` is an array of paths / fnmatch patterns to constrain diff + * - `max_size` is a file size above which a blob will be marked as binary */ typedef struct { - unsigned int version; /**< version for the struct */ - uint32_t flags; /**< defaults to GIT_DIFF_NORMAL */ - uint16_t context_lines; /**< defaults to 3 */ - uint16_t interhunk_lines; /**< defaults to 0 */ - char *old_prefix; /**< defaults to "a" */ - char *new_prefix; /**< defaults to "b" */ - git_strarray pathspec; /**< defaults to show all paths */ - git_off_t max_size; /**< defaults to 512Mb */ + unsigned int version; /**< version for the struct */ + uint32_t flags; /**< defaults to GIT_DIFF_NORMAL */ + uint16_t context_lines; /**< defaults to 3 */ + uint16_t interhunk_lines; /**< defaults to 0 */ + const char *old_prefix; /**< defaults to "a" */ + const char *new_prefix; /**< defaults to "b" */ + git_strarray pathspec; /**< defaults to show all paths */ + git_off_t max_size; /**< defaults to 512mb */ } git_diff_options; +#define GIT_DIFF_OPTIONS_VERSION 1 +#define GIT_DIFF_OPTIONS_INIT {GIT_DIFF_OPTIONS_VERSION} + /** * The diff list object that contains all individual file deltas. */ @@ -139,6 +178,13 @@ typedef enum { /** * What type of change is described by a git_diff_delta? + * + * `GIT_DELTA_RENAMED` and `GIT_DELTA_COPIED` will only show up if you run + * `git_diff_find_similar()` on the diff list object. + * + * `GIT_DELTA_TYPECHANGE` only shows up given `GIT_DIFF_INCLUDE_TYPECHANGE` + * in the option flags (otherwise type changes will be split into ADDED / + * DELETED pairs). */ typedef enum { GIT_DELTA_UNMODIFIED = 0, @@ -154,6 +200,21 @@ typedef enum { /** * Description of one side of a diff. + * + * The `oid` is the `git_oid` of the item. If it represents an absent side + * of a diff (e.g. the `old_file` of a `GIT_DELTA_ADDED` delta), then the + * oid will be zeroes. + * + * `path` is the NUL-terminated path to the file relative to the working + * directory of the repository. + * + * `size` is the size of the file in bytes. + * + * `flags` is a combination of the `git_diff_file_flag_t` types, but those + * are largely internal values. + * + * `mode` is, roughly, the stat() st_mode value for the item. This will be + * restricted to one of the `git_filemode_t` values. */ typedef struct { git_oid oid; @@ -304,6 +365,8 @@ typedef struct { unsigned int target_limit; } git_diff_find_options; +#define GIT_DIFF_FIND_OPTIONS_VERSION 1 +#define GIT_DIFF_FIND_OPTIONS_INIT {GIT_DIFF_FIND_OPTIONS_VERSION} /** @name Diff List Generator Functions * diff --git a/include/git2/errors.h b/include/git2/errors.h index 9dd42f0c4..63b6bc8ee 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -30,6 +30,7 @@ enum { GIT_EORPHANEDHEAD = -9, GIT_EUNMERGED = -10, GIT_ENONFASTFORWARD = -11, + GIT_EINVALIDSPEC = -12, GIT_PASSTHROUGH = -30, GIT_ITEROVER = -31, diff --git a/include/git2/object.h b/include/git2/object.h index fcc56cb27..e5ca17e16 100644 --- a/include/git2/object.h +++ b/include/git2/object.h @@ -179,8 +179,9 @@ GIT_EXTERN(size_t) git_object__size(git_otype type); * * @param peeled Pointer to the peeled git_object * @param object The object to be processed - * @param target_type The type of the requested object - * @return 0 or an error code + * @param target_type The type of the requested object (GIT_OBJ_COMMIT, + * GIT_OBJ_TAG, GIT_OBJ_TREE, GIT_OBJ_BLOB or GIT_OBJ_ANY). + * @return 0 on success, GIT_EAMBIGUOUS, GIT_ENOTFOUND or an error code */ GIT_EXTERN(int) git_object_peel( git_object **peeled, diff --git a/include/git2/odb_backend.h b/include/git2/odb_backend.h index 04658f9b3..19a154022 100644 --- a/include/git2/odb_backend.h +++ b/include/git2/odb_backend.h @@ -33,6 +33,7 @@ typedef int (*git_odb_foreach_cb)(const git_oid *id, void *payload); * An instance for a custom backend */ struct git_odb_backend { + unsigned int version; git_odb *odb; /* read and read_prefix each return to libgit2 a buffer which @@ -98,6 +99,9 @@ struct git_odb_backend { void (* free)(struct git_odb_backend *); }; +#define GIT_ODB_BACKEND_VERSION 1 +#define GIT_ODB_BACKEND_INIT {GIT_ODB_BACKEND_VERSION} + /** Streaming mode */ enum { GIT_STREAM_RDONLY = (1 << 1), diff --git a/include/git2/reflog.h b/include/git2/reflog.h index 45dff2165..418826d1d 100644 --- a/include/git2/reflog.h +++ b/include/git2/reflog.h @@ -63,9 +63,12 @@ GIT_EXTERN(int) git_reflog_append(git_reflog *reflog, const git_oid *id, const g * * The reflog to be renamed is expected to already exist * + * The new name will be checked for validity. + * See `git_reference_create_symbolic()` for rules about valid names. + * * @param ref the reference * @param name the new name of the reference - * @return 0 or an error code + * @return 0 on success, GIT_EINVALIDSPEC or an error code */ GIT_EXTERN(int) git_reflog_rename(git_reference *ref, const char *name); diff --git a/include/git2/refs.h b/include/git2/refs.h index c92646115..cfc96a68c 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -26,13 +26,13 @@ GIT_BEGIN_DECL * * The returned reference must be freed by the user. * - * See `git_reference_create_symbolic()` for documentation about valid - * reference names. + * The name will be checked for validity. + * See `git_reference_create_symbolic()` for rules about valid names. * * @param out pointer to the looked-up reference * @param repo the repository to look up the reference * @param name the long name for the reference (e.g. HEAD, refs/heads/master, refs/tags/v0.1.0, ...) - * @return 0 or an error code (ENOTFOUND, EINVALIDSPEC) + * @return 0 on success, ENOTFOUND, EINVALIDSPEC or an error code. */ GIT_EXTERN(int) git_reference_lookup(git_reference **out, git_repository *repo, const char *name); @@ -43,11 +43,13 @@ GIT_EXTERN(int) git_reference_lookup(git_reference **out, git_repository *repo, * through to the object id that it refers to. This avoids having to * allocate or free any `git_reference` objects for simple situations. * + * The name will be checked for validity. + * See `git_reference_create_symbolic()` for rules about valid names. + * * @param out Pointer to oid to be filled in * @param repo The repository in which to look up the reference * @param name The long name for the reference - * @return 0 on success, -1 if name could not be resolved (EINVALIDSPEC, - * ENOTFOUND, etc) + * @return 0 on success, ENOTFOUND, EINVALIDSPEC or an error code. */ GIT_EXTERN(int) git_reference_name_to_id( git_oid *out, git_repository *repo, const char *name); @@ -79,7 +81,7 @@ GIT_EXTERN(int) git_reference_name_to_id( * @param name The name of the reference * @param target The target of the reference * @param force Overwrite existing references - * @return 0 or an error code (EEXISTS, EINVALIDSPEC) + * @return 0 on success, EEXISTS, EINVALIDSPEC or an error code */ GIT_EXTERN(int) git_reference_symbolic_create(git_reference **out, git_repository *repo, const char *name, const char *target, int force); @@ -111,7 +113,7 @@ GIT_EXTERN(int) git_reference_symbolic_create(git_reference **out, git_repositor * @param name The name of the reference * @param id The object id pointed to by the reference. * @param force Overwrite existing references - * @return 0 or an error code (EINVALIDSPEC, EEXISTS) + * @return 0 on success, EEXISTS, EINVALIDSPEC or an error code */ GIT_EXTERN(int) git_reference_create(git_reference **out, git_repository *repo, const char *name, const git_oid *id, int force); @@ -193,9 +195,12 @@ GIT_EXTERN(git_repository *) git_reference_owner(const git_reference *ref); * * The reference will be automatically updated in memory and on disk. * + * The target name will be checked for validity. + * See `git_reference_create_symbolic()` for rules about valid names. + * * @param ref The reference * @param target The new target for the reference - * @return 0 or an error code (EINVALIDSPEC) + * @return 0 on success, EINVALIDSPEC or an error code */ GIT_EXTERN(int) git_reference_symbolic_set_target(git_reference *ref, const char *target); @@ -216,8 +221,9 @@ GIT_EXTERN(int) git_reference_set_target(git_reference *ref, const git_oid *id); * Rename an existing reference. * * This method works for both direct and symbolic references. - * The new name will be checked for validity and may be - * modified into a normalized form. + * + * The new name will be checked for validity. + * See `git_reference_create_symbolic()` for rules about valid names. * * The given git_reference will be updated in place. * @@ -234,7 +240,7 @@ GIT_EXTERN(int) git_reference_set_target(git_reference *ref, const git_oid *id); * @param ref The reference to rename * @param name The new name for the reference * @param force Overwrite an existing reference - * @return 0 or an error code (EINVALIDSPEC, EEXISTS) + * @return 0 on success, EINVALIDSPEC, EEXISTS or an error code * */ GIT_EXTERN(int) git_reference_rename(git_reference *ref, const char *name, int force); @@ -381,7 +387,7 @@ GIT_EXTERN(int) git_reference_foreach_glob( git_repository *repo, const char *glob, unsigned int list_flags, - int (*callback)(const char *reference_name, void *payload), + git_reference_foreach_cb callback, void *payload); /** @@ -446,13 +452,15 @@ typedef enum { * Once normalized, if the reference name is valid, it will be returned in * the user allocated buffer. * + * See `git_reference_create_symbolic()` for rules about valid names. + * * @param buffer_out User allocated buffer to store normalized name * @param buffer_size Size of buffer_out * @param name Reference name to be checked. * @param flags Flags to constrain name validation rules - see the * GIT_REF_FORMAT constants above. - * @return 0 on success or error code (GIT_EBUFS if buffer is too small, -1 - * if reference is invalid) + * @return 0 on success, GIT_EBUFS if buffer is too small, EINVALIDSPEC + * or an error code. */ GIT_EXTERN(int) git_reference_normalize_name( char *buffer_out, @@ -471,8 +479,9 @@ GIT_EXTERN(int) git_reference_normalize_name( * * @param peeled Pointer to the peeled git_object * @param ref The reference to be processed - * @param target_type The type of the requested object - * @return 0 or an error code + * @param target_type The type of the requested object (GIT_OBJ_COMMIT, + * GIT_OBJ_TAG, GIT_OBJ_TREE, GIT_OBJ_BLOB or GIT_OBJ_ANY). + * @return 0 on success, GIT_EAMBIGUOUS, GIT_ENOTFOUND or an error code */ GIT_EXTERN(int) git_reference_peel( git_object **out, diff --git a/include/git2/remote.h b/include/git2/remote.h index 6c70d7fbc..af73ca8b3 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -39,30 +39,39 @@ typedef int (*git_remote_rename_problem_cb)(const char *problematic_refspec, voi * Create a remote with the default refspecs in memory. You can use * this when you have a URL instead of a remote's name. * + * The name, when provided, will be checked for validity. + * See `git_tag_create()` for rules about valid names. + * * @param out pointer to the new remote object * @param repo the associated repository - * @param name the remote's name + * @param name the optional remote's name * @param url the remote repository's URL * @param fetch the fetch refspec to use for this remote - * @return 0 or an error code + * @return 0, GIT_EINVALIDSPEC or an error code */ GIT_EXTERN(int) git_remote_new(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch); /** * Get the information for a particular remote * + * The name will be checked for validity. + * See `git_tag_create()` for rules about valid names. + * * @param out pointer to the new remote object * @param repo the associated repository * @param name the remote's name - * @return 0 or an error code + * @return 0, GIT_ENOTFOUND, GIT_EINVALIDSPEC or an error code */ GIT_EXTERN(int) git_remote_load(git_remote **out, git_repository *repo, const char *name); /** * Save a remote to its repository's configuration * + * One can't save a nameless inmemory remote. Doing so will + * result in a GIT_EINVALIDSPEC being returned. + * * @param remote the remote to save to config - * @return 0 or an error code + * @return 0, GIT_EINVALIDSPEC or an error code */ GIT_EXTERN(int) git_remote_save(const git_remote *remote); @@ -338,12 +347,16 @@ typedef enum git_remote_completion_type { * Set the calbacks to be called by the remote. */ struct git_remote_callbacks { + unsigned int version; void (*progress)(const char *str, int len, void *data); int (*completion)(git_remote_completion_type type, void *data); int (*update_tips)(const char *refname, const git_oid *a, const git_oid *b, void *data); void *payload; }; +#define GIT_REMOTE_CALLBACKS_VERSION 1 +#define GIT_REMOTE_CALLBACKS_INIT {GIT_REMOTE_CALLBACKS_VERSION} + /** * Set the callbacks for a remote * @@ -352,8 +365,9 @@ struct git_remote_callbacks { * * @param remote the remote to configure * @param callbacks a pointer to the user's callback settings + * @return 0 or an error code */ -GIT_EXTERN(void) git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks); +GIT_EXTERN(int) git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks); /** * Get the statistics structure that is filled in by the fetch operation. @@ -391,12 +405,15 @@ GIT_EXTERN(void) git_remote_set_autotag( * All remote-tracking branches and configuration settings * for the remote are updated. * + * The new name will be checked for validity. + * See `git_tag_create()` for rules about valid names. + * * @param remote the remote to rename * @param new_name the new name the remote should bear * @param callback Optional callback to notify the consumer of fetch refspecs * that haven't been automatically updated and need potential manual tweaking. * @param payload Additional data to pass to the callback - * @return 0 or an error code + * @return 0, GIT_EINVALIDSPEC or an error code */ GIT_EXTERN(int) git_remote_rename( git_remote *remote, diff --git a/include/git2/repository.h b/include/git2/repository.h index e91108a33..216f59b51 100644 --- a/include/git2/repository.h +++ b/include/git2/repository.h @@ -239,6 +239,7 @@ typedef enum { * will be added pointing to this URL. */ typedef struct { + unsigned int version; uint32_t flags; uint32_t mode; const char *workdir_path; @@ -248,6 +249,9 @@ typedef struct { const char *origin_url; } git_repository_init_options; +#define GIT_REPOSITORY_INIT_OPTIONS_VERSION 1 +#define GIT_REPOSITORY_INIT_OPTIONS_INIT {GIT_REPOSITORY_INIT_OPTIONS_VERSION} + /** * Create a new Git repository in the given folder with extended controls. * diff --git a/include/git2/revparse.h b/include/git2/revparse.h index 4567027e5..3df6fef7f 100644 --- a/include/git2/revparse.h +++ b/include/git2/revparse.h @@ -27,7 +27,8 @@ GIT_BEGIN_DECL * @param out pointer to output object * @param repo the repository to search in * @param spec the textual specification for an object - * @return on success, GIT_ERROR otherwise (use git_error_last for information about the error) + * @return 0 on success, GIT_ENOTFOUND, GIT_EAMBIGUOUS, + * GIT_EINVALIDSPEC or an error code */ GIT_EXTERN(int) git_revparse_single(git_object **out, git_repository *repo, const char *spec); diff --git a/include/git2/status.h b/include/git2/status.h index c6926f343..a898d1f34 100644 --- a/include/git2/status.h +++ b/include/git2/status.h @@ -164,6 +164,9 @@ typedef struct { git_strarray pathspec; } git_status_options; +#define GIT_STATUS_OPTIONS_VERSION 1 +#define GIT_STATUS_OPTIONS_INIT {GIT_STATUS_OPTIONS_VERSION} + /** * Gather file status information and run callbacks as requested. * diff --git a/include/git2/strarray.h b/include/git2/strarray.h index 030567978..338d13873 100644 --- a/include/git2/strarray.h +++ b/include/git2/strarray.h @@ -19,11 +19,10 @@ GIT_BEGIN_DECL /** Array of strings */ -typedef struct _git_strarray git_strarray; -struct _git_strarray { +typedef struct git_strarray { char **strings; size_t count; -}; +} git_strarray; /** * Close a string array object diff --git a/include/git2/tag.h b/include/git2/tag.h index f82a6c9f9..a9773be56 100644 --- a/include/git2/tag.h +++ b/include/git2/tag.h @@ -144,6 +144,10 @@ GIT_EXTERN(const char *) git_tag_message(const git_tag *tag); * The message will not be cleaned up. This can be achieved * through `git_message_prettify()`. * + * The tag name will be checked for validity. You must avoid + * the characters '~', '^', ':', '\\', '?', '[', and '*', and the + * sequences ".." and "@{" which have special meaning to revparse. + * * @param oid Pointer where to store the OID of the * newly created tag. If the tag already exists, this parameter * will be the oid of the existing tag, and the function will @@ -165,7 +169,7 @@ GIT_EXTERN(const char *) git_tag_message(const git_tag *tag); * * @param force Overwrite existing references * - * @return 0 or an error code + * @return 0 on success, GIT_EINVALIDSPEC or an error code * A tag object is written to the ODB, and a proper reference * is written in the /refs/tags folder, pointing to it */ @@ -200,6 +204,9 @@ GIT_EXTERN(int) git_tag_create_frombuffer( * this target object. If `force` is true and a reference * already exists with the given name, it'll be replaced. * + * The tag name will be checked for validity. + * See `git_tag_create()` for rules about valid names. + * * @param oid Pointer where to store the OID of the provided * target object. If the tag already exists, this parameter * will be filled with the oid of the existing pointed object @@ -216,7 +223,7 @@ GIT_EXTERN(int) git_tag_create_frombuffer( * * @param force Overwrite existing references * - * @return 0 or an error code + * @return 0 on success, GIT_EINVALIDSPEC or an error code * A proper reference is written in the /refs/tags folder, * pointing to the provided target object */ @@ -230,12 +237,15 @@ GIT_EXTERN(int) git_tag_create_lightweight( /** * Delete an existing tag reference. * + * The tag name will be checked for validity. + * See `git_tag_create()` for rules about valid names. + * * @param repo Repository where lives the tag * * @param tag_name Name of the tag to be deleted; * this name is validated for consistency. * - * @return 0 or an error code + * @return 0 on success, GIT_EINVALIDSPEC or an error code */ GIT_EXTERN(int) git_tag_delete( git_repository *repo, diff --git a/include/git2/transport.h b/include/git2/transport.h index 61726922f..00beb4472 100644 --- a/include/git2/transport.h +++ b/include/git2/transport.h @@ -82,6 +82,7 @@ typedef enum { typedef void (*git_transport_message_cb)(const char *str, int len, void *data); typedef struct git_transport { + unsigned int version; /* Set progress and error callbacks */ int (*set_callbacks)(struct git_transport *transport, git_transport_message_cb progress_cb, @@ -140,6 +141,9 @@ typedef struct git_transport { void (*free)(struct git_transport *transport); } git_transport; +#define GIT_TRANSPORT_VERSION 1 +#define GIT_TRANSPORT_INIT {GIT_TRANSPORT_VERSION} + /** * Function to use to create a transport from a URL. The transport database * is scanned to find a transport that implements the scheme of the URI (i.e. @@ -284,6 +288,7 @@ typedef int (*git_smart_subtransport_cb)( typedef struct git_smart_subtransport_definition { /* The function to use to create the git_smart_subtransport */ git_smart_subtransport_cb callback; + /* True if the protocol is stateless; false otherwise. For example, * http:// is stateless, but git:// is not. */ unsigned rpc : 1; diff --git a/src/checkout.c b/src/checkout.c index a3166bfa5..33de7adf3 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -203,21 +203,25 @@ static int checkout_blob( return error; } -static int retrieve_symlink_caps(git_repository *repo, bool *can_symlink) +static int retrieve_symlink_caps(git_repository *repo, bool *out) { git_config *cfg; + int can_symlink = 0; int error; if (git_repository_config__weakptr(&cfg, repo) < 0) return -1; - error = git_config_get_bool((int *)can_symlink, cfg, "core.symlinks"); + error = git_config_get_bool(&can_symlink, cfg, "core.symlinks"); /* If "core.symlinks" is not found anywhere, default to true. */ if (error == GIT_ENOTFOUND) { - *can_symlink = true; + can_symlink = true; error = 0; } + + if (error >= 0) + *out = can_symlink; return error; } @@ -228,7 +232,7 @@ static void normalize_options( assert(normalized); if (!proposed) - memset(normalized, 0, sizeof(git_checkout_opts)); + GIT_INIT_STRUCTURE(normalized, GIT_CHECKOUT_OPTS_VERSION); else memmove(normalized, proposed, sizeof(git_checkout_opts)); @@ -607,7 +611,7 @@ int git_checkout_index( git_checkout_opts *opts) { git_diff_list *diff = NULL; - git_diff_options diff_opts = {0}; + git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT; git_checkout_opts checkout_opts; checkout_diff_data data; git_buf workdir = GIT_BUF_INIT; @@ -617,6 +621,8 @@ int git_checkout_index( assert(repo); + GITERR_CHECK_VERSION(opts, GIT_CHECKOUT_OPTS_VERSION, "git_checkout_opts"); + if ((error = git_repository__ensure_not_bare(repo, "checkout")) < 0) return error; diff --git a/src/common.h b/src/common.h index a35239e3d..211e5b543 100644 --- a/src/common.h +++ b/src/common.h @@ -61,9 +61,38 @@ void giterr_set(int error_class, const char *string, ...); /** * Set the error message for a regex failure, using the internal regex - * error code lookup. + * error code lookup and return a libgit error code. */ -void giterr_set_regex(const regex_t *regex, int error_code); +int giterr_set_regex(const regex_t *regex, int error_code); + +/** + * Check a versioned structure for validity + */ +GIT_INLINE(int) giterr__check_version(const void *structure, unsigned int expected_max, const char *name) +{ + unsigned int actual; + + if (!structure) + return 0; + + actual = *(const unsigned int*)structure; + if (actual > 0 && actual <= expected_max) + return 0; + + giterr_set(GITERR_INVALID, "Invalid version %d on %s", actual, name); + return -1; +} +#define GITERR_CHECK_VERSION(S,V,N) if (giterr__check_version(S,V,N) < 0) return -1 + +/** + * Initialize a structure with a version. + */ +GIT_INLINE(void) git__init_structure(void *structure, size_t len, unsigned int version) +{ + memset(structure, 0, len); + *((int*)structure) = version; +} +#define GIT_INIT_STRUCTURE(S,V) git__init_structure(S, sizeof(*S), V) /* NOTE: other giterr functions are in the public errors.h header file */ diff --git a/src/config.c b/src/config.c index 6347f7df7..d422447cf 100644 --- a/src/config.c +++ b/src/config.c @@ -259,6 +259,8 @@ int git_config_add_backend( assert(cfg && file); + GITERR_CHECK_VERSION(file, GIT_CONFIG_BACKEND_VERSION, "git_config_backend"); + if ((result = file->open(file, level)) < 0) return result; diff --git a/src/config_cache.c b/src/config_cache.c index ca9602e56..244202351 100644 --- a/src/config_cache.c +++ b/src/config_cache.c @@ -24,7 +24,7 @@ struct map_data { * core.eol * Sets the line ending type to use in the working directory for * files that have the text property set. Alternatives are lf, crlf - * and native, which uses the platform’s native line ending. The default + * and native, which uses the platform's native line ending. The default * value is native. See gitattributes(5) for more information on * end-of-line conversion. */ diff --git a/src/config_file.c b/src/config_file.c index 354a91986..6e29832d4 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -545,10 +545,10 @@ int git_config_file__ondisk(git_config_backend **out, const char *path) { diskfile_backend *backend; - backend = git__malloc(sizeof(diskfile_backend)); + backend = git__calloc(1, sizeof(diskfile_backend)); GITERR_CHECK_ALLOC(backend); - memset(backend, 0x0, sizeof(diskfile_backend)); + backend->parent.version = GIT_CONFIG_BACKEND_VERSION; backend->file_path = git__strdup(path); GITERR_CHECK_ALLOC(backend->file_path); diff --git a/src/delta-apply.c b/src/delta-apply.c index 815ca8f16..85e2ef88f 100644 --- a/src/delta-apply.c +++ b/src/delta-apply.c @@ -36,6 +36,19 @@ static int hdr_sz( return 0; } +int git__delta_read_header( + const unsigned char *delta, + size_t delta_len, + size_t *base_sz, + size_t *res_sz) +{ + const unsigned char *delta_end = delta + delta_len; + if ((hdr_sz(base_sz, &delta, delta_end) < 0) || + (hdr_sz(res_sz, &delta, delta_end) < 0)) + return -1; + return 0; +} + int git__delta_apply( git_rawobj *out, const unsigned char *base, diff --git a/src/delta-apply.h b/src/delta-apply.h index 66fa76d43..9aea4ac9f 100644 --- a/src/delta-apply.h +++ b/src/delta-apply.h @@ -30,4 +30,21 @@ extern int git__delta_apply( const unsigned char *delta, size_t delta_len); +/** + * Read the header of a git binary delta. + * + * @param delta the delta to execute copy/insert instructions from. + * @param delta_len total number of bytes in the delta. + * @param base_sz pointer to store the base size field. + * @param res_sz pointer to store the result size field. + * @return + * - 0 on a successful decoding the header. + * - GIT_ERROR if the delta is corrupt. + */ +extern int git__delta_read_header( + const unsigned char *delta, + size_t delta_len, + size_t *base_sz, + size_t *res_sz); + #endif diff --git a/src/diff.c b/src/diff.c index 86f76f9c0..c4bfc3687 100644 --- a/src/diff.c +++ b/src/diff.c @@ -285,7 +285,7 @@ static git_diff_list *git_diff_list_alloc( goto fail; if (diff->opts.flags & GIT_DIFF_REVERSE) { - char *swap = diff->opts.old_prefix; + const char *swap = diff->opts.old_prefix; diff->opts.old_prefix = diff->opts.new_prefix; diff->opts.new_prefix = swap; } @@ -755,14 +755,14 @@ fail: return error; } - #define DIFF_FROM_ITERATORS(MAKE_FIRST, MAKE_SECOND) do { \ git_iterator *a = NULL, *b = NULL; \ char *pfx = opts ? git_pathspec_prefix(&opts->pathspec) : NULL; \ - if (!(error = MAKE_FIRST) && !(error = MAKE_SECOND)) \ + GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); \ + if (!(error = MAKE_FIRST) && !(error = MAKE_SECOND)) \ error = diff_from_iterators(diff, repo, a, b, opts); \ git__free(pfx); git_iterator_free(a); git_iterator_free(b); \ - } while (0) +} while (0) int git_diff_tree_to_tree( git_diff_list **diff, diff --git a/src/diff.h b/src/diff.h index 1e3be7593..f93bab18d 100644 --- a/src/diff.h +++ b/src/diff.h @@ -61,5 +61,6 @@ extern bool git_diff_delta__should_skip( extern int git_diff__oid_for_file( git_repository *, const char *, uint16_t, git_off_t, git_oid *); + #endif diff --git a/src/diff_output.c b/src/diff_output.c index e137fd0f2..b18255d58 100644 --- a/src/diff_output.c +++ b/src/diff_output.c @@ -1266,6 +1266,8 @@ int git_diff_blobs( git_diff_delta delta; git_diff_patch patch; + GITERR_CHECK_VERSION(options, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); + if (options && (options->flags & GIT_DIFF_REVERSE)) { git_blob *swap = old_blob; old_blob = new_blob; diff --git a/src/diff_tform.c b/src/diff_tform.c index 987d4b8e6..0c588594a 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -187,7 +187,8 @@ static int normalize_find_opts( if (given != NULL) memcpy(opts, given, sizeof(*opts)); else { - memset(opts, 0, sizeof(*opts)); + git_diff_find_options init = GIT_DIFF_FIND_OPTIONS_INIT; + memmove(opts, &init, sizeof(init)); opts->flags = GIT_DIFF_FIND_RENAMES; @@ -198,6 +199,8 @@ static int normalize_find_opts( opts->flags = GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES; } + GITERR_CHECK_VERSION(opts, GIT_DIFF_FIND_OPTIONS_VERSION, "git_diff_find_options"); + /* some flags imply others */ if (opts->flags & GIT_DIFF_FIND_RENAMES_FROM_REWRITES) diff --git a/src/errors.c b/src/errors.c index ac7fa934d..e62507216 100644 --- a/src/errors.c +++ b/src/errors.c @@ -93,11 +93,18 @@ void giterr_set_str(int error_class, const char *string) set_error(error_class, message); } -void giterr_set_regex(const regex_t *regex, int error_code) +int giterr_set_regex(const regex_t *regex, int error_code) { char error_buf[1024]; regerror(error_code, regex, error_buf, sizeof(error_buf)); giterr_set_str(GITERR_REGEX, error_buf); + + if (error_code == REG_NOMATCH) + return GIT_ENOTFOUND; + else if (error_code > REG_BADPAT) + return GIT_EINVALIDSPEC; + else + return -1; } void giterr_clear(void) diff --git a/src/iterator.c b/src/iterator.c index bd586ce99..0fdf0c69d 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -340,14 +340,6 @@ static int index_iterator__current( index_iterator *ii = (index_iterator *)self; const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current); - if (ie != NULL && - ii->base.end != NULL && - ITERATOR_PREFIXCMP(ii->base, ie->path, ii->base.end) > 0) - { - ii->current = git_index_entrycount(ii->index); - ie = NULL; - } - if (entry) *entry = ie; @@ -360,6 +352,29 @@ static int index_iterator__at_end(git_iterator *self) return (ii->current >= git_index_entrycount(ii->index)); } +static void index_iterator__skip_conflicts( + index_iterator *ii) +{ + size_t entrycount = git_index_entrycount(ii->index); + const git_index_entry *ie; + + while (ii->current < entrycount) { + ie = git_index_get_byindex(ii->index, ii->current); + + if (ie == NULL || + (ii->base.end != NULL && + ITERATOR_PREFIXCMP(ii->base, ie->path, ii->base.end) > 0)) { + ii->current = entrycount; + break; + } + + if (git_index_entry_stage(ie) == 0) + break; + + ii->current++; + } +} + static int index_iterator__advance( git_iterator *self, const git_index_entry **entry) { @@ -368,6 +383,8 @@ static int index_iterator__advance( if (ii->current < git_index_entrycount(ii->index)) ii->current++; + index_iterator__skip_conflicts(ii); + return index_iterator__current(self, entry); } @@ -382,7 +399,9 @@ static int index_iterator__seek(git_iterator *self, const char *prefix) static int index_iterator__reset(git_iterator *self) { index_iterator *ii = (index_iterator *)self; - ii->current = 0; + ii->current = ii->base.start ? + git_index__prefix_position(ii->index, ii->base.start) : 0; + index_iterator__skip_conflicts(ii); return 0; } @@ -406,7 +425,8 @@ int git_iterator_for_index_range( ii->index = index; ii->base.ignore_case = ii->index->ignore_case; - ii->current = start ? git_index__prefix_position(ii->index, start) : 0; + + index_iterator__reset((git_iterator *)ii); *iter = (git_iterator *)ii; diff --git a/src/notes.c b/src/notes.c index dd36cc2fe..f96b5b139 100644 --- a/src/notes.c +++ b/src/notes.c @@ -11,6 +11,7 @@ #include "refs.h" #include "config.h" #include "iterator.h" +#include "signature.h" static int find_subtree_in_current_level( git_tree **out, diff --git a/src/object.c b/src/object.c index f88c2ba50..d57b6468c 100644 --- a/src/object.c +++ b/src/object.c @@ -304,12 +304,6 @@ size_t git_object__size(git_otype type) return git_objects_table[type].size; } -static int peel_error(int error, const char* msg) -{ - giterr_set(GITERR_INVALID, "The given object cannot be peeled - %s", msg); - return error; -} - static int dereference_object(git_object **dereferenced, git_object *obj) { git_otype type = git_object_type(obj); @@ -322,22 +316,46 @@ static int dereference_object(git_object **dereferenced, git_object *obj) return git_tag_target(dereferenced, (git_tag*)obj); case GIT_OBJ_BLOB: - return peel_error(GIT_ERROR, "cannot dereference blob"); + return GIT_ENOTFOUND; case GIT_OBJ_TREE: - return peel_error(GIT_ERROR, "cannot dereference tree"); + return GIT_EAMBIGUOUS; default: - return peel_error(GIT_ENOTFOUND, "unexpected object type encountered"); + return GIT_EINVALIDSPEC; } } +static int peel_error(int error, const git_oid *oid, git_otype type) +{ + const char *type_name; + char hex_oid[GIT_OID_HEXSZ + 1]; + + type_name = git_object_type2string(type); + + git_oid_fmt(hex_oid, oid); + hex_oid[GIT_OID_HEXSZ] = '\0'; + + giterr_set(GITERR_OBJECT, "The git_object of id '%s' can not be " + "successfully peeled into a %s (git_otype=%i).", hex_oid, type_name, type); + + return error; +} + int git_object_peel( git_object **peeled, const git_object *object, git_otype target_type) { git_object *source, *deref = NULL; + int error; + + if (target_type != GIT_OBJ_TAG && + target_type != GIT_OBJ_COMMIT && + target_type != GIT_OBJ_TREE && + target_type != GIT_OBJ_BLOB && + target_type != GIT_OBJ_ANY) + return GIT_EINVALIDSPEC; assert(object && peeled); @@ -346,7 +364,7 @@ int git_object_peel( source = (git_object *)object; - while (!dereference_object(&deref, source)) { + while (!(error = dereference_object(&deref, source))) { if (source != object) git_object_free(source); @@ -371,6 +389,10 @@ int git_object_peel( git_object_free(source); git_object_free(deref); - return -1; + + if (error) + error = peel_error(error, git_object_id(object), target_type); + + return error; } @@ -369,6 +369,8 @@ static int add_backend_internal(git_odb *odb, git_odb_backend *backend, int prio assert(odb && backend); + GITERR_CHECK_VERSION(backend, GIT_ODB_BACKEND_VERSION, "git_odb_backend"); + /* Check if the backend is already owned by another ODB */ assert(!backend->odb || backend->odb == odb); diff --git a/src/odb_loose.c b/src/odb_loose.c index e2f1aec32..df86d903e 100644 --- a/src/odb_loose.c +++ b/src/odb_loose.c @@ -915,6 +915,7 @@ int git_odb_backend_loose( backend = git__calloc(1, sizeof(loose_backend)); GITERR_CHECK_ALLOC(backend); + backend->parent.version = GIT_ODB_BACKEND_VERSION; backend->objects_dir = git__strdup(objects_dir); GITERR_CHECK_ALLOC(backend->objects_dir); diff --git a/src/odb_pack.c b/src/odb_pack.c index 35bf1580d..b1a46c9ed 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -384,19 +384,18 @@ cleanup: * ***********************************************************/ -/* -int pack_backend__read_header(git_rawobj *obj, git_odb_backend *backend, const git_oid *oid) +static int pack_backend__read_header(size_t *len_p, git_otype *type_p, struct git_odb_backend *backend, const git_oid *oid) { - pack_location location; + struct git_pack_entry e; + int error; - assert(obj && backend && oid); + assert(len_p && type_p && backend && oid); - if (locate_packfile(&location, (struct pack_backend *)backend, oid) < 0) - return GIT_ENOTFOUND; + if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0) + return error; - return read_header_packed(obj, &location); + return git_packfile_resolve_header(len_p, type_p, e.p, e.offset); } -*/ static int pack_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid) { @@ -570,6 +569,7 @@ int git_odb_backend_one_pack(git_odb_backend **backend_out, const char *idx) backend = git__calloc(1, sizeof(struct pack_backend)); GITERR_CHECK_ALLOC(backend); + backend->parent.version = GIT_ODB_BACKEND_VERSION; if (git_vector_init(&backend->packs, 1, NULL) < 0) goto on_error; @@ -579,7 +579,7 @@ int git_odb_backend_one_pack(git_odb_backend **backend_out, const char *idx) backend->parent.read = &pack_backend__read; backend->parent.read_prefix = &pack_backend__read_prefix; - backend->parent.read_header = NULL; + backend->parent.read_header = &pack_backend__read_header; backend->parent.exists = &pack_backend__exists; backend->parent.foreach = &pack_backend__foreach; backend->parent.free = &pack_backend__free; @@ -602,6 +602,7 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) backend = git__calloc(1, sizeof(struct pack_backend)); GITERR_CHECK_ALLOC(backend); + backend->parent.version = GIT_ODB_BACKEND_VERSION; if (git_vector_init(&backend->packs, 8, packfile_sort__cb) < 0 || git_buf_joinpath(&path, objects_dir, "pack") < 0) @@ -616,7 +617,7 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) backend->parent.read = &pack_backend__read; backend->parent.read_prefix = &pack_backend__read_prefix; - backend->parent.read_header = NULL; + backend->parent.read_header = &pack_backend__read_header; backend->parent.exists = &pack_backend__exists; backend->parent.foreach = &pack_backend__foreach; backend->parent.writepack = &pack_backend__writepack; diff --git a/src/pack.c b/src/pack.c index 01531d631..520e13828 100644 --- a/src/pack.c +++ b/src/pack.c @@ -277,6 +277,56 @@ int git_packfile_unpack_header( return 0; } +int git_packfile_resolve_header( + size_t *size_p, + git_otype *type_p, + struct git_pack_file *p, + git_off_t offset) +{ + git_mwindow *w_curs = NULL; + git_off_t curpos = offset; + size_t size; + git_otype type; + git_off_t base_offset; + int error; + + error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos); + git_mwindow_close(&w_curs); + if (error < 0) + return error; + + if (type == GIT_OBJ_OFS_DELTA || type == GIT_OBJ_REF_DELTA) { + size_t base_size; + git_rawobj delta; + base_offset = get_delta_base(p, &w_curs, &curpos, type, offset); + git_mwindow_close(&w_curs); + error = packfile_unpack_compressed(&delta, p, &w_curs, &curpos, size, type); + git_mwindow_close(&w_curs); + if (error < 0) + return error; + error = git__delta_read_header(delta.data, delta.len, &base_size, size_p); + git__free(delta.data); + if (error < 0) + return error; + } else + *size_p = size; + + while (type == GIT_OBJ_OFS_DELTA || type == GIT_OBJ_REF_DELTA) { + curpos = base_offset; + error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos); + git_mwindow_close(&w_curs); + if (error < 0) + return error; + if (type != GIT_OBJ_OFS_DELTA && type != GIT_OBJ_REF_DELTA) + break; + base_offset = get_delta_base(p, &w_curs, &curpos, type, base_offset); + git_mwindow_close(&w_curs); + } + *type_p = type; + + return error; +} + static int packfile_unpack_delta( git_rawobj *obj, struct git_pack_file *p, diff --git a/src/pack.h b/src/pack.h index 3355cd21f..188ea2bbd 100644 --- a/src/pack.h +++ b/src/pack.h @@ -93,6 +93,12 @@ int git_packfile_unpack_header( git_mwindow **w_curs, git_off_t *curpos); +int git_packfile_resolve_header( + size_t *size_p, + git_otype *type_p, + struct git_pack_file *p, + git_off_t offset); + int git_packfile_unpack(git_rawobj *obj, struct git_pack_file *p, git_off_t *obj_offset); int packfile_unpack_compressed( git_rawobj *obj, diff --git a/src/reflog.c b/src/reflog.c index ac481fb81..df799b113 100644 --- a/src/reflog.c +++ b/src/reflog.c @@ -340,21 +340,29 @@ cleanup: int git_reflog_rename(git_reference *ref, const char *new_name) { - int error = -1, fd; + int error, fd; git_buf old_path = GIT_BUF_INIT; git_buf new_path = GIT_BUF_INIT; git_buf temp_path = GIT_BUF_INIT; + git_buf normalized = GIT_BUF_INIT; assert(ref && new_name); + if ((error = git_reference__normalize_name( + &normalized, new_name, GIT_REF_FORMAT_ALLOW_ONELEVEL)) < 0) + goto cleanup; + + error = -1; + if (git_buf_joinpath(&temp_path, git_reference_owner(ref)->path_repository, GIT_REFLOG_DIR) < 0) return -1; if (git_buf_joinpath(&old_path, git_buf_cstr(&temp_path), ref->name) < 0) goto cleanup; - if (git_buf_joinpath(&new_path, git_buf_cstr(&temp_path), new_name) < 0) - goto cleanup; + if (git_buf_joinpath(&new_path, + git_buf_cstr(&temp_path), git_buf_cstr(&normalized)) < 0) + goto cleanup; /* * Move the reflog to a temporary place. This two-phase renaming is required @@ -386,6 +394,7 @@ cleanup: git_buf_free(&temp_path); git_buf_free(&old_path); git_buf_free(&new_path); + git_buf_free(&normalized); return error; } diff --git a/src/refs.c b/src/refs.c index 76c9f42ba..85813096b 100644 --- a/src/refs.c +++ b/src/refs.c @@ -1215,11 +1215,11 @@ int git_reference_symbolic_create( git_reference *ref = NULL; int error; - if (git_reference__normalize_name_lax( + if ((error = git_reference__normalize_name_lax( normalized, sizeof(normalized), - name) < 0) - return -1; + name)) < 0) + return error; if ((error = reference_can_write(repo, normalized, NULL, force)) < 0) return error; @@ -1255,11 +1255,11 @@ int git_reference_create( git_reference *ref = NULL; char normalized[GIT_REFNAME_MAX]; - if (git_reference__normalize_name_lax( + if ((error = git_reference__normalize_name_lax( normalized, sizeof(normalized), - name) < 0) - return -1; + name)) < 0) + return error; if ((error = reference_can_write(repo, normalized, NULL, force)) < 0) return error; @@ -1330,6 +1330,7 @@ int git_reference_set_target(git_reference *ref, const git_oid *id) */ int git_reference_symbolic_set_target(git_reference *ref, const char *target) { + int error; char normalized[GIT_REFNAME_MAX]; if ((ref->flags & GIT_REF_SYMBOLIC) == 0) { @@ -1338,11 +1339,11 @@ int git_reference_symbolic_set_target(git_reference *ref, const char *target) return -1; } - if (git_reference__normalize_name_lax( + if ((error = git_reference__normalize_name_lax( normalized, sizeof(normalized), - target)) - return -1; + target)) < 0) + return error; git__free(ref->target.symbolic); ref->target.symbolic = git__strdup(normalized); @@ -1363,12 +1364,12 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force) GIT_REF_FORMAT_ALLOW_ONELEVEL : GIT_REF_FORMAT_NORMAL; - if (git_reference_normalize_name( + if ((result = git_reference_normalize_name( normalized, sizeof(normalized), new_name, - normalization_flags) < 0) - return -1; + normalization_flags)) < 0) + return result; if ((result = reference_can_write(ref->owner, normalized, ref->name, force)) < 0) return result; @@ -1645,7 +1646,7 @@ int git_reference__normalize_name( // Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100 char *current; - int segment_len, segments_count = 0, error = -1; + int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC; unsigned int process_flags; bool normalize = (buf != NULL); assert(name); @@ -1677,8 +1678,10 @@ int git_reference__normalize_name( git_buf_truncate(buf, cur_len + segment_len + (segments_count ? 1 : 0)); - if (git_buf_oom(buf)) + if (git_buf_oom(buf)) { + error = -1; goto cleanup; + } } segments_count++; @@ -1721,7 +1724,7 @@ int git_reference__normalize_name( error = 0; cleanup: - if (error) + if (error == GIT_EINVALIDSPEC) giterr_set( GITERR_REFERENCE, "The given reference name '%s' is not valid", name); @@ -1962,8 +1965,12 @@ int git_reference__is_valid_name( const char *refname, unsigned int flags) { + int error; + + error = git_reference__normalize_name(NULL, refname, flags) == 0; giterr_clear(); - return git_reference__normalize_name(NULL, refname, flags) == 0; + + return error; } int git_reference_is_valid_name( diff --git a/src/remote.c b/src/remote.c index c84911aa1..5b75e510c 100644 --- a/src/remote.c +++ b/src/remote.c @@ -57,6 +57,32 @@ static int download_tags_value(git_remote *remote, git_config *cfg) return error; } +static int ensure_remote_name_is_valid(const char *name) +{ + git_buf buf = GIT_BUF_INIT; + git_refspec refspec; + int error = -1; + + if (!name || *name == '\0') + goto cleanup; + + git_buf_printf(&buf, "refs/heads/test:refs/remotes/%s/test", name); + error = git_refspec__parse(&refspec, git_buf_cstr(&buf), true); + + git_buf_free(&buf); + git_refspec__free(&refspec); + +cleanup: + if (error) { + giterr_set( + GITERR_CONFIG, + "'%s' is not a valid remote name.", name); + error = GIT_EINVALIDSPEC; + } + + return error; +} + int git_remote_new(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch) { git_remote *remote; @@ -79,6 +105,12 @@ int git_remote_new(git_remote **out, git_repository *repo, const char *name, con GITERR_CHECK_ALLOC(remote->url); if (name != NULL) { + int error; + if ((error = ensure_remote_name_is_valid(name)) < 0) { + git_remote_free(remote); + return error; + } + remote->name = git__strdup(name); GITERR_CHECK_ALLOC(remote->name); } @@ -111,6 +143,9 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name) assert(out && repo && name); + if ((error = ensure_remote_name_is_valid(name)) < 0) + return error; + if (git_repository_config__weakptr(&config, repo) < 0) return -1; @@ -212,30 +247,6 @@ cleanup: return error; } -static int ensure_remote_name_is_valid(const char *name) -{ - git_buf buf = GIT_BUF_INIT; - git_refspec refspec; - int error = -1; - - if (!name || *name == '\0') - goto cleanup; - - git_buf_printf(&buf, "refs/heads/test:refs/remotes/%s/test", name); - error = git_refspec__parse(&refspec, git_buf_cstr(&buf), true); - - git_buf_free(&buf); - git_refspec__free(&refspec); - -cleanup: - if (error) - giterr_set( - GITERR_CONFIG, - "'%s' is not a valid remote name.", name); - - return error; -} - static int update_config_refspec( git_config *config, const char *remote_name, @@ -279,8 +290,8 @@ int git_remote_save(const git_remote *remote) assert(remote); - if (ensure_remote_name_is_valid(remote->name) < 0) - return -1; + if ((error = ensure_remote_name_is_valid(remote->name)) < 0) + return error; if (git_repository_config__weakptr(&config, remote->repo) < 0) return -1; @@ -958,6 +969,10 @@ int git_remote_list(git_strarray *remotes_list, git_repository *repo) int git_remote_add(git_remote **out, git_repository *repo, const char *name, const char *url) { git_buf buf = GIT_BUF_INIT; + int error; + + if ((error = ensure_remote_name_is_valid(name)) < 0) + return error; if (git_buf_printf(&buf, "+refs/heads/*:refs/remotes/%s/*", name) < 0) return -1; @@ -985,10 +1000,12 @@ void git_remote_check_cert(git_remote *remote, int check) remote->check_cert = check; } -void git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks) +int git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks) { assert(remote && callbacks); + GITERR_CHECK_VERSION(callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); + memcpy(&remote->callbacks, callbacks, sizeof(git_remote_callbacks)); if (remote->transport && remote->transport->set_callbacks) @@ -996,6 +1013,8 @@ void git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callback remote->callbacks.progress, NULL, remote->callbacks.payload); + + return 0; } void git_remote_set_cred_acquire_cb( @@ -1011,6 +1030,8 @@ int git_remote_set_transport(git_remote *remote, git_transport *transport) { assert(remote && transport); + GITERR_CHECK_VERSION(transport, GIT_TRANSPORT_VERSION, "git_transport"); + if (remote->transport) { giterr_set(GITERR_NET, "A transport is already bound to this remote"); return -1; diff --git a/src/repository.c b/src/repository.c index b49b49b7a..10ed12b64 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1151,9 +1151,8 @@ static int repo_init_create_origin(git_repository *repo, const char *url) int git_repository_init( git_repository **repo_out, const char *path, unsigned is_bare) { - git_repository_init_options opts; + git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; - memset(&opts, 0, sizeof(opts)); opts.flags = GIT_REPOSITORY_INIT_MKPATH; /* don't love this default */ if (is_bare) opts.flags |= GIT_REPOSITORY_INIT_BARE; @@ -1171,6 +1170,8 @@ int git_repository_init_ext( assert(out && given_repo && opts); + GITERR_CHECK_VERSION(opts, GIT_REPOSITORY_INIT_OPTIONS_VERSION, "git_repository_init_options"); + error = repo_init_directories(&repo_path, &wd_path, given_repo, opts); if (error < 0) goto cleanup; diff --git a/src/reset.c b/src/reset.c index d410a8806..17b4b900c 100644 --- a/src/reset.c +++ b/src/reset.c @@ -69,7 +69,7 @@ int git_reset( git_index *index = NULL; git_tree *tree = NULL; int error = -1; - git_checkout_opts opts; + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; assert(repo && target); assert(reset_type == GIT_RESET_SOFT @@ -136,7 +136,6 @@ int git_reset( goto cleanup; } - memset(&opts, 0, sizeof(opts)); opts.checkout_strategy = GIT_CHECKOUT_FORCE; if (git_checkout_index(repo, NULL, &opts) < 0) { diff --git a/src/revparse.c b/src/revparse.c index 308b92923..ade03d0e4 100644 --- a/src/revparse.c +++ b/src/revparse.c @@ -13,12 +13,6 @@ #include "git2.h" -static int revspec_error(const char *revspec) -{ - giterr_set(GITERR_INVALID, "Failed to parse revision specifier - Invalid pattern '%s'", revspec); - return -1; -} - static int disambiguate_refname(git_reference **out, git_repository *repo, const char *refname) { int error, i; @@ -51,7 +45,7 @@ static int disambiguate_refname(git_reference **out, git_repository *repo, const goto cleanup; if (!git_reference_is_valid_name(git_buf_cstr(&refnamebuf))) { - error = GIT_ENOTFOUND; + error = GIT_EINVALIDSPEC; continue; } @@ -90,17 +84,18 @@ static int build_regex(regex_t *regex, const char *pattern) if (*pattern == '\0') { giterr_set(GITERR_REGEX, "Empty pattern"); - return -1; + return GIT_EINVALIDSPEC; } error = regcomp(regex, pattern, REG_EXTENDED); if (!error) return 0; - giterr_set_regex(regex, error); + error = giterr_set_regex(regex, error); + regfree(regex); - return -1; + return error; } static int maybe_describe(git_object**out, git_repository *repo, const char *spec) @@ -174,7 +169,7 @@ static int try_parse_numeric(int *n, const char *curly_braces_content) return 0; } -static int retrieve_previously_checked_out_branch_or_revision(git_object **out, git_reference **base_ref, git_repository *repo, const char *spec, const char *identifier, size_t position) +static int retrieve_previously_checked_out_branch_or_revision(git_object **out, git_reference **base_ref, git_repository *repo, const char *identifier, size_t position) { git_reference *ref = NULL; git_reflog *reflog = NULL; @@ -189,7 +184,7 @@ static int retrieve_previously_checked_out_branch_or_revision(git_object **out, cur = position; if (*identifier != '\0' || *base_ref != NULL) - return revspec_error(spec); + return GIT_EINVALIDSPEC; if (build_regex(&preg, "checkout: moving from (.*) to .*") < 0) return -1; @@ -332,6 +327,11 @@ static int retrieve_remote_tracking_reference(git_reference **base_ref, const ch *base_ref = NULL; } + if (!git_reference_is_branch(ref)) { + error = GIT_EINVALIDSPEC; + goto cleanup; + } + if ((error = git_branch_tracking(&tracking, ref)) < 0) goto cleanup; @@ -357,13 +357,13 @@ static int handle_at_syntax(git_object **out, git_reference **ref, const char *s is_numeric = !try_parse_numeric(&parsed, curly_braces_content); if (*curly_braces_content == '-' && (!is_numeric || parsed == 0)) { - error = revspec_error(spec); + error = GIT_EINVALIDSPEC; goto cleanup; } if (is_numeric) { if (parsed < 0) - error = retrieve_previously_checked_out_branch_or_revision(out, ref, repo, spec, git_buf_cstr(&identifier), -parsed); + error = retrieve_previously_checked_out_branch_or_revision(out, ref, repo, git_buf_cstr(&identifier), -parsed); else error = retrieve_revobject_from_reflog(out, ref, repo, git_buf_cstr(&identifier), parsed); @@ -416,8 +416,9 @@ static int handle_caret_parent_syntax(git_object **out, git_object *obj, int n) git_object *temp_commit = NULL; int error; - if (git_object_peel(&temp_commit, obj, GIT_OBJ_COMMIT) < 0) - return -1; + if ((error = git_object_peel(&temp_commit, obj, GIT_OBJ_COMMIT)) < 0) + return (error == GIT_EAMBIGUOUS || error == GIT_ENOTFOUND) ? + GIT_EINVALIDSPEC : error; if (n == 0) { *out = temp_commit; @@ -435,8 +436,9 @@ static int handle_linear_syntax(git_object **out, git_object *obj, int n) git_object *temp_commit = NULL; int error; - if (git_object_peel(&temp_commit, obj, GIT_OBJ_COMMIT) < 0) - return -1; + if ((error = git_object_peel(&temp_commit, obj, GIT_OBJ_COMMIT)) < 0) + return (error == GIT_EAMBIGUOUS || error == GIT_ENOTFOUND) ? + GIT_EINVALIDSPEC : error; error = git_commit_nth_gen_ancestor((git_commit **)out, (git_commit*)temp_commit, n); @@ -453,8 +455,8 @@ static int handle_colon_syntax( int error = -1; git_tree_entry *entry = NULL; - if (git_object_peel(&tree, obj, GIT_OBJ_TREE) < 0) - return -1; + if ((error = git_object_peel(&tree, obj, GIT_OBJ_TREE)) < 0) + return error == GIT_ENOTFOUND ? GIT_EINVALIDSPEC : error; if (*path == '\0') { *out = tree; @@ -507,21 +509,21 @@ static int handle_grep_syntax(git_object **out, git_repository *repo, const git_ { regex_t preg; git_revwalk *walk = NULL; - int error = -1; + int error; - if (build_regex(&preg, pattern) < 0) - return -1; + if ((error = build_regex(&preg, pattern)) < 0) + return error; - if (git_revwalk_new(&walk, repo) < 0) + if ((error = git_revwalk_new(&walk, repo)) < 0) goto cleanup; git_revwalk_sorting(walk, GIT_SORT_TIME); if (spec_oid == NULL) { // TODO: @carlosmn: The glob should be refs/* but this makes git_revwalk_next() fails - if (git_revwalk_push_glob(walk, GIT_REFS_HEADS_DIR "*") < 0) + if ((error = git_revwalk_push_glob(walk, GIT_REFS_HEADS_DIR "*")) < 0) goto cleanup; - } else if (git_revwalk_push(walk, spec_oid) < 0) + } else if ((error = git_revwalk_push(walk, spec_oid)) < 0) goto cleanup; error = walk_and_search(out, walk, &preg); @@ -546,7 +548,7 @@ static int handle_caret_curly_syntax(git_object **out, git_object *obj, const ch expected_type = parse_obj_type(curly_braces_content); if (expected_type == GIT_OBJ_BAD) - return -1; + return GIT_EINVALIDSPEC; return git_object_peel(out, obj, expected_type); } @@ -560,13 +562,13 @@ static int extract_curly_braces_content(git_buf *buf, const char *spec, size_t * (*pos)++; if (spec[*pos] == '\0' || spec[*pos] != '{') - return revspec_error(spec); + return GIT_EINVALIDSPEC; (*pos)++; while (spec[*pos] != '}') { if (spec[*pos] == '\0') - return revspec_error(spec); + return GIT_EINVALIDSPEC; git_buf_putc(buf, spec[(*pos)++]); } @@ -610,7 +612,7 @@ static int extract_how_many(int *n, const char *spec, size_t *pos) if (git__isdigit(spec[*pos])) { if ((git__strtol32(&parsed, spec + *pos, &end_ptr, 10) < 0) < 0) - return revspec_error(spec); + return GIT_EINVALIDSPEC; accumulated += (parsed - 1); *pos = end_ptr - spec; @@ -655,7 +657,7 @@ static int ensure_base_rev_loaded(git_object **object, git_reference **reference } if (!allow_empty_identifier && identifier_len == 0) - return revspec_error(spec); + return GIT_EINVALIDSPEC; if (git_buf_put(&identifier, spec, identifier_len) < 0) return -1; @@ -666,12 +668,12 @@ static int ensure_base_rev_loaded(git_object **object, git_reference **reference return error; } -static int ensure_base_rev_is_not_known_yet(git_object *object, const char *spec) +static int ensure_base_rev_is_not_known_yet(git_object *object) { if (object == NULL) return 0; - return revspec_error(spec); + return GIT_EINVALIDSPEC; } static bool any_left_hand_identifier(git_object *object, git_reference *reference, size_t identifier_len) @@ -688,12 +690,12 @@ static bool any_left_hand_identifier(git_object *object, git_reference *referenc return false; } -static int ensure_left_hand_identifier_is_not_known_yet(git_object *object, git_reference *reference, const char *spec) +static int ensure_left_hand_identifier_is_not_known_yet(git_object *object, git_reference *reference) { - if (!ensure_base_rev_is_not_known_yet(object, spec) && reference == NULL) + if (!ensure_base_rev_is_not_known_yet(object) && reference == NULL) return 0; - return revspec_error(spec); + return GIT_EINVALIDSPEC; } int git_revparse_single(git_object **out, git_repository *repo, const char *spec) @@ -800,7 +802,7 @@ int git_revparse_single(git_object **out, git_repository *repo, const char *spec if ((error = extract_curly_braces_content(&buf, spec, &pos)) < 0) goto cleanup; - if ((error = ensure_base_rev_is_not_known_yet(base_rev, spec)) < 0) + if ((error = ensure_base_rev_is_not_known_yet(base_rev)) < 0) goto cleanup; if ((error = handle_at_syntax(&temp_object, &reference, spec, identifier_len, repo, git_buf_cstr(&buf))) < 0) @@ -815,7 +817,7 @@ int git_revparse_single(git_object **out, git_repository *repo, const char *spec } default: - if ((error = ensure_left_hand_identifier_is_not_known_yet(base_rev, reference, spec)) < 0) + if ((error = ensure_left_hand_identifier_is_not_known_yet(base_rev, reference)) < 0) goto cleanup; pos++; @@ -830,8 +832,13 @@ int git_revparse_single(git_object **out, git_repository *repo, const char *spec error = 0; cleanup: - if (error) + if (error) { + if (error == GIT_EINVALIDSPEC) + giterr_set(GITERR_INVALID, + "Failed to parse revision specifier - Invalid pattern '%s'", spec); + git_object_free(base_rev); + } git_reference_free(reference); git_buf_free(&buf); return error; diff --git a/src/signature.c b/src/signature.c index 0159488a4..7d043e6cf 100644 --- a/src/signature.c +++ b/src/signature.c @@ -264,7 +264,7 @@ int git_signature__parse(git_signature *sig, const char **buffer_out, const char *line_end, *name_end, *email_end, *tz_start, *time_start; int error = 0; - memset(sig, 0x0, sizeof(git_signature)); + memset(sig, 0, sizeof(git_signature)); if ((line_end = memchr(buffer, ender, buffer_end - buffer)) == NULL) return signature_error("no newline given"); diff --git a/src/stash.c b/src/stash.c index 107cbe3ca..e32d8fa31 100644 --- a/src/stash.c +++ b/src/stash.c @@ -14,6 +14,7 @@ #include "git2/stash.h" #include "git2/status.h" #include "git2/checkout.h" +#include "signature.h" static int create_error(int error, const char *msg) { @@ -229,7 +230,7 @@ static int build_untracked_tree( { git_tree *i_tree = NULL; git_diff_list *diff = NULL; - git_diff_options opts = {0}; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; struct cb_data data = {0}; int error = -1; @@ -315,7 +316,7 @@ static int build_workdir_tree( git_repository *repo = git_index_owner(index); git_tree *b_tree = NULL; git_diff_list *diff = NULL, *diff2 = NULL; - git_diff_options opts = {0}; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; struct cb_data data = {0}; int error = -1; @@ -471,9 +472,8 @@ static int ensure_there_are_changes_to_stash( bool include_ignored_files) { int error; - git_status_options opts; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; - memset(&opts, 0, sizeof(opts)); opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; if (include_untracked_files) opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | @@ -498,9 +498,7 @@ static int reset_index_and_workdir( git_commit *commit, bool remove_untracked) { - git_checkout_opts opts; - - memset(&opts, 0, sizeof(git_checkout_opts)); + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; opts.checkout_strategy = GIT_CHECKOUT_UPDATE_MODIFIED | GIT_CHECKOUT_UPDATE_UNTRACKED; diff --git a/src/status.c b/src/status.c index c7dea2c71..1ad835adb 100644 --- a/src/status.c +++ b/src/status.c @@ -108,7 +108,7 @@ int git_status_foreach_ext( void *payload) { int err = 0; - git_diff_options diffopt; + git_diff_options diffopt = GIT_DIFF_OPTIONS_INIT; git_diff_list *idx2head = NULL, *wd2idx = NULL; git_tree *head = NULL; git_status_show_t show = @@ -117,6 +117,8 @@ int git_status_foreach_ext( assert(show <= GIT_STATUS_SHOW_INDEX_THEN_WORKDIR); + GITERR_CHECK_VERSION(opts, GIT_STATUS_OPTIONS_VERSION, "git_status_options"); + if (show != GIT_STATUS_SHOW_INDEX_ONLY && (err = git_repository__ensure_not_bare(repo, "status")) < 0) return err; @@ -126,7 +128,6 @@ int git_status_foreach_ext( !(err == GIT_ENOTFOUND || err == GIT_EORPHANEDHEAD)) return err; - memset(&diffopt, 0, sizeof(diffopt)); memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec)); diffopt.flags = GIT_DIFF_INCLUDE_TYPECHANGE; @@ -181,9 +182,8 @@ int git_status_foreach( git_status_cb callback, void *payload) { - git_status_options opts; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; - memset(&opts, 0, sizeof(opts)); opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED | GIT_STATUS_OPT_INCLUDE_UNTRACKED | @@ -224,16 +224,14 @@ int git_status_file( const char *path) { int error; - git_status_options opts; - struct status_file_info sfi; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + struct status_file_info sfi = {0}; assert(status_flags && repo && path); - memset(&sfi, 0, sizeof(sfi)); if ((sfi.expected = git__strdup(path)) == NULL) return -1; - memset(&opts, 0, sizeof(opts)); opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED | GIT_STATUS_OPT_INCLUDE_UNTRACKED | diff --git a/src/submodule.c b/src/submodule.c index c117255d4..21a1875c2 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -205,7 +205,7 @@ int git_submodule_add_setup( git_config_backend *mods = NULL; git_submodule *sm; git_buf name = GIT_BUF_INIT, real_url = GIT_BUF_INIT; - git_repository_init_options initopt; + git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT; git_repository *subrepo = NULL; assert(repo && url && path); @@ -275,7 +275,6 @@ int git_submodule_add_setup( * Old style: sub-repo goes directly into repo/<name>/.git/ */ - memset(&initopt, 0, sizeof(initopt)); initopt.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_NO_REINIT; initopt.origin_url = real_url.ptr; @@ -1439,7 +1438,7 @@ static int submodule_wd_status(unsigned int *status, git_submodule *sm) if (sm_repo != NULL) { git_tree *sm_head; - git_diff_options opt; + git_diff_options opt = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff; /* the diffs below could be optimized with an early termination @@ -1452,7 +1451,6 @@ static int submodule_wd_status(unsigned int *status, git_submodule *sm) if ((error = git_repository_head_tree(&sm_head, sm_repo)) < 0) return error; - memset(&opt, 0, sizeof(opt)); if (sm->ignore == GIT_SUBMODULE_IGNORE_NONE) opt.flags |= GIT_DIFF_INCLUDE_UNTRACKED; @@ -251,7 +251,7 @@ static int git_tag_create__internal( error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag_name); if (error < 0 && error != GIT_ENOTFOUND) - return -1; + goto cleanup; /** Ensure the tag name doesn't conflict with an already existing * reference unless overwriting has explictly been requested **/ @@ -269,6 +269,7 @@ static int git_tag_create__internal( error = git_reference_create(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite); +cleanup: git_reference_free(new_ref); git_buf_free(&ref_name); return error; @@ -384,7 +385,7 @@ int git_tag_delete(git_repository *repo, const char *tag_name) git_buf_free(&ref_name); if (error < 0) - return -1; + return error; return git_reference_delete(tag_ref); } diff --git a/src/transports/local.c b/src/transports/local.c index 62e8024b5..768daf3a8 100644 --- a/src/transports/local.c +++ b/src/transports/local.c @@ -403,11 +403,10 @@ int git_transport_local(git_transport **out, git_remote *owner, void *param) GIT_UNUSED(param); - t = git__malloc(sizeof(transport_local)); + t = git__calloc(1, sizeof(transport_local)); GITERR_CHECK_ALLOC(t); - memset(t, 0x0, sizeof(transport_local)); - + t->parent.version = GIT_TRANSPORT_VERSION; t->parent.connect = local_connect; t->parent.negotiate_fetch = local_negotiate_fetch; t->parent.download_pack = local_download_pack; diff --git a/src/transports/smart.c b/src/transports/smart.c index 94d389b52..5300a47c8 100644 --- a/src/transports/smart.c +++ b/src/transports/smart.c @@ -303,6 +303,7 @@ int git_transport_smart(git_transport **out, git_remote *owner, void *param) t = git__calloc(sizeof(transport_smart), 1); GITERR_CHECK_ALLOC(t); + t->parent.version = GIT_TRANSPORT_VERSION; t->parent.set_callbacks = git_smart__set_callbacks; t->parent.connect = git_smart__connect; t->parent.close = git_smart__close; diff --git a/src/tree.c b/src/tree.c index fedf4b604..efb991df1 100644 --- a/src/tree.c +++ b/src/tree.c @@ -18,12 +18,33 @@ static bool valid_filemode(const int filemode) { return (filemode == GIT_FILEMODE_TREE || filemode == GIT_FILEMODE_BLOB - || filemode == GIT_FILEMODE_BLOB_GROUP_WRITABLE || filemode == GIT_FILEMODE_BLOB_EXECUTABLE || filemode == GIT_FILEMODE_LINK || filemode == GIT_FILEMODE_COMMIT); } +GIT_INLINE(git_filemode_t) normalize_filemode(git_filemode_t filemode) +{ + /* Tree bits set, but it's not a commit */ + if (filemode & GIT_FILEMODE_TREE && !(filemode & 0100000)) + return GIT_FILEMODE_TREE; + + /* If any of the x bits is set */ + if (filemode & 0111) + return GIT_FILEMODE_BLOB_EXECUTABLE; + + /* 16XXXX means commit */ + if ((filemode & GIT_FILEMODE_COMMIT) == GIT_FILEMODE_COMMIT) + return GIT_FILEMODE_COMMIT; + + /* 12XXXX means commit */ + if ((filemode & GIT_FILEMODE_LINK) == GIT_FILEMODE_LINK) + return GIT_FILEMODE_LINK; + + /* Otherwise, return a blob */ + return GIT_FILEMODE_BLOB; +} + static int valid_entry_name(const char *filename) { return *filename != '\0' && @@ -320,10 +341,11 @@ static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buf git_tree_entry *entry; int attr; - if (git__strtol32(&attr, buffer, &buffer, 8) < 0 || - !buffer || !valid_filemode(attr)) + if (git__strtol32(&attr, buffer, &buffer, 8) < 0 || !buffer) return tree_error("Failed to parse tree. Can't parse filemode", NULL); + attr = normalize_filemode(attr); /* make sure to normalize the filemode */ + if (*buffer++ != ' ') return tree_error("Failed to parse tree. Object is corrupted", NULL); @@ -529,19 +551,6 @@ static void sort_entries(git_treebuilder *bld) git_vector_sort(&bld->entries); } -GIT_INLINE(git_filemode_t) normalize_filemode(git_filemode_t filemode) -{ - /* 100664 mode is an early design mistake. Tree entries may bear - * this mode in some old git repositories, but it's now deprecated. - * We silently normalize while inserting new entries in a tree - * being built. - */ - if (filemode == GIT_FILEMODE_BLOB_GROUP_WRITABLE) - return GIT_FILEMODE_BLOB; - - return filemode; -} - int git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source) { git_treebuilder *bld; @@ -565,7 +574,7 @@ int git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source) if (append_entry( bld, entry_src->filename, &entry_src->oid, - normalize_filemode((git_filemode_t)entry_src->attr)) < 0) + entry_src->attr) < 0) goto on_error; } } @@ -593,8 +602,6 @@ int git_treebuilder_insert( if (!valid_filemode(filemode)) return tree_error("Failed to insert entry. Invalid filemode for file", filename); - filemode = normalize_filemode(filemode); - if (!valid_entry_name(filename)) return tree_error("Failed to insert entry. Invalid name for a tree entry", filename); diff --git a/tests-clar/checkout/index.c b/tests-clar/checkout/index.c index b6d637223..a67765b26 100644 --- a/tests-clar/checkout/index.c +++ b/tests-clar/checkout/index.c @@ -25,7 +25,7 @@ void test_checkout_index__initialize(void) { git_tree *tree; - memset(&g_opts, 0, sizeof(g_opts)); + GIT_INIT_STRUCTURE(&g_opts, GIT_CHECKOUT_OPTS_VERSION); g_opts.checkout_strategy = GIT_CHECKOUT_SAFE; g_repo = cl_git_sandbox_init("testrepo"); @@ -66,7 +66,7 @@ void test_checkout_index__cannot_checkout_a_bare_repository(void) { test_checkout_index__cleanup(); - memset(&g_opts, 0, sizeof(g_opts)); + GIT_INIT_STRUCTURE(&g_opts, GIT_CHECKOUT_OPTS_VERSION); g_repo = cl_git_sandbox_init("testrepo.git"); cl_git_fail(git_checkout_index(g_repo, NULL, NULL)); @@ -426,3 +426,21 @@ void test_checkout_index__can_overcome_name_clashes(void) git_index_free(index); } + +void test_checkout_index__validates_struct_version(void) +{ + const git_error *err; + + g_opts.version = 1024; + cl_git_fail(git_checkout_index(g_repo, NULL, &g_opts)); + + err = giterr_last(); + cl_assert_equal_i(err->klass, GITERR_INVALID); + + g_opts.version = 0; + giterr_clear(); + cl_git_fail(git_checkout_index(g_repo, NULL, &g_opts)); + + err = giterr_last(); + cl_assert_equal_i(err->klass, GITERR_INVALID); +} diff --git a/tests-clar/checkout/tree.c b/tests-clar/checkout/tree.c index 534c46086..88dbe4ffc 100644 --- a/tests-clar/checkout/tree.c +++ b/tests-clar/checkout/tree.c @@ -11,7 +11,7 @@ void test_checkout_tree__initialize(void) { g_repo = cl_git_sandbox_init("testrepo"); - memset(&g_opts, 0, sizeof(g_opts)); + GIT_INIT_STRUCTURE(&g_opts, GIT_CHECKOUT_OPTS_VERSION); g_opts.checkout_strategy = GIT_CHECKOUT_SAFE; } diff --git a/tests-clar/checkout/typechange.c b/tests-clar/checkout/typechange.c index cd34885de..98c15bcb7 100644 --- a/tests-clar/checkout/typechange.c +++ b/tests-clar/checkout/typechange.c @@ -38,7 +38,7 @@ void test_checkout_typechange__checkout_typechanges(void) { int i; git_object *obj; - git_checkout_opts opts = {0}; + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; opts.checkout_strategy = GIT_CHECKOUT_FORCE; diff --git a/tests-clar/commit/parse.c b/tests-clar/commit/parse.c index bbb502cb5..8075f2619 100644 --- a/tests-clar/commit/parse.c +++ b/tests-clar/commit/parse.c @@ -149,7 +149,7 @@ void test_commit_parse__signature(void) { const char *str = passcase->string; size_t len = strlen(passcase->string); - struct git_signature person = {NULL, NULL, {0, 0}}; + struct git_signature person = {0}; cl_git_pass(git_signature__parse(&person, &str, str + len, passcase->header, '\n')); cl_assert(strcmp(passcase->name, person.name) == 0); cl_assert(strcmp(passcase->email, person.email) == 0); @@ -162,7 +162,7 @@ void test_commit_parse__signature(void) { const char *str = failcase->string; size_t len = strlen(failcase->string); - git_signature person = {NULL, NULL, {0, 0}}; + git_signature person = {0}; cl_git_fail(git_signature__parse(&person, &str, str + len, failcase->header, '\n')); git__free(person.name); git__free(person.email); } diff --git a/tests-clar/config/backend.c b/tests-clar/config/backend.c new file mode 100644 index 000000000..65dbccc60 --- /dev/null +++ b/tests-clar/config/backend.c @@ -0,0 +1,21 @@ +#include "clar_libgit2.h" + +void test_config_backend__checks_version(void) +{ + git_config *cfg; + git_config_backend backend = GIT_CONFIG_BACKEND_INIT; + const git_error *err; + + backend.version = 1024; + + cl_git_pass(git_config_new(&cfg)); + cl_git_fail(git_config_add_backend(cfg, &backend, 0, false)); + err = giterr_last(); + cl_assert_equal_i(GITERR_INVALID, err->klass); + + giterr_clear(); + backend.version = 1024; + cl_git_fail(git_config_add_backend(cfg, &backend, 0, false)); + err = giterr_last(); + cl_assert_equal_i(GITERR_INVALID, err->klass); +} diff --git a/tests-clar/diff/blob.c b/tests-clar/diff/blob.c index 6a5645d4b..d7fdba0e6 100644 --- a/tests-clar/diff/blob.c +++ b/tests-clar/diff/blob.c @@ -12,7 +12,7 @@ void test_diff_blob__initialize(void) g_repo = cl_git_sandbox_init("attr"); - memset(&opts, 0, sizeof(opts)); + GIT_INIT_STRUCTURE(&opts, GIT_DIFF_OPTIONS_VERSION); opts.context_lines = 1; opts.interhunk_lines = 0; @@ -313,3 +313,25 @@ void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void) git_blob_free(old_d); } + +void test_diff_blob__checks_options_version_too_low(void) +{ + const git_error *err; + + opts.version = 0; + cl_git_fail(git_diff_blobs( + d, alien, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + err = giterr_last(); + cl_assert_equal_i(GITERR_INVALID, err->klass); +} + +void test_diff_blob__checks_options_version_too_high(void) +{ + const git_error *err; + + opts.version = 1024; + cl_git_fail(git_diff_blobs( + d, alien, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + err = giterr_last(); + cl_assert_equal_i(GITERR_INVALID, err->klass); +} diff --git a/tests-clar/diff/diff_helpers.h b/tests-clar/diff/diff_helpers.h index 12591f63e..49c265285 100644 --- a/tests-clar/diff/diff_helpers.h +++ b/tests-clar/diff/diff_helpers.h @@ -48,3 +48,4 @@ extern int diff_foreach_via_iterator( void *data); extern void diff_print(FILE *fp, git_diff_list *diff); + diff --git a/tests-clar/diff/diffiter.c b/tests-clar/diff/diffiter.c index 133392c21..306a13eb9 100644 --- a/tests-clar/diff/diffiter.c +++ b/tests-clar/diff/diffiter.c @@ -76,7 +76,7 @@ void test_diff_diffiter__iterate_files_2(void) void test_diff_diffiter__iterate_files_and_hunks(void) { git_repository *repo = cl_git_sandbox_init("status"); - git_diff_options opts = {0}; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff = NULL; size_t d, num_d; int file_count = 0, hunk_count = 0; @@ -129,7 +129,7 @@ void test_diff_diffiter__iterate_files_and_hunks(void) void test_diff_diffiter__max_size_threshold(void) { git_repository *repo = cl_git_sandbox_init("status"); - git_diff_options opts = {0}; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff = NULL; int file_count = 0, binary_count = 0, hunk_count = 0; size_t d, num_d; @@ -207,7 +207,7 @@ void test_diff_diffiter__max_size_threshold(void) void test_diff_diffiter__iterate_all(void) { git_repository *repo = cl_git_sandbox_init("status"); - git_diff_options opts = {0}; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff = NULL; diff_expects exp = {0}; size_t d, num_d; @@ -280,7 +280,7 @@ static void iterate_over_patch(git_diff_patch *patch, diff_expects *exp) void test_diff_diffiter__iterate_randomly_while_saving_state(void) { git_repository *repo = cl_git_sandbox_init("status"); - git_diff_options opts = {0}; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff = NULL; diff_expects exp = {0}; git_diff_patch *patches[PATCH_CACHE]; @@ -441,3 +441,25 @@ void test_diff_diffiter__iterate_and_generate_patch_text(void) git_diff_list_free(diff); } + +void test_diff_diffiter__checks_options_version(void) +{ + git_repository *repo = cl_git_sandbox_init("status"); + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_diff_list *diff = NULL; + const git_error *err; + + opts.version = 0; + opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED; + + cl_git_fail(git_diff_workdir_to_index(&diff, repo, NULL, &opts)); + err = giterr_last(); + cl_assert_equal_i(GITERR_INVALID, err->klass); + + giterr_clear(); + opts.version = 1024; + cl_git_fail(git_diff_workdir_to_index(&diff, repo, NULL, &opts)); + err = giterr_last(); + cl_assert_equal_i(GITERR_INVALID, err->klass); +} + diff --git a/tests-clar/diff/index.c b/tests-clar/diff/index.c index 9591e3457..267b3291c 100644 --- a/tests-clar/diff/index.c +++ b/tests-clar/diff/index.c @@ -20,7 +20,7 @@ void test_diff_index__0(void) const char *b_commit = "0017bd4ab1ec3"; /* the start */ git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit); git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit); - git_diff_options opts = {0}; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff = NULL; diff_expects exp; @@ -113,7 +113,7 @@ void test_diff_index__1(void) const char *b_commit = "0017bd4ab1ec3"; /* the start */ git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit); git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit); - git_diff_options opts = {0}; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff = NULL; diff_expects exp; @@ -140,3 +140,24 @@ void test_diff_index__1(void) git_tree_free(a); git_tree_free(b); } + +void test_diff_index__checks_options_version(void) +{ + const char *a_commit = "26a125ee1bf"; + git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit); + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_diff_list *diff; + const git_error *err; + + opts.version = 0; + cl_git_fail(git_diff_index_to_tree(&diff, g_repo, a, NULL, &opts)); + err = giterr_last(); + cl_assert_equal_i(GITERR_INVALID, err->klass); + + giterr_clear(); + opts.version = 1024; + cl_git_fail(git_diff_index_to_tree(&diff, g_repo, a, NULL, &opts)); + err = giterr_last(); + cl_assert_equal_i(GITERR_INVALID, err->klass); +} + diff --git a/tests-clar/diff/rename.c b/tests-clar/diff/rename.c index 0d57f8ff0..2995b4ef5 100644 --- a/tests-clar/diff/rename.c +++ b/tests-clar/diff/rename.c @@ -33,8 +33,8 @@ void test_diff_rename__match_oid(void) const char *new_sha = "2bc7f351d20b53f1c72c16c4b036e491c478c49a"; git_tree *old_tree, *new_tree; git_diff_list *diff; - git_diff_options diffopts = {0}; - git_diff_find_options opts; + git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; + git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; diff_expects exp; old_tree = resolve_commit_oid_to_tree(g_repo, old_sha); @@ -43,7 +43,6 @@ void test_diff_rename__match_oid(void) /* Must pass GIT_DIFF_INCLUDE_UNMODIFIED if you expect to emulate * --find-copies-harder during rename transformion... */ - memset(&diffopts, 0, sizeof(diffopts)); diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED; cl_git_pass(git_diff_tree_to_tree( @@ -85,7 +84,6 @@ void test_diff_rename__match_oid(void) * 31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 \ * 2bc7f351d20b53f1c72c16c4b036e491c478c49a */ - memset(&opts, 0, sizeof(opts)); opts.flags = GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED; cl_git_pass(git_diff_find_similar(diff, &opts)); @@ -103,3 +101,35 @@ void test_diff_rename__match_oid(void) git_tree_free(old_tree); git_tree_free(new_tree); } + +void test_diff_rename__checks_options_version(void) +{ + const char *old_sha = "31e47d8c1fa36d7f8d537b96158e3f024de0a9f2"; + const char *new_sha = "2bc7f351d20b53f1c72c16c4b036e491c478c49a"; + git_tree *old_tree, *new_tree; + git_diff_list *diff; + git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; + git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; + const git_error *err; + + old_tree = resolve_commit_oid_to_tree(g_repo, old_sha); + new_tree = resolve_commit_oid_to_tree(g_repo, new_sha); + diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED; + cl_git_pass(git_diff_tree_to_tree( + &diff, g_repo, old_tree, new_tree, &diffopts)); + + opts.version = 0; + cl_git_fail(git_diff_find_similar(diff, &opts)); + err = giterr_last(); + cl_assert_equal_i(GITERR_INVALID, err->klass); + + giterr_clear(); + opts.version = 1024; + cl_git_fail(git_diff_find_similar(diff, &opts)); + err = giterr_last(); + cl_assert_equal_i(GITERR_INVALID, err->klass); + + git_diff_list_free(diff); + git_tree_free(old_tree); + git_tree_free(new_tree); +} diff --git a/tests-clar/diff/tree.c b/tests-clar/diff/tree.c index 58dc4e6fa..442e53b25 100644 --- a/tests-clar/diff/tree.c +++ b/tests-clar/diff/tree.c @@ -19,7 +19,7 @@ void test_diff_tree__0(void) const char *b_commit = "370fe9ec22"; const char *c_commit = "f5b0af1fb4f5c"; git_tree *a, *b, *c; - git_diff_options opts = {0}; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff = NULL; diff_expects exp; @@ -175,7 +175,7 @@ void test_diff_tree__bare(void) const char *a_commit = "8496071c1b46c85"; const char *b_commit = "be3563ae3f79"; git_tree *a, *b; - git_diff_options opts = {0}; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff = NULL; diff_expects exp; @@ -264,7 +264,7 @@ void test_diff_tree__larger_hunks(void) const char *a_commit = "d70d245ed97ed2aa596dd1af6536e4bfdb047b69"; const char *b_commit = "7a9e0b02e63179929fed24f0a3e0f19168114d10"; git_tree *a, *b; - git_diff_options opts = {0}; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff = NULL; size_t d, num_d, h, num_h, l, num_l, header_len, line_len; const git_diff_delta *delta; @@ -319,3 +319,31 @@ void test_diff_tree__larger_hunks(void) git_tree_free(a); git_tree_free(b); } + +void test_diff_tree__checks_options_version(void) +{ + const char *a_commit = "8496071c1b46c85"; + const char *b_commit = "be3563ae3f79"; + git_tree *a, *b; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_diff_list *diff = NULL; + const git_error *err; + + g_repo = cl_git_sandbox_init("testrepo.git"); + + cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL); + cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL); + + opts.version = 0; + cl_git_fail(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts)); + err = giterr_last(); + cl_assert_equal_i(GITERR_INVALID, err->klass); + + giterr_clear(); + opts.version = 1024; + cl_git_fail(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts)); + err = giterr_last(); + + git_tree_free(a); + git_tree_free(b); +} diff --git a/tests-clar/diff/workdir.c b/tests-clar/diff/workdir.c index 57c88c3e5..10d58b118 100644 --- a/tests-clar/diff/workdir.c +++ b/tests-clar/diff/workdir.c @@ -15,7 +15,7 @@ void test_diff_workdir__cleanup(void) void test_diff_workdir__to_index(void) { - git_diff_options opts = {0}; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff = NULL; diff_expects exp; int use_iterator; @@ -69,7 +69,7 @@ void test_diff_workdir__to_tree(void) const char *a_commit = "26a125ee1bf"; /* the current HEAD */ const char *b_commit = "0017bd4ab1ec3"; /* the start */ git_tree *a, *b; - git_diff_options opts = {0}; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff = NULL; git_diff_list *diff2 = NULL; diff_expects exp; @@ -202,7 +202,7 @@ void test_diff_workdir__to_tree(void) void test_diff_workdir__to_index_with_pathspec(void) { - git_diff_options opts = {0}; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff = NULL; diff_expects exp; char *pathspec = NULL; @@ -420,7 +420,7 @@ void test_diff_workdir__filemode_changes_with_filemode_false(void) void test_diff_workdir__head_index_and_workdir_all_differ(void) { - git_diff_options opts = {0}; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff_i2t = NULL, *diff_w2i = NULL; diff_expects exp; char *pathspec = "staged_changes_modified_file"; @@ -518,7 +518,7 @@ void test_diff_workdir__head_index_and_workdir_all_differ(void) void test_diff_workdir__eof_newline_changes(void) { - git_diff_options opts = {0}; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff = NULL; diff_expects exp; char *pathspec = "current_file"; @@ -678,7 +678,7 @@ void test_diff_workdir__larger_hunks(void) const char *a_commit = "d70d245ed97ed2aa596dd1af6536e4bfdb047b69"; const char *b_commit = "7a9e0b02e63179929fed24f0a3e0f19168114d10"; git_tree *a, *b; - git_diff_options opts = {0}; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; size_t i, d, num_d, h, num_h, l, num_l, header_len, line_len; g_repo = cl_git_sandbox_init("diff"); @@ -763,7 +763,7 @@ void test_diff_workdir__submodules(void) { const char *a_commit = "873585b94bdeabccea991ea5e3ec1a277895b698"; git_tree *a; - git_diff_options opts = {0}; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff = NULL; diff_expects exp; @@ -822,7 +822,7 @@ void test_diff_workdir__submodules(void) void test_diff_workdir__cannot_diff_against_a_bare_repository(void) { - git_diff_options opts = {0}; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff = NULL; git_tree *tree; @@ -843,7 +843,7 @@ void test_diff_workdir__to_null_tree(void) { git_diff_list *diff; diff_expects exp; - git_diff_options opts = {0}; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; opts.flags = GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_RECURSE_UNTRACKED_DIRS; @@ -861,3 +861,23 @@ void test_diff_workdir__to_null_tree(void) git_diff_list_free(diff); } + +void test_diff_workdir__checks_options_version(void) +{ + git_diff_list *diff; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + const git_error *err; + + g_repo = cl_git_sandbox_init("status"); + + opts.version = 0; + cl_git_fail(git_diff_workdir_to_tree(&diff, g_repo, NULL, &opts)); + err = giterr_last(); + cl_assert_equal_i(GITERR_INVALID, err->klass); + + giterr_clear(); + opts.version = 1024; + cl_git_fail(git_diff_workdir_to_tree(&diff, g_repo, NULL, &opts)); + err = giterr_last(); + cl_assert_equal_i(GITERR_INVALID, err->klass); +} diff --git a/tests-clar/network/fetch.c b/tests-clar/network/fetch.c index 84c947291..a5e2c02a7 100644 --- a/tests-clar/network/fetch.c +++ b/tests-clar/network/fetch.c @@ -36,10 +36,9 @@ static void progress(const git_transfer_progress *stats, void *payload) static void do_fetch(const char *url, git_remote_autotag_option_t flag, int n) { git_remote *remote; - git_remote_callbacks callbacks; + git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; size_t bytes_received = 0; - memset(&callbacks, 0, sizeof(git_remote_callbacks)); callbacks.update_tips = update_tips; counter = 0; @@ -79,6 +78,8 @@ void test_network_fetch__no_tags_http(void) static void transferProgressCallback(const git_transfer_progress *stats, void *payload) { bool *invoked = (bool *)payload; + + GIT_UNUSED(stats); *invoked = true; } diff --git a/tests-clar/network/push_util.h b/tests-clar/network/push_util.h index 2f4dffce4..759122aa6 100644 --- a/tests-clar/network/push_util.h +++ b/tests-clar/network/push_util.h @@ -11,7 +11,8 @@ extern const git_oid OID_ZERO; * record data in a record_callbacks_data instance. * @param data pointer to a record_callbacks_data instance */ -#define RECORD_CALLBACKS_INIT(data) { NULL, NULL, record_update_tips_cb, data } +#define RECORD_CALLBACKS_INIT(data) \ + { GIT_REMOTE_CALLBACKS_VERSION, NULL, NULL, record_update_tips_cb, data } typedef struct { char *name; diff --git a/tests-clar/network/remoterename.c b/tests-clar/network/remoterename.c index b14554572..bd582314d 100644 --- a/tests-clar/network/remoterename.c +++ b/tests-clar/network/remoterename.c @@ -121,7 +121,9 @@ void test_network_remoterename__new_name_can_contain_dots(void) void test_network_remoterename__new_name_must_conform_to_reference_naming_conventions(void) { - cl_git_fail(git_remote_rename(_remote, "new@{name", dont_call_me_cb, NULL)); + cl_assert_equal_i( + GIT_EINVALIDSPEC, + git_remote_rename(_remote, "new@{name", dont_call_me_cb, NULL)); } void test_network_remoterename__renamed_name_is_persisted(void) diff --git a/tests-clar/network/remotes.c b/tests-clar/network/remotes.c index 14fda1670..70df001e7 100644 --- a/tests-clar/network/remotes.c +++ b/tests-clar/network/remotes.c @@ -202,6 +202,11 @@ void test_network_remotes__loading_a_missing_remote_returns_ENOTFOUND(void) cl_assert_equal_i(GIT_ENOTFOUND, git_remote_load(&_remote, _repo, "just-left-few-minutes-ago")); } +void test_network_remotes__loading_with_an_invalid_name_returns_EINVALIDSPEC(void) +{ + cl_assert_equal_i(GIT_EINVALIDSPEC, git_remote_load(&_remote, _repo, "Inv@{id")); +} + /* * $ git remote add addtest http://github.com/libgit2/libgit2 * @@ -229,8 +234,9 @@ void test_network_remotes__cannot_add_a_nameless_remote(void) { git_remote *remote; - cl_git_fail(git_remote_add(&remote, _repo, NULL, "git://github.com/libgit2/libgit2")); - cl_git_fail(git_remote_add(&remote, _repo, "", "git://github.com/libgit2/libgit2")); + cl_assert_equal_i( + GIT_EINVALIDSPEC, + git_remote_add(&remote, _repo, NULL, "git://github.com/libgit2/libgit2")); } void test_network_remotes__cannot_save_a_nameless_remote(void) @@ -239,13 +245,34 @@ void test_network_remotes__cannot_save_a_nameless_remote(void) cl_git_pass(git_remote_new(&remote, _repo, NULL, "git://github.com/libgit2/libgit2", NULL)); - cl_git_fail(git_remote_save(remote)); + cl_assert_equal_i(GIT_EINVALIDSPEC, git_remote_save(remote)); git_remote_free(remote); +} + +void test_network_remotes__cannot_add_a_remote_with_an_invalid_name(void) +{ + git_remote *remote; - cl_git_pass(git_remote_new(&remote, _repo, "", "git://github.com/libgit2/libgit2", NULL)); + cl_assert_equal_i( + GIT_EINVALIDSPEC, + git_remote_add(&remote, _repo, "Inv@{id", "git://github.com/libgit2/libgit2")); - cl_git_fail(git_remote_save(remote)); - git_remote_free(remote); + cl_assert_equal_i( + GIT_EINVALIDSPEC, + git_remote_add(&remote, _repo, "", "git://github.com/libgit2/libgit2")); +} + +void test_network_remotes__cannot_initialize_a_remote_with_an_invalid_name(void) +{ + git_remote *remote; + + cl_assert_equal_i( + GIT_EINVALIDSPEC, + git_remote_new(&remote, _repo, "Inv@{id", "git://github.com/libgit2/libgit2", NULL)); + + cl_assert_equal_i( + GIT_EINVALIDSPEC, + git_remote_new(&remote, _repo, "", "git://github.com/libgit2/libgit2", NULL)); } void test_network_remotes__tagopt(void) @@ -279,3 +306,23 @@ void test_network_remotes__cannot_load_with_an_empty_url(void) cl_git_fail(git_remote_load(&remote, _repo, "empty-remote-url")); cl_assert(giterr_last()->klass == GITERR_INVALID); } + +void test_network_remotes__check_structure_version(void) +{ + git_transport transport = GIT_TRANSPORT_INIT; + const git_error *err; + + git_remote_free(_remote); + cl_git_pass(git_remote_new(&_remote, _repo, NULL, "test-protocol://localhost", NULL)); + + transport.version = 0; + cl_git_fail(git_remote_set_transport(_remote, &transport)); + err = giterr_last(); + cl_assert_equal_i(GITERR_INVALID, err->klass); + + giterr_clear(); + transport.version = 1024; + cl_git_fail(git_remote_set_transport(_remote, &transport)); + err = giterr_last(); + cl_assert_equal_i(GITERR_INVALID, err->klass); +} diff --git a/tests-clar/object/peel.c b/tests-clar/object/peel.c index a19772858..bb0bbd096 100644 --- a/tests-clar/object/peel.c +++ b/tests-clar/object/peel.c @@ -79,12 +79,12 @@ void test_object_peel__can_peel_a_commit(void) void test_object_peel__cannot_peel_a_tree(void) { - assert_peel_error(GIT_ERROR, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_BLOB); + assert_peel_error(GIT_EAMBIGUOUS, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_BLOB); } void test_object_peel__cannot_peel_a_blob(void) { - assert_peel_error(GIT_ERROR, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJ_COMMIT); + assert_peel_error(GIT_ENOTFOUND, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJ_COMMIT); } void test_object_peel__target_any_object_for_type_change(void) @@ -98,8 +98,13 @@ void test_object_peel__target_any_object_for_type_change(void) "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_TREE); /* fail to peel tree */ - assert_peel_error(GIT_ERROR, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_ANY); + assert_peel_error(GIT_EAMBIGUOUS, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_ANY); /* fail to peel blob */ - assert_peel_error(GIT_ERROR, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJ_ANY); + assert_peel_error(GIT_ENOTFOUND, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJ_ANY); +} + +void test_object_peel__should_use_a_well_known_type(void) +{ + assert_peel_error(GIT_EINVALIDSPEC, "7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJ__EXT2); } diff --git a/tests-clar/object/tag/write.c b/tests-clar/object/tag/write.c index ad6ca76b2..eb0ac2897 100644 --- a/tests-clar/object/tag/write.c +++ b/tests-clar/object/tag/write.c @@ -88,7 +88,6 @@ void test_object_tag_write__overwrite(void) git_object_free(target); git_signature_free(tagger); - } void test_object_tag_write__replace(void) @@ -190,3 +189,33 @@ void test_object_tag_write__delete(void) git_reference_free(ref_tag); } + +void test_object_tag_write__creating_with_an_invalid_name_returns_EINVALIDSPEC(void) +{ + git_oid target_id, tag_id; + git_signature *tagger; + git_object *target; + + git_oid_fromstr(&target_id, tagged_commit); + cl_git_pass(git_object_lookup(&target, g_repo, &target_id, GIT_OBJ_COMMIT)); + + cl_git_pass(git_signature_new(&tagger, tagger_name, tagger_email, 123456789, 60)); + + cl_assert_equal_i(GIT_EINVALIDSPEC, + git_tag_create(&tag_id, g_repo, + "Inv@{id", target, tagger, tagger_message, 0) + ); + + cl_assert_equal_i(GIT_EINVALIDSPEC, + git_tag_create_lightweight(&tag_id, g_repo, + "Inv@{id", target, 0) + ); + + git_object_free(target); + git_signature_free(tagger); +} + +void test_object_tag_write__deleting_with_an_invalid_name_returns_EINVALIDSPEC(void) +{ + cl_assert_equal_i(GIT_EINVALIDSPEC, git_tag_delete(g_repo, "Inv@{id")); +} diff --git a/tests-clar/object/tree/attributes.c b/tests-clar/object/tree/attributes.c index 054f67137..b5319d30e 100644 --- a/tests-clar/object/tree/attributes.c +++ b/tests-clar/object/tree/attributes.c @@ -34,14 +34,14 @@ void test_object_tree_attributes__group_writable_tree_entries_created_with_an_an entry = git_tree_entry_byname(tree, "old_mode.txt"); cl_assert_equal_i( - GIT_FILEMODE_BLOB_GROUP_WRITABLE, + GIT_FILEMODE_BLOB, git_tree_entry_filemode(entry)); git_tree_free(tree); git_repository_free(repo); } -void test_object_tree_attributes__normalize_attributes_when_inserting_in_a_new_tree(void) +void test_object_tree_attributes__treebuilder_reject_invalid_filemode(void) { git_repository *repo; git_treebuilder *builder; @@ -55,28 +55,14 @@ void test_object_tree_attributes__normalize_attributes_when_inserting_in_a_new_t cl_git_pass(git_treebuilder_create(&builder, NULL)); - cl_git_pass(git_treebuilder_insert( + cl_git_fail(git_treebuilder_insert( &entry, builder, "normalized.txt", &bid, GIT_FILEMODE_BLOB_GROUP_WRITABLE)); - cl_assert_equal_i( - GIT_FILEMODE_BLOB, - git_tree_entry_filemode(entry)); - - cl_git_pass(git_treebuilder_write(&tid, repo, builder)); git_treebuilder_free(builder); - - cl_git_pass(git_tree_lookup(&tree, repo, &tid)); - - entry = git_tree_entry_byname(tree, "normalized.txt"); - cl_assert_equal_i( - GIT_FILEMODE_BLOB, - git_tree_entry_filemode(entry)); - - git_tree_free(tree); cl_git_sandbox_cleanup(); } @@ -113,3 +99,22 @@ void test_object_tree_attributes__normalize_attributes_when_creating_a_tree_from git_tree_free(tree); cl_git_sandbox_cleanup(); } + +void test_object_tree_attributes__normalize_600(void) +{ + git_oid id; + git_tree *tree; + git_repository *repo; + const git_tree_entry *entry; + + repo = cl_git_sandbox_init("deprecated-mode.git"); + + git_oid_fromstr(&id, "0810fb7818088ff5ac41ee49199b51473b1bd6c7"); + cl_git_pass(git_tree_lookup(&tree, repo, &id)); + + entry = git_tree_entry_byname(tree, "ListaTeste.xml"); + cl_assert_equal_i(entry->attr, GIT_FILEMODE_BLOB); + + git_tree_free(tree); + cl_git_sandbox_cleanup(); +} diff --git a/tests-clar/odb/sorting.c b/tests-clar/odb/sorting.c index bf64f6af4..b4f9e44bc 100644 --- a/tests-clar/odb/sorting.c +++ b/tests-clar/odb/sorting.c @@ -11,11 +11,11 @@ static git_odb_backend *new_backend(int position) { fake_backend *b; - b = git__malloc(sizeof(fake_backend)); + b = git__calloc(1, sizeof(fake_backend)); if (b == NULL) return NULL; - memset(b, 0x0, sizeof(fake_backend)); + b->base.version = GIT_ODB_BACKEND_VERSION; b->position = position; return (git_odb_backend *)b; } diff --git a/tests-clar/refs/branches/create.c b/tests-clar/refs/branches/create.c index a8c4d4f51..693a592a3 100644 --- a/tests-clar/refs/branches/create.c +++ b/tests-clar/refs/branches/create.c @@ -65,3 +65,12 @@ void test_refs_branches_create__can_force_create_over_an_existing_branch(void) cl_git_pass(git_oid_cmp(git_reference_target(branch), git_commit_id(target))); cl_assert_equal_s("refs/heads/br2", git_reference_name(branch)); } + + +void test_refs_branches_create__creating_a_branch_with_an_invalid_name_returns_EINVALIDSPEC(void) +{ + retrieve_known_commit(&target, repo); + + cl_assert_equal_i(GIT_EINVALIDSPEC, + git_branch_create(&branch, repo, "inv@{id", target, 0)); +}
\ No newline at end of file diff --git a/tests-clar/refs/branches/lookup.c b/tests-clar/refs/branches/lookup.c index d07ed0ed8..95d49a4b3 100644 --- a/tests-clar/refs/branches/lookup.c +++ b/tests-clar/refs/branches/lookup.c @@ -35,3 +35,11 @@ void test_refs_branches_lookup__trying_to_retrieve_an_unknown_branch_returns_ENO cl_assert_equal_i(GIT_ENOTFOUND, git_branch_lookup(&branch, repo, "where/are/you", GIT_BRANCH_LOCAL)); cl_assert_equal_i(GIT_ENOTFOUND, git_branch_lookup(&branch, repo, "over/here", GIT_BRANCH_REMOTE)); } + +void test_refs_branches_lookup__trying_to_retrieve_a_branch_with_an_invalid_name_returns_EINVALIDSPEC(void) +{ + cl_assert_equal_i(GIT_EINVALIDSPEC, + git_branch_lookup(&branch, repo, "are/you/inv@{id", GIT_BRANCH_LOCAL)); + cl_assert_equal_i(GIT_EINVALIDSPEC, + git_branch_lookup(&branch, repo, "yes/i am", GIT_BRANCH_REMOTE)); +} diff --git a/tests-clar/refs/branches/move.c b/tests-clar/refs/branches/move.c index 4bf1d69d0..17fb6dfe6 100644 --- a/tests-clar/refs/branches/move.c +++ b/tests-clar/refs/branches/move.c @@ -51,6 +51,11 @@ void test_refs_branches_move__can_not_move_a_branch_if_its_destination_name_coll cl_assert_equal_i(GIT_EEXISTS, git_branch_move(ref, "master", 0)); } +void test_refs_branches_move__moving_a_branch_with_an_invalid_name_returns_EINVALIDSPEC(void) +{ + cl_assert_equal_i(GIT_EINVALIDSPEC, git_branch_move(ref, "Inv@{id", 0)); +} + void test_refs_branches_move__can_not_move_a_non_branch(void) { git_reference *tag; diff --git a/tests-clar/refs/branches/tracking.c b/tests-clar/refs/branches/tracking.c index e8b2f24d7..bc41e0633 100644 --- a/tests-clar/refs/branches/tracking.c +++ b/tests-clar/refs/branches/tracking.c @@ -61,11 +61,12 @@ void test_refs_branches_tracking__trying_to_retrieve_a_remote_tracking_reference cl_assert_equal_i(GIT_ENOTFOUND, git_branch_tracking(&tracking, branch)); } -static void assert_merge_and_or_remote_key_missing(git_repository *repository, git_object *target, const char *entry_name) +static void assert_merge_and_or_remote_key_missing(git_repository *repository, const git_commit *target, const char *entry_name) { git_reference *branch; - cl_git_pass(git_branch_create(&branch, repository, entry_name, target, 0)); + cl_assert_equal_i(GIT_OBJ_COMMIT, git_object_type(target)); + cl_git_pass(git_branch_create(&branch, repository, entry_name, (git_commit*)target, 0)); cl_assert_equal_i(GIT_ENOTFOUND, git_branch_tracking(&tracking, branch)); @@ -76,19 +77,19 @@ void test_refs_branches_tracking__retrieve_a_remote_tracking_reference_from_a_br { git_reference *head; git_repository *repository; - git_object *target; + git_commit *target; repository = cl_git_sandbox_init("testrepo.git"); cl_git_pass(git_repository_head(&head, repository)); - cl_git_pass(git_reference_peel(&target, head, GIT_OBJ_COMMIT)); + cl_git_pass(git_reference_peel(((git_object **)&target), head, GIT_OBJ_COMMIT)); git_reference_free(head); assert_merge_and_or_remote_key_missing(repository, target, "remoteless"); assert_merge_and_or_remote_key_missing(repository, target, "mergeless"); assert_merge_and_or_remote_key_missing(repository, target, "mergeandremoteless"); - git_object_free(target); + git_commit_free(target); cl_git_sandbox_cleanup(); } diff --git a/tests-clar/refs/create.c b/tests-clar/refs/create.c index bef9bfd24..56c323d8a 100644 --- a/tests-clar/refs/create.c +++ b/tests-clar/refs/create.c @@ -25,16 +25,11 @@ void test_refs_create__symbolic(void) git_reference *new_reference, *looked_up_ref, *resolved_ref; git_repository *repo2; git_oid id; - git_buf ref_path = GIT_BUF_INIT; const char *new_head_tracker = "ANOTHER_HEAD_TRACKER"; git_oid_fromstr(&id, current_master_tip); - /* Retrieve the physical path to the symbolic ref for further cleaning */ - cl_git_pass(git_buf_joinpath(&ref_path, g_repo->path_repository, new_head_tracker)); - git_buf_free(&ref_path); - /* Create and write the new symbolic reference */ cl_git_pass(git_reference_symbolic_create(&new_reference, g_repo, new_head_tracker, current_head_target, 0)); @@ -72,13 +67,11 @@ void test_refs_create__deep_symbolic(void) // create a deep symbolic reference git_reference *new_reference, *looked_up_ref, *resolved_ref; git_oid id; - git_buf ref_path = GIT_BUF_INIT; const char *new_head_tracker = "deep/rooted/tracker"; git_oid_fromstr(&id, current_master_tip); - cl_git_pass(git_buf_joinpath(&ref_path, g_repo->path_repository, new_head_tracker)); cl_git_pass(git_reference_symbolic_create(&new_reference, g_repo, new_head_tracker, current_head_target, 0)); cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, new_head_tracker)); cl_git_pass(git_reference_resolve(&resolved_ref, looked_up_ref)); @@ -87,7 +80,6 @@ void test_refs_create__deep_symbolic(void) git_reference_free(new_reference); git_reference_free(looked_up_ref); git_reference_free(resolved_ref); - git_buf_free(&ref_path); } void test_refs_create__oid(void) @@ -96,15 +88,11 @@ void test_refs_create__oid(void) git_reference *new_reference, *looked_up_ref; git_repository *repo2; git_oid id; - git_buf ref_path = GIT_BUF_INIT; const char *new_head = "refs/heads/new-head"; git_oid_fromstr(&id, current_master_tip); - /* Retrieve the physical path to the symbolic ref for further cleaning */ - cl_git_pass(git_buf_joinpath(&ref_path, g_repo->path_repository, new_head)); - /* Create and write the new object id reference */ cl_git_pass(git_reference_create(&new_reference, g_repo, new_head, &id, 0)); @@ -128,7 +116,6 @@ void test_refs_create__oid(void) git_reference_free(new_reference); git_reference_free(looked_up_ref); - git_buf_free(&ref_path); } void test_refs_create__oid_unknown(void) @@ -162,3 +149,19 @@ void test_refs_create__propagate_eexists(void) error = git_reference_symbolic_create(&ref, g_repo, "HEAD", current_head_target, false); cl_assert(error == GIT_EEXISTS); } + +void test_refs_create__creating_a_reference_with_an_invalid_name_returns_EINVALIDSPEC(void) +{ + git_reference *new_reference; + git_oid id; + + const char *name = "refs/heads/inv@{id"; + + git_oid_fromstr(&id, current_master_tip); + + cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_create( + &new_reference, g_repo, name, &id, 0)); + + cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_symbolic_create( + &new_reference, g_repo, name, current_head_target, 0)); +} diff --git a/tests-clar/refs/normalize.c b/tests-clar/refs/normalize.c index a144ef5c0..870a533ca 100644 --- a/tests-clar/refs/normalize.c +++ b/tests-clar/refs/normalize.c @@ -21,7 +21,9 @@ static void ensure_refname_invalid(unsigned int flags, const char *input_refname { char buffer_out[GIT_REFNAME_MAX]; - cl_git_fail(git_reference_normalize_name(buffer_out, sizeof(buffer_out), input_refname, flags)); + cl_assert_equal_i( + GIT_EINVALIDSPEC, + git_reference_normalize_name(buffer_out, sizeof(buffer_out), input_refname, flags)); } void test_refs_normalize__can_normalize_a_direct_reference_name(void) diff --git a/tests-clar/refs/peel.c b/tests-clar/refs/peel.c index 6fa6009d5..34bd02ce0 100644 --- a/tests-clar/refs/peel.c +++ b/tests-clar/refs/peel.c @@ -78,7 +78,7 @@ void test_refs_peel__can_peel_a_symbolic_reference(void) void test_refs_peel__cannot_peel_into_a_non_existing_target(void) { - assert_peel_error(GIT_ERROR, "refs/tags/point_to_blob", GIT_OBJ_TAG); + assert_peel_error(GIT_ENOTFOUND, "refs/tags/point_to_blob", GIT_OBJ_TAG); } void test_refs_peel__can_peel_into_any_non_tag_object(void) diff --git a/tests-clar/refs/read.c b/tests-clar/refs/read.c index c10a540c0..3e2a59afd 100644 --- a/tests-clar/refs/read.c +++ b/tests-clar/refs/read.c @@ -223,11 +223,19 @@ void test_refs_read__trailing(void) void test_refs_read__unfound_return_ENOTFOUND(void) { git_reference *reference; + git_oid id; - cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&reference, g_repo, "TEST_MASTER")); - cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&reference, g_repo, "refs/test/master")); - cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&reference, g_repo, "refs/tags/test/master")); - cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&reference, g_repo, "refs/tags/test/farther/master")); + cl_assert_equal_i(GIT_ENOTFOUND, + git_reference_lookup(&reference, g_repo, "TEST_MASTER")); + cl_assert_equal_i(GIT_ENOTFOUND, + git_reference_lookup(&reference, g_repo, "refs/test/master")); + cl_assert_equal_i(GIT_ENOTFOUND, + git_reference_lookup(&reference, g_repo, "refs/tags/test/master")); + cl_assert_equal_i(GIT_ENOTFOUND, + git_reference_lookup(&reference, g_repo, "refs/tags/test/farther/master")); + + cl_assert_equal_i(GIT_ENOTFOUND, + git_reference_name_to_id(&id, g_repo, "refs/tags/test/farther/master")); } static void assert_is_branch(const char *name, bool expected_branchness) @@ -245,3 +253,15 @@ void test_refs_read__can_determine_if_a_reference_is_a_local_branch(void) assert_is_branch("refs/remotes/test/master", false); assert_is_branch("refs/tags/e90810b", false); } + +void test_refs_read__invalid_name_returns_EINVALIDSPEC(void) +{ + git_reference *reference; + git_oid id; + + cl_assert_equal_i(GIT_EINVALIDSPEC, + git_reference_lookup(&reference, g_repo, "refs/heads/Inv@{id")); + + cl_assert_equal_i(GIT_EINVALIDSPEC, + git_reference_name_to_id(&id, g_repo, "refs/heads/Inv@{id")); +} diff --git a/tests-clar/refs/reflog/reflog.c b/tests-clar/refs/reflog/reflog.c index 8743c8a76..19ee53567 100644 --- a/tests-clar/refs/reflog/reflog.c +++ b/tests-clar/refs/reflog/reflog.c @@ -170,3 +170,15 @@ void test_refs_reflog_reflog__cannot_write_a_moved_reflog(void) git_buf_free(&moved_log_path); git_buf_free(&master_log_path); } + +void test_refs_reflog_reflog__renaming_with_an_invalid_name_returns_EINVALIDSPEC(void) +{ + git_reference *master; + + cl_git_pass(git_reference_lookup(&master, g_repo, "refs/heads/master")); + + cl_assert_equal_i(GIT_EINVALIDSPEC, + git_reflog_rename(master, "refs/heads/Inv@{id")); + + git_reference_free(master); +} diff --git a/tests-clar/refs/rename.c b/tests-clar/refs/rename.c index ec5c12507..bfdef15fa 100644 --- a/tests-clar/refs/rename.c +++ b/tests-clar/refs/rename.c @@ -180,10 +180,14 @@ void test_refs_rename__invalid_name(void) cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_test_head_name)); /* Can not be renamed with an invalid name. */ - cl_git_fail(git_reference_rename(looked_up_ref, "Hello! I'm a very invalid name.", 0)); - - /* Can not be renamed outside of the refs hierarchy. */ - cl_git_fail(git_reference_rename(looked_up_ref, "i-will-sudo-you", 0)); + cl_assert_equal_i( + GIT_EINVALIDSPEC, + git_reference_rename(looked_up_ref, "Hello! I'm a very invalid name.", 0)); + + /* Can not be renamed outside of the refs hierarchy + * unless it's ALL_CAPS_AND_UNDERSCORES. + */ + cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_rename(looked_up_ref, "i-will-sudo-you", 0)); /* Failure to rename it hasn't corrupted its state */ git_reference_free(looked_up_ref); diff --git a/tests-clar/refs/revparse.c b/tests-clar/refs/revparse.c index 3698b5197..81a6bc469 100644 --- a/tests-clar/refs/revparse.c +++ b/tests-clar/refs/revparse.c @@ -7,9 +7,6 @@ static git_repository *g_repo; static git_object *g_obj; -static char g_orig_tz[16] = {0}; - - /* Helpers */ static void test_object_inrepo(const char *spec, const char *expected_oid, git_repository *repo) @@ -37,19 +34,12 @@ static void test_object(const char *spec, const char *expected_oid) void test_refs_revparse__initialize(void) { - char *tz = cl_getenv("TZ"); - if (tz) - strcpy(g_orig_tz, tz); - cl_setenv("TZ", "UTC"); - cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git"))); } void test_refs_revparse__cleanup(void) { git_repository_free(g_repo); - g_obj = NULL; - cl_setenv("TZ", g_orig_tz); } void test_refs_revparse__nonexistant_object(void) @@ -59,13 +49,17 @@ void test_refs_revparse__nonexistant_object(void) test_object("this-does-not-exist~2", NULL); } -void test_refs_revparse__invalid_reference_name(void) +static void assert_invalid_spec(const char *invalid_spec) { - cl_git_fail(git_revparse_single(&g_obj, g_repo, "this doesn't make sense")); - cl_git_fail(git_revparse_single(&g_obj, g_repo, "this doesn't make sense^1")); - cl_git_fail(git_revparse_single(&g_obj, g_repo, "this doesn't make sense~2")); - cl_git_fail(git_revparse_single(&g_obj, g_repo, "")); + cl_assert_equal_i( + GIT_EINVALIDSPEC, git_revparse_single(&g_obj, g_repo, invalid_spec)); +} +void test_refs_revparse__invalid_reference_name(void) +{ + assert_invalid_spec("this doesn't make sense"); + assert_invalid_spec("Inv@{id"); + assert_invalid_spec(""); } void test_refs_revparse__shas(void) @@ -104,9 +98,11 @@ void test_refs_revparse__describe_output(void) void test_refs_revparse__nth_parent(void) { - cl_git_fail(git_revparse_single(&g_obj, g_repo, "be3563a^-1")); - cl_git_fail(git_revparse_single(&g_obj, g_repo, "^")); - cl_git_fail(git_revparse_single(&g_obj, g_repo, "be3563a^{tree}^")); + assert_invalid_spec("be3563a^-1"); + assert_invalid_spec("^"); + assert_invalid_spec("be3563a^{tree}^"); + assert_invalid_spec("point_to_blob^{blob}^"); + assert_invalid_spec("this doesn't make sense^1"); test_object("be3563a^1", "9fd738e8f7967c078dceed8190330fc8648ee56a"); test_object("be3563a^", "9fd738e8f7967c078dceed8190330fc8648ee56a"); @@ -133,8 +129,10 @@ void test_refs_revparse__not_tag(void) void test_refs_revparse__to_type(void) { - cl_git_fail(git_revparse_single(&g_obj, g_repo, "wrapped_tag^{blob}")); - cl_git_fail(git_revparse_single(&g_obj, g_repo, "wrapped_tag^{trip}")); + assert_invalid_spec("wrapped_tag^{trip}"); + test_object("point_to_blob^{commit}", NULL); + cl_assert_equal_i( + GIT_EAMBIGUOUS, git_revparse_single(&g_obj, g_repo, "wrapped_tag^{blob}")); test_object("wrapped_tag^{commit}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); test_object("wrapped_tag^{tree}", "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162"); @@ -144,11 +142,15 @@ void test_refs_revparse__to_type(void) void test_refs_revparse__linear_history(void) { - cl_git_fail(git_revparse_single(&g_obj, g_repo, "~")); - cl_git_fail(git_revparse_single(&g_obj, g_repo, "foo~bar")); - cl_git_fail(git_revparse_single(&g_obj, g_repo, "master~bar")); - cl_git_fail(git_revparse_single(&g_obj, g_repo, "master~-1")); - cl_git_fail(git_revparse_single(&g_obj, g_repo, "master~0bar")); + assert_invalid_spec("~"); + test_object("foo~bar", NULL); + + assert_invalid_spec("master~bar"); + assert_invalid_spec("master~-1"); + assert_invalid_spec("master~0bar"); + assert_invalid_spec("this doesn't make sense~2"); + assert_invalid_spec("be3563a^{tree}~"); + assert_invalid_spec("point_to_blob^{blob}~"); test_object("master~0", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); test_object("master~1", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); @@ -159,10 +161,10 @@ void test_refs_revparse__linear_history(void) void test_refs_revparse__chaining(void) { - cl_git_fail(git_revparse_single(&g_obj, g_repo, "master@{0}@{0}")); - cl_git_fail(git_revparse_single(&g_obj, g_repo, "@{u}@{-1}")); - cl_git_fail(git_revparse_single(&g_obj, g_repo, "@{-1}@{-1}")); - cl_git_fail(git_revparse_single(&g_obj, g_repo, "@{-3}@{0}")); + assert_invalid_spec("master@{0}@{0}"); + assert_invalid_spec("@{u}@{-1}"); + assert_invalid_spec("@{-1}@{-1}"); + assert_invalid_spec("@{-3}@{0}"); test_object("master@{0}~1^1", "9fd738e8f7967c078dceed8190330fc8648ee56a"); test_object("@{u}@{0}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); @@ -178,8 +180,9 @@ void test_refs_revparse__chaining(void) void test_refs_revparse__upstream(void) { - cl_git_fail(git_revparse_single(&g_obj, g_repo, "e90810b@{u}")); - cl_git_fail(git_revparse_single(&g_obj, g_repo, "refs/tags/e90810b@{u}")); + assert_invalid_spec("e90810b@{u}"); + assert_invalid_spec("refs/tags/e90810b@{u}"); + test_object("refs/heads/e90810b@{u}", NULL); test_object("master@{upstream}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); test_object("@{u}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); @@ -190,7 +193,7 @@ void test_refs_revparse__upstream(void) void test_refs_revparse__ordinal(void) { - cl_git_fail(git_revparse_single(&g_obj, g_repo, "master@{-2}")); + assert_invalid_spec("master@{-2}"); /* TODO: make the test below actually fail * cl_git_fail(git_revparse_single(&g_obj, g_repo, "master@{1a}")); @@ -212,9 +215,9 @@ void test_refs_revparse__ordinal(void) void test_refs_revparse__previous_head(void) { - cl_git_fail(git_revparse_single(&g_obj, g_repo, "@{-xyz}")); - cl_git_fail(git_revparse_single(&g_obj, g_repo, "@{-0}")); - cl_git_fail(git_revparse_single(&g_obj, g_repo, "@{-1b}")); + assert_invalid_spec("@{-xyz}"); + assert_invalid_spec("@{-0}"); + assert_invalid_spec("@{-1b}"); test_object("@{-42}", NULL); @@ -271,9 +274,9 @@ void test_refs_revparse__reflog_of_a_ref_under_refs(void) void test_refs_revparse__revwalk(void) { - cl_git_fail(git_revparse_single(&g_obj, g_repo, "master^{/not found in any commit}")); - cl_git_fail(git_revparse_single(&g_obj, g_repo, "master^{/merge}")); - cl_git_fail(git_revparse_single(&g_obj, g_repo, "master^{/((}")); + test_object("master^{/not found in any commit}", NULL); + test_object("master^{/merge}", NULL); + assert_invalid_spec("master^{/((}"); test_object("master^{/anoth}", "5b5b025afb0b4c913b4c338a42934a3863bf3644"); test_object("master^{/Merge}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); @@ -354,8 +357,9 @@ void test_refs_revparse__date(void) void test_refs_revparse__colon(void) { - cl_git_fail(git_revparse_single(&g_obj, g_repo, ":/")); - cl_git_fail(git_revparse_single(&g_obj, g_repo, ":2:README")); + assert_invalid_spec(":/"); + assert_invalid_spec("point_to_blob:readme.txt"); + cl_git_fail(git_revparse_single(&g_obj, g_repo, ":2:README")); /* Not implemented */ test_object(":/not found in any commit", NULL); test_object("subtrees:ab/42.txt", NULL); @@ -445,11 +449,8 @@ void test_refs_revparse__disambiguation(void) void test_refs_revparse__a_too_short_objectid_returns_EAMBIGUOUS(void) { - int result; - - result = git_revparse_single(&g_obj, g_repo, "e90"); - - cl_assert_equal_i(GIT_EAMBIGUOUS, result); + cl_assert_equal_i( + GIT_EAMBIGUOUS, git_revparse_single(&g_obj, g_repo, "e90")); } void test_refs_revparse__issue_994(void) diff --git a/tests-clar/refs/update.c b/tests-clar/refs/update.c new file mode 100644 index 000000000..6c2107ee2 --- /dev/null +++ b/tests-clar/refs/update.c @@ -0,0 +1,29 @@ +#include "clar_libgit2.h" + +#include "refs.h" + +static git_repository *g_repo; + +void test_refs_update__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo.git"); +} + +void test_refs_update__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_refs_update__updating_the_target_of_a_symref_with_an_invalid_name_returns_EINVALIDSPEC(void) +{ + git_reference *head; + + cl_git_pass(git_reference_lookup(&head, g_repo, GIT_HEAD_FILE)); + + cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head)); + + cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_symbolic_set_target( + head, "refs/heads/inv@{id")); + + git_reference_free(head); +} diff --git a/tests-clar/repo/init.c b/tests-clar/repo/init.c index 3b14c97f2..c0acbed5a 100644 --- a/tests-clar/repo/init.c +++ b/tests-clar/repo/init.c @@ -304,8 +304,7 @@ void test_repo_init__sets_logAllRefUpdates_according_to_type_of_repository(void) void test_repo_init__extended_0(void) { - git_repository_init_options opts; - memset(&opts, 0, sizeof(opts)); + git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; /* without MKDIR this should fail */ cl_git_fail(git_repository_init_ext(&_repo, "extended", &opts)); @@ -327,8 +326,7 @@ void test_repo_init__extended_1(void) git_reference *ref; git_remote *remote; struct stat st; - git_repository_init_options opts; - memset(&opts, 0, sizeof(opts)); + git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_NO_DOTGIT_DIR; @@ -367,8 +365,7 @@ void test_repo_init__extended_1(void) void test_repo_init__extended_with_template(void) { - git_repository_init_options opts; - memset(&opts, 0, sizeof(opts)); + git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_BARE; opts.template_path = cl_fixture("template"); diff --git a/tests-clar/resources/deprecated-mode.git/objects/08/10fb7818088ff5ac41ee49199b51473b1bd6c7 b/tests-clar/resources/deprecated-mode.git/objects/08/10fb7818088ff5ac41ee49199b51473b1bd6c7 Binary files differnew file mode 100644 index 000000000..52d56936a --- /dev/null +++ b/tests-clar/resources/deprecated-mode.git/objects/08/10fb7818088ff5ac41ee49199b51473b1bd6c7 diff --git a/tests-clar/status/ignore.c b/tests-clar/status/ignore.c index 27f9d85b9..e2e4aaf18 100644 --- a/tests-clar/status/ignore.c +++ b/tests-clar/status/ignore.c @@ -180,9 +180,6 @@ void test_status_ignore__subdirectories(void) { status_entry_single st; int ignored; - git_status_options opts; - - GIT_UNUSED(opts); g_repo = cl_git_sandbox_init("empty_standard_repo"); @@ -216,11 +213,6 @@ void test_status_ignore__subdirectories(void) cl_git_mkfile( "empty_standard_repo/test/ignore_me/file", "I'm going to be ignored!"); - opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; - opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED | - GIT_STATUS_OPT_INCLUDE_UNTRACKED | - GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; - memset(&st, 0, sizeof(st)); cl_git_pass(git_status_foreach(g_repo, cb_status__single, &st)); cl_assert_equal_i(2, st.count); diff --git a/tests-clar/status/worktree.c b/tests-clar/status/worktree.c index 838a04377..a1f7f8684 100644 --- a/tests-clar/status/worktree.c +++ b/tests-clar/status/worktree.c @@ -111,7 +111,7 @@ void test_status_worktree__swap_subdir_and_file(void) status_entry_counts counts; git_repository *repo = cl_git_sandbox_init("status"); git_index *index; - git_status_options opts; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; bool ignore_case; cl_git_pass(git_repository_index(&index, repo)); @@ -133,7 +133,6 @@ void test_status_worktree__swap_subdir_and_file(void) counts.expected_paths = ignore_case ? entry_paths3_icase : entry_paths3; counts.expected_statuses = ignore_case ? entry_statuses3_icase : entry_statuses3; - memset(&opts, 0, sizeof(opts)); opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_INCLUDE_IGNORED; @@ -150,7 +149,7 @@ void test_status_worktree__swap_subdir_with_recurse_and_pathspec(void) { status_entry_counts counts; git_repository *repo = cl_git_sandbox_init("status"); - git_status_options opts; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; /* first alter the contents of the worktree */ cl_git_pass(p_rename("status/current_file", "status/swap")); @@ -167,7 +166,6 @@ void test_status_worktree__swap_subdir_with_recurse_and_pathspec(void) counts.expected_paths = entry_paths4; counts.expected_statuses = entry_statuses4; - memset(&opts, 0, sizeof(opts)); opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; /* TODO: set pathspec to "current_file" eventually */ @@ -691,7 +689,7 @@ void test_status_worktree__filemode_changes(void) { git_repository *repo = cl_git_sandbox_init("filemodes"); status_entry_counts counts; - git_status_options opts; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; git_config *cfg; /* overwrite stored filemode with platform appropriate value */ @@ -708,7 +706,6 @@ void test_status_worktree__filemode_changes(void) filemode_statuses[i] = GIT_STATUS_CURRENT; } - memset(&opts, 0, sizeof(opts)); opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_INCLUDE_IGNORED | GIT_STATUS_OPT_INCLUDE_UNMODIFIED; @@ -746,7 +743,7 @@ static int cb_status__expected_path(const char *p, unsigned int s, void *payload void test_status_worktree__disable_pathspec_match(void) { git_repository *repo; - git_status_options opts; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; char *file_with_bracket = "LICENSE[1].md", *imaginary_file_with_bracket = "LICENSE[1-2].md"; @@ -754,7 +751,6 @@ void test_status_worktree__disable_pathspec_match(void) cl_git_mkfile("pathspec/LICENSE[1].md", "screaming bracket\n"); cl_git_mkfile("pathspec/LICENSE1.md", "no bracket\n"); - memset(&opts, 0, sizeof(opts)); opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH; opts.pathspec.count = 1; @@ -843,3 +839,79 @@ void test_status_worktree__line_endings_dont_count_as_changes_with_autocrlf(void cl_assert_equal_i(GIT_STATUS_CURRENT, status); } + +void test_status_worktree__conflicted_item(void) +{ + git_repository *repo = cl_git_sandbox_init("status"); + git_index *index; + unsigned int status; + git_index_entry ancestor_entry, our_entry, their_entry; + + memset(&ancestor_entry, 0x0, sizeof(git_index_entry)); + memset(&our_entry, 0x0, sizeof(git_index_entry)); + memset(&their_entry, 0x0, sizeof(git_index_entry)); + + ancestor_entry.path = "modified_file"; + git_oid_fromstr(&ancestor_entry.oid, + "452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); + + our_entry.path = "modified_file"; + git_oid_fromstr(&our_entry.oid, + "452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); + + their_entry.path = "modified_file"; + git_oid_fromstr(&their_entry.oid, + "452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); + + cl_git_pass(git_status_file(&status, repo, "modified_file")); + cl_assert_equal_i(GIT_STATUS_WT_MODIFIED, status); + + cl_git_pass(git_repository_index(&index, repo)); + cl_git_pass(git_index_conflict_add(index, &ancestor_entry, + &our_entry, &their_entry)); + + cl_git_pass(git_status_file(&status, repo, "modified_file")); + cl_assert_equal_i(GIT_STATUS_WT_MODIFIED, status); + + git_index_free(index); +} + +void test_status_worktree__conflict_with_diff3(void) +{ + git_repository *repo = cl_git_sandbox_init("status"); + git_index *index; + unsigned int status; + git_index_entry ancestor_entry, our_entry, their_entry; + + memset(&ancestor_entry, 0x0, sizeof(git_index_entry)); + memset(&our_entry, 0x0, sizeof(git_index_entry)); + memset(&their_entry, 0x0, sizeof(git_index_entry)); + + ancestor_entry.path = "modified_file"; + git_oid_fromstr(&ancestor_entry.oid, + "452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); + + our_entry.path = "modified_file"; + git_oid_fromstr(&our_entry.oid, + "452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); + + their_entry.path = "modified_file"; + git_oid_fromstr(&their_entry.oid, + "452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); + + cl_git_pass(git_status_file(&status, repo, "modified_file")); + cl_assert_equal_i(GIT_STATUS_WT_MODIFIED, status); + + cl_git_pass(git_repository_index(&index, repo)); + + cl_git_pass(git_index_remove(index, "modified_file", 0)); + cl_git_pass(git_index_conflict_add(index, &ancestor_entry, + &our_entry, &their_entry)); + + cl_git_pass(git_status_file(&status, repo, "modified_file")); + + cl_assert_equal_i(GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_NEW, status); + + git_index_free(index); +} + |
