@procedures/git 0.2.3 → 0.3.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 (141) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/README.md +77 -12
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/module.d.ts.map +1 -1
  5. package/dist/module.js +312 -2
  6. package/dist/module.js.map +1 -1
  7. package/dist/procedures/git/blame.d.ts +17 -0
  8. package/dist/procedures/git/blame.d.ts.map +1 -0
  9. package/dist/procedures/git/blame.js +69 -0
  10. package/dist/procedures/git/blame.js.map +1 -0
  11. package/dist/procedures/git/branch/add.d.ts +12 -0
  12. package/dist/procedures/git/branch/add.d.ts.map +1 -0
  13. package/dist/procedures/git/branch/add.js +21 -0
  14. package/dist/procedures/git/branch/add.js.map +1 -0
  15. package/dist/procedures/git/branch/delete.d.ts +13 -0
  16. package/dist/procedures/git/branch/delete.d.ts.map +1 -0
  17. package/dist/procedures/git/branch/delete.js +24 -0
  18. package/dist/procedures/git/branch/delete.js.map +1 -0
  19. package/dist/procedures/git/branch/list.d.ts +13 -0
  20. package/dist/procedures/git/branch/list.d.ts.map +1 -0
  21. package/dist/procedures/git/branch/list.js +37 -0
  22. package/dist/procedures/git/branch/list.js.map +1 -0
  23. package/dist/procedures/git/branch.d.ts +8 -2
  24. package/dist/procedures/git/branch.d.ts.map +1 -1
  25. package/dist/procedures/git/branch.js +18 -37
  26. package/dist/procedures/git/branch.js.map +1 -1
  27. package/dist/procedures/git/commit.d.ts.map +1 -1
  28. package/dist/procedures/git/commit.js +9 -11
  29. package/dist/procedures/git/commit.js.map +1 -1
  30. package/dist/procedures/git/index.d.ts +19 -0
  31. package/dist/procedures/git/index.d.ts.map +1 -1
  32. package/dist/procedures/git/index.js +19 -0
  33. package/dist/procedures/git/index.js.map +1 -1
  34. package/dist/procedures/git/init.d.ts +6 -2
  35. package/dist/procedures/git/init.d.ts.map +1 -1
  36. package/dist/procedures/git/init.js +8 -4
  37. package/dist/procedures/git/init.js.map +1 -1
  38. package/dist/procedures/git/merge/abort.d.ts +14 -0
  39. package/dist/procedures/git/merge/abort.d.ts.map +1 -0
  40. package/dist/procedures/git/merge/abort.js +18 -0
  41. package/dist/procedures/git/merge/abort.js.map +1 -0
  42. package/dist/procedures/git/merge.d.ts +16 -0
  43. package/dist/procedures/git/merge.d.ts.map +1 -0
  44. package/dist/procedures/git/merge.js +41 -0
  45. package/dist/procedures/git/merge.js.map +1 -0
  46. package/dist/procedures/git/push.d.ts.map +1 -1
  47. package/dist/procedures/git/push.js +4 -2
  48. package/dist/procedures/git/push.js.map +1 -1
  49. package/dist/procedures/git/rebase/abort.d.ts +13 -0
  50. package/dist/procedures/git/rebase/abort.d.ts.map +1 -0
  51. package/dist/procedures/git/rebase/abort.js +17 -0
  52. package/dist/procedures/git/rebase/abort.js.map +1 -0
  53. package/dist/procedures/git/rebase/continue.d.ts +13 -0
  54. package/dist/procedures/git/rebase/continue.d.ts.map +1 -0
  55. package/dist/procedures/git/rebase/continue.js +23 -0
  56. package/dist/procedures/git/rebase/continue.js.map +1 -0
  57. package/dist/procedures/git/rebase.d.ts +22 -0
  58. package/dist/procedures/git/rebase.d.ts.map +1 -0
  59. package/dist/procedures/git/rebase.js +46 -0
  60. package/dist/procedures/git/rebase.js.map +1 -0
  61. package/dist/procedures/git/refs.d.ts +31 -0
  62. package/dist/procedures/git/refs.d.ts.map +1 -0
  63. package/dist/procedures/git/refs.js +52 -0
  64. package/dist/procedures/git/refs.js.map +1 -0
  65. package/dist/procedures/git/reset.d.ts +13 -0
  66. package/dist/procedures/git/reset.d.ts.map +1 -0
  67. package/dist/procedures/git/reset.js +31 -0
  68. package/dist/procedures/git/reset.js.map +1 -0
  69. package/dist/procedures/git/restore.d.ts +13 -0
  70. package/dist/procedures/git/restore.d.ts.map +1 -0
  71. package/dist/procedures/git/restore.js +26 -0
  72. package/dist/procedures/git/restore.js.map +1 -0
  73. package/dist/procedures/git/revParse.d.ts +13 -0
  74. package/dist/procedures/git/revParse.d.ts.map +1 -0
  75. package/dist/procedures/git/revParse.js +23 -0
  76. package/dist/procedures/git/revParse.js.map +1 -0
  77. package/dist/procedures/git/run.d.ts +2 -0
  78. package/dist/procedures/git/run.d.ts.map +1 -1
  79. package/dist/procedures/git/run.js +1 -0
  80. package/dist/procedures/git/run.js.map +1 -1
  81. package/dist/procedures/git/show.d.ts +15 -0
  82. package/dist/procedures/git/show.d.ts.map +1 -0
  83. package/dist/procedures/git/show.js +49 -0
  84. package/dist/procedures/git/show.js.map +1 -0
  85. package/dist/procedures/git/tag/add.d.ts +12 -0
  86. package/dist/procedures/git/tag/add.d.ts.map +1 -0
  87. package/dist/procedures/git/tag/add.js +25 -0
  88. package/dist/procedures/git/tag/add.js.map +1 -0
  89. package/dist/procedures/git/tag/delete.d.ts +12 -0
  90. package/dist/procedures/git/tag/delete.d.ts.map +1 -0
  91. package/dist/procedures/git/tag/delete.js +19 -0
  92. package/dist/procedures/git/tag/delete.js.map +1 -0
  93. package/dist/procedures/git/tag/list.d.ts +12 -0
  94. package/dist/procedures/git/tag/list.d.ts.map +1 -0
  95. package/dist/procedures/git/tag/list.js +34 -0
  96. package/dist/procedures/git/tag/list.js.map +1 -0
  97. package/dist/procedures/git/worktree/add.d.ts +13 -0
  98. package/dist/procedures/git/worktree/add.d.ts.map +1 -0
  99. package/dist/procedures/git/worktree/add.js +35 -0
  100. package/dist/procedures/git/worktree/add.js.map +1 -0
  101. package/dist/procedures/git/worktree/list.d.ts +17 -0
  102. package/dist/procedures/git/worktree/list.d.ts.map +1 -0
  103. package/dist/procedures/git/worktree/list.js +52 -0
  104. package/dist/procedures/git/worktree/list.js.map +1 -0
  105. package/dist/procedures/git/worktree/remove.d.ts +13 -0
  106. package/dist/procedures/git/worktree/remove.d.ts.map +1 -0
  107. package/dist/procedures/git/worktree/remove.js +20 -0
  108. package/dist/procedures/git/worktree/remove.js.map +1 -0
  109. package/dist/types.d.ts +308 -0
  110. package/dist/types.d.ts.map +1 -1
  111. package/dist/types.js +265 -0
  112. package/dist/types.js.map +1 -1
  113. package/package.json +4 -4
  114. package/src/module.ts +411 -2
  115. package/src/procedures/git/blame.ts +68 -0
  116. package/src/procedures/git/branch/add.ts +24 -0
  117. package/src/procedures/git/branch/delete.ts +29 -0
  118. package/src/procedures/git/branch/list.ts +40 -0
  119. package/src/procedures/git/branch.ts +19 -40
  120. package/src/procedures/git/commit.ts +9 -11
  121. package/src/procedures/git/index.ts +19 -0
  122. package/src/procedures/git/init.ts +11 -10
  123. package/src/procedures/git/merge/abort.ts +20 -0
  124. package/src/procedures/git/merge.ts +42 -0
  125. package/src/procedures/git/push.ts +4 -2
  126. package/src/procedures/git/rebase/abort.ts +19 -0
  127. package/src/procedures/git/rebase/continue.ts +25 -0
  128. package/src/procedures/git/rebase.ts +48 -0
  129. package/src/procedures/git/refs.ts +55 -0
  130. package/src/procedures/git/reset.ts +34 -0
  131. package/src/procedures/git/restore.ts +27 -0
  132. package/src/procedures/git/revParse.ts +24 -0
  133. package/src/procedures/git/run.ts +3 -0
  134. package/src/procedures/git/show.ts +57 -0
  135. package/src/procedures/git/tag/add.ts +27 -0
  136. package/src/procedures/git/tag/delete.ts +22 -0
  137. package/src/procedures/git/tag/list.ts +36 -0
  138. package/src/procedures/git/worktree/add.ts +35 -0
  139. package/src/procedures/git/worktree/list.ts +48 -0
  140. package/src/procedures/git/worktree/remove.ts +21 -0
  141. package/src/types.ts +623 -0
