Commit Diff


commit - a99f7ffc57dc4c3c349f8b430c9230c23a68bd8a
commit + 4c5ec309ca6064e8c942e1e40491448a8c80c96d
blob - cd475edb8c7a87f47dfce9825d2cfbefa4e97551
blob + 7f09ae57a41e4ec5ad9871016e300ad1652086b9
--- dulwich/config.py
+++ dulwich/config.py
@@ -483,6 +483,46 @@ def _strip_comments(line: bytes) -> bytes:
     return line
 
 
+def _parse_section_header_line(line: bytes) -> Tuple[Section, bytes]:
+    # Parse section header ("[bla]")
+    line = _strip_comments(line).rstrip()
+    in_quotes = False
+    escaped = False
+    for i, c in enumerate(line):
+        if escaped:
+            escaped = False
+            continue
+        if c == ord(b'"'):
+            in_quotes = not in_quotes
+        if c == ord(b'\\'):
+            escaped = True
+        if c == ord(b']') and not in_quotes:
+            last = i
+            break
+    else:
+        raise ValueError("expected trailing ]")
+    pts = line[1:last].split(b" ", 1)
+    line = line[last + 1:]
+    section: Section
+    if len(pts) == 2:
+        if pts[1][:1] != b'"' or pts[1][-1:] != b'"':
+            raise ValueError("Invalid subsection %r" % pts[1])
+        else:
+            pts[1] = pts[1][1:-1]
+        if not _check_section_name(pts[0]):
+            raise ValueError("invalid section name %r" % pts[0])
+        section = (pts[0], pts[1])
+    else:
+        if not _check_section_name(pts[0]):
+            raise ValueError("invalid section name %r" % pts[0])
+        pts = pts[0].split(b".", 1)
+        if len(pts) == 2:
+            section = (pts[0], pts[1])
+        else:
+            section = (pts[0],)
+    return section, line
+
+
 class ConfigFile(ConfigDict):
     """A Git configuration file, like .git/config or ~/.gitconfig."""
 
@@ -508,31 +548,8 @@ class ConfigFile(ConfigDict):
                 line = line[3:]
             line = line.lstrip()
             if setting is None:
-                # Parse section header ("[bla]")
                 if len(line) > 0 and line[:1] == b"[":
-                    line = _strip_comments(line).rstrip()
-                    try:
-                        last = line.index(b"]")
-                    except ValueError:
-                        raise ValueError("expected trailing ]")
-                    pts = line[1:last].split(b" ", 1)
-                    line = line[last + 1 :]
-                    if len(pts) == 2:
-                        if pts[1][:1] != b'"' or pts[1][-1:] != b'"':
-                            raise ValueError("Invalid subsection %r" % pts[1])
-                        else:
-                            pts[1] = pts[1][1:-1]
-                        if not _check_section_name(pts[0]):
-                            raise ValueError("invalid section name %r" % pts[0])
-                        section = (pts[0], pts[1])
-                    else:
-                        if not _check_section_name(pts[0]):
-                            raise ValueError("invalid section name %r" % pts[0])
-                        pts = pts[0].split(b".", 1)
-                        if len(pts) == 2:
-                            section = (pts[0], pts[1])
-                        else:
-                            section = (pts[0],)
+                    section, line = _parse_section_header_line(line)
                     ret._values.setdefault(section)
                 if _strip_comments(line).strip() == b"":
                     continue
blob - 3078b6e638646abf6b8a5f739aa6d380b89f3cb2
blob + eea02e2c2e0e67ddfcd48c3e07731957da5f22fa
--- dulwich/tests/test_config.py
+++ dulwich/tests/test_config.py
@@ -103,6 +103,10 @@ class ConfigFileTests(TestCase):
     def test_comment_character_within_section_string(self):
         cf = self.from_file(b'[branch "foo#bar"] # a comment\nbar= foo\n')
         self.assertEqual(ConfigFile({(b"branch", b"foo#bar"): {b"bar": b"foo"}}), cf)
+
+    def test_closing_bracket_within_section_string(self):
+        cf = self.from_file(b'[branch "foo]bar"] # a comment\nbar= foo\n')
+        self.assertEqual(ConfigFile({(b"branch", b"foo]bar"): {b"bar": b"foo"}}), cf)
 
     def test_from_file_section(self):
         cf = self.from_file(b"[core]\nfoo = bar\n")