hk config explain stash reports git-config source but never reads it #1031
Replies: 3 comments
-
|
Root-caused and fixed on my fork branch: https://github.com/Distortedlogic/hk/tree/git-config-live-fix (diff vs jdx:main). Root cause: Fix: swap the three Regression test: Note: I could not open a PR because |
Beta Was this translation helpful? Give feedback.
-
|
Update: GitHub's PR-creation API returns Since I can't open the PR, here's the patch inline so you can From a4121aabb511eddd7be183626831f68992527cf6 Mon Sep 17 00:00:00 2001
From: distortedlogic <jermeek@gmail.com>
Date: Sat, 4 Jul 2026 14:26:24 -0400
Subject: [PATCH] settings: use Config::get_string so live git configs actually
surface values
collect_git_map called cfg.get_str(key) for enum, string, and path
settings. libgit2 rejects borrow-returning reads on a live (non-snapshot)
Config with 'get_string called on a live config object', and
Repository::config() hands out a live Config in the hot path.
Result: every string/enum/path setting silently failed to read its git
config value. Empirically 'hk config explain stash' with
'git config --global hk.stash none' set:
Before:
GIT CONFIG: hk.stash
Source: defaults # <- wrong; git-config value never seen
Current value: null
After (this PR):
GIT CONFIG: hk.stash
\u2713 Set to: none
Source: git(hk.stash)
Current value: "none"
Config::get_string returns an owned String and works on live configs
(the codebase already uses it in read_git_string_list and in the ec
settings module at line 63). Threading the same call through the enum,
string, and path branches lets the git layer participate in the merge
as the schema promises.
Regression test test_git_map_reads_string_setting_from_live_config
opens a Config directly (no snapshot), asserts get_string returns the
value, and asserts get_str still fails with the exact libgit2 message
so a future refactor cannot quietly reintroduce the borrow path.
---
src/settings.rs | 43 ++++++++++++++++++++++++++++++++++++++++---
1 file changed, 40 insertions(+), 3 deletions(-)
diff --git a/src/settings.rs b/src/settings.rs
index b4dd6ed..da531a9 100644
--- a/src/settings.rs
+++ b/src/settings.rs
@@ -435,13 +435,17 @@ impl Settings {
}
}
"string" | "enum" => {
- if let Ok(v) = cfg.get_str(key) {
- map.insert(setting_name, SettingValue::String(v.to_string()));
+ // `get_str` requires a snapshot on a live Config (fails with
+ // "get_string called on a live config object"). `get_string`
+ // returns an owned String and works on live configs, which is
+ // what `Repository::config()` hands out.
+ if let Ok(v) = cfg.get_string(key) {
+ map.insert(setting_name, SettingValue::String(v));
break;
}
}
"path" => {
- if let Ok(v) = cfg.get_str(key) {
+ if let Ok(v) = cfg.get_string(key) {
map.insert(setting_name, SettingValue::Path(PathBuf::from(v)));
break;
}
@@ -910,4 +914,37 @@ mod tests {
assert!(merged.warnings.contains("warning4"));
assert_eq!(merged.warnings.len(), 1);
}
+
+ /// Regression: `collect_git_map` must use `Config::get_string` (owned) rather
+ /// than `Config::get_str` (borrowed). libgit2 rejects borrow-returning reads
+ /// on a live (non-snapshot) Config with `get_string called on a live config
+ /// object`, and `Repository::config()` hands out a live Config. Before this
+ /// fix, every string/enum/path setting silently failed to read its git-config
+ /// value (`hk config explain <key>` reported `Source: defaults` for the git
+ /// row even after `git config --global hk.<key> ...` was set).
+ #[test]
+ fn test_git_map_reads_string_setting_from_live_config() {
+ use git2::Config;
+
+ let tmp = tempfile::tempdir().expect("tempdir");
+ let cfg_path = tmp.path().join("gitconfig");
+ std::fs::write(&cfg_path, "[hk]\n\tstash = none\n").expect("write gitconfig");
+ let mut cfg = Config::open(&cfg_path).expect("open cfg");
+ // Explicitly *not* calling snapshot() to mirror the live-config path
+ // that `Repository::config()` returns in production.
+ let value = cfg.get_string("hk.stash").expect("hk.stash must read on a live Config");
+ assert_eq!(value, "none");
+
+ // And confirm the historical `get_str` call would still fail on the
+ // same live config — pinning the exact libgit2 behavior so a future
+ // refactor doesn't quietly reintroduce it.
+ let str_err = cfg
+ .get_str("hk.stash")
+ .expect_err("get_str must fail on a live config");
+ assert!(
+ str_err.message().contains("live config object"),
+ "unexpected libgit2 error message: {:?}",
+ str_err.message()
+ );
+ }
}
--
2.55.0Or cherry-pick from the branch: Distortedlogic/hk@git-config-live-fix (single commit).
|
Beta Was this translation helpful? Give feedback.
-
|
Opened a ready-for-review PR for this in #1042: #1042 The PR applies the live-config fix by switching string, enum, and path git-config reads in Validation run:
The commit includes a This comment was generated by Codex. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Summary
hk config explain stashclaims to readhk.stashfrom git-config as a resolution source, but empirically the value never appears in the resolved output.Version
hk 1.48.0Reproduction
Runtime behavior is consistent with the display: setting
git config --global hk.stash nonedoes not prevent a subsequentHK_STASH=git hk run pre-commit --from-hookfrom stashing.Expected
Either:
hkactually consultshk.stashfrom git-config and reports the resolved value inhk config explain stash; orhk config explainoutput so downstream tooling doesn't rely on a source that isn't actually read.Impact
The
stash = "none"policy proposed inhk's own recommendedconfiguration guide becomes unenforceable at the git-config layer, so the pkl-level
stashfield is the only line of defense. That field is also overridable byHK_STASHenv and--stashCLI, so there is currently no way to makestash = "none"non-negotiable across a fleet.Beta Was this translation helpful? Give feedback.
All reactions