summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--builtin/grep.c3
-rw-r--r--builtin/ls-files.c192
-rw-r--r--builtin/submodule--helper.c9
-rw-r--r--cache.h62
-rw-r--r--config.c216
-rw-r--r--config.h24
-rw-r--r--environment.c91
-rw-r--r--git.c2
-rw-r--r--path.c130
-rw-r--r--path.h82
-rw-r--r--repository.c242
-rw-r--r--repository.h97
-rw-r--r--setup.c33
-rw-r--r--submodule-config.c70
-rw-r--r--submodule-config.h10
-rw-r--r--submodule.c35
-rw-r--r--submodule.h4
-rwxr-xr-xt/t3007-ls-files-recurse-submodules.sh39
-rw-r--r--worktree.c3
20 files changed, 972 insertions, 373 deletions
diff --git a/Makefile b/Makefile
index b94cd5633c..9c9c42f8f9 100644
--- a/Makefile
+++ b/Makefile
@@ -840,6 +840,7 @@ LIB_OBJS += refs/ref-cache.o
LIB_OBJS += ref-filter.o
LIB_OBJS += remote.o
LIB_OBJS += replace_object.o
+LIB_OBJS += repository.o
LIB_OBJS += rerere.o
LIB_OBJS += resolve-undo.o
LIB_OBJS += revision.o
diff --git a/builtin/grep.c b/builtin/grep.c
index f752f642ff..fa351c49f4 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -4,6 +4,7 @@
* Copyright (c) 2006 Junio C Hamano
*/
#include "cache.h"
+#include "repository.h"
#include "config.h"
#include "blob.h"
#include "tree.h"
@@ -643,7 +644,7 @@ static int grep_submodule_launch(struct grep_opt *opt,
static int grep_submodule(struct grep_opt *opt, const struct object_id *oid,
const char *filename, const char *path)
{
- if (!is_submodule_initialized(path))
+ if (!is_submodule_active(the_repository, path))
return 0;
if (!is_submodule_populated_gently(path, NULL)) {
/*
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index b12d0bb612..b8514a0029 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -5,7 +5,9 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
+#define NO_THE_INDEX_COMPATIBILITY_MACROS
#include "cache.h"
+#include "repository.h"
#include "config.h"
#include "quote.h"
#include "dir.h"
@@ -32,10 +34,8 @@ static int line_terminator = '\n';
static int debug_mode;
static int show_eol;
static int recurse_submodules;
-static struct argv_array submodule_options = ARGV_ARRAY_INIT;
static const char *prefix;
-static const char *super_prefix;
static int max_prefix_len;
static int prefix_len;
static struct pathspec pathspec;
@@ -74,24 +74,11 @@ static void write_eolinfo(const struct index_state *istate,
static void write_name(const char *name)
{
/*
- * Prepend the super_prefix to name to construct the full_name to be
- * written.
- */
- struct strbuf full_name = STRBUF_INIT;
- if (super_prefix) {
- strbuf_addstr(&full_name, super_prefix);
- strbuf_addstr(&full_name, name);
- name = full_name.buf;
- }
-
- /*
* With "--full-name", prefix_len=0; this caller needs to pass
* an empty string in that case (a NULL is good for "").
*/
write_name_quoted_relative(name, prefix_len ? prefix : NULL,
stdout, line_terminator);
-
- strbuf_release(&full_name);
}
static const char *get_tag(const struct cache_entry *ce, const char *tag)
@@ -210,83 +197,38 @@ static void show_killed_files(const struct index_state *istate,
}
}
-/*
- * Compile an argv_array with all of the options supported by --recurse_submodules
- */
-static void compile_submodule_options(const char **argv,
- const struct dir_struct *dir,
- int show_tag)
-{
- if (line_terminator == '\0')
- argv_array_push(&submodule_options, "-z");
- if (show_tag)
- argv_array_push(&submodule_options, "-t");
- if (show_valid_bit)
- argv_array_push(&submodule_options, "-v");
- if (show_cached)
- argv_array_push(&submodule_options, "--cached");
- if (show_eol)
- argv_array_push(&submodule_options, "--eol");
- if (debug_mode)
- argv_array_push(&submodule_options, "--debug");
-
- /* Add Pathspecs */
- argv_array_push(&submodule_options, "--");
- for (; *argv; argv++)
- argv_array_push(&submodule_options, *argv);
-}
+static void show_files(struct repository *repo, struct dir_struct *dir);
-/**
- * Recursively call ls-files on a submodule
- */
-static void show_gitlink(const struct cache_entry *ce)
+static void show_submodule(struct repository *superproject,
+ struct dir_struct *dir, const char *path)
{
- struct child_process cp = CHILD_PROCESS_INIT;
- int status;
- char *dir;
-
- prepare_submodule_repo_env(&cp.env_array);
- argv_array_push(&cp.env_array, GIT_DIR_ENVIRONMENT);
-
- if (prefix_len)
- argv_array_pushf(&cp.env_array, "%s=%s",
- GIT_TOPLEVEL_PREFIX_ENVIRONMENT,
- prefix);
- argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
- super_prefix ? super_prefix : "",
- ce->name);
- argv_array_push(&cp.args, "ls-files");
- argv_array_push(&cp.args, "--recurse-submodules");
-
- /* add supported options */
- argv_array_pushv(&cp.args, submodule_options.argv);
-
- cp.git_cmd = 1;
- dir = mkpathdup("%s/%s", get_git_work_tree(), ce->name);
- cp.dir = dir;
- status = run_command(&cp);
- free(dir);
- if (status)
- exit(status);
+ struct repository submodule;
+
+ if (repo_submodule_init(&submodule, superproject, path))
+ return;
+
+ if (repo_read_index(&submodule) < 0)
+ die("index file corrupt");
+
+ repo_read_gitmodules(&submodule);
+
+ show_files(&submodule, dir);
+
+ repo_clear(&submodule);
}
-static void show_ce_entry(const struct index_state *istate,
- const char *tag, const struct cache_entry *ce)
+static void show_ce(struct repository *repo, struct dir_struct *dir,
+ const struct cache_entry *ce, const char *fullname,
+ const char *tag)
{
- struct strbuf name = STRBUF_INIT;
- int len = max_prefix_len;
- if (super_prefix)
- strbuf_addstr(&name, super_prefix);
- strbuf_addstr(&name, ce->name);
-
- if (len > ce_namelen(ce))
+ if (max_prefix_len > strlen(fullname))
die("git ls-files: internal error - cache entry not superset of prefix");
if (recurse_submodules && S_ISGITLINK(ce->ce_mode) &&
- submodule_path_match(&pathspec, name.buf, ps_matched)) {
- show_gitlink(ce);
- } else if (match_pathspec(&pathspec, name.buf, name.len,
- len, ps_matched,
+ is_submodule_active(repo, ce->name)) {
+ show_submodule(repo, dir, ce->name);
+ } else if (match_pathspec(&pathspec, fullname, strlen(fullname),
+ max_prefix_len, ps_matched,
S_ISDIR(ce->ce_mode) ||
S_ISGITLINK(ce->ce_mode))) {
tag = get_tag(ce, tag);
@@ -300,12 +242,10 @@ static void show_ce_entry(const struct index_state *istate,
find_unique_abbrev(ce->oid.hash, abbrev),
ce_stage(ce));
}
- write_eolinfo(istate, ce, ce->name);
- write_name(ce->name);
+ write_eolinfo(repo->index, ce, fullname);
+ write_name(fullname);
print_debug(ce);
}
-
- strbuf_release(&name);
}
static void show_ru_info(const struct index_state *istate)
@@ -338,59 +278,79 @@ static void show_ru_info(const struct index_state *istate)
}
static int ce_excluded(struct dir_struct *dir, struct index_state *istate,
- const struct cache_entry *ce)
+ const char *fullname, const struct cache_entry *ce)
{
int dtype = ce_to_dtype(ce);
- return is_excluded(dir, istate, ce->name, &dtype);
+ return is_excluded(dir, istate, fullname, &dtype);
+}
+
+static void construct_fullname(struct strbuf *out, const struct repository *repo,
+ const struct cache_entry *ce)
+{
+ strbuf_reset(out);
+ if (repo->submodule_prefix)
+ strbuf_addstr(out, repo->submodule_prefix);
+ strbuf_addstr(out, ce->name);
}
-static void show_files(struct index_state *istate, struct dir_struct *dir)
+static void show_files(struct repository *repo, struct dir_struct *dir)
{
int i;
+ struct strbuf fullname = STRBUF_INIT;
/* For cached/deleted files we don't need to even do the readdir */
if (show_others || show_killed) {
if (!show_others)
dir->flags |= DIR_COLLECT_KILLED_ONLY;
- fill_directory(dir, istate, &pathspec);
+ fill_directory(dir, repo->index, &pathspec);
if (show_others)
- show_other_files(istate, dir);
+ show_other_files(repo->index, dir);
if (show_killed)
- show_killed_files(istate, dir);
+ show_killed_files(repo->index, dir);
}
if (show_cached || show_stage) {
- for (i = 0; i < istate->cache_nr; i++) {
- const struct cache_entry *ce = istate->cache[i];
+ for (i = 0; i < repo->index->cache_nr; i++) {
+ const struct cache_entry *ce = repo->index->cache[i];
+
+ construct_fullname(&fullname, repo, ce);
+
if ((dir->flags & DIR_SHOW_IGNORED) &&
- !ce_excluded(dir, istate, ce))
+ !ce_excluded(dir, repo->index, fullname.buf, ce))
continue;
if (show_unmerged && !ce_stage(ce))
continue;
if (ce->ce_flags & CE_UPDATE)
continue;
- show_ce_entry(istate, ce_stage(ce) ? tag_unmerged :
- (ce_skip_worktree(ce) ? tag_skip_worktree : tag_cached), ce);
+ show_ce(repo, dir, ce, fullname.buf,
+ ce_stage(ce) ? tag_unmerged :
+ (ce_skip_worktree(ce) ? tag_skip_worktree :
+ tag_cached));
}
}
if (show_deleted || show_modified) {
- for (i = 0; i < istate->cache_nr; i++) {
- const struct cache_entry *ce = istate->cache[i];
+ for (i = 0; i < repo->index->cache_nr; i++) {
+ const struct cache_entry *ce = repo->index->cache[i];
struct stat st;
int err;
+
+ construct_fullname(&fullname, repo, ce);
+
if ((dir->flags & DIR_SHOW_IGNORED) &&
- !ce_excluded(dir, istate, ce))
+ !ce_excluded(dir, repo->index, fullname.buf, ce))
continue;
if (ce->ce_flags & CE_UPDATE)
continue;
if (ce_skip_worktree(ce))
continue;
- err = lstat(ce->name, &st);
+ err = lstat(fullname.buf, &st);
if (show_deleted && err)
- show_ce_entry(istate, tag_removed, ce);
- if (show_modified && ie_modified(istate, ce, &st, 0))
- show_ce_entry(istate, tag_modified, ce);
+ show_ce(repo, dir, ce, fullname.buf, tag_removed);
+ if (show_modified && ie_modified(repo->index, ce, &st, 0))
+ show_ce(repo, dir, ce, fullname.buf, tag_modified);
}
}
+
+ strbuf_release(&fullname);
}
/*
@@ -615,10 +575,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
prefix = cmd_prefix;
if (prefix)
prefix_len = strlen(prefix);
- super_prefix = get_super_prefix();
git_config(git_default_config, NULL);
- if (read_cache() < 0)
+ if (repo_read_index(the_repository) < 0)
die("index file corrupt");
argc = parse_options(argc, argv, prefix, builtin_ls_files_options,
@@ -652,7 +611,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
setup_work_tree();
if (recurse_submodules)
- compile_submodule_options(argv, &dir, show_tag);
+ repo_read_gitmodules(the_repository);
if (recurse_submodules &&
(show_stage || show_deleted || show_others || show_unmerged ||
@@ -670,7 +629,10 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
/*
* Find common prefix for all pathspec's
* This is used as a performance optimization which unfortunately cannot
- * be done when recursing into submodules
+ * be done when recursing into submodules because when a pathspec is
+ * given which spans repository boundaries you can't simply remove the
+ * submodule entry because the pathspec may match something inside the
+ * submodule.
*/
if (recurse_submodules)
max_prefix = NULL;
@@ -678,7 +640,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
max_prefix = common_prefix(&pathspec);
max_prefix_len = get_common_prefix_len(max_prefix);
- prune_index(&the_index, max_prefix, max_prefix_len);
+ prune_index(the_repository->index, max_prefix, max_prefix_len);
/* Treat unmatching pathspec elements as errors */
if (pathspec.nr && error_unmatch)
@@ -699,11 +661,13 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
*/
if (show_stage || show_unmerged)
die("ls-files --with-tree is incompatible with -s or -u");
- overlay_tree_on_index(&the_index, with_tree, max_prefix);
+ overlay_tree_on_index(the_repository->index, with_tree, max_prefix);
}
- show_files(&the_index, &dir);
+
+ show_files(the_repository, &dir);
+
if (show_resolve_undo)
- show_ru_info(&the_index);
+ show_ru_info(the_repository->index);
if (ps_matched) {
int bad;
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 8517032b3e..e1b06c41d8 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1,4 +1,5 @@
#include "builtin.h"
+#include "repository.h"
#include "cache.h"
#include "config.h"
#include "parse-options.h"
@@ -280,7 +281,7 @@ static void module_list_active(struct module_list *list)
for (i = 0; i < list->nr; i++) {
const struct cache_entry *ce = list->entries[i];
- if (!is_submodule_initialized(ce->name))
+ if (!is_submodule_active(the_repository, ce->name))
continue;
ALLOC_GROW(active_modules.entries,
@@ -362,7 +363,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
*
* Set active flag for the submodule being initialized
*/
- if (!is_submodule_initialized(path)) {
+ if (!is_submodule_active(the_repository, path)) {
strbuf_reset(&sb);
strbuf_addf(&sb, "submodule.%s.active", sub->name);
git_config_set_gently(sb.buf, "true");
@@ -817,7 +818,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
}
/* Check if the submodule has been initialized. */
- if (!is_submodule_initialized(ce->name)) {
+ if (!is_submodule_active(the_repository, ce->name)) {
next_submodule_warn_missing(suc, out, displaypath);
goto cleanup;
}
@@ -1193,7 +1194,7 @@ static int is_active(int argc, const char **argv, const char *prefix)
gitmodules_config();
- return !is_submodule_initialized(argv[1]);
+ return !is_submodule_active(the_repository, argv[1]);
}
#define SUPPORT_SUPER_PREFIX (1<<0)
diff --git a/cache.h b/cache.h
index 96055c2229..c958fc3ce5 100644
--- a/cache.h
+++ b/cache.h
@@ -11,6 +11,7 @@
#include "string-list.h"
#include "pack-revindex.h"
#include "hash.h"
+#include "path.h"
#ifndef platform_SHA_CTX
/*
@@ -462,6 +463,8 @@ static inline enum object_type object_type(unsigned int mode)
*/
extern const char * const local_repo_env[];
+extern void setup_git_env(void);
+
/*
* Returns true iff we have a configured git repository (either via
* setup_git_directory, or in the environment via $GIT_DIR).
@@ -769,7 +772,6 @@ extern int core_apply_sparse_checkout;
extern int precomposed_unicode;
extern int protect_hfs;
extern int protect_ntfs;
-extern int git_db_env, git_index_env, git_graft_env, git_common_dir_env;
/*
* Include broken refs in all ref iterations, which will
@@ -892,64 +894,6 @@ extern void check_repository_format(void);
#define TYPE_CHANGED 0x0040
/*
- * Return a statically allocated filename, either generically (mkpath), in
- * the repository directory (git_path), or in a submodule's repository
- * directory (git_path_submodule). In all cases, note that the result
- * may be overwritten by another call to _any_ of the functions. Consider
- * using the safer "dup" or "strbuf" formats below (in some cases, the
- * unsafe versions have already been removed).
- */
-extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
-extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
-extern const char *git_common_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
-
-extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
- __attribute__((format (printf, 3, 4)));
-extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
- __attribute__((format (printf, 2, 3)));
-extern void strbuf_git_common_path(struct strbuf *sb, const char *fmt, ...)
- __attribute__((format (printf, 2, 3)));
-extern char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
- __attribute__((format (printf, 2, 3)));
-extern int strbuf_git_path_submodule(struct strbuf *sb, const char *path,
- const char *fmt, ...)
- __attribute__((format (printf, 3, 4)));
-extern char *git_pathdup(const char *fmt, ...)
- __attribute__((format (printf, 1, 2)));
-extern char *mkpathdup(const char *fmt, ...)
- __attribute__((format (printf, 1, 2)));
-extern char *git_pathdup_submodule(const char *path, const char *fmt, ...)
- __attribute__((format (printf, 2, 3)));
-
-extern void report_linked_checkout_garbage(void);
-
-/*
- * You can define a static memoized git path like:
- *
- * static GIT_PATH_FUNC(git_path_foo, "FOO");
- *
- * or use one of the global ones below.
- */
-#define GIT_PATH_FUNC(func, filename) \
- const char *func(void) \
- { \
- static char *ret; \
- if (!ret) \
- ret = git_pathdup(filename); \
- return ret; \
- }
-
-const char *git_path_cherry_pick_head(void);
-const char *git_path_revert_head(void);
-const char *git_path_squash_msg(void);
-const char *git_path_merge_msg(void);
-const char *git_path_merge_rr(void);
-const char *git_path_merge_mode(void);
-const char *git_path_merge_head(void);
-const char *git_path_fetch_head(void);
-const char *git_path_shallow(void);
-
-/*
* Return the name of the file in the local object database that would
* be used to store a loose object with the specified sha1. The
* return value is a pointer to a statically allocated buffer that is
diff --git a/config.c b/config.c
index 1cd40a5fe6..4638b0696a 100644
--- a/config.c
+++ b/config.c
@@ -7,6 +7,7 @@
*/
#include "cache.h"
#include "config.h"
+#include "repository.h"
#include "lockfile.h"
#include "exec_cmd.h"
#include "strbuf.h"
@@ -72,13 +73,6 @@ static int core_compression_seen;
static int pack_compression_seen;
static int zlib_compression_seen;
-/*
- * Default config_set that contains key-value pairs from the usual set of config
- * config files (i.e repo specific .git/config, user wide ~/.gitconfig, XDG
- * config file and the global /etc/gitconfig)
- */
-static struct config_set the_config_set;
-
static int config_file_fgetc(struct config_source *conf)
{
return getc_unlocked(conf->u.file);
@@ -1604,31 +1598,6 @@ int config_with_options(config_fn_t fn, void *data,
return do_git_config_sequence(opts, fn, data);
}
-static void git_config_raw(config_fn_t fn, void *data)
-{
- struct config_options opts = {0};
-
- opts.respect_includes = 1;
- if (have_git_dir()) {
- opts.commondir = get_git_common_dir();
- opts.git_dir = get_git_dir();
- }
-
- if (config_with_options(fn, data, NULL, &opts) < 0)
- /*
- * config_with_options() normally returns only
- * zero, as most errors are fatal, and
- * non-fatal potential errors are guarded by "if"
- * statements that are entered only when no error is
- * possible.
- *
- * If we ever encounter a non-fatal error, it means
- * something went really wrong and we should stop
- * immediately.
- */
- die(_("unknown error occurred while reading the configuration files"));
-}
-
static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
{
int i, value_index;
@@ -1682,14 +1651,6 @@ void read_early_config(config_fn_t cb, void *data)
strbuf_release(&gitdir);
}
-static void git_config_check_init(void);
-
-void git_config(config_fn_t fn, void *data)
-{
- git_config_check_init();
- configset_iter(&the_config_set, fn, data);
-}
-
static struct config_set_element *configset_find_element(struct config_set *cs, const char *key)
{
struct config_set_element k;
@@ -1899,87 +1860,194 @@ int git_configset_get_pathname(struct config_set *cs, const char *key, const cha
return 1;
}
-static void git_config_check_init(void)
+/* Functions use to read configuration from a repository */
+static void repo_read_config(struct repository *repo)
{
- if (the_config_set.hash_initialized)
+ struct config_options opts;
+
+ opts.respect_includes = 1;
+ opts.commondir = repo->commondir;
+ opts.git_dir = repo->gitdir;
+
+ if (!repo->config)
+ repo->config = xcalloc(1, sizeof(struct config_set));
+ else
+ git_configset_clear(repo->config);
+
+ git_configset_init(repo->config);
+
+ if (config_with_options(config_set_callback, repo->config, NULL, &opts) < 0)
+ /*
+ * config_with_options() normally returns only
+ * zero, as most errors are fatal, and
+ * non-fatal potential errors are guarded by "if"
+ * statements that are entered only when no error is
+ * possible.
+ *
+ * If we ever encounter a non-fatal error, it means
+ * something went really wrong and we should stop
+ * immediately.
+ */
+ die(_("unknown error occurred while reading the configuration files"));
+}
+
+static void git_config_check_init(struct repository *repo)
+{
+ if (repo->config && repo->config->hash_initialized)
return;
- git_configset_init(&the_config_set);
- git_config_raw(config_set_callback, &the_config_set);
+ repo_read_config(repo);
}
-void git_config_clear(void)
+static void repo_config_clear(struct repository *repo)
{
- if (!the_config_set.hash_initialized)
+ if (!repo->config || !repo->config->hash_initialized)
return;
- git_configset_clear(&the_config_set);
+ git_configset_clear(repo->config);
}
-int git_config_get_value(const char *key, const char **value)
+void repo_config(struct repository *repo, config_fn_t fn, void *data)
{
- git_config_check_init();
- return git_configset_get_value(&the_config_set, key, value);
+ git_config_check_init(repo);
+ configset_iter(repo->config, fn, data);
}
-const struct string_list *git_config_get_value_multi(const char *key)
+int repo_config_get_value(struct repository *repo,
+ const char *key, const char **value)
{
- git_config_check_init();
- return git_configset_get_value_multi(&the_config_set, key);
+ git_config_check_init(repo);
+ return git_configset_get_value(repo->config, key, value);
}
-int git_config_get_string_const(const char *key, const char **dest)
+const struct string_list *repo_config_get_value_multi(struct repository *repo,
+ const char *key)
+{
+ git_config_check_init(repo);
+ return git_configset_get_value_multi(repo->config, key);
+}
+
+int repo_config_get_string_const(struct repository *repo,
+ const char *key, const char **dest)
+{
+ int ret;
+ git_config_check_init(repo);
+ ret = git_configset_get_string_const(repo->config, key, dest);
+ if (ret < 0)
+ git_die_config(key, NULL);
+ return ret;
+}
+
+int repo_config_get_string(struct repository *repo,
+ const char *key, char **dest)
+{
+ git_config_check_init(repo);
+ return repo_config_get_string_const(repo, key, (const char **)dest);
+}
+
+int repo_config_get_int(struct repository *repo,
+ const char *key, int *dest)
+{
+ git_config_check_init(repo);
+ return git_configset_get_int(repo->config, key, dest);
+}
+
+int repo_config_get_ulong(struct repository *repo,
+ const char *key, unsigned long *dest)
+{
+ git_config_check_init(repo);
+ return git_configset_get_ulong(repo->config, key, dest);
+}
+
+int repo_config_get_bool(struct repository *repo,
+ const char *key, int *dest)
+{
+ git_config_check_init(repo);
+ return git_configset_get_bool(repo->config, key, dest);
+}
+
+int repo_config_get_bool_or_int(struct repository *repo,
+ const char *key, int *is_bool, int *dest)
+{
+ git_config_check_init(repo);
+ return git_configset_get_bool_or_int(repo->config, key, is_bool, dest);
+}
+
+int repo_config_get_maybe_bool(struct repository *repo,
+ const char *key, int *dest)
+{
+ git_config_check_init(repo);
+ return git_configset_get_maybe_bool(repo->config, key, dest);
+}
+
+int repo_config_get_pathname(struct repository *repo,
+ const char *key, const char **dest)
{
int ret;
- git_config_check_init();
- ret = git_configset_get_string_const(&the_config_set, key, dest);
+ git_config_check_init(repo);
+ ret = git_configset_get_pathname(repo->config, key, dest);
if (ret < 0)
git_die_config(key, NULL);
return ret;
}
+/* Functions used historically to read configuration from 'the_repository' */
+void git_config(config_fn_t fn, void *data)
+{
+ repo_config(the_repository, fn, data);
+}
+
+void git_config_clear(void)
+{
+ repo_config_clear(the_repository);
+}
+
+int git_config_get_value(const char *key, const char **value)
+{
+ return repo_config_get_value(the_repository, key, value);
+}
+
+const struct string_list *git_config_get_value_multi(const char *key)
+{
+ return repo_config_get_value_multi(the_repository, key);
+}
+
+int git_config_get_string_const(const char *key, const char **dest)
+{
+ return repo_config_get_string_const(the_repository, key, dest);
+}
+
int git_config_get_string(const char *key, char **dest)
{
- git_config_check_init();
- return git_config_get_string_const(key, (const char **)dest);
+ return repo_config_get_string(the_repository, key, dest);
}
int git_config_get_int(const char *key, int *dest)
{
- git_config_check_init();
- return git_configset_get_int(&the_config_set, key, dest);
+ return repo_config_get_int(the_repository, key, dest);
}
int git_config_get_ulong(const char *key, unsigned long *dest)
{
- git_config_check_init();
- return git_configset_get_ulong(&the_config_set, key, dest);
+ return repo_config_get_ulong(the_repository, key, dest);
}
int git_config_get_bool(const char *key, int *dest)
{
- git_config_check_init();
- return git_configset_get_bool(&the_config_set, key, dest);
+ return repo_config_get_bool(the_repository, key, dest);
}
int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest)
{
- git_config_check_init();
- return git_configset_get_bool_or_int(&the_config_set, key, is_bool, dest);
+ return repo_config_get_bool_or_int(the_repository, key, is_bool, dest);
}
int git_config_get_maybe_bool(const char *key, int *dest)
{
- git_config_check_init();
- return git_configset_get_maybe_bool(&the_config_set, key, dest);
+ return repo_config_get_maybe_bool(the_repository, key, dest);
}
int git_config_get_pathname(const char *key, const char **dest)
{
- int ret;
- git_config_check_init();
- ret = git_configset_get_pathname(&the_config_set, key, dest);
- if (ret < 0)
- git_die_config(key, NULL);
- return ret;
+ return repo_config_get_pathname(the_repository, key, dest);
}
int git_config_get_expiry(const char *key, const char **output)
diff --git a/config.h b/config.h
index 9e038cce25..0352da117b 100644
--- a/config.h
+++ b/config.h
@@ -163,6 +163,30 @@ extern int git_configset_get_bool_or_int(struct config_set *cs, const char *key,
extern int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest);
extern int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest);
+/* Functions for reading a repository's config */
+struct repository;
+extern void repo_config(struct repository *repo, config_fn_t fn, void *data);
+extern int repo_config_get_value(struct repository *repo,
+ const char *key, const char **value);
+extern const struct string_list *repo_config_get_value_multi(struct repository *repo,
+ const char *key);
+extern int repo_config_get_string_const(struct repository *repo,
+ const char *key, const char **dest);
+extern int repo_config_get_string(struct repository *repo,
+ const char *key, char **dest);
+extern int repo_config_get_int(struct repository *repo,
+ const char *key, int *dest);
+extern int repo_config_get_ulong(struct repository *repo,
+ const char *key, unsigned long *dest);
+extern int repo_config_get_bool(struct repository *repo,
+ const char *key, int *dest);
+extern int repo_config_get_bool_or_int(struct repository *repo,
+ const char *key, int *is_bool, int *dest);
+extern int repo_config_get_maybe_bool(struct repository *repo,
+ const char *key, int *dest);
+extern int repo_config_get_pathname(struct repository *repo,
+ const char *key, const char **dest);
+
extern int git_config_get_value(const char *key, const char **value);
extern const struct string_list *git_config_get_value_multi(const char *key);
extern void git_config_clear(void);
diff --git a/environment.c b/environment.c
index d40b21fb72..3fd4b10845 100644
--- a/environment.c
+++ b/environment.c
@@ -8,6 +8,7 @@
* are.
*/
#include "cache.h"
+#include "repository.h"
#include "config.h"
#include "refs.h"
#include "fmt-merge-msg.h"
@@ -95,17 +96,11 @@ int ignore_untracked_cache_config;
/* This is set by setup_git_dir_gently() and/or git_default_config() */
char *git_work_tree_cfg;
-static char *work_tree;
static const char *namespace;
-static size_t namespace_len;
static const char *super_prefix;
-static const char *git_dir, *git_common_dir;
-static char *git_object_dir, *git_index_file, *git_graft_file;
-int git_db_env, git_index_env, git_graft_env, git_common_dir_env;
-
/*
* Repository-local GIT_* environment variables; see cache.h for details.
*/
@@ -149,48 +144,17 @@ static char *expand_namespace(const char *raw_namespace)
return strbuf_detach(&buf, NULL);
}
-static char *git_path_from_env(const char *envvar, const char *git_dir,
- const char *path, int *fromenv)
+void setup_git_env(void)
{
- const char *value = getenv(envvar);
- if (!value)
- return xstrfmt("%s/%s", git_dir, path);
- if (fromenv)
- *fromenv = 1;
- return xstrdup(value);
-}
-
-static void setup_git_env(void)
-{
- struct strbuf sb = STRBUF_INIT;
- const char *gitfile;
const char *shallow_file;
const char *replace_ref_base;
- git_dir = getenv(GIT_DIR_ENVIRONMENT);
- if (!git_dir) {
- if (!startup_info->have_repository)
- BUG("setup_git_env called without repository");
- git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
- }
- gitfile = read_gitfile(git_dir);
- git_dir = xstrdup(gitfile ? gitfile : git_dir);
- if (get_common_dir(&sb, git_dir))
- git_common_dir_env = 1;
- git_common_dir = strbuf_detach(&sb, NULL);
- git_object_dir = git_path_from_env(DB_ENVIRONMENT, git_common_dir,
- "objects", &git_db_env);
- git_index_file = git_path_from_env(INDEX_ENVIRONMENT, git_dir,
- "index", &git_index_env);
- git_graft_file = git_path_from_env(GRAFT_ENVIRONMENT, git_common_dir,
- "info/grafts", &git_graft_env);
if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
check_replace_refs = 0;
replace_ref_base = getenv(GIT_REPLACE_REF_BASE_ENVIRONMENT);
git_replace_ref_base = xstrdup(replace_ref_base ? replace_ref_base
: "refs/replace/");
namespace = expand_namespace(getenv(GIT_NAMESPACE_ENVIRONMENT));
- namespace_len = strlen(namespace);
shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);
if (shallow_file)
set_alternate_shallow_file(shallow_file, 0);
@@ -205,36 +169,36 @@ int is_bare_repository(void)
int have_git_dir(void)
{
return startup_info->have_repository
- || git_dir
- || getenv(GIT_DIR_ENVIRONMENT);
+ || the_repository->gitdir;
}
const char *get_git_dir(void)
{
- if (!git_dir)
- setup_git_env();
- return git_dir;
+ if (!the_repository->gitdir)
+ BUG("git environment hasn't been setup");
+ return the_repository->gitdir;
}
const char *get_git_common_dir(void)
{
- if (!git_dir)
- setup_git_env();
- return git_common_dir;
+ if (!the_repository->commondir)
+ BUG("git environment hasn't been setup");
+ return the_repository->commondir;
}
const char *get_git_namespace(void)
{
if (!namespace)
- setup_git_env();
+ BUG("git environment hasn't been setup");
return namespace;
}
const char *strip_namespace(const char *namespaced_ref)
{
- if (!starts_with(namespaced_ref, get_git_namespace()))
- return NULL;
- return namespaced_ref + namespace_len;
+ const char *out;
+ if (skip_prefix(namespaced_ref, get_git_namespace(), &out))
+ return out;
+ return NULL;
}
const char *get_super_prefix(void)
@@ -258,26 +222,26 @@ void set_git_work_tree(const char *new_work_tree)
{
if (git_work_tree_initialized) {
new_work_tree = real_path(new_work_tree);
- if (strcmp(new_work_tree, work_tree))
+ if (strcmp(new_work_tree, the_repository->worktree))
die("internal error: work tree has already been set\n"
"Current worktree: %s\nNew worktree: %s",
- work_tree, new_work_tree);
+ the_repository->worktree, new_work_tree);
return;
}
git_work_tree_initialized = 1;
- work_tree = real_pathdup(new_work_tree, 1);
+ repo_set_worktree(the_repository, new_work_tree);
}
const char *get_git_work_tree(void)
{
- return work_tree;
+ return the_repository->worktree;
}
char *get_object_directory(void)
{
- if (!git_object_dir)
- setup_git_env();
- return git_object_dir;
+ if (!the_repository->objectdir)
+ BUG("git environment hasn't been setup");
+ return the_repository->objectdir;
}
int odb_mkstemp(struct strbuf *template, const char *pattern)
@@ -315,22 +279,23 @@ int odb_pack_keep(const char *name)
char *get_index_file(void)
{
- if (!git_index_file)
- setup_git_env();
- return git_index_file;
+ if (!the_repository->index_file)
+ BUG("git environment hasn't been setup");
+ return the_repository->index_file;
}
char *get_graft_file(void)
{
- if (!git_graft_file)
- setup_git_env();
- return git_graft_file;
+ if (!the_repository->graft_file)
+ BUG("git environment hasn't been setup");
+ return the_repository->graft_file;
}
int set_git_dir(const char *path)
{
if (setenv(GIT_DIR_ENVIRONMENT, path, 1))
return error("Could not set GIT_DIR to '%s'", path);
+ repo_set_gitdir(the_repository, path);
setup_git_env();
return 0;
}
diff --git a/git.c b/git.c
index 5be27b07e5..489aab4d83 100644
--- a/git.c
+++ b/git.c
@@ -400,7 +400,7 @@ static struct cmd_struct commands[] = {
{ "init-db", cmd_init_db },
{ "interpret-trailers", cmd_interpret_trailers, RUN_SETUP_GENTLY },
{ "log", cmd_log, RUN_SETUP },
- { "ls-files", cmd_ls_files, RUN_SETUP | SUPPORT_SUPER_PREFIX },
+ { "ls-files", cmd_ls_files, RUN_SETUP },
{ "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
{ "ls-tree", cmd_ls_tree, RUN_SETUP },
{ "mailinfo", cmd_mailinfo, RUN_SETUP_GENTLY },
diff --git a/path.c b/path.c
index c1cb1cf627..e485f9f931 100644
--- a/path.c
+++ b/path.c
@@ -2,11 +2,13 @@
* Utilities for paths and pathnames
*/
#include "cache.h"
+#include "repository.h"
#include "strbuf.h"
#include "string-list.h"
#include "dir.h"
#include "worktree.h"
#include "submodule-config.h"
+#include "path.h"
static int get_st_mode_bits(const char *path, int *mode)
{
@@ -343,8 +345,6 @@ static void update_common_dir(struct strbuf *buf, int git_dir_len,
{
char *base = buf->buf + git_dir_len;
init_common_trie();
- if (!common_dir)
- common_dir = get_git_common_dir();
if (trie_find(&common_trie, base, check_common, NULL) > 0)
replace_dir(buf, git_dir_len, common_dir);
}
@@ -355,7 +355,7 @@ void report_linked_checkout_garbage(void)
const struct common_dir *p;
int len;
- if (!git_common_dir_env)
+ if (!the_repository->different_commondir)
return;
strbuf_addf(&sb, "%s/", get_git_dir());
len = sb.len;
@@ -371,42 +371,78 @@ void report_linked_checkout_garbage(void)
strbuf_release(&sb);
}
-static void adjust_git_path(struct strbuf *buf, int git_dir_len)
+static void adjust_git_path(const struct repository *repo,
+ struct strbuf *buf, int git_dir_len)
{
const char *base = buf->buf + git_dir_len;
- if (git_graft_env && is_dir_file(base, "info", "grafts"))
+ if (is_dir_file(base, "info", "grafts"))
strbuf_splice(buf, 0, buf->len,
- get_graft_file(), strlen(get_graft_file()));
- else if (git_index_env && !strcmp(base, "index"))
+ repo->graft_file, strlen(repo->graft_file));
+ else if (!strcmp(base, "index"))
strbuf_splice(buf, 0, buf->len,
- get_index_file(), strlen(get_index_file()));
- else if (git_db_env && dir_prefix(base, "objects"))
- replace_dir(buf, git_dir_len + 7, get_object_directory());
+ repo->index_file, strlen(repo->index_file));
+ else if (dir_prefix(base, "objects"))
+ replace_dir(buf, git_dir_len + 7, repo->objectdir);
else if (git_hooks_path && dir_prefix(base, "hooks"))
replace_dir(buf, git_dir_len + 5, git_hooks_path);
- else if (git_common_dir_env)
- update_common_dir(buf, git_dir_len, NULL);
+ else if (repo->different_commondir)
+ update_common_dir(buf, git_dir_len, repo->commondir);
}
-static void do_git_path(const struct worktree *wt, struct strbuf *buf,
+static void strbuf_worktree_gitdir(struct strbuf *buf,
+ const struct repository *repo,
+ const struct worktree *wt)
+{
+ if (!wt)
+ strbuf_addstr(buf, repo->gitdir);
+ else if (!wt->id)
+ strbuf_addstr(buf, repo->commondir);
+ else
+ strbuf_git_common_path(buf, repo, "worktrees/%s", wt->id);
+}
+
+static void do_git_path(const struct repository *repo,
+ const struct worktree *wt, struct strbuf *buf,
const char *fmt, va_list args)
{
int gitdir_len;
- strbuf_addstr(buf, get_worktree_git_dir(wt));
+ strbuf_worktree_gitdir(buf, repo, wt);
if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
strbuf_addch(buf, '/');
gitdir_len = buf->len;
strbuf_vaddf(buf, fmt, args);
- adjust_git_path(buf, gitdir_len);
+ if (!wt)
+ adjust_git_path(repo, buf, gitdir_len);
strbuf_cleanup_path(buf);
}
+char *repo_git_path(const struct repository *repo,
+ const char *fmt, ...)
+{
+ struct strbuf path = STRBUF_INIT;
+ va_list args;
+ va_start(args, fmt);
+ do_git_path(repo, NULL, &path, fmt, args);
+ va_end(args);
+ return strbuf_detach(&path, NULL);
+}
+
+void strbuf_repo_git_path(struct strbuf *sb,
+ const struct repository *repo,
+ const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ do_git_path(repo, NULL, sb, fmt, args);
+ va_end(args);
+}
+
char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
{
va_list args;
strbuf_reset(buf);
va_start(args, fmt);
- do_git_path(NULL, buf, fmt, args);
+ do_git_path(the_repository, NULL, buf, fmt, args);
va_end(args);
return buf->buf;
}
@@ -415,7 +451,7 @@ void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
- do_git_path(NULL, sb, fmt, args);
+ do_git_path(the_repository, NULL, sb, fmt, args);
va_end(args);
}
@@ -424,7 +460,7 @@ const char *git_path(const char *fmt, ...)
struct strbuf *pathname = get_pathname();
va_list args;
va_start(args, fmt);
- do_git_path(NULL, pathname, fmt, args);
+ do_git_path(the_repository, NULL, pathname, fmt, args);
va_end(args);
return pathname->buf;
}
@@ -434,7 +470,7 @@ char *git_pathdup(const char *fmt, ...)
struct strbuf path = STRBUF_INIT;
va_list args;
va_start(args, fmt);
- do_git_path(NULL, &path, fmt, args);
+ do_git_path(the_repository, NULL, &path, fmt, args);
va_end(args);
return strbuf_detach(&path, NULL);
}
@@ -465,11 +501,52 @@ const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...)
struct strbuf *pathname = get_pathname();
va_list args;
va_start(args, fmt);
- do_git_path(wt, pathname, fmt, args);
+ do_git_path(the_repository, wt, pathname, fmt, args);
va_end(args);
return pathname->buf;
}
+static void do_worktree_path(const struct repository *repo,
+ struct strbuf *buf,
+ const char *fmt, va_list args)
+{
+ strbuf_addstr(buf, repo->worktree);
+ if(buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
+ strbuf_addch(buf, '/');
+
+ strbuf_vaddf(buf, fmt, args);
+ strbuf_cleanup_path(buf);
+}
+
+char *repo_worktree_path(const struct repository *repo, const char *fmt, ...)
+{
+ struct strbuf path = STRBUF_INIT;
+ va_list args;
+
+ if (!repo->worktree)
+ return NULL;
+
+ va_start(args, fmt);
+ do_worktree_path(repo, &path, fmt, args);
+ va_end(args);
+
+ return strbuf_detach(&path, NULL);
+}
+
+void strbuf_repo_worktree_path(struct strbuf *sb,
+ const struct repository *repo,
+ const char *fmt, ...)
+{
+ va_list args;
+
+ if (!repo->worktree)
+ return;
+
+ va_start(args, fmt);
+ do_worktree_path(repo, sb, fmt, args);
+ va_end(args);
+}
+
/* Returns 0 on success, negative on failure. */
static int do_submodule_path(struct strbuf *buf, const char *path,
const char *fmt, va_list args)
@@ -524,11 +601,12 @@ int strbuf_git_path_submodule(struct strbuf *buf, const char *path,
return err;
}
-static void do_git_common_path(struct strbuf *buf,
+static void do_git_common_path(const struct repository *repo,
+ struct strbuf *buf,
const char *fmt,
va_list args)
{
- strbuf_addstr(buf, get_git_common_dir());
+ strbuf_addstr(buf, repo->commondir);
if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
strbuf_addch(buf, '/');
strbuf_vaddf(buf, fmt, args);
@@ -540,16 +618,18 @@ const char *git_common_path(const char *fmt, ...)
struct strbuf *pathname = get_pathname();
va_list args;
va_start(args, fmt);
- do_git_common_path(pathname, fmt, args);
+ do_git_common_path(the_repository, pathname, fmt, args);
va_end(args);
return pathname->buf;
}
-void strbuf_git_common_path(struct strbuf *sb, const char *fmt, ...)
+void strbuf_git_common_path(struct strbuf *sb,
+ const struct repository *repo,
+ const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
- do_git_common_path(sb, fmt, args);
+ do_git_common_path(repo, sb, fmt, args);
va_end(args);
}
diff --git a/path.h b/path.h
new file mode 100644
index 0000000000..9541620c79
--- /dev/null
+++ b/path.h
@@ -0,0 +1,82 @@
+#ifndef PATH_H
+#define PATH_H
+
+struct repository;
+
+/*
+ * Return a statically allocated filename, either generically (mkpath), in
+ * the repository directory (git_path), or in a submodule's repository
+ * directory (git_path_submodule). In all cases, note that the result
+ * may be overwritten by another call to _any_ of the functions. Consider
+ * using the safer "dup" or "strbuf" formats below (in some cases, the
+ * unsafe versions have already been removed).
+ */
+extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+extern const char *git_common_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+
+extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
+ __attribute__((format (printf, 3, 4)));
+extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
+ __attribute__((format (printf, 2, 3)));
+extern void strbuf_git_common_path(struct strbuf *sb,
+ const struct repository *repo,
+ const char *fmt, ...)
+ __attribute__((format (printf, 3, 4)));
+extern char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
+ __attribute__((format (printf, 2, 3)));
+extern int strbuf_git_path_submodule(struct strbuf *sb, const char *path,
+ const char *fmt, ...)
+ __attribute__((format (printf, 3, 4)));
+extern char *git_pathdup(const char *fmt, ...)
+ __attribute__((format (printf, 1, 2)));
+extern char *mkpathdup(const char *fmt, ...)
+ __attribute__((format (printf, 1, 2)));
+extern char *git_pathdup_submodule(const char *path, const char *fmt, ...)
+ __attribute__((format (printf, 2, 3)));
+
+extern char *repo_git_path(const struct repository *repo,
+ const char *fmt, ...)
+ __attribute__((format (printf, 2, 3)));
+extern void strbuf_repo_git_path(struct strbuf *sb,
+ const struct repository *repo,
+ const char *fmt, ...)
+ __attribute__((format (printf, 3, 4)));
+
+extern char *repo_worktree_path(const struct repository *repo,
+ const char *fmt, ...)
+ __attribute__((format (printf, 2, 3)));
+extern void strbuf_repo_worktree_path(struct strbuf *sb,
+ const struct repository *repo,
+ const char *fmt, ...)
+ __attribute__((format (printf, 3, 4)));
+
+extern void report_linked_checkout_garbage(void);
+
+/*
+ * You can define a static memoized git path like:
+ *
+ * static GIT_PATH_FUNC(git_path_foo, "FOO");
+ *
+ * or use one of the global ones below.
+ */
+#define GIT_PATH_FUNC(func, filename) \
+ const char *func(void) \
+ { \
+ static char *ret; \
+ if (!ret) \
+ ret = git_pathdup(filename); \
+ return ret; \
+ }
+
+const char *git_path_cherry_pick_head(void);
+const char *git_path_revert_head(void);
+const char *git_path_squash_msg(void);
+const char *git_path_merge_msg(void);
+const char *git_path_merge_rr(void);
+const char *git_path_merge_mode(void);
+const char *git_path_merge_head(void);
+const char *git_path_fetch_head(void);
+const char *git_path_shallow(void);
+
+#endif /* PATH_H */
diff --git a/repository.c b/repository.c
new file mode 100644
index 0000000000..edca907404
--- /dev/null
+++ b/repository.c
@@ -0,0 +1,242 @@
+#include "cache.h"
+#include "repository.h"
+#include "config.h"
+#include "submodule-config.h"
+
+/* The main repository */
+static struct repository the_repo;
+struct repository *the_repository = &the_repo;
+
+static char *git_path_from_env(const char *envvar, const char *git_dir,
+ const char *path, int fromenv)
+{
+ if (fromenv) {
+ const char *value = getenv(envvar);
+ if (value)
+ return xstrdup(value);
+ }
+
+ return xstrfmt("%s/%s", git_dir, path);
+}
+
+static int find_common_dir(struct strbuf *sb, const char *gitdir, int fromenv)
+{
+ if (fromenv) {
+ const char *value = getenv(GIT_COMMON_DIR_ENVIRONMENT);
+ if (value) {
+ strbuf_addstr(sb, value);
+ return 1;
+ }
+ }
+
+ return get_common_dir_noenv(sb, gitdir);
+}
+
+static void repo_setup_env(struct repository *repo)
+{
+ struct strbuf sb = STRBUF_INIT;
+
+ repo->different_commondir = find_common_dir(&sb, repo->gitdir,
+ !repo->ignore_env);
+ repo->commondir = strbuf_detach(&sb, NULL);
+ repo->objectdir = git_path_from_env(DB_ENVIRONMENT, repo->commondir,
+ "objects", !repo->ignore_env);
+ repo->graft_file = git_path_from_env(GRAFT_ENVIRONMENT, repo->commondir,
+ "info/grafts", !repo->ignore_env);
+ repo->index_file = git_path_from_env(INDEX_ENVIRONMENT, repo->gitdir,
+ "index", !repo->ignore_env);
+}
+
+void repo_set_gitdir(struct repository *repo, const char *path)
+{
+ const char *gitfile = read_gitfile(path);
+
+ /*
+ * NEEDSWORK: Eventually we want to be able to free gitdir and the rest
+ * of the environment before reinitializing it again, but we have some
+ * crazy code paths where we try to set gitdir with the current gitdir
+ * and we don't want to free gitdir before copying the passed in value.
+ */
+ repo->gitdir = xstrdup(gitfile ? gitfile : path);
+
+ repo_setup_env(repo);
+}
+
+/*
+ * Attempt to resolve and set the provided 'gitdir' for repository 'repo'.
+ * Return 0 upon success and a non-zero value upon failure.
+ */
+static int repo_init_gitdir(struct repository *repo, const char *gitdir)
+{
+ int ret = 0;
+ int error = 0;
+ char *abspath = NULL;
+ const char *resolved_gitdir;
+
+ abspath = real_pathdup(gitdir, 0);
+ if (!abspath) {
+ ret = -1;
+ goto out;
+ }
+
+ /* 'gitdir' must reference the gitdir directly */
+ resolved_gitdir = resolve_gitdir_gently(abspath, &error);
+ if (!resolved_gitdir) {
+ ret = -1;
+ goto out;
+ }
+
+ repo_set_gitdir(repo, resolved_gitdir);
+
+out:
+ free(abspath);
+ return ret;
+}
+
+void repo_set_worktree(struct repository *repo, const char *path)
+{
+ repo->worktree = real_pathdup(path, 1);
+}
+
+static int read_and_verify_repository_format(struct repository_format *format,
+ const char *commondir)
+{
+ int ret = 0;
+ struct strbuf sb = STRBUF_INIT;
+
+ strbuf_addf(&sb, "%s/config", commondir);
+ read_repository_format(format, sb.buf);
+ strbuf_reset(&sb);
+
+ if (verify_repository_format(format, &sb) < 0) {
+ warning("%s", sb.buf);
+ ret = -1;
+ }
+
+ strbuf_release(&sb);
+ return ret;
+}
+
+/*
+ * Initialize 'repo' based on the provided 'gitdir'.
+ * Return 0 upon success and a non-zero value upon failure.
+ */
+int repo_init(struct repository *repo, const char *gitdir, const char *worktree)
+{
+ struct repository_format format;
+ memset(repo, 0, sizeof(*repo));
+
+ repo->ignore_env = 1;
+
+ if (repo_init_gitdir(repo, gitdir))
+ goto error;
+
+ if (read_and_verify_repository_format(&format, repo->commondir))
+ goto error;
+
+ if (worktree)
+ repo_set_worktree(repo, worktree);
+
+ return 0;
+
+error:
+ repo_clear(repo);
+ return -1;
+}
+
+/*
+ * Initialize 'submodule' as the submodule given by 'path' in parent repository
+ * 'superproject'.
+ * Return 0 upon success and a non-zero value upon failure.
+ */
+int repo_submodule_init(struct repository *submodule,
+ struct repository *superproject,
+ const char *path)
+{
+ const struct submodule *sub;
+ struct strbuf gitdir = STRBUF_INIT;
+ struct strbuf worktree = STRBUF_INIT;
+ int ret = 0;
+
+ sub = submodule_from_cache(superproject, null_sha1, path);
+ if (!sub) {
+ ret = -1;
+ goto out;
+ }
+
+ strbuf_repo_worktree_path(&gitdir, superproject, "%s/.git", path);
+ strbuf_repo_worktree_path(&worktree, superproject, "%s", path);
+
+ if (repo_init(submodule, gitdir.buf, worktree.buf)) {
+ /*
+ * If initilization fails then it may be due to the submodule
+ * not being populated in the superproject's worktree. Instead
+ * we can try to initilize the submodule by finding it's gitdir
+ * in the superproject's 'modules' directory. In this case the
+ * submodule would not have a worktree.
+ */
+ strbuf_reset(&gitdir);
+ strbuf_repo_git_path(&gitdir, superproject,
+ "modules/%s", sub->name);
+
+ if (repo_init(submodule, gitdir.buf, NULL)) {
+ ret = -1;
+ goto out;
+ }
+ }
+
+ submodule->submodule_prefix = xstrfmt("%s%s/",
+ superproject->submodule_prefix ?
+ superproject->submodule_prefix :
+ "", path);
+
+out:
+ strbuf_release(&gitdir);
+ strbuf_release(&worktree);
+ return ret;
+}
+
+void repo_clear(struct repository *repo)
+{
+ free(repo->gitdir);
+ repo->gitdir = NULL;
+ free(repo->commondir);
+ repo->commondir = NULL;
+ free(repo->objectdir);
+ repo->objectdir = NULL;
+ free(repo->graft_file);
+ repo->graft_file = NULL;
+ free(repo->index_file);
+ repo->index_file = NULL;
+ free(repo->worktree);
+ repo->worktree = NULL;
+ free(repo->submodule_prefix);
+ repo->submodule_prefix = NULL;
+
+ if (repo->config) {
+ git_configset_clear(repo->config);
+ free(repo->config);
+ repo->config = NULL;
+ }
+
+ if (repo->submodule_cache) {
+ submodule_cache_free(repo->submodule_cache);
+ repo->submodule_cache = NULL;
+ }
+
+ if (repo->index) {
+ discard_index(repo->index);
+ free(repo->index);
+ repo->index = NULL;
+ }
+}
+
+int repo_read_index(struct repository *repo)
+{
+ if (!repo->index)
+ repo->index = xcalloc(1, sizeof(*repo->index));
+ else
+ discard_index(repo->index);
+
+ return read_index_from(repo->index, repo->index_file);
+}
diff --git a/repository.h b/repository.h
new file mode 100644
index 0000000000..417787f3ef
--- /dev/null
+++ b/repository.h
@@ -0,0 +1,97 @@
+#ifndef REPOSITORY_H
+#define REPOSITORY_H
+
+struct config_set;
+struct index_state;
+struct submodule_cache;
+
+struct repository {
+ /* Environment */
+ /*
+ * Path to the git directory.
+ * Cannot be NULL after initialization.
+ */
+ char *gitdir;
+
+ /*
+ * Path to the common git directory.
+ * Cannot be NULL after initialization.
+ */
+ char *commondir;
+
+ /*
+ * Path to the repository's object store.
+ * Cannot be NULL after initialization.
+ */
+ char *objectdir;
+
+ /*
+ * Path to the repository's graft file.
+ * Cannot be NULL after initialization.
+ */
+ char *graft_file;
+
+ /*
+ * Path to the current worktree's index file.
+ * Cannot be NULL after initialization.
+ */
+ char *index_file;
+
+ /*
+ * Path to the working directory.
+ * A NULL value indicates that there is no working directory.
+ */
+ char *worktree;
+
+ /*
+ * Path from the root of the top-level superproject down to this
+ * repository. This is only non-NULL if the repository is initialized
+ * as a submodule of another repository.
+ */
+ char *submodule_prefix;
+
+ /* Subsystems */
+ /*
+ * Repository's config which contains key-value pairs from the usual
+ * set of config files (i.e. repo specific .git/config, user wide
+ * ~/.gitconfig, XDG config file and the global /etc/gitconfig)
+ */
+ struct config_set *config;
+
+ /* Repository's submodule config as defined by '.gitmodules' */
+ struct submodule_cache *submodule_cache;
+
+ /*
+ * Repository's in-memory index.
+ * 'repo_read_index()' can be used to populate 'index'.
+ */
+ struct index_state *index;
+
+ /* Configurations */
+ /*
+ * Bit used during initialization to indicate if repository state (like
+ * the location of the 'objectdir') should be read from the
+ * environment. By default this bit will be set at the begining of
+ * 'repo_init()' so that all repositories will ignore the environment.
+ * The exception to this is 'the_repository', which doesn't go through
+ * the normal 'repo_init()' process.
+ */
+ unsigned ignore_env:1;
+
+ /* Indicate if a repository has a different 'commondir' from 'gitdir' */
+ unsigned different_commondir:1;
+};
+
+extern struct repository *the_repository;
+
+extern void repo_set_gitdir(struct repository *repo, const char *path);
+extern void repo_set_worktree(struct repository *repo, const char *path);
+extern int repo_init(struct repository *repo, const char *gitdir, const char *worktree);
+extern int repo_submodule_init(struct repository *submodule,
+ struct repository *superproject,
+ const char *path);
+extern void repo_clear(struct repository *repo);
+
+extern int repo_read_index(struct repository *repo);
+
+#endif /* REPOSITORY_H */
diff --git a/setup.c b/setup.c
index 358fbc2e53..860507e1fd 100644
--- a/setup.c
+++ b/setup.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "repository.h"
#include "config.h"
#include "dir.h"
#include "string-list.h"
@@ -398,6 +399,11 @@ void setup_work_tree(void)
if (getenv(GIT_WORK_TREE_ENVIRONMENT))
setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1);
+ /*
+ * NEEDSWORK: this call can essentially be set_git_dir(get_git_dir())
+ * which can cause some problems when trying to free the old value of
+ * gitdir.
+ */
set_git_dir(remove_leading_path(git_dir, work_tree));
initialized = 1;
}
@@ -1079,6 +1085,12 @@ const char *setup_git_directory_gently(int *nongit_ok)
die("BUG: unhandled setup_git_directory_1() result");
}
+ /*
+ * NEEDSWORK: This was a hack in order to get ls-files and grep to have
+ * properly formated output when recursing submodules. Once ls-files
+ * and grep have been changed to perform this recursing in-process this
+ * needs to be removed.
+ */
env_prefix = getenv(GIT_TOPLEVEL_PREFIX_ENVIRONMENT);
if (env_prefix)
prefix = env_prefix;
@@ -1091,6 +1103,27 @@ const char *setup_git_directory_gently(int *nongit_ok)
startup_info->have_repository = !nongit_ok || !*nongit_ok;
startup_info->prefix = prefix;
+ /*
+ * Not all paths through the setup code will call 'set_git_dir()' (which
+ * directly sets up the environment) so in order to guarantee that the
+ * environment is in a consistent state after setup, explicitly setup
+ * the environment if we have a repository.
+ *
+ * NEEDSWORK: currently we allow bogus GIT_DIR values to be set in some
+ * code paths so we also need to explicitly setup the environment if
+ * the user has set GIT_DIR. It may be beneficial to disallow bogus
+ * GIT_DIR values at some point in the future.
+ */
+ if (startup_info->have_repository || getenv(GIT_DIR_ENVIRONMENT)) {
+ if (!the_repository->gitdir) {
+ const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
+ if (!gitdir)
+ gitdir = DEFAULT_GIT_DIR_ENVIRONMENT;
+ repo_set_gitdir(the_repository, gitdir);
+ setup_git_env();
+ }
+ }
+
strbuf_release(&dir);
strbuf_release(&gitdir);
diff --git a/submodule-config.c b/submodule-config.c
index d8f8d5ea32..37cfcceb95 100644
--- a/submodule-config.c
+++ b/submodule-config.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "repository.h"
#include "config.h"
#include "submodule-config.h"
#include "submodule.h"
@@ -15,6 +16,7 @@
struct submodule_cache {
struct hashmap for_path;
struct hashmap for_name;
+ unsigned initialized:1;
};
/*
@@ -31,9 +33,6 @@ enum lookup_type {
lookup_path
};
-static struct submodule_cache the_submodule_cache;
-static int is_cache_init;
-
static int config_path_cmp(const struct submodule_entry *a,
const struct submodule_entry *b,
const void *unused)
@@ -50,10 +49,16 @@ static int config_name_cmp(const struct submodule_entry *a,
hashcmp(a->config->gitmodules_sha1, b->config->gitmodules_sha1);
}
-static void cache_init(struct submodule_cache *cache)
+static struct submodule_cache *submodule_cache_alloc(void)
+{
+ return xcalloc(1, sizeof(struct submodule_cache));
+}
+
+static void submodule_cache_init(struct submodule_cache *cache)
{
hashmap_init(&cache->for_path, (hashmap_cmp_fn) config_path_cmp, 0);
hashmap_init(&cache->for_name, (hashmap_cmp_fn) config_name_cmp, 0);
+ cache->initialized = 1;
}
static void free_one_config(struct submodule_entry *entry)
@@ -65,11 +70,14 @@ static void free_one_config(struct submodule_entry *entry)
free(entry->config);
}
-static void cache_free(struct submodule_cache *cache)
+static void submodule_cache_clear(struct submodule_cache *cache)
{
struct hashmap_iter iter;
struct submodule_entry *entry;
+ if (!cache->initialized)
+ return;
+
/*
* We iterate over the name hash here to be symmetric with the
* allocation of struct submodule entries. Each is allocated by
@@ -81,6 +89,13 @@ static void cache_free(struct submodule_cache *cache)
hashmap_free(&cache->for_path, 1);
hashmap_free(&cache->for_name, 1);
+ cache->initialized = 0;
+}
+
+void submodule_cache_free(struct submodule_cache *cache)
+{
+ submodule_cache_clear(cache);
+ free(cache);
}
static unsigned int hash_sha1_string(const unsigned char *sha1,
@@ -494,43 +509,62 @@ out:
return submodule;
}
-static void ensure_cache_init(void)
+static void submodule_cache_check_init(struct repository *repo)
{
- if (is_cache_init)
+ if (repo->submodule_cache && repo->submodule_cache->initialized)
return;
- cache_init(&the_submodule_cache);
- is_cache_init = 1;
+ if (!repo->submodule_cache)
+ repo->submodule_cache = submodule_cache_alloc();
+
+ submodule_cache_init(repo->submodule_cache);
}
-int parse_submodule_config_option(const char *var, const char *value)
+int submodule_config_option(struct repository *repo,
+ const char *var, const char *value)
{
struct parse_config_parameter parameter;
- parameter.cache = &the_submodule_cache;
+
+ submodule_cache_check_init(repo);
+
+ parameter.cache = repo->submodule_cache;
parameter.treeish_name = NULL;
parameter.gitmodules_sha1 = null_sha1;
parameter.overwrite = 1;
- ensure_cache_init();
return parse_config(var, value, &parameter);
}
+int parse_submodule_config_option(const char *var, const char *value)
+{
+ return submodule_config_option(the_repository, var, value);
+}
+
const struct submodule *submodule_from_name(const unsigned char *treeish_name,
const char *name)
{
- ensure_cache_init();
- return config_from(&the_submodule_cache, treeish_name, name, lookup_name);
+ submodule_cache_check_init(the_repository);
+ return config_from(the_repository->submodule_cache, treeish_name, name, lookup_name);
}
const struct submodule *submodule_from_path(const unsigned char *treeish_name,
const char *path)
{
- ensure_cache_init();
- return config_from(&the_submodule_cache, treeish_name, path, lookup_path);
+ submodule_cache_check_init(the_repository);
+ return config_from(the_repository->submodule_cache, treeish_name, path, lookup_path);
+}
+
+const struct submodule *submodule_from_cache(struct repository *repo,
+ const unsigned char *treeish_name,
+ const char *key)
+{
+ submodule_cache_check_init(repo);
+ return config_from(repo->submodule_cache, treeish_name,
+ key, lookup_path);
}
void submodule_free(void)
{
- cache_free(&the_submodule_cache);
- is_cache_init = 0;
+ if (the_repository->submodule_cache)
+ submodule_cache_clear(the_repository->submodule_cache);
}
diff --git a/submodule-config.h b/submodule-config.h
index d434ecdb45..bc45a25e85 100644
--- a/submodule-config.h
+++ b/submodule-config.h
@@ -22,14 +22,24 @@ struct submodule {
int recommend_shallow;
};
+struct submodule_cache;
+struct repository;
+
+extern void submodule_cache_free(struct submodule_cache *cache);
+
extern int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
extern int parse_update_recurse_submodules_arg(const char *opt, const char *arg);
extern int parse_push_recurse_submodules_arg(const char *opt, const char *arg);
extern int parse_submodule_config_option(const char *var, const char *value);
+extern int submodule_config_option(struct repository *repo,
+ const char *var, const char *value);
extern const struct submodule *submodule_from_name(
const unsigned char *commit_or_tree, const char *name);
extern const struct submodule *submodule_from_path(
const unsigned char *commit_or_tree, const char *path);
+extern const struct submodule *submodule_from_cache(struct repository *repo,
+ const unsigned char *treeish_name,
+ const char *key);
extern int gitmodule_sha1_from_commit(const unsigned char *commit_sha1,
unsigned char *gitmodules_sha1,
struct strbuf *rev);
diff --git a/submodule.c b/submodule.c
index 71e0c5a85b..da2b484879 100644
--- a/submodule.c
+++ b/submodule.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "repository.h"
#include "config.h"
#include "submodule-config.h"
#include "submodule.h"
@@ -255,6 +256,20 @@ void gitmodules_config(void)
}
}
+static int gitmodules_cb(const char *var, const char *value, void *data)
+{
+ struct repository *repo = data;
+ return submodule_config_option(repo, var, value);
+}
+
+void repo_read_gitmodules(struct repository *repo)
+{
+ char *gitmodules_path = repo_worktree_path(repo, ".gitmodules");
+
+ git_config_from_file(gitmodules_cb, gitmodules_path, repo);
+ free(gitmodules_path);
+}
+
void gitmodules_config_sha1(const unsigned char *commit_sha1)
{
struct strbuf rev = STRBUF_INIT;
@@ -268,21 +283,17 @@ void gitmodules_config_sha1(const unsigned char *commit_sha1)
}
/*
- * NEEDSWORK: With the addition of different configuration options to determine
- * if a submodule is of interests, the validity of this function's name comes
- * into question. Once the dust has settled and more concrete terminology is
- * decided upon, come up with a more proper name for this function. One
- * potential candidate could be 'is_submodule_active()'.
- *
* Determine if a submodule has been initialized at a given 'path'
*/
-int is_submodule_initialized(const char *path)
+int is_submodule_active(struct repository *repo, const char *path)
{
int ret = 0;
char *key = NULL;
char *value = NULL;
const struct string_list *sl;
- const struct submodule *module = submodule_from_path(null_sha1, path);
+ const struct submodule *module;
+
+ module = submodule_from_cache(repo, null_sha1, path);
/* early return if there isn't a path->module mapping */
if (!module)
@@ -290,14 +301,14 @@ int is_submodule_initialized(const char *path)
/* submodule.<name>.active is set */
key = xstrfmt("submodule.%s.active", module->name);
- if (!git_config_get_bool(key, &ret)) {
+ if (!repo_config_get_bool(repo, key, &ret)) {
free(key);
return ret;
}
free(key);
/* submodule.active is set */
- sl = git_config_get_value_multi("submodule.active");
+ sl = repo_config_get_value_multi(repo, "submodule.active");
if (sl) {
struct pathspec ps;
struct argv_array args = ARGV_ARRAY_INIT;
@@ -317,7 +328,7 @@ int is_submodule_initialized(const char *path)
/* fallback to checking if the URL is set */
key = xstrfmt("submodule.%s.url", module->name);
- ret = !git_config_get_string(key, &value);
+ ret = !repo_config_get_string(repo, key, &value);
free(value);
free(key);
@@ -1517,7 +1528,7 @@ int submodule_move_head(const char *path,
const struct submodule *sub;
int *error_code_ptr, error_code;
- if (!is_submodule_initialized(path))
+ if (!is_submodule_active(the_repository, path))
return 0;
if (flags & SUBMODULE_MOVE_HEAD_FORCE)
diff --git a/submodule.h b/submodule.h
index cbe5c1726f..623ce6ad77 100644
--- a/submodule.h
+++ b/submodule.h
@@ -1,6 +1,7 @@
#ifndef SUBMODULE_H
#define SUBMODULE_H
+struct repository;
struct diff_options;
struct argv_array;
struct oid_array;
@@ -46,8 +47,9 @@ int option_parse_recurse_submodules_worktree_updater(const struct option *opt,
const char *arg, int unset);
void load_submodule_cache(void);
extern void gitmodules_config(void);
+extern void repo_read_gitmodules(struct repository *repo);
extern void gitmodules_config_sha1(const unsigned char *commit_sha1);
-extern int is_submodule_initialized(const char *path);
+extern int is_submodule_active(struct repository *repo, const char *path);
/*
* Determine if a submodule has been populated at a given 'path' by checking if
* the <path>/.git resolves to a valid git repository.
diff --git a/t/t3007-ls-files-recurse-submodules.sh b/t/t3007-ls-files-recurse-submodules.sh
index ebb956fd16..318b5bce7e 100755
--- a/t/t3007-ls-files-recurse-submodules.sh
+++ b/t/t3007-ls-files-recurse-submodules.sh
@@ -135,6 +135,45 @@ test_expect_success '--recurse-submodules and pathspecs setup' '
test_cmp expect actual
'
+test_expect_success 'inactive submodule' '
+ test_when_finished "git config --bool submodule.submodule.active true" &&
+ test_when_finished "git -C submodule config --bool submodule.subsub.active true" &&
+ git config --bool submodule.submodule.active "false" &&
+
+ cat >expect <<-\EOF &&
+ .gitmodules
+ a
+ b/b
+ h.txt
+ sib/file
+ sub/file
+ submodule
+ EOF
+
+ git ls-files --recurse-submodules >actual &&
+ test_cmp expect actual &&
+
+ git config --bool submodule.submodule.active "true" &&
+ git -C submodule config --bool submodule.subsub.active "false" &&
+
+ cat >expect <<-\EOF &&
+ .gitmodules
+ a
+ b/b
+ h.txt
+ sib/file
+ sub/file
+ submodule/.gitmodules
+ submodule/c
+ submodule/f.TXT
+ submodule/g.txt
+ submodule/subsub
+ EOF
+
+ git ls-files --recurse-submodules >actual &&
+ test_cmp expect actual
+'
+
test_expect_success '--recurse-submodules and pathspecs' '
cat >expect <<-\EOF &&
h.txt
diff --git a/worktree.c b/worktree.c
index 2801c6d52b..e28ffbeb09 100644
--- a/worktree.c
+++ b/worktree.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "repository.h"
#include "refs.h"
#include "strbuf.h"
#include "worktree.h"
@@ -76,7 +77,7 @@ static struct worktree *get_linked_worktree(const char *id)
if (!id)
die("Missing linked worktree name");
- strbuf_git_common_path(&path, "worktrees/%s/gitdir", id);
+ strbuf_git_common_path(&path, the_repository, "worktrees/%s/gitdir", id);
if (strbuf_read_file(&worktree_path, path.buf, 0) <= 0)
/* invalid gitdir file */
goto done;