All of lore.kernel.org
 help / color / mirror / Atom feed
* Feature request: Exponential search in git bisect
@ 2020-10-09 12:56 Manuel Bärenz
  2020-10-10  9:22 ` Christian Couder
  2020-10-27 12:10 ` Ævar Arnfjörð Bjarmason
  0 siblings, 2 replies; 10+ messages in thread
From: Manuel Bärenz @ 2020-10-09 12:56 UTC (permalink / raw)
  To: git

This feature was requested 8 years ago and briefly discussed:
https://public-inbox.org/git/20120318212957.GS1219@chaosreigns.com/


  TL;DR

Before doing git bisect, I want to use exponential search to
automatically find a good commit, in logarithmic time.


  Scenario

  * I have a bug in HEAD.
  * I strongly suspect that it was introduced some time ago, but I don't
    know when exactly.
  * I have an automated test that will find the bug if the test can run
    properly.
  * Most of the commits in the repository are not testable, i.e. the
    test doesn't run properly. (E.g. because the feature it tests wasn't
    introduced yet, refactoring, etc.)
  * I have no idea what a good commit might be, because I don't know
    when the first /testable/ good commit is.

This sounds like a standard application for git bisect. No matter how
big the repo, with binary search we would expect to find the first bad
commit in logarithmic time.


  Failed attempt

The zeroth idea might be trying to find the good commit by hand, by
reading changelogs, by trying some commits, whatever. In some
situations, this is not feasible. In fact, such situations occur
frequently for me, for example for undocumented features, unversioned
rolling releases, incidental complexity leading to older commits not
being testable, etc.

The first idea that comes to mind - and it was recommended 8 years agos,
and I've tried it a few times already - is to simply mark the root
commit as good. (Now, there might be several roots, but that's a puzzle
you typically only have to figure out once per repo.) This sounds great
in theory because binary search should get through the good old commits
in logarithmic time.

The problem with this approach is that if most older commits are
untestable, I have to git bisect skip them. This basically kills the
logarithmic performance, because bisect skip doesn't do binary search,
but something rather random. Just yesterday I killed a bisect search
that took hours because it kept skipping and didn't find actual good
commits.

You might say that instead of skipping old commits, one should mark them
as good. That's problematic because then I might accidentally mark a
commit as good that was already untestable bad. Given that bisect has no
undo functionality, that can quickly mess up my search. Distinguishing
untestable good from untestable bad is really hard automatically. I
shouldn't have to do that.

Long story short: Going from the root commit typically isn't feasible.
I've tried it.


  Proposal: Exponential search

Instead of going from the root commit, what I do manually before
starting git bisect is this:

git checkout HEAD~10
./test.sh # Says: "Bug is present"
git checkout HEAD~20
./test.sh # Says: "Bug is still present"
git checkout HEAD~40
./test.sh # Says: "Bug is still present"
[...] # Proceed exponentially
git checkout HEAD~640
./test.sh # Says: "Bug is GONE!"
git bisect good

This technique is known as
https://en.wikipedia.org/wiki/Exponential_search, and it works very well
in practice. I find a good commit long before I enter the "untestable
good" region. But it's tedious to do this manually. In this example, I
needed to run the script 8 times manually, but of course it can be more
often, and compiling and running the test may take time. This is ok for
a one-off search, but it's tedious for regular usages.

Yes, I could wrap this up in a shell script, but I guess there are
caveats that I didn't think of when the history isn't linear. Maybe
someone even already has, and I'm unaware of that. But it feels like
this could be a proper git bisect feature, and a very useful one.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Feature request: Exponential search in git bisect
  2020-10-09 12:56 Feature request: Exponential search in git bisect Manuel Bärenz
@ 2020-10-10  9:22 ` Christian Couder
  2020-10-10  9:46   ` Christian Couder
  2020-10-25 17:15   ` Philip Oakley
  2020-10-27 12:10 ` Ævar Arnfjörð Bjarmason
  1 sibling, 2 replies; 10+ messages in thread
From: Christian Couder @ 2020-10-10  9:22 UTC (permalink / raw)
  To: Manuel Bärenz; +Cc: git

On Fri, Oct 9, 2020 at 9:35 PM Manuel Bärenz <manuel@enigmage.de> wrote:
>
> This feature was requested 8 years ago and briefly discussed:
> https://public-inbox.org/git/20120318212957.GS1219@chaosreigns.com/
>
>
>   TL;DR
>
> Before doing git bisect, I want to use exponential search to
> automatically find a good commit, in logarithmic time.

Ok, but the conclusion of the above discussion is that the problem
with this idea is being able to distinguish between a commit that is
bad and a commit where the feature that you want to test cannot be
tested for example because it hasn't been implemented yet.

>   Scenario
>
>   * I have a bug in HEAD.
>   * I strongly suspect that it was introduced some time ago, but I don't
>     know when exactly.
>   * I have an automated test that will find the bug if the test can run
>     properly.
>   * Most of the commits in the repository are not testable, i.e. the
>     test doesn't run properly. (E.g. because the feature it tests wasn't
>     introduced yet, refactoring, etc.)
>   * I have no idea what a good commit might be, because I don't know
>     when the first /testable/ good commit is.

Ok, so your test cannot distinguish between a bad commit and a commit
where the feature hasn't been implemented.

When you say that most of the commits in the repository are not
testable, it usually means that the feature has been implemented
relatively recently compared to the history of the project.

