Skip to content

Commit 1461c35

Browse files
committed
fix: skip a UTF-8 BOM when reading config files
git ignores a UTF-8 BOM at the start of a config file, so a file written by a Windows editor still parses. GitConfigParser raised MissingSectionHeaderError instead, because the BOM was decoded into the first line, which then no longer matched a section header. Evidence on git 2.47.3: `git config -f bom.cfg --list` prints core.bare=true for a file starting with the three BOM bytes, while GitPython raised MissingSectionHeaderError. With this change both read the same values, also when the BOM file is pulled in through include.path. The new test fails without the change.
1 parent 303c48f commit 1461c35

2 files changed

Lines changed: 18 additions & 2 deletions

File tree

git/config.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -550,9 +550,14 @@ def parse_value(value: str) -> str:
550550

551551
while True:
552552
# We assume to read binary!
553-
line = fp.readline().decode(defenc)
554-
if not line:
553+
raw_line = fp.readline()
554+
if not raw_line:
555555
break
556+
if lineno == 0 and raw_line.startswith(b"\xef\xbb\xbf"):
557+
# A UTF-8 BOM is not part of the content. git skips it, so a
558+
# config file written by a Windows editor still parses.
559+
raw_line = raw_line[3:]
560+
line = raw_line.decode(defenc)
556561
lineno = lineno + 1
557562
# Comment or blank line?
558563
if line.strip() == "" or self.re_comment.match(line):

test/test_config.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,17 @@ def test_comment_backslash_does_not_continue_value(self, rw_dir):
354354
with GitConfigParser(config_path) as config:
355355
self.assertEqual(config.get_value("a", "x"), "two")
356356

357+
def test_utf8_bom_is_skipped_like_git(self):
358+
"""git skips a UTF-8 BOM at the start of a config file, so one written by
359+
a Windows editor still parses. Expectations are what
360+
`git config -f <file> --list` prints on git 2.47.3."""
361+
content = b"\xef\xbb\xbf[core]\n\tbare = true\n"
362+
config_file = io.BytesIO(content)
363+
config_file.name = "bom.config"
364+
config = GitConfigParser(config_file)
365+
config.read()
366+
self.assertIs(config.get_value("core", "bare"), True)
367+
357368
def test_config_value_with_trailing_new_line(self):
358369
config_content = b'[section-header]\nkey:"value\n"'
359370
config_file = io.BytesIO(config_content)

0 commit comments

Comments
 (0)