git 4.3.2 → 5.0.0.beta.2

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 (297) hide show
  1. checksums.yaml +4 -4
  2. data/.github/copilot-instructions.md +73 -2705
  3. data/.github/prompts/iteratively-address-copilot-reviews.prompt.md +188 -0
  4. data/.github/pull_request_template.md +3 -1
  5. data/.github/skills/breaking-change-analysis/SKILL.md +102 -0
  6. data/.github/skills/ci-cd-troubleshooting/SKILL.md +264 -0
  7. data/.github/skills/command-implementation/REFERENCE.md +993 -0
  8. data/.github/skills/command-implementation/SKILL.md +229 -0
  9. data/.github/skills/command-test-conventions/SKILL.md +660 -0
  10. data/.github/skills/command-yard-documentation/SKILL.md +426 -0
  11. data/.github/skills/dependency-management/SKILL.md +72 -0
  12. data/.github/skills/development-workflow/SKILL.md +506 -0
  13. data/.github/skills/extract-command-from-lib/SKILL.md +487 -0
  14. data/.github/skills/extract-facade-from-base-lib/KEYWORD_ARG_REMEDIATION.md +22 -0
  15. data/.github/skills/extract-facade-from-base-lib/SKILL.md +600 -0
  16. data/.github/skills/facade-implementation/REFERENCE.md +840 -0
  17. data/.github/skills/facade-implementation/SKILL.md +274 -0
  18. data/.github/skills/facade-test-conventions/SKILL.md +394 -0
  19. data/.github/skills/facade-yard-documentation/SKILL.md +429 -0
  20. data/.github/skills/make-skill-template/SKILL.md +176 -0
  21. data/.github/skills/pr-readiness-review/SKILL.md +185 -0
  22. data/.github/skills/project-context/SKILL.md +313 -0
  23. data/.github/skills/pull-request-review/SKILL.md +168 -0
  24. data/.github/skills/refactor-command-to-commandlineresult/SKILL.md +131 -0
  25. data/.github/skills/release-management/SKILL.md +125 -0
  26. data/.github/skills/review-arguments-dsl/CHECKLIST.md +788 -0
  27. data/.github/skills/review-arguments-dsl/SKILL.md +214 -0
  28. data/.github/skills/review-backward-compatibility/SKILL.md +275 -0
  29. data/.github/skills/review-cross-command-consistency/SKILL.md +139 -0
  30. data/.github/skills/reviewing-skills/SKILL.md +189 -0
  31. data/.github/skills/rspec-unit-testing-standards/SKILL.md +639 -0
  32. data/.github/skills/tdd-refactor-step/SKILL.md +236 -0
  33. data/.github/skills/test-debugging/SKILL.md +160 -0
  34. data/.github/skills/yard-documentation/SKILL.md +793 -0
  35. data/.github/workflows/continuous_integration.yml +3 -2
  36. data/.github/workflows/enforce_conventional_commits.yml +1 -1
  37. data/.github/workflows/experimental_continuous_integration.yml +2 -2
  38. data/.github/workflows/release.yml +3 -4
  39. data/.gitignore +8 -0
  40. data/.husky/pre-commit +13 -0
  41. data/.release-please-manifest.json +1 -1
  42. data/.rspec +3 -0
  43. data/.rubocop.yml +12 -3
  44. data/.rubocop_todo.yml +23 -5
  45. data/.yardopts +1 -0
  46. data/CHANGELOG.md +0 -40
  47. data/CONTRIBUTING.md +694 -53
  48. data/README.md +67 -15
  49. data/Rakefile +61 -9
  50. data/UPGRADING.md +141 -0
  51. data/commitlint.test +4 -0
  52. data/git.gemspec +18 -7
  53. data/lib/git/args_builder.rb +0 -8
  54. data/lib/git/branch.rb +369 -43
  55. data/lib/git/branch_delete_failure.rb +31 -0
  56. data/lib/git/branch_delete_result.rb +63 -0
  57. data/lib/git/branch_info.rb +178 -0
  58. data/lib/git/branches.rb +122 -24
  59. data/lib/git/command_line/base.rb +255 -0
  60. data/lib/git/command_line/capturing.rb +251 -0
  61. data/lib/git/command_line/result.rb +96 -0
  62. data/lib/git/command_line/streaming.rb +196 -0
  63. data/lib/git/command_line.rb +43 -322
  64. data/lib/git/command_line_result.rb +4 -88
  65. data/lib/git/commands/add.rb +131 -0
  66. data/lib/git/commands/am/abort.rb +43 -0
  67. data/lib/git/commands/am/apply.rb +252 -0
  68. data/lib/git/commands/am/continue.rb +43 -0
  69. data/lib/git/commands/am/quit.rb +43 -0
  70. data/lib/git/commands/am/retry.rb +47 -0
  71. data/lib/git/commands/am/show_current_patch.rb +64 -0
  72. data/lib/git/commands/am/skip.rb +42 -0
  73. data/lib/git/commands/am.rb +33 -0
  74. data/lib/git/commands/apply.rb +237 -0
  75. data/lib/git/commands/archive/list_formats.rb +46 -0
  76. data/lib/git/commands/archive.rb +140 -0
  77. data/lib/git/commands/arguments.rb +3510 -0
  78. data/lib/git/commands/base.rb +404 -0
  79. data/lib/git/commands/branch/copy.rb +94 -0
  80. data/lib/git/commands/branch/create.rb +173 -0
  81. data/lib/git/commands/branch/delete.rb +80 -0
  82. data/lib/git/commands/branch/list.rb +162 -0
  83. data/lib/git/commands/branch/move.rb +94 -0
  84. data/lib/git/commands/branch/set_upstream.rb +86 -0
  85. data/lib/git/commands/branch/show_current.rb +49 -0
  86. data/lib/git/commands/branch/unset_upstream.rb +57 -0
  87. data/lib/git/commands/branch.rb +34 -0
  88. data/lib/git/commands/cat_file/batch.rb +369 -0
  89. data/lib/git/commands/cat_file/filtered.rb +105 -0
  90. data/lib/git/commands/cat_file/raw.rb +216 -0
  91. data/lib/git/commands/cat_file.rb +49 -0
  92. data/lib/git/commands/checkout/branch.rb +151 -0
  93. data/lib/git/commands/checkout/files.rb +115 -0
  94. data/lib/git/commands/checkout.rb +38 -0
  95. data/lib/git/commands/checkout_index.rb +105 -0
  96. data/lib/git/commands/clean.rb +100 -0
  97. data/lib/git/commands/clone.rb +240 -0
  98. data/lib/git/commands/commit.rb +272 -0
  99. data/lib/git/commands/commit_tree.rb +100 -0
  100. data/lib/git/commands/config_option_syntax/add.rb +83 -0
  101. data/lib/git/commands/config_option_syntax/get.rb +117 -0
  102. data/lib/git/commands/config_option_syntax/get_all.rb +115 -0
  103. data/lib/git/commands/config_option_syntax/get_color.rb +91 -0
  104. data/lib/git/commands/config_option_syntax/get_color_bool.rb +93 -0
  105. data/lib/git/commands/config_option_syntax/get_regexp.rb +115 -0
  106. data/lib/git/commands/config_option_syntax/get_urlmatch.rb +107 -0
  107. data/lib/git/commands/config_option_syntax/list.rb +107 -0
  108. data/lib/git/commands/config_option_syntax/remove_section.rb +74 -0
  109. data/lib/git/commands/config_option_syntax/rename_section.rb +78 -0
  110. data/lib/git/commands/config_option_syntax/replace_all.rb +104 -0
  111. data/lib/git/commands/config_option_syntax/set.rb +114 -0
  112. data/lib/git/commands/config_option_syntax/unset.rb +89 -0
  113. data/lib/git/commands/config_option_syntax/unset_all.rb +89 -0
  114. data/lib/git/commands/config_option_syntax.rb +56 -0
  115. data/lib/git/commands/describe.rb +155 -0
  116. data/lib/git/commands/diff.rb +656 -0
  117. data/lib/git/commands/diff_files.rb +518 -0
  118. data/lib/git/commands/diff_index.rb +496 -0
  119. data/lib/git/commands/fetch.rb +352 -0
  120. data/lib/git/commands/fsck.rb +136 -0
  121. data/lib/git/commands/gc.rb +132 -0
  122. data/lib/git/commands/grep.rb +338 -0
  123. data/lib/git/commands/init.rb +99 -0
  124. data/lib/git/commands/log.rb +632 -0
  125. data/lib/git/commands/ls_files.rb +191 -0
  126. data/lib/git/commands/ls_remote.rb +155 -0
  127. data/lib/git/commands/ls_tree.rb +131 -0
  128. data/lib/git/commands/maintenance/register.rb +75 -0
  129. data/lib/git/commands/maintenance/run.rb +104 -0
  130. data/lib/git/commands/maintenance/start.rb +66 -0
  131. data/lib/git/commands/maintenance/stop.rb +55 -0
  132. data/lib/git/commands/maintenance/unregister.rb +79 -0
  133. data/lib/git/commands/maintenance.rb +31 -0
  134. data/lib/git/commands/merge/abort.rb +44 -0
  135. data/lib/git/commands/merge/continue.rb +44 -0
  136. data/lib/git/commands/merge/quit.rb +46 -0
  137. data/lib/git/commands/merge/start.rb +245 -0
  138. data/lib/git/commands/merge.rb +28 -0
  139. data/lib/git/commands/merge_base.rb +86 -0
  140. data/lib/git/commands/mv.rb +77 -0
  141. data/lib/git/commands/name_rev.rb +114 -0
  142. data/lib/git/commands/pull.rb +377 -0
  143. data/lib/git/commands/push.rb +246 -0
  144. data/lib/git/commands/read_tree.rb +149 -0
  145. data/lib/git/commands/remote/add.rb +91 -0
  146. data/lib/git/commands/remote/get_url.rb +66 -0
  147. data/lib/git/commands/remote/list.rb +54 -0
  148. data/lib/git/commands/remote/prune.rb +61 -0
  149. data/lib/git/commands/remote/remove.rb +52 -0
  150. data/lib/git/commands/remote/rename.rb +69 -0
  151. data/lib/git/commands/remote/set_branches.rb +63 -0
  152. data/lib/git/commands/remote/set_head.rb +82 -0
  153. data/lib/git/commands/remote/set_url.rb +71 -0
  154. data/lib/git/commands/remote/set_url_add.rb +61 -0
  155. data/lib/git/commands/remote/set_url_delete.rb +64 -0
  156. data/lib/git/commands/remote/show.rb +71 -0
  157. data/lib/git/commands/remote/update.rb +72 -0
  158. data/lib/git/commands/remote.rb +42 -0
  159. data/lib/git/commands/repack.rb +277 -0
  160. data/lib/git/commands/reset.rb +147 -0
  161. data/lib/git/commands/rev_parse.rb +297 -0
  162. data/lib/git/commands/revert/abort.rb +45 -0
  163. data/lib/git/commands/revert/continue.rb +57 -0
  164. data/lib/git/commands/revert/quit.rb +47 -0
  165. data/lib/git/commands/revert/skip.rb +44 -0
  166. data/lib/git/commands/revert/start.rb +153 -0
  167. data/lib/git/commands/revert.rb +29 -0
  168. data/lib/git/commands/rm.rb +114 -0
  169. data/lib/git/commands/show.rb +632 -0
  170. data/lib/git/commands/show_ref/exclude_existing.rb +120 -0
  171. data/lib/git/commands/show_ref/exists.rb +78 -0
  172. data/lib/git/commands/show_ref/list.rb +145 -0
  173. data/lib/git/commands/show_ref/verify.rb +120 -0
  174. data/lib/git/commands/show_ref.rb +42 -0
  175. data/lib/git/commands/stash/apply.rb +75 -0
  176. data/lib/git/commands/stash/branch.rb +65 -0
  177. data/lib/git/commands/stash/clear.rb +41 -0
  178. data/lib/git/commands/stash/create.rb +58 -0
  179. data/lib/git/commands/stash/drop.rb +67 -0
  180. data/lib/git/commands/stash/list.rb +39 -0
  181. data/lib/git/commands/stash/pop.rb +78 -0
  182. data/lib/git/commands/stash/push.rb +103 -0
  183. data/lib/git/commands/stash/show.rb +149 -0
  184. data/lib/git/commands/stash/store.rb +63 -0
  185. data/lib/git/commands/stash.rb +38 -0
  186. data/lib/git/commands/status.rb +169 -0
  187. data/lib/git/commands/symbolic_ref/delete.rb +68 -0
  188. data/lib/git/commands/symbolic_ref/read.rb +95 -0
  189. data/lib/git/commands/symbolic_ref/update.rb +76 -0
  190. data/lib/git/commands/symbolic_ref.rb +38 -0
  191. data/lib/git/commands/tag/create.rb +139 -0
  192. data/lib/git/commands/tag/delete.rb +55 -0
  193. data/lib/git/commands/tag/list.rb +143 -0
  194. data/lib/git/commands/tag/verify.rb +71 -0
  195. data/lib/git/commands/tag.rb +26 -0
  196. data/lib/git/commands/update_ref/batch.rb +140 -0
  197. data/lib/git/commands/update_ref/delete.rb +92 -0
  198. data/lib/git/commands/update_ref/update.rb +106 -0
  199. data/lib/git/commands/update_ref.rb +42 -0
  200. data/lib/git/commands/version.rb +57 -0
  201. data/lib/git/commands/worktree/add.rb +140 -0
  202. data/lib/git/commands/worktree/list.rb +64 -0
  203. data/lib/git/commands/worktree/lock.rb +58 -0
  204. data/lib/git/commands/worktree/management_base.rb +51 -0
  205. data/lib/git/commands/worktree/move.rb +66 -0
  206. data/lib/git/commands/worktree/prune.rb +67 -0
  207. data/lib/git/commands/worktree/remove.rb +63 -0
  208. data/lib/git/commands/worktree/repair.rb +76 -0
  209. data/lib/git/commands/worktree/unlock.rb +47 -0
  210. data/lib/git/commands/worktree.rb +43 -0
  211. data/lib/git/commands/write_tree.rb +68 -0
  212. data/lib/git/commands.rb +87 -0
  213. data/lib/git/config.rb +17 -0
  214. data/lib/git/config_entry_info.rb +106 -0
  215. data/lib/git/configuring.rb +665 -0
  216. data/lib/git/deprecation.rb +9 -0
  217. data/lib/git/detached_head_info.rb +54 -0
  218. data/lib/git/diff.rb +293 -7
  219. data/lib/git/diff_file_numstat_info.rb +29 -0
  220. data/lib/git/diff_file_patch_info.rb +134 -0
  221. data/lib/git/diff_file_raw_info.rb +127 -0
  222. data/lib/git/diff_info.rb +169 -0
  223. data/lib/git/diff_path_status.rb +67 -19
  224. data/lib/git/diff_result.rb +32 -0
  225. data/lib/git/diff_stats.rb +51 -14
  226. data/lib/git/dirstat_info.rb +86 -0
  227. data/lib/git/errors.rb +65 -2
  228. data/lib/git/execution_context/global.rb +31 -0
  229. data/lib/git/execution_context/repository.rb +136 -0
  230. data/lib/git/execution_context.rb +501 -0
  231. data/lib/git/file_ref.rb +74 -0
  232. data/lib/git/fsck_object.rb +9 -9
  233. data/lib/git/fsck_result.rb +1 -1
  234. data/lib/git/log.rb +9 -2
  235. data/lib/git/object.rb +90 -27
  236. data/lib/git/parsers/branch.rb +224 -0
  237. data/lib/git/parsers/cat_file.rb +111 -0
  238. data/lib/git/parsers/config_entry.rb +110 -0
  239. data/lib/git/parsers/diff.rb +585 -0
  240. data/lib/git/parsers/fsck.rb +133 -0
  241. data/lib/git/parsers/grep.rb +42 -0
  242. data/lib/git/parsers/ls_remote.rb +79 -0
  243. data/lib/git/parsers/ls_tree.rb +58 -0
  244. data/lib/git/parsers/stash.rb +208 -0
  245. data/lib/git/parsers/tag.rb +257 -0
  246. data/lib/git/remote.rb +120 -9
  247. data/lib/git/repository/branching.rb +743 -0
  248. data/lib/git/repository/committing.rb +187 -0
  249. data/lib/git/repository/configuring.rb +351 -0
  250. data/lib/git/repository/context_helpers.rb +264 -0
  251. data/lib/git/repository/diffing.rb +775 -0
  252. data/lib/git/repository/factories.rb +682 -0
  253. data/lib/git/repository/inspecting.rb +252 -0
  254. data/lib/git/repository/logging.rb +247 -0
  255. data/lib/git/repository/maintenance.rb +65 -0
  256. data/lib/git/repository/merging.rb +357 -0
  257. data/lib/git/repository/object_operations.rb +1199 -0
  258. data/lib/git/repository/path_resolver.rb +207 -0
  259. data/lib/git/repository/remote_operations.rb +898 -0
  260. data/lib/git/repository/shared_private.rb +51 -0
  261. data/lib/git/repository/staging.rb +554 -0
  262. data/lib/git/repository/stashing.rb +143 -0
  263. data/lib/git/repository/status_operations.rb +201 -0
  264. data/lib/git/repository/worktree_operations.rb +159 -0
  265. data/lib/git/repository.rb +203 -1
  266. data/lib/git/stash.rb +78 -4
  267. data/lib/git/stash_info.rb +104 -0
  268. data/lib/git/stashes.rb +125 -13
  269. data/lib/git/status.rb +215 -18
  270. data/lib/git/tag_delete_failure.rb +31 -0
  271. data/lib/git/tag_delete_result.rb +63 -0
  272. data/lib/git/tag_info.rb +105 -0
  273. data/lib/git/version.rb +109 -2
  274. data/lib/git/version_constraint.rb +81 -0
  275. data/lib/git/worktree.rb +108 -6
  276. data/lib/git/worktrees.rb +94 -7
  277. data/lib/git.rb +260 -61
  278. data/redesign/1_architecture_existing.md +54 -18
  279. data/redesign/2_architecture_redesign.md +365 -46
  280. data/redesign/3_architecture_implementation.md +1497 -63
  281. data/redesign/Phase 4 - Step A.md +360 -0
  282. data/redesign/beta_release.md +107 -0
  283. data/redesign/c1c2_audit.md +566 -0
  284. data/redesign/c1c2_bucket6_lib_orphans.md +626 -0
  285. data/redesign/config_design.rb +501 -0
  286. data/tasks/gem_tasks.rake +4 -0
  287. data/tasks/npm_tasks.rake +7 -0
  288. data/tasks/rspec.rake +48 -0
  289. data/tasks/test.rake +13 -1
  290. data/tasks/yard.rake +34 -7
  291. metadata +365 -22
  292. data/lib/git/base.rb +0 -1128
  293. data/lib/git/index.rb +0 -6
  294. data/lib/git/lib.rb +0 -2277
  295. data/lib/git/path.rb +0 -38
  296. data/lib/git/working_directory.rb +0 -6
  297. /data/{release-please-config.json → .release-please-config.json} +0 -0