> This sounds like a standard application for git bisect. No matter how
> big the repo, with binary search we would expect to find the first bad
> commit in logarithmic time.

Not necessarily. If you cannot distinguish in any way between a bad
commit and a commit where the feature hasn't been implemented, then it
might be very difficult to find a good commit. And you need a good
commit before you can properly bisect.

Suppose for example that the first bad commit (the commit that
introduced the bug you are looking for) is a direct child of the
commit that introduced the feature. Then unless you are very lucky any
binary search using your script will only test either bad commits or
commits where the feature hasn't been implemented yet, and it is
unable to distinguish between them in your scenario.

>   Failed attempt
>
> The zeroth idea might be trying to find the good commit by hand, by
> reading changelogs, by trying some commits, whatever. In some
> situations, this is not feasible. In fact, such situations occur
> frequently for me, for example for undocumented features, unversioned
> rolling releases, incidental complexity leading to older commits not
> being testable, etc.

I understand that it's not always easy to find a good commit.

> The first idea that comes to mind - and it was recommended 8 years agos,
> and I've tried it a few times already - is to simply mark the root
> commit as good. (Now, there might be several roots, but that's a puzzle
> you typically only have to figure out once per repo.) This sounds great
> in theory because binary search should get through the good old commits
> in logarithmic time.

It is not necessarily a good idea to do that. And in the thread, what
was actually suggested by Peff (Jeff King) was to test released
versions (for example 1.6.0, 1.7.0, etc in the Git code base).

> The problem with this approach is that if most older commits are
> untestable, I have to git bisect skip them.

Skipping untestable commits is not always the right thing to do. If
you know that they are untestable because the feature has not been
implemented yet, it might be better to mark them as good instead.

This is by the way what an ideal script should do. It should mark
commits where the feature has not been implemented yet as good, and
should "skip" only the commits where the feature has already been
implemented but that are not testable for another reason, like another
"temporary" bug or "temporary" compile failures.

> This basically kills the
> logarithmic performance, because bisect skip doesn't do binary search,
> but something rather random.

I would say that the actual reason here is that bisect skip is used
when it shouldn't be used.

> Just yesterday I killed a bisect search
> that took hours because it kept skipping and didn't find actual good
> commits.

Ok.

> You might say that instead of skipping old commits, one should mark them
> as good.

Yes, they should be marked as good when the feature has not been
implemented yet.

> That's problematic because then I might accidentally mark a
> commit as good that was already untestable bad. Given that bisect has no
> undo functionality, that can quickly mess up my search. Distinguishing
> untestable good from untestable bad is really hard automatically. I
> shouldn't have to do that.

Sometimes it's not very difficult to test if the feature has been
implemented yet or not. For example with Git you could check if an
option for a command has been implemented by just checking if it
appears in the doc. So the bisection script could first check that and
exit 0 (which means good) if the option doesn't appear in the doc. If
it appears in the doc, then it could do the regular test: "skip" if
untestable, "good" if there is no bug, "bad" otherwise.

> Long story short: Going from the root commit typically isn't feasible.
> I've tried it.

It seems that you might not have tried in the best possible way.

>   Proposal: Exponential search
>
> Instead of going from the root commit, what I do manually before
> starting git bisect is this:
>
> git checkout HEAD~10
> ./test.sh # Says: "Bug is present"
> git checkout HEAD~20
> ./test.sh # Says: "Bug is still present"
> git checkout HEAD~40
> ./test.sh # Says: "Bug is still present"
> [...] # Proceed exponentially
> git checkout HEAD~640
> ./test.sh # Says: "Bug is GONE!"
> git bisect good

If your script cannot distinguish between a bad commit and a commit
where the feature hasn't been implemented, then you were lucky that
that HEAD~640 was good. If the feature had been introduced between
HEAD~639 and HEAD~321 then your script would have said  "Bug is still
present".

> This technique is known as
> https://en.wikipedia.org/wiki/Exponential_search, and it works very well
> in practice. I find a good commit long before I enter the "untestable
> good" region.

If you are lucky, yes, you find a good commit long before you enter
the "untestable good" region.

> But it's tedious to do this manually. In this example, I
> needed to run the script 8 times manually, but of course it can be more
> often, and compiling and running the test may take time. This is ok for
> a one-off search, but it's tedious for regular usages.
>
> Yes, I could wrap this up in a shell script, but I guess there are
> caveats that I didn't think of when the history isn't linear. Maybe
> someone even already has, and I'm unaware of that. But it feels like
> this could be a proper git bisect feature, and a very useful one.

I agree that it could be useful. It could be especially useful if
people have a script that can actually distinguish between a bad
commit and a commit where the feature hasn't been implemented.

