Git fork

Merge branch 'jk/setup-revisions-freefix'

There are double frees and leaks around setup_revisions() API used
in "git stash show", which has been fixed, and setup_revisions()
API gained a wrapper to make it more ergonomic when using it with
strvec-manged argc/argv pairs.

* jk/setup-revisions-freefix:
revision: retain argv NULL invariant in setup_revisions()
treewide: pass strvecs around for setup_revisions_from_strvec()
treewide: use setup_revisions_from_strvec() when we have a strvec
revision: add wrapper to setup_revisions() from a strvec
revision: manage memory ownership of argv in setup_revisions()
stash: tell setup_revisions() to free our allocated strings

+85 -42
+1 -4
bisect.c
··· 674 const char *bad_format, const char *good_format, 675 int read_paths) 676 { 677 - struct setup_revision_opt opt = { 678 - .free_removed_argv_elements = 1, 679 - }; 680 int i; 681 682 repo_init_revisions(r, revs, prefix); ··· 693 if (read_paths) 694 read_bisect_paths(rev_argv); 695 696 - setup_revisions(rev_argv->nr, rev_argv->v, revs, &opt); 697 } 698 699 static void bisect_common(struct rev_info *revs)
··· 674 const char *bad_format, const char *good_format, 675 int read_paths) 676 { 677 int i; 678 679 repo_init_revisions(r, revs, prefix); ··· 690 if (read_paths) 691 read_bisect_paths(rev_argv); 692 693 + setup_revisions_from_strvec(rev_argv, revs, NULL); 694 } 695 696 static void bisect_common(struct rev_info *revs)
+2 -1
builtin/describe.c
··· 580 NULL); 581 582 repo_init_revisions(the_repository, &revs, NULL); 583 - if (setup_revisions(args.nr, args.v, &revs, NULL) > 1) 584 BUG("setup_revisions could not handle all args?"); 585 586 if (prepare_revision_walk(&revs))
··· 580 NULL); 581 582 repo_init_revisions(the_repository, &revs, NULL); 583 + setup_revisions_from_strvec(&args, &revs, NULL); 584 + if (args.nr > 1) 585 BUG("setup_revisions could not handle all args?"); 586 587 if (prepare_revision_walk(&revs))
+3 -3
builtin/pack-objects.c
··· 4650 die(_("failed to pack objects via path-walk")); 4651 } 4652 4653 - static void get_object_list(struct rev_info *revs, int ac, const char **av) 4654 { 4655 struct setup_revision_opt s_r_opt = { 4656 .allow_exclude_promisor_objects = 1, ··· 4660 int save_warning; 4661 4662 save_commit_buffer = 0; 4663 - setup_revisions(ac, av, revs, &s_r_opt); 4664 4665 /* make sure shallows are read */ 4666 is_repository_shallow(the_repository); ··· 5229 revs.include_check = is_not_in_promisor_pack; 5230 revs.include_check_obj = is_not_in_promisor_pack_obj; 5231 } 5232 - get_object_list(&revs, rp.nr, rp.v); 5233 release_revisions(&revs); 5234 } 5235 cleanup_preferred_base();
··· 4650 die(_("failed to pack objects via path-walk")); 4651 } 4652 4653 + static void get_object_list(struct rev_info *revs, struct strvec *argv) 4654 { 4655 struct setup_revision_opt s_r_opt = { 4656 .allow_exclude_promisor_objects = 1, ··· 4660 int save_warning; 4661 4662 save_commit_buffer = 0; 4663 + setup_revisions_from_strvec(argv, revs, &s_r_opt); 4664 4665 /* make sure shallows are read */ 4666 is_repository_shallow(the_repository); ··· 5229 revs.include_check = is_not_in_promisor_pack; 5230 revs.include_check_obj = is_not_in_promisor_pack_obj; 5231 } 5232 + get_object_list(&revs, &rp); 5233 release_revisions(&revs); 5234 } 5235 cleanup_preferred_base();
+1 -2
builtin/rebase.c
··· 299 oid_to_hex(&opts->restrict_revision->object.oid)); 300 301 ret = sequencer_make_script(the_repository, &todo_list.buf, 302 - make_script_args.nr, make_script_args.v, 303 - flags); 304 if (ret) { 305 error(_("could not generate todo list")); 306 goto cleanup;
··· 299 oid_to_hex(&opts->restrict_revision->object.oid)); 300 301 ret = sequencer_make_script(the_repository, &todo_list.buf, 302 + &make_script_args, flags); 303 if (ret) { 304 error(_("could not generate todo list")); 305 goto cleanup;
+2 -2
builtin/stash.c
··· 1015 } 1016 } 1017 1018 - argc = setup_revisions(revision_args.nr, revision_args.v, &rev, NULL); 1019 - if (argc > 1) 1020 goto usage; 1021 if (!rev.diffopt.output_format) { 1022 rev.diffopt.output_format = DIFF_FORMAT_PATCH;
··· 1015 } 1016 } 1017 1018 + setup_revisions_from_strvec(&revision_args, &rev, NULL); 1019 + if (revision_args.nr > 1) 1020 goto usage; 1021 if (!rev.diffopt.output_format) { 1022 rev.diffopt.output_format = DIFF_FORMAT_PATCH;
+2 -8
builtin/submodule--helper.c
··· 616 struct rev_info rev = REV_INFO_INIT; 617 struct strbuf buf = STRBUF_INIT; 618 const char *git_dir; 619 - struct setup_revision_opt opt = { 620 - .free_removed_argv_elements = 1, 621 - }; 622 623 if (validate_submodule_path(path) < 0) 624 die(NULL); ··· 655 656 repo_init_revisions(the_repository, &rev, NULL); 657 rev.abbrev = 0; 658 - setup_revisions(diff_files_args.nr, diff_files_args.v, &rev, &opt); 659 run_diff_files(&rev, 0); 660 661 if (!diff_result_code(&rev)) { ··· 1094 { 1095 struct strvec diff_args = STRVEC_INIT; 1096 struct rev_info rev; 1097 - struct setup_revision_opt opt = { 1098 - .free_removed_argv_elements = 1, 1099 - }; 1100 struct module_cb_list list = MODULE_CB_LIST_INIT; 1101 int ret = 0; 1102 ··· 1114 repo_init_revisions(the_repository, &rev, info->prefix); 1115 rev.abbrev = 0; 1116 precompose_argv_prefix(diff_args.nr, diff_args.v, NULL); 1117 - setup_revisions(diff_args.nr, diff_args.v, &rev, &opt); 1118 rev.diffopt.output_format = DIFF_FORMAT_NO_OUTPUT | DIFF_FORMAT_CALLBACK; 1119 rev.diffopt.format_callback = submodule_summary_callback; 1120 rev.diffopt.format_callback_data = &list;
··· 616 struct rev_info rev = REV_INFO_INIT; 617 struct strbuf buf = STRBUF_INIT; 618 const char *git_dir; 619 620 if (validate_submodule_path(path) < 0) 621 die(NULL); ··· 652 653 repo_init_revisions(the_repository, &rev, NULL); 654 rev.abbrev = 0; 655 + setup_revisions_from_strvec(&diff_files_args, &rev, NULL); 656 run_diff_files(&rev, 0); 657 658 if (!diff_result_code(&rev)) { ··· 1091 { 1092 struct strvec diff_args = STRVEC_INIT; 1093 struct rev_info rev; 1094 struct module_cb_list list = MODULE_CB_LIST_INIT; 1095 int ret = 0; 1096 ··· 1108 repo_init_revisions(the_repository, &rev, info->prefix); 1109 rev.abbrev = 0; 1110 precompose_argv_prefix(diff_args.nr, diff_args.v, NULL); 1111 + setup_revisions_from_strvec(&diff_args, &rev, NULL); 1112 rev.diffopt.output_format = DIFF_FORMAT_NO_OUTPUT | DIFF_FORMAT_CALLBACK; 1113 rev.diffopt.format_callback = submodule_summary_callback; 1114 rev.diffopt.format_callback_data = &list;
+1 -1
http-push.c
··· 1941 strvec_pushf(&commit_argv, "^%s", 1942 oid_to_hex(&ref->old_oid)); 1943 repo_init_revisions(the_repository, &revs, setup_git_directory()); 1944 - setup_revisions(commit_argv.nr, commit_argv.v, &revs, NULL); 1945 revs.edge_hint = 0; /* just in case */ 1946 1947 /* Generate a list of objects that need to be pushed */
··· 1941 strvec_pushf(&commit_argv, "^%s", 1942 oid_to_hex(&ref->old_oid)); 1943 repo_init_revisions(the_repository, &revs, setup_git_directory()); 1944 + setup_revisions_from_strvec(&commit_argv, &revs, NULL); 1945 revs.edge_hint = 0; /* just in case */ 1946 1947 /* Generate a list of objects that need to be pushed */
+1 -4
remote.c
··· 2143 struct object_id oid; 2144 struct commit *ours, *theirs; 2145 struct rev_info revs; 2146 - struct setup_revision_opt opt = { 2147 - .free_removed_argv_elements = 1, 2148 - }; 2149 struct strvec argv = STRVEC_INIT; 2150 2151 /* Cannot stat if what we used to build on no longer exists */ ··· 2180 strvec_push(&argv, "--"); 2181 2182 repo_init_revisions(the_repository, &revs, NULL); 2183 - setup_revisions(argv.nr, argv.v, &revs, &opt); 2184 if (prepare_revision_walk(&revs)) 2185 die(_("revision walk setup failed")); 2186
··· 2143 struct object_id oid; 2144 struct commit *ours, *theirs; 2145 struct rev_info revs; 2146 struct strvec argv = STRVEC_INIT; 2147 2148 /* Cannot stat if what we used to build on no longer exists */ ··· 2177 strvec_push(&argv, "--"); 2178 2179 repo_init_revisions(the_repository, &revs, NULL); 2180 + setup_revisions_from_strvec(&argv, &revs, NULL); 2181 if (prepare_revision_walk(&revs)) 2182 die(_("revision walk setup failed")); 2183
+46 -3
revision.c
··· 2321 return num; 2322 } 2323 2324 static int handle_revision_opt(struct rev_info *revs, int argc, const char **argv, 2325 int *unkc, const char **unkv, 2326 const struct setup_revision_opt* opt) ··· 2342 starts_with(arg, "--branches=") || starts_with(arg, "--tags=") || 2343 starts_with(arg, "--remotes=") || starts_with(arg, "--no-walk=")) 2344 { 2345 - unkv[(*unkc)++] = arg; 2346 return 1; 2347 } 2348 ··· 2706 } else { 2707 int opts = diff_opt_parse(&revs->diffopt, argv, argc, revs->prefix); 2708 if (!opts) 2709 - unkv[(*unkc)++] = arg; 2710 return opts; 2711 } 2712 ··· 3018 3019 if (!strcmp(arg, "--stdin")) { 3020 if (revs->disable_stdin) { 3021 - argv[left++] = arg; 3022 continue; 3023 } 3024 if (revs->read_from_stdin++) ··· 3174 revs->show_notes_given = 1; 3175 } 3176 3177 return left; 3178 } 3179 3180 static void release_revisions_cmdline(struct rev_cmdline_info *cmdline)
··· 2321 return num; 2322 } 2323 2324 + static void overwrite_argv(int *argc, const char **argv, 2325 + const char **value, 2326 + const struct setup_revision_opt *opt) 2327 + { 2328 + /* 2329 + * Detect the case when we are overwriting ourselves. The assignment 2330 + * itself would be a noop either way, but this lets us avoid corner 2331 + * cases around the free() and NULL operations. 2332 + */ 2333 + if (*value != argv[*argc]) { 2334 + if (opt && opt->free_removed_argv_elements) 2335 + free((char *)argv[*argc]); 2336 + argv[*argc] = *value; 2337 + *value = NULL; 2338 + } 2339 + (*argc)++; 2340 + } 2341 + 2342 static int handle_revision_opt(struct rev_info *revs, int argc, const char **argv, 2343 int *unkc, const char **unkv, 2344 const struct setup_revision_opt* opt) ··· 2360 starts_with(arg, "--branches=") || starts_with(arg, "--tags=") || 2361 starts_with(arg, "--remotes=") || starts_with(arg, "--no-walk=")) 2362 { 2363 + overwrite_argv(unkc, unkv, &argv[0], opt); 2364 return 1; 2365 } 2366 ··· 2724 } else { 2725 int opts = diff_opt_parse(&revs->diffopt, argv, argc, revs->prefix); 2726 if (!opts) 2727 + overwrite_argv(unkc, unkv, &argv[0], opt); 2728 return opts; 2729 } 2730 ··· 3036 3037 if (!strcmp(arg, "--stdin")) { 3038 if (revs->disable_stdin) { 3039 + overwrite_argv(&left, argv, &argv[i], opt); 3040 continue; 3041 } 3042 if (revs->read_from_stdin++) ··· 3192 revs->show_notes_given = 1; 3193 } 3194 3195 + if (argv) { 3196 + if (opt && opt->free_removed_argv_elements) 3197 + free((char *)argv[left]); 3198 + argv[left] = NULL; 3199 + } 3200 + 3201 return left; 3202 + } 3203 + 3204 + void setup_revisions_from_strvec(struct strvec *argv, struct rev_info *revs, 3205 + struct setup_revision_opt *opt) 3206 + { 3207 + struct setup_revision_opt fallback_opt; 3208 + int ret; 3209 + 3210 + if (!opt) { 3211 + memset(&fallback_opt, 0, sizeof(fallback_opt)); 3212 + opt = &fallback_opt; 3213 + } 3214 + opt->free_removed_argv_elements = 1; 3215 + 3216 + ret = setup_revisions(argv->nr, argv->v, revs, opt); 3217 + 3218 + for (size_t i = ret; i < argv->nr; i++) 3219 + free((char *)argv->v[i]); 3220 + argv->nr = ret; 3221 } 3222 3223 static void release_revisions_cmdline(struct rev_cmdline_info *cmdline)
+2
revision.h
··· 441 }; 442 int setup_revisions(int argc, const char **argv, struct rev_info *revs, 443 struct setup_revision_opt *); 444 445 /** 446 * Free data allocated in a "struct rev_info" after it's been
··· 441 }; 442 int setup_revisions(int argc, const char **argv, struct rev_info *revs, 443 struct setup_revision_opt *); 444 + void setup_revisions_from_strvec(struct strvec *argv, struct rev_info *revs, 445 + struct setup_revision_opt *); 446 447 /** 448 * Free data allocated in a "struct rev_info" after it's been
+4 -3
sequencer.c
··· 6052 return 0; 6053 } 6054 6055 - int sequencer_make_script(struct repository *r, struct strbuf *out, int argc, 6056 - const char **argv, unsigned flags) 6057 { 6058 char *format = NULL; 6059 struct pretty_print_context pp = {0}; ··· 6094 pp.fmt = revs.commit_format; 6095 pp.output_encoding = get_log_output_encoding(); 6096 6097 - if (setup_revisions(argc, argv, &revs, NULL) > 1) { 6098 ret = error(_("make_script: unhandled options")); 6099 goto cleanup; 6100 }
··· 6052 return 0; 6053 } 6054 6055 + int sequencer_make_script(struct repository *r, struct strbuf *out, 6056 + struct strvec *argv, unsigned flags) 6057 { 6058 char *format = NULL; 6059 struct pretty_print_context pp = {0}; ··· 6094 pp.fmt = revs.commit_format; 6095 pp.output_encoding = get_log_output_encoding(); 6096 6097 + setup_revisions_from_strvec(argv, &revs, NULL); 6098 + if (argv->nr > 1) { 6099 ret = error(_("make_script: unhandled options")); 6100 goto cleanup; 6101 }
+2 -2
sequencer.h
··· 186 #define TODO_LIST_REAPPLY_CHERRY_PICKS (1U << 7) 187 #define TODO_LIST_WARN_SKIPPED_CHERRY_PICKS (1U << 8) 188 189 - int sequencer_make_script(struct repository *r, struct strbuf *out, int argc, 190 - const char **argv, unsigned flags); 191 192 int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags, 193 const char *shortrevisions, const char *onto_name,
··· 186 #define TODO_LIST_REAPPLY_CHERRY_PICKS (1U << 7) 187 #define TODO_LIST_WARN_SKIPPED_CHERRY_PICKS (1U << 8) 188 189 + int sequencer_make_script(struct repository *r, struct strbuf *out, 190 + struct strvec *argv, unsigned flags); 191 192 int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags, 193 const char *shortrevisions, const char *onto_name,
+2 -2
shallow.c
··· 213 * are marked with shallow_flag. The list of border/shallow commits 214 * are also returned. 215 */ 216 - struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av, 217 int shallow_flag, 218 int not_shallow_flag) 219 { ··· 232 233 repo_init_revisions(the_repository, &revs, NULL); 234 save_commit_buffer = 0; 235 - setup_revisions(ac, av, &revs, NULL); 236 237 if (prepare_revision_walk(&revs)) 238 die("revision walk setup failed");
··· 213 * are marked with shallow_flag. The list of border/shallow commits 214 * are also returned. 215 */ 216 + struct commit_list *get_shallow_commits_by_rev_list(struct strvec *argv, 217 int shallow_flag, 218 int not_shallow_flag) 219 { ··· 232 233 repo_init_revisions(the_repository, &revs, NULL); 234 save_commit_buffer = 0; 235 + setup_revisions_from_strvec(argv, &revs, NULL); 236 237 if (prepare_revision_walk(&revs)) 238 die("revision walk setup failed");
+3 -2
shallow.h
··· 7 #include "strbuf.h" 8 9 struct oid_array; 10 11 void set_alternate_shallow_file(struct repository *r, const char *path, int override); 12 int register_shallow(struct repository *r, const struct object_id *oid); ··· 36 37 struct commit_list *get_shallow_commits(struct object_array *heads, 38 int depth, int shallow_flag, int not_shallow_flag); 39 - struct commit_list *get_shallow_commits_by_rev_list( 40 - int ac, const char **av, int shallow_flag, int not_shallow_flag); 41 int write_shallow_commits(struct strbuf *out, int use_pack_protocol, 42 const struct oid_array *extra); 43
··· 7 #include "strbuf.h" 8 9 struct oid_array; 10 + struct strvec; 11 12 void set_alternate_shallow_file(struct repository *r, const char *path, int override); 13 int register_shallow(struct repository *r, const struct object_id *oid); ··· 37 38 struct commit_list *get_shallow_commits(struct object_array *heads, 39 int depth, int shallow_flag, int not_shallow_flag); 40 + struct commit_list *get_shallow_commits_by_rev_list(struct strvec *argv, 41 + int shallow_flag, int not_shallow_flag); 42 int write_shallow_commits(struct strbuf *out, int use_pack_protocol, 43 const struct oid_array *extra); 44
+1 -1
submodule.c
··· 900 save_warning = warn_on_object_refname_ambiguity; 901 warn_on_object_refname_ambiguity = 0; 902 repo_init_revisions(r, &rev, NULL); 903 - setup_revisions(argv->nr, argv->v, &rev, &s_r_opt); 904 warn_on_object_refname_ambiguity = save_warning; 905 if (prepare_revision_walk(&rev)) 906 die(_("revision walk setup failed"));
··· 900 save_warning = warn_on_object_refname_ambiguity; 901 warn_on_object_refname_ambiguity = 0; 902 repo_init_revisions(r, &rev, NULL); 903 + setup_revisions_from_strvec(argv, &rev, &s_r_opt); 904 warn_on_object_refname_ambiguity = save_warning; 905 if (prepare_revision_walk(&rev)) 906 die(_("revision walk setup failed"));
+9
t/t3903-stash.sh
··· 1741 ) 1742 ' 1743 1744 test_done
··· 1741 ) 1742 ' 1743 1744 + test_expect_success SANITIZE_LEAK 'stash show handles -- without leaking' ' 1745 + git stash show -- 1746 + ' 1747 + 1748 + test_expect_success 'controlled error return on unrecognized option' ' 1749 + test_expect_code 129 git stash show -p --invalid 2>usage && 1750 + grep -e "^usage: git stash show" usage 1751 + ' 1752 + 1753 test_done
+3 -4
upload-pack.c
··· 913 } 914 915 static void deepen_by_rev_list(struct upload_pack_data *data, 916 - int ac, 917 - const char **av) 918 { 919 struct commit_list *result; 920 921 disable_commit_graph(the_repository); 922 - result = get_shallow_commits_by_rev_list(ac, av, SHALLOW, NOT_SHALLOW); 923 send_shallow(data, result); 924 free_commit_list(result); 925 send_unshallow(data); ··· 955 struct object *o = data->want_obj.objects[i].item; 956 strvec_push(&av, oid_to_hex(&o->oid)); 957 } 958 - deepen_by_rev_list(data, av.nr, av.v); 959 strvec_clear(&av); 960 ret = 1; 961 } else {
··· 913 } 914 915 static void deepen_by_rev_list(struct upload_pack_data *data, 916 + struct strvec *argv) 917 { 918 struct commit_list *result; 919 920 disable_commit_graph(the_repository); 921 + result = get_shallow_commits_by_rev_list(argv, SHALLOW, NOT_SHALLOW); 922 send_shallow(data, result); 923 free_commit_list(result); 924 send_unshallow(data); ··· 954 struct object *o = data->want_obj.objects[i].item; 955 strvec_push(&av, oid_to_hex(&o->oid)); 956 } 957 + deepen_by_rev_list(data, &av); 958 strvec_clear(&av); 959 ret = 1; 960 } else {