diff mbox series

[v3] submodule: port subcommand 'set-branch' from shell to C

Message ID 20200521163819.12544-1-shouryashukla.oo@gmail.com (mailing list archive)
State New, archived
Headers show
Series [v3] submodule: port subcommand 'set-branch' from shell to C | expand

Commit Message

Shourya Shukla May 21, 2020, 4:38 p.m. UTC
Convert submodule subcommand 'set-branch' to a builtin and call it via
'git-submodule.sh'.

Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Mentored-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>
Helped-by: Denton Liu <liu.denton@gmail.com>
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Shourya Shukla <shouryashukla.oo@gmail.com>
---
Thank you for the review Eric. I have changed the commit message,
and the error prompts. Also, I have added a brief comment about
the `quiet` option.

 builtin/submodule--helper.c | 45 +++++++++++++++++++++++++++++++++++++
 git-submodule.sh            | 32 +++-----------------------
 2 files changed, 48 insertions(+), 29 deletions(-)

Comments

Junio C Hamano May 21, 2020, 6:44 p.m. UTC | #1
Shourya Shukla <shouryashukla.oo@gmail.com> writes:

> Convert submodule subcommand 'set-branch' to a builtin and call it via
> 'git-submodule.sh'.
>
> Mentored-by: Christian Couder <chriscool@tuxfamily.org>
> Mentored-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>
> Helped-by: Denton Liu <liu.denton@gmail.com>
> Helped-by: Eric Sunshine <sunshine@sunshineco.com>
> Signed-off-by: Shourya Shukla <shouryashukla.oo@gmail.com>
> ---
> Thank you for the review Eric. I have changed the commit message,
> and the error prompts. Also, I have added a brief comment about
> the `quiet` option.

Sorry, I may have missed the previous rounds of discussion, but the
comment adds more puzzles than it helps readers.  "is currently not
used" can be seen from the code, but it is totally unclear why it is
not used.  Is that a design decision to always keep quiet or always
talkative (if so, "suppress output..." is not a good description)?
Is that that this is a WIP patch that the behaviour the option aims
to achieve hasn't been implemented?  Is it that no existing callers
pass "-q" to the scripted version, so there is no need to support
it (if so, why do we even accept it in the first place)?  Is it that
all existing callers pass "-q" so we need to accept it, but there is
nothing we need to make verbose so the variable is not passed around
in the codepath?
Denton Liu May 21, 2020, 7:03 p.m. UTC | #2
On Thu, May 21, 2020 at 11:44:22AM -0700, Junio C Hamano wrote:
> Shourya Shukla <shouryashukla.oo@gmail.com> writes:
> 
> > Convert submodule subcommand 'set-branch' to a builtin and call it via
> > 'git-submodule.sh'.
> >
> > Mentored-by: Christian Couder <chriscool@tuxfamily.org>
> > Mentored-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>
> > Helped-by: Denton Liu <liu.denton@gmail.com>
> > Helped-by: Eric Sunshine <sunshine@sunshineco.com>
> > Signed-off-by: Shourya Shukla <shouryashukla.oo@gmail.com>
> > ---
> > Thank you for the review Eric. I have changed the commit message,
> > and the error prompts. Also, I have added a brief comment about
> > the `quiet` option.
> 
> Sorry, I may have missed the previous rounds of discussion, but the
> comment adds more puzzles than it helps readers.  "is currently not
> used" can be seen from the code, but it is totally unclear why it is
> not used.  Is that a design decision to always keep quiet or always
> talkative (if so, "suppress output..." is not a good description)?
> Is that that this is a WIP patch that the behaviour the option aims
> to achieve hasn't been implemented?  Is it that no existing callers
> pass "-q" to the scripted version, so there is no need to support
> it (if so, why do we even accept it in the first place)?  Is it that
> all existing callers pass "-q" so we need to accept it, but there is
> nothing we need to make verbose so the variable is not passed around
> in the codepath?

As the original author of the shell code, I had it accept -q because,
with the other subcommmands, you can pass -q either before or after the
subcommand such as

	$ git submodule -q sync

or
	$ git submodule sync -q

and I wanted set-branch to retain that behaviour even though -q
ultimately doesn't affect set-branch at all since it's already a quiet
command.