So if someone plans to implement that in Git, particular care in the
documentation should be taken to explain the issues related to testing
a feature when it might not be implemented yet.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Feature request: Exponential search in git bisect
  2020-10-10  9:22 ` Christian Couder
@ 2020-10-10  9:46   ` Christian Couder
  2020-10-25 17:15   ` Philip Oakley
  1 sibling, 0 replies; 10+ messages in thread
From: Christian Couder @ 2020-10-10  9:46 UTC (permalink / raw)
  To: Manuel Bärenz; +Cc: git

On Sat, Oct 10, 2020 at 11:22 AM Christian Couder
<christian.couder@gmail.com> wrote:
>
> On Fri, Oct 9, 2020 at 9:35 PM Manuel Bärenz <manuel@enigmage.de> wrote:

> > That's problematic because then I might accidentally mark a
> > commit as good that was already untestable bad. Given that bisect has no
> > undo functionality, that can quickly mess up my search. Distinguishing
> > untestable good from untestable bad is really hard automatically. I
> > shouldn't have to do that.
>
> Sometimes it's not very difficult to test if the feature has been
> implemented yet or not. For example with Git you could check if an
> option for a command has been implemented by just checking if it
> appears in the doc. So the bisection script could first check that and
> exit 0 (which means good) if the option doesn't appear in the doc. If
> it appears in the doc, then it could do the regular test: "skip" if
> untestable, "good" if there is no bug, "bad" otherwise.

Also note that it might actually be simpler in many cases to find when
a feature has been implemented by different means than some checks in
the script.

For example in the above example to find using the documentation when
an option for a Git command has been implemented, one could use:

git log --reverse  -S'--option' Documentation/git-command.txt

It could also work by using `git log --reverse  -S...` to find when
some functions or variables related to the feature appeared in the
code itself.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Feature request: Exponential search in git bisect
  2020-10-10  9:22 ` Christian Couder
  2020-10-10  9:46   ` Christian Couder
@ 2020-10-25 17:15   ` Philip Oakley
  2020-10-26 18:13     ` Junio C Hamano
  2020-11-01 20:17     ` Manuel Bärenz
  1 sibling, 2 replies; 10+ messages in thread
From: Philip Oakley @ 2020-10-25 17:15 UTC (permalink / raw)
  To: Christian Couder, Manuel Bärenz; +Cc: git

Hi Manuel,

On 10/10/2020 10:22, Christian Couder wrote:
> On Fri, Oct 9, 2020 at 9:35 PM Manuel Bärenz <manuel@enigmage.de> wrote:
>> This feature was requested 8 years ago and briefly discussed:
>> https://public-inbox.org/git/20120318212957.GS1219@chaosreigns.com/
>>
>>
>>   TL;DR
>>
>> Before doing git bisect, I want to use exponential search to
>> automatically find a good commit, in logarithmic time.
> Ok, but the conclusion of the above discussion is that the problem
> with this idea is being able to distinguish between a commit that is
> bad and a commit where the feature that you want to test cannot be
> tested for example because it hasn't been implemented yet.

Does any of the proposed improvement in the "bisect: loosen halfway()
check for a large number of commits"
https://lore.kernel.org/git/20201022103806.26680-1-szeder.dev@gmail.com/
assist in this.

Or is the problem still with the need for a three way test that shows
Too_Old / Good / Bad ?

