git 4.3.2 → 5.0.0.beta.1

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 (280) hide show
  1. checksums.yaml +4 -4
  2. data/.github/copilot-instructions.md +67 -2705
  3. data/.github/pull_request_template.md +3 -1
  4. data/.github/skills/breaking-change-analysis/SKILL.md +102 -0
  5. data/.github/skills/ci-cd-troubleshooting/SKILL.md +264 -0
  6. data/.github/skills/command-implementation/REFERENCE.md +993 -0
  7. data/.github/skills/command-implementation/SKILL.md +229 -0
  8. data/.github/skills/command-test-conventions/SKILL.md +660 -0
  9. data/.github/skills/command-yard-documentation/SKILL.md +426 -0
  10. data/.github/skills/dependency-management/SKILL.md +72 -0
  11. data/.github/skills/development-workflow/SKILL.md +506 -0
  12. data/.github/skills/extract-command-from-lib/SKILL.md +487 -0
  13. data/.github/skills/extract-facade-from-base-lib/SKILL.md +586 -0
  14. data/.github/skills/facade-implementation/REFERENCE.md +840 -0
  15. data/.github/skills/facade-implementation/SKILL.md +260 -0
  16. data/.github/skills/facade-test-conventions/SKILL.md +380 -0
  17. data/.github/skills/facade-yard-documentation/SKILL.md +429 -0
  18. data/.github/skills/make-skill-template/SKILL.md +176 -0
  19. data/.github/skills/pr-readiness-review/SKILL.md +185 -0
  20. data/.github/skills/project-context/SKILL.md +313 -0
  21. data/.github/skills/pull-request-review/SKILL.md +168 -0
  22. data/.github/skills/refactor-command-to-commandlineresult/SKILL.md +131 -0
  23. data/.github/skills/release-management/SKILL.md +125 -0
  24. data/.github/skills/review-arguments-dsl/CHECKLIST.md +788 -0
  25. data/.github/skills/review-arguments-dsl/SKILL.md +214 -0
  26. data/.github/skills/review-backward-compatibility/SKILL.md +275 -0
  27. data/.github/skills/review-cross-command-consistency/SKILL.md +139 -0
  28. data/.github/skills/reviewing-skills/SKILL.md +189 -0
  29. data/.github/skills/rspec-unit-testing-standards/SKILL.md +639 -0
  30. data/.github/skills/tdd-refactor-step/SKILL.md +236 -0
  31. data/.github/skills/test-debugging/SKILL.md +160 -0
  32. data/.github/skills/yard-documentation/SKILL.md +793 -0
  33. data/.github/workflows/continuous_integration.yml +3 -2
  34. data/.github/workflows/enforce_conventional_commits.yml +1 -1
  35. data/.github/workflows/experimental_continuous_integration.yml +2 -2
  36. data/.github/workflows/release.yml +3 -4
  37. data/.gitignore +8 -0
  38. data/.husky/pre-commit +13 -0
  39. data/.release-please-manifest.json +1 -1
  40. data/.rspec +3 -0
  41. data/.rubocop.yml +7 -3
  42. data/.rubocop_todo.yml +23 -5
  43. data/.yardopts +1 -0
  44. data/CHANGELOG.md +0 -40
  45. data/CONTRIBUTING.md +694 -53
  46. data/README.md +17 -5
  47. data/Rakefile +61 -9
  48. data/commitlint.test +4 -0
  49. data/git.gemspec +14 -8
  50. data/lib/git/args_builder.rb +0 -8
  51. data/lib/git/base.rb +486 -410
  52. data/lib/git/branch.rb +380 -43
  53. data/lib/git/branch_delete_failure.rb +31 -0
  54. data/lib/git/branch_delete_result.rb +63 -0
  55. data/lib/git/branch_info.rb +178 -0
  56. data/lib/git/branches.rb +130 -24
  57. data/lib/git/command_line/base.rb +245 -0
  58. data/lib/git/command_line/capturing.rb +249 -0
  59. data/lib/git/command_line/result.rb +96 -0
  60. data/lib/git/command_line/streaming.rb +194 -0
  61. data/lib/git/command_line.rb +43 -322
  62. data/lib/git/command_line_result.rb +4 -88
  63. data/lib/git/commands/add.rb +131 -0
  64. data/lib/git/commands/am/abort.rb +43 -0
  65. data/lib/git/commands/am/apply.rb +252 -0
  66. data/lib/git/commands/am/continue.rb +43 -0
  67. data/lib/git/commands/am/quit.rb +43 -0
  68. data/lib/git/commands/am/retry.rb +47 -0
  69. data/lib/git/commands/am/show_current_patch.rb +64 -0
  70. data/lib/git/commands/am/skip.rb +42 -0
  71. data/lib/git/commands/am.rb +33 -0
  72. data/lib/git/commands/apply.rb +237 -0
  73. data/lib/git/commands/archive/list_formats.rb +46 -0
  74. data/lib/git/commands/archive.rb +140 -0
  75. data/lib/git/commands/arguments.rb +3510 -0
  76. data/lib/git/commands/base.rb +403 -0
  77. data/lib/git/commands/branch/copy.rb +94 -0
  78. data/lib/git/commands/branch/create.rb +173 -0
  79. data/lib/git/commands/branch/delete.rb +80 -0
  80. data/lib/git/commands/branch/list.rb +162 -0
  81. data/lib/git/commands/branch/move.rb +94 -0
  82. data/lib/git/commands/branch/set_upstream.rb +86 -0
  83. data/lib/git/commands/branch/show_current.rb +49 -0
  84. data/lib/git/commands/branch/unset_upstream.rb +57 -0
  85. data/lib/git/commands/branch.rb +34 -0
  86. data/lib/git/commands/cat_file/batch.rb +364 -0
  87. data/lib/git/commands/cat_file/filtered.rb +105 -0
  88. data/lib/git/commands/cat_file/raw.rb +210 -0
  89. data/lib/git/commands/cat_file.rb +49 -0
  90. data/lib/git/commands/checkout/branch.rb +151 -0
  91. data/lib/git/commands/checkout/files.rb +115 -0
  92. data/lib/git/commands/checkout.rb +38 -0
  93. data/lib/git/commands/checkout_index.rb +105 -0
  94. data/lib/git/commands/clean.rb +100 -0
  95. data/lib/git/commands/clone.rb +240 -0
  96. data/lib/git/commands/commit.rb +272 -0
  97. data/lib/git/commands/commit_tree.rb +100 -0
  98. data/lib/git/commands/config_option_syntax/add.rb +83 -0
  99. data/lib/git/commands/config_option_syntax/get.rb +117 -0
  100. data/lib/git/commands/config_option_syntax/get_all.rb +115 -0
  101. data/lib/git/commands/config_option_syntax/get_color.rb +91 -0
  102. data/lib/git/commands/config_option_syntax/get_color_bool.rb +93 -0
  103. data/lib/git/commands/config_option_syntax/get_regexp.rb +115 -0
  104. data/lib/git/commands/config_option_syntax/get_urlmatch.rb +102 -0
  105. data/lib/git/commands/config_option_syntax/list.rb +107 -0
  106. data/lib/git/commands/config_option_syntax/remove_section.rb +74 -0
  107. data/lib/git/commands/config_option_syntax/rename_section.rb +78 -0
  108. data/lib/git/commands/config_option_syntax/replace_all.rb +104 -0
  109. data/lib/git/commands/config_option_syntax/set.rb +114 -0
  110. data/lib/git/commands/config_option_syntax/unset.rb +89 -0
  111. data/lib/git/commands/config_option_syntax/unset_all.rb +89 -0
  112. data/lib/git/commands/config_option_syntax.rb +56 -0
  113. data/lib/git/commands/describe.rb +155 -0
  114. data/lib/git/commands/diff.rb +656 -0
  115. data/lib/git/commands/diff_files.rb +518 -0
  116. data/lib/git/commands/diff_index.rb +496 -0
  117. data/lib/git/commands/fetch.rb +352 -0
  118. data/lib/git/commands/fsck.rb +136 -0
  119. data/lib/git/commands/gc.rb +132 -0
  120. data/lib/git/commands/grep.rb +338 -0
  121. data/lib/git/commands/init.rb +99 -0
  122. data/lib/git/commands/log.rb +632 -0
  123. data/lib/git/commands/ls_files.rb +191 -0
  124. data/lib/git/commands/ls_remote.rb +155 -0
  125. data/lib/git/commands/ls_tree.rb +131 -0
  126. data/lib/git/commands/maintenance/register.rb +75 -0
  127. data/lib/git/commands/maintenance/run.rb +104 -0
  128. data/lib/git/commands/maintenance/start.rb +66 -0
  129. data/lib/git/commands/maintenance/stop.rb +55 -0
  130. data/lib/git/commands/maintenance/unregister.rb +79 -0
  131. data/lib/git/commands/maintenance.rb +31 -0
  132. data/lib/git/commands/merge/abort.rb +44 -0
  133. data/lib/git/commands/merge/continue.rb +44 -0
  134. data/lib/git/commands/merge/quit.rb +46 -0
  135. data/lib/git/commands/merge/start.rb +245 -0
  136. data/lib/git/commands/merge.rb +28 -0
  137. data/lib/git/commands/merge_base.rb +86 -0
  138. data/lib/git/commands/mv.rb +77 -0
  139. data/lib/git/commands/name_rev.rb +114 -0
  140. data/lib/git/commands/pull.rb +377 -0
  141. data/lib/git/commands/push.rb +246 -0
  142. data/lib/git/commands/read_tree.rb +149 -0
  143. data/lib/git/commands/remote/add.rb +91 -0
  144. data/lib/git/commands/remote/get_url.rb +66 -0
  145. data/lib/git/commands/remote/list.rb +54 -0
  146. data/lib/git/commands/remote/prune.rb +61 -0
  147. data/lib/git/commands/remote/remove.rb +52 -0
  148. data/lib/git/commands/remote/rename.rb +69 -0
  149. data/lib/git/commands/remote/set_branches.rb +63 -0
  150. data/lib/git/commands/remote/set_head.rb +82 -0
  151. data/lib/git/commands/remote/set_url.rb +71 -0
  152. data/lib/git/commands/remote/set_url_add.rb +61 -0
  153. data/lib/git/commands/remote/set_url_delete.rb +64 -0
  154. data/lib/git/commands/remote/show.rb +71 -0
  155. data/lib/git/commands/remote/update.rb +72 -0
  156. data/lib/git/commands/remote.rb +42 -0
  157. data/lib/git/commands/repack.rb +277 -0
  158. data/lib/git/commands/reset.rb +147 -0
  159. data/lib/git/commands/rev_parse.rb +297 -0
  160. data/lib/git/commands/revert/abort.rb +45 -0
  161. data/lib/git/commands/revert/continue.rb +57 -0
  162. data/lib/git/commands/revert/quit.rb +47 -0
  163. data/lib/git/commands/revert/skip.rb +44 -0
  164. data/lib/git/commands/revert/start.rb +153 -0
  165. data/lib/git/commands/revert.rb +29 -0
  166. data/lib/git/commands/rm.rb +114 -0
  167. data/lib/git/commands/show.rb +632 -0
  168. data/lib/git/commands/show_ref/exclude_existing.rb +120 -0
  169. data/lib/git/commands/show_ref/exists.rb +78 -0
  170. data/lib/git/commands/show_ref/list.rb +145 -0
  171. data/lib/git/commands/show_ref/verify.rb +120 -0
  172. data/lib/git/commands/show_ref.rb +42 -0
  173. data/lib/git/commands/stash/apply.rb +75 -0
  174. data/lib/git/commands/stash/branch.rb +65 -0
  175. data/lib/git/commands/stash/clear.rb +41 -0
  176. data/lib/git/commands/stash/create.rb +58 -0
  177. data/lib/git/commands/stash/drop.rb +67 -0
  178. data/lib/git/commands/stash/list.rb +39 -0
  179. data/lib/git/commands/stash/pop.rb +78 -0
  180. data/lib/git/commands/stash/push.rb +103 -0
  181. data/lib/git/commands/stash/show.rb +149 -0
  182. data/lib/git/commands/stash/store.rb +63 -0
  183. data/lib/git/commands/stash.rb +38 -0
  184. data/lib/git/commands/status.rb +169 -0
  185. data/lib/git/commands/symbolic_ref/delete.rb +68 -0
  186. data/lib/git/commands/symbolic_ref/read.rb +95 -0
  187. data/lib/git/commands/symbolic_ref/update.rb +76 -0
  188. data/lib/git/commands/symbolic_ref.rb +38 -0
  189. data/lib/git/commands/tag/create.rb +139 -0
  190. data/lib/git/commands/tag/delete.rb +55 -0
  191. data/lib/git/commands/tag/list.rb +143 -0
  192. data/lib/git/commands/tag/verify.rb +71 -0
  193. data/lib/git/commands/tag.rb +26 -0
  194. data/lib/git/commands/update_ref/batch.rb +140 -0
  195. data/lib/git/commands/update_ref/delete.rb +92 -0
  196. data/lib/git/commands/update_ref/update.rb +106 -0
  197. data/lib/git/commands/update_ref.rb +42 -0
  198. data/lib/git/commands/version.rb +52 -0
  199. data/lib/git/commands/worktree/add.rb +140 -0
  200. data/lib/git/commands/worktree/list.rb +64 -0
  201. data/lib/git/commands/worktree/lock.rb +58 -0
  202. data/lib/git/commands/worktree/management_base.rb +51 -0
  203. data/lib/git/commands/worktree/move.rb +66 -0
  204. data/lib/git/commands/worktree/prune.rb +67 -0
  205. data/lib/git/commands/worktree/remove.rb +63 -0
  206. data/lib/git/commands/worktree/repair.rb +76 -0
  207. data/lib/git/commands/worktree/unlock.rb +47 -0
  208. data/lib/git/commands/worktree.rb +43 -0
  209. data/lib/git/commands/write_tree.rb +68 -0
  210. data/lib/git/commands.rb +89 -0
  211. data/lib/git/detached_head_info.rb +54 -0
  212. data/lib/git/diff.rb +297 -7
  213. data/lib/git/diff_file_numstat_info.rb +29 -0
  214. data/lib/git/diff_file_patch_info.rb +134 -0
  215. data/lib/git/diff_file_raw_info.rb +127 -0
  216. data/lib/git/diff_info.rb +169 -0
  217. data/lib/git/diff_path_status.rb +78 -19
  218. data/lib/git/diff_result.rb +32 -0
  219. data/lib/git/diff_stats.rb +59 -14
  220. data/lib/git/dirstat_info.rb +86 -0
  221. data/lib/git/errors.rb +65 -2
  222. data/lib/git/execution_context/global.rb +56 -0
  223. data/lib/git/execution_context/repository.rb +147 -0
  224. data/lib/git/execution_context.rb +482 -0
  225. data/lib/git/file_ref.rb +74 -0
  226. data/lib/git/fsck_object.rb +9 -9
  227. data/lib/git/fsck_result.rb +1 -1
  228. data/lib/git/lib.rb +1606 -1028
  229. data/lib/git/log.rb +15 -2
  230. data/lib/git/object.rb +92 -22
  231. data/lib/git/parsers/branch.rb +224 -0
  232. data/lib/git/parsers/cat_file.rb +111 -0
  233. data/lib/git/parsers/diff.rb +585 -0
  234. data/lib/git/parsers/fsck.rb +133 -0
  235. data/lib/git/parsers/grep.rb +42 -0
  236. data/lib/git/parsers/ls_tree.rb +58 -0
  237. data/lib/git/parsers/stash.rb +208 -0
  238. data/lib/git/parsers/tag.rb +257 -0
  239. data/lib/git/remote.rb +133 -9
  240. data/lib/git/repository/branching.rb +572 -0
  241. data/lib/git/repository/committing.rb +191 -0
  242. data/lib/git/repository/configuring.rb +156 -0
  243. data/lib/git/repository/diffing.rb +775 -0
  244. data/lib/git/repository/inspecting.rb +153 -0
  245. data/lib/git/repository/logging.rb +247 -0
  246. data/lib/git/repository/merging.rb +295 -0
  247. data/lib/git/repository/object_operations.rb +1101 -0
  248. data/lib/git/repository/path_resolver.rb +207 -0
  249. data/lib/git/repository/remote_operations.rb +753 -0
  250. data/lib/git/repository/shared_private.rb +51 -0
  251. data/lib/git/repository/staging.rb +390 -0
  252. data/lib/git/repository/stashing.rb +107 -0
  253. data/lib/git/repository/status_operations.rb +180 -0
  254. data/lib/git/repository/worktree_operations.rb +159 -0
  255. data/lib/git/repository.rb +264 -1
  256. data/lib/git/stash.rb +85 -4
  257. data/lib/git/stash_info.rb +104 -0
  258. data/lib/git/stashes.rb +130 -13
  259. data/lib/git/status.rb +224 -18
  260. data/lib/git/tag_delete_failure.rb +31 -0
  261. data/lib/git/tag_delete_result.rb +63 -0
  262. data/lib/git/tag_info.rb +105 -0
  263. data/lib/git/version.rb +109 -2
  264. data/lib/git/version_constraint.rb +81 -0
  265. data/lib/git/worktree.rb +120 -5
  266. data/lib/git/worktrees.rb +107 -7
  267. data/lib/git.rb +114 -18
  268. data/redesign/1_architecture_existing.md +54 -18
  269. data/redesign/2_architecture_redesign.md +365 -46
  270. data/redesign/3_architecture_implementation.md +1451 -54
  271. data/tasks/gem_tasks.rake +4 -0
  272. data/tasks/npm_tasks.rake +7 -0
  273. data/tasks/rspec.rake +48 -0
  274. data/tasks/test.rake +13 -1
  275. data/tasks/yard.rake +34 -7
  276. metadata +349 -20
  277. data/lib/git/index.rb +0 -6
  278. data/lib/git/path.rb +0 -38
  279. data/lib/git/working_directory.rb +0 -6
  280. /data/{release-please-config.json → .release-please-config.json} +0 -0
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ # Represents a git version constraint with minimum and upper bound versions
5
+ #
6
+ # Used by {Git::Commands::Base.requires_git_version} to declare version requirements
7
+ # and by {Git::VersionError} to report constraint violations.
8
+ #
9
+ # @example Minimum version only
10
+ # constraint = Git::VersionConstraint.new(min: Git::Version.parse('2.30.0'))
11
+ # constraint.too_old?(Git::Version.parse('2.28.0')) #=> true
12
+ # constraint.too_new?(Git::Version.parse('2.28.0')) #=> false
13
+ #
14
+ # @example Upper bound only
15
+ # constraint = Git::VersionConstraint.new(before: Git::Version.parse('2.50.0'))
16
+ # constraint.too_old?(Git::Version.parse('2.51.0')) #=> false
17
+ # constraint.too_new?(Git::Version.parse('2.51.0')) #=> true
18
+ #
19
+ # @example Both bounds
20
+ # constraint = Git::VersionConstraint.new(
21
+ # min: Git::Version.parse('2.30.0'),
22
+ # before: Git::Version.parse('2.50.0')
23
+ # )
24
+ # constraint.satisfied_by?(Git::Version.parse('2.40.0')) #=> true
25
+ #
26
+ # @api public
27
+ #
28
+ VersionConstraint = Data.define(:min, :before) do
29
+ # @param min [Git::Version, nil] minimum version (inclusive)
30
+ # @param before [Git::Version, nil] upper bound version (exclusive)
31
+ def initialize(min: nil, before: nil)
32
+ super
33
+ end
34
+
35
+ # Check if the given version is too old (below the minimum)
36
+ #
37
+ # @param version [Git::Version] the version to check
38
+ #
39
+ # @return [Boolean] true if version is below the minimum, false otherwise
40
+ #
41
+ def too_old?(version)
42
+ return false unless min
43
+
44
+ version < min
45
+ end
46
+
47
+ # Check if the given version is too new (at or past the upper bound)
48
+ #
49
+ # @param version [Git::Version] the version to check
50
+ #
51
+ # @return [Boolean] true if version is at or past the upper bound, false otherwise
52
+ #
53
+ def too_new?(version)
54
+ return false unless before
55
+
56
+ version >= before
57
+ end
58
+
59
+ # Check if the given version satisfies this constraint
60
+ #
61
+ # @param version [Git::Version] the version to check
62
+ #
63
+ # @return [Boolean] true if the version satisfies the constraint
64
+ #
65
+ def satisfied_by?(version)
66
+ !too_old?(version) && !too_new?(version)
67
+ end
68
+
69
+ # Return a human-readable representation of this constraint
70
+ #
71
+ # @return [String] the constraint in git version range form
72
+ #
73
+ def to_s
74
+ return ">= #{min}, < #{before}" if min && before
75
+ return ">= #{min}" if min
76
+ return "< #{before}" if before
77
+
78
+ 'any version'
79
+ end
80
+ end
81
+ end
data/lib/git/worktree.rb CHANGED
@@ -1,12 +1,52 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'git/path'
3
+ require 'git/base'
4
4
 
