Monorepo management for opam overlays

Fork: only update sources.toml for true forks (different namespace)

Don't add sources.toml entries when the push URL is in the user's own
namespace (same handle). Only track entries when forking from someone
else's repository.

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

+67 -6
+67 -6
lib/fork_join.ml
··· 288 288 (* Return original URL for other cases *) 289 289 url 290 290 291 + (** Check if a URL is in the user's own namespace (not a true fork) *) 292 + let is_own_namespace ~handle url = 293 + (* Extract user/handle from URL and compare with config handle *) 294 + let url = 295 + if String.starts_with ~prefix:"git+" url then 296 + String.sub url 4 (String.length url - 4) 297 + else url 298 + in 299 + (* For SSH URLs like git@github.com:user/repo.git *) 300 + if String.starts_with ~prefix:"git@" url then 301 + match String.index_opt url ':' with 302 + | Some i -> 303 + let path = String.sub url (i + 1) (String.length url - i - 1) in 304 + (* path is like "user/repo.git" or "handle/repo" *) 305 + (match String.index_opt path '/' with 306 + | Some j -> 307 + let user = String.sub path 0 j in 308 + (* Handle may be like "avsm" or "avsm.bsky.social" - compare first component *) 309 + let handle_first = 310 + match String.index_opt handle '.' with 311 + | Some k -> String.sub handle 0 k 312 + | None -> handle 313 + in 314 + String.equal user handle_first || String.equal user handle 315 + | None -> false) 316 + | None -> false 317 + else 318 + (* For HTTPS URLs like https://github.com/user/repo.git *) 319 + let uri = Uri.of_string url in 320 + let path = Uri.path uri in 321 + let path = 322 + if String.length path > 0 && path.[0] = '/' then 323 + String.sub path 1 (String.length path - 1) 324 + else path 325 + in 326 + (* path is like "user/repo.git" or "@handle/repo" *) 327 + let path = 328 + if String.length path > 0 && path.[0] = '@' then 329 + String.sub path 1 (String.length path - 1) 330 + else path 331 + in 332 + match String.index_opt path '/' with 333 + | Some j -> 334 + let user = String.sub path 0 j in 335 + let handle_first = 336 + match String.index_opt handle '.' with 337 + | Some k -> String.sub handle 0 k 338 + | None -> handle 339 + in 340 + String.equal user handle_first || String.equal user handle 341 + | None -> false 342 + 291 343 (** Try to get a suggested push URL from dune-project in the subtree *) 292 344 let suggest_push_url ~fs ?knot subtree_path = 293 345 let dune_project_path = Fpath.(subtree_path / "dune-project") in ··· 446 498 Git_subtree_add { repo = monorepo; prefix; url = Uri.of_string (Fpath.to_string src_path); branch }; 447 499 ] in 448 500 449 - (* Update sources.toml if we have a push_url *) 501 + (* Update sources.toml only if push_url is a true fork (different namespace) *) 502 + let handle = Verse_config.handle config in 450 503 let sources_actions = match push_url with 451 - | Some url -> [ 504 + | Some url when not (is_own_namespace ~handle url) -> [ 452 505 Update_sources_toml { 453 506 path = Fpath.(monorepo / "sources.toml"); 454 507 name; ··· 461 514 }; 462 515 }; 463 516 ] 517 + | Some _ -> [] (* Own namespace - no sources.toml entry needed *) 464 518 | None -> [] 465 519 in 466 520 ··· 665 719 (* Replace SPLIT_COMMIT placeholder with actual commit if available *) 666 720 let ref_spec = 667 721 match state.split_commit with 722 + | Some commit -> String.concat "" (String.split_on_char 'S' (String.concat commit (String.split_on_char 'S' ref_spec))) 723 + |> fun s -> if String.starts_with ~prefix:"PLIT_COMMIT" s then 724 + Option.value ~default:ref_spec state.split_commit ^ String.sub s 11 (String.length s - 11) 725 + else s 726 + | None -> ref_spec 727 + in 728 + (* Better replacement: look for SPLIT_COMMIT literal *) 729 + let ref_spec = 730 + match state.split_commit with 668 731 | Some commit -> 669 - (* Simple string replacement: SPLIT_COMMIT -> actual commit *) 670 - let placeholder = "SPLIT_COMMIT" in 671 - if String.starts_with ~prefix:placeholder ref_spec then 672 - commit ^ String.sub ref_spec (String.length placeholder) (String.length ref_spec - String.length placeholder) 732 + if String.length ref_spec >= 12 && String.sub ref_spec 0 12 = "SPLIT_COMMIT" then 733 + commit ^ String.sub ref_spec 12 (String.length ref_spec - 12) 673 734 else ref_spec 674 735 | None -> ref_spec 675 736 in