@@ -1,6 +1,208 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'find'
4
+ require 'pathname'
5
+
6
+ require 'git/configuring'
7
+ require 'git/deprecation'
8
+ require 'git/execution_context/repository'
9
+ require 'git/repository/branching'
10
+ require 'git/repository/context_helpers'
11
+ require 'git/repository/committing'
12
+ require 'git/repository/configuring'
13
+ require 'git/repository/diffing'
14
+ require 'git/repository/factories'
15
+ require 'git/repository/inspecting'
16
+ require 'git/repository/logging'
17
+ require 'git/repository/maintenance'
18
+ require 'git/repository/merging'
19
+ require 'git/repository/object_operations'
20
+ require 'git/repository/remote_operations'
21
+ require 'git/repository/staging'
22
+ require 'git/repository/stashing'
23
+ require 'git/repository/status_operations'
24
+ require 'git/repository/worktree_operations'
25
+
3
26
  module Git
4
- class Repository < Path
27
+ # The main public interface for interacting with a Git repository
28
+ #
29
+ # `Git::Repository` is the **orchestration layer** for all git operations. It acts
30
+ # as the glue between the user-facing API and the underlying components, but
31
+ # contains minimal domain logic itself. For each operation it:
32
+ #
33
+ # 1. **Pre-processes arguments** — transforms user-provided values into forms
34
+ # suitable for the command layer (e.g. path expansion, option normalization,
35
+ # Ruby-idiomatic defaults, deprecation handling, input validation).
36
+ # 2. **Calls commands** — invokes one or more `Git::Commands::*` classes via the
37
+ # injected `Git::ExecutionContext::Repository`.
38
+ # 3. **Builds rich return values** — passes raw command output through
39
+ # `Git::Parsers::*` classes and result-class factory methods to assemble the
40
+ # meaningful Ruby objects the caller expects.
41
+ #
42
+ # Some operations are genuinely one-line delegators when no pre/post-processing is
43
+ # needed (e.g. `add`, `reset`), but many are short orchestration sequences that
44
+ # coordinate argument preparation, one or more command calls, and result assembly.
45
+ #
46
+ # Facade methods are organized into focused modules under `lib/git/repository/`
47
+ # (e.g. {Git::Repository::Staging}) and included into this class.
48
+ #
49
+ # @api public
50
+ #
51
+ class Repository
52
+ extend Git::Repository::Factories
53
+
54
+ include Git::Configuring
55
+ include Git::Repository::Branching
56
+ include Git::Repository::ContextHelpers
57
+ include Git::Repository::Committing
58
+ include Git::Repository::Configuring
59
+ include Git::Repository::Diffing
60
+ include Git::Repository::Inspecting
61
+ include Git::Repository::Logging
62
+ include Git::Repository::Maintenance
63
+ include Git::Repository::Merging
64
+ include Git::Repository::ObjectOperations
65
+ include Git::Repository::RemoteOperations
66
+ include Git::Repository::Staging
67
+ include Git::Repository::Stashing
68
+ include Git::Repository::StatusOperations
69
+ include Git::Repository::WorktreeOperations
70
+
71
+ # @return [Git::ExecutionContext::Repository] the execution context used to run
72
+ # git commands for this repository
73
+ # @api private
74
+ attr_reader :execution_context
75
+
76
+ # @param execution_context [Git::ExecutionContext::Repository] the context used
77
+ # to run git commands for this repository; must not be nil
78
+ #
79
+ # @raise [ArgumentError] if `execution_context` is nil
80
+ #
81
+ def initialize(execution_context:)
82
+ raise ArgumentError, 'execution_context must not be nil' if execution_context.nil?
83
+
84
+ @execution_context = execution_context
85
+ end
86
+
87
+ # Returns the root of the working tree, or `nil` for a bare repository
88
+ #
89
+ # @example Get the working directory path
90
+ # repository.dir #=> #<Pathname:/path/to/repo>
91
+ #
92
+ # @return [Pathname, nil] the working directory path, or `nil` when bare
93
+ #
94
+ def dir
95
+ working_dir = execution_context.git_work_dir
96
+ working_dir && Pathname.new(working_dir)
97
+ end
98
+
99
+ # Returns the repository (`.git`) directory
100
+ #
101
+ # @example Get the repository directory path
102
+ # repository.repo #=> #<Pathname:/path/to/repo/.git>
103
+ #
104
+ # @return [Pathname, nil] the repository directory path
105
+ #
106
+ def repo
107
+ repository = execution_context.git_dir
108
+ repository && Pathname.new(repository)
109
+ end
110
+
111
+ # Returns the git index file
112
+ #
113
+ # @example Get the index file path
114
+ # repository.index #=> #<Pathname:/path/to/repo/.git/index>
115
+ #
116
+ # @return [Pathname, nil] the index file path
117
+ #
118
+ def index
119
+ index_file = execution_context.git_index_file
120
+ index_file && Pathname.new(index_file)
121
+ end
122
+
123
+ # Returns `self` after emitting a deprecation warning.
124
+ #
125
+ # Legacy callers that used `git.lib.some_method` can migrate to calling the
126
+ # facade method directly on the repository object. This shim will be removed
127
+ # in v6.0.0.
128
+ #
129
+ # @return [self]
130
+ #
131
+ # @api private
132
+ #
133
+ def lib
134
+ Git::Deprecation.warn(
135
+ 'Git::Repository#lib is deprecated and will be removed in v6.0.0. ' \
136
+ 'Use the repository object directly.'
137
+ )
138
+ self
139
+ end
140
+
141
+ # @return [String, nil] the git directory path
142
+ #
143
+ # @api private
144
+ def git_dir = execution_context.git_dir
145
+
146
+ # @return [String, nil] the working directory path
147
+ #
148
+ # @api private
149
+ def git_work_dir = execution_context.git_work_dir
150
+
151
+ # @return [String, nil] the index file path
152
+ #
153
+ # @api private
154
+ def git_index_file = execution_context.git_index_file
155
+
156
+ # @return [Git::Version] the installed git version
157
+ #
158
+ # @api private
159
+ def git_version(timeout: nil) = execution_context.git_version(timeout: timeout)
160
+
161
+ # @return [String, nil] the SSH wrapper path
162
+ #
163
+ # @api private
164
+ def git_ssh = execution_context.git_ssh
165
+
166
+ # @return [String, :use_global_config] the path to the git binary
167
+ #
168
+ # @api private
169
+ def binary_path = execution_context.binary_path
170
+
171
+ # Returns the size of the repository directory in bytes
172
+ #
173
+ # Sums the sizes of every regular file under the repository (`.git`)
174
+ # directory in a single traversal. Symbolic links are not followed, so files
175
+ # that physically live outside the repository (reached through a symlinked
176
+ # directory) are never counted. Files that disappear mid-traversal are
177
+ # silently skipped.
178
+ #
179
+ # @example Get the repository size in bytes
180
+ # repository.repo_size #=> 12345
181
+ #
182
+ # @return [Integer] the total size in bytes of the repository directory
183
+ #
184
+ def repo_size
185
+ repository = repo
186
+ return 0 unless repository&.directory?
187
+
188
+ total = 0
189
+ Find.find(repository.to_s) do |path|
190
+ stat = File.lstat(path)
191
+ total += stat.size if stat.file?
192
+ rescue Errno::ENOENT
193
+ next
194
+ end
195
+ total
196
+ end
197
+
198
+ private
199
+
200
+ # All git config scopes are valid in a repository context
201
+ #
202
+ # @return [void]
203
+ #
204
+ def assert_valid_scope!(**)
205
+ nil
206
+ end
5
207
  end
