Skip to content

Commit f10fccb

Browse files
committed
fix(doctor): address PR feedback
- Check errors in test setup helpers instead of silently discarding them (health_test.go, git_test.go) - Skip provenance-comment assertions on git < 2.45 instead of failing (config_test.go, e2e/doctor_test.go) - Discriminate "unknown option" from real errors in setForkPointWithComment so permissions/locking failures propagate instead of triggering the old-git warning (cmd/doctor.go, internal/config/config.go) - Delete unused GetConfigPath (dead code left over from the commentForkPointChange removal) Note: PR.md also updated to clarify abbreviated SHA in provenance comments (not included in this commit; will be added manually).
1 parent 8da1dd4 commit f10fccb

7 files changed

Lines changed: 74 additions & 28 deletions

File tree

cmd/doctor.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package cmd
44
import (
55
"fmt"
66
"os"
7+
"strings"
78
"time"
89

910
"github.com/boneskull/gh-stack/internal/config"
@@ -248,14 +249,24 @@ func setForkPointWithComment(s *style.Style, cfg *config.Config, branch, oldSHA,
248249
return nil
249250
}
250251

251-
// --comment likely unsupported; fall back to plain set and warn once.
252+
// Only fall back for unsupported --comment flag; propagate real errors.
253+
if !isUnsupportedCommentErr(err) {
254+
return err
255+
}
252256
if setErr := cfg.SetForkPoint(branch, newSHA); setErr != nil {
253257
return setErr
254258
}
255259
warnOldGit(s)
256260
return nil
257261
}
258262

263+
// isUnsupportedCommentErr returns true if the error indicates that
264+
// git config --comment is not recognized (git < 2.45).
265+
func isUnsupportedCommentErr(err error) bool {
266+
msg := err.Error()
267+
return strings.Contains(msg, "unknown option")
268+
}
269+
259270
var oldGitWarned bool
260271

261272
// warnOldGit prints a one-time warning that the user's git version is too old

