mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-07 20:17:59 +00:00
Merge branch 'new-error-handling' of github.com:libgit2/libgit2 into new-error-handling
This commit is contained in:
commit
dcfdb958e2
@ -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
|
||||
|
@ -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),
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
*
|
||||
|
103
include/git2/submodule.h
Normal file
103
include/git2/submodule.h
Normal file
@ -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
|
65
src/attr.c
65
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -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<git_attr_assignment> */
|
||||
const char *cfg_attr_file; /* cached value of core.attributesfile */
|
||||
const char *cfg_excl_file; /* cached value of core.excludesfile */
|
||||
} git_attr_cache;
|
||||
|
||||
extern int git_attr_cache__init(git_repository *repo);
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
24
src/buffer.c
24
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;
|
||||
|
44
src/config.c
44
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,15 +283,13 @@ 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;
|
||||
@ -301,19 +299,17 @@ int git_config_get_mapped(git_config *cfg, const char *name, git_cvar_map *maps,
|
||||
case GIT_CVAR_TRUE: {
|
||||
int bool_val;
|
||||
|
||||
if (parse_bool(&bool_val, value) == 0 &&
|
||||
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;
|
||||
|
||||
break;
|
||||
|
||||
case GIT_CVAR_STRING:
|
||||
@ -321,9 +317,25 @@ int git_config_get_mapped(git_config *cfg, const char *name, git_cvar_map *maps,
|
||||
*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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
31
src/config_file.h
Normal file
31
src/config_file.h
Normal file
@ -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
|
||||
|
171
src/diff.c
171
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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,6 +378,7 @@ int git_diff_foreach(
|
||||
*/
|
||||
if (git_oid_cmp(&delta->old.oid, &delta->new.oid) == 0) {
|
||||
delta->status = GIT_DELTA_UNMODIFIED;
|
||||
if ((diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
20
src/ignore.c
20
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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
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. */
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
205
src/status.c
205
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)
|
||||
|
376
src/submodule.c
Normal file
376
src/submodule.c
Normal file
@ -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;
|
||||
}
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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[] = {
|
||||
|
42
tests-clar/refs/unicode.c
Normal file
42
tests-clar/refs/unicode.c
Normal file
@ -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);
|
||||
}
|
@ -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;
|
||||
|
117
tests-clar/status/submodules.c
Normal file
117
tests-clar/status/submodules.c
Normal file
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
tests/resources/issue_592/.gitted/COMMIT_EDITMSG
Normal file
BIN
tests/resources/issue_592/.gitted/COMMIT_EDITMSG
Normal file
Binary file not shown.
BIN
tests/resources/issue_592/.gitted/HEAD
Normal file
BIN
tests/resources/issue_592/.gitted/HEAD
Normal file
Binary file not shown.
BIN
tests/resources/issue_592/.gitted/config
Normal file
BIN
tests/resources/issue_592/.gitted/config
Normal file
Binary file not shown.
BIN
tests/resources/issue_592/.gitted/index
Normal file
BIN
tests/resources/issue_592/.gitted/index
Normal file
Binary file not shown.
BIN
tests/resources/issue_592/.gitted/info/exclude
Normal file
BIN
tests/resources/issue_592/.gitted/info/exclude
Normal file
Binary file not shown.
BIN
tests/resources/issue_592/.gitted/logs/HEAD
Normal file
BIN
tests/resources/issue_592/.gitted/logs/HEAD
Normal file
Binary file not shown.
BIN
tests/resources/issue_592/.gitted/logs/refs/heads/master
Normal file
BIN
tests/resources/issue_592/.gitted/logs/refs/heads/master
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
tests/resources/issue_592/.gitted/refs/heads/master
Normal file
BIN
tests/resources/issue_592/.gitted/refs/heads/master
Normal file
Binary file not shown.
BIN
tests/resources/issue_592/a.txt
Normal file
BIN
tests/resources/issue_592/a.txt
Normal file
Binary file not shown.
BIN
tests/resources/issue_592/c/a.txt
Normal file
BIN
tests/resources/issue_592/c/a.txt
Normal file
Binary file not shown.
BIN
tests/resources/issue_592/l.txt
Normal file
BIN
tests/resources/issue_592/l.txt
Normal file
Binary file not shown.
BIN
tests/resources/issue_592/t/a.txt
Normal file
BIN
tests/resources/issue_592/t/a.txt
Normal file
Binary file not shown.
BIN
tests/resources/issue_592/t/b.txt
Normal file
BIN
tests/resources/issue_592/t/b.txt
Normal file
Binary file not shown.
BIN
tests/resources/submodules/.gitted/HEAD
Normal file
BIN
tests/resources/submodules/.gitted/HEAD
Normal file
Binary file not shown.
BIN
tests/resources/submodules/.gitted/config
Normal file
BIN
tests/resources/submodules/.gitted/config
Normal file
Binary file not shown.
BIN
tests/resources/submodules/.gitted/description
Normal file
BIN
tests/resources/submodules/.gitted/description
Normal file
Binary file not shown.
BIN
tests/resources/submodules/.gitted/index
Normal file
BIN
tests/resources/submodules/.gitted/index
Normal file
Binary file not shown.
BIN
tests/resources/submodules/.gitted/info/exclude
Normal file
BIN
tests/resources/submodules/.gitted/info/exclude
Normal file
Binary file not shown.
BIN
tests/resources/submodules/.gitted/info/refs
Normal file
BIN
tests/resources/submodules/.gitted/info/refs
Normal file
Binary file not shown.
BIN
tests/resources/submodules/.gitted/logs/HEAD
Normal file
BIN
tests/resources/submodules/.gitted/logs/HEAD
Normal file
Binary file not shown.
BIN
tests/resources/submodules/.gitted/logs/refs/heads/master
Normal file
BIN
tests/resources/submodules/.gitted/logs/refs/heads/master
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
tests/resources/submodules/.gitted/objects/info/packs
Normal file
BIN
tests/resources/submodules/.gitted/objects/info/packs
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
tests/resources/submodules/.gitted/packed-refs
Normal file
BIN
tests/resources/submodules/.gitted/packed-refs
Normal file
Binary file not shown.
BIN
tests/resources/submodules/.gitted/refs/heads/master
Normal file
BIN
tests/resources/submodules/.gitted/refs/heads/master
Normal file
Binary file not shown.
BIN
tests/resources/submodules/added
Normal file
BIN
tests/resources/submodules/added
Normal file
Binary file not shown.
BIN
tests/resources/submodules/gitmodules
Normal file
BIN
tests/resources/submodules/gitmodules
Normal file
Binary file not shown.
BIN
tests/resources/submodules/ignored
Normal file
BIN
tests/resources/submodules/ignored
Normal file
Binary file not shown.
BIN
tests/resources/submodules/modified
Normal file
BIN
tests/resources/submodules/modified
Normal file
Binary file not shown.
BIN
tests/resources/submodules/testrepo/.gitted/HEAD
Normal file
BIN
tests/resources/submodules/testrepo/.gitted/HEAD
Normal file
Binary file not shown.
BIN
tests/resources/submodules/testrepo/.gitted/config
Normal file
BIN
tests/resources/submodules/testrepo/.gitted/config
Normal file
Binary file not shown.
BIN
tests/resources/submodules/testrepo/.gitted/description
Normal file
BIN
tests/resources/submodules/testrepo/.gitted/description
Normal file
Binary file not shown.
BIN
tests/resources/submodules/testrepo/.gitted/index
Normal file
BIN
tests/resources/submodules/testrepo/.gitted/index
Normal file
Binary file not shown.
BIN
tests/resources/submodules/testrepo/.gitted/info/exclude
Normal file
BIN
tests/resources/submodules/testrepo/.gitted/info/exclude
Normal file
Binary file not shown.
BIN
tests/resources/submodules/testrepo/.gitted/logs/HEAD
Normal file
BIN
tests/resources/submodules/testrepo/.gitted/logs/HEAD
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user