Skip to content

Commit a66cbdb

Browse files
fix checkout during bisect
Signed-off-by: cinereal <cinereal@riseup.net>
1 parent 8f258a3 commit a66cbdb

File tree

6 files changed

+123
-5
lines changed

6 files changed

+123
-5
lines changed

pkg/commands/git_commands/bisect.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,16 @@ func (self *BisectCommands) GetInfoForGitDir(gitDir string) *BisectInfo {
9797
currentHash := strings.TrimSpace(string(currentContent))
9898
info.current = currentHash
9999

100+
// Read actual HEAD to detect manual checkouts during bisect.
101+
// During bisect, HEAD is always a detached hash (not a symbolic ref).
102+
headContent, err := os.ReadFile(filepath.Join(gitDir, "HEAD"))
103+
if err == nil {
104+
headStr := strings.TrimSpace(string(headContent))
105+
if !strings.HasPrefix(headStr, "ref: ") {
106+
info.headHash = headStr
107+
}
108+
}
109+
100110
return info
101111
}
102112

pkg/commands/git_commands/bisect_info.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,13 @@ type BisectInfo struct {
3232
// map of commit hashes to their status
3333
statusMap map[string]BisectStatus
3434

35-
// the hash of the commit that's under test
35+
// the hash of the commit that git bisect expects to be under test
36+
// (from BISECT_EXPECTED_REV)
3637
current string
38+
39+
// the actual HEAD hash, which may differ from current if the user
40+
// manually checked out a different commit during bisect
41+
headHash string
3742
}
3843

3944
type BisectStatus int
@@ -63,6 +68,12 @@ func (self *BisectInfo) GetCurrentHash() string {
6368
return self.current
6469
}
6570

71+
// GetHeadHash returns the actual HEAD commit hash. During bisect this may
72+
// differ from GetCurrentHash() if the user manually checked out a commit.
73+
func (self *BisectInfo) GetHeadHash() string {
74+
return self.headHash
75+
}
76+
6677
func (self *BisectInfo) GetStartHash() string {
6778
return self.start
6879
}

pkg/gui/controllers/bisect_controller.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,13 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c
6868
// Originally we were allowing the user to, from the bisect menu, select whether
6969
// they were talking about the selected commit or the current bisect commit,
7070
// and that was a bit confusing (and required extra keypresses).
71-
selectCurrentAfter := info.GetCurrentHash() == "" || info.GetCurrentHash() == commit.Hash()
71+
// Use actual HEAD to determine the current commit, since the user may have
72+
// manually checked out a different commit during bisect.
73+
headHash := info.GetHeadHash()
74+
if headHash == "" {
75+
headHash = info.GetCurrentHash()
76+
}
77+
selectCurrentAfter := info.GetCurrentHash() == "" || headHash == commit.Hash()
7278
// we need to wait to reselect if our bisect commits aren't ancestors of our 'start'
7379
// ref, because we'll be reloading our commits in that case.
7480
waitToReselect := selectCurrentAfter && !self.c.Git().Bisect.ReachableFromStart(info)
@@ -78,7 +84,7 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c
7884
// use the selected commit in that case.
7985

8086
bisecting := info.GetCurrentHash() != ""
81-
hashToMark := lo.Ternary(bisecting, info.GetCurrentHash(), commit.Hash())
87+
hashToMark := lo.Ternary(bisecting, headHash, commit.Hash())
8288
shortHashToMark := utils.ShortHash(hashToMark)
8389

