Skip to content
Permalink
7dad263334
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
286 lines (239 sloc) 7.64 KB
#include "builtin.h"
#include "cache.h"
#include "parse-options.h"
#include "quote.h"
#include "pathspec.h"
#include "dir.h"
#include "utf8.h"
#include "submodule.h"
#include "submodule-config.h"
#include "string-list.h"
#include "run-command.h"
struct module_list {
const struct cache_entry **entries;
int alloc, nr;
};
#define MODULE_LIST_INIT { NULL, 0, 0 }
static int module_list_compute(int argc, const char **argv,
const char *prefix,
struct pathspec *pathspec,
struct module_list *list)
{
int i, result = 0;
char *max_prefix, *ps_matched = NULL;
int max_prefix_len;
parse_pathspec(pathspec, 0,
PATHSPEC_PREFER_FULL |
PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,
prefix, argv);
/* Find common prefix for all pathspec's */
max_prefix = common_prefix(pathspec);
max_prefix_len = max_prefix ? strlen(max_prefix) : 0;
if (pathspec->nr)
ps_matched = xcalloc(pathspec->nr, 1);
if (read_cache() < 0)
die(_("index file corrupt"));
for (i = 0; i < active_nr; i++) {
const struct cache_entry *ce = active_cache[i];
if (!S_ISGITLINK(ce->ce_mode) ||
!match_pathspec(pathspec, ce->name, ce_namelen(ce),
max_prefix_len, ps_matched, 1))
continue;
ALLOC_GROW(list->entries, list->nr + 1, list->alloc);
list->entries[list->nr++] = ce;
while (i + 1 < active_nr &&
!strcmp(ce->name, active_cache[i + 1]->name))
/*
* Skip entries with the same name in different stages
* to make sure an entry is returned only once.
*/
i++;
}
free(max_prefix);
if (ps_matched && report_path_error(ps_matched, pathspec, prefix))
result = -1;
free(ps_matched);
return result;
}
static int module_list(int argc, const char **argv, const char *prefix)
{
int i;
struct pathspec pathspec;
struct module_list list = MODULE_LIST_INIT;
struct option module_list_options[] = {
OPT_STRING(0, "prefix", &prefix,
N_("path"),
N_("alternative anchor for relative paths")),
OPT_END()
};
const char *const git_submodule_helper_usage[] = {
N_("git submodule--helper list [--prefix=<path>] [<path>...]"),
NULL
};
argc = parse_options(argc, argv, prefix, module_list_options,
git_submodule_helper_usage, 0);
if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {
printf("#unmatched\n");
return 1;
}
for (i = 0; i < list.nr; i++) {
const struct cache_entry *ce = list.entries[i];
if (ce_stage(ce))
printf("%06o %s U\t", ce->ce_mode, sha1_to_hex(null_sha1));
else
printf("%06o %s %d\t", ce->ce_mode, sha1_to_hex(ce->sha1), ce_stage(ce));
utf8_fprintf(stdout, "%s\n", ce->name);
}
return 0;
}
static int module_name(int argc, const char **argv, const char *prefix)
{
const struct submodule *sub;
if (argc != 2)
usage(_("git submodule--helper name <path>"));
gitmodules_config();
sub = submodule_from_path(null_sha1, argv[1]);
if (!sub)
die(_("no submodule mapping found in .gitmodules for path '%s'"),
argv[1]);
printf("%s\n", sub->name);
return 0;
}
static int clone_submodule(const char *path, const char *gitdir, const char *url,
const char *depth, const char *reference, int quiet)
{
struct child_process cp;
child_process_init(&cp);
argv_array_push(&cp.args, "clone");
argv_array_push(&cp.args, "--no-checkout");
if (quiet)
argv_array_push(&cp.args, "--quiet");
if (depth && *depth)
argv_array_pushl(&cp.args, "--depth", depth, NULL);
if (reference && *reference)
argv_array_pushl(&cp.args, "--reference", reference, NULL);
if (gitdir && *gitdir)
argv_array_pushl(&cp.args, "--separate-git-dir", gitdir, NULL);
argv_array_push(&cp.args, url);
argv_array_push(&cp.args, path);
cp.git_cmd = 1;
cp.env = local_repo_env;
cp.no_stdin = 1;
return run_command(&cp);
}
static int module_clone(int argc, const char **argv, const char *prefix)
{
const char *path = NULL, *name = NULL, *url = NULL;
const char *reference = NULL, *depth = NULL;
int quiet = 0;
FILE *submodule_dot_git;
char *sm_gitdir, *cwd, *p;
struct strbuf rel_path = STRBUF_INIT;
struct strbuf sb = STRBUF_INIT;
struct option module_clone_options[] = {
OPT_STRING(0, "prefix", &prefix,
N_("path"),
N_("alternative anchor for relative paths")),
OPT_STRING(0, "path", &path,
N_("path"),
N_("where the new submodule will be cloned to")),
OPT_STRING(0, "name", &name,
N_("string"),
N_("name of the new submodule")),
OPT_STRING(0, "url", &url,
N_("string"),
N_("url where to clone the submodule from")),
OPT_STRING(0, "reference", &reference,
N_("string"),
N_("reference repository")),
OPT_STRING(0, "depth", &depth,
N_("string"),
N_("depth for shallow clones")),
OPT__QUIET(&quiet, "Suppress output for cloning a submodule"),
OPT_END()
};
const char *const git_submodule_helper_usage[] = {
N_("git submodule--helper clone [--prefix=<path>] [--quiet] "
"[--reference <repository>] [--name <name>] [--depth <depth>] "
"--url <url> --path <path>"),
NULL
};
argc = parse_options(argc, argv, prefix, module_clone_options,
git_submodule_helper_usage, 0);
if (argc || !url || !path)
usage_with_options(git_submodule_helper_usage,
module_clone_options);
strbuf_addf(&sb, "%s/modules/%s", get_git_dir(), name);
sm_gitdir = strbuf_detach(&sb, NULL);
if (!file_exists(sm_gitdir)) {
if (safe_create_leading_directories_const(sm_gitdir) < 0)
die(_("could not create directory '%s'"), sm_gitdir);
if (clone_submodule(path, sm_gitdir, url, depth, reference, quiet))
die(_("clone of '%s' into submodule path '%s' failed"),
url, path);
} else {
if (safe_create_leading_directories_const(path) < 0)
die(_("could not create directory '%s'"), path);
strbuf_addf(&sb, "%s/index", sm_gitdir);
unlink_or_warn(sb.buf);
strbuf_reset(&sb);
}
/* Write a .git file in the submodule to redirect to the superproject. */
if (safe_create_leading_directories_const(path) < 0)
die(_("could not create directory '%s'"), path);
if (path && *path)
strbuf_addf(&sb, "%s/.git", path);
else
strbuf_addstr(&sb, ".git");
if (safe_create_leading_directories_const(sb.buf) < 0)
die(_("could not create leading directories of '%s'"), sb.buf);
submodule_dot_git = fopen(sb.buf, "w");
if (!submodule_dot_git)
die_errno(_("cannot open file '%s'"), sb.buf);
fprintf(submodule_dot_git, "gitdir: %s\n",
relative_path(sm_gitdir, path, &rel_path));
if (fclose(submodule_dot_git))
die(_("could not close file %s"), sb.buf);
strbuf_reset(&sb);
strbuf_reset(&rel_path);
cwd = xgetcwd();
/* Redirect the worktree of the submodule in the superproject's config */
if (!is_absolute_path(sm_gitdir)) {
strbuf_addf(&sb, "%s/%s", cwd, sm_gitdir);
free(sm_gitdir);
sm_gitdir = strbuf_detach(&sb, NULL);
}
strbuf_addf(&sb, "%s/%s", cwd, path);
p = git_pathdup_submodule(path, "config");
if (!p)
die(_("could not get submodule directory for '%s'"), path);
git_config_set_in_file(p, "core.worktree",
relative_path(sb.buf, sm_gitdir, &rel_path));
strbuf_release(&sb);
strbuf_release(&rel_path);
free(sm_gitdir);
free(cwd);
free(p);
return 0;
}
struct cmd_struct {
const char *cmd;
int (*fn)(int, const char **, const char *);
};
static struct cmd_struct commands[] = {
{"list", module_list},
{"name", module_name},
{"clone", module_clone},
};
int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
{
int i;
if (argc < 2)
die(_("fatal: submodule--helper subcommand must be "
"called with a subcommand"));
for (i = 0; i < ARRAY_SIZE(commands); i++)
if (!strcmp(argv[1], commands[i].cmd))
return commands[i].fn(argc - 1, argv + 1, prefix);
die(_("fatal: '%s' is not a valid submodule--helper "
"subcommand"), argv[1]);
}