5
5
  module Git
6
6
  # A worktree in a Git repository
7
+ #
8
+ # Represents a single linked or main worktree. Constructed by
9
+ # {Git::Repository::WorktreeOperations#worktree} or populated by
10
+ # {Git::Worktrees}.
11
+ #
12
+ # Accepts either a {Git::Repository} (new form) or a {Git::Base} (legacy form)
13
+ # as the `base` argument. The `is_a?(Git::Base)` guard routes git operations
14
+ # through the facade repository and will be removed when {Git::Base} is
15
+ # deleted in Phase 4.
16
+ #
17
+ # @example Add and remove a linked worktree
18
+ # worktree = repo.worktree('/path/to/new-worktree')
19
+ # worktree.add
20
+ # worktree.remove
21
+ #
22
+ # @api public
23
+ #
7
24
  class Worktree
8
- attr_accessor :full, :dir
25
+ # Full worktree descriptor including the optional commitish
26
+ #
27
+ # @return [String] the filesystem path, space-separated with the commitish
28
+ # when one was given at construction time
29
+ #
30
+ attr_accessor :full
9
31
 
32
+ # Filesystem path of this worktree
33
+ #
34
+ # @return [String] the filesystem path of the worktree directory
35
+ #
36
+ attr_accessor :dir
37
+
38
+ # Creates a new Worktree object
39
+ #
40
+ # @param base [Git::Base, Git::Repository] the repository that owns this
41
+ # worktree
42
+ #
43
+ # @param dir [String] filesystem path of the worktree
44
+ #
45
+ # @param gcommit [String, nil] commitish associated with the worktree;
46
+ # when non-nil it is appended to {#full}
47
+ #
48
+ # @return [void]
49
+ #
10
50
  def initialize(base, dir, gcommit = nil)
