-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The infrastructure to rewrite "git submodule" in C is being built incrementally. Let's polish these early parts well enough and make them graduate to 'next' and 'master', so that the more involved follow-up can start cooking on a solid ground. * sb/submodule-helper: submodule: rewrite `module_clone` shell function in C submodule: rewrite `module_name` shell function in C submodule: rewrite `module_list` shell function in C
- Loading branch information
Showing
6 changed files
with
301 additions
and
149 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,282 @@ | ||
#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>] [--url <url>]" | ||
"[--depth <depth>] [--] [<path>...]"), | ||
NULL | ||
}; | ||
|
||
argc = parse_options(argc, argv, prefix, module_clone_options, | ||
git_submodule_helper_usage, 0); | ||
|
||
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]); | ||
} |
Oops, something went wrong.