{"thread":{"id":"64201","subject":"[PATCH 0/2] libgit-rs: add get_bool() method to ConfigSet","startedAt":"2025-09-25T11:44:33Z","lastAt":"2025-10-08T13:36:56Z","messageCount":22,"participants":["ions via GitGitGadget","ionnss via GitGitGadget","Chris Torek","Phillip Wood","Junio C Hamano","brian m. carlson"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"527307","messageId":"pull.1977.git.1758800669.gitgitgadget@gmail.com","threadId":"64201","inReplyTo":null,"subject":"[PATCH 0/2] libgit-rs: add get_bool() method to ConfigSet","fromName":"ions via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-25T11:44:27Z","receivedAt":"2025-09-25T11:44:33Z","isPatch":true,"sender":{"key":"name:ions","avatar":null},"body":"Purpose\n\nThis pull request introduces a get_bool() method to the ConfigSet module in\nthe libgit-rs library. The goal is to enhance the functionality of ConfigSet\nby providing a way to fetch and handle boolean configuration values more\neasily and consistently.\n\nImplementation Details\n\n• Added a get_bool() method to the ConfigSet module.\n\n• The method retrieves configuration values as boolean values, ensuring\nproper parsing and error handling.\n\n• This addition simplifies the process of working with boolean\nconfigurations for developers using the ConfigSet module.\n\nTesting\n\n• Added unit tests to verify the correctness of the get_bool() method.\n\n• Tested edge cases to ensure robustness.\n\nionnss (2):\n  po: fix escaped underscores in README.md\n  libgit-rs: add get_bool() method to ConfigSet\n\n contrib/libgit-rs/src/config.rs    | 24 ++++++++++++++++++++++++\n contrib/libgit-rs/testdata/config3 |  2 ++\n po/README.md                       |  6 +++---\n 3 files changed, 29 insertions(+), 3 deletions(-)\n\n\nbase-commit: bb69721404348ea2db0a081c41ab6ebfe75bdec8\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1977%2Fionnss%2Fadd-rust-configset-get-bool-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1977/ionnss/add-rust-configset-get-bool-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/1977\n-- \ngitgitgadget\n"},{"id":"527308","messageId":"d7810781fc3af1902c99cccbd50853dd630a0080.1758800669.git.gitgitgadget@gmail.com","threadId":"64201","inReplyTo":"pull.1977.git.1758800669.gitgitgadget@gmail.com","subject":"[PATCH 1/2] po: fix escaped underscores in README.md","fromName":"ionnss via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-25T11:44:28Z","receivedAt":"2025-09-25T11:44:34Z","isPatch":true,"sender":{"key":"name:ionnss","avatar":null},"body":"From: ionnss <zara.leonardo@gmail.com>\n\nRemove unnecessary backslashes from language code examples.\nThe underscores in \"ll\\_CC\" and \"zh\\_CN\" don't need escaping\nin Markdown.\n\nSigned-off-by: ionnss <zara.leonardo@gmail.com>\n---\n po/README.md | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/po/README.md b/po/README.md\nindex ec08aa24ad..d7757bed4e 100644\n--- a/po/README.md\n+++ b/po/README.md\n@@ -13,9 +13,9 @@ We will use XX as an alias to refer to the language translation code in\n the following paragraphs, for example we use \"po/XX.po\" to refer to the\n translation file for a specific language. But this doesn't mean that\n the language code has only two letters. The language code can be in one\n-of two forms: \"ll\" or \"ll\\_CC\". Here \"ll\" is the ISO 639 two-letter\n+of two forms: \"ll\" or \"ll_CC\". Here \"ll\" is the ISO 639 two-letter\n language code and \"CC\" is the ISO 3166 two-letter code for country names\n-and subdivisions. For example: \"de\" for German language code, \"zh\\_CN\"\n+and subdivisions. For example: \"de\" for German language code, \"zh_CN\"\n for Simplified Chinese language code.\n \n \n@@ -126,7 +126,7 @@ you add a translation for the first time by running:\n make po-init PO_FILE=po/XX.po\n ```\n \n-where XX is the locale, e.g. \"de\", \"is\", \"pt\\_BR\", \"zh\\_CN\", etc.\n+where XX is the locale, e.g. \"de\", \"is\", \"pt_BR\", \"zh_CN\", etc.\n \n The newly generated message file \"po/XX.po\" is based on the core pot\n file \"po/git-core.pot\", so it contains only a minimal set of messages\n-- \ngitgitgadget\n\n"},{"id":"527309","messageId":"a5904a2ac00ea6de142344272b45f6b4697b4f98.1758800669.git.gitgitgadget@gmail.com","threadId":"64201","inReplyTo":"pull.1977.git.1758800669.gitgitgadget@gmail.com","subject":"[PATCH 2/2] libgit-rs: add get_bool() method to ConfigSet","fromName":"ionnss via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-25T11:44:29Z","receivedAt":"2025-09-25T11:44:35Z","isPatch":true,"sender":{"key":"name:ionnss","avatar":null},"body":"From: ionnss <zara.leonardo@gmail.com>\n\nAdd support for parsing boolean configuration values in the Rust\nConfigSet API. The method follows Git's standard boolean parsing\nrules, accepting true/yes/on/1 as true and false/no/off/0 as false.\n\nThe implementation reuses the existing get_string() infrastructure\nand adds case-insensitive boolean parsing logic.\n\nSigned-off-by: ionnss <zara.leonardo@gmail.com>\n---\n contrib/libgit-rs/src/config.rs    | 24 ++++++++++++++++++++++++\n contrib/libgit-rs/testdata/config3 |  2 ++\n 2 files changed, 26 insertions(+)\n\ndiff --git a/contrib/libgit-rs/src/config.rs b/contrib/libgit-rs/src/config.rs\nindex 6bf04845c8..3f4a32c72d 100644\n--- a/contrib/libgit-rs/src/config.rs\n+++ b/contrib/libgit-rs/src/config.rs\n@@ -68,6 +68,26 @@ impl ConfigSet {\n             Some(owned_str)\n         }\n     }\n+\n+    pub fn get_bool(&mut self, key: &str) -> Option<bool> {\n+        let key = CString::new(key).expect(\"Couldn't convert key to CString\");\n+        let mut val: *mut c_char = std::ptr::null_mut();\n+        unsafe {\n+            if libgit_configset_get_string(self.0, key.as_ptr(), &mut val as *mut *mut c_char) != 0\n+            {\n+                return None;\n+            }\n+            let borrowed_str = CStr::from_ptr(val);\n+            let owned_str =\n+                String::from(borrowed_str.to_str().expect(\"Couldn't convert val to str\"));\n+            free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side\n+            match owned_str.to_lowercase().as_str() {\n+                \"true\" | \"yes\" | \"on\" | \"1\" => Some(true),\n+                \"false\" | \"no\" | \"off\" | \"0\" => Some(false),\n+                _ => None,\n+            }\n+        }\n+    }\n }\n \n impl Default for ConfigSet {\n@@ -102,5 +122,9 @@ mod tests {\n         assert_eq!(cs.get_int(\"trace2.eventNesting\"), Some(3));\n         // ConfigSet returns None for missing key\n         assert_eq!(cs.get_string(\"foo.bar\"), None);\n+        // Test boolean parsing\n+        assert_eq!(cs.get_bool(\"test.booleanValue\"), Some(true));\n+        // Test missing boolean key\n+        assert_eq!(cs.get_bool(\"missing.boolean\"), None);\n     }\n }\ndiff --git a/contrib/libgit-rs/testdata/config3 b/contrib/libgit-rs/testdata/config3\nindex ca7b9a7c38..83a474ccef 100644\n--- a/contrib/libgit-rs/testdata/config3\n+++ b/contrib/libgit-rs/testdata/config3\n@@ -1,2 +1,4 @@\n [trace2]\n \teventNesting = 3\n+[test]\n+\tbooleanValue = true\n-- \ngitgitgadget\n"},{"id":"527393","messageId":"CAPx1Gvcj-mVfXFMpLuvda_s89e_3jwdjMMdywEOya16yStXvFw@mail.gmail.com","threadId":"64201","inReplyTo":"a5904a2ac00ea6de142344272b45f6b4697b4f98.1758800669.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 2/2] libgit-rs: add get_bool() method to ConfigSet","fromName":"Chris Torek","fromEmail":"chris.torek@gmail.com","sentAt":"2025-09-26T06:43:27Z","receivedAt":"2025-09-26T06:43:41Z","isPatch":true,"sender":{"key":"chris.torek@gmail.com","avatar":"https://avatars.githubusercontent.com/u/16826774?v=4"},"body":"A bit minor, and I'm not a real Rust programmer, but:\n\nOn Thu, Sep 25, 2025 at 4:44 AM ionnss via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> From: ionnss <zara.leonardo@gmail.com>\n>\n> Add support for parsing boolean configuration values in the Rust\n> ConfigSet API. The method follows Git's standard boolean parsing\n> rules, accepting true/yes/on/1 as true and false/no/off/0 as false.\n>\n> The implementation reuses the existing get_string() infrastructure\n> and adds case-insensitive boolean parsing logic.\n>\n> Signed-off-by: ionnss <zara.leonardo@gmail.com>\n> ---\n>  contrib/libgit-rs/src/config.rs    | 24 ++++++++++++++++++++++++\n>  contrib/libgit-rs/testdata/config3 |  2 ++\n>  2 files changed, 26 insertions(+)\n>\n> diff --git a/contrib/libgit-rs/src/config.rs b/contrib/libgit-rs/src/config.rs\n> index 6bf04845c8..3f4a32c72d 100644\n> --- a/contrib/libgit-rs/src/config.rs\n> +++ b/contrib/libgit-rs/src/config.rs\n> @@ -68,6 +68,26 @@ impl ConfigSet {\n>              Some(owned_str)\n>          }\n>      }\n> +\n> +    pub fn get_bool(&mut self, key: &str) -> Option<bool> {\n> +        let key = CString::new(key).expect(\"Couldn't convert key to CString\");\n\nThe string argument for `.expect` should be phrased in\na more positive manner in terms of what is expected,\nsince failure will cause a panic. So, something like:\n\n    let key = CString::new(key).expect(\"boolean key should be valid CString\");\n\nwhich would produce, e.g.,\n\n    panic: boolean key should be valid CString: ... details of key ...\n\nA similar rule applies to the later `.expect`.\n\nChris\n"},{"id":"527408","messageId":"9e53f617-1b17-4d36-9c35-fc86c0ddfb19@gmail.com","threadId":"64201","inReplyTo":"a5904a2ac00ea6de142344272b45f6b4697b4f98.1758800669.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 2/2] libgit-rs: add get_bool() method to ConfigSet","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-09-26T09:58:04Z","receivedAt":"2025-09-26T09:58:07Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 25/09/2025 12:44, ionnss via GitGitGadget wrote:\n> From: ionnss <zara.leonardo@gmail.com>\n> \n> Add support for parsing boolean configuration values in the Rust\n> ConfigSet API. The method follows Git's standard boolean parsing\n> rules, accepting true/yes/on/1 as true and false/no/off/0 as false.\n> \n> The implementation reuses the existing get_string() infrastructure\n> and adds case-insensitive boolean parsing logic.\n\nIt's nice to know that someone is using the rust bindings. The code in \ncontrib/libgit-rs is intended to be safe wrappers around the unsafe \nfunctions in contrib/libgit-sys which wrap git's C code. I think what we \nneed to do here is add a binding for git_configset_get_bool() to \nlibgit-sys and then wrap that in libgit-rs. We don't want to start \nimplementing the parsing separately as they'll inevitably end up \nbehaving differently to git. For example what you have here parses \"00\" \nor \"100\" differently to git.\n\nThanks\n\nPhillip\n\n> Signed-off-by: ionnss <zara.leonardo@gmail.com>\n> ---\n>   contrib/libgit-rs/src/config.rs    | 24 ++++++++++++++++++++++++\n>   contrib/libgit-rs/testdata/config3 |  2 ++\n>   2 files changed, 26 insertions(+)\n> \n> diff --git a/contrib/libgit-rs/src/config.rs b/contrib/libgit-rs/src/config.rs\n> index 6bf04845c8..3f4a32c72d 100644\n> --- a/contrib/libgit-rs/src/config.rs\n> +++ b/contrib/libgit-rs/src/config.rs\n> @@ -68,6 +68,26 @@ impl ConfigSet {\n>               Some(owned_str)\n>           }\n>       }\n> +\n> +    pub fn get_bool(&mut self, key: &str) -> Option<bool> {\n> +        let key = CString::new(key).expect(\"Couldn't convert key to CString\");\n> +        let mut val: *mut c_char = std::ptr::null_mut();\n> +        unsafe {\n> +            if libgit_configset_get_string(self.0, key.as_ptr(), &mut val as *mut *mut c_char) != 0\n> +            {\n> +                return None;\n> +            }\n> +            let borrowed_str = CStr::from_ptr(val);\n> +            let owned_str =\n> +                String::from(borrowed_str.to_str().expect(\"Couldn't convert val to str\"));\n> +            free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side\n> +            match owned_str.to_lowercase().as_str() {\n> +                \"true\" | \"yes\" | \"on\" | \"1\" => Some(true),\n> +                \"false\" | \"no\" | \"off\" | \"0\" => Some(false),\n> +                _ => None,\n> +            }\n> +        }\n> +    }\n>   }\n>   \n>   impl Default for ConfigSet {\n> @@ -102,5 +122,9 @@ mod tests {\n>           assert_eq!(cs.get_int(\"trace2.eventNesting\"), Some(3));\n>           // ConfigSet returns None for missing key\n>           assert_eq!(cs.get_string(\"foo.bar\"), None);\n> +        // Test boolean parsing\n> +        assert_eq!(cs.get_bool(\"test.booleanValue\"), Some(true));\n> +        // Test missing boolean key\n> +        assert_eq!(cs.get_bool(\"missing.boolean\"), None);\n>       }\n>   }\n> diff --git a/contrib/libgit-rs/testdata/config3 b/contrib/libgit-rs/testdata/config3\n> index ca7b9a7c38..83a474ccef 100644\n> --- a/contrib/libgit-rs/testdata/config3\n> +++ b/contrib/libgit-rs/testdata/config3\n> @@ -1,2 +1,4 @@\n>   [trace2]\n>   \teventNesting = 3\n> +[test]\n> +\tbooleanValue = true\n\n"},{"id":"527434","messageId":"xmqqy0q1b0ff.fsf@gitster.g","threadId":"64201","inReplyTo":"9e53f617-1b17-4d36-9c35-fc86c0ddfb19@gmail.com","subject":"Re: [PATCH 2/2] libgit-rs: add get_bool() method to ConfigSet","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-26T17:15:00Z","receivedAt":"2025-09-26T17:15:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n> On 25/09/2025 12:44, ionnss via GitGitGadget wrote:\n>> From: ionnss <zara.leonardo@gmail.com>\n>> Add support for parsing boolean configuration values in the Rust\n>> ConfigSet API. The method follows Git's standard boolean parsing\n>> rules, accepting true/yes/on/1 as true and false/no/off/0 as false.\n>> The implementation reuses the existing get_string() infrastructure\n>> and adds case-insensitive boolean parsing logic.\n>\n> It's nice to know that someone is using the rust bindings. The code in\n> contrib/libgit-rs is intended to be safe wrappers around the unsafe\n> functions in contrib/libgit-sys which wrap git's C code. I think what\n> we need to do here is add a binding for git_configset_get_bool() to\n> libgit-sys and then wrap that in libgit-rs. We don't want to start\n> implementing the parsing separately as they'll inevitably end up\n> behaving differently to git. For example what you have here parses\n> \"00\" or \"100\" differently to git.\n>\n> Thanks\n\nThanks for paying attention to an important detail on the design\ncriteria.\n\nAs this is a binding to allow Rust programs to access the feature\nimplemented and offered by Git, I fully agree that reimplementing\nwhat Git does in an incompatible way in Rust would not help the\ntargetted intended audiences at all (that is better off done in a\nproject that aims to reimplement what Git does, like gitoxide,\ntargetting native Rust solution).\n\nIf I were not paying attention, I would very likely have missed the\nmisparses of number-as-bool, distracted and blinded by the prettier\nand nicer syntax the language offers over the original in C X-<.\n\nThanks.\n"},{"id":"527468","messageId":"pull.1977.v2.git.1758931659.gitgitgadget@gmail.com","threadId":"64201","inReplyTo":"pull.1977.git.1758800669.gitgitgadget@gmail.com","subject":"[PATCH v2 0/3] libgit-rs: add get_bool() method to ConfigSet","fromName":"ions via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-27T00:07:36Z","receivedAt":"2025-09-27T00:07:43Z","isPatch":true,"sender":{"key":"name:ions","avatar":null},"body":"Purpose\n\nThis pull request introduces a get_bool() method to the ConfigSet module in\nthe libgit-rs library. The goal is to enhance the functionality of ConfigSet\nby providing a way to fetch and handle boolean configuration values more\neasily and consistently.\n\nImplementation Details\n\n• Added a get_bool() method to the ConfigSet module.\n\n• The method retrieves configuration values as boolean values, ensuring\nproper parsing and error handling.\n\n• This addition simplifies the process of working with boolean\nconfigurations for developers using the ConfigSet module.\n\nTesting\n\n• Added unit tests to verify the correctness of the get_bool() method.\n\n• Tested edge cases to ensure robustness.\n\nionnss (3):\n  po: fix escaped underscores in README.md\n  libgit-rs: add get_bool() method to ConfigSet\n  libgit-rs: address review feedback for get_bool()\n\n contrib/libgit-rs/src/config.rs    | 27 +++++++++++++++++++++++++++\n contrib/libgit-rs/testdata/config3 |  2 +-\n contrib/libgit-rs/testdata/config4 | 10 ++++++++++\n contrib/libgit-sys/src/lib.rs      |  6 ++++++\n po/README.md                       |  6 +++---\n 5 files changed, 47 insertions(+), 4 deletions(-)\n create mode 100644 contrib/libgit-rs/testdata/config4\n\n\nbase-commit: bb69721404348ea2db0a081c41ab6ebfe75bdec8\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1977%2Fionnss%2Fadd-rust-configset-get-bool-v2\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1977/ionnss/add-rust-configset-get-bool-v2\nPull-Request: https://github.com/gitgitgadget/git/pull/1977\n\nRange-diff vs v1:\n\n 1:  d7810781fc = 1:  d7810781fc po: fix escaped underscores in README.md\n 2:  a5904a2ac0 = 2:  a5904a2ac0 libgit-rs: add get_bool() method to ConfigSet\n -:  ---------- > 3:  43784e3ff9 libgit-rs: address review feedback for get_bool()\n\n-- \ngitgitgadget\n"},{"id":"527469","messageId":"d7810781fc3af1902c99cccbd50853dd630a0080.1758931659.git.gitgitgadget@gmail.com","threadId":"64201","inReplyTo":"pull.1977.v2.git.1758931659.gitgitgadget@gmail.com","subject":"[PATCH v2 1/3] po: fix escaped underscores in README.md","fromName":"ionnss via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-27T00:07:37Z","receivedAt":"2025-09-27T00:07:46Z","isPatch":true,"sender":{"key":"name:ionnss","avatar":null},"body":"From: ionnss <zara.leonardo@gmail.com>\n\nRemove unnecessary backslashes from language code examples.\nThe underscores in \"ll\\_CC\" and \"zh\\_CN\" don't need escaping\nin Markdown.\n\nSigned-off-by: ionnss <zara.leonardo@gmail.com>\n---\n po/README.md | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/po/README.md b/po/README.md\nindex ec08aa24ad..d7757bed4e 100644\n--- a/po/README.md\n+++ b/po/README.md\n@@ -13,9 +13,9 @@ We will use XX as an alias to refer to the language translation code in\n the following paragraphs, for example we use \"po/XX.po\" to refer to the\n translation file for a specific language. But this doesn't mean that\n the language code has only two letters. The language code can be in one\n-of two forms: \"ll\" or \"ll\\_CC\". Here \"ll\" is the ISO 639 two-letter\n+of two forms: \"ll\" or \"ll_CC\". Here \"ll\" is the ISO 639 two-letter\n language code and \"CC\" is the ISO 3166 two-letter code for country names\n-and subdivisions. For example: \"de\" for German language code, \"zh\\_CN\"\n+and subdivisions. For example: \"de\" for German language code, \"zh_CN\"\n for Simplified Chinese language code.\n \n \n@@ -126,7 +126,7 @@ you add a translation for the first time by running:\n make po-init PO_FILE=po/XX.po\n ```\n \n-where XX is the locale, e.g. \"de\", \"is\", \"pt\\_BR\", \"zh\\_CN\", etc.\n+where XX is the locale, e.g. \"de\", \"is\", \"pt_BR\", \"zh_CN\", etc.\n \n The newly generated message file \"po/XX.po\" is based on the core pot\n file \"po/git-core.pot\", so it contains only a minimal set of messages\n-- \ngitgitgadget\n\n"},{"id":"527470","messageId":"a5904a2ac00ea6de142344272b45f6b4697b4f98.1758931659.git.gitgitgadget@gmail.com","threadId":"64201","inReplyTo":"pull.1977.v2.git.1758931659.gitgitgadget@gmail.com","subject":"[PATCH v2 2/3] libgit-rs: add get_bool() method to ConfigSet","fromName":"ionnss via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-27T00:07:38Z","receivedAt":"2025-09-27T00:07:48Z","isPatch":true,"sender":{"key":"name:ionnss","avatar":null},"body":"From: ionnss <zara.leonardo@gmail.com>\n\nAdd support for parsing boolean configuration values in the Rust\nConfigSet API. The method follows Git's standard boolean parsing\nrules, accepting true/yes/on/1 as true and false/no/off/0 as false.\n\nThe implementation reuses the existing get_string() infrastructure\nand adds case-insensitive boolean parsing logic.\n\nSigned-off-by: ionnss <zara.leonardo@gmail.com>\n---\n contrib/libgit-rs/src/config.rs    | 24 ++++++++++++++++++++++++\n contrib/libgit-rs/testdata/config3 |  2 ++\n 2 files changed, 26 insertions(+)\n\ndiff --git a/contrib/libgit-rs/src/config.rs b/contrib/libgit-rs/src/config.rs\nindex 6bf04845c8..3f4a32c72d 100644\n--- a/contrib/libgit-rs/src/config.rs\n+++ b/contrib/libgit-rs/src/config.rs\n@@ -68,6 +68,26 @@ impl ConfigSet {\n             Some(owned_str)\n         }\n     }\n+\n+    pub fn get_bool(&mut self, key: &str) -> Option<bool> {\n+        let key = CString::new(key).expect(\"Couldn't convert key to CString\");\n+        let mut val: *mut c_char = std::ptr::null_mut();\n+        unsafe {\n+            if libgit_configset_get_string(self.0, key.as_ptr(), &mut val as *mut *mut c_char) != 0\n+            {\n+                return None;\n+            }\n+            let borrowed_str = CStr::from_ptr(val);\n+            let owned_str =\n+                String::from(borrowed_str.to_str().expect(\"Couldn't convert val to str\"));\n+            free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side\n+            match owned_str.to_lowercase().as_str() {\n+                \"true\" | \"yes\" | \"on\" | \"1\" => Some(true),\n+                \"false\" | \"no\" | \"off\" | \"0\" => Some(false),\n+                _ => None,\n+            }\n+        }\n+    }\n }\n \n impl Default for ConfigSet {\n@@ -102,5 +122,9 @@ mod tests {\n         assert_eq!(cs.get_int(\"trace2.eventNesting\"), Some(3));\n         // ConfigSet returns None for missing key\n         assert_eq!(cs.get_string(\"foo.bar\"), None);\n+        // Test boolean parsing\n+        assert_eq!(cs.get_bool(\"test.booleanValue\"), Some(true));\n+        // Test missing boolean key\n+        assert_eq!(cs.get_bool(\"missing.boolean\"), None);\n     }\n }\ndiff --git a/contrib/libgit-rs/testdata/config3 b/contrib/libgit-rs/testdata/config3\nindex ca7b9a7c38..83a474ccef 100644\n--- a/contrib/libgit-rs/testdata/config3\n+++ b/contrib/libgit-rs/testdata/config3\n@@ -1,2 +1,4 @@\n [trace2]\n \teventNesting = 3\n+[test]\n+\tbooleanValue = true\n-- \ngitgitgadget\n\n"},{"id":"527471","messageId":"43784e3ff991929b5c314ad5b9ab6573e6ca48c4.1758931659.git.gitgitgadget@gmail.com","threadId":"64201","inReplyTo":"pull.1977.v2.git.1758931659.gitgitgadget@gmail.com","subject":"[PATCH v2 3/3] libgit-rs: address review feedback for get_bool()","fromName":"ionnss via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-27T00:07:39Z","receivedAt":"2025-09-27T00:07:51Z","isPatch":true,"sender":{"key":"name:ionnss","avatar":null},"body":"From: ionnss <zara.leonardo@gmail.com>\n\n- Use git_configset_get_bool() C function instead of reimplementing parsing\n- Fix libgit_configset_get_bool() function signature in bindings\n- Improve .expect() error messages to be more descriptive\n- Add comprehensive boolean tests including edge cases (00, 100, 007)\n\nThis addresses feedback from Phillip Wood and Chris Torek about using\nGit's actual boolean parsing logic rather than duplicating it in Rust.\n\nSigned-off-by: ionnss <zara.leonardo@gmail.com>\n---\n contrib/libgit-rs/src/config.rs    | 33 ++++++++++++++++--------------\n contrib/libgit-rs/testdata/config3 |  4 +---\n contrib/libgit-rs/testdata/config4 | 10 +++++++++\n contrib/libgit-sys/src/lib.rs      |  6 ++++++\n 4 files changed, 35 insertions(+), 18 deletions(-)\n create mode 100644 contrib/libgit-rs/testdata/config4\n\ndiff --git a/contrib/libgit-rs/src/config.rs b/contrib/libgit-rs/src/config.rs\nindex 3f4a32c72d..b280b952b2 100644\n--- a/contrib/libgit-rs/src/config.rs\n+++ b/contrib/libgit-rs/src/config.rs\n@@ -69,24 +69,18 @@ impl ConfigSet {\n         }\n     }\n \n+    /// Load the value for the given key and attempt to parse it as a boolean. Dies with a fatal error\n+    /// if the value cannot be parsed. Returns None if the key is not present.\n     pub fn get_bool(&mut self, key: &str) -> Option<bool> {\n-        let key = CString::new(key).expect(\"Couldn't convert key to CString\");\n-        let mut val: *mut c_char = std::ptr::null_mut();\n+        let key = CString::new(key).expect(\"config key should be valid CString\");\n+        let mut val: c_int = 0;\n         unsafe {\n-            if libgit_configset_get_string(self.0, key.as_ptr(), &mut val as *mut *mut c_char) != 0\n-            {\n+            if libgit_configset_get_bool(self.0, key.as_ptr(), &mut val as *mut c_int) != 0 {\n                 return None;\n             }\n-            let borrowed_str = CStr::from_ptr(val);\n-            let owned_str =\n-                String::from(borrowed_str.to_str().expect(\"Couldn't convert val to str\"));\n-            free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side\n-            match owned_str.to_lowercase().as_str() {\n-                \"true\" | \"yes\" | \"on\" | \"1\" => Some(true),\n-                \"false\" | \"no\" | \"off\" | \"0\" => Some(false),\n-                _ => None,\n-            }\n         }\n+\n+        Some(val != 0)\n     }\n }\n \n@@ -115,6 +109,7 @@ mod tests {\n             Path::new(\"testdata/config1\"),\n             Path::new(\"testdata/config2\"),\n             Path::new(\"testdata/config3\"),\n+            Path::new(\"testdata/config4\"),\n         ]);\n         // ConfigSet retrieves correct value\n         assert_eq!(cs.get_int(\"trace2.eventTarget\"), Some(1));\n@@ -122,8 +117,16 @@ mod tests {\n         assert_eq!(cs.get_int(\"trace2.eventNesting\"), Some(3));\n         // ConfigSet returns None for missing key\n         assert_eq!(cs.get_string(\"foo.bar\"), None);\n-        // Test boolean parsing\n-        assert_eq!(cs.get_bool(\"test.booleanValue\"), Some(true));\n+        // Test boolean parsing - comprehensive tests\n+        assert_eq!(cs.get_bool(\"test.boolTrue\"), Some(true));\n+        assert_eq!(cs.get_bool(\"test.boolFalse\"), Some(false));\n+        assert_eq!(cs.get_bool(\"test.boolYes\"), Some(true));\n+        assert_eq!(cs.get_bool(\"test.boolNo\"), Some(false));\n+        assert_eq!(cs.get_bool(\"test.boolOne\"), Some(true));\n+        assert_eq!(cs.get_bool(\"test.boolZero\"), Some(false));\n+        assert_eq!(cs.get_bool(\"test.boolZeroZero\"), Some(false)); // \"00\" → false\n+        assert_eq!(cs.get_bool(\"test.boolHundred\"), Some(true)); // \"100\" → true\n+        assert_eq!(cs.get_bool(\"test.boolSeven\"), Some(true)); // \"007\" → true\n         // Test missing boolean key\n         assert_eq!(cs.get_bool(\"missing.boolean\"), None);\n     }\ndiff --git a/contrib/libgit-rs/testdata/config3 b/contrib/libgit-rs/testdata/config3\nindex 83a474ccef..3ea5b96f12 100644\n--- a/contrib/libgit-rs/testdata/config3\n+++ b/contrib/libgit-rs/testdata/config3\n@@ -1,4 +1,2 @@\n [trace2]\n-\teventNesting = 3\n-[test]\n-\tbooleanValue = true\n+\teventNesting = 3\n\\ No newline at end of file\ndiff --git a/contrib/libgit-rs/testdata/config4 b/contrib/libgit-rs/testdata/config4\nnew file mode 100644\nindex 0000000000..5b75385c38\n--- /dev/null\n+++ b/contrib/libgit-rs/testdata/config4\n@@ -0,0 +1,10 @@\n+[test]\n+\tboolTrue = true\n+\tboolFalse = false\n+\tboolYes = yes\n+\tboolNo = no\n+\tboolOne = 1\n+\tboolZero = 0\n+\tboolZeroZero = 00\n+\tboolHundred = 100\n+\tboolSeven = 007\ndiff --git a/contrib/libgit-sys/src/lib.rs b/contrib/libgit-sys/src/lib.rs\nindex 4bfc650450..b104fda8f6 100644\n--- a/contrib/libgit-sys/src/lib.rs\n+++ b/contrib/libgit-sys/src/lib.rs\n@@ -43,6 +43,12 @@ extern \"C\" {\n         dest: *mut *mut c_char,\n     ) -> c_int;\n \n+    pub fn libgit_configset_get_bool(\n+        cs: *mut libgit_config_set,\n+        key: *const c_char,\n+        dest: *mut c_int,\n+    ) -> c_int;\n+\n }\n \n #[cfg(test)]\n-- \ngitgitgadget\n"},{"id":"527473","messageId":"xmqqzfag8xgw.fsf@gitster.g","threadId":"64201","inReplyTo":"pull.1977.v2.git.1758931659.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 0/3] libgit-rs: add get_bool() method to ConfigSet","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-27T02:01:51Z","receivedAt":"2025-09-27T02:01:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"ions via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> Purpose\n>\n> This pull request introduces a get_bool() method to the ConfigSet module in\n> the libgit-rs library. The goal is to enhance the functionality of ConfigSet\n> by providing a way to fetch and handle boolean configuration values more\n> easily and consistently.\n>\n> Implementation Details\n>\n> • Added a get_bool() method to the ConfigSet module.\n>\n> • The method retrieves configuration values as boolean values, ensuring\n> proper parsing and error handling.\n>\n> • This addition simplifies the process of working with boolean\n> configurations for developers using the ConfigSet module.\n>\n> Testing\n>\n> • Added unit tests to verify the correctness of the get_bool() method.\n>\n> • Tested edge cases to ensure robustness.\n\nSomebody may suggest a better way to organize your cover letter for\nsecond and subsequent iterations, but it is helpful to returning\nreaders to list what changed since the previous iteration.\n\n> ionnss (3):\n>   po: fix escaped underscores in README.md\n>   libgit-rs: add get_bool() method to ConfigSet\n>   libgit-rs: address review feedback for get_bool()\n\nI am not sure if you really wanted to make this a thre-patch series.\nThe README.md patch looks pretty much independent and irrelevant to\nthe Rust topic to me.  I am not a GGG user, but there must be a way\nto tell it to send out a two-patch series from this branch.\n\n> base-commit: bb69721404348ea2db0a081c41ab6ebfe75bdec8\n\nPerhaps you'd need to rebase the top two commit on top of this\nbb697214 (The twelfth batch, 2025-09-23).  Currently your two\ncommits that belong to this libgit-rs series are not built on top of\nthat commit in the mainline.  They are built on top of d7810781fc,\nas can be seen below.\n\n> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1977%2Fionnss%2Fadd-rust-configset-get-bool-v2\n> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1977/ionnss/add-rust-configset-get-bool-v2\n> Pull-Request: https://github.com/gitgitgadget/git/pull/1977\n>\n> Range-diff vs v1:\n>\n>  1:  d7810781fc = 1:  d7810781fc po: fix escaped underscores in README.md\n>  2:  a5904a2ac0 = 2:  a5904a2ac0 libgit-rs: add get_bool() method to ConfigSet\n>  -:  ---------- > 3:  43784e3ff9 libgit-rs: address review feedback for get_bool()\n\nActually, I do not think you want a two-patch series, either.\n\nReview comments (sorry, I do not recall the details) must have\npointed out what was wrong in the initial iteration, but this\niteration is carrying the same badness without correcting anyting in\na5904a2ac0 and resending it with \"oops that was wrong in the\nprevious step, and here is a band-aid on top\" commit that is the new\n43784e3ff9.\n\nUntil there is a concensus that your topic is ready and gets merged\nto 'next', you have the luxury to pretend as if you are a perfect\ndeveloper who never made such mistakes that can easily be pointed\nout on the mailing list by others.  In short, anything in 43784e3ff9\nthat removes lines that were added in a5904a2ac0 and replaces them\nwith something else are unwanted \"I went there, it was a bad idea,\nhere I am correcting the earlier mistake\" changes.\n\nAnd the way to pretend that you are a perfect developer is to\nrewrite (i.e. \"commit --amend\") your \"add get_bool()\" with the new\nstrategy to wrap the C version used by real Git, instead of\nreimplementing it manually in Rust.  This is a chance you have to\npretend that you weren't even tempted to reimplement the thing in\nRust and went straight to the right solution.  So grab that chance.\nOnce your topic gets merged to 'next', you no longer have that\nopportunity to wholesale replace your patches to pretend that you\nnever made mistakes.\n\nThis is because nobody reading \"git log -p\" later is interested in\nyour (or anybody's) mistakes that have already been corrected.  It\nmight record your cherished memory to celebrate your growth as a\ndeveloper, but that is not something the history of a public project\nis used for.\n\nAfter your topic is merged to 'next', we'd go incremental.  This is\nbecause being in 'next' means that enough reviewers agreed that a\nparticular iteration is good enough.  If later it was found that\nthey are not good enough and need corrections, such a mistake that\nnobody managed to spot during the review becomes worth recording and\nits correction worth describing as a separate patch on top.\n\nBut we are not there yet with this series.\n\nThanks.\n"},{"id":"527474","messageId":"pull.1977.v3.git.1758945111.gitgitgadget@gmail.com","threadId":"64201","inReplyTo":"pull.1977.v2.git.1758931659.gitgitgadget@gmail.com","subject":"[PATCH v3 0/3] libgit-rs: add get_bool() method to ConfigSet","fromName":"ions via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-27T03:51:48Z","receivedAt":"2025-09-27T03:51:54Z","isPatch":true,"sender":{"key":"name:ions","avatar":null},"body":"Purpose\n\nThis pull request introduces a get_bool() method to the ConfigSet module in\nthe libgit-rs library. The goal is to enhance the functionality of ConfigSet\nby providing a way to fetch and handle boolean configuration values more\neasily and consistently.\n\nImplementation Details\n\n• Added a get_bool() method to the ConfigSet module.\n\n• The method retrieves configuration values as boolean values, ensuring\nproper parsing and error handling.\n\n• This addition simplifies the process of working with boolean\nconfigurations for developers using the ConfigSet module.\n\nTesting\n\n• Added unit tests to verify the correctness of the get_bool() method.\n\n• Tested edge cases to ensure robustness.\n\nionnss (3):\n  po: fix escaped underscores in README.md\n  libgit-rs: add get_bool() method to ConfigSet\n  libgit-rs: add get_ulong() and get_pathname() methods\n\n contrib/libgit-rs/src/config.rs    | 83 +++++++++++++++++++++++++++++-\n contrib/libgit-rs/testdata/config3 |  2 +-\n contrib/libgit-rs/testdata/config4 | 13 +++++\n contrib/libgit-sys/src/lib.rs      | 24 ++++++++-\n po/README.md                       |  6 +--\n 5 files changed, 121 insertions(+), 7 deletions(-)\n create mode 100644 contrib/libgit-rs/testdata/config4\n\n\nbase-commit: bb69721404348ea2db0a081c41ab6ebfe75bdec8\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1977%2Fionnss%2Fadd-rust-configset-get-bool-v3\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1977/ionnss/add-rust-configset-get-bool-v3\nPull-Request: https://github.com/gitgitgadget/git/pull/1977\n\nRange-diff vs v2:\n\n 1:  d7810781fc = 1:  d7810781fc po: fix escaped underscores in README.md\n 3:  43784e3ff9 ! 2:  479c263bc1 libgit-rs: address review feedback for get_bool()\n     @@ Metadata\n      Author: ionnss <zara.leonardo@gmail.com>\n      \n       ## Commit message ##\n     -    libgit-rs: address review feedback for get_bool()\n     +    libgit-rs: add get_bool() method to ConfigSet\n      \n     -    - Use git_configset_get_bool() C function instead of reimplementing parsing\n     -    - Fix libgit_configset_get_bool() function signature in bindings\n     -    - Improve .expect() error messages to be more descriptive\n     -    - Add comprehensive boolean tests including edge cases (00, 100, 007)\n     +    Add support for parsing boolean configuration values using Git's\n     +    git_configset_get_bool() C function. This ensures consistent behavior\n     +    with Git's native boolean parsing logic.\n      \n     -    This addresses feedback from Phillip Wood and Chris Torek about using\n     -    Git's actual boolean parsing logic rather than duplicating it in Rust.\n     +    The method handles all Git boolean formats (true/false, yes/no, on/off,\n     +    1/0) and edge cases like \"00\" and \"100\" correctly.\n     +\n     +    Includes comprehensive tests for various boolean formats and edge cases.\n      \n          Signed-off-by: ionnss <zara.leonardo@gmail.com>\n      \n       ## contrib/libgit-rs/src/config.rs ##\n      @@ contrib/libgit-rs/src/config.rs: impl ConfigSet {\n     +             Some(owned_str)\n               }\n           }\n     - \n     ++\n      +    /// Load the value for the given key and attempt to parse it as a boolean. Dies with a fatal error\n      +    /// if the value cannot be parsed. Returns None if the key is not present.\n     -     pub fn get_bool(&mut self, key: &str) -> Option<bool> {\n     --        let key = CString::new(key).expect(\"Couldn't convert key to CString\");\n     --        let mut val: *mut c_char = std::ptr::null_mut();\n     ++    pub fn get_bool(&mut self, key: &str) -> Option<bool> {\n      +        let key = CString::new(key).expect(\"config key should be valid CString\");\n      +        let mut val: c_int = 0;\n     -         unsafe {\n     --            if libgit_configset_get_string(self.0, key.as_ptr(), &mut val as *mut *mut c_char) != 0\n     --            {\n     ++        unsafe {\n      +            if libgit_configset_get_bool(self.0, key.as_ptr(), &mut val as *mut c_int) != 0 {\n     -                 return None;\n     -             }\n     --            let borrowed_str = CStr::from_ptr(val);\n     --            let owned_str =\n     --                String::from(borrowed_str.to_str().expect(\"Couldn't convert val to str\"));\n     --            free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side\n     --            match owned_str.to_lowercase().as_str() {\n     --                \"true\" | \"yes\" | \"on\" | \"1\" => Some(true),\n     --                \"false\" | \"no\" | \"off\" | \"0\" => Some(false),\n     --                _ => None,\n     --            }\n     -         }\n     ++                return None;\n     ++            }\n     ++        }\n      +\n      +        Some(val != 0)\n     -     }\n     ++    }\n       }\n       \n     + impl Default for ConfigSet {\n      @@ contrib/libgit-rs/src/config.rs: mod tests {\n                   Path::new(\"testdata/config1\"),\n                   Path::new(\"testdata/config2\"),\n     @@ contrib/libgit-rs/src/config.rs: mod tests {\n               assert_eq!(cs.get_int(\"trace2.eventNesting\"), Some(3));\n               // ConfigSet returns None for missing key\n               assert_eq!(cs.get_string(\"foo.bar\"), None);\n     --        // Test boolean parsing\n     --        assert_eq!(cs.get_bool(\"test.booleanValue\"), Some(true));\n      +        // Test boolean parsing - comprehensive tests\n      +        assert_eq!(cs.get_bool(\"test.boolTrue\"), Some(true));\n      +        assert_eq!(cs.get_bool(\"test.boolFalse\"), Some(false));\n     @@ contrib/libgit-rs/src/config.rs: mod tests {\n      +        assert_eq!(cs.get_bool(\"test.boolZero\"), Some(false));\n      +        assert_eq!(cs.get_bool(\"test.boolZeroZero\"), Some(false)); // \"00\" → false\n      +        assert_eq!(cs.get_bool(\"test.boolHundred\"), Some(true)); // \"100\" → true\n     -+        assert_eq!(cs.get_bool(\"test.boolSeven\"), Some(true)); // \"007\" → true\n     -         // Test missing boolean key\n     -         assert_eq!(cs.get_bool(\"missing.boolean\"), None);\n     ++        // Test missing boolean key\n     ++        assert_eq!(cs.get_bool(\"missing.boolean\"), None);\n           }\n     + }\n      \n       ## contrib/libgit-rs/testdata/config3 ##\n      @@\n       [trace2]\n      -\teventNesting = 3\n     --[test]\n     --\tbooleanValue = true\n      +\teventNesting = 3\n       \\ No newline at end of file\n      \n     @@ contrib/libgit-rs/testdata/config4 (new)\n      +\tboolZero = 0\n      +\tboolZeroZero = 00\n      +\tboolHundred = 100\n     -+\tboolSeven = 007\n      \n       ## contrib/libgit-sys/src/lib.rs ##\n      @@ contrib/libgit-sys/src/lib.rs: extern \"C\" {\n 2:  a5904a2ac0 ! 3:  1ac8d76819 libgit-rs: add get_bool() method to ConfigSet\n     @@ Metadata\n      Author: ionnss <zara.leonardo@gmail.com>\n      \n       ## Commit message ##\n     -    libgit-rs: add get_bool() method to ConfigSet\n     +    libgit-rs: add get_ulong() and get_pathname() methods\n      \n     -    Add support for parsing boolean configuration values in the Rust\n     -    ConfigSet API. The method follows Git's standard boolean parsing\n     -    rules, accepting true/yes/on/1 as true and false/no/off/0 as false.\n     +    Expand the ConfigSet API with additional configuration value types:\n      \n     -    The implementation reuses the existing get_string() infrastructure\n     -    and adds case-insensitive boolean parsing logic.\n     +    - get_ulong(): Parse unsigned long integers for large numeric values\n     +    - get_pathname(): Parse file paths, returning PathBuf for type safety\n     +\n     +    Both functions follow the same pattern as existing get_* methods,\n     +    using Git's C functions for consistent parsing behavior.\n     +\n     +    Add comprehensive tests covering normal cases, edge cases, and\n     +    error handling for all new functionality.\n      \n          Signed-off-by: ionnss <zara.leonardo@gmail.com>\n      \n       ## contrib/libgit-rs/src/config.rs ##\n     +@@\n     + use std::ffi::{c_void, CStr, CString};\n     +-use std::path::Path;\n     ++use std::path::{Path, PathBuf};\n     + \n     + #[cfg(has_std__ffi__c_char)]\n     +-use std::ffi::{c_char, c_int};\n     ++use std::ffi::{c_char, c_int, c_ulong};\n     + \n     + #[cfg(not(has_std__ffi__c_char))]\n     + #[allow(non_camel_case_types)]\n     +@@ contrib/libgit-rs/src/config.rs: type c_char = i8;\n     + #[allow(non_camel_case_types)]\n     + type c_int = i32;\n     + \n     ++#[cfg(not(has_std__ffi__c_char))]\n     ++#[allow(non_camel_case_types)]\n     ++type c_ulong = u64;\n     ++\n     + use libgit_sys::*;\n     + \n     + /// A ConfigSet is an in-memory cache for config-like files such as `.gitmodules` or `.gitconfig`.\n      @@ contrib/libgit-rs/src/config.rs: impl ConfigSet {\n     -             Some(owned_str)\n     -         }\n     + \n     +         Some(val != 0)\n           }\n      +\n     -+    pub fn get_bool(&mut self, key: &str) -> Option<bool> {\n     -+        let key = CString::new(key).expect(\"Couldn't convert key to CString\");\n     ++    /// Load the value for the given key and attempt to parse it as an unsigned long. Dies with a fatal error\n     ++    /// if the value cannot be parsed. Returns None if the key is not present.\n     ++    pub fn get_ulong(&mut self, key: &str) -> Option<u64> {\n     ++        let key = CString::new(key).expect(\"config key should be valid CString\");\n     ++        let mut val: c_ulong = 0;\n     ++        unsafe {\n     ++            if libgit_configset_get_ulong(self.0, key.as_ptr(), &mut val as *mut c_ulong) != 0 {\n     ++                return None;\n     ++            }\n     ++        }\n     ++        Some(val as u64)\n     ++    }\n     ++\n     ++    /// Load the value for the given key and attempt to parse it as a file path. Dies with a fatal error\n     ++    /// if the value cannot be converted to a PathBuf. Returns None if the key is not present.\n     ++    pub fn get_pathname(&mut self, key: &str) -> Option<PathBuf> {\n     ++        let key = CString::new(key).expect(\"config key should be valid CString\");\n      +        let mut val: *mut c_char = std::ptr::null_mut();\n      +        unsafe {\n     -+            if libgit_configset_get_string(self.0, key.as_ptr(), &mut val as *mut *mut c_char) != 0\n     ++            if libgit_configset_get_pathname(self.0, key.as_ptr(), &mut val as *mut *mut c_char)\n     ++                != 0\n      +            {\n      +                return None;\n      +            }\n      +            let borrowed_str = CStr::from_ptr(val);\n     -+            let owned_str =\n     -+                String::from(borrowed_str.to_str().expect(\"Couldn't convert val to str\"));\n     ++            let owned_str = String::from(\n     ++                borrowed_str\n     ++                    .to_str()\n     ++                    .expect(\"config path should be valid UTF-8\"),\n     ++            );\n      +            free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side\n     -+            match owned_str.to_lowercase().as_str() {\n     -+                \"true\" | \"yes\" | \"on\" | \"1\" => Some(true),\n     -+                \"false\" | \"no\" | \"off\" | \"0\" => Some(false),\n     -+                _ => None,\n     -+            }\n     ++            Some(PathBuf::from(owned_str))\n      +        }\n      +    }\n       }\n       \n       impl Default for ConfigSet {\n      @@ contrib/libgit-rs/src/config.rs: mod tests {\n     -         assert_eq!(cs.get_int(\"trace2.eventNesting\"), Some(3));\n     -         // ConfigSet returns None for missing key\n     -         assert_eq!(cs.get_string(\"foo.bar\"), None);\n     -+        // Test boolean parsing\n     -+        assert_eq!(cs.get_bool(\"test.booleanValue\"), Some(true));\n     -+        // Test missing boolean key\n     -+        assert_eq!(cs.get_bool(\"missing.boolean\"), None);\n     +         assert_eq!(cs.get_bool(\"test.boolHundred\"), Some(true)); // \"100\" → true\n     +         // Test missing boolean key\n     +         assert_eq!(cs.get_bool(\"missing.boolean\"), None);\n     ++        // Test ulong parsing\n     ++        assert_eq!(cs.get_ulong(\"test.ulongSmall\"), Some(42));\n     ++        assert_eq!(cs.get_ulong(\"test.ulongBig\"), Some(4294967296)); // > 32-bit int\n     ++        assert_eq!(cs.get_ulong(\"missing.ulong\"), None);\n     ++        // Test pathname parsing\n     ++        assert_eq!(\n     ++            cs.get_pathname(\"test.pathRelative\"),\n     ++            Some(PathBuf::from(\"./some/path\"))\n     ++        );\n     ++        assert_eq!(\n     ++            cs.get_pathname(\"test.pathAbsolute\"),\n     ++            Some(PathBuf::from(\"/usr/bin/git\"))\n     ++        );\n     ++        assert_eq!(cs.get_pathname(\"missing.path\"), None);\n           }\n       }\n      \n     - ## contrib/libgit-rs/testdata/config3 ##\n     + ## contrib/libgit-rs/testdata/config4 ##\n     +@@\n     + \tboolZero = 0\n     + \tboolZeroZero = 00\n     + \tboolHundred = 100\n     ++\tulongSmall = 42\n     ++\tulongBig = 4294967296\n     ++\tpathRelative = ./some/path\n     ++\tpathAbsolute = /usr/bin/git\n     +\n     + ## contrib/libgit-sys/src/lib.rs ##\n      @@\n     - [trace2]\n     - \teventNesting = 3\n     -+[test]\n     -+\tbooleanValue = true\n     + use std::ffi::c_void;\n     + \n     + #[cfg(has_std__ffi__c_char)]\n     +-use std::ffi::{c_char, c_int};\n     ++use std::ffi::{c_char, c_int, c_ulong};\n     + \n     + #[cfg(not(has_std__ffi__c_char))]\n     + #[allow(non_camel_case_types)]\n     +@@ contrib/libgit-sys/src/lib.rs: pub type c_char = i8;\n     + #[allow(non_camel_case_types)]\n     + pub type c_int = i32;\n     + \n     ++#[cfg(not(has_std__ffi__c_char))]\n     ++#[allow(non_camel_case_types)]\n     ++pub type c_ulong = u64;\n     ++\n     + extern crate libz_sys;\n     + \n     + #[allow(non_camel_case_types)]\n     +@@ contrib/libgit-sys/src/lib.rs: extern \"C\" {\n     +         dest: *mut c_int,\n     +     ) -> c_int;\n     + \n     ++    pub fn libgit_configset_get_ulong(\n     ++        cs: *mut libgit_config_set,\n     ++        key: *const c_char,\n     ++        dest: *mut c_ulong,\n     ++    ) -> c_int;\n     ++\n     ++    pub fn libgit_configset_get_pathname(\n     ++        cs: *mut libgit_config_set,\n     ++        key: *const c_char,\n     ++        dest: *mut *mut c_char,\n     ++    ) -> c_int;\n     ++\n     + }\n     + \n     + #[cfg(test)]\n\n-- \ngitgitgadget\n"},{"id":"527475","messageId":"d7810781fc3af1902c99cccbd50853dd630a0080.1758945111.git.gitgitgadget@gmail.com","threadId":"64201","inReplyTo":"pull.1977.v3.git.1758945111.gitgitgadget@gmail.com","subject":"[PATCH v3 1/3] po: fix escaped underscores in README.md","fromName":"ionnss via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-27T03:51:49Z","receivedAt":"2025-09-27T03:51:55Z","isPatch":true,"sender":{"key":"name:ionnss","avatar":null},"body":"From: ionnss <zara.leonardo@gmail.com>\n\nRemove unnecessary backslashes from language code examples.\nThe underscores in \"ll\\_CC\" and \"zh\\_CN\" don't need escaping\nin Markdown.\n\nSigned-off-by: ionnss <zara.leonardo@gmail.com>\n---\n po/README.md | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/po/README.md b/po/README.md\nindex ec08aa24ad..d7757bed4e 100644\n--- a/po/README.md\n+++ b/po/README.md\n@@ -13,9 +13,9 @@ We will use XX as an alias to refer to the language translation code in\n the following paragraphs, for example we use \"po/XX.po\" to refer to the\n translation file for a specific language. But this doesn't mean that\n the language code has only two letters. The language code can be in one\n-of two forms: \"ll\" or \"ll\\_CC\". Here \"ll\" is the ISO 639 two-letter\n+of two forms: \"ll\" or \"ll_CC\". Here \"ll\" is the ISO 639 two-letter\n language code and \"CC\" is the ISO 3166 two-letter code for country names\n-and subdivisions. For example: \"de\" for German language code, \"zh\\_CN\"\n+and subdivisions. For example: \"de\" for German language code, \"zh_CN\"\n for Simplified Chinese language code.\n \n \n@@ -126,7 +126,7 @@ you add a translation for the first time by running:\n make po-init PO_FILE=po/XX.po\n ```\n \n-where XX is the locale, e.g. \"de\", \"is\", \"pt\\_BR\", \"zh\\_CN\", etc.\n+where XX is the locale, e.g. \"de\", \"is\", \"pt_BR\", \"zh_CN\", etc.\n \n The newly generated message file \"po/XX.po\" is based on the core pot\n file \"po/git-core.pot\", so it contains only a minimal set of messages\n-- \ngitgitgadget\n\n"},{"id":"527476","messageId":"479c263bc19aa5cdfe9d990b2521f88b1620759c.1758945111.git.gitgitgadget@gmail.com","threadId":"64201","inReplyTo":"pull.1977.v3.git.1758945111.gitgitgadget@gmail.com","subject":"[PATCH v3 2/3] libgit-rs: add get_bool() method to ConfigSet","fromName":"ionnss via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-27T03:51:50Z","receivedAt":"2025-09-27T03:51:57Z","isPatch":true,"sender":{"key":"name:ionnss","avatar":null},"body":"From: ionnss <zara.leonardo@gmail.com>\n\nAdd support for parsing boolean configuration values using Git's\ngit_configset_get_bool() C function. This ensures consistent behavior\nwith Git's native boolean parsing logic.\n\nThe method handles all Git boolean formats (true/false, yes/no, on/off,\n1/0) and edge cases like \"00\" and \"100\" correctly.\n\nIncludes comprehensive tests for various boolean formats and edge cases.\n\nSigned-off-by: ionnss <zara.leonardo@gmail.com>\n---\n contrib/libgit-rs/src/config.rs    | 26 ++++++++++++++++++++++++++\n contrib/libgit-rs/testdata/config3 |  2 +-\n contrib/libgit-rs/testdata/config4 |  9 +++++++++\n contrib/libgit-sys/src/lib.rs      |  6 ++++++\n 4 files changed, 42 insertions(+), 1 deletion(-)\n create mode 100644 contrib/libgit-rs/testdata/config4\n\ndiff --git a/contrib/libgit-rs/src/config.rs b/contrib/libgit-rs/src/config.rs\nindex 6bf04845c8..72ee88801b 100644\n--- a/contrib/libgit-rs/src/config.rs\n+++ b/contrib/libgit-rs/src/config.rs\n@@ -68,6 +68,20 @@ impl ConfigSet {\n             Some(owned_str)\n         }\n     }\n+\n+    /// Load the value for the given key and attempt to parse it as a boolean. Dies with a fatal error\n+    /// if the value cannot be parsed. Returns None if the key is not present.\n+    pub fn get_bool(&mut self, key: &str) -> Option<bool> {\n+        let key = CString::new(key).expect(\"config key should be valid CString\");\n+        let mut val: c_int = 0;\n+        unsafe {\n+            if libgit_configset_get_bool(self.0, key.as_ptr(), &mut val as *mut c_int) != 0 {\n+                return None;\n+            }\n+        }\n+\n+        Some(val != 0)\n+    }\n }\n \n impl Default for ConfigSet {\n@@ -95,6 +109,7 @@ mod tests {\n             Path::new(\"testdata/config1\"),\n             Path::new(\"testdata/config2\"),\n             Path::new(\"testdata/config3\"),\n+            Path::new(\"testdata/config4\"),\n         ]);\n         // ConfigSet retrieves correct value\n         assert_eq!(cs.get_int(\"trace2.eventTarget\"), Some(1));\n@@ -102,5 +117,16 @@ mod tests {\n         assert_eq!(cs.get_int(\"trace2.eventNesting\"), Some(3));\n         // ConfigSet returns None for missing key\n         assert_eq!(cs.get_string(\"foo.bar\"), None);\n+        // Test boolean parsing - comprehensive tests\n+        assert_eq!(cs.get_bool(\"test.boolTrue\"), Some(true));\n+        assert_eq!(cs.get_bool(\"test.boolFalse\"), Some(false));\n+        assert_eq!(cs.get_bool(\"test.boolYes\"), Some(true));\n+        assert_eq!(cs.get_bool(\"test.boolNo\"), Some(false));\n+        assert_eq!(cs.get_bool(\"test.boolOne\"), Some(true));\n+        assert_eq!(cs.get_bool(\"test.boolZero\"), Some(false));\n+        assert_eq!(cs.get_bool(\"test.boolZeroZero\"), Some(false)); // \"00\" → false\n+        assert_eq!(cs.get_bool(\"test.boolHundred\"), Some(true)); // \"100\" → true\n+        // Test missing boolean key\n+        assert_eq!(cs.get_bool(\"missing.boolean\"), None);\n     }\n }\ndiff --git a/contrib/libgit-rs/testdata/config3 b/contrib/libgit-rs/testdata/config3\nindex ca7b9a7c38..3ea5b96f12 100644\n--- a/contrib/libgit-rs/testdata/config3\n+++ b/contrib/libgit-rs/testdata/config3\n@@ -1,2 +1,2 @@\n [trace2]\n-\teventNesting = 3\n+\teventNesting = 3\n\\ No newline at end of file\ndiff --git a/contrib/libgit-rs/testdata/config4 b/contrib/libgit-rs/testdata/config4\nnew file mode 100644\nindex 0000000000..c0755a32be\n--- /dev/null\n+++ b/contrib/libgit-rs/testdata/config4\n@@ -0,0 +1,9 @@\n+[test]\n+\tboolTrue = true\n+\tboolFalse = false\n+\tboolYes = yes\n+\tboolNo = no\n+\tboolOne = 1\n+\tboolZero = 0\n+\tboolZeroZero = 00\n+\tboolHundred = 100\ndiff --git a/contrib/libgit-sys/src/lib.rs b/contrib/libgit-sys/src/lib.rs\nindex 4bfc650450..b104fda8f6 100644\n--- a/contrib/libgit-sys/src/lib.rs\n+++ b/contrib/libgit-sys/src/lib.rs\n@@ -43,6 +43,12 @@ extern \"C\" {\n         dest: *mut *mut c_char,\n     ) -> c_int;\n \n+    pub fn libgit_configset_get_bool(\n+        cs: *mut libgit_config_set,\n+        key: *const c_char,\n+        dest: *mut c_int,\n+    ) -> c_int;\n+\n }\n \n #[cfg(test)]\n-- \ngitgitgadget\n\n"},{"id":"527477","messageId":"1ac8d768194b15eaf536000ed5f76f36dd0a39b2.1758945111.git.gitgitgadget@gmail.com","threadId":"64201","inReplyTo":"pull.1977.v3.git.1758945111.gitgitgadget@gmail.com","subject":"[PATCH v3 3/3] libgit-rs: add get_ulong() and get_pathname() methods","fromName":"ionnss via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-27T03:51:51Z","receivedAt":"2025-09-27T03:51:59Z","isPatch":true,"sender":{"key":"name:ionnss","avatar":null},"body":"From: ionnss <zara.leonardo@gmail.com>\n\nExpand the ConfigSet API with additional configuration value types:\n\n- get_ulong(): Parse unsigned long integers for large numeric values\n- get_pathname(): Parse file paths, returning PathBuf for type safety\n\nBoth functions follow the same pattern as existing get_* methods,\nusing Git's C functions for consistent parsing behavior.\n\nAdd comprehensive tests covering normal cases, edge cases, and\nerror handling for all new functionality.\n\nSigned-off-by: ionnss <zara.leonardo@gmail.com>\n---\n contrib/libgit-rs/src/config.rs    | 57 ++++++++++++++++++++++++++++--\n contrib/libgit-rs/testdata/config4 |  4 +++\n contrib/libgit-sys/src/lib.rs      | 18 +++++++++-\n 3 files changed, 76 insertions(+), 3 deletions(-)\n\ndiff --git a/contrib/libgit-rs/src/config.rs b/contrib/libgit-rs/src/config.rs\nindex 72ee88801b..ffd9f311b6 100644\n--- a/contrib/libgit-rs/src/config.rs\n+++ b/contrib/libgit-rs/src/config.rs\n@@ -1,8 +1,8 @@\n use std::ffi::{c_void, CStr, CString};\n-use std::path::Path;\n+use std::path::{Path, PathBuf};\n \n #[cfg(has_std__ffi__c_char)]\n-use std::ffi::{c_char, c_int};\n+use std::ffi::{c_char, c_int, c_ulong};\n \n #[cfg(not(has_std__ffi__c_char))]\n #[allow(non_camel_case_types)]\n@@ -12,6 +12,10 @@ type c_char = i8;\n #[allow(non_camel_case_types)]\n type c_int = i32;\n \n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+type c_ulong = u64;\n+\n use libgit_sys::*;\n \n /// A ConfigSet is an in-memory cache for config-like files such as `.gitmodules` or `.gitconfig`.\n@@ -82,6 +86,41 @@ impl ConfigSet {\n \n         Some(val != 0)\n     }\n+\n+    /// Load the value for the given key and attempt to parse it as an unsigned long. Dies with a fatal error\n+    /// if the value cannot be parsed. Returns None if the key is not present.\n+    pub fn get_ulong(&mut self, key: &str) -> Option<u64> {\n+        let key = CString::new(key).expect(\"config key should be valid CString\");\n+        let mut val: c_ulong = 0;\n+        unsafe {\n+            if libgit_configset_get_ulong(self.0, key.as_ptr(), &mut val as *mut c_ulong) != 0 {\n+                return None;\n+            }\n+        }\n+        Some(val as u64)\n+    }\n+\n+    /// Load the value for the given key and attempt to parse it as a file path. Dies with a fatal error\n+    /// if the value cannot be converted to a PathBuf. Returns None if the key is not present.\n+    pub fn get_pathname(&mut self, key: &str) -> Option<PathBuf> {\n+        let key = CString::new(key).expect(\"config key should be valid CString\");\n+        let mut val: *mut c_char = std::ptr::null_mut();\n+        unsafe {\n+            if libgit_configset_get_pathname(self.0, key.as_ptr(), &mut val as *mut *mut c_char)\n+                != 0\n+            {\n+                return None;\n+            }\n+            let borrowed_str = CStr::from_ptr(val);\n+            let owned_str = String::from(\n+                borrowed_str\n+                    .to_str()\n+                    .expect(\"config path should be valid UTF-8\"),\n+            );\n+            free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side\n+            Some(PathBuf::from(owned_str))\n+        }\n+    }\n }\n \n impl Default for ConfigSet {\n@@ -128,5 +167,19 @@ mod tests {\n         assert_eq!(cs.get_bool(\"test.boolHundred\"), Some(true)); // \"100\" → true\n         // Test missing boolean key\n         assert_eq!(cs.get_bool(\"missing.boolean\"), None);\n+        // Test ulong parsing\n+        assert_eq!(cs.get_ulong(\"test.ulongSmall\"), Some(42));\n+        assert_eq!(cs.get_ulong(\"test.ulongBig\"), Some(4294967296)); // > 32-bit int\n+        assert_eq!(cs.get_ulong(\"missing.ulong\"), None);\n+        // Test pathname parsing\n+        assert_eq!(\n+            cs.get_pathname(\"test.pathRelative\"),\n+            Some(PathBuf::from(\"./some/path\"))\n+        );\n+        assert_eq!(\n+            cs.get_pathname(\"test.pathAbsolute\"),\n+            Some(PathBuf::from(\"/usr/bin/git\"))\n+        );\n+        assert_eq!(cs.get_pathname(\"missing.path\"), None);\n     }\n }\ndiff --git a/contrib/libgit-rs/testdata/config4 b/contrib/libgit-rs/testdata/config4\nindex c0755a32be..bd621ab480 100644\n--- a/contrib/libgit-rs/testdata/config4\n+++ b/contrib/libgit-rs/testdata/config4\n@@ -7,3 +7,7 @@\n \tboolZero = 0\n \tboolZeroZero = 00\n \tboolHundred = 100\n+\tulongSmall = 42\n+\tulongBig = 4294967296\n+\tpathRelative = ./some/path\n+\tpathAbsolute = /usr/bin/git\ndiff --git a/contrib/libgit-sys/src/lib.rs b/contrib/libgit-sys/src/lib.rs\nindex b104fda8f6..07386572ec 100644\n--- a/contrib/libgit-sys/src/lib.rs\n+++ b/contrib/libgit-sys/src/lib.rs\n@@ -1,7 +1,7 @@\n use std::ffi::c_void;\n \n #[cfg(has_std__ffi__c_char)]\n-use std::ffi::{c_char, c_int};\n+use std::ffi::{c_char, c_int, c_ulong};\n \n #[cfg(not(has_std__ffi__c_char))]\n #[allow(non_camel_case_types)]\n@@ -11,6 +11,10 @@ pub type c_char = i8;\n #[allow(non_camel_case_types)]\n pub type c_int = i32;\n \n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+pub type c_ulong = u64;\n+\n extern crate libz_sys;\n \n #[allow(non_camel_case_types)]\n@@ -49,6 +53,18 @@ extern \"C\" {\n         dest: *mut c_int,\n     ) -> c_int;\n \n+    pub fn libgit_configset_get_ulong(\n+        cs: *mut libgit_config_set,\n+        key: *const c_char,\n+        dest: *mut c_ulong,\n+    ) -> c_int;\n+\n+    pub fn libgit_configset_get_pathname(\n+        cs: *mut libgit_config_set,\n+        key: *const c_char,\n+        dest: *mut *mut c_char,\n+    ) -> c_int;\n+\n }\n \n #[cfg(test)]\n-- \ngitgitgadget\n"},{"id":"527585","messageId":"a3febed5-22aa-41cb-a602-39eae148c131@gmail.com","threadId":"64201","inReplyTo":"479c263bc19aa5cdfe9d990b2521f88b1620759c.1758945111.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 2/3] libgit-rs: add get_bool() method to ConfigSet","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-09-29T13:23:48Z","receivedAt":"2025-09-29T13:23:46Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 27/09/2025 04:51, ionnss via GitGitGadget wrote:\n> From: ionnss <zara.leonardo@gmail.com>\n> \n> Add support for parsing boolean configuration values using Git's\n> git_configset_get_bool() C function. This ensures consistent behavior\n> with Git's native boolean parsing logic.\n> \n> The method handles all Git boolean formats (true/false, yes/no, on/off,\n> 1/0) and edge cases like \"00\" and \"100\" correctly.\n>  \n> Includes comprehensive tests for various boolean formats and edge cases.\n\nUnfortunately when I try to run those tests with\n\n    make INCLUDE_LIBGIT_RS=1 && cd contrib/libgit-rs && cargo test\n\nI see\n\n   = note: /usr/bin/ld: \n/home/phil/src/git/update-ref-date/contrib/libgit-rs/target/debug/deps/libgit-8c021564b9d1ec26.9j3smxv8a5f6u8czhaxq401mn.rcgu.o: \nin function `libgit::config::ConfigSet::get_bool':\n  \n/home/phil/src/git/update-ref-date/contrib/libgit-rs/src/config.rs:78: \nundefined reference to `libgit_configset_get_bool'\n           collect2: error: ld returned 1 exit status\n\nThis happens because libgit_configset_get_bool() is not defined in \ncontrib/libgit-sys/public_symbol_export.[ch]. You need to wrap \nconfigset_get_bool() in the some way that configset_get_int() is.\n\nThanks\n\nPhillip\n\n> Signed-off-by: ionnss <zara.leonardo@gmail.com>\n> ---\n>   contrib/libgit-rs/src/config.rs    | 26 ++++++++++++++++++++++++++\n>   contrib/libgit-rs/testdata/config3 |  2 +-\n>   contrib/libgit-rs/testdata/config4 |  9 +++++++++\n>   contrib/libgit-sys/src/lib.rs      |  6 ++++++\n>   4 files changed, 42 insertions(+), 1 deletion(-)\n>   create mode 100644 contrib/libgit-rs/testdata/config4\n> \n> diff --git a/contrib/libgit-rs/src/config.rs b/contrib/libgit-rs/src/config.rs\n> index 6bf04845c8..72ee88801b 100644\n> --- a/contrib/libgit-rs/src/config.rs\n> +++ b/contrib/libgit-rs/src/config.rs\n> @@ -68,6 +68,20 @@ impl ConfigSet {\n>               Some(owned_str)\n>           }\n>       }\n> +\n> +    /// Load the value for the given key and attempt to parse it as a boolean. Dies with a fatal error\n> +    /// if the value cannot be parsed. Returns None if the key is not present.\n> +    pub fn get_bool(&mut self, key: &str) -> Option<bool> {\n> +        let key = CString::new(key).expect(\"config key should be valid CString\");\n> +        let mut val: c_int = 0;\n> +        unsafe {\n> +            if libgit_configset_get_bool(self.0, key.as_ptr(), &mut val as *mut c_int) != 0 {\n> +                return None;\n> +            }\n> +        }\n> +\n> +        Some(val != 0)\n> +    }\n>   }\n>   \n>   impl Default for ConfigSet {\n> @@ -95,6 +109,7 @@ mod tests {\n>               Path::new(\"testdata/config1\"),\n>               Path::new(\"testdata/config2\"),\n>               Path::new(\"testdata/config3\"),\n> +            Path::new(\"testdata/config4\"),\n>           ]);\n>           // ConfigSet retrieves correct value\n>           assert_eq!(cs.get_int(\"trace2.eventTarget\"), Some(1));\n> @@ -102,5 +117,16 @@ mod tests {\n>           assert_eq!(cs.get_int(\"trace2.eventNesting\"), Some(3));\n>           // ConfigSet returns None for missing key\n>           assert_eq!(cs.get_string(\"foo.bar\"), None);\n> +        // Test boolean parsing - comprehensive tests\n> +        assert_eq!(cs.get_bool(\"test.boolTrue\"), Some(true));\n> +        assert_eq!(cs.get_bool(\"test.boolFalse\"), Some(false));\n> +        assert_eq!(cs.get_bool(\"test.boolYes\"), Some(true));\n> +        assert_eq!(cs.get_bool(\"test.boolNo\"), Some(false));\n> +        assert_eq!(cs.get_bool(\"test.boolOne\"), Some(true));\n> +        assert_eq!(cs.get_bool(\"test.boolZero\"), Some(false));\n> +        assert_eq!(cs.get_bool(\"test.boolZeroZero\"), Some(false)); // \"00\" → false\n> +        assert_eq!(cs.get_bool(\"test.boolHundred\"), Some(true)); // \"100\" → true\n> +        // Test missing boolean key\n> +        assert_eq!(cs.get_bool(\"missing.boolean\"), None);\n>       }\n>   }\n> diff --git a/contrib/libgit-rs/testdata/config3 b/contrib/libgit-rs/testdata/config3\n> index ca7b9a7c38..3ea5b96f12 100644\n> --- a/contrib/libgit-rs/testdata/config3\n> +++ b/contrib/libgit-rs/testdata/config3\n> @@ -1,2 +1,2 @@\n>   [trace2]\n> -\teventNesting = 3\n> +\teventNesting = 3\n> \\ No newline at end of file\n> diff --git a/contrib/libgit-rs/testdata/config4 b/contrib/libgit-rs/testdata/config4\n> new file mode 100644\n> index 0000000000..c0755a32be\n> --- /dev/null\n> +++ b/contrib/libgit-rs/testdata/config4\n> @@ -0,0 +1,9 @@\n> +[test]\n> +\tboolTrue = true\n> +\tboolFalse = false\n> +\tboolYes = yes\n> +\tboolNo = no\n> +\tboolOne = 1\n> +\tboolZero = 0\n> +\tboolZeroZero = 00\n> +\tboolHundred = 100\n> diff --git a/contrib/libgit-sys/src/lib.rs b/contrib/libgit-sys/src/lib.rs\n> index 4bfc650450..b104fda8f6 100644\n> --- a/contrib/libgit-sys/src/lib.rs\n> +++ b/contrib/libgit-sys/src/lib.rs\n> @@ -43,6 +43,12 @@ extern \"C\" {\n>           dest: *mut *mut c_char,\n>       ) -> c_int;\n>   \n> +    pub fn libgit_configset_get_bool(\n> +        cs: *mut libgit_config_set,\n> +        key: *const c_char,\n> +        dest: *mut c_int,\n> +    ) -> c_int;\n> +\n>   }\n>   \n>   #[cfg(test)]\n\n"},{"id":"527586","messageId":"9e7da34f-8d1d-4223-a160-e0223984aeaf@gmail.com","threadId":"64201","inReplyTo":"1ac8d768194b15eaf536000ed5f76f36dd0a39b2.1758945111.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 3/3] libgit-rs: add get_ulong() and get_pathname() methods","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-09-29T13:23:59Z","receivedAt":"2025-09-29T13:23:56Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 27/09/2025 04:51, ionnss via GitGitGadget wrote:\n> From: ionnss <zara.leonardo@gmail.com>\n> \n> Expand the ConfigSet API with additional configuration value types:\n> \n> - get_ulong(): Parse unsigned long integers for large numeric values\n\nI'm torn as to whether we should keep the same name as the C function as \nyou have done, or call it get_u64 to avoid the name depending on a \nplatform dependent type. It is very welcome that this function returns u64.\n\n> - get_pathname(): Parse file paths, returning PathBuf for type safety\n\nI'm not sure why these two functions are in the same commit, it would \nmake more sense to separate them out I think like you have done for \nget_bool().\n\n> Both functions follow the same pattern as existing get_* methods,\n> using Git's C functions for consistent parsing behavior.\n> \n> Add comprehensive tests covering normal cases, edge cases, and\n> error handling for all new functionality.\n\nWe can debate what constituets \"comprehensive\" but it is good to see \nthat the new functions are tested.\n\n> diff --git a/contrib/libgit-rs/src/config.rs b/contrib/libgit-rs/src/config.rs\n> index 72ee88801b..ffd9f311b6 100644\n> --- a/contrib/libgit-rs/src/config.rs\n> +++ b/contrib/libgit-rs/src/config.rs\n> @@ -1,8 +1,8 @@\n>   use std::ffi::{c_void, CStr, CString};\n> -use std::path::Path;\n> +use std::path::{Path, PathBuf};\n>   \n>   #[cfg(has_std__ffi__c_char)]\n> -use std::ffi::{c_char, c_int};\n> +use std::ffi::{c_char, c_int, c_ulong};\n>   \n>   #[cfg(not(has_std__ffi__c_char))]\n>   #[allow(non_camel_case_types)]\n> @@ -12,6 +12,10 @@ type c_char = i8;\n>   #[allow(non_camel_case_types)]\n>   type c_int = i32;\n>   \n> +#[cfg(not(has_std__ffi__c_char))]\n> +#[allow(non_camel_case_types)]\n> +type c_ulong = u64;\nThis is a bit problematic as the type depends on the platform. I'm not \nentirely clear why the current code doesn't just rely on having std::ffi \ndefine these types.\n\n> +\n>   use libgit_sys::*;\n>   \n>   /// A ConfigSet is an in-memory cache for config-like files such as `.gitmodules` or `.gitconfig`.\n> @@ -82,6 +86,41 @@ impl ConfigSet {\n>   \n>           Some(val != 0)\n>       }\n> +\n> +    /// Load the value for the given key and attempt to parse it as an unsigned long. Dies with a fatal error\n> +    /// if the value cannot be parsed. Returns None if the key is not present.\n\nPlease wrap the comments to 80 columns\n\n> +    pub fn get_ulong(&mut self, key: &str) -> Option<u64> {\n> +        let key = CString::new(key).expect(\"config key should be valid CString\");\n> +        let mut val: c_ulong = 0;\n> +        unsafe {\n> +            if libgit_configset_get_ulong(self.0, key.as_ptr(), &mut val as *mut c_ulong) != 0 {\n> +                return None;\n> +            }\n> +        }\n> +        Some(val as u64)\n> +    }\n\nThis looks good\n\n> +    /// Load the value for the given key and attempt to parse it as a file path. Dies with a fatal error\n> +    /// if the value cannot be converted to a PathBuf. Returns None if the key is not present.\n> +    pub fn get_pathname(&mut self, key: &str) -> Option<PathBuf> {\n> +        let key = CString::new(key).expect(\"config key should be valid CString\");\n> +        let mut val: *mut c_char = std::ptr::null_mut();\n> +        unsafe {\n> +            if libgit_configset_get_pathname(self.0, key.as_ptr(), &mut val as *mut *mut c_char)\n> +                != 0\n> +            {\n> +                return None;\n> +            }\n> +            let borrowed_str = CStr::from_ptr(val);\n> +            let owned_str = String::from(\n> +                borrowed_str\n> +                    .to_str()\n> +                    .expect(\"config path should be valid UTF-8\"),\n\nAs we're returning a PathBuf it is a shame that we're restricted to \nUTF-8 encoded paths. Unfortunately rust's standard library does not seem \nto provide an easy way to convert a CStr to an OsString on windows as \none needs to convert it to a UTF-16 encoded string first.\n\n> +    pub fn libgit_configset_get_ulong(\n> +        cs: *mut libgit_config_set,\n> +        key: *const c_char,\n> +        dest: *mut c_ulong,\n> +    ) -> c_int;\n> +\n> +    pub fn libgit_configset_get_pathname(\n> +        cs: *mut libgit_config_set,\n> +        key: *const c_char,\n> +        dest: *mut *mut c_char,\n> +    ) -> c_int;\n\nAs with the previous patch you need to define these functions in \npublic_symbol_export.[ch]\n\nThanks\n\nPhillip\n\n\n"},{"id":"527587","messageId":"5cceeaa7-d019-49c9-8849-78643788142e@gmail.com","threadId":"64201","inReplyTo":"d7810781fc3af1902c99cccbd50853dd630a0080.1758945111.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 1/3] po: fix escaped underscores in README.md","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-09-29T13:26:10Z","receivedAt":"2025-09-29T13:26:08Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"As Junio has already said, you should drop this patch from this series \nas it is unrelated to the others and submit it separately (which I think \nyou may have done already) Running\n\n     git rebase --onto HEAD~3 HEAD~2\n\nshould do the trick\n\nThanks\n\nPhillip\n\nOn 27/09/2025 04:51, ionnss via GitGitGadget wrote:\n> From: ionnss <zara.leonardo@gmail.com>\n> \n> Remove unnecessary backslashes from language code examples.\n> The underscores in \"ll\\_CC\" and \"zh\\_CN\" don't need escaping\n> in Markdown.\n> \n> Signed-off-by: ionnss <zara.leonardo@gmail.com>\n> ---\n>   po/README.md | 6 +++---\n>   1 file changed, 3 insertions(+), 3 deletions(-)\n> \n> diff --git a/po/README.md b/po/README.md\n> index ec08aa24ad..d7757bed4e 100644\n> --- a/po/README.md\n> +++ b/po/README.md\n> @@ -13,9 +13,9 @@ We will use XX as an alias to refer to the language translation code in\n>   the following paragraphs, for example we use \"po/XX.po\" to refer to the\n>   translation file for a specific language. But this doesn't mean that\n>   the language code has only two letters. The language code can be in one\n> -of two forms: \"ll\" or \"ll\\_CC\". Here \"ll\" is the ISO 639 two-letter\n> +of two forms: \"ll\" or \"ll_CC\". Here \"ll\" is the ISO 639 two-letter\n>   language code and \"CC\" is the ISO 3166 two-letter code for country names\n> -and subdivisions. For example: \"de\" for German language code, \"zh\\_CN\"\n> +and subdivisions. For example: \"de\" for German language code, \"zh_CN\"\n>   for Simplified Chinese language code.\n>   \n>   \n> @@ -126,7 +126,7 @@ you add a translation for the first time by running:\n>   make po-init PO_FILE=po/XX.po\n>   ```\n>   \n> -where XX is the locale, e.g. \"de\", \"is\", \"pt\\_BR\", \"zh\\_CN\", etc.\n> +where XX is the locale, e.g. \"de\", \"is\", \"pt_BR\", \"zh_CN\", etc.\n>   \n>   The newly generated message file \"po/XX.po\" is based on the core pot\n>   file \"po/git-core.pot\", so it contains only a minimal set of messages\n\n"},{"id":"527622","messageId":"pull.1977.v4.git.1759221968318.gitgitgadget@gmail.com","threadId":"64201","inReplyTo":"pull.1977.v3.git.1758945111.gitgitgadget@gmail.com","subject":"[PATCH v4] libgit-rs: add get_bool(), get_ulong(), and get_pathname() methods","fromName":"ions via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-30T08:46:08Z","receivedAt":"2025-09-30T08:46:11Z","isPatch":true,"sender":{"key":"name:ions","avatar":null},"body":"From: ionnss <zara.leonardo@gmail.com>\n\nExpand ConfigSet API with three new configuration value parsers:\n\n- get_bool(): Parse boolean values using git_configset_get_bool()\n- get_ulong(): Parse unsigned long values\n- get_pathname(): Parse file paths, returning PathBuf\n\nAll methods use Git's C functions for consistent behavior and\ninclude wrapper functions in public_symbol_export.[ch] for FFI.\n\nIncludes comprehensive tests for all new functionality.\n\nSigned-off-by: ionnss <zara.leonardo@gmail.com>\n---\n    libgit-rs: add get_bool(), get_ulong(), and get_pathname() methods to\n    ConfigSet\n    \n    \n    libgit-rs: Enhanced ConfigSet API\n    =================================\n    \n    This series expands the ConfigSet API with support for additional\n    configuration value types, following Git's established patterns by\n    wrapping native C functions.\n    \n    \n    Implementation\n    ==============\n    \n    Adds three new ConfigSet methods:\n    \n     * get_bool(): Parse boolean values using git_configset_get_bool()\n     * get_ulong(): Parse unsigned long integers using\n       git_configset_get_ulong()\n     * get_pathname(): Parse file paths using git_configset_get_pathname(),\n       returning PathBuf\n    \n    All methods follow the existing pattern of get_int() and get_string(),\n    ensuring consistent behavior with Git's native parsing logic.\n    \n    \n    Changes in v4\n    =============\n    \n     * Fixed missing wrapper functions in public_symbol_export.[ch]\n       (Philip's feedback)\n     * Rebased to correct base commit bb69721404 (Junio's feedback)\n     * Squashed into single clean commit as requested\n     * Removed unrelated README.md commit\n     * Fixed commit message formatting per GitGitGadget requirements\n    \n    The ConfigSet API now supports 5 configuration value types: int, string,\n    bool, ulong, and pathname.\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1977%2Fionnss%2Fadd-rust-configset-get-bool-v4\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1977/ionnss/add-rust-configset-get-bool-v4\nPull-Request: https://github.com/gitgitgadget/git/pull/1977\n\nRange-diff vs v3:\n\n 1:  d7810781fc < -:  ---------- po: fix escaped underscores in README.md\n 2:  479c263bc1 < -:  ---------- libgit-rs: add get_bool() method to ConfigSet\n 3:  1ac8d76819 ! 1:  618deca808 libgit-rs: add get_ulong() and get_pathname() methods\n     @@ Metadata\n      Author: ionnss <zara.leonardo@gmail.com>\n      \n       ## Commit message ##\n     -    libgit-rs: add get_ulong() and get_pathname() methods\n     +    libgit-rs: add get_bool(), get_ulong(), and get_pathname() methods\n      \n     -    Expand the ConfigSet API with additional configuration value types:\n     +    Expand ConfigSet API with three new configuration value parsers:\n      \n     -    - get_ulong(): Parse unsigned long integers for large numeric values\n     -    - get_pathname(): Parse file paths, returning PathBuf for type safety\n     +    - get_bool(): Parse boolean values using git_configset_get_bool()\n     +    - get_ulong(): Parse unsigned long values\n     +    - get_pathname(): Parse file paths, returning PathBuf\n      \n     -    Both functions follow the same pattern as existing get_* methods,\n     -    using Git's C functions for consistent parsing behavior.\n     +    All methods use Git's C functions for consistent behavior and\n     +    include wrapper functions in public_symbol_export.[ch] for FFI.\n      \n     -    Add comprehensive tests covering normal cases, edge cases, and\n     -    error handling for all new functionality.\n     +    Includes comprehensive tests for all new functionality.\n      \n          Signed-off-by: ionnss <zara.leonardo@gmail.com>\n      \n     @@ contrib/libgit-rs/src/config.rs: type c_char = i8;\n       \n       /// A ConfigSet is an in-memory cache for config-like files such as `.gitmodules` or `.gitconfig`.\n      @@ contrib/libgit-rs/src/config.rs: impl ConfigSet {\n     - \n     -         Some(val != 0)\n     +             Some(owned_str)\n     +         }\n           }\n      +\n     ++    /// Load the value for the given key and attempt to parse it as a boolean. Dies with a fatal error\n     ++    /// if the value cannot be parsed. Returns None if the key is not present.\n     ++    pub fn get_bool(&mut self, key: &str) -> Option<bool> {\n     ++        let key = CString::new(key).expect(\"config key should be valid CString\");\n     ++        let mut val: c_int = 0;\n     ++        unsafe {\n     ++            if libgit_configset_get_bool(self.0, key.as_ptr(), &mut val as *mut c_int) != 0 {\n     ++                return None;\n     ++            }\n     ++        }\n     ++\n     ++        Some(val != 0)\n     ++    }\n     ++\n      +    /// Load the value for the given key and attempt to parse it as an unsigned long. Dies with a fatal error\n      +    /// if the value cannot be parsed. Returns None if the key is not present.\n      +    pub fn get_ulong(&mut self, key: &str) -> Option<u64> {\n     @@ contrib/libgit-rs/src/config.rs: impl ConfigSet {\n       \n       impl Default for ConfigSet {\n      @@ contrib/libgit-rs/src/config.rs: mod tests {\n     -         assert_eq!(cs.get_bool(\"test.boolHundred\"), Some(true)); // \"100\" → true\n     -         // Test missing boolean key\n     -         assert_eq!(cs.get_bool(\"missing.boolean\"), None);\n     +             Path::new(\"testdata/config1\"),\n     +             Path::new(\"testdata/config2\"),\n     +             Path::new(\"testdata/config3\"),\n     ++            Path::new(\"testdata/config4\"),\n     +         ]);\n     +         // ConfigSet retrieves correct value\n     +         assert_eq!(cs.get_int(\"trace2.eventTarget\"), Some(1));\n     +@@ contrib/libgit-rs/src/config.rs: mod tests {\n     +         assert_eq!(cs.get_int(\"trace2.eventNesting\"), Some(3));\n     +         // ConfigSet returns None for missing key\n     +         assert_eq!(cs.get_string(\"foo.bar\"), None);\n     ++        // Test boolean parsing - comprehensive tests\n     ++        assert_eq!(cs.get_bool(\"test.boolTrue\"), Some(true));\n     ++        assert_eq!(cs.get_bool(\"test.boolFalse\"), Some(false));\n     ++        assert_eq!(cs.get_bool(\"test.boolYes\"), Some(true));\n     ++        assert_eq!(cs.get_bool(\"test.boolNo\"), Some(false));\n     ++        assert_eq!(cs.get_bool(\"test.boolOne\"), Some(true));\n     ++        assert_eq!(cs.get_bool(\"test.boolZero\"), Some(false));\n     ++        assert_eq!(cs.get_bool(\"test.boolZeroZero\"), Some(false)); // \"00\" → false\n     ++        assert_eq!(cs.get_bool(\"test.boolHundred\"), Some(true)); // \"100\" → true\n     ++        // Test missing boolean key\n     ++        assert_eq!(cs.get_bool(\"missing.boolean\"), None);\n      +        // Test ulong parsing\n      +        assert_eq!(cs.get_ulong(\"test.ulongSmall\"), Some(42));\n      +        assert_eq!(cs.get_ulong(\"test.ulongBig\"), Some(4294967296)); // > 32-bit int\n     @@ contrib/libgit-rs/src/config.rs: mod tests {\n           }\n       }\n      \n     - ## contrib/libgit-rs/testdata/config4 ##\n     + ## contrib/libgit-rs/testdata/config3 ##\n      @@\n     - \tboolZero = 0\n     - \tboolZeroZero = 00\n     - \tboolHundred = 100\n     + [trace2]\n     +-\teventNesting = 3\n     ++\teventNesting = 3\n     + \\ No newline at end of file\n     +\n     + ## contrib/libgit-rs/testdata/config4 (new) ##\n     +@@\n     ++[test]\n     ++\tboolTrue = true\n     ++\tboolFalse = false\n     ++\tboolYes = yes\n     ++\tboolNo = no\n     ++\tboolOne = 1\n     ++\tboolZero = 0\n     ++\tboolZeroZero = 00\n     ++\tboolHundred = 100\n      +\tulongSmall = 42\n      +\tulongBig = 4294967296\n      +\tpathRelative = ./some/path\n      +\tpathAbsolute = /usr/bin/git\n      \n     + ## contrib/libgit-sys/public_symbol_export.c ##\n     +@@ contrib/libgit-sys/public_symbol_export.c: int libgit_configset_get_string(struct libgit_config_set *cs, const char *key,\n     + \treturn git_configset_get_string(&cs->cs, key, dest);\n     + }\n     + \n     ++int libgit_configset_get_bool(struct libgit_config_set *cs, const char *key,\n     ++\t\t\t      int *dest)\n     ++{\n     ++\treturn git_configset_get_bool(&cs->cs, key, dest);\n     ++}\n     ++\n     ++int libgit_configset_get_ulong(struct libgit_config_set *cs, const char *key,\n     ++\t\t\t       unsigned long *dest)\n     ++{\n     ++\treturn git_configset_get_ulong(&cs->cs, key, dest);\n     ++}\n     ++\n     ++int libgit_configset_get_pathname(struct libgit_config_set *cs, const char *key,\n     ++\t\t\t\t  char **dest)\n     ++{\n     ++\treturn git_configset_get_pathname(&cs->cs, key, dest);\n     ++}\n     ++\n     + const char *libgit_user_agent(void)\n     + {\n     + \treturn git_user_agent();\n     +\n     + ## contrib/libgit-sys/public_symbol_export.h ##\n     +@@ contrib/libgit-sys/public_symbol_export.h: int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int\n     + \n     + int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest);\n     + \n     ++int libgit_configset_get_bool(struct libgit_config_set *cs, const char *key, int *dest);\n     ++\n     ++int libgit_configset_get_ulong(struct libgit_config_set *cs, const char *key, unsigned long *dest);\n     ++\n     ++int libgit_configset_get_pathname(struct libgit_config_set *cs, const char *key, char **dest);\n     ++\n     + const char *libgit_user_agent(void);\n     + \n     + const char *libgit_user_agent_sanitized(void);\n     +\n       ## contrib/libgit-sys/src/lib.rs ##\n      @@\n       use std::ffi::c_void;\n     @@ contrib/libgit-sys/src/lib.rs: pub type c_char = i8;\n       \n       #[allow(non_camel_case_types)]\n      @@ contrib/libgit-sys/src/lib.rs: extern \"C\" {\n     -         dest: *mut c_int,\n     +         dest: *mut *mut c_char,\n           ) -> c_int;\n       \n     ++    pub fn libgit_configset_get_bool(\n     ++        cs: *mut libgit_config_set,\n     ++        key: *const c_char,\n     ++        dest: *mut c_int,\n     ++    ) -> c_int;\n     ++\n      +    pub fn libgit_configset_get_ulong(\n      +        cs: *mut libgit_config_set,\n      +        key: *const c_char,\n\n\n contrib/libgit-rs/src/config.rs           | 83 ++++++++++++++++++++++-\n contrib/libgit-rs/testdata/config3        |  2 +-\n contrib/libgit-rs/testdata/config4        | 13 ++++\n contrib/libgit-sys/public_symbol_export.c | 18 +++++\n contrib/libgit-sys/public_symbol_export.h |  6 ++\n contrib/libgit-sys/src/lib.rs             | 24 ++++++-\n 6 files changed, 142 insertions(+), 4 deletions(-)\n create mode 100644 contrib/libgit-rs/testdata/config4\n\ndiff --git a/contrib/libgit-rs/src/config.rs b/contrib/libgit-rs/src/config.rs\nindex 6bf04845c8..ffd9f311b6 100644\n--- a/contrib/libgit-rs/src/config.rs\n+++ b/contrib/libgit-rs/src/config.rs\n@@ -1,8 +1,8 @@\n use std::ffi::{c_void, CStr, CString};\n-use std::path::Path;\n+use std::path::{Path, PathBuf};\n \n #[cfg(has_std__ffi__c_char)]\n-use std::ffi::{c_char, c_int};\n+use std::ffi::{c_char, c_int, c_ulong};\n \n #[cfg(not(has_std__ffi__c_char))]\n #[allow(non_camel_case_types)]\n@@ -12,6 +12,10 @@ type c_char = i8;\n #[allow(non_camel_case_types)]\n type c_int = i32;\n \n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+type c_ulong = u64;\n+\n use libgit_sys::*;\n \n /// A ConfigSet is an in-memory cache for config-like files such as `.gitmodules` or `.gitconfig`.\n@@ -68,6 +72,55 @@ impl ConfigSet {\n             Some(owned_str)\n         }\n     }\n+\n+    /// Load the value for the given key and attempt to parse it as a boolean. Dies with a fatal error\n+    /// if the value cannot be parsed. Returns None if the key is not present.\n+    pub fn get_bool(&mut self, key: &str) -> Option<bool> {\n+        let key = CString::new(key).expect(\"config key should be valid CString\");\n+        let mut val: c_int = 0;\n+        unsafe {\n+            if libgit_configset_get_bool(self.0, key.as_ptr(), &mut val as *mut c_int) != 0 {\n+                return None;\n+            }\n+        }\n+\n+        Some(val != 0)\n+    }\n+\n+    /// Load the value for the given key and attempt to parse it as an unsigned long. Dies with a fatal error\n+    /// if the value cannot be parsed. Returns None if the key is not present.\n+    pub fn get_ulong(&mut self, key: &str) -> Option<u64> {\n+        let key = CString::new(key).expect(\"config key should be valid CString\");\n+        let mut val: c_ulong = 0;\n+        unsafe {\n+            if libgit_configset_get_ulong(self.0, key.as_ptr(), &mut val as *mut c_ulong) != 0 {\n+                return None;\n+            }\n+        }\n+        Some(val as u64)\n+    }\n+\n+    /// Load the value for the given key and attempt to parse it as a file path. Dies with a fatal error\n+    /// if the value cannot be converted to a PathBuf. Returns None if the key is not present.\n+    pub fn get_pathname(&mut self, key: &str) -> Option<PathBuf> {\n+        let key = CString::new(key).expect(\"config key should be valid CString\");\n+        let mut val: *mut c_char = std::ptr::null_mut();\n+        unsafe {\n+            if libgit_configset_get_pathname(self.0, key.as_ptr(), &mut val as *mut *mut c_char)\n+                != 0\n+            {\n+                return None;\n+            }\n+            let borrowed_str = CStr::from_ptr(val);\n+            let owned_str = String::from(\n+                borrowed_str\n+                    .to_str()\n+                    .expect(\"config path should be valid UTF-8\"),\n+            );\n+            free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side\n+            Some(PathBuf::from(owned_str))\n+        }\n+    }\n }\n \n impl Default for ConfigSet {\n@@ -95,6 +148,7 @@ mod tests {\n             Path::new(\"testdata/config1\"),\n             Path::new(\"testdata/config2\"),\n             Path::new(\"testdata/config3\"),\n+            Path::new(\"testdata/config4\"),\n         ]);\n         // ConfigSet retrieves correct value\n         assert_eq!(cs.get_int(\"trace2.eventTarget\"), Some(1));\n@@ -102,5 +156,30 @@ mod tests {\n         assert_eq!(cs.get_int(\"trace2.eventNesting\"), Some(3));\n         // ConfigSet returns None for missing key\n         assert_eq!(cs.get_string(\"foo.bar\"), None);\n+        // Test boolean parsing - comprehensive tests\n+        assert_eq!(cs.get_bool(\"test.boolTrue\"), Some(true));\n+        assert_eq!(cs.get_bool(\"test.boolFalse\"), Some(false));\n+        assert_eq!(cs.get_bool(\"test.boolYes\"), Some(true));\n+        assert_eq!(cs.get_bool(\"test.boolNo\"), Some(false));\n+        assert_eq!(cs.get_bool(\"test.boolOne\"), Some(true));\n+        assert_eq!(cs.get_bool(\"test.boolZero\"), Some(false));\n+        assert_eq!(cs.get_bool(\"test.boolZeroZero\"), Some(false)); // \"00\" → false\n+        assert_eq!(cs.get_bool(\"test.boolHundred\"), Some(true)); // \"100\" → true\n+        // Test missing boolean key\n+        assert_eq!(cs.get_bool(\"missing.boolean\"), None);\n+        // Test ulong parsing\n+        assert_eq!(cs.get_ulong(\"test.ulongSmall\"), Some(42));\n+        assert_eq!(cs.get_ulong(\"test.ulongBig\"), Some(4294967296)); // > 32-bit int\n+        assert_eq!(cs.get_ulong(\"missing.ulong\"), None);\n+        // Test pathname parsing\n+        assert_eq!(\n+            cs.get_pathname(\"test.pathRelative\"),\n+            Some(PathBuf::from(\"./some/path\"))\n+        );\n+        assert_eq!(\n+            cs.get_pathname(\"test.pathAbsolute\"),\n+            Some(PathBuf::from(\"/usr/bin/git\"))\n+        );\n+        assert_eq!(cs.get_pathname(\"missing.path\"), None);\n     }\n }\ndiff --git a/contrib/libgit-rs/testdata/config3 b/contrib/libgit-rs/testdata/config3\nindex ca7b9a7c38..3ea5b96f12 100644\n--- a/contrib/libgit-rs/testdata/config3\n+++ b/contrib/libgit-rs/testdata/config3\n@@ -1,2 +1,2 @@\n [trace2]\n-\teventNesting = 3\n+\teventNesting = 3\n\\ No newline at end of file\ndiff --git a/contrib/libgit-rs/testdata/config4 b/contrib/libgit-rs/testdata/config4\nnew file mode 100644\nindex 0000000000..bd621ab480\n--- /dev/null\n+++ b/contrib/libgit-rs/testdata/config4\n@@ -0,0 +1,13 @@\n+[test]\n+\tboolTrue = true\n+\tboolFalse = false\n+\tboolYes = yes\n+\tboolNo = no\n+\tboolOne = 1\n+\tboolZero = 0\n+\tboolZeroZero = 00\n+\tboolHundred = 100\n+\tulongSmall = 42\n+\tulongBig = 4294967296\n+\tpathRelative = ./some/path\n+\tpathAbsolute = /usr/bin/git\ndiff --git a/contrib/libgit-sys/public_symbol_export.c b/contrib/libgit-sys/public_symbol_export.c\nindex dfbb257115..8d6a0e1ba5 100644\n--- a/contrib/libgit-sys/public_symbol_export.c\n+++ b/contrib/libgit-sys/public_symbol_export.c\n@@ -46,6 +46,24 @@ int libgit_configset_get_string(struct libgit_config_set *cs, const char *key,\n \treturn git_configset_get_string(&cs->cs, key, dest);\n }\n \n+int libgit_configset_get_bool(struct libgit_config_set *cs, const char *key,\n+\t\t\t      int *dest)\n+{\n+\treturn git_configset_get_bool(&cs->cs, key, dest);\n+}\n+\n+int libgit_configset_get_ulong(struct libgit_config_set *cs, const char *key,\n+\t\t\t       unsigned long *dest)\n+{\n+\treturn git_configset_get_ulong(&cs->cs, key, dest);\n+}\n+\n+int libgit_configset_get_pathname(struct libgit_config_set *cs, const char *key,\n+\t\t\t\t  char **dest)\n+{\n+\treturn git_configset_get_pathname(&cs->cs, key, dest);\n+}\n+\n const char *libgit_user_agent(void)\n {\n \treturn git_user_agent();\ndiff --git a/contrib/libgit-sys/public_symbol_export.h b/contrib/libgit-sys/public_symbol_export.h\nindex 701db92d53..f9adda6561 100644\n--- a/contrib/libgit-sys/public_symbol_export.h\n+++ b/contrib/libgit-sys/public_symbol_export.h\n@@ -11,6 +11,12 @@ int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int\n \n int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest);\n \n+int libgit_configset_get_bool(struct libgit_config_set *cs, const char *key, int *dest);\n+\n+int libgit_configset_get_ulong(struct libgit_config_set *cs, const char *key, unsigned long *dest);\n+\n+int libgit_configset_get_pathname(struct libgit_config_set *cs, const char *key, char **dest);\n+\n const char *libgit_user_agent(void);\n \n const char *libgit_user_agent_sanitized(void);\ndiff --git a/contrib/libgit-sys/src/lib.rs b/contrib/libgit-sys/src/lib.rs\nindex 4bfc650450..07386572ec 100644\n--- a/contrib/libgit-sys/src/lib.rs\n+++ b/contrib/libgit-sys/src/lib.rs\n@@ -1,7 +1,7 @@\n use std::ffi::c_void;\n \n #[cfg(has_std__ffi__c_char)]\n-use std::ffi::{c_char, c_int};\n+use std::ffi::{c_char, c_int, c_ulong};\n \n #[cfg(not(has_std__ffi__c_char))]\n #[allow(non_camel_case_types)]\n@@ -11,6 +11,10 @@ pub type c_char = i8;\n #[allow(non_camel_case_types)]\n pub type c_int = i32;\n \n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+pub type c_ulong = u64;\n+\n extern crate libz_sys;\n \n #[allow(non_camel_case_types)]\n@@ -43,6 +47,24 @@ extern \"C\" {\n         dest: *mut *mut c_char,\n     ) -> c_int;\n \n+    pub fn libgit_configset_get_bool(\n+        cs: *mut libgit_config_set,\n+        key: *const c_char,\n+        dest: *mut c_int,\n+    ) -> c_int;\n+\n+    pub fn libgit_configset_get_ulong(\n+        cs: *mut libgit_config_set,\n+        key: *const c_char,\n+        dest: *mut c_ulong,\n+    ) -> c_int;\n+\n+    pub fn libgit_configset_get_pathname(\n+        cs: *mut libgit_config_set,\n+        key: *const c_char,\n+        dest: *mut *mut c_char,\n+    ) -> c_int;\n+\n }\n \n #[cfg(test)]\n\nbase-commit: bb69721404348ea2db0a081c41ab6ebfe75bdec8\n-- \ngitgitgadget\n"},{"id":"527675","messageId":"0cb55558-a48e-42ac-a80e-a24ee2be55ee@gmail.com","threadId":"64201","inReplyTo":"pull.1977.v4.git.1759221968318.gitgitgadget@gmail.com","subject":"Re: [PATCH v4] libgit-rs: add get_bool(), get_ulong(), and get_pathname() methods","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-10-01T10:15:28Z","receivedAt":"2025-10-01T10:15:32Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"[I've cc'd brian to sanity check my suggestion for handling c_ulong in \nrust 1.63 which lacks std::ffi::c_ulong]\n\nOn 30/09/2025 09:46, ions via GitGitGadget wrote:\n> From: ionnss <zara.leonardo@gmail.com>\n> \n> Expand ConfigSet API with three new configuration value parsers:\n> \n> - get_bool(): Parse boolean values using git_configset_get_bool()\n> - get_ulong(): Parse unsigned long values\n> - get_pathname(): Parse file paths, returning PathBuf\n\nI would be nice to explain in the commit message why we require paths to \nbe utf-8 encoded. I've left one comment below, apart from that I think \nthis looks good.\n> --- a/contrib/libgit-sys/src/lib.rs\n> +++ b/contrib/libgit-sys/src/lib.rs\n> @@ -1,7 +1,7 @@\n>   use std::ffi::c_void;\n>   \n>   #[cfg(has_std__ffi__c_char)]\n> -use std::ffi::{c_char, c_int};\n> +use std::ffi::{c_char, c_int, c_ulong};\n>   \n>   #[cfg(not(has_std__ffi__c_char))]\n>   #[allow(non_camel_case_types)]\n> @@ -11,6 +11,10 @@ pub type c_char = i8;\n>   #[allow(non_camel_case_types)]\n>   pub type c_int = i32;\n>   \n> +#[cfg(not(has_std__ffi__c_char))]\n> +#[allow(non_camel_case_types)]\n> +pub type c_ulong = u64;\n\nAs I said before this wont work because C's ulong type is platform \ndependent so you cannot assume it 64 bits wide. Looking at the previous \ndiscussion[1] the reason we have these fallback definitions is because \nstd::ffi::c_int etc were only added in rust 1.64 and we want to support \nrust 1.63 as that is the version shipped by Debian oldstable. I think it \nwould be better to have a separate preparatory patch that changes the \nexisting fallbacks to\n\n#[cfg(not(has_std__ff__c_char))]\nuse std::os::raw::{c_char, c_int};\n\nand then this patch can add \"c_ulong\" to the list.\n\nThanks\n\nPhillip\n\n[1] \nhttps://lore.kernel.org/git/ZtivGeDZ_MZDEDB_@tapette.crustytoothpaste.net/\n\n\n"},{"id":"528041","messageId":"aOQyioJChUkJOXRb@fruit.crustytoothpaste.net","threadId":"64201","inReplyTo":"0cb55558-a48e-42ac-a80e-a24ee2be55ee@gmail.com","subject":"Re: [PATCH v4] libgit-rs: add get_bool(), get_ulong(), and get_pathname() methods","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-10-06T21:20:10Z","receivedAt":"2025-10-06T21:20:13Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2025-10-01 at 10:15:28, Phillip Wood wrote:\n> [I've cc'd brian to sanity check my suggestion for handling c_ulong in rust\n> 1.63 which lacks std::ffi::c_ulong]\n> \n> As I said before this wont work because C's ulong type is platform dependent\n> so you cannot assume it 64 bits wide. Looking at the previous discussion[1]\n> the reason we have these fallback definitions is because std::ffi::c_int etc\n> were only added in rust 1.64 and we want to support rust 1.63 as that is the\n> version shipped by Debian oldstable. I think it would be better to have a\n> separate preparatory patch that changes the existing fallbacks to\n> \n> #[cfg(not(has_std__ff__c_char))]\n> use std::os::raw::{c_char, c_int};\n> \n> and then this patch can add \"c_ulong\" to the list.\n\nIt's just fine to use `std::os::raw` in general without needing to use\n`std::ffi` conditionally.  I'd just default to that until we move away\nfrom Rust 1.63.  That's what I do myself.\n-- \nbrian m. carlson (they/them)\nToronto, Ontario, CA\n"},{"id":"528242","messageId":"da003e1f-99b4-4f13-a052-294d3e5f3eab@gmail.com","threadId":"64201","inReplyTo":"aOQyioJChUkJOXRb@fruit.crustytoothpaste.net","subject":"Re: [PATCH v4] libgit-rs: add get_bool(), get_ulong(), and get_pathname() methods","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-10-08T13:36:51Z","receivedAt":"2025-10-08T13:36:56Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 06/10/2025 22:20, brian m. carlson wrote:\n> On 2025-10-01 at 10:15:28, Phillip Wood wrote:\n>> [I've cc'd brian to sanity check my suggestion for handling c_ulong in rust\n>> 1.63 which lacks std::ffi::c_ulong]\n>>\n>> As I said before this wont work because C's ulong type is platform dependent\n>> so you cannot assume it 64 bits wide. Looking at the previous discussion[1]\n>> the reason we have these fallback definitions is because std::ffi::c_int etc\n>> were only added in rust 1.64 and we want to support rust 1.63 as that is the\n>> version shipped by Debian oldstable. I think it would be better to have a\n>> separate preparatory patch that changes the existing fallbacks to\n>>\n>> #[cfg(not(has_std__ff__c_char))]\n>> use std::os::raw::{c_char, c_int};\n>>\n>> and then this patch can add \"c_ulong\" to the list.\n> \n> It's just fine to use `std::os::raw` in general without needing to use\n> `std::ffi` conditionally.  I'd just default to that until we move away\n> from Rust 1.63.  That's what I do myself.\n\nThanks brian, that sounds much simpler. Lets add a preparatory commit to \nuse std::os::raw unconditionally.\n\nThanks\n\nPhillip\n"}]}