codeowners-git 2.0.2 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +362 -3
  2. package/dist/cli.js +630 -46
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -12,8 +12,10 @@ Managing large-scale migrations in big monorepos with multiple codeowners can be
12
12
  - Creating compact, team-specific branches with only their affected files.
13
13
  - Streamlining the review process with smaller, targeted PRs.
14
14
  - **Graceful error handling** with automatic recovery from failures.
15
+ - **Dry-run previews** to see exactly what will happen before committing.
16
+ - **JSON output** for piping to other tools, scripts, and agent workflows.
15
17
 
16
- > ❗❗ ❗ **Note:** Starting from v2.0.0, this tool works with **staged files**. Stage your changes with `git add` before running commands.
18
+ > ❗❗ ❗ **Note:** Starting from v2.0.0, this tool works with **staged files**. Stage your changes with `git add` before running commands. Alternatively, use `multi-branch --source <branch>` to split directly from an existing branch or PR without staging.
17
19
 
18
20
  https://github.com/user-attachments/assets/7cc0a924-f03e-47f3-baad-63eca9e8e4a8
19
21
 
@@ -149,6 +151,7 @@ Options:
149
151
  - `--group, -g` Group files by code owner
150
152
  - `--exclusive, -e` Only include files with a single owner (no co-owned files)
151
153
  - `--co-owned, -c` Only include files with multiple owners (co-owned files)
154
+ - `--json` Output results as JSON (suppresses all other output)
152
155
 
153
156
  Examples:
154
157
 
@@ -180,6 +183,12 @@ cg list --co-owned
180
183
 
181
184
  # List co-owned files where @myteam is one of the owners
182
185
  cg list --include "@myteam" --co-owned
186
+
187
+ # Output as JSON for piping to other tools
188
+ cg list --json
189
+
190
+ # JSON output with filters (pipe to jq)
191
+ cg list -i "@myteam" --group --json | jq '.grouped'
183
192
  ```
184
193
 
185
194
  ### `branch`
@@ -212,8 +221,15 @@ Options:
212
221
  - `--append` Add commits to existing branch instead of creating a new one
213
222
  - `--pr` Create a pull request after pushing (requires `--push` and GitHub CLI)
214
223
  - `--draft-pr` Create a draft pull request after pushing (requires `--push` and GitHub CLI)
224
+ - `--pr-body` Custom PR body text (overrides the repo's PR template). Requires `--pr` or `--draft-pr`.
215
225
  - `--exclusive, -e` Only include files where the owner is the sole owner (no co-owned files)
216
226
  - `--co-owned, -c` Only include files with multiple owners (co-owned files)
227
+ - `--source, -s` Source branch or commit to extract changes from (creates a temp branch from the default branch). No staging required.
228
+ - `--compare-main` Compare source against main branch instead of detecting merge-base (use with `--source`)
229
+ - `--dry-run` Preview the operation without making any changes
230
+ - `--json` Output results as JSON (suppresses all other output)
231
+
232
+ > **Note:** `--source` cannot be used when there are staged changes.
217
233
 
218
234
  Example:
219
235
 
@@ -253,6 +269,28 @@ cg branch -i @myteam -b "feature/exclusive" -m "Team exclusive changes" -p --exc
253
269
 
254
270
  # Only include co-owned files where @myteam is one of the owners
255
271
  cg branch -i @myteam -b "feature/co-owned" -m "Co-owned changes" -p --co-owned
272
+
273
+ # Preview what would happen without making any changes
274
+ cg branch -i @myteam -b "feature/new" -m "Add feature" --dry-run
275
+
276
+ # Dry-run with JSON output (for agents/scripts)
277
+ cg branch -i @myteam -b "feature/new" -m "Add feature" --dry-run --json
278
+
279
+ # Normal execution with JSON output
280
+ cg branch -i @myteam -b "feature/new" -m "Add feature" -p --json
281
+
282
+ # Create a PR with a custom body (overrides repo PR template)
283
+ cg branch -i @myteam -b "feature/new" -m "Add feature" -p --pr --pr-body "## Summary
284
+ Migrated files owned by @myteam.
285
+
286
+ ## Reviewer Notes
287
+ Auto-generated PR from codeowners-git."
288
+
289
+ # Extract a single team's files from an existing branch
290
+ cg branch -s feature/big-migration -i @myteam -b "feature/myteam-migration" -m "Migrate" -p --pr
291
+
292
+ # Preview what would be extracted from a source branch
293
+ cg branch -s origin/feature/big-migration -i @myteam -b "feature/myteam" -m "Migrate" --dry-run
256
294
  ```