package/src/module.ts CHANGED
@@ -126,6 +126,103 @@ import {
126
126
  GitStashExportOutputSchema,
127
127
  GitStashImportOutputSchema,
128
128
  } from "./types.js";
129
+ import { gitShow } from "./procedures/git/show.js";
130
+ import { gitBlame } from "./procedures/git/blame.js";
131
+ import { gitTagList } from "./procedures/git/tag/list.js";
132
+ import { gitTagAdd } from "./procedures/git/tag/add.js";
133
+ import { gitTagDelete } from "./procedures/git/tag/delete.js";
134
+ import { gitRevParse } from "./procedures/git/revParse.js";
135
+ import { gitReset } from "./procedures/git/reset.js";
136
+ import { gitRestore } from "./procedures/git/restore.js";
137
+ import { gitMerge } from "./procedures/git/merge.js";
138
+ import { gitMergeAbort } from "./procedures/git/merge/abort.js";
139
+ import { gitRebase } from "./procedures/git/rebase.js";
140
+ import { gitRebaseContinue } from "./procedures/git/rebase/continue.js";
141
+ import { gitRebaseAbort } from "./procedures/git/rebase/abort.js";
142
+ import { gitWorktreeList } from "./procedures/git/worktree/list.js";
143
+ import { gitWorktreeAdd } from "./procedures/git/worktree/add.js";
144
+ import { gitWorktreeRemove } from "./procedures/git/worktree/remove.js";
145
+ import { gitBranchList } from "./procedures/git/branch/list.js";
146
+ import { gitBranchAdd } from "./procedures/git/branch/add.js";
147
+ import { gitBranchDelete } from "./procedures/git/branch/delete.js";
148
+ import {
149
+ GitShowInputSchema,
150
+ GitShowOutputSchema,
151
+ type GitShowInput,
152
+ type GitShowOutput,
153
+ GitBlameInputSchema,
154
+ GitBlameOutputSchema,
155
+ type GitBlameInput,
156
+ type GitBlameOutput,
157
+ GitTagListInputSchema,
158
+ GitTagListOutputSchema,
159
+ type GitTagListInput,
160
+ type GitTagListOutput,
161
+ GitTagAddInputSchema,
162
+ GitTagAddOutputSchema,
163
+ type GitTagAddInput,
164
+ type GitTagAddOutput,
165
+ GitTagDeleteInputSchema,
166
+ GitTagDeleteOutputSchema,
167
+ type GitTagDeleteInput,
168
+ type GitTagDeleteOutput,
169
+ GitRevParseInputSchema,
170
+ GitRevParseOutputSchema,
171
+ type GitRevParseInput,
172
+ type GitRevParseOutput,
173
+ GitResetInputSchema,
174
+ GitResetOutputSchema,
175
+ type GitResetInput,
176
+ type GitResetOutput,
177
+ GitRestoreInputSchema,
178
+ GitRestoreOutputSchema,
179
+ type GitRestoreInput,
180
+ type GitRestoreOutput,
181
+ GitMergeInputSchema,
182
+ GitMergeOutputSchema,
183
+ type GitMergeInput,
184
+ type GitMergeOutput,
185
+ GitMergeAbortInputSchema,
186
+ GitMergeAbortOutputSchema,
187
+ type GitMergeAbortInput,
188
+ type GitMergeAbortOutput,
189
+ GitRebaseInputSchema,
190
+ GitRebaseOutputSchema,
191
+ type GitRebaseInput,
192
+ type GitRebaseOutput,
193
+ GitRebaseContinueInputSchema,
194
+ GitRebaseContinueOutputSchema,
195
+ type GitRebaseContinueInput,
196
+ type GitRebaseContinueOutput,
197
+ GitRebaseAbortInputSchema,
198
+ GitRebaseAbortOutputSchema,
199
+ type GitRebaseAbortInput,
200
+ type GitRebaseAbortOutput,
201
+ GitWorktreeListInputSchema,
202
+ GitWorktreeListOutputSchema,
203
+ type GitWorktreeListInput,
204
+ type GitWorktreeListOutput,
205
+ GitWorktreeAddInputSchema,
206
+ GitWorktreeAddOutputSchema,
207
+ type GitWorktreeAddInput,
208
+ type GitWorktreeAddOutput,
209
+ GitWorktreeRemoveInputSchema,
210
+ GitWorktreeRemoveOutputSchema,
211
+ type GitWorktreeRemoveInput,
212
+ type GitWorktreeRemoveOutput,
213
+ GitBranchListInputSchema,
214
+ GitBranchListOutputSchema,
215
+ type GitBranchListInput,
216
+ type GitBranchListOutput,
217
+ GitBranchAddInputSchema,
218
+ GitBranchAddOutputSchema,
219
+ type GitBranchAddInput,
220
+ type GitBranchAddOutput,
221
+ GitBranchDeleteInputSchema,
222
+ GitBranchDeleteOutputSchema,
223
+ type GitBranchDeleteInput,
224
+ type GitBranchDeleteOutput,
225
+ } from "./types.js";
129
226
 
