diff mbox series

[v4,24/27] builtin/rebase: do not assign default backend to non-constant field

Message ID e19457d20c80f9ce332f2d890a5089972e28f0cf.1717504517.git.ps@pks.im (mailing list archive)
State Superseded
Headers show
Series Compile with `-Wwrite-strings` | expand

Commit Message

Patrick Steinhardt June 4, 2024, 12:38 p.m. UTC
The `struct rebase_options::default_backend` field is a non-constant
string, but is being assigned a constant via `REBASE_OPTIONS_INIT`.
Refactor the code to initialize and release options via two functions
`rebase_options_init()` and `rebase_options_release()`. Like this, we
can easily adapt the former funnction to use `xstrdup()` on the default
value without hiding it away in a macro.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/rebase.c | 67 ++++++++++++++++++++++++++++--------------------
 1 file changed, 39 insertions(+), 28 deletions(-)

Comments

Phillip Wood June 4, 2024, 2:06 p.m. UTC | #1
Hi Patrick

On 04/06/2024 13:38, Patrick Steinhardt wrote:
> The `struct rebase_options::default_backend` field is a non-constant
> string, but is being assigned a constant via `REBASE_OPTIONS_INIT`.
> Refactor the code to initialize and release options via two functions
> `rebase_options_init()` and `rebase_options_release()`. Like this, we
> can easily adapt the former funnction to use `xstrdup()` on the default
> value without hiding it away in a macro.

Personally I'd be happy with

-		.default_backend = "merge",		\
+		.default_backend = xstrdup("merge"),	\

rather than adding an init function. I do agree that adding 
rebase_options_release() is a good idea and the rest of the changes look 
good to me

Thanks

Phillip

> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
>   builtin/rebase.c | 67 ++++++++++++++++++++++++++++--------------------
>   1 file changed, 39 insertions(+), 28 deletions(-)
> 
> diff --git a/builtin/rebase.c b/builtin/rebase.c
> index 14d4f0a5e6..11f276012c 100644
> --- a/builtin/rebase.c
> +++ b/builtin/rebase.c
> @@ -131,25 +131,40 @@ struct rebase_options {
>   	int config_update_refs;
>   };
>   
> -#define REBASE_OPTIONS_INIT {			  	\
> -		.type = REBASE_UNSPECIFIED,	  	\
> -		.empty = EMPTY_UNSPECIFIED,	  	\
> -		.keep_empty = 1,			\
> -		.default_backend = "merge",	  	\
> -		.flags = REBASE_NO_QUIET, 		\
> -		.git_am_opts = STRVEC_INIT,		\
> -		.exec = STRING_LIST_INIT_NODUP,		\
> -		.git_format_patch_opt = STRBUF_INIT,	\
> -		.fork_point = -1,			\
> -		.reapply_cherry_picks = -1,             \
> -		.allow_empty_message = 1,               \
> -		.autosquash = -1,                       \
> -		.rebase_merges = -1,                    \
> -		.config_rebase_merges = -1,             \
> -		.update_refs = -1,                      \
> -		.config_update_refs = -1,               \
> -		.strategy_opts = STRING_LIST_INIT_NODUP,\
> -	}
> +static void rebase_options_init(struct rebase_options *opts)
> +{
> +	memset(opts, 0, sizeof(*opts));
> +	opts->type = REBASE_UNSPECIFIED;
> +	opts->empty = EMPTY_UNSPECIFIED;
> +	opts->default_backend = xstrdup("merge");
> +	opts->keep_empty = 1;
> +	opts->flags = REBASE_NO_QUIET;
> +	strvec_init(&opts->git_am_opts);
> +	string_list_init_nodup(&opts->exec);
> +	strbuf_init(&opts->git_format_patch_opt, 0);
> +	opts->fork_point = -1;
> +	opts->reapply_cherry_picks = -1;
> +	opts->allow_empty_message = 1;
> +	opts->autosquash = -1;
> +	opts->rebase_merges = -1;
> +	opts->config_rebase_merges = -1;
> +	opts->update_refs = -1;
> +	opts->config_update_refs = -1;
> +	string_list_init_nodup(&opts->strategy_opts);
> +}
> +
> +static void rebase_options_release(struct rebase_options *opts)
> +{
> +	free(opts->default_backend);
> +	free(opts->reflog_action);
> +	free(opts->head_name);
> +	strvec_clear(&opts->git_am_opts);
> +	free(opts->gpg_sign_opt);
> +	string_list_clear(&opts->exec, 0);
> +	free(opts->strategy);
> +	string_list_clear(&opts->strategy_opts, 0);
> +	strbuf_release(&opts->git_format_patch_opt);
> +}
>   
>   static struct replay_opts get_replay_opts(const struct rebase_options *opts)
>   {
> @@ -796,6 +811,7 @@ static int rebase_config(const char *var, const char *value,
>   	}
>   
>   	if (!strcmp(var, "rebase.backend")) {
> +		FREE_AND_NULL(opts->default_backend);
>   		return git_config_string(&opts->default_backend, var, value);
>   	}
>   
> @@ -1045,7 +1061,7 @@ static int check_exec_cmd(const char *cmd)
>   
>   int cmd_rebase(int argc, const char **argv, const char *prefix)
>   {
> -	struct rebase_options options = REBASE_OPTIONS_INIT;
> +	struct rebase_options options;
>   	const char *branch_name;
>   	int ret, flags, total_argc, in_progress = 0;
>   	int keep_base = 0;
> @@ -1178,6 +1194,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>   	};
>   	int i;
>   
> +	rebase_options_init(&options);
> +
>   	if (argc == 2 && !strcmp(argv[1], "-h"))
>   		usage_with_options(builtin_rebase_usage,
>   				   builtin_rebase_options);
> @@ -1833,14 +1851,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>   cleanup:
>   	strbuf_release(&buf);
>   	strbuf_release(&revisions);
> -	free(options.reflog_action);
> -	free(options.head_name);
> -	strvec_clear(&options.git_am_opts);
> -	free(options.gpg_sign_opt);
> -	string_list_clear(&options.exec, 0);
> -	free(options.strategy);
> -	string_list_clear(&options.strategy_opts, 0);
> -	strbuf_release(&options.git_format_patch_opt);
> +	rebase_options_release(&options);
>   	free(squash_onto_name);
>   	free(keep_base_onto_name);
>   	return !!ret;
Patrick Steinhardt June 5, 2024, 5:40 a.m. UTC | #2
On Tue, Jun 04, 2024 at 03:06:38PM +0100, Phillip Wood wrote:
> Hi Patrick
> 
> On 04/06/2024 13:38, Patrick Steinhardt wrote:
> > The `struct rebase_options::default_backend` field is a non-constant
> > string, but is being assigned a constant via `REBASE_OPTIONS_INIT`.
> > Refactor the code to initialize and release options via two functions
> > `rebase_options_init()` and `rebase_options_release()`. Like this, we
> > can easily adapt the former funnction to use `xstrdup()` on the default
> > value without hiding it away in a macro.
> 
> Personally I'd be happy with
> 
> -		.default_backend = "merge",		\
> +		.default_backend = xstrdup("merge"),	\
> 
> rather than adding an init function. I do agree that adding
> rebase_options_release() is a good idea and the rest of the changes look
> good to me

Do we have any other cases where we allocate inside of a `_INIT` style
macro? If so, I'd go with that precedence and just allocate inside of
the macro. But if we don't, then I think I'm leaning more towards the
way I did it in this patch.

Happy to be convinced otherwise, I don't really feel all that strongly
about this. I'm merely aiming for the interface wth least surprises.

Patrick
Phillip Wood June 5, 2024, 1:06 p.m. UTC | #3
On 05/06/2024 06:40, Patrick Steinhardt wrote:
> On Tue, Jun 04, 2024 at 03:06:38PM +0100, Phillip Wood wrote:

> Do we have any other cases where we allocate inside of a `_INIT` style
> macro? If so, I'd go with that precedence and just allocate inside of
> the macro. But if we don't, then I think I'm leaning more towards the
> way I did it in this patch.

I recently added an allocation to REPLAY_OPTS_INIT, I'm not sure if 
there are any others. In general code that does

	struct foo = FOO_INIT;

and does not call

	foo_release();

is asking for trouble so I don't feel that allocations in _INIT macros 
are generally a problem. The only reason not to have allocations in an 
_INIT macro is if it might be used to initialize a file scope or global 
variable but that's not the case here.

> Happy to be convinced otherwise, I don't really feel all that strongly
> about this. I'm merely aiming for the interface wth least surprises.

I'm not that fussed either, but I do prefer our _INIT macros over 
_init() functions as I think they're nicer to use and easier to write 
(no need to worry about memset() to zero out the struct).

Best Wishes

Phillip
Junio C Hamano June 5, 2024, 4:11 p.m. UTC | #4
Patrick Steinhardt <ps@pks.im> writes:

>> Personally I'd be happy with
>> 
>> -		.default_backend = "merge",		\
>> +		.default_backend = xstrdup("merge"),	\
>> 
>> rather than adding an init function. I do agree that adding
>> rebase_options_release() is a good idea and the rest of the changes look
>> good to me
>
> Do we have any other cases where we allocate inside of a `_INIT` style
> macro? If so, I'd go with that precedence and just allocate inside of
> the macro. But if we don't, then I think I'm leaning more towards the
> way I did it in this patch.
>
> Happy to be convinced otherwise, I don't really feel all that strongly
> about this. I'm merely aiming for the interface wth least surprises.

FWIW, I am OK either way.

Having _init() and _release() may look symmetrical and there _might_
be cases where an initialization with a simple _INIT macro may not
suffice and we must have _init() function.

But otherwise, especially with .designated_initializers, the _INIT
macro makes it slightly easier to read (but not all much for this
particular case, whose initial values have too many non-zero values
and pretty much everything must be spelled out).

Thanks.
Patrick Steinhardt June 6, 2024, 9:50 a.m. UTC | #5
On Wed, Jun 05, 2024 at 02:06:55PM +0100, Phillip Wood wrote:
> On 05/06/2024 06:40, Patrick Steinhardt wrote:
> > On Tue, Jun 04, 2024 at 03:06:38PM +0100, Phillip Wood wrote:
> 
> > Do we have any other cases where we allocate inside of a `_INIT` style
> > macro? If so, I'd go with that precedence and just allocate inside of
> > the macro. But if we don't, then I think I'm leaning more towards the
> > way I did it in this patch.
> 
> I recently added an allocation to REPLAY_OPTS_INIT, I'm not sure if there
> are any others. In general code that does
> 
> 	struct foo = FOO_INIT;
> 
> and does not call
> 
> 	foo_release();
> 
> is asking for trouble so I don't feel that allocations in _INIT macros are
> generally a problem. The only reason not to have allocations in an _INIT
> macro is if it might be used to initialize a file scope or global variable
> but that's not the case here.
> 
> > Happy to be convinced otherwise, I don't really feel all that strongly
> > about this. I'm merely aiming for the interface wth least surprises.
> 
> I'm not that fussed either, but I do prefer our _INIT macros over _init()
> functions as I think they're nicer to use and easier to write (no need to
> worry about memset() to zero out the struct).

