summaryrefslogtreecommitdiff
path: root/src/submodule.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/submodule.c')
-rw-r--r--src/submodule.c207
1 files changed, 197 insertions, 10 deletions
diff --git a/src/submodule.c b/src/submodule.c
index d2af1440a..d89dd049f 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -475,7 +475,7 @@ int git_submodule_repo_init(
/* get the configured remote url of the submodule */
if ((error = git_buf_printf(&buf, "submodule.%s.url", sm->name)) < 0 ||
- (error = git_repository_config(&cfg, sm->repo)) < 0 ||
+ (error = git_repository_config_snapshot(&cfg, sm->repo)) < 0 ||
(error = git_config_get_string(&configured_url, cfg, buf.ptr)) < 0 ||
(error = submodule_repo_init(&sub_repo, sm->repo, sm->path, configured_url, use_gitlink)) < 0)
goto done;
@@ -790,7 +790,7 @@ git_submodule_ignore_t git_submodule_set_ignore(
return old;
}
-git_submodule_update_t git_submodule_update(git_submodule *submodule)
+git_submodule_update_t git_submodule_update_strategy(git_submodule *submodule)
{
assert(submodule);
return (submodule->update < GIT_SUBMODULE_UPDATE_CHECKOUT) ?
@@ -835,6 +835,178 @@ git_submodule_recurse_t git_submodule_set_fetch_recurse_submodules(
return old;
}
+static int submodule_repo_create(
+ git_repository **out,
+ git_repository *parent_repo,
+ const char *path)
+{
+ int error = 0;
+ git_buf workdir = GIT_BUF_INIT, repodir = GIT_BUF_INIT;
+ git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+ git_repository *subrepo = NULL;
+
+ initopt.flags =
+ GIT_REPOSITORY_INIT_MKPATH |
+ GIT_REPOSITORY_INIT_NO_REINIT |
+ GIT_REPOSITORY_INIT_NO_DOTGIT_DIR |
+ GIT_REPOSITORY_INIT_RELATIVE_GITLINK;
+
+ /* Workdir: path to sub-repo working directory */
+ error = git_buf_joinpath(&workdir, git_repository_workdir(parent_repo), path);
+ if (error < 0)
+ goto cleanup;
+
+ initopt.workdir_path = workdir.ptr;
+
+ /**
+ * Repodir: path to the sub-repo. sub-repo goes in:
+ * <repo-dir>/modules/<name>/ with a gitlink in the
+ * sub-repo workdir directory to that repository.
+ */
+ error = git_buf_join3(
+ &repodir, '/', git_repository_path(parent_repo), "modules", path);
+ if (error < 0)
+ goto cleanup;
+
+ error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt);
+
+cleanup:
+ git_buf_free(&workdir);
+ git_buf_free(&repodir);
+
+ *out = subrepo;
+
+ return error;
+}
+
+/**
+ * Callback to override sub-repository creation when
+ * cloning a sub-repository.
+ */
+static int git_submodule_update_repo_init_cb(
+ git_repository **out,
+ const char *path,
+ int bare,
+ void *payload)
+{
+ GIT_UNUSED(bare);
+ git_submodule *sm = payload;
+
+ return submodule_repo_create(out, sm->repo, path);
+}
+
+int git_submodule_update(git_submodule *sm, int init, git_submodule_update_options *_update_options)
+{
+ int error;
+ unsigned int submodule_status;
+ git_config *config = NULL;
+ const char *submodule_url;
+ git_repository *sub_repo = NULL;
+ git_remote *remote = NULL;
+ git_object *target_commit = NULL;
+ git_buf buf = GIT_BUF_INIT;
+ git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
+ git_clone_options clone_options = GIT_CLONE_OPTIONS_INIT;
+
+ assert(sm);
+
+ if (_update_options)
+ memcpy(&update_options, _update_options, sizeof(git_submodule_update_options));
+
+ GITERR_CHECK_VERSION(&update_options, GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, "git_submodule_update_options");
+
+ /* Copy over the remote callbacks */
+ clone_options.remote_callbacks = update_options.remote_callbacks;
+ clone_options.signature = update_options.signature;
+
+ /* Get the status of the submodule to determine if it is already initialized */
+ if ((error = git_submodule_status(&submodule_status, sm)) < 0)
+ goto done;
+
+ /*
+ * If submodule work dir is not already initialized, check to see
+ * what we need to do (initialize, clone, return error...)
+ */
+ if (submodule_status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) {
+ /*
+ * Work dir is not initialized, check to see if the submodule
+ * info has been copied into .git/config
+ */
+ if ((error = git_repository_config_snapshot(&config, sm->repo)) < 0 ||
+ (error = git_buf_printf(&buf, "submodule.%s.url", git_submodule_name(sm))) < 0)
+ goto done;
+
+ if ((error = git_config_get_string(&submodule_url, config, git_buf_cstr(&buf))) < 0) {
+ /*
+ * If the error is not "not found" or if it is "not found" and we are not
+ * initializing the submodule, then return error.
+ */
+ if (error != GIT_ENOTFOUND)
+ goto done;
+
+ if (error == GIT_ENOTFOUND && !init) {
+ giterr_set(GITERR_SUBMODULE, "Submodule is not initialized.");
+ error = GIT_ERROR;
+ goto done;
+ }
+
+ /* The submodule has not been initialized yet - initialize it now.*/
+ if ((error = git_submodule_init(sm, 0)) < 0)
+ goto done;
+
+ git_config_free(config);
+ config = NULL;
+
+ if ((error = git_repository_config_snapshot(&config, sm->repo)) < 0 ||
+ (error = git_config_get_string(&submodule_url, config, git_buf_cstr(&buf))) < 0)
+ goto done;
+ }
+
+ /** submodule is initialized - now clone it **/
+ /* override repo creation */
+ clone_options.repository_cb = git_submodule_update_repo_init_cb;
+ clone_options.repository_cb_payload = sm;
+
+ /*
+ * Do not perform checkout as part of clone, instead we
+ * will checkout the specific commit manually.
+ */
+ clone_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_NONE;
+ update_options.checkout_opts.checkout_strategy = update_options.clone_checkout_strategy;
+
+ if ((error = git_clone(&sub_repo, submodule_url, sm->path, &clone_options)) < 0 ||
+ (error = git_repository_set_head_detached(sub_repo, git_submodule_index_id(sm), update_options.signature, NULL)) < 0 ||
+ (error = git_checkout_head(sub_repo, &update_options.checkout_opts)) != 0)
+ goto done;
+ } else {
+ /**
+ * Work dir is initialized - look up the commit in the parent repository's index,
+ * update the workdir contents of the subrepository, and set the subrepository's
+ * head to the new commit.
+ */
+ if ((error = git_submodule_open(&sub_repo, sm)) < 0 ||
+ (error = git_object_lookup(&target_commit, sub_repo, git_submodule_index_id(sm), GIT_OBJ_COMMIT)) < 0 ||
+ (error = git_checkout_tree(sub_repo, target_commit, &update_options.checkout_opts)) != 0 ||
+ (error = git_repository_set_head_detached(sub_repo, git_submodule_index_id(sm), update_options.signature, NULL)) < 0)
+ goto done;
+
+ /* Invalidate the wd flags as the workdir has been updated. */
+ sm->flags = sm->flags &
+ ~(GIT_SUBMODULE_STATUS_IN_WD |
+ GIT_SUBMODULE_STATUS__WD_OID_VALID |
+ GIT_SUBMODULE_STATUS__WD_SCANNED);
+ }
+
+done:
+ git_buf_free(&buf);
+ git_config_free(config);
+ git_object_free(target_commit);
+ git_remote_free(remote);
+ git_repository_free(sub_repo);
+
+ return error;
+}
+
int git_submodule_init(git_submodule *sm, int overwrite)
{
int error;
@@ -853,7 +1025,7 @@ int git_submodule_init(git_submodule *sm, int overwrite)
/* write "submodule.NAME.url" */
- if ((git_submodule_resolve_url(&effective_submodule_url, sm->repo, sm->url)) < 0 ||
+ if ((error = git_submodule_resolve_url(&effective_submodule_url, sm->repo, sm->url)) < 0 ||
(error = git_buf_printf(&key, "submodule.%s.url", sm->name)) < 0 ||
(error = git_config__update_entry(
cfg, key.ptr, effective_submodule_url.ptr, overwrite != 0, false)) < 0)
@@ -1867,16 +2039,31 @@ static int lookup_head_remote_key(git_buf *remote_name, git_repository *repo)
if ((error = git_repository_head(&head, repo)) < 0)
return error;
+ /**
+ * If head does not refer to a branch, then return
+ * GIT_ENOTFOUND to indicate that we could not find
+ * a remote key for the local tracking branch HEAD points to.
+ **/
+ if (!git_reference_is_branch(head)) {
+ giterr_set(GITERR_INVALID,
+ "HEAD does not refer to a branch.");
+ error = GIT_ENOTFOUND;
+ goto done;
+ }
+
/* lookup remote tracking branch of HEAD */
- if (!(error = git_branch_upstream_name(
- &upstream_name, repo, git_reference_name(head))))
- {
- /* lookup remote of remote tracking branch */
- error = git_branch_remote_name(remote_name, repo, upstream_name.ptr);
+ if ((error = git_branch_upstream_name(
+ &upstream_name,
+ repo,
+ git_reference_name(head))) < 0)
+ goto done;
- git_buf_free(&upstream_name);
- }
+ /* lookup remote of remote tracking branch */
+ if ((error = git_branch_remote_name(remote_name, repo, upstream_name.ptr)) < 0)
+ goto done;
+done:
+ git_buf_free(&upstream_name);
git_reference_free(head);
return error;