{"thread":{"id":"56429","subject":"[PATCH v6 0/6] Finish converting git bisect to C part 4","startedAt":"2021-09-02T09:05:20Z","lastAt":"2021-09-09T07:52:11Z","messageCount":17,"participants":["Miriam Rubio","Junio C Hamano","Johannes Schindelin","Miriam R."],"isPatch":true,"patchVersion":6,"patchTotal":6},"messages":[{"id":"434496","messageId":"20210902090421.93113-1-mirucam@gmail.com","threadId":"56429","inReplyTo":null,"subject":"[PATCH v6 0/6] Finish converting git bisect to C part 4","fromName":"Miriam Rubio","fromEmail":"mirucam@gmail.com","sentAt":"2021-09-02T09:04:15Z","receivedAt":"2021-09-02T09:05:20Z","isPatch":true,"sender":{"key":"mirucam@gmail.com","avatar":"https://avatars.githubusercontent.com/u/56339109?v=4"},"body":"These patches correspond to a fourth part of patch series \nof Outreachy project \"Finish converting `git bisect` from shell to C\" \nstarted by Pranit Bauva and Tanushree Tumane\n(https://public-inbox.org/git/pull.117.git.gitgitgadget@gmail.com) and\ncontinued by me.\n\nThis fourth part is formed by reimplementations of some `git bisect` \nsubcommands, addition of tests and removal of some temporary subcommands.\n\nThese patch series emails were generated from:\nhttps://gitlab.com/mirucam/git/commits/git-bisect-work-part4-v6.\n\nI would like to thank Johannes Schindelin for reviewing this patch \nseries.\n\nSpecific changes\n----------------\n\n[5/6] bisect--helper: reimplement `bisect_run` shell function in C\n* Format line.\n* Use copy_fd() in print_file_to_stdout().\n---\n\nMiriam Rubio (3):\n  t6030-bisect-porcelain: add tests to control bisect run exit cases\n  t6030-bisect-porcelain: add test for bisect visualize\n  bisect--helper: retire `--bisect-next-check` subcommand\n\nPranit Bauva (2):\n  run-command: make `exists_in_PATH()` non-static\n  bisect--helper: reimplement `bisect_visualize()`shell function in C\n\nTanushree Tumane (1):\n  bisect--helper: reimplement `bisect_run` shell function in C\n\n builtin/bisect--helper.c    | 152 ++++++++++++++++++++++++++++++++++--\n git-bisect.sh               |  87 +--------------------\n run-command.c               |   2 +-\n run-command.h               |  12 +++\n t/t6030-bisect-porcelain.sh |  18 +++++\n 5 files changed, 177 insertions(+), 94 deletions(-)\n\n-- \n2.29.2\n\n"},{"id":"434497","messageId":"20210902090421.93113-2-mirucam@gmail.com","threadId":"56429","inReplyTo":"20210902090421.93113-1-mirucam@gmail.com","subject":"[PATCH v6 1/6] t6030-bisect-porcelain: add tests to control bisect run exit cases","fromName":"Miriam Rubio","fromEmail":"mirucam@gmail.com","sentAt":"2021-09-02T09:04:16Z","receivedAt":"2021-09-02T09:05:21Z","isPatch":true,"sender":{"key":"mirucam@gmail.com","avatar":"https://avatars.githubusercontent.com/u/56339109?v=4"},"body":"There is a gap on bisect run test coverage related with error exits.\nAdd two tests to control these error cases.\n\nSigned-off-by: Miriam Rubio <mirucam@gmail.com>\n---\n t/t6030-bisect-porcelain.sh | 11 +++++++++++\n 1 file changed, 11 insertions(+)\n\ndiff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh\nindex a1baf4e451..e61b8143fd 100755\n--- a/t/t6030-bisect-porcelain.sh\n+++ b/t/t6030-bisect-porcelain.sh\n@@ -962,4 +962,15 @@ test_expect_success 'bisect handles annotated tags' '\n \tgrep \"$bad is the first bad commit\" output\n '\n \n+test_expect_success 'bisect run fails with exit code equals or greater than 128' '\n+\twrite_script test_script.sh <<-\\EOF &&\n+\texit 128 >/dev/null\n+\tEOF\n+\ttest_must_fail git bisect run ./test_script.sh > my_bisect_log.txt &&\n+\twrite_script test_script.sh <<-\\EOF &&\n+\texit 255 >/dev/null\n+\tEOF\n+\ttest_must_fail git bisect run ./test_script.sh >> my_bisect_log.txt\n+'\n+\n test_done\n-- \n2.29.2\n\n"},{"id":"434498","messageId":"20210902090421.93113-3-mirucam@gmail.com","threadId":"56429","inReplyTo":"20210902090421.93113-1-mirucam@gmail.com","subject":"[PATCH v6 2/6] t6030-bisect-porcelain: add test for bisect visualize","fromName":"Miriam Rubio","fromEmail":"mirucam@gmail.com","sentAt":"2021-09-02T09:04:17Z","receivedAt":"2021-09-02T09:05:23Z","isPatch":true,"sender":{"key":"mirucam@gmail.com","avatar":"https://avatars.githubusercontent.com/u/56339109?v=4"},"body":"Add a test to control breakages in bisect visualize command.\n\nSigned-off-by: Miriam Rubio <mirucam@gmail.com>\n---\n t/t6030-bisect-porcelain.sh | 7 +++++++\n 1 file changed, 7 insertions(+)\n\ndiff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh\nindex e61b8143fd..f13eeac9ce 100755\n--- a/t/t6030-bisect-porcelain.sh\n+++ b/t/t6030-bisect-porcelain.sh\n@@ -973,4 +973,11 @@ test_expect_success 'bisect run fails with exit code equals or greater than 128'\n \ttest_must_fail git bisect run ./test_script.sh >> my_bisect_log.txt\n '\n \n+test_expect_success 'bisect visualize with a filename with dash and space' '\n+\techo \"My test line\" >> -hello\\ 2 &&\n+\tgit add -- -hello\\ 2 &&\n+\tgit commit --quiet -m \"Add test line\" -- -hello\\ 2 &&\n+\tgit bisect visualize -p -- -hello\\ 2 > my_bisect_log.txt\n+'\n+\n test_done\n-- \n2.29.2\n\n"},{"id":"434500","messageId":"20210902090421.93113-4-mirucam@gmail.com","threadId":"56429","inReplyTo":"20210902090421.93113-1-mirucam@gmail.com","subject":"[PATCH v6 3/6] run-command: make `exists_in_PATH()` non-static","fromName":"Miriam Rubio","fromEmail":"mirucam@gmail.com","sentAt":"2021-09-02T09:04:18Z","receivedAt":"2021-09-02T09:05:23Z","isPatch":true,"sender":{"key":"mirucam@gmail.com","avatar":"https://avatars.githubusercontent.com/u/56339109?v=4"},"body":"From: Pranit Bauva <pranit.bauva@gmail.com>\n\nRemoves the `static` keyword from `exists_in_PATH()` function\nand declares the function in `run-command.h` file.\nThe function will be used in bisect_visualize() in a later\ncommit.\n\nMentored by: Christian Couder <chriscool@tuxfamily.org>\nMentored by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\nSigned-off-by: Tanushree Tumane <tanushreetumane@gmail.com>\nSigned-off-by: Miriam Rubio <mirucam@gmail.com>\n---\n run-command.c |  2 +-\n run-command.h | 12 ++++++++++++\n 2 files changed, 13 insertions(+), 1 deletion(-)\n\ndiff --git a/run-command.c b/run-command.c\nindex f72e72cce7..390f46819f 100644\n--- a/run-command.c\n+++ b/run-command.c\n@@ -210,7 +210,7 @@ static char *locate_in_PATH(const char *file)\n \treturn NULL;\n }\n \n-static int exists_in_PATH(const char *file)\n+int exists_in_PATH(const char *file)\n {\n \tchar *r = locate_in_PATH(file);\n \tint found = r != NULL;\ndiff --git a/run-command.h b/run-command.h\nindex af1296769f..54d74b706f 100644\n--- a/run-command.h\n+++ b/run-command.h\n@@ -182,6 +182,18 @@ void child_process_clear(struct child_process *);\n \n int is_executable(const char *name);\n \n+/**\n+ * Search if a $PATH for a command exists.  This emulates the path search that\n+ * execvp would perform, without actually executing the command so it\n+ * can be used before fork() to prepare to run a command using\n+ * execve() or after execvp() to diagnose why it failed.\n+ *\n+ * The caller should ensure that file contains no directory separators.\n+ *\n+ * Returns 1 if it is found in $PATH or 0 if the command could not be found.\n+ */\n+int exists_in_PATH(const char *file);\n+\n /**\n  * Start a sub-process. Takes a pointer to a `struct child_process`\n  * that specifies the details and returns pipe FDs (if requested).\n-- \n2.29.2\n\n"},{"id":"434499","messageId":"20210902090421.93113-5-mirucam@gmail.com","threadId":"56429","inReplyTo":"20210902090421.93113-1-mirucam@gmail.com","subject":"[PATCH v6 4/6] bisect--helper: reimplement `bisect_visualize()`shell function in C","fromName":"Miriam Rubio","fromEmail":"mirucam@gmail.com","sentAt":"2021-09-02T09:04:19Z","receivedAt":"2021-09-02T09:05:25Z","isPatch":true,"sender":{"key":"mirucam@gmail.com","avatar":"https://avatars.githubusercontent.com/u/56339109?v=4"},"body":"From: Pranit Bauva <pranit.bauva@gmail.com>\n\nReimplement the `bisect_visualize()` shell function\nin C and also add `--bisect-visualize` subcommand to\n`git bisect--helper` to call it from git-bisect.sh.\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\nSigned-off-by: Tanushree Tumane <tanushreetumane@gmail.com>\nSigned-off-by: Miriam Rubio <mirucam@gmail.com>\n---\n builtin/bisect--helper.c | 48 +++++++++++++++++++++++++++++++++++++++-\n git-bisect.sh            | 25 +--------------------\n 2 files changed, 48 insertions(+), 25 deletions(-)\n\ndiff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\nindex f184eaeac6..1e118a966a 100644\n--- a/builtin/bisect--helper.c\n+++ b/builtin/bisect--helper.c\n@@ -30,6 +30,7 @@ static const char * const git_bisect_helper_usage[] = {\n \tN_(\"git bisect--helper --bisect-state (good|old) [<rev>...]\"),\n \tN_(\"git bisect--helper --bisect-replay <filename>\"),\n \tN_(\"git bisect--helper --bisect-skip [(<rev>|<range>)...]\"),\n+\tN_(\"git bisect--helper --bisect-visualize\"),\n \tNULL\n };\n \n@@ -1036,6 +1037,44 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar\n \treturn res;\n }\n \n+static int bisect_visualize(struct bisect_terms *terms, const char **argv, int argc)\n+{\n+\tstruct strvec args = STRVEC_INIT;\n+\tint flags = RUN_COMMAND_NO_STDIN, res = 0;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n+\tif (bisect_next_check(terms, NULL) != 0)\n+\t\treturn BISECT_FAILED;\n+\n+\tif (!argc) {\n+\t\tif ((getenv(\"DISPLAY\") || getenv(\"SESSIONNAME\") || getenv(\"MSYSTEM\") ||\n+\t\t     getenv(\"SECURITYSESSIONID\")) && exists_in_PATH(\"gitk\"))\n+\t\t\tstrvec_push(&args, \"gitk\");\n+\t\telse {\n+\t\t\tstrvec_push(&args, \"log\");\n+\t\t\tflags |= RUN_GIT_CMD;\n+\t\t}\n+\t} else {\n+\t\tif (argv[0][0] == '-') {\n+\t\t\tstrvec_push(&args, \"log\");\n+\t\t\tflags |= RUN_GIT_CMD;\n+\t\t} else if (strcmp(argv[0], \"tig\") && !starts_with(argv[0], \"git\"))\n+\t\t\tflags |= RUN_GIT_CMD;\n+\n+\t\tstrvec_pushv(&args, argv);\n+\t}\n+\n+\tstrvec_pushl(&args, \"--bisect\", \"--\", NULL);\n+\n+\tstrbuf_read_file(&sb, git_path_bisect_names(), 0);\n+\tsq_dequote_to_strvec(sb.buf, &args);\n+\tstrbuf_release(&sb);\n+\n+\tres = run_command_v_opt(args.v, flags);\n+\tstrvec_clear(&args);\n+\treturn res;\n+}\n+\n int cmd_bisect__helper(int argc, const char **argv, const char *prefix)\n {\n \tenum {\n@@ -1048,7 +1087,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)\n \t\tBISECT_STATE,\n \t\tBISECT_LOG,\n \t\tBISECT_REPLAY,\n-\t\tBISECT_SKIP\n+\t\tBISECT_SKIP,\n+\t\tBISECT_VISUALIZE,\n \t} cmdmode = 0;\n \tint res = 0, nolog = 0;\n \tstruct option options[] = {\n@@ -1070,6 +1110,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)\n \t\t\t N_(\"replay the bisection process from the given file\"), BISECT_REPLAY),\n \t\tOPT_CMDMODE(0, \"bisect-skip\", &cmdmode,\n \t\t\t N_(\"skip some commits for checkout\"), BISECT_SKIP),\n+\t\tOPT_CMDMODE(0, \"bisect-visualize\", &cmdmode,\n+\t\t\t N_(\"visualize the bisection\"), BISECT_VISUALIZE),\n \t\tOPT_BOOL(0, \"no-log\", &nolog,\n \t\t\t N_(\"no log for BISECT_WRITE\")),\n \t\tOPT_END()\n@@ -1131,6 +1173,10 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)\n \t\tget_terms(&terms);\n \t\tres = bisect_skip(&terms, argv, argc);\n \t\tbreak;\n+\tcase BISECT_VISUALIZE:\n+\t\tget_terms(&terms);\n+\t\tres = bisect_visualize(&terms, argv, argc);\n+\t\tbreak;\n \tdefault:\n \t\tBUG(\"unknown subcommand %d\", cmdmode);\n \t}\ndiff --git a/git-bisect.sh b/git-bisect.sh\nindex 6a7afaea8d..95f7f3fb8c 100755\n--- a/git-bisect.sh\n+++ b/git-bisect.sh\n@@ -39,29 +39,6 @@ _x40=\"$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40\"\n TERM_BAD=bad\n TERM_GOOD=good\n \n-bisect_visualize() {\n-\tgit bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit\n-\n-\tif test $# = 0\n-\tthen\n-\t\tif test -n \"${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}\" &&\n-\t\t\ttype gitk >/dev/null 2>&1\n-\t\tthen\n-\t\t\tset gitk\n-\t\telse\n-\t\t\tset git log\n-\t\tfi\n-\telse\n-\t\tcase \"$1\" in\n-\t\tgit*|tig) ;;\n-\t\t-*)\tset git log \"$@\" ;;\n-\t\t*)\tset git \"$@\" ;;\n-\t\tesac\n-\tfi\n-\n-\teval '\"$@\"' --bisect -- $(cat \"$GIT_DIR/BISECT_NAMES\")\n-}\n-\n bisect_run () {\n \tgit bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit\n \n@@ -152,7 +129,7 @@ case \"$#\" in\n \t\t# Not sure we want \"next\" at the UI level anymore.\n \t\tgit bisect--helper --bisect-next \"$@\" || exit ;;\n \tvisualize|view)\n-\t\tbisect_visualize \"$@\" ;;\n+\t\tgit bisect--helper --bisect-visualize \"$@\" || exit;;\n \treset)\n \t\tgit bisect--helper --bisect-reset \"$@\" ;;\n \treplay)\n-- \n2.29.2\n\n"},{"id":"434501","messageId":"20210902090421.93113-6-mirucam@gmail.com","threadId":"56429","inReplyTo":"20210902090421.93113-1-mirucam@gmail.com","subject":"[PATCH v6 5/6] bisect--helper: reimplement `bisect_run` shell","fromName":"Miriam Rubio","fromEmail":"mirucam@gmail.com","sentAt":"2021-09-02T09:04:20Z","receivedAt":"2021-09-02T09:05:26Z","isPatch":true,"sender":{"key":"mirucam@gmail.com","avatar":"https://avatars.githubusercontent.com/u/56339109?v=4"},"body":"From: Tanushree Tumane <tanushreetumane@gmail.com>\n\nReimplement the `bisect_run()` shell function\nin C and also add `--bisect-run` subcommand to\n`git bisect--helper` to call it from git-bisect.sh.\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nSigned-off-by: Tanushree Tumane <tanushreetumane@gmail.com>\nSigned-off-by: Miriam Rubio <mirucam@gmail.com>\n---\n builtin/bisect--helper.c | 97 ++++++++++++++++++++++++++++++++++++++++\n git-bisect.sh            | 62 +------------------------\n 2 files changed, 98 insertions(+), 61 deletions(-)\n\ndiff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\nindex 1e118a966a..8e9ed9c318 100644\n--- a/builtin/bisect--helper.c\n+++ b/builtin/bisect--helper.c\n@@ -18,6 +18,7 @@ static GIT_PATH_FUNC(git_path_bisect_log, \"BISECT_LOG\")\n static GIT_PATH_FUNC(git_path_head_name, \"head-name\")\n static GIT_PATH_FUNC(git_path_bisect_names, \"BISECT_NAMES\")\n static GIT_PATH_FUNC(git_path_bisect_first_parent, \"BISECT_FIRST_PARENT\")\n+static GIT_PATH_FUNC(git_path_bisect_run, \"BISECT_RUN\")\n \n static const char * const git_bisect_helper_usage[] = {\n \tN_(\"git bisect--helper --bisect-reset [<commit>]\"),\n@@ -31,6 +32,7 @@ static const char * const git_bisect_helper_usage[] = {\n \tN_(\"git bisect--helper --bisect-replay <filename>\"),\n \tN_(\"git bisect--helper --bisect-skip [(<rev>|<range>)...]\"),\n \tN_(\"git bisect--helper --bisect-visualize\"),\n+\tN_(\"git bisect--helper --bisect-run <cmd>...\"),\n \tNULL\n };\n \n@@ -144,6 +146,19 @@ static int append_to_file(const char *path, const char *format, ...)\n \treturn res;\n }\n \n+static int print_file_to_stdout(const char *path)\n+{\n+\tint fd = open(path, O_RDONLY);\n+\tint ret = 0;\n+\n+\tif (fd < 0)\n+\t\treturn error_errno(_(\"cannot open file '%s' for reading\"), path);\n+\tif (copy_fd(fd, 1) < 0)\n+\t\tret = error_errno(_(\"failed to read '%s'\"), path);\n+\tclose(fd);\n+\treturn ret;\n+}\n+\n static int check_term_format(const char *term, const char *orig_term)\n {\n \tint res;\n@@ -1075,6 +1090,79 @@ static int bisect_visualize(struct bisect_terms *terms, const char **argv, int a\n \treturn res;\n }\n \n+static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)\n+{\n+\tint res = BISECT_OK;\n+\tstruct strbuf command = STRBUF_INIT;\n+\tstruct strvec args = STRVEC_INIT;\n+\tstruct strvec run_args = STRVEC_INIT;\n+\tconst char *new_state;\n+\tint temporary_stdout_fd, saved_stdout;\n+\n+\tif (bisect_next_check(terms, NULL))\n+\t\treturn BISECT_FAILED;\n+\n+\tif (argc)\n+\t\tsq_quote_argv(&command, argv);\n+\telse {\n+\t\terror(_(\"bisect run failed: no command provided.\"));\n+\t\treturn BISECT_FAILED;\n+\t}\n+\n+\tstrvec_push(&run_args, command.buf);\n+\n+\twhile (1) {\n+\t\tstrvec_clear(&args);\n+\n+\t\tprintf(_(\"running %s\\n\"), command.buf);\n+\t\tres = run_command_v_opt(run_args.v, RUN_USING_SHELL);\n+\n+\t\tif (res < 0 || 128 <= res) {\n+\t\t\terror(_(\"bisect run failed: exit code %d from\"\n+\t\t\t\t\" '%s' is < 0 or >= 128\"), res, command.buf);\n+\t\t\tstrbuf_release(&command);\n+\t\t\treturn res;\n+\t\t}\n+\n+\t\tif (res == 125)\n+\t\t\tnew_state = \"skip\";\n+\t\telse\n+\t\t\tnew_state = res > 0 ? terms->term_bad : terms->term_good;\n+\n+\t\ttemporary_stdout_fd = open(git_path_bisect_run(), O_CREAT | O_WRONLY | O_TRUNC, 0666);\n+\t\tsaved_stdout = dup(1);\n+\t\tdup2(temporary_stdout_fd, 1);\n+\n+\t\tres = bisect_state(terms, &new_state, 1);\n+\n+\t\tdup2(saved_stdout, 1);\n+\t\tclose(saved_stdout);\n+\t\tclose(temporary_stdout_fd);\n+\n+\t\tprint_file_to_stdout(git_path_bisect_run());\n+\n+\t\tif (res == BISECT_ONLY_SKIPPED_LEFT)\n+\t\t\terror(_(\"bisect run cannot continue any more\"));\n+\t\telse if (res == BISECT_INTERNAL_SUCCESS_MERGE_BASE) {\n+\t\t\tprintf(_(\"bisect run success\"));\n+\t\t\tres = BISECT_OK;\n+\t\t} else if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) {\n+\t\t\tprintf(_(\"bisect found first bad commit\"));\n+\t\t\tres = BISECT_OK;\n+\t\t} else if (res) {\n+\t\t\terror(_(\"bisect run failed:'git bisect--helper --bisect-state\"\n+\t\t\t\" %s' exited with error code %d\"), args.v[0], res);\n+\t\t} else {\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tstrbuf_release(&command);\n+\t\tstrvec_clear(&args);\n+\t\tstrvec_clear(&run_args);\n+\t\treturn res;\n+\t}\n+}\n+\n int cmd_bisect__helper(int argc, const char **argv, const char *prefix)\n {\n \tenum {\n@@ -1089,6 +1177,7 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)\n \t\tBISECT_REPLAY,\n \t\tBISECT_SKIP,\n \t\tBISECT_VISUALIZE,\n+\t\tBISECT_RUN,\n \t} cmdmode = 0;\n \tint res = 0, nolog = 0;\n \tstruct option options[] = {\n@@ -1112,6 +1201,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)\n \t\t\t N_(\"skip some commits for checkout\"), BISECT_SKIP),\n \t\tOPT_CMDMODE(0, \"bisect-visualize\", &cmdmode,\n \t\t\t N_(\"visualize the bisection\"), BISECT_VISUALIZE),\n+\t\tOPT_CMDMODE(0, \"bisect-run\", &cmdmode,\n+\t\t\t N_(\"use <cmd>... to automatically bisect.\"), BISECT_RUN),\n \t\tOPT_BOOL(0, \"no-log\", &nolog,\n \t\t\t N_(\"no log for BISECT_WRITE\")),\n \t\tOPT_END()\n@@ -1177,6 +1268,12 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)\n \t\tget_terms(&terms);\n \t\tres = bisect_visualize(&terms, argv, argc);\n \t\tbreak;\n+\tcase BISECT_RUN:\n+\t\tif (!argc)\n+\t\t\treturn error(_(\"bisect run failed: no command provided.\"));\n+\t\tget_terms(&terms);\n+\t\tres = bisect_run(&terms, argv, argc);\n+\t\tbreak;\n \tdefault:\n \t\tBUG(\"unknown subcommand %d\", cmdmode);\n \t}\ndiff --git a/git-bisect.sh b/git-bisect.sh\nindex 95f7f3fb8c..e83d011e17 100755\n--- a/git-bisect.sh\n+++ b/git-bisect.sh\n@@ -39,66 +39,6 @@ _x40=\"$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40\"\n TERM_BAD=bad\n TERM_GOOD=good\n \n-bisect_run () {\n-\tgit bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit\n-\n-\ttest -n \"$*\" || die \"$(gettext \"bisect run failed: no command provided.\")\"\n-\n-\twhile true\n-\tdo\n-\t\tcommand=\"$@\"\n-\t\teval_gettextln \"running \\$command\"\n-\t\t\"$@\"\n-\t\tres=$?\n-\n-\t\t# Check for really bad run error.\n-\t\tif [ $res -lt 0 -o $res -ge 128 ]\n-\t\tthen\n-\t\t\teval_gettextln \"bisect run failed:\n-exit code \\$res from '\\$command' is < 0 or >= 128\" >&2\n-\t\t\texit $res\n-\t\tfi\n-\n-\t\t# Find current state depending on run success or failure.\n-\t\t# A special exit code of 125 means cannot test.\n-\t\tif [ $res -eq 125 ]\n-\t\tthen\n-\t\t\tstate='skip'\n-\t\telif [ $res -gt 0 ]\n-\t\tthen\n-\t\t\tstate=\"$TERM_BAD\"\n-\t\telse\n-\t\t\tstate=\"$TERM_GOOD\"\n-\t\tfi\n-\n-\t\tgit bisect--helper --bisect-state $state >\"$GIT_DIR/BISECT_RUN\"\n-\t\tres=$?\n-\n-\t\tcat \"$GIT_DIR/BISECT_RUN\"\n-\n-\t\tif sane_grep \"first $TERM_BAD commit could be any of\" \"$GIT_DIR/BISECT_RUN\" \\\n-\t\t\t>/dev/null\n-\t\tthen\n-\t\t\tgettextln \"bisect run cannot continue any more\" >&2\n-\t\t\texit $res\n-\t\tfi\n-\n-\t\tif [ $res -ne 0 ]\n-\t\tthen\n-\t\t\teval_gettextln \"bisect run failed:\n-'bisect-state \\$state' exited with error code \\$res\" >&2\n-\t\t\texit $res\n-\t\tfi\n-\n-\t\tif sane_grep \"is the first $TERM_BAD commit\" \"$GIT_DIR/BISECT_RUN\" >/dev/null\n-\t\tthen\n-\t\t\tgettextln \"bisect run success\"\n-\t\t\texit 0;\n-\t\tfi\n-\n-\tdone\n-}\n-\n get_terms () {\n \tif test -s \"$GIT_DIR/BISECT_TERMS\"\n \tthen\n@@ -137,7 +77,7 @@ case \"$#\" in\n \tlog)\n \t\tgit bisect--helper --bisect-log || exit ;;\n \trun)\n-\t\tbisect_run \"$@\" ;;\n+\t\tgit bisect--helper --bisect-run \"$@\" || exit;;\n \tterms)\n \t\tgit bisect--helper --bisect-terms \"$@\" || exit;;\n \t*)\n-- \n2.29.2\n\n"},{"id":"434502","messageId":"20210902090421.93113-7-mirucam@gmail.com","threadId":"56429","inReplyTo":"20210902090421.93113-1-mirucam@gmail.com","subject":"[PATCH v6 6/6] bisect--helper: retire `--bisect-next-check` subcommand","fromName":"Miriam Rubio","fromEmail":"mirucam@gmail.com","sentAt":"2021-09-02T09:04:21Z","receivedAt":"2021-09-02T09:05:28Z","isPatch":true,"sender":{"key":"mirucam@gmail.com","avatar":"https://avatars.githubusercontent.com/u/56339109?v=4"},"body":"After reimplementation of `git bisect run` in C,\n`--bisect-next-check` subcommand is not needed anymore.\n\nLet's remove it from options list and code.\n\nMentored by: Christian Couder <chriscool@tuxfamily.org>\nSigned-off-by: Miriam Rubio <mirucam@gmail.com>\n---\n builtin/bisect--helper.c | 7 -------\n 1 file changed, 7 deletions(-)\n\ndiff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\nindex 8e9ed9c318..10c4cd24a1 100644\n--- a/builtin/bisect--helper.c\n+++ b/builtin/bisect--helper.c\n@@ -22,7 +22,6 @@ static GIT_PATH_FUNC(git_path_bisect_run, \"BISECT_RUN\")\n \n static const char * const git_bisect_helper_usage[] = {\n \tN_(\"git bisect--helper --bisect-reset [<commit>]\"),\n-\tN_(\"git bisect--helper --bisect-next-check <good_term> <bad_term> [<term>]\"),\n \tN_(\"git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]\"),\n \tN_(\"git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}=<term>]\"\n \t\t\t\t\t    \" [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]\"),\n@@ -1222,12 +1221,6 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)\n \t\t\treturn error(_(\"--bisect-reset requires either no argument or a commit\"));\n \t\tres = bisect_reset(argc ? argv[0] : NULL);\n \t\tbreak;\n-\tcase BISECT_NEXT_CHECK:\n-\t\tif (argc != 2 && argc != 3)\n-\t\t\treturn error(_(\"--bisect-next-check requires 2 or 3 arguments\"));\n-\t\tset_terms(&terms, argv[1], argv[0]);\n-\t\tres = bisect_next_check(&terms, argc == 3 ? argv[2] : NULL);\n-\t\tbreak;\n \tcase BISECT_TERMS:\n \t\tif (argc > 1)\n \t\t\treturn error(_(\"--bisect-terms requires 0 or 1 argument\"));\n-- \n2.29.2\n\n"},{"id":"434600","messageId":"xmqqv93iirev.fsf@gitster.g","threadId":"56429","inReplyTo":"20210902090421.93113-2-mirucam@gmail.com","subject":"Re: [PATCH v6 1/6] t6030-bisect-porcelain: add tests to control bisect run exit cases","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-09-02T21:44:24Z","receivedAt":"2021-09-02T21:44:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Miriam Rubio <mirucam@gmail.com> writes:\n\n> There is a gap on bisect run test coverage related with error exits.\n> Add two tests to control these error cases.\n>\n> Signed-off-by: Miriam Rubio <mirucam@gmail.com>\n> ---\n>  t/t6030-bisect-porcelain.sh | 11 +++++++++++\n>  1 file changed, 11 insertions(+)\n>\n> diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh\n> index a1baf4e451..e61b8143fd 100755\n> --- a/t/t6030-bisect-porcelain.sh\n> +++ b/t/t6030-bisect-porcelain.sh\n> @@ -962,4 +962,15 @@ test_expect_success 'bisect handles annotated tags' '\n>  \tgrep \"$bad is the first bad commit\" output\n>  '\n>  \n> +test_expect_success 'bisect run fails with exit code equals or greater than 128' '\n> +\twrite_script test_script.sh <<-\\EOF &&\n> +\texit 128 >/dev/null\n> +\tEOF\n> +\ttest_must_fail git bisect run ./test_script.sh > my_bisect_log.txt &&\n> +\twrite_script test_script.sh <<-\\EOF &&\n> +\texit 255 >/dev/null\n> +\tEOF\n> +\ttest_must_fail git bisect run ./test_script.sh >> my_bisect_log.txt\n> +'\n\nTwo and a half glitches.\n\n * It is not obvious why you need to redirect output from \"exit\" to\n   /dev/null; drop them or explain the reason in the proposed log\n   message, perhaps.\n\n * The contents of my_bisect_log.txt is never inspected.  If it does\n   not matter how the command fails, not inspecting is perfectly OK,\n   but then perhaps not capturing it is the right thing to do?  We\n   do not even want to redirect the output to /dev/null, as the\n   output from the commands run in these test pieces will not be\n   shown unless the test scripts are run under an option for\n   debugging purposes.\n\n * Style: no space after \">\" or \">>\" before my_bisect_log.txt\n\nThanks.\n"},{"id":"434602","messageId":"xmqqpmtqiqf9.fsf@gitster.g","threadId":"56429","inReplyTo":"20210902090421.93113-3-mirucam@gmail.com","subject":"Re: [PATCH v6 2/6] t6030-bisect-porcelain: add test for bisect visualize","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-09-02T22:05:46Z","receivedAt":"2021-09-02T22:05:50Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Miriam Rubio <mirucam@gmail.com> writes:\n\n> Add a test to control breakages in bisect visualize command.\n>\n> Signed-off-by: Miriam Rubio <mirucam@gmail.com>\n> ---\n>  t/t6030-bisect-porcelain.sh | 7 +++++++\n>  1 file changed, 7 insertions(+)\n>\n> diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh\n> index e61b8143fd..f13eeac9ce 100755\n> --- a/t/t6030-bisect-porcelain.sh\n> +++ b/t/t6030-bisect-porcelain.sh\n> @@ -973,4 +973,11 @@ test_expect_success 'bisect run fails with exit code equals or greater than 128'\n>  \ttest_must_fail git bisect run ./test_script.sh >> my_bisect_log.txt\n>  '\n>  \n> +test_expect_success 'bisect visualize with a filename with dash and space' '\n> +\techo \"My test line\" >> -hello\\ 2 &&\n\nThe same style guide for redirection applies here.\n\nAlso, it makes sense to quote such an unusual filename for human\nreaders, i.e.\n\n\techo \"My test line\" >\"./-hello 2\" &&\n\n> +\tgit add -- -hello\\ 2 &&\n> +\tgit commit --quiet -m \"Add test line\" -- -hello\\ 2 &&\n\nLikewise.  \n\nEspecially since this is not a test for \"git add\" or \"git commit\",\ninstead of writing \"-hello 2\", \"./-hello 2\" may help human readers\nbetter.\n\n> +\tgit bisect visualize -p -- -hello\\ 2 > my_bisect_log.txt\n\nThis one, if it is meant to test the pathspec parsing of the command\nbeing tested (i.e. \"git bisect\"), is probably better to be left\nwithout \"./\" prefix, i.e. \"-hello 2\".\n\nThe same comment applies to the redirection into my_bisect_log.txt\nfile.  It is better not to redirect this at all.\n\nThis is the first use of \"git bisect visualize\" in our tests.  How\nare we making sure that we won't open gitk and leave it hanging and\ndoing silly things like that?\n\n    ... goes and looks ...\n\nAh, OK.  \"git bisect --help\" makes it clear that giving an option\nlike \"-p\" tells us to run \"git log\", so we are OK.\n\nTHanks.\n\n\n\n\n> +'\n> +\n>  test_done\n"},{"id":"434603","messageId":"xmqqk0jyipt1.fsf@gitster.g","threadId":"56429","inReplyTo":"20210902090421.93113-4-mirucam@gmail.com","subject":"Re: [PATCH v6 3/6] run-command: make `exists_in_PATH()` non-static","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-09-02T22:19:06Z","receivedAt":"2021-09-02T22:19:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Miriam Rubio <mirucam@gmail.com> writes:\n\n> From: Pranit Bauva <pranit.bauva@gmail.com>\n>\n> Removes the `static` keyword from `exists_in_PATH()` function\n> and declares the function in `run-command.h` file.\n\n\"Remove\" and \"declare\", as if we are giving an order to somebody\nelse to make these changes.\n\n> The function will be used in bisect_visualize() in a later\n> commit.\n>\n> Mentored by: Christian Couder <chriscool@tuxfamily.org>\n> Mentored by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n> Signed-off-by: Tanushree Tumane <tanushreetumane@gmail.com>\n> Signed-off-by: Miriam Rubio <mirucam@gmail.com>\n> ---\n>  run-command.c |  2 +-\n>  run-command.h | 12 ++++++++++++\n>  2 files changed, 13 insertions(+), 1 deletion(-)\n>\n> diff --git a/run-command.c b/run-command.c\n> index f72e72cce7..390f46819f 100644\n> --- a/run-command.c\n> +++ b/run-command.c\n> @@ -210,7 +210,7 @@ static char *locate_in_PATH(const char *file)\n>  \treturn NULL;\n>  }\n>  \n> -static int exists_in_PATH(const char *file)\n> +int exists_in_PATH(const char *file)\n>  {\n>  \tchar *r = locate_in_PATH(file);\n>  \tint found = r != NULL;\n> diff --git a/run-command.h b/run-command.h\n> index af1296769f..54d74b706f 100644\n> --- a/run-command.h\n> +++ b/run-command.h\n> @@ -182,6 +182,18 @@ void child_process_clear(struct child_process *);\n>  \n>  int is_executable(const char *name);\n>  \n> +/**\n> + * Search if a $PATH for a command exists.  This emulates the path search that\n\nThe first sentence does not make sense to me.  Isn't this for\nchecking if a command exists in one of the directories on $PATH?\n\n\tCheck if the command exists on $PATH.\n\nmay make more sense, especially since \"search\" may hint that the\ncaller may be able to learn where it exists, which is not the case.\n\n> + * execvp would perform, without actually executing the command so it\n> + * can be used before fork() to prepare to run a command using\n> + * execve() or after execvp() to diagnose why it failed.\n> + *\n> + * The caller should ensure that file contains no directory separators.\n\nConsistently use \"command\" instead of \"file\" and rename the\nparameter in the prototype below from \"file\" to \"command\".\n\nAlternatively, you can rewrite the first paragraph above to make\nsure that it is clear to the readers that \"command\" it refers to is\nactually the \"file\" parameter the function takes.  A rewrite of the\nfirst sentence I just rewrote above may become\n\n\tCheck if an executable \"file\" exists on $PATH.\n\nwhich does not look too bad, but \"executing the file so it can ...\"\nand \"to run a file using...\" smell a bit strange, and that is why I\nsuggested to consistently use \"command\" instead.\n\n> + *\n> + * Returns 1 if it is found in $PATH or 0 if the command could not be found.\n> + */\n> +int exists_in_PATH(const char *file);\n\nThanks.\n"},{"id":"434604","messageId":"xmqqfsumipd4.fsf@gitster.g","threadId":"56429","inReplyTo":"20210902090421.93113-5-mirucam@gmail.com","subject":"Re: [PATCH v6 4/6] bisect--helper: reimplement `bisect_visualize()`shell function in C","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-09-02T22:28:39Z","receivedAt":"2021-09-02T22:28:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Miriam Rubio <mirucam@gmail.com> writes:\n\n> From: Pranit Bauva <pranit.bauva@gmail.com>\n\nNeed a SP before \"shell\" on the title line.\n\n> Reimplement the `bisect_visualize()` shell function\n> in C and also add `--bisect-visualize` subcommand to\n> `git bisect--helper` to call it from git-bisect.sh.\n\nNice.\n\n> +static int bisect_visualize(struct bisect_terms *terms, const char **argv, int argc)\n> +{\n> +\tstruct strvec args = STRVEC_INIT;\n> +\tint flags = RUN_COMMAND_NO_STDIN, res = 0;\n> +\tstruct strbuf sb = STRBUF_INIT;\n> +\n> +\tif (bisect_next_check(terms, NULL) != 0)\n> +\t\treturn BISECT_FAILED;\n> +\n> +\tif (!argc) {\n> +\t\tif ((getenv(\"DISPLAY\") || getenv(\"SESSIONNAME\") || getenv(\"MSYSTEM\") ||\n> +\t\t     getenv(\"SECURITYSESSIONID\")) && exists_in_PATH(\"gitk\"))\n> +\t\t\tstrvec_push(&args, \"gitk\");\n> +\t\telse {\n> +\t\t\tstrvec_push(&args, \"log\");\n> +\t\t\tflags |= RUN_GIT_CMD;\n> +\t\t}\n\nLet's have {} on the if() side, even though it only has one\nstatement and does not require one, because the else side needs one.\n\n> +\t} else {\n> +\t\tif (argv[0][0] == '-') {\n> +\t\t\tstrvec_push(&args, \"log\");\n> +\t\t\tflags |= RUN_GIT_CMD;\n\nOK, any -option makes it \"git log -option ...\" invocation.\n\n> +\t\t} else if (strcmp(argv[0], \"tig\") && !starts_with(argv[0], \"git\"))\n> +\t\t\tflags |= RUN_GIT_CMD;\n\nOK, when the first token is \"tig\", or it begins with \"git\", the\nscripted version just leaves the command line intact.  Everything\nelse is taken as a subcommand to git.  And this conditional is a\nfaithful translation of that logic.\n\n> +\t\tstrvec_pushv(&args, argv);\n> +\t}\n> +\n> +\tstrvec_pushl(&args, \"--bisect\", \"--\", NULL);\n> +\n> +\tstrbuf_read_file(&sb, git_path_bisect_names(), 0);\n> +\tsq_dequote_to_strvec(sb.buf, &args);\n> +\tstrbuf_release(&sb);\n> +\n> +\tres = run_command_v_opt(args.v, flags);\n> +\tstrvec_clear(&args);\n> +\treturn res;\n\nOK.\n\nThe code is quite easy to follow, thanks to many helpers that have\nbeen invented for this exact purpose, like sq_dequote_to_strvec().\n\n> diff --git a/git-bisect.sh b/git-bisect.sh\n> index 6a7afaea8d..95f7f3fb8c 100755\n> --- a/git-bisect.sh\n> +++ b/git-bisect.sh\n> @@ -39,29 +39,6 @@ _x40=\"$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40\"\n>  TERM_BAD=bad\n>  TERM_GOOD=good\n>  \n> -bisect_visualize() {\n> ...\n> -}\n> -\n>  bisect_run () {\n>  \tgit bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit\n>  \n> @@ -152,7 +129,7 @@ case \"$#\" in\n>  \t\t# Not sure we want \"next\" at the UI level anymore.\n>  \t\tgit bisect--helper --bisect-next \"$@\" || exit ;;\n>  \tvisualize|view)\n> -\t\tbisect_visualize \"$@\" ;;\n> +\t\tgit bisect--helper --bisect-visualize \"$@\" || exit;;\n\nNice.\n\nThanks.\n"},{"id":"434613","messageId":"xmqqtuj2h7cp.fsf@gitster.g","threadId":"56429","inReplyTo":"20210902090421.93113-6-mirucam@gmail.com","subject":"Re: [PATCH v6 5/6] bisect--helper: reimplement `bisect_run` shell","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-09-02T23:43:02Z","receivedAt":"2021-09-02T23:43:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Miriam Rubio <mirucam@gmail.com> writes:\n\n> From: Tanushree Tumane <tanushreetumane@gmail.com>\n>\n> Reimplement the `bisect_run()` shell function\n> in C and also add `--bisect-run` subcommand to\n> `git bisect--helper` to call it from git-bisect.sh.\n>\n> Mentored-by: Christian Couder <chriscool@tuxfamily.org>\n> Signed-off-by: Tanushree Tumane <tanushreetumane@gmail.com>\n> Signed-off-by: Miriam Rubio <mirucam@gmail.com>\n> ---\n>  builtin/bisect--helper.c | 97 ++++++++++++++++++++++++++++++++++++++++\n>  git-bisect.sh            | 62 +------------------------\n>  2 files changed, 98 insertions(+), 61 deletions(-)\n>\n> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\n> index 1e118a966a..8e9ed9c318 100644\n> --- a/builtin/bisect--helper.c\n> +++ b/builtin/bisect--helper.c\n> @@ -18,6 +18,7 @@ static GIT_PATH_FUNC(git_path_bisect_log, \"BISECT_LOG\")\n>  static GIT_PATH_FUNC(git_path_head_name, \"head-name\")\n>  static GIT_PATH_FUNC(git_path_bisect_names, \"BISECT_NAMES\")\n>  static GIT_PATH_FUNC(git_path_bisect_first_parent, \"BISECT_FIRST_PARENT\")\n> +static GIT_PATH_FUNC(git_path_bisect_run, \"BISECT_RUN\")\n>  \n>  static const char * const git_bisect_helper_usage[] = {\n>  \tN_(\"git bisect--helper --bisect-reset [<commit>]\"),\n> @@ -31,6 +32,7 @@ static const char * const git_bisect_helper_usage[] = {\n>  \tN_(\"git bisect--helper --bisect-replay <filename>\"),\n>  \tN_(\"git bisect--helper --bisect-skip [(<rev>|<range>)...]\"),\n>  \tN_(\"git bisect--helper --bisect-visualize\"),\n> +\tN_(\"git bisect--helper --bisect-run <cmd>...\"),\n>  \tNULL\n>  };\n>  \n> @@ -144,6 +146,19 @@ static int append_to_file(const char *path, const char *format, ...)\n>  \treturn res;\n>  }\n>  \n> +static int print_file_to_stdout(const char *path)\n> +{\n> +\tint fd = open(path, O_RDONLY);\n> +\tint ret = 0;\n> +\n> +\tif (fd < 0)\n> +\t\treturn error_errno(_(\"cannot open file '%s' for reading\"), path);\n> +\tif (copy_fd(fd, 1) < 0)\n> +\t\tret = error_errno(_(\"failed to read '%s'\"), path);\n> +\tclose(fd);\n> +\treturn ret;\n> +}\n> +\n>  static int check_term_format(const char *term, const char *orig_term)\n>  {\n>  \tint res;\n> @@ -1075,6 +1090,79 @@ static int bisect_visualize(struct bisect_terms *terms, const char **argv, int a\n>  \treturn res;\n>  }\n>  \n> +static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)\n> +{\n> +\tint res = BISECT_OK;\n> +\tstruct strbuf command = STRBUF_INIT;\n> +\tstruct strvec args = STRVEC_INIT;\n> +\tstruct strvec run_args = STRVEC_INIT;\n> +\tconst char *new_state;\n> +\tint temporary_stdout_fd, saved_stdout;\n> +\n> +\tif (bisect_next_check(terms, NULL))\n> +\t\treturn BISECT_FAILED;\n> +\n> +\tif (argc)\n> +\t\tsq_quote_argv(&command, argv);\n> +\telse {\n> +\t\terror(_(\"bisect run failed: no command provided.\"));\n> +\t\treturn BISECT_FAILED;\n> +\t}\n> +\tstrvec_push(&run_args, command.buf);\n> +\n> +\twhile (1) {\n> +\t\tstrvec_clear(&args);\n> +\n> +\t\tprintf(_(\"running %s\\n\"), command.buf);\n> +\t\tres = run_command_v_opt(run_args.v, RUN_USING_SHELL);\n> +\n> +\t\tif (res < 0 || 128 <= res) {\n> +\t\t\terror(_(\"bisect run failed: exit code %d from\"\n> +\t\t\t\t\" '%s' is < 0 or >= 128\"), res, command.buf);\n> +\t\t\tstrbuf_release(&command);\n> +\t\t\treturn res;\n> +\t\t}\n> +\n> +\t\tif (res == 125)\n> +\t\t\tnew_state = \"skip\";\n> +\t\telse\n> +\t\t\tnew_state = res > 0 ? terms->term_bad : terms->term_good;\n\nIt is easier to follow the code if you spelled out this part as\n\n\t\telse if (!res)\n\t\t\tnew_state = terms->term_good;\n\t\telse\n\t\t\tnew_state = terms->term_bad;\n\nbecause that would consistently handle the three cases.  Of course\nyou _could_ do\n\n\t\tnew_state = (res == 125)\n\t\t\t  ? \"skip\"\n\t\t\t  : (res > 0)\n\t\t\t  ? terms->term_bad\n\t\t\t  : terms->term_good;\n\ninstead, but that would be harder to read.\n\n\n> +\t\ttemporary_stdout_fd = open(git_path_bisect_run(), O_CREAT | O_WRONLY | O_TRUNC, 0666);\n\nCan this open fail, and if it fails, what do we want to do?\n\n> +\t\tsaved_stdout = dup(1);\n> +\t\tdup2(temporary_stdout_fd, 1);\n> +\n> +\t\tres = bisect_state(terms, &new_state, 1);\n> +\n> +\t\tdup2(saved_stdout, 1);\n> +\t\tclose(saved_stdout);\n> +\t\tclose(temporary_stdout_fd);\n\nHmph, now you lost me.  Whose output are we working around here with\nthe redirection?  \n\n\t... goes and looks ...\n\nAhh, OK.  bisect_next_all() to bisect_checkout() all assume that\nthey only need to write to the standard output, so we need to do\nthis dance (unless we are willing to update the bisect.c functions\nto accept FILE * as parameter, that is).\n\nHowever, they use not just write(2) but stdio to do their output,\nno?  Don't we need to fflush(stdout) around the redirection dance,\none to empty the output that was associated with the real standard\noutput stream before asking bisect_state() to write to fd #1 via\nstdio, and one more time to flush out what bisect_state() wrote to\nthe stdio after the call returns before closing the fd we opened to\nthe BISECT_RUN file?\n\n> +\t\tprint_file_to_stdout(git_path_bisect_run());\n\nOK.  So this corresponds to the \"write bisect-state to ./git/BISECT_RUN\nand then cat it\" in the scripted version.\n\n> +\t\tif (res == BISECT_ONLY_SKIPPED_LEFT)\n> +\t\t\terror(_(\"bisect run cannot continue any more\"));\n> +\t\telse if (res == BISECT_INTERNAL_SUCCESS_MERGE_BASE) {\n> +\t\t\tprintf(_(\"bisect run success\"));\n> +\t\t\tres = BISECT_OK;\n> +\t\t} else if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) {\n> +\t\t\tprintf(_(\"bisect found first bad commit\"));\n> +\t\t\tres = BISECT_OK;\n> +\t\t} else if (res) {\n> +\t\t\terror(_(\"bisect run failed:'git bisect--helper --bisect-state\"\n> +\t\t\t\" %s' exited with error code %d\"), args.v[0], res);\n> +\t\t} else {\n> +\t\t\tcontinue;\n> +\t\t}\n> +\n> +\t\tstrbuf_release(&command);\n> +\t\tstrvec_clear(&args);\n> +\t\tstrvec_clear(&run_args);\n> +\t\treturn res;\n> +\t}\n> +}\n\nOK, the \"res to diag\" and clearing the resources at the end of the\nfunction looks good to me.\n\nThanks.\n"},{"id":"434614","messageId":"xmqqpmtqh7c3.fsf@gitster.g","threadId":"56429","inReplyTo":"20210902090421.93113-7-mirucam@gmail.com","subject":"Re: [PATCH v6 6/6] bisect--helper: retire `--bisect-next-check` subcommand","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-09-02T23:43:24Z","receivedAt":"2021-09-02T23:43:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Miriam Rubio <mirucam@gmail.com> writes:\n\n> After reimplementation of `git bisect run` in C,\n> `--bisect-next-check` subcommand is not needed anymore.\n>\n> Let's remove it from options list and code.\n\nYay.  Nice.\n"},{"id":"434775","messageId":"nycvar.QRO.7.76.6.2109060923390.55@tvgsbejvaqbjf.bet","threadId":"56429","inReplyTo":"xmqqtuj2h7cp.fsf@gitster.g","subject":"Re: [PATCH v6 5/6] bisect--helper: reimplement `bisect_run` shell","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-09-06T07:33:27Z","receivedAt":"2021-09-06T07:33:29Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio & Miriam,\n\nOn Thu, 2 Sep 2021, Junio C Hamano wrote:\n\n> Miriam Rubio <mirucam@gmail.com> writes:\n>\n> [...]\n> > @@ -1075,6 +1090,79 @@ static int bisect_visualize(struct bisect_terms *terms, const char **argv, int a\n> >  \treturn res;\n> >  }\n> >\n> > +static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)\n> > +{\n> > +\tint res = BISECT_OK;\n> > +\tstruct strbuf command = STRBUF_INIT;\n> > +\tstruct strvec args = STRVEC_INIT;\n> > +\tstruct strvec run_args = STRVEC_INIT;\n> > +\tconst char *new_state;\n> > +\tint temporary_stdout_fd, saved_stdout;\n> > +\n> > +\tif (bisect_next_check(terms, NULL))\n> > +\t\treturn BISECT_FAILED;\n> > +\n> > +\tif (argc)\n> > +\t\tsq_quote_argv(&command, argv);\n> > +\telse {\n> > +\t\terror(_(\"bisect run failed: no command provided.\"));\n> > +\t\treturn BISECT_FAILED;\n> > +\t}\n> > +\tstrvec_push(&run_args, command.buf);\n> > +\n> > +\twhile (1) {\n> > +\t\tstrvec_clear(&args);\n> > +\n> > +\t\tprintf(_(\"running %s\\n\"), command.buf);\n> > +\t\tres = run_command_v_opt(run_args.v, RUN_USING_SHELL);\n> > +\n> > +\t\tif (res < 0 || 128 <= res) {\n> > +\t\t\terror(_(\"bisect run failed: exit code %d from\"\n> > +\t\t\t\t\" '%s' is < 0 or >= 128\"), res, command.buf);\n> > +\t\t\tstrbuf_release(&command);\n> > +\t\t\treturn res;\n> > +\t\t}\n> > +\n> > +\t\tif (res == 125)\n> > +\t\t\tnew_state = \"skip\";\n> > +\t\telse\n> > +\t\t\tnew_state = res > 0 ? terms->term_bad : terms->term_good;\n>\n> It is easier to follow the code if you spelled out this part as\n>\n> \t\telse if (!res)\n> \t\t\tnew_state = terms->term_good;\n> \t\telse\n> \t\t\tnew_state = terms->term_bad;\n>\n> because that would consistently handle the three cases.  Of course\n> you _could_ do\n>\n> \t\tnew_state = (res == 125)\n> \t\t\t  ? \"skip\"\n> \t\t\t  : (res > 0)\n> \t\t\t  ? terms->term_bad\n> \t\t\t  : terms->term_good;\n>\n> instead, but that would be harder to read.\n\nFWIW I agree with this, after seeing the resulting code.\n\n> > +\t\ttemporary_stdout_fd = open(git_path_bisect_run(), O_CREAT | O_WRONLY | O_TRUNC, 0666);\n>\n> Can this open fail, and if it fails, what do we want to do?\n>\n> > +\t\tsaved_stdout = dup(1);\n> > +\t\tdup2(temporary_stdout_fd, 1);\n> > +\n> > +\t\tres = bisect_state(terms, &new_state, 1);\n> > +\n> > +\t\tdup2(saved_stdout, 1);\n> > +\t\tclose(saved_stdout);\n> > +\t\tclose(temporary_stdout_fd);\n>\n> Hmph, now you lost me.  Whose output are we working around here with\n> the redirection?\n>\n> \t... goes and looks ...\n>\n> Ahh, OK.  bisect_next_all() to bisect_checkout() all assume that\n> they only need to write to the standard output, so we need to do\n> this dance (unless we are willing to update the bisect.c functions\n> to accept FILE * as parameter, that is).\n>\n> However, they use not just write(2) but stdio to do their output,\n> no?  Don't we need to fflush(stdout) around the redirection dance,\n> one to empty the output that was associated with the real standard\n> output stream before asking bisect_state() to write to fd #1 via\n> stdio, and one more time to flush out what bisect_state() wrote to\n> the stdio after the call returns before closing the fd we opened to\n> the BISECT_RUN file?\n\nYes, we would have to `fflush(stdout)`.\n\nHowever, I still don't like that we play such a `dup2()` game. I gave it a\nquick try to avoid it (see the diff below, which corresponds to the commit\nI pushed up as `git-bisect-work-part4-v7` to\nhttps://github.com/dscho/git), which still could benefit from a bit of\npolishing (maybe we should rethink the object model and extend/rename\n`bisect_terms` to `bisect_state` and accumulate more fields, such as\n`out_fd`.\n\nObviously this will need to be cleaned up, and while I would _love_ to see\nthis make it into your next iteration, ultimately it is up to you, Miriam,\nto decide whether you want to build on my diff (quite possibly making the\nentire object model of the bisect part of Git's code more elegant and more\nmaintainable), and up to you, Junio, to decide whether you would be\nwilling to accept the patch series without this refactoring.\n\n-- snipsnap --\ndiff --git a/bisect.c b/bisect.c\nindex af2863d044b..405bf60b4b6 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -683,20 +683,21 @@ static void bisect_common(struct rev_info *revs)\n }\n\n static enum bisect_error error_if_skipped_commits(struct commit_list *tried,\n-\t\t\t\t    const struct object_id *bad)\n+\t\t\t\t\t\t  const struct object_id *bad,\n+\t\t\t\t\t\t  FILE *out)\n {\n \tif (!tried)\n \t\treturn BISECT_OK;\n\n-\tprintf(\"There are only 'skip'ped commits left to test.\\n\"\n-\t       \"The first %s commit could be any of:\\n\", term_bad);\n+\tfprintf(out, \"There are only 'skip'ped commits left to test.\\n\"\n+\t\t\"The first %s commit could be any of:\\n\", term_bad);\n\n \tfor ( ; tried; tried = tried->next)\n-\t\tprintf(\"%s\\n\", oid_to_hex(&tried->item->object.oid));\n+\t\tfprintf(out, \"%s\\n\", oid_to_hex(&tried->item->object.oid));\n\n \tif (bad)\n-\t\tprintf(\"%s\\n\", oid_to_hex(bad));\n-\tprintf(_(\"We cannot bisect more!\\n\"));\n+\t\tfprintf(out, \"%s\\n\", oid_to_hex(bad));\n+\tfprintf(out, _(\"We cannot bisect more!\\n\"));\n\n \treturn BISECT_ONLY_SKIPPED_LEFT;\n }\n@@ -725,10 +726,12 @@ static int is_expected_rev(const struct object_id *oid)\n \treturn res;\n }\n\n-static enum bisect_error bisect_checkout(const struct object_id *bisect_rev, int no_checkout)\n+static enum bisect_error bisect_checkout(const struct object_id *bisect_rev,\n+\t\t\t\t\t int no_checkout, FILE *out)\n {\n \tchar bisect_rev_hex[GIT_MAX_HEXSZ + 1];\n \tenum bisect_error res = BISECT_OK;\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n\n \toid_to_hex_r(bisect_rev_hex, bisect_rev);\n \tupdate_ref(NULL, \"BISECT_EXPECTED_REV\", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);\n@@ -749,7 +752,10 @@ static enum bisect_error bisect_checkout(const struct object_id *bisect_rev, int\n \t}\n\n \targv_show_branch[1] = bisect_rev_hex;\n-\tres = run_command_v_opt(argv_show_branch, RUN_GIT_CMD);\n+\tcp.argv = argv_show_branch;\n+\tcp.git_cmd = 1;\n+\tcp.out = dup(fileno(out));\n+\tres = run_command(&cp);\n \t/*\n \t * Errors in `run_command()` itself, signaled by res < 0,\n \t * and errors in the child process, signaled by res > 0\n@@ -841,7 +847,8 @@ static void handle_skipped_merge_base(const struct object_id *mb)\n  * for early success, this will be converted back to 0 in\n  * check_good_are_ancestors_of_bad().\n  */\n-static enum bisect_error check_merge_bases(int rev_nr, struct commit **rev, int no_checkout)\n+static enum bisect_error check_merge_bases(int rev_nr, struct commit **rev,\n+\t\t\t\t\t   int no_checkout, FILE *out)\n {\n \tenum bisect_error res = BISECT_OK;\n \tstruct commit_list *result;\n@@ -858,8 +865,8 @@ static enum bisect_error check_merge_bases(int rev_nr, struct commit **rev, int\n \t\t} else if (0 <= oid_array_lookup(&skipped_revs, mb)) {\n \t\t\thandle_skipped_merge_base(mb);\n \t\t} else {\n-\t\t\tprintf(_(\"Bisecting: a merge base must be tested\\n\"));\n-\t\t\tres = bisect_checkout(mb, no_checkout);\n+\t\t\tfprintf(out, _(\"Bisecting: a merge base must be tested\\n\"));\n+\t\t\tres = bisect_checkout(mb, no_checkout, out);\n \t\t\tif (!res)\n \t\t\t\t/* indicate early success */\n \t\t\t\tres = BISECT_INTERNAL_SUCCESS_MERGE_BASE;\n@@ -898,8 +905,9 @@ static int check_ancestors(struct repository *r, int rev_nr,\n  */\n\n static enum bisect_error check_good_are_ancestors_of_bad(struct repository *r,\n-\t\t\t\t\t    const char *prefix,\n-\t\t\t\t\t    int no_checkout)\n+\t\t\t\t\t\t\t const char *prefix,\n+\t\t\t\t\t\t\t int no_checkout,\n+\t\t\t\t\t\t\t FILE *out)\n {\n \tchar *filename;\n \tstruct stat st;\n@@ -924,7 +932,7 @@ static enum bisect_error check_good_are_ancestors_of_bad(struct repository *r,\n\n \trev = get_bad_and_good_commits(r, &rev_nr);\n \tif (check_ancestors(r, rev_nr, rev, prefix))\n-\t\tres = check_merge_bases(rev_nr, rev, no_checkout);\n+\t\tres = check_merge_bases(rev_nr, rev, no_checkout, out);\n \tfree(rev);\n\n \tif (!res) {\n@@ -953,7 +961,7 @@ static enum bisect_error check_good_are_ancestors_of_bad(struct repository *r,\n  */\n static void show_diff_tree(struct repository *r,\n \t\t\t   const char *prefix,\n-\t\t\t   struct commit *commit)\n+\t\t\t   struct commit *commit, FILE *out)\n {\n \tconst char *argv[] = {\n \t\t\"diff-tree\", \"--pretty\", \"--stat\", \"--summary\", \"--cc\", NULL\n@@ -964,6 +972,7 @@ static void show_diff_tree(struct repository *r,\n \trepo_init_revisions(r, &opt, prefix);\n\n \tsetup_revisions(ARRAY_SIZE(argv) - 1, argv, &opt, NULL);\n+\topt.diffopt.file = out;\n \tlog_tree_commit(&opt, commit);\n }\n\n@@ -1007,7 +1016,8 @@ void read_bisect_terms(const char **read_bad, const char **read_good)\n  * the end of bisect_helper::cmd_bisect__helper() helps bypassing\n  * all the code related to finding a commit to test.\n  */\n-enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n+enum bisect_error bisect_next_all(struct repository *r, const char *prefix,\n+\t\t\t\t  FILE *out)\n {\n \tstruct rev_info revs;\n \tstruct commit_list *tried;\n@@ -1032,7 +1042,7 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \tif (skipped_revs.nr)\n \t\tbisect_flags |= FIND_BISECTION_ALL;\n\n-\tres = check_good_are_ancestors_of_bad(r, prefix, no_checkout);\n+\tres = check_good_are_ancestors_of_bad(r, prefix, no_checkout, out);\n \tif (res)\n \t\treturn res;\n\n@@ -1051,10 +1061,10 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \t\t * We should return error here only if the \"bad\"\n \t\t * commit is also a \"skip\" commit.\n \t\t */\n-\t\tres = error_if_skipped_commits(tried, NULL);\n+\t\tres = error_if_skipped_commits(tried, NULL, out);\n \t\tif (res < 0)\n \t\t\treturn res;\n-\t\tprintf(_(\"%s was both %s and %s\\n\"),\n+\t\tfprintf(out, _(\"%s was both %s and %s\\n\"),\n \t\t       oid_to_hex(current_bad_oid),\n \t\t       term_good,\n \t\t       term_bad);\n@@ -1072,13 +1082,13 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \tbisect_rev = &revs.commits->item->object.oid;\n\n \tif (oideq(bisect_rev, current_bad_oid)) {\n-\t\tres = error_if_skipped_commits(tried, current_bad_oid);\n+\t\tres = error_if_skipped_commits(tried, current_bad_oid, out);\n \t\tif (res)\n \t\t\treturn res;\n-\t\tprintf(\"%s is the first %s commit\\n\", oid_to_hex(bisect_rev),\n-\t\t\tterm_bad);\n+\t\tfprintf(out, \"%s is the first %s commit\\n\",\n+\t\t\toid_to_hex(bisect_rev), term_bad);\n\n-\t\tshow_diff_tree(r, prefix, revs.commits->item);\n+\t\tshow_diff_tree(r, prefix, revs.commits->item, out);\n \t\t/*\n \t\t * This means the bisection process succeeded.\n \t\t * Using BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND (-10)\n@@ -1098,14 +1108,14 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \t * TRANSLATORS: the last %s will be replaced with \"(roughly %d\n \t * steps)\" translation.\n \t */\n-\tprintf(Q_(\"Bisecting: %d revision left to test after this %s\\n\",\n-\t\t  \"Bisecting: %d revisions left to test after this %s\\n\",\n-\t\t  nr), nr, steps_msg);\n+\tfprintf(out, Q_(\"Bisecting: %d revision left to test after this %s\\n\",\n+\t\t\t\"Bisecting: %d revisions left to test after this %s\\n\",\n+\t\t\tnr), nr, steps_msg);\n \tfree(steps_msg);\n \t/* Clean up objects used, as they will be reused. */\n \trepo_clear_commit_marks(r, ALL_REV_FLAGS);\n\n-\treturn bisect_checkout(bisect_rev, no_checkout);\n+\treturn bisect_checkout(bisect_rev, no_checkout, out);\n }\n\n static inline int log2i(int n)\ndiff --git a/bisect.h b/bisect.h\nindex ec24ac2d7ee..72bfd7b0053 100644\n--- a/bisect.h\n+++ b/bisect.h\n@@ -61,7 +61,8 @@ enum bisect_error {\n \tBISECT_INTERNAL_SUCCESS_MERGE_BASE = -11\n };\n\n-enum bisect_error bisect_next_all(struct repository *r, const char *prefix);\n+enum bisect_error bisect_next_all(struct repository *r, const char *prefix,\n+\t\t\t\t  FILE *out);\n\n int estimate_bisect_steps(int all);\n\ndiff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\nindex 1c96580bd49..29969763d35 100644\n--- a/builtin/bisect--helper.c\n+++ b/builtin/bisect--helper.c\n@@ -581,7 +581,8 @@ static int bisect_successful(struct bisect_terms *terms)\n \treturn res;\n }\n\n-static enum bisect_error bisect_next(struct bisect_terms *terms, const char *prefix)\n+static enum bisect_error bisect_next(struct bisect_terms *terms,\n+\t\t\t\t     const char *prefix, FILE *out)\n {\n \tenum bisect_error res;\n\n@@ -592,7 +593,7 @@ static enum bisect_error bisect_next(struct bisect_terms *terms, const char *pre\n \t\treturn BISECT_FAILED;\n\n \t/* Perform all bisection computation */\n-\tres = bisect_next_all(the_repository, prefix);\n+\tres = bisect_next_all(the_repository, prefix, out);\n\n \tif (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) {\n \t\tres = bisect_successful(terms);\n@@ -604,12 +605,13 @@ static enum bisect_error bisect_next(struct bisect_terms *terms, const char *pre\n \treturn res;\n }\n\n-static enum bisect_error bisect_auto_next(struct bisect_terms *terms, const char *prefix)\n+static enum bisect_error bisect_auto_next(struct bisect_terms *terms,\n+\t\t\t\t\t  const char *prefix, FILE *out)\n {\n \tif (bisect_next_check(terms, NULL))\n \t\treturn BISECT_OK;\n\n-\treturn bisect_next(terms, prefix);\n+\treturn bisect_next(terms, prefix, out);\n }\n\n static enum bisect_error bisect_start(struct bisect_terms *terms, const char **argv, int argc)\n@@ -808,7 +810,7 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, const char **a\n \tif (res)\n \t\treturn res;\n\n-\tres = bisect_auto_next(terms, NULL);\n+\tres = bisect_auto_next(terms, NULL, stdout);\n \tif (!is_bisect_success(res))\n \t\tbisect_clean_state();\n \treturn res;\n@@ -847,7 +849,7 @@ static int bisect_autostart(struct bisect_terms *terms)\n }\n\n static enum bisect_error bisect_state(struct bisect_terms *terms, const char **argv,\n-\t\t\t\t      int argc)\n+\t\t\t\t      int argc, FILE *out)\n {\n \tconst char *state;\n \tint i, verify_expected = 1;\n@@ -924,7 +926,7 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, const char **a\n \t}\n\n \toid_array_clear(&revs);\n-\treturn bisect_auto_next(terms, NULL);\n+\treturn bisect_auto_next(terms, NULL, out);\n }\n\n static enum bisect_error bisect_log(void)\n@@ -1013,7 +1015,7 @@ static enum bisect_error bisect_replay(struct bisect_terms *terms, const char *f\n \tif (res)\n \t\treturn BISECT_FAILED;\n\n-\treturn bisect_auto_next(terms, NULL);\n+\treturn bisect_auto_next(terms, NULL, stdout);\n }\n\n static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **argv, int argc)\n@@ -1045,7 +1047,7 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar\n \t\t\tstrvec_push(&argv_state, argv[i]);\n \t\t}\n \t}\n-\tres = bisect_state(terms, argv_state.v, argv_state.nr);\n+\tres = bisect_state(terms, argv_state.v, argv_state.nr, stdout);\n\n \tstrvec_clear(&argv_state);\n \treturn res;\n@@ -1096,7 +1098,6 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)\n \tstruct strvec args = STRVEC_INIT;\n \tstruct strvec run_args = STRVEC_INIT;\n \tconst char *new_state;\n-\tint temporary_stdout_fd, saved_stdout;\n\n \tif (bisect_next_check(terms, NULL))\n \t\treturn BISECT_FAILED;\n@@ -1111,6 +1112,8 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)\n \tstrvec_push(&run_args, command.buf);\n\n \twhile (1) {\n+\t\tFILE *f;\n+\n \t\tstrvec_clear(&args);\n\n \t\tprintf(_(\"running %s\\n\"), command.buf);\n@@ -1130,19 +1133,13 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)\n \t\telse\n \t\t\tnew_state = terms->term_bad;\n\n-\t\ttemporary_stdout_fd = open(git_path_bisect_run(), O_CREAT | O_WRONLY | O_TRUNC, 0666);\n+\t\tf = fopen_for_writing(git_path_bisect_run());\n\n-\t\tif (temporary_stdout_fd < 0)\n+\t\tif (!f)\n \t\t\treturn error_errno(_(\"cannot open file '%s' for writing\"), git_path_bisect_run());\n\n-\t\tsaved_stdout = dup(1);\n-\t\tdup2(temporary_stdout_fd, 1);\n-\n-\t\tres = bisect_state(terms, &new_state, 1);\n-\n-\t\tdup2(saved_stdout, 1);\n-\t\tclose(saved_stdout);\n-\t\tclose(temporary_stdout_fd);\n+\t\tres = bisect_state(terms, &new_state, 1, f);\n+\t\tfclose(f);\n\n \t\tprint_file_to_stdout(git_path_bisect_run());\n\n@@ -1240,12 +1237,12 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)\n \t\tif (argc)\n \t\t\treturn error(_(\"--bisect-next requires 0 arguments\"));\n \t\tget_terms(&terms);\n-\t\tres = bisect_next(&terms, prefix);\n+\t\tres = bisect_next(&terms, prefix, stdout);\n \t\tbreak;\n \tcase BISECT_STATE:\n \t\tset_terms(&terms, \"bad\", \"good\");\n \t\tget_terms(&terms);\n-\t\tres = bisect_state(&terms, argv, argc);\n+\t\tres = bisect_state(&terms, argv, argc, stdout);\n \t\tbreak;\n \tcase BISECT_LOG:\n \t\tif (argc)\n"},{"id":"434779","messageId":"CAN7CjDANWsWwPcAG2cftAiadwaWZNXBtL=Q8MrqH2xVMj7kUOg@mail.gmail.com","threadId":"56429","inReplyTo":"nycvar.QRO.7.76.6.2109060923390.55@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v6 5/6] bisect--helper: reimplement `bisect_run` shell","fromName":"Miriam R.","fromEmail":"mirucam@gmail.com","sentAt":"2021-09-06T08:34:35Z","receivedAt":"2021-09-06T08:34:54Z","isPatch":true,"sender":{"key":"mirucam@gmail.com","avatar":"https://avatars.githubusercontent.com/u/56339109?v=4"},"body":"Hi Johannes,\n\nEl lun, 6 sept 2021 a las 9:33, Johannes Schindelin\n(<Johannes.Schindelin@gmx.de>) escribió:\n>\n> Hi Junio & Miriam,\n>\n> On Thu, 2 Sep 2021, Junio C Hamano wrote:\n>\n> > Miriam Rubio <mirucam@gmail.com> writes:\n> >\n> > [...]\n> > > @@ -1075,6 +1090,79 @@ static int bisect_visualize(struct bisect_terms *terms, const char **argv, int a\n> > >     return res;\n> > >  }\n> > >\n> > > +static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)\n> > > +{\n> > > +   int res = BISECT_OK;\n> > > +   struct strbuf command = STRBUF_INIT;\n> > > +   struct strvec args = STRVEC_INIT;\n> > > +   struct strvec run_args = STRVEC_INIT;\n> > > +   const char *new_state;\n> > > +   int temporary_stdout_fd, saved_stdout;\n> > > +\n> > > +   if (bisect_next_check(terms, NULL))\n> > > +           return BISECT_FAILED;\n> > > +\n> > > +   if (argc)\n> > > +           sq_quote_argv(&command, argv);\n> > > +   else {\n> > > +           error(_(\"bisect run failed: no command provided.\"));\n> > > +           return BISECT_FAILED;\n> > > +   }\n> > > +   strvec_push(&run_args, command.buf);\n> > > +\n> > > +   while (1) {\n> > > +           strvec_clear(&args);\n> > > +\n> > > +           printf(_(\"running %s\\n\"), command.buf);\n> > > +           res = run_command_v_opt(run_args.v, RUN_USING_SHELL);\n> > > +\n> > > +           if (res < 0 || 128 <= res) {\n> > > +                   error(_(\"bisect run failed: exit code %d from\"\n> > > +                           \" '%s' is < 0 or >= 128\"), res, command.buf);\n> > > +                   strbuf_release(&command);\n> > > +                   return res;\n> > > +           }\n> > > +\n> > > +           if (res == 125)\n> > > +                   new_state = \"skip\";\n> > > +           else\n> > > +                   new_state = res > 0 ? terms->term_bad : terms->term_good;\n> >\n> > It is easier to follow the code if you spelled out this part as\n> >\n> >               else if (!res)\n> >                       new_state = terms->term_good;\n> >               else\n> >                       new_state = terms->term_bad;\n> >\n> > because that would consistently handle the three cases.  Of course\n> > you _could_ do\n> >\n> >               new_state = (res == 125)\n> >                         ? \"skip\"\n> >                         : (res > 0)\n> >                         ? terms->term_bad\n> >                         : terms->term_good;\n> >\n> > instead, but that would be harder to read.\n>\n> FWIW I agree with this, after seeing the resulting code.\n>\n> > > +           temporary_stdout_fd = open(git_path_bisect_run(), O_CREAT | O_WRONLY | O_TRUNC, 0666);\n> >\n> > Can this open fail, and if it fails, what do we want to do?\n> >\n> > > +           saved_stdout = dup(1);\n> > > +           dup2(temporary_stdout_fd, 1);\n> > > +\n> > > +           res = bisect_state(terms, &new_state, 1);\n> > > +\n> > > +           dup2(saved_stdout, 1);\n> > > +           close(saved_stdout);\n> > > +           close(temporary_stdout_fd);\n> >\n> > Hmph, now you lost me.  Whose output are we working around here with\n> > the redirection?\n> >\n> >       ... goes and looks ...\n> >\n> > Ahh, OK.  bisect_next_all() to bisect_checkout() all assume that\n> > they only need to write to the standard output, so we need to do\n> > this dance (unless we are willing to update the bisect.c functions\n> > to accept FILE * as parameter, that is).\n> >\n> > However, they use not just write(2) but stdio to do their output,\n> > no?  Don't we need to fflush(stdout) around the redirection dance,\n> > one to empty the output that was associated with the real standard\n> > output stream before asking bisect_state() to write to fd #1 via\n> > stdio, and one more time to flush out what bisect_state() wrote to\n> > the stdio after the call returns before closing the fd we opened to\n> > the BISECT_RUN file?\n>\n> Yes, we would have to `fflush(stdout)`.\n>\n> However, I still don't like that we play such a `dup2()` game. I gave it a\n> quick try to avoid it (see the diff below, which corresponds to the commit\n> I pushed up as `git-bisect-work-part4-v7` to\n> https://github.com/dscho/git), which still could benefit from a bit of\n> polishing (maybe we should rethink the object model and extend/rename\n> `bisect_terms` to `bisect_state` and accumulate more fields, such as\n> `out_fd`.\n>\n> Obviously this will need to be cleaned up, and while I would _love_ to see\n> this make it into your next iteration, ultimately it is up to you, Miriam,\n> to decide whether you want to build on my diff (quite possibly making the\n> entire object model of the bisect part of Git's code more elegant and more\n> maintainable), and up to you, Junio, to decide whether you would be\n> willing to accept the patch series without this refactoring.\n>\nI also don’t love this `dup2()` game but I implemented it as a\npossible solution to recreate the cat command as it is\nin the shell script, without changing behavior or parameters in other functions.\nAlso thank you for your solution, I agree that it is more elegant and\nmaintainable.\n\nIf Junio accepts the patch series with my `dup2()` solution, I can\nimplement your suggestion as an improvement after finishing the\nporting of git bisect to C. Because after this patch series, there\nwill be only one last patch series left and I believe rethinking the\nobject model and extend/rename `bisect_terms` to `bisect_state` and\naccumulate more fields, such as `out_fd` should be better separated of\nthe porting project.\n\nBest,\nMiriam.\n\n> -- snipsnap --\n> diff --git a/bisect.c b/bisect.c\n> index af2863d044b..405bf60b4b6 100644\n> --- a/bisect.c\n> +++ b/bisect.c\n> @@ -683,20 +683,21 @@ static void bisect_common(struct rev_info *revs)\n>  }\n>\n>  static enum bisect_error error_if_skipped_commits(struct commit_list *tried,\n> -                                   const struct object_id *bad)\n> +                                                 const struct object_id *bad,\n> +                                                 FILE *out)\n>  {\n>         if (!tried)\n>                 return BISECT_OK;\n>\n> -       printf(\"There are only 'skip'ped commits left to test.\\n\"\n> -              \"The first %s commit could be any of:\\n\", term_bad);\n> +       fprintf(out, \"There are only 'skip'ped commits left to test.\\n\"\n> +               \"The first %s commit could be any of:\\n\", term_bad);\n>\n>         for ( ; tried; tried = tried->next)\n> -               printf(\"%s\\n\", oid_to_hex(&tried->item->object.oid));\n> +               fprintf(out, \"%s\\n\", oid_to_hex(&tried->item->object.oid));\n>\n>         if (bad)\n> -               printf(\"%s\\n\", oid_to_hex(bad));\n> -       printf(_(\"We cannot bisect more!\\n\"));\n> +               fprintf(out, \"%s\\n\", oid_to_hex(bad));\n> +       fprintf(out, _(\"We cannot bisect more!\\n\"));\n>\n>         return BISECT_ONLY_SKIPPED_LEFT;\n>  }\n> @@ -725,10 +726,12 @@ static int is_expected_rev(const struct object_id *oid)\n>         return res;\n>  }\n>\n> -static enum bisect_error bisect_checkout(const struct object_id *bisect_rev, int no_checkout)\n> +static enum bisect_error bisect_checkout(const struct object_id *bisect_rev,\n> +                                        int no_checkout, FILE *out)\n>  {\n>         char bisect_rev_hex[GIT_MAX_HEXSZ + 1];\n>         enum bisect_error res = BISECT_OK;\n> +       struct child_process cp = CHILD_PROCESS_INIT;\n>\n>         oid_to_hex_r(bisect_rev_hex, bisect_rev);\n>         update_ref(NULL, \"BISECT_EXPECTED_REV\", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);\n> @@ -749,7 +752,10 @@ static enum bisect_error bisect_checkout(const struct object_id *bisect_rev, int\n>         }\n>\n>         argv_show_branch[1] = bisect_rev_hex;\n> -       res = run_command_v_opt(argv_show_branch, RUN_GIT_CMD);\n> +       cp.argv = argv_show_branch;\n> +       cp.git_cmd = 1;\n> +       cp.out = dup(fileno(out));\n> +       res = run_command(&cp);\n>         /*\n>          * Errors in `run_command()` itself, signaled by res < 0,\n>          * and errors in the child process, signaled by res > 0\n> @@ -841,7 +847,8 @@ static void handle_skipped_merge_base(const struct object_id *mb)\n>   * for early success, this will be converted back to 0 in\n>   * check_good_are_ancestors_of_bad().\n>   */\n> -static enum bisect_error check_merge_bases(int rev_nr, struct commit **rev, int no_checkout)\n> +static enum bisect_error check_merge_bases(int rev_nr, struct commit **rev,\n> +                                          int no_checkout, FILE *out)\n>  {\n>         enum bisect_error res = BISECT_OK;\n>         struct commit_list *result;\n> @@ -858,8 +865,8 @@ static enum bisect_error check_merge_bases(int rev_nr, struct commit **rev, int\n>                 } else if (0 <= oid_array_lookup(&skipped_revs, mb)) {\n>                         handle_skipped_merge_base(mb);\n>                 } else {\n> -                       printf(_(\"Bisecting: a merge base must be tested\\n\"));\n> -                       res = bisect_checkout(mb, no_checkout);\n> +                       fprintf(out, _(\"Bisecting: a merge base must be tested\\n\"));\n> +                       res = bisect_checkout(mb, no_checkout, out);\n>                         if (!res)\n>                                 /* indicate early success */\n>                                 res = BISECT_INTERNAL_SUCCESS_MERGE_BASE;\n> @@ -898,8 +905,9 @@ static int check_ancestors(struct repository *r, int rev_nr,\n>   */\n>\n>  static enum bisect_error check_good_are_ancestors_of_bad(struct repository *r,\n> -                                           const char *prefix,\n> -                                           int no_checkout)\n> +                                                        const char *prefix,\n> +                                                        int no_checkout,\n> +                                                        FILE *out)\n>  {\n>         char *filename;\n>         struct stat st;\n> @@ -924,7 +932,7 @@ static enum bisect_error check_good_are_ancestors_of_bad(struct repository *r,\n>\n>         rev = get_bad_and_good_commits(r, &rev_nr);\n>         if (check_ancestors(r, rev_nr, rev, prefix))\n> -               res = check_merge_bases(rev_nr, rev, no_checkout);\n> +               res = check_merge_bases(rev_nr, rev, no_checkout, out);\n>         free(rev);\n>\n>         if (!res) {\n> @@ -953,7 +961,7 @@ static enum bisect_error check_good_are_ancestors_of_bad(struct repository *r,\n>   */\n>  static void show_diff_tree(struct repository *r,\n>                            const char *prefix,\n> -                          struct commit *commit)\n> +                          struct commit *commit, FILE *out)\n>  {\n>         const char *argv[] = {\n>                 \"diff-tree\", \"--pretty\", \"--stat\", \"--summary\", \"--cc\", NULL\n> @@ -964,6 +972,7 @@ static void show_diff_tree(struct repository *r,\n>         repo_init_revisions(r, &opt, prefix);\n>\n>         setup_revisions(ARRAY_SIZE(argv) - 1, argv, &opt, NULL);\n> +       opt.diffopt.file = out;\n>         log_tree_commit(&opt, commit);\n>  }\n>\n> @@ -1007,7 +1016,8 @@ void read_bisect_terms(const char **read_bad, const char **read_good)\n>   * the end of bisect_helper::cmd_bisect__helper() helps bypassing\n>   * all the code related to finding a commit to test.\n>   */\n> -enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n> +enum bisect_error bisect_next_all(struct repository *r, const char *prefix,\n> +                                 FILE *out)\n>  {\n>         struct rev_info revs;\n>         struct commit_list *tried;\n> @@ -1032,7 +1042,7 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n>         if (skipped_revs.nr)\n>                 bisect_flags |= FIND_BISECTION_ALL;\n>\n> -       res = check_good_are_ancestors_of_bad(r, prefix, no_checkout);\n> +       res = check_good_are_ancestors_of_bad(r, prefix, no_checkout, out);\n>         if (res)\n>                 return res;\n>\n> @@ -1051,10 +1061,10 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n>                  * We should return error here only if the \"bad\"\n>                  * commit is also a \"skip\" commit.\n>                  */\n> -               res = error_if_skipped_commits(tried, NULL);\n> +               res = error_if_skipped_commits(tried, NULL, out);\n>                 if (res < 0)\n>                         return res;\n> -               printf(_(\"%s was both %s and %s\\n\"),\n> +               fprintf(out, _(\"%s was both %s and %s\\n\"),\n>                        oid_to_hex(current_bad_oid),\n>                        term_good,\n>                        term_bad);\n> @@ -1072,13 +1082,13 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n>         bisect_rev = &revs.commits->item->object.oid;\n>\n>         if (oideq(bisect_rev, current_bad_oid)) {\n> -               res = error_if_skipped_commits(tried, current_bad_oid);\n> +               res = error_if_skipped_commits(tried, current_bad_oid, out);\n>                 if (res)\n>                         return res;\n> -               printf(\"%s is the first %s commit\\n\", oid_to_hex(bisect_rev),\n> -                       term_bad);\n> +               fprintf(out, \"%s is the first %s commit\\n\",\n> +                       oid_to_hex(bisect_rev), term_bad);\n>\n> -               show_diff_tree(r, prefix, revs.commits->item);\n> +               show_diff_tree(r, prefix, revs.commits->item, out);\n>                 /*\n>                  * This means the bisection process succeeded.\n>                  * Using BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND (-10)\n> @@ -1098,14 +1108,14 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n>          * TRANSLATORS: the last %s will be replaced with \"(roughly %d\n>          * steps)\" translation.\n>          */\n> -       printf(Q_(\"Bisecting: %d revision left to test after this %s\\n\",\n> -                 \"Bisecting: %d revisions left to test after this %s\\n\",\n> -                 nr), nr, steps_msg);\n> +       fprintf(out, Q_(\"Bisecting: %d revision left to test after this %s\\n\",\n> +                       \"Bisecting: %d revisions left to test after this %s\\n\",\n> +                       nr), nr, steps_msg);\n>         free(steps_msg);\n>         /* Clean up objects used, as they will be reused. */\n>         repo_clear_commit_marks(r, ALL_REV_FLAGS);\n>\n> -       return bisect_checkout(bisect_rev, no_checkout);\n> +       return bisect_checkout(bisect_rev, no_checkout, out);\n>  }\n>\n>  static inline int log2i(int n)\n> diff --git a/bisect.h b/bisect.h\n> index ec24ac2d7ee..72bfd7b0053 100644\n> --- a/bisect.h\n> +++ b/bisect.h\n> @@ -61,7 +61,8 @@ enum bisect_error {\n>         BISECT_INTERNAL_SUCCESS_MERGE_BASE = -11\n>  };\n>\n> -enum bisect_error bisect_next_all(struct repository *r, const char *prefix);\n> +enum bisect_error bisect_next_all(struct repository *r, const char *prefix,\n> +                                 FILE *out);\n>\n>  int estimate_bisect_steps(int all);\n>\n> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\n> index 1c96580bd49..29969763d35 100644\n> --- a/builtin/bisect--helper.c\n> +++ b/builtin/bisect--helper.c\n> @@ -581,7 +581,8 @@ static int bisect_successful(struct bisect_terms *terms)\n>         return res;\n>  }\n>\n> -static enum bisect_error bisect_next(struct bisect_terms *terms, const char *prefix)\n> +static enum bisect_error bisect_next(struct bisect_terms *terms,\n> +                                    const char *prefix, FILE *out)\n>  {\n>         enum bisect_error res;\n>\n> @@ -592,7 +593,7 @@ static enum bisect_error bisect_next(struct bisect_terms *terms, const char *pre\n>                 return BISECT_FAILED;\n>\n>         /* Perform all bisection computation */\n> -       res = bisect_next_all(the_repository, prefix);\n> +       res = bisect_next_all(the_repository, prefix, out);\n>\n>         if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) {\n>                 res = bisect_successful(terms);\n> @@ -604,12 +605,13 @@ static enum bisect_error bisect_next(struct bisect_terms *terms, const char *pre\n>         return res;\n>  }\n>\n> -static enum bisect_error bisect_auto_next(struct bisect_terms *terms, const char *prefix)\n> +static enum bisect_error bisect_auto_next(struct bisect_terms *terms,\n> +                                         const char *prefix, FILE *out)\n>  {\n>         if (bisect_next_check(terms, NULL))\n>                 return BISECT_OK;\n>\n> -       return bisect_next(terms, prefix);\n> +       return bisect_next(terms, prefix, out);\n>  }\n>\n>  static enum bisect_error bisect_start(struct bisect_terms *terms, const char **argv, int argc)\n> @@ -808,7 +810,7 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, const char **a\n>         if (res)\n>                 return res;\n>\n> -       res = bisect_auto_next(terms, NULL);\n> +       res = bisect_auto_next(terms, NULL, stdout);\n>         if (!is_bisect_success(res))\n>                 bisect_clean_state();\n>         return res;\n> @@ -847,7 +849,7 @@ static int bisect_autostart(struct bisect_terms *terms)\n>  }\n>\n>  static enum bisect_error bisect_state(struct bisect_terms *terms, const char **argv,\n> -                                     int argc)\n> +                                     int argc, FILE *out)\n>  {\n>         const char *state;\n>         int i, verify_expected = 1;\n> @@ -924,7 +926,7 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, const char **a\n>         }\n>\n>         oid_array_clear(&revs);\n> -       return bisect_auto_next(terms, NULL);\n> +       return bisect_auto_next(terms, NULL, out);\n>  }\n>\n>  static enum bisect_error bisect_log(void)\n> @@ -1013,7 +1015,7 @@ static enum bisect_error bisect_replay(struct bisect_terms *terms, const char *f\n>         if (res)\n>                 return BISECT_FAILED;\n>\n> -       return bisect_auto_next(terms, NULL);\n> +       return bisect_auto_next(terms, NULL, stdout);\n>  }\n>\n>  static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **argv, int argc)\n> @@ -1045,7 +1047,7 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar\n>                         strvec_push(&argv_state, argv[i]);\n>                 }\n>         }\n> -       res = bisect_state(terms, argv_state.v, argv_state.nr);\n> +       res = bisect_state(terms, argv_state.v, argv_state.nr, stdout);\n>\n>         strvec_clear(&argv_state);\n>         return res;\n> @@ -1096,7 +1098,6 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)\n>         struct strvec args = STRVEC_INIT;\n>         struct strvec run_args = STRVEC_INIT;\n>         const char *new_state;\n> -       int temporary_stdout_fd, saved_stdout;\n>\n>         if (bisect_next_check(terms, NULL))\n>                 return BISECT_FAILED;\n> @@ -1111,6 +1112,8 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)\n>         strvec_push(&run_args, command.buf);\n>\n>         while (1) {\n> +               FILE *f;\n> +\n>                 strvec_clear(&args);\n>\n>                 printf(_(\"running %s\\n\"), command.buf);\n> @@ -1130,19 +1133,13 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)\n>                 else\n>                         new_state = terms->term_bad;\n>\n> -               temporary_stdout_fd = open(git_path_bisect_run(), O_CREAT | O_WRONLY | O_TRUNC, 0666);\n> +               f = fopen_for_writing(git_path_bisect_run());\n>\n> -               if (temporary_stdout_fd < 0)\n> +               if (!f)\n>                         return error_errno(_(\"cannot open file '%s' for writing\"), git_path_bisect_run());\n>\n> -               saved_stdout = dup(1);\n> -               dup2(temporary_stdout_fd, 1);\n> -\n> -               res = bisect_state(terms, &new_state, 1);\n> -\n> -               dup2(saved_stdout, 1);\n> -               close(saved_stdout);\n> -               close(temporary_stdout_fd);\n> +               res = bisect_state(terms, &new_state, 1, f);\n> +               fclose(f);\n>\n>                 print_file_to_stdout(git_path_bisect_run());\n>\n> @@ -1240,12 +1237,12 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)\n>                 if (argc)\n>                         return error(_(\"--bisect-next requires 0 arguments\"));\n>                 get_terms(&terms);\n> -               res = bisect_next(&terms, prefix);\n> +               res = bisect_next(&terms, prefix, stdout);\n>                 break;\n>         case BISECT_STATE:\n>                 set_terms(&terms, \"bad\", \"good\");\n>                 get_terms(&terms);\n> -               res = bisect_state(&terms, argv, argc);\n> +               res = bisect_state(&terms, argv, argc, stdout);\n>                 break;\n>         case BISECT_LOG:\n>                 if (argc)\n"},{"id":"434909","messageId":"xmqqlf48b5io.fsf@gitster.g","threadId":"56429","inReplyTo":"CAN7CjDANWsWwPcAG2cftAiadwaWZNXBtL=Q8MrqH2xVMj7kUOg@mail.gmail.com","subject":"Re: [PATCH v6 5/6] bisect--helper: reimplement `bisect_run` shell","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-09-07T18:32:47Z","receivedAt":"2021-09-07T18:32:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Miriam R.\" <mirucam@gmail.com> writes:\n\n>> However, I still don't like that we play such a `dup2()` game. I gave it a\n>> quick try to avoid it (see the diff below, which corresponds to the commit\n>> I pushed up as `git-bisect-work-part4-v7` to\n>> https://github.com/dscho/git), which still could benefit from a bit of\n>> polishing (maybe we should rethink the object model and extend/rename\n>> `bisect_terms` to `bisect_state` and accumulate more fields, such as\n>> `out_fd`.\n>>\n>> Obviously this will need to be cleaned up, and while I would _love_ to see\n>> this make it into your next iteration, ultimately it is up to you, Miriam,\n>> to decide whether you want to build on my diff (quite possibly making the\n>> entire object model of the bisect part of Git's code more elegant and more\n>> maintainable), and up to you, Junio, to decide whether you would be\n>> willing to accept the patch series without this refactoring.\n\nIf the code paths involved are shallow and narrow enough that not\ntoo many existing callers need to start passing FILE *stdout down\n(from the looks of your illustration patch, it does not seem to be\ntoo bad), I do not mind a series that is a bit longer than the\ncurrent 6-patch series that has a preliminary enhancement step that\nallows callers to pass their own \"FILE *\" for output destination\nbefore the main part of the topic.\n\nThanks.\n"},{"id":"435217","messageId":"nycvar.QRO.7.76.6.2109090922310.55@tvgsbejvaqbjf.bet","threadId":"56429","inReplyTo":"xmqqlf48b5io.fsf@gitster.g","subject":"Re: [PATCH v6 5/6] bisect--helper: reimplement `bisect_run` shell","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-09-09T07:51:55Z","receivedAt":"2021-09-09T07:52:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 7 Sep 2021, Junio C Hamano wrote:\n\n> \"Miriam R.\" <mirucam@gmail.com> writes:\n>\n> >> However, I still don't like that we play such a `dup2()` game. I gave it a\n> >> quick try to avoid it (see the diff below, which corresponds to the commit\n> >> I pushed up as `git-bisect-work-part4-v7` to\n> >> https://github.com/dscho/git), which still could benefit from a bit of\n> >> polishing (maybe we should rethink the object model and extend/rename\n> >> `bisect_terms` to `bisect_state` and accumulate more fields, such as\n> >> `out_fd`.\n> >>\n> >> Obviously this will need to be cleaned up, and while I would _love_ to see\n> >> this make it into your next iteration, ultimately it is up to you, Miriam,\n> >> to decide whether you want to build on my diff (quite possibly making the\n> >> entire object model of the bisect part of Git's code more elegant and more\n> >> maintainable), and up to you, Junio, to decide whether you would be\n> >> willing to accept the patch series without this refactoring.\n>\n> If the code paths involved are shallow and narrow enough that not\n> too many existing callers need to start passing FILE *stdout down\n> (from the looks of your illustration patch, it does not seem to be\n> too bad), I do not mind a series that is a bit longer than the\n> current 6-patch series that has a preliminary enhancement step that\n> allows callers to pass their own \"FILE *\" for output destination\n> before the main part of the topic.\n\nMy impression, from the diff that I sent, is that this is too deep and\nwide, and indeed needs a follow-up patch series as indicated by Miriam. My\npreference would be (as I hinted at) to accumulate relevant data (such as\nthe terms and, yes, the `FILE *`) into a `struct bisect_state` and pass\nthat around. Sort of a light-weight object-oriented design, similar to how\nwe do things in `builtin/am.c` with `struct am_state`.\n\nThanks,\nDscho\n\n"}]}