e2e/doctor_test.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,17 +139,19 @@ func TestDoctorFixUpdatesConfig(t *testing.T) {
139139
// Verify inline provenance comment was written to the config value line.
140140
// git config --comment (Git 2.45+) appends "# replaces <abbrev> (<timestamp>)"
141141
// on the same line as the stackForkPoint value.
142+
// On older git the comment won't be present; that's fine (doctor falls back gracefully).
142143
configData, err := os.ReadFile(filepath.Join(env.WorkDir, ".git", "config"))
143144
if err != nil {
144145
t.Fatalf("failed to read .git/config: %v", err)
145146
}
146147
configStr := string(configData)
147-
if !strings.Contains(configStr, "# replaces") {
148-
t.Error("expected inline provenance comment '# replaces ...' in .git/config")
149-
}
150-
abbrevOld := originalFP[:7]
151-
if !strings.Contains(configStr, abbrevOld) {
152-
t.Errorf("expected abbreviated old fork point %s in config comment", abbrevOld)
148+
if strings.Contains(configStr, "# replaces") {
149+
abbrevOld := originalFP[:7]
150+
if !strings.Contains(configStr, abbrevOld) {
151+
t.Errorf("expected abbreviated old fork point %s in config comment", abbrevOld)
152+
}
153+
} else {
154+
t.Log("git config --comment not supported; skipping provenance comment assertion")
153155
}
154156
}
155157

internal/config/config.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,20 @@ func (c *Config) SetForkPoint(branch, sha string) error {
118118

119119
// SetForkPointWithComment stores the fork point SHA with an inline comment.
120120
// Requires Git 2.45+. The comment appears after the value on the same line.
121+
// Returns an error with stderr content on failure, so callers can distinguish
122+
// "unknown option" (old git) from other failures.
121123
func (c *Config) SetForkPointWithComment(branch, sha, comment string) error {
122124
key := "branch." + branch + ".stackForkPoint"
123-
return exec.Command("git", "-C", c.repoPath, "config", "--comment", comment, key, sha).Run()
125+
cmd := exec.Command("git", "-C", c.repoPath, "config", "--comment", comment, key, sha)
126+
var stderr bytes.Buffer
127+
cmd.Stderr = &stderr
128+
if err := cmd.Run(); err != nil {
129+
if stderr.Len() > 0 {
130+
return errors.New(strings.TrimSpace(stderr.String()))
131+
}
132+
return err
133+
}
134+
return nil
124135
}
125136

126137
// RemoveForkPoint removes the stored fork point for a branch.

internal/config/config_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,8 @@ func TestSetForkPointWithComment(t *testing.T) {
203203
comment := "replaces deadbee (2026-02-14T00:00:00Z)"
204204

205205
if err := cfg.SetForkPointWithComment("feature", sha, comment); err != nil {
206-
t.Fatalf("SetForkPointWithComment failed: %v", err)
206+
// git config --comment requires Git 2.45+; skip on older versions.
207+
t.Skipf("SetForkPointWithComment not supported (git too old?): %v", err)
207208
}
208209

209210
// Value should be readable via GetForkPoint (git config ignores inline comments)

internal/git/git.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -366,13 +366,6 @@ func (g *Git) GetGitDir() string {
366366
return filepath.Join(g.repoPath, ".git")
367367
}
368368

369-
// GetConfigPath returns the path to the repo's git config file,
370-
// resolved via `git rev-parse --git-path config` so it works
371-
// correctly in linked worktrees and submodules.
372-
func (g *Git) GetConfigPath() (string, error) {
373-
return g.run("rev-parse", "--git-path", "config")
374-
}
375-
376369
// Fetch fetches from origin.
377370
func (g *Git) Fetch() error {
378371
return g.runInteractive("fetch", "origin")

internal/git/git_test.go

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -730,15 +730,33 @@ func TestIsAncestor(t *testing.T) {
730730
initial, _ := g.GetTip("HEAD")
731731

732732
// Create a linear chain: initial -> A -> B
733-
os.WriteFile(filepath.Join(dir, "a.txt"), []byte("a"), 0644)
734-
exec.Command("git", "-C", dir, "add", ".").Run()
735-
exec.Command("git", "-C", dir, "commit", "-m", "commit A").Run()
736-
shaA, _ := g.GetTip("HEAD")
733+
if err := os.WriteFile(filepath.Join(dir, "a.txt"), []byte("a"), 0644); err != nil {
734+
t.Fatalf("WriteFile failed: %v", err)
735+
}
736+
if out, err := exec.Command("git", "-C", dir, "add", ".").CombinedOutput(); err != nil {
737+
t.Fatalf("git add failed: %v\n%s", err, out)
738+
}
739+
if out, err := exec.Command("git", "-C", dir, "commit", "-m", "commit A").CombinedOutput(); err != nil {
740+
t.Fatalf("git commit failed: %v\n%s", err, out)
741+
}
742+
shaA, err := g.GetTip("HEAD")
743+
if err != nil {
744+
t.Fatalf("GetTip failed: %v", err)
745+
}
737746

738-
os.WriteFile(filepath.Join(dir, "b.txt"), []byte("b"), 0644)
739-
exec.Command("git", "-C", dir, "add", ".").Run()
740-
exec.Command("git", "-C", dir, "commit", "-m", "commit B").Run()
741-
shaB, _ := g.GetTip("HEAD")
747+
if err := os.WriteFile(filepath.Join(dir, "b.txt"), []byte("b"), 0644); err != nil {
748+
t.Fatalf("WriteFile failed: %v", err)
749+
}
750+
if out, err := exec.Command("git", "-C", dir, "add", ".").CombinedOutput(); err != nil {
751+
t.Fatalf("git add failed: %v\n%s", err, out)
752+
}
753+
if out, err := exec.Command("git", "-C", dir, "commit", "-m", "commit B").CombinedOutput(); err != nil {
754+
t.Fatalf("git commit failed: %v\n%s", err, out)
755+
}
756+
shaB, err := g.GetTip("HEAD")
757+
if err != nil {
758+
t.Fatalf("GetTip failed: %v", err)
759+
}
742760

743761
// initial is ancestor of B
744762
isAnc, err := g.IsAncestor(initial, shaB)
@@ -801,9 +819,15 @@ func TestGetForkPoint(t *testing.T) {
801819
g.CreateAndCheckout("child")
802820

803821
// Add commits on child
804-
os.WriteFile(filepath.Join(dir, "child.txt"), []byte("child"), 0644)
805-
exec.Command("git", "-C", dir, "add", ".").Run()
806-
exec.Command("git", "-C", dir, "commit", "-m", "child commit").Run()
822+
if err := os.WriteFile(filepath.Join(dir, "child.txt"), []byte("child"), 0644); err != nil {
823+
t.Fatalf("WriteFile failed: %v", err)
824+
}
825+
if out, err := exec.Command("git", "-C", dir, "add", ".").CombinedOutput(); err != nil {
826+
t.Fatalf("git add failed: %v\n%s", err, out)
827+
}
828+
if out, err := exec.Command("git", "-C", dir, "commit", "-m", "child commit").CombinedOutput(); err != nil {
829+
t.Fatalf("git commit failed: %v\n%s", err, out)
830+
}
807831

808832
// GetForkPoint should return the divergence point
809833
fp, err := g.GetForkPoint(trunk, "child")

internal/health/health_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,13 @@ func TestCheckBranch_MissingParent(t *testing.T) {
128128

129129
// Now create "middle" as parent, point child at it, then delete it
130130
run := func(args ...string) {
131+
t.Helper()
131132
cmd := exec.Command("git", args...)
132133
cmd.Dir = dir
133-
cmd.Run()
134+
out, err := cmd.CombinedOutput()
135+
if err != nil {
136+
t.Fatalf("git %v failed: %v\n%s", args, err, out)
137+
}
134138
}
135139
run("checkout", trunk)
136140
run("checkout", "-b", "middle")

0 commit comments

Comments
 (0)