6
208
  end
data/lib/git/stash.rb CHANGED
@@ -1,26 +1,100 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Git
4
- # A stash in a Git repository
4
+ # Represents a single stash entry in a Git repository
5
+ #
6
+ # @example Create a stash and inspect the result
7
+ # stash = Git::Stash.new(repo, 'WIP: feature work')
8
+ # stash.message #=> "WIP: feature work"
9
+ # stash.saved? #=> true
10
+ #
11
+ # @api public
12
+ #
5
13
  class Stash
6
- def initialize(base, message, save: false)
14
+ # Initialize a Stash object
15
+ #
16
+ # When `existing` is `false` (the default), immediately calls {#save} to push
17
+ # the current working-directory state onto the stash stack.
18
+ #
19
+ # @param base [Git::Repository] the git repository
20
+ #
21
+ # @param message [String] the stash message
22
+ #
23
+ # @param existing [Boolean] (false) when `true`, wraps an existing stash entry
24
+ # without pushing any changes
25
+ #
26
+ # @return [void]
27
+ #
28
+ # @example Create a new stash entry
29
+ # stash = Git::Stash.new(repo, 'WIP: feature work')
30
+ # stash.saved? #=> true
31
+ #
32
+ # @example Reference an existing stash without pushing
33
+ # stash = Git::Stash.new(repo, 'WIP: feature work', existing: true)
34
+ # stash.saved? #=> nil
35
+ #
36
+ def initialize(base, message, existing: false)
7
37
  @base = base