130
227
  // =============================================================================
131
228
  // Effects
@@ -273,7 +370,10 @@ const gitBranchProcedure = createProcedure()
273
370
  .input(zodAdapter<GitBranchInput>(GitBranchInputSchema))
274
371
  .output(zodAdapter<GitBranchOutput>(GitBranchOutputSchema))
275
372
  .meta({
276
- description: "Branch operations",
373
+ description: "Deprecated: use git.branch.list, git.branch.add or git.branch.delete. Branch operations",
374
+ // Roadmap R40: it stays for one release, and calls the code of the three new procedures
375
+ deprecated: true,
376
+ deprecationMessage: "Use git.branch.list, git.branch.add or git.branch.delete. git.branch goes away in the next release.",
277
377
  args: ["name"],
278
378
  shorts: { delete: "d", list: "l", remote: "r", cwd: "C" },
279
379
  output: "json",
@@ -327,7 +427,7 @@ const gitInitProcedure = createProcedure()
327
427
  args: [],
328
428
  shorts: { cwd: "C", bare: "b", initialBranch: "B" },
329
429
  output: "json",
330
- // It runs `git init` only when the folder has no .git (it calls fs.exists)
430
+ // It runs `git init` only when the folder has no .git
331
431
  effects: { reads: ["fs"], writes: ["fs"], destructive: false, idempotent: true },
332
432
  })