11
51
  @full = dir
12
52
  @full += " #{gcommit}" unless gcommit.nil?
@@ -15,25 +55,100 @@ module Git
15
55
  @gcommit = gcommit
16
56
  end
17
57
 
58
+ # Returns the commit (or commitish string) associated with this worktree
59
+ #
60
+ # When a commitish string was supplied at construction time (e.g. by
61
+ # {Git::Worktrees} which passes the raw SHA from `git worktree list`), that
62
+ # string is returned as-is. Otherwise the value is lazily resolved on first
63
+ # call via `worktree_repository.gcommit(@full)` and the result is memoized.
64
+ #
65
+ # @example When resolved lazily (no commitish at construction)
66
+ # worktree = repo.worktree('/path/to/wt')
67
+ # worktree.gcommit # => #<Git::Object::Commit ...>
68
+ #
69
+ # @example When the commitish was given at construction
70
+ # worktree = repo.worktrees['/path/to/wt']
71
+ # worktree.gcommit # => "4bef5ab8c9..." (raw SHA string)
72
+ #
73
+ # @return [Git::Object::Commit, String] a commit object when lazily
74
+ # resolved, or the raw commitish string when pre-set at construction
75
+ #
76
+ # @raise [Git::FailedError] if git must resolve the commit and exits with a
77
+ # non-zero exit status
78
+ #
18
79
  def gcommit
