Introduce a "repository" object to eventually make it easier to
work in multiple repositories (the primary focus is to work with
the superproject and its submodules) in a single process.

* bw/repo-object:
  ls-files: use repository object
  repository: enable initialization of submodules
  submodule: convert is_submodule_initialized to work on a repository
  submodule: add repo_read_gitmodules
  submodule-config: store the_submodule_cache in the_repository
  repository: add index_state to struct repo
  config: read config from a repository object
  path: add repo_worktree_path and strbuf_repo_worktree_path
  path: add repo_git_path and strbuf_repo_git_path
  path: worktree_git_path() should not use file relocation
  path: convert do_git_path to take a 'struct repository'
  path: convert strbuf_git_common_path to take a 'struct repository'
  path: always pass in commondir to update_common_dir
  path: create path.h
  environment: store worktree in the_repository
  environment: place key repository state in the_repository
  repository: introduce the repository object
  environment: remove namespace_len variable
  setup: add comment indicating a hack
  setup: don't perform lazy initialization of repository state
This commit is contained in:
Junio C Hamano 2017-07-05 13:32:55 -07:00
Родитель 5116f791c1 188dce131f
Коммит 85ce4a6828
20 изменённых файлов: 974 добавлений и 375 удалений

Просмотреть файл

@ -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

Просмотреть файл

@ -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)) {
/*

Просмотреть файл

@ -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;
@ -73,25 +73,12 @@ 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");
static void show_files(struct repository *repo, struct dir_struct *dir);
/* Add Pathspecs */
argv_array_push(&submodule_options, "--");
for (; *argv; argv++)
argv_array_push(&submodule_options, *argv);
static void show_submodule(struct repository *superproject,
struct dir_struct *dir, const char *path)
{
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);
}
/**
* Recursively call ls-files on a submodule
*/
static void show_gitlink(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 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);
}
static void show_ce_entry(const struct index_state *istate,
const char *tag, const struct cache_entry *ce)
{
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 show_files(struct index_state *istate, struct dir_struct *dir)
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 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;

Просмотреть файл

@ -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)

62
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
@ -891,64 +893,6 @@ extern void check_repository_format(void);
#define DATA_CHANGED 0x0020
#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

218
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);
}
static void repo_config_clear(struct repository *repo)
{
if (!repo->config || !repo->config->hash_initialized)
return;
git_configset_clear(repo->config);
}
void repo_config(struct repository *repo, config_fn_t fn, void *data)
{
git_config_check_init(repo);
configset_iter(repo->config, fn, data);
}
int repo_config_get_value(struct repository *repo,
const char *key, const char **value)
{
git_config_check_init(repo);
return git_configset_get_value(repo->config, key, value);
}
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(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)
{
if (!the_config_set.hash_initialized)
return;
git_configset_clear(&the_config_set);
repo_config_clear(the_repository);
}
int git_config_get_value(const char *key, const char **value)
{
git_config_check_init();
return git_configset_get_value(&the_config_set, key, value);
return repo_config_get_value(the_repository, key, value);
}
const struct string_list *git_config_get_value_multi(const char *key)
{
git_config_check_init();
return git_configset_get_value_multi(&the_config_set, key);
return repo_config_get_value_multi(the_repository, key);
}
int git_config_get_string_const(const char *key, const char **dest)
{
int ret;
git_config_check_init();
ret = git_configset_get_string_const(&the_config_set, key, dest);
if (ret < 0)
git_die_config(key, NULL);
return ret;
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)

Просмотреть файл

@ -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);

Просмотреть файл

@ -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()))
const char *out;
if (skip_prefix(namespaced_ref, get_git_namespace(), &out))
return out;
return NULL;
return namespaced_ref + namespace_len;
}
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;
}

2
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 },

130
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);
}

82
path.h Normal file
Просмотреть файл

@ -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 */

242
repository.c Normal file
Просмотреть файл

@ -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);
}

97
repository.h Normal file
Просмотреть файл

@ -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 */

33
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);

Просмотреть файл

@ -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);
}

Просмотреть файл

@ -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);

Просмотреть файл

@ -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)

Просмотреть файл

@ -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.

Просмотреть файл

@ -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

Просмотреть файл

@ -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;