8
38
  @message = message
9
- self.save unless save
39
+ save unless existing
10
40
  end
11
41
 
42
+ # Saves the current working-directory state to the stash stack
43
+ #
44
+ # @return [Boolean] `true` if changes were stashed, `false` if there were no
45
+ # local changes to save
46
+ #
47
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
48
+ #
49
+ # @example Save changes to the stash stack
50
+ # stash = Git::Stash.new(repo, 'WIP', existing: true)
51
+ # stash.save #=> true
52
+ #
12
53
  def save
13
- @saved = @base.lib.stash_save(@message)
54
+ @saved = stash_repository.stash_save(@message)
14
55
  end
15
56
 
57
+ # Returns whether the stash was saved successfully
58
+ #
59
+ # @return [Boolean, nil] `true` if changes were stashed, `false` if there were no
60
+ # local changes, `nil` if {#save} has not been called (e.g. `existing: true`)
61
+ #
62
+ # @example Check if changes were stashed
63
+ # stash = Git::Stash.new(repo, 'WIP: feature work')
64
+ # stash.saved? #=> true
65
+ #
16
66
  def saved?
17
67
  @saved
18
68
  end
19
69
 
70
+ # Returns the stash description
71
+ #
72
+ # @return [String] the stash message
73
+ #
74
+ # @example Read the stash message
75
+ # stash = Git::Stash.new(repo, 'WIP: feature work', existing: true)
76
+ # stash.message #=> "WIP: feature work"
77
+ #
20
78
  attr_reader :message
