2 <html xmlns=
"http://www.w3.org/1999/xhtml" lang=
"en">
4 <meta charset=
"UTF-8"/>
5 <meta http-equiv=
"X-UA-Compatible" content=
"IE=edge"/>
6 <meta name=
"viewport" content=
"width=device-width, initial-scale=1.0"/>
7 <meta name=
"generator" content=
"Asciidoctor 2.0.20"/>
8 <meta name=
"author" content=
"Christian Couder"/>
9 <title>Fighting regressions with git bisect
</title>
10 <link rel=
"stylesheet" href=
"https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700"/>
12 /*! Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */
13 /* Uncomment the following line when using as a custom stylesheet */
14 /* @import
"https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700"; */
15 html{font-family:sans-serif;-webkit-text-size-adjust:
100%}
17 a:focus{outline:thin dotted}
18 a:active,a:hover{outline:
0}
19 h1{font-size:
2em;margin:
.67em
0}
20 b,strong{font-weight:bold}
22 abbr[title]{cursor:help;border-bottom:
1px dotted #dddddf;text-decoration:none}
23 dfn{font-style:italic}
25 mark{background:#ff0;color:#
000}
26 code,kbd,pre,samp{font-family:monospace;font-size:
1em}
27 pre{white-space:pre-wrap}
28 q{quotes:
"\201C" "\201D" "\2018" "\2019"}
30 sub,sup{font-size:
75%;line-height:
0;position:relative;vertical-align:baseline}
34 svg:not(:root){overflow:hidden}
36 audio,video{display:inline-block}
37 audio:not([controls]){display:none;height:
0}
38 fieldset{border:
1px solid silver;margin:
0 2px;padding:
.35em
.625em
.75em}
39 legend{border:
0;padding:
0}
40 button,input,select,textarea{font-family:inherit;font-size:
100%;margin:
0}
41 button,input{line-height:normal}
42 button,select{text-transform:none}
43 button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}
44 button[disabled],html input[disabled]{cursor:default}
45 input[type=checkbox],input[type=radio]{padding:
0}
46 button::-moz-focus-inner,input::-moz-focus-inner{border:
0;padding:
0}
47 textarea{overflow:auto;vertical-align:top}
48 table{border-collapse:collapse;border-spacing:
0}
49 *,::before,::after{box-sizing:border-box}
50 html,body{font-size:
100%}
51 body{background:#fff;color:rgba(
0,
0,
0,
.8);padding:
0;margin:
0;font-family:
"Noto Serif",
"DejaVu Serif",serif;line-height:
1;position:relative;cursor:auto;-moz-tab-size:
4;-o-tab-size:
4;tab-size:
4;word-wrap:anywhere;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}
52 a:hover{cursor:pointer}
53 img,object,embed{max-width:
100%;height:auto}
54 object,embed{height:
100%}
55 img{-ms-interpolation-mode:bicubic}
56 .left{float:left!important}
57 .right{float:right!important}
58 .text-left{text-align:left!important}
59 .text-right{text-align:right!important}
60 .text-center{text-align:center!important}
61 .text-justify{text-align:justify!important}
63 img,object,svg{display:inline-block;vertical-align:middle}
64 textarea{height:auto;min-height:
50px}
66 .subheader,.admonitionblock td.content
>.title,.audioblock
>.title,.exampleblock
>.title,.imageblock
>.title,.listingblock
>.title,.literalblock
>.title,.stemblock
>.title,.openblock
>.title,.paragraph
>.title,.quoteblock
>.title,table.tableblock
>.title,.verseblock
>.title,.videoblock
>.title,.dlist
>.title,.olist
>.title,.ulist
>.title,.qlist
>.title,.hdlist
>.title{line-height:
1.45;color:#
7a2518;font-weight:
400;margin-top:
0;margin-bottom:
.25em}
67 div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock
>.content
>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:
0;padding:
0}
68 a{color:#
2156a5;text-decoration:underline;line-height:inherit}
69 a:hover,a:focus{color:#
1d4b8f}
71 p{line-height:
1.6;margin-bottom:
1.25em;text-rendering:optimizeLegibility}
72 p aside{font-size:
.875em;line-height:
1.35;font-style:italic}
73 h1,h2,h3,#toctitle,.sidebarblock
>.content
>.title,h4,h5,h6{font-family:
"Open Sans",
"DejaVu Sans",sans-serif;font-weight:
300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:
1em;margin-bottom:
.5em;line-height:
1.0125em}
74 h1 small,h2 small,h3 small,#toctitle small,.sidebarblock
>.content
>.title small,h4 small,h5 small,h6 small{font-size:
60%;color:#e99b8f;line-height:
0}
76 h2{font-size:
1.6875em}
77 h3,#toctitle,.sidebarblock
>.content
>.title{font-size:
1.375em}
78 h4,h5{font-size:
1.125em}
80 hr{border:solid #dddddf;border-width:
1px
0 0;clear:both;margin:
1.25em
0 1.1875em}
81 em,i{font-style:italic;line-height:inherit}
82 strong,b{font-weight:bold;line-height:inherit}
83 small{font-size:
60%;line-height:inherit}
84 code{font-family:
"Droid Sans Mono",
"DejaVu Sans Mono",monospace;font-weight:
400;color:rgba(
0,
0,
0,
.9)}
85 ul,ol,dl{line-height:
1.6;margin-bottom:
1.25em;list-style-position:outside;font-family:inherit}
86 ul,ol{margin-left:
1.5em}
87 ul li ul,ul li ol{margin-left:
1.25em;margin-bottom:
0}
88 ul.circle{list-style-type:circle}
89 ul.disc{list-style-type:disc}
90 ul.square{list-style-type:square}
91 ul.circle ul:not([class]),ul.disc ul:not([class]),ul.square ul:not([class]){list-style:inherit}
92 ol li ul,ol li ol{margin-left:
1.25em;margin-bottom:
0}
93 dl dt{margin-bottom:
.3125em;font-weight:bold}
94 dl dd{margin-bottom:
1.25em}
95 blockquote{margin:
0 0 1.25em;padding:
.5625em
1.25em
0 1.1875em;border-left:
1px solid #ddd}
96 blockquote,blockquote p{line-height:
1.6;color:rgba(
0,
0,
0,
.85)}
97 @media screen and (min-width:
768px){h1,h2,h3,#toctitle,.sidebarblock
>.content
>.title,h4,h5,h6{line-height:
1.2}
99 h2{font-size:
2.3125em}
100 h3,#toctitle,.sidebarblock
>.content
>.title{font-size:
1.6875em}
101 h4{font-size:
1.4375em}}
102 table{background:#fff;margin-bottom:
1.25em;border:
1px solid #dedede;word-wrap:normal}
103 table thead,table tfoot{background:#f7f8f7}
104 table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:
.5em
.625em
.625em;font-size:inherit;color:rgba(
0,
0,
0,
.8);text-align:left}
105 table tr th,table tr td{padding:
.5625em
.625em;font-size:inherit;color:rgba(
0,
0,
0,
.8)}
106 table tr.even,table tr.alt{background:#f8f8f7}
107 table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{line-height:
1.6}
108 h1,h2,h3,#toctitle,.sidebarblock
>.content
>.title,h4,h5,h6{line-height:
1.2;word-spacing:-
.05em}
109 h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock
>.content
>.title strong,h4 strong,h5 strong,h6 strong{font-weight:
400}
110 .center{margin-left:auto;margin-right:auto}
112 .clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:
" ";display:table}
113 .clearfix::after,.float-group::after{clear:both}
114 :not(pre).nobreak{word-wrap:normal}
115 :not(pre).nowrap{white-space:nowrap}
116 :not(pre).pre-wrap{white-space:pre-wrap}
117 :not(pre):not([class^=L])
>code{font-size:
.9375em;font-style:normal!important;letter-spacing:
0;padding:
.1em
.5ex;word-spacing:-
.15em;background:#f7f7f8;border-radius:
4px;line-height:
1.45;text-rendering:optimizeSpeed}
118 pre{color:rgba(
0,
0,
0,
.9);font-family:
"Droid Sans Mono",
"DejaVu Sans Mono",monospace;line-height:
1.45;text-rendering:optimizeSpeed}
119 pre code,pre pre{color:inherit;font-size:inherit;line-height:inherit}
120 pre
>code{display:block}
121 pre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal}
122 em em{font-style:normal}
123 strong strong{font-weight:
400}
124 .keyseq{color:rgba(
51,
51,
51,
.8)}
125 kbd{font-family:
"Droid Sans Mono",
"DejaVu Sans Mono",monospace;display:inline-block;color:rgba(
0,
0,
0,
.8);font-size:
.65em;line-height:
1.45;background:#f7f7f7;border:
1px solid #ccc;border-radius:
3px;box-shadow:
0 1px
0 rgba(
0,
0,
0,
.2),inset
0 0 0 .1em #fff;margin:
0 .15em;padding:
.2em
.5em;vertical-align:middle;position:relative;top:-
.1em;white-space:nowrap}
126 .keyseq kbd:first-child{margin-left:
0}
127 .keyseq kbd:last-child{margin-right:
0}
128 .menuseq,.menuref{color:#
000}
129 .menuseq b:not(.caret),.menuref{font-weight:inherit}
130 .menuseq{word-spacing:-
.02em}
131 .menuseq b.caret{font-size:
1.25em;line-height:
.8}
132 .menuseq i.caret{font-weight:bold;text-align:center;width:
.45em}
133 b.button::before,b.button::after{position:relative;top:-
1px;font-weight:
400}
134 b.button::before{content:
"[";padding:
0 3px
0 2px}
135 b.button::after{content:
"]";padding:
0 2px
0 3px}
136 p a
>code:hover{color:rgba(
0,
0,
0,
.9)}
137 #header,#content,#footnotes,#footer{width:
100%;margin:
0 auto;max-width:
62.5em;*zoom:
1;position:relative;padding-left:
.9375em;padding-right:
.9375em}
138 #header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:
" ";display:table}
139 #header::after,#content::after,#footnotes::after,#footer::after{clear:both}
140 #content{margin-top:
1.25em}
141 #content::before{content:none}
142 #header
>h1:first-child{color:rgba(
0,
0,
0,
.85);margin-top:
2.25rem;margin-bottom:
0}
143 #header
>h1:first-child+#toc{margin-top:
8px;border-top:
1px solid #dddddf}
144 #header
>h1:only-child,body.toc2 #header
>h1:nth-last-child(
2){border-bottom:
1px solid #dddddf;padding-bottom:
8px}
145 #header .details{border-bottom:
1px solid #dddddf;line-height:
1.45;padding-top:
.25em;padding-bottom:
.25em;padding-left:
.25em;color:rgba(
0,
0,
0,
.6);display:flex;flex-flow:row wrap}
146 #header .details span:first-child{margin-left:-
.125em}
147 #header .details span.email a{color:rgba(
0,
0,
0,
.85)}
148 #header .details br{display:none}
149 #header .details br+span::before{content:
"\00a0\2013\00a0"}
150 #header .details br+span.author::before{content:
"\00a0\22c5\00a0";color:rgba(
0,
0,
0,
.85)}
151 #header .details br+span#revremark::before{content:
"\00a0|\00a0"}
152 #header #revnumber{text-transform:capitalize}
153 #header #revnumber::after{content:
"\00a0"}
154 #content
>h1:first-child:not([class]){color:rgba(
0,
0,
0,
.85);border-bottom:
1px solid #dddddf;padding-bottom:
8px;margin-top:
0;padding-top:
1rem;margin-bottom:
1.25rem}
155 #toc{border-bottom:
1px solid #e7e7e9;padding-bottom:
.5em}
156 #toc
>ul{margin-left:
.125em}
157 #toc ul.sectlevel0
>li
>a{font-style:italic}
158 #toc ul.sectlevel0 ul.sectlevel1{margin:
.5em
0}
159 #toc ul{font-family:
"Open Sans",
"DejaVu Sans",sans-serif;list-style-type:none}
160 #toc li{line-height:
1.3334;margin-top:
.3334em}
161 #toc a{text-decoration:none}
162 #toc a:active{text-decoration:underline}
163 #toctitle{color:#
7a2518;font-size:
1.2em}
164 @media screen and (min-width:
768px){#toctitle{font-size:
1.375em}
165 body.toc2{padding-left:
15em;padding-right:
0}
166 #toc.toc2{margin-top:
0!important;background:#f8f8f7;position:fixed;width:
15em;left:
0;top:
0;border-right:
1px solid #e7e7e9;border-top-width:
0!important;border-bottom-width:
0!important;z-index:
1000;padding:
1.25em
1em;height:
100%;overflow:auto}
167 #toc.toc2 #toctitle{margin-top:
0;margin-bottom:
.8rem;font-size:
1.2em}
168 #toc.toc2
>ul{font-size:
.9em;margin-bottom:
0}
169 #toc.toc2 ul ul{margin-left:
0;padding-left:
1em}
170 #toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:
0;margin-top:
.5em;margin-bottom:
.5em}
171 body.toc2.toc-right{padding-left:
0;padding-right:
15em}
172 body.toc2.toc-right #toc.toc2{border-right-width:
0;border-left:
1px solid #e7e7e9;left:auto;right:
0}}
173 @media screen and (min-width:
1280px){body.toc2{padding-left:
20em;padding-right:
0}
174 #toc.toc2{width:
20em}
175 #toc.toc2 #toctitle{font-size:
1.375em}
176 #toc.toc2
>ul{font-size:
.95em}
177 #toc.toc2 ul ul{padding-left:
1.25em}
178 body.toc2.toc-right{padding-left:
0;padding-right:
20em}}
179 #content #toc{border:
1px solid #e0e0dc;margin-bottom:
1.25em;padding:
1.25em;background:#f8f8f7;border-radius:
4px}
180 #content #toc
>:first-child{margin-top:
0}
181 #content #toc
>:last-child{margin-bottom:
0}
182 #footer{max-width:none;background:rgba(
0,
0,
0,
.8);padding:
1.25em}
183 #footer-text{color:hsla(
0,
0%,
100%,
.8);line-height:
1.44}
184 #content{margin-bottom:
.625em}
185 .sect1{padding-bottom:
.625em}
186 @media screen and (min-width:
768px){#content{margin-bottom:
1.25em}
187 .sect1{padding-bottom:
1.25em}}
188 .sect1:last-child{padding-bottom:
0}
189 .sect1+.sect1{border-top:
1px solid #e7e7e9}
190 #content h1
>a.anchor,h2
>a.anchor,h3
>a.anchor,#toctitle
>a.anchor,.sidebarblock
>.content
>.title
>a.anchor,h4
>a.anchor,h5
>a.anchor,h6
>a.anchor{position:absolute;z-index:
1001;width:
1.5ex;margin-left:-
1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:
400}
191 #content h1
>a.anchor::before,h2
>a.anchor::before,h3
>a.anchor::before,#toctitle
>a.anchor::before,.sidebarblock
>.content
>.title
>a.anchor::before,h4
>a.anchor::before,h5
>a.anchor::before,h6
>a.anchor::before{content:
"\00A7";font-size:
.85em;display:block;padding-top:
.1em}
192 #content h1:hover
>a.anchor,#content h1
>a.anchor:hover,h2:hover
>a.anchor,h2
>a.anchor:hover,h3:hover
>a.anchor,#toctitle:hover
>a.anchor,.sidebarblock
>.content
>.title:hover
>a.anchor,h3
>a.anchor:hover,#toctitle
>a.anchor:hover,.sidebarblock
>.content
>.title
>a.anchor:hover,h4:hover
>a.anchor,h4
>a.anchor:hover,h5:hover
>a.anchor,h5
>a.anchor:hover,h6:hover
>a.anchor,h6
>a.anchor:hover{visibility:visible}
193 #content h1
>a.link,h2
>a.link,h3
>a.link,#toctitle
>a.link,.sidebarblock
>.content
>.title
>a.link,h4
>a.link,h5
>a.link,h6
>a.link{color:#ba3925;text-decoration:none}
194 #content h1
>a.link:hover,h2
>a.link:hover,h3
>a.link:hover,#toctitle
>a.link:hover,.sidebarblock
>.content
>.title
>a.link:hover,h4
>a.link:hover,h5
>a.link:hover,h6
>a.link:hover{color:#a53221}
195 details,.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:
1.25em}
196 details{margin-left:
1.25rem}
197 details
>summary{cursor:pointer;display:block;position:relative;line-height:
1.6;margin-bottom:
.625rem;outline:none;-webkit-tap-highlight-color:transparent}
198 details
>summary::-webkit-details-marker{display:none}
199 details
>summary::before{content:
"";border:solid transparent;border-left:solid;border-width:
.3em
0 .3em
.5em;position:absolute;top:
.5em;left:-
1.25rem;transform:translateX(
15%)}
200 details[open]
>summary::before{border:solid transparent;border-top:solid;border-width:
.5em
.3em
0;transform:translateY(
15%)}
201 details
>summary::after{content:
"";width:
1.25rem;height:
1em;position:absolute;top:
.3em;left:-
1.25rem}
202 .admonitionblock td.content
>.title,.audioblock
>.title,.exampleblock
>.title,.imageblock
>.title,.listingblock
>.title,.literalblock
>.title,.stemblock
>.title,.openblock
>.title,.paragraph
>.title,.quoteblock
>.title,table.tableblock
>.title,.verseblock
>.title,.videoblock
>.title,.dlist
>.title,.olist
>.title,.ulist
>.title,.qlist
>.title,.hdlist
>.title{text-rendering:optimizeLegibility;text-align:left;font-family:
"Noto Serif",
"DejaVu Serif",serif;font-size:
1rem;font-style:italic}
203 table.tableblock.fit-content
>caption.title{white-space:nowrap;width:
0}
204 .paragraph.lead
>p,#preamble
>.sectionbody
>[class=paragraph]:first-of-type p{font-size:
1.21875em;line-height:
1.6;color:rgba(
0,
0,
0,
.85)}
205 .admonitionblock
>table{border-collapse:separate;border:
0;background:none;width:
100%}
206 .admonitionblock
>table td.icon{text-align:center;width:
80px}
207 .admonitionblock
>table td.icon img{max-width:none}
208 .admonitionblock
>table td.icon .title{font-weight:bold;font-family:
"Open Sans",
"DejaVu Sans",sans-serif;text-transform:uppercase}
209 .admonitionblock
>table td.content{padding-left:
1.125em;padding-right:
1.25em;border-left:
1px solid #dddddf;color:rgba(
0,
0,
0,
.6);word-wrap:anywhere}
210 .admonitionblock
>table td.content
>:last-child
>:last-child{margin-bottom:
0}
211 .exampleblock
>.content{border:
1px solid #e6e6e6;margin-bottom:
1.25em;padding:
1.25em;background:#fff;border-radius:
4px}
212 .sidebarblock{border:
1px solid #dbdbd6;margin-bottom:
1.25em;padding:
1.25em;background:#f3f3f2;border-radius:
4px}
213 .sidebarblock
>.content
>.title{color:#
7a2518;margin-top:
0;text-align:center}
214 .exampleblock
>.content
>:first-child,.sidebarblock
>.content
>:first-child{margin-top:
0}
215 .exampleblock
>.content
>:last-child,.exampleblock
>.content
>:last-child
>:last-child,.exampleblock
>.content .olist
>ol
>li:last-child
>:last-child,.exampleblock
>.content .ulist
>ul
>li:last-child
>:last-child,.exampleblock
>.content .qlist
>ol
>li:last-child
>:last-child,.sidebarblock
>.content
>:last-child,.sidebarblock
>.content
>:last-child
>:last-child,.sidebarblock
>.content .olist
>ol
>li:last-child
>:last-child,.sidebarblock
>.content .ulist
>ul
>li:last-child
>:last-child,.sidebarblock
>.content .qlist
>ol
>li:last-child
>:last-child{margin-bottom:
0}
216 .literalblock pre,.listingblock
>.content
>pre{border-radius:
4px;overflow-x:auto;padding:
1em;font-size:
.8125em}
217 @media screen and (min-width:
768px){.literalblock pre,.listingblock
>.content
>pre{font-size:
.90625em}}
218 @media screen and (min-width:
1280px){.literalblock pre,.listingblock
>.content
>pre{font-size:
1em}}
219 .literalblock pre,.listingblock
>.content
>pre:not(.highlight),.listingblock
>.content
>pre[class=highlight],.listingblock
>.content
>pre[class^=
"highlight "]{background:#f7f7f8}
220 .literalblock.output pre{color:#f7f7f8;background:rgba(
0,
0,
0,
.9)}
221 .listingblock
>.content{position:relative}
222 .listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:
.75em;top:
.425rem;right:
.5rem;line-height:
1;text-transform:uppercase;color:inherit;opacity:
.5}
223 .listingblock:hover code[data-lang]::before{display:block}
224 .listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:
.5em;color:inherit;opacity:
.5}
225 .listingblock.terminal pre .command:not([data-prompt])::before{content:
"$"}
226 .listingblock pre.highlightjs{padding:
0}
227 .listingblock pre.highlightjs
>code{padding:
1em;border-radius:
4px}
228 .listingblock pre.prettyprint{border-width:
0}
229 .prettyprint{background:#f7f7f8}
230 pre.prettyprint .linenums{line-height:
1.45;margin-left:
2em}
231 pre.prettyprint li{background:none;list-style-type:inherit;padding-left:
0}
232 pre.prettyprint li code[data-lang]::before{opacity:
1}
233 pre.prettyprint li:not(:first-child) code[data-lang]::before{display:none}
234 table.linenotable{border-collapse:separate;border:
0;margin-bottom:
0;background:none}
235 table.linenotable td[class]{color:inherit;vertical-align:top;padding:
0;line-height:inherit;white-space:normal}
236 table.linenotable td.code{padding-left:
.75em}
237 table.linenotable td.linenos,pre.pygments .linenos{border-right:
1px solid;opacity:
.35;padding-right:
.5em;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
238 pre.pygments span.linenos{display:inline-block;margin-right:
.75em}
239 .quoteblock{margin:
0 1em
1.25em
1.5em;display:table}
240 .quoteblock:not(.excerpt)
>.title{margin-left:-
1.5em;margin-bottom:
.75em}
241 .quoteblock blockquote,.quoteblock p{color:rgba(
0,
0,
0,
.85);font-size:
1.15rem;line-height:
1.75;word-spacing:
.1em;letter-spacing:
0;font-style:italic;text-align:justify}
242 .quoteblock blockquote{margin:
0;padding:
0;border:
0}
243 .quoteblock blockquote::before{content:
"\201c";float:left;font-size:
2.75em;font-weight:bold;line-height:
.6em;margin-left:-
.6em;color:#
7a2518;text-shadow:
0 1px
2px rgba(
0,
0,
0,
.1)}
244 .quoteblock blockquote
>.paragraph:last-child p{margin-bottom:
0}
245 .quoteblock .attribution{margin-top:
.75em;margin-right:
.5ex;text-align:right}
246 .verseblock{margin:
0 1em
1.25em}
247 .verseblock pre{font-family:
"Open Sans",
"DejaVu Sans",sans-serif;font-size:
1.15rem;color:rgba(
0,
0,
0,
.85);font-weight:
300;text-rendering:optimizeLegibility}
248 .verseblock pre strong{font-weight:
400}
249 .verseblock .attribution{margin-top:
1.25rem;margin-left:
.5ex}
250 .quoteblock .attribution,.verseblock .attribution{font-size:
.9375em;line-height:
1.45;font-style:italic}
251 .quoteblock .attribution br,.verseblock .attribution br{display:none}
252 .quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-
.025em;color:rgba(
0,
0,
0,
.6)}
253 .quoteblock.abstract blockquote::before,.quoteblock.excerpt blockquote::before,.quoteblock .quoteblock blockquote::before{display:none}
254 .quoteblock.abstract blockquote,.quoteblock.abstract p,.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{line-height:
1.6;word-spacing:
0}
255 .quoteblock.abstract{margin:
0 1em
1.25em;display:block}
256 .quoteblock.abstract
>.title{margin:
0 0 .375em;font-size:
1.15em;text-align:center}
257 .quoteblock.excerpt
>blockquote,.quoteblock .quoteblock{padding:
0 0 .25em
1em;border-left:
.25em solid #dddddf}
258 .quoteblock.excerpt,.quoteblock .quoteblock{margin-left:
0}
259 .quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{color:inherit;font-size:
1.0625rem}
260 .quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;font-size:
.85rem;text-align:left;margin-right:
0}
261 p.tableblock:last-child{margin-bottom:
0}
262 td.tableblock
>.content{margin-bottom:
1.25em;word-wrap:anywhere}
263 td.tableblock
>.content
>:last-child{margin-bottom:-
1.25em}
264 table.tableblock,th.tableblock,td.tableblock{border:
0 solid #dedede}
265 table.grid-all
>*
>tr
>*{border-width:
1px}
266 table.grid-cols
>*
>tr
>*{border-width:
0 1px}
267 table.grid-rows
>*
>tr
>*{border-width:
1px
0}
268 table.frame-all{border-width:
1px}
269 table.frame-ends{border-width:
1px
0}
270 table.frame-sides{border-width:
0 1px}
271 table.frame-none
>colgroup+*
>:first-child
>*,table.frame-sides
>colgroup+*
>:first-child
>*{border-top-width:
0}
272 table.frame-none
>:last-child
>:last-child
>*,table.frame-sides
>:last-child
>:last-child
>*{border-bottom-width:
0}
273 table.frame-none
>*
>tr
>:first-child,table.frame-ends
>*
>tr
>:first-child{border-left-width:
0}
274 table.frame-none
>*
>tr
>:last-child,table.frame-ends
>*
>tr
>:last-child{border-right-width:
0}
275 table.stripes-all
>*
>tr,table.stripes-odd
>*
>tr:nth-of-type(odd),table.stripes-even
>*
>tr:nth-of-type(even),table.stripes-hover
>*
>tr:hover{background:#f8f8f7}
276 th.halign-left,td.halign-left{text-align:left}
277 th.halign-right,td.halign-right{text-align:right}
278 th.halign-center,td.halign-center{text-align:center}
279 th.valign-top,td.valign-top{vertical-align:top}
280 th.valign-bottom,td.valign-bottom{vertical-align:bottom}
281 th.valign-middle,td.valign-middle{vertical-align:middle}
282 table thead th,table tfoot th{font-weight:bold}
283 tbody tr th{background:#f7f8f7}
284 tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(
0,
0,
0,
.8);font-weight:bold}
285 p.tableblock
>code:only-child{background:none;padding:
0}
286 p.tableblock{font-size:
1em}
287 ol{margin-left:
1.75em}
288 ul li ol{margin-left:
1.5em}
289 dl dd{margin-left:
1.125em}
290 dl dd:last-child,dl dd:last-child
>:last-child{margin-bottom:
0}
291 li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:
.625em}
292 ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}
293 ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:
.625em}
294 ul.unstyled,ol.unstyled{margin-left:
0}
295 li
>p:empty:only-child::before{content:
"";display:inline-block}
296 ul.checklist
>li
>p:first-child{margin-left:-
1em}
297 ul.checklist
>li
>p:first-child
>.fa-square-o:first-child,ul.checklist
>li
>p:first-child
>.fa-check-square-o:first-child{width:
1.25em;font-size:
.8em;position:relative;bottom:
.125em}
298 ul.checklist
>li
>p:first-child
>input[type=checkbox]:first-child{margin-right:
.25em}
299 ul.inline{display:flex;flex-flow:row wrap;list-style:none;margin:
0 0 .625em -
1.25em}
300 ul.inline
>li{margin-left:
1.25em}
301 .unstyled dl dt{font-weight:
400;font-style:normal}
302 ol.arabic{list-style-type:decimal}
303 ol.decimal{list-style-type:decimal-leading-zero}
304 ol.loweralpha{list-style-type:lower-alpha}
305 ol.upperalpha{list-style-type:upper-alpha}
306 ol.lowerroman{list-style-type:lower-roman}
307 ol.upperroman{list-style-type:upper-roman}
308 ol.lowergreek{list-style-type:lower-greek}
309 .hdlist
>table,.colist
>table{border:
0;background:none}
310 .hdlist
>table
>tbody
>tr,.colist
>table
>tbody
>tr{background:none}
311 td.hdlist1,td.hdlist2{vertical-align:top;padding:
0 .625em}
312 td.hdlist1{font-weight:bold;padding-bottom:
1.25em}
313 td.hdlist2{word-wrap:anywhere}
314 .literalblock+.colist,.listingblock+.colist{margin-top:-
.5em}
315 .colist td:not([class]):first-child{padding:
.4em
.75em
0;line-height:
1;vertical-align:top}
316 .colist td:not([class]):first-child img{max-width:none}
317 .colist td:not([class]):last-child{padding:
.25em
0}
318 .thumb,.th{line-height:
0;display:inline-block;border:
4px solid #fff;box-shadow:
0 0 0 1px #ddd}
319 .imageblock.left{margin:
.25em
.625em
1.25em
0}
320 .imageblock.right{margin:
.25em
0 1.25em
.625em}
321 .imageblock
>.title{margin-bottom:
0}
322 .imageblock.thumb,.imageblock.th{border-width:
6px}
323 .imageblock.thumb
>.title,.imageblock.th
>.title{padding:
0 .125em}
324 .image.left,.image.right{margin-top:
.25em;margin-bottom:
.25em;display:inline-block;line-height:
0}
325 .image.left{margin-right:
.625em}
326 .image.right{margin-left:
.625em}
327 a.image{text-decoration:none;display:inline-block}
328 a.image object{pointer-events:none}
329 sup.footnote,sup.footnoteref{font-size:
.875em;position:static;vertical-align:super}
330 sup.footnote a,sup.footnoteref a{text-decoration:none}
331 sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}
332 #footnotes{padding-top:
.75em;padding-bottom:
.75em;margin-bottom:
.625em}
333 #footnotes hr{width:
20%;min-width:
6.25em;margin:-
.25em
0 .75em;border-width:
1px
0 0}
334 #footnotes .footnote{padding:
0 .375em
0 .225em;line-height:
1.3334;font-size:
.875em;margin-left:
1.2em;margin-bottom:
.2em}
335 #footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-
1.05em}
336 #footnotes .footnote:last-of-type{margin-bottom:
0}
337 #content #footnotes{margin-top:-
.625em;margin-bottom:
0;padding:
.75em
0}
338 div.unbreakable{page-break-inside:avoid}
339 .big{font-size:larger}
340 .small{font-size:smaller}
341 .underline{text-decoration:underline}
342 .overline{text-decoration:overline}
343 .line-through{text-decoration:line-through}
345 .aqua-background{background:#
00fafa}
347 .black-background{background:#
000}
349 .blue-background{background:#
0000fa}
350 .fuchsia{color:#bf00bf}
351 .fuchsia-background{background:#fa00fa}
353 .gray-background{background:#
7d7d7d}
354 .green{color:#
006000}
355 .green-background{background:#
007d00}
357 .lime-background{background:#
00fa00}
358 .maroon{color:#
600000}
359 .maroon-background{background:#
7d0000}
361 .navy-background{background:#
00007d}
362 .olive{color:#
606000}
363 .olive-background{background:#
7d7d00}
364 .purple{color:#
600060}
365 .purple-background{background:#
7d007d}
367 .red-background{background:#fa0000}
368 .silver{color:#
909090}
369 .silver-background{background:#bcbcbc}
371 .teal-background{background:#
007d7d}
372 .white{color:#bfbfbf}
373 .white-background{background:#fafafa}
374 .yellow{color:#bfbf00}
375 .yellow-background{background:#fafa00}
376 span.icon
>.fa{cursor:default}
377 a span.icon
>.fa{cursor:inherit}
378 .admonitionblock td.icon [class^=
"fa icon-"]{font-size:
2.5em;text-shadow:
1px
1px
2px rgba(
0,
0,
0,
.5);cursor:default}
379 .admonitionblock td.icon .icon-note::before{content:
"\f05a";color:#
19407c}
380 .admonitionblock td.icon .icon-tip::before{content:
"\f0eb";text-shadow:
1px
1px
2px rgba(
155,
155,
0,
.8);color:#
111}
381 .admonitionblock td.icon .icon-warning::before{content:
"\f071";color:#bf6900}
382 .admonitionblock td.icon .icon-caution::before{content:
"\f06d";color:#bf3400}
383 .admonitionblock td.icon .icon-important::before{content:
"\f06a";color:#bf0000}
384 .conum[data-value]{display:inline-block;color:#fff!important;background:rgba(
0,
0,
0,
.8);border-radius:
50%;text-align:center;font-size:
.75em;width:
1.67em;height:
1.67em;line-height:
1.67em;font-family:
"Open Sans",
"DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
385 .conum[data-value] *{color:#fff!important}
386 .conum[data-value]+b{display:none}
387 .conum[data-value]::after{content:attr(data-value)}
388 pre .conum[data-value]{position:relative;top:-
.125em}
389 b.conum *{color:inherit!important}
390 .conum:not([data-value]):empty{display:none}
391 dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
392 h1,h2,p,td.content,span.alt,summary{letter-spacing:-
.01em}
393 p strong,td.content strong,div.footnote strong{letter-spacing:-
.005em}
394 p,blockquote,dt,td.content,td.hdlist1,span.alt,summary{font-size:
1.0625rem}
395 p{margin-bottom:
1.25rem}
396 .sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:
1em}
397 .exampleblock
>.content{background:#fffef7;border-color:#e0e0dc;box-shadow:
0 1px
4px #e0e0dc}
398 .print-only{display:none!important}
399 @page{margin:
1.25cm
.75cm}
400 @media print{*{box-shadow:none!important;text-shadow:none!important}
402 a{color:inherit!important;text-decoration:underline!important}
403 a.bare,a[href^=
"#"],a[href^=
"mailto:"]{text-decoration:none!important}
404 a[href^=
"http:"]:not(.bare)::after,a[href^=
"https:"]:not(.bare)::after{content:
"(" attr(href)
")";display:inline-block;font-size:
.875em;padding-left:
.25em}
405 abbr[title]{border-bottom:
1px dotted}
406 abbr[title]::after{content:
" (" attr(title)
")"}
407 pre,blockquote,tr,img,object,svg{page-break-inside:avoid}
408 thead{display:table-header-group}
410 p,blockquote,dt,td.content{font-size:
1em;orphans:
3;widows:
3}
411 h2,h3,#toctitle,.sidebarblock
>.content
>.title{page-break-after:avoid}
412 #header,#content,#footnotes,#footer{max-width:none}
413 #toc,.sidebarblock,.exampleblock
>.content{background:none!important}
414 #toc{border-bottom:
1px solid #dddddf!important;padding-bottom:
0!important}
415 body.book #header{text-align:center}
416 body.book #header
>h1:first-child{border:
0!important;margin:
2.5em
0 1em}
417 body.book #header .details{border:
0!important;display:block;padding:
0!important}
418 body.book #header .details span:first-child{margin-left:
0!important}
419 body.book #header .details br{display:block}
420 body.book #header .details br+span::before{content:none!important}
421 body.book #toc{border:
0!important;text-align:left!important;padding:
0!important;margin:
0!important}
422 body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1
>h2{page-break-before:always}
423 .listingblock code[data-lang]::before{display:block}
424 #footer{padding:
0 .9375em}
425 .hide-on-print{display:none!important}
426 .print-only{display:block!important}
427 .hide-for-print{display:none!important}
428 .show-for-print{display:inherit!important}}
429 @media amzn-kf8,print{#header
>h1:first-child{margin-top:
1.25rem}
430 .sect1{padding:
0!important}
431 .sect1+.sect1{border:
0}
432 #footer{background:none}
433 #footer-text{color:rgba(
0,
0,
0,
.6);font-size:
.9em}}
434 @media amzn-kf8{#header,#content,#footnotes,#footer{padding:
0}}
442 <body class=
"article">
444 <h1>Fighting regressions with git bisect
</h1>
445 <div class=
"details">
446 <span id=
"author" class=
"author">Christian Couder
</span><br/>
447 <span id=
"email" class=
"email"><a href=
"mailto:chriscool@tuxfamily.org">chriscool@tuxfamily.org
</a></span><br/>
448 <span id=
"revdate">2024-
10-
06</span>
453 <h2 id=
"_abstract">Abstract
</h2>
454 <div class=
"sectionbody">
455 <div class=
"paragraph">
456 <p>"git bisect" enables software users and developers to easily find the
457 commit that introduced a regression. We show why it is important to
458 have good tools to fight regressions. We describe how
"git bisect"
459 works from the outside and the algorithms it uses inside. Then we
460 explain how to take advantage of
"git bisect" to improve current
461 practices. And we discuss how
"git bisect" could improve in the
467 <h2 id=
"_introduction_to_git_bisect">Introduction to
"git bisect"</h2>
468 <div class=
"sectionbody">
469 <div class=
"paragraph">
470 <p>Git is a Distributed Version Control system (DVCS) created by Linus
471 Torvalds and maintained by Junio Hamano.
</p>
473 <div class=
"paragraph">
474 <p>In Git like in many other Version Control Systems (VCS), the different
475 states of the data that is managed by the system are called
476 commits. And, as VCS are mostly used to manage software source code,
477 sometimes
"interesting" changes of behavior in the software are
478 introduced in some commits.
</p>
480 <div class=
"paragraph">
481 <p>In fact people are specially interested in commits that introduce a
482 "bad" behavior, called a bug or a regression. They are interested in
483 these commits because a commit (hopefully) contains a very small set
484 of source code changes. And it
’s much easier to understand and
485 properly fix a problem when you only need to check a very small set of
486 changes, than when you don
’t know where look in the first place.
</p>
488 <div class=
"paragraph">
489 <p>So to help people find commits that introduce a
"bad" behavior, the
490 "git bisect" set of commands was invented. And it follows of course
491 that in
"git bisect" parlance, commits where the
"interesting
492 behavior" is present are called
"bad" commits, while other commits are
493 called
"good" commits. And a commit that introduce the behavior we are
494 interested in is called a
"first bad commit". Note that there could be
495 more than one
"first bad commit" in the commit space we are searching.
</p>
497 <div class=
"paragraph">
498 <p>So
"git bisect" is designed to help find a
"first bad commit". And to
499 be as efficient as possible, it tries to perform a binary search.
</p>
504 <h2 id=
"_fighting_regressions_overview">Fighting regressions overview
</h2>
505 <div class=
"sectionbody">
507 <h3 id=
"_regressions_a_big_problem">Regressions: a big problem
</h3>
508 <div class=
"paragraph">
509 <p>Regressions are a big problem in the software industry. But it
’s
510 difficult to put some real numbers behind that claim.
</p>
512 <div class=
"paragraph">
513 <p>There are some numbers about bugs in general, like a NIST study in
514 2002 <a href=
"#1">[
1]
</a> that said:
</p>
516 <div class=
"quoteblock">
518 <div class=
"paragraph">
519 <p>Software bugs, or errors, are so prevalent and so detrimental that
520 they cost the U.S. economy an estimated $
59.5 billion annually, or
521 about
0.6 percent of the gross domestic product, according to a newly
522 released study commissioned by the Department of Commerce
’s National
523 Institute of Standards and Technology (NIST). At the national level,
524 over half of the costs are borne by software users and the remainder
525 by software developers/vendors. The study also found that, although
526 all errors cannot be removed, more than a third of these costs, or an
527 estimated $
22.2 billion, could be eliminated by an improved testing
528 infrastructure that enables earlier and more effective identification
529 and removal of software defects. These are the savings associated with
530 finding an increased percentage (but not
100 percent) of errors closer
531 to the development stages in which they are introduced. Currently,
532 over half of all errors are not found until
"downstream" in the
533 development process or during post-sale software use.
</p>
537 <div class=
"paragraph">
540 <div class=
"quoteblock">
542 <div class=
"paragraph">
543 <p>Software developers already spend approximately
80 percent of
544 development costs on identifying and correcting defects, and yet few
545 products of any type other than software are shipped with such high
546 levels of errors.
</p>
550 <div class=
"paragraph">
551 <p>Eventually the conclusion started with:
</p>
553 <div class=
"quoteblock">
555 <div class=
"paragraph">
556 <p>The path to higher software quality is significantly improved software
561 <div class=
"paragraph">
562 <p>There are other estimates saying that
80% of the cost related to
563 software is about maintenance
<a href=
"#2">[
2]
</a>.
</p>
565 <div class=
"paragraph">
566 <p>Though, according to Wikipedia
<a href=
"#3">[
3]
</a>:
</p>
568 <div class=
"quoteblock">
570 <div class=
"paragraph">
571 <p>A common perception of maintenance is that it is merely fixing
572 bugs. However, studies and surveys over the years have indicated that
573 the majority, over
80%, of the maintenance effort is used for
574 non-corrective actions (Pigosky
1997). This perception is perpetuated
575 by users submitting problem reports that in reality are functionality
576 enhancements to the system.
</p>
580 <div class=
"paragraph">
581 <p>But we can guess that improving on existing software is very costly
582 because you have to watch out for regressions. At least this would
583 make the above studies consistent among themselves.
</p>
585 <div class=
"paragraph">
586 <p>Of course some kind of software is developed, then used during some
587 time without being improved on much, and then finally thrown away. In
588 this case, of course, regressions may not be a big problem. But on the
589 other hand, there is a lot of big software that is continually
590 developed and maintained during years or even tens of years by a lot
591 of people. And as there are often many people who depend (sometimes
592 critically) on such software, regressions are a really big problem.
</p>
594 <div class=
"paragraph">
595 <p>One such software is the Linux kernel. And if we look at the Linux
596 kernel, we can see that a lot of time and effort is spent to fight
597 regressions. The release cycle start with a
2 weeks long merge
598 window. Then the first release candidate (rc) version is tagged. And
599 after that about
7 or
8 more rc versions will appear with around one
600 week between each of them, before the final release.
</p>
602 <div class=
"paragraph">
603 <p>The time between the first rc release and the final release is
604 supposed to be used to test rc versions and fight bugs and especially
605 regressions. And this time is more than
80% of the release cycle
606 time. But this is not the end of the fight yet, as of course it
607 continues after the release.
</p>
609 <div class=
"paragraph">
610 <p>And then this is what Ingo Molnar (a well known Linux kernel
611 developer) says about his use of git bisect:
</p>
613 <div class=
"quoteblock">
615 <div class=
"paragraph">
616 <p>I most actively use it during the merge window (when a lot of trees
617 get merged upstream and when the influx of bugs is the highest) - and
618 yes, there have been cases that i used it multiple times a day. My
619 average is roughly once a day.
</p>
623 <div class=
"paragraph">
624 <p>So regressions are fought all the time by developers, and indeed it is
625 well known that bugs should be fixed as soon as possible, so as soon
626 as they are found. That
’s why it is interesting to have good tools for
631 <h3 id=
"_other_tools_to_fight_regressions">Other tools to fight regressions
</h3>
632 <div class=
"paragraph">
633 <p>So what are the tools used to fight regressions? They are nearly the
634 same as those used to fight regular bugs. The only specific tools are
635 test suites and tools similar as
"git bisect".
</p>
637 <div class=
"paragraph">
638 <p>Test suites are very nice. But when they are used alone, they are
639 supposed to be used so that all the tests are checked after each
640 commit. This means that they are not very efficient, because many
641 tests are run for no interesting result, and they suffer from
642 combinatorial explosion.
</p>
644 <div class=
"paragraph">
645 <p>In fact the problem is that big software often has many different
646 configuration options and that each test case should pass for each
647 configuration after each commit. So if you have for each release: N
648 configurations, M commits and T test cases, you should perform:
</p>
650 <div class=
"listingblock">
651 <div class=
"content">
652 <pre>N * M * T tests
</pre>
655 <div class=
"paragraph">
656 <p>where N, M and T are all growing with the size your software.
</p>
658 <div class=
"paragraph">
659 <p>So very soon it will not be possible to completely test everything.
</p>
661 <div class=
"paragraph">
662 <p>And if some bugs slip through your test suite, then you can add a test
663 to your test suite. But if you want to use your new improved test
664 suite to find where the bug slipped in, then you will either have to
665 emulate a bisection process or you will perhaps bluntly test each
666 commit backward starting from the
"bad" commit you have which may be
673 <h2 id=
"_git_bisect_overview">"git bisect" overview
</h2>
674 <div class=
"sectionbody">
676 <h3 id=
"_starting_a_bisection">Starting a bisection
</h3>
677 <div class=
"paragraph">
678 <p>The first
"git bisect" subcommand to use is
"git bisect start" to
679 start the search. Then bounds must be set to limit the commit
680 space. This is done usually by giving one
"bad" and at least one
681 "good" commit. They can be passed in the initial call to
"git bisect
682 start" like this:
</p>
684 <div class=
"listingblock">
685 <div class=
"content">
686 <pre>$ git bisect start [BAD [GOOD...]]
</pre>
689 <div class=
"paragraph">
690 <p>or they can be set using:
</p>
692 <div class=
"listingblock">
693 <div class=
"content">
694 <pre>$ git bisect bad [COMMIT]
</pre>
697 <div class=
"paragraph">
700 <div class=
"listingblock">
701 <div class=
"content">
702 <pre>$ git bisect good [COMMIT...]
</pre>
705 <div class=
"paragraph">
706 <p>where BAD, GOOD and COMMIT are all names that can be resolved to a
709 <div class=
"paragraph">
710 <p>Then
"git bisect" will checkout a commit of its choosing and ask the
711 user to test it, like this:
</p>
713 <div class=
"listingblock">
714 <div class=
"content">
715 <pre>$ git bisect start v2.6
.27 v2.6
.25
716 Bisecting:
10928 revisions left to test after this (roughly
14 steps)
717 [
2ec65f8b89ea003c27ff7723525a2ee335a2b393] x86: clean up using max_low_pfn on
32-bit
</pre>
720 <div class=
"paragraph">
721 <p>Note that the example that we will use is really a toy example, we
722 will be looking for the first commit that has a version like
723 "2.6.26-something", that is the commit that has a
"SUBLEVEL = 26" line
724 in the top level Makefile. This is a toy example because there are
725 better ways to find this commit with Git than using
"git bisect" (for
726 example
"git blame" or
"git log -S<string>").
</p>
730 <h3 id=
"_driving_a_bisection_manually">Driving a bisection manually
</h3>
731 <div class=
"paragraph">
732 <p>At this point there are basically
2 ways to drive the search. It can
733 be driven manually by the user or it can be driven automatically by a
734 script or a command.
</p>
736 <div class=
"paragraph">
737 <p>If the user is driving it, then at each step of the search, the user
738 will have to test the current commit and say if it is
"good" or
"bad"
739 using the
"git bisect good" or
"git bisect bad" commands respectively
740 that have been described above. For example:
</p>
742 <div class=
"listingblock">
743 <div class=
"content">
744 <pre>$ git bisect bad
745 Bisecting:
5480 revisions left to test after this (roughly
13 steps)
746 [
66c0b394f08fd89236515c1c84485ea712a157be] KVM: kill file-
>f_count abuse in kvm
</pre>
749 <div class=
"paragraph">
750 <p>And after a few more steps like that,
"git bisect" will eventually
751 find a first bad commit:
</p>
753 <div class=
"listingblock">
754 <div class=
"content">
755 <pre>$ git bisect bad
756 2ddcca36c8bcfa251724fe342c8327451988be0d is the first bad commit
757 commit
2ddcca36c8bcfa251724fe342c8327451988be0d
758 Author: Linus Torvalds
<torvalds@linux-foundation.org
>
759 Date: Sat May
3 11:
59:
44 2008 -
0700
763 :
100644 100644 5cf82581...
4492984e... M Makefile
</pre>
766 <div class=
"paragraph">
767 <p>At this point we can see what the commit does, check it out (if it
’s
768 not already checked out) or tinker with it, for example:
</p>
770 <div class=
"listingblock">
771 <div class=
"content">
773 commit
2ddcca36c8bcfa251724fe342c8327451988be0d
774 Author: Linus Torvalds
<torvalds@linux-foundation.org
>
775 Date: Sat May
3 11:
59:
44 2008 -
0700
779 diff --git a/Makefile b/Makefile
780 index
5cf8258.
.4492984 100644
790 NAME = Funky Weasel is Jiggy wit it
792 # *DOCUMENTATION*
</pre>
795 <div class=
"paragraph">
796 <p>And when we are finished we can use
"git bisect reset" to go back to
797 the branch we were in before we started bisecting:
</p>
799 <div class=
"listingblock">
800 <div class=
"content">
801 <pre>$ git bisect reset
802 Checking out files:
100% (
21549/
21549), done.
803 Previous HEAD position was
2ddcca3... Linux
2.6.26-rc1
804 Switched to branch 'master'
</pre>
809 <h3 id=
"_driving_a_bisection_automatically">Driving a bisection automatically
</h3>
810 <div class=
"paragraph">
811 <p>The other way to drive the bisection process is to tell
"git bisect"
812 to launch a script or command at each bisection step to know if the
813 current commit is
"good" or
"bad". To do that, we use the
"git bisect
814 run" command. For example:
</p>
816 <div class=
"listingblock">
817 <div class=
"content">
818 <pre>$ git bisect start v2.6
.27 v2.6
.25
819 Bisecting:
10928 revisions left to test after this (roughly
14 steps)
820 [
2ec65f8b89ea003c27ff7723525a2ee335a2b393] x86: clean up using max_low_pfn on
32-bit
822 $ git bisect run grep '^SUBLEVEL =
25' Makefile
823 running grep ^SUBLEVEL =
25 Makefile
824 Bisecting:
5480 revisions left to test after this (roughly
13 steps)
825 [
66c0b394f08fd89236515c1c84485ea712a157be] KVM: kill file-
>f_count abuse in kvm
826 running grep ^SUBLEVEL =
25 Makefile
828 Bisecting:
2740 revisions left to test after this (roughly
12 steps)
829 [
671294719628f1671faefd4882764886f8ad08cb] V4L/DVB(
7879): Adding cx18 Support for mxl5005s
832 running grep ^SUBLEVEL =
25 Makefile
833 Bisecting:
0 revisions left to test after this (roughly
0 steps)
834 [
2ddcca36c8bcfa251724fe342c8327451988be0d] Linux
2.6.26-rc1
835 running grep ^SUBLEVEL =
25 Makefile
836 2ddcca36c8bcfa251724fe342c8327451988be0d is the first bad commit
837 commit
2ddcca36c8bcfa251724fe342c8327451988be0d
838 Author: Linus Torvalds
<torvalds@linux-foundation.org
>
839 Date: Sat May
3 11:
59:
44 2008 -
0700
843 :
100644 100644 5cf82581...
4492984e... M Makefile
844 bisect run success
</pre>
847 <div class=
"paragraph">
848 <p>In this example, we passed
"grep <em>^SUBLEVEL = 25</em> Makefile" as
849 parameter to
"git bisect run". This means that at each step, the grep
850 command we passed will be launched. And if it exits with code
0 (that
851 means success) then git bisect will mark the current state as
852 "good". If it exits with code
1 (or any code between
1 and
127
853 included, except the special code
125), then the current state will be
856 <div class=
"paragraph">
857 <p>Exit code between
128 and
255 are special to
"git bisect run". They
858 make it stop immediately the bisection process. This is useful for
859 example if the command passed takes too long to complete, because you
860 can kill it with a signal and it will stop the bisection process.
</p>
862 <div class=
"paragraph">
863 <p>It can also be useful in scripts passed to
"git bisect run" to
"exit
864 255" if some very abnormal situation is detected.
</p>
868 <h3 id=
"_avoiding_untestable_commits">Avoiding untestable commits
</h3>
869 <div class=
"paragraph">
870 <p>Sometimes it happens that the current state cannot be tested, for
871 example if it does not compile because there was a bug preventing it
872 at that time. This is what the special exit code
125 is for. It tells
873 "git bisect run" that the current commit should be marked as
874 untestable and that another one should be chosen and checked out.
</p>
876 <div class=
"paragraph">
877 <p>If the bisection process is driven manually, you can use
"git bisect
878 skip" to do the same thing. (In fact the special exit code
125 makes
879 "git bisect run" use
"git bisect skip" in the background.)
</p>
881 <div class=
"paragraph">
882 <p>Or if you want more control, you can inspect the current state using
883 for example
"git bisect visualize". It will launch gitk (or
"git log"
884 if the
<code>DISPLAY
</code> environment variable is not set) to help you find a
885 better bisection point.
</p>
887 <div class=
"paragraph">
888 <p>Either way, if you have a string of untestable commits, it might
889 happen that the regression you are looking for has been introduced by
890 one of these untestable commits. In this case it
’s not possible to
891 tell for sure which commit introduced the regression.
</p>
893 <div class=
"paragraph">
894 <p>So if you used
"git bisect skip" (or the run script exited with
895 special code
125) you could get a result like this:
</p>
897 <div class=
"listingblock">
898 <div class=
"content">
899 <pre>There are only 'skip'ped commits left to test.
900 The first bad commit could be any of:
901 15722f2fa328eaba97022898a305ffc8172db6b1
902 78e86cf3e850bd755bb71831f42e200626fbd1e0
903 e15b73ad3db9b48d7d1ade32f8cd23a751fe0ace
904 070eab2303024706f2924822bfec8b9847e4ac1b
905 We cannot bisect more!
</pre>
910 <h3 id=
"_saving_a_log_and_replaying_it">Saving a log and replaying it
</h3>
911 <div class=
"paragraph">
912 <p>If you want to show other people your bisection process, you can get a
913 log using for example:
</p>
915 <div class=
"listingblock">
916 <div class=
"content">
917 <pre>$ git bisect log
> bisect_log.txt
</pre>
920 <div class=
"paragraph">
921 <p>And it is possible to replay it using:
</p>
923 <div class=
"listingblock">
924 <div class=
"content">
925 <pre>$ git bisect replay bisect_log.txt
</pre>
932 <h2 id=
"_git_bisect_details">"git bisect" details
</h2>
933 <div class=
"sectionbody">
935 <h3 id=
"_bisection_algorithm">Bisection algorithm
</h3>
936 <div class=
"paragraph">
937 <p>As the Git commits form a directed acyclic graph (DAG), finding the
938 best bisection commit to test at each step is not so simple. Anyway
939 Linus found and implemented a
"truly stupid" algorithm, later improved
940 by Junio Hamano, that works quite well.
</p>
942 <div class=
"paragraph">
943 <p>So the algorithm used by
"git bisect" to find the best bisection
944 commit when there are no skipped commits is the following:
</p>
946 <div class=
"paragraph">
947 <p>1) keep only the commits that:
</p>
949 <div class=
"paragraph">
950 <p>a) are ancestor of the
"bad" commit (including the
"bad" commit itself),
951 b) are not ancestor of a
"good" commit (excluding the
"good" commits).
</p>
953 <div class=
"paragraph">
954 <p>This means that we get rid of the uninteresting commits in the DAG.
</p>
956 <div class=
"paragraph">
957 <p>For example if we start with a graph like this:
</p>
959 <div class=
"listingblock">
960 <div class=
"content">
961 <pre>G-Y-G-W-W-W-X-X-X-X
969 -
> time goes this way -
></pre>
972 <div class=
"paragraph">
973 <p>where B is the
"bad" commit,
"G" are
"good" commits and W, X, and Y
974 are other commits, we will get the following graph after this first
977 <div class=
"listingblock">
978 <div class=
"content">
986 <div class=
"paragraph">
987 <p>So only the W and B commits will be kept. Because commits X and Y will
988 have been removed by rules a) and b) respectively, and because commits
989 G are removed by rule b) too.
</p>
991 <div class=
"paragraph">
992 <p>Note for Git users, that it is equivalent as keeping only the commit
995 <div class=
"listingblock">
996 <div class=
"content">
997 <pre>git rev-list BAD --not GOOD1 GOOD2...
</pre>
1000 <div class=
"paragraph">
1001 <p>Also note that we don
’t require the commits that are kept to be
1002 descendants of a
"good" commit. So in the following example, commits W
1003 and Z will be kept:
</p>
1005 <div class=
"listingblock">
1006 <div class=
"content">
1012 <div class=
"paragraph">
1013 <p>2) starting from the
"good" ends of the graph, associate to each
1014 commit the number of ancestors it has plus one
</p>
1016 <div class=
"paragraph">
1017 <p>For example with the following graph where H is the
"bad" commit and A
1018 and D are some parents of some
"good" commits:
</p>
1020 <div class=
"listingblock">
1021 <div class=
"content">
1029 <div class=
"paragraph">
1030 <p>this will give:
</p>
1032 <div class=
"listingblock">
1033 <div class=
"content">
1042 <div class=
"paragraph">
1043 <p>3) associate to each commit: min(X, N - X)
</p>
1045 <div class=
"paragraph">
1046 <p>where X is the value associated to the commit in step
2) and N is the
1047 total number of commits in the graph.
</p>
1049 <div class=
"paragraph">
1050 <p>In the above example we have N =
8, so this will give:
</p>
1052 <div class=
"listingblock">
1053 <div class=
"content">
1062 <div class=
"paragraph">
1063 <p>4) the best bisection point is the commit with the highest associated
1066 <div class=
"paragraph">
1067 <p>So in the above example the best bisection point is commit C.
</p>
1069 <div class=
"paragraph">
1070 <p>5) note that some shortcuts are implemented to speed up the algorithm
</p>
1072 <div class=
"paragraph">
1073 <p>As we know N from the beginning, we know that min(X, N - X) can
’t be
1074 greater than N/
2. So during steps
2) and
3), if we would associate N/
2
1075 to a commit, then we know this is the best bisection point. So in this
1076 case we can just stop processing any other commit and return the
1081 <h3 id=
"_bisection_algorithm_debugging">Bisection algorithm debugging
</h3>
1082 <div class=
"paragraph">
1083 <p>For any commit graph, you can see the number associated with each
1084 commit using
"git rev-list --bisect-all".
</p>
1086 <div class=
"paragraph">
1087 <p>For example, for the above graph, a command like:
</p>
1089 <div class=
"listingblock">
1090 <div class=
"content">
1091 <pre>$ git rev-list --bisect-all BAD --not GOOD1 GOOD2
</pre>
1094 <div class=
"paragraph">
1095 <p>would output something like:
</p>
1097 <div class=
"listingblock">
1098 <div class=
"content">
1099 <pre>e15b73ad3db9b48d7d1ade32f8cd23a751fe0ace (dist=
3)
1100 15722f2fa328eaba97022898a305ffc8172db6b1 (dist=
2)
1101 78e86cf3e850bd755bb71831f42e200626fbd1e0 (dist=
2)
1102 a1939d9a142de972094af4dde9a544e577ddef0e (dist=
2)
1103 070eab2303024706f2924822bfec8b9847e4ac1b (dist=
1)
1104 a3864d4f32a3bf5ed177ddef598490a08760b70d (dist=
1)
1105 a41baa717dd74f1180abf55e9341bc7a0bb9d556 (dist=
1)
1106 9e622a6dad403b71c40979743bb9d5be17b16bd6 (dist=
0)
</pre>
1111 <h3 id=
"_bisection_algorithm_discussed">Bisection algorithm discussed
</h3>
1112 <div class=
"paragraph">
1113 <p>First let
’s define
"best bisection point". We will say that a commit X
1114 is a best bisection point or a best bisection commit if knowing its
1115 state (
"good" or
"bad") gives as much information as possible whether
1116 the state of the commit happens to be
"good" or
"bad".
</p>
1118 <div class=
"paragraph">
1119 <p>This means that the best bisection commits are the commits where the
1120 following function is maximum:
</p>
1122 <div class=
"listingblock">
1123 <div class=
"content">
1124 <pre>f(X) = min(information_if_good(X), information_if_bad(X))
</pre>
1127 <div class=
"paragraph">
1128 <p>where information_if_good(X) is the information we get if X is good
1129 and information_if_bad(X) is the information we get if X is bad.
</p>
1131 <div class=
"paragraph">
1132 <p>Now we will suppose that there is only one
"first bad commit". This
1133 means that all its descendants are
"bad" and all the other commits are
1134 "good". And we will suppose that all commits have an equal probability
1135 of being good or bad, or of being the first bad commit, so knowing the
1136 state of c commits gives always the same amount of information
1137 wherever these c commits are on the graph and whatever c is. (So we
1138 suppose that these commits being for example on a branch or near a
1139 good or a bad commit does not give more or less information).
</p>
1141 <div class=
"paragraph">
1142 <p>Let
’s also suppose that we have a cleaned up graph like one after step
1143 1) in the bisection algorithm above. This means that we can measure
1144 the information we get in terms of number of commit we can remove
1145 from the graph..
</p>
1147 <div class=
"paragraph">
1148 <p>And let
’s take a commit X in the graph.
</p>
1150 <div class=
"paragraph">
1151 <p>If X is found to be
"good", then we know that its ancestors are all
1152 "good", so we want to say that:
</p>
1154 <div class=
"listingblock">
1155 <div class=
"content">
1156 <pre>information_if_good(X) = number_of_ancestors(X) (TRUE)
</pre>
1159 <div class=
"paragraph">
1160 <p>And this is true because at step
1) b) we remove the ancestors of the
1163 <div class=
"paragraph">
1164 <p>If X is found to be
"bad", then we know that its descendants are all
1165 "bad", so we want to say that:
</p>
1167 <div class=
"listingblock">
1168 <div class=
"content">
1169 <pre>information_if_bad(X) = number_of_descendants(X) (WRONG)
</pre>
1172 <div class=
"paragraph">
1173 <p>But this is wrong because at step
1) a) we keep only the ancestors of
1174 the bad commit. So we get more information when a commit is marked as
1175 "bad", because we also know that the ancestors of the previous
"bad"
1176 commit that are not ancestors of the new
"bad" commit are not the
1177 first bad commit. We don
’t know if they are good or bad, but we know
1178 that they are not the first bad commit because they are not ancestor
1179 of the new
"bad" commit.
</p>
1181 <div class=
"paragraph">
1182 <p>So when a commit is marked as
"bad" we know we can remove all the
1183 commits in the graph except those that are ancestors of the new
"bad"
1184 commit. This means that:
</p>
1186 <div class=
"listingblock">
1187 <div class=
"content">
1188 <pre>information_if_bad(X) = N - number_of_ancestors(X) (TRUE)
</pre>
1191 <div class=
"paragraph">
1192 <p>where N is the number of commits in the (cleaned up) graph.
</p>
1194 <div class=
"paragraph">
1195 <p>So in the end this means that to find the best bisection commits we
1196 should maximize the function:
</p>
1198 <div class=
"listingblock">
1199 <div class=
"content">
1200 <pre>f(X) = min(number_of_ancestors(X), N - number_of_ancestors(X))
</pre>
1203 <div class=
"paragraph">
1204 <p>And this is nice because at step
2) we compute number_of_ancestors(X)
1205 and so at step
3) we compute f(X).
</p>
1207 <div class=
"paragraph">
1208 <p>Let
’s take the following graph as an example:
</p>
1210 <div class=
"listingblock">
1211 <div class=
"content">
1219 <div class=
"paragraph">
1220 <p>If we compute the following non optimal function on it:
</p>
1222 <div class=
"listingblock">
1223 <div class=
"content">
1224 <pre>g(X) = min(number_of_ancestors(X), number_of_descendants(X))
</pre>
1227 <div class=
"paragraph">
1230 <div class=
"listingblock">
1231 <div class=
"content">
1241 <div class=
"paragraph">
1242 <p>but with the algorithm used by git bisect we get:
</p>
1244 <div class=
"listingblock">
1245 <div class=
"content">
1255 <div class=
"paragraph">
1256 <p>So we chose G, H, K or L as the best bisection point, which is better
1257 than F. Because if for example L is bad, then we will know not only
1258 that L, M and N are bad but also that G, H, I and J are not the first
1259 bad commit (since we suppose that there is only one first bad commit
1260 and it must be an ancestor of L).
</p>
1262 <div class=
"paragraph">
1263 <p>So the current algorithm seems to be the best possible given what we
1264 initially supposed.
</p>
1268 <h3 id=
"_skip_algorithm">Skip algorithm
</h3>
1269 <div class=
"paragraph">
1270 <p>When some commits have been skipped (using
"git bisect skip"), then
1271 the bisection algorithm is the same for step
1) to
3). But then we use
1272 roughly the following steps:
</p>
1274 <div class=
"paragraph">
1275 <p>6) sort the commit by decreasing associated value
</p>
1277 <div class=
"paragraph">
1278 <p>7) if the first commit has not been skipped, we can return it and stop
1281 <div class=
"paragraph">
1282 <p>8) otherwise filter out all the skipped commits in the sorted list
</p>
1284 <div class=
"paragraph">
1285 <p>9) use a pseudo random number generator (PRNG) to generate a random
1286 number between
0 and
1</p>
1288 <div class=
"paragraph">
1289 <p>10) multiply this random number with its square root to bias it toward
1292 <div class=
"paragraph">
1293 <p>11) multiply the result by the number of commits in the filtered list
1294 to get an index into this list
</p>
1296 <div class=
"paragraph">
1297 <p>12) return the commit at the computed index
</p>
1301 <h3 id=
"_skip_algorithm_discussed">Skip algorithm discussed
</h3>
1302 <div class=
"paragraph">
1303 <p>After step
7) (in the skip algorithm), we could check if the second
1304 commit has been skipped and return it if it is not the case. And in
1305 fact that was the algorithm we used from when
"git bisect skip" was
1306 developed in Git version
1.5.4 (released on February
1st
2008) until
1307 Git version
1.6.4 (released July
29th
2009).
</p>
1309 <div class=
"paragraph">
1310 <p>But Ingo Molnar and H. Peter Anvin (another well known linux kernel
1311 developer) both complained that sometimes the best bisection points
1312 all happened to be in an area where all the commits are
1313 untestable. And in this case the user was asked to test many
1314 untestable commits, which could be very inefficient.
</p>
1316 <div class=
"paragraph">
1317 <p>Indeed untestable commits are often untestable because a breakage was
1318 introduced at one time, and that breakage was fixed only after many
1319 other commits were introduced.
</p>
1321 <div class=
"paragraph">
1322 <p>This breakage is of course most of the time unrelated to the breakage
1323 we are trying to locate in the commit graph. But it prevents us to
1324 know if the interesting
"bad behavior" is present or not.
</p>
1326 <div class=
"paragraph">
1327 <p>So it is a fact that commits near an untestable commit have a high
1328 probability of being untestable themselves. And the best bisection
1329 commits are often found together too (due to the bisection algorithm).
</p>
1331 <div class=
"paragraph">
1332 <p>This is why it is a bad idea to just chose the next best unskipped
1333 bisection commit when the first one has been skipped.
</p>
1335 <div class=
"paragraph">
1336 <p>We found that most commits on the graph may give quite a lot of
1337 information when they are tested. And the commits that will not on
1338 average give a lot of information are the one near the good and bad
1341 <div class=
"paragraph">
1342 <p>So using a PRNG with a bias to favor commits away from the good and
1343 bad commits looked like a good choice.
</p>
1345 <div class=
"paragraph">
1346 <p>One obvious improvement to this algorithm would be to look for a
1347 commit that has an associated value near the one of the best bisection
1348 commit, and that is on another branch, before using the PRNG. Because
1349 if such a commit exists, then it is not very likely to be untestable
1350 too, so it will probably give more information than a nearly randomly
1355 <h3 id=
"_checking_merge_bases">Checking merge bases
</h3>
1356 <div class=
"paragraph">
1357 <p>There is another tweak in the bisection algorithm that has not been
1358 described in the
"bisection algorithm" above.
</p>
1360 <div class=
"paragraph">
1361 <p>We supposed in the previous examples that the
"good" commits were
1362 ancestors of the
"bad" commit. But this is not a requirement of
"git
1365 <div class=
"paragraph">
1366 <p>Of course the
"bad" commit cannot be an ancestor of a
"good" commit,
1367 because the ancestors of the good commits are supposed to be
1368 "good". And all the
"good" commits must be related to the bad commit.
1369 They cannot be on a branch that has no link with the branch of the
1370 "bad" commit. But it is possible for a good commit to be related to a
1371 bad commit and yet not be neither one of its ancestor nor one of its
1374 <div class=
"paragraph">
1375 <p>For example, there can be a
"main" branch, and a
"dev" branch that was
1376 forked of the main branch at a commit named
"D" like this:
</p>
1378 <div class=
"listingblock">
1379 <div class=
"content">
1380 <pre>A-B-C-D-E-F-G
<--main
1382 H-I-J
<--dev
</pre>
1385 <div class=
"paragraph">
1386 <p>The commit
"D" is called a
"merge base" for branch
"main" and
"dev"
1387 because it
’s the best common ancestor for these branches for a merge.
</p>
1389 <div class=
"paragraph">
1390 <p>Now let
’s suppose that commit J is bad and commit G is good and that
1391 we apply the bisection algorithm like it has been previously
1394 <div class=
"paragraph">
1395 <p>As described in step
1) b) of the bisection algorithm, we remove all
1396 the ancestors of the good commits because they are supposed to be good
1399 <div class=
"paragraph">
1400 <p>So we would be left with only:
</p>
1402 <div class=
"listingblock">
1403 <div class=
"content">
1407 <div class=
"paragraph">
1408 <p>But what happens if the first bad commit is
"B" and if it has been
1409 fixed in the
"main" branch by commit
"F"?
</p>
1411 <div class=
"paragraph">
1412 <p>The result of such a bisection would be that we would find that H is
1413 the first bad commit, when in fact it
’s B. So that would be wrong!
</p>
1415 <div class=
"paragraph">
1416 <p>And yes it can happen in practice that people working on one branch
1417 are not aware that people working on another branch fixed a bug! It
1418 could also happen that F fixed more than one bug or that it is a
1419 revert of some big development effort that was not ready to be
1422 <div class=
"paragraph">
1423 <p>In fact development teams often maintain both a development branch and
1424 a maintenance branch, and it would be quite easy for them if
"git
1425 bisect" just worked when they want to bisect a regression on the
1426 development branch that is not on the maintenance branch. They should
1427 be able to start bisecting using:
</p>
1429 <div class=
"listingblock">
1430 <div class=
"content">
1431 <pre>$ git bisect start dev main
</pre>
1434 <div class=
"paragraph">
1435 <p>To enable that additional nice feature, when a bisection is started
1436 and when some good commits are not ancestors of the bad commit, we
1437 first compute the merge bases between the bad and the good commits and
1438 we chose these merge bases as the first commits that will be checked
1441 <div class=
"paragraph">
1442 <p>If it happens that one merge base is bad, then the bisection process
1443 is stopped with a message like:
</p>
1445 <div class=
"listingblock">
1446 <div class=
"content">
1447 <pre>The merge base BBBBBB is bad.
1448 This means the bug has been fixed between BBBBBB and [GGGGGG,...].
</pre>
1451 <div class=
"paragraph">
1452 <p>where BBBBBB is the sha1 hash of the bad merge base and [GGGGGG,
…​]
1453 is a comma separated list of the sha1 of the good commits.
</p>
1455 <div class=
"paragraph">
1456 <p>If some of the merge bases are skipped, then the bisection process
1457 continues, but the following message is printed for each skipped merge
1460 <div class=
"listingblock">
1461 <div class=
"content">
1462 <pre>Warning: the merge base between BBBBBB and [GGGGGG,...] must be skipped.
1463 So we cannot be sure the first bad commit is between MMMMMM and BBBBBB.
1464 We continue anyway.
</pre>
1467 <div class=
"paragraph">
1468 <p>where BBBBBB is the sha1 hash of the bad commit, MMMMMM is the sha1
1469 hash of the merge base that is skipped and [GGGGGG,
…​] is a comma
1470 separated list of the sha1 of the good commits.
</p>
1472 <div class=
"paragraph">
1473 <p>So if there is no bad merge base, the bisection process continues as
1474 usual after this step.
</p>
1480 <h2 id=
"_best_bisecting_practices">Best bisecting practices
</h2>
1481 <div class=
"sectionbody">
1483 <h3 id=
"_using_test_suites_and_git_bisect_together">Using test suites and git bisect together
</h3>
1484 <div class=
"paragraph">
1485 <p>If you both have a test suite and use git bisect, then it becomes less
1486 important to check that all tests pass after each commit. Though of
1487 course it is probably a good idea to have some checks to avoid
1488 breaking too many things because it could make bisecting other bugs
1491 <div class=
"paragraph">
1492 <p>You can focus your efforts to check at a few points (for example rc
1493 and beta releases) that all the T test cases pass for all the N
1494 configurations. And when some tests don
’t pass you can use
"git
1495 bisect" (or better
"git bisect run"). So you should perform roughly:
</p>
1497 <div class=
"listingblock">
1498 <div class=
"content">
1499 <pre>c * N * T + b * M * log2(M) tests
</pre>
1502 <div class=
"paragraph">
1503 <p>where c is the number of rounds of test (so a small constant) and b is
1504 the ratio of bug per commit (hopefully a small constant too).
</p>
1506 <div class=
"paragraph">
1507 <p>So of course it
’s much better as it
’s O(N * T) vs O(N * T * M) if
1508 you would test everything after each commit.
</p>
1510 <div class=
"paragraph">
1511 <p>This means that test suites are good to prevent some bugs from being
1512 committed and they are also quite good to tell you that you have some
1513 bugs. But they are not so good to tell you where some bugs have been
1514 introduced. To tell you that efficiently, git bisect is needed.
</p>
1516 <div class=
"paragraph">
1517 <p>The other nice thing with test suites, is that when you have one, you
1518 already know how to test for bad behavior. So you can use this
1519 knowledge to create a new test case for
"git bisect" when it appears
1520 that there is a regression. So it will be easier to bisect the bug and
1521 fix it. And then you can add the test case you just created to your
1524 <div class=
"paragraph">
1525 <p>So if you know how to create test cases and how to bisect, you will be
1526 subject to a virtuous circle:
</p>
1528 <div class=
"paragraph">
1529 <p>more tests
⇒ easier to create tests
⇒ easier to bisect
⇒ more tests
</p>
1531 <div class=
"paragraph">
1532 <p>So test suites and
"git bisect" are complementary tools that are very
1533 powerful and efficient when used together.
</p>
1537 <h3 id=
"_bisecting_build_failures">Bisecting build failures
</h3>
1538 <div class=
"paragraph">
1539 <p>You can very easily automatically bisect broken builds using something
1542 <div class=
"listingblock">
1543 <div class=
"content">
1544 <pre>$ git bisect start BAD GOOD
1545 $ git bisect run make
</pre>
1550 <h3 id=
"_passing_sh_c_some_commands_to_git_bisect_run">Passing sh -c
"some commands" to
"git bisect run"</h3>
1551 <div class=
"paragraph">
1554 <div class=
"listingblock">
1555 <div class=
"content">
1556 <pre>$ git bisect run sh -c
"make || exit 125; ./my_app | grep 'good output'"</pre>
1559 <div class=
"paragraph">
1560 <p>On the other hand if you do this often, then it can be worth having
1561 scripts to avoid too much typing.
</p>
1565 <h3 id=
"_finding_performance_regressions">Finding performance regressions
</h3>
1566 <div class=
"paragraph">
1567 <p>Here is an example script that comes slightly modified from a real
1568 world script used by Junio Hamano
<a href=
"#4">[
4]
</a>.
</p>
1570 <div class=
"paragraph">
1571 <p>This script can be passed to
"git bisect run" to find the commit that
1572 introduced a performance regression:
</p>
1574 <div class=
"listingblock">
1575 <div class=
"content">
1578 # Build errors are not what I am interested in.
1579 make my_app || exit
255
1581 # We are checking if it stops in a reasonable amount of time, so
1582 # let it run in the background...
1584 ./my_app
>log
2>&1 &
1586 # ... and grab its process ID.
1589 # ... and then wait for sufficiently long.
1592 # ... and then see if the process is still there.
1595 # It is still running -- that is bad.
1596 kill $pid; sleep
1; kill $pid;
1599 # It has already finished (the $pid process was no more),
1607 <h3 id=
"_following_general_best_practices">Following general best practices
</h3>
1608 <div class=
"paragraph">
1609 <p>It is obviously a good idea not to have commits with changes that
1610 knowingly break things, even if some other commits later fix the
1613 <div class=
"paragraph">
1614 <p>It is also a good idea when using any VCS to have only one small
1615 logical change in each commit.
</p>
1617 <div class=
"paragraph">
1618 <p>The smaller the changes in your commit, the most effective
"git
1619 bisect" will be. And you will probably need
"git bisect" less in the
1620 first place, as small changes are easier to review even if they are
1621 only reviewed by the committer.
</p>
1623 <div class=
"paragraph">
1624 <p>Another good idea is to have good commit messages. They can be very
1625 helpful to understand why some changes were made.
</p>
1627 <div class=
"paragraph">
1628 <p>These general best practices are very helpful if you bisect often.
</p>
1632 <h3 id=
"_avoiding_bug_prone_merges">Avoiding bug prone merges
</h3>
1633 <div class=
"paragraph">
1634 <p>First merges by themselves can introduce some regressions even when
1635 the merge needs no source code conflict resolution. This is because a
1636 semantic change can happen in one branch while the other branch is not
1639 <div class=
"paragraph">
1640 <p>For example one branch can change the semantic of a function while the
1641 other branch add more calls to the same function.
</p>
1643 <div class=
"paragraph">
1644 <p>This is made much worse if many files have to be fixed to resolve
1645 conflicts. That
’s why such merges are called
"evil merges". They can
1646 make regressions very difficult to track down. It can even be
1647 misleading to know the first bad commit if it happens to be such a
1648 merge, because people might think that the bug comes from bad conflict
1649 resolution when it comes from a semantic change in one branch.
</p>
1651 <div class=
"paragraph">
1652 <p>Anyway
"git rebase" can be used to linearize history. This can be used
1653 either to avoid merging in the first place. Or it can be used to
1654 bisect on a linear history instead of the non linear one, as this
1655 should give more information in case of a semantic change in one
1658 <div class=
"paragraph">
1659 <p>Merges can be also made simpler by using smaller branches or by using
1660 many topic branches instead of only long version related branches.
</p>
1662 <div class=
"paragraph">
1663 <p>And testing can be done more often in special integration branches
1664 like linux-next for the linux kernel.
</p>
1668 <h3 id=
"_adapting_your_work_flow">Adapting your work-flow
</h3>
1669 <div class=
"paragraph">
1670 <p>A special work-flow to process regressions can give great results.
</p>
1672 <div class=
"paragraph">
1673 <p>Here is an example of a work-flow used by Andreas Ericsson:
</p>
1678 <p>write, in the test suite, a test script that exposes the regression
</p>
1681 <p>use
"git bisect run" to find the commit that introduced it
</p>
1684 <p>fix the bug that is often made obvious by the previous step
</p>
1687 <p>commit both the fix and the test script (and if needed more tests)
</p>
1691 <div class=
"paragraph">
1692 <p>And here is what Andreas said about this work-flow
<a href=
"#5">[
5]
</a>:
</p>
1694 <div class=
"quoteblock">
1696 <div class=
"paragraph">
1697 <p>To give some hard figures, we used to have an average report-to-fix
1698 cycle of
142.6 hours (according to our somewhat weird bug-tracker
1699 which just measures wall-clock time). Since we moved to Git, we
’ve
1700 lowered that to
16.2 hours. Primarily because we can stay on top of
1701 the bug fixing now, and because everyone
’s jockeying to get to fix
1702 bugs (we
’re quite proud of how lazy we are to let Git find the bugs
1703 for us). Each new release results in ~
40% fewer bugs (almost certainly
1704 due to how we now feel about writing tests).
</p>
1708 <div class=
"paragraph">
1709 <p>Clearly this work-flow uses the virtuous circle between test suites
1710 and
"git bisect". In fact it makes it the standard procedure to deal
1711 with regression.
</p>
1713 <div class=
"paragraph">
1714 <p>In other messages Andreas says that they also use the
"best practices"
1715 described above: small logical commits, topic branches, no evil
1716 merge,
…​ These practices all improve the bisectability of the commit
1717 graph, by making it easier and more useful to bisect.
</p>
1719 <div class=
"paragraph">
1720 <p>So a good work-flow should be designed around the above points. That
1721 is making bisecting easier, more useful and standard.
</p>
1725 <h3 id=
"_involving_qa_people_and_if_possible_end_users">Involving QA people and if possible end users
</h3>
1726 <div class=
"paragraph">
1727 <p>One nice about
"git bisect" is that it is not only a developer
1728 tool. It can effectively be used by QA people or even end users (if
1729 they have access to the source code or if they can get access to all
1732 <div class=
"paragraph">
1733 <p>There was a discussion at one point on the linux kernel mailing list
1734 of whether it was ok to always ask end user to bisect, and very good
1735 points were made to support the point of view that it is ok.
</p>
1737 <div class=
"paragraph">
1738 <p>For example David Miller wrote
<a href=
"#6">[
6]
</a>:
</p>
1740 <div class=
"quoteblock">
1742 <div class=
"paragraph">
1743 <p>What people don
’t get is that this is a situation where the
"end node
1744 principle" applies. When you have limited resources (here: developers)
1745 you don
’t push the bulk of the burden upon them. Instead you push
1746 things out to the resource you have a lot of, the end nodes (here:
1747 users), so that the situation actually scales.
</p>
1751 <div class=
"paragraph">
1752 <p>This means that it is often
"cheaper" if QA people or end users can do
1755 <div class=
"paragraph">
1756 <p>What is interesting too is that end users that are reporting bugs (or
1757 QA people that reproduced a bug) have access to the environment where
1758 the bug happens. So they can often more easily reproduce a
1759 regression. And if they can bisect, then more information will be
1760 extracted from the environment where the bug happens, which means that
1761 it will be easier to understand and then fix the bug.
</p>
1763 <div class=
"paragraph">
1764 <p>For open source projects it can be a good way to get more useful
1765 contributions from end users, and to introduce them to QA and
1766 development activities.
</p>
1770 <h3 id=
"_using_complex_scripts">Using complex scripts
</h3>
1771 <div class=
"paragraph">
1772 <p>In some cases like for kernel development it can be worth developing
1773 complex scripts to be able to fully automate bisecting.
</p>
1775 <div class=
"paragraph">
1776 <p>Here is what Ingo Molnar says about that
<a href=
"#7">[
7]
</a>:
</p>
1778 <div class=
"quoteblock">
1780 <div class=
"paragraph">
1781 <p>i have a fully automated bootup-hang bisection script. It is based on
1782 "git-bisect run". I run the script, it builds and boots kernels fully
1783 automatically, and when the bootup fails (the script notices that via
1784 the serial log, which it continuously watches - or via a timeout, if
1785 the system does not come up within
10 minutes it
’s a
"bad" kernel),
1786 the script raises my attention via a beep and i power cycle the test
1787 box. (yeah, i should make use of a managed power outlet to
100%
1794 <h3 id=
"_combining_test_suites_git_bisect_and_other_systems_together">Combining test suites, git bisect and other systems together
</h3>
1795 <div class=
"paragraph">
1796 <p>We have seen that test suites and git bisect are very powerful when
1797 used together. It can be even more powerful if you can combine them
1798 with other systems.
</p>
1800 <div class=
"paragraph">
1801 <p>For example some test suites could be run automatically at night with
1802 some unusual (or even random) configurations. And if a regression is
1803 found by a test suite, then
"git bisect" can be automatically
1804 launched, and its result can be emailed to the author of the first bad
1805 commit found by
"git bisect", and perhaps other people too. And a new
1806 entry in the bug tracking system could be automatically created too.
</p>
1812 <h2 id=
"_the_future_of_bisecting">The future of bisecting
</h2>
1813 <div class=
"sectionbody">
1815 <h3 id=
"_git_replace">"git replace"</h3>
1816 <div class=
"paragraph">
1817 <p>We saw earlier that
"git bisect skip" is now using a PRNG to try to
1818 avoid areas in the commit graph where commits are untestable. The
1819 problem is that sometimes the first bad commit will be in an
1820 untestable area.
</p>
1822 <div class=
"paragraph">
1823 <p>To simplify the discussion we will suppose that the untestable area is
1824 a simple string of commits and that it was created by a breakage
1825 introduced by one commit (let
’s call it BBC for bisect breaking
1826 commit) and later fixed by another one (let
’s call it BFC for bisect
1829 <div class=
"paragraph">
1832 <div class=
"listingblock">
1833 <div class=
"content">
1834 <pre>...-Y-BBC-X1-X2-X3-X4-X5-X6-BFC-Z-...
</pre>
1837 <div class=
"paragraph">
1838 <p>where we know that Y is good and BFC is bad, and where BBC and X1 to
1839 X6 are untestable.
</p>
1841 <div class=
"paragraph">
1842 <p>In this case if you are bisecting manually, what you can do is create
1843 a special branch that starts just before the BBC. The first commit in
1844 this branch should be the BBC with the BFC squashed into it. And the
1845 other commits in the branch should be the commits between BBC and BFC
1846 rebased on the first commit of the branch and then the commit after
1847 BFC also rebased on.
</p>
1849 <div class=
"paragraph">
1852 <div class=
"listingblock">
1853 <div class=
"content">
1854 <pre> (BBC+BFC)-X1'-X2'-X3'-X4'-X5'-X6'-Z'
1856 ...-Y-BBC-X1-X2-X3-X4-X5-X6-BFC-Z-...
</pre>
1859 <div class=
"paragraph">
1860 <p>where commits quoted with ' have been rebased.
</p>
1862 <div class=
"paragraph">
1863 <p>You can easily create such a branch with Git using interactive rebase.
</p>
1865 <div class=
"paragraph">
1866 <p>For example using:
</p>
1868 <div class=
"listingblock">
1869 <div class=
"content">
1870 <pre>$ git rebase -i Y Z
</pre>
1873 <div class=
"paragraph">
1874 <p>and then moving BFC after BBC and squashing it.
</p>
1876 <div class=
"paragraph">
1877 <p>After that you can start bisecting as usual in the new branch and you
1878 should eventually find the first bad commit.
</p>
1880 <div class=
"paragraph">
1883 <div class=
"listingblock">
1884 <div class=
"content">
1885 <pre>$ git bisect start Z' Y
</pre>
1888 <div class=
"paragraph">
1889 <p>If you are using
"git bisect run", you can use the same manual fix up
1890 as above, and then start another
"git bisect run" in the special
1891 branch. Or as the
"git bisect" man page says, the script passed to
1892 "git bisect run" can apply a patch before it compiles and test the
1893 software
<a href=
"#8">[
8]
</a>. The patch should turn a current untestable commits
1894 into a testable one. So the testing will result in
"good" or
"bad" and
1895 "git bisect" will be able to find the first bad commit. And the script
1896 should not forget to remove the patch once the testing is done before
1897 exiting from the script.
</p>
1899 <div class=
"paragraph">
1900 <p>(Note that instead of a patch you can use
"git cherry-pick BFC" to
1901 apply the fix, and in this case you should use
"git reset --hard
1902 HEAD^" to revert the cherry-pick after testing and before returning
1903 from the script.)
</p>
1905 <div class=
"paragraph">
1906 <p>But the above ways to work around untestable areas are a little bit
1907 clunky. Using special branches is nice because these branches can be
1908 shared by developers like usual branches, but the risk is that people
1909 will get many such branches. And it disrupts the normal
"git bisect"
1910 work-flow. So, if you want to use
"git bisect run" completely
1911 automatically, you have to add special code in your script to restart
1912 bisection in the special branches.
</p>
1914 <div class=
"paragraph">
1915 <p>Anyway one can notice in the above special branch example that the Z'
1916 and Z commits should point to the same source code state (the same
1917 "tree" in git parlance). That
’s because Z' result from applying the
1918 same changes as Z just in a slightly different order.
</p>
1920 <div class=
"paragraph">
1921 <p>So if we could just
"replace" Z by Z' when we bisect, then we would
1922 not need to add anything to a script. It would just work for anyone in
1923 the project sharing the special branches and the replacements.
</p>
1925 <div class=
"paragraph">
1926 <p>With the example above that would give:
</p>
1928 <div class=
"listingblock">
1929 <div class=
"content">
1930 <pre> (BBC+BFC)-X1'-X2'-X3'-X4'-X5'-X6'-Z'-...
1932 ...-Y-BBC-X1-X2-X3-X4-X5-X6-BFC-Z
</pre>
1935 <div class=
"paragraph">
1936 <p>That
’s why the
"git replace" command was created. Technically it
1937 stores replacements
"refs" in the
"refs/replace/" hierarchy. These
1938 "refs" are like branches (that are stored in
"refs/heads/") or tags
1939 (that are stored in
"refs/tags"), and that means that they can
1940 automatically be shared like branches or tags among developers.
</p>
1942 <div class=
"paragraph">
1943 <p>"git replace" is a very powerful mechanism. It can be used to fix
1944 commits in already released history, for example to change the commit
1945 message or the author. And it can also be used instead of git
"grafts"
1946 to link a repository with another old repository.
</p>
1948 <div class=
"paragraph">
1949 <p>In fact it
’s this last feature that
"sold" it to the Git community, so
1950 it is now in the
"master" branch of Git
’s Git repository and it should
1951 be released in Git
1.6.5 in October or November
2009.
</p>
1953 <div class=
"paragraph">
1954 <p>One problem with
"git replace" is that currently it stores all the
1955 replacements refs in
"refs/replace/", but it would be perhaps better
1956 if the replacement refs that are useful only for bisecting would be in
1957 "refs/replace/bisect/". This way the replacement refs could be used
1958 only for bisecting, while other refs directly in
"refs/replace/" would
1959 be used nearly all the time.
</p>
1963 <h3 id=
"_bisecting_sporadic_bugs">Bisecting sporadic bugs
</h3>
1964 <div class=
"paragraph">
1965 <p>Another possible improvement to
"git bisect" would be to optionally
1966 add some redundancy to the tests performed so that it would be more
1967 reliable when tracking sporadic bugs.
</p>
1969 <div class=
"paragraph">
1970 <p>This has been requested by some kernel developers because some bugs
1971 called sporadic bugs do not appear in all the kernel builds because
1972 they are very dependent on the compiler output.
</p>
1974 <div class=
"paragraph">
1975 <p>The idea is that every
3 test for example,
"git bisect" could ask the
1976 user to test a commit that has already been found to be
"good" or
1977 "bad" (because one of its descendants or one of its ancestors has been
1978 found to be
"good" or
"bad" respectively). If it happens that a commit
1979 has been previously incorrectly classified then the bisection can be
1980 aborted early, hopefully before too many mistakes have been made. Then
1981 the user will have to look at what happened and then restart the
1982 bisection using a fixed bisect log.
</p>
1984 <div class=
"paragraph">
1985 <p>There is already a project called BBChop created by Ealdwulf Wuffinga
1986 on Github that does something like that using Bayesian Search Theory
1987 <a href=
"#9">[
9]
</a>:
</p>
1989 <div class=
"quoteblock">
1991 <div class=
"paragraph">
1992 <p>BBChop is like
<em>git bisect
</em> (or equivalent), but works when your bug
1993 is intermittent. That is, it works in the presence of false negatives
1994 (when a version happens to work this time even though it contains the
1995 bug). It assumes that there are no false positives (in principle, the
1996 same approach would work, but adding it may be non-trivial).
</p>
2000 <div class=
"paragraph">
2001 <p>But BBChop is independent of any VCS and it would be easier for Git
2002 users to have something integrated in Git.
</p>
2008 <h2 id=
"_conclusion">Conclusion
</h2>
2009 <div class=
"sectionbody">
2010 <div class=
"paragraph">
2011 <p>We have seen that regressions are an important problem, and that
"git
2012 bisect" has nice features that complement very well practices and
2013 other tools, especially test suites, that are generally used to fight
2014 regressions. But it might be needed to change some work-flows and
2015 (bad) habits to get the most out of it.
</p>
2017 <div class=
"paragraph">
2018 <p>Some improvements to the algorithms inside
"git bisect" are possible
2019 and some new features could help in some cases, but overall
"git
2020 bisect" works already very well, is used a lot, and is already very
2021 useful. To back up that last claim, let
’s give the final word to Ingo
2022 Molnar when he was asked by the author how much time does he think
2023 "git bisect" saves him when he uses it:
</p>
2025 <div class=
"quoteblock">
2027 <div class=
"paragraph">
2028 <p>a
<em>lot
</em>.
</p>
2030 <div class=
"paragraph">
2031 <p>About ten years ago did i do my first
<em>bisection
</em> of a Linux patch
2032 queue. That was prior the Git (and even prior the BitKeeper) days. I
2033 literally days spent sorting out patches, creating what in essence
2034 were standalone commits that i guessed to be related to that bug.
</p>
2036 <div class=
"paragraph">
2037 <p>It was a tool of absolute last resort. I
’d rather spend days looking
2038 at printk output than do a manual
<em>patch bisection
</em>.
</p>
2040 <div class=
"paragraph">
2041 <p>With Git bisect it
’s a breeze: in the best case i can get a ~
15 step
2042 kernel bisection done in
20-
30 minutes, in an automated way. Even with
2043 manual help or when bisecting multiple, overlapping bugs, it
’s rarely
2044 more than an hour.
</p>
2046 <div class=
"paragraph">
2047 <p>In fact it
’s invaluable because there are bugs i would never even
2048 <em>try
</em> to debug if it wasn
’t for git bisect. In the past there were bug
2049 patterns that were immediately hopeless for me to debug - at best i
2050 could send the crash/bug signature to lkml and hope that someone else
2051 can think of something.
</p>
2053 <div class=
"paragraph">
2054 <p>And even if a bisection fails today it tells us something valuable
2055 about the bug: that it
’s non-deterministic - timing or kernel image
2056 layout dependent.
</p>
2058 <div class=
"paragraph">
2059 <p>So git bisect is unconditional goodness - and feel free to quote that
2067 <h2 id=
"_acknowledgments">Acknowledgments
</h2>
2068 <div class=
"sectionbody">
2069 <div class=
"paragraph">
2070 <p>Many thanks to Junio Hamano for his help in reviewing this paper, for
2071 reviewing the patches I sent to the Git mailing list, for discussing
2072 some ideas and helping me improve them, for improving
"git bisect" a
2073 lot and for his awesome work in maintaining and developing Git.
</p>
2075 <div class=
"paragraph">
2076 <p>Many thanks to Ingo Molnar for giving me very useful information that
2077 appears in this paper, for commenting on this paper, for his
2078 suggestions to improve
"git bisect" and for evangelizing
"git bisect"
2079 on the linux kernel mailing lists.
</p>
2081 <div class=
"paragraph">
2082 <p>Many thanks to Linus Torvalds for inventing, developing and
2083 evangelizing
"git bisect", Git and Linux.
</p>
2085 <div class=
"paragraph">
2086 <p>Many thanks to the many other great people who helped one way or
2087 another when I worked on Git, especially to Andreas Ericsson, Johannes
2088 Schindelin, H. Peter Anvin, Daniel Barkalow, Bill Lear, John Hawley,
2089 Shawn O. Pierce, Jeff King, Sam Vilain, Jon Seymour.
</p>
2091 <div class=
"paragraph">
2092 <p>Many thanks to the Linux-Kongress program committee for choosing the
2093 author to given a talk and for publishing this paper.
</p>
2098 <h2 id=
"_references">References
</h2>
2099 <div class=
"sectionbody">
2103 <p>[[[
1]]]
<a href=
"https://web.archive.org/web/20091206032101/http://www.nist.gov/public_affairs/releases/n02-10.htm"><em>Software Errors Cost U.S. Economy $
59.5 Billion Annually
</em>. Nist News Release.
</a> See also
<a href=
"https://www.nist.gov/system/files/documents/director/planning/report02-3.pdf"><em>The Economic Impacts of Inadequate Infratructure for Software Testing
</em>. Nist Planning Report
02-
3</a>, Executive Summary and Chapter
8.
</p>
2106 <p>[[[
2]]]
<a href=
"https://www.oracle.com/java/technologies/javase/codeconventions-introduction.html"><em>Code Conventions for the Java Programming Language:
1. Introduction
</em>. Sun Microsystems.
</a></p>
2109 <p>[[[
3]]]
<a href=
"https://en.wikipedia.org/wiki/Software_maintenance"><em>Software maintenance
</em>. Wikipedia.
</a></p>
2112 <p>[[[
4]]]
<a href=
"https://lore.kernel.org/git/7vps5xsbwp.fsf_-_@assigned-by-dhcp.cox.net/">Junio C Hamano.
<em>Automated bisect success story
</em>.
</a></p>
2115 <p>[[[
5]]]
<a href=
"https://lwn.net/Articles/317154/">Christian Couder.
<em>Fully automated bisecting with
"git bisect run"</em>. LWN.net.
</a></p>
2118 <p>[[[
6]]]
<a href=
"https://lwn.net/Articles/277872/">Jonathan Corbet.
<em>Bisection divides users and developers
</em>. LWN.net.
</a></p>
2121 <p>[[[
7]]]
<a href=
"https://lore.kernel.org/lkml/20071207113734.GA14598@elte.hu/">Ingo Molnar.
<em>Re: BUG
2.6.23-rc3 can
’t see sd partitions on Alpha
</em>. Linux-kernel mailing list.
</a></p>
2124 <p>[[[
8]]]
<a href=
"https://www.kernel.org/pub/software/scm/git/docs/git-bisect.html">Junio C Hamano and the git-list.
<em>git-bisect(
1) Manual Page
</em>. Linux Kernel Archives.
</a></p>
2127 <p>[[[
9]]]
<a href=
"https://github.com/Ealdwulf/bbchop">Ealdwulf.
<em>bbchop
</em>. GitHub.
</a></p>
2135 <div id=
"footer-text">
2136 Last updated
2023-
01-
21 17:
52:
14 -
0800