A monorepo management tool for the agentic ages
at main 399 lines 9.7 kB view raw
1(** Git operations wrapped with Eio and robust error handling. 2 3 All git commands are executed via [Eio.Process] with proper logging 4 and error context. Errors are wrapped in [Eio.Exn.Io] with context 5 chains for debugging. *) 6 7(** {1 Error Types} *) 8 9type error = 10 | Command_failed of { 11 cmd : string list; 12 exit_code : int; 13 stdout : string; 14 stderr : string; 15 } 16 | Not_a_repository 17 | Remote_exists of string 18 | Remote_not_found of string 19 | Branch_exists of string 20 | Branch_not_found of string 21 | Merge_conflict of { branch : string; conflicting_files : string list } 22 | Rebase_conflict of { onto : string; hint : string } 23 | Uncommitted_changes 24 | Not_on_branch 25 | Detached_head 26 27val pp_error : Format.formatter -> error -> unit 28 29type Eio.Exn.err += E of error 30 31val err : error -> exn 32(** [err e] creates an [Eio.Exn.Io] exception with the given error. *) 33 34(** {1 Types} *) 35 36type proc_mgr = [ `Generic | `Unix ] Eio.Process.mgr_ty Eio.Resource.t 37type path = Eio.Fs.dir_ty Eio.Path.t 38 39(** {1 Low-level execution} *) 40 41val run : 42 proc_mgr:proc_mgr -> 43 ?cwd:path -> 44 ?audit:Audit.context -> 45 string list -> 46 (string, error) result 47(** [run ~proc_mgr args] executes [git args] and returns stdout on success. 48 If [audit] is provided, records the operation to the audit context. *) 49 50val run_exn : 51 proc_mgr:proc_mgr -> 52 ?cwd:path -> 53 ?audit:Audit.context -> 54 string list -> 55 string 56(** [run_exn ~proc_mgr args] executes [git args] and returns stdout. 57 Raises on failure with context. If [audit] is provided, records the operation. *) 58 59val run_lines : 60 proc_mgr:proc_mgr -> 61 ?cwd:path -> 62 ?audit:Audit.context -> 63 string list -> 64 string list 65(** [run_lines ~proc_mgr args] executes and splits output by newlines. 66 If [audit] is provided, records the operation. *) 67 68(** {1 Queries - Safe read-only operations} *) 69 70val is_repository : path -> bool 71(** [is_repository path] checks if [path] contains a [.git] directory. *) 72 73val current_branch : 74 proc_mgr:proc_mgr -> 75 cwd:path -> 76 string option 77(** [current_branch] returns [Some branch] if on a branch, [None] if detached. *) 78 79val current_branch_exn : 80 proc_mgr:proc_mgr -> 81 cwd:path -> 82 string 83(** [current_branch_exn] returns current branch or raises [Not_on_branch]. *) 84 85val current_head : 86 proc_mgr:proc_mgr -> 87 cwd:path -> 88 string 89(** [current_head] returns the current HEAD SHA. *) 90 91val has_uncommitted_changes : 92 proc_mgr:proc_mgr -> 93 cwd:path -> 94 bool 95(** [has_uncommitted_changes] returns true if there are staged or unstaged changes. *) 96 97val remote_exists : 98 proc_mgr:proc_mgr -> 99 cwd:path -> 100 string -> 101 bool 102(** [remote_exists ~proc_mgr ~cwd name] checks if remote [name] exists. *) 103 104val branch_exists : 105 proc_mgr:proc_mgr -> 106 cwd:path -> 107 string -> 108 bool 109(** [branch_exists ~proc_mgr ~cwd name] checks if branch [name] exists. *) 110 111val rev_parse : 112 proc_mgr:proc_mgr -> 113 cwd:path -> 114 string -> 115 string option 116(** [rev_parse ~proc_mgr ~cwd ref] returns the SHA for [ref], or [None]. *) 117 118val rev_parse_exn : 119 proc_mgr:proc_mgr -> 120 cwd:path -> 121 string -> 122 string 123(** [rev_parse_exn] returns SHA or raises. *) 124 125val rev_parse_short : 126 proc_mgr:proc_mgr -> 127 cwd:path -> 128 string -> 129 string 130(** [rev_parse_short] returns abbreviated SHA. *) 131 132val ls_remote_default_branch : 133 proc_mgr:proc_mgr -> 134 cwd:path -> 135 url:string -> 136 string 137(** [ls_remote_default_branch ~proc_mgr ~cwd ~url] detects the default branch of remote. *) 138 139val list_remotes : 140 proc_mgr:proc_mgr -> 141 cwd:path -> 142 string list 143(** [list_remotes] returns all remote names. *) 144 145val remote_url : 146 proc_mgr:proc_mgr -> 147 cwd:path -> 148 string -> 149 string option 150(** [remote_url ~proc_mgr ~cwd name] returns the URL for remote [name]. *) 151 152val log_oneline : 153 proc_mgr:proc_mgr -> 154 cwd:path -> 155 ?max_count:int -> 156 string -> 157 string -> 158 string list 159(** [log_oneline ~proc_mgr ~cwd from_ref to_ref] returns commit summaries. *) 160 161val diff_stat : 162 proc_mgr:proc_mgr -> 163 cwd:path -> 164 string -> 165 string -> 166 string 167(** [diff_stat ~proc_mgr ~cwd from_ref to_ref] returns diff statistics. *) 168 169val ls_tree : 170 proc_mgr:proc_mgr -> 171 cwd:path -> 172 tree:string -> 173 path:string -> 174 bool 175(** [ls_tree ~proc_mgr ~cwd ~tree ~path] checks if [path] exists in [tree]. *) 176 177val rev_list_count : 178 proc_mgr:proc_mgr -> 179 cwd:path -> 180 string -> 181 string -> 182 int 183(** [rev_list_count ~proc_mgr ~cwd from_ref to_ref] counts commits between refs. *) 184 185(** {1 Idempotent mutations - Safe to re-run} *) 186 187val ensure_remote : 188 proc_mgr:proc_mgr -> 189 cwd:path -> 190 name:string -> 191 url:string -> 192 [ `Created | `Existed | `Updated ] 193(** [ensure_remote] adds remote if missing, updates URL if different. *) 194 195val ensure_branch : 196 proc_mgr:proc_mgr -> 197 cwd:path -> 198 name:string -> 199 start_point:string -> 200 [ `Created | `Existed ] 201(** [ensure_branch] creates branch if it doesn't exist. *) 202 203val ensure_vendored_remotes : 204 proc_mgr:proc_mgr -> 205 cwd:path -> 206 Config.vendored_package list -> 207 int 208(** [ensure_vendored_remotes ~proc_mgr ~cwd packages] ensures remotes exist for 209 all vendored packages. Returns the number of remotes created. 210 Use this to recreate remotes after cloning a workspace. *) 211 212(** {1 State-changing operations} *) 213 214val init : 215 proc_mgr:proc_mgr -> 216 cwd:path -> 217 unit 218(** [init] initializes a new git repository. *) 219 220val fetch : 221 proc_mgr:proc_mgr -> 222 cwd:path -> 223 remote:string -> 224 unit 225(** [fetch] fetches from a remote. *) 226 227val fetch_with_tags : 228 proc_mgr:proc_mgr -> 229 cwd:path -> 230 remote:string -> 231 unit 232(** [fetch_with_tags] fetches from a remote including all tags. *) 233 234val resolve_branch_or_tag : 235 proc_mgr:proc_mgr -> 236 cwd:path -> 237 remote:string -> 238 ref_name:string -> 239 string 240(** [resolve_branch_or_tag] tries to resolve a ref first as a remote tracking 241 branch (remote/ref_name), then as a tag (refs/tags/ref_name). Returns the 242 resolved ref or raises an exception if neither exists. *) 243 244val checkout : 245 proc_mgr:proc_mgr -> 246 cwd:path -> 247 string -> 248 unit 249(** [checkout] switches to a branch or commit. *) 250 251val checkout_orphan : 252 proc_mgr:proc_mgr -> 253 cwd:path -> 254 string -> 255 unit 256(** [checkout_orphan] creates and switches to a new orphan branch. *) 257 258val read_tree_prefix : 259 proc_mgr:proc_mgr -> 260 cwd:path -> 261 prefix:string -> 262 tree:string -> 263 unit 264(** [read_tree_prefix] reads a tree into the index with a path prefix. *) 265 266val checkout_index : 267 proc_mgr:proc_mgr -> 268 cwd:path -> 269 unit 270(** [checkout_index] checks out files from the index to working directory. *) 271 272val rm_rf : 273 proc_mgr:proc_mgr -> 274 cwd:path -> 275 target:string -> 276 unit 277(** [rm_rf] removes files/directories from git tracking. *) 278 279val rm_cached_rf : 280 proc_mgr:proc_mgr -> 281 cwd:path -> 282 unit 283(** [rm_cached_rf] removes all files from index (for orphan branch setup). *) 284 285val add_all : 286 proc_mgr:proc_mgr -> 287 cwd:path -> 288 unit 289(** [add_all] stages all changes. *) 290 291val commit : 292 proc_mgr:proc_mgr -> 293 cwd:path -> 294 message:string -> 295 unit 296(** [commit] creates a commit with the given message. *) 297 298val commit_allow_empty : 299 proc_mgr:proc_mgr -> 300 cwd:path -> 301 message:string -> 302 unit 303(** [commit_allow_empty] creates a commit even if there are no changes. *) 304 305val branch_create : 306 proc_mgr:proc_mgr -> 307 cwd:path -> 308 name:string -> 309 start_point:string -> 310 unit 311(** [branch_create] creates a new branch at [start_point]. *) 312 313val branch_force : 314 proc_mgr:proc_mgr -> 315 cwd:path -> 316 name:string -> 317 point:string -> 318 unit 319(** [branch_force] moves branch to point (creates if needed). *) 320 321val remote_add : 322 proc_mgr:proc_mgr -> 323 cwd:path -> 324 name:string -> 325 url:string -> 326 unit 327(** [remote_add] adds a new remote. *) 328 329val remote_set_url : 330 proc_mgr:proc_mgr -> 331 cwd:path -> 332 name:string -> 333 url:string -> 334 unit 335(** [remote_set_url] updates the URL of an existing remote. *) 336 337val merge_allow_unrelated : 338 proc_mgr:proc_mgr -> 339 cwd:path -> 340 branch:string -> 341 message:string -> 342 (unit, [ `Conflict of string list ]) result 343(** [merge_allow_unrelated] merges with [--allow-unrelated-histories]. 344 Returns [Error (`Conflict files)] if there are conflicts. *) 345 346val rebase : 347 proc_mgr:proc_mgr -> 348 cwd:path -> 349 onto:string -> 350 (unit, [ `Conflict of string ]) result 351(** [rebase] rebases current branch onto [onto]. 352 Returns [Error (`Conflict hint)] if there are conflicts. *) 353 354val rebase_abort : 355 proc_mgr:proc_mgr -> 356 cwd:path -> 357 unit 358(** [rebase_abort] aborts an in-progress rebase. *) 359 360val merge_abort : 361 proc_mgr:proc_mgr -> 362 cwd:path -> 363 unit 364(** [merge_abort] aborts an in-progress merge. *) 365 366val reset_hard : 367 proc_mgr:proc_mgr -> 368 cwd:path -> 369 string -> 370 unit 371(** [reset_hard] does a hard reset to the given ref. *) 372 373val clean_fd : 374 proc_mgr:proc_mgr -> 375 cwd:path -> 376 unit 377(** [clean_fd] removes untracked files and directories. *) 378 379val filter_repo_to_subdirectory : 380 proc_mgr:proc_mgr -> 381 cwd:path -> 382 branch:string -> 383 subdirectory:string -> 384 unit 385(** [filter_repo_to_subdirectory ~proc_mgr ~cwd ~branch ~subdirectory] 386 rewrites the history of [branch] so all files are moved into [subdirectory]. 387 Uses git-filter-repo for fast history rewriting. Preserves full commit history. *) 388 389val filter_repo_from_subdirectory : 390 proc_mgr:proc_mgr -> 391 cwd:path -> 392 branch:string -> 393 subdirectory:string -> 394 unit 395(** [filter_repo_from_subdirectory ~proc_mgr ~cwd ~branch ~subdirectory] 396 rewrites the history of [branch] extracting only files from [subdirectory] 397 and placing them at the repository root. This is the inverse of 398 [filter_repo_to_subdirectory]. Uses git-filter-repo --subdirectory-filter. 399 Preserves full commit history for files that were in the subdirectory. *)