21
79
 
80
+ # Returns the stash description as a string
81
+ #
82
+ # @return [String] the stash message
83
+ #
84
+ # @example Convert stash to string
85
+ # stash = Git::Stash.new(repo, 'WIP: feature work', existing: true)
86
+ # stash.to_s #=> "WIP: feature work"
87
+ #
22
88
  def to_s
23
89
  message
24
90
  end
91
+
92
+ private
93
+
94
+ # @return [Git::Repository]
95
+ #
96
+ def stash_repository
97
+ @base
98
+ end
25
99
  end
26
100
  end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ # Immutable value object representing stash entry information
5
+ #
6
+ # StashInfo encapsulates the parsed data from `git stash list` output.
7
+ # Each entry contains comprehensive information about the stash including
8
+ # its index, reference name, commit SHA, branch, message, author/committer
9
+ # details, and timestamps.
10
+ #
11
+ # @api public
12
+ #
13
+ # @example Create a StashInfo from parsed stash list output
14
+ # info = Git::StashInfo.new(
15
+ # index: 0,
16
+ # name: 'stash@{0}',
17
+ # oid: 'abc123def456789...',
18
+ # short_oid: 'abc123d',
19
+ # branch: 'main',
20
+ # message: 'WIP on main: abc123 Initial commit',
21
+ # author_name: 'Jane Doe',
22
+ # author_email: 'jane@example.com',
23
+ # author_date: '2026-01-24T10:30:00-08:00',
24
+ # committer_name: 'Jane Doe',
25
+ # committer_email: 'jane@example.com',
26
+ # committer_date: '2026-01-24T10:30:00-08:00'
27
+ # )
28
+ #
29
+ # info.index # => 0
30
+ # info.name # => 'stash@{0}'
31
+ # info.oid # => 'abc123def456789...'
32
+ # info.short_oid # => 'abc123d'
33
+ # info.branch # => 'main'
34
+ # info.message # => 'WIP on main: abc123 Initial commit'
35
+ # info.author_name # => 'Jane Doe'
36
+ # info.author_email # => 'jane@example.com'
37
+ # info.author_date # => '2026-01-24T10:30:00-08:00'
38
+ # info.committer_name # => 'Jane Doe'
39
+ # info.committer_email # => 'jane@example.com'
40
+ # info.committer_date # => '2026-01-24T10:30:00-08:00'
41
+ #
42
+ # @!attribute [r] index
43
+ # @return [Integer] the stash index (0, 1, 2, ...)
44
+ #
45
+ # @!attribute [r] name
46
+ # @return [String] the stash reference name (e.g., 'stash@\\{0\\}')
47
+ #
48
+ # @!attribute [r] oid
49
+ # @return [String] the full 40-character object identifier of the stash
50
+ #
51
+ # @!attribute [r] short_oid
52
+ # @return [String] the abbreviated object identifier (typically 7 characters)
53
+ #
54
+ # @!attribute [r] branch
55
+ # @return [String, nil] the branch name where the stash was created,
56
+ # or nil for custom stash messages
57
+ #
58
+ # @!attribute [r] message
59
+ # @return [String] the stash message (e.g., 'WIP on main: abc123 commit msg')
60
+ #
61
+ # @!attribute [r] author_name
62
+ # @return [String] the name of the stash author
63
+ #
64
+ # @!attribute [r] author_email
65
+ # @return [String] the email of the stash author
66
+ #
67
+ # @!attribute [r] author_date
68
+ # @return [String] the author date in ISO 8601 format
69
+ #
70
+ # @!attribute [r] committer_name
71
+ # @return [String] the name of the stash committer
72
+ #
73
+ # @!attribute [r] committer_email
74
+ # @return [String] the email of the stash committer
75
+ #
76
+ # @!attribute [r] committer_date
77
+ # @return [String] the committer date in ISO 8601 format
78
+ #
79
+ StashInfo = Data.define(
80
+ :index,
81
+ :name,
82
+ :oid,
83
+ :short_oid,
84
+ :branch,
85
+ :message,
86
+ :author_name,
87
+ :author_email,
88
+ :author_date,
89
+ :committer_name,
90
+ :committer_email,
91
+ :committer_date
92
+ ) do
93
+ # Returns the stash reference name
94
+ #
95
+ # @return [String] the stash name (e.g., 'stash@\\{0}')
96
+ #
97
+ # @example
98
+ # info.to_s # => 'stash@{0}'
99
+ #
100
+ def to_s
101
+ name
102
+ end
103
+ end
104
+ end
data/lib/git/stashes.rb CHANGED
@@ -1,56 +1,168 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Git
4
- # object that holds all the available stashes
4
+ # Collection of stash entries for a Git repository
5
+ #
6
+ # @example Iterate over stash entries
7
+ # git.stashes.each { |s| puts s.message }
8
+ #
9
+ # @example Check and apply a stash
10
+ # git.stashes.size #=> 2
11
+ # git.stashes.apply
12
+ #
13
+ # @api public
14
+ #
5
15
  class Stashes