Perhaps as a follow-up to this patch, we could stop accepting -q in
set-branch. I highly doubt that anyone is using it anyway.
Junio C Hamano May 21, 2020, 7:50 p.m. UTC | #3
Denton Liu <liu.denton@gmail.com> writes:

>> Sorry, I may have missed the previous rounds of discussion, but the
>> comment adds more puzzles than it helps readers.  "is currently not
>> used" can be seen from the code, but it is totally unclear why it is
>> not used.  Is that a design decision to always keep quiet or always
>> talkative (if so, "suppress output..." is not a good description)?
>> Is that that this is a WIP patch that the behaviour the option aims
>> to achieve hasn't been implemented?  Is it that no existing callers
>> pass "-q" to the scripted version, so there is no need to support
>> it (if so, why do we even accept it in the first place)?  Is it that
>> all existing callers pass "-q" so we need to accept it, but there is
>> nothing we need to make verbose so the variable is not passed around
>> in the codepath?
>
> As the original author of the shell code, I had it accept -q because,
> with the other subcommmands, you can pass -q either before or after the
> subcommand such as
>
> 	$ git submodule -q sync
>
> or
> 	$ git submodule sync -q
>
> and I wanted set-branch to retain that behaviour even though -q
> ultimately doesn't affect set-branch at all since it's already a quiet
> command.

OK, so "we accept -q for uniformity across subcommands, but there is
nothing to make less verbose in this subcommand" is the answer to my
question.

That cannot be read from "... is currently not used"; especially
with "currently", I expect that most readers would expect we would
start using it in the (near) future, and some other readers would
guess that something used to be talkative and we squelched it using
the option but there no longer is such need because that something
is now quiet by default and there is no option to make it talkative.
Đoàn Trần Công Danh May 21, 2020, 11:04 p.m. UTC | #4
Hi Shourya,