19
- @gcommit ||= @base.gcommit(@full)
80
+ @gcommit ||= worktree_repository.gcommit(@full)
20
81
  @gcommit
21
82
  end
22
83
 
84
+ # Creates this worktree on disk
85
+ #
86
+ # Runs `git worktree add` for {#dir}, optionally at the commitish passed
87
+ # at construction time.
88
+ #
89
+ # @example Add a worktree
90
+ # worktree = repo.worktree('/path/to/new-worktree')
91
+ # worktree.add
92
+ #
93
+ # @return [String] stdout from the git command
94
+ #
95
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
96
+ #
23
97
  def add
24
- @base.lib.worktree_add(@dir, @gcommit)
98
+ worktree_repository.worktree_add(@dir, @gcommit)
25
99
  end
26
100
 
101
+ # Removes this worktree from disk
102
+ #
103
+ # Runs `git worktree remove` for {#dir}.
104
+ #
105
+ # @example Remove a worktree
106
+ # worktree.remove
107
+ #
108
+ # @return [String] stdout from the git command (typically empty)
109
+ #
110
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
111
+ #
27
112
  def remove
28
- @base.lib.worktree_remove(@dir)
113
+ worktree_repository.worktree_remove(@dir)
29
114
  end
30
115
 
116
+ # Returns an array containing the full worktree descriptor
117
+ #
118
+ # @example Get the descriptor array
119
+ # worktree.to_a # => ["/path/to/worktree"]
120
+ #
121
+ # @return [Array<String>] array containing the full worktree descriptor
122
+ #
31
123
  def to_a
