|
17 | 17 | from threading import Lock |
18 | 18 | from typing import Any, Callable, Dict, Iterator, List, Optional, Sequence, Tuple, cast |
19 | 19 |
|
20 | | -from git.compat import safe_decode |
| 20 | +from git.compat import defenc, safe_decode |
21 | 21 | from git.exc import GitCommandError |
22 | 22 |
|
23 | 23 | _selection = os.environ.get("GIT_PYTHON_BACKEND") |
@@ -382,6 +382,48 @@ def _for_each_ref(repo: Any, args: List[str], kwargs: Dict[str, Any]) -> bytes: |
382 | 382 | ) |
383 | 383 |
|
384 | 384 |
|
| 385 | +def submodule_config(source: Any) -> Any: |
| 386 | + """Read the fields used by submodule objects from their selected config source.""" |
| 387 | + if gix is None: |
| 388 | + return NotImplemented |
| 389 | + try: |
| 390 | + if isinstance(source, io.BytesIO): |
| 391 | + data = source.getvalue() |
| 392 | + else: |
| 393 | + with open(source, "rb") as stream: |
| 394 | + data = stream.read() |
| 395 | + # ponytail: no bound section enumeration; conservatively reject other '[' |
| 396 | + # occurrences and duplicate sections until GIX-12 can preserve their order. |
| 397 | + if b"\0" in data or re.search(rb"\[(?!submodule[ \t])", data, re.I): |
| 398 | + raise _Unsupported("non-submodule configuration sections (GIX-12)") |
| 399 | + modules = gix.ModulesFile.from_bytes(data) |
| 400 | + with modules.names() as cursor: |
| 401 | + names = list(cursor) |
| 402 | + if data.count(b"[") != len(names): |
| 403 | + raise _Unsupported("duplicate or ambiguous configuration sections (GIX-12)") |
| 404 | + config = modules.config() |
| 405 | + result = {} |
| 406 | + for raw_name in names: |
| 407 | + name = raw_name.decode(defenc) |
| 408 | + values = {} |
| 409 | + for option in ("path", "url", "branch"): |
| 410 | + key = "submodule." + name + "." + option |
| 411 | + value = config.string(key) |
| 412 | + if value is None: |
| 413 | + if option != "branch" or config.boolean(key) is not None: |
| 414 | + raise _Unsupported("missing or implicit submodule fields") |
| 415 | + else: |
| 416 | + values[option] = value.decode(defenc) |
| 417 | + result[name] = values |
| 418 | + except _Unsupported as exc: |
| 419 | + return _fallback("Submodule.config", str(exc)) |
| 420 | + except (gix.Error, OSError, UnicodeError) as exc: |
| 421 | + _logger.debug("native submodule configuration: %s", exc) |
| 422 | + return _fallback("Submodule.config", "native parse diagnostics") |
| 423 | + record("Submodule.config", "native") |
| 424 | + return result |
| 425 | + |
| 426 | + |
385 | 427 | def _config(repo: Any, args: List[str], kwargs: Dict[str, Any]) -> bytes: |
386 | 428 | if len(args) == 2 and args[0] == "--get": |
387 | 429 | snapshot = repo.config_snapshot() |
|
0 commit comments