6
16
  include Enumerable
7
17
 
18
+ # Initialize the stashes collection
19
+ #
20
+ # Loads all existing stash entries from the repository at construction time.
21
+ #
22
+ # @param base [Git::Repository] the git repository
23
+ #
24
+ # @return [void]
25
+ #
26
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
27
+ #
28
+ # @example Load stashes for a repository
29
+ # stashes = Git::Stashes.new(repo)
30
+ # stashes.size #=> 2
31
+ #
8
32
  def initialize(base)
9
33
  @stashes = []
10
-
11
34
  @base = base
12
35
 
13
- @base.lib.stashes_all.each do |indexed_message|
14
- _index, message = indexed_message
15
- @stashes.unshift(Git::Stash.new(@base, message, save: true))
36
+ stash_repository.stashes_all.each do |stash|
37
+ message = stash[1]
38
+ @stashes.unshift(Git::Stash.new(@base, message, existing: true))
16
39
  end
17
40
  end
18
41
 
42
+ # Returns all stash entries as an array of index and message pairs
43
+ #
44
+ # Entries are listed in oldest-first order matching {Git::Repository#stashes_all}.
45
+ #
46
+ # @example List all stash entries
47
+ # git.stashes.all #=> [[0, "testing-stash-all"], [1, "another-stash"]]
19
48
  #