>
>>   Scenario
>>
>>   * I have a bug in HEAD.
>>   * I strongly suspect that it was introduced some time ago, but I don't
>>     know when exactly.
>>   * I have an automated test that will find the bug if the test can run
>>     properly.
>>   * Most of the commits in the repository are not testable, i.e. the
>>     test doesn't run properly. (E.g. because the feature it tests wasn't
>>     introduced yet, refactoring, etc.)
>>   * I have no idea what a good commit might be, because I don't know
>>     when the first /testable/ good commit is.
> Ok, so your test cannot distinguish between a bad commit and a commit
> where the feature hasn't been implemented.
>
> When you say that most of the commits in the repository are not
> testable, it usually means that the feature has been implemented
> relatively recently compared to the history of the project.
>
>> This sounds like a standard application for git bisect. No matter how
>> big the repo, with binary search we would expect to find the first bad
>> commit in logarithmic time.
> Not necessarily. If you cannot distinguish in any way between a bad
> commit and a commit where the feature hasn't been implemented, then it
> might be very difficult to find a good commit. And you need a good
> commit before you can properly bisect.
>
> Suppose for example that the first bad commit (the commit that
> introduced the bug you are looking for) is a direct child of the
> commit that introduced the feature. Then unless you are very lucky any
> binary search using your script will only test either bad commits or
> commits where the feature hasn't been implemented yet, and it is
> unable to distinguish between them in your scenario.
>
>>   Failed attempt
>>
>> The zeroth idea might be trying to find the good commit by hand, by
>> reading changelogs, by trying some commits, whatever. In some
>> situations, this is not feasible. In fact, such situations occur
>> frequently for me, for example for undocumented features, unversioned
>> rolling releases, incidental complexity leading to older commits not
>> being testable, etc.
> I understand that it's not always easy to find a good commit.
>
>> The first idea that comes to mind - and it was recommended 8 years agos,
>> and I've tried it a few times already - is to simply mark the root
>> commit as good. (Now, there might be several roots, but that's a puzzle
>> you typically only have to figure out once per repo.) This sounds great
>> in theory because binary search should get through the good old commits
>> in logarithmic time.
> It is not necessarily a good idea to do that. And in the thread, what
> was actually suggested by Peff (Jeff King) was to test released
> versions (for example 1.6.0, 1.7.0, etc in the Git code base).
>
>> The problem with this approach is that if most older commits are
>> untestable, I have to git bisect skip them.
> Skipping untestable commits is not always the right thing to do. If
> you know that they are untestable because the feature has not been
> implemented yet, it might be better to mark them as good instead.
>
> This is by the way what an ideal script should do. It should mark
> commits where the feature has not been implemented yet as good, and
> should "skip" only the commits where the feature has already been
> implemented but that are not testable for another reason, like another
> "temporary" bug or "temporary" compile failures.
>
>> This basically kills the
>> logarithmic performance, because bisect skip doesn't do binary search,
>> but something rather random.
> I would say that the actual reason here is that bisect skip is used
> when it shouldn't be used.
>
>> Just yesterday I killed a bisect search
>> that took hours because it kept skipping and didn't find actual good
>> commits.
> Ok.
>
>> You might say that instead of skipping old commits, one should mark them
>> as good.
> Yes, they should be marked as good when the feature has not been
> implemented yet.
>
>> That's problematic because then I might accidentally mark a
>> commit as good that was already untestable bad. Given that bisect has no
>> undo functionality, that can quickly mess up my search. Distinguishing
>> untestable good from untestable bad is really hard automatically. I
>> shouldn't have to do that.
> Sometimes it's not very difficult to test if the feature has been
> implemented yet or not. For example with Git you could check if an
> option for a command has been implemented by just checking if it
> appears in the doc. So the bisection script could first check that and
> exit 0 (which means good) if the option doesn't appear in the doc. If
> it appears in the doc, then it could do the regular test: "skip" if
> untestable, "good" if there is no bug, "bad" otherwise.
>
>> Long story short: Going from the root commit typically isn't feasible.
>> I've tried it.
> It seems that you might not have tried in the best possible way.
>
>>   Proposal: Exponential search
>>
>> Instead of going from the root commit, what I do manually before
>> starting git bisect is this:
>>
>> git checkout HEAD~10
>> ./test.sh # Says: "Bug is present"
>> git checkout HEAD~20
>> ./test.sh # Says: "Bug is still present"
>> git checkout HEAD~40
>> ./test.sh # Says: "Bug is still present"
>> [...] # Proceed exponentially
>> git checkout HEAD~640
>> ./test.sh # Says: "Bug is GONE!"
>> git bisect good
> If your script cannot distinguish between a bad commit and a commit
> where the feature hasn't been implemented, then you were lucky that
> that HEAD~640 was good. If the feature had been introduced between
> HEAD~639 and HEAD~321 then your script would have said  "Bug is still
> present".
>
>> This technique is known as
>> https://en.wikipedia.org/wiki/Exponential_search, and it works very well
>> in practice. I find a good commit long before I enter the "untestable
>> good" region.
> If you are lucky, yes, you find a good commit long before you enter
> the "untestable good" region.
>
>> But it's tedious to do this manually. In this example, I
>> needed to run the script 8 times manually, but of course it can be more
>> often, and compiling and running the test may take time. This is ok for
>> a one-off search, but it's tedious for regular usages.
>>
>> Yes, I could wrap this up in a shell script, but I guess there are
>> caveats that I didn't think of when the history isn't linear. Maybe
>> someone even already has, and I'm unaware of that. But it feels like
>> this could be a proper git bisect feature, and a very useful one.
> I agree that it could be useful. It could be especially useful if
> people have a script that can actually distinguish between a bad
> commit and a commit where the feature hasn't been implemented.
>
> So if someone plans to implement that in Git, particular care in the
> documentation should be taken to explain the issues related to testing
> a feature when it might not be implemented yet.


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Feature request: Exponential search in git bisect
  2020-10-25 17:15   ` Philip Oakley
@ 2020-10-26 18:13     ` Junio C Hamano
  2020-10-26 20:59       ` Philip Oakley
  2020-11-01 20:17     ` Manuel Bärenz
  1 sibling, 1 reply; 10+ messages in thread
From: Junio C Hamano @ 2020-10-26 18:13 UTC (permalink / raw)
  To: Philip Oakley; +Cc: Christian Couder, Manuel Bärenz, git

Philip Oakley <philipoakley@iee.email> writes:

>> Ok, but the conclusion of the above discussion is that the problem
>> with this idea is being able to distinguish between a commit that is
>> bad and a commit where the feature that you want to test cannot be
>> tested for example because it hasn't been implemented yet.
>
> Does any of the proposed improvement in the "bisect: loosen halfway()
> check for a large number of commits"
> https://lore.kernel.org/git/20201022103806.26680-1-szeder.dev@gmail.com/
> assist in this.

I doubt it.  

If you cannot say if a rev is testable or not, it would not help you
much if Git asked "is this good, bad or untestable?" question 5
times faster, I suspect.



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Feature request: Exponential search in git bisect
  2020-10-26 18:13     ` Junio C Hamano
@ 2020-10-26 20:59       ` Philip Oakley
  0 siblings, 0 replies; 10+ messages in thread
From: Philip Oakley @ 2020-10-26 20:59 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Christian Couder, Manuel Bärenz, git

Hi Junio,

On 26/10/2020 18:13, Junio C Hamano wrote:
> Philip Oakley <philipoakley@iee.email> writes:
>
>>> Ok, but the conclusion of the above discussion is that the problem
>>> with this idea is being able to distinguish between a commit that is
>>> bad and a commit where the feature that you want to test cannot be
>>> tested for example because it hasn't been implemented yet.
>> Does any of the proposed improvement in the "bisect: loosen halfway()
>> check for a large number of commits"
>> https://lore.kernel.org/git/20201022103806.26680-1-szeder.dev@gmail.com/
>> assist in this.
> I doubt it.  
>
> If you cannot say if a rev is testable or not, it would not help you
> much if Git asked "is this good, bad or untestable?" question 5
> times faster, I suspect.
>
>
I was more thinking along the lines, perhaps, of a narrower condition of
"Too_Old" for such historical commits, rather than saying some
mid-history commit just happens to be untestable in a rather
indeterminate way. That would be distinctly different from the generic
'untestable' condition where a commit needs to be skipped.

