diff --git a/Makefile b/Makefile index 59d98ba688..2f5f16847a 100644 --- a/Makefile +++ b/Makefile @@ -1338,6 +1338,7 @@ UNIT_TEST_PROGRAMS += t-mem-pool UNIT_TEST_PROGRAMS += t-prio-queue UNIT_TEST_PROGRAMS += t-strbuf UNIT_TEST_PROGRAMS += t-strcmp-offset +UNIT_TEST_PROGRAMS += t-strvec UNIT_TEST_PROGRAMS += t-trailer UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS)) UNIT_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS)) diff --git a/alias.c b/alias.c index 5a238f2e30..4daafd9bda 100644 --- a/alias.c +++ b/alias.c @@ -21,9 +21,11 @@ static int config_alias_cb(const char *key, const char *value, return 0; if (data->alias) { - if (!strcasecmp(p, data->alias)) - return git_config_string((const char **)&data->v, + if (!strcasecmp(p, data->alias)) { + FREE_AND_NULL(data->v); + return git_config_string(&data->v, key, value); + } } else if (data->list) { string_list_append(data->list, p); } diff --git a/attr.c b/attr.c index 38c586ab63..300f994ba6 100644 --- a/attr.c +++ b/attr.c @@ -25,7 +25,7 @@ #include "tree-walk.h" #include "object-name.h" -const char *git_attr_tree; +char *git_attr_tree; const char git_attr__true[] = "(builtin)true"; const char git_attr__false[] = "\0(builtin)false"; diff --git a/attr.h b/attr.h index 127998ae01..e7cc318b0c 100644 --- a/attr.h +++ b/attr.h @@ -236,6 +236,6 @@ const char *git_attr_global_file(void); /* Return whether the system gitattributes file is enabled and should be used. */ int git_attr_system_is_enabled(void); -extern const char *git_attr_tree; +extern char *git_attr_tree; #endif /* ATTR_H */ diff --git a/builtin/blame.c b/builtin/blame.c index fadba1a530..e09ff0155a 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -718,7 +718,7 @@ static int git_blame_config(const char *var, const char *value, return 0; } if (!strcmp(var, "blame.ignorerevsfile")) { - const char *str; + char *str; int ret; ret = git_config_pathname(&str, var, value); diff --git a/builtin/checkout.c b/builtin/checkout.c index f90a4ca4b7..3cf44b4683 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -1275,12 +1275,12 @@ static void setup_new_branch_info_and_source_tree( } } -static const char *parse_remote_branch(const char *arg, - struct object_id *rev, - int could_be_checkout_paths) +static char *parse_remote_branch(const char *arg, + struct object_id *rev, + int could_be_checkout_paths) { int num_matches = 0; - const char *remote = unique_tracking_name(arg, rev, &num_matches); + char *remote = unique_tracking_name(arg, rev, &num_matches); if (remote && could_be_checkout_paths) { die(_("'%s' could be both a local file and a tracking branch.\n" @@ -1316,6 +1316,7 @@ static int parse_branchname_arg(int argc, const char **argv, const char **new_branch = &opts->new_branch; int argcount = 0; const char *arg; + char *remote = NULL; int dash_dash_pos; int has_dash_dash = 0; int i; @@ -1416,8 +1417,8 @@ static int parse_branchname_arg(int argc, const char **argv, recover_with_dwim = 0; if (recover_with_dwim) { - const char *remote = parse_remote_branch(arg, rev, - could_be_checkout_paths); + remote = parse_remote_branch(arg, rev, + could_be_checkout_paths); if (remote) { *new_branch = arg; arg = remote; @@ -1459,6 +1460,7 @@ static int parse_branchname_arg(int argc, const char **argv, argc--; } + free(remote); return argcount; } diff --git a/builtin/commit.c b/builtin/commit.c index 78bfae2164..f53e7e86ff 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -107,7 +107,7 @@ static enum { } commit_style; static const char *logfile, *force_author; -static const char *template_file; +static char *template_file; /* * The _message variables are commit names from which to take * the commit message and/or authorship. @@ -133,7 +133,7 @@ static struct strvec trailer_args = STRVEC_INIT; * is specified explicitly. */ static enum commit_msg_cleanup_mode cleanup_mode; -static const char *cleanup_arg; +static char *cleanup_arg; static enum commit_whence whence; static int use_editor = 1, include_status = 1; diff --git a/builtin/config.c b/builtin/config.c index c38264c999..20a0b64090 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -289,7 +289,7 @@ static int format_config(const struct config_display_options *opts, else strbuf_addstr(buf, v ? "true" : "false"); } else if (opts->type == TYPE_PATH) { - const char *v; + char *v; if (git_config_pathname(&v, key_, value_) < 0) return -1; strbuf_addstr(buf, v); diff --git a/builtin/credential.c b/builtin/credential.c index 5100d441f2..b72e76dd9a 100644 --- a/builtin/credential.c +++ b/builtin/credential.c @@ -39,5 +39,7 @@ int cmd_credential(int argc, const char **argv, const char *prefix UNUSED) } else { usage(usage_msg); } + + credential_clear(&c); return 0; } diff --git a/builtin/log.c b/builtin/log.c index c8ce0c0d88..78a247d8a9 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -48,22 +48,8 @@ #define COVER_FROM_AUTO_MAX_SUBJECT_LEN 100 #define FORMAT_PATCH_NAME_MAX_DEFAULT 64 -/* Set a default date-time format for git log ("log.date" config variable) */ -static const char *default_date_mode = NULL; - -static int default_abbrev_commit; -static int default_show_root = 1; -static int default_follow; -static int default_show_signature; -static int default_encode_email_headers = 1; -static int decoration_style; -static int decoration_given; -static int use_mailmap_config = 1; static unsigned int force_in_body_from; static int stdout_mboxrd; -static const char *fmt_patch_subject_prefix = "PATCH"; -static int fmt_patch_name_max = FORMAT_PATCH_NAME_MAX_DEFAULT; -static const char *fmt_pretty; static int format_no_prefix; static const char * const builtin_log_usage[] = { @@ -111,6 +97,39 @@ static int parse_decoration_style(const char *value) return -1; } +struct log_config { + int default_abbrev_commit; + int default_show_root; + int default_follow; + int default_show_signature; + int default_encode_email_headers; + int decoration_style; + int decoration_given; + int use_mailmap_config; + char *fmt_patch_subject_prefix; + int fmt_patch_name_max; + char *fmt_pretty; + char *default_date_mode; +}; + +static void log_config_init(struct log_config *cfg) +{ + memset(cfg, 0, sizeof(*cfg)); + cfg->default_show_root = 1; + cfg->default_encode_email_headers = 1; + cfg->use_mailmap_config = 1; + cfg->fmt_patch_subject_prefix = xstrdup("PATCH"); + cfg->fmt_patch_name_max = FORMAT_PATCH_NAME_MAX_DEFAULT; + cfg->decoration_style = auto_decoration_style(); +} + +static void log_config_release(struct log_config *cfg) +{ + free(cfg->default_date_mode); + free(cfg->fmt_pretty); + free(cfg->fmt_patch_subject_prefix); +} + static int use_default_decoration_filter = 1; static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP; static struct string_list decorate_refs_exclude_config = STRING_LIST_INIT_NODUP; @@ -127,20 +146,22 @@ static int clear_decorations_callback(const struct option *opt UNUSED, return 0; } -static int decorate_callback(const struct option *opt UNUSED, const char *arg, +static int decorate_callback(const struct option *opt, const char *arg, int unset) { - if (unset) - decoration_style = 0; - else if (arg) - decoration_style = parse_decoration_style(arg); - else - decoration_style = DECORATE_SHORT_REFS; + struct log_config *cfg = opt->value; - if (decoration_style < 0) + if (unset) + cfg->decoration_style = 0; + else if (arg) + cfg->decoration_style = parse_decoration_style(arg); + else + cfg->decoration_style = DECORATE_SHORT_REFS; + + if (cfg->decoration_style < 0) die(_("invalid --decorate option: %s"), arg); - decoration_given = 1; + cfg->decoration_given = 1; return 0; } @@ -160,32 +181,26 @@ static int log_line_range_callback(const struct option *option, const char *arg, return 0; } -static void init_log_defaults(void) +static void cmd_log_init_defaults(struct rev_info *rev, + struct log_config *cfg) { - init_diff_ui_defaults(); - - decoration_style = auto_decoration_style(); -} - -static void cmd_log_init_defaults(struct rev_info *rev) -{ - if (fmt_pretty) - get_commit_format(fmt_pretty, rev); - if (default_follow) + if (cfg->fmt_pretty) + get_commit_format(cfg->fmt_pretty, rev); + if (cfg->default_follow) rev->diffopt.flags.default_follow_renames = 1; rev->verbose_header = 1; init_diffstat_widths(&rev->diffopt); rev->diffopt.flags.recursive = 1; rev->diffopt.flags.allow_textconv = 1; - rev->abbrev_commit = default_abbrev_commit; - rev->show_root_diff = default_show_root; - rev->subject_prefix = fmt_patch_subject_prefix; - rev->patch_name_max = fmt_patch_name_max; - rev->show_signature = default_show_signature; - rev->encode_email_headers = default_encode_email_headers; + rev->abbrev_commit = cfg->default_abbrev_commit; + rev->show_root_diff = cfg->default_show_root; + rev->subject_prefix = cfg->fmt_patch_subject_prefix; + rev->patch_name_max = cfg->fmt_patch_name_max; + rev->show_signature = cfg->default_show_signature; + rev->encode_email_headers = cfg->default_encode_email_headers; - if (default_date_mode) - parse_date_format(default_date_mode, &rev->date_mode); + if (cfg->default_date_mode) + parse_date_format(cfg->default_date_mode, &rev->date_mode); } static void set_default_decoration_filter(struct decoration_filter *decoration_filter) @@ -233,7 +248,8 @@ static void set_default_decoration_filter(struct decoration_filter *decoration_f } static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, - struct rev_info *rev, struct setup_revision_opt *opt) + struct rev_info *rev, struct setup_revision_opt *opt, + struct log_config *cfg) { struct userformat_want w; int quiet = 0, source = 0, mailmap; @@ -258,7 +274,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, N_("pattern"), N_("only decorate refs that match ")), OPT_STRING_LIST(0, "decorate-refs-exclude", &decorate_refs_exclude, N_("pattern"), N_("do not decorate refs that match ")), - OPT_CALLBACK_F(0, "decorate", NULL, NULL, N_("decorate options"), + OPT_CALLBACK_F(0, "decorate", cfg, NULL, N_("decorate options"), PARSE_OPT_OPTARG, decorate_callback), OPT_CALLBACK('L', NULL, &line_cb, "range:file", N_("trace the evolution of line range , or function : in "), @@ -269,7 +285,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, line_cb.rev = rev; line_cb.prefix = prefix; - mailmap = use_mailmap_config; + mailmap = cfg->use_mailmap_config; argc = parse_options(argc, argv, prefix, builtin_log_options, builtin_log_usage, PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT | @@ -314,8 +330,8 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, * "log --pretty=raw" is special; ignore UI oriented * configuration variables such as decoration. */ - if (!decoration_given) - decoration_style = 0; + if (!cfg->decoration_given) + cfg->decoration_style = 0; if (!rev->abbrev_commit_given) rev->abbrev_commit = 0; } @@ -326,24 +342,24 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, * Disable decoration loading if the format will not * show them anyway. */ - decoration_style = 0; - } else if (!decoration_style) { + cfg->decoration_style = 0; + } else if (!cfg->decoration_style) { /* * If we are going to show them, make sure we do load * them here, but taking care not to override a * specific style set by config or --decorate. */ - decoration_style = DECORATE_SHORT_REFS; + cfg->decoration_style = DECORATE_SHORT_REFS; } } - if (decoration_style || rev->simplify_by_decoration) { + if (cfg->decoration_style || rev->simplify_by_decoration) { set_default_decoration_filter(&decoration_filter); - if (decoration_style) + if (cfg->decoration_style) rev->show_decorations = 1; - load_ref_decorations(&decoration_filter, decoration_style); + load_ref_decorations(&decoration_filter, cfg->decoration_style); } if (rev->line_level_traverse) @@ -353,16 +369,11 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, } static void cmd_log_init(int argc, const char **argv, const char *prefix, - struct rev_info *rev, struct setup_revision_opt *opt) + struct rev_info *rev, struct setup_revision_opt *opt, + struct log_config *cfg) { - cmd_log_init_defaults(rev); - cmd_log_init_finish(argc, argv, prefix, rev, opt); -} - -static int cmd_log_deinit(int ret, struct rev_info *rev) -{ - release_revisions(rev); - return ret; + cmd_log_init_defaults(rev, cfg); + cmd_log_init_finish(argc, argv, prefix, rev, opt, cfg); } /* @@ -566,30 +577,37 @@ static int cmd_log_walk(struct rev_info *rev) static int git_log_config(const char *var, const char *value, const struct config_context *ctx, void *cb) { + struct log_config *cfg = cb; const char *slot_name; - if (!strcmp(var, "format.pretty")) - return git_config_string(&fmt_pretty, var, value); - if (!strcmp(var, "format.subjectprefix")) - return git_config_string(&fmt_patch_subject_prefix, var, value); + if (!strcmp(var, "format.pretty")) { + FREE_AND_NULL(cfg->fmt_pretty); + return git_config_string(&cfg->fmt_pretty, var, value); + } + if (!strcmp(var, "format.subjectprefix")) { + FREE_AND_NULL(cfg->fmt_patch_subject_prefix); + return git_config_string(&cfg->fmt_patch_subject_prefix, var, value); + } if (!strcmp(var, "format.filenamemaxlength")) { - fmt_patch_name_max = git_config_int(var, value, ctx->kvi); + cfg->fmt_patch_name_max = git_config_int(var, value, ctx->kvi); return 0; } if (!strcmp(var, "format.encodeemailheaders")) { - default_encode_email_headers = git_config_bool(var, value); + cfg->default_encode_email_headers = git_config_bool(var, value); return 0; } if (!strcmp(var, "log.abbrevcommit")) { - default_abbrev_commit = git_config_bool(var, value); + cfg->default_abbrev_commit = git_config_bool(var, value); return 0; } - if (!strcmp(var, "log.date")) - return git_config_string(&default_date_mode, var, value); + if (!strcmp(var, "log.date")) { + FREE_AND_NULL(cfg->default_date_mode); + return git_config_string(&cfg->default_date_mode, var, value); + } if (!strcmp(var, "log.decorate")) { - decoration_style = parse_decoration_style(value); - if (decoration_style < 0) - decoration_style = 0; /* maybe warn? */ + cfg->decoration_style = parse_decoration_style(value); + if (cfg->decoration_style < 0) + cfg->decoration_style = 0; /* maybe warn? */ return 0; } if (!strcmp(var, "log.diffmerges")) { @@ -598,21 +616,21 @@ static int git_log_config(const char *var, const char *value, return diff_merges_config(value); } if (!strcmp(var, "log.showroot")) { - default_show_root = git_config_bool(var, value); + cfg->default_show_root = git_config_bool(var, value); return 0; } if (!strcmp(var, "log.follow")) { - default_follow = git_config_bool(var, value); + cfg->default_follow = git_config_bool(var, value); return 0; } if (skip_prefix(var, "color.decorate.", &slot_name)) return parse_decorate_color_config(var, slot_name, value); if (!strcmp(var, "log.mailmap")) { - use_mailmap_config = git_config_bool(var, value); + cfg->use_mailmap_config = git_config_bool(var, value); return 0; } if (!strcmp(var, "log.showsignature")) { - default_show_signature = git_config_bool(var, value); + cfg->default_show_signature = git_config_bool(var, value); return 0; } @@ -621,11 +639,14 @@ static int git_log_config(const char *var, const char *value, int cmd_whatchanged(int argc, const char **argv, const char *prefix) { + struct log_config cfg; struct rev_info rev; struct setup_revision_opt opt; + int ret; - init_log_defaults(); - git_config(git_log_config, NULL); + log_config_init(&cfg); + init_diff_ui_defaults(); + git_config(git_log_config, &cfg); repo_init_revisions(the_repository, &rev, prefix); git_config(grep_config, &rev.grep_filter); @@ -635,10 +656,15 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix) memset(&opt, 0, sizeof(opt)); opt.def = "HEAD"; opt.revarg_opt = REVARG_COMMITTISH; - cmd_log_init(argc, argv, prefix, &rev, &opt); + cmd_log_init(argc, argv, prefix, &rev, &opt, &cfg); if (!rev.diffopt.output_format) rev.diffopt.output_format = DIFF_FORMAT_RAW; - return cmd_log_deinit(cmd_log_walk(&rev), &rev); + + ret = cmd_log_walk(&rev); + + release_revisions(&rev); + log_config_release(&cfg); + return ret; } static void show_tagger(const char *buf, struct rev_info *rev) @@ -733,14 +759,16 @@ static void show_setup_revisions_tweak(struct rev_info *rev) int cmd_show(int argc, const char **argv, const char *prefix) { + struct log_config cfg; struct rev_info rev; unsigned int i; struct setup_revision_opt opt; struct pathspec match_all; int ret = 0; - init_log_defaults(); - git_config(git_log_config, NULL); + log_config_init(&cfg); + init_diff_ui_defaults(); + git_config(git_log_config, &cfg); if (the_repository->gitdir) { prepare_repo_settings(the_repository); @@ -759,10 +787,14 @@ int cmd_show(int argc, const char **argv, const char *prefix) memset(&opt, 0, sizeof(opt)); opt.def = "HEAD"; opt.tweak = show_setup_revisions_tweak; - cmd_log_init(argc, argv, prefix, &rev, &opt); + cmd_log_init(argc, argv, prefix, &rev, &opt, &cfg); - if (!rev.no_walk) - return cmd_log_deinit(cmd_log_walk(&rev), &rev); + if (!rev.no_walk) { + ret = cmd_log_walk(&rev); + release_revisions(&rev); + log_config_release(&cfg); + return ret; + } rev.diffopt.no_free = 1; for (i = 0; i < rev.pending.nr && !ret; i++) { @@ -832,8 +864,10 @@ int cmd_show(int argc, const char **argv, const char *prefix) rev.diffopt.no_free = 0; diff_free(&rev.diffopt); + release_revisions(&rev); + log_config_release(&cfg); - return cmd_log_deinit(ret, &rev); + return ret; } /* @@ -841,11 +875,14 @@ int cmd_show(int argc, const char **argv, const char *prefix) */ int cmd_log_reflog(int argc, const char **argv, const char *prefix) { + struct log_config cfg; struct rev_info rev; struct setup_revision_opt opt; + int ret; - init_log_defaults(); - git_config(git_log_config, NULL); + log_config_init(&cfg); + init_diff_ui_defaults(); + git_config(git_log_config, &cfg); repo_init_revisions(the_repository, &rev, prefix); init_reflog_walk(&rev.reflog_info); @@ -854,14 +891,18 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix) rev.verbose_header = 1; memset(&opt, 0, sizeof(opt)); opt.def = "HEAD"; - cmd_log_init_defaults(&rev); + cmd_log_init_defaults(&rev, &cfg); rev.abbrev_commit = 1; rev.commit_format = CMIT_FMT_ONELINE; rev.use_terminator = 1; rev.always_show_header = 1; - cmd_log_init_finish(argc, argv, prefix, &rev, &opt); + cmd_log_init_finish(argc, argv, prefix, &rev, &opt, &cfg); - return cmd_log_deinit(cmd_log_walk(&rev), &rev); + ret = cmd_log_walk(&rev); + + release_revisions(&rev); + log_config_release(&cfg); + return ret; } static void log_setup_revisions_tweak(struct rev_info *rev) @@ -876,11 +917,14 @@ static void log_setup_revisions_tweak(struct rev_info *rev) int cmd_log(int argc, const char **argv, const char *prefix) { + struct log_config cfg; struct rev_info rev; struct setup_revision_opt opt; + int ret; - init_log_defaults(); - git_config(git_log_config, NULL); + log_config_init(&cfg); + init_diff_ui_defaults(); + git_config(git_log_config, &cfg); repo_init_revisions(the_repository, &rev, prefix); git_config(grep_config, &rev.grep_filter); @@ -890,42 +934,17 @@ int cmd_log(int argc, const char **argv, const char *prefix) opt.def = "HEAD"; opt.revarg_opt = REVARG_COMMITTISH; opt.tweak = log_setup_revisions_tweak; - cmd_log_init(argc, argv, prefix, &rev, &opt); - return cmd_log_deinit(cmd_log_walk(&rev), &rev); + cmd_log_init(argc, argv, prefix, &rev, &opt, &cfg); + + ret = cmd_log_walk(&rev); + + release_revisions(&rev); + log_config_release(&cfg); + return ret; } /* format-patch */ -static const char *fmt_patch_suffix = ".patch"; -static int numbered = 0; -static int auto_number = 1; - -static char *default_attach = NULL; - -static struct string_list extra_hdr = STRING_LIST_INIT_NODUP; -static struct string_list extra_to = STRING_LIST_INIT_NODUP; -static struct string_list extra_cc = STRING_LIST_INIT_NODUP; - -static void add_header(const char *value) -{ - struct string_list_item *item; - int len = strlen(value); - while (len && value[len - 1] == '\n') - len--; - - if (!strncasecmp(value, "to: ", 4)) { - item = string_list_append(&extra_to, value + 4); - len -= 4; - } else if (!strncasecmp(value, "cc: ", 4)) { - item = string_list_append(&extra_cc, value + 4); - len -= 4; - } else { - item = string_list_append(&extra_hdr, value); - } - - item->string[len] = '\0'; -} - enum cover_setting { COVER_UNSET, COVER_OFF, @@ -952,17 +971,61 @@ enum auto_base_setting { AUTO_BASE_WHEN_ABLE }; -static enum thread_level thread; -static int do_signoff; -static enum auto_base_setting auto_base; -static char *from; -static const char *signature = git_version_string; -static const char *signature_file; -static enum cover_setting config_cover_letter; -static const char *config_output_directory; -static enum cover_from_description cover_from_description_mode = COVER_FROM_MESSAGE; -static int show_notes; -static struct display_notes_opt notes_opt; +struct format_config { + struct log_config log; + enum thread_level thread; + int do_signoff; + enum auto_base_setting auto_base; + char *base_commit; + char *from; + char *signature; + char *signature_file; + enum cover_setting config_cover_letter; + char *config_output_directory; + enum cover_from_description cover_from_description_mode; + int show_notes; + struct display_notes_opt notes_opt; + int numbered_cmdline_opt; + int numbered; + int auto_number; + char *default_attach; + struct string_list extra_hdr; + struct string_list extra_to; + struct string_list extra_cc; + int keep_subject; + int subject_prefix; + struct strbuf sprefix; + char *fmt_patch_suffix; +}; + +static void format_config_init(struct format_config *cfg) +{ + memset(cfg, 0, sizeof(*cfg)); + log_config_init(&cfg->log); + cfg->cover_from_description_mode = COVER_FROM_MESSAGE; + cfg->auto_number = 1; + string_list_init_dup(&cfg->extra_hdr); + string_list_init_dup(&cfg->extra_to); + string_list_init_dup(&cfg->extra_cc); + strbuf_init(&cfg->sprefix, 0); + cfg->fmt_patch_suffix = xstrdup(".patch"); +} + +static void format_config_release(struct format_config *cfg) +{ + log_config_release(&cfg->log); + free(cfg->base_commit); + free(cfg->from); + free(cfg->signature); + free(cfg->signature_file); + free(cfg->config_output_directory); + free(cfg->default_attach); + string_list_clear(&cfg->extra_hdr, 0); + string_list_clear(&cfg->extra_to, 0); + string_list_clear(&cfg->extra_cc, 0); + strbuf_release(&cfg->sprefix); + free(cfg->fmt_patch_suffix); +} static enum cover_from_description parse_cover_from_description(const char *arg) { @@ -980,27 +1043,51 @@ static enum cover_from_description parse_cover_from_description(const char *arg) die(_("%s: invalid cover from description mode"), arg); } +static void add_header(struct format_config *cfg, const char *value) +{ + struct string_list_item *item; + int len = strlen(value); + while (len && value[len - 1] == '\n') + len--; + + if (!strncasecmp(value, "to: ", 4)) { + item = string_list_append(&cfg->extra_to, value + 4); + len -= 4; + } else if (!strncasecmp(value, "cc: ", 4)) { + item = string_list_append(&cfg->extra_cc, value + 4); + len -= 4; + } else { + item = string_list_append(&cfg->extra_hdr, value); + } + + item->string[len] = '\0'; +} + static int git_format_config(const char *var, const char *value, const struct config_context *ctx, void *cb) { + struct format_config *cfg = cb; + if (!strcmp(var, "format.headers")) { if (!value) die(_("format.headers without value")); - add_header(value); + add_header(cfg, value); return 0; } - if (!strcmp(var, "format.suffix")) - return git_config_string(&fmt_patch_suffix, var, value); + if (!strcmp(var, "format.suffix")) { + FREE_AND_NULL(cfg->fmt_patch_suffix); + return git_config_string(&cfg->fmt_patch_suffix, var, value); + } if (!strcmp(var, "format.to")) { if (!value) return config_error_nonbool(var); - string_list_append(&extra_to, value); + string_list_append(&cfg->extra_to, value); return 0; } if (!strcmp(var, "format.cc")) { if (!value) return config_error_nonbool(var); - string_list_append(&extra_cc, value); + string_list_append(&cfg->extra_cc, value); return 0; } if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff") || @@ -1009,69 +1096,76 @@ static int git_format_config(const char *var, const char *value, } if (!strcmp(var, "format.numbered")) { if (value && !strcasecmp(value, "auto")) { - auto_number = 1; + cfg->auto_number = 1; return 0; } - numbered = git_config_bool(var, value); - auto_number = auto_number && numbered; + cfg->numbered = git_config_bool(var, value); + cfg->auto_number = cfg->auto_number && cfg->numbered; return 0; } if (!strcmp(var, "format.attach")) { - if (value && *value) - default_attach = xstrdup(value); - else if (value && !*value) - FREE_AND_NULL(default_attach); - else - default_attach = xstrdup(git_version_string); + if (value && *value) { + FREE_AND_NULL(cfg->default_attach); + cfg->default_attach = xstrdup(value); + } else if (value && !*value) { + FREE_AND_NULL(cfg->default_attach); + } else { + FREE_AND_NULL(cfg->default_attach); + cfg->default_attach = xstrdup(git_version_string); + } return 0; } if (!strcmp(var, "format.thread")) { if (value && !strcasecmp(value, "deep")) { - thread = THREAD_DEEP; + cfg->thread = THREAD_DEEP; return 0; } if (value && !strcasecmp(value, "shallow")) { - thread = THREAD_SHALLOW; + cfg->thread = THREAD_SHALLOW; return 0; } - thread = git_config_bool(var, value) ? THREAD_SHALLOW : THREAD_UNSET; + cfg->thread = git_config_bool(var, value) ? THREAD_SHALLOW : THREAD_UNSET; return 0; } if (!strcmp(var, "format.signoff")) { - do_signoff = git_config_bool(var, value); + cfg->do_signoff = git_config_bool(var, value); return 0; } - if (!strcmp(var, "format.signature")) - return git_config_string(&signature, var, value); - if (!strcmp(var, "format.signaturefile")) - return git_config_pathname(&signature_file, var, value); + if (!strcmp(var, "format.signature")) { + FREE_AND_NULL(cfg->signature); + return git_config_string(&cfg->signature, var, value); + } + if (!strcmp(var, "format.signaturefile")) { + FREE_AND_NULL(cfg->signature_file); + return git_config_pathname(&cfg->signature_file, var, value); + } if (!strcmp(var, "format.coverletter")) { if (value && !strcasecmp(value, "auto")) { - config_cover_letter = COVER_AUTO; + cfg->config_cover_letter = COVER_AUTO; return 0; } - config_cover_letter = git_config_bool(var, value) ? COVER_ON : COVER_OFF; + cfg->config_cover_letter = git_config_bool(var, value) ? COVER_ON : COVER_OFF; return 0; } - if (!strcmp(var, "format.outputdirectory")) - return git_config_string(&config_output_directory, var, value); + if (!strcmp(var, "format.outputdirectory")) { + FREE_AND_NULL(cfg->config_output_directory); + return git_config_string(&cfg->config_output_directory, var, value); + } if (!strcmp(var, "format.useautobase")) { if (value && !strcasecmp(value, "whenAble")) { - auto_base = AUTO_BASE_WHEN_ABLE; + cfg->auto_base = AUTO_BASE_WHEN_ABLE; return 0; } - auto_base = git_config_bool(var, value) ? AUTO_BASE_ALWAYS : AUTO_BASE_NEVER; + cfg->auto_base = git_config_bool(var, value) ? AUTO_BASE_ALWAYS : AUTO_BASE_NEVER; return 0; } if (!strcmp(var, "format.from")) { int b = git_parse_maybe_bool(value); - free(from); + FREE_AND_NULL(cfg->from); if (b < 0) - from = xstrdup(value); + cfg->from = xstrdup(value); else if (b) - from = xstrdup(git_committer_info(IDENT_NO_DATE)); - else - from = NULL; + cfg->from = xstrdup(git_committer_info(IDENT_NO_DATE)); return 0; } if (!strcmp(var, "format.forceinbodyfrom")) { @@ -1081,15 +1175,15 @@ static int git_format_config(const char *var, const char *value, if (!strcmp(var, "format.notes")) { int b = git_parse_maybe_bool(value); if (b < 0) - enable_ref_display_notes(¬es_opt, &show_notes, value); + enable_ref_display_notes(&cfg->notes_opt, &cfg->show_notes, value); else if (b) - enable_default_display_notes(¬es_opt, &show_notes); + enable_default_display_notes(&cfg->notes_opt, &cfg->show_notes); else - disable_display_notes(¬es_opt, &show_notes); + disable_display_notes(&cfg->notes_opt, &cfg->show_notes); return 0; } if (!strcmp(var, "format.coverfromdescription")) { - cover_from_description_mode = parse_cover_from_description(value); + cfg->cover_from_description_mode = parse_cover_from_description(value); return 0; } if (!strcmp(var, "format.mboxrd")) { @@ -1110,7 +1204,7 @@ static int git_format_config(const char *var, const char *value, if (!strcmp(var, "diff.noprefix")) return 0; - return git_log_config(var, value, ctx, cb); + return git_log_config(var, value, ctx, &cfg->log); } static const char *output_directory = NULL; @@ -1198,7 +1292,7 @@ static void gen_message_id(struct rev_info *info, char *base) info->message_id = strbuf_detach(&buf, NULL); } -static void print_signature(FILE *file) +static void print_signature(const char *signature, FILE *file) { if (!signature || !*signature) return; @@ -1268,14 +1362,15 @@ static void prepare_cover_text(struct pretty_print_context *pp, const char *branch_name, struct strbuf *sb, const char *encoding, - int need_8bit_cte) + int need_8bit_cte, + const struct format_config *cfg) { const char *subject = "*** SUBJECT HERE ***"; const char *body = "*** BLURB HERE ***"; struct strbuf description_sb = STRBUF_INIT; struct strbuf subject_sb = STRBUF_INIT; - if (cover_from_description_mode == COVER_FROM_NONE) + if (cfg->cover_from_description_mode == COVER_FROM_NONE) goto do_pp; if (description_file && *description_file) @@ -1285,13 +1380,13 @@ static void prepare_cover_text(struct pretty_print_context *pp, if (!description_sb.len) goto do_pp; - if (cover_from_description_mode == COVER_FROM_SUBJECT || - cover_from_description_mode == COVER_FROM_AUTO) + if (cfg->cover_from_description_mode == COVER_FROM_SUBJECT || + cfg->cover_from_description_mode == COVER_FROM_AUTO) body = format_subject(&subject_sb, description_sb.buf, " "); - if (cover_from_description_mode == COVER_FROM_MESSAGE || - (cover_from_description_mode == COVER_FROM_AUTO && - subject_sb.len > COVER_FROM_AUTO_MAX_SUBJECT_LEN)) + if (cfg->cover_from_description_mode == COVER_FROM_MESSAGE || + (cfg->cover_from_description_mode == COVER_FROM_AUTO && + subject_sb.len > COVER_FROM_AUTO_MAX_SUBJECT_LEN)) body = description_sb.buf; else subject = subject_sb.buf; @@ -1328,7 +1423,8 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file, int nr, struct commit **list, const char *description_file, const char *branch_name, - int quiet) + int quiet, + const struct format_config *cfg) { const char *committer; struct shortlog log; @@ -1367,7 +1463,7 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file, pp.encode_email_headers = rev->encode_email_headers; pp_user_info(&pp, NULL, &sb, committer, encoding); prepare_cover_text(&pp, description_file, branch_name, &sb, - encoding, need_8bit_cte); + encoding, need_8bit_cte, cfg); fprintf(rev->diffopt.file, "%s\n", sb.buf); free(pp.after_subject); @@ -1468,29 +1564,30 @@ static const char * const builtin_format_patch_usage[] = { NULL }; -static int keep_subject = 0; +struct keep_callback_data { + struct format_config *cfg; + struct rev_info *revs; +}; static int keep_callback(const struct option *opt, const char *arg, int unset) { + struct keep_callback_data *data = opt->value; BUG_ON_OPT_NEG(unset); BUG_ON_OPT_ARG(arg); - ((struct rev_info *)opt->value)->total = -1; - keep_subject = 1; + data->revs->total = -1; + data->cfg->keep_subject = 1; return 0; } -static int subject_prefix = 0; - static int subject_prefix_callback(const struct option *opt, const char *arg, int unset) { - struct strbuf *sprefix; + struct format_config *cfg = opt->value; BUG_ON_OPT_NEG(unset); - sprefix = opt->value; - subject_prefix = 1; - strbuf_reset(sprefix); - strbuf_addstr(sprefix, arg); + cfg->subject_prefix = 1; + strbuf_reset(&cfg->sprefix); + strbuf_addstr(&cfg->sprefix, arg); return 0; } @@ -1507,15 +1604,14 @@ static int rfc_callback(const struct option *opt, const char *arg, return 0; } -static int numbered_cmdline_opt = 0; - static int numbered_callback(const struct option *opt, const char *arg, int unset) { + struct format_config *cfg = opt->value; BUG_ON_OPT_ARG(arg); - *(int *)opt->value = numbered_cmdline_opt = unset ? 0 : 1; + cfg->numbered = cfg->numbered_cmdline_opt = unset ? 0 : 1; if (unset) - auto_number = 0; + cfg->auto_number = 0; return 0; } @@ -1539,13 +1635,14 @@ static int output_directory_callback(const struct option *opt, const char *arg, static int thread_callback(const struct option *opt, const char *arg, int unset) { - enum thread_level *thread = (enum thread_level *)opt->value; + struct format_config *cfg = opt->value; + if (unset) - *thread = THREAD_UNSET; + cfg->thread = THREAD_UNSET; else if (!arg || !strcmp(arg, "shallow")) - *thread = THREAD_SHALLOW; + cfg->thread = THREAD_SHALLOW; else if (!strcmp(arg, "deep")) - *thread = THREAD_DEEP; + cfg->thread = THREAD_DEEP; /* * Please update _git_formatpatch() in git-completion.bash * when you add new options. @@ -1581,15 +1678,17 @@ static int inline_callback(const struct option *opt, const char *arg, int unset) return 0; } -static int header_callback(const struct option *opt UNUSED, const char *arg, +static int header_callback(const struct option *opt, const char *arg, int unset) { + struct format_config *cfg = opt->value; + if (unset) { - string_list_clear(&extra_hdr, 0); - string_list_clear(&extra_to, 0); - string_list_clear(&extra_cc, 0); + string_list_clear(&cfg->extra_hdr, 0); + string_list_clear(&cfg->extra_to, 0); + string_list_clear(&cfg->extra_cc, 0); } else { - add_header(arg); + add_header(cfg, arg); } return 0; } @@ -1611,17 +1710,17 @@ static int from_callback(const struct option *opt, const char *arg, int unset) static int base_callback(const struct option *opt, const char *arg, int unset) { - const char **base_commit = opt->value; + struct format_config *cfg = opt->value; if (unset) { - auto_base = AUTO_BASE_NEVER; - *base_commit = NULL; + cfg->auto_base = AUTO_BASE_NEVER; + FREE_AND_NULL(cfg->base_commit); } else if (!strcmp(arg, "auto")) { - auto_base = AUTO_BASE_ALWAYS; - *base_commit = NULL; + cfg->auto_base = AUTO_BASE_ALWAYS; + FREE_AND_NULL(cfg->base_commit); } else { - auto_base = AUTO_BASE_NEVER; - *base_commit = arg; + cfg->auto_base = AUTO_BASE_NEVER; + cfg->base_commit = xstrdup(arg); } return 0; } @@ -1632,7 +1731,7 @@ struct base_tree_info { struct object_id *patch_id; }; -static struct commit *get_base_commit(const char *base_commit, +static struct commit *get_base_commit(const struct format_config *cfg, struct commit **list, int total) { @@ -1640,9 +1739,9 @@ static struct commit *get_base_commit(const char *base_commit, struct commit **rev; int i = 0, rev_nr = 0, auto_select, die_on_failure, ret; - switch (auto_base) { + switch (cfg->auto_base) { case AUTO_BASE_NEVER: - if (base_commit) { + if (cfg->base_commit) { auto_select = 0; die_on_failure = 1; } else { @@ -1652,11 +1751,11 @@ static struct commit *get_base_commit(const char *base_commit, break; case AUTO_BASE_ALWAYS: case AUTO_BASE_WHEN_ABLE: - if (base_commit) { + if (cfg->base_commit) { BUG("requested automatic base selection but a commit was provided"); } else { auto_select = 1; - die_on_failure = auto_base == AUTO_BASE_ALWAYS; + die_on_failure = cfg->auto_base == AUTO_BASE_ALWAYS; } break; default: @@ -1664,9 +1763,9 @@ static struct commit *get_base_commit(const char *base_commit, } if (!auto_select) { - base = lookup_commit_reference_by_name(base_commit); + base = lookup_commit_reference_by_name(cfg->base_commit); if (!base) - die(_("unknown commit %s"), base_commit); + die(_("unknown commit %s"), cfg->base_commit); } else { struct branch *curr_branch = branch_get(NULL); const char *upstream = branch_get_upstream(curr_branch, NULL); @@ -1884,6 +1983,7 @@ static void infer_range_diff_ranges(struct strbuf *r1, int cmd_format_patch(int argc, const char **argv, const char *prefix) { + struct format_config cfg; struct commit *commit; struct commit **list = NULL; struct rev_info rev; @@ -1908,7 +2008,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) char *cover_from_description_arg = NULL; char *description_file = NULL; char *branch_name = NULL; - char *base_commit = NULL; struct base_tree_info bases; struct commit *base; int show_progress = 0; @@ -1919,18 +2018,24 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) struct strbuf rdiff1 = STRBUF_INIT; struct strbuf rdiff2 = STRBUF_INIT; struct strbuf rdiff_title = STRBUF_INIT; - struct strbuf sprefix = STRBUF_INIT; const char *rfc = NULL; int creation_factor = -1; + const char *signature = git_version_string; + const char *signature_file_arg = NULL; + struct keep_callback_data keep_callback_data = { + .cfg = &cfg, + .revs = &rev, + }; + const char *fmt_patch_suffix = NULL; const struct option builtin_format_patch_options[] = { - OPT_CALLBACK_F('n', "numbered", &numbered, NULL, + OPT_CALLBACK_F('n', "numbered", &cfg, NULL, N_("use [PATCH n/m] even with a single patch"), PARSE_OPT_NOARG, numbered_callback), - OPT_CALLBACK_F('N', "no-numbered", &numbered, NULL, + OPT_CALLBACK_F('N', "no-numbered", &cfg, NULL, N_("use [PATCH] even with multiple patches"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, no_numbered_callback), - OPT_BOOL('s', "signoff", &do_signoff, N_("add a Signed-off-by trailer")), + OPT_BOOL('s', "signoff", &cfg.do_signoff, N_("add a Signed-off-by trailer")), OPT_BOOL(0, "stdout", &use_stdout, N_("print patches to standard out")), OPT_BOOL(0, "cover-letter", &cover_letter, @@ -1943,7 +2048,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) N_("start numbering patches at instead of 1")), OPT_STRING('v', "reroll-count", &reroll_count, N_("reroll-count"), N_("mark the series as Nth re-roll")), - OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max, + OPT_INTEGER(0, "filename-max-length", &cfg.log.fmt_patch_name_max, N_("max length of output filename")), OPT_CALLBACK_F(0, "rfc", &rfc, N_("rfc"), N_("add (default 'RFC') before 'PATCH'"), @@ -1953,13 +2058,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) N_("generate parts of a cover letter based on a branch's description")), OPT_FILENAME(0, "description-file", &description_file, N_("use branch description from file")), - OPT_CALLBACK_F(0, "subject-prefix", &sprefix, N_("prefix"), + OPT_CALLBACK_F(0, "subject-prefix", &cfg, N_("prefix"), N_("use [] instead of [PATCH]"), PARSE_OPT_NONEG, subject_prefix_callback), OPT_CALLBACK_F('o', "output-directory", &output_directory, N_("dir"), N_("store resulting files in "), PARSE_OPT_NONEG, output_directory_callback), - OPT_CALLBACK_F('k', "keep-subject", &rev, NULL, + OPT_CALLBACK_F('k', "keep-subject", &keep_callback_data, NULL, N_("don't strip/add [PATCH]"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback), OPT_BOOL(0, "no-binary", &no_binary_diff, @@ -1972,11 +2077,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) N_("show patch format instead of default (patch + stat)"), 1, PARSE_OPT_NONEG), OPT_GROUP(N_("Messaging")), - OPT_CALLBACK(0, "add-header", NULL, N_("header"), + OPT_CALLBACK(0, "add-header", &cfg, N_("header"), N_("add email header"), header_callback), - OPT_STRING_LIST(0, "to", &extra_to, N_("email"), N_("add To: header")), - OPT_STRING_LIST(0, "cc", &extra_cc, N_("email"), N_("add Cc: header")), - OPT_CALLBACK_F(0, "from", &from, N_("ident"), + OPT_STRING_LIST(0, "to", &cfg.extra_to, N_("email"), N_("add To: header")), + OPT_STRING_LIST(0, "cc", &cfg.extra_cc, N_("email"), N_("add Cc: header")), + OPT_CALLBACK_F(0, "from", &cfg.from, N_("ident"), N_("set From address to (or committer ident if absent)"), PARSE_OPT_OPTARG, from_callback), OPT_STRING(0, "in-reply-to", &in_reply_to, N_("message-id"), @@ -1988,15 +2093,15 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) N_("inline the patch"), PARSE_OPT_OPTARG | PARSE_OPT_NONEG, inline_callback), - OPT_CALLBACK_F(0, "thread", &thread, N_("style"), + OPT_CALLBACK_F(0, "thread", &cfg, N_("style"), N_("enable message threading, styles: shallow, deep"), PARSE_OPT_OPTARG, thread_callback), OPT_STRING(0, "signature", &signature, N_("signature"), N_("add a signature")), - OPT_CALLBACK_F(0, "base", &base_commit, N_("base-commit"), + OPT_CALLBACK_F(0, "base", &cfg, N_("base-commit"), N_("add prerequisite tree info to the patch series"), 0, base_callback), - OPT_FILENAME(0, "signature-file", &signature_file, + OPT_FILENAME(0, "signature-file", &signature_file_arg, N_("add a signature from a file")), OPT__QUIET(&quiet, N_("don't print the patch filenames")), OPT_BOOL(0, "progress", &show_progress, @@ -2013,20 +2118,17 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) OPT_END() }; - extra_hdr.strdup_strings = 1; - extra_to.strdup_strings = 1; - extra_cc.strdup_strings = 1; - - init_log_defaults(); - init_display_notes(¬es_opt); - git_config(git_format_config, NULL); + format_config_init(&cfg); + init_diff_ui_defaults(); + init_display_notes(&cfg.notes_opt); + git_config(git_format_config, &cfg); repo_init_revisions(the_repository, &rev, prefix); git_config(grep_config, &rev.grep_filter); - rev.show_notes = show_notes; - memcpy(&rev.notes_opt, ¬es_opt, sizeof(notes_opt)); + rev.show_notes = cfg.show_notes; + memcpy(&rev.notes_opt, &cfg.notes_opt, sizeof(cfg.notes_opt)); rev.commit_format = CMIT_FMT_EMAIL; - rev.encode_email_headers = default_encode_email_headers; + rev.encode_email_headers = cfg.log.default_encode_email_headers; rev.expand_tabs_in_log_default = 0; rev.verbose_header = 1; rev.diff = 1; @@ -2037,12 +2139,12 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) s_r_opt.def = "HEAD"; s_r_opt.revarg_opt = REVARG_COMMITTISH; - strbuf_addstr(&sprefix, fmt_patch_subject_prefix); + strbuf_addstr(&cfg.sprefix, cfg.log.fmt_patch_subject_prefix); if (format_no_prefix) diff_set_noprefix(&rev.diffopt); - if (default_attach) { - rev.mime_boundary = default_attach; + if (cfg.default_attach) { + rev.mime_boundary = cfg.default_attach; rev.no_inline = 1; } @@ -2058,60 +2160,63 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) rev.force_in_body_from = force_in_body_from; + if (!fmt_patch_suffix) + fmt_patch_suffix = cfg.fmt_patch_suffix; + /* Make sure "0000-$sub.patch" gives non-negative length for $sub */ - if (fmt_patch_name_max <= strlen("0000-") + strlen(fmt_patch_suffix)) - fmt_patch_name_max = strlen("0000-") + strlen(fmt_patch_suffix); + if (cfg.log.fmt_patch_name_max <= strlen("0000-") + strlen(fmt_patch_suffix)) + cfg.log.fmt_patch_name_max = strlen("0000-") + strlen(fmt_patch_suffix); if (cover_from_description_arg) - cover_from_description_mode = parse_cover_from_description(cover_from_description_arg); + cfg.cover_from_description_mode = parse_cover_from_description(cover_from_description_arg); if (rfc && rfc[0]) { - subject_prefix = 1; + cfg.subject_prefix = 1; if (rfc[0] == '-') - strbuf_addf(&sprefix, " %s", rfc + 1); + strbuf_addf(&cfg.sprefix, " %s", rfc + 1); else - strbuf_insertf(&sprefix, 0, "%s ", rfc); + strbuf_insertf(&cfg.sprefix, 0, "%s ", rfc); } if (reroll_count) { - strbuf_addf(&sprefix, " v%s", reroll_count); + strbuf_addf(&cfg.sprefix, " v%s", reroll_count); rev.reroll_count = reroll_count; } - rev.subject_prefix = sprefix.buf; + rev.subject_prefix = cfg.sprefix.buf; - for (i = 0; i < extra_hdr.nr; i++) { - strbuf_addstr(&buf, extra_hdr.items[i].string); + for (i = 0; i < cfg.extra_hdr.nr; i++) { + strbuf_addstr(&buf, cfg.extra_hdr.items[i].string); strbuf_addch(&buf, '\n'); } - if (extra_to.nr) + if (cfg.extra_to.nr) strbuf_addstr(&buf, "To: "); - for (i = 0; i < extra_to.nr; i++) { + for (i = 0; i < cfg.extra_to.nr; i++) { if (i) strbuf_addstr(&buf, " "); - strbuf_addstr(&buf, extra_to.items[i].string); - if (i + 1 < extra_to.nr) + strbuf_addstr(&buf, cfg.extra_to.items[i].string); + if (i + 1 < cfg.extra_to.nr) strbuf_addch(&buf, ','); strbuf_addch(&buf, '\n'); } - if (extra_cc.nr) + if (cfg.extra_cc.nr) strbuf_addstr(&buf, "Cc: "); - for (i = 0; i < extra_cc.nr; i++) { + for (i = 0; i < cfg.extra_cc.nr; i++) { if (i) strbuf_addstr(&buf, " "); - strbuf_addstr(&buf, extra_cc.items[i].string); - if (i + 1 < extra_cc.nr) + strbuf_addstr(&buf, cfg.extra_cc.items[i].string); + if (i + 1 < cfg.extra_cc.nr) strbuf_addch(&buf, ','); strbuf_addch(&buf, '\n'); } rev.extra_headers = to_free = strbuf_detach(&buf, NULL); - if (from) { - if (split_ident_line(&rev.from_ident, from, strlen(from))) - die(_("invalid ident line: %s"), from); + if (cfg.from) { + if (split_ident_line(&rev.from_ident, cfg.from, strlen(cfg.from))) + die(_("invalid ident line: %s"), cfg.from); } if (start_number < 0) @@ -2122,14 +2227,14 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) * and it would conflict with --keep-subject (-k) from the * command line, reset "numbered". */ - if (numbered && keep_subject && !numbered_cmdline_opt) - numbered = 0; + if (cfg.numbered && cfg.keep_subject && !cfg.numbered_cmdline_opt) + cfg.numbered = 0; - if (numbered && keep_subject) + if (cfg.numbered && cfg.keep_subject) die(_("options '%s' and '%s' cannot be used together"), "-n", "-k"); - if (keep_subject && subject_prefix) + if (cfg.keep_subject && cfg.subject_prefix) die(_("options '%s' and '%s' cannot be used together"), "--subject-prefix/--rfc", "-k"); - rev.preserve_subject = keep_subject; + rev.preserve_subject = cfg.keep_subject; argc = setup_revisions(argc, argv, &rev, &s_r_opt); if (argc > 1) @@ -2156,7 +2261,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) rev.always_show_header = 1; rev.zero_commit = zero_commit; - rev.patch_name_max = fmt_patch_name_max; + rev.patch_name_max = cfg.log.fmt_patch_name_max; if (!rev.diffopt.flags.text && !no_binary_diff) rev.diffopt.flags.binary = 1; @@ -2177,7 +2282,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) int saved; if (!output_directory) - output_directory = config_output_directory; + output_directory = cfg.config_output_directory; output_directory = set_outdir(prefix, output_directory); if (rev.diffopt.use_color != GIT_COLOR_ALWAYS) @@ -2275,14 +2380,14 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) goto done; total = nr; if (cover_letter == -1) { - if (config_cover_letter == COVER_AUTO) + if (cfg.config_cover_letter == COVER_AUTO) cover_letter = (total > 1); else - cover_letter = (config_cover_letter == COVER_ON); + cover_letter = (cfg.config_cover_letter == COVER_ON); } - if (!keep_subject && auto_number && (total > 1 || cover_letter)) - numbered = 1; - if (numbered) + if (!cfg.keep_subject && cfg.auto_number && (total > 1 || cover_letter)) + cfg.numbered = 1; + if (cfg.numbered) rev.total = total + start_number - 1; if (idiff_prev.nr) { @@ -2314,27 +2419,40 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) _("Range-diff against v%d:")); } + /* + * The order of precedence is: + * + * 1. The `--signature` and `--no-signature` options. + * 2. The `--signature-file` option. + * 3. The `format.signature` config. + * 4. The `format.signatureFile` config. + * 5. Default `git_version_string`. + */ if (!signature) { ; /* --no-signature inhibits all signatures */ } else if (signature && signature != git_version_string) { ; /* non-default signature already set */ - } else if (signature_file) { + } else if (signature_file_arg || (cfg.signature_file && !cfg.signature)) { struct strbuf buf = STRBUF_INIT; + const char *signature_file = signature_file_arg ? + signature_file_arg : cfg.signature_file; if (strbuf_read_file(&buf, signature_file, 128) < 0) die_errno(_("unable to read signature file '%s'"), signature_file); signature = strbuf_detach(&buf, NULL); + } else if (cfg.signature) { + signature = cfg.signature; } memset(&bases, 0, sizeof(bases)); - base = get_base_commit(base_commit, list, nr); + base = get_base_commit(&cfg, list, nr); if (base) { reset_revision_walk(); clear_object_flags(UNINTERESTING); prepare_bases(&bases, base, list, nr); } - if (in_reply_to || thread || cover_letter) { + if (in_reply_to || cfg.thread || cover_letter) { rev.ref_message_ids = xmalloc(sizeof(*rev.ref_message_ids)); string_list_init_dup(rev.ref_message_ids); } @@ -2345,19 +2463,19 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) rev.numbered_files = just_numbers; rev.patch_suffix = fmt_patch_suffix; if (cover_letter) { - if (thread) + if (cfg.thread) gen_message_id(&rev, "cover"); make_cover_letter(&rev, !!output_directory, - origin, nr, list, description_file, branch_name, quiet); + origin, nr, list, description_file, branch_name, quiet, &cfg); print_bases(&bases, rev.diffopt.file); - print_signature(rev.diffopt.file); + print_signature(signature, rev.diffopt.file); total++; start_number--; /* interdiff/range-diff in cover-letter; omit from patches */ rev.idiff_oid1 = NULL; rev.rdiff1 = NULL; } - rev.add_signoff = do_signoff; + rev.add_signoff = cfg.do_signoff; if (show_progress) progress = start_delayed_progress(_("Generating patches"), total); @@ -2367,7 +2485,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) commit = list[nr]; rev.nr = total - nr + (start_number - 1); /* Make the second and subsequent mails replies to the first */ - if (thread) { + if (cfg.thread) { /* Have we already had a message ID? */ if (rev.message_id) { /* @@ -2391,7 +2509,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) * letter is a reply to the * --in-reply-to, if specified. */ - if (thread == THREAD_SHALLOW + if (cfg.thread == THREAD_SHALLOW && rev.ref_message_ids->nr > 0 && (!cover_letter || rev.nr > 1)) free(rev.message_id); @@ -2424,7 +2542,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) mime_boundary_leader, rev.mime_boundary); else - print_signature(rev.diffopt.file); + print_signature(signature, rev.diffopt.file); } if (output_directory) fclose(rev.diffopt.file); @@ -2432,9 +2550,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) stop_progress(&progress); free(list); free(branch_name); - string_list_clear(&extra_to, 0); - string_list_clear(&extra_cc, 0); - string_list_clear(&extra_hdr, 0); if (ignore_if_in_upstream) free_patch_ids(&ids); @@ -2444,13 +2559,14 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) strbuf_release(&rdiff1); strbuf_release(&rdiff2); strbuf_release(&rdiff_title); - strbuf_release(&sprefix); free(to_free); free(rev.message_id); if (rev.ref_message_ids) string_list_clear(rev.ref_message_ids, 0); free(rev.ref_message_ids); - return cmd_log_deinit(0, &rev); + release_revisions(&rev); + format_config_release(&cfg); + return 0; } static int add_pending_commit(const char *arg, struct rev_info *revs, int flags) diff --git a/builtin/merge.c b/builtin/merge.c index e4bd65eeba..daed2d4e1e 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -100,7 +100,7 @@ static struct strategy all_strategy[] = { { "subtree", NO_FAST_FORWARD | NO_TRIVIAL }, }; -static const char *pull_twohead, *pull_octopus; +static char *pull_twohead, *pull_octopus; enum ff_type { FF_NO, @@ -110,7 +110,7 @@ enum ff_type { static enum ff_type fast_forward = FF_ALLOW; -static const char *cleanup_arg; +static char *cleanup_arg; static enum commit_msg_cleanup_mode cleanup_mode; static int option_parse_message(const struct option *opt, diff --git a/builtin/mv.c b/builtin/mv.c index 74aa9746aa..81ca910de6 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -20,6 +20,7 @@ #include "read-cache-ll.h" #include "repository.h" #include "setup.h" +#include "strvec.h" #include "submodule.h" #include "entry.h" @@ -38,45 +39,35 @@ enum update_mode { #define DUP_BASENAME 1 #define KEEP_TRAILING_SLASH 2 -static const char **internal_prefix_pathspec(const char *prefix, - const char **pathspec, - int count, unsigned flags) +static void internal_prefix_pathspec(struct strvec *out, + const char *prefix, + const char **pathspec, + int count, unsigned flags) { - int i; - const char **result; int prefixlen = prefix ? strlen(prefix) : 0; - ALLOC_ARRAY(result, count + 1); /* Create an intermediate copy of the pathspec based on the flags */ - for (i = 0; i < count; i++) { - int length = strlen(pathspec[i]); - int to_copy = length; - char *it; + for (int i = 0; i < count; i++) { + size_t length = strlen(pathspec[i]); + size_t to_copy = length; + const char *maybe_basename; + char *trimmed, *prefixed_path; + while (!(flags & KEEP_TRAILING_SLASH) && to_copy > 0 && is_dir_sep(pathspec[i][to_copy - 1])) to_copy--; - it = xmemdupz(pathspec[i], to_copy); - if (flags & DUP_BASENAME) { - result[i] = xstrdup(basename(it)); - free(it); - } else { - result[i] = it; - } - } - result[count] = NULL; + trimmed = xmemdupz(pathspec[i], to_copy); + maybe_basename = (flags & DUP_BASENAME) ? basename(trimmed) : trimmed; + prefixed_path = prefix_path(prefix, prefixlen, maybe_basename); + strvec_push(out, prefixed_path); - /* Prefix the pathspec and free the old intermediate strings */ - for (i = 0; i < count; i++) { - const char *match = prefix_path(prefix, prefixlen, result[i]); - free((char *) result[i]); - result[i] = match; + free(prefixed_path); + free(trimmed); } - - return result; } -static const char *add_slash(const char *path) +static char *add_slash(const char *path) { size_t len = strlen(path); if (len && path[len - 1] != '/') { @@ -86,32 +77,34 @@ static const char *add_slash(const char *path) with_slash[len] = 0; return with_slash; } - return path; + return xstrdup(path); } #define SUBMODULE_WITH_GITDIR ((const char *)1) -static void prepare_move_submodule(const char *src, int first, - const char **submodule_gitfile) +static const char *submodule_gitfile_path(const char *src, int first) { struct strbuf submodule_dotgit = STRBUF_INIT; + const char *path; + if (!S_ISGITLINK(the_repository->index->cache[first]->ce_mode)) die(_("Directory %s is in index and no submodule?"), src); if (!is_staging_gitmodules_ok(the_repository->index)) die(_("Please stage your changes to .gitmodules or stash them to proceed")); + strbuf_addf(&submodule_dotgit, "%s/.git", src); - *submodule_gitfile = read_gitfile(submodule_dotgit.buf); - if (*submodule_gitfile) - *submodule_gitfile = xstrdup(*submodule_gitfile); - else - *submodule_gitfile = SUBMODULE_WITH_GITDIR; + + path = read_gitfile(submodule_dotgit.buf); strbuf_release(&submodule_dotgit); + if (path) + return path; + return SUBMODULE_WITH_GITDIR; } static int index_range_of_same_dir(const char *src, int length, int *first_p, int *last_p) { - const char *src_w_slash = add_slash(src); + char *src_w_slash = add_slash(src); int first, last, len_w_slash = length + 1; first = index_name_pos(the_repository->index, src_w_slash, len_w_slash); @@ -124,8 +117,8 @@ static int index_range_of_same_dir(const char *src, int length, if (strncmp(path, src_w_slash, len_w_slash)) break; } - if (src_w_slash != src) - free((char *)src_w_slash); + + free(src_w_slash); *first_p = first; *last_p = last; return last - first; @@ -141,7 +134,7 @@ static int index_range_of_same_dir(const char *src, int length, static int empty_dir_has_sparse_contents(const char *name) { int ret = 0; - const char *with_slash = add_slash(name); + char *with_slash = add_slash(name); int length = strlen(with_slash); int pos = index_name_pos(the_repository->index, with_slash, length); @@ -159,8 +152,7 @@ static int empty_dir_has_sparse_contents(const char *name) } free_return: - if (with_slash != name) - free((char *)with_slash); + free(with_slash); return ret; } @@ -177,18 +169,23 @@ int cmd_mv(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "sparse", &ignore_sparse, N_("allow updating entries outside of the sparse-checkout cone")), OPT_END(), }; - const char **source, **destination, **dest_path, **submodule_gitfile; - const char *dst_w_slash; + struct strvec sources = STRVEC_INIT; + struct strvec dest_paths = STRVEC_INIT; + struct strvec destinations = STRVEC_INIT; + struct strvec submodule_gitfiles_to_free = STRVEC_INIT; + const char **submodule_gitfiles; + char *dst_w_slash = NULL; const char **src_dir = NULL; int src_dir_nr = 0, src_dir_alloc = 0; struct strbuf a_src_dir = STRBUF_INIT; enum update_mode *modes, dst_mode = 0; struct stat st, dest_st; - struct string_list src_for_dst = STRING_LIST_INIT_NODUP; + struct string_list src_for_dst = STRING_LIST_INIT_DUP; struct lock_file lock_file = LOCK_INIT; struct cache_entry *ce; - struct string_list only_match_skip_worktree = STRING_LIST_INIT_NODUP; - struct string_list dirty_paths = STRING_LIST_INIT_NODUP; + struct string_list only_match_skip_worktree = STRING_LIST_INIT_DUP; + struct string_list dirty_paths = STRING_LIST_INIT_DUP; + int ret; git_config(git_default_config, NULL); @@ -201,7 +198,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (repo_read_index(the_repository) < 0) die(_("index file corrupt")); - source = internal_prefix_pathspec(prefix, argv, argc, 0); + internal_prefix_pathspec(&sources, prefix, argv, argc, 0); CALLOC_ARRAY(modes, argc); /* @@ -212,45 +209,39 @@ int cmd_mv(int argc, const char **argv, const char *prefix) flags = KEEP_TRAILING_SLASH; if (argc == 1 && is_directory(argv[0]) && !is_directory(argv[1])) flags = 0; - dest_path = internal_prefix_pathspec(prefix, argv + argc, 1, flags); - dst_w_slash = add_slash(dest_path[0]); - submodule_gitfile = xcalloc(argc, sizeof(char *)); + internal_prefix_pathspec(&dest_paths, prefix, argv + argc, 1, flags); + dst_w_slash = add_slash(dest_paths.v[0]); + submodule_gitfiles = xcalloc(argc, sizeof(char *)); - if (dest_path[0][0] == '\0') + if (dest_paths.v[0][0] == '\0') /* special case: "." was normalized to "" */ - destination = internal_prefix_pathspec(dest_path[0], argv, argc, DUP_BASENAME); - else if (!lstat(dest_path[0], &st) && - S_ISDIR(st.st_mode)) { - destination = internal_prefix_pathspec(dst_w_slash, argv, argc, DUP_BASENAME); + internal_prefix_pathspec(&destinations, dest_paths.v[0], argv, argc, DUP_BASENAME); + else if (!lstat(dest_paths.v[0], &st) && S_ISDIR(st.st_mode)) { + internal_prefix_pathspec(&destinations, dst_w_slash, argv, argc, DUP_BASENAME); + } else if (!path_in_sparse_checkout(dst_w_slash, the_repository->index) && + empty_dir_has_sparse_contents(dst_w_slash)) { + internal_prefix_pathspec(&destinations, dst_w_slash, argv, argc, DUP_BASENAME); + dst_mode = SKIP_WORKTREE_DIR; + } else if (argc != 1) { + die(_("destination '%s' is not a directory"), dest_paths.v[0]); } else { - if (!path_in_sparse_checkout(dst_w_slash, the_repository->index) && - empty_dir_has_sparse_contents(dst_w_slash)) { - destination = internal_prefix_pathspec(dst_w_slash, argv, argc, DUP_BASENAME); - dst_mode = SKIP_WORKTREE_DIR; - } else if (argc != 1) { - die(_("destination '%s' is not a directory"), dest_path[0]); - } else { - destination = dest_path; - /* - * is a file outside of sparse-checkout - * cone. Insist on cone mode here for backward - * compatibility. We don't want dst_mode to be assigned - * for a file when the repo is using no-cone mode (which - * is deprecated at this point) sparse-checkout. As - * SPARSE here is only considering cone-mode situation. - */ - if (!path_in_cone_mode_sparse_checkout(destination[0], the_repository->index)) - dst_mode = SPARSE; - } - } - if (dst_w_slash != dest_path[0]) { - free((char *)dst_w_slash); - dst_w_slash = NULL; + strvec_pushv(&destinations, dest_paths.v); + + /* + * is a file outside of sparse-checkout + * cone. Insist on cone mode here for backward + * compatibility. We don't want dst_mode to be assigned + * for a file when the repo is using no-cone mode (which + * is deprecated at this point) sparse-checkout. As + * SPARSE here is only considering cone-mode situation. + */ + if (!path_in_cone_mode_sparse_checkout(destinations.v[0], the_repository->index)) + dst_mode = SPARSE; } /* Checking */ for (i = 0; i < argc; i++) { - const char *src = source[i], *dst = destination[i]; + const char *src = sources.v[i], *dst = destinations.v[i]; int length; const char *bad = NULL; int skip_sparse = 0; @@ -265,12 +256,14 @@ int cmd_mv(int argc, const char **argv, const char *prefix) pos = index_name_pos(the_repository->index, src, length); if (pos < 0) { - const char *src_w_slash = add_slash(src); + char *src_w_slash = add_slash(src); if (!path_in_sparse_checkout(src_w_slash, the_repository->index) && empty_dir_has_sparse_contents(src)) { + free(src_w_slash); modes[i] |= SKIP_WORKTREE_DIR; goto dir_check; } + free(src_w_slash); /* only error if existence is expected. */ if (!(modes[i] & SPARSE)) bad = _("bad source"); @@ -310,12 +303,16 @@ int cmd_mv(int argc, const char **argv, const char *prefix) dir_check: if (S_ISDIR(st.st_mode)) { - int j, dst_len, n; + char *dst_with_slash; + size_t dst_with_slash_len; + int j, n; int first = index_name_pos(the_repository->index, src, length), last; if (first >= 0) { - prepare_move_submodule(src, first, - submodule_gitfile + i); + const char *path = submodule_gitfile_path(src, first); + if (path != SUBMODULE_WITH_GITDIR) + path = strvec_push(&submodule_gitfiles_to_free, path); + submodule_gitfiles[i] = path; goto act_on_entry; } else if (index_range_of_same_dir(src, length, &first, &last) < 1) { @@ -330,24 +327,28 @@ int cmd_mv(int argc, const char **argv, const char *prefix) src_dir[src_dir_nr++] = src; n = argc + last - first; - REALLOC_ARRAY(source, n); - REALLOC_ARRAY(destination, n); REALLOC_ARRAY(modes, n); - REALLOC_ARRAY(submodule_gitfile, n); + REALLOC_ARRAY(submodule_gitfiles, n); - dst = add_slash(dst); - dst_len = strlen(dst); + dst_with_slash = add_slash(dst); + dst_with_slash_len = strlen(dst_with_slash); for (j = 0; j < last - first; j++) { const struct cache_entry *ce = the_repository->index->cache[first + j]; const char *path = ce->name; - source[argc + j] = path; - destination[argc + j] = - prefix_path(dst, dst_len, path + length + 1); + char *prefixed_path = prefix_path(dst_with_slash, dst_with_slash_len, path + length + 1); + + strvec_push(&sources, path); + strvec_push(&destinations, prefixed_path); + memset(modes + argc + j, 0, sizeof(enum update_mode)); modes[argc + j] |= ce_skip_worktree(ce) ? SPARSE : INDEX; - submodule_gitfile[argc + j] = NULL; + submodule_gitfiles[argc + j] = NULL; + + free(prefixed_path); } + + free(dst_with_slash); argc += last - first; goto act_on_entry; } @@ -428,23 +429,25 @@ int cmd_mv(int argc, const char **argv, const char *prefix) remove_entry: if (--argc > 0) { int n = argc - i; - MOVE_ARRAY(source + i, source + i + 1, n); - MOVE_ARRAY(destination + i, destination + i + 1, n); + strvec_remove(&sources, i); + strvec_remove(&destinations, i); MOVE_ARRAY(modes + i, modes + i + 1, n); - MOVE_ARRAY(submodule_gitfile + i, - submodule_gitfile + i + 1, n); + MOVE_ARRAY(submodule_gitfiles + i, + submodule_gitfiles + i + 1, n); i--; } } if (only_match_skip_worktree.nr) { advise_on_updating_sparse_paths(&only_match_skip_worktree); - if (!ignore_errors) - return 1; + if (!ignore_errors) { + ret = 1; + goto out; + } } for (i = 0; i < argc; i++) { - const char *src = source[i], *dst = destination[i]; + const char *src = sources.v[i], *dst = destinations.v[i]; enum update_mode mode = modes[i]; int pos; int sparse_and_dirty = 0; @@ -464,12 +467,12 @@ int cmd_mv(int argc, const char **argv, const char *prefix) continue; die_errno(_("renaming '%s' failed"), src); } - if (submodule_gitfile[i]) { + if (submodule_gitfiles[i]) { if (!update_path_in_gitmodules(src, dst)) gitmodules_modified = 1; - if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR) + if (submodule_gitfiles[i] != SUBMODULE_WITH_GITDIR) connect_work_tree_and_git_dir(dst, - submodule_gitfile[i], + submodule_gitfiles[i], 1); } @@ -565,11 +568,18 @@ int cmd_mv(int argc, const char **argv, const char *prefix) COMMIT_LOCK | SKIP_IF_UNCHANGED)) die(_("Unable to write new index file")); + ret = 0; + +out: + free(dst_w_slash); string_list_clear(&src_for_dst, 0); string_list_clear(&dirty_paths, 0); - UNLEAK(source); - UNLEAK(dest_path); - free(submodule_gitfile); + string_list_clear(&only_match_skip_worktree, 0); + strvec_clear(&sources); + strvec_clear(&dest_paths); + strvec_clear(&destinations); + strvec_clear(&submodule_gitfiles_to_free); + free(submodule_gitfiles); free(modes); - return 0; + return ret; } diff --git a/builtin/rebase.c b/builtin/rebase.c index 0466d9414a..14d4f0a5e6 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -83,7 +83,7 @@ static const char *action_names[] = { struct rebase_options { enum rebase_type type; enum empty_type empty; - const char *default_backend; + char *default_backend; const char *state_dir; struct commit *upstream; const char *upstream_name; diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index be8969a84a..01c1f04ece 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -88,7 +88,7 @@ static struct strbuf push_cert = STRBUF_INIT; static struct object_id push_cert_oid; static struct signature_check sigcheck; static const char *push_cert_nonce; -static const char *cert_nonce_seed; +static char *cert_nonce_seed; static struct strvec hidden_refs = STRVEC_INIT; static const char *NONCE_UNSOLICITED = "UNSOLICITED"; @@ -168,13 +168,13 @@ static int receive_pack_config(const char *var, const char *value, } if (strcmp(var, "receive.fsck.skiplist") == 0) { - const char *path; + char *path; if (git_config_pathname(&path, var, value)) return 1; strbuf_addf(&fsck_msg_types, "%cskiplist=%s", fsck_msg_types.len ? ',' : '=', path); - free((char *)path); + free(path); return 0; } diff --git a/builtin/repack.c b/builtin/repack.c index 58ad82dd97..f0317fa94a 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -48,10 +48,10 @@ static const char incremental_bitmap_conflict_error[] = N_( ); struct pack_objects_args { - const char *window; - const char *window_memory; - const char *depth; - const char *threads; + char *window; + char *window_memory; + char *depth; + char *threads; unsigned long max_pack_size; int no_reuse_delta; int no_reuse_object; diff --git a/builtin/worktree.c b/builtin/worktree.c index 893e973871..1d51e54fcd 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -736,16 +736,14 @@ static int dwim_orphan(const struct add_opts *opts, int opt_track, int remote) return 1; } -static const char *dwim_branch(const char *path, const char **new_branch) +static char *dwim_branch(const char *path, char **new_branch) { int n; int branch_exists; const char *s = worktree_basename(path, &n); - const char *branchname = xstrndup(s, n); + char *branchname = xstrndup(s, n); struct strbuf ref = STRBUF_INIT; - UNLEAK(branchname); - branch_exists = !strbuf_check_branch_ref(&ref, branchname) && refs_ref_exists(get_main_ref_store(the_repository), ref.buf); @@ -756,8 +754,7 @@ static const char *dwim_branch(const char *path, const char **new_branch) *new_branch = branchname; if (guess_remote) { struct object_id oid; - const char *remote = - unique_tracking_name(*new_branch, &oid, NULL); + char *remote = unique_tracking_name(*new_branch, &oid, NULL); return remote; } return NULL; @@ -769,6 +766,8 @@ static int add(int ac, const char **av, const char *prefix) const char *new_branch_force = NULL; char *path; const char *branch; + char *branch_to_free = NULL; + char *new_branch_to_free = NULL; const char *new_branch = NULL; const char *opt_track = NULL; const char *lock_reason = NULL; @@ -859,16 +858,17 @@ static int add(int ac, const char **av, const char *prefix) opts.orphan = dwim_orphan(&opts, !!opt_track, 0); } else if (ac < 2) { /* DWIM: Guess branch name from path. */ - const char *s = dwim_branch(path, &new_branch); + char *s = dwim_branch(path, &new_branch_to_free); if (s) - branch = s; + branch = branch_to_free = s; + new_branch = new_branch_to_free; /* DWIM: Infer --orphan when repo has no refs. */ opts.orphan = (!s) && dwim_orphan(&opts, !!opt_track, 1); } else if (ac == 2) { struct object_id oid; struct commit *commit; - const char *remote; + char *remote; commit = lookup_commit_reference_by_name(branch); if (!commit) { @@ -923,6 +923,8 @@ static int add(int ac, const char **av, const char *prefix) ret = add_worktree(path, branch, &opts); free(path); + free(branch_to_free); + free(new_branch_to_free); return ret; } diff --git a/checkout.c b/checkout.c index 4256e71a7c..cfaea4bd10 100644 --- a/checkout.c +++ b/checkout.c @@ -45,8 +45,8 @@ static int check_tracking_name(struct remote *remote, void *cb_data) return 0; } -const char *unique_tracking_name(const char *name, struct object_id *oid, - int *dwim_remotes_matched) +char *unique_tracking_name(const char *name, struct object_id *oid, + int *dwim_remotes_matched) { struct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT; const char *default_remote = NULL; diff --git a/checkout.h b/checkout.h index 3c514a5ab4..ba15a13fb3 100644 --- a/checkout.h +++ b/checkout.h @@ -8,8 +8,8 @@ * tracking branch. Return the name of the remote if such a branch * exists, NULL otherwise. */ -const char *unique_tracking_name(const char *name, - struct object_id *oid, - int *dwim_remotes_matched); +char *unique_tracking_name(const char *name, + struct object_id *oid, + int *dwim_remotes_matched); #endif /* CHECKOUT_H */ diff --git a/commit-reach.c b/commit-reach.c index 8f9b008f87..384aee1ab3 100644 --- a/commit-reach.c +++ b/commit-reach.c @@ -1106,6 +1106,10 @@ void ahead_behind(struct repository *r, /* STALE is used here, PARENT2 is used by insert_no_dup(). */ repo_clear_commit_marks(r, PARENT2 | STALE); + while (prio_queue_peek(&queue)) { + struct commit *c = prio_queue_get(&queue); + free_bit_array(c); + } clear_bit_arrays(&bit_arrays); clear_prio_queue(&queue); } diff --git a/config.c b/config.c index 14461312b3..abce05b774 100644 --- a/config.c +++ b/config.c @@ -1338,7 +1338,7 @@ int git_config_bool(const char *name, const char *value) return v; } -int git_config_string(const char **dest, const char *var, const char *value) +int git_config_string(char **dest, const char *var, const char *value) { if (!value) return config_error_nonbool(var); @@ -1346,7 +1346,7 @@ int git_config_string(const char **dest, const char *var, const char *value) return 0; } -int git_config_pathname(const char **dest, const char *var, const char *value) +int git_config_pathname(char **dest, const char *var, const char *value) { if (!value) return config_error_nonbool(var); @@ -1414,11 +1414,15 @@ static int git_default_core_config(const char *var, const char *value, return 0; } - if (!strcmp(var, "core.attributesfile")) + if (!strcmp(var, "core.attributesfile")) { + FREE_AND_NULL(git_attributes_file); return git_config_pathname(&git_attributes_file, var, value); + } - if (!strcmp(var, "core.hookspath")) + if (!strcmp(var, "core.hookspath")) { + FREE_AND_NULL(git_hooks_path); return git_config_pathname(&git_hooks_path, var, value); + } if (!strcmp(var, "core.bare")) { is_bare_repository_cfg = git_config_bool(var, value); @@ -1553,8 +1557,10 @@ static int git_default_core_config(const char *var, const char *value, return 0; } - if (!strcmp(var, "core.checkroundtripencoding")) + if (!strcmp(var, "core.checkroundtripencoding")) { + FREE_AND_NULL(check_roundtrip_encoding); return git_config_string(&check_roundtrip_encoding, var, value); + } if (!strcmp(var, "core.notesref")) { if (!value) @@ -1563,8 +1569,10 @@ static int git_default_core_config(const char *var, const char *value, return 0; } - if (!strcmp(var, "core.editor")) + if (!strcmp(var, "core.editor")) { + FREE_AND_NULL(editor_program); return git_config_string(&editor_program, var, value); + } if (!strcmp(var, "core.commentchar") || !strcmp(var, "core.commentstring")) { @@ -1582,11 +1590,13 @@ static int git_default_core_config(const char *var, const char *value, return 0; } - if (!strcmp(var, "core.askpass")) + if (!strcmp(var, "core.askpass")) { + FREE_AND_NULL(askpass_program); return git_config_string(&askpass_program, var, value); + } if (!strcmp(var, "core.excludesfile")) { - free((char *)excludes_file); + FREE_AND_NULL(excludes_file); return git_config_pathname(&excludes_file, var, value); } @@ -1689,11 +1699,15 @@ static int git_default_sparse_config(const char *var, const char *value) static int git_default_i18n_config(const char *var, const char *value) { - if (!strcmp(var, "i18n.commitencoding")) + if (!strcmp(var, "i18n.commitencoding")) { + FREE_AND_NULL(git_commit_encoding); return git_config_string(&git_commit_encoding, var, value); + } - if (!strcmp(var, "i18n.logoutputencoding")) + if (!strcmp(var, "i18n.logoutputencoding")) { + FREE_AND_NULL(git_log_output_encoding); return git_config_string(&git_log_output_encoding, var, value); + } /* Add other config variables here and to Documentation/config.txt. */ return 0; @@ -1766,10 +1780,15 @@ static int git_default_push_config(const char *var, const char *value) static int git_default_mailmap_config(const char *var, const char *value) { - if (!strcmp(var, "mailmap.file")) + if (!strcmp(var, "mailmap.file")) { + FREE_AND_NULL(git_mailmap_file); return git_config_pathname(&git_mailmap_file, var, value); - if (!strcmp(var, "mailmap.blob")) + } + + if (!strcmp(var, "mailmap.blob")) { + FREE_AND_NULL(git_mailmap_blob); return git_config_string(&git_mailmap_blob, var, value); + } /* Add other config variables here and to Documentation/config.txt. */ return 0; @@ -1777,8 +1796,10 @@ static int git_default_mailmap_config(const char *var, const char *value) static int git_default_attr_config(const char *var, const char *value) { - if (!strcmp(var, "attr.tree")) + if (!strcmp(var, "attr.tree")) { + FREE_AND_NULL(git_attr_tree); return git_config_string(&git_attr_tree, var, value); + } /* * Add other attribute related config variables here and to @@ -2405,7 +2426,7 @@ int git_configset_get_string(struct config_set *set, const char *key, char **des { const char *value; if (!git_configset_get_value(set, key, &value, NULL)) - return git_config_string((const char **)dest, key, value); + return git_config_string(dest, key, value); else return 1; } @@ -2483,7 +2504,7 @@ int git_configset_get_maybe_bool(struct config_set *set, const char *key, int *d return 1; } -int git_configset_get_pathname(struct config_set *set, const char *key, const char **dest) +int git_configset_get_pathname(struct config_set *set, const char *key, char **dest) { const char *value; if (!git_configset_get_value(set, key, &value, NULL)) @@ -2628,7 +2649,7 @@ int repo_config_get_maybe_bool(struct repository *repo, } int repo_config_get_pathname(struct repository *repo, - const char *key, const char **dest) + const char *key, char **dest) { int ret; git_config_check_init(repo); @@ -2727,7 +2748,7 @@ int git_config_get_maybe_bool(const char *key, int *dest) return repo_config_get_maybe_bool(the_repository, key, dest); } -int git_config_get_pathname(const char *key, const char **dest) +int git_config_get_pathname(const char *key, char **dest) { return repo_config_get_pathname(the_repository, key, dest); } diff --git a/config.h b/config.h index e66c84520b..8620aa731b 100644 --- a/config.h +++ b/config.h @@ -280,13 +280,13 @@ int git_config_bool(const char *, const char *); * Allocates and copies the value string into the `dest` parameter; if no * string is given, prints an error message and returns -1. */ -int git_config_string(const char **, const char *, const char *); +int git_config_string(char **, const char *, const char *); /** * Similar to `git_config_string`, but expands `~` or `~user` into the * user's home directory when found at the beginning of the path. */ -int git_config_pathname(const char **, const char *, const char *); +int git_config_pathname(char **, const char *, const char *); int git_config_expiry_date(timestamp_t *, const char *, const char *); int git_config_color(char *, const char *, const char *); @@ -541,7 +541,7 @@ int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned lon int git_configset_get_bool(struct config_set *cs, const char *key, int *dest); int git_configset_get_bool_or_int(struct config_set *cs, const char *key, int *is_bool, int *dest); int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest); -int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest); +int git_configset_get_pathname(struct config_set *cs, const char *key, char **dest); /* Functions for reading a repository's config */ struct repository; @@ -577,7 +577,7 @@ int repo_config_get_bool_or_int(struct repository *repo, int repo_config_get_maybe_bool(struct repository *repo, const char *key, int *dest); int repo_config_get_pathname(struct repository *repo, - const char *key, const char **dest); + const char *key, char **dest); /* * Functions for reading protected config. By definition, protected @@ -687,7 +687,7 @@ int git_config_get_maybe_bool(const char *key, int *dest); * Similar to `git_config_get_string`, but expands `~` or `~user` into * the user's home directory when found at the beginning of the path. */ -int git_config_get_pathname(const char *key, const char **dest); +int git_config_get_pathname(const char *key, char **dest); int git_config_get_index_threads(int *dest); int git_config_get_split_index(void); diff --git a/convert.c b/convert.c index 35b25eb3cb..f2b9f01354 100644 --- a/convert.c +++ b/convert.c @@ -345,30 +345,32 @@ static int check_roundtrip(const char *enc_name) * space separated encodings (eg. "UTF-16, ASCII, CP1125"). * Search for the given encoding in that string. */ - const char *found = strcasestr(check_roundtrip_encoding, enc_name); + const char *encoding = check_roundtrip_encoding ? + check_roundtrip_encoding : "SHIFT-JIS"; + const char *found = strcasestr(encoding, enc_name); const char *next; int len; if (!found) return 0; next = found + strlen(enc_name); - len = strlen(check_roundtrip_encoding); + len = strlen(encoding); return (found && ( /* - * check that the found encoding is at the - * beginning of check_roundtrip_encoding or - * that it is prefixed with a space or comma + * Check that the found encoding is at the beginning of + * encoding or that it is prefixed with a space or + * comma. */ - found == check_roundtrip_encoding || ( + found == encoding || ( (isspace(found[-1]) || found[-1] == ',') ) ) && ( /* - * check that the found encoding is at the - * end of check_roundtrip_encoding or - * that it is suffixed with a space or comma + * Check that the found encoding is at the end of + * encoding or that it is suffixed with a space + * or comma. */ - next == check_roundtrip_encoding + len || ( - next < check_roundtrip_encoding + len && + next == encoding + len || ( + next < encoding + len && (isspace(next[0]) || next[0] == ',') ) )); @@ -979,9 +981,9 @@ int async_query_available_blobs(const char *cmd, struct string_list *available_p static struct convert_driver { const char *name; struct convert_driver *next; - const char *smudge; - const char *clean; - const char *process; + char *smudge; + char *clean; + char *process; int required; } *user_convert, **user_convert_tail; diff --git a/convert.h b/convert.h index ab8b4fa68d..d925589444 100644 --- a/convert.h +++ b/convert.h @@ -92,7 +92,7 @@ void convert_attrs(struct index_state *istate, struct conv_attrs *ca, const char *path); extern enum eol core_eol; -extern const char *check_roundtrip_encoding; +extern char *check_roundtrip_encoding; const char *get_cached_convert_stats_ascii(struct index_state *istate, const char *path); const char *get_wt_convert_stats_ascii(const char *path); diff --git a/delta-islands.c b/delta-islands.c index 4ac3c10551..89d51b72e3 100644 --- a/delta-islands.c +++ b/delta-islands.c @@ -313,7 +313,7 @@ struct island_load_data { size_t nr; size_t alloc; }; -static const char *core_island_name; +static char *core_island_name; static void free_config_regexes(struct island_load_data *ild) { diff --git a/diff.c b/diff.c index ded9ac70df..e70301df76 100644 --- a/diff.c +++ b/diff.c @@ -56,14 +56,14 @@ static int diff_color_moved_default; static int diff_color_moved_ws_default; static int diff_context_default = 3; static int diff_interhunk_context_default; -static const char *diff_word_regex_cfg; -static const char *external_diff_cmd_cfg; -static const char *diff_order_file_cfg; +static char *diff_word_regex_cfg; +static char *external_diff_cmd_cfg; +static char *diff_order_file_cfg; int diff_auto_refresh_index = 1; static int diff_mnemonic_prefix; static int diff_no_prefix; -static const char *diff_src_prefix = "a/"; -static const char *diff_dst_prefix = "b/"; +static char *diff_src_prefix; +static char *diff_dst_prefix; static int diff_relative; static int diff_stat_name_width; static int diff_stat_graph_width; @@ -411,9 +411,11 @@ int git_diff_ui_config(const char *var, const char *value, return 0; } if (!strcmp(var, "diff.srcprefix")) { + FREE_AND_NULL(diff_src_prefix); return git_config_string(&diff_src_prefix, var, value); } if (!strcmp(var, "diff.dstprefix")) { + FREE_AND_NULL(diff_dst_prefix); return git_config_string(&diff_dst_prefix, var, value); } if (!strcmp(var, "diff.relative")) { @@ -3433,8 +3435,8 @@ void diff_set_noprefix(struct diff_options *options) void diff_set_default_prefix(struct diff_options *options) { - options->a_prefix = diff_src_prefix; - options->b_prefix = diff_dst_prefix; + options->a_prefix = diff_src_prefix ? diff_src_prefix : "a/"; + options->b_prefix = diff_dst_prefix ? diff_dst_prefix : "b/"; } struct userdiff_driver *get_textconv(struct repository *r, @@ -5371,8 +5373,8 @@ static int diff_opt_default_prefix(const struct option *opt, BUG_ON_OPT_NEG(unset); BUG_ON_OPT_ARG(optarg); - diff_src_prefix = "a/"; - diff_dst_prefix = "b/"; + FREE_AND_NULL(diff_src_prefix); + FREE_AND_NULL(diff_dst_prefix); diff_set_default_prefix(options); return 0; } diff --git a/environment.c b/environment.c index a73ba9c12c..701d515135 100644 --- a/environment.c +++ b/environment.c @@ -42,12 +42,12 @@ int is_bare_repository_cfg = -1; /* unspecified */ int warn_ambiguous_refs = 1; int warn_on_object_refname_ambiguity = 1; int repository_format_precious_objects; -const char *git_commit_encoding; -const char *git_log_output_encoding; +char *git_commit_encoding; +char *git_log_output_encoding; char *apply_default_whitespace; char *apply_default_ignorewhitespace; -const char *git_attributes_file; -const char *git_hooks_path; +char *git_attributes_file; +char *git_hooks_path; int zlib_compression_level = Z_BEST_SPEED; int pack_compression_level = Z_DEFAULT_COMPRESSION; int fsync_object_files = -1; @@ -58,13 +58,13 @@ size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE; size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT; size_t delta_base_cache_limit = 96 * 1024 * 1024; unsigned long big_file_threshold = 512 * 1024 * 1024; -const char *editor_program; -const char *askpass_program; -const char *excludes_file; +char *editor_program; +char *askpass_program; +char *excludes_file; enum auto_crlf auto_crlf = AUTO_CRLF_FALSE; enum eol core_eol = EOL_UNSET; int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN; -const char *check_roundtrip_encoding = "SHIFT-JIS"; +char *check_roundtrip_encoding; enum branch_track git_branch_track = BRANCH_TRACK_REMOTE; enum rebase_setup_type autorebase = AUTOREBASE_NEVER; enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED; diff --git a/environment.h b/environment.h index 0b2d457f07..e9f01d4d11 100644 --- a/environment.h +++ b/environment.h @@ -131,8 +131,8 @@ extern int warn_ambiguous_refs; extern int warn_on_object_refname_ambiguity; extern char *apply_default_whitespace; extern char *apply_default_ignorewhitespace; -extern const char *git_attributes_file; -extern const char *git_hooks_path; +extern char *git_attributes_file; +extern char *git_hooks_path; extern int zlib_compression_level; extern int pack_compression_level; extern size_t packed_git_window_size; @@ -224,12 +224,12 @@ int odb_pack_keep(const char *name); const char *get_log_output_encoding(void); const char *get_commit_output_encoding(void); -extern const char *git_commit_encoding; -extern const char *git_log_output_encoding; +extern char *git_commit_encoding; +extern char *git_log_output_encoding; -extern const char *editor_program; -extern const char *askpass_program; -extern const char *excludes_file; +extern char *editor_program; +extern char *askpass_program; +extern char *excludes_file; /* * Should we print an ellipsis after an abbreviated SHA-1 value diff --git a/fetch-pack.c b/fetch-pack.c index 7d2aef21ad..eba9e420ea 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -1860,13 +1860,13 @@ static int fetch_pack_config_cb(const char *var, const char *value, const char *msg_id; if (strcmp(var, "fetch.fsck.skiplist") == 0) { - const char *path; + char *path ; if (git_config_pathname(&path, var, value)) return 1; strbuf_addf(&fsck_msg_types, "%cskiplist=%s", fsck_msg_types.len ? ',' : '=', path); - free((char *)path); + free(path); return 0; } diff --git a/fsck.c b/fsck.c index 78af29d264..dd0a33028e 100644 --- a/fsck.c +++ b/fsck.c @@ -1274,13 +1274,13 @@ int git_fsck_config(const char *var, const char *value, const char *msg_id; if (strcmp(var, "fsck.skiplist") == 0) { - const char *path; + char *path; struct strbuf sb = STRBUF_INIT; if (git_config_pathname(&path, var, value)) return 1; strbuf_addf(&sb, "skiplist=%s", path); - free((char *)path); + free(path); fsck_set_msg_types(options, sb.buf); strbuf_release(&sb); return 0; diff --git a/fsmonitor-settings.c b/fsmonitor-settings.c index a6a9e6bc19..e818583420 100644 --- a/fsmonitor-settings.c +++ b/fsmonitor-settings.c @@ -103,6 +103,7 @@ static struct fsmonitor_settings *alloc_settings(void) static void lookup_fsmonitor_settings(struct repository *r) { const char *const_str; + char *to_free = NULL; int bool_value; if (r->settings.fsmonitor) @@ -129,8 +130,9 @@ static void lookup_fsmonitor_settings(struct repository *r) break; case -1: /* config value set to an arbitrary string */ - if (repo_config_get_pathname(r, "core.fsmonitor", &const_str)) + if (repo_config_get_pathname(r, "core.fsmonitor", &to_free)) return; /* should not happen */ + const_str = to_free; break; default: /* should not happen */ @@ -141,6 +143,7 @@ static void lookup_fsmonitor_settings(struct repository *r) fsm_settings__set_hook(r, const_str); else fsm_settings__set_disabled(r); + free(to_free); } enum fsmonitor_mode fsm_settings__get_mode(struct repository *r) diff --git a/gpg-interface.c b/gpg-interface.c index 1ff94266d2..5193223714 100644 --- a/gpg-interface.c +++ b/gpg-interface.c @@ -27,12 +27,14 @@ static void gpg_interface_lazy_init(void) } static char *configured_signing_key; -static const char *ssh_default_key_command, *ssh_allowed_signers, *ssh_revocation_file; +static char *ssh_default_key_command; +static char *ssh_allowed_signers; +static char *ssh_revocation_file; static enum signature_trust_level configured_min_trust_level = TRUST_UNDEFINED; struct gpg_format { const char *name; - const char *program; + char *program; const char **verify_args; const char **sigs; int (*verify_signed_buffer)(struct signature_check *sigc, diff --git a/http.c b/http.c index 752c879c1f..67cc47d28f 100644 --- a/http.c +++ b/http.c @@ -38,11 +38,11 @@ char curl_errorstr[CURL_ERROR_SIZE]; static int curl_ssl_verify = -1; static int curl_ssl_try; -static const char *curl_http_version = NULL; -static const char *ssl_cert; -static const char *ssl_cert_type; -static const char *ssl_cipherlist; -static const char *ssl_version; +static char *curl_http_version; +static char *ssl_cert; +static char *ssl_cert_type; +static char *ssl_cipherlist; +static char *ssl_version; static struct { const char *name; long ssl_version; @@ -59,23 +59,23 @@ static struct { { "tlsv1.3", CURL_SSLVERSION_TLSv1_3 }, #endif }; -static const char *ssl_key; -static const char *ssl_key_type; -static const char *ssl_capath; -static const char *curl_no_proxy; +static char *ssl_key; +static char *ssl_key_type; +static char *ssl_capath; +static char *curl_no_proxy; #ifdef GIT_CURL_HAVE_CURLOPT_PINNEDPUBLICKEY -static const char *ssl_pinnedkey; +static char *ssl_pinnedkey; #endif -static const char *ssl_cainfo; +static char *ssl_cainfo; static long curl_low_speed_limit = -1; static long curl_low_speed_time = -1; static int curl_ftp_no_epsv; -static const char *curl_http_proxy; -static const char *http_proxy_authmethod; +static char *curl_http_proxy; +static char *http_proxy_authmethod; -static const char *http_proxy_ssl_cert; -static const char *http_proxy_ssl_key; -static const char *http_proxy_ssl_ca_info; +static char *http_proxy_ssl_cert; +static char *http_proxy_ssl_key; +static char *http_proxy_ssl_ca_info; static struct credential proxy_cert_auth = CREDENTIAL_INIT; static int proxy_ssl_cert_password_required; @@ -95,7 +95,7 @@ static struct { */ }; #ifdef CURLGSSAPI_DELEGATION_FLAG -static const char *curl_deleg; +static char *curl_deleg; static struct { const char *name; long curl_deleg_param; @@ -108,11 +108,11 @@ static struct { static struct credential proxy_auth = CREDENTIAL_INIT; static const char *curl_proxyuserpwd; -static const char *curl_cookie_file; +static char *curl_cookie_file; static int curl_save_cookies; struct credential http_auth = CREDENTIAL_INIT; static int http_proactive_auth; -static const char *user_agent; +static char *user_agent; static int curl_empty_auth = -1; enum http_follow_config http_follow_config = HTTP_FOLLOW_INITIAL; @@ -592,10 +592,10 @@ static void init_curl_http_auth(CURL *result) } /* *var must be free-able */ -static void var_override(const char **var, char *value) +static void var_override(char **var, char *value) { if (value) { - free((void *)*var); + free(*var); *var = xstrdup(value); } } @@ -1233,11 +1233,13 @@ static CURL *get_curl_handle(void) return result; } -static void set_from_env(const char **var, const char *envname) +static void set_from_env(char **var, const char *envname) { const char *val = getenv(envname); - if (val) - *var = val; + if (val) { + FREE_AND_NULL(*var); + *var = xstrdup(val); + } } void http_init(struct remote *remote, const char *url, int proactive_auth) diff --git a/imap-send.c b/imap-send.c index c0130d0e02..a5d1510180 100644 --- a/imap-send.c +++ b/imap-send.c @@ -70,16 +70,16 @@ static char *next_arg(char **); struct imap_server_conf { const char *name; - const char *tunnel; - const char *host; + char *tunnel; + char *host; int port; - const char *folder; - const char *user; - const char *pass; + char *folder; + char *user; + char *pass; int use_ssl; int ssl_verify; int use_html; - const char *auth_method; + char *auth_method; }; static struct imap_server_conf server = { diff --git a/mailmap.c b/mailmap.c index 3d6a5e9400..b2efe29b3d 100644 --- a/mailmap.c +++ b/mailmap.c @@ -6,8 +6,8 @@ #include "object-store-ll.h" #include "setup.h" -const char *git_mailmap_file; -const char *git_mailmap_blob; +char *git_mailmap_file; +char *git_mailmap_blob; struct mailmap_info { char *name; diff --git a/mailmap.h b/mailmap.h index 0f8fd2c586..cbda9bc5e0 100644 --- a/mailmap.h +++ b/mailmap.h @@ -3,8 +3,8 @@ struct string_list; -extern const char *git_mailmap_file; -extern const char *git_mailmap_blob; +extern char *git_mailmap_file; +extern char *git_mailmap_blob; int read_mailmap(struct string_list *map); void clear_mailmap(struct string_list *map); diff --git a/merge-ll.c b/merge-ll.c index bf1077ae09..e29b15fa4a 100644 --- a/merge-ll.c +++ b/merge-ll.c @@ -27,9 +27,9 @@ typedef enum ll_merge_result (*ll_merge_fn)(const struct ll_merge_driver *, struct ll_merge_driver { const char *name; - const char *description; + char *description; ll_merge_fn fn; - const char *recursive; + char *recursive; struct ll_merge_driver *next; char *cmdline; }; @@ -268,7 +268,7 @@ static enum ll_merge_result ll_ext_merge(const struct ll_merge_driver *fn, * merge.default and merge.driver configuration items */ static struct ll_merge_driver *ll_user_merge, **ll_user_merge_tail; -static const char *default_ll_merge; +static char *default_ll_merge; static int read_merge_config(const char *var, const char *value, const struct config_context *ctx UNUSED, diff --git a/pager.c b/pager.c index b8822a9381..e9e121db69 100644 --- a/pager.c +++ b/pager.c @@ -13,7 +13,7 @@ int pager_use_color = 1; #endif static struct child_process pager_process; -static const char *pager_program; +static char *pager_program; /* Is the value coming back from term_columns() just a guess? */ static int term_columns_guessed; diff --git a/pretty.c b/pretty.c index 7ead078998..22a81506b7 100644 --- a/pretty.c +++ b/pretty.c @@ -62,7 +62,7 @@ static int git_pretty_formats_config(const char *var, const char *value, { struct cmt_fmt_map *commit_format = NULL; const char *name; - const char *fmt; + char *fmt; int i; if (!skip_prefix(var, "pretty.", &name)) @@ -93,13 +93,17 @@ static int git_pretty_formats_config(const char *var, const char *value, if (git_config_string(&fmt, var, value)) return -1; - if (skip_prefix(fmt, "format:", &fmt)) + if (skip_prefix(fmt, "format:", &commit_format->user_format)) { commit_format->is_tformat = 0; - else if (skip_prefix(fmt, "tformat:", &fmt) || strchr(fmt, '%')) + } else if (skip_prefix(fmt, "tformat:", &commit_format->user_format)) { commit_format->is_tformat = 1; - else + } else if (strchr(fmt, '%')) { + commit_format->is_tformat = 1; + commit_format->user_format = fmt; + } else { commit_format->is_alias = 1; - commit_format->user_format = fmt; + commit_format->user_format = fmt; + } return 0; } diff --git a/promisor-remote.h b/promisor-remote.h index 2cb9eda9ea..88cb599c39 100644 --- a/promisor-remote.h +++ b/promisor-remote.h @@ -13,7 +13,7 @@ struct object_id; */ struct promisor_remote { struct promisor_remote *next; - const char *partial_clone_filter; + char *partial_clone_filter; const char name[FLEX_ARRAY]; }; diff --git a/remote.c b/remote.c index 5898da2bb5..dcb5492c85 100644 --- a/remote.c +++ b/remote.c @@ -430,29 +430,29 @@ static int handle_config(const char *key, const char *value, else if (!strcmp(subkey, "prunetags")) remote->prune_tags = git_config_bool(key, value); else if (!strcmp(subkey, "url")) { - const char *v; + char *v; if (git_config_string(&v, key, value)) return -1; add_url(remote, v); } else if (!strcmp(subkey, "pushurl")) { - const char *v; + char *v; if (git_config_string(&v, key, value)) return -1; add_pushurl(remote, v); } else if (!strcmp(subkey, "push")) { - const char *v; + char *v; if (git_config_string(&v, key, value)) return -1; refspec_append(&remote->push, v); - free((char *)v); + free(v); } else if (!strcmp(subkey, "fetch")) { - const char *v; + char *v; if (git_config_string(&v, key, value)) return -1; refspec_append(&remote->fetch, v); - free((char *)v); + free(v); } else if (!strcmp(subkey, "receivepack")) { - const char *v; + char *v; if (git_config_string(&v, key, value)) return -1; if (!remote->receivepack) @@ -460,7 +460,7 @@ static int handle_config(const char *key, const char *value, else error(_("more than one receivepack given, using the first")); } else if (!strcmp(subkey, "uploadpack")) { - const char *v; + char *v; if (git_config_string(&v, key, value)) return -1; if (!remote->uploadpack) @@ -473,10 +473,10 @@ static int handle_config(const char *key, const char *value, else if (!strcmp(value, "--tags")) remote->fetch_tags = 2; } else if (!strcmp(subkey, "proxy")) { - return git_config_string((const char **)&remote->http_proxy, + return git_config_string(&remote->http_proxy, key, value); } else if (!strcmp(subkey, "proxyauthmethod")) { - return git_config_string((const char **)&remote->http_proxy_authmethod, + return git_config_string(&remote->http_proxy_authmethod, key, value); } else if (!strcmp(subkey, "vcs")) { return git_config_string(&remote->foreign_vcs, key, value); diff --git a/remote.h b/remote.h index dfd4837e60..e8c6655e42 100644 --- a/remote.h +++ b/remote.h @@ -46,7 +46,7 @@ struct remote_state { struct hashmap branches_hash; struct branch *current_branch; - const char *pushremote_name; + char *pushremote_name; struct rewrites rewrites; struct rewrites rewrites_push; @@ -65,7 +65,7 @@ struct remote { int origin, configured_in_repo; - const char *foreign_vcs; + char *foreign_vcs; /* An array of all of the url_nr URLs configured for the remote */ const char **url; @@ -309,9 +309,9 @@ struct branch { const char *refname; /* The name of the remote listed in the configuration. */ - const char *remote_name; + char *remote_name; - const char *pushremote_name; + char *pushremote_name; /* An array of the "merge" lines in the configuration. */ const char **merge_name; diff --git a/sequencer.c b/sequencer.c index aa2a239835..30513e87bf 100644 --- a/sequencer.c +++ b/sequencer.c @@ -306,7 +306,7 @@ static int git_sequencer_config(const char *k, const char *v, } if (!opts->default_strategy && !strcmp(k, "pull.twohead")) { - int ret = git_config_string((const char**)&opts->default_strategy, k, v); + int ret = git_config_string(&opts->default_strategy, k, v); if (ret == 0) { /* * pull.twohead is allowed to be multi-valued; we only diff --git a/setup.c b/setup.c index 3ade79d245..e47946d0e7 100644 --- a/setup.c +++ b/setup.c @@ -1230,13 +1230,13 @@ static int safe_directory_cb(const char *key, const char *value, } else if (!strcmp(value, "*")) { data->is_safe = 1; } else { - const char *interpolated = NULL; + char *interpolated = NULL; if (!git_config_pathname(&interpolated, key, value) && !fspathcmp(data->path, interpolated ? interpolated : value)) data->is_safe = 1; - free((char *)interpolated); + free(interpolated); } return 0; @@ -1875,7 +1875,7 @@ static int template_dir_cb(const char *key, const char *value, char *path = NULL; FREE_AND_NULL(data->path); - if (!git_config_pathname((const char **)&path, key, value)) + if (!git_config_pathname(&path, key, value)) data->path = path ? path : xstrdup(value); } diff --git a/strbuf.c b/strbuf.c index 0d929e4e19..e1076c9891 100644 --- a/strbuf.c +++ b/strbuf.c @@ -691,8 +691,10 @@ int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term) int strbuf_appendwholeline(struct strbuf *sb, FILE *fp, int term) { struct strbuf line = STRBUF_INIT; - if (strbuf_getwholeline(&line, fp, term)) + if (strbuf_getwholeline(&line, fp, term)) { + strbuf_release(&line); return EOF; + } strbuf_addbuf(sb, &line); strbuf_release(&line); return 0; diff --git a/strvec.c b/strvec.c index 178f4f3748..d4073ec9fa 100644 --- a/strvec.c +++ b/strvec.c @@ -56,6 +56,26 @@ void strvec_pushv(struct strvec *array, const char **items) strvec_push(array, *items); } +const char *strvec_replace(struct strvec *array, size_t idx, const char *replacement) +{ + char *to_free; + if (idx >= array->nr) + BUG("index outside of array boundary"); + to_free = (char *) array->v[idx]; + array->v[idx] = xstrdup(replacement); + free(to_free); + return array->v[idx]; +} + +void strvec_remove(struct strvec *array, size_t idx) +{ + if (idx >= array->nr) + BUG("index outside of array boundary"); + free((char *)array->v[idx]); + memmove(array->v + idx, array->v + idx + 1, (array->nr - idx) * sizeof(char *)); + array->nr--; +} + void strvec_pop(struct strvec *array) { if (!array->nr) diff --git a/strvec.h b/strvec.h index 4715d3e51f..6c7e8b7d50 100644 --- a/strvec.h +++ b/strvec.h @@ -64,6 +64,19 @@ void strvec_pushl(struct strvec *, ...); /* Push a null-terminated array of strings onto the end of the array. */ void strvec_pushv(struct strvec *, const char **); +/** + * Replace the value at the given index with a new value. The index must be + * valid. Returns a pointer to the inserted value. + */ +const char *strvec_replace(struct strvec *array, size_t idx, const char *replacement); + +/* + * Remove the value at the given index. The remainder of the array will be + * moved to fill the resulting gap. The provided index must point into the + * array. + */ +void strvec_remove(struct strvec *array, size_t idx); + /** * Remove the final element from the array. If there are no * elements in the array, do nothing. diff --git a/submodule-config.c b/submodule-config.c index 11428b4ada..ec45ea67b9 100644 --- a/submodule-config.c +++ b/submodule-config.c @@ -91,6 +91,8 @@ static void free_one_config(struct submodule_entry *entry) free((void *) entry->config->path); free((void *) entry->config->name); free((void *) entry->config->branch); + free((void *) entry->config->url); + free((void *) entry->config->ignore); free((void *) entry->config->update_strategy.command); free(entry->config); } diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh index 432f029d48..6a76b7fdbd 100755 --- a/t/t0300-credentials.sh +++ b/t/t0300-credentials.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='basic credential helper tests' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-credential.sh diff --git a/t/t0611-reftable-httpd.sh b/t/t0611-reftable-httpd.sh index 5e05b9c1f2..2805995cc8 100755 --- a/t/t0611-reftable-httpd.sh +++ b/t/t0611-reftable-httpd.sh @@ -2,6 +2,7 @@ test_description='reftable HTTPD tests' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-httpd.sh diff --git a/t/t1013-read-tree-submodule.sh b/t/t1013-read-tree-submodule.sh index bfc90d4cf2..cf8b94ebed 100755 --- a/t/t1013-read-tree-submodule.sh +++ b/t/t1013-read-tree-submodule.sh @@ -2,6 +2,7 @@ test_description='read-tree can handle submodules' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-submodule-update.sh diff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh index 40d3c42618..53e5b290b9 100755 --- a/t/t1306-xdg-files.sh +++ b/t/t1306-xdg-files.sh @@ -7,6 +7,7 @@ test_description='Compatibility with $XDG_CONFIG_HOME/git/ files' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'read config: xdg file exists and ~/.gitconfig doesn'\''t' ' diff --git a/t/t1350-config-hooks-path.sh b/t/t1350-config-hooks-path.sh index 45a0492917..ceeb7ac3a4 100755 --- a/t/t1350-config-hooks-path.sh +++ b/t/t1350-config-hooks-path.sh @@ -2,6 +2,7 @@ test_description='Test the core.hooksPath configuration variable' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'set up a pre-commit hook in core.hooksPath' ' diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index ec3443cc87..bbee2783ab 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -4,6 +4,8 @@ # test_description='Test git update-ref and basic ref logging' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh Z=$ZERO_OID diff --git a/t/t2013-checkout-submodule.sh b/t/t2013-checkout-submodule.sh index b2bdd1fcb4..3c1d663d94 100755 --- a/t/t2013-checkout-submodule.sh +++ b/t/t2013-checkout-submodule.sh @@ -2,6 +2,7 @@ test_description='checkout can handle submodules' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-submodule-update.sh diff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh index a3b1449ef1..2caada3d83 100755 --- a/t/t2024-checkout-dwim.sh +++ b/t/t2024-checkout-dwim.sh @@ -4,6 +4,7 @@ test_description='checkout Ensures that checkout on an unborn branch does what the user expects' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # Is the current branch "refs/heads/$1"? diff --git a/t/t2060-switch.sh b/t/t2060-switch.sh index c91c4db936..77b2346291 100755 --- a/t/t2060-switch.sh +++ b/t/t2060-switch.sh @@ -5,6 +5,7 @@ test_description='switch basic functionality' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t3007-ls-files-recurse-submodules.sh b/t/t3007-ls-files-recurse-submodules.sh index 61771eec83..f04bdc8c78 100755 --- a/t/t3007-ls-files-recurse-submodules.sh +++ b/t/t3007-ls-files-recurse-submodules.sh @@ -6,6 +6,7 @@ This test verifies the recurse-submodules feature correctly lists files from submodules. ' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup directory structure and submodules' ' diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index 758963b189..e627f08a17 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='git branch display tests' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-terminal.sh diff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh index fcc40d6fe1..22452ff84c 100755 --- a/t/t3415-rebase-autosquash.sh +++ b/t/t3415-rebase-autosquash.sh @@ -5,6 +5,7 @@ test_description='auto squash' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-rebase.sh diff --git a/t/t3426-rebase-submodule.sh b/t/t3426-rebase-submodule.sh index ba069dccbd..94ea88e384 100755 --- a/t/t3426-rebase-submodule.sh +++ b/t/t3426-rebase-submodule.sh @@ -2,6 +2,7 @@ test_description='rebase can handle submodules' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-submodule-update.sh . "$TEST_DIRECTORY"/lib-rebase.sh diff --git a/t/t3512-cherry-pick-submodule.sh b/t/t3512-cherry-pick-submodule.sh index f22d1ddead..9387a22a9e 100755 --- a/t/t3512-cherry-pick-submodule.sh +++ b/t/t3512-cherry-pick-submodule.sh @@ -5,6 +5,7 @@ test_description='cherry-pick can handle submodules' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-submodule-update.sh diff --git a/t/t3513-revert-submodule.sh b/t/t3513-revert-submodule.sh index 8bfe3ed246..e178968b40 100755 --- a/t/t3513-revert-submodule.sh +++ b/t/t3513-revert-submodule.sh @@ -2,6 +2,7 @@ test_description='revert can handle submodules' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-submodule-update.sh diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh index 98259e2ada..31ac31d4bc 100755 --- a/t/t3600-rm.sh +++ b/t/t3600-rm.sh @@ -8,6 +8,7 @@ test_description='Test of the various options to git rm.' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # Setup some files to be removed, some with funny characters diff --git a/t/t3906-stash-submodule.sh b/t/t3906-stash-submodule.sh index 0f7348ec21..0f61f01ef4 100755 --- a/t/t3906-stash-submodule.sh +++ b/t/t3906-stash-submodule.sh @@ -2,6 +2,7 @@ test_description='stash can handle submodules' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-submodule-update.sh diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh index 49c042a38a..cd1931dd55 100755 --- a/t/t4001-diff-rename.sh +++ b/t/t4001-diff-rename.sh @@ -3,9 +3,9 @@ # Copyright (c) 2005 Junio C Hamano # -test_description='Test rename detection in diff engine. +test_description='Test rename detection in diff engine.' -' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-diff.sh diff --git a/t/t4041-diff-submodule-option.sh b/t/t4041-diff-submodule-option.sh index 0c1502d4b0..8fc40e75eb 100755 --- a/t/t4041-diff-submodule-option.sh +++ b/t/t4041-diff-submodule-option.sh @@ -12,6 +12,7 @@ This test tries to verify the sanity of the --submodule option of git diff. GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # Tested non-UTF-8 encoding diff --git a/t/t4043-diff-rename-binary.sh b/t/t4043-diff-rename-binary.sh index 2a2cf91352..e486493908 100755 --- a/t/t4043-diff-rename-binary.sh +++ b/t/t4043-diff-rename-binary.sh @@ -5,6 +5,7 @@ test_description='Move a binary file' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh diff --git a/t/t4059-diff-submodule-not-initialized.sh b/t/t4059-diff-submodule-not-initialized.sh index d489230df8..668f526303 100755 --- a/t/t4059-diff-submodule-not-initialized.sh +++ b/t/t4059-diff-submodule-not-initialized.sh @@ -9,6 +9,7 @@ This test tries to verify that add_submodule_odb works when the submodule was initialized previously but the checkout has since been removed. ' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # Tested non-UTF-8 encoding diff --git a/t/t4060-diff-submodule-option-diff-format.sh b/t/t4060-diff-submodule-option-diff-format.sh index 97c6424cd5..8ce67442d9 100755 --- a/t/t4060-diff-submodule-option-diff-format.sh +++ b/t/t4060-diff-submodule-option-diff-format.sh @@ -10,6 +10,7 @@ test_description='Support for diff format verbose submodule difference in git di This test tries to verify the sanity of --submodule=diff option of git diff. ' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # Tested non-UTF-8 encoding diff --git a/t/t4120-apply-popt.sh b/t/t4120-apply-popt.sh index 697e86c0ff..f788428540 100755 --- a/t/t4120-apply-popt.sh +++ b/t/t4120-apply-popt.sh @@ -5,6 +5,7 @@ test_description='git apply -p handling.' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success setup ' diff --git a/t/t4137-apply-submodule.sh b/t/t4137-apply-submodule.sh index 07d5262537..ebd0d4ad17 100755 --- a/t/t4137-apply-submodule.sh +++ b/t/t4137-apply-submodule.sh @@ -2,6 +2,7 @@ test_description='git apply handling submodules' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-submodule-update.sh diff --git a/t/t4210-log-i18n.sh b/t/t4210-log-i18n.sh index 75216f19ce..7120030b5c 100755 --- a/t/t4210-log-i18n.sh +++ b/t/t4210-log-i18n.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='test log with i18n features' + +TEST_PASSES_SANITIZE_LEAK=true . ./lib-gettext.sh # two forms of é diff --git a/t/t5563-simple-http-auth.sh b/t/t5563-simple-http-auth.sh index 5d5caa3f58..4af796de67 100755 --- a/t/t5563-simple-http-auth.sh +++ b/t/t5563-simple-http-auth.sh @@ -2,6 +2,7 @@ test_description='test http auth header and credential helper interop' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-httpd.sh diff --git a/t/t5564-http-proxy.sh b/t/t5564-http-proxy.sh index 9da5134614..bb35b87071 100755 --- a/t/t5564-http-proxy.sh +++ b/t/t5564-http-proxy.sh @@ -2,6 +2,7 @@ test_description="test fetching through http proxy" +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-httpd.sh diff --git a/t/t5581-http-curl-verbose.sh b/t/t5581-http-curl-verbose.sh index cded79c16b..724f610054 100755 --- a/t/t5581-http-curl-verbose.sh +++ b/t/t5581-http-curl-verbose.sh @@ -4,6 +4,7 @@ test_description='test GIT_CURL_VERBOSE' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-httpd.sh start_httpd diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh index 573eb97a0f..f1623b1c06 100755 --- a/t/t6006-rev-list-format.sh +++ b/t/t6006-rev-list-format.sh @@ -8,6 +8,7 @@ test_description='git rev-list --pretty=format test' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-terminal.sh diff --git a/t/t6041-bisect-submodule.sh b/t/t6041-bisect-submodule.sh index 82013fc903..3946e18089 100755 --- a/t/t6041-bisect-submodule.sh +++ b/t/t6041-bisect-submodule.sh @@ -2,6 +2,7 @@ test_description='bisect can handle submodules' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-submodule-update.sh diff --git a/t/t6400-merge-df.sh b/t/t6400-merge-df.sh index 3de4ef6bd9..27d6efdc9a 100755 --- a/t/t6400-merge-df.sh +++ b/t/t6400-merge-df.sh @@ -7,6 +7,7 @@ test_description='Test merge with directory/file conflicts' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'prepare repository' ' diff --git a/t/t6412-merge-large-rename.sh b/t/t6412-merge-large-rename.sh index ca018d11f5..d0863a8fb5 100755 --- a/t/t6412-merge-large-rename.sh +++ b/t/t6412-merge-large-rename.sh @@ -4,6 +4,7 @@ test_description='merging with large rename matrix' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh count() { diff --git a/t/t6426-merge-skip-unneeded-updates.sh b/t/t6426-merge-skip-unneeded-updates.sh index b059475ed0..62f0180325 100755 --- a/t/t6426-merge-skip-unneeded-updates.sh +++ b/t/t6426-merge-skip-unneeded-updates.sh @@ -22,6 +22,7 @@ test_description="merge cases" # underscore notation is to differentiate different # files that might be renamed into each other's paths.) +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-merge.sh diff --git a/t/t6429-merge-sequence-rename-caching.sh b/t/t6429-merge-sequence-rename-caching.sh index 0f39ed0d08..cb1c4ceef7 100755 --- a/t/t6429-merge-sequence-rename-caching.sh +++ b/t/t6429-merge-sequence-rename-caching.sh @@ -2,6 +2,7 @@ test_description="remember regular & dir renames in sequence of merges" +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # diff --git a/t/t6438-submodule-directory-file-conflicts.sh b/t/t6438-submodule-directory-file-conflicts.sh index 8df67a0ef9..3594190af8 100755 --- a/t/t6438-submodule-directory-file-conflicts.sh +++ b/t/t6438-submodule-directory-file-conflicts.sh @@ -2,6 +2,7 @@ test_description='merge can handle submodules' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-submodule-update.sh diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh index 879a6dce60..86258f9f43 100755 --- a/t/t7001-mv.sh +++ b/t/t7001-mv.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='git mv in subdirs' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-diff-data.sh diff --git a/t/t7005-editor.sh b/t/t7005-editor.sh index 5fcf281dfb..b9822294fe 100755 --- a/t/t7005-editor.sh +++ b/t/t7005-editor.sh @@ -2,6 +2,7 @@ test_description='GIT_EDITOR, core.editor, and stuff' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh unset EDITOR VISUAL GIT_EDITOR diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh index 62d9f846ce..2add26d768 100755 --- a/t/t7102-reset.sh +++ b/t/t7102-reset.sh @@ -10,6 +10,7 @@ Documented tests for git reset' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh commit_msg () { diff --git a/t/t7112-reset-submodule.sh b/t/t7112-reset-submodule.sh index a3e2413bc3..b0d3d93b0b 100755 --- a/t/t7112-reset-submodule.sh +++ b/t/t7112-reset-submodule.sh @@ -2,6 +2,7 @@ test_description='reset can handle submodules' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-submodule-update.sh diff --git a/t/t7417-submodule-path-url.sh b/t/t7417-submodule-path-url.sh index 5e3051da8b..dbbb3853dc 100755 --- a/t/t7417-submodule-path-url.sh +++ b/t/t7417-submodule-path-url.sh @@ -4,6 +4,7 @@ test_description='check handling of .gitmodule path with dash' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t7421-submodule-summary-add.sh b/t/t7421-submodule-summary-add.sh index ce64d8b137..479c8fdde1 100755 --- a/t/t7421-submodule-summary-add.sh +++ b/t/t7421-submodule-summary-add.sh @@ -10,6 +10,7 @@ while making sure to add submodules using `git submodule add` instead of `git add` as done in t7401. ' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh index 185248a4cd..01e1e8a8f7 100755 --- a/t/t9129-git-svn-i18n-commitencoding.sh +++ b/t/t9129-git-svn-i18n-commitencoding.sh @@ -4,7 +4,6 @@ test_description='git svn honors i18n.commitEncoding in config' -TEST_FAILS_SANITIZE_LEAK=true . ./lib-git-svn.sh compare_git_head_with () { diff --git a/t/t9139-git-svn-non-utf8-commitencoding.sh b/t/t9139-git-svn-non-utf8-commitencoding.sh index b7f756b2b7..22d80b0be2 100755 --- a/t/t9139-git-svn-non-utf8-commitencoding.sh +++ b/t/t9139-git-svn-non-utf8-commitencoding.sh @@ -4,7 +4,6 @@ test_description='git svn refuses to dcommit non-UTF8 messages' -TEST_FAILS_SANITIZE_LEAK=true . ./lib-git-svn.sh # ISO-2022-JP can pass for valid UTF-8, so skipping that in this test diff --git a/t/unit-tests/t-strvec.c b/t/unit-tests/t-strvec.c new file mode 100644 index 0000000000..f17fb10d9e --- /dev/null +++ b/t/unit-tests/t-strvec.c @@ -0,0 +1,269 @@ +#include "test-lib.h" +#include "strbuf.h" +#include "strvec.h" + +#define check_strvec(vec, ...) \ + check_strvec_loc(TEST_LOCATION(), vec, __VA_ARGS__) +static void check_strvec_loc(const char *loc, struct strvec *vec, ...) +{ + va_list ap; + size_t nr = 0; + + va_start(ap, vec); + while (1) { + const char *str = va_arg(ap, const char *); + if (!str) + break; + + if (!check_uint(vec->nr, >, nr) || + !check_uint(vec->alloc, >, nr) || + !check_str(vec->v[nr], str)) { + struct strbuf msg = STRBUF_INIT; + strbuf_addf(&msg, "strvec index %"PRIuMAX, (uintmax_t) nr); + test_assert(loc, msg.buf, 0); + strbuf_release(&msg); + return; + } + + nr++; + } + + check_uint(vec->nr, ==, nr); + check_uint(vec->alloc, >=, nr); + check_pointer_eq(vec->v[nr], NULL); +} + +static void t_static_init(void) +{ + struct strvec vec = STRVEC_INIT; + check_pointer_eq(vec.v, empty_strvec); + check_uint(vec.nr, ==, 0); + check_uint(vec.alloc, ==, 0); +} + +static void t_dynamic_init(void) +{ + struct strvec vec; + strvec_init(&vec); + check_pointer_eq(vec.v, empty_strvec); + check_uint(vec.nr, ==, 0); + check_uint(vec.alloc, ==, 0); +} + +static void t_clear(void) +{ + struct strvec vec = STRVEC_INIT; + strvec_push(&vec, "foo"); + strvec_clear(&vec); + check_pointer_eq(vec.v, empty_strvec); + check_uint(vec.nr, ==, 0); + check_uint(vec.alloc, ==, 0); +} + +static void t_push(void) +{ + struct strvec vec = STRVEC_INIT; + + strvec_push(&vec, "foo"); + check_strvec(&vec, "foo", NULL); + + strvec_push(&vec, "bar"); + check_strvec(&vec, "foo", "bar", NULL); + + strvec_clear(&vec); +} + +static void t_pushf(void) +{ + struct strvec vec = STRVEC_INIT; + strvec_pushf(&vec, "foo: %d", 1); + check_strvec(&vec, "foo: 1", NULL); + strvec_clear(&vec); +} + +static void t_pushl(void) +{ + struct strvec vec = STRVEC_INIT; + strvec_pushl(&vec, "foo", "bar", "baz", NULL); + check_strvec(&vec, "foo", "bar", "baz", NULL); + strvec_clear(&vec); +} + +static void t_pushv(void) +{ + const char *strings[] = { + "foo", "bar", "baz", NULL, + }; + struct strvec vec = STRVEC_INIT; + + strvec_pushv(&vec, strings); + check_strvec(&vec, "foo", "bar", "baz", NULL); + + strvec_clear(&vec); +} + +static void t_replace_at_head(void) +{ + struct strvec vec = STRVEC_INIT; + strvec_pushl(&vec, "foo", "bar", "baz", NULL); + strvec_replace(&vec, 0, "replaced"); + check_strvec(&vec, "replaced", "bar", "baz", NULL); + strvec_clear(&vec); +} + +static void t_replace_at_tail(void) +{ + struct strvec vec = STRVEC_INIT; + strvec_pushl(&vec, "foo", "bar", "baz", NULL); + strvec_replace(&vec, 2, "replaced"); + check_strvec(&vec, "foo", "bar", "replaced", NULL); + strvec_clear(&vec); +} + +static void t_replace_in_between(void) +{ + struct strvec vec = STRVEC_INIT; + strvec_pushl(&vec, "foo", "bar", "baz", NULL); + strvec_replace(&vec, 1, "replaced"); + check_strvec(&vec, "foo", "replaced", "baz", NULL); + strvec_clear(&vec); +} + +static void t_replace_with_substring(void) +{ + struct strvec vec = STRVEC_INIT; + strvec_pushl(&vec, "foo", NULL); + strvec_replace(&vec, 0, vec.v[0] + 1); + check_strvec(&vec, "oo", NULL); + strvec_clear(&vec); +} + +static void t_remove_at_head(void) +{ + struct strvec vec = STRVEC_INIT; + strvec_pushl(&vec, "foo", "bar", "baz", NULL); + strvec_remove(&vec, 0); + check_strvec(&vec, "bar", "baz", NULL); + strvec_clear(&vec); +} + +static void t_remove_at_tail(void) +{ + struct strvec vec = STRVEC_INIT; + strvec_pushl(&vec, "foo", "bar", "baz", NULL); + strvec_remove(&vec, 2); + check_strvec(&vec, "foo", "bar", NULL); + strvec_clear(&vec); +} + +static void t_remove_in_between(void) +{ + struct strvec vec = STRVEC_INIT; + strvec_pushl(&vec, "foo", "bar", "baz", NULL); + strvec_remove(&vec, 1); + check_strvec(&vec, "foo", "baz", NULL); + strvec_clear(&vec); +} + +static void t_pop_empty_array(void) +{ + struct strvec vec = STRVEC_INIT; + strvec_pop(&vec); + check_strvec(&vec, NULL); + strvec_clear(&vec); +} + +static void t_pop_non_empty_array(void) +{ + struct strvec vec = STRVEC_INIT; + strvec_pushl(&vec, "foo", "bar", "baz", NULL); + strvec_pop(&vec); + check_strvec(&vec, "foo", "bar", NULL); + strvec_clear(&vec); +} + +static void t_split_empty_string(void) +{ + struct strvec vec = STRVEC_INIT; + strvec_split(&vec, ""); + check_strvec(&vec, NULL); + strvec_clear(&vec); +} + +static void t_split_single_item(void) +{ + struct strvec vec = STRVEC_INIT; + strvec_split(&vec, "foo"); + check_strvec(&vec, "foo", NULL); + strvec_clear(&vec); +} + +static void t_split_multiple_items(void) +{ + struct strvec vec = STRVEC_INIT; + strvec_split(&vec, "foo bar baz"); + check_strvec(&vec, "foo", "bar", "baz", NULL); + strvec_clear(&vec); +} + +static void t_split_whitespace_only(void) +{ + struct strvec vec = STRVEC_INIT; + strvec_split(&vec, " \t\n"); + check_strvec(&vec, NULL); + strvec_clear(&vec); +} + +static void t_split_multiple_consecutive_whitespaces(void) +{ + struct strvec vec = STRVEC_INIT; + strvec_split(&vec, "foo\n\t bar"); + check_strvec(&vec, "foo", "bar", NULL); + strvec_clear(&vec); +} + +static void t_detach(void) +{ + struct strvec vec = STRVEC_INIT; + const char **detached; + + strvec_push(&vec, "foo"); + + detached = strvec_detach(&vec); + check_str(detached[0], "foo"); + check_pointer_eq(detached[1], NULL); + + check_pointer_eq(vec.v, empty_strvec); + check_uint(vec.nr, ==, 0); + check_uint(vec.alloc, ==, 0); + + free((char *) detached[0]); + free(detached); +} + +int cmd_main(int argc, const char **argv) +{ + TEST(t_static_init(), "static initialization"); + TEST(t_dynamic_init(), "dynamic initialization"); + TEST(t_clear(), "clear"); + TEST(t_push(), "push"); + TEST(t_pushf(), "pushf"); + TEST(t_pushl(), "pushl"); + TEST(t_pushv(), "pushv"); + TEST(t_replace_at_head(), "replace at head"); + TEST(t_replace_in_between(), "replace in between"); + TEST(t_replace_at_tail(), "replace at tail"); + TEST(t_replace_with_substring(), "replace with substring"); + TEST(t_remove_at_head(), "remove at head"); + TEST(t_remove_in_between(), "remove in between"); + TEST(t_remove_at_tail(), "remove at tail"); + TEST(t_pop_empty_array(), "pop with empty array"); + TEST(t_pop_non_empty_array(), "pop with non-empty array"); + TEST(t_split_empty_string(), "split empty string"); + TEST(t_split_single_item(), "split single item"); + TEST(t_split_multiple_items(), "split multiple items"); + TEST(t_split_whitespace_only(), "split whitespace only"); + TEST(t_split_multiple_consecutive_whitespaces(), "split multiple consecutive whitespaces"); + TEST(t_detach(), "detach"); + return test_done(); +} diff --git a/t/unit-tests/test-lib.c b/t/unit-tests/test-lib.c index 66d6980ffb..3c513ce59a 100644 --- a/t/unit-tests/test-lib.c +++ b/t/unit-tests/test-lib.c @@ -318,6 +318,19 @@ int check_bool_loc(const char *loc, const char *check, int ok) union test__tmp test__tmp[2]; +int check_pointer_eq_loc(const char *loc, const char *check, int ok, + const void *a, const void *b) +{ + int ret = test_assert(loc, check, ok); + + if (!ret) { + test_msg(" left: %p", a); + test_msg(" right: %p", b); + } + + return ret; +} + int check_int_loc(const char *loc, const char *check, int ok, intmax_t a, intmax_t b) { diff --git a/t/unit-tests/test-lib.h b/t/unit-tests/test-lib.h index a8f07ae0b7..2de6d715d5 100644 --- a/t/unit-tests/test-lib.h +++ b/t/unit-tests/test-lib.h @@ -75,6 +75,18 @@ int test_assert(const char *location, const char *check, int ok); check_bool_loc(TEST_LOCATION(), #x, x) int check_bool_loc(const char *loc, const char *check, int ok); +/* + * Compare two integers. Prints a message with the two values if the + * comparison fails. NB this is not thread safe. + */ +#define check_pointer_eq(a, b) \ + (test__tmp[0].p = (a), test__tmp[1].p = (b), \ + check_pointer_eq_loc(TEST_LOCATION(), #a" == "#b, \ + test__tmp[0].p == test__tmp[1].p, \ + test__tmp[0].p, test__tmp[1].p)) +int check_pointer_eq_loc(const char *loc, const char *check, int ok, + const void *a, const void *b); + /* * Compare two integers. Prints a message with the two values if the * comparison fails. NB this is not thread safe. @@ -136,6 +148,7 @@ union test__tmp { intmax_t i; uintmax_t u; char c; + const void *p; }; extern union test__tmp test__tmp[2]; diff --git a/transport-helper.c b/transport-helper.c index 780fcaf529..9820947ab2 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -22,7 +22,7 @@ static int debug; struct helper_data { - const char *name; + char *name; struct child_process *helper; FILE *out; unsigned fetch : 1, @@ -111,6 +111,7 @@ static void do_take_over(struct transport *transport) data = (struct helper_data *)transport->data; transport_take_over(transport, data->helper); fclose(data->out); + free(data->name); free(data); } @@ -253,6 +254,7 @@ static int disconnect_helper(struct transport *transport) close(data->helper->out); fclose(data->out); res = finish_command(data->helper); + FREE_AND_NULL(data->name); FREE_AND_NULL(data->helper); } return res; @@ -1297,7 +1299,7 @@ static struct transport_vtable vtable = { int transport_helper_init(struct transport *transport, const char *name) { struct helper_data *data = xcalloc(1, sizeof(*data)); - data->name = name; + data->name = xstrdup(name); transport_check_allowed(name); diff --git a/transport.c b/transport.c index 0ad04b77fd..83ddea8fbc 100644 --- a/transport.c +++ b/transport.c @@ -1176,6 +1176,7 @@ struct transport *transport_get(struct remote *remote, const char *url) int len = external_specification_len(url); char *handler = xmemdupz(url, len); transport_helper_init(ret, handler); + free(handler); } if (ret->smart_options) { diff --git a/upload-pack.c b/upload-pack.c index bbfb04c8bd..b726f7a57d 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -94,7 +94,7 @@ struct upload_pack_data { struct packet_writer writer; - const char *pack_objects_hook; + char *pack_objects_hook; unsigned stateless_rpc : 1; /* v0 only */ unsigned no_done : 1; /* v0 only */ diff --git a/userdiff.h b/userdiff.h index d726804c3e..cc8e5abfef 100644 --- a/userdiff.h +++ b/userdiff.h @@ -7,19 +7,19 @@ struct index_state; struct repository; struct userdiff_funcname { - const char *pattern; + char *pattern; int cflags; }; struct userdiff_driver { const char *name; - const char *external; - const char *algorithm; + char *external; + char *algorithm; int binary; struct userdiff_funcname funcname; - const char *word_regex; - const char *word_regex_multi_byte; - const char *textconv; + char *word_regex; + char *word_regex_multi_byte; + char *textconv; struct notes_cache *textconv_cache; int textconv_want_cache; };