333
433
  .handler(async (input: GitInitInput, ctx): Promise<GitInitOutput> => {
@@ -604,6 +704,295 @@ const gitStashImportProcedure = createProcedure()
604
704
  })
605
705
  .build();
606
706
 
707
+ // =============================================================================
708
+ // Roadmap R40: show, blame, tags, rev-parse, reset, restore, merge, rebase, worktrees, branches
709
+ // =============================================================================
710
+
711
+ /** A new ref (a tag or a branch). Git refuses a ref that exists, so a second call changes nothing. */
712
+ const ADDS_REF: Effects = { reads: ["fs"], writes: ["fs"], destructive: false, idempotent: true };
713
+
714
+ /**
715
+ * A deletion: of a ref, a working tree, or the state of a stopped merge or rebase. A second call
716
+ * fails, and changes nothing.
717
+ */
718
+ const DELETES: Effects = { reads: ["fs"], writes: ["fs"], destructive: true, idempotent: true };
719
+
720
+ const gitShowProcedure = createProcedure()
721
+ .path(["git", "show"])
722
+ .input(zodAdapter<GitShowInput>(GitShowInputSchema))
723
+ .output(zodAdapter<GitShowOutput>(GitShowOutputSchema))
724
+ .meta({
725
+ description: "Show a commit (its fields, files and diff), or a file at a commit",
726
+ args: ["ref"],
727
+ shorts: { path: "p", stat: "s", cwd: "C" },
728
+ output: "json",
729
+ effects: READS_REPOSITORY,
730
+ })
731
+ .handler(async (input: GitShowInput, ctx): Promise<GitShowOutput> => gitShow(input, ctx))
732
+ .build();
733
+
734
+ const gitBlameProcedure = createProcedure()
735
+ .path(["git", "blame"])
736
+ .input(zodAdapter<GitBlameInput>(GitBlameInputSchema))
737
+ .output(zodAdapter<GitBlameOutput>(GitBlameOutputSchema))
738
+ .meta({
739
+ description: "Show the commit and the author of each line of a file",
740
+ args: ["path"],
741
+ shorts: { ref: "r", cwd: "C" },
742
+ output: "json",
743
+ effects: READS_REPOSITORY,
744
+ })
745
+ .handler(async (input: GitBlameInput, ctx): Promise<GitBlameOutput> => gitBlame(input, ctx))
746
+ .build();
747
+
748
+ const gitTagListProcedure = createProcedure()
749
+ .path(["git", "tag", "list"])
750
+ .input(zodAdapter<GitTagListInput>(GitTagListInputSchema))
751
+ .output(zodAdapter<GitTagListOutput>(GitTagListOutputSchema))
752
+ .meta({
753
+ description: "List the tags, with their commits",
754
+ args: ["pattern"],
755
+ shorts: { cwd: "C" },
756
+ output: "json",
757
+ effects: READS_REPOSITORY,
758
+ })
759
+ .handler(async (input: GitTagListInput, ctx): Promise<GitTagListOutput> => gitTagList(input, ctx))
760
+ .build();
761
+
762
+ const gitTagAddProcedure = createProcedure()
763
+ .path(["git", "tag", "add"])
764
+ .input(zodAdapter<GitTagAddInput>(GitTagAddInputSchema))
765
+ .output(zodAdapter<GitTagAddOutput>(GitTagAddOutputSchema))
766
+ .meta({
767
+ description: "Make a tag. With a message, the tag is annotated. An existing tag is an error",
768
+ args: ["name", "ref"],
769
+ shorts: { message: "m", cwd: "C" },
770
+ output: "json",
771
+ effects: ADDS_REF,
772
+ })
773
+ .handler(async (input: GitTagAddInput, ctx): Promise<GitTagAddOutput> => gitTagAdd(input, ctx))
774
+ .build();
775
+
776
+ const gitTagDeleteProcedure = createProcedure()
777
+ .path(["git", "tag", "delete"])
778
+ .input(zodAdapter<GitTagDeleteInput>(GitTagDeleteInputSchema))
779
+ .output(zodAdapter<GitTagDeleteOutput>(GitTagDeleteOutputSchema))
780
+ .meta({
781
+ description: "Delete a tag",
782
+ args: ["name"],
783
+ shorts: { cwd: "C" },
784
+ output: "json",
785
+ effects: DELETES,
786
+ })
787
+ .handler(async (input: GitTagDeleteInput, ctx): Promise<GitTagDeleteOutput> => gitTagDelete(input, ctx))
788
+ .build();
789
+
790
+ const gitRevParseProcedure = createProcedure()
791
+ .path(["git", "revParse"])
792
+ .input(zodAdapter<GitRevParseInput>(GitRevParseInputSchema))
793
+ .output(zodAdapter<GitRevParseOutput>(GitRevParseOutputSchema))
794
+ .meta({
795
+ description: "Give the hash or the short name of a ref. An unknown ref is an error",
796
+ args: ["ref"],
797
+ shorts: { abbrevRef: "a", short: "s", cwd: "C" },
798
+ output: "json",
799
+ effects: READS_REPOSITORY,
800
+ })
801
+ .handler(async (input: GitRevParseInput, ctx): Promise<GitRevParseOutput> => gitRevParse(input, ctx))
802
+ .build();
803
+
804
+ const gitResetProcedure = createProcedure()
805
+ .path(["git", "reset"])
806
+ .input(zodAdapter<GitResetInput>(GitResetInputSchema))
807
+ .output(zodAdapter<GitResetOutput>(GitResetOutputSchema))
808
+ .meta({
809
+ description: "Move HEAD to a commit, and reset the index (mixed) or also the working tree (hard)",
810
+ args: ["ref"],
811
+ shorts: { mode: "m", cwd: "C" },
812
+ output: "json",
813
+ // The mode hard deletes the changes that are not committed. A relative ref (HEAD~1) moves
814
+ // HEAD again at a second call, so it is not idempotent.
815
+ effects: { reads: ["fs"], writes: ["fs"], destructive: true },
816
+ })
817
+ .handler(async (input: GitResetInput, ctx): Promise<GitResetOutput> => gitReset(input, ctx))
818
+ .build();
819
+
820
+ const gitRestoreProcedure = createProcedure()
821
+ .path(["git", "restore"])
822
+ .input(zodAdapter<GitRestoreInput>(GitRestoreInputSchema))
823
+ .output(zodAdapter<GitRestoreOutput>(GitRestoreOutputSchema))
824
+ .meta({
825
+ description: "Restore files of the working tree or the index. The changes of the files are lost",
826
+ args: ["paths"],
827
+ shorts: { source: "s", staged: "S", worktree: "W", cwd: "C" },
828
+ output: "json",
829
+ // It replaces the changes of the files. Git starts the post-checkout hook for it.
830
+ effects: { reads: ["fs"], writes: ["fs", "process"], destructive: true, idempotent: true },
831
+ })
832
+ .handler(async (input: GitRestoreInput, ctx): Promise<GitRestoreOutput> => gitRestore(input, ctx))
833
+ .build();
834
+
835
+ const gitMergeProcedure = createProcedure()
836
+ .path(["git", "merge"])
837
+ .input(zodAdapter<GitMergeInput>(GitMergeInputSchema))
838
+ .output(zodAdapter<GitMergeOutput>(GitMergeOutputSchema))
839
+ .meta({
840
+ description: "Merge a branch or a commit into the current branch. Conflicts give conflict: true",
841
+ args: ["ref"],
842
+ shorts: { message: "m", cwd: "C" },
843
+ output: "json",
844
+ // As git.pull: it changes the working tree, and starts the merge hooks
845
+ effects: { reads: ["fs"], writes: ["fs", "process"], idempotent: true },
846
+ })
847
+ .handler(async (input: GitMergeInput, ctx): Promise<GitMergeOutput> => gitMerge(input, ctx))
848
+ .build();
849
+
850
+ const gitMergeAbortProcedure = createProcedure()
851
+ .path(["git", "merge", "abort"])
852
+ .input(zodAdapter<GitMergeAbortInput>(GitMergeAbortInputSchema))
853
+ .output(zodAdapter<GitMergeAbortOutput>(GitMergeAbortOutputSchema))
854
+ .meta({
855
+ description: "Stop a merge that has conflicts, and put back the state before it",
856
+ args: [],
857
+ shorts: { cwd: "C" },
858
+ output: "json",
859
+ // The resolutions of the conflicts are lost
860
+ effects: DELETES,
861
+ })
862
+ .handler(async (input: GitMergeAbortInput, ctx): Promise<GitMergeAbortOutput> => gitMergeAbort(input, ctx))
863
+ .build();
864
+
865
+ const gitRebaseProcedure = createProcedure()
866
+ .path(["git", "rebase"])
867
+ .input(zodAdapter<GitRebaseInput>(GitRebaseInputSchema))
868
+ .output(zodAdapter<GitRebaseOutput>(GitRebaseOutputSchema))
869
+ .meta({
870
+ description: "Rebase the current branch on another branch (not interactive). Conflicts give conflict: true",
871
+ args: ["upstream"],
872
+ shorts: { onto: "o", branch: "b", cwd: "C" },
873
+ output: "json",
874
+ // It replaces the commits of the branch, and starts the rebase and checkout hooks
875
+ effects: { reads: ["fs"], writes: ["fs", "process"], destructive: true, idempotent: true },
876
+ })
877
+ .handler(async (input: GitRebaseInput, ctx): Promise<GitRebaseOutput> => gitRebase(input, ctx))
878
+ .build();
879
+
880
+ const gitRebaseContinueProcedure = createProcedure()
881
+ .path(["git", "rebase", "continue"])
882
+ .input(zodAdapter<GitRebaseContinueInput>(GitRebaseContinueInputSchema))
883
+ .output(zodAdapter<GitRebaseContinueOutput>(GitRebaseContinueOutputSchema))
884
+ .meta({
885
+ description: "Continue a rebase after the conflicts are resolved and staged",
886
+ args: [],
887
+ shorts: { cwd: "C" },
888
+ output: "json",
889
+ effects: { reads: ["fs"], writes: ["fs", "process"], destructive: true },
890
+ })
891
+ .handler(async (input: GitRebaseContinueInput, ctx): Promise<GitRebaseContinueOutput> => gitRebaseContinue(input, ctx))
892
+ .build();
893
+
894
+ const gitRebaseAbortProcedure = createProcedure()
895
+ .path(["git", "rebase", "abort"])
896
+ .input(zodAdapter<GitRebaseAbortInput>(GitRebaseAbortInputSchema))
897
+ .output(zodAdapter<GitRebaseAbortOutput>(GitRebaseAbortOutputSchema))
898
+ .meta({
899
+ description: "Stop a rebase, and go back to the branch as it was before the rebase",
900
+ args: [],
901
+ shorts: { cwd: "C" },
902
+ output: "json",
903
+ // The resolutions of the conflicts are lost. Git starts no hook for it.
904
+ effects: DELETES,
905
+ })
906
+ .handler(async (input: GitRebaseAbortInput, ctx): Promise<GitRebaseAbortOutput> => gitRebaseAbort(input, ctx))
907
+ .build();
908
+
909
+ const gitWorktreeListProcedure = createProcedure()
910
+ .path(["git", "worktree", "list"])
911
+ .input(zodAdapter<GitWorktreeListInput>(GitWorktreeListInputSchema))
912
+ .output(zodAdapter<GitWorktreeListOutput>(GitWorktreeListOutputSchema))
913
+ .meta({
914
+ description: "List the working trees of the repository",
915
+ args: [],
916
+ shorts: { cwd: "C" },
917
+ output: "json",
918
+ effects: READS_REPOSITORY,
919
+ })
920
+ .handler(async (input: GitWorktreeListInput, ctx): Promise<GitWorktreeListOutput> => gitWorktreeList(input, ctx))
921
+ .build();
922
+
923
+ const gitWorktreeAddProcedure = createProcedure()
924
+ .path(["git", "worktree", "add"])
925
+ .input(zodAdapter<GitWorktreeAddInput>(GitWorktreeAddInputSchema))
926
+ .output(zodAdapter<GitWorktreeAddOutput>(GitWorktreeAddOutputSchema))
927
+ .meta({
928
+ description: "Add a working tree in a new folder",
929
+ args: ["path", "ref"],
930
+ shorts: { branch: "b", detach: "d", cwd: "C" },
931
+ output: "json",
932
+ // Git refuses a folder that is not empty. It starts the post-checkout hook in the new folder.
933
+ effects: { reads: ["fs"], writes: ["fs", "process"], destructive: false, idempotent: true },
934
+ })
935
+ .handler(async (input: GitWorktreeAddInput, ctx): Promise<GitWorktreeAddOutput> => gitWorktreeAdd(input, ctx))
936
+ .build();
937
+
938
+ const gitWorktreeRemoveProcedure = createProcedure()
939
+ .path(["git", "worktree", "remove"])
940
+ .input(zodAdapter<GitWorktreeRemoveInput>(GitWorktreeRemoveInputSchema))
941
+ .output(zodAdapter<GitWorktreeRemoveOutput>(GitWorktreeRemoveOutputSchema))
942
+ .meta({
943
+ description: "Remove a working tree and its folder",
944
+ args: ["path"],
945
+ shorts: { force: "f", cwd: "C" },
946
+ output: "json",
947
+ // With force, the changes of the working tree are lost
948
+ effects: DELETES,
949
+ })
950
+ .handler(async (input: GitWorktreeRemoveInput, ctx): Promise<GitWorktreeRemoveOutput> => gitWorktreeRemove(input, ctx))
951
+ .build();
952
+
953
+ const gitBranchListProcedure = createProcedure()
954
+ .path(["git", "branch", "list"])
955
+ .input(zodAdapter<GitBranchListInput>(GitBranchListInputSchema))
956
+ .output(zodAdapter<GitBranchListOutput>(GitBranchListOutputSchema))
957
+ .meta({
958
+ description: "List the branches, and give the current branch",
959
+ args: [],
960
+ shorts: { remote: "r", cwd: "C" },
961
+ output: "json",
962
+ effects: READS_REPOSITORY,
963
+ })
964
+ .handler(async (input: GitBranchListInput, ctx): Promise<GitBranchListOutput> => gitBranchList(input, ctx))
965
+ .build();
966
+
967
+ const gitBranchAddProcedure = createProcedure()
968
+ .path(["git", "branch", "add"])
969
+ .input(zodAdapter<GitBranchAddInput>(GitBranchAddInputSchema))
970
+ .output(zodAdapter<GitBranchAddOutput>(GitBranchAddOutputSchema))
971
+ .meta({
972
+ description: "Make a branch. An existing branch is an error",
973
+ args: ["name", "startPoint"],
974
+ shorts: { cwd: "C" },
975
+ output: "json",
976
+ effects: ADDS_REF,
977
+ })
978
+ .handler(async (input: GitBranchAddInput, ctx): Promise<GitBranchAddOutput> => gitBranchAdd(input, ctx))
979
+ .build();
980
+
981
+ const gitBranchDeleteProcedure = createProcedure()
982
+ .path(["git", "branch", "delete"])
983
+ .input(zodAdapter<GitBranchDeleteInput>(GitBranchDeleteInputSchema))
984
+ .output(zodAdapter<GitBranchDeleteOutput>(GitBranchDeleteOutputSchema))
985
+ .meta({
986
+ description: "Delete a branch. With force, also a branch that is not merged",
987
+ args: ["name"],
988
+ shorts: { force: "f", cwd: "C" },
989
+ output: "json",
990
+ // With force, the commits of the branch can be lost
991
+ effects: DELETES,
992
+ })
993
+ .handler(async (input: GitBranchDeleteInput, ctx): Promise<GitBranchDeleteOutput> => gitBranchDelete(input, ctx))
994
+ .build();
995
+
607
996
  // =============================================================================