It would still need an update to bisect to exponentially find the
suitable start (good) zone for the real bisection.

Clearly if the user badly codes that 'Too_Old' test then they will get
pulled up short, but that would be expected.

The other point was to highlight to Manuel that if the halfway check
improvement was relevant to his situation then it may be that simply
saying those 'Too_Old' commits were 'good' anyway, and just use the
improvement it gave.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Feature request: Exponential search in git bisect
  2020-10-09 12:56 Feature request: Exponential search in git bisect Manuel Bärenz
  2020-10-10  9:22 ` Christian Couder
@ 2020-10-27 12:10 ` Ævar Arnfjörð Bjarmason
  2020-11-01 20:30   ` Manuel Bärenz
  1 sibling, 1 reply; 10+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2020-10-27 12:10 UTC (permalink / raw)
  To: Manuel Bärenz; +Cc: git, Han-Wen Nienhuys


On Fri, Oct 09 2020, Manuel Bärenz wrote:

> This feature was requested 8 years ago and briefly discussed:
> https://public-inbox.org/git/20120318212957.GS1219@chaosreigns.com/
>
>
>   TL;DR
>
> Before doing git bisect, I want to use exponential search to
> automatically find a good commit, in logarithmic time.
>
>
>   Scenario
>
>   * I have a bug in HEAD.
>   * I strongly suspect that it was introduced some time ago, but I don't
>     know when exactly.
>   * I have an automated test that will find the bug if the test can run
>     properly.
>   * Most of the commits in the repository are not testable, i.e. the
>     test doesn't run properly. (E.g. because the feature it tests wasn't
>     introduced yet, refactoring, etc.)
>   * I have no idea what a good commit might be, because I don't know
>     when the first /testable/ good commit is.
>
> This sounds like a standard application for git bisect. No matter how
> big the repo, with binary search we would expect to find the first bad
> commit in logarithmic time.
>
>
>   Failed attempt
>
> The zeroth idea might be trying to find the good commit by hand, by
> reading changelogs, by trying some commits, whatever. In some
> situations, this is not feasible. In fact, such situations occur
> frequently for me, for example for undocumented features, unversioned
> rolling releases, incidental complexity leading to older commits not
> being testable, etc.
>
> The first idea that comes to mind - and it was recommended 8 years agos,
> and I've tried it a few times already - is to simply mark the root
> commit as good. (Now, there might be several roots, but that's a puzzle
> you typically only have to figure out once per repo.) This sounds great
> in theory because binary search should get through the good old commits
> in logarithmic time.
>
> The problem with this approach is that if most older commits are
> untestable, I have to git bisect skip them. This basically kills the
> logarithmic performance, because bisect skip doesn't do binary search,
> but something rather random. Just yesterday I killed a bisect search
> that took hours because it kept skipping and didn't find actual good
> commits.
>
> You might say that instead of skipping old commits, one should mark them
> as good. That's problematic because then I might accidentally mark a
> commit as good that was already untestable bad. Given that bisect has no
> undo functionality, that can quickly mess up my search. Distinguishing
> untestable good from untestable bad is really hard automatically. I
> shouldn't have to do that.
>
> Long story short: Going from the root commit typically isn't feasible.
> I've tried it.
>
>
>   Proposal: Exponential search
>
> Instead of going from the root commit, what I do manually before
> starting git bisect is this:
>
> git checkout HEAD~10
> ./test.sh # Says: "Bug is present"
> git checkout HEAD~20
> ./test.sh # Says: "Bug is still present"
> git checkout HEAD~40
> ./test.sh # Says: "Bug is still present"
> [...] # Proceed exponentially
> git checkout HEAD~640
> ./test.sh # Says: "Bug is GONE!"
> git bisect good
>
> This technique is known as
> https://en.wikipedia.org/wiki/Exponential_search, and it works very well
> in practice. I find a good commit long before I enter the "untestable
> good" region. But it's tedious to do this manually. In this example, I
> needed to run the script 8 times manually, but of course it can be more
> often, and compiling and running the test may take time. This is ok for
> a one-off search, but it's tedious for regular usages.
>
> Yes, I could wrap this up in a shell script, but I guess there are
> caveats that I didn't think of when the history isn't linear. Maybe
> someone even already has, and I'm unaware of that. But it feels like
> this could be a proper git bisect feature, and a very useful one.

Let's suppose we have a repository with 700 linear commits:
    
    set -e
    
    cd /tmp
    rm -rf /tmp/test-repo
    mkdir /tmp/test-repo
    cd /tmp/test-repo
    git init
    
    for i in $(seq 1 700)
    do
        touch $i
        git add $i
        git commit -m"add $i"
    done

Then let's bisect it from the root:
    
    git bisect start HEAD HEAD~699

And let's further suppose that the feature wasn't introduced until
commit 650, and it's broken since 653.

With the bisect method of finding this we're going to start our session
with these commits:

    [bef4b96adf5f082b1103c170eb6a41d1526fa919] add 350 => good
    [d271fdb29129dbba723d3eac1035b58b6dc6f583] add 525 => good
    [b0c9b7da646334a6c7eadb333b5ae77ec35388b3] add 612 => good
    [2a78858d04cc5542e176dd8f68fa2660b8b48ab3] add 656 => bad (or skip)

Whereas with this proposed exponential search it'll be commits #:
    
    2
    4
    8
    16
    32
    64
    128
    256
    512

So we're going to test 8 commits before we get past the mid-point that
bisect now starts with. Of course that may be more efficient, but if the
regression is in some random place I don't see why we wouldn't test the
middle instead of weighing things towards the start of the history. If
the relevant commit is an early one like #50 bisect is still faster.

With the disclaimer that I may be missing something, I'm just plowing
ahead:

I don't see the usefulness of this proposed exponential search, but I
definitely *do* see the usefulness of a "bisect undo", and I've been
bitten many times by the lack of that myself. We should definitely have
that.

And as Christian pointed out in [1] it seems you're (understandably, it
can be confusing) conflating skip and "good" in your example. So to
re-state the problem you have, if I were to use "skip" in the above
example bisect for me will do:
    
    [bef4b96adf5f082b1103c170eb6a41d1526fa919] add 350 => skip
    [15c181442dcbd785bf2b28cfddcf1932aaa71c42] add 351 => skip
    [c985feffa2c92b2d3d9804a43b215e26c7275549] add 374 => skip
    [effa691ec5dc2d80c0b070c4d8ac9fa70cbfea9f] add 168 => skip
    [212a5ee3ff55834196661d0632f584098e9f6fb2] add 369 => skip
    [2ca67a4500c9f3cd489b9e529d41eb99252dc8f6] add 179 => skip
    [4067ee067e2298e1b104f4ff9aab15f4e815e101] add 337 => skip
    [...]

If only there was a way to be on 350 and say "skip everything up to this
point", so I implemented it!:

    [bef4b96adf5f082b1103c170eb6a41d1526fa919] add 350 => skip-to-here
    [15c181442dcbd785bf2b28cfddcf1932aaa71c42] add 351 => skip
    [6f7b5ca1dcc21c28af658a172136e503d7a2d0ea] add 420
    [...]

etc. now we're not jumping around in 1..350:

    $ git config alias.bisect-skip-to-here
    !f() { good=$(git for-each-ref refs/bisect/good* --format="%(objectname)"); git bisect skip $good..HEAD; }; f

Great eh? Not really, because I've just discovered a really tedious and
expensive (I created 350 "skip" refs) of re-inventing what I could do if
I just did "good" on commit 350[2] :)

I started looking at how to implement "git bisect undo", it should be
possible by either winding .git/refs/bisect based on the BISECT_LOG, or
simply keeping copies of .git/refs/bisect (and adjusting HEAD), but then
I wondered if this was something we could get for free with the ref
transactions in the reftable. CC'd Han-Wen in case he's got feedback,
maybe it's trivial, or maybe I'm imagining things.

1. https://lore.kernel.org/git/CAP8UFD2dWrUXJUuiFtgCu6krbnbGGqJZ7K+6KqstGTcNmSc_xQ@mail.gmail.com/
2. Not exactly, because in skip-to-here on 350 we'll next test 351, but
   "good" will test 525, but as argued earlier that's a feature.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Feature request: Exponential search in git bisect
  2020-10-25 17:15   ` Philip Oakley
  2020-10-26 18:13     ` Junio C Hamano
@ 2020-11-01 20:17     ` Manuel Bärenz
  1 sibling, 0 replies; 10+ messages in thread
