Monorepo management for opam overlays
at main 85 lines 3.2 kB view raw
1type error = 2 | Git_error of Git.error 3 | Feature_exists of string 4 | Feature_not_found of string 5 | Config_error of string 6 7let pp_error ppf = function 8 | Git_error e -> Fmt.pf ppf "Git error: %a" Git.pp_error e 9 | Feature_exists name -> Fmt.pf ppf "Feature '%s' already exists" name 10 | Feature_not_found name -> Fmt.pf ppf "Feature '%s' not found" name 11 | Config_error msg -> Fmt.pf ppf "Configuration error: %s" msg 12 13let error_hint = function 14 | Git_error _ -> Some "Check that the monorepo is properly initialized" 15 | Feature_exists name -> 16 Some (Printf.sprintf "Run 'monopam feature remove %s' first if you want to recreate it" name) 17 | Feature_not_found name -> 18 Some (Printf.sprintf "Run 'monopam feature list' to see available features, or 'monopam feature add %s' to create it" name) 19 | Config_error _ -> Some "Run 'monopam init' to create a workspace configuration" 20 21let pp_error_with_hint ppf e = 22 pp_error ppf e; 23 match error_hint e with 24 | Some hint -> Fmt.pf ppf "@.Hint: %s" hint 25 | None -> () 26 27type entry = { 28 name : string; 29 path : Fpath.t; 30 branch : string; 31} 32 33let pp_entry ppf e = 34 Fmt.pf ppf "%s -> %a (branch: %s)" e.name Fpath.pp e.path e.branch 35 36(* Get the work directory path: root/work *) 37let work_path config = Fpath.(Verse_config.root config / "work") 38 39(* Get the feature worktree path: root/work/<name> *) 40let feature_path config name = Fpath.(work_path config / name) 41 42let add ~proc ~fs ~config ~name () = 43 let mono = Verse_config.mono_path config in 44 let work_dir = work_path config in 45 let wt_path = feature_path config name in 46 (* Check if feature already exists *) 47 if Git.Worktree.exists ~proc ~fs ~repo:mono ~path:wt_path then 48 Error (Feature_exists name) 49 else begin 50 (* Ensure work directory exists *) 51 let work_eio = Eio.Path.(fs / Fpath.to_string work_dir) in 52 (try Eio.Path.mkdirs ~perm:0o755 work_eio with Eio.Io _ -> ()); 53 (* Create the worktree with a new branch *) 54 match Git.Worktree.add ~proc ~fs ~repo:mono ~path:wt_path ~branch:name () with 55 | Error e -> Error (Git_error e) 56 | Ok () -> Ok { name; path = wt_path; branch = name } 57 end 58 59let remove ~proc ~fs ~config ~name ~force () = 60 let mono = Verse_config.mono_path config in 61 let wt_path = feature_path config name in 62 (* Check if feature exists *) 63 if not (Git.Worktree.exists ~proc ~fs ~repo:mono ~path:wt_path) then 64 Error (Feature_not_found name) 65 else 66 match Git.Worktree.remove ~proc ~fs ~repo:mono ~path:wt_path ~force () with 67 | Error e -> Error (Git_error e) 68 | Ok () -> Ok () 69 70let list ~proc ~fs ~config () = 71 let mono = Verse_config.mono_path config in 72 let work_dir = work_path config in 73 let all_worktrees = Git.Worktree.list ~proc ~fs mono in 74 (* Filter to only worktrees under work/ directory *) 75 List.filter_map (fun (wt : Git.Worktree.entry) -> 76 (* Check if this worktree is under the work directory *) 77 let wt_str = Fpath.to_string wt.path in 78 let work_str = Fpath.to_string work_dir in 79 if String.starts_with ~prefix:work_str wt_str then 80 let name = Fpath.basename wt.path in 81 let branch = Option.value ~default:name wt.branch in 82 Some { name; path = wt.path; branch } 83 else 84 None 85 ) all_worktrees