Skip to content

Commit

Permalink
Added submodule API and use in status
Browse files Browse the repository at this point in the history
When processing status for a newly checked out repo, it is
possible that there will be submodules that have not yet been
initialized.  The only way to distinguish these from untracked
directories is to have some knowledge of submodules.  This
commit adds a new submodule API which, given a name or path,
can determine if it appears to be a submodule and can give
information about the submodule.
  • Loading branch information
arrbee committed Mar 28, 2012
1 parent 277e304 commit bfc9ca5
Show file tree
Hide file tree
Showing 11 changed files with 573 additions and 13 deletions.
2 changes: 1 addition & 1 deletion include/git2.h
Expand Up @@ -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
105 changes: 105 additions & 0 deletions include/git2/submodule.h
@@ -0,0 +1,105 @@
/*
* 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_SUBMOUDLE_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);

#define GIT_SUBMODULE_HEAD "[internal]HEAD"

/**
* 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
6 changes: 3 additions & 3 deletions src/config.c
Expand Up @@ -201,7 +201,7 @@ 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) {
Expand Down Expand Up @@ -301,7 +301,7 @@ 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;
Expand Down Expand Up @@ -372,7 +372,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) {
Expand Down
2 changes: 2 additions & 0 deletions src/config.h
Expand Up @@ -25,4 +25,6 @@ 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);

#endif
3 changes: 3 additions & 0 deletions src/config_file.c
Expand Up @@ -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)
Expand Down
31 changes: 31 additions & 0 deletions 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

30 changes: 22 additions & 8 deletions src/iterator.c
Expand Up @@ -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 {
Expand Down Expand Up @@ -424,13 +425,24 @@ 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)
{
size_t len = strlen(wi->entry.path);
assert(wi->entry.path[len - 1] == '/');
wi->entry.path[len - 1] = '\0';
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;
Expand Down Expand Up @@ -489,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. */
Expand Down
1 change: 1 addition & 0 deletions src/repository.c
Expand Up @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions src/repository.h
Expand Up @@ -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;
Expand Down Expand Up @@ -120,4 +121,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

0 comments on commit bfc9ca5

Please sign in to comment.