20
- # Returns an multi-dimensional Array of elements that have been stash saved.
21
- # Array is based on position and name. See Example
49
+ # @return [Array<Array(Integer, String)>] array of `[index, message]` pairs where
50
+ # index 0 is the oldest stash
51
+ #
52
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
22
53
  #
23
- # @example Returns Array of items that have been stashed
24
- # .all - [0, "testing-stash-all"]]
25
- # @return [Array]
26
54
  def all
27
- @base.lib.stashes_all
55
+ stash_repository.stashes_all
28
56
  end
29
57
 
58
+ # Saves the current working-directory state to a new stash entry
59
+ #
60
+ # @param message [String] the stash message
61
+ #
62
+ # @return [void]
63
+ #
64
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
65
+ #
66
+ # @example Save current changes to the stash
67
+ # git.stashes.save('WIP: feature work')
68
+ # git.stashes.size #=> 1
69
+ #
30
70
  def save(message)
31
71
  s = Git::Stash.new(@base, message)
32
72
  @stashes.unshift(s) if s.saved?
33
73
  end
34
74
 
75
+ # Applies a stash entry to the working directory
76
+ #
77
+ # @param index [Integer, nil] the stash index to apply (default: latest)
78
+ #
79
+ # @return [String] the output from the git stash apply command
80
+ #
81
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
82
+ #
83
+ # @example Apply the most recent stash
84
+ # git.stashes.apply
85
+ #
86
+ # @example Apply a specific stash by index
87
+ # git.stashes.apply(1)
88
+ #
35
89
  def apply(index = nil)
