@@ -1541,9 +1541,11 @@ struct module_clone_data {
unsigned int dissociate: 1;
unsigned int require_init: 1;
int single_branch;
+ int single_tag;
};
#define MODULE_CLONE_DATA_INIT { \
.single_branch = -1, \
+ .single_tag = -1, \
}
struct submodule_alternate_setup {
@@ -1754,6 +1756,10 @@ static int clone_submodule(const struct module_clone_data *clone_data,
strvec_push(&cp.args, clone_data->single_branch ?
"--single-branch" :
"--no-single-branch");
+ if (clone_data->single_tag >= 0)
+ strvec_push(&cp.args, clone_data->single_tag ?
+ "--single-branch" :
+ "--no-single-branch");
strvec_push(&cp.args, "--");
strvec_push(&cp.args, clone_data->url);
@@ -1981,6 +1987,7 @@ struct update_data {
int depth;
int max_jobs;
int single_branch;
+ int single_tag;
int recommend_shallow;
unsigned int require_init;
unsigned int force;
@@ -2004,6 +2011,7 @@ struct update_data {
.recommend_shallow = -1, \
.references = STRING_LIST_INIT_DUP, \
.single_branch = -1, \
+ .single_tag = -1, \
.max_jobs = 1, \
}
@@ -2151,6 +2159,10 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
strvec_push(&child->args, suc->update_data->single_branch ?
"--single-branch" :
"--no-single-branch");
+ if (suc->update_data->single_tag >= 0)
+ strvec_push(&child->args, suc->update_data->single_tag ?
+ "--single-tag" :
+ "--no-single-tag");
cleanup:
free(displaypath);
@@ -2297,6 +2309,7 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet,
cp.dir = module_path;
strvec_push(&cp.args, "fetch");
+
if (quiet)
strvec_push(&cp.args, "--quiet");
if (depth)
@@ -2434,6 +2447,30 @@ static int run_update_procedure(const struct update_data *ud)
return run_update_command(ud, subforce);
}
+static int remote_submodule_tag(const char *path, const char **tag)
+{
+ const struct submodule *sub;
+ char *key;
+ *tag = NULL;
+
+ sub = submodule_from_path(the_repository, null_oid(), path);
+ if (!sub)
+ return die_message(_("could not initialize submodule at path '%s'"),
+ path);
+
+ key = xstrfmt("submodule.%s.tag", sub->name);
+ if (repo_config_get_string_tmp(the_repository, key, tag))
+ *tag = sub->tag;
+ free(key);
+
+ if (!*tag) {
+ /* No tag found */
+ return 1;
+ }
+
+ return 0;
+}
+
static int remote_submodule_branch(const char *path, const char **branch)
{
const struct submodule *sub;
@@ -2579,6 +2616,10 @@ static void update_data_to_args(const struct update_data *update_data,
strvec_push(args, update_data->single_branch ?
"--single-branch" :
"--no-single-branch");
+ if (update_data->single_tag >= 0)
+ strvec_push(args, update_data->single_tag ?
+ "--single-branch" :
+ "--no-single-branch");
}
static int update_submodule(struct update_data *update_data)
@@ -2606,16 +2647,22 @@ static int update_submodule(struct update_data *update_data)
if (update_data->remote) {
char *remote_name;
const char *branch;
+ const char *tag;
char *remote_ref;
int code;
code = get_default_remote_submodule(update_data->sm_path, &remote_name);
if (code)
return code;
- code = remote_submodule_branch(update_data->sm_path, &branch);
- if (code)
- return code;
- remote_ref = xstrfmt("refs/remotes/%s/%s", remote_name, branch);
+ code = remote_submodule_tag(update_data->sm_path, &tag);
+ if (!code && update_data->single_tag) {
+ remote_ref = xstrfmt("refs/tags/%s", tag);
+ } else {
+ code = remote_submodule_branch(update_data->sm_path, &branch);
+ if (code)
+ return code;
+ remote_ref = xstrfmt("refs/remotes/%s/%s", remote_name, branch);
+ }
free(remote_name);
@@ -2781,6 +2828,8 @@ static int module_update(int argc, const char **argv, const char *prefix)
N_("disallow cloning into non-empty directory, implies --init")),
OPT_BOOL(0, "single-branch", &opt.single_branch,
N_("clone only one branch, HEAD or --branch")),
+ OPT_BOOL(0, "single-tag", &opt.single_tag,
+ N_("clone only one tag, --tag")),
OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
OPT_END()
};
@@ -2790,7 +2839,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
" [-N|--no-fetch] [-f|--force]"
" [--checkout|--merge|--rebase]"
" [--[no-]recommend-shallow] [--reference <repository>]"
- " [--recursive] [--[no-]single-branch] [--] [<path>...]"),
+ " [--recursive] [--[no-]single-branch] [--[no-]single-tag] [--] [<path>...]"),
NULL
};
@@ -3058,6 +3107,52 @@ static int module_set_branch(int argc, const char **argv, const char *prefix)
return !!ret;
}
+
+static int module_set_tag(int argc, const char **argv, const char *prefix)
+{
+ int ret;
+ const char *opt_tag = NULL;
+ const char *path;
+ char *config_name;
+ struct option options[] = {
+ /*
+ * We accept the `quiet` option for uniformity across subcommands,
+ * though there is nothing to make less verbose in this subcommand.
+ */
+ OPT_NOOP_NOARG('q', "quiet"),
+
+ OPT_STRING('t', "tag", &opt_tag, N_("tag"),
+ N_("set the tracking tag")),
+ OPT_END()
+ };
+
+ const char *const usage[] = {
+ N_("git submodule set-tag [-q|--quiet] (-t|--tag) <tag> <path>"),
+ NULL
+ };
+ const struct submodule *sub;
+
+ argc = parse_options(argc, argv, prefix, options, usage, 0);
+
+ if (!opt_tag)
+ die(_("--tag required"));
+
+ if (argc != 1 || !(path = argv[0]))
+ usage_with_options(usage, options);
+
+ sub = submodule_from_path(the_repository, null_oid(), path);
+
+ if (!sub)
+ die(_("no submodule mapping found in .gitmodules for path '%s'"),
+ path);
+
+ config_name = xstrfmt("submodule.%s.tag", sub->tag);
+ ret = config_set_in_gitmodules_file_gently(config_name, opt_tag);
+
+ free(config_name);
+ return !!ret;
+}
+
static int module_create_branch(int argc, const char **argv, const char *prefix)
{
enum branch_track track;
@@ -3098,6 +3193,7 @@ static int module_create_branch(int argc, const char **argv, const char *prefix)
struct add_data {
const char *prefix;
const char *branch;
+ const char *tag;
const char *reference_path;
char *sm_path;
const char *sm_name;
@@ -3219,7 +3315,9 @@ static int add_submodule(const struct add_data *add_data)
*/
strvec_pushl(&cp.args, "checkout", "-f", "-q", NULL);
- if (add_data->branch) {
+ if (add_data->tag) {
+ strvec_pushf(&cp.args, "%s", add_data->tag);
+ } else if (add_data->branch) {
strvec_pushl(&cp.args, "-B", add_data->branch, NULL);
strvec_pushf(&cp.args, "origin/%s", add_data->branch);
}
@@ -3272,7 +3370,11 @@ static void configure_added_submodule(struct add_data *add_data)
config_submodule_in_gitmodules(add_data->sm_name, "url", add_data->repo))
die(_("Failed to register submodule '%s'"), add_data->sm_path);
- if (add_data->branch) {
+ if (add_data->tag) {
+ if (config_submodule_in_gitmodules(add_data->sm_name,
+ "tag", add_data->tag))
+ die(_("Failed to register submodule '%s'"), add_data->sm_path);
+ } else if (add_data->branch) {
if (config_submodule_in_gitmodules(add_data->sm_name,
"branch", add_data->branch))
die(_("Failed to register submodule '%s'"), add_data->sm_path);
@@ -3371,6 +3473,8 @@ static int module_add(int argc, const char **argv, const char *prefix)
struct option options[] = {
OPT_STRING('b', "branch", &add_data.branch, N_("branch"),
N_("branch of repository to add as submodule")),
+ OPT_STRING('t', "tag", &add_data.tag, N_("tag"),
+ N_("tag of repository to add as submodule")),
OPT__FORCE(&force, N_("allow adding an otherwise ignored submodule path"),
PARSE_OPT_NOCOMPLETE),
OPT__QUIET(&quiet, N_("print only error messages")),
@@ -3506,6 +3610,7 @@ int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
OPT_SUBCOMMAND("set-url", &fn, module_set_url),
OPT_SUBCOMMAND("set-branch", &fn, module_set_branch),
OPT_SUBCOMMAND("create-branch", &fn, module_create_branch),
+ OPT_SUBCOMMAND("set-tag", &fn, module_set_tag),
OPT_END()
};
argc = parse_options(argc, argv, prefix, options, usage, 0);
@@ -3057,6 +3057,9 @@ _git_remote ()
set-head,--*)
__gitcomp_builtin remote_set-head
;;
+ set-tag,--*)
+ __gitcomp_builtin remote_set-tag
+ ;;
set-branches,--*)
__gitcomp_builtin remote_set-branches
;;
@@ -3471,7 +3474,7 @@ _git_submodule ()
{
__git_has_doubledash && return
- local subcommands="add status init deinit update set-branch set-url summary foreach sync absorbgitdirs"
+ local subcommands="add status init deinit update set-branch set-tag set-url summary foreach sync absorbgitdirs"
local subcommand="$(__git_find_on_cmdline "$subcommands")"
if [ -z "$subcommand" ]; then
case "$cur" in
@@ -3502,6 +3505,9 @@ _git_submodule ()
--force --rebase --merge --reference --depth --recursive --jobs
"
;;
+ set-tag,--*)
+ __gitcomp "--tag"
+ ;;
set-branch,--*)
__gitcomp "--default --branch"
;;
@@ -6,12 +6,13 @@
dashless=$(basename "$0" | sed -e 's/-/ /')
USAGE="[--quiet] [--cached]
- or: $dashless [--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <repository>] [--] <repository> [<path>]
+ or: $dashless [--quiet] add [-b <branch>] [-t <tag>] [-f|--force] [--name <name>] [--reference <repository>] [--] <repository> [<path>]
or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...]
or: $dashless [--quiet] init [--] [<path>...]
or: $dashless [--quiet] deinit [-f|--force] (--all| [--] <path>...)
- or: $dashless [--quiet] update [--init [--filter=<filter-spec>]] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--[no-]recommend-shallow] [--reference <repository>] [--recursive] [--[no-]single-branch] [--] [<path>...]
+ or: $dashless [--quiet] update [--init [--filter=<filter-spec>]] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--[no-]recommend-shallow] [--reference <repository>] [--recursive] [--[no-]single-branch] [--[no-]single-tag] [--] [<path>...]
or: $dashless [--quiet] set-branch (--default|--branch <branch>) [--] <path>
+ or: $dashless [--quiet] set-tag (--tag <tag>) [--] <path>
or: $dashless [--quiet] set-url [--] <path> <newurl>
or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
or: $dashless [--quiet] foreach [--recursive] <command>
@@ -49,6 +50,7 @@ depth=
progress=
dissociate=
single_branch=
+single_tag=
jobs=
recommend_shallow=
filter=
@@ -77,6 +79,11 @@ cmd_add()
branch=$2
shift
;;
+ -t | --tag)
+ case "$2" in '') usage ;; esac
+ tag=$2
+ shift
+ ;;
-f | --force)
force=$1
;;
@@ -129,7 +136,7 @@ cmd_add()
usage
fi
- git ${wt_prefix:+-C "$wt_prefix"} submodule--helper add ${quiet:+--quiet} ${force:+--force} ${progress:+"--progress"} ${branch:+--branch "$branch"} ${reference_path:+--reference "$reference_path"} ${dissociate:+--dissociate} ${custom_name:+--name "$custom_name"} ${depth:+"$depth"} -- "$@"
+ git ${wt_prefix:+-C "$wt_prefix"} submodule--helper add ${quiet:+--quiet} ${force:+--force} ${progress:+"--progress"} ${branch:+--branch "$branch"} ${tag:+--tag "$tag"} ${reference_path:+--reference "$reference_path"} ${dissociate:+--dissociate} ${custom_name:+--name "$custom_name"} ${depth:+"$depth"} -- "$@"
}
#
@@ -316,6 +323,12 @@ cmd_update()
--no-single-branch)
single_branch="--no-single-branch"
;;
+ --single-tag)
+ single_tag="--single-tag"
+ ;;
+ --no-single-tag)
+ single_tag="--no-single-tag"
+ ;;
--filter)
case "$2" in '') usage ;; esac
filter="--filter=$2"
@@ -355,6 +368,7 @@ cmd_update()
${require_init:+--require-init} \
${dissociate:+"--dissociate"} \
$single_branch \
+ $single_tag \
$recommend_shallow \
$jobs \
$filter \
@@ -402,6 +416,43 @@ cmd_set_branch() {
git ${wt_prefix:+-C "$wt_prefix"} submodule--helper set-branch ${quiet:+--quiet} ${branch:+--branch "$branch"} ${default:+--default} -- "$@"
}
+#
+# Configures a submodule's default tag
+#
+# $@ = requested path
+#
+cmd_set_tag() {
+ default=
+ tag=
+
+ while test $# -ne 0
+ do
+ case "$1" in
+ -q|--quiet)
+ # we don't do anything with this but we need to accept it
+ ;;
+ -t|--tag)
+ case "$2" in '') usage ;; esac
+ tag=$2
+ shift
+ ;;
+ --)
+ shift
+ break
+ ;;
+ -*)
+ usage
+ ;;
+ *)
+ break
+ ;;
+ esac
+ shift
+ done
+
+ git ${wt_prefix:+-C "$wt_prefix"} submodule--helper set-tag ${quiet:+--quiet} ${tag:+--tag "$tag"} -- "$@"
+}
+
#
# Configures a submodule's remote url
#
@@ -571,7 +622,7 @@ cmd_absorbgitdirs()
while test $# != 0 && test -z "$command"
do
case "$1" in
- add | foreach | init | deinit | update | set-branch | set-url | status | summary | sync | absorbgitdirs)
+ add | foreach | init | deinit | update | set-branch | set-tag | set-url | status | summary | sync | absorbgitdirs)
command=$1
;;
-q|--quiet)
@@ -93,6 +93,7 @@ static void free_one_config(struct submodule_entry *entry)
free((void *) entry->config->branch);
free((void *) entry->config->url);
free((void *) entry->config->ignore);
+ free((void *) entry->config->tag);
free((void *) entry->config->update_strategy.command);
free(entry->config);
}
@@ -415,6 +416,7 @@ static struct submodule *lookup_or_create_by_name(struct submodule_cache *cache,
submodule->fetch_recurse = RECURSE_SUBMODULES_NONE;
submodule->ignore = NULL;
submodule->branch = NULL;
+ submodule->tag = NULL;
submodule->recommend_shallow = -1;
oidcpy(&submodule->gitmodules_oid, gitmodules_oid);
@@ -660,6 +662,8 @@ static int parse_config(const char *var, const char *value,
} else if (!strcmp(item.buf, "branch")) {
if (!value)
ret = config_error_nonbool(var);
+ else if (submodule->branch && submodule->tag)
+ die(_("can not specify both tag and branch '%s'"), var);
else if (!me->overwrite && submodule->branch)
warn_multiple_config(me->treeish_name, submodule->name,
"branch");
@@ -667,6 +671,16 @@ static int parse_config(const char *var, const char *value,
free((void *)submodule->branch);
submodule->branch = xstrdup(value);
}
+ } else if (!strcmp(item.buf, "tag")) {
+ if (!value)
+ ret = config_error_nonbool(var);
+ else if (!me->overwrite && submodule->tag)
+ warn_multiple_config(me->treeish_name, submodule->name,
+ "tag");
+ else {
+ free((void *)submodule->tag);
+ submodule->tag = xstrdup(value);
+ }
}
strbuf_release(&name);
@@ -38,6 +38,7 @@ struct submodule {
enum submodule_recurse_mode fetch_recurse;
const char *ignore;
const char *branch;
+ const char *tag;
struct submodule_update_strategy update_strategy;
/* the object id of the responsible .gitmodules file */
struct object_id gitmodules_oid;