stash: always have the owner of "stash_info" free it

Change the initialization of the "revision" member of "struct stash_info" to be initialized vi a macro, and more importantly that that initializing function be tasked to free it, usually via a "goto cleanup" pattern. Despite the "revision" name (and the topic of the series containing this commit) the "stash info" has nothing to do with the "struct rev_info". I'm making this change because in the subsequent commit when we do want to free the "struct rev_info" via a "goto cleanup" pattern we'd otherwise free() uninitialized memory in some cases, as we only strbuf_init() the string in get_stash_info(). So while it's not the smallest possible change, let's convert all users of this pattern in the file while we're at it. A good follow-up to this change would be to change all the "ret = -1; goto done;" in this file to instead use a "goto cleanup", and initialize "int ret = -1" at the start of the relevant functions. That would allow us to drop a lot of needless brace verbosity on two-line "if" statements, but let's leave that alone for now. To ensure that there's a 1=1 mapping between owners of the "struct stash_info" and free_stash_info() change the assert_stash_ref() function to be a trivial get_stash_info_assert() wrapper. The caller will call free_stash_info(), and by returning -1 we'll eventually (via !!ret) exit with status 1 anyway. Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Ævar Arnfjörð Bjarmason committed Apr 13, 2022 at 22:01 UTC 5e480176fed01aeb47735f525002203ac6e462d0
1 file changed +59 -48
builtin/stash.c
+59 -48
@@ -116,6 +116,10 @@ struct stash_info {
116 int has_u;
117 };
118
119 +#define STASH_INFO_INIT { \
120 + .revision = STRBUF_INIT, \
121 +}
122 +
123 static void free_stash_info(struct stash_info *info)
124 {
125 strbuf_release(&info->revision);
@@ -157,10 +161,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
161 if (argc == 1)
162 commit = argv[0];
163
160 - strbuf_init(&info->revision, 0);
164 if (!commit) {
165 if (!ref_exists(ref_stash)) {
163 - free_stash_info(info);
166 fprintf_ln(stderr, _("No stash entries found."));
167 return -1;
168 }
@@ -174,11 +176,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
176
177 revision = info->revision.buf;
178
177 - if (get_oid(revision, &info->w_commit)) {
178 - error(_("%s is not a valid reference"), revision);
179 - free_stash_info(info);
180 - return -1;
181 - }
179 + if (get_oid(revision, &info->w_commit))
180 + return error(_("%s is not a valid reference"), revision);
181
182 assert_stash_like(info, revision);
183
@@ -197,7 +196,7 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
196 info->is_stash_ref = !strcmp(expanded_ref, ref_stash);
197 break;
198 default: /* Invalid or ambiguous */
200 - free_stash_info(info);
199 + break;
200 }
201
202 free(expanded_ref);
@@ -598,10 +597,10 @@ restore_untracked:
597
598 static int apply_stash(int argc, const char **argv, const char *prefix)
599 {
601 - int ret;
600 + int ret = -1;
601 int quiet = 0;
602 int index = 0;
604 - struct stash_info info;
603 + struct stash_info info = STASH_INFO_INIT;
604 struct option options[] = {
605 OPT__QUIET(&quiet, N_("be quiet, only report errors")),
606 OPT_BOOL(0, "index", &index,
@@ -613,9 +612,10 @@ static int apply_stash(int argc, const char **argv, const char *prefix)
612 git_stash_apply_usage, 0);
613
614 if (get_stash_info(&info, argc, argv))
616 - return -1;
615 + goto cleanup;
616
617 ret = do_apply_stash(prefix, &info, index, quiet);
618 +cleanup:
619 free_stash_info(&info);
620 return ret;
621 }
@@ -651,20 +651,25 @@ static int do_drop_stash(struct stash_info *info, int quiet)
651 return 0;
652 }
653
654 -static void assert_stash_ref(struct stash_info *info)
654 +static int get_stash_info_assert(struct stash_info *info, int argc,
655 + const char **argv)
656 {
656 - if (!info->is_stash_ref) {
657 - error(_("'%s' is not a stash reference"), info->revision.buf);
658 - free_stash_info(info);
659 - exit(1);
660 - }
657 + int ret = get_stash_info(info, argc, argv);
658 +
659 + if (ret < 0)
660 + return ret;
661 +
662 + if (!info->is_stash_ref)
663 + return error(_("'%s' is not a stash reference"), info->revision.buf);
664 +
665 + return 0;
666 }
667
668 static int drop_stash(int argc, const char **argv, const char *prefix)
669 {
665 - int ret;
670 + int ret = -1;
671 int quiet = 0;
667 - struct stash_info info;
672 + struct stash_info info = STASH_INFO_INIT;
673 struct option options[] = {
674 OPT__QUIET(&quiet, N_("be quiet, only report errors")),
675 OPT_END()
@@ -673,22 +678,21 @@ static int drop_stash(int argc, const char **argv, const char *prefix)
678 argc = parse_options(argc, argv, prefix, options,
679 git_stash_drop_usage, 0);
680
676 - if (get_stash_info(&info, argc, argv))
677 - return -1;
678 -
679 - assert_stash_ref(&info);
681 + if (get_stash_info_assert(&info, argc, argv))
682 + goto cleanup;
683
684 ret = do_drop_stash(&info, quiet);
685 +cleanup:
686 free_stash_info(&info);
687 return ret;
688 }
689
690 static int pop_stash(int argc, const char **argv, const char *prefix)
691 {
688 - int ret;
692 + int ret = -1;
693 int index = 0;
694 int quiet = 0;
691 - struct stash_info info;
695 + struct stash_info info = STASH_INFO_INIT;
696 struct option options[] = {
697 OPT__QUIET(&quiet, N_("be quiet, only report errors")),
698 OPT_BOOL(0, "index", &index,
@@ -699,25 +703,25 @@ static int pop_stash(int argc, const char **argv, const char *prefix)
703 argc = parse_options(argc, argv, prefix, options,
704 git_stash_pop_usage, 0);
705
702 - if (get_stash_info(&info, argc, argv))
703 - return -1;
706 + if (get_stash_info_assert(&info, argc, argv))
707 + goto cleanup;
708
705 - assert_stash_ref(&info);
709 if ((ret = do_apply_stash(prefix, &info, index, quiet)))
710 printf_ln(_("The stash entry is kept in case "
711 "you need it again."));
712 else
713 ret = do_drop_stash(&info, quiet);
714
715 +cleanup:
716 free_stash_info(&info);
717 return ret;
718 }
719
720 static int branch_stash(int argc, const char **argv, const char *prefix)
721 {
718 - int ret;
722 + int ret = -1;
723 const char *branch = NULL;
720 - struct stash_info info;
724 + struct stash_info info = STASH_INFO_INIT;
725 struct child_process cp = CHILD_PROCESS_INIT;
726 struct option options[] = {
727 OPT_END()
@@ -734,7 +738,7 @@ static int branch_stash(int argc, const char **argv, const char *prefix)
738 branch = argv[0];
739
740 if (get_stash_info(&info, argc - 1, argv + 1))
737 - return -1;
741 + goto cleanup;
742
743 cp.git_cmd = 1;
744 strvec_pushl(&cp.args, "checkout", "-b", NULL);
@@ -746,8 +750,8 @@ static int branch_stash(int argc, const char **argv, const char *prefix)
750 if (!ret && info.is_stash_ref)
751 ret = do_drop_stash(&info, 0);
752
753 +cleanup:
754 free_stash_info(&info);
750 -
755 return ret;
756 }
757
@@ -825,8 +829,8 @@ static void diff_include_untracked(const struct stash_info *info, struct diff_op
829 static int show_stash(int argc, const char **argv, const char *prefix)
830 {
831 int i;
828 - int ret = 0;
829 - struct stash_info info;
832 + int ret = -1;
833 + struct stash_info info = STASH_INFO_INIT;
834 struct rev_info rev;
835 struct strvec stash_args = STRVEC_INIT;
836 struct strvec revision_args = STRVEC_INIT;
@@ -844,6 +848,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)
848 UNTRACKED_ONLY, PARSE_OPT_NONEG),
849 OPT_END()
850 };
851 + int do_usage = 0;
852
853 init_diff_ui_defaults();
854 git_config(git_diff_ui_config, NULL);
@@ -861,10 +866,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)
866 strvec_push(&revision_args, argv[i]);
867 }
868
864 - ret = get_stash_info(&info, stash_args.nr, stash_args.v);
865 - strvec_clear(&stash_args);
866 - if (ret)
867 - return -1;
869 + if (get_stash_info(&info, stash_args.nr, stash_args.v))
870 + goto cleanup;
871
872 /*
873 * The config settings are applied only if there are not passed
@@ -878,16 +881,14 @@ static int show_stash(int argc, const char **argv, const char *prefix)
881 rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
882
883 if (!show_stat && !show_patch) {
881 - free_stash_info(&info);
882 - return 0;
884 + ret = 0;
885 + goto cleanup;
886 }
887 }
888
889 argc = setup_revisions(revision_args.nr, revision_args.v, &rev, NULL);
887 - if (argc > 1) {
888 - free_stash_info(&info);
889 - usage_with_options(git_stash_show_usage, options);
890 - }
890 + if (argc > 1)
891 + goto usage;
892 if (!rev.diffopt.output_format) {
893 rev.diffopt.output_format = DIFF_FORMAT_PATCH;
894 diff_setup_done(&rev.diffopt);
@@ -912,8 +913,16 @@ static int show_stash(int argc, const char **argv, const char *prefix)
913 }
914 log_tree_diff_flush(&rev);
915
916 + ret = diff_result_code(&rev.diffopt, 0);
917 +cleanup:
918 + strvec_clear(&stash_args);
919 free_stash_info(&info);
916 - return diff_result_code(&rev.diffopt, 0);
920 + if (do_usage)
921 + usage_with_options(git_stash_show_usage, options);
922 + return ret;
923 +usage:
924 + do_usage = 1;
925 + goto cleanup;
926 }
927
928 static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,
@@ -1409,9 +1418,9 @@ done:
1418
1419 static int create_stash(int argc, const char **argv, const char *prefix)
1420 {
1412 - int ret = 0;
1421 + int ret;
1422 struct strbuf stash_msg_buf = STRBUF_INIT;
1414 - struct stash_info info;
1423 + struct stash_info info = STASH_INFO_INIT;
1424 struct pathspec ps;
1425
1426 /* Starting with argv[1], since argv[0] is "create" */
@@ -1426,6 +1435,7 @@ static int create_stash(int argc, const char **argv, const char *prefix)
1435 if (!ret)
1436 printf_ln("%s", oid_to_hex(&info.w_commit));
1437
1438 + free_stash_info(&info);
1439 strbuf_release(&stash_msg_buf);
1440 return ret;
1441 }
@@ -1434,7 +1444,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
1444 int keep_index, int patch_mode, int include_untracked, int only_staged)
1445 {
1446 int ret = 0;
1437 - struct stash_info info;
1447 + struct stash_info info = STASH_INFO_INIT;
1448 struct strbuf patch = STRBUF_INIT;
1449 struct strbuf stash_msg_buf = STRBUF_INIT;
1450 struct strbuf untracked_files = STRBUF_INIT;
@@ -1632,6 +1642,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
1642 }
1643
1644 done:
1645 + free_stash_info(&info);
1646 strbuf_release(&stash_msg_buf);
1647 return ret;
1648 }