Blame src/submodule.c

Packit Service 20376f
/*
Packit Service 20376f
 * Copyright (C) the libgit2 contributors. All rights reserved.
Packit Service 20376f
 *
Packit Service 20376f
 * This file is part of libgit2, distributed under the GNU GPL v2 with
Packit Service 20376f
 * a Linking Exception. For full terms see the included COPYING file.
Packit Service 20376f
 */
Packit Service 20376f
Packit Service 20376f
#include "common.h"
Packit Service 20376f
#include "git2/config.h"
Packit Service 20376f
#include "git2/sys/config.h"
Packit Service 20376f
#include "git2/types.h"
Packit Service 20376f
#include "git2/index.h"
Packit Service 20376f
#include "buffer.h"
Packit Service 20376f
#include "buf_text.h"
Packit Service 20376f
#include "vector.h"
Packit Service 20376f
#include "posix.h"
Packit Service 20376f
#include "config_file.h"
Packit Service 20376f
#include "config.h"
Packit Service 20376f
#include "repository.h"
Packit Service 20376f
#include "submodule.h"
Packit Service 20376f
#include "tree.h"
Packit Service 20376f
#include "iterator.h"
Packit Service 20376f
#include "path.h"
Packit Service 20376f
#include "index.h"
Packit Service 20376f
#include "worktree.h"
Packit Service 20376f
Packit Service 20376f
#define GIT_MODULES_FILE ".gitmodules"
Packit Service 20376f
Packit Service 20376f
static git_cvar_map _sm_update_map[] = {
Packit Service 20376f
	{GIT_CVAR_STRING, "checkout", GIT_SUBMODULE_UPDATE_CHECKOUT},
Packit Service 20376f
	{GIT_CVAR_STRING, "rebase", GIT_SUBMODULE_UPDATE_REBASE},
Packit Service 20376f
	{GIT_CVAR_STRING, "merge", GIT_SUBMODULE_UPDATE_MERGE},
Packit Service 20376f
	{GIT_CVAR_STRING, "none", GIT_SUBMODULE_UPDATE_NONE},
Packit Service 20376f
	{GIT_CVAR_FALSE, NULL, GIT_SUBMODULE_UPDATE_NONE},
Packit Service 20376f
	{GIT_CVAR_TRUE, NULL, GIT_SUBMODULE_UPDATE_CHECKOUT},
Packit Service 20376f
};
Packit Service 20376f
Packit Service 20376f
static git_cvar_map _sm_ignore_map[] = {
Packit Service 20376f
	{GIT_CVAR_STRING, "none", GIT_SUBMODULE_IGNORE_NONE},
Packit Service 20376f
	{GIT_CVAR_STRING, "untracked", GIT_SUBMODULE_IGNORE_UNTRACKED},
Packit Service 20376f
	{GIT_CVAR_STRING, "dirty", GIT_SUBMODULE_IGNORE_DIRTY},
Packit Service 20376f
	{GIT_CVAR_STRING, "all", GIT_SUBMODULE_IGNORE_ALL},
Packit Service 20376f
	{GIT_CVAR_FALSE, NULL, GIT_SUBMODULE_IGNORE_NONE},
Packit Service 20376f
	{GIT_CVAR_TRUE, NULL, GIT_SUBMODULE_IGNORE_ALL},
Packit Service 20376f
};
Packit Service 20376f
Packit Service 20376f
static git_cvar_map _sm_recurse_map[] = {
Packit Service 20376f
	{GIT_CVAR_STRING, "on-demand", GIT_SUBMODULE_RECURSE_ONDEMAND},
Packit Service 20376f
	{GIT_CVAR_FALSE, NULL, GIT_SUBMODULE_RECURSE_NO},
Packit Service 20376f
	{GIT_CVAR_TRUE, NULL, GIT_SUBMODULE_RECURSE_YES},
Packit Service 20376f
};
Packit Service 20376f
Packit Service 20376f
enum {
Packit Service 20376f
	CACHE_OK = 0,
Packit Service 20376f
	CACHE_REFRESH = 1,
Packit Service 20376f
	CACHE_FLUSH = 2
Packit Service 20376f
};
Packit Service 20376f
enum {
Packit Service 20376f
	GITMODULES_EXISTING = 0,
Packit Service 20376f
	GITMODULES_CREATE = 1,
Packit Service 20376f
};
Packit Service 20376f
Packit Service 20376f
static kh_inline khint_t str_hash_no_trailing_slash(const char *s)
Packit Service 20376f
{
Packit Service 20376f
	khint_t h;
Packit Service 20376f
Packit Service 20376f
	for (h = 0; *s; ++s)
Packit Service 20376f
		if (s[1] != '\0' || *s != '/')
Packit Service 20376f
			h = (h << 5) - h + *s;
Packit Service 20376f
Packit Service 20376f
	return h;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static kh_inline int str_equal_no_trailing_slash(const char *a, const char *b)
Packit Service 20376f
{
Packit Service 20376f
	size_t alen = a ? strlen(a) : 0;
Packit Service 20376f
	size_t blen = b ? strlen(b) : 0;
Packit Service 20376f
Packit Service 20376f
	if (alen > 0 && a[alen - 1] == '/')
Packit Service 20376f
		alen--;
Packit Service 20376f
	if (blen > 0 && b[blen - 1] == '/')
Packit Service 20376f
		blen--;
Packit Service 20376f
Packit Service 20376f
	return (alen == 0 && blen == 0) ||
Packit Service 20376f
		(alen == blen && strncmp(a, b, alen) == 0);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
__KHASH_IMPL(
Packit Service 20376f
	str, static kh_inline, const char *, void *, 1,
Packit Service 20376f
	str_hash_no_trailing_slash, str_equal_no_trailing_slash)
Packit Service 20376f
Packit Service 20376f
static int submodule_alloc(git_submodule **out, git_repository *repo, const char *name);
Packit Service 20376f
static git_config_backend *open_gitmodules(git_repository *repo, int gitmod);
Packit Service 20376f
static git_config *gitmodules_snapshot(git_repository *repo);
Packit Service 20376f
static int get_url_base(git_buf *url, git_repository *repo);
Packit Service 20376f
static int lookup_head_remote_key(git_buf *remote_key, git_repository *repo);
Packit Service 20376f
static int lookup_default_remote(git_remote **remote, git_repository *repo);
Packit Service 20376f
static int submodule_load_each(const git_config_entry *entry, void *payload);
Packit Service 20376f
static int submodule_read_config(git_submodule *sm, git_config *cfg);
Packit Service 20376f
static int submodule_load_from_wd_lite(git_submodule *);
Packit Service 20376f
static void submodule_get_index_status(unsigned int *, git_submodule *);
Packit Service 20376f
static void submodule_get_wd_status(unsigned int *, git_submodule *, git_repository *, git_submodule_ignore_t);
Packit Service 20376f
static void submodule_update_from_index_entry(git_submodule *sm, const git_index_entry *ie);
Packit Service 20376f
static void submodule_update_from_head_data(git_submodule *sm, mode_t mode, const git_oid *id);
Packit Service 20376f
Packit Service 20376f
static int submodule_cmp(const void *a, const void *b)
Packit Service 20376f
{
Packit Service 20376f
	return strcmp(((git_submodule *)a)->name, ((git_submodule *)b)->name);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int submodule_config_key_trunc_puts(git_buf *key, const char *suffix)
Packit Service 20376f
{
Packit Service 20376f
	ssize_t idx = git_buf_rfind(key, '.');
Packit Service 20376f
	git_buf_truncate(key, (size_t)(idx + 1));
Packit Service 20376f
	return git_buf_puts(key, suffix);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/*
Packit Service 20376f
 * PUBLIC APIS
Packit Service 20376f
 */
Packit Service 20376f
Packit Service 20376f
static void submodule_set_lookup_error(int error, const char *name)
Packit Service 20376f
{
Packit Service 20376f
	if (!error)
Packit Service 20376f
		return;
Packit Service 20376f
Packit Service 20376f
	giterr_set(GITERR_SUBMODULE, (error == GIT_ENOTFOUND) ?
Packit Service 20376f
		"no submodule named '%s'" :
Packit Service 20376f
		"submodule '%s' has not been added yet", name);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
typedef struct {
Packit Service 20376f
	const char *path;
Packit Service 20376f
	char *name;
Packit Service 20376f
} fbp_data;
Packit Service 20376f
Packit Service 20376f
static int find_by_path(const git_config_entry *entry, void *payload)
Packit Service 20376f
{
Packit Service 20376f
	fbp_data *data = payload;
Packit Service 20376f
Packit Service 20376f
	if (!strcmp(entry->value, data->path)) {
Packit Service 20376f
		const char *fdot, *ldot;
Packit Service 20376f
		fdot = strchr(entry->name, '.');
Packit Service 20376f
		ldot = strrchr(entry->name, '.');
Packit Service 20376f
		data->name = git__strndup(fdot + 1, ldot - fdot - 1);
Packit Service 20376f
		GITERR_CHECK_ALLOC(data->name);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/**
Packit Service 20376f
 * Release the name map returned by 'load_submodule_names'.
Packit Service 20376f
 */
Packit Service 20376f
static void free_submodule_names(git_strmap *names)
Packit Service 20376f
{
Packit Service 20376f
	git_buf *name = 0;
Packit Service 20376f
	if (names == NULL)
Packit Service 20376f
		return;
Packit Service 20376f
	git_strmap_foreach_value(names, name, {
Packit Service 20376f
		git__free(name);
Packit Service 20376f
	});
Packit Service 20376f
	git_strmap_free(names);
Packit Service 20376f
	return;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/**
Packit Service 20376f
 * Map submodule paths to names.
Packit Service 20376f
 * TODO: for some use-cases, this might need case-folding on a
Packit Service 20376f
 * case-insensitive filesystem
Packit Service 20376f
 */
Packit Service 20376f
static int load_submodule_names(git_strmap *out, git_repository *repo, git_config *cfg)
Packit Service 20376f
{
Packit Service 20376f
	const char *key = "submodule\\..*\\.path";
Packit Service 20376f
	git_config_iterator *iter;
Packit Service 20376f
	git_config_entry *entry;
Packit Service 20376f
	git_buf buf = GIT_BUF_INIT;
Packit Service 20376f
	int rval, isvalid;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_config_iterator_glob_new(&iter, cfg, key)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	while (git_config_next(&entry, iter) == 0) {
Packit Service 20376f
		const char *fdot, *ldot;
Packit Service 20376f
		fdot = strchr(entry->name, '.');
Packit Service 20376f
		ldot = strrchr(entry->name, '.');
Packit Service 20376f
Packit Service 20376f
		git_buf_clear(&buf;;
Packit Service 20376f
		git_buf_put(&buf, fdot + 1, ldot - fdot - 1);
Packit Service 20376f
		isvalid = git_submodule_name_is_valid(repo, buf.ptr, 0);
Packit Service 20376f
		if (isvalid < 0) {
Packit Service 20376f
			error = isvalid;
Packit Service 20376f
			goto out;
Packit Service 20376f
		}
Packit Service 20376f
		if (!isvalid)
Packit Service 20376f
			continue;
Packit Service 20376f
Packit Service 20376f
		git_strmap_insert(out, entry->value, git_buf_detach(&buf), &rval);
Packit Service 20376f
		if (rval < 0) {
Packit Service 20376f
			giterr_set(GITERR_NOMEMORY, "error inserting submodule into hash table");
Packit Service 20376f
			return -1;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
	if (error == GIT_ITEROVER)
Packit Service 20376f
		error = 0;
Packit Service 20376f
Packit Service 20376f
out:
Packit Service 20376f
	git_buf_free(&buf;;
Packit Service 20376f
	git_config_iterator_free(iter);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_submodule_lookup(
Packit Service 20376f
	git_submodule **out, /* NULL if user only wants to test existence */
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const char *name)    /* trailing slash is allowed */
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	unsigned int location;
Packit Service 20376f
	git_submodule *sm;
Packit Service 20376f
Packit Service 20376f
	assert(repo && name);
Packit Service 20376f
Packit Service 20376f
	if (repo->submodule_cache != NULL) {
Packit Service 20376f
		khiter_t pos = git_strmap_lookup_index(repo->submodule_cache, name);
Packit Service 20376f
		if (git_strmap_valid_index(repo->submodule_cache, pos)) {
Packit Service 20376f
			if (out) {
Packit Service 20376f
				*out = git_strmap_value_at(repo->submodule_cache, pos);
Packit Service 20376f
				GIT_REFCOUNT_INC(*out);
Packit Service 20376f
			}
Packit Service 20376f
			return 0;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((error = submodule_alloc(&sm, repo, name)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_submodule_reload(sm, false)) < 0) {
Packit Service 20376f
		git_submodule_free(sm);
Packit Service 20376f
		return error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((error = git_submodule_location(&location, sm)) < 0) {
Packit Service 20376f
		git_submodule_free(sm);
Packit Service 20376f
		return error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* If it's not configured or we're looking by path  */
Packit Service 20376f
	if (location == 0 || location == GIT_SUBMODULE_STATUS_IN_WD) {
Packit Service 20376f
		git_config_backend *mods;
Packit Service 20376f
		const char *pattern = "submodule\\..*\\.path";
Packit Service 20376f
		git_buf path = GIT_BUF_INIT;
Packit Service 20376f
		fbp_data data = { NULL, NULL };
Packit Service 20376f
Packit Service 20376f
		git_buf_puts(&path, name);
Packit Service 20376f
		while (path.ptr[path.size-1] == '/') {
Packit Service 20376f
			path.ptr[--path.size] = '\0';
Packit Service 20376f
		}
Packit Service 20376f
		data.path = path.ptr;
Packit Service 20376f
Packit Service 20376f
		mods = open_gitmodules(repo, GITMODULES_EXISTING);
Packit Service 20376f
Packit Service 20376f
		if (mods)
Packit Service 20376f
			error = git_config_file_foreach_match(mods, pattern, find_by_path, &data);
Packit Service 20376f
Packit Service 20376f
		git_config_file_free(mods);
Packit Service 20376f
Packit Service 20376f
		if (error < 0) {
Packit Service 20376f
			git_submodule_free(sm);
Packit Service 20376f
			git_buf_free(&path);
Packit Service 20376f
			return error;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if (data.name) {
Packit Service 20376f
			git__free(sm->name);
Packit Service 20376f
			sm->name = data.name;
Packit Service 20376f
			sm->path = git_buf_detach(&path);
Packit Service 20376f
Packit Service 20376f
			/* Try to load again with the right name */
Packit Service 20376f
			if ((error = git_submodule_reload(sm, false)) < 0) {
Packit Service 20376f
				git_submodule_free(sm);
Packit Service 20376f
				return error;
Packit Service 20376f
			}
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		git_buf_free(&path);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((error = git_submodule_location(&location, sm)) < 0) {
Packit Service 20376f
		git_submodule_free(sm);
Packit Service 20376f
		return error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* If we still haven't found it, do the WD check */
Packit Service 20376f
	if (location == 0 || location == GIT_SUBMODULE_STATUS_IN_WD) {
Packit Service 20376f
		git_submodule_free(sm);
Packit Service 20376f
		error = GIT_ENOTFOUND;
Packit Service 20376f
Packit Service 20376f
		/* If it's not configured, we still check if there's a repo at the path */
Packit Service 20376f
		if (git_repository_workdir(repo)) {
Packit Service 20376f
			git_buf path = GIT_BUF_INIT;
Packit Service 20376f
			if (git_buf_join3(&path,
Packit Service 20376f
					  '/', git_repository_workdir(repo), name, DOT_GIT) < 0)
Packit Service 20376f
				return -1;
Packit Service 20376f
Packit Service 20376f
			if (git_path_exists(path.ptr))
Packit Service 20376f
				error = GIT_EEXISTS;
Packit Service 20376f
Packit Service 20376f
			git_buf_free(&path);
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		submodule_set_lookup_error(error, name);
Packit Service 20376f
		return error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (out)
Packit Service 20376f
		*out = sm;
Packit Service 20376f
	else
Packit Service 20376f
		git_submodule_free(sm);
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_submodule_name_is_valid(git_repository *repo, const char *name, int flags)
Packit Service 20376f
{
Packit Service 20376f
	git_buf buf = GIT_BUF_INIT;
Packit Service 20376f
	int error, isvalid;
Packit Service 20376f
Packit Service 20376f
	if (flags == 0)
Packit Service 20376f
		flags = GIT_PATH_REJECT_FILESYSTEM_DEFAULTS;
Packit Service 20376f
Packit Service 20376f
	/* Avoid allocating a new string if we can avoid it */
Packit Service 20376f
	if (strchr(name, '\\') != NULL) {
Packit Service 20376f
		if ((error = git_path_normalize_slashes(&buf, name)) < 0)
Packit Service 20376f
			return error;
Packit Service 20376f
	} else {
Packit Service 20376f
		git_buf_attach_notowned(&buf, name, strlen(name));
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	isvalid =  git_path_isvalid(repo, buf.ptr, 0, flags);
Packit Service 20376f
	git_buf_free(&buf;;
Packit Service 20376f
Packit Service 20376f
	return isvalid;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static void submodule_free_dup(void *sm)
Packit Service 20376f
{
Packit Service 20376f
	git_submodule_free(sm);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int submodule_get_or_create(git_submodule **out, git_repository *repo, git_strmap *map, const char *name)
Packit Service 20376f
{
Packit Service 20376f
	int error = 0;
Packit Service 20376f
	khiter_t pos;
Packit Service 20376f
	git_submodule *sm = NULL;
Packit Service 20376f
Packit Service 20376f
	pos = git_strmap_lookup_index(map, name);
Packit Service 20376f
	if (git_strmap_valid_index(map, pos)) {
Packit Service 20376f
		sm = git_strmap_value_at(map, pos);
Packit Service 20376f
		goto done;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* if the submodule doesn't exist yet in the map, create it */
Packit Service 20376f
	if ((error = submodule_alloc(&sm, repo, name)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	pos = git_strmap_put(map, sm->name, &error);
Packit Service 20376f
	/* nobody can beat us to adding it */
Packit Service 20376f
	assert(error != 0);
Packit Service 20376f
	if (error < 0) {
Packit Service 20376f
		git_submodule_free(sm);
Packit Service 20376f
		return error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git_strmap_set_value_at(map, pos, sm);
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	GIT_REFCOUNT_INC(sm);
Packit Service 20376f
	*out = sm;
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int submodules_from_index(git_strmap *map, git_index *idx, git_config *cfg)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	git_iterator *i = NULL;
Packit Service 20376f
	const git_index_entry *entry;
Packit Service 20376f
	git_strmap *names = 0;
Packit Service 20376f
Packit Service 20376f
	git_strmap_alloc(&names);
Packit Service 20376f
	if ((error = load_submodule_names(names, git_index_owner(idx), cfg)))
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_iterator_for_index(&i, git_index_owner(idx), idx, NULL)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	while (!(error = git_iterator_advance(&entry, i))) {
Packit Service 20376f
		khiter_t pos = git_strmap_lookup_index(map, entry->path);
Packit Service 20376f
		git_submodule *sm;
Packit Service 20376f
Packit Service 20376f
		if (git_strmap_valid_index(map, pos)) {
Packit Service 20376f
			sm = git_strmap_value_at(map, pos);
Packit Service 20376f
Packit Service 20376f
			if (S_ISGITLINK(entry->mode))
Packit Service 20376f
				submodule_update_from_index_entry(sm, entry);
Packit Service 20376f
			else
Packit Service 20376f
				sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE;
Packit Service 20376f
		} else if (S_ISGITLINK(entry->mode)) {
Packit Service 20376f
			khiter_t name_pos;
Packit Service 20376f
			const char *name;
Packit Service 20376f
Packit Service 20376f
			name_pos = git_strmap_lookup_index(names, entry->path);
Packit Service 20376f
			if (git_strmap_valid_index(names, name_pos)) {
Packit Service 20376f
				name = git_strmap_value_at(names, name_pos);
Packit Service 20376f
			} else {
Packit Service 20376f
				name = entry->path;
Packit Service 20376f
			}
Packit Service 20376f
Packit Service 20376f
			if (!submodule_get_or_create(&sm, git_index_owner(idx), map, name)) {
Packit Service 20376f
				submodule_update_from_index_entry(sm, entry);
Packit Service 20376f
				git_submodule_free(sm);
Packit Service 20376f
			}
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (error == GIT_ITEROVER)
Packit Service 20376f
		error = 0;
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git_iterator_free(i);
Packit Service 20376f
	free_submodule_names(names);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int submodules_from_head(git_strmap *map, git_tree *head, git_config *cfg)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	git_iterator *i = NULL;
Packit Service 20376f
	const git_index_entry *entry;
Packit Service 20376f
	git_strmap *names = 0;
Packit Service 20376f
	git_strmap_alloc(&names);
Packit Service 20376f
	if ((error = load_submodule_names(names, git_tree_owner(head), cfg)))
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_iterator_for_tree(&i, head, NULL)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	while (!(error = git_iterator_advance(&entry, i))) {
Packit Service 20376f
		khiter_t pos = git_strmap_lookup_index(map, entry->path);
Packit Service 20376f
		git_submodule *sm;
Packit Service 20376f
Packit Service 20376f
		if (git_strmap_valid_index(map, pos)) {
Packit Service 20376f
			sm = git_strmap_value_at(map, pos);
Packit Service 20376f
Packit Service 20376f
			if (S_ISGITLINK(entry->mode))
Packit Service 20376f
				submodule_update_from_head_data(sm, entry->mode, &entry->id);
Packit Service 20376f
			else
Packit Service 20376f
				sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE;
Packit Service 20376f
		} else if (S_ISGITLINK(entry->mode)) {
Packit Service 20376f
			khiter_t name_pos;
Packit Service 20376f
			const char *name;
Packit Service 20376f
Packit Service 20376f
			name_pos = git_strmap_lookup_index(names, entry->path);
Packit Service 20376f
			if (git_strmap_valid_index(names, name_pos)) {
Packit Service 20376f
				name = git_strmap_value_at(names, name_pos);
Packit Service 20376f
			} else {
Packit Service 20376f
				name = entry->path;
Packit Service 20376f
			}
Packit Service 20376f
Packit Service 20376f
			if (!submodule_get_or_create(&sm, git_tree_owner(head), map, name)) {
Packit Service 20376f
				submodule_update_from_head_data(
Packit Service 20376f
					sm, entry->mode, &entry->id);
Packit Service 20376f
				git_submodule_free(sm);
Packit Service 20376f
			}
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (error == GIT_ITEROVER)
Packit Service 20376f
		error = 0;
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git_iterator_free(i);
Packit Service 20376f
	free_submodule_names(names);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/* If have_sm is true, sm is populated, otherwise map an repo are. */
Packit Service 20376f
typedef struct {
Packit Service 20376f
	git_config *mods;
Packit Service 20376f
	git_strmap *map;
Packit Service 20376f
	git_repository *repo;
Packit Service 20376f
} lfc_data;
Packit Service 20376f
Packit Service 20376f
int git_submodule__map(git_repository *repo, git_strmap *map)
Packit Service 20376f
{
Packit Service 20376f
	int error = 0;
Packit Service 20376f
	git_index *idx = NULL;
Packit Service 20376f
	git_tree *head = NULL;
Packit Service 20376f
	const char *wd = NULL;
Packit Service 20376f
	git_buf path = GIT_BUF_INIT;
Packit Service 20376f
	git_submodule *sm;
Packit Service 20376f
	git_config *mods = NULL;
Packit Service 20376f
	uint32_t mask;
Packit Service 20376f
Packit Service 20376f
	assert(repo && map);
Packit Service 20376f
Packit Service 20376f
	/* get sources that we will need to check */
Packit Service 20376f
	if (git_repository_index(&idx, repo) < 0)
Packit Service 20376f
		giterr_clear();
Packit Service 20376f
	if (git_repository_head_tree(&head, repo) < 0)
Packit Service 20376f
		giterr_clear();
Packit Service 20376f
Packit Service 20376f
	wd = git_repository_workdir(repo);
Packit Service 20376f
	if (wd && (error = git_buf_joinpath(&path, wd, GIT_MODULES_FILE)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	/* clear submodule flags that are to be refreshed */
Packit Service 20376f
	mask = 0;
Packit Service 20376f
	mask |= GIT_SUBMODULE_STATUS_IN_INDEX |
Packit Service 20376f
		GIT_SUBMODULE_STATUS__INDEX_FLAGS |
Packit Service 20376f
		GIT_SUBMODULE_STATUS__INDEX_OID_VALID |
Packit Service 20376f
		GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES;
Packit Service 20376f
Packit Service 20376f
	mask |= GIT_SUBMODULE_STATUS_IN_HEAD |
Packit Service 20376f
		GIT_SUBMODULE_STATUS__HEAD_OID_VALID;
Packit Service 20376f
	mask |= GIT_SUBMODULE_STATUS_IN_CONFIG;
Packit Service 20376f
	if (mask != 0)
Packit Service 20376f
		mask |= GIT_SUBMODULE_STATUS_IN_WD |
Packit Service 20376f
			GIT_SUBMODULE_STATUS__WD_SCANNED |
Packit Service 20376f
			GIT_SUBMODULE_STATUS__WD_FLAGS |
Packit Service 20376f
			GIT_SUBMODULE_STATUS__WD_OID_VALID;
Packit Service 20376f
Packit Service 20376f
	/* add submodule information from .gitmodules */
Packit Service 20376f
	if (wd) {
Packit Service 20376f
		lfc_data data = { 0 };
Packit Service 20376f
		data.map = map;
Packit Service 20376f
		data.repo = repo;
Packit Service 20376f
Packit Service 20376f
		if ((mods = gitmodules_snapshot(repo)) == NULL)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
Packit Service 20376f
		data.mods = mods;
Packit Service 20376f
		if ((error = git_config_foreach(
Packit Service 20376f
			    mods, submodule_load_each, &data)) < 0)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
	}
Packit Service 20376f
	/* add back submodule information from index */
Packit Service 20376f
	if (mods && idx) {
Packit Service 20376f
		if ((error = submodules_from_index(map, idx, mods)) < 0)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
	}
Packit Service 20376f
	/* add submodule information from HEAD */
Packit Service 20376f
	if (mods && head) {
Packit Service 20376f
		if ((error = submodules_from_head(map, head, mods)) < 0)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
	}
Packit Service 20376f
	/* shallow scan submodules in work tree as needed */
Packit Service 20376f
	if (wd && mask != 0) {
Packit Service 20376f
		git_strmap_foreach_value(map, sm, {
Packit Service 20376f
				submodule_load_from_wd_lite(sm);
Packit Service 20376f
			});
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
cleanup:
Packit Service 20376f
	git_config_free(mods);
Packit Service 20376f
	/* TODO: if we got an error, mark submodule config as invalid? */
Packit Service 20376f
	git_index_free(idx);
Packit Service 20376f
	git_tree_free(head);
Packit Service 20376f
	git_buf_free(&path);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_submodule_foreach(
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	git_submodule_cb callback,
Packit Service 20376f
	void *payload)
Packit Service 20376f
{
Packit Service 20376f
	git_vector snapshot = GIT_VECTOR_INIT;
Packit Service 20376f
	git_strmap *submodules;
Packit Service 20376f
	git_submodule *sm;
Packit Service 20376f
	int error;
Packit Service 20376f
	size_t i;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_strmap_alloc(&submodules)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_submodule__map(repo, submodules)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	if (!(error = git_vector_init(
Packit Service 20376f
			&snapshot, git_strmap_num_entries(submodules), submodule_cmp))) {
Packit Service 20376f
Packit Service 20376f
		git_strmap_foreach_value(submodules, sm, {
Packit Service 20376f
			if ((error = git_vector_insert(&snapshot, sm)) < 0)
Packit Service 20376f
				break;
Packit Service 20376f
			GIT_REFCOUNT_INC(sm);
Packit Service 20376f
		});
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (error < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	git_vector_uniq(&snapshot, submodule_free_dup);
Packit Service 20376f
Packit Service 20376f
	git_vector_foreach(&snapshot, i, sm) {
Packit Service 20376f
		if ((error = callback(sm, sm->name, payload)) != 0) {
Packit Service 20376f
			giterr_set_after_callback(error);
Packit Service 20376f
			break;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git_vector_foreach(&snapshot, i, sm)
Packit Service 20376f
		git_submodule_free(sm);
Packit Service 20376f
	git_vector_free(&snapshot);
Packit Service 20376f
Packit Service 20376f
	git_strmap_foreach_value(submodules, sm, {
Packit Service 20376f
		git_submodule_free(sm);
Packit Service 20376f
	});
Packit Service 20376f
	git_strmap_free(submodules);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int submodule_repo_init(
Packit Service 20376f
	git_repository **out,
Packit Service 20376f
	git_repository *parent_repo,
Packit Service 20376f
	const char *path,
Packit Service 20376f
	const char *url,
Packit Service 20376f
	bool use_gitlink)
Packit Service 20376f
{
Packit Service 20376f
	int error = 0;
Packit Service 20376f
	git_buf workdir = GIT_BUF_INIT, repodir = GIT_BUF_INIT;
Packit Service 20376f
	git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT;
Packit Service 20376f
	git_repository *subrepo = NULL;
Packit Service 20376f
Packit Service 20376f
	error = git_buf_joinpath(&workdir, git_repository_workdir(parent_repo), path);
Packit Service 20376f
	if (error < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	initopt.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_NO_REINIT;
Packit Service 20376f
	initopt.origin_url = url;
Packit Service 20376f
Packit Service 20376f
	/* init submodule repository and add origin remote as needed */
Packit Service 20376f
Packit Service 20376f
	/* New style: sub-repo goes in <repo-dir>/modules/<name>/ with a
Packit Service 20376f
	 * gitlink in the sub-repo workdir directory to that repository
Packit Service 20376f
	 *
Packit Service 20376f
	 * Old style: sub-repo goes directly into repo/<name>/.git/
Packit Service 20376f
	 */
Packit Service 20376f
	 if (use_gitlink) {
Packit Service 20376f
		error = git_repository_item_path(&repodir, parent_repo, GIT_REPOSITORY_ITEM_MODULES);
Packit Service 20376f
		if (error < 0)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
		error = git_buf_joinpath(&repodir, repodir.ptr, path);
Packit Service 20376f
		if (error < 0)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
Packit Service 20376f
		initopt.workdir_path = workdir.ptr;
Packit Service 20376f
		initopt.flags |=
Packit Service 20376f
			GIT_REPOSITORY_INIT_NO_DOTGIT_DIR |
Packit Service 20376f
			GIT_REPOSITORY_INIT_RELATIVE_GITLINK;
Packit Service 20376f
Packit Service 20376f
		error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt);
Packit Service 20376f
	} else
Packit Service 20376f
		error = git_repository_init_ext(&subrepo, workdir.ptr, &initopt);
Packit Service 20376f
Packit Service 20376f
cleanup:
Packit Service 20376f
	git_buf_free(&workdir);
Packit Service 20376f
	git_buf_free(&repodir);
Packit Service 20376f
Packit Service 20376f
	*out = subrepo;
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_submodule_add_setup(
Packit Service 20376f
	git_submodule **out,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const char *url,
Packit Service 20376f
	const char *path,
Packit Service 20376f
	int use_gitlink)
Packit Service 20376f
{
Packit Service 20376f
	int error = 0;
Packit Service 20376f
	git_config_backend *mods = NULL;
Packit Service 20376f
	git_submodule *sm = NULL;
Packit Service 20376f
	git_buf name = GIT_BUF_INIT, real_url = GIT_BUF_INIT;
Packit Service 20376f
	git_repository *subrepo = NULL;
Packit Service 20376f
Packit Service 20376f
	assert(repo && url && path);
Packit Service 20376f
Packit Service 20376f
	/* see if there is already an entry for this submodule */
Packit Service 20376f
Packit Service 20376f
	if (git_submodule_lookup(NULL, repo, path) < 0)
Packit Service 20376f
		giterr_clear();
Packit Service 20376f
	else {
Packit Service 20376f
		giterr_set(GITERR_SUBMODULE,
Packit Service 20376f
			"attempt to add submodule '%s' that already exists", path);
Packit Service 20376f
		return GIT_EEXISTS;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* validate and normalize path */
Packit Service 20376f
Packit Service 20376f
	if (git__prefixcmp(path, git_repository_workdir(repo)) == 0)
Packit Service 20376f
		path += strlen(git_repository_workdir(repo));
Packit Service 20376f
Packit Service 20376f
	if (git_path_root(path) >= 0) {
Packit Service 20376f
		giterr_set(GITERR_SUBMODULE, "submodule path must be a relative path");
Packit Service 20376f
		error = -1;
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* update .gitmodules */
Packit Service 20376f
Packit Service 20376f
	if (!(mods = open_gitmodules(repo, GITMODULES_CREATE))) {
Packit Service 20376f
		giterr_set(GITERR_SUBMODULE,
Packit Service 20376f
			"adding submodules to a bare repository is not supported");
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((error = git_buf_printf(&name, "submodule.%s.path", path)) < 0 ||
Packit Service 20376f
		(error = git_config_file_set_string(mods, name.ptr, path)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	if ((error = submodule_config_key_trunc_puts(&name, "url")) < 0 ||
Packit Service 20376f
		(error = git_config_file_set_string(mods, name.ptr, url)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	git_buf_clear(&name);
Packit Service 20376f
Packit Service 20376f
	/* init submodule repository and add origin remote as needed */
Packit Service 20376f
Packit Service 20376f
	error = git_buf_joinpath(&name, git_repository_workdir(repo), path);
Packit Service 20376f
	if (error < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	/* if the repo does not already exist, then init a new repo and add it.
Packit Service 20376f
	 * Otherwise, just add the existing repo.
Packit Service 20376f
	 */
Packit Service 20376f
	if (!(git_path_exists(name.ptr) &&
Packit Service 20376f
		git_path_contains(&name, DOT_GIT))) {
Packit Service 20376f
Packit Service 20376f
		/* resolve the actual URL to use */
Packit Service 20376f
		if ((error = git_submodule_resolve_url(&real_url, repo, url)) < 0)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
Packit Service 20376f
		 if ((error = submodule_repo_init(&subrepo, repo, path, real_url.ptr, use_gitlink)) < 0)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((error = git_submodule_lookup(&sm, repo, path)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	error = git_submodule_init(sm, false);
Packit Service 20376f
Packit Service 20376f
cleanup:
Packit Service 20376f
	if (error && sm) {
Packit Service 20376f
		git_submodule_free(sm);
Packit Service 20376f
		sm = NULL;
Packit Service 20376f
	}
Packit Service 20376f
	if (out != NULL)
Packit Service 20376f
		*out = sm;
Packit Service 20376f
Packit Service 20376f
	git_config_file_free(mods);
Packit Service 20376f
	git_repository_free(subrepo);
Packit Service 20376f
	git_buf_free(&real_url);
Packit Service 20376f
	git_buf_free(&name);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_submodule_repo_init(
Packit Service 20376f
	git_repository **out,
Packit Service 20376f
	const git_submodule *sm,
Packit Service 20376f
	int use_gitlink)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	git_repository *sub_repo = NULL;
Packit Service 20376f
	const char *configured_url;
Packit Service 20376f
	git_config *cfg = NULL;
Packit Service 20376f
	git_buf buf = GIT_BUF_INIT;
Packit Service 20376f
Packit Service 20376f
	assert(out && sm);
Packit Service 20376f
Packit Service 20376f
	/* get the configured remote url of the submodule */
Packit Service 20376f
	if ((error = git_buf_printf(&buf, "submodule.%s.url", sm->name)) < 0 ||
Packit Service 20376f
		(error = git_repository_config_snapshot(&cfg, sm->repo)) < 0 ||
Packit Service 20376f
		(error = git_config_get_string(&configured_url, cfg, buf.ptr)) < 0 ||
Packit Service 20376f
		(error = submodule_repo_init(&sub_repo, sm->repo, sm->path, configured_url, use_gitlink)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	*out = sub_repo;
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git_config_free(cfg);
Packit Service 20376f
	git_buf_free(&buf;;
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_submodule_add_finalize(git_submodule *sm)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	git_index *index;
Packit Service 20376f
Packit Service 20376f
	assert(sm);
Packit Service 20376f
Packit Service 20376f
	if ((error = git_repository_index__weakptr(&index, sm->repo)) < 0 ||
Packit Service 20376f
		(error = git_index_add_bypath(index, GIT_MODULES_FILE)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	return git_submodule_add_to_index(sm, true);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_submodule_add_to_index(git_submodule *sm, int write_index)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	git_repository *sm_repo = NULL;
Packit Service 20376f
	git_index *index;
Packit Service 20376f
	git_buf path = GIT_BUF_INIT;
Packit Service 20376f
	git_commit *head;
Packit Service 20376f
	git_index_entry entry;
Packit Service 20376f
	struct stat st;
Packit Service 20376f
Packit Service 20376f
	assert(sm);
Packit Service 20376f
Packit Service 20376f
	/* force reload of wd OID by git_submodule_open */
Packit Service 20376f
	sm->flags = sm->flags & ~GIT_SUBMODULE_STATUS__WD_OID_VALID;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_repository_index__weakptr(&index, sm->repo)) < 0 ||
Packit Service 20376f
		(error = git_buf_joinpath(
Packit Service 20376f
			&path, git_repository_workdir(sm->repo), sm->path)) < 0 ||
Packit Service 20376f
		(error = git_submodule_open(&sm_repo, sm)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	/* read stat information for submodule working directory */
Packit Service 20376f
	if (p_stat(path.ptr, &st) < 0) {
Packit Service 20376f
		giterr_set(GITERR_SUBMODULE,
Packit Service 20376f
			"cannot add submodule without working directory");
Packit Service 20376f
		error = -1;
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	memset(&entry, 0, sizeof(entry));
Packit Service 20376f
	entry.path = sm->path;
Packit Service 20376f
	git_index_entry__init_from_stat(
Packit Service 20376f
		&entry, &st, !(git_index_caps(index) & GIT_INDEXCAP_NO_FILEMODE));
Packit Service 20376f
Packit Service 20376f
	/* calling git_submodule_open will have set sm->wd_oid if possible */
Packit Service 20376f
	if ((sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) == 0) {
Packit Service 20376f
		giterr_set(GITERR_SUBMODULE,
Packit Service 20376f
			"cannot add submodule without HEAD to index");
Packit Service 20376f
		error = -1;
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
	}
Packit Service 20376f
	git_oid_cpy(&entry.id, &sm->wd_oid);
Packit Service 20376f
Packit Service 20376f
	if ((error = git_commit_lookup(&head, sm_repo, &sm->wd_oid)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	entry.ctime.seconds = (int32_t)git_commit_time(head);
Packit Service 20376f
	entry.ctime.nanoseconds = 0;
Packit Service 20376f
	entry.mtime.seconds = (int32_t)git_commit_time(head);
Packit Service 20376f
	entry.mtime.nanoseconds = 0;
Packit Service 20376f
Packit Service 20376f
	git_commit_free(head);
Packit Service 20376f
Packit Service 20376f
	/* add it */
Packit Service 20376f
	error = git_index_add(index, &entry);
Packit Service 20376f
Packit Service 20376f
	/* write it, if requested */
Packit Service 20376f
	if (!error && write_index) {
Packit Service 20376f
		error = git_index_write(index);
Packit Service 20376f
Packit Service 20376f
		if (!error)
Packit Service 20376f
			git_oid_cpy(&sm->index_oid, &sm->wd_oid);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
cleanup:
Packit Service 20376f
	git_repository_free(sm_repo);
Packit Service 20376f
	git_buf_free(&path);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
const char *git_submodule_update_to_str(git_submodule_update_t update)
Packit Service 20376f
{
Packit Service 20376f
	int i;
Packit Service 20376f
	for (i = 0; i < (int)ARRAY_SIZE(_sm_update_map); ++i)
Packit Service 20376f
		if (_sm_update_map[i].map_value == (int)update)
Packit Service 20376f
			return _sm_update_map[i].str_match;
Packit Service 20376f
	return NULL;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
git_repository *git_submodule_owner(git_submodule *submodule)
Packit Service 20376f
{
Packit Service 20376f
	assert(submodule);
Packit Service 20376f
	return submodule->repo;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
const char *git_submodule_name(git_submodule *submodule)
Packit Service 20376f
{
Packit Service 20376f
	assert(submodule);
Packit Service 20376f
	return submodule->name;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
const char *git_submodule_path(git_submodule *submodule)
Packit Service 20376f
{
Packit Service 20376f
	assert(submodule);
Packit Service 20376f
	return submodule->path;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
const char *git_submodule_url(git_submodule *submodule)
Packit Service 20376f
{
Packit Service 20376f
	assert(submodule);
Packit Service 20376f
	return submodule->url;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_submodule_resolve_url(git_buf *out, git_repository *repo, const char *url)
Packit Service 20376f
{
Packit Service 20376f
	int error = 0;
Packit Service 20376f
	git_buf normalized = GIT_BUF_INIT;
Packit Service 20376f
Packit Service 20376f
	assert(out && repo && url);
Packit Service 20376f
Packit Service 20376f
	git_buf_sanitize(out);
Packit Service 20376f
Packit Service 20376f
	/* We do this in all platforms in case someone on Windows created the .gitmodules */
Packit Service 20376f
	if (strchr(url, '\\')) {
Packit Service 20376f
		if ((error = git_path_normalize_slashes(&normalized, url)) < 0)
Packit Service 20376f
			return error;
Packit Service 20376f
Packit Service 20376f
		url = normalized.ptr;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
Packit Service 20376f
	if (git_path_is_relative(url)) {
Packit Service 20376f
		if (!(error = get_url_base(out, repo)))
Packit Service 20376f
			error = git_path_apply_relative(out, url);
Packit Service 20376f
	} else if (strchr(url, ':') != NULL || url[0] == '/') {
Packit Service 20376f
		error = git_buf_sets(out, url);
Packit Service 20376f
	} else {
Packit Service 20376f
		giterr_set(GITERR_SUBMODULE, "invalid format for submodule URL");
Packit Service 20376f
		error = -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git_buf_free(&normalized);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int write_var(git_repository *repo, const char *name, const char *var, const char *val)
Packit Service 20376f
{
Packit Service 20376f
	git_buf key = GIT_BUF_INIT;
Packit Service 20376f
	git_config_backend *mods;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	mods = open_gitmodules(repo, GITMODULES_CREATE);
Packit Service 20376f
	if (!mods)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_buf_printf(&key, "submodule.%s.%s", name, var)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	if (val)
Packit Service 20376f
		error = git_config_file_set_string(mods, key.ptr, val);
Packit Service 20376f
	else
Packit Service 20376f
		error = git_config_file_delete(mods, key.ptr);
Packit Service 20376f
Packit Service 20376f
	git_buf_free(&key);
Packit Service 20376f
Packit Service 20376f
cleanup:
Packit Service 20376f
	git_config_file_free(mods);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int write_mapped_var(git_repository *repo, const char *name, git_cvar_map *maps, size_t nmaps, const char *var, int ival)
Packit Service 20376f
{
Packit Service 20376f
	git_cvar_t type;
Packit Service 20376f
	const char *val;
Packit Service 20376f
Packit Service 20376f
	if (git_config_lookup_map_enum(&type, &val, maps, nmaps, ival) < 0) {
Packit Service 20376f
		giterr_set(GITERR_SUBMODULE, "invalid value for %s", var);
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (type == GIT_CVAR_TRUE)
Packit Service 20376f
		val = "true";
Packit Service 20376f
Packit Service 20376f
	return write_var(repo, name, var, val);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
const char *git_submodule_branch(git_submodule *submodule)
Packit Service 20376f
{
Packit Service 20376f
	assert(submodule);
Packit Service 20376f
	return submodule->branch;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_submodule_set_branch(git_repository *repo, const char *name, const char *branch)
Packit Service 20376f
{
Packit Service 20376f
Packit Service 20376f
	assert(repo && name);
Packit Service 20376f
Packit Service 20376f
	return write_var(repo, name, "branch", branch);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_submodule_set_url(git_repository *repo, const char *name, const char *url)
Packit Service 20376f
{
Packit Service 20376f
	assert(repo && name && url);
Packit Service 20376f
Packit Service 20376f
	return write_var(repo, name, "url", url);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
const git_oid *git_submodule_index_id(git_submodule *submodule)
Packit Service 20376f
{
Packit Service 20376f
	assert(submodule);
Packit Service 20376f
Packit Service 20376f
	if (submodule->flags & GIT_SUBMODULE_STATUS__INDEX_OID_VALID)
Packit Service 20376f
		return &submodule->index_oid;
Packit Service 20376f
	else
Packit Service 20376f
		return NULL;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
const git_oid *git_submodule_head_id(git_submodule *submodule)
Packit Service 20376f
{
Packit Service 20376f
	assert(submodule);
Packit Service 20376f
Packit Service 20376f
	if (submodule->flags & GIT_SUBMODULE_STATUS__HEAD_OID_VALID)
Packit Service 20376f
		return &submodule->head_oid;
Packit Service 20376f
	else
Packit Service 20376f
		return NULL;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
const git_oid *git_submodule_wd_id(git_submodule *submodule)
Packit Service 20376f
{
Packit Service 20376f
	assert(submodule);
Packit Service 20376f
Packit Service 20376f
	/* load unless we think we have a valid oid */
Packit Service 20376f
	if (!(submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID)) {
Packit Service 20376f
		git_repository *subrepo;
Packit Service 20376f
Packit Service 20376f
		/* calling submodule open grabs the HEAD OID if possible */
Packit Service 20376f
		if (!git_submodule_open_bare(&subrepo, submodule))
Packit Service 20376f
			git_repository_free(subrepo);
Packit Service 20376f
		else
Packit Service 20376f
			giterr_clear();
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID)
Packit Service 20376f
		return &submodule->wd_oid;
Packit Service 20376f
	else
Packit Service 20376f
		return NULL;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
git_submodule_ignore_t git_submodule_ignore(git_submodule *submodule)
Packit Service 20376f
{
Packit Service 20376f
	assert(submodule);
Packit Service 20376f
	return (submodule->ignore < GIT_SUBMODULE_IGNORE_NONE) ?
Packit Service 20376f
		GIT_SUBMODULE_IGNORE_NONE : submodule->ignore;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_submodule_set_ignore(git_repository *repo, const char *name, git_submodule_ignore_t ignore)
Packit Service 20376f
{
Packit Service 20376f
	assert(repo && name);
Packit Service 20376f
Packit Service 20376f
	return write_mapped_var(repo, name, _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), "ignore", ignore);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
git_submodule_update_t git_submodule_update_strategy(git_submodule *submodule)
Packit Service 20376f
{
Packit Service 20376f
	assert(submodule);
Packit Service 20376f
	return (submodule->update < GIT_SUBMODULE_UPDATE_CHECKOUT) ?
Packit Service 20376f
		GIT_SUBMODULE_UPDATE_CHECKOUT : submodule->update;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_submodule_set_update(git_repository *repo, const char *name, git_submodule_update_t update)
Packit Service 20376f
{
Packit Service 20376f
	assert(repo && name);
Packit Service 20376f
Packit Service 20376f
	return write_mapped_var(repo, name, _sm_update_map, ARRAY_SIZE(_sm_update_map), "update", update);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
git_submodule_recurse_t git_submodule_fetch_recurse_submodules(
Packit Service 20376f
	git_submodule *submodule)
Packit Service 20376f
{
Packit Service 20376f
	assert(submodule);
Packit Service 20376f
	return submodule->fetch_recurse;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_submodule_set_fetch_recurse_submodules(git_repository *repo, const char *name, git_submodule_recurse_t recurse)
Packit Service 20376f
{
Packit Service 20376f
	assert(repo && name);
Packit Service 20376f
Packit Service 20376f
	return write_mapped_var(repo, name, _sm_recurse_map, ARRAY_SIZE(_sm_recurse_map), "fetchRecurseSubmodules", recurse);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int submodule_repo_create(
Packit Service 20376f
	git_repository **out,
Packit Service 20376f
	git_repository *parent_repo,
Packit Service 20376f
	const char *path)
Packit Service 20376f
{
Packit Service 20376f
	int error = 0;
Packit Service 20376f
	git_buf workdir = GIT_BUF_INIT, repodir = GIT_BUF_INIT;
Packit Service 20376f
	git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT;
Packit Service 20376f
	git_repository *subrepo = NULL;
Packit Service 20376f
Packit Service 20376f
	initopt.flags =
Packit Service 20376f
		GIT_REPOSITORY_INIT_MKPATH |
Packit Service 20376f
		GIT_REPOSITORY_INIT_NO_REINIT |
Packit Service 20376f
		GIT_REPOSITORY_INIT_NO_DOTGIT_DIR |
Packit Service 20376f
		GIT_REPOSITORY_INIT_RELATIVE_GITLINK;
Packit Service 20376f
Packit Service 20376f
	/* Workdir: path to sub-repo working directory */
Packit Service 20376f
	error = git_buf_joinpath(&workdir, git_repository_workdir(parent_repo), path);
Packit Service 20376f
	if (error < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	initopt.workdir_path = workdir.ptr;
Packit Service 20376f
Packit Service 20376f
	/**
Packit Service 20376f
	 * Repodir: path to the sub-repo. sub-repo goes in:
Packit Service 20376f
	 * <repo-dir>/modules/<name>/ with a gitlink in the
Packit Service 20376f
	 * sub-repo workdir directory to that repository.
Packit Service 20376f
	 */
Packit Service 20376f
	error = git_repository_item_path(&repodir, parent_repo, GIT_REPOSITORY_ITEM_MODULES);
Packit Service 20376f
	if (error < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
	error = git_buf_joinpath(&repodir, repodir.ptr, path);
Packit Service 20376f
	if (error < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt);
Packit Service 20376f
Packit Service 20376f
cleanup:
Packit Service 20376f
	git_buf_free(&workdir);
Packit Service 20376f
	git_buf_free(&repodir);
Packit Service 20376f
Packit Service 20376f
	*out = subrepo;
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/**
Packit Service 20376f
 * Callback to override sub-repository creation when
Packit Service 20376f
 * cloning a sub-repository.
Packit Service 20376f
 */
Packit Service 20376f
static int git_submodule_update_repo_init_cb(
Packit Service 20376f
	git_repository **out,
Packit Service 20376f
	const char *path,
Packit Service 20376f
	int bare,
Packit Service 20376f
	void *payload)
Packit Service 20376f
{
Packit Service 20376f
	git_submodule *sm;
Packit Service 20376f
Packit Service 20376f
	GIT_UNUSED(bare);
Packit Service 20376f
Packit Service 20376f
	sm = payload;
Packit Service 20376f
Packit Service 20376f
	return submodule_repo_create(out, sm->repo, path);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_submodule_update_init_options(git_submodule_update_options *opts, unsigned int version)
Packit Service 20376f
{
Packit Service 20376f
	GIT_INIT_STRUCTURE_FROM_TEMPLATE(
Packit Service 20376f
		opts, version, git_submodule_update_options, GIT_SUBMODULE_UPDATE_OPTIONS_INIT);
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_submodule_update(git_submodule *sm, int init, git_submodule_update_options *_update_options)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	unsigned int submodule_status;
Packit Service 20376f
	git_config *config = NULL;
Packit Service 20376f
	const char *submodule_url;
Packit Service 20376f
	git_repository *sub_repo = NULL;
Packit Service 20376f
	git_remote *remote = NULL;
Packit Service 20376f
	git_object *target_commit = NULL;
Packit Service 20376f
	git_buf buf = GIT_BUF_INIT;
Packit Service 20376f
	git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
Packit Service 20376f
	git_clone_options clone_options = GIT_CLONE_OPTIONS_INIT;
Packit Service 20376f
Packit Service 20376f
	assert(sm);
Packit Service 20376f
Packit Service 20376f
	if (_update_options)
Packit Service 20376f
		memcpy(&update_options, _update_options, sizeof(git_submodule_update_options));
Packit Service 20376f
Packit Service 20376f
	GITERR_CHECK_VERSION(&update_options, GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, "git_submodule_update_options");
Packit Service 20376f
Packit Service 20376f
	/* Copy over the remote callbacks */
Packit Service 20376f
	memcpy(&clone_options.fetch_opts, &update_options.fetch_opts, sizeof(git_fetch_options));
Packit Service 20376f
Packit Service 20376f
	/* Get the status of the submodule to determine if it is already initialized  */
Packit Service 20376f
	if ((error = git_submodule_status(&submodule_status, sm->repo, sm->name, GIT_SUBMODULE_IGNORE_UNSPECIFIED)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	/*
Packit Service 20376f
	 * If submodule work dir is not already initialized, check to see
Packit Service 20376f
	 * what we need to do (initialize, clone, return error...)
Packit Service 20376f
	 */
Packit Service 20376f
	if (submodule_status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) {
Packit Service 20376f
		/*
Packit Service 20376f
		 * Work dir is not initialized, check to see if the submodule
Packit Service 20376f
		 * info has been copied into .git/config
Packit Service 20376f
		 */
Packit Service 20376f
		if ((error = git_repository_config_snapshot(&config, sm->repo)) < 0 ||
Packit Service 20376f
			(error = git_buf_printf(&buf, "submodule.%s.url", git_submodule_name(sm))) < 0)
Packit Service 20376f
			goto done;
Packit Service 20376f
Packit Service 20376f
		if ((error = git_config_get_string(&submodule_url, config, git_buf_cstr(&buf))) < 0) {
Packit Service 20376f
			/*
Packit Service 20376f
			 * If the error is not "not found" or if it is "not found" and we are not
Packit Service 20376f
			 * initializing the submodule, then return error.
Packit Service 20376f
			 */
Packit Service 20376f
			if (error != GIT_ENOTFOUND)
Packit Service 20376f
				goto done;
Packit Service 20376f
Packit Service 20376f
			if (!init) {
Packit Service 20376f
				giterr_set(GITERR_SUBMODULE, "submodule is not initialized");
Packit Service 20376f
				error = GIT_ERROR;
Packit Service 20376f
				goto done;
Packit Service 20376f
			}
Packit Service 20376f
Packit Service 20376f
			/* The submodule has not been initialized yet - initialize it now.*/
Packit Service 20376f
			if ((error = git_submodule_init(sm, 0)) < 0)
Packit Service 20376f
				goto done;
Packit Service 20376f
Packit Service 20376f
			git_config_free(config);
Packit Service 20376f
			config = NULL;
Packit Service 20376f
Packit Service 20376f
			if ((error = git_repository_config_snapshot(&config, sm->repo)) < 0 ||
Packit Service 20376f
				(error = git_config_get_string(&submodule_url, config, git_buf_cstr(&buf))) < 0)
Packit Service 20376f
				goto done;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		/** submodule is initialized - now clone it **/
Packit Service 20376f
		/* override repo creation */
Packit Service 20376f
		clone_options.repository_cb = git_submodule_update_repo_init_cb;
Packit Service 20376f
		clone_options.repository_cb_payload = sm;
Packit Service 20376f
Packit Service 20376f
		/*
Packit Service 20376f
		 * Do not perform checkout as part of clone, instead we
Packit Service 20376f
		 * will checkout the specific commit manually.
Packit Service 20376f
		 */
Packit Service 20376f
		clone_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_NONE;
Packit Service 20376f
Packit Service 20376f
		if ((error = git_clone(&sub_repo, submodule_url, sm->path, &clone_options)) < 0 ||
Packit Service 20376f
			(error = git_repository_set_head_detached(sub_repo, git_submodule_index_id(sm))) < 0 ||
Packit Service 20376f
			(error = git_checkout_head(sub_repo, &update_options.checkout_opts)) != 0)
Packit Service 20376f
			goto done;
Packit Service 20376f
	} else {
Packit Service 20376f
		const git_oid *oid;
Packit Service 20376f
Packit Service 20376f
		/**
Packit Service 20376f
		 * Work dir is initialized - look up the commit in the parent repository's index,
Packit Service 20376f
		 * update the workdir contents of the subrepository, and set the subrepository's
Packit Service 20376f
		 * head to the new commit.
Packit Service 20376f
		 */
Packit Service 20376f
		if ((error = git_submodule_open(&sub_repo, sm)) < 0)
Packit Service 20376f
			goto done;
Packit Service 20376f
Packit Service 20376f
		if ((oid = git_submodule_index_id(sm)) == NULL) {
Packit Service 20376f
			giterr_set(GITERR_SUBMODULE, "could not get ID of submodule in index");
Packit Service 20376f
			error = -1;
Packit Service 20376f
			goto done;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		/* Look up the target commit in the submodule. */
Packit Service 20376f
		if ((error = git_object_lookup(&target_commit, sub_repo, oid, GIT_OBJ_COMMIT)) < 0) {
Packit Service 20376f
			/* If it isn't found then fetch and try again. */
Packit Service 20376f
			if (error != GIT_ENOTFOUND || !update_options.allow_fetch ||
Packit Service 20376f
				(error = lookup_default_remote(&remote, sub_repo)) < 0 ||
Packit Service 20376f
				(error = git_remote_fetch(remote, NULL, &update_options.fetch_opts, NULL)) < 0 ||
Packit Service 20376f
				(error = git_object_lookup(&target_commit, sub_repo, git_submodule_index_id(sm), GIT_OBJ_COMMIT)) < 0)
Packit Service 20376f
				goto done;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if ((error = git_checkout_tree(sub_repo, target_commit, &update_options.checkout_opts)) != 0 ||
Packit Service 20376f
			(error = git_repository_set_head_detached(sub_repo, git_submodule_index_id(sm))) < 0)
Packit Service 20376f
			goto done;
Packit Service 20376f
Packit Service 20376f
		/* Invalidate the wd flags as the workdir has been updated. */
Packit Service 20376f
		sm->flags = sm->flags &
Packit Service 20376f
			~(GIT_SUBMODULE_STATUS_IN_WD |
Packit Service 20376f
		  	GIT_SUBMODULE_STATUS__WD_OID_VALID |
Packit Service 20376f
		  	GIT_SUBMODULE_STATUS__WD_SCANNED);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git_buf_free(&buf;;
Packit Service 20376f
	git_config_free(config);
Packit Service 20376f
	git_object_free(target_commit);
Packit Service 20376f
	git_remote_free(remote);
Packit Service 20376f
	git_repository_free(sub_repo);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_submodule_init(git_submodule *sm, int overwrite)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	const char *val;
Packit Service 20376f
	git_buf key = GIT_BUF_INIT, effective_submodule_url = GIT_BUF_INIT;
Packit Service 20376f
	git_config *cfg = NULL;
Packit Service 20376f
Packit Service 20376f
	if (!sm->url) {
Packit Service 20376f
		giterr_set(GITERR_SUBMODULE,
Packit Service 20376f
			"no URL configured for submodule '%s'", sm->name);
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((error = git_repository_config(&cfg, sm->repo)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	/* write "submodule.NAME.url" */
Packit Service 20376f
Packit Service 20376f
	if ((error = git_submodule_resolve_url(&effective_submodule_url, sm->repo, sm->url)) < 0 ||
Packit Service 20376f
		(error = git_buf_printf(&key, "submodule.%s.url", sm->name)) < 0 ||
Packit Service 20376f
		(error = git_config__update_entry(
Packit Service 20376f
			cfg, key.ptr, effective_submodule_url.ptr, overwrite != 0, false)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	/* write "submodule.NAME.update" if not default */
Packit Service 20376f
Packit Service 20376f
	val = (sm->update == GIT_SUBMODULE_UPDATE_CHECKOUT) ?
Packit Service 20376f
		NULL : git_submodule_update_to_str(sm->update);
Packit Service 20376f
Packit Service 20376f
	if ((error = git_buf_printf(&key, "submodule.%s.update", sm->name)) < 0 ||
Packit Service 20376f
		(error = git_config__update_entry(
Packit Service 20376f
			cfg, key.ptr, val, overwrite != 0, false)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	/* success */
Packit Service 20376f
Packit Service 20376f
cleanup:
Packit Service 20376f
	git_config_free(cfg);
Packit Service 20376f
	git_buf_free(&key);
Packit Service 20376f
	git_buf_free(&effective_submodule_url);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_submodule_sync(git_submodule *sm)
Packit Service 20376f
{
Packit Service 20376f
	int error = 0;
Packit Service 20376f
	git_config *cfg = NULL;
Packit Service 20376f
	git_buf key = GIT_BUF_INIT;
Packit Service 20376f
	git_repository *smrepo = NULL;
Packit Service 20376f
Packit Service 20376f
	if (!sm->url) {
Packit Service 20376f
		giterr_set(GITERR_SUBMODULE,
Packit Service 20376f
			"no URL configured for submodule '%s'", sm->name);
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* copy URL over to config only if it already exists */
Packit Service 20376f
Packit Service 20376f
	if (!(error = git_repository_config__weakptr(&cfg, sm->repo)) &&
Packit Service 20376f
		!(error = git_buf_printf(&key, "submodule.%s.url", sm->name)))
Packit Service 20376f
		error = git_config__update_entry(cfg, key.ptr, sm->url, true, true);
Packit Service 20376f
Packit Service 20376f
	/* if submodule exists in the working directory, update remote url */
Packit Service 20376f
Packit Service 20376f
	if (!error &&
Packit Service 20376f
		(sm->flags & GIT_SUBMODULE_STATUS_IN_WD) != 0 &&
Packit Service 20376f
		!(error = git_submodule_open(&smrepo, sm)))
Packit Service 20376f
	{
Packit Service 20376f
		git_buf remote_name = GIT_BUF_INIT;
Packit Service 20376f
Packit Service 20376f
		if ((error = git_repository_config__weakptr(&cfg, smrepo)) < 0)
Packit Service 20376f
			/* return error from reading submodule config */;
Packit Service 20376f
		else if ((error = lookup_head_remote_key(&remote_name, smrepo)) < 0) {
Packit Service 20376f
			giterr_clear();
Packit Service 20376f
			error = git_buf_sets(&key, "remote.origin.url");
Packit Service 20376f
		} else {
Packit Service 20376f
			error = git_buf_join3(
Packit Service 20376f
				&key, '.', "remote", remote_name.ptr, "url");
Packit Service 20376f
			git_buf_free(&remote_name);
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if (!error)
Packit Service 20376f
			error = git_config__update_entry(cfg, key.ptr, sm->url, true, false);
Packit Service 20376f
Packit Service 20376f
		git_repository_free(smrepo);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git_buf_free(&key);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int git_submodule__open(
Packit Service 20376f
	git_repository **subrepo, git_submodule *sm, bool bare)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	git_buf path = GIT_BUF_INIT;
Packit Service 20376f
	unsigned int flags = GIT_REPOSITORY_OPEN_NO_SEARCH;
Packit Service 20376f
	const char *wd;
Packit Service 20376f
Packit Service 20376f
	assert(sm && subrepo);
Packit Service 20376f
Packit Service 20376f
	if (git_repository__ensure_not_bare(
Packit Service 20376f
			sm->repo, "open submodule repository") < 0)
Packit Service 20376f
		return GIT_EBAREREPO;
Packit Service 20376f
Packit Service 20376f
	wd = git_repository_workdir(sm->repo);
Packit Service 20376f
Packit Service 20376f
	if (git_buf_joinpath(&path, wd, sm->path) < 0 ||
Packit Service 20376f
		git_buf_joinpath(&path, path.ptr, DOT_GIT) < 0)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	sm->flags = sm->flags &
Packit Service 20376f
		~(GIT_SUBMODULE_STATUS_IN_WD |
Packit Service 20376f
		  GIT_SUBMODULE_STATUS__WD_OID_VALID |
Packit Service 20376f
		  GIT_SUBMODULE_STATUS__WD_SCANNED);
Packit Service 20376f
Packit Service 20376f
	if (bare)
Packit Service 20376f
		flags |= GIT_REPOSITORY_OPEN_BARE;
Packit Service 20376f
Packit Service 20376f
	error = git_repository_open_ext(subrepo, path.ptr, flags, wd);
Packit Service 20376f
Packit Service 20376f
	/* if we opened the submodule successfully, grab HEAD OID, etc. */
Packit Service 20376f
	if (!error) {
Packit Service 20376f
		sm->flags |= GIT_SUBMODULE_STATUS_IN_WD |
Packit Service 20376f
			GIT_SUBMODULE_STATUS__WD_SCANNED;
Packit Service 20376f
Packit Service 20376f
		if (!git_reference_name_to_id(&sm->wd_oid, *subrepo, GIT_HEAD_FILE))
Packit Service 20376f
			sm->flags |= GIT_SUBMODULE_STATUS__WD_OID_VALID;
Packit Service 20376f
		else
Packit Service 20376f
			giterr_clear();
Packit Service 20376f
	} else if (git_path_exists(path.ptr)) {
Packit Service 20376f
		sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED |
Packit Service 20376f
			GIT_SUBMODULE_STATUS_IN_WD;
Packit Service 20376f
	} else {
Packit Service 20376f
		git_buf_rtruncate_at_char(&path, '/'); /* remove "/.git" */
Packit Service 20376f
Packit Service 20376f
		if (git_path_isdir(path.ptr))
Packit Service 20376f
			sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git_buf_free(&path);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_submodule_open_bare(git_repository **subrepo, git_submodule *sm)
Packit Service 20376f
{
Packit Service 20376f
	return git_submodule__open(subrepo, sm, true);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_submodule_open(git_repository **subrepo, git_submodule *sm)
Packit Service 20376f
{
Packit Service 20376f
	return git_submodule__open(subrepo, sm, false);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static void submodule_update_from_index_entry(
Packit Service 20376f
	git_submodule *sm, const git_index_entry *ie)
Packit Service 20376f
{
Packit Service 20376f
	bool already_found = (sm->flags & GIT_SUBMODULE_STATUS_IN_INDEX) != 0;
Packit Service 20376f
Packit Service 20376f
	if (!S_ISGITLINK(ie->mode)) {
Packit Service 20376f
		if (!already_found)
Packit Service 20376f
			sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE;
Packit Service 20376f
	} else {
Packit Service 20376f
		if (already_found)
Packit Service 20376f
			sm->flags |= GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES;
Packit Service 20376f
		else
Packit Service 20376f
			git_oid_cpy(&sm->index_oid, &ie->id);
Packit Service 20376f
Packit Service 20376f
		sm->flags |= GIT_SUBMODULE_STATUS_IN_INDEX |
Packit Service 20376f
			GIT_SUBMODULE_STATUS__INDEX_OID_VALID;
Packit Service 20376f
	}
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int submodule_update_index(git_submodule *sm)
Packit Service 20376f
{
Packit Service 20376f
	git_index *index;
Packit Service 20376f
	const git_index_entry *ie;
Packit Service 20376f
Packit Service 20376f
	if (git_repository_index__weakptr(&index, sm->repo) < 0)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	sm->flags = sm->flags &
Packit Service 20376f
		~(GIT_SUBMODULE_STATUS_IN_INDEX |
Packit Service 20376f
		  GIT_SUBMODULE_STATUS__INDEX_OID_VALID);
Packit Service 20376f
Packit Service 20376f
	if (!(ie = git_index_get_bypath(index, sm->path, 0)))
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	submodule_update_from_index_entry(sm, ie);
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static void submodule_update_from_head_data(
Packit Service 20376f
	git_submodule *sm, mode_t mode, const git_oid *id)
Packit Service 20376f
{
Packit Service 20376f
	if (!S_ISGITLINK(mode))
Packit Service 20376f
		sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE;
Packit Service 20376f
	else {
Packit Service 20376f
		git_oid_cpy(&sm->head_oid, id);
Packit Service 20376f
Packit Service 20376f
		sm->flags |= GIT_SUBMODULE_STATUS_IN_HEAD |
Packit Service 20376f
			GIT_SUBMODULE_STATUS__HEAD_OID_VALID;
Packit Service 20376f
	}
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int submodule_update_head(git_submodule *submodule)
Packit Service 20376f
{
Packit Service 20376f
	git_tree *head = NULL;
Packit Service 20376f
	git_tree_entry *te = NULL;
Packit Service 20376f
Packit Service 20376f
	submodule->flags = submodule->flags &
Packit Service 20376f
		~(GIT_SUBMODULE_STATUS_IN_HEAD |
Packit Service 20376f
		  GIT_SUBMODULE_STATUS__HEAD_OID_VALID);
Packit Service 20376f
Packit Service 20376f
	/* if we can't look up file in current head, then done */
Packit Service 20376f
	if (git_repository_head_tree(&head, submodule->repo) < 0 ||
Packit Service 20376f
		git_tree_entry_bypath(&te, head, submodule->path) < 0)
Packit Service 20376f
		giterr_clear();
Packit Service 20376f
	else
Packit Service 20376f
		submodule_update_from_head_data(submodule, te->attr, git_tree_entry_id(te));
Packit Service 20376f
Packit Service 20376f
	git_tree_entry_free(te);
Packit Service 20376f
	git_tree_free(head);
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_submodule_reload(git_submodule *sm, int force)
Packit Service 20376f
{
Packit Service 20376f
	int error = 0, isvalid;
Packit Service 20376f
	git_config *mods;
Packit Service 20376f
Packit Service 20376f
	GIT_UNUSED(force);
Packit Service 20376f
Packit Service 20376f
	assert(sm);
Packit Service 20376f
Packit Service 20376f
	isvalid = git_submodule_name_is_valid(sm->repo, sm->name, 0);
Packit Service 20376f
	if (isvalid <= 0) {
Packit Service 20376f
		/* This should come with a warning, but we've no API for that */
Packit Service 20376f
		return isvalid;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (!git_repository_is_bare(sm->repo)) {
Packit Service 20376f
		/* refresh config data */
Packit Service 20376f
		mods = gitmodules_snapshot(sm->repo);
Packit Service 20376f
		if (mods != NULL) {
Packit Service 20376f
			error = submodule_read_config(sm, mods);
Packit Service 20376f
			git_config_free(mods);
Packit Service 20376f
Packit Service 20376f
			if (error < 0)
Packit Service 20376f
				return error;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		/* refresh wd data */
Packit Service 20376f
		sm->flags &=
Packit Service 20376f
			~(GIT_SUBMODULE_STATUS_IN_WD |
Packit Service 20376f
			  GIT_SUBMODULE_STATUS__WD_OID_VALID |
Packit Service 20376f
			  GIT_SUBMODULE_STATUS__WD_FLAGS);
Packit Service 20376f
Packit Service 20376f
		error = submodule_load_from_wd_lite(sm);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (error == 0 && (error = submodule_update_index(sm)) == 0)
Packit Service 20376f
		error = submodule_update_head(sm);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static void submodule_copy_oid_maybe(
Packit Service 20376f
	git_oid *tgt, const git_oid *src, bool valid)
Packit Service 20376f
{
Packit Service 20376f
	if (tgt) {
Packit Service 20376f
		if (valid)
Packit Service 20376f
			memcpy(tgt, src, sizeof(*tgt));
Packit Service 20376f
		else
Packit Service 20376f
			memset(tgt, 0, sizeof(*tgt));
Packit Service 20376f
	}
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_submodule__status(
Packit Service 20376f
	unsigned int *out_status,
Packit Service 20376f
	git_oid *out_head_id,
Packit Service 20376f
	git_oid *out_index_id,
Packit Service 20376f
	git_oid *out_wd_id,
Packit Service 20376f
	git_submodule *sm,
Packit Service 20376f
	git_submodule_ignore_t ign)
Packit Service 20376f
{
Packit Service 20376f
	unsigned int status;
Packit Service 20376f
	git_repository *smrepo = NULL;
Packit Service 20376f
Packit Service 20376f
	if (ign == GIT_SUBMODULE_IGNORE_UNSPECIFIED)
Packit Service 20376f
		ign = sm->ignore;
Packit Service 20376f
Packit Service 20376f
	/* only return location info if ignore == all */
Packit Service 20376f
	if (ign == GIT_SUBMODULE_IGNORE_ALL) {
Packit Service 20376f
		*out_status = (sm->flags & GIT_SUBMODULE_STATUS__IN_FLAGS);
Packit Service 20376f
		return 0;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* If the user has requested caching submodule state, performing these
Packit Service 20376f
	 * expensive operations (especially `submodule_update_head`, which is
Packit Service 20376f
	 * bottlenecked on `git_repository_head_tree`) eliminates much of the
Packit Service 20376f
	 * advantage.  We will, therefore, interpret the request for caching to
Packit Service 20376f
	 * apply here to and skip them.
Packit Service 20376f
	 */
Packit Service 20376f
Packit Service 20376f
	if (sm->repo->submodule_cache == NULL) {
Packit Service 20376f
		/* refresh the index OID */
Packit Service 20376f
		if (submodule_update_index(sm) < 0)
Packit Service 20376f
			return -1;
Packit Service 20376f
Packit Service 20376f
		/* refresh the HEAD OID */
Packit Service 20376f
		if (submodule_update_head(sm) < 0)
Packit Service 20376f
			return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* for ignore == dirty, don't scan the working directory */
Packit Service 20376f
	if (ign == GIT_SUBMODULE_IGNORE_DIRTY) {
Packit Service 20376f
		/* git_submodule_open_bare will load WD OID data */
Packit Service 20376f
		if (git_submodule_open_bare(&smrepo, sm) < 0)
Packit Service 20376f
			giterr_clear();
Packit Service 20376f
		else
Packit Service 20376f
			git_repository_free(smrepo);
Packit Service 20376f
		smrepo = NULL;
Packit Service 20376f
	} else if (git_submodule_open(&smrepo, sm) < 0) {
Packit Service 20376f
		giterr_clear();
Packit Service 20376f
		smrepo = NULL;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	status = GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(sm->flags);
Packit Service 20376f
Packit Service 20376f
	submodule_get_index_status(&status, sm);
Packit Service 20376f
	submodule_get_wd_status(&status, sm, smrepo, ign);
Packit Service 20376f
Packit Service 20376f
	git_repository_free(smrepo);
Packit Service 20376f
Packit Service 20376f
	*out_status = status;
Packit Service 20376f
Packit Service 20376f
	submodule_copy_oid_maybe(out_head_id, &sm->head_oid,
Packit Service 20376f
		(sm->flags & GIT_SUBMODULE_STATUS__HEAD_OID_VALID) != 0);
Packit Service 20376f
	submodule_copy_oid_maybe(out_index_id, &sm->index_oid,
Packit Service 20376f
		(sm->flags & GIT_SUBMODULE_STATUS__INDEX_OID_VALID) != 0);
Packit Service 20376f
	submodule_copy_oid_maybe(out_wd_id, &sm->wd_oid,
Packit Service 20376f
		(sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) != 0);
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_submodule_status(unsigned int *status, git_repository *repo, const char *name, git_submodule_ignore_t ignore)
Packit Service 20376f
{
Packit Service 20376f
	git_submodule *sm;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	assert(status && repo && name);
Packit Service 20376f
Packit Service 20376f
	if ((error = git_submodule_lookup(&sm, repo, name)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	error = git_submodule__status(status, NULL, NULL, NULL, sm, ignore);
Packit Service 20376f
	git_submodule_free(sm);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_submodule_location(unsigned int *location, git_submodule *sm)
Packit Service 20376f
{
Packit Service 20376f
	assert(location && sm);
Packit Service 20376f
Packit Service 20376f
	return git_submodule__status(
Packit Service 20376f
		location, NULL, NULL, NULL, sm, GIT_SUBMODULE_IGNORE_ALL);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/*
Packit Service 20376f
 * INTERNAL FUNCTIONS
Packit Service 20376f
 */
Packit Service 20376f
Packit Service 20376f
static int submodule_alloc(
Packit Service 20376f
	git_submodule **out, git_repository *repo, const char *name)
Packit Service 20376f
{
Packit Service 20376f
	size_t namelen;
Packit Service 20376f
	git_submodule *sm;
Packit Service 20376f
Packit Service 20376f
	if (!name || !(namelen = strlen(name))) {
Packit Service 20376f
		giterr_set(GITERR_SUBMODULE, "invalid submodule name");
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	sm = git__calloc(1, sizeof(git_submodule));
Packit Service 20376f
	GITERR_CHECK_ALLOC(sm);
Packit Service 20376f
Packit Service 20376f
	sm->name = sm->path = git__strdup(name);
Packit Service 20376f
	if (!sm->name) {
Packit Service 20376f
		git__free(sm);
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	GIT_REFCOUNT_INC(sm);
Packit Service 20376f
	sm->ignore = sm->ignore_default = GIT_SUBMODULE_IGNORE_NONE;
Packit Service 20376f
	sm->update = sm->update_default = GIT_SUBMODULE_UPDATE_CHECKOUT;
Packit Service 20376f
	sm->fetch_recurse = sm->fetch_recurse_default = GIT_SUBMODULE_RECURSE_NO;
Packit Service 20376f
	sm->repo   = repo;
Packit Service 20376f
	sm->branch = NULL;
Packit Service 20376f
Packit Service 20376f
	*out = sm;
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static void submodule_release(git_submodule *sm)
Packit Service 20376f
{
Packit Service 20376f
	if (!sm)
Packit Service 20376f
		return;
Packit Service 20376f
Packit Service 20376f
	if (sm->repo) {
Packit Service 20376f
		sm->repo = NULL;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (sm->path != sm->name)
Packit Service 20376f
		git__free(sm->path);
Packit Service 20376f
	git__free(sm->name);
Packit Service 20376f
	git__free(sm->url);
Packit Service 20376f
	git__free(sm->branch);
Packit Service 20376f
	git__memzero(sm, sizeof(*sm));
Packit Service 20376f
	git__free(sm);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
void git_submodule_free(git_submodule *sm)
Packit Service 20376f
{
Packit Service 20376f
	if (!sm)
Packit Service 20376f
		return;
Packit Service 20376f
	GIT_REFCOUNT_DEC(sm, submodule_release);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int submodule_config_error(const char *property, const char *value)
Packit Service 20376f
{
Packit Service 20376f
	giterr_set(GITERR_INVALID,
Packit Service 20376f
		"invalid value for submodule '%s' property: '%s'", property, value);
Packit Service 20376f
	return -1;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_submodule_parse_ignore(git_submodule_ignore_t *out, const char *value)
Packit Service 20376f
{
Packit Service 20376f
	int val;
Packit Service 20376f
Packit Service 20376f
	if (git_config_lookup_map_value(
Packit Service 20376f
			&val, _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), value) < 0) {
Packit Service 20376f
		*out = GIT_SUBMODULE_IGNORE_NONE;
Packit Service 20376f
		return submodule_config_error("ignore", value);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	*out = (git_submodule_ignore_t)val;
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_submodule_parse_update(git_submodule_update_t *out, const char *value)
Packit Service 20376f
{
Packit Service 20376f
	int val;
Packit Service 20376f
Packit Service 20376f
	if (git_config_lookup_map_value(
Packit Service 20376f
			&val, _sm_update_map, ARRAY_SIZE(_sm_update_map), value) < 0) {
Packit Service 20376f
		*out = GIT_SUBMODULE_UPDATE_CHECKOUT;
Packit Service 20376f
		return submodule_config_error("update", value);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	*out = (git_submodule_update_t)val;
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_submodule_parse_recurse(git_submodule_recurse_t *out, const char *value)
Packit Service 20376f
{
Packit Service 20376f
	int val;
Packit Service 20376f
Packit Service 20376f
	if (git_config_lookup_map_value(
Packit Service 20376f
			&val, _sm_recurse_map, ARRAY_SIZE(_sm_recurse_map), value) < 0) {
Packit Service 20376f
		*out = GIT_SUBMODULE_RECURSE_YES;
Packit Service 20376f
		return submodule_config_error("recurse", value);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	*out = (git_submodule_recurse_t)val;
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int get_value(const char **out, git_config *cfg, git_buf *buf, const char *name, const char *field)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	git_buf_clear(buf);
Packit Service 20376f
Packit Service 20376f
	if ((error = git_buf_printf(buf, "submodule.%s.%s", name, field)) < 0 ||
Packit Service 20376f
	    (error = git_config_get_string(out, cfg, buf->ptr)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static bool looks_like_command_line_option(const char *s)
Packit Service 20376f
{
Packit Service 20376f
	if (s && s[0] == '-')
Packit Service 20376f
		return true;
Packit Service 20376f
Packit Service 20376f
	return false;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int submodule_read_config(git_submodule *sm, git_config *cfg)
Packit Service 20376f
{
Packit Service 20376f
	git_buf key = GIT_BUF_INIT;
Packit Service 20376f
	const char *value;
Packit Service 20376f
	int error, in_config = 0;
Packit Service 20376f
Packit Service 20376f
	/*
Packit Service 20376f
	 * TODO: Look up path in index and if it is present but not a GITLINK
Packit Service 20376f
	 * then this should be deleted (at least to match git's behavior)
Packit Service 20376f
	 */
Packit Service 20376f
Packit Service 20376f
	if ((error = get_value(&value, cfg, &key, sm->name, "path")) == 0) {
Packit Service 20376f
		in_config = 1;
Packit Service 20376f
		/* We would warn here if we had that API */
Packit Service 20376f
		if (!looks_like_command_line_option(value)) {
Packit Service 20376f
	/*
Packit Service 20376f
	 * TODO: if case insensitive filesystem, then the following strcmp
Packit Service 20376f
	 * should be strcasecmp
Packit Service 20376f
	 */
Packit Service 20376f
			if (strcmp(sm->name, value) != 0) {
Packit Service 20376f
				if (sm->path != sm->name)
Packit Service 20376f
					git__free(sm->path);
Packit Service 20376f
				sm->path = git__strdup(value);
Packit Service 20376f
				GITERR_CHECK_ALLOC(sm->path);
Packit Service 20376f
			}
Packit Service 20376f
Packit Service 20376f
		}
Packit Service 20376f
	} else if (error != GIT_ENOTFOUND) {
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((error = get_value(&value, cfg, &key, sm->name, "url")) == 0) {
Packit Service 20376f
		/* We would warn here if we had that API */
Packit Service 20376f
		if (!looks_like_command_line_option(value)) {
Packit Service 20376f
			in_config = 1;
Packit Service 20376f
			sm->url = git__strdup(value);
Packit Service 20376f
			GITERR_CHECK_ALLOC(sm->url);
Packit Service 20376f
		}
Packit Service 20376f
	} else if (error != GIT_ENOTFOUND) {
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((error = get_value(&value, cfg, &key, sm->name, "branch")) == 0) {
Packit Service 20376f
		in_config = 1;
Packit Service 20376f
		sm->branch = git__strdup(value);
Packit Service 20376f
		GITERR_CHECK_ALLOC(sm->branch);
Packit Service 20376f
	} else if (error != GIT_ENOTFOUND) {
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((error = get_value(&value, cfg, &key, sm->name, "update")) == 0) {
Packit Service 20376f
		in_config = 1;
Packit Service 20376f
		if ((error = git_submodule_parse_update(&sm->update, value)) < 0)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
		sm->update_default = sm->update;
Packit Service 20376f
	} else if (error != GIT_ENOTFOUND) {
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((error = get_value(&value, cfg, &key, sm->name, "fetchRecurseSubmodules")) == 0) {
Packit Service 20376f
		in_config = 1;
Packit Service 20376f
		if ((error = git_submodule_parse_recurse(&sm->fetch_recurse, value)) < 0)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
		sm->fetch_recurse_default = sm->fetch_recurse;
Packit Service 20376f
	} else if (error != GIT_ENOTFOUND) {
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((error = get_value(&value, cfg, &key, sm->name, "ignore")) == 0) {
Packit Service 20376f
		in_config = 1;
Packit Service 20376f
		if ((error = git_submodule_parse_ignore(&sm->ignore, value)) < 0)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
		sm->ignore_default = sm->ignore;
Packit Service 20376f
	} else if (error != GIT_ENOTFOUND) {
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (in_config)
Packit Service 20376f
		sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG;
Packit Service 20376f
Packit Service 20376f
	error = 0;
Packit Service 20376f
Packit Service 20376f
cleanup:
Packit Service 20376f
	git_buf_free(&key);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int submodule_load_each(const git_config_entry *entry, void *payload)
Packit Service 20376f
{
Packit Service 20376f
	lfc_data *data = payload;
Packit Service 20376f
	const char *namestart, *property;
Packit Service 20376f
	git_strmap_iter pos;
Packit Service 20376f
	git_strmap *map = data->map;
Packit Service 20376f
	git_buf name = GIT_BUF_INIT;
Packit Service 20376f
	git_submodule *sm;
Packit Service 20376f
	int error, isvalid;
Packit Service 20376f
Packit Service 20376f
	if (git__prefixcmp(entry->name, "submodule.") != 0)
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	namestart = entry->name + strlen("submodule.");
Packit Service 20376f
	property  = strrchr(namestart, '.');
Packit Service 20376f
Packit Service 20376f
	if (!property || (property == namestart))
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	property++;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_buf_set(&name, namestart, property - namestart -1)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	isvalid = git_submodule_name_is_valid(data->repo, name.ptr, 0);
Packit Service 20376f
	if (isvalid <= 0) {
Packit Service 20376f
		error = isvalid;
Packit Service 20376f
		goto done;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/*
Packit Service 20376f
	 * Now that we have the submodule's name, we can use that to
Packit Service 20376f
	 * figure out whether it's in the map. If it's not, we create
Packit Service 20376f
	 * a new submodule, load the config and insert it. If it's
Packit Service 20376f
	 * already inserted, we've already loaded it, so we skip.
Packit Service 20376f
	 */
Packit Service 20376f
	pos = git_strmap_lookup_index(map, name.ptr);
Packit Service 20376f
	if (git_strmap_valid_index(map, pos)) {
Packit Service 20376f
		error = 0;
Packit Service 20376f
		goto done;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((error = submodule_alloc(&sm, data->repo, name.ptr)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	if ((error = submodule_read_config(sm, data->mods)) < 0) {
Packit Service 20376f
		git_submodule_free(sm);
Packit Service 20376f
		goto done;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git_strmap_insert(map, sm->name, sm, &error);
Packit Service 20376f
	assert(error != 0);
Packit Service 20376f
	if (error < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	error = 0;
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git_buf_free(&name);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int submodule_load_from_wd_lite(git_submodule *sm)
Packit Service 20376f
{
Packit Service 20376f
	git_buf path = GIT_BUF_INIT;
Packit Service 20376f
Packit Service 20376f
	if (git_buf_joinpath(&path, git_repository_workdir(sm->repo), sm->path) < 0)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	if (git_path_isdir(path.ptr))
Packit Service 20376f
		sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED;
Packit Service 20376f
Packit Service 20376f
	if (git_path_contains(&path, DOT_GIT))
Packit Service 20376f
		sm->flags |= GIT_SUBMODULE_STATUS_IN_WD;
Packit Service 20376f
Packit Service 20376f
	git_buf_free(&path);
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/**
Packit Service 20376f
 * Returns a snapshot of $WORK_TREE/.gitmodules.
Packit Service 20376f
 *
Packit Service 20376f
 * We ignore any errors and just pretend the file isn't there.
Packit Service 20376f
 */
Packit Service 20376f
static git_config *gitmodules_snapshot(git_repository *repo)
Packit Service 20376f
{
Packit Service 20376f
	const char *workdir = git_repository_workdir(repo);
Packit Service 20376f
	git_config *mods = NULL, *snap = NULL;
Packit Service 20376f
	git_buf path = GIT_BUF_INIT;
Packit Service 20376f
Packit Service 20376f
	if (workdir != NULL) {
Packit Service 20376f
		if (git_buf_joinpath(&path, workdir, GIT_MODULES_FILE) != 0)
Packit Service 20376f
			return NULL;
Packit Service 20376f
Packit Service 20376f
		if (git_config_open_ondisk(&mods, path.ptr) < 0)
Packit Service 20376f
			mods = NULL;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git_buf_free(&path);
Packit Service 20376f
Packit Service 20376f
	if (mods) {
Packit Service 20376f
		git_config_snapshot(&snap, mods);
Packit Service 20376f
		git_config_free(mods);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return snap;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static git_config_backend *open_gitmodules(
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	int okay_to_create)
Packit Service 20376f
{
Packit Service 20376f
	const char *workdir = git_repository_workdir(repo);
Packit Service 20376f
	git_buf path = GIT_BUF_INIT;
Packit Service 20376f
	git_config_backend *mods = NULL;
Packit Service 20376f
Packit Service 20376f
	if (workdir != NULL) {
Packit Service 20376f
		if (git_buf_joinpath(&path, workdir, GIT_MODULES_FILE) != 0)
Packit Service 20376f
			return NULL;
Packit Service 20376f
Packit Service 20376f
		if (okay_to_create || git_path_isfile(path.ptr)) {
Packit Service 20376f
			/* git_config_file__ondisk should only fail if OOM */
Packit Service 20376f
			if (git_config_file__ondisk(&mods, path.ptr) < 0)
Packit Service 20376f
				mods = NULL;
Packit Service 20376f
			/* open should only fail here if the file is malformed */
Packit Service 20376f
			else if (git_config_file_open(mods, GIT_CONFIG_LEVEL_LOCAL) < 0) {
Packit Service 20376f
				git_config_file_free(mods);
Packit Service 20376f
				mods = NULL;
Packit Service 20376f
			}
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git_buf_free(&path);
Packit Service 20376f
Packit Service 20376f
	return mods;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/* Lookup name of remote of the local tracking branch HEAD points to */
Packit Service 20376f
static int lookup_head_remote_key(git_buf *remote_name, git_repository *repo)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	git_reference *head = NULL;
Packit Service 20376f
	git_buf upstream_name = GIT_BUF_INIT;
Packit Service 20376f
Packit Service 20376f
	/* lookup and dereference HEAD */
Packit Service 20376f
	if ((error = git_repository_head(&head, repo)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	/**
Packit Service 20376f
	 * If head does not refer to a branch, then return
Packit Service 20376f
	 * GIT_ENOTFOUND to indicate that we could not find
Packit Service 20376f
	 * a remote key for the local tracking branch HEAD points to.
Packit Service 20376f
	 **/
Packit Service 20376f
	if (!git_reference_is_branch(head)) {
Packit Service 20376f
		giterr_set(GITERR_INVALID,
Packit Service 20376f
			"HEAD does not refer to a branch.");
Packit Service 20376f
		error = GIT_ENOTFOUND;
Packit Service 20376f
		goto done;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* lookup remote tracking branch of HEAD */
Packit Service 20376f
	if ((error = git_branch_upstream_name(
Packit Service 20376f
		&upstream_name,
Packit Service 20376f
		repo,
Packit Service 20376f
		git_reference_name(head))) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	/* lookup remote of remote tracking branch */
Packit Service 20376f
	if ((error = git_branch_remote_name(remote_name, repo, upstream_name.ptr)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git_buf_free(&upstream_name);
Packit Service 20376f
	git_reference_free(head);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/* Lookup the remote of the local tracking branch HEAD points to */
Packit Service 20376f
static int lookup_head_remote(git_remote **remote, git_repository *repo)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	git_buf remote_name = GIT_BUF_INIT;
Packit Service 20376f
Packit Service 20376f
	/* lookup remote of remote tracking branch name */
Packit Service 20376f
	if (!(error = lookup_head_remote_key(&remote_name, repo)))
Packit Service 20376f
		error = git_remote_lookup(remote, repo, remote_name.ptr);
Packit Service 20376f
Packit Service 20376f
	git_buf_free(&remote_name);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/* Lookup remote, either from HEAD or fall back on origin */
Packit Service 20376f
static int lookup_default_remote(git_remote **remote, git_repository *repo)
Packit Service 20376f
{
Packit Service 20376f
	int error = lookup_head_remote(remote, repo);
Packit Service 20376f
Packit Service 20376f
	/* if that failed, use 'origin' instead */
Packit Service 20376f
	if (error == GIT_ENOTFOUND)
Packit Service 20376f
		error = git_remote_lookup(remote, repo, "origin");
Packit Service 20376f
Packit Service 20376f
	if (error == GIT_ENOTFOUND)
Packit Service 20376f
		giterr_set(
Packit Service 20376f
			GITERR_SUBMODULE,
Packit Service 20376f
			"cannot get default remote for submodule - no local tracking "
Packit Service 20376f
			"branch for HEAD and origin does not exist");
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int get_url_base(git_buf *url, git_repository *repo)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	git_worktree *wt = NULL;
Packit Service 20376f
	git_remote *remote = NULL;
Packit Service 20376f
Packit Service 20376f
	if ((error = lookup_default_remote(&remote, repo)) == 0) {
Packit Service 20376f
		error = git_buf_sets(url, git_remote_url(remote));
Packit Service 20376f
		goto out;
Packit Service 20376f
	} else if (error != GIT_ENOTFOUND)
Packit Service 20376f
		goto out;
Packit Service 20376f
	else
Packit Service 20376f
		giterr_clear();
Packit Service 20376f
Packit Service 20376f
	/* if repository does not have a default remote, use workdir instead */
Packit Service 20376f
	if (git_repository_is_worktree(repo)) {
Packit Service 20376f
		if ((error = git_worktree_open_from_repository(&wt, repo)) < 0)
Packit Service 20376f
			goto out;
Packit Service 20376f
		error = git_buf_sets(url, wt->parent_path);
Packit Service 20376f
	} else
Packit Service 20376f
		error = git_buf_sets(url, git_repository_workdir(repo));
Packit Service 20376f
Packit Service 20376f
out:
Packit Service 20376f
	git_remote_free(remote);
Packit Service 20376f
	git_worktree_free(wt);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static void submodule_get_index_status(unsigned int *status, git_submodule *sm)
Packit Service 20376f
{
Packit Service 20376f
	const git_oid *head_oid  = git_submodule_head_id(sm);
Packit Service 20376f
	const git_oid *index_oid = git_submodule_index_id(sm);
Packit Service 20376f
Packit Service 20376f
	*status = *status & ~GIT_SUBMODULE_STATUS__INDEX_FLAGS;
Packit Service 20376f
Packit Service 20376f
	if (!head_oid) {
Packit Service 20376f
		if (index_oid)
Packit Service 20376f
			*status |= GIT_SUBMODULE_STATUS_INDEX_ADDED;
Packit Service 20376f
	}
Packit Service 20376f
	else if (!index_oid)
Packit Service 20376f
		*status |= GIT_SUBMODULE_STATUS_INDEX_DELETED;
Packit Service 20376f
	else if (!git_oid_equal(head_oid, index_oid))
Packit Service 20376f
		*status |= GIT_SUBMODULE_STATUS_INDEX_MODIFIED;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
Packit Service 20376f
static void submodule_get_wd_status(
Packit Service 20376f
	unsigned int *status,
Packit Service 20376f
	git_submodule *sm,
Packit Service 20376f
	git_repository *sm_repo,
Packit Service 20376f
	git_submodule_ignore_t ign)
Packit Service 20376f
{
Packit Service 20376f
	const git_oid *index_oid = git_submodule_index_id(sm);
Packit Service 20376f
	const git_oid *wd_oid =
Packit Service 20376f
		(sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) ? &sm->wd_oid : NULL;
Packit Service 20376f
	git_tree *sm_head = NULL;
Packit Service 20376f
	git_index *index = NULL;
Packit Service 20376f
	git_diff_options opt = GIT_DIFF_OPTIONS_INIT;
Packit Service 20376f
	git_diff *diff;
Packit Service 20376f
Packit Service 20376f
	*status = *status & ~GIT_SUBMODULE_STATUS__WD_FLAGS;
Packit Service 20376f
Packit Service 20376f
	if (!index_oid) {
Packit Service 20376f
		if (wd_oid)
Packit Service 20376f
			*status |= GIT_SUBMODULE_STATUS_WD_ADDED;
Packit Service 20376f
	}
Packit Service 20376f
	else if (!wd_oid) {
Packit Service 20376f
		if ((sm->flags & GIT_SUBMODULE_STATUS__WD_SCANNED) != 0 &&
Packit Service 20376f
			(sm->flags & GIT_SUBMODULE_STATUS_IN_WD) == 0)
Packit Service 20376f
			*status |= GIT_SUBMODULE_STATUS_WD_UNINITIALIZED;
Packit Service 20376f
		else
Packit Service 20376f
			*status |= GIT_SUBMODULE_STATUS_WD_DELETED;
Packit Service 20376f
	}
Packit Service 20376f
	else if (!git_oid_equal(index_oid, wd_oid))
Packit Service 20376f
		*status |= GIT_SUBMODULE_STATUS_WD_MODIFIED;
Packit Service 20376f
Packit Service 20376f
	/* if we have no repo, then we're done */
Packit Service 20376f
	if (!sm_repo)
Packit Service 20376f
		return;
Packit Service 20376f
Packit Service 20376f
	/* the diffs below could be optimized with an early termination
Packit Service 20376f
	 * option to the git_diff functions, but for now this is sufficient
Packit Service 20376f
	 * (and certainly no worse that what core git does).
Packit Service 20376f
	 */
Packit Service 20376f
Packit Service 20376f
	if (ign == GIT_SUBMODULE_IGNORE_NONE)
Packit Service 20376f
		opt.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
Packit Service 20376f
Packit Service 20376f
	(void)git_repository_index__weakptr(&index, sm_repo);
Packit Service 20376f
Packit Service 20376f
	/* if we don't have an unborn head, check diff with index */
Packit Service 20376f
	if (git_repository_head_tree(&sm_head, sm_repo) < 0)
Packit Service 20376f
		giterr_clear();
Packit Service 20376f
	else {
Packit Service 20376f
		/* perform head to index diff on submodule */
Packit Service 20376f
		if (git_diff_tree_to_index(&diff, sm_repo, sm_head, index, &opt) < 0)
Packit Service 20376f
			giterr_clear();
Packit Service 20376f
		else {
Packit Service 20376f
			if (git_diff_num_deltas(diff) > 0)
Packit Service 20376f
				*status |= GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED;
Packit Service 20376f
			git_diff_free(diff);
Packit Service 20376f
			diff = NULL;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		git_tree_free(sm_head);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* perform index-to-workdir diff on submodule */
Packit Service 20376f
	if (git_diff_index_to_workdir(&diff, sm_repo, index, &opt) < 0)
Packit Service 20376f
		giterr_clear();
Packit Service 20376f
	else {
Packit Service 20376f
		size_t untracked =
Packit Service 20376f
			git_diff_num_deltas_of_type(diff, GIT_DELTA_UNTRACKED);
Packit Service 20376f
Packit Service 20376f
		if (untracked > 0)
Packit Service 20376f
			*status |= GIT_SUBMODULE_STATUS_WD_UNTRACKED;
Packit Service 20376f
Packit Service 20376f
		if (git_diff_num_deltas(diff) != untracked)
Packit Service 20376f
			*status |= GIT_SUBMODULE_STATUS_WD_WD_MODIFIED;
Packit Service 20376f
Packit Service 20376f
		git_diff_free(diff);
Packit Service 20376f
		diff = NULL;
Packit Service 20376f
	}
Packit Service 20376f
}