| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent cf43820 commit 71af3aa
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -511,6 +511,24 @@ def is_line_continuation(value: str) -> bool: | |||
| 511 | 511 | return False | |
| 512 | 512 | return escaped | |
| 513 | 513 | ||
| 514 | + def strip_inline_comment(value: str) -> str: | ||
| 515 | + """Cut an unquoted ``#`` or ``;`` comment, as git's ``parse_value`` does. | ||
| 516 | + | ||
| 517 | + Quoting and backslash escapes are honoured, so a ``#`` inside a quoted | ||
| 518 | + value is literal and an unterminated quote swallows the rest of the line. | ||
| 519 | + """ | ||
| 520 | + quoted = escaped = False | ||
| 521 | + for index, char in enumerate(value): | ||
| 522 | + if escaped: | ||
| 523 | + escaped = False | ||
| 524 | + elif char == "\\": | ||
| 525 | + escaped = True | ||
| 526 | + elif char == '"': | ||
| 527 | + quoted = not quoted | ||
| 528 | + elif char in "#;" and not quoted: | ||
| 529 | + return value[:index] | ||
| 530 | + return value | ||
| 531 | + | ||
| 514 | 532 | def parse_value(value: str) -> str: | |
| 515 | 533 | parsed: List[str] = [] | |
| 516 | 534 | whitespace: List[str] = [] | |
@@ -575,11 +593,7 @@ def parse_value(value: str) -> str: | |||
| 575 | 593 | optname, vi, optval = mo.group("option", "vi", "value") | |
| 576 | 594 | optname = self.optionxform(optname.rstrip()) | |
| 577 | 595 | ||
| 578 | - if vi in ("=", ":") and ";" in optval and not optval.strip().startswith('"'): | ||
| 579 | - pos = optval.find(";") | ||
| 580 | - if pos != -1 and optval[pos - 1].isspace(): | ||
| 581 | - optval = optval[:pos] | ||
| 582 | - optval = optval.strip() | ||
| 596 | + optval = strip_inline_comment(optval).strip() | ||
| 583 | 597 | ||
| 584 | 598 | if len(optval) < 2 or optval[0] != '"': | |
| 585 | 599 | # Does not open quoting. | |
@@ -589,7 +603,6 @@ def parse_value(value: str) -> str: | |||
| 589 | 603 | # next line is appended before the complete value is | |
| 590 | 604 | # parsed. An even number means the last backslash is | |
| 591 | 605 | # escaped and the value ends there. | |
| 592 | - continued = False | ||
| 593 | 606 | while True: | |
| 594 | 607 | if not is_line_continuation(optval): | |
| 595 | 608 | break | |
@@ -603,9 +616,7 @@ def parse_value(value: str) -> str: | |||
| 603 | 616 | while joined.endswith("\n") or joined.endswith("\r"): | |
| 604 | 617 | joined = joined[:-1] | |
| 605 | 618 | optval = optval[:-1] + joined | |
| 606 | - continued = True | ||
| 607 | - if continued: | ||
| 608 | - optval = parse_value(optval) | ||
| 619 | + optval = parse_value(optval) | ||
| 609 | 620 | elif optval[-1] != '"': | |
| 610 | 621 | # Opens quoting and does not close: appears to start multi-line quoting. | |
| 611 | 622 | is_multi_line = True | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -239,6 +239,29 @@ def test_multi_line_config(self): | |||
| 239 | 239 | ) | |
| 240 | 240 | self.assertEqual(len(config.sections()), 23) | |
| 241 | 241 | ||
| 242 | + def test_inline_comments_are_stripped_like_git(self): | ||
| 243 | + """A `#` or `;` outside quotes starts a comment, with or without a space | ||
| 244 | + before it, and whether or not the value is quoted. Expectations are what | ||
| 245 | + `git config -f <file> --get a.k` prints on git 2.50.1.""" | ||
| 246 | + cases = [ | ||
| 247 | + (b"[a]\n\tk = value # comment\n", "value"), | ||
| 248 | + (b"[a]\n\tk = value ; comment\n", "value"), | ||
| 249 | + (b"[a]\n\tk = value#nospace\n", "value"), | ||
| 250 | + (b"[a]\n\tk = value;nospace\n", "value"), | ||
| 251 | + (b"[a]\n\tk = a # b ; c\n", "a"), | ||
| 252 | + (b'[a]\n\tk = "quoted" # after\n', "quoted"), | ||
| 253 | + # A comment character inside quotes is literal. | ||
| 254 | + (b'[a]\n\tk = "has # inside"\n', "has # inside"), | ||
| 255 | + (b'[a]\n\tk = "has ; inside"\n', "has ; inside"), | ||
| 256 | + ] | ||
| 257 | + for content, expected in cases: | ||
| 258 | + config_file = io.BytesIO(content) | ||
| 259 | + config_file.name = "inline_comment.config" | ||
| 260 | + config = GitConfigParser(config_file) | ||
| 261 | + config.read() | ||
| 262 | + with self.subTest(content=content): | ||
| 263 | + self.assertEqual(config.get_value("a", "k"), expected) | ||
| 264 | + | ||
| 242 | 265 | def test_backslash_line_continuation(self): | |
| 243 | 266 | """An unquoted value ending in a backslash continues on the next line, | |
| 244 | 267 | exactly as git config parses it: the final backslash and the newline | |
@@ -250,7 +273,10 @@ def test_backslash_line_continuation(self): | |||
| 250 | 273 | (b"[a]\n\tk = one\\\n two ; ignored\n", "one two"), | |
| 251 | 274 | (b'[a]\n\tk = one\\\n "two"\n', "one two"), | |
| 252 | 275 | (b"[a]\n\tk = one\\\n two\\tthree\n", "one two\tthree"), | |
| 253 | - (b"[a]\n\tk = val\\\\\n next\n", "val\\\\"), | ||
| 276 | + # An even number of trailing backslashes is an escaped backslash, not a | ||
| 277 | + # continuation, so the value ends here and the escape resolves: | ||
| 278 | + # `git config --get a.k` prints `val\\`. | ||
| 279 | + (b"[a]\n\tk = val\\\\\n next\n", "val\\"), | ||
| 254 | 280 | (b"[a]\n\tk = end\\\n", "end"), | |
| 255 | 281 | (b"[alias]\n\tco = checkout \\\n\t\t-v\n", "checkout \t\t-v"), | |
| 256 | 282 | ] | |
| Back | FazBrowse Home | New Git URL |
0 commit comments