32
124
  [@full]
33
125
  end
34
126
 
127
+ # Returns the full worktree descriptor as a string
128
+ #
129
+ # @example Get the descriptor string
130
+ # worktree.to_s # => "/path/to/worktree"
131
+ #
132
+ # @return [String] the full worktree descriptor (path and optional commitish)
133
+ #
35
134
  def to_s
36
135
  @full
37
136
  end
137
+
138
+ private
139
+
140
+ # Resolves the {Git::Repository} for worktree operations
141
+ #
142
+ # Accepts either a {Git::Repository} (new form) or a {Git::Base} (legacy).
143
+ # The `is_a?(Git::Base)` guard will be removed when {Git::Base} is deleted
144
+ # in Phase 4.
145
+ #
146
+ # @return [Git::Repository] the repository used for worktree operations
147
+ #
148
+ # @api private
149
+ #
150
+ def worktree_repository
151
+ @base.is_a?(Git::Base) ? @base.facade_repository : @base
152
+ end
38
153
  end
39
154
  end
data/lib/git/worktrees.rb CHANGED
@@ -1,47 +1,147 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'git/base'
4
+
3
5
  module Git
4
- # object that holds all the available worktrees
6
+ # Collection of all Git worktrees in a repository
7
+ #
8
+ # Wraps every linked and main worktree and provides enumeration and
9
+ # path-based lookup.
10
+ #
11
+ # Accepts either a {Git::Repository} (new form) or a {Git::Base} (legacy
12
+ # form) as the `base` argument. The `is_a?(Git::Base)` guard routes git
13
+ # operations through the facade repository and will be removed when
14
+ # {Git::Base} is deleted in Phase 4.
15
+ #
16
+ # @example Enumerate all worktrees
17
+ # worktrees = repo.worktrees
18
+ # worktrees.each { |wt| puts wt.dir }
19
+ #
20
+ # @api public
21
+ #
5
22
  class Worktrees
