diff --git a/include/git2.h b/include/git2.h index 1711ff8be..7d053c4af 100644 --- a/include/git2.h +++ b/include/git2.h @@ -40,7 +40,7 @@ #include "git2/net.h" #include "git2/status.h" #include "git2/indexer.h" - +#include "git2/submodule.h" #include "git2/notes.h" #endif diff --git a/include/git2/diff.h b/include/git2/diff.h index 0e7c02fd0..0c9f620c1 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -39,7 +39,9 @@ enum { GIT_DIFF_IGNORE_SUBMODULES = (1 << 5), GIT_DIFF_PATIENCE = (1 << 6), GIT_DIFF_INCLUDE_IGNORED = (1 << 7), - GIT_DIFF_INCLUDE_UNTRACKED = (1 << 8) + GIT_DIFF_INCLUDE_UNTRACKED = (1 << 8), + GIT_DIFF_INCLUDE_UNMODIFIED = (1 << 9), + GIT_DIFF_RECURSE_UNTRACKED_DIRS = (1 << 10), }; /** diff --git a/include/git2/status.h b/include/git2/status.h index 5c45dae1e..f5fc95f0a 100644 --- a/include/git2/status.h +++ b/include/git2/status.h @@ -46,6 +46,79 @@ GIT_BEGIN_DECL */ GIT_EXTERN(int) git_status_foreach(git_repository *repo, int (*callback)(const char *, unsigned int, void *), void *payload); +/** + * Select the files on which to report status. + * + * - GIT_STATUS_SHOW_INDEX_AND_WORKDIR is the default. This is the + * rough equivalent of `git status --porcelain` where each file + * will receive a callback indicating its status in the index and + * in the workdir. + * - GIT_STATUS_SHOW_INDEX_ONLY will only make callbacks for index + * side of status. The status of the index contents relative to + * the HEAD will be given. + * - GIT_STATUS_SHOW_WORKDIR_ONLY will only make callbacks for the + * workdir side of status, reporting the status of workdir content + * relative to the index. + * - GIT_STATUS_SHOW_INDEX_THEN_WORKDIR behaves like index-only + * followed by workdir-only, causing two callbacks to be issued + * per file (first index then workdir). This is slightly more + * efficient than making separate calls. This makes it easier to + * emulate the output of a plain `git status`. + */ +typedef enum { + GIT_STATUS_SHOW_INDEX_AND_WORKDIR = 0, + GIT_STATUS_SHOW_INDEX_ONLY = 1, + GIT_STATUS_SHOW_WORKDIR_ONLY = 2, + GIT_STATUS_SHOW_INDEX_THEN_WORKDIR = 3, +} git_status_show_t; + +/** + * Flags to control status callbacks + * + * - GIT_STATUS_OPT_INCLUDE_UNTRACKED says that callbacks should + * be made on untracked files. These will only be made if the + * workdir files are included in the status "show" option. + * - GIT_STATUS_OPT_INCLUDE_IGNORED says that ignored files should + * get callbacks. Again, these callbacks will only be made if + * the workdir files are included in the status "show" option. + * Right now, there is no option to include all files in + * directories that are ignored completely. + * - GIT_STATUS_OPT_INCLUDE_UNMODIFIED indicates that callback + * should be made even on unmodified files. + * - GIT_STATUS_OPT_EXCLUDE_SUBMODULES indicates that directories + * which appear to be submodules should just be skipped over. + * - GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS indicates that the + * contents of untracked directories should be included in the + * status. Normally if an entire directory is new, then just + * the top-level directory will be included (with a trailing + * slash on the entry name). Given this flag, the directory + * itself will not be included, but all the files in it will. + */ +#define GIT_STATUS_OPT_INCLUDE_UNTRACKED (1 << 0) +#define GIT_STATUS_OPT_INCLUDE_IGNORED (1 << 1) +#define GIT_STATUS_OPT_INCLUDE_UNMODIFIED (1 << 2) +#define GIT_STATUS_OPT_EXCLUDE_SUBMODULES (1 << 3) +#define GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS (1 << 4) + +/** + * Options to control how callbacks will be made by + * `git_status_foreach_ext()`. + */ +typedef struct { + git_status_show_t show; + unsigned int flags; + git_strarray pathspec; +} git_status_options; + +/** + * Gather file status information and run callbacks as requested. + */ +GIT_EXTERN(int) git_status_foreach_ext( + git_repository *repo, + git_status_options *opts, + int (*callback)(const char *, unsigned int, void *), + void *payload); + /** * Get file status for a single file * diff --git a/include/git2/submodule.h b/include/git2/submodule.h new file mode 100644 index 000000000..930168275 --- /dev/null +++ b/include/git2/submodule.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2012 the libgit2 contributors + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_git_submodule_h__ +#define INCLUDE_git_submodule_h__ + +#include "common.h" +#include "types.h" +#include "oid.h" + +/** + * @file git2/submodule.h + * @brief Git submodule management utilities + * @defgroup git_submodule Git submodule management routines + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +typedef enum { + GIT_SUBMODULE_UPDATE_CHECKOUT = 0, + GIT_SUBMODULE_UPDATE_REBASE = 1, + GIT_SUBMODULE_UPDATE_MERGE = 2 +} git_submodule_update_t; + +typedef enum { + GIT_SUBMODULE_IGNORE_ALL = 0, /* never dirty */ + GIT_SUBMODULE_IGNORE_DIRTY = 1, /* only dirty if HEAD moved */ + GIT_SUBMODULE_IGNORE_UNTRACKED = 2, /* dirty if tracked files change */ + GIT_SUBMODULE_IGNORE_NONE = 3 /* any change or untracked == dirty */ +} git_submodule_ignore_t; + +/** + * Description of submodule + * + * This record describes a submodule found in a repository. There + * should be an entry for every submodule found in the HEAD and for + * every submodule described in .gitmodules. The fields are as follows: + * + * - `name` is the name of the submodule from .gitmodules. + * - `path` is the path to the submodule from the repo working directory. + * It is almost always the same as `name`. + * - `url` is the url for the submodule. + * - `oid` is the HEAD SHA1 for the submodule. + * - `update` is a value from above - see gitmodules(5) update. + * - `ignore` is a value from above - see gitmodules(5) ignore. + * - `fetch_recurse` is 0 or 1 - see gitmodules(5) fetchRecurseSubmodules. + * - `refcount` is for internal use. + * + * If the submodule has been added to .gitmodules but not yet git added, + * then the `oid` will be zero. If the submodule has been deleted, but + * the delete has not been committed yet, then the `oid` will be set, but + * the `url` will be NULL. + */ +typedef struct { + char *name; + char *path; + char *url; + git_oid oid; /* sha1 of submodule HEAD ref or zero if not committed */ + git_submodule_update_t update; + git_submodule_ignore_t ignore; + int fetch_recurse; + int refcount; +} git_submodule; + +/** + * Iterate over all submodules of a repository. + * + * @param repo The repository + * @param callback Function to be called with the name of each submodule. + * Return a non-zero value to terminate the iteration. + * @param payload Extra data to pass to callback + * @return 0 on success, -1 on error, or non-zero return value of callback + */ +GIT_EXTERN(int) git_submodule_foreach( + git_repository *repo, + int (*callback)(const char *name, void *payload), + void *payload); + +/** + * Lookup submodule information by name or path. + * + * Given either the submodule name or path (they are ususally the same), + * this returns a structure describing the submodule. If the submodule + * does not exist, this will return GIT_ENOTFOUND and set the submodule + * pointer to NULL. + * + * @param submodule Pointer to submodule description object pointer.. + * @param repo The repository. + * @param name The name of the submodule. Trailing slashes will be ignored. + * @return 0 on success, GIT_ENOTFOUND if submodule does not exist, -1 on error + */ +GIT_EXTERN(int) git_submodule_lookup( + git_submodule **submodule, + git_repository *repo, + const char *name); + +/** @} */ +GIT_END_DECL +#endif diff --git a/src/attr.c b/src/attr.c index 2c5add34f..5cf96acf7 100644 --- a/src/attr.c +++ b/src/attr.c @@ -196,7 +196,8 @@ bool git_attr_cache__is_cached(git_repository *repo, const char *path) const char *cache_key = path; if (repo && git__prefixcmp(cache_key, git_repository_workdir(repo)) == 0) cache_key += strlen(git_repository_workdir(repo)); - return (git_hashtable_lookup(repo->attrcache.files, cache_key) != NULL); + return (git_hashtable_lookup( + git_repository_attr_cache(repo)->files, cache_key) != NULL); } int git_attr_cache__lookup_or_create_file( @@ -207,7 +208,7 @@ int git_attr_cache__lookup_or_create_file( git_attr_file **file_ptr) { int error; - git_attr_cache *cache = &repo->attrcache; + git_attr_cache *cache = git_repository_attr_cache(repo); git_attr_file *file = NULL; if ((file = git_hashtable_lookup(cache->files, key)) != NULL) { @@ -293,7 +294,6 @@ static int collect_attr_files( { int error; git_buf dir = GIT_BUF_INIT; - git_config *cfg; const char *workdir = git_repository_workdir(repo); attr_walk_up_info info; @@ -312,7 +312,8 @@ static int collect_attr_files( * - $GIT_PREFIX/etc/gitattributes */ - error = push_attrs(repo, files, repo->path_repository, GIT_ATTR_FILE_INREPO); + error = push_attrs( + repo, files, git_repository_path(repo), GIT_ATTR_FILE_INREPO); if (error < 0) goto cleanup; @@ -322,22 +323,18 @@ static int collect_attr_files( if (error < 0) goto cleanup; - if (!(error = git_repository_config(&cfg, repo))) { - const char *core_attribs = NULL; - git_config_get_string(cfg, GIT_ATTR_CONFIG, &core_attribs); - git_clearerror(); /* don't care if attributesfile is not set */ - if (core_attribs) - error = push_attrs(repo, files, NULL, core_attribs); - git_config_free(cfg); + if (git_repository_attr_cache(repo)->cfg_attr_file != NULL) { + error = push_attrs( + repo, files, NULL, git_repository_attr_cache(repo)->cfg_attr_file); + if (error < 0) + goto cleanup; } - if (!error) { - error = git_futils_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM); - if (!error) - error = push_attrs(repo, files, NULL, dir.ptr); - else if (error == GIT_ENOTFOUND) - error = 0; - } + error = git_futils_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM); + if (!error) + error = push_attrs(repo, files, NULL, dir.ptr); + else if (error == GIT_ENOTFOUND) + error = 0; cleanup: if (error < 0) @@ -350,11 +347,26 @@ static int collect_attr_files( int git_attr_cache__init(git_repository *repo) { - git_attr_cache *cache = &repo->attrcache; + git_attr_cache *cache = git_repository_attr_cache(repo); + git_config *cfg; if (cache->initialized) return 0; + /* cache config settings for attributes and ignores */ + if (git_repository_config(&cfg, repo) < 0) + return -1; + if (git_config_get_string(cfg, GIT_ATTR_CONFIG, &cache->cfg_attr_file)) { + giterr_clear(); + cache->cfg_attr_file = NULL; + } + if (git_config_get_string(cfg, GIT_IGNORE_CONFIG, &cache->cfg_excl_file)) { + giterr_clear(); + cache->cfg_excl_file = NULL; + } + git_config_free(cfg); + + /* allocate hashtable for attribute and ignore file contents */ if (cache->files == NULL) { cache->files = git_hashtable_alloc( 8, git_hash__strhash_cb, git_hash__strcmp_cb); @@ -362,6 +374,7 @@ int git_attr_cache__init(git_repository *repo) return -1; } + /* allocate hashtable for attribute macros */ if (cache->macros == NULL) { cache->macros = git_hashtable_alloc( 8, git_hash__strhash_cb, git_hash__strcmp_cb); @@ -378,30 +391,30 @@ int git_attr_cache__init(git_repository *repo) void git_attr_cache_flush( git_repository *repo) { + git_hashtable *table; + if (!repo) return; - if (repo->attrcache.files) { + if ((table = git_repository_attr_cache(repo)->files) != NULL) { git_attr_file *file; - GIT_HASHTABLE_FOREACH_VALUE(repo->attrcache.files, file, - git_attr_file__free(file)); + GIT_HASHTABLE_FOREACH_VALUE(table, file, git_attr_file__free(file)); + git_hashtable_free(table); - git_hashtable_free(repo->attrcache.files); - repo->attrcache.files = NULL; + git_repository_attr_cache(repo)->files = NULL; } - if (repo->attrcache.macros) { + if ((table = git_repository_attr_cache(repo)->macros) != NULL) { git_attr_rule *rule; - GIT_HASHTABLE_FOREACH_VALUE(repo->attrcache.macros, rule, - git_attr_rule__free(rule)); + GIT_HASHTABLE_FOREACH_VALUE(table, rule, git_attr_rule__free(rule)); + git_hashtable_free(table); - git_hashtable_free(repo->attrcache.macros); - repo->attrcache.macros = NULL; + git_repository_attr_cache(repo)->macros = NULL; } - repo->attrcache.initialized = 0; + git_repository_attr_cache(repo)->initialized = 0; } int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro) @@ -411,5 +424,5 @@ int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro) return 0; return git_hashtable_insert( - repo->attrcache.macros, macro->match.pattern, macro); + git_repository_attr_cache(repo)->macros, macro->match.pattern, macro); } diff --git a/src/attr.h b/src/attr.h index eccda0ed7..350c0ebad 100644 --- a/src/attr.h +++ b/src/attr.h @@ -9,10 +9,15 @@ #include "attr_file.h" +#define GIT_ATTR_CONFIG "core.attributesfile" +#define GIT_IGNORE_CONFIG "core.excludesfile" + typedef struct { int initialized; git_hashtable *files; /* hash path to git_attr_file of rules */ git_hashtable *macros; /* hash name to vector */ + const char *cfg_attr_file; /* cached value of core.attributesfile */ + const char *cfg_excl_file; /* cached value of core.excludesfile */ } git_attr_cache; extern int git_attr_cache__init(git_repository *repo); diff --git a/src/attr_file.c b/src/attr_file.c index 646bd044c..6568313e5 100644 --- a/src/attr_file.c +++ b/src/attr_file.c @@ -495,8 +495,8 @@ int git_attr_assignment__parse( /* expand macros (if given a repo with a macro cache) */ if (repo != NULL && assign->value == git_attr__true) { - git_attr_rule *macro = - git_hashtable_lookup(repo->attrcache.macros, assign->name); + git_attr_rule *macro = git_hashtable_lookup( + git_repository_attr_cache(repo)->macros, assign->name); if (macro != NULL) { unsigned int i; diff --git a/src/attr_file.h b/src/attr_file.h index 6284c5386..53e479ad9 100644 --- a/src/attr_file.h +++ b/src/attr_file.h @@ -14,7 +14,6 @@ #define GIT_ATTR_FILE ".gitattributes" #define GIT_ATTR_FILE_INREPO "info/attributes" #define GIT_ATTR_FILE_SYSTEM "gitattributes" -#define GIT_ATTR_CONFIG "core.attributesfile" #define GIT_ATTR_FNMATCH_NEGATIVE (1U << 0) #define GIT_ATTR_FNMATCH_DIRECTORY (1U << 1) diff --git a/src/buffer.c b/src/buffer.c index ec0302b9a..c23803564 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -256,8 +256,8 @@ int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...) { va_list ap; int i; - size_t total_size = 0; - char *out; + size_t total_size = 0, original_size = buf->size; + char *out, *original = buf->ptr; if (buf->size > 0 && buf->ptr[buf->size - 1] != separator) ++total_size; /* space for initial separator */ @@ -281,8 +281,9 @@ int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...) va_end(ap); /* expand buffer if needed */ - if (total_size > 0 && - git_buf_grow(buf, buf->size + total_size + 1) < 0) + if (total_size == 0) + return 0; + if (git_buf_grow(buf, buf->size + total_size + 1) < 0) return -1; out = buf->ptr + buf->size; @@ -300,12 +301,23 @@ int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...) if (!segment) continue; + /* deal with join that references buffer's original content */ + if (segment >= original && segment < original + original_size) { + size_t offset = (segment - original); + segment = buf->ptr + offset; + segment_len = original_size - offset; + } else { + segment_len = strlen(segment); + } + /* skip leading separators */ if (out > buf->ptr && out[-1] == separator) - while (*segment == separator) segment++; + while (segment_len > 0 && *segment == separator) { + segment++; + segment_len--; + } /* copy over next buffer */ - segment_len = strlen(segment); if (segment_len > 0) { memmove(out, segment, segment_len); out += segment_len; diff --git a/src/config.c b/src/config.c index 77598d6a6..250bfa652 100644 --- a/src/config.c +++ b/src/config.c @@ -140,7 +140,7 @@ int git_config_add_file(git_config *cfg, git_config_file *file, int priority) int git_config_foreach(git_config *cfg, int (*fn)(const char *, const char *, void *), void *data) { - int ret = GIT_SUCCESS; + int ret = 0; unsigned int i; file_internal *internal; git_config_file *file; @@ -201,25 +201,25 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value) return file->set(file, name, value); } -static int parse_bool(int *out, const char *value) +int git_config_parse_bool(int *out, const char *value) { /* A missing value means true */ if (value == NULL) { *out = 1; - return GIT_SUCCESS; + return 0; } if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on")) { *out = 1; - return GIT_SUCCESS; + return 0; } if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off")) { *out = 0; - return GIT_SUCCESS; + return 0; } return GIT_EINVALIDTYPE; @@ -283,47 +283,59 @@ static int parse_int32(int32_t *out, const char *value) /*********** * Getters ***********/ -int git_config_get_mapped(git_config *cfg, const char *name, git_cvar_map *maps, size_t map_n, int *out) +int git_config_lookup_map_value( + git_cvar_map *maps, size_t map_n, const char *value, int *out) { size_t i; - const char *value; - int error; - error = git_config_get_string(cfg, name, &value); - if (error < GIT_SUCCESS) - return error; + if (!value) + return GIT_ENOTFOUND; for (i = 0; i < map_n; ++i) { git_cvar_map *m = maps + i; switch (m->cvar_type) { - case GIT_CVAR_FALSE: - case GIT_CVAR_TRUE: { - int bool_val; + case GIT_CVAR_FALSE: + case GIT_CVAR_TRUE: { + int bool_val; - if (parse_bool(&bool_val, value) == 0 && - bool_val == (int)m->cvar_type) { - *out = m->map_value; - return 0; - } - - break; + if (git_config_parse_bool(&bool_val, value) == 0 && + bool_val == (int)m->cvar_type) { + *out = m->map_value; + return 0; } + break; + } - case GIT_CVAR_INT32: - if (parse_int32(out, value) == 0) - return 0; + case GIT_CVAR_INT32: + if (parse_int32(out, value) == 0) + return 0; + break; - break; - - case GIT_CVAR_STRING: - if (strcasecmp(value, m->str_match) == 0) { - *out = m->map_value; - return 0; - } + case GIT_CVAR_STRING: + if (strcasecmp(value, m->str_match) == 0) { + *out = m->map_value; + return 0; + } + break; } } + return GIT_ENOTFOUND; +} + +int git_config_get_mapped(git_config *cfg, const char *name, git_cvar_map *maps, size_t map_n, int *out) +{ + const char *value; + int error; + + error = git_config_get_string(cfg, name, &value); + if (error < 0) + return error; + + if (!git_config_lookup_map_value(maps, map_n, value, out)) + return 0; + giterr_set(GITERR_CONFIG, "Failed to map the '%s' config variable with a valid value", name); return -1; @@ -372,7 +384,7 @@ int git_config_get_bool(git_config *cfg, const char *name, int *out) if (ret < 0) return ret; - if (parse_bool(out, value) == 0) + if (git_config_parse_bool(out, value) == 0) return 0; if (parse_int32(out, value) == 0) { @@ -449,7 +461,7 @@ int git_config_set_multivar(git_config *cfg, const char *name, const char *regex internal = git_vector_get(&cfg->files, i - 1); file = internal->file; ret = file->set_multivar(file, name, regexp, value); - if (ret < GIT_SUCCESS && ret != GIT_ENOTFOUND) + if (ret < 0 && ret != GIT_ENOTFOUND) return ret; } diff --git a/src/config.h b/src/config.h index 59d1d9a26..82e98ce51 100644 --- a/src/config.h +++ b/src/config.h @@ -25,4 +25,9 @@ struct git_config { extern int git_config_find_global_r(git_buf *global_config_path); extern int git_config_find_system_r(git_buf *system_config_path); +extern int git_config_parse_bool(int *out, const char *bool_string); + +extern int git_config_lookup_map_value( + git_cvar_map *maps, size_t map_n, const char *value, int *out); + #endif diff --git a/src/config_file.c b/src/config_file.c index 077e2c03f..e16606512 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -192,6 +192,9 @@ static int file_foreach(git_config_file *backend, int (*fn)(const char *, const cvar_t *var; const char *key; + if (!b->values) + return 0; + GIT_HASHTABLE_FOREACH(b->values, key, var, do { if (fn(key, var->value, data) < 0) @@ -331,6 +334,7 @@ static int config_get_multivar( var = var->next; } while (var != NULL); + regfree(®ex); } else { /* no regex; go through all the variables */ do { diff --git a/src/config_file.h b/src/config_file.h new file mode 100644 index 000000000..0080b5713 --- /dev/null +++ b/src/config_file.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2012 the libgit2 contributors + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_config_file_h__ +#define INCLUDE_config_file_h__ + +#include "git2/config.h" + +GIT_INLINE(int) git_config_file_open(git_config_file *cfg) +{ + return cfg->open(cfg); +} + +GIT_INLINE(void) git_config_file_free(git_config_file *cfg) +{ + cfg->free(cfg); +} + +GIT_INLINE(int) git_config_file_foreach( + git_config_file *cfg, + int (*fn)(const char *key, const char *value, void *data), + void *data) +{ + return cfg->foreach(cfg, fn, data); +} + +#endif + diff --git a/src/diff.c b/src/diff.c index 69c944c63..54e8dd166 100644 --- a/src/diff.c +++ b/src/diff.c @@ -8,6 +8,7 @@ #include "git2/diff.h" #include "diff.h" #include "fileops.h" +#include "config.h" static void diff_delta__free(git_diff_delta *delta) { @@ -132,7 +133,17 @@ static int diff_delta__from_one( git_delta_t status, const git_index_entry *entry) { - git_diff_delta *delta = diff_delta__alloc(diff, status, entry->path); + git_diff_delta *delta; + + if (status == GIT_DELTA_IGNORED && + (diff->opts.flags & GIT_DIFF_INCLUDE_IGNORED) == 0) + return 0; + + if (status == GIT_DELTA_UNTRACKED && + (diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0) + return 0; + + delta = diff_delta__alloc(diff, status, entry->path); GITERR_CHECK_ALLOC(delta); /* This fn is just for single-sided diffs */ @@ -168,6 +179,10 @@ static int diff_delta__from_two( { git_diff_delta *delta; + if (status == GIT_DELTA_UNMODIFIED && + (diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0) + return 0; + if ((diff->opts.flags & GIT_DIFF_REVERSE) != 0) { const git_index_entry *temp = old; old = new; @@ -219,15 +234,38 @@ static int diff_delta__cmp(const void *a, const void *b) return val ? val : ((int)da->status - (int)db->status); } +static int config_bool(git_config *cfg, const char *name, int defvalue) +{ + int val = defvalue; + if (git_config_get_bool(cfg, name, &val) < 0) + giterr_clear(); + return val; +} + static git_diff_list *git_diff_list_alloc( git_repository *repo, const git_diff_options *opts) { + git_config *cfg; git_diff_list *diff = git__calloc(1, sizeof(git_diff_list)); if (diff == NULL) return NULL; diff->repo = repo; + /* load config values that affect diff behavior */ + if (git_repository_config(&cfg, repo) < 0) + goto fail; + if (config_bool(cfg, "core.symlinks", 1)) + diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_HAS_SYMLINKS; + if (config_bool(cfg, "core.ignorestat", 0)) + diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_ASSUME_UNCHANGED; + if (config_bool(cfg, "core.filemode", 1)) + diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_EXEC_BIT; + if (config_bool(cfg, "core.trustctime", 1)) + diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_CTIME; + /* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */ + git_config_free(cfg); + if (opts == NULL) return diff; @@ -238,10 +276,8 @@ static git_diff_list *git_diff_list_alloc( diff->opts.dst_prefix = diff_strdup_prefix( opts->dst_prefix ? opts->dst_prefix : DIFF_DST_PREFIX_DEFAULT); - if (!diff->opts.src_prefix || !diff->opts.dst_prefix) { - git__free(diff); - return NULL; - } + if (!diff->opts.src_prefix || !diff->opts.dst_prefix) + goto fail; if (diff->opts.flags & GIT_DIFF_REVERSE) { char *swap = diff->opts.src_prefix; @@ -249,16 +285,19 @@ static git_diff_list *git_diff_list_alloc( diff->opts.dst_prefix = swap; } - if (git_vector_init(&diff->deltas, 0, diff_delta__cmp) < 0) { - git__free(diff->opts.src_prefix); - git__free(diff->opts.dst_prefix); - git__free(diff); - return NULL; - } + if (git_vector_init(&diff->deltas, 0, diff_delta__cmp) < 0) + goto fail; /* TODO: do something safe with the pathspec strarray */ return diff; + +fail: + git_vector_free(&diff->deltas); + git__free(diff->opts.src_prefix); + git__free(diff->opts.dst_prefix); + git__free(diff); + return NULL; } void git_diff_list_free(git_diff_list *diff) @@ -312,6 +351,8 @@ static int oid_for_workdir_item( return result; } +#define EXEC_BIT_MASK 0000111 + static int maybe_modified( git_iterator *old, const git_index_entry *oitem, @@ -320,53 +361,92 @@ static int maybe_modified( git_diff_list *diff) { git_oid noid, *use_noid = NULL; + git_delta_t status = GIT_DELTA_MODIFIED; + unsigned int omode = oitem->mode; + unsigned int nmode = nitem->mode; GIT_UNUSED(old); - /* support "assume unchanged" & "skip worktree" bits */ - if ((oitem->flags_extended & GIT_IDXENTRY_INTENT_TO_ADD) != 0 || - (oitem->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE) != 0) - return 0; + /* on platforms with no symlinks, promote plain files to symlinks */ + if (S_ISLNK(omode) && S_ISREG(nmode) && + !(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS)) + nmode = GIT_MODE_TYPE(omode) | (nmode & GIT_MODE_PERMS_MASK); - if (GIT_MODE_TYPE(oitem->mode) != GIT_MODE_TYPE(nitem->mode)) { + /* on platforms with no execmode, clear exec bit from comparisons */ + if (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_EXEC_BIT)) { + omode = omode & ~EXEC_BIT_MASK; + nmode = nmode & ~EXEC_BIT_MASK; + } + + /* support "assume unchanged" (badly, b/c we still stat everything) */ + if ((diff->diffcaps & GIT_DIFFCAPS_ASSUME_UNCHANGED) != 0) + status = (oitem->flags_extended & GIT_IDXENTRY_INTENT_TO_ADD) ? + GIT_DELTA_MODIFIED : GIT_DELTA_UNMODIFIED; + + /* support "skip worktree" index bit */ + else if ((oitem->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE) != 0) + status = GIT_DELTA_UNMODIFIED; + + /* if basic type of file changed, then split into delete and add */ + else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) { if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 || diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem) < 0) return -1; return 0; } - if (git_oid_cmp(&oitem->oid, &nitem->oid) == 0 && - oitem->mode == nitem->mode) - return 0; + /* if oids and modes match, then file is unmodified */ + else if (git_oid_cmp(&oitem->oid, &nitem->oid) == 0 && + omode == nmode) + status = GIT_DELTA_UNMODIFIED; + + /* if we have a workdir item with an unknown oid, check deeper */ + else if (git_oid_iszero(&nitem->oid) && new->type == GIT_ITERATOR_WORKDIR) { + /* TODO: add check against index file st_mtime to avoid racy-git */ - if (git_oid_iszero(&nitem->oid) && new->type == GIT_ITERATOR_WORKDIR) { /* if they files look exactly alike, then we'll assume the same */ if (oitem->file_size == nitem->file_size && - oitem->ctime.seconds == nitem->ctime.seconds && + (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) || + (oitem->ctime.seconds == nitem->ctime.seconds)) && oitem->mtime.seconds == nitem->mtime.seconds && - oitem->dev == nitem->dev && + (!(diff->diffcaps & GIT_DIFFCAPS_USE_DEV) || + (oitem->dev == nitem->dev)) && oitem->ino == nitem->ino && oitem->uid == nitem->uid && oitem->gid == nitem->gid) - return 0; + status = GIT_DELTA_UNMODIFIED; + + else if (S_ISGITLINK(nmode)) { + git_submodule *sub; + + if ((diff->opts.flags & GIT_DIFF_IGNORE_SUBMODULES) != 0) + status = GIT_DELTA_UNMODIFIED; + else if (git_submodule_lookup(&sub, diff->repo, nitem->path) < 0) + return -1; + else if (sub->ignore == GIT_SUBMODULE_IGNORE_ALL) + status = GIT_DELTA_UNMODIFIED; + else { + /* TODO: support other GIT_SUBMODULE_IGNORE values */ + status = GIT_DELTA_UNMODIFIED; + } + } /* TODO: check git attributes so we will not have to read the file * in if it is marked binary. */ - if (oid_for_workdir_item(diff->repo, nitem, &noid) < 0) + else if (oid_for_workdir_item(diff->repo, nitem, &noid) < 0) return -1; - if (git_oid_cmp(&oitem->oid, &noid) == 0 && - oitem->mode == nitem->mode) - return 0; + else if (git_oid_cmp(&oitem->oid, &noid) == 0 && + omode == nmode) + status = GIT_DELTA_UNMODIFIED; /* store calculated oid so we don't have to recalc later */ use_noid = &noid; } - return diff_delta__from_two( - diff, GIT_DELTA_MODIFIED, oitem, nitem, use_noid); + return diff_delta__from_two(diff, status, oitem, nitem, use_noid); } static int diff_from_iterators( @@ -418,7 +498,12 @@ static int diff_from_iterators( is_ignored = git_iterator_current_is_ignored(new); if (S_ISDIR(nitem->mode)) { - if (git__prefixcmp(oitem->path, nitem->path) == 0) { + /* recurse into directory if explicitly requested or + * if there are tracked items inside the directory + */ + if ((diff->opts.flags & GIT_DIFF_RECURSE_UNTRACKED_DIRS) || + (oitem && git__prefixcmp(oitem->path, nitem->path) == 0)) + { if (is_ignored) ignore_prefix = nitem->path; if (git_iterator_advance_into_directory(new, &nitem) < 0) @@ -538,23 +623,27 @@ int git_diff_merge( const git_diff_list *from) { int error = 0; - unsigned int i = 0, j = 0; git_vector onto_new; git_diff_delta *delta; + unsigned int i, j; + + assert(onto && from); + + if (!from->deltas.length) + return 0; if (git_vector_init(&onto_new, onto->deltas.length, diff_delta__cmp) < 0) return -1; - while (!error && (i < onto->deltas.length || j < from->deltas.length)) { - git_diff_delta *o = git_vector_get(&onto->deltas, i); - const git_diff_delta *f = git_vector_get_const(&from->deltas, j); - const char *opath = !o ? NULL : o->old.path ? o->old.path : o->new.path; - const char *fpath = !f ? NULL : f->old.path ? f->old.path : f->new.path; + for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) { + git_diff_delta *o = GIT_VECTOR_GET(&onto->deltas, i); + const git_diff_delta *f = GIT_VECTOR_GET(&from->deltas, j); + int cmp = !f ? -1 : !o ? 1 : strcmp(o->old.path, f->old.path); - if (opath && (!fpath || strcmp(opath, fpath) < 0)) { + if (cmp < 0) { delta = diff_delta__dup(o); i++; - } else if (fpath && (!opath || strcmp(opath, fpath) > 0)) { + } else if (cmp > 0) { delta = diff_delta__dup(f); j++; } else { @@ -563,10 +652,11 @@ int git_diff_merge( j++; } - error = !delta ? -1 : git_vector_insert(&onto_new, delta); + if ((error = !delta ? -1 : git_vector_insert(&onto_new, delta)) < 0) + break; } - if (error == 0) { + if (!error) { git_vector_swap(&onto->deltas, &onto_new); onto->new_src = from->new_src; } @@ -577,3 +667,4 @@ int git_diff_merge( return error; } + diff --git a/src/diff.h b/src/diff.h index 7d69199ea..b4a375586 100644 --- a/src/diff.h +++ b/src/diff.h @@ -13,12 +13,21 @@ #include "iterator.h" #include "repository.h" +enum { + GIT_DIFFCAPS_HAS_SYMLINKS = (1 << 0), /* symlinks on platform? */ + GIT_DIFFCAPS_ASSUME_UNCHANGED = (1 << 1), /* use stat? */ + GIT_DIFFCAPS_TRUST_EXEC_BIT = (1 << 2), /* use st_mode exec bit? */ + GIT_DIFFCAPS_TRUST_CTIME = (1 << 3), /* use st_ctime? */ + GIT_DIFFCAPS_USE_DEV = (1 << 4), /* use st_dev? */ +}; + struct git_diff_list { git_repository *repo; git_diff_options opts; git_vector deltas; /* vector of git_diff_file_delta */ git_iterator_type_t old_src; git_iterator_type_t new_src; + uint32_t diffcaps; }; #endif diff --git a/src/diff_output.c b/src/diff_output.c index 638cabca5..f4c214314 100644 --- a/src/diff_output.c +++ b/src/diff_output.c @@ -314,7 +314,8 @@ int git_diff_foreach( git_blob *old_blob = NULL, *new_blob = NULL; git_map old_data, new_data; - if (delta->status == GIT_DELTA_UNMODIFIED) + if (delta->status == GIT_DELTA_UNMODIFIED && + (diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0) continue; if (delta->status == GIT_DELTA_IGNORED && @@ -377,7 +378,8 @@ int git_diff_foreach( */ if (git_oid_cmp(&delta->old.oid, &delta->new.oid) == 0) { delta->status = GIT_DELTA_UNMODIFIED; - goto cleanup; + if ((diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0) + goto cleanup; } } } diff --git a/src/fileops.c b/src/fileops.c index f1f820ab7..b3bb3890e 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -62,7 +62,18 @@ int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode int git_futils_creat_locked(const char *path, const mode_t mode) { - int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode); + int fd; + +#ifdef GIT_WIN32 + wchar_t* buf; + + buf = gitwin_to_utf16(path); + fd = _wopen(buf, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode); + git__free(buf); +#else + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode); +#endif + if (fd < 0) { giterr_set(GITERR_OS, "Failed to create locked file '%s'", path); return -1; diff --git a/src/ignore.c b/src/ignore.c index be00efd1b..1827eda82 100644 --- a/src/ignore.c +++ b/src/ignore.c @@ -1,11 +1,9 @@ #include "ignore.h" #include "path.h" -#include "git2/config.h" #define GIT_IGNORE_INTERNAL "[internal]exclude" #define GIT_IGNORE_FILE_INREPO "info/exclude" #define GIT_IGNORE_FILE ".gitignore" -#define GIT_IGNORE_CONFIG "core.excludesfile" static int load_ignore_file( git_repository *repo, const char *path, git_attr_file *ignores) @@ -73,7 +71,6 @@ static int push_one_ignore(void *ref, git_buf *path) int git_ignore__for_path(git_repository *repo, const char *path, git_ignores *ignores) { int error = 0; - git_config *cfg; const char *workdir = git_repository_workdir(repo); assert(ignores); @@ -104,26 +101,19 @@ int git_ignore__for_path(git_repository *repo, const char *path, git_ignores *ig /* load .git/info/exclude */ error = push_ignore(repo, &ignores->ign_global, - repo->path_repository, GIT_IGNORE_FILE_INREPO); + git_repository_path(repo), GIT_IGNORE_FILE_INREPO); if (error < 0) goto cleanup; /* load core.excludesfile */ - if ((error = git_repository_config(&cfg, repo)) == 0) { - const char *core_ignore; - error = git_config_get_string(cfg, GIT_IGNORE_CONFIG, &core_ignore); - if (error == 0 && core_ignore != NULL) - error = push_ignore(repo, &ignores->ign_global, NULL, core_ignore); - else { - error = 0; - giterr_clear(); /* don't care if attributesfile is not set */ - } - git_config_free(cfg); - } + if (git_repository_attr_cache(repo)->cfg_excl_file != NULL) + error = push_ignore(repo, &ignores->ign_global, NULL, + git_repository_attr_cache(repo)->cfg_excl_file); cleanup: if (error < 0) git_ignore__free(ignores); + return error; } diff --git a/src/iterator.c b/src/iterator.c index cc15b5f67..3a3be1755 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -9,6 +9,7 @@ #include "tree.h" #include "ignore.h" #include "buffer.h" +#include "git2/submodule.h" typedef struct tree_iterator_frame tree_iterator_frame; struct tree_iterator_frame { @@ -121,7 +122,6 @@ static int tree_iterator__advance( break; tree_iterator__pop_frame(ti); - git_buf_rtruncate_at_char(&ti->path, '/'); } if (te && entry_is_tree(te)) @@ -425,9 +425,25 @@ static int workdir_iterator__update_entry(workdir_iterator *wi) return 0; /* if error, ignore it and ignore file */ /* detect submodules */ - if (S_ISDIR(wi->entry.mode) && - git_path_contains(&wi->path, DOT_GIT) == true) - wi->entry.mode = S_IFGITLINK; + if (S_ISDIR(wi->entry.mode)) { + bool is_submodule = git_path_contains(&wi->path, DOT_GIT); + + /* if there is no .git, still check submodules data */ + if (!is_submodule) { + int res = git_submodule_lookup(NULL, wi->repo, wi->entry.path); + is_submodule = (res == 0); + if (res == GIT_ENOTFOUND) + giterr_clear(); + } + + /* if submodule, mark as GITLINK and remove trailing slash */ + if (is_submodule) { + size_t len = strlen(wi->entry.path); + assert(wi->entry.path[len - 1] == '/'); + wi->entry.path[len - 1] = '\0'; + wi->entry.mode = S_IFGITLINK; + } + } return 0; } @@ -485,7 +501,9 @@ int git_iterator_advance_into_directory( workdir_iterator *wi = (workdir_iterator *)iter; if (iter->type == GIT_ITERATOR_WORKDIR && - wi->entry.path && S_ISDIR(wi->entry.mode)) + wi->entry.path && + S_ISDIR(wi->entry.mode) && + !S_ISGITLINK(wi->entry.mode)) { if (workdir_iterator__expand_dir(wi) < 0) /* if error loading or if empty, skip the directory. */ diff --git a/src/odb_loose.c b/src/odb_loose.c index 085df428a..b593d1846 100644 --- a/src/odb_loose.c +++ b/src/odb_loose.c @@ -772,7 +772,7 @@ static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_ static int loose_backend__write(git_oid *oid, git_odb_backend *_backend, const void *data, size_t len, git_otype type) { - int error, header_len; + int error = 0, header_len; git_buf final_path = GIT_BUF_INIT; char header[64]; git_filebuf fbuf = GIT_FILEBUF_INIT; diff --git a/src/repository.c b/src/repository.c index 45bedcbe0..4e0f9d491 100644 --- a/src/repository.c +++ b/src/repository.c @@ -64,6 +64,7 @@ void git_repository_free(git_repository *repo) git_cache_free(&repo->objects); git_repository__refcache_free(&repo->references); git_attr_cache_flush(repo); + git_submodule_config_free(repo); git__free(repo->path_repository); git__free(repo->workdir); diff --git a/src/repository.h b/src/repository.h index b5dcc1340..178f29742 100644 --- a/src/repository.h +++ b/src/repository.h @@ -83,6 +83,7 @@ struct git_repository { git_cache objects; git_refcache references; git_attr_cache attrcache; + git_hashtable *submodules; char *path_repository; char *workdir; @@ -100,6 +101,11 @@ void git_object__free(void *object); int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end, const char *header); void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid); +GIT_INLINE(git_attr_cache *) git_repository_attr_cache(git_repository *repo) +{ + return &repo->attrcache; +} + /* * Weak pointers to repository internals. * @@ -120,4 +126,9 @@ int git_repository_index__weakptr(git_index **out, git_repository *repo); int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar); void git_repository__cvar_cache_clear(git_repository *repo); +/* + * Submodule cache + */ +extern void git_submodule_config_free(git_repository *repo); + #endif diff --git a/src/status.c b/src/status.c index 2221db3d9..7cd914f21 100644 --- a/src/status.c +++ b/src/status.c @@ -15,6 +15,209 @@ #include "repository.h" #include "ignore.h" +#include "git2/diff.h" +#include "diff.h" + +static int resolve_head_to_tree(git_tree **tree, git_repository *repo) +{ + git_reference *head = NULL; + git_object *obj = NULL; + + if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0) + return -1; + + if (git_reference_oid(head) == NULL) { + git_reference *resolved; + + if (git_reference_resolve(&resolved, head) < 0) { + /* cannot resolve HEAD - probably brand new repo */ + giterr_clear(); + git_reference_free(head); + return GIT_ENOTFOUND; + } + + git_reference_free(head); + head = resolved; + } + + if (git_object_lookup(&obj, repo, git_reference_oid(head), GIT_OBJ_ANY) < 0) + goto fail; + + git_reference_free(head); + + switch (git_object_type(obj)) { + case GIT_OBJ_TREE: + *tree = (git_tree *)obj; + break; + case GIT_OBJ_COMMIT: + if (git_commit_tree(tree, (git_commit *)obj) < 0) + goto fail; + git_object_free(obj); + break; + default: + goto fail; + } + + return 0; + +fail: + git_object_free(obj); + git_reference_free(head); + return -1; +} + +static unsigned int index_delta2status(git_delta_t index_status) +{ + unsigned int st = GIT_STATUS_CURRENT; + + switch (index_status) { + case GIT_DELTA_ADDED: + case GIT_DELTA_COPIED: + case GIT_DELTA_RENAMED: + st = GIT_STATUS_INDEX_NEW; + break; + case GIT_DELTA_DELETED: + st = GIT_STATUS_INDEX_DELETED; + break; + case GIT_DELTA_MODIFIED: + st = GIT_STATUS_INDEX_MODIFIED; + break; + default: + break; + } + + return st; +} + +static unsigned int workdir_delta2status(git_delta_t workdir_status) +{ + unsigned int st = GIT_STATUS_CURRENT; + + switch (workdir_status) { + case GIT_DELTA_ADDED: + case GIT_DELTA_COPIED: + case GIT_DELTA_RENAMED: + case GIT_DELTA_UNTRACKED: + st = GIT_STATUS_WT_NEW; + break; + case GIT_DELTA_DELETED: + st = GIT_STATUS_WT_DELETED; + break; + case GIT_DELTA_MODIFIED: + st = GIT_STATUS_WT_MODIFIED; + break; + case GIT_DELTA_IGNORED: + st = GIT_STATUS_IGNORED; + break; + default: + break; + } + + return st; +} + +int git_status_foreach_ext( + git_repository *repo, + git_status_options *opts, + int (*cb)(const char *, unsigned int, void *), + void *cbdata) +{ + int err = 0, cmp; + git_diff_options diffopt; + git_diff_list *idx2head = NULL, *wd2idx = NULL; + git_tree *head = NULL; + git_status_show_t show = + opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR; + git_diff_delta *i2h, *w2i; + unsigned int i, j, i_max, j_max; + + assert(show <= GIT_STATUS_SHOW_INDEX_THEN_WORKDIR); + + switch (resolve_head_to_tree(&head, repo)) { + case 0: break; + case GIT_ENOTFOUND: return 0; + default: return -1; + } + + memset(&diffopt, 0, sizeof(diffopt)); + memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec)); + + if ((opts->flags & GIT_STATUS_OPT_INCLUDE_UNTRACKED) != 0) + diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNTRACKED; + if ((opts->flags & GIT_STATUS_OPT_INCLUDE_IGNORED) != 0) + diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_IGNORED; + if ((opts->flags & GIT_STATUS_OPT_INCLUDE_UNMODIFIED) != 0) + diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNMODIFIED; + if ((opts->flags & GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS) != 0) + diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_UNTRACKED_DIRS; + /* TODO: support EXCLUDE_SUBMODULES flag */ + + if (show != GIT_STATUS_SHOW_WORKDIR_ONLY && + (err = git_diff_index_to_tree(repo, &diffopt, head, &idx2head)) < 0) + goto cleanup; + + if (show != GIT_STATUS_SHOW_INDEX_ONLY && + (err = git_diff_workdir_to_index(repo, &diffopt, &wd2idx)) < 0) + goto cleanup; + + if (show == GIT_STATUS_SHOW_INDEX_THEN_WORKDIR) { + for (i = 0; !err && i < idx2head->deltas.length; i++) { + i2h = GIT_VECTOR_GET(&idx2head->deltas, i); + err = cb(i2h->old.path, index_delta2status(i2h->status), cbdata); + } + git_diff_list_free(idx2head); + idx2head = NULL; + } + + i_max = idx2head ? idx2head->deltas.length : 0; + j_max = wd2idx ? wd2idx->deltas.length : 0; + + for (i = 0, j = 0; !err && (i < i_max || j < j_max); ) { + i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL; + w2i = wd2idx ? GIT_VECTOR_GET(&wd2idx->deltas,j) : NULL; + + cmp = !w2i ? -1 : !i2h ? 1 : strcmp(i2h->old.path, w2i->old.path); + + if (cmp < 0) { + err = cb(i2h->old.path, index_delta2status(i2h->status), cbdata); + i++; + } else if (cmp > 0) { + err = cb(w2i->old.path, workdir_delta2status(w2i->status), cbdata); + j++; + } else { + err = cb(i2h->old.path, index_delta2status(i2h->status) | + workdir_delta2status(w2i->status), cbdata); + i++; j++; + } + } + +cleanup: + git_tree_free(head); + git_diff_list_free(idx2head); + git_diff_list_free(wd2idx); + return err; +} + +int git_status_foreach( + git_repository *repo, + int (*callback)(const char *, unsigned int, void *), + void *payload) +{ + git_status_options opts; + + 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; + + return git_status_foreach_ext(repo, &opts, callback, payload); +} + + +/* + * the old stuff + */ + struct status_entry { git_index_time mtime; @@ -461,7 +664,7 @@ static int status_cmp(const void *a, const void *b) #define DEFAULT_SIZE 16 -int git_status_foreach( +int git_status_foreach_old( git_repository *repo, int (*callback)(const char *, unsigned int, void *), void *payload) diff --git a/src/submodule.c b/src/submodule.c new file mode 100644 index 000000000..907e43e88 --- /dev/null +++ b/src/submodule.c @@ -0,0 +1,376 @@ +/* + * Copyright (C) 2012 the libgit2 contributors + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "common.h" +#include "git2/config.h" +#include "git2/types.h" +#include "git2/repository.h" +#include "git2/index.h" +#include "git2/submodule.h" +#include "buffer.h" +#include "hashtable.h" +#include "vector.h" +#include "posix.h" +#include "config_file.h" +#include "config.h" +#include "repository.h" + +static git_cvar_map _sm_update_map[] = { + {GIT_CVAR_STRING, "checkout", GIT_SUBMODULE_UPDATE_CHECKOUT}, + {GIT_CVAR_STRING, "rebase", GIT_SUBMODULE_UPDATE_REBASE}, + {GIT_CVAR_STRING, "merge", GIT_SUBMODULE_UPDATE_MERGE} +}; + +static git_cvar_map _sm_ignore_map[] = { + {GIT_CVAR_STRING, "all", GIT_SUBMODULE_IGNORE_ALL}, + {GIT_CVAR_STRING, "dirty", GIT_SUBMODULE_IGNORE_DIRTY}, + {GIT_CVAR_STRING, "untracked", GIT_SUBMODULE_IGNORE_UNTRACKED}, + {GIT_CVAR_STRING, "none", GIT_SUBMODULE_IGNORE_NONE} +}; + +static uint32_t strhash_no_trailing_slash(const void *key, int hash_id) +{ + static uint32_t hash_seeds[GIT_HASHTABLE_HASHES] = { + 0x01010101, + 0x12345678, + 0xFEDCBA98 + }; + + size_t key_len = key ? strlen((const char *)key) : 0; + if (key_len > 0 && ((const char *)key)[key_len - 1] == '/') + key_len--; + + return git__hash(key, (int)key_len, hash_seeds[hash_id]); +} + +static int strcmp_no_trailing_slash(const void *a, const void *b) +{ + const char *astr = (const char *)a; + const char *bstr = (const char *)b; + size_t alen = a ? strlen(astr) : 0; + size_t blen = b ? strlen(bstr) : 0; + int cmp; + + if (alen > 0 && astr[alen - 1] == '/') + alen--; + if (blen > 0 && bstr[blen - 1] == '/') + blen--; + + cmp = strncmp(astr, bstr, min(alen, blen)); + if (cmp == 0) + cmp = (alen < blen) ? -1 : (alen > blen) ? 1 : 0; + + return cmp; +} + +static git_submodule *submodule_alloc(const char *name) +{ + git_submodule *sm = git__calloc(1, sizeof(git_submodule)); + if (sm == NULL) + return sm; + + sm->path = sm->name = git__strdup(name); + if (!sm->name) { + git__free(sm); + return NULL; + } + + return sm; +} + +static void submodule_release(git_submodule *sm, int decr) +{ + if (!sm) + return; + + sm->refcount -= decr; + + if (sm->refcount == 0) { + if (sm->name != sm->path) + git__free(sm->path); + git__free(sm->name); + git__free(sm->url); + git__free(sm); + } +} + +static int submodule_from_entry( + git_hashtable *smcfg, git_index_entry *entry) +{ + git_submodule *sm; + void *old_sm; + + sm = git_hashtable_lookup(smcfg, entry->path); + if (!sm) + sm = submodule_alloc(entry->path); + + git_oid_cpy(&sm->oid, &entry->oid); + + if (strcmp(sm->path, entry->path) != 0) { + if (sm->path != sm->name) { + git__free(sm->path); + sm->path = sm->name; + } + sm->path = git__strdup(entry->path); + if (!sm->path) + goto fail; + } + + if (git_hashtable_insert2(smcfg, sm->path, sm, &old_sm) < 0) + goto fail; + sm->refcount++; + + if (old_sm && ((git_submodule *)old_sm) != sm) { + /* TODO: log warning about multiple entrys for same submodule path */ + submodule_release(old_sm, 1); + } + + return 0; + +fail: + submodule_release(sm, 0); + return -1; +} + +static int submodule_from_config( + const char *key, const char *value, void *data) +{ + git_hashtable *smcfg = data; + const char *namestart; + const char *property; + git_buf name = GIT_BUF_INIT; + git_submodule *sm; + void *old_sm = NULL; + bool is_path; + + if (git__prefixcmp(key, "submodule.") != 0) + return 0; + + namestart = key + strlen("submodule."); + property = strrchr(namestart, '.'); + if (property == NULL) + return 0; + property++; + is_path = (strcmp(property, "path") == 0); + + if (git_buf_set(&name, namestart, property - namestart - 1) < 0) + return -1; + + sm = git_hashtable_lookup(smcfg, name.ptr); + if (!sm && is_path) + sm = git_hashtable_lookup(smcfg, value); + if (!sm) + sm = submodule_alloc(name.ptr); + if (!sm) + goto fail; + + if (strcmp(sm->name, name.ptr) != 0) { + assert(sm->path == sm->name); + sm->name = git_buf_detach(&name); + if (git_hashtable_insert2(smcfg, sm->name, sm, &old_sm) < 0) + goto fail; + sm->refcount++; + } + else if (is_path && strcmp(sm->path, value) != 0) { + assert(sm->path == sm->name); + if ((sm->path = git__strdup(value)) == NULL || + git_hashtable_insert2(smcfg, sm->path, sm, &old_sm) < 0) + goto fail; + sm->refcount++; + } + git_buf_free(&name); + + if (old_sm && ((git_submodule *)old_sm) != sm) { + /* TODO: log entry about multiple submodules with same path */ + submodule_release(old_sm, 1); + } + + if (is_path) + return 0; + + /* copy other properties into submodule entry */ + if (strcmp(property, "url") == 0) { + if (sm->url) { + git__free(sm->url); + sm->url = NULL; + } + if ((sm->url = git__strdup(value)) == NULL) + goto fail; + } + else if (strcmp(property, "update") == 0) { + int val; + if (git_config_lookup_map_value( + _sm_update_map, ARRAY_SIZE(_sm_update_map), value, &val) < 0) { + giterr_set(GITERR_INVALID, + "Invalid value for submodule update property: '%s'", value); + goto fail; + } + sm->update = (git_submodule_update_t)val; + } + else if (strcmp(property, "fetchRecurseSubmodules") == 0) { + if (git_config_parse_bool(&sm->fetch_recurse, value) < 0) + goto fail; + } + else if (strcmp(property, "ignore") == 0) { + int val; + if (git_config_lookup_map_value( + _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), value, &val) < 0) { + giterr_set(GITERR_INVALID, + "Invalid value for submodule ignore property: '%s'", value); + goto fail; + } + sm->ignore = (git_submodule_ignore_t)val; + } + /* ignore other unknown submodule properties */ + + return 0; + +fail: + submodule_release(sm, 0); + git_buf_free(&name); + return -1; +} + +static int load_submodule_config(git_repository *repo) +{ + int error; + git_index *index; + unsigned int i, max_i; + git_oid gitmodules_oid; + git_hashtable *smcfg; + struct git_config_file *mods = NULL; + + if (repo->submodules) + return 0; + + /* submodule data is kept in a hashtable with each submodule stored + * under both its name and its path. These are usually the same, but + * that is not guaranteed. + */ + smcfg = git_hashtable_alloc( + 4, strhash_no_trailing_slash, strcmp_no_trailing_slash); + GITERR_CHECK_ALLOC(smcfg); + + /* scan index for gitmodules (and .gitmodules entry) */ + if ((error = git_repository_index__weakptr(&index, repo)) < 0) + goto cleanup; + memset(&gitmodules_oid, 0, sizeof(gitmodules_oid)); + max_i = git_index_entrycount(index); + + for (i = 0; i < max_i; i++) { + git_index_entry *entry = git_index_get(index, i); + if (S_ISGITLINK(entry->mode)) { + if ((error = submodule_from_entry(smcfg, entry)) < 0) + goto cleanup; + } + else if (strcmp(entry->path, ".gitmodules") == 0) + git_oid_cpy(&gitmodules_oid, &entry->oid); + } + + /* load .gitmodules from workdir if it exists */ + if (git_repository_workdir(repo) != NULL) { + /* look in workdir for .gitmodules */ + git_buf path = GIT_BUF_INIT; + if (!git_buf_joinpath( + &path, git_repository_workdir(repo), ".gitmodules") && + git_path_isfile(path.ptr)) + { + if (!(error = git_config_file__ondisk(&mods, path.ptr))) + error = git_config_file_open(mods); + } + git_buf_free(&path); + } + + /* load .gitmodules from object cache if not in workdir */ + if (!error && mods == NULL && !git_oid_iszero(&gitmodules_oid)) { + /* TODO: is it worth loading gitmodules from object cache? */ + } + + /* process .gitmodules info */ + if (!error && mods != NULL) + error = git_config_file_foreach(mods, submodule_from_config, smcfg); + + /* store submodule config in repo */ + if (!error) + repo->submodules = smcfg; + +cleanup: + if (mods != NULL) + git_config_file_free(mods); + if (error) + git_hashtable_free(smcfg); + return error; +} + +void git_submodule_config_free(git_repository *repo) +{ + git_hashtable *smcfg = repo->submodules; + git_submodule *sm; + + repo->submodules = NULL; + + if (smcfg == NULL) + return; + + GIT_HASHTABLE_FOREACH_VALUE(smcfg, sm, { submodule_release(sm,1); }); + git_hashtable_free(smcfg); +} + +static int submodule_cmp(const void *a, const void *b) +{ + return strcmp(((git_submodule *)a)->name, ((git_submodule *)b)->name); +} + +int git_submodule_foreach( + git_repository *repo, + int (*callback)(const char *name, void *payload), + void *payload) +{ + int error; + git_submodule *sm; + git_vector seen = GIT_VECTOR_INIT; + seen._cmp = submodule_cmp; + + if ((error = load_submodule_config(repo)) < 0) + return error; + + GIT_HASHTABLE_FOREACH_VALUE( + repo->submodules, sm, { + /* usually the following will not come into play */ + if (sm->refcount > 1) { + if (git_vector_bsearch(&seen, sm) != GIT_ENOTFOUND) + continue; + if ((error = git_vector_insert(&seen, sm)) < 0) + break; + } + + if ((error = callback(sm->name, payload)) < 0) + break; + }); + + git_vector_free(&seen); + + return error; +} + +int git_submodule_lookup( + git_submodule **sm_ptr, /* NULL allowed if user only wants to test */ + git_repository *repo, + const char *name) /* trailing slash is allowed */ +{ + git_submodule *sm; + + if (load_submodule_config(repo) < 0) + return -1; + + sm = git_hashtable_lookup(repo->submodules, name); + + if (sm_ptr) + *sm_ptr = sm; + + return sm ? 0 : GIT_ENOTFOUND; +} diff --git a/src/tree.h b/src/tree.h index 0bff41312..fd00afde5 100644 --- a/src/tree.h +++ b/src/tree.h @@ -32,7 +32,7 @@ struct git_treebuilder { GIT_INLINE(unsigned int) entry_is_tree(const struct git_tree_entry *e) { - return e->attr & 040000; + return (S_ISDIR(e->attr) && !S_ISGITLINK(e->attr)); } void git_tree__free(git_tree *tree); diff --git a/src/vector.h b/src/vector.h index 180edbf7c..5bc27914a 100644 --- a/src/vector.h +++ b/src/vector.h @@ -44,6 +44,8 @@ GIT_INLINE(const void *) git_vector_get_const(const git_vector *v, unsigned int return (position < v->length) ? v->contents[position] : NULL; } +#define GIT_VECTOR_GET(V,I) ((I) < (V)->length ? (V)->contents[(I)] : NULL) + GIT_INLINE(void *) git_vector_last(git_vector *v) { return (v->length > 0) ? git_vector_get(v, v->length - 1) : NULL; diff --git a/tests-clar/core/errors.c b/tests-clar/core/errors.c index 52b2652c8..c781000d5 100644 --- a/tests-clar/core/errors.c +++ b/tests-clar/core/errors.c @@ -46,6 +46,7 @@ void test_core_errors__new_school(void) { struct stat st; assert(p_lstat("this_file_does_not_exist", &st) < 0); + GIT_UNUSED(st); } giterr_set(GITERR_OS, "stat failed"); /* internal fn */ diff --git a/tests-clar/diff/iterator.c b/tests-clar/diff/iterator.c index 3953fd83f..60f416fad 100644 --- a/tests-clar/diff/iterator.c +++ b/tests-clar/diff/iterator.c @@ -139,6 +139,40 @@ void test_diff_iterator__tree_3(void) tree_iterator_test("status", "0017bd4ab1e", 8, expected_tree_3); } +/* $ git ls-tree -r --name-only 24fa9a9fc4e202313e24b648087495441dab432b */ +const char *expected_tree_4[] = { + "attr0", + "attr1", + "attr2", + "attr3", + "binfile", + "gitattributes", + "macro_bad", + "macro_test", + "root_test1", + "root_test2", + "root_test3", + "root_test4.txt", + "sub/abc", + "sub/file", + "sub/sub/file", + "sub/sub/subsub.txt", + "sub/subdir_test1", + "sub/subdir_test2.txt", + "subdir/.gitattributes", + "subdir/abc", + "subdir/subdir_test1", + "subdir/subdir_test2.txt", + "subdir2/subdir2_test1", + NULL +}; + +void test_diff_iterator__tree_4(void) +{ + tree_iterator_test( + "attr", "24fa9a9fc4e202313e24b648087495441dab432b", + 23, expected_tree_4); +} /* -- INDEX ITERATOR TESTS -- */ @@ -188,6 +222,12 @@ static const char *expected_index_0[] = { "root_test2", "root_test3", "root_test4.txt", + "sub/abc", + "sub/file", + "sub/sub/file", + "sub/sub/subsub.txt", + "sub/subdir_test1", + "sub/subdir_test2.txt", "subdir/.gitattributes", "subdir/abc", "subdir/subdir_test1", @@ -208,6 +248,12 @@ static const char *expected_index_oids_0[] = { "4d713dc48e6b1bd75b0d61ad078ba9ca3a56745d", "108bb4e7fd7b16490dc33ff7d972151e73d7166e", "fe773770c5a6cc7185580c9204b1ff18a33ff3fc", + "3e42ffc54a663f9401cc25843d6c0e71a33e4249", + "45b983be36b73c0788dc9cbcb76cbb80fc7bb057", + "45b983be36b73c0788dc9cbcb76cbb80fc7bb057", + "9e5bdc47d6a80f2be0ea3049ad74231b94609242", + "e563cf4758f0d646f1b14b76016aa17fa9e549a4", + "fb5067b1aef3ac1ada4b379dbcb7d17255df7d78", "99eae476896f4907224978b88e5ecaa6c5bb67a9", "3e42ffc54a663f9401cc25843d6c0e71a33e4249", "e563cf4758f0d646f1b14b76016aa17fa9e549a4", @@ -217,7 +263,7 @@ static const char *expected_index_oids_0[] = { void test_diff_iterator__index_0(void) { - index_iterator_test("attr", 17, expected_index_0, expected_index_oids_0); + index_iterator_test("attr", 23, expected_index_0, expected_index_oids_0); } static const char *expected_index_1[] = { diff --git a/tests-clar/refs/unicode.c b/tests-clar/refs/unicode.c new file mode 100644 index 000000000..889c85666 --- /dev/null +++ b/tests-clar/refs/unicode.c @@ -0,0 +1,42 @@ +#include "clar_libgit2.h" + +static git_repository *repo; + +void test_refs_unicode__initialize(void) +{ + cl_fixture_sandbox("testrepo.git"); + + cl_git_pass(git_repository_open(&repo, "testrepo.git")); +} + +void test_refs_unicode__cleanup(void) +{ + git_repository_free(repo); + cl_fixture_cleanup("testrepo.git"); +} + +void test_refs_unicode__create_and_lookup(void) +{ + git_reference *ref0, *ref1, *ref2; + git_repository *repo2; + + const char *REFNAME = "refs/heads/" "\305" "ngstr" "\366" "m"; + const char *master = "refs/heads/master"; + + /* Create the reference */ + cl_git_pass(git_reference_lookup(&ref0, repo, master)); + cl_git_pass(git_reference_create_oid(&ref1, repo, REFNAME, git_reference_oid(ref0), 0)); + cl_assert(strcmp(REFNAME, git_reference_name(ref1)) == 0); + + /* Lookup the reference in a different instance of the repository */ + cl_git_pass(git_repository_open(&repo2, "testrepo.git")); + cl_git_pass(git_reference_lookup(&ref2, repo2, REFNAME)); + + cl_assert(git_oid_cmp(git_reference_oid(ref1), git_reference_oid(ref2)) == 0); + cl_assert(strcmp(REFNAME, git_reference_name(ref2)) == 0); + + git_reference_free(ref0); + git_reference_free(ref1); + git_reference_free(ref2); + git_repository_free(repo2); +} diff --git a/tests-clar/status/status_data.h b/tests-clar/status/status_data.h index 1a68648f4..e60b67cb3 100644 --- a/tests-clar/status/status_data.h +++ b/tests-clar/status/status_data.h @@ -8,6 +8,8 @@ struct status_entry_counts { int expected_entry_count; }; +/* entries for a plain copy of tests/resources/status */ + static const char *entry_paths0[] = { "file_deleted", "ignored_file", @@ -48,3 +50,153 @@ static const unsigned int entry_statuses0[] = { static const size_t entry_count0 = 15; +/* entries for a copy of tests/resources/status with all content + * deleted from the working directory + */ + +static const char *entry_paths2[] = { + "current_file", + "file_deleted", + "modified_file", + "staged_changes", + "staged_changes_file_deleted", + "staged_changes_modified_file", + "staged_delete_file_deleted", + "staged_delete_modified_file", + "staged_new_file", + "staged_new_file_deleted_file", + "staged_new_file_modified_file", + "subdir.txt", + "subdir/current_file", + "subdir/deleted_file", + "subdir/modified_file", +}; + +static const unsigned int entry_statuses2[] = { + GIT_STATUS_WT_DELETED, + GIT_STATUS_WT_DELETED, + GIT_STATUS_WT_DELETED, + GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED, + GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED, + GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED, + GIT_STATUS_INDEX_DELETED, + GIT_STATUS_INDEX_DELETED, + GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW, + GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW, + GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW, + GIT_STATUS_WT_DELETED, + GIT_STATUS_WT_DELETED, + GIT_STATUS_WT_DELETED, + GIT_STATUS_WT_DELETED, +}; + +static const size_t entry_count2 = 15; + +/* entries for a copy of tests/resources/status with some mods */ + +static const char *entry_paths3[] = { + ".HEADER", + "42-is-not-prime.sigh", + "README.md", + "current_file", + "current_file/", + "file_deleted", + "ignored_file", + "modified_file", + "new_file", + "staged_changes", + "staged_changes_file_deleted", + "staged_changes_modified_file", + "staged_delete_file_deleted", + "staged_delete_modified_file", + "staged_new_file", + "staged_new_file_deleted_file", + "staged_new_file_modified_file", + "subdir", + "subdir/current_file", + "subdir/deleted_file", + "subdir/modified_file", +}; + +static const unsigned int entry_statuses3[] = { + GIT_STATUS_WT_NEW, + GIT_STATUS_WT_NEW, + GIT_STATUS_WT_NEW, + GIT_STATUS_WT_DELETED, + GIT_STATUS_WT_NEW, + GIT_STATUS_WT_DELETED, + GIT_STATUS_IGNORED, + GIT_STATUS_WT_MODIFIED, + GIT_STATUS_WT_NEW, + GIT_STATUS_INDEX_MODIFIED, + GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED, + GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED, + GIT_STATUS_INDEX_DELETED, + GIT_STATUS_WT_NEW | GIT_STATUS_INDEX_DELETED, + GIT_STATUS_INDEX_NEW, + GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW, + GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_NEW, + GIT_STATUS_WT_NEW, + GIT_STATUS_WT_DELETED, + GIT_STATUS_WT_DELETED, + GIT_STATUS_WT_DELETED, +}; + +static const size_t entry_count3 = 21; + + +/* entries for a copy of tests/resources/status with some mods + * and different options to the status call + */ + +static const char *entry_paths4[] = { + ".new_file", + "current_file", + "current_file/current_file", + "current_file/modified_file", + "current_file/new_file", + "file_deleted", + "modified_file", + "new_file", + "staged_changes", + "staged_changes_file_deleted", + "staged_changes_modified_file", + "staged_delete_file_deleted", + "staged_delete_modified_file", + "staged_new_file", + "staged_new_file_deleted_file", + "staged_new_file_modified_file", + "subdir", + "subdir/current_file", + "subdir/deleted_file", + "subdir/modified_file", + "zzz_new_dir/new_file", + "zzz_new_file" +}; + +static const unsigned int entry_statuses4[] = { + GIT_STATUS_WT_NEW, + GIT_STATUS_WT_DELETED, + GIT_STATUS_WT_NEW, + GIT_STATUS_WT_NEW, + GIT_STATUS_WT_NEW, + GIT_STATUS_WT_DELETED, + GIT_STATUS_WT_MODIFIED, + GIT_STATUS_WT_NEW, + GIT_STATUS_INDEX_MODIFIED, + GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED, + GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED, + GIT_STATUS_INDEX_DELETED, + GIT_STATUS_WT_NEW | GIT_STATUS_INDEX_DELETED, + GIT_STATUS_INDEX_NEW, + GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW, + GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_NEW, + GIT_STATUS_WT_NEW, + GIT_STATUS_WT_DELETED, + GIT_STATUS_WT_DELETED, + GIT_STATUS_WT_DELETED, + GIT_STATUS_WT_NEW, + GIT_STATUS_WT_NEW, +}; + +static const size_t entry_count4 = 22; diff --git a/tests-clar/status/submodules.c b/tests-clar/status/submodules.c new file mode 100644 index 000000000..969158825 --- /dev/null +++ b/tests-clar/status/submodules.c @@ -0,0 +1,117 @@ +#include "clar_libgit2.h" +#include "buffer.h" +#include "path.h" +#include "posix.h" + +static git_repository *g_repo = NULL; + +void test_status_submodules__initialize(void) +{ + git_buf modpath = GIT_BUF_INIT; + + g_repo = cl_git_sandbox_init("submodules"); + + cl_fixture_sandbox("testrepo.git"); + + cl_git_pass(git_buf_sets(&modpath, git_repository_workdir(g_repo))); + cl_assert(git_path_dirname_r(&modpath, modpath.ptr) >= 0); + cl_git_pass(git_buf_joinpath(&modpath, modpath.ptr, "testrepo.git\n")); + + p_rename("submodules/gitmodules", "submodules/.gitmodules"); + cl_git_append2file("submodules/.gitmodules", modpath.ptr); + git_buf_free(&modpath); + + p_rename("submodules/testrepo/.gitted", "submodules/testrepo/.git"); +} + +void test_status_submodules__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_status_submodules__api(void) +{ + git_submodule *sm; + + cl_assert(git_submodule_lookup(NULL, g_repo, "nonexistent") == GIT_ENOTFOUND); + + cl_assert(git_submodule_lookup(NULL, g_repo, "modified") == GIT_ENOTFOUND); + + cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); + cl_assert(sm != NULL); + cl_assert_equal_s("testrepo", sm->name); + cl_assert_equal_s("testrepo", sm->path); +} + +static int +cb_status__submodule_count(const char *p, unsigned int s, void *payload) +{ + volatile int *count = (int *)payload; + + GIT_UNUSED(p); + GIT_UNUSED(s); + + (*count)++; + + return 0; +} + +void test_status_submodules__0(void) +{ + int counts = 0; + + cl_assert(git_path_isdir("submodules/.git")); + cl_assert(git_path_isdir("submodules/testrepo/.git")); + cl_assert(git_path_isfile("submodules/.gitmodules")); + + cl_git_pass( + git_status_foreach(g_repo, cb_status__submodule_count, &counts) + ); + + cl_assert(counts == 6); +} + +static const char *expected_files[] = { + ".gitmodules", + "added", + "deleted", + "ignored", + "modified", + "untracked" +}; + +static unsigned int expected_status[] = { + GIT_STATUS_WT_MODIFIED, + GIT_STATUS_INDEX_NEW, + GIT_STATUS_INDEX_DELETED, + GIT_STATUS_IGNORED, + GIT_STATUS_WT_MODIFIED, + GIT_STATUS_WT_NEW +}; + +static int +cb_status__match(const char *p, unsigned int s, void *payload) +{ + volatile int *index = (int *)payload; + + cl_assert_equal_s(expected_files[*index], p); + cl_assert(expected_status[*index] == s); + (*index)++; + + return 0; +} + +void test_status_submodules__1(void) +{ + int index = 0; + + cl_assert(git_path_isdir("submodules/.git")); + cl_assert(git_path_isdir("submodules/testrepo/.git")); + cl_assert(git_path_isfile("submodules/.gitmodules")); + + cl_git_pass( + git_status_foreach(g_repo, cb_status__match, &index) + ); + + cl_assert(index == 6); +} diff --git a/tests-clar/status/worktree.c b/tests-clar/status/worktree.c index f80975795..efdf6f41b 100644 --- a/tests-clar/status/worktree.c +++ b/tests-clar/status/worktree.c @@ -2,7 +2,9 @@ #include "fileops.h" #include "ignore.h" #include "status_data.h" - +#include "posix.h" +#include "util.h" +#include "path.h" /** * Auxiliary methods @@ -43,7 +45,6 @@ cb_status__count(const char *p, unsigned int s, void *payload) return 0; } - /** * Initializer * @@ -68,6 +69,7 @@ void test_status_worktree__cleanup(void) /** * Tests - Status determination on a working tree */ +/* this test is equivalent to t18-status.c:statuscb0 */ void test_status_worktree__whole_repository(void) { struct status_entry_counts counts; @@ -87,6 +89,7 @@ void test_status_worktree__whole_repository(void) cl_assert(counts.wrong_sorted_path == 0); } +/* this test is equivalent to t18-status.c:statuscb1 */ void test_status_worktree__empty_repository(void) { int count = 0; @@ -97,6 +100,122 @@ void test_status_worktree__empty_repository(void) cl_assert(count == 0); } +static int remove_file_cb(void *data, git_buf *file) +{ + const char *filename = git_buf_cstr(file); + + GIT_UNUSED(data); + + if (git__suffixcmp(filename, ".git") == 0) + return 0; + + if (git_path_isdir(filename)) + cl_git_pass(git_futils_rmdir_r(filename, 1)); + else + cl_git_pass(p_unlink(git_buf_cstr(file))); + + return 0; +} + +/* this test is equivalent to t18-status.c:statuscb2 */ +void test_status_worktree__purged_worktree(void) +{ + struct status_entry_counts counts; + git_repository *repo = cl_git_sandbox_init("status"); + git_buf workdir = GIT_BUF_INIT; + + /* first purge the contents of the worktree */ + cl_git_pass(git_buf_sets(&workdir, git_repository_workdir(repo))); + cl_git_pass(git_path_direach(&workdir, remove_file_cb, NULL)); + git_buf_free(&workdir); + + /* now get status */ + memset(&counts, 0x0, sizeof(struct status_entry_counts)); + counts.expected_entry_count = entry_count2; + counts.expected_paths = entry_paths2; + counts.expected_statuses = entry_statuses2; + + cl_git_pass( + git_status_foreach(repo, cb_status__normal, &counts) + ); + + cl_assert(counts.entry_count == counts.expected_entry_count); + cl_assert(counts.wrong_status_flags_count == 0); + cl_assert(counts.wrong_sorted_path == 0); +} + +/* this test is similar to t18-status.c:statuscb3 */ +void test_status_worktree__swap_subdir_and_file(void) +{ + struct status_entry_counts counts; + git_repository *repo = cl_git_sandbox_init("status"); + git_status_options opts; + + /* first alter the contents of the worktree */ + cl_git_pass(p_rename("status/current_file", "status/swap")); + cl_git_pass(p_rename("status/subdir", "status/current_file")); + cl_git_pass(p_rename("status/swap", "status/subdir")); + + cl_git_mkfile("status/.HEADER", "dummy"); + cl_git_mkfile("status/42-is-not-prime.sigh", "dummy"); + cl_git_mkfile("status/README.md", "dummy"); + + /* now get status */ + memset(&counts, 0x0, sizeof(struct status_entry_counts)); + counts.expected_entry_count = entry_count3; + counts.expected_paths = entry_paths3; + counts.expected_statuses = entry_statuses3; + + memset(&opts, 0, sizeof(opts)); + opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | + GIT_STATUS_OPT_INCLUDE_IGNORED; + + cl_git_pass( + git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) + ); + + cl_assert(counts.entry_count == counts.expected_entry_count); + cl_assert(counts.wrong_status_flags_count == 0); + cl_assert(counts.wrong_sorted_path == 0); + +} + +void test_status_worktree__swap_subdir_with_recurse_and_pathspec(void) +{ + struct status_entry_counts counts; + git_repository *repo = cl_git_sandbox_init("status"); + git_status_options opts; + + /* first alter the contents of the worktree */ + cl_git_pass(p_rename("status/current_file", "status/swap")); + cl_git_pass(p_rename("status/subdir", "status/current_file")); + cl_git_pass(p_rename("status/swap", "status/subdir")); + cl_git_mkfile("status/.new_file", "dummy"); + cl_git_pass(git_futils_mkdir_r("status/zzz_new_dir", NULL, 0777)); + cl_git_mkfile("status/zzz_new_dir/new_file", "dummy"); + cl_git_mkfile("status/zzz_new_file", "dummy"); + + /* now get status */ + memset(&counts, 0x0, sizeof(struct status_entry_counts)); + counts.expected_entry_count = entry_count4; + 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 */ + + cl_git_pass( + git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) + ); + + cl_assert(counts.entry_count == counts.expected_entry_count); + cl_assert(counts.wrong_status_flags_count == 0); + cl_assert(counts.wrong_sorted_path == 0); +} + +/* this test is equivalent to t18-status.c:singlestatus0 */ void test_status_worktree__single_file(void) { int i; @@ -111,6 +230,54 @@ void test_status_worktree__single_file(void) } } +/* this test is equivalent to t18-status.c:singlestatus1 */ +void test_status_worktree__single_nonexistent_file(void) +{ + int error; + unsigned int status_flags; + git_repository *repo = cl_git_sandbox_init("status"); + + error = git_status_file(&status_flags, repo, "nonexistent"); + cl_git_fail(error); + cl_assert(error == GIT_ENOTFOUND); +} + +/* this test is equivalent to t18-status.c:singlestatus2 */ +void test_status_worktree__single_nonexistent_file_empty_repo(void) +{ + int error; + unsigned int status_flags; + git_repository *repo = cl_git_sandbox_init("empty_standard_repo"); + + error = git_status_file(&status_flags, repo, "nonexistent"); + cl_git_fail(error); + cl_assert(error == GIT_ENOTFOUND); +} + +/* this test is equivalent to t18-status.c:singlestatus3 */ +void test_status_worktree__single_file_empty_repo(void) +{ + unsigned int status_flags; + git_repository *repo = cl_git_sandbox_init("empty_standard_repo"); + + cl_git_mkfile("empty_standard_repo/new_file", "new_file\n"); + + cl_git_pass(git_status_file(&status_flags, repo, "new_file")); + cl_assert(status_flags == GIT_STATUS_WT_NEW); +} + +/* this test is equivalent to t18-status.c:singlestatus4 */ +void test_status_worktree__single_folder(void) +{ + int error; + unsigned int status_flags; + git_repository *repo = cl_git_sandbox_init("status"); + + error = git_status_file(&status_flags, repo, "subdir"); + cl_git_fail(error); +} + + void test_status_worktree__ignores(void) { int i, ignored; @@ -133,3 +300,87 @@ void test_status_worktree__ignores(void) ); cl_assert(ignored); } + +static int cb_status__check_592(const char *p, unsigned int s, void *payload) +{ + GIT_UNUSED(payload); + + if (s != GIT_STATUS_WT_DELETED || (payload != NULL && strcmp(p, (const char *)payload) != 0)) + return -1; + + return 0; +} + +void test_status_worktree__issue_592(void) +{ + git_repository *repo; + git_buf path = GIT_BUF_INIT; + + repo = cl_git_sandbox_init("issue_592"); + cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "l.txt")); + cl_git_pass(p_unlink(git_buf_cstr(&path))); + + cl_git_pass(git_status_foreach(repo, cb_status__check_592, "l.txt")); + + git_buf_free(&path); +} + +void test_status_worktree__issue_592_2(void) +{ + git_repository *repo; + git_buf path = GIT_BUF_INIT; + + repo = cl_git_sandbox_init("issue_592"); + cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "c/a.txt")); + cl_git_pass(p_unlink(git_buf_cstr(&path))); + + cl_git_pass(git_status_foreach(repo, cb_status__check_592, "c/a.txt")); + + git_buf_free(&path); +} + +void test_status_worktree__issue_592_3(void) +{ + git_repository *repo; + git_buf path = GIT_BUF_INIT; + + repo = cl_git_sandbox_init("issue_592"); + + cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "c")); + cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), 1)); + + cl_git_pass(git_status_foreach(repo, cb_status__check_592, "c/a.txt")); + + git_buf_free(&path); +} + +void test_status_worktree__issue_592_4(void) +{ + git_repository *repo; + git_buf path = GIT_BUF_INIT; + + repo = cl_git_sandbox_init("issue_592"); + + cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "t/b.txt")); + cl_git_pass(p_unlink(git_buf_cstr(&path))); + + cl_git_pass(git_status_foreach(repo, cb_status__check_592, "t/b.txt")); + + git_buf_free(&path); +} + +void test_status_worktree__issue_592_5(void) +{ + git_repository *repo; + git_buf path = GIT_BUF_INIT; + + repo = cl_git_sandbox_init("issue_592"); + + cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "t")); + cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), 1)); + cl_git_pass(p_mkdir(git_buf_cstr(&path), 0777)); + + cl_git_pass(git_status_foreach(repo, cb_status__check_592, NULL)); + + git_buf_free(&path); +} diff --git a/tests/resources/attr/.gitted/index b/tests/resources/attr/.gitted/index index 19fa99d5b..1d60eab8f 100644 Binary files a/tests/resources/attr/.gitted/index and b/tests/resources/attr/.gitted/index differ diff --git a/tests/resources/attr/.gitted/logs/HEAD b/tests/resources/attr/.gitted/logs/HEAD index 68fcff2c5..73f00f345 100644 Binary files a/tests/resources/attr/.gitted/logs/HEAD and b/tests/resources/attr/.gitted/logs/HEAD differ diff --git a/tests/resources/attr/.gitted/logs/refs/heads/master b/tests/resources/attr/.gitted/logs/refs/heads/master index 68fcff2c5..73f00f345 100644 Binary files a/tests/resources/attr/.gitted/logs/refs/heads/master and b/tests/resources/attr/.gitted/logs/refs/heads/master differ diff --git a/tests/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e0 b/tests/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e0 new file mode 100644 index 000000000..b537899f2 Binary files /dev/null and b/tests/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e0 differ diff --git a/tests/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432b b/tests/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432b new file mode 100644 index 000000000..e7099bbaa Binary files /dev/null and b/tests/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432b differ diff --git a/tests/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4 b/tests/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4 new file mode 100644 index 000000000..f90f0d79c Binary files /dev/null and b/tests/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4 differ diff --git a/tests/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 new file mode 100644 index 000000000..7ca4ceed5 Binary files /dev/null and b/tests/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 differ diff --git a/tests/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485 b/tests/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485 new file mode 100644 index 000000000..6fcc549b4 Binary files /dev/null and b/tests/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485 differ diff --git a/tests/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1 b/tests/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1 new file mode 100644 index 000000000..4b57836cd Binary files /dev/null and b/tests/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1 differ diff --git a/tests/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242 b/tests/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242 new file mode 100644 index 000000000..d6385ec8d Binary files /dev/null and b/tests/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242 differ diff --git a/tests/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0e b/tests/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0e new file mode 100644 index 000000000..1005f944a Binary files /dev/null and b/tests/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0e differ diff --git a/tests/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596b b/tests/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596b new file mode 100644 index 000000000..44d703b2e Binary files /dev/null and b/tests/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596b differ diff --git a/tests/resources/attr/.gitted/refs/heads/master b/tests/resources/attr/.gitted/refs/heads/master index 7f8bbe3e7..8768776b3 100644 Binary files a/tests/resources/attr/.gitted/refs/heads/master and b/tests/resources/attr/.gitted/refs/heads/master differ diff --git a/tests/resources/issue_592/.gitted/COMMIT_EDITMSG b/tests/resources/issue_592/.gitted/COMMIT_EDITMSG new file mode 100644 index 000000000..5852f4463 Binary files /dev/null and b/tests/resources/issue_592/.gitted/COMMIT_EDITMSG differ diff --git a/tests/resources/issue_592/.gitted/HEAD b/tests/resources/issue_592/.gitted/HEAD new file mode 100644 index 000000000..cb089cd89 Binary files /dev/null and b/tests/resources/issue_592/.gitted/HEAD differ diff --git a/tests/resources/issue_592/.gitted/config b/tests/resources/issue_592/.gitted/config new file mode 100644 index 000000000..78387c50b Binary files /dev/null and b/tests/resources/issue_592/.gitted/config differ diff --git a/tests/resources/issue_592/.gitted/index b/tests/resources/issue_592/.gitted/index new file mode 100644 index 000000000..eaeb5d761 Binary files /dev/null and b/tests/resources/issue_592/.gitted/index differ diff --git a/tests/resources/issue_592/.gitted/info/exclude b/tests/resources/issue_592/.gitted/info/exclude new file mode 100644 index 000000000..a5196d1be Binary files /dev/null and b/tests/resources/issue_592/.gitted/info/exclude differ diff --git a/tests/resources/issue_592/.gitted/logs/HEAD b/tests/resources/issue_592/.gitted/logs/HEAD new file mode 100644 index 000000000..f19fe35a6 Binary files /dev/null and b/tests/resources/issue_592/.gitted/logs/HEAD differ diff --git a/tests/resources/issue_592/.gitted/logs/refs/heads/master b/tests/resources/issue_592/.gitted/logs/refs/heads/master new file mode 100644 index 000000000..f19fe35a6 Binary files /dev/null and b/tests/resources/issue_592/.gitted/logs/refs/heads/master differ diff --git a/tests/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0e b/tests/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0e new file mode 100644 index 000000000..05dec10f7 Binary files /dev/null and b/tests/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0e differ diff --git a/tests/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8 b/tests/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8 new file mode 100644 index 000000000..e997e1b49 Binary files /dev/null and b/tests/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8 differ diff --git a/tests/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e85 b/tests/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e85 new file mode 100644 index 000000000..c49a8be58 Binary files /dev/null and b/tests/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e85 differ diff --git a/tests/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792 b/tests/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792 new file mode 100644 index 000000000..25d44d938 Binary files /dev/null and b/tests/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792 differ diff --git a/tests/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84 b/tests/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84 new file mode 100644 index 000000000..1d6e38d37 Binary files /dev/null and b/tests/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84 differ diff --git a/tests/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21 b/tests/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21 new file mode 100644 index 000000000..36c5b9aab Binary files /dev/null and b/tests/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21 differ diff --git a/tests/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7 b/tests/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7 new file mode 100644 index 000000000..c08ecd5ed Binary files /dev/null and b/tests/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7 differ diff --git a/tests/resources/issue_592/.gitted/refs/heads/master b/tests/resources/issue_592/.gitted/refs/heads/master new file mode 100644 index 000000000..1f6669628 Binary files /dev/null and b/tests/resources/issue_592/.gitted/refs/heads/master differ diff --git a/tests/resources/issue_592/a.txt b/tests/resources/issue_592/a.txt new file mode 100644 index 000000000..f1adef63c Binary files /dev/null and b/tests/resources/issue_592/a.txt differ diff --git a/tests/resources/issue_592/c/a.txt b/tests/resources/issue_592/c/a.txt new file mode 100644 index 000000000..f1adef63c Binary files /dev/null and b/tests/resources/issue_592/c/a.txt differ diff --git a/tests/resources/issue_592/l.txt b/tests/resources/issue_592/l.txt new file mode 100644 index 000000000..f1adef63c Binary files /dev/null and b/tests/resources/issue_592/l.txt differ diff --git a/tests/resources/issue_592/t/a.txt b/tests/resources/issue_592/t/a.txt new file mode 100644 index 000000000..f1adef63c Binary files /dev/null and b/tests/resources/issue_592/t/a.txt differ diff --git a/tests/resources/issue_592/t/b.txt b/tests/resources/issue_592/t/b.txt new file mode 100644 index 000000000..f1adef63c Binary files /dev/null and b/tests/resources/issue_592/t/b.txt differ diff --git a/tests/resources/submodules/.gitted/HEAD b/tests/resources/submodules/.gitted/HEAD new file mode 100644 index 000000000..cb089cd89 Binary files /dev/null and b/tests/resources/submodules/.gitted/HEAD differ diff --git a/tests/resources/submodules/.gitted/config b/tests/resources/submodules/.gitted/config new file mode 100644 index 000000000..af107929f Binary files /dev/null and b/tests/resources/submodules/.gitted/config differ diff --git a/tests/resources/submodules/.gitted/description b/tests/resources/submodules/.gitted/description new file mode 100644 index 000000000..498b267a8 Binary files /dev/null and b/tests/resources/submodules/.gitted/description differ diff --git a/tests/resources/submodules/.gitted/index b/tests/resources/submodules/.gitted/index new file mode 100644 index 000000000..97bf8ef51 Binary files /dev/null and b/tests/resources/submodules/.gitted/index differ diff --git a/tests/resources/submodules/.gitted/info/exclude b/tests/resources/submodules/.gitted/info/exclude new file mode 100644 index 000000000..dfc411579 Binary files /dev/null and b/tests/resources/submodules/.gitted/info/exclude differ diff --git a/tests/resources/submodules/.gitted/info/refs b/tests/resources/submodules/.gitted/info/refs new file mode 100644 index 000000000..ba17abdde Binary files /dev/null and b/tests/resources/submodules/.gitted/info/refs differ diff --git a/tests/resources/submodules/.gitted/logs/HEAD b/tests/resources/submodules/.gitted/logs/HEAD new file mode 100644 index 000000000..87a7bdafc Binary files /dev/null and b/tests/resources/submodules/.gitted/logs/HEAD differ diff --git a/tests/resources/submodules/.gitted/logs/refs/heads/master b/tests/resources/submodules/.gitted/logs/refs/heads/master new file mode 100644 index 000000000..87a7bdafc Binary files /dev/null and b/tests/resources/submodules/.gitted/logs/refs/heads/master differ diff --git a/tests/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e b/tests/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e new file mode 100644 index 000000000..2c3c2cb61 Binary files /dev/null and b/tests/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e differ diff --git a/tests/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357 b/tests/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357 new file mode 100644 index 000000000..c85fb5512 Binary files /dev/null and b/tests/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357 differ diff --git a/tests/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc6850 b/tests/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc6850 new file mode 100644 index 000000000..1c8dbdf9f Binary files /dev/null and b/tests/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc6850 differ diff --git a/tests/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888 b/tests/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888 new file mode 100644 index 000000000..3d78bd6be Binary files /dev/null and b/tests/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888 differ diff --git a/tests/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818 b/tests/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818 new file mode 100644 index 000000000..6e0b49e86 Binary files /dev/null and b/tests/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818 differ diff --git a/tests/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae b/tests/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae new file mode 100644 index 000000000..082a58941 Binary files /dev/null and b/tests/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae differ diff --git a/tests/resources/submodules/.gitted/objects/info/packs b/tests/resources/submodules/.gitted/objects/info/packs new file mode 100644 index 000000000..0785ef698 Binary files /dev/null and b/tests/resources/submodules/.gitted/objects/info/packs differ diff --git a/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx b/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx new file mode 100644 index 000000000..810fc3181 Binary files /dev/null and b/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx differ diff --git a/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack b/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack new file mode 100644 index 000000000..c25c4a73f Binary files /dev/null and b/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack differ diff --git a/tests/resources/submodules/.gitted/packed-refs b/tests/resources/submodules/.gitted/packed-refs new file mode 100644 index 000000000..a6450691e Binary files /dev/null and b/tests/resources/submodules/.gitted/packed-refs differ diff --git a/tests/resources/submodules/.gitted/refs/heads/master b/tests/resources/submodules/.gitted/refs/heads/master new file mode 100644 index 000000000..32b935853 Binary files /dev/null and b/tests/resources/submodules/.gitted/refs/heads/master differ diff --git a/tests/resources/submodules/added b/tests/resources/submodules/added new file mode 100644 index 000000000..d5f7fc3f7 Binary files /dev/null and b/tests/resources/submodules/added differ diff --git a/tests/resources/submodules/gitmodules b/tests/resources/submodules/gitmodules new file mode 100644 index 000000000..1262f8bb0 Binary files /dev/null and b/tests/resources/submodules/gitmodules differ diff --git a/tests/resources/submodules/ignored b/tests/resources/submodules/ignored new file mode 100644 index 000000000..092bfb9bd Binary files /dev/null and b/tests/resources/submodules/ignored differ diff --git a/tests/resources/submodules/modified b/tests/resources/submodules/modified new file mode 100644 index 000000000..452216e1d Binary files /dev/null and b/tests/resources/submodules/modified differ diff --git a/tests/resources/submodules/testrepo/.gitted/HEAD b/tests/resources/submodules/testrepo/.gitted/HEAD new file mode 100644 index 000000000..cb089cd89 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/HEAD differ diff --git a/tests/resources/submodules/testrepo/.gitted/config b/tests/resources/submodules/testrepo/.gitted/config new file mode 100644 index 000000000..d6dcad12b Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/config differ diff --git a/tests/resources/submodules/testrepo/.gitted/description b/tests/resources/submodules/testrepo/.gitted/description new file mode 100644 index 000000000..498b267a8 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/description differ diff --git a/tests/resources/submodules/testrepo/.gitted/index b/tests/resources/submodules/testrepo/.gitted/index new file mode 100644 index 000000000..3eb8d84fe Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/index differ diff --git a/tests/resources/submodules/testrepo/.gitted/info/exclude b/tests/resources/submodules/testrepo/.gitted/info/exclude new file mode 100644 index 000000000..a5196d1be Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/info/exclude differ diff --git a/tests/resources/submodules/testrepo/.gitted/logs/HEAD b/tests/resources/submodules/testrepo/.gitted/logs/HEAD new file mode 100644 index 000000000..147643a30 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/logs/HEAD differ diff --git a/tests/resources/submodules/testrepo/.gitted/logs/refs/heads/master b/tests/resources/submodules/testrepo/.gitted/logs/refs/heads/master new file mode 100644 index 000000000..147643a30 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/logs/refs/heads/master differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 new file mode 100644 index 000000000..cedb2a22e Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 new file mode 100644 index 000000000..93a16f146 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd new file mode 100644 index 000000000..ba0bfb30c Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b b/tests/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b new file mode 100644 index 000000000..225c45734 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d b/tests/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d new file mode 100644 index 000000000..df40d99af Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 b/tests/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 new file mode 100644 index 000000000..321eaa867 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc b/tests/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc new file mode 100644 index 000000000..9bb5b623b Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 new file mode 100644 index 000000000..7ca4ceed5 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 new file mode 100644 index 000000000..8953b6cef Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 new file mode 100644 index 000000000..c1f22c54f Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a new file mode 100644 index 000000000..2ef4faa0f Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af b/tests/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af new file mode 100644 index 000000000..716b0c64b Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980 b/tests/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980 new file mode 100644 index 000000000..23c462f34 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980 differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d new file mode 100644 index 000000000..2f9b6b6e3 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 new file mode 100644 index 000000000..5df58dda5 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 b/tests/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 new file mode 100644 index 000000000..4cc3f4dff Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 b/tests/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 new file mode 100644 index 000000000..bf7b2bb68 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a new file mode 100644 index 000000000..a79612435 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f new file mode 100644 index 000000000..f8588696b Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 b/tests/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 new file mode 100644 index 000000000..29c8e824d Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd new file mode 100644 index 000000000..d0d7e736e Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 new file mode 100644 index 000000000..18a7f61c2 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 b/tests/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 new file mode 100644 index 000000000..d95254674 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 b/tests/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 new file mode 100644 index 000000000..f460f2547 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 b/tests/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 new file mode 100644 index 000000000..f613670e2 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/tests/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 new file mode 100644 index 000000000..0817229bc Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd new file mode 100644 index 000000000..75f541f10 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f b/tests/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f new file mode 100644 index 000000000..a67d6e647 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 new file mode 100644 index 000000000..711223894 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 b/tests/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 new file mode 100644 index 000000000..b135eccda Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 b/tests/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 new file mode 100644 index 000000000..82e2790e8 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 new file mode 100644 index 000000000..697c94c92 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 new file mode 100644 index 000000000..112998d42 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 new file mode 100644 index 000000000..12bf5f3e3 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx new file mode 100644 index 000000000..5068f2818 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack new file mode 100644 index 000000000..a6a1f3020 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx new file mode 100644 index 000000000..94c3c71da Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack new file mode 100644 index 000000000..74c7fe4f3 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx new file mode 100644 index 000000000..555cfa977 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx differ diff --git a/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack new file mode 100644 index 000000000..4d539ed0a Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack differ diff --git a/tests/resources/submodules/testrepo/.gitted/packed-refs b/tests/resources/submodules/testrepo/.gitted/packed-refs new file mode 100644 index 000000000..9c0433e1c Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/packed-refs differ diff --git a/tests/resources/submodules/testrepo/.gitted/refs/heads/master b/tests/resources/submodules/testrepo/.gitted/refs/heads/master new file mode 100644 index 000000000..3d8f0a402 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/refs/heads/master differ diff --git a/tests/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD b/tests/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD new file mode 100644 index 000000000..6efe28fff Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD differ diff --git a/tests/resources/submodules/testrepo/README b/tests/resources/submodules/testrepo/README new file mode 100644 index 000000000..a8233120f Binary files /dev/null and b/tests/resources/submodules/testrepo/README differ diff --git a/tests/resources/submodules/testrepo/branch_file.txt b/tests/resources/submodules/testrepo/branch_file.txt new file mode 100644 index 000000000..3697d64be Binary files /dev/null and b/tests/resources/submodules/testrepo/branch_file.txt differ diff --git a/tests/resources/submodules/testrepo/new.txt b/tests/resources/submodules/testrepo/new.txt new file mode 100644 index 000000000..a71586c1d Binary files /dev/null and b/tests/resources/submodules/testrepo/new.txt differ diff --git a/tests/resources/submodules/unmodified b/tests/resources/submodules/unmodified new file mode 100644 index 000000000..092bfb9bd Binary files /dev/null and b/tests/resources/submodules/unmodified differ diff --git a/tests/resources/submodules/untracked b/tests/resources/submodules/untracked new file mode 100644 index 000000000..092bfb9bd Binary files /dev/null and b/tests/resources/submodules/untracked differ