257
295
 
258
296
  ### `multi-branch`
@@ -287,10 +325,15 @@ Options:
287
325
  - `--append` Add commits to existing branches instead of creating new ones
288
326
  - `--pr` Create pull requests after pushing (requires `--push` and GitHub CLI)
289
327
  - `--draft-pr` Create draft pull requests after pushing (requires `--push` and GitHub CLI)
328
+ - `--pr-body` Custom PR body text (overrides the repo's PR template). Requires `--pr` or `--draft-pr`. The same body is used for all branches.
290
329
  - `--exclusive, -e` Only include files where each owner is the sole owner (no co-owned files)
291
330
  - `--co-owned, -c` Only include files with multiple owners (co-owned files)
331
+ - `--source, -s` Source branch or commit to split (extracts changes onto a temp branch from the default branch). No staging required — the tool handles extraction automatically.
332
+ - `--compare-main` Compare source against main branch instead of detecting merge-base (use with `--source`)
333
+ - `--dry-run` Preview the operation without making any changes
334
+ - `--json` Output results as JSON (suppresses all other output)
292
335
 
293
- > **Note:** You cannot use both `--ignore` and `--include` options at the same time. You also cannot use both `--exclusive` and `--co-owned` options at the same time.
336
+ > **Note:** You cannot use both `--ignore` and `--include` options at the same time. You also cannot use both `--exclusive` and `--co-owned` options at the same time. `--source` cannot be used when there are staged changes.
294
337
 
295
338
  Example:
296
339
 
@@ -336,11 +379,39 @@ cg multi-branch -b "feature/exclusive" -m "Exclusive changes" -p --exclusive
336
379
 
337
380
  # Only include co-owned files
338
381
  cg multi-branch -b "feature/co-owned" -m "Co-owned changes" -p --co-owned
382
+
383
+ # Preview all branches that would be created
384
+ cg multi-branch -b "feature/migration" -m "Migrate" --dry-run
385
+
386
+ # Dry-run with JSON output
387
+ cg multi-branch -b "feature/migration" -m "Migrate" --dry-run --json
388
+
389
+ # Pipe dry-run JSON to see owners with matching files
390
+ cg multi-branch -b "mig" -m "Fix" --dry-run --json | jq '.owners[] | select(.files | length > 0)'
391
+
392
+ # Normal execution with JSON output
393
+ cg multi-branch -b "feature/migration" -m "Migrate" -p --json
394
+
395
+ # Split an existing branch (e.g., from a PR) into per-team branches
396
+ cg multi-branch -s feature/big-migration -b "migration" -m "Migrate" -p --pr
397
+
398
+ # Split from a remote ref
399
+ cg multi-branch -s origin/feature/big-migration -b "migration" -m "Migrate" -p
400
+
401
+ # Preview a source split with dry-run
402
+ cg multi-branch -s feature/big-migration -b "migration" -m "Migrate" --dry-run
403
+
404
+ # Combine --source with path filtering
405
+ cg multi-branch -s feature/big-migration "src/services/**" -b "migration" -m "Migrate services" -p
406
+
407
+ # Use a custom PR body for all generated PRs
408
+ cg multi-branch -b "migration" -m "Migrate" -p --draft-pr --pr-body "## Summary
409
+ Auto-split migration PR by codeowner."
339
410
  ```
340
411
 
341
412
  This will:
342
413
 
343
- 1. Find all codeowners for the staged files
414
+ 1. Find all codeowners for the staged files (or from `--source` ref if provided)
344
415
  2. Apply any ignore/include filters if specified
345
416
  3. For each codeowner (e.g., @team-a, @team-b):
346
417
  - Create a branch like `feature/new-feature/team-a`
@@ -348,6 +419,13 @@ This will:
348
419
  - Add a commit message like "Add new feature - @team-a"
349
420
  - Push each branch to the remote if the `-p` flag is provided
350
421
 
422
+ When `--source` is provided, the tool automatically:
423
+
424
+ 1. Creates a temporary branch from the default branch (main/master)
425
+ 2. Extracts the changed files from the source ref
426
+ 3. Stages them and runs the normal split logic
427
+ 4. Cleans up the temporary branch and returns to your original branch
428
+
351
429
  ### `extract`
352
430
 
353
431
  Extract file changes from a source branch or commit to your working directory. This is useful when you want to copy changes from another branch to review and then stage them for committing using the `branch` command.
@@ -371,6 +449,8 @@ Options:
371
449
  - `--compare-main` Compare source against main branch instead of detecting merge-base
372
450
  - `--exclusive, -e` Only include files where the owner is the sole owner (no co-owned files)
373
451
  - `--co-owned, -c` Only include files with multiple owners (co-owned files)
452
+ - `--dry-run` Preview the operation without making any changes
453
+ - `--json` Output results as JSON (suppresses all other output)
374
454
 
375
455
  Examples:
376
456
 
@@ -403,6 +483,21 @@ cg extract -s feature/other-team --co-owned
403
483
 
404
484
  # Extract co-owned files where @my-team is one of the owners
405
485
  cg extract -s feature/other-team -i "@my-team" --co-owned
486
+
487
+ # Preview what would be extracted without making any changes
488
+ cg extract -s feature/other-team --dry-run
489
+
490
+ # Dry-run with owner filter
491
+ cg extract -s feature/other-team -i "@my-team" --dry-run
492
+
493
+ # Dry-run with JSON output (for agents/scripts)
494
+ cg extract -s feature/other-team -i "@my-team" --dry-run --json
495
+
496
+ # Normal execution with JSON output
497
+ cg extract -s feature/other-team -i "@my-team" --json
498
+
499
+ # Pipe JSON to jq to get just the file list
500
+ cg extract -s feature/other-team --json | jq '.files'
406
501
  ```
407
502
 
408
503
  > **Note:** Files are extracted to your working directory (unstaged), allowing you to review and modify them. Stage the files with `git add`, then use the `branch` command to create a branch, commit, push, and create PRs.
@@ -477,6 +572,270 @@ The tool automatically handles:
477
572
 
478
573
  > **Note:** State files are stored in `~/.codeowners-git/state/` outside your project directory, so no `.gitignore` entries are needed.
479
574
 
575
+ ## Dry Run & JSON Output
576
+
577
+ ### Dry Run (`--dry-run`)
578
+
579
+ Available on: `branch`, `multi-branch`, `extract`
580
+
581
+ The `--dry-run` flag shows a complete preview of what would happen without performing any git operations. No branches are created, no files are committed, and nothing is pushed.
582
+
583
+ ```bash
584
+ # Preview branch creation
585
+ cg branch -i @myteam -b "feature/new" -m "Add feature" --dry-run
586
+
587
+ # Preview all branches in a multi-branch run
588
+ cg multi-branch -b "feature/migration" -m "Migrate" --dry-run
589
+
590
+ # Preview file extraction
591
+ cg extract -s feature/other-team -i "@myteam" --dry-run
592
+ ```
593
+
594
+ The dry-run output includes:
595
+
596
+ - **branch**: Owner, branch name, branch existence, commit message, matched files, excluded files, push/PR/flag settings
597
+ - **multi-branch**: Per-owner breakdown (branch, message, files), uncovered files, unowned files, summary totals
598
+ - **extract**: Source, compare target, files to extract, excluded files, filter settings
599
+
600
+ ### JSON Output (`--json`)
601
+
602
+ Available on: `list`, `branch`, `multi-branch`, `extract`
603
+
604
+ The `--json` flag outputs machine-readable JSON to stdout and suppresses all human-readable log messages. This is useful for piping to other tools, scripts, and agent workflows.
605
+
606
+ ```bash
607
+ # JSON output for any command
608
+ cg list --json
609
+ cg branch -i @myteam -b "feature/new" -m "Add feature" --json
610
+ cg multi-branch -b "feature/migration" -m "Migrate" --json
611
+ cg extract -s feature/other-team --json
612
+ ```
613
+
614
+ ### Combining `--dry-run` and `--json`
615
+
616
+ The two flags work together — `--dry-run --json` outputs the dry-run preview as structured JSON:
617
+
618
+ ```bash
619
+ cg branch -i @myteam -b "feature/new" -m "Add feature" --dry-run --json
620
+ cg multi-branch -b "feature/migration" -m "Migrate" --dry-run --json
621
+ cg extract -s feature/other-team --dry-run --json
622
+ ```
623
+
624
+ ### JSON Schema Examples
625
+
626
+ Every JSON response includes a `command` field identifying the source command.
627
+
628
+ **`list --json`**
629
+
630
+ ```json
631
+ {
632
+ "command": "list",
633
+ "files": [
634
+ { "file": "src/index.ts", "owners": ["@org/team-a"] },
635
+ { "file": "src/shared.ts", "owners": ["@org/team-a", "@org/team-b"] }
636
+ ],
637
+ "filters": {
638
+ "include": null,
639
+ "pathPattern": null,
640
+ "exclusive": false,
641
+ "coOwned": false
642
+ }
643
+ }
644
+ ```
645
+
646
+ **`list --group --json`**
647
+
648
+ ```json
649
+ {
650
+ "command": "list",
651
+ "grouped": {
652
+ "@org/team-a": ["src/index.ts", "src/shared.ts"],
653
+ "@org/team-b": ["src/shared.ts"]
654
+ },
655
+ "filters": {
656
+ "include": null,
657
+ "pathPattern": null,
658
+ "exclusive": false,
659
+ "coOwned": false
660
+ }
661
+ }
662
+ ```
663
+
664
+ **`branch --dry-run --json`**
665
+
666
+ ```json
667
+ {
668
+ "command": "branch",
669
+ "dryRun": true,
670
+ "owner": "@org/team-a",
671
+ "branch": "feature/new/team-a",
672
+ "branchExists": false,
673
+ "message": "Add feature - @org/team-a",
674
+ "files": ["src/index.ts"],
675
+ "excludedFiles": ["src/other.ts"],
676
+ "options": {
677
+ "push": true,
678
+ "remote": "origin",
679
+ "force": false,
680
+ "pr": false,
681
+ "draftPr": false,
682
+ "noVerify": false,
683
+ "append": false,
684
+ "exclusive": false,
685
+ "coOwned": false,
686
+ "pathPattern": null
687
+ }
688
+ }
689
+ ```
690
+
691
+ **`branch --json`** (normal execution)
692
+
693
+ ```json
694
+ {
695
+ "command": "branch",
696
+ "dryRun": false,
697
+ "success": true,
698
+ "branchName": "feature/new/team-a",
699
+ "owner": "@org/team-a",
700
+ "files": ["src/index.ts"],
701
+ "pushed": true,
702
+ "prUrl": "https://github.com/org/repo/pull/42",
703
+ "prNumber": 42,
704
+ "error": null
705
+ }
706
+ ```
707
+
708
+ **`multi-branch --dry-run --json`**
709
+
710
+ ```json
711
+ {
712
+ "command": "multi-branch",
713
+ "dryRun": true,
714
+ "owners": [
715
+ {
716
+ "owner": "@org/team-a",
717
+ "branch": "feature/migration/team-a",
718
+ "message": "Migrate - @org/team-a",
719
+ "files": ["src/index.ts"]
720
+ },
721
+ {
722
+ "owner": "@org/team-b",
723
+ "branch": "feature/migration/team-b",
724
+ "message": "Migrate - @org/team-b",
725
+ "files": ["src/shared.ts"]
726
+ }
727
+ ],
728
+ "uncoveredFiles": [],
729
+ "filesWithoutOwners": [],
730
+ "totalFiles": 2,
731
+ "coveredFiles": 2,
732
+ "options": {
733
+ "baseBranch": "feature/migration",
734
+ "baseMessage": "Migrate",
735
+ "push": false,
736
+ "remote": "origin",
737
+ "force": false,
738
+ "pr": false,
739
+ "draftPr": false,
740
+ "noVerify": false,
741
+ "append": false,
742
+ "exclusive": false,
743
+ "coOwned": false,
744
+ "pathPattern": null,
745
+ "defaultOwner": null
746
+ }
747
+ }
748
+ ```
749
+
750
+ **`multi-branch --json`** (normal execution)
751
+
752
+ ```json
753
+ {
754
+ "command": "multi-branch",
755
+ "dryRun": false,
756
+ "success": true,
757
+ "totalOwners": 2,
758
+ "successCount": 2,
759
+ "failureCount": 0,
760
+ "results": [
761
+ {
762
+ "owner": "@org/team-a",
763
+ "branch": "feature/migration/team-a",
764
+ "success": true,
765
+ "files": ["src/index.ts"],
766
+ "pushed": true,
767
+ "prUrl": null,
768
+ "prNumber": null,
769
+ "error": null
770
+ }
771
+ ]
772
+ }
773
+ ```
774
+
775
+ **`extract --dry-run --json`**
776
+
777
+ ```json
778
+ {
779
+ "command": "extract",
780
+ "dryRun": true,
781
+ "source": "feature/other-team",
782
+ "compareTarget": "main",
783
+ "files": ["src/component.tsx"],
784
+ "excludedFiles": ["src/unrelated.ts"],
785
+ "totalChanged": 2,
786
+ "options": {
787
+ "include": "@org/team-a",
788
+ "pathPattern": null,
789
+ "exclusive": false,
790
+ "coOwned": false,
791
+ "compareMain": false
792
+ }
793
+ }
794
+ ```
795
+
796
+ **`extract --json`** (normal execution)
797
+
798
+ ```json
799
+ {
800
+ "command": "extract",
801
+ "dryRun": false,
802
+ "source": "feature/other-team",
803
+ "compareTarget": "main",
804
+ "files": ["src/component.tsx"],
805
+ "totalChanged": 2
806
+ }
807
+ ```
808
+
809
+ **Error responses** (any command)
810
+
811
+ ```json
812
+ {
813
+ "command": "branch",
814
+ "error": "Error: No staged files found"
815
+ }
816
+ ```
817
+
818
+ ### Piping Examples
819
+
820
+ ```bash
821
+ # Count files per owner
822
+ cg list --group --json | jq '.grouped | to_entries[] | {owner: .key, count: (.value | length)}'
823
+
824
+ # Get list of branches that would be created
825
+ cg multi-branch -b "mig" -m "Fix" --dry-run --json | jq '.owners[].branch'
826
+
827
+ # Find owners with more than 5 files
828
+ cg multi-branch -b "mig" -m "Fix" --dry-run --json | jq '.owners[] | select(.files | length > 5) | .owner'
829
+
830
+ # Check if a branch operation succeeded
831
+ cg branch -i @myteam -b "feat" -m "Update" -p --json | jq '.success'
832
+
833
+ # List only extracted file paths
834
+ cg extract -s feature/other --json | jq -r '.files[]'
835
+ ```
836
+
837
+ > **Note:** The `recover` command does not support `--dry-run` or `--json` because it is an interactive command with user prompts.
838
+
480
839
  ## Contributing
481
840
 
482
841
  1. Clone the repository