6
23
  include Enumerable
7
24
 
25
+ # Creates a new Worktrees collection populated from the given repository
26
+ #
27
+ # @param base [Git::Base, Git::Repository] the repository to enumerate
28
+ # worktrees from
29
+ #
30
+ # @return [void]
31
+ #
32
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
33
+ #
8
34
  def initialize(base)
9
35
  @worktrees = {}
10
36
 
11
37
  @base = base
12
38
 
13
- # Array contains [dir, git_hash]
14
- @base.lib.worktrees_all.each do |w|
39
+ worktree_repository.worktrees_all.each do |w|
15
40
  @worktrees[w[0]] = Git::Worktree.new(@base, w[0], w[1])
16
41
  end
17
42
  end
18
43
 
19
- # array like methods
20
-
44
+ # Returns the number of worktrees in the collection
45
+ #
46
+ # @example Count all worktrees
47
+ # repo.worktrees.size # => 2
48
+ #
49
+ # @return [Integer] the total number of worktrees
50
+ #
21
51
  def size
22
52
  @worktrees.size
23
53
  end
24
54
 
55
+ # Iterates over every worktree in the collection
56
+ #
57
+ # @overload each
58
+ #
59
+ # @example Get an enumerator over all worktrees
60
+ # enum = repo.worktrees.each
61
+ #
62
+ # @return [Enumerator<Git::Worktree>] an enumerator over all worktrees
63
+ #
64
+ # @overload each(&block)
65
+ #
66
+ # @example Print every worktree path
67
+ # repo.worktrees.each { |wt| puts wt.dir }
68
+ #
69
+ # @return [Array<Git::Worktree>] the full list of worktrees
70
+ #
71
+ # @yield [worktree] passes each worktree to the block
72
+ #
73
+ # @yieldparam worktree [Git::Worktree] a worktree in the repository
74
+ #
75
+ # @yieldreturn [void]
76
+ #
25
77
  def each(&)
26
78
  @worktrees.values.each(&)
27
79
  end
28
80
 
81
+ # Returns the worktree with the given path
82
+ #
83
+ # Supports lookup by the filesystem path of the worktree directory or by
84
+ # the full worktree descriptor (path and optional commitish).
85
+ #
86
+ # @example Look up a worktree by path
87
+ # repo.worktrees['/path/to/linked-worktree']
88
+ #
89
+ # @param worktree_name [#to_s] the path (or full descriptor) of the
90
+ # worktree to retrieve
91
+ #
92
+ # @return [Git::Worktree, nil] the matching worktree, or `nil` if not found
93
+ #
29
94
  def [](worktree_name)
30
95
  @worktrees.values.each_with_object(@worktrees) do |worktree, worktrees|
31
96
  worktrees[worktree.full] ||= worktree
32
97
  end[worktree_name.to_s]
33
98
  end
34
99
 
100
+ # Returns a string listing all worktrees, one per line
101
+ #
102
+ # @example Display all worktrees
103
+ # puts repo.worktrees.to_s
104
+ #
105
+ # @return [String] a newline-separated listing of worktree descriptors
106
+ #
35
107
  def to_s
36
- out = ''
108
+ out = +''
37
109
  @worktrees.each_value do |b|
38
110
  out << b.to_s << "\n"
39
111
  end
40
112
  out
41
113
  end
42
114
 
115
+ # Removes stale administrative files for worktrees that no longer exist
116
+ #
117
+ # Runs `git worktree prune` to clean up any lingering worktree metadata
118
+ # for linked worktrees whose directories have been deleted.
119
+ #
120
+ # @example Prune stale worktree metadata
121
+ # repo.worktrees.prune
122
+ #
123
+ # @return [String] stdout from the git command (typically empty)
124
+ #
125
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
126
+ #
43
127
  def prune
44
- @base.lib.worktree_prune
128
+ worktree_repository.worktree_prune
129
+ end
130
+
131
+ private
132
+
133
+ # Resolves the {Git::Repository} for this collection of worktrees
134
+ #
135
+ # Accepts either a {Git::Repository} (new form) or a {Git::Base} (legacy).
136
+ # The `is_a?(Git::Base)` guard will be removed when {Git::Base} is deleted
137
+ # in Phase 4.
138
+ #
139
+ # @return [Git::Repository] the repository used to enumerate worktrees
140
+ #
141
+ # @api private
142
+ #
143
+ def worktree_repository
144
+ @base.is_a?(Git::Base) ? @base.facade_repository : @base
45
145
  end
