Merge branch 'ar/submodule-add-more'
More parts of "git submodule add" has been rewritten in C. * ar/submodule-add-more: submodule--helper: rename compute_submodule_clone_url() submodule--helper: remove resolve-relative-url subcommand submodule--helper: remove add-config subcommand submodule--helper: remove add-clone subcommand submodule--helper: convert the bulk of cmd_add() to C dir: libify and export helper functions from clone.c submodule--helper: remove repeated code in sync_submodule() submodule--helper: refactor resolve_relative_url() helper submodule--helper: add options for compute_submodule_clone_url()
This commit is contained in:
commit
bbeca063cf
118
builtin/clone.c
118
builtin/clone.c
@ -217,120 +217,6 @@ static char *get_repo_path(const char *repo, int *is_bundle)
|
|||||||
return canon;
|
return canon;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *guess_dir_name(const char *repo, int is_bundle, int is_bare)
|
|
||||||
{
|
|
||||||
const char *end = repo + strlen(repo), *start, *ptr;
|
|
||||||
size_t len;
|
|
||||||
char *dir;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Skip scheme.
|
|
||||||
*/
|
|
||||||
start = strstr(repo, "://");
|
|
||||||
if (start == NULL)
|
|
||||||
start = repo;
|
|
||||||
else
|
|
||||||
start += 3;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Skip authentication data. The stripping does happen
|
|
||||||
* greedily, such that we strip up to the last '@' inside
|
|
||||||
* the host part.
|
|
||||||
*/
|
|
||||||
for (ptr = start; ptr < end && !is_dir_sep(*ptr); ptr++) {
|
|
||||||
if (*ptr == '@')
|
|
||||||
start = ptr + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Strip trailing spaces, slashes and /.git
|
|
||||||
*/
|
|
||||||
while (start < end && (is_dir_sep(end[-1]) || isspace(end[-1])))
|
|
||||||
end--;
|
|
||||||
if (end - start > 5 && is_dir_sep(end[-5]) &&
|
|
||||||
!strncmp(end - 4, ".git", 4)) {
|
|
||||||
end -= 5;
|
|
||||||
while (start < end && is_dir_sep(end[-1]))
|
|
||||||
end--;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Strip trailing port number if we've got only a
|
|
||||||
* hostname (that is, there is no dir separator but a
|
|
||||||
* colon). This check is required such that we do not
|
|
||||||
* strip URI's like '/foo/bar:2222.git', which should
|
|
||||||
* result in a dir '2222' being guessed due to backwards
|
|
||||||
* compatibility.
|
|
||||||
*/
|
|
||||||
if (memchr(start, '/', end - start) == NULL
|
|
||||||
&& memchr(start, ':', end - start) != NULL) {
|
|
||||||
ptr = end;
|
|
||||||
while (start < ptr && isdigit(ptr[-1]) && ptr[-1] != ':')
|
|
||||||
ptr--;
|
|
||||||
if (start < ptr && ptr[-1] == ':')
|
|
||||||
end = ptr - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Find last component. To remain backwards compatible we
|
|
||||||
* also regard colons as path separators, such that
|
|
||||||
* cloning a repository 'foo:bar.git' would result in a
|
|
||||||
* directory 'bar' being guessed.
|
|
||||||
*/
|
|
||||||
ptr = end;
|
|
||||||
while (start < ptr && !is_dir_sep(ptr[-1]) && ptr[-1] != ':')
|
|
||||||
ptr--;
|
|
||||||
start = ptr;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Strip .{bundle,git}.
|
|
||||||
*/
|
|
||||||
len = end - start;
|
|
||||||
strip_suffix_mem(start, &len, is_bundle ? ".bundle" : ".git");
|
|
||||||
|
|
||||||
if (!len || (len == 1 && *start == '/'))
|
|
||||||
die(_("No directory name could be guessed.\n"
|
|
||||||
"Please specify a directory on the command line"));
|
|
||||||
|
|
||||||
if (is_bare)
|
|
||||||
dir = xstrfmt("%.*s.git", (int)len, start);
|
|
||||||
else
|
|
||||||
dir = xstrndup(start, len);
|
|
||||||
/*
|
|
||||||
* Replace sequences of 'control' characters and whitespace
|
|
||||||
* with one ascii space, remove leading and trailing spaces.
|
|
||||||
*/
|
|
||||||
if (*dir) {
|
|
||||||
char *out = dir;
|
|
||||||
int prev_space = 1 /* strip leading whitespace */;
|
|
||||||
for (end = dir; *end; ++end) {
|
|
||||||
char ch = *end;
|
|
||||||
if ((unsigned char)ch < '\x20')
|
|
||||||
ch = '\x20';
|
|
||||||
if (isspace(ch)) {
|
|
||||||
if (prev_space)
|
|
||||||
continue;
|
|
||||||
prev_space = 1;
|
|
||||||
} else
|
|
||||||
prev_space = 0;
|
|
||||||
*out++ = ch;
|
|
||||||
}
|
|
||||||
*out = '\0';
|
|
||||||
if (out > dir && prev_space)
|
|
||||||
out[-1] = '\0';
|
|
||||||
}
|
|
||||||
return dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void strip_trailing_slashes(char *dir)
|
|
||||||
{
|
|
||||||
char *end = dir + strlen(dir);
|
|
||||||
|
|
||||||
while (dir < end - 1 && is_dir_sep(end[-1]))
|
|
||||||
end--;
|
|
||||||
*end = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
static int add_one_reference(struct string_list_item *item, void *cb_data)
|
static int add_one_reference(struct string_list_item *item, void *cb_data)
|
||||||
{
|
{
|
||||||
struct strbuf err = STRBUF_INIT;
|
struct strbuf err = STRBUF_INIT;
|
||||||
@ -1039,8 +925,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
|||||||
if (argc == 2)
|
if (argc == 2)
|
||||||
dir = xstrdup(argv[1]);
|
dir = xstrdup(argv[1]);
|
||||||
else
|
else
|
||||||
dir = guess_dir_name(repo_name, is_bundle, option_bare);
|
dir = git_url_basename(repo_name, is_bundle, option_bare);
|
||||||
strip_trailing_slashes(dir);
|
strip_dir_trailing_slashes(dir);
|
||||||
|
|
||||||
dest_exists = path_exists(dir);
|
dest_exists = path_exists(dir);
|
||||||
if (dest_exists && !is_empty_dir(dir))
|
if (dest_exists && !is_empty_dir(dir))
|
||||||
|
@ -199,34 +199,28 @@ static char *relative_url(const char *remote_url,
|
|||||||
return strbuf_detach(&sb, NULL);
|
return strbuf_detach(&sb, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int resolve_relative_url(int argc, const char **argv, const char *prefix)
|
static char *resolve_relative_url(const char *rel_url, const char *up_path, int quiet)
|
||||||
{
|
{
|
||||||
char *remoteurl = NULL;
|
char *remoteurl, *resolved_url;
|
||||||
char *remote = get_default_remote();
|
char *remote = get_default_remote();
|
||||||
const char *up_path = NULL;
|
struct strbuf remotesb = STRBUF_INIT;
|
||||||
char *res;
|
|
||||||
const char *url;
|
|
||||||
struct strbuf sb = STRBUF_INIT;
|
|
||||||
|
|
||||||
if (argc != 2 && argc != 3)
|
strbuf_addf(&remotesb, "remote.%s.url", remote);
|
||||||
die("resolve-relative-url only accepts one or two arguments");
|
if (git_config_get_string(remotesb.buf, &remoteurl)) {
|
||||||
|
if (!quiet)
|
||||||
url = argv[1];
|
warning(_("could not look up configuration '%s'. "
|
||||||
strbuf_addf(&sb, "remote.%s.url", remote);
|
"Assuming this repository is its own "
|
||||||
free(remote);
|
"authoritative upstream."),
|
||||||
|
remotesb.buf);
|
||||||
if (git_config_get_string(sb.buf, &remoteurl))
|
|
||||||
/* the repository is its own authoritative upstream */
|
|
||||||
remoteurl = xgetcwd();
|
remoteurl = xgetcwd();
|
||||||
|
}
|
||||||
|
resolved_url = relative_url(remoteurl, rel_url, up_path);
|
||||||
|
|
||||||
if (argc == 3)
|
free(remote);
|
||||||
up_path = argv[2];
|
|
||||||
|
|
||||||
res = relative_url(remoteurl, url, up_path);
|
|
||||||
puts(res);
|
|
||||||
free(res);
|
|
||||||
free(remoteurl);
|
free(remoteurl);
|
||||||
return 0;
|
strbuf_release(&remotesb);
|
||||||
|
|
||||||
|
return resolved_url;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int resolve_relative_url_test(int argc, const char **argv, const char *prefix)
|
static int resolve_relative_url_test(int argc, const char **argv, const char *prefix)
|
||||||
@ -590,26 +584,6 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *compute_submodule_clone_url(const char *rel_url)
|
|
||||||
{
|
|
||||||
char *remoteurl, *relurl;
|
|
||||||
char *remote = get_default_remote();
|
|
||||||
struct strbuf remotesb = STRBUF_INIT;
|
|
||||||
|
|
||||||
strbuf_addf(&remotesb, "remote.%s.url", remote);
|
|
||||||
if (git_config_get_string(remotesb.buf, &remoteurl)) {
|
|
||||||
warning(_("could not look up configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
|
|
||||||
remoteurl = xgetcwd();
|
|
||||||
}
|
|
||||||
relurl = relative_url(remoteurl, rel_url, NULL);
|
|
||||||
|
|
||||||
free(remote);
|
|
||||||
free(remoteurl);
|
|
||||||
strbuf_release(&remotesb);
|
|
||||||
|
|
||||||
return relurl;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct init_cb {
|
struct init_cb {
|
||||||
const char *prefix;
|
const char *prefix;
|
||||||
unsigned int flags;
|
unsigned int flags;
|
||||||
@ -660,7 +634,7 @@ static void init_submodule(const char *path, const char *prefix,
|
|||||||
if (starts_with_dot_dot_slash(url) ||
|
if (starts_with_dot_dot_slash(url) ||
|
||||||
starts_with_dot_slash(url)) {
|
starts_with_dot_slash(url)) {
|
||||||
char *oldurl = url;
|
char *oldurl = url;
|
||||||
url = compute_submodule_clone_url(oldurl);
|
url = resolve_relative_url(oldurl, NULL, 0);
|
||||||
free(oldurl);
|
free(oldurl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1380,20 +1354,10 @@ static void sync_submodule(const char *path, const char *prefix,
|
|||||||
if (sub && sub->url) {
|
if (sub && sub->url) {
|
||||||
if (starts_with_dot_dot_slash(sub->url) ||
|
if (starts_with_dot_dot_slash(sub->url) ||
|
||||||
starts_with_dot_slash(sub->url)) {
|
starts_with_dot_slash(sub->url)) {
|
||||||
char *remote_url, *up_path;
|
char *up_path = get_up_path(path);
|
||||||
char *remote = get_default_remote();
|
sub_origin_url = resolve_relative_url(sub->url, up_path, 1);
|
||||||
strbuf_addf(&sb, "remote.%s.url", remote);
|
super_config_url = resolve_relative_url(sub->url, NULL, 1);
|
||||||
|
|
||||||
if (git_config_get_string(sb.buf, &remote_url))
|
|
||||||
remote_url = xgetcwd();
|
|
||||||
|
|
||||||
up_path = get_up_path(path);
|
|
||||||
sub_origin_url = relative_url(remote_url, sub->url, up_path);
|
|
||||||
super_config_url = relative_url(remote_url, sub->url, NULL);
|
|
||||||
|
|
||||||
free(remote);
|
|
||||||
free(up_path);
|
free(up_path);
|
||||||
free(remote_url);
|
|
||||||
} else {
|
} else {
|
||||||
sub_origin_url = xstrdup(sub->url);
|
sub_origin_url = xstrdup(sub->url);
|
||||||
super_config_url = xstrdup(sub->url);
|
super_config_url = xstrdup(sub->url);
|
||||||
@ -2134,7 +2098,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
|
|||||||
if (repo_config_get_string_tmp(the_repository, sb.buf, &url)) {
|
if (repo_config_get_string_tmp(the_repository, sb.buf, &url)) {
|
||||||
if (starts_with_dot_slash(sub->url) ||
|
if (starts_with_dot_slash(sub->url) ||
|
||||||
starts_with_dot_dot_slash(sub->url)) {
|
starts_with_dot_dot_slash(sub->url)) {
|
||||||
url = compute_submodule_clone_url(sub->url);
|
url = resolve_relative_url(sub->url, NULL, 0);
|
||||||
need_free_url = 1;
|
need_free_url = 1;
|
||||||
} else
|
} else
|
||||||
url = sub->url;
|
url = sub->url;
|
||||||
@ -2765,7 +2729,7 @@ struct add_data {
|
|||||||
const char *prefix;
|
const char *prefix;
|
||||||
const char *branch;
|
const char *branch;
|
||||||
const char *reference_path;
|
const char *reference_path;
|
||||||
const char *sm_path;
|
char *sm_path;
|
||||||
const char *sm_name;
|
const char *sm_name;
|
||||||
const char *repo;
|
const char *repo;
|
||||||
const char *realrepo;
|
const char *realrepo;
|
||||||
@ -2877,65 +2841,6 @@ static int add_submodule(const struct add_data *add_data)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int add_clone(int argc, const char **argv, const char *prefix)
|
|
||||||
{
|
|
||||||
int force = 0, quiet = 0, dissociate = 0, progress = 0;
|
|
||||||
struct add_data add_data = ADD_DATA_INIT;
|
|
||||||
|
|
||||||
struct option options[] = {
|
|
||||||
OPT_STRING('b', "branch", &add_data.branch,
|
|
||||||
N_("branch"),
|
|
||||||
N_("branch of repository to checkout on cloning")),
|
|
||||||
OPT_STRING(0, "prefix", &prefix,
|
|
||||||
N_("path"),
|
|
||||||
N_("alternative anchor for relative paths")),
|
|
||||||
OPT_STRING(0, "path", &add_data.sm_path,
|
|
||||||
N_("path"),
|
|
||||||
N_("where the new submodule will be cloned to")),
|
|
||||||
OPT_STRING(0, "name", &add_data.sm_name,
|
|
||||||
N_("string"),
|
|
||||||
N_("name of the new submodule")),
|
|
||||||
OPT_STRING(0, "url", &add_data.realrepo,
|
|
||||||
N_("string"),
|
|
||||||
N_("url where to clone the submodule from")),
|
|
||||||
OPT_STRING(0, "reference", &add_data.reference_path,
|
|
||||||
N_("repo"),
|
|
||||||
N_("reference repository")),
|
|
||||||
OPT_BOOL(0, "dissociate", &dissociate,
|
|
||||||
N_("use --reference only while cloning")),
|
|
||||||
OPT_INTEGER(0, "depth", &add_data.depth,
|
|
||||||
N_("depth for shallow clones")),
|
|
||||||
OPT_BOOL(0, "progress", &progress,
|
|
||||||
N_("force cloning progress")),
|
|
||||||
OPT__FORCE(&force, N_("allow adding an otherwise ignored submodule path"),
|
|
||||||
PARSE_OPT_NOCOMPLETE),
|
|
||||||
OPT__QUIET(&quiet, "suppress output for cloning a submodule"),
|
|
||||||
OPT_END()
|
|
||||||
};
|
|
||||||
|
|
||||||
const char *const usage[] = {
|
|
||||||
N_("git submodule--helper add-clone [<options>...] "
|
|
||||||
"--url <url> --path <path> --name <name>"),
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
argc = parse_options(argc, argv, prefix, options, usage, 0);
|
|
||||||
|
|
||||||
if (argc != 0)
|
|
||||||
usage_with_options(usage, options);
|
|
||||||
|
|
||||||
add_data.prefix = prefix;
|
|
||||||
add_data.progress = !!progress;
|
|
||||||
add_data.dissociate = !!dissociate;
|
|
||||||
add_data.force = !!force;
|
|
||||||
add_data.quiet = !!quiet;
|
|
||||||
|
|
||||||
if (add_submodule(&add_data))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)
|
static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)
|
||||||
{
|
{
|
||||||
char *key;
|
char *key;
|
||||||
@ -3016,50 +2921,164 @@ static void configure_added_submodule(struct add_data *add_data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int add_config(int argc, const char **argv, const char *prefix)
|
static void die_on_index_match(const char *path, int force)
|
||||||
{
|
{
|
||||||
int force = 0;
|
struct pathspec ps;
|
||||||
|
const char *args[] = { path, NULL };
|
||||||
|
parse_pathspec(&ps, 0, PATHSPEC_PREFER_CWD, NULL, args);
|
||||||
|
|
||||||
|
if (read_cache_preload(NULL) < 0)
|
||||||
|
die(_("index file corrupt"));
|
||||||
|
|
||||||
|
if (ps.nr) {
|
||||||
|
int i;
|
||||||
|
char *ps_matched = xcalloc(ps.nr, 1);
|
||||||
|
|
||||||
|
/* TODO: audit for interaction with sparse-index. */
|
||||||
|
ensure_full_index(&the_index);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Since there is only one pathspec, we just need
|
||||||
|
* need to check ps_matched[0] to know if a cache
|
||||||
|
* entry matched.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < active_nr; i++) {
|
||||||
|
ce_path_match(&the_index, active_cache[i], &ps,
|
||||||
|
ps_matched);
|
||||||
|
|
||||||
|
if (ps_matched[0]) {
|
||||||
|
if (!force)
|
||||||
|
die(_("'%s' already exists in the index"),
|
||||||
|
path);
|
||||||
|
if (!S_ISGITLINK(active_cache[i]->ce_mode))
|
||||||
|
die(_("'%s' already exists in the index "
|
||||||
|
"and is not a submodule"), path);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(ps_matched);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void die_on_repo_without_commits(const char *path)
|
||||||
|
{
|
||||||
|
struct strbuf sb = STRBUF_INIT;
|
||||||
|
strbuf_addstr(&sb, path);
|
||||||
|
if (is_nonbare_repository_dir(&sb)) {
|
||||||
|
struct object_id oid;
|
||||||
|
if (resolve_gitlink_ref(path, "HEAD", &oid) < 0)
|
||||||
|
die(_("'%s' does not have a commit checked out"), path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int module_add(int argc, const char **argv, const char *prefix)
|
||||||
|
{
|
||||||
|
int force = 0, quiet = 0, progress = 0, dissociate = 0;
|
||||||
struct add_data add_data = ADD_DATA_INIT;
|
struct add_data add_data = ADD_DATA_INIT;
|
||||||
|
|
||||||
struct option options[] = {
|
struct option options[] = {
|
||||||
OPT_STRING('b', "branch", &add_data.branch,
|
OPT_STRING('b', "branch", &add_data.branch, N_("branch"),
|
||||||
N_("branch"),
|
N_("branch of repository to add as submodule")),
|
||||||
N_("branch of repository to store in "
|
|
||||||
"the submodule configuration")),
|
|
||||||
OPT_STRING(0, "url", &add_data.repo,
|
|
||||||
N_("string"),
|
|
||||||
N_("url to clone submodule from")),
|
|
||||||
OPT_STRING(0, "resolved-url", &add_data.realrepo,
|
|
||||||
N_("string"),
|
|
||||||
N_("url to clone the submodule from, after it has "
|
|
||||||
"been dereferenced relative to parent's url, "
|
|
||||||
"in the case where <url> is a relative url")),
|
|
||||||
OPT_STRING(0, "path", &add_data.sm_path,
|
|
||||||
N_("path"),
|
|
||||||
N_("where the new submodule will be cloned to")),
|
|
||||||
OPT_STRING(0, "name", &add_data.sm_name,
|
|
||||||
N_("string"),
|
|
||||||
N_("name of the new submodule")),
|
|
||||||
OPT__FORCE(&force, N_("allow adding an otherwise ignored submodule path"),
|
OPT__FORCE(&force, N_("allow adding an otherwise ignored submodule path"),
|
||||||
PARSE_OPT_NOCOMPLETE),
|
PARSE_OPT_NOCOMPLETE),
|
||||||
|
OPT__QUIET(&quiet, N_("print only error messages")),
|
||||||
|
OPT_BOOL(0, "progress", &progress, N_("force cloning progress")),
|
||||||
|
OPT_STRING(0, "reference", &add_data.reference_path, N_("repository"),
|
||||||
|
N_("reference repository")),
|
||||||
|
OPT_BOOL(0, "dissociate", &dissociate, N_("borrow the objects from reference repositories")),
|
||||||
|
OPT_STRING(0, "name", &add_data.sm_name, N_("name"),
|
||||||
|
N_("sets the submodule’s name to the given string "
|
||||||
|
"instead of defaulting to its path")),
|
||||||
|
OPT_INTEGER(0, "depth", &add_data.depth, N_("depth for shallow clones")),
|
||||||
OPT_END()
|
OPT_END()
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *const usage[] = {
|
const char *const usage[] = {
|
||||||
N_("git submodule--helper add-config "
|
N_("git submodule--helper add [<options>] [--] <repository> [<path>]"),
|
||||||
"[--force|-f] [--branch|-b <branch>] "
|
|
||||||
"--url <url> --resolved-url <resolved-url> "
|
|
||||||
"--path <path> --name <name>"),
|
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
argc = parse_options(argc, argv, prefix, options, usage, 0);
|
argc = parse_options(argc, argv, prefix, options, usage, 0);
|
||||||
|
|
||||||
if (argc)
|
if (!is_writing_gitmodules_ok())
|
||||||
|
die(_("please make sure that the .gitmodules file is in the working tree"));
|
||||||
|
|
||||||
|
if (prefix && *prefix &&
|
||||||
|
add_data.reference_path && !is_absolute_path(add_data.reference_path))
|
||||||
|
add_data.reference_path = xstrfmt("%s%s", prefix, add_data.reference_path);
|
||||||
|
|
||||||
|
if (argc == 0 || argc > 2)
|
||||||
usage_with_options(usage, options);
|
usage_with_options(usage, options);
|
||||||
|
|
||||||
|
add_data.repo = argv[0];
|
||||||
|
if (argc == 1)
|
||||||
|
add_data.sm_path = git_url_basename(add_data.repo, 0, 0);
|
||||||
|
else
|
||||||
|
add_data.sm_path = xstrdup(argv[1]);
|
||||||
|
|
||||||
|
if (prefix && *prefix && !is_absolute_path(add_data.sm_path))
|
||||||
|
add_data.sm_path = xstrfmt("%s%s", prefix, add_data.sm_path);
|
||||||
|
|
||||||
|
if (starts_with_dot_dot_slash(add_data.repo) ||
|
||||||
|
starts_with_dot_slash(add_data.repo)) {
|
||||||
|
if (prefix)
|
||||||
|
die(_("Relative path can only be used from the toplevel "
|
||||||
|
"of the working tree"));
|
||||||
|
|
||||||
|
/* dereference source url relative to parent's url */
|
||||||
|
add_data.realrepo = resolve_relative_url(add_data.repo, NULL, 1);
|
||||||
|
} else if (is_dir_sep(add_data.repo[0]) || strchr(add_data.repo, ':')) {
|
||||||
|
add_data.realrepo = add_data.repo;
|
||||||
|
} else {
|
||||||
|
die(_("repo URL: '%s' must be absolute or begin with ./|../"),
|
||||||
|
add_data.repo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* normalize path:
|
||||||
|
* multiple //; leading ./; /./; /../;
|
||||||
|
*/
|
||||||
|
normalize_path_copy(add_data.sm_path, add_data.sm_path);
|
||||||
|
strip_dir_trailing_slashes(add_data.sm_path);
|
||||||
|
|
||||||
|
die_on_index_match(add_data.sm_path, force);
|
||||||
|
die_on_repo_without_commits(add_data.sm_path);
|
||||||
|
|
||||||
|
if (!force) {
|
||||||
|
int exit_code = -1;
|
||||||
|
struct strbuf sb = STRBUF_INIT;
|
||||||
|
struct child_process cp = CHILD_PROCESS_INIT;
|
||||||
|
cp.git_cmd = 1;
|
||||||
|
cp.no_stdout = 1;
|
||||||
|
strvec_pushl(&cp.args, "add", "--dry-run", "--ignore-missing",
|
||||||
|
"--no-warn-embedded-repo", add_data.sm_path, NULL);
|
||||||
|
if ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {
|
||||||
|
strbuf_complete_line(&sb);
|
||||||
|
fputs(sb.buf, stderr);
|
||||||
|
free(add_data.sm_path);
|
||||||
|
return exit_code;
|
||||||
|
}
|
||||||
|
strbuf_release(&sb);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!add_data.sm_name)
|
||||||
|
add_data.sm_name = add_data.sm_path;
|
||||||
|
|
||||||
|
if (check_submodule_name(add_data.sm_name))
|
||||||
|
die(_("'%s' is not a valid submodule name"), add_data.sm_name);
|
||||||
|
|
||||||
|
add_data.prefix = prefix;
|
||||||
add_data.force = !!force;
|
add_data.force = !!force;
|
||||||
|
add_data.quiet = !!quiet;
|
||||||
|
add_data.progress = !!progress;
|
||||||
|
add_data.dissociate = !!dissociate;
|
||||||
|
|
||||||
|
if (add_submodule(&add_data)) {
|
||||||
|
free(add_data.sm_path);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
configure_added_submodule(&add_data);
|
configure_added_submodule(&add_data);
|
||||||
|
free(add_data.sm_path);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -3076,13 +3095,11 @@ static struct cmd_struct commands[] = {
|
|||||||
{"list", module_list, 0},
|
{"list", module_list, 0},
|
||||||
{"name", module_name, 0},
|
{"name", module_name, 0},
|
||||||
{"clone", module_clone, 0},
|
{"clone", module_clone, 0},
|
||||||
{"add-clone", add_clone, 0},
|
{"add", module_add, SUPPORT_SUPER_PREFIX},
|
||||||
{"add-config", add_config, 0},
|
|
||||||
{"update-module-mode", module_update_module_mode, 0},
|
{"update-module-mode", module_update_module_mode, 0},
|
||||||
{"update-clone", update_clone, 0},
|
{"update-clone", update_clone, 0},
|
||||||
{"ensure-core-worktree", ensure_core_worktree, 0},
|
{"ensure-core-worktree", ensure_core_worktree, 0},
|
||||||
{"relative-path", resolve_relative_path, 0},
|
{"relative-path", resolve_relative_path, 0},
|
||||||
{"resolve-relative-url", resolve_relative_url, 0},
|
|
||||||
{"resolve-relative-url-test", resolve_relative_url_test, 0},
|
{"resolve-relative-url-test", resolve_relative_url_test, 0},
|
||||||
{"foreach", module_foreach, SUPPORT_SUPER_PREFIX},
|
{"foreach", module_foreach, SUPPORT_SUPER_PREFIX},
|
||||||
{"init", module_init, SUPPORT_SUPER_PREFIX},
|
{"init", module_init, SUPPORT_SUPER_PREFIX},
|
||||||
|
114
dir.c
114
dir.c
@ -2970,6 +2970,120 @@ int is_empty_dir(const char *path)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *git_url_basename(const char *repo, int is_bundle, int is_bare)
|
||||||
|
{
|
||||||
|
const char *end = repo + strlen(repo), *start, *ptr;
|
||||||
|
size_t len;
|
||||||
|
char *dir;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Skip scheme.
|
||||||
|
*/
|
||||||
|
start = strstr(repo, "://");
|
||||||
|
if (start == NULL)
|
||||||
|
start = repo;
|
||||||
|
else
|
||||||
|
start += 3;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Skip authentication data. The stripping does happen
|
||||||
|
* greedily, such that we strip up to the last '@' inside
|
||||||
|
* the host part.
|
||||||
|
*/
|
||||||
|
for (ptr = start; ptr < end && !is_dir_sep(*ptr); ptr++) {
|
||||||
|
if (*ptr == '@')
|
||||||
|
start = ptr + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Strip trailing spaces, slashes and /.git
|
||||||
|
*/
|
||||||
|
while (start < end && (is_dir_sep(end[-1]) || isspace(end[-1])))
|
||||||
|
end--;
|
||||||
|
if (end - start > 5 && is_dir_sep(end[-5]) &&
|
||||||
|
!strncmp(end - 4, ".git", 4)) {
|
||||||
|
end -= 5;
|
||||||
|
while (start < end && is_dir_sep(end[-1]))
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Strip trailing port number if we've got only a
|
||||||
|
* hostname (that is, there is no dir separator but a
|
||||||
|
* colon). This check is required such that we do not
|
||||||
|
* strip URI's like '/foo/bar:2222.git', which should
|
||||||
|
* result in a dir '2222' being guessed due to backwards
|
||||||
|
* compatibility.
|
||||||
|
*/
|
||||||
|
if (memchr(start, '/', end - start) == NULL
|
||||||
|
&& memchr(start, ':', end - start) != NULL) {
|
||||||
|
ptr = end;
|
||||||
|
while (start < ptr && isdigit(ptr[-1]) && ptr[-1] != ':')
|
||||||
|
ptr--;
|
||||||
|
if (start < ptr && ptr[-1] == ':')
|
||||||
|
end = ptr - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find last component. To remain backwards compatible we
|
||||||
|
* also regard colons as path separators, such that
|
||||||
|
* cloning a repository 'foo:bar.git' would result in a
|
||||||
|
* directory 'bar' being guessed.
|
||||||
|
*/
|
||||||
|
ptr = end;
|
||||||
|
while (start < ptr && !is_dir_sep(ptr[-1]) && ptr[-1] != ':')
|
||||||
|
ptr--;
|
||||||
|
start = ptr;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Strip .{bundle,git}.
|
||||||
|
*/
|
||||||
|
len = end - start;
|
||||||
|
strip_suffix_mem(start, &len, is_bundle ? ".bundle" : ".git");
|
||||||
|
|
||||||
|
if (!len || (len == 1 && *start == '/'))
|
||||||
|
die(_("No directory name could be guessed.\n"
|
||||||
|
"Please specify a directory on the command line"));
|
||||||
|
|
||||||
|
if (is_bare)
|
||||||
|
dir = xstrfmt("%.*s.git", (int)len, start);
|
||||||
|
else
|
||||||
|
dir = xstrndup(start, len);
|
||||||
|
/*
|
||||||
|
* Replace sequences of 'control' characters and whitespace
|
||||||
|
* with one ascii space, remove leading and trailing spaces.
|
||||||
|
*/
|
||||||
|
if (*dir) {
|
||||||
|
char *out = dir;
|
||||||
|
int prev_space = 1 /* strip leading whitespace */;
|
||||||
|
for (end = dir; *end; ++end) {
|
||||||
|
char ch = *end;
|
||||||
|
if ((unsigned char)ch < '\x20')
|
||||||
|
ch = '\x20';
|
||||||
|
if (isspace(ch)) {
|
||||||
|
if (prev_space)
|
||||||
|
continue;
|
||||||
|
prev_space = 1;
|
||||||
|
} else
|
||||||
|
prev_space = 0;
|
||||||
|
*out++ = ch;
|
||||||
|
}
|
||||||
|
*out = '\0';
|
||||||
|
if (out > dir && prev_space)
|
||||||
|
out[-1] = '\0';
|
||||||
|
}
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
void strip_dir_trailing_slashes(char *dir)
|
||||||
|
{
|
||||||
|
char *end = dir + strlen(dir);
|
||||||
|
|
||||||
|
while (dir < end - 1 && is_dir_sep(end[-1]))
|
||||||
|
end--;
|
||||||
|
*end = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)
|
static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)
|
||||||
{
|
{
|
||||||
DIR *dir;
|
DIR *dir;
|
||||||
|
11
dir.h
11
dir.h
@ -453,6 +453,17 @@ static inline int is_dot_or_dotdot(const char *name)
|
|||||||
|
|
||||||
int is_empty_dir(const char *dir);
|
int is_empty_dir(const char *dir);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Retrieve the "humanish" basename of the given Git URL.
|
||||||
|
*
|
||||||
|
* For example:
|
||||||
|
* /path/to/repo.git => "repo"
|
||||||
|
* host.xz:foo/.git => "foo"
|
||||||
|
* http://example.com/user/bar.baz => "bar.baz"
|
||||||
|
*/
|
||||||
|
char *git_url_basename(const char *repo, int is_bundle, int is_bare);
|
||||||
|
void strip_dir_trailing_slashes(char *dir);
|
||||||
|
|
||||||
void setup_standard_excludes(struct dir_struct *dir);
|
void setup_standard_excludes(struct dir_struct *dir);
|
||||||
|
|
||||||
char *get_sparse_checkout_filename(void);
|
char *get_sparse_checkout_filename(void);
|
||||||
|
@ -145,104 +145,12 @@ cmd_add()
|
|||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
|
|
||||||
if ! git submodule--helper config --check-writeable >/dev/null 2>&1
|
if test -z "$1"
|
||||||
then
|
then
|
||||||
die "fatal: $(eval_gettext "please make sure that the .gitmodules file is in the working tree")"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test -n "$reference_path"
|
|
||||||
then
|
|
||||||
is_absolute_path "$reference_path" ||
|
|
||||||
reference_path="$wt_prefix$reference_path"
|
|
||||||
|
|
||||||
reference="--reference=$reference_path"
|
|
||||||
fi
|
|
||||||
|
|
||||||
repo=$1
|
|
||||||
sm_path=$2
|
|
||||||
|
|
||||||
if test -z "$sm_path"; then
|
|
||||||
sm_path=$(printf '%s\n' "$repo" |
|
|
||||||
sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test -z "$repo" || test -z "$sm_path"; then
|
|
||||||
usage
|
usage
|
||||||
fi
|
fi
|
||||||
|
|
||||||
is_absolute_path "$sm_path" || sm_path="$wt_prefix$sm_path"
|
git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper add ${GIT_QUIET:+--quiet} ${force:+--force} ${progress:+"--progress"} ${branch:+--branch "$branch"} ${reference_path:+--reference "$reference_path"} ${dissociate:+--dissociate} ${custom_name:+--name "$custom_name"} ${depth:+"$depth"} -- "$@"
|
||||||
|
|
||||||
# assure repo is absolute or relative to parent
|
|
||||||
case "$repo" in
|
|
||||||
./*|../*)
|
|
||||||
test -z "$wt_prefix" ||
|
|
||||||
die "fatal: $(gettext "Relative path can only be used from the toplevel of the working tree")"
|
|
||||||
|
|
||||||
# dereference source url relative to parent's url
|
|
||||||
realrepo=$(git submodule--helper resolve-relative-url "$repo") || exit
|
|
||||||
;;
|
|
||||||
*:*|/*)
|
|
||||||
# absolute url
|
|
||||||
realrepo=$repo
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
die "fatal: $(eval_gettext "repo URL: '\$repo' must be absolute or begin with ./|../")"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# normalize path:
|
|
||||||
# multiple //; leading ./; /./; /../; trailing /
|
|
||||||
sm_path=$(printf '%s/\n' "$sm_path" |
|
|
||||||
sed -e '
|
|
||||||
s|//*|/|g
|
|
||||||
s|^\(\./\)*||
|
|
||||||
s|/\(\./\)*|/|g
|
|
||||||
:start
|
|
||||||
s|\([^/]*\)/\.\./||
|
|
||||||
tstart
|
|
||||||
s|/*$||
|
|
||||||
')
|
|
||||||
if test -z "$force"
|
|
||||||
then
|
|
||||||
git ls-files --error-unmatch "$sm_path" > /dev/null 2>&1 &&
|
|
||||||
die "fatal: $(eval_gettext "'\$sm_path' already exists in the index")"
|
|
||||||
else
|
|
||||||
git ls-files -s "$sm_path" | sane_grep -v "^160000" > /dev/null 2>&1 &&
|
|
||||||
die "fatal: $(eval_gettext "'\$sm_path' already exists in the index and is not a submodule")"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test -d "$sm_path" &&
|
|
||||||
test -z $(git -C "$sm_path" rev-parse --show-cdup 2>/dev/null)
|
|
||||||
then
|
|
||||||
git -C "$sm_path" rev-parse --verify -q HEAD >/dev/null ||
|
|
||||||
die "fatal: $(eval_gettext "'\$sm_path' does not have a commit checked out")"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test -z "$force"
|
|
||||||
then
|
|
||||||
dryerr=$(git add --dry-run --ignore-missing --no-warn-embedded-repo "$sm_path" 2>&1 >/dev/null)
|
|
||||||
res=$?
|
|
||||||
if test $res -ne 0
|
|
||||||
then
|
|
||||||
echo >&2 "$dryerr"
|
|
||||||
exit $res
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test -n "$custom_name"
|
|
||||||
then
|
|
||||||
sm_name="$custom_name"
|
|
||||||
else
|
|
||||||
sm_name="$sm_path"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! git submodule--helper check-name "$sm_name"
|
|
||||||
then
|
|
||||||
die "fatal: $(eval_gettext "'$sm_name' is not a valid submodule name")"
|
|
||||||
fi
|
|
||||||
|
|
||||||
git submodule--helper add-clone ${GIT_QUIET:+--quiet} ${force:+"--force"} ${progress:+"--progress"} ${branch:+--branch "$branch"} --prefix "$wt_prefix" --path "$sm_path" --name "$sm_name" --url "$realrepo" ${reference:+"$reference"} ${dissociate:+"--dissociate"} ${depth:+"$depth"} || exit
|
|
||||||
git submodule--helper add-config ${force:+--force} ${branch:+--branch "$branch"} --url "$repo" --resolved-url "$realrepo" --path "$sm_path" --name "$sm_name"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
|
Loading…
x
Reference in New Issue
Block a user