8490
// For marking a commit as bad, when we're not already bisecting, we require
@@ -130,7 +136,7 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c
130136
Key: 's',
131137
},
132138
}
133-
if info.GetCurrentHash() != "" && info.GetCurrentHash() != commit.Hash() {
139+
if info.GetCurrentHash() != "" && headHash != commit.Hash() {
134140
menuItems = append(menuItems, lo.ToPtr(types.MenuItem{
135141
Label: fmt.Sprintf(self.c.Tr.Bisect.SkipSelected, commit.ShortHash()),
136142
OnPress: func() error {

pkg/gui/presentation/commits.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,14 @@ func getBisectStatus(index int, commitHash string, bisectInfo *git_commands.Bise
287287
return BisectStatusNone
288288
}
289289

290-
if bisectInfo.GetCurrentHash() == commitHash {
290+
// Use actual HEAD hash to determine the current position, since the user
291+
// may have manually checked out a different commit during bisect.
292+
// Fall back to BISECT_EXPECTED_REV if HEAD hash is not available.
293+
currentHash := bisectInfo.GetHeadHash()
294+
if currentHash == "" {
295+
currentHash = bisectInfo.GetCurrentHash()
296+
}
297+
if currentHash == commitHash {
291298
return BisectStatusCurrent
292299
}
293300

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package bisect
2+
3+
import (
4+
"github.com/jesseduffield/lazygit/pkg/config"
5+
. "github.com/jesseduffield/lazygit/pkg/integration/components"
6+
)
7+
8+
var CheckoutDuringBisect = NewIntegrationTest(NewIntegrationTestArgs{
9+
Description: "Checkout a different commit during a bisect and verify the current marker follows HEAD",
10+
ExtraCmdArgs: []string{},
11+
Skip: false,
12+
SetupRepo: func(shell *Shell) {
13+
shell.
14+
NewBranch("mybranch").
15+
CreateNCommits(10)
16+
},
17+
SetupConfig: func(cfg *config.AppConfig) {
18+
cfg.GetUserConfig().Git.Log.ShowGraph = "never"
19+
},
20+
Run: func(t *TestDriver, keys config.KeybindingConfig) {
21+
t.Views().Commits().
22+
Focus().
23+
SelectedLine(Contains("CI commit 10")).
24+
Press(keys.Commits.ViewBisectOptions).
25+
Tap(func() {
26+
t.ExpectPopup().Menu().Title(Equals("Bisect")).Select(MatchesRegexp(`Mark .* as bad`)).Confirm()
27+
}).
28+
NavigateToLine(Contains("CI commit 01")).
29+
Press(keys.Commits.ViewBisectOptions).
30+
Tap(func() {
31+
t.ExpectPopup().Menu().Title(Equals("Bisect")).Select(MatchesRegexp(`Mark .* as good`)).Confirm()
32+
}).
33+
// bisect has auto-selected commit 05 as current
34+
Lines(
35+
Contains("CI commit 10").Contains("<-- bad"),
36+
Contains("CI commit 09").DoesNotContain("<--"),
37+
Contains("CI commit 08").DoesNotContain("<--"),
38+
Contains("CI commit 07").DoesNotContain("<--"),
39+
Contains("CI commit 06").DoesNotContain("<--"),
40+
Contains("CI commit 05").Contains("<-- current").IsSelected(),
41+
Contains("CI commit 04").DoesNotContain("<--"),
42+
Contains("CI commit 03").DoesNotContain("<--"),
43+
Contains("CI commit 02").DoesNotContain("<--"),
44+
Contains("CI commit 01").Contains("<-- good"),
45+
).
46+
// now checkout commit 08 manually
47+
NavigateToLine(Contains("CI commit 08")).
48+
Press(keys.Commits.CheckoutCommit).
49+
Tap(func() {
50+
t.ExpectPopup().Menu().
51+
Title(Contains("Checkout branch or commit")).
52+
Select(MatchesRegexp("Checkout commit .* as detached head")).
53+
Confirm()
54+
})
55+
56+
// after checkout, go back to commits panel and verify the current
57+
// marker moved to commit 08
58+
t.Views().Commits().
59+
Focus().
60+
Lines(
61+
Contains("CI commit 10").Contains("<-- bad"),
62+
Contains("CI commit 09").DoesNotContain("<--"),
63+
Contains("CI commit 08").Contains("<-- current"),
64+
Contains("CI commit 07").DoesNotContain("<--"),
65+
Contains("CI commit 06").DoesNotContain("<--"),
66+
Contains("CI commit 05").DoesNotContain("<--"),
67+
Contains("CI commit 04").DoesNotContain("<--"),
68+
Contains("CI commit 03").DoesNotContain("<--"),
69+
Contains("CI commit 02").DoesNotContain("<--"),
70+
Contains("CI commit 01").Contains("<-- good"),
71+
).
72+
// mark the manually checked-out commit as good via bisect menu
73+
NavigateToLine(Contains("CI commit 08")).
74+
Press(keys.Commits.ViewBisectOptions).
75+
Tap(func() {
76+
// the bisect menu should show the HEAD commit (08) as current
77+
t.ExpectPopup().Menu().Title(Equals("Bisect")).
78+
Select(MatchesRegexp(`Mark .* as good`)).Confirm()
79+
}).
80+
// after marking 08 as good, bisect narrows the range
81+
SelectedLine(Contains("<-- current"))
82+
},
83+
})

pkg/integration/tests/test_list.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636

3737
var tests = []*components.IntegrationTest{
3838
bisect.Basic,
39+
bisect.CheckoutDuringBisect,
3940
bisect.ChooseTerms,
4041
bisect.FromOtherBranch,
4142
bisect.Skip,

0 commit comments

Comments
 (0)