From: Manuel Bärenz @ 2020-11-01 20:17 UTC (permalink / raw)
  To: Philip Oakley, Christian Couder; +Cc: git


On 25.10.20 18:15, Philip Oakley wrote:
> Hi Manuel,
>
> On 10/10/2020 10:22, Christian Couder wrote:
>> On Fri, Oct 9, 2020 at 9:35 PM Manuel Bärenz <manuel@enigmage.de> wrote:
>>> This feature was requested 8 years ago and briefly discussed:
>>> https://public-inbox.org/git/20120318212957.GS1219@chaosreigns.com/
>>>
>>>
>>>   TL;DR
>>>
>>> Before doing git bisect, I want to use exponential search to
>>> automatically find a good commit, in logarithmic time.
>> Ok, but the conclusion of the above discussion is that the problem
>> with this idea is being able to distinguish between a commit that is
>> bad and a commit where the feature that you want to test cannot be
>> tested for example because it hasn't been implemented yet.
> Does any of the proposed improvement in the "bisect: loosen halfway()
> check for a large number of commits"
> https://lore.kernel.org/git/20201022103806.26680-1-szeder.dev@gmail.com/
> assist in this.

I'm afraid not, since the time to recompile and run tests usually
dominates the search (at least in my case). So the fewer commits have to
be tested, the better.

The hard part is distinguishing in an automated way on an old commit
when to skip and when to mark as good.



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Feature request: Exponential search in git bisect
  2020-10-27 12:10 ` Ævar Arnfjörð Bjarmason
@ 2020-11-01 20:30   ` Manuel Bärenz
  2020-11-02 10:36     ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 10+ messages in thread
From: Manuel Bärenz @ 2020-11-01 20:30 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason; +Cc: git, Han-Wen Nienhuys


