Monorepo management for opam overlays

Extract branch from dev-repo URL fragment

When the dev-repo field contains a URL fragment like #staging or #master,
extract it and use it as the package's branch. This allows specifying
branches in opam files rather than relying on config overrides.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

+15 -8
+2 -1
lib/forks.ml
··· 267 267 | None -> None 268 268 | Some url_str -> 269 269 if Opam_repo.is_git_url url_str then 270 - Some (pkg_name, Opam_repo.normalize_git_url url_str) 270 + let url, _branch = Opam_repo.normalize_git_url url_str in 271 + Some (pkg_name, url) 271 272 else None 272 273 with _ -> None 273 274 with _ -> None)
+6 -3
lib/opam_repo.ml
··· 31 31 | true -> String.sub url 4 (String.length url - 4) 32 32 | false -> url 33 33 in 34 - Uri.of_string url 34 + let uri = Uri.of_string url in 35 + let branch = Uri.fragment uri in 36 + let uri_without_fragment = Uri.with_fragment uri None in 37 + (uri_without_fragment, branch) 35 38 36 39 module OP = OpamParserTypes.FullPos 37 40 ··· 115 118 | Some url -> 116 119 if not (is_git_url url) then Error (Not_git_remote (name, url)) 117 120 else 118 - let dev_repo = normalize_git_url url in 121 + let dev_repo, branch = normalize_git_url url in 119 122 let depends = find_depends opamfile.file_contents in 120 123 let synopsis = find_synopsis opamfile.file_contents in 121 - Ok (Package.create ~name ~version ~dev_repo ~depends ?synopsis ()) 124 + Ok (Package.create ~name ~version ~dev_repo ?branch ~depends ?synopsis ()) 122 125 with 123 126 | Eio.Io _ as e -> Error (Io_error (Printexc.to_string e)) 124 127 | exn -> Error (Parse_error (path_str, Printexc.to_string exn)))
+7 -4
lib/opam_repo.mli
··· 69 69 Accepts URLs starting with "git+" or "git://" or ending with ".git", as well 70 70 as SSH-style URLs like "git@github.com:...". *) 71 71 72 - val normalize_git_url : string -> Uri.t 72 + val normalize_git_url : string -> Uri.t * string option 73 73 (** [normalize_git_url url] normalizes a git URL by removing the "git+" prefix 74 - if present. 74 + if present, and extracts the branch from the URL fragment if present. 75 + 76 + Returns [(uri, branch)] where [branch] is [Some b] if the URL had a 77 + fragment like "#main", or [None] otherwise. 75 78 76 - For example, "git+https://example.com/repo.git" becomes 77 - "https://example.com/repo.git". *) 79 + For example, "git+https://example.com/repo.git#main" becomes 80 + ("https://example.com/repo.git", Some "main"). *) 78 81 79 82 val scan_opam_files_for_deps : fs:_ Eio.Path.t -> Fpath.t -> string list 80 83 (** [scan_opam_files_for_deps ~fs dir_path] scans a directory for .opam files