Message ID | 20200128144026.53128-10-mirucam@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Finish converting git bisect to C part 1 | expand |
Hi Miriam, On Tue, 28 Jan 2020, Miriam Rubio wrote: > @@ -893,18 +901,20 @@ static void check_good_are_ancestors_of_bad(struct repository *r, > if (check_ancestors(r, rev_nr, rev, prefix)) > res = check_merge_bases(rev_nr, rev, no_checkout); > free(rev); > - if (res) > - exit(res == -11 ? 0 : -res); > > - /* Create file BISECT_ANCESTORS_OK. */ > - fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600); > - if (fd < 0) > - warning_errno(_("could not create file '%s'"), > - filename); > - else > - close(fd); > + if (!res) > + { Please move the opening `{` to the same line as the `if (!res)`. > + /* Create file BISECT_ANCESTORS_OK. */ > + fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600); > + if (fd < 0) > + warning_errno(_("could not create file '%s'"), > + filename); > + else > + close(fd); > + } I wonder whether this would be easier to read: if (res == -11) res = 0; else if (res) ; /* error out */ else if ((fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600)) < 0) res = warning_errno(_("could not create file '%s'"), filename); else close(fd); Note: my code explicitly assigns `res = -1` if the file could not be created, which is technically a change in behavior, but I think it is actually a bug fix. > done: > free(filename); > + return res; > } > > /* > @@ -975,7 +985,9 @@ int bisect_next_all(struct repository *r, const char *prefix, int no_checkout) > if (read_bisect_refs()) > die(_("reading bisect refs failed")); I see that there is still a `die()` here, and you left it alone in this patch because at this point, only the callers of `check_good_are_ancestors_of_bad()` need to be addressed. Good. At a later point, this will have to be dealt with, of course. Another thing will need to be handled, too: while I was looking at the code whether any resources need to be released (returning a negative integer does not release memory or close file handles, unlike `die()`), I stumbled across the fact that `term_bad` and `term_good` are file-local variables. They will need to be made attributes of a `struct` and will need to be released properly, i.e. the ownership will have to be clarified (is a failed `bisect_next_all()` responsible for releasing the memory it allocated via `read_bisect_terms()`, or its caller?). Just something to keep in mind. Or better: to jot down in a TODO list. > > - check_good_are_ancestors_of_bad(r, prefix, no_checkout); > + res = check_good_are_ancestors_of_bad(r, prefix, no_checkout); > + if (res) > + return res; > > bisect_rev_setup(r, &revs, prefix, "%s", "^%s", 1); > revs.limited = 1; > diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c > index 826fcba2ed..3442bfe2cb 100644 > --- a/builtin/bisect--helper.c > +++ b/builtin/bisect--helper.c > @@ -666,7 +666,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) > > switch (cmdmode) { > case NEXT_ALL: > - return bisect_next_all(the_repository, prefix, no_checkout); > + res = bisect_next_all(the_repository, prefix, no_checkout); > + break; > case WRITE_TERMS: > if (argc != 2) > return error(_("--write-terms requires two arguments")); > @@ -713,5 +714,12 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) > return error("BUG: unknown subcommand '%d'", cmdmode); > } > free_terms(&terms); > - return !!res; > + /* > + * Handle early success > + * From check_merge_bases > check_good_are_ancestors_of_bad > bisect_next_all > + */ > + if (res == -11) > + res = 0; Hmm. Is this the correct place, though? Should `bisect_next_all()` not be the function that already turns `-11` into `0`? In other words, _which_ code are we supposed to skip over when `check_good_are_ancestors_of_bad()` returns `-11`? In other words, where would the `catch` of the `try`/`catch` be, if we had portable exceptions in C? > + > + return res < 0 ? -res : res; This is a change in behavior, though: previously we guaranteed that the exit code is either 0 on success or 1 upon failure. I am not quite sure that we want to change that behavior. Ciao, Dscho > } > -- > 2.21.1 (Apple Git-122.3) > >
Hi, El jue., 30 ene. 2020 a las 14:46, Johannes Schindelin (<Johannes.Schindelin@gmx.de>) escribió: > > Hi Miriam, > > On Tue, 28 Jan 2020, Miriam Rubio wrote: > > > @@ -893,18 +901,20 @@ static void check_good_are_ancestors_of_bad(struct repository *r, > > if (check_ancestors(r, rev_nr, rev, prefix)) > > res = check_merge_bases(rev_nr, rev, no_checkout); > > free(rev); > > - if (res) > > - exit(res == -11 ? 0 : -res); > > > > - /* Create file BISECT_ANCESTORS_OK. */ > > - fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600); > > - if (fd < 0) > > - warning_errno(_("could not create file '%s'"), > > - filename); > > - else > > - close(fd); > > + if (!res) > > + { > > Please move the opening `{` to the same line as the `if (!res)`. Noted. > > > + /* Create file BISECT_ANCESTORS_OK. */ > > + fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600); > > + if (fd < 0) > > + warning_errno(_("could not create file '%s'"), > > + filename); > > + else > > + close(fd); > > + } > > I wonder whether this would be easier to read: > > if (res == -11) > res = 0; > else if (res) > ; /* error out */ > else if ((fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600)) < 0) > res = warning_errno(_("could not create file '%s'"), filename); > else > close(fd); > Yes, I think it is a good improvement. > Note: my code explicitly assigns `res = -1` if the file could not be > created, which is technically a change in behavior, but I think it is > actually a bug fix. Aha. If my mentor is ok with this change, I will apply the improvement you suggested :). > > > done: > > free(filename); > > + return res; > > } > > > > /* > > @@ -975,7 +985,9 @@ int bisect_next_all(struct repository *r, const char *prefix, int no_checkout) > > if (read_bisect_refs()) > > die(_("reading bisect refs failed")); > > I see that there is still a `die()` here, and you left it alone in this > patch because at this point, only the callers of > `check_good_are_ancestors_of_bad()` need to be addressed. Good. > > At a later point, this will have to be dealt with, of course. > > Another thing will need to be handled, too: while I was looking at the > code whether any resources need to be released (returning a negative > integer does not release memory or close file handles, unlike `die()`), I > stumbled across the fact that `term_bad` and `term_good` are file-local > variables. They will need to be made attributes of a `struct` and will > need to be released properly, i.e. the ownership will have to be clarified > (is a failed `bisect_next_all()` responsible for releasing the memory it > allocated via `read_bisect_terms()`, or its caller?). > > Just something to keep in mind. Or better: to jot down in a TODO list. Ok. I will write this down for future improvements. Thank you! > > > > > - check_good_are_ancestors_of_bad(r, prefix, no_checkout); > > + res = check_good_are_ancestors_of_bad(r, prefix, no_checkout); > > + if (res) > > + return res; > > > > bisect_rev_setup(r, &revs, prefix, "%s", "^%s", 1); > > revs.limited = 1; > > diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c > > index 826fcba2ed..3442bfe2cb 100644 > > --- a/builtin/bisect--helper.c > > +++ b/builtin/bisect--helper.c > > @@ -666,7 +666,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) > > > > switch (cmdmode) { > > case NEXT_ALL: > > - return bisect_next_all(the_repository, prefix, no_checkout); > > + res = bisect_next_all(the_repository, prefix, no_checkout); > > + break; > > case WRITE_TERMS: > > if (argc != 2) > > return error(_("--write-terms requires two arguments")); > > @@ -713,5 +714,12 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) > > return error("BUG: unknown subcommand '%d'", cmdmode); > > } > > free_terms(&terms); > > - return !!res; > > + /* > > + * Handle early success > > + * From check_merge_bases > check_good_are_ancestors_of_bad > bisect_next_all > > + */ > > + if (res == -11) > > + res = 0; > > Hmm. Is this the correct place, though? Should `bisect_next_all()` not be > the function that already turns `-11` into `0`? In other words, _which_ > code are we supposed to skip over when `check_good_are_ancestors_of_bad()` > returns `-11`? In other words, where would the `catch` of the > `try`/`catch` be, if we had portable exceptions in C? I think there must be a reason to do it there (but I don't know exactly), because there are some comments in code that say explicitly that this -11 to 0 is done in cmd_bisect_helper(), when the bisecting command exits. > > > + > > + return res < 0 ? -res : res; > > This is a change in behavior, though: previously we guaranteed that the > exit code is either 0 on success or 1 upon failure. I am not quite sure > that we want to change that behavior. I think this is because different error codes might enable a bisecting script calling the bisect command that uses this function to do different things depending on the exit status of the bisect command. Thank you for reviewing. Best, Miriam. > > Ciao, > Dscho > > > } > > -- > > 2.21.1 (Apple Git-122.3) > > > >
Hi Miriam, On Thu, 30 Jan 2020, Miriam R. wrote: > El jue., 30 ene. 2020 a las 14:46, Johannes Schindelin > (<Johannes.Schindelin@gmx.de>) escribió: > > > > On Tue, 28 Jan 2020, Miriam Rubio wrote: > > > > > + > > > + return res < 0 ? -res : res; > > > > This is a change in behavior, though: previously we guaranteed that the > > exit code is either 0 on success or 1 upon failure. I am not quite sure > > that we want to change that behavior. > > I think this is because different error codes might enable a bisecting > script calling the bisect command that uses this function to do > different things depending on the exit status of the bisect command. Oops. I am _totally_ wrong on this. As you are changing a lot of `exit(<n>)` to `return -<n>` with the intention to turn the value into an exit code only at the `cmd_bisect__helper()` level, this is actually required a change. I am a bit uneasy about this, but I could not see any return values in `bisect.c` other than 0 and -1, prior to this patch series. However, I would love to see this refactored into its own commit, more toward the beginning of the patch series, with a very clean commit message that describes that intention of being _the_ exit point from `bisect.c`. Without this change, you simply cannot change the `exit(<n>);` calls in `bisect.c` for any `<n>` other than 0 or 1. Sorry that it took me so long to wrap my head around this rather trivial idea. Ciao, Dscho
Hi, El jue., 30 ene. 2020 a las 16:01, Johannes Schindelin (<Johannes.Schindelin@gmx.de>) escribió: > > Hi Miriam, > > On Thu, 30 Jan 2020, Miriam R. wrote: > > > El jue., 30 ene. 2020 a las 14:46, Johannes Schindelin > > (<Johannes.Schindelin@gmx.de>) escribió: > > > > > > On Tue, 28 Jan 2020, Miriam Rubio wrote: > > > > > > > + > > > > + return res < 0 ? -res : res; > > > > > > This is a change in behavior, though: previously we guaranteed that the > > > exit code is either 0 on success or 1 upon failure. I am not quite sure > > > that we want to change that behavior. > > > > I think this is because different error codes might enable a bisecting > > script calling the bisect command that uses this function to do > > different things depending on the exit status of the bisect command. > > Oops. I am _totally_ wrong on this. > > As you are changing a lot of `exit(<n>)` to `return -<n>` with the > intention to turn the value into an exit code only at the > `cmd_bisect__helper()` level, this is actually required a change. > > I am a bit uneasy about this, but I could not see any return values in > `bisect.c` other than 0 and -1, prior to this patch series. > > However, I would love to see this refactored into its own commit, more > toward the beginning of the patch series, with a very clean commit message > that describes that intention of being _the_ exit point from `bisect.c`. Ok. Noted > > Without this change, you simply cannot change the `exit(<n>);` calls in > `bisect.c` for any `<n>` other than 0 or 1. > > Sorry that it took me so long to wrap my head around this rather trivial > idea. Please, don't worry :) Thank you again! Best, Miriam. > > Ciao, > Dscho
Hi Dscho, On Thu, Jan 30, 2020 at 2:46 PM Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote: > > On Tue, 28 Jan 2020, Miriam Rubio wrote: > > + /* Create file BISECT_ANCESTORS_OK. */ > > + fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600); > > + if (fd < 0) > > + warning_errno(_("could not create file '%s'"), > > + filename); > > + else > > + close(fd); > > + } > > I wonder whether this would be easier to read: > > if (res == -11) > res = 0; > else if (res) > ; /* error out */ > else if ((fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600)) < 0) > res = warning_errno(_("could not create file '%s'"), filename); > else > close(fd); > > Note: my code explicitly assigns `res = -1` if the file could not be > created, which is technically a change in behavior, but I think it is > actually a bug fix. I don't think so. I think creating the BISECT_ANCESTORS_OK file is not absolutely necessary. If it doesn't exist we will just check if ancestors are ok again at the next bisection step, which will take a bit of time, but which will not make us give any false result, or prevent us from continuing the bisection process. I think that it's also the reason why warning_errno(...) is used in case we could not create the file instead of error_errno(...). We just want to signal with a warning that something might be wrong because we could not create the file, but not stop everything because of that. Best, Christian.
Hi Chris, On Thu, 30 Jan 2020, Christian Couder wrote: > Hi Dscho, > > On Thu, Jan 30, 2020 at 2:46 PM Johannes Schindelin > <Johannes.Schindelin@gmx.de> wrote: > > > > On Tue, 28 Jan 2020, Miriam Rubio wrote: > > > > + /* Create file BISECT_ANCESTORS_OK. */ > > > + fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600); > > > + if (fd < 0) > > > + warning_errno(_("could not create file '%s'"), > > > + filename); > > > + else > > > + close(fd); > > > + } > > > > I wonder whether this would be easier to read: > > > > if (res == -11) > > res = 0; > > else if (res) > > ; /* error out */ > > else if ((fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600)) < 0) > > res = warning_errno(_("could not create file '%s'"), filename); > > else > > close(fd); > > > > Note: my code explicitly assigns `res = -1` if the file could not be > > created, which is technically a change in behavior, but I think it is > > actually a bug fix. > > I don't think so. I think creating the BISECT_ANCESTORS_OK file is not > absolutely necessary. If it doesn't exist we will just check if > ancestors are ok again at the next bisection step, which will take a > bit of time, but which will not make us give any false result, or > prevent us from continuing the bisection process. > > I think that it's also the reason why warning_errno(...) is used in > case we could not create the file instead of error_errno(...). We just > want to signal with a warning that something might be wrong because we > could not create the file, but not stop everything because of that. Thank you for this explanation, it makes sense to me. Maybe a code comment would be in order? Ciao, Dscho
Hi Dscho, On Fri, Jan 31, 2020 at 10:07 AM Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote: > > On Thu, 30 Jan 2020, Christian Couder wrote: > > > On Thu, Jan 30, 2020 at 2:46 PM Johannes Schindelin > > <Johannes.Schindelin@gmx.de> wrote: > > > > > > On Tue, 28 Jan 2020, Miriam Rubio wrote: > > > > > > + /* Create file BISECT_ANCESTORS_OK. */ > > > > + fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600); > > > > + if (fd < 0) > > > > + warning_errno(_("could not create file '%s'"), > > > > + filename); > > > > + else > > > > + close(fd); > > > > + } > > > > > > I wonder whether this would be easier to read: > > > > > > if (res == -11) > > > res = 0; > > > else if (res) > > > ; /* error out */ > > > else if ((fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600)) < 0) > > > res = warning_errno(_("could not create file '%s'"), filename); > > > else > > > close(fd); > > > > > > Note: my code explicitly assigns `res = -1` if the file could not be > > > created, which is technically a change in behavior, but I think it is > > > actually a bug fix. > > > > I don't think so. I think creating the BISECT_ANCESTORS_OK file is not > > absolutely necessary. If it doesn't exist we will just check if > > ancestors are ok again at the next bisection step, which will take a > > bit of time, but which will not make us give any false result, or > > prevent us from continuing the bisection process. > > > > I think that it's also the reason why warning_errno(...) is used in > > case we could not create the file instead of error_errno(...). We just > > want to signal with a warning that something might be wrong because we > > could not create the file, but not stop everything because of that. > > Thank you for this explanation, it makes sense to me. > > Maybe a code comment would be in order? Yeah, I agree it would help. Thanks for your review, Christian.
Hi, El vie., 31 ene. 2020 a las 10:15, Christian Couder (<christian.couder@gmail.com>) escribió: > > Hi Dscho, > > On Fri, Jan 31, 2020 at 10:07 AM Johannes Schindelin > <Johannes.Schindelin@gmx.de> wrote: > > > > On Thu, 30 Jan 2020, Christian Couder wrote: > > > > > On Thu, Jan 30, 2020 at 2:46 PM Johannes Schindelin > > > <Johannes.Schindelin@gmx.de> wrote: > > > > > > > > On Tue, 28 Jan 2020, Miriam Rubio wrote: > > > > > > > > + /* Create file BISECT_ANCESTORS_OK. */ > > > > > + fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600); > > > > > + if (fd < 0) > > > > > + warning_errno(_("could not create file '%s'"), > > > > > + filename); > > > > > + else > > > > > + close(fd); > > > > > + } > > > > > > > > I wonder whether this would be easier to read: > > > > > > > > if (res == -11) > > > > res = 0; > > > > else if (res) > > > > ; /* error out */ > > > > else if ((fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600)) < 0) > > > > res = warning_errno(_("could not create file '%s'"), filename); > > > > else > > > > close(fd); > > > > > > > > Note: my code explicitly assigns `res = -1` if the file could not be > > > > created, which is technically a change in behavior, but I think it is > > > > actually a bug fix. > > > > > > I don't think so. I think creating the BISECT_ANCESTORS_OK file is not > > > absolutely necessary. If it doesn't exist we will just check if > > > ancestors are ok again at the next bisection step, which will take a > > > bit of time, but which will not make us give any false result, or > > > prevent us from continuing the bisection process. > > > > > > I think that it's also the reason why warning_errno(...) is used in > > > case we could not create the file instead of error_errno(...). We just > > > want to signal with a warning that something might be wrong because we > > > could not create the file, but not stop everything because of that. > > > > Thank you for this explanation, it makes sense to me. > > > > Maybe a code comment would be in order? > > Yeah, I agree it would help. > Noted. Thank you both for the review! Best, Miriam > Thanks for your review, > Christian.
diff --git a/bisect.c b/bisect.c index 2a6566d066..d519e10827 100644 --- a/bisect.c +++ b/bisect.c @@ -865,19 +865,27 @@ static int check_ancestors(struct repository *r, int rev_nr, * * If that's not the case, we need to check the merge bases. * If a merge base must be tested by the user, its source code will be - * checked out to be tested by the user and we will exit. + * checked out to be tested by the user and we will return. */ -static void check_good_are_ancestors_of_bad(struct repository *r, + +static int check_good_are_ancestors_of_bad(struct repository *r, const char *prefix, int no_checkout) { - char *filename = git_pathdup("BISECT_ANCESTORS_OK"); + char *filename; struct stat st; int fd, rev_nr, res = 0; struct commit **rev; + /* + * We don't want to clean the bisection state + * as we need to get back to where we started + * by using `git bisect reset`. + */ if (!current_bad_oid) - die(_("a %s revision is needed"), term_bad); + return error(_("a %s revision is needed"), term_bad); + + filename = git_pathdup("BISECT_ANCESTORS_OK"); /* Check if file BISECT_ANCESTORS_OK exists. */ if (!stat(filename, &st) && S_ISREG(st.st_mode)) @@ -893,18 +901,20 @@ static void check_good_are_ancestors_of_bad(struct repository *r, if (check_ancestors(r, rev_nr, rev, prefix)) res = check_merge_bases(rev_nr, rev, no_checkout); free(rev); - if (res) - exit(res == -11 ? 0 : -res); - /* Create file BISECT_ANCESTORS_OK. */ - fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600); - if (fd < 0) - warning_errno(_("could not create file '%s'"), - filename); - else - close(fd); + if (!res) + { + /* Create file BISECT_ANCESTORS_OK. */ + fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600); + if (fd < 0) + warning_errno(_("could not create file '%s'"), + filename); + else + close(fd); + } done: free(filename); + return res; } /* @@ -975,7 +985,9 @@ int bisect_next_all(struct repository *r, const char *prefix, int no_checkout) if (read_bisect_refs()) die(_("reading bisect refs failed")); - check_good_are_ancestors_of_bad(r, prefix, no_checkout); + res = check_good_are_ancestors_of_bad(r, prefix, no_checkout); + if (res) + return res; bisect_rev_setup(r, &revs, prefix, "%s", "^%s", 1); revs.limited = 1; diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c index 826fcba2ed..3442bfe2cb 100644 --- a/builtin/bisect--helper.c +++ b/builtin/bisect--helper.c @@ -666,7 +666,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) switch (cmdmode) { case NEXT_ALL: - return bisect_next_all(the_repository, prefix, no_checkout); + res = bisect_next_all(the_repository, prefix, no_checkout); + break; case WRITE_TERMS: if (argc != 2) return error(_("--write-terms requires two arguments")); @@ -713,5 +714,12 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) return error("BUG: unknown subcommand '%d'", cmdmode); } free_terms(&terms); - return !!res; + /* + * Handle early success + * From check_merge_bases > check_good_are_ancestors_of_bad > bisect_next_all + */ + if (res == -11) + res = 0; + + return res < 0 ? -res : res; }