> Let's suppose we have a repository with 700 linear commits:
>     
>     set -e
>     
>     cd /tmp
>     rm -rf /tmp/test-repo
>     mkdir /tmp/test-repo
>     cd /tmp/test-repo
>     git init
>     
>     for i in $(seq 1 700)
>     do
>         touch $i
>         git add $i
>         git commit -m"add $i"
>     done
>
> Then let's bisect it from the root:
>     
>     git bisect start HEAD HEAD~699
>
> And let's further suppose that the feature wasn't introduced until
> commit 650, and it's broken since 653.
>
> With the bisect method of finding this we're going to start our session
> with these commits:
>
>     [bef4b96adf5f082b1103c170eb6a41d1526fa919] add 350 => good
>     [d271fdb29129dbba723d3eac1035b58b6dc6f583] add 525 => good
>     [b0c9b7da646334a6c7eadb333b5ae77ec35388b3] add 612 => good
>     [2a78858d04cc5542e176dd8f68fa2660b8b48ab3] add 656 => bad (or skip)
>
> Whereas with this proposed exponential search it'll be commits #:
>     
>     2
>     4
>     8
>     16
>     32
>     64
>     128
>     256
>     512
>
> So we're going to test 8 commits before we get past the mid-point that
> bisect now starts with. Of course that may be more efficient, but if the
> regression is in some random place I don't see why we wouldn't test the
> middle instead of weighing things towards the start of the history. If
> the relevant commit is an early one like #50 bisect is still faster.
That's true. If the feature was broken n commits ago, and exponential
search has to be useful, then the feature must not have been introduced
later than a*n commits ago, where a is the exponent.
> With the disclaimer that I may be missing something, I'm just plowing
> ahead:
>
> I don't see the usefulness of this proposed exponential search, but I
> definitely *do* see the usefulness of a "bisect undo", and I've been
> bitten many times by the lack of that myself. We should definitely have
> that.
What exactly would you undo? If an older commit has been marked as
"bad", should e.g. a later "good" marker undo the earlier "bad" marker?
I don't know how this helps because the bigger problem is that if I tend
to mark old commits as good rather than skip, I will more likely
accidentally mark a bad commit as good, and I don't see how I could undo
that.
> And as Christian pointed out in [1] it seems you're (understandably, it
> can be confusing) conflating skip and "good" in your example.
Yes, knowingly, unfortunately. Or to put it more precise: I cannot write
a script that is good enough to really detect a good old commit reliably.
>  So to
> re-state the problem you have, if I were to use "skip" in the above
> example bisect for me will do:
>     
>     [bef4b96adf5f082b1103c170eb6a41d1526fa919] add 350 => skip
>     [15c181442dcbd785bf2b28cfddcf1932aaa71c42] add 351 => skip
>     [c985feffa2c92b2d3d9804a43b215e26c7275549] add 374 => skip
>     [effa691ec5dc2d80c0b070c4d8ac9fa70cbfea9f] add 168 => skip
>     [212a5ee3ff55834196661d0632f584098e9f6fb2] add 369 => skip
>     [2ca67a4500c9f3cd489b9e529d41eb99252dc8f6] add 179 => skip
>     [4067ee067e2298e1b104f4ff9aab15f4e815e101] add 337 => skip
>     [...]
>
> If only there was a way to be on 350 and say "skip everything up to this
> point", so I implemented it!:
>
>     [bef4b96adf5f082b1103c170eb6a41d1526fa919] add 350 => skip-to-here
>     [15c181442dcbd785bf2b28cfddcf1932aaa71c42] add 351 => skip
>     [6f7b5ca1dcc21c28af658a172136e503d7a2d0ea] add 420
>     [...]
>
> etc. now we're not jumping around in 1..350:
>
>     $ git config alias.bisect-skip-to-here
>     !f() { good=$(git for-each-ref refs/bisect/good* --format="%(objectname)"); git bisect skip $good..HEAD; }; f
>
> Great eh? Not really, because I've just discovered a really tedious and
> expensive (I created 350 "skip" refs) of re-inventing what I could do if
> I just did "good" on commit 350[2] :)

I'm not entirely sure whether I understand yet. If you mark a whole
range of commits untestable, then yes. But how do you know whether the
whole range can be skipped? Maybe the feature was really introduced in
300, 320 broke it and 350 just broke it because of a typo?

