summaryrefslogtreecommitdiff
path: root/src/repository.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/repository.c')
-rw-r--r--src/repository.c222
1 files changed, 146 insertions, 76 deletions
diff --git a/src/repository.c b/src/repository.c
index 994b13bd..ebd60360 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -24,6 +24,8 @@
#define GIT_REPO_VERSION 0
+#define GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
+
static void drop_odb(git_repository *repo)
{
if (repo->_odb != NULL) {
@@ -681,6 +683,7 @@ static bool is_chmod_supported(const char *file_path)
return false;
_is_supported = (st1.st_mode != st2.st_mode);
+
return _is_supported;
}
@@ -702,20 +705,45 @@ cleanup:
return _is_insensitive;
}
+static bool are_symlinks_supported(const char *wd_path)
+{
+ git_buf path = GIT_BUF_INIT;
+ int fd;
+ struct stat st;
+ static int _symlinks_supported = -1;
+
+ if (_symlinks_supported > -1)
+ return _symlinks_supported;
+
+ if ((fd = git_futils_mktmp(&path, wd_path)) < 0 ||
+ p_close(fd) < 0 ||
+ p_unlink(path.ptr) < 0 ||
+ p_symlink("testing", path.ptr) < 0 ||
+ p_lstat(path.ptr, &st) < 0)
+ _symlinks_supported = false;
+ else
+ _symlinks_supported = (S_ISLNK(st.st_mode) != 0);
+
+ (void)p_unlink(path.ptr);
+ git_buf_free(&path);
+
+ return _symlinks_supported;
+}
+
static int repo_init_config(
- const char *git_dir, git_repository_init_options *opts)
+ const char *repo_dir,
+ const char *work_dir,
+ git_repository_init_options *opts)
{
+ int error = 0;
git_buf cfg_path = GIT_BUF_INIT;
git_config *config = NULL;
-#define SET_REPO_CONFIG(type, name, val) {\
- if (git_config_set_##type(config, name, val) < 0) { \
- git_buf_free(&cfg_path); \
- git_config_free(config); \
- return -1; } \
-}
+#define SET_REPO_CONFIG(TYPE, NAME, VAL) do {\
+ if ((error = git_config_set_##TYPE(config, NAME, VAL)) < 0) \
+ goto cleanup; } while (0)
- if (git_buf_joinpath(&cfg_path, git_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
+ if (git_buf_joinpath(&cfg_path, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
return -1;
if (git_config_open_ondisk(&config, git_buf_cstr(&cfg_path)) < 0) {
@@ -724,12 +752,8 @@ static int repo_init_config(
}
if ((opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0 &&
- check_repositoryformatversion(config) < 0)
- {
- git_buf_free(&cfg_path);
- git_config_free(config);
- return -1;
- }
+ (error = check_repositoryformatversion(config)) < 0)
+ goto cleanup;
SET_REPO_CONFIG(
bool, "core.bare", (opts->flags & GIT_REPOSITORY_INIT_BARE) != 0);
@@ -738,25 +762,42 @@ static int repo_init_config(
SET_REPO_CONFIG(
bool, "core.filemode", is_chmod_supported(git_buf_cstr(&cfg_path)));
- if (!(opts->flags & GIT_REPOSITORY_INIT_BARE))
+ if (!(opts->flags & GIT_REPOSITORY_INIT_BARE)) {
SET_REPO_CONFIG(bool, "core.logallrefupdates", true);
+ if (!are_symlinks_supported(work_dir))
+ SET_REPO_CONFIG(bool, "core.symlinks", false);
+
+ if (!(opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD)) {
+ SET_REPO_CONFIG(string, "core.worktree", work_dir);
+ }
+ else if ((opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0) {
+ if ((error = git_config_delete(config, "core.worktree")) < 0)
+ goto cleanup;
+ }
+ } else {
+ if (!are_symlinks_supported(repo_dir))
+ SET_REPO_CONFIG(bool, "core.symlinks", false);
+ }
+
if (!(opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) &&
- is_filesystem_case_insensitive(git_dir))
+ is_filesystem_case_insensitive(repo_dir))
SET_REPO_CONFIG(bool, "core.ignorecase", true);
- if (opts->flags & GIT_REPOSITORY_INIT_SHARED_GROUP) {
+ if (opts->mode == GIT_REPOSITORY_INIT_SHARED_GROUP) {
SET_REPO_CONFIG(int32, "core.sharedrepository", 1);
SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
- } else if (opts->flags & GIT_REPOSITORY_INIT_SHARED_ALL) {
+ }
+ else if (opts->mode == GIT_REPOSITORY_INIT_SHARED_ALL) {
SET_REPO_CONFIG(int32, "core.sharedrepository", 2);
SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
}
+cleanup:
git_buf_free(&cfg_path);
git_config_free(config);
- return 0;
+ return error;
}
static int repo_write_template(
@@ -848,6 +889,17 @@ cleanup:
return error;
}
+static mode_t pick_dir_mode(git_repository_init_options *opts)
+{
+ if (opts->mode == GIT_REPOSITORY_INIT_SHARED_UMASK)
+ return 0755;
+ if (opts->mode == GIT_REPOSITORY_INIT_SHARED_GROUP)
+ return (0775 | S_ISGID);
+ if (opts->mode == GIT_REPOSITORY_INIT_SHARED_ALL)
+ return (0777 | S_ISGID);
+ return opts->mode;
+}
+
#include "repo_template.h"
static int repo_init_structure(
@@ -855,8 +907,11 @@ static int repo_init_structure(
const char *work_dir,
git_repository_init_options *opts)
{
+ int error = 0;
repo_template_item *tpl;
- mode_t gid = 0;
+ bool external_tpl =
+ ((opts->flags & GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE) != 0);
+ mode_t dmode = pick_dir_mode(opts);
/* Hide the ".git" directory */
if ((opts->flags & GIT_REPOSITORY_INIT_BARE) != 0) {
@@ -874,32 +929,60 @@ static int repo_init_structure(
return -1;
}
- if ((opts->flags & GIT_REPOSITORY_INIT_SHARED_GROUP) != 0 ||
- (opts->flags & GIT_REPOSITORY_INIT_SHARED_ALL) != 0)
- gid = S_ISGID;
+ /* Copy external template if requested */
+ if (external_tpl) {
+ git_config *cfg;
+ const char *tdir;
- /* TODO: honor GIT_REPOSITORY_INIT_USE_EXTERNAL_TEMPLATE if set */
+ if (opts->template_path)
+ tdir = opts->template_path;
+ else if ((error = git_config_open_outside_repo(&cfg)) < 0)
+ return error;
+ else {
+ error = git_config_get_string(&tdir, cfg, "init.templatedir");
- /* Copy internal template as needed */
+ git_config_free(cfg);
- for (tpl = repo_template; tpl->path; ++tpl) {
- if (!tpl->content) {
- if (git_futils_mkdir_r(tpl->path, repo_dir, tpl->mode | gid) < 0)
- return -1;
+ if (error && error != GIT_ENOTFOUND)
+ return error;
+
+ giterr_clear();
+ tdir = GIT_TEMPLATE_DIR;
}
- else {
+
+ error = git_futils_cp_r(tdir, repo_dir,
+ GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_CHMOD, dmode);
+
+ if (error < 0) {
+ if (strcmp(tdir, GIT_TEMPLATE_DIR) != 0)
+ return error;
+
+ /* if template was default, ignore error and use internal */
+ giterr_clear();
+ external_tpl = false;
+ }
+ }
+
+ /* Copy internal template
+ * - always ensure existence of dirs
+ * - only create files if no external template was specified
+ */
+ for (tpl = repo_template; !error && tpl->path; ++tpl) {
+ if (!tpl->content)
+ error = git_futils_mkdir(
+ tpl->path, repo_dir, dmode, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD);
+ else if (!external_tpl) {
const char *content = tpl->content;
if (opts->description && strcmp(tpl->path, GIT_DESC_FILE) == 0)
content = opts->description;
- if (repo_write_template(
- repo_dir, false, tpl->path, tpl->mode, false, content) < 0)
- return -1;
+ error = repo_write_template(
+ repo_dir, false, tpl->path, tpl->mode, false, content);
}
}
- return 0;
+ return error;
}
static int repo_init_directories(
@@ -910,6 +993,7 @@ static int repo_init_directories(
{
int error = 0;
bool add_dotgit, has_dotgit, natural_wd;
+ mode_t dirmode;
/* set up repo path */
@@ -930,15 +1014,9 @@ static int repo_init_directories(
if ((opts->flags & GIT_REPOSITORY_INIT_BARE) == 0) {
if (opts->workdir_path) {
- if (git_path_root(opts->workdir_path) < 0) {
- if (git_path_dirname_r(wd_path, repo_path->ptr) < 0 ||
- git_buf_putc(wd_path, '/') < 0 ||
- git_buf_puts(wd_path, opts->workdir_path) < 0)
- return -1;
- } else {
- if (git_buf_sets(wd_path, opts->workdir_path) < 0)
- return -1;
- }
+ if (git_path_join_unrooted(
+ wd_path, opts->workdir_path, repo_path->ptr, NULL) < 0)
+ return -1;
} else if (has_dotgit) {
if (git_path_dirname_r(wd_path, repo_path->ptr) < 0)
return -1;
@@ -962,43 +1040,33 @@ static int repo_init_directories(
if (natural_wd)
opts->flags |= GIT_REPOSITORY_INIT__NATURAL_WD;
- /* pick mode */
-
- if ((opts->flags & GIT_REPOSITORY_INIT_SHARED_CUSTOM) != 0)
- /* leave mode as is */;
- else if ((opts->flags & GIT_REPOSITORY_INIT_SHARED_GROUP) != 0)
- opts->mode = 0775 | S_ISGID;
- else if ((opts->flags & GIT_REPOSITORY_INIT_SHARED_ALL) != 0)
- opts->mode = 0777 | S_ISGID;
- else if ((opts->flags & GIT_REPOSITORY_INIT_BARE) != 0)
- opts->mode = 0755;
- else
- opts->mode = 0755;
-
/* create directories as needed / requested */
- if ((opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0) {
- error = git_futils_mkdir_r(repo_path->ptr, NULL, opts->mode);
+ dirmode = pick_dir_mode(opts);
- if (!error && !natural_wd && wd_path->size > 0)
- error = git_futils_mkdir_r(wd_path->ptr, NULL, opts->mode);
+ if ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 && has_dotgit) {
+ git_buf p = GIT_BUF_INIT;
+ if ((error = git_path_dirname_r(&p, repo_path->ptr)) >= 0)
+ error = git_futils_mkdir(p.ptr, NULL, dirmode, 0);
+ git_buf_free(&p);
}
- else if ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0) {
- if (has_dotgit) {
- git_buf p = GIT_BUF_INIT;
- if ((error = git_path_dirname_r(&p, repo_path->ptr)) >= 0)
- error = git_futils_mkdir_q(p.ptr, opts->mode);
- git_buf_free(&p);
- }
- if (!error)
- error = git_futils_mkdir_q(repo_path->ptr, opts->mode);
-
- if (!error && !natural_wd && wd_path->size > 0)
- error = git_futils_mkdir_q(wd_path->ptr, opts->mode);
+ if ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 ||
+ (opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0 ||
+ has_dotgit)
+ {
+ uint32_t mkflag = GIT_MKDIR_CHMOD;
+ if ((opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0)
+ mkflag |= GIT_MKDIR_PATH;
+ error = git_futils_mkdir(repo_path->ptr, NULL, dirmode, mkflag);
}
- else if (has_dotgit)
- error = git_futils_mkdir_q(repo_path->ptr, opts->mode);
+
+ if (wd_path->size > 0 &&
+ !natural_wd &&
+ ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 ||
+ (opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0))
+ error = git_futils_mkdir(wd_path->ptr, NULL, dirmode & ~S_ISGID,
+ (opts->flags & GIT_REPOSITORY_INIT_MKPATH) ? GIT_MKDIR_PATH : 0);
/* prettify both directories now that they are created */
@@ -1063,14 +1131,16 @@ int git_repository_init_ext(
opts->flags |= GIT_REPOSITORY_INIT__IS_REINIT;
- error = repo_init_config(git_buf_cstr(&repo_path), opts);
+ error = repo_init_config(
+ git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts);
/* TODO: reinitialize the templates */
}
else {
if (!(error = repo_init_structure(
git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts)) &&
- !(error = repo_init_config(git_buf_cstr(&repo_path), opts)))
+ !(error = repo_init_config(
+ git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts)))
error = repo_init_create_head(
git_buf_cstr(&repo_path), opts->initial_head);
}