36
- @base.lib.stash_apply(index)
90
+ stash_repository.stash_apply(index)
37
91
  end
38
92
 
93
+ # Removes all stash entries
94
+ #
95
+ # @return [void]
96
+ #
97
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
98
+ #
99
+ # @example Clear all stashes
100
+ # git.stashes.clear
101
+ # git.stashes.size #=> 0
102
+ #
39
103
  def clear
40
- @base.lib.stash_clear
104
+ stash_repository.stash_clear
41
105
  @stashes = []
106
+ nil
42
107
  end
43
108
 
109
+ # Returns the number of stash entries
110
+ #
111
+ # @return [Integer] the number of stashes
112
+ #
113
+ # @example Check how many stashes exist
114
+ # git.stashes.size #=> 2
115
+ #
44
116
  def size
45
117
  @stashes.size
46
118
  end
47
119
 
120
+ # Iterates over each stash entry in newest-first order
121
+ #
122
+ # @example Iterate over stashes
123
+ # git.stashes.each { |s| puts s.message }
124
+ #
125
+ # @overload each
126
+ #
127
+ # @return [Enumerator<Git::Stash>] an enumerator over stash entries
128
+ #
129
+ # @overload each(&block)
130
+ #
131
+ # @yield [stash] each stash entry
132
+ #
133
+ # @yieldparam stash [Git::Stash] the current stash entry
134
+ #
135
+ # @yieldreturn [void]
136
+ #
137
+ # @return [Array<Git::Stash>] the stash entries
138
+ #
48
139
  def each(&)
49
140
  @stashes.each(&)
50
141
  end
51
142
 
143
+ # Returns the stash entry at the given index
144
+ #
145
+ # Stashes are stored in newest-first order; index 0 is the most recent stash.
146
+ #
147
+ # @param index [Integer, #to_i] the stash index (0 = most recent)
148
+ #
149
+ # @return [Git::Stash, nil] the stash entry, or `nil` if the index is out of bounds
150
+ #
151
+ # @example Access the most recent stash
152
+ # git.stashes[0].message #=> "WIP: feature work"
153
+ #
52
154
  def [](index)
53
155
  @stashes[index.to_i]
54
156
  end
157
+
158
+ private
159
+
160
+ # Returns the facade interface for stash operations
161
+ #
162
+ # @return [Git::Repository]
163
+ #
164
+ def stash_repository
165
+ @base
166
+ end
55
167
  end
56
168
  end