608
997
  // Registration
609
998
  // =============================================================================
@@ -642,6 +1031,26 @@ export const gitModule: ProcedureModule = defineModule("@procedures/git", [
642
1031
  gitStashDropProcedure,
643
1032
  gitStashExportProcedure,
644
1033
  gitStashImportProcedure,
1034
+ // Roadmap R40
1035
+ gitShowProcedure,
1036
+ gitBlameProcedure,
1037
+ gitTagListProcedure,
1038
+ gitTagAddProcedure,
1039
+ gitTagDeleteProcedure,
1040
+ gitRevParseProcedure,
1041
+ gitResetProcedure,
1042
+ gitRestoreProcedure,
1043
+ gitMergeProcedure,
1044
+ gitMergeAbortProcedure,
1045
+ gitRebaseProcedure,
1046
+ gitRebaseContinueProcedure,
1047
+ gitRebaseAbortProcedure,
1048
+ gitWorktreeListProcedure,
1049
+ gitWorktreeAddProcedure,
1050
+ gitWorktreeRemoveProcedure,
1051
+ gitBranchListProcedure,
1052
+ gitBranchAddProcedure,
1053
+ gitBranchDeleteProcedure,
645
1054
  ]);
646
1055
 
647
1056
  export default gitModule;
@@ -0,0 +1,68 @@
1
+ /**
2
+ * git.blame procedure
3
+ *
4
+ * Show the commit and the author of each line of a file (roadmap R40).
5
+ *
6
+ * The procedure reads `git blame --line-porcelain`: each line of the file comes with all the
7
+ * fields of its commit, and its text after a tab.
8
+ */
9
+
10
+ import { gitArg } from "./args.js";
11
+ import { git, type GitContext } from "./run.js";
12
+ import type { GitBlameInput, GitBlameLine, GitBlameOutput } from "../../types.js";
13
+
14
+ /** This function reads the output of `git blame --line-porcelain`. */
15
+ export function parseBlame(output: string): GitBlameLine[] {
16
+ const lines: GitBlameLine[] = [];
17
+ let current: Partial<GitBlameLine> & { time?: number } = {};
18
+ for (const row of output.split("\n")) {
19
+ if (row.startsWith("\t")) {
20
+ // The text of the line ends the entry
21
+ lines.push({
22
+ line: current.line ?? lines.length + 1,
23
+ hash: current.hash ?? "",
24
+ author: current.author ?? "",
25
+ email: current.email ?? "",
26
+ date: current.time !== undefined ? new Date(current.time * 1000).toISOString() : "",
27
+ summary: current.summary ?? "",
28
+ content: row.slice(1),
29
+ });
30
+ current = {};
31
+ continue;
32
+ }
33
+ const space = row.indexOf(" ");
34
+ const key = space < 0 ? row : row.slice(0, space);
35
+ const value = space < 0 ? "" : row.slice(space + 1);
36
+ if (/^[0-9a-f]{40,64}$/.test(key)) {
37
+ // "<hash> <line in the commit> <line in the file> [<lines of the group>]"
38
+ current.hash = key;
39
+ current.line = Number(value.split(" ")[1]);
40
+ } else if (key === "author") {
41
+ current.author = value;
42
+ } else if (key === "author-mail") {
43
+ current.email = value.replace(/^<|>$/g, "");
44
+ } else if (key === "author-time") {
45
+ current.time = Number(value);
46
+ } else if (key === "summary") {
47
+ current.summary = value;
48
+ }
49
+ }
50
+ return lines;
51
+ }
52
+
53
+ /**
54
+ * Show the commit and the author of each line of a file
55
+ */
56
+ export async function gitBlame(input: GitBlameInput, ctx: GitContext = {}): Promise<GitBlameOutput> {
57
+ const { path, ref, startLine, endLine, cwd } = input;
58
+ const args = ["blame", "--line-porcelain"];
59
+ if (startLine !== undefined || endLine !== undefined) {
60
+ args.push("-L", `${startLine ?? 1},${endLine ?? ""}`);
61
+ }
62
+ if (ref) args.push(gitArg("ref", ref));
63
+ // After "--", a path that starts with "-" stays a path
64
+ args.push("--", path);
65
+
66
+ const output = await git(args, { cwd, signal: ctx.signal });
67
+ return { path, lines: parseBlame(output) };
68
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * git.branch.add procedure
3
+ *
4
+ * Make a branch (roadmap R40). An existing branch is an error: the procedure never moves a branch.
5
+ */
6
+
7
+ import { gitArg } from "../args.js";
8
+ import { git, type GitContext } from "../run.js";
9
+ import { commitOf } from "../refs.js";
10
+ import type { GitBranchAddInput, GitBranchAddOutput } from "../../../types.js";
11
+
12
+ /**
13
+ * Make a branch
14
+ */
15
+ export async function gitBranchAdd(input: GitBranchAddInput, ctx: GitContext = {}): Promise<GitBranchAddOutput> {
16
+ const name = gitArg("name", input.name);
17
+ const run = { cwd: input.cwd, signal: ctx.signal };
18
+
19
+ const args = ["branch", name];
20
+ if (input.startPoint) args.push(gitArg("startPoint", input.startPoint));
21
+ await git(args, run);
22
+
23
+ return { name, hash: await commitOf(`refs/heads/${name}`, run) };
24
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * git.branch.delete procedure
3
+ *
4
+ * Delete a branch (roadmap R40). Git refuses a branch that is not merged, unless `force` is true.
5
+ * The result gives the last commit of the branch, so git.branch.add can make it again.
6
+ */
7
+
8
+ import { gitArg } from "../args.js";
9
+ import { git, GitError, type GitContext } from "../run.js";
10
+ import type { GitBranchDeleteInput, GitBranchDeleteOutput } from "../../../types.js";
11
+
12
+ /**
13
+ * Delete a branch
14
+ */
15
+ export async function gitBranchDelete(input: GitBranchDeleteInput, ctx: GitContext = {}): Promise<GitBranchDeleteOutput> {
16
+ const name = gitArg("name", input.name);
17
+ const run = { cwd: input.cwd, signal: ctx.signal };
18
+
19
+ // With --quiet, a missing branch gives exit 1 and no message. Outside a repository, git gives 128.
20
+ const hash = await git(["rev-parse", "--verify", "--quiet", `refs/heads/${name}^{commit}`], run).then(
21
+ (output) => output.trim(),
22
+ (error: unknown) => {
23
+ if (error instanceof GitError && error.exitCode === 1) throw new Error(`branch '${name}' not found`);
24
+ throw error;
25
+ },
26
+ );
27
+ await git(["branch", input.force ? "-D" : "-d", name], run);
28
+ return { name, hash };
29
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * git.branch.list procedure
3
+ *
4
+ * List the branches (roadmap R40). It works in a repository without commits too: there are no
5
+ * branches yet, and `current` is the branch that HEAD names.
6
+ */
7
+
8
+ import { git, type GitContext } from "../run.js";
9
+ import { currentBranch } from "../refs.js";
10
+ import type { GitBranchInfo, GitBranchListInput, GitBranchListOutput } from "../../../types.js";
11
+
12
+ /**
13
+ * List the branches
14
+ */
15
+ export async function gitBranchList(input: GitBranchListInput, ctx: GitContext = {}): Promise<GitBranchListOutput> {
16
+ const run = { cwd: input.cwd, signal: ctx.signal };
17
+ const current = await currentBranch(run);
18
+
19
+ const args = ["branch"];
20
+ if (input.remote) args.push("-a");
21
+ // The full ref name tells local from remote branches: a local "feature/x" also contains "/"
22
+ args.push("--format=%(refname)|%(refname:short)|%(HEAD)|%(upstream:short)");
23
+
24
+ const output = await git(args, run);
25
+ const branches: GitBranchInfo[] = output
26
+ .split("\n")
27
+ .filter(Boolean)
28
+ .map((line) => {
29
+ const [fullName = "", branchName = "", head = "", tracking = ""] = line.split("|");
30
+ const result: GitBranchInfo = {
31
+ name: branchName.replace(/^remotes\//, ""),
32
+ current: head === "*",
33
+ remote: fullName.startsWith("refs/remotes/"),
34
+ };
35
+ if (tracking) result.tracking = tracking;
36
+ return result;
37
+ });
38
+
39
+ return { branches, current };
40
+ }