Okay, given that there is precedent, and given that Junio also seems to
slightly lean into the direction of the `_INIT` macro, I'll adapt this
as proposed by you.

Patrick
diff mbox series

Patch

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 14d4f0a5e6..11f276012c 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -131,25 +131,40 @@  struct rebase_options {
 	int config_update_refs;
 };
 
-#define REBASE_OPTIONS_INIT {			  	\
-		.type = REBASE_UNSPECIFIED,	  	\
-		.empty = EMPTY_UNSPECIFIED,	  	\
-		.keep_empty = 1,			\
-		.default_backend = "merge",	  	\
-		.flags = REBASE_NO_QUIET, 		\
-		.git_am_opts = STRVEC_INIT,		\
-		.exec = STRING_LIST_INIT_NODUP,		\
-		.git_format_patch_opt = STRBUF_INIT,	\
-		.fork_point = -1,			\
-		.reapply_cherry_picks = -1,             \
-		.allow_empty_message = 1,               \
-		.autosquash = -1,                       \
-		.rebase_merges = -1,                    \
-		.config_rebase_merges = -1,             \
-		.update_refs = -1,                      \
-		.config_update_refs = -1,               \
-		.strategy_opts = STRING_LIST_INIT_NODUP,\
-	}
+static void rebase_options_init(struct rebase_options *opts)
+{
+	memset(opts, 0, sizeof(*opts));
+	opts->type = REBASE_UNSPECIFIED;
+	opts->empty = EMPTY_UNSPECIFIED;
+	opts->default_backend = xstrdup("merge");
+	opts->keep_empty = 1;
+	opts->flags = REBASE_NO_QUIET;
+	strvec_init(&opts->git_am_opts);
+	string_list_init_nodup(&opts->exec);
+	strbuf_init(&opts->git_format_patch_opt, 0);
+	opts->fork_point = -1;
+	opts->reapply_cherry_picks = -1;
+	opts->allow_empty_message = 1;
+	opts->autosquash = -1;
+	opts->rebase_merges = -1;
+	opts->config_rebase_merges = -1;
+	opts->update_refs = -1;
+	opts->config_update_refs = -1;
+	string_list_init_nodup(&opts->strategy_opts);
+}
+
+static void rebase_options_release(struct rebase_options *opts)
+{
+	free(opts->default_backend);
+	free(opts->reflog_action);
+	free(opts->head_name);
+	strvec_clear(&opts->git_am_opts);
+	free(opts->gpg_sign_opt);
+	string_list_clear(&opts->exec, 0);
+	free(opts->strategy);
+	string_list_clear(&opts->strategy_opts, 0);
+	strbuf_release(&opts->git_format_patch_opt);
+}
 
 static struct replay_opts get_replay_opts(const struct rebase_options *opts)
 {
@@ -796,6 +811,7 @@  static int rebase_config(const char *var, const char *value,
 	}
 
 	if (!strcmp(var, "rebase.backend")) {
+		FREE_AND_NULL(opts->default_backend);
 		return git_config_string(&opts->default_backend, var, value);
 	}
 
@@ -1045,7 +1061,7 @@  static int check_exec_cmd(const char *cmd)
 
 int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
-	struct rebase_options options = REBASE_OPTIONS_INIT;
+	struct rebase_options options;
 	const char *branch_name;
 	int ret, flags, total_argc, in_progress = 0;
 	int keep_base = 0;
@@ -1178,6 +1194,8 @@  int cmd_rebase(int argc, const char **argv, const char *prefix)
 	};
 	int i;
 
+	rebase_options_init(&options);
+
 	if (argc == 2 && !strcmp(argv[1], "-h"))
 		usage_with_options(builtin_rebase_usage,
 				   builtin_rebase_options);
@@ -1833,14 +1851,7 @@  int cmd_rebase(int argc, const char **argv, const char *prefix)
 cleanup:
 	strbuf_release(&buf);
 	strbuf_release(&revisions);
-	free(options.reflog_action);
-	free(options.head_name);
-	strvec_clear(&options.git_am_opts);
-	free(options.gpg_sign_opt);
-	string_list_clear(&options.exec, 0);
-	free(options.strategy);
-	string_list_clear(&options.strategy_opts, 0);
-	strbuf_release(&options.git_format_patch_opt);
+	rebase_options_release(&options);
 	free(squash_onto_name);
 	free(keep_base_onto_name);
 	return !!ret;