Maybe another possible, simpler feature would be a different
(configurable?) skip algorithm. It could move exponentially as well.
(I'm not sure how useful that would be, though.)


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Feature request: Exponential search in git bisect
  2020-11-01 20:30   ` Manuel Bärenz
@ 2020-11-02 10:36     ` Ævar Arnfjörð Bjarmason
  0 siblings, 0 replies; 10+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2020-11-02 10:36 UTC (permalink / raw)
  To: Manuel Bärenz; +Cc: git, Han-Wen Nienhuys


On Sun, Nov 01 2020, Manuel Bärenz wrote:

>> Let's suppose we have a repository with 700 linear commits:
>>     
>>     set -e
>>     
>>     cd /tmp
>>     rm -rf /tmp/test-repo
>>     mkdir /tmp/test-repo
>>     cd /tmp/test-repo
>>     git init
>>     
>>     for i in $(seq 1 700)
>>     do
>>         touch $i
>>         git add $i
>>         git commit -m"add $i"
>>     done
>>
>> Then let's bisect it from the root:
>>     
>>     git bisect start HEAD HEAD~699
>>
>> And let's further suppose that the feature wasn't introduced until
>> commit 650, and it's broken since 653.
>>
>> With the bisect method of finding this we're going to start our session
>> with these commits:
>>
>>     [bef4b96adf5f082b1103c170eb6a41d1526fa919] add 350 => good
>>     [d271fdb29129dbba723d3eac1035b58b6dc6f583] add 525 => good
>>     [b0c9b7da646334a6c7eadb333b5ae77ec35388b3] add 612 => good
>>     [2a78858d04cc5542e176dd8f68fa2660b8b48ab3] add 656 => bad (or skip)
>>
>> Whereas with this proposed exponential search it'll be commits #:
>>     
>>     2
>>     4
>>     8
>>     16
>>     32
>>     64
>>     128
>>     256
>>     512
>>
>> So we're going to test 8 commits before we get past the mid-point that
>> bisect now starts with. Of course that may be more efficient, but if the
>> regression is in some random place I don't see why we wouldn't test the
>> middle instead of weighing things towards the start of the history. If
>> the relevant commit is an early one like #50 bisect is still faster.
> That's true. If the feature was broken n commits ago, and exponential
> search has to be useful, then the feature must not have been introduced
> later than a*n commits ago, where a is the exponent.

Right, but it seems like a rather trivial saving in even those cases
compared to binary search, and I'd think in practice any such gains
would be outweighted by the practical trade-off that in real
repositories older commits tend to be harder to test/build
(e.g. requiring old library versions or compilers).

>> With the disclaimer that I may be missing something, I'm just plowing
>> ahead:
>>
>> I don't see the usefulness of this proposed exponential search, but I
>> definitely *do* see the usefulness of a "bisect undo", and I've been
>> bitten many times by the lack of that myself. We should definitely have
>> that.
> What exactly would you undo? If an older commit has been marked as
> "bad", should e.g. a later "good" marker undo the earlier "bad" marker?
> I don't know how this helps because the bigger problem is that if I tend
> to mark old commits as good rather than skip, I will more likely
> accidentally mark a bad commit as good, and I don't see how I could undo
> that.

Sometimes I mistype "good" or "bad" (usually via some shell history
accident) and have to manually recover from seeing where I'd narrowed
down before with "log", so just an "undo" that allows you to revert the
previous step(s) would be useful.

>> And as Christian pointed out in [1] it seems you're (understandably, it
>> can be confusing) conflating skip and "good" in your example.
> Yes, knowingly, unfortunately. Or to put it more precise: I cannot write
> a script that is good enough to really detect a good old commit reliably.
>>  So to
>> re-state the problem you have, if I were to use "skip" in the above
>> example bisect for me will do:
>>     
>>     [bef4b96adf5f082b1103c170eb6a41d1526fa919] add 350 => skip
>>     [15c181442dcbd785bf2b28cfddcf1932aaa71c42] add 351 => skip
>>     [c985feffa2c92b2d3d9804a43b215e26c7275549] add 374 => skip
>>     [effa691ec5dc2d80c0b070c4d8ac9fa70cbfea9f] add 168 => skip
>>     [212a5ee3ff55834196661d0632f584098e9f6fb2] add 369 => skip
>>     [2ca67a4500c9f3cd489b9e529d41eb99252dc8f6] add 179 => skip
>>     [4067ee067e2298e1b104f4ff9aab15f4e815e101] add 337 => skip
>>     [...]
>>
>> If only there was a way to be on 350 and say "skip everything up to this
>> point", so I implemented it!:
>>
>>     [bef4b96adf5f082b1103c170eb6a41d1526fa919] add 350 => skip-to-here
>>     [15c181442dcbd785bf2b28cfddcf1932aaa71c42] add 351 => skip
>>     [6f7b5ca1dcc21c28af658a172136e503d7a2d0ea] add 420
>>     [...]
>>
>> etc. now we're not jumping around in 1..350:
>>
>>     $ git config alias.bisect-skip-to-here
>>     !f() { good=$(git for-each-ref refs/bisect/good* --format="%(objectname)"); git bisect skip $good..HEAD; }; f
>>
>> Great eh? Not really, because I've just discovered a really tedious and
>> expensive (I created 350 "skip" refs) of re-inventing what I could do if
>> I just did "good" on commit 350[2] :)
>
> I'm not entirely sure whether I understand yet. If you mark a whole
> range of commits untestable, then yes. But how do you know whether the
> whole range can be skipped? Maybe the feature was really introduced in
> 300, 320 broke it and 350 just broke it because of a typo?

I don't, but whatever problems I have with mislabeling 1..350 you'll
also have in mislabeling 16..32, 32..64 etc.

> Maybe another possible, simpler feature would be a different
> (configurable?) skip algorithm. It could move exponentially as well.
> (I'm not sure how useful that would be, though.)

I think the feature that would solve the problem you described in your
original E-Mail would be some sort of verification mode for
bisect. I.e. after we find that commit 656 is the bad one we could
optionally continue testing. If we then found that something was "good"
on the side that should be "bad" or the other way around we'd walk back
in some "undo on steroids" mode to figure out where the mislabeling
happened.

But I don't see how the problem you described would be solved by
changing how we do the commit walk during bisect. We could binary
search, we could walk exponentially, we could test in chunks of 100
etc. All of those approaches would go equally wrong if we mislabel good
as bad or bad as good.


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2020-11-02 10:36 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-09 12:56 Feature request: Exponential search in git bisect Manuel Bärenz
2020-10-10  9:22 ` Christian Couder
2020-10-10  9:46   ` Christian Couder
2020-10-25 17:15   ` Philip Oakley
2020-10-26 18:13     ` Junio C Hamano
2020-10-26 20:59       ` Philip Oakley
2020-11-01 20:17     ` Manuel Bärenz
2020-10-27 12:10 ` Ævar Arnfjörð Bjarmason
2020-11-01 20:30   ` Manuel Bärenz
2020-11-02 10:36     ` Ævar Arnfjörð Bjarmason

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.