On 2020-05-21 22:08:19+0530, Shourya Shukla <shouryashukla.oo@gmail.com> wrote:
> Thank you for the review Eric. I have changed the commit message,
> and the error prompts. Also, I have added a brief comment about
> the `quiet` option.
> 
> +	/*
> +	 * The `quiet` option is present for backward compatibility
> +	 * but is currently not used.
> +	 */
> +	int quiet = 0, opt_default = 0;
> +	const char *opt_branch = NULL;
> +	const char *path;
> +	char *config_name;
> +
> +	struct option options[] = {
> +		OPT__QUIET(&quiet,
> +			N_("suppress output for setting default tracking branch")),

IIUC, this option is provided to be backward compatible with old shell
version, and this option doesn't affect anything.

Would it make sense to hide quiet from default usage, via:

	OPT_NOOP_NOARG(0, "quiet")

I may missed some discussion related to the decision to keep it
OPT__QUIET.

> +		OPT_BOOL(0, "default", &opt_default,
> +			N_("set the default tracking branch to master")),
> +		OPT_STRING(0, "branch", &opt_branch, N_("branch"),
> +			N_("set the default tracking branch")),
> +		OPT_END()
> +	};
> +	const char *const usage[] = {
> +		N_("git submodule--helper set-branch [--quiet] (-d|--default) <path>"),
> +		N_("git submodule--helper set-branch [--quiet] (-b|--branch) <branch> <path>"),

And if above comment is applicable, remove `--quiet` from here.

> +	git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper set-branch ${GIT_QUIET:+--quiet} ${branch:+--branch $branch} ${default:+--default} -- "$@"

I think we need to quote `$branch`, no?

	${branch:+--branch "$branch"}
Shourya Shukla May 22, 2020, 7:39 p.m. UTC | #5
On 21/05 12:50, Junio C Hamano wrote:
> OK, so "we accept -q for uniformity across subcommands, but there is
> nothing to make less verbose in this subcommand" is the answer to my
> question.
> 
> That cannot be read from "... is currently not used"; especially
> with "currently", I expect that most readers would expect we would
> start using it in the (near) future, and some other readers would
> guess that something used to be talkative and we squelched it using
> the option but there no longer is such need because that something
> is now quiet by default and there is no option to make it talkative.

What do you think should be the most apt comment here? Also, the rest of
the code is fine right?
Johannes Schindelin May 22, 2020, 10:21 p.m. UTC | #6
Hi Shourya,

On Thu, 21 May 2020, Shourya Shukla wrote:

> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index f50745a03f..d14b9856a3 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -2284,6 +2284,50 @@ static int module_set_url(int argc, const char **argv, const char *prefix)
>  	return 0;
>  }
>
> +static int module_set_branch(int argc, const char **argv, const char *prefix)
> +{
> +	/*
> +	 * The `quiet` option is present for backward compatibility
> +	 * but is currently not used.
> +	 */
> +	int quiet = 0, opt_default = 0;
> +	const char *opt_branch = NULL;
> +	const char *path;
> +	char *config_name;
> +
> +	struct option options[] = {
> +		OPT__QUIET(&quiet,
> +			N_("suppress output for setting default tracking branch")),
> +		OPT_BOOL(0, "default", &opt_default,
> +			N_("set the default tracking branch to master")),
> +		OPT_STRING(0, "branch", &opt_branch, N_("branch"),
> +			N_("set the default tracking branch")),
> +		OPT_END()
> +	};
> +	const char *const usage[] = {
> +		N_("git submodule--helper set-branch [--quiet] (-d|--default) <path>"),
> +		N_("git submodule--helper set-branch [--quiet] (-b|--branch) <branch> <path>"),
> +		NULL
> +	};
> +
> +	argc = parse_options(argc, argv, prefix, options, usage, 0);
> +
> +	if (!opt_branch && !opt_default)
> +		die(_("--branch or --default required"));
> +
> +	if (opt_branch && opt_default)
> +		die(_("--branch and --default are mutually exclusive"));
> +
> +	if (argc != 1 || !(path = argv[0]))
> +		usage_with_options(usage, options);
> +
> +	config_name = xstrfmt("submodule.%s.branch", path);
> +	config_set_in_gitmodules_file_gently(config_name, opt_branch);

What happens if this fails? E.g. when the permission is denied or disk is
full? This C code would then still `return 0`, pretending that it
succeeded. But the original shell script calls `git submodule--helper
config [...]` which calls `module_config()`, which in turn passes through
the return value of the `config_set_in_gitmodules_file_gently()` call.

In other words, you need something like this:

	int ret;

	[...]

	ret = config_set_in_gitmodules_file_gently(config_name, opt_branch);

	free(config_name);
	return ret;

> +
> +	free(config_name);
> +	return 0;
> +}
> +
>  #define SUPPORT_SUPER_PREFIX (1<<0)
>
>  struct cmd_struct {
> @@ -2315,6 +2359,7 @@ static struct cmd_struct commands[] = {
>  	{"check-name", check_name, 0},
>  	{"config", module_config, 0},
>  	{"set-url", module_set_url, 0},
> +	{"set-branch", module_set_branch, 0},

BTW I just noticed that the return value of these helpers is returned by
the `cmd_submodule__helper()` function. That is not correct, as the
convention is for Git's functions to return negative values in case of
errors _except_ for `cmd_*()` functions, which need to return an exit code
(valid values are between 0 and 127).

So I think we'll also need this (it's unrelated to your patch, at least
unrelated enough that it merits its own, separate patch):

-                       return commands[i].fn(argc - 1, argv + 1, prefix);
+                       return !!commands[i].fn(argc - 1, argv + 1, prefix);

Ciao,
Dscho

>  };
>
>  int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
> diff --git a/git-submodule.sh b/git-submodule.sh
> index 39ebdf25b5..8c56191f77 100755
> --- a/git-submodule.sh
> +++ b/git-submodule.sh
> @@ -719,7 +719,7 @@ cmd_update()
>  # $@ = requested path
>  #
>  cmd_set_branch() {
> -	unset_branch=false
> +	default=
>  	branch=
>
>  	while test $# -ne 0
> @@ -729,7 +729,7 @@ cmd_set_branch() {
>  			# we don't do anything with this but we need to accept it
>  			;;
>  		-d|--default)
> -			unset_branch=true
> +			default=1
>  			;;
>  		-b|--branch)
>  			case "$2" in '') usage ;; esac
> @@ -750,33 +750,7 @@ cmd_set_branch() {
>  		shift
>  	done
>
> -	if test $# -ne 1
> -	then
> -		usage
> -	fi
> -
> -	# we can't use `git submodule--helper name` here because internally, it
> -	# hashes the path so a trailing slash could lead to an unintentional no match
> -	name="$(git submodule--helper list "$1" | cut -f2)"
> -	if test -z "$name"
> -	then
> -		exit 1
> -	fi
> -
> -	test -n "$branch"; has_branch=$?
> -	test "$unset_branch" = true; has_unset_branch=$?
> -
> -	if test $((!$has_branch != !$has_unset_branch)) -eq 0
> -	then
> -		usage
> -	fi
> -
> -	if test $has_branch -eq 0
> -	then
> -		git submodule--helper config submodule."$name".branch "$branch"
> -	else
> -		git submodule--helper config --unset submodule."$name".branch
> -	fi
> +	git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper set-branch ${GIT_QUIET:+--quiet} ${branch:+--branch $branch} ${default:+--default} -- "$@"
>  }
>
>  #
> --
> 2.26.2
>
>
Junio C Hamano May 24, 2020, 4:07 p.m. UTC | #7
Shourya Shukla <shouryashukla.oo@gmail.com> writes:

> On 21/05 12:50, Junio C Hamano wrote:
>> OK, so "we accept -q for uniformity across subcommands, but there is
>> nothing to make less verbose in this subcommand" is the answer to my
>> question.
>> 
>> That cannot be read from "... is currently not used"; especially
>> with "currently", I expect that most readers would expect we would
>> start using it in the (near) future, and some other readers would
>> guess that something used to be talkative and we squelched it using
>> the option but there no longer is such need because that something
>> is now quiet by default and there is no option to make it talkative.
>
> What do you think should be the most apt comment here?

"we accept -q for uniformity across subcommands, but there is nothing
to make less verbose in this subcommand", perhaps?

> Also, the rest of the code is fine right?

I didn't spot anything bad worth pointing out when I sent the review
message, but that does not necessarily mean the code is "fine" ;-) 

I see you have v4 sent out already, which probably has more
improvements based on others' input.  Thanks for working on this
topic.
Junio C Hamano May 24, 2020, 11:15 p.m. UTC | #8
Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

>> +	config_set_in_gitmodules_file_gently(config_name, opt_branch);
>
> What happens if this fails? E.g. when the permission is denied or disk is
> full? This C code would then still `return 0`, pretending that it
> succeeded. But the original shell script calls `git submodule--helper
> config [...]` which calls `module_config()`, which in turn passes through
> the return value of the `config_set_in_gitmodules_file_gently()` call.
>
> In other words, you need something like this:
>
> 	int ret;
>
> 	[...]
>
> 	ret = config_set_in_gitmodules_file_gently(config_name, opt_branch);
>
> 	free(config_name);
> 	return ret;

Making sure we check the return value of helper functions we call is
a good discipline, but this is not quite enough.

> So I think we'll also need this (it's unrelated to your patch, at least
> unrelated enough that it merits its own, separate patch):
>
> -                       return commands[i].fn(argc - 1, argv + 1, prefix);
> +                       return !!commands[i].fn(argc - 1, argv + 1, prefix);

I checked (not all but most of the) functions in that commands[]
table and they all seem to return 0 for success and positive
non-zero for failure.

config_set_in_gitmodules_file_gently() takes the return value of a
helper function in its 'ret', gives an warning if it is negative,
and returns that 'ret' literally to the caller.  You suggestion
allows module_set_branch() return a negative value as-is.  You'd
need to return !!ret from there.

The "unrelated" change becomes only necessary if you do not do the
!!ret in module_set_branch(); otherwise it is unneeded, I think.

Thanks.
Junio C Hamano May 24, 2020, 11:18 p.m. UTC | #9
Junio C Hamano <gitster@pobox.com> writes:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>
>>> +	config_set_in_gitmodules_file_gently(config_name, opt_branch);
>>
>> What happens if this fails? E.g. when the permission is denied or disk is
>> full? This C code would then still `return 0`, pretending that it
>> succeeded. But the original shell script calls `git submodule--helper
>> config [...]` which calls `module_config()`, which in turn passes through
>> the return value of the `config_set_in_gitmodules_file_gently()` call.
>>
>> In other words, you need something like this:
>>
>> 	int ret;
>>
>> 	[...]
>>
>> 	ret = config_set_in_gitmodules_file_gently(config_name, opt_branch);
>>
>> 	free(config_name);
>> 	return ret;
>
> Making sure we check the return value of helper functions we call is
> a good discipline,...

By the way, another topic by you for set-url has exactly the same
issue.  Its call to config_set_in_gitmodules_file_gently() can fail.

So can the call to sync_submodule(), but when it fails it won't come
back, so we do not have to worry about not capturing its return
value ;-)
diff mbox series

Patch

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index f50745a03f..d14b9856a3 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2284,6 +2284,50 @@  static int module_set_url(int argc, const char **argv, const char *prefix)
 	return 0;
 }
 
+static int module_set_branch(int argc, const char **argv, const char *prefix)
+{
+	/*
+	 * The `quiet` option is present for backward compatibility
+	 * but is currently not used.
+	 */
+	int quiet = 0, opt_default = 0;
+	const char *opt_branch = NULL;
+	const char *path;
+	char *config_name;
+
+	struct option options[] = {
+		OPT__QUIET(&quiet,
+			N_("suppress output for setting default tracking branch")),
+		OPT_BOOL(0, "default", &opt_default,
+			N_("set the default tracking branch to master")),
+		OPT_STRING(0, "branch", &opt_branch, N_("branch"),
+			N_("set the default tracking branch")),
+		OPT_END()
+	};
+	const char *const usage[] = {
+		N_("git submodule--helper set-branch [--quiet] (-d|--default) <path>"),
+		N_("git submodule--helper set-branch [--quiet] (-b|--branch) <branch> <path>"),
+		NULL
+	};
+
+	argc = parse_options(argc, argv, prefix, options, usage, 0);
+
+	if (!opt_branch && !opt_default)
+		die(_("--branch or --default required"));
+
+	if (opt_branch && opt_default)
+		die(_("--branch and --default are mutually exclusive"));
+
+	if (argc != 1 || !(path = argv[0]))
+		usage_with_options(usage, options);
+
+	config_name = xstrfmt("submodule.%s.branch", path);
+	config_set_in_gitmodules_file_gently(config_name, opt_branch);
+
+	free(config_name);
+	return 0;
+}
+
 #define SUPPORT_SUPER_PREFIX (1<<0)
 
 struct cmd_struct {
@@ -2315,6 +2359,7 @@  static struct cmd_struct commands[] = {
 	{"check-name", check_name, 0},
 	{"config", module_config, 0},
 	{"set-url", module_set_url, 0},
+	{"set-branch", module_set_branch, 0},
 };
 
 int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
diff --git a/git-submodule.sh b/git-submodule.sh
index 39ebdf25b5..8c56191f77 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -719,7 +719,7 @@  cmd_update()
 # $@ = requested path
 #
 cmd_set_branch() {
-	unset_branch=false
+	default=
 	branch=
 
 	while test $# -ne 0
@@ -729,7 +729,7 @@  cmd_set_branch() {
 			# we don't do anything with this but we need to accept it
 			;;
 		-d|--default)
-			unset_branch=true
+			default=1
 			;;
 		-b|--branch)
 			case "$2" in '') usage ;; esac
@@ -750,33 +750,7 @@  cmd_set_branch() {
 		shift
 	done
 
-	if test $# -ne 1
-	then
-		usage
-	fi
-
-	# we can't use `git submodule--helper name` here because internally, it
-	# hashes the path so a trailing slash could lead to an unintentional no match
-	name="$(git submodule--helper list "$1" | cut -f2)"
-	if test -z "$name"
-	then
-		exit 1
-	fi
-
-	test -n "$branch"; has_branch=$?
-	test "$unset_branch" = true; has_unset_branch=$?
-
-	if test $((!$has_branch != !$has_unset_branch)) -eq 0
-	then
-		usage
-	fi
-
-	if test $has_branch -eq 0
-	then
-		git submodule--helper config submodule."$name".branch "$branch"
-	else
-		git submodule--helper config --unset submodule."$name".branch
-	fi
+	git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper set-branch ${GIT_QUIET:+--quiet} ${branch:+--branch $branch} ${default:+--default} -- "$@"
 }
 
 #