46
146
  end
47
147
  end
data/lib/git.rb CHANGED
@@ -3,36 +3,61 @@
3
3
  require 'active_support'
4
4
  require 'active_support/deprecation'
5
5
 
6
+ require 'git/version'
7
+
6
8
  module Git
7
9
  Deprecation = ActiveSupport::Deprecation.new('5.0.0', 'Git')
10
+
11
+ # Minimum git version required by this gem
12
+ #
13
+ # Commands and features may require newer versions, but this is the absolute
14
+ # minimum supported version for the gem as a whole.
15
+ #
16
+ # @return [Git::Version]
17
+ #
18
+ # @api public
19
+ #
20
+ MINIMUM_GIT_VERSION = Version.parse('2.28.0')
8
21
  end
9
22
 
10
23
  require 'git/author'
11
- require 'git/base'
12
24
  require 'git/branch'
25
+ require 'git/branch_info'
13
26
  require 'git/branches'
14
27
  require 'git/command_line_result'
15
28
  require 'git/command_line'
29
+ require 'git/commands/init'
16
30
  require 'git/config'
17
31
  require 'git/diff'
32
+ require 'git/diff_file_numstat_info'
33
+ require 'git/diff_file_patch_info'
34
+ require 'git/diff_file_raw_info'
35
+ require 'git/diff_info'
36
+ require 'git/parsers/diff'
37
+ require 'git/diff_result'
38
+ require 'git/dirstat_info'
18
39
  require 'git/encoding_utils'
19
40
  require 'git/errors'
20
41
  require 'git/escaped_path'
42
+ require 'git/execution_context'
43
+ require 'git/file_ref'
21
44
  require 'git/fsck_object'
22
45
  require 'git/fsck_result'
23
- require 'git/index'
46
+ require 'git/version_constraint'
24
47
  require 'git/lib'
25
48
  require 'git/log'
26
49
  require 'git/object'
27
- require 'git/path'
28
50
  require 'git/remote'
29
51
  require 'git/repository'
52
+ require 'git/base'
30
53
  require 'git/status'
31
54
  require 'git/stash'
55
+ require 'git/stash_info'
32
56
  require 'git/stashes'
57
+ require 'git/tag_delete_failure'
58
+ require 'git/tag_delete_result'
59
+ require 'git/tag_info'
33
60
  require 'git/url'
34
- require 'git/version'
35
- require 'git/working_directory'
36
61
  require 'git/worktree'
37
62
  require 'git/worktrees'
38
63
 
@@ -43,7 +68,7 @@ require 'git/worktrees'
43
68
  #
44
69
  # @author Scott Chacon (mailto:schacon@gmail.com)
45
70
  #
46
- module Git # rubocop:disable Style/OneClassPerFile
71
+ module Git
47
72
  # g.config('user.name', 'Scott Chacon') # sets value
48
73
  # g.config('user.email', 'email@email.com') # sets value
49
74
  # g.config('user.name') # returns 'Scott Chacon'
@@ -123,9 +148,9 @@ module Git # rubocop:disable Style/OneClassPerFile
123
148
  #
124
149
  # @param directory [Pathname, nil] The directory to clone into
125
150
  #
126
- # If `directory` is a relative directory it is relative to the `path` option if
127
- # given. If `path` is not given, `directory` is relative to the current working
128
- # directory.
151
+ # If `directory` is a relative path it is relative to the `:chdir` option if
152
+ # given. If `:chdir` is not given, `directory` is relative to the current
153
+ # working directory.
129
154
  #
130
155
  # If `nil`, `directory` will be set to the basename of the last component of
131
156
  # the path from the `repository_url`. For example, for the URL:
@@ -176,9 +201,14 @@ module Git # rubocop:disable Style/OneClassPerFile
176
201
  # @option options [String] :origin Use the value instead `origin` to track
177
202
  # the upstream repository.
178
203
  #
179
- # @option options [Pathname] :path The directory to clone into. May be used
180
- # as an alternative to the `directory` parameter. If specified, the
181
- # `path` option is used instead of the `directory` parameter.
204
+ # @option options [Pathname] :chdir Run `git clone` from within this directory.
205
+ #
206
+ # The `directory` parameter (or the repository basename when `directory` is nil)
207
+ # is resolved relative to `:chdir`, just as if you had `cd`'d into it before
208
+ # running `git clone`. The returned path is the join of `:chdir` and the
209
+ # cloned directory path.
210
+ #
211
+ # @option options [Pathname] :path Deprecated — use `:chdir` instead.
182
212
  #
183
213
  # @option options [Boolean] :recursive After the clone is created, initialize
184
214
  # all submodules within, using their default settings.
@@ -191,8 +221,10 @@ module Git # rubocop:disable Style/OneClassPerFile
191
221
  #
192
222
  # @example Clone into a different directory `my-ruby-git`
193
223
  # git = Git.clone('https://github.com/ruby-git/ruby-git.git', 'my-ruby-git')
194
- # # or:
195
- # git = Git.clone('https://github.com/ruby-git/ruby-git.git', path: 'my-ruby-git')
224
+ #
225
+ # @example Clone into a specific parent directory
226
+ # git = Git.clone('https://github.com/ruby-git/ruby-git.git', chdir: '/path/to/projects')
227
+ # # clones into /path/to/projects/ruby-git
196
228
  #
197
229
  # @example Create a bare repository in the directory `ruby-git.git`
198
230
  # git = Git.clone('https://github.com/ruby-git/ruby-git.git', bare: true)
@@ -220,8 +252,6 @@ module Git # rubocop:disable Style/OneClassPerFile
220
252
  # of the cloned local working copy or cloned repository.
221
253
  #
222
254
  def self.clone(repository_url, directory = nil, options = {})
223
- clone_to_options = options.slice(:bare, :mirror)
224
- directory ||= Git::URL.clone_to(repository_url, **clone_to_options)
225
255
  Base.clone(repository_url, directory, options)
226
256
  end
227
257
 
@@ -329,6 +359,8 @@ module Git # rubocop:disable Style/OneClassPerFile
329
359
  # and converted to an absolute path using
330
360
  # [File.expand_path](https://www.rubydoc.info/stdlib/core/File.expand_path).
331
361
  #
362
+ # @option options [Pathname] :separate_git_dir Alias for `:repository`.
363
+ #
332
364
  # @option options [String, nil] :git_ssh An optional custom SSH command
333
365
  #
334
366
  # - If not specified, uses the global config (Git.configure { |c| c.git_ssh = ... }).
@@ -357,7 +389,30 @@ module Git # rubocop:disable Style/OneClassPerFile
357
389
  # @see https://git-scm.com/docs/git-init git init
358
390
  #
359
391
  def self.init(directory = '.', options = {})
360
- Base.init(directory, options)
392
+ require_relative 'git/commands/init'
393
+
394
+ options = options.dup
395
+ options[:repository] ||= options.delete(:separate_git_dir)
396
+ init_opts = options.slice(:bare, :initial_branch)
397
+ init_opts[:separate_git_dir] = options[:repository] if options.key?(:repository)
398
+ Git::Commands::Init.new(Git::Lib.new(nil, options[:log])).call(directory, **init_opts)
399
+
400
+ open_initialized_repository(directory, options)
401
+ end
402
+
403
+ # Open the repository after initialization
404
+ #
405
+ # @param directory [String] the directory containing the repository
406
+ # @param options [Hash] the options hash
407
+ # @return [Git::Base] the opened repository
408
+ # @api private
409
+ #
410
+ private_class_method def self.open_initialized_repository(directory, options)
411
+ if options[:bare]
412
+ Git.bare(options[:repository] || directory, options.slice(:log, :git_ssh).compact)
413
+ else
414
+ Git.open(directory, options.slice(:log, :git_ssh, :index, :repository).compact)
415
+ end
361
416
  end
362
417
 
363
418
  # returns a Hash containing information about the references
@@ -426,6 +481,37 @@ module Git # rubocop:disable Style/OneClassPerFile
426
481
  Base.open(working_dir, options)
427
482
  end
428
483
 
484
+ # Return the version of a git binary as a {Git::Version}
485
+ #
486
+ # @param binary_path [String, nil] path to the git binary; defaults to
487
+ # `Git::Base.config.binary_path`
488
+ #
489
+ # @return [Git::Version] the parsed git version
490
+ #
491
+ # @raise [Git::UnexpectedResultError] if the version output cannot be parsed
492
+ #
493
+ # @raise [Git::FailedError] if the git binary exits with a non-zero status
494
+ #
495
+ # @raise [Git::Error] if the binary is not found or fails to launch
496
+ #
497
+ # @example Default binary
498
+ # Git.git_version #=> #<Git::Version 2.42.0>
499
+ #
500
+ # @example Explicit binary path
501
+ # Git.git_version('/opt/homebrew/bin/git') #=> #<Git::Version 2.42.0>
502
+ #
503
+ def self.git_version(binary_path = nil)
504
+ path = binary_path || Git::Base.config.binary_path
505
+ Git::Lib.cached_git_version(path) { run_git_version(path) }
506
+ end
507
+
508
+ # @api private
509
+ def self.run_git_version(path)
510
+ output = Git::Commands::Version.new(Git::ExecutionContext::Global.new(binary_path: path)).call.stdout
511
+ Git::Version.parse(output)
512
+ end
513
+ private_class_method :run_git_version
514
+
429
515
  # Return the version of the git binary
430
516
  #
431
517
  # @example
@@ -433,7 +519,17 @@ module Git # rubocop:disable Style/OneClassPerFile
433
519
  #
434
520
  # @return [Array<Integer>] the version of the git binary
435
521
  #
522
+ # @deprecated Use {Git.git_version} instead, which returns a {Git::Version} (not an Array).
523
+ # For the legacy array shape, call: `Git.git_version.to_a`.
524
+ # The optional binary_path argument is preserved: `Git.git_version(binary_path)`.
525
+ #
436
526
  def self.binary_version(binary_path = Git::Base.config.binary_path)
437
- Base.binary_version(binary_path)
527
+ Git::Deprecation.warn(
528
+ 'Git.binary_version is deprecated and will be removed in 6.0. ' \
529
+ 'Use Git.git_version instead, which returns a Git::Version ' \
530
+ '(not an Array). For the legacy array shape, call: Git.git_version.to_a. ' \
531
+ 'The optional binary_path argument is preserved: Git.git_version(binary_path).'
532
+ )
533
+ git_version(binary_path).to_a
438
534
  end
439
535
  end