Better merge behavior for git-gate repos on extends #238
Reference in New Issue
Block a user
Delete Branch "git-gate-repo-field-merge-on-extends"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #237.
Summary
_pre_merge_git_reposinmanifest_extends.py: before parsing a child bottle, fills missing fields in same-named git-gate repo entries from the parent entry. Parent fields serve as defaults; child-declared fields win. This lets a child override onlykey:(or any subset of fields) without repeatingurl:andhost_key:._entry_to_rawhelper to convert aManifestGitEntryback to its YAML-equivalent raw dict (needed to reconstruct parent defaults for merging)._merge_git_remotesto key byNameinstead ofUpstreamHost. Name is the natural unique identity for a repo entry; host-keying incorrectly collapsed two repos with different names on the same host.TestExtendsGitMergecovering: field-merge ofkey:only, child URL override, and same-name-plus-new-name in one extend.@@ -133,0 +154,4 @@def _pre_merge_git_repos(parent_git: "tuple[ManifestGitEntry, ...]",child_raw: dict[str, object],) -> dict[str, object]:This is way too bespoke: I want a more generic deep merge for git-config repos that looks basically like this:
Should use a safe accessor/account for possible values not being there, but this seems like way too many lines for a relatively simple merge
Replace the bespoke _pre_merge_git_repos loop and _merge_git_remotes with a single _merge_git_repos_raw that does a name-keyed union merge at the raw dict level: build parent_repos from _entry_to_raw, then for each name in set(child) | set(parent) produce {**parent.get(n,{}), **child.get(n,{})}. child.git after from_dict already has the full merged set, so _merge_git_remotes is no longer needed.@@ -133,0 +154,4 @@def _merge_git_repos_raw(parent: "tuple[ManifestGitEntry, ...]",Agreed, this is too bespoke. The clean version is a per-name shallow merge —
git-gate.reposis already{ <name>: <entry> }in the raw manifest, so:drops both
_pre_merge_git_reposand_entry_to_raw, and makes the_merge_git_remoteshost-keying change moot — name is the identity here, so the two-repos-on-one-host collision goes away without special-casing.One wrinkle to flag:
_merge_bottlescurrently receives the parent already parsed (and the parent may itself be a merge result), so there is no parent raw repos dict to merge against — that's the only reason_entry_to_rawexisted. To keep the merge a pure dict overlay I'll thread the resolved repos dict through_resolve_one_bottlerather than reconstructing it from parsed entries. Reworking the PR to that shape and keeping the threeTestExtendsGitMergecases._validate_git_entries was written for static keys (PRD 0008) and ran os.path.isfile() on every entry's IdentityFile. gitea-provider repos (PRD 0047/0048) create their deploy key at provision time, so IdentityFile is empty at parse — tripping the check with an empty path ("git upstream key file not found for '<name>': "). Gate the host-file check on the static provider; gitea entries have nothing to verify here. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>