git 1.19.1 → 5.5.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 (265) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +5 -1
  3. data/AI_POLICY.md +24 -0
  4. data/CHANGELOG.md +689 -0
  5. data/CODE_OF_CONDUCT.md +25 -0
  6. data/CONTRIBUTING.md +1175 -97
  7. data/GOVERNANCE.md +106 -0
  8. data/LICENSE +1 -1
  9. data/MAINTAINERS.md +17 -4
  10. data/README.md +476 -320
  11. data/UPGRADING.md +1138 -0
  12. data/git.gemspec +124 -36
  13. data/lib/git/author.rb +39 -7
  14. data/lib/git/author_info.rb +66 -0
  15. data/lib/git/branch.rb +615 -65
  16. data/lib/git/branch_delete_failure.rb +34 -0
  17. data/lib/git/branch_delete_result.rb +66 -0
  18. data/lib/git/branch_info.rb +237 -0
  19. data/lib/git/branches.rb +167 -44
  20. data/lib/git/command_line/base.rb +247 -0
  21. data/lib/git/command_line/capturing.rb +308 -0
  22. data/lib/git/command_line/result.rb +88 -0
  23. data/lib/git/command_line/streaming.rb +236 -0
  24. data/lib/git/command_line.rb +52 -0
  25. data/lib/git/commands/add.rb +139 -0
  26. data/lib/git/commands/am/abort.rb +43 -0
  27. data/lib/git/commands/am/apply.rb +263 -0
  28. data/lib/git/commands/am/continue.rb +43 -0
  29. data/lib/git/commands/am/quit.rb +43 -0
  30. data/lib/git/commands/am/retry.rb +49 -0
  31. data/lib/git/commands/am/show_current_patch.rb +64 -0
  32. data/lib/git/commands/am/skip.rb +42 -0
  33. data/lib/git/commands/am.rb +33 -0
  34. data/lib/git/commands/apply.rb +242 -0
  35. data/lib/git/commands/archive/list_formats.rb +46 -0
  36. data/lib/git/commands/archive.rb +145 -0
  37. data/lib/git/commands/arguments.rb +4521 -0
  38. data/lib/git/commands/base.rb +502 -0
  39. data/lib/git/commands/branch/copy.rb +102 -0
  40. data/lib/git/commands/branch/create.rb +177 -0
  41. data/lib/git/commands/branch/delete.rb +88 -0
  42. data/lib/git/commands/branch/list.rb +178 -0
  43. data/lib/git/commands/branch/move.rb +102 -0
  44. data/lib/git/commands/branch/set_upstream.rb +86 -0
  45. data/lib/git/commands/branch/show_current.rb +49 -0
  46. data/lib/git/commands/branch/unset_upstream.rb +53 -0
  47. data/lib/git/commands/branch.rb +34 -0
  48. data/lib/git/commands/cat_file/batch.rb +385 -0
  49. data/lib/git/commands/cat_file/filtered.rb +105 -0
  50. data/lib/git/commands/cat_file/raw.rb +271 -0
  51. data/lib/git/commands/cat_file.rb +49 -0
  52. data/lib/git/commands/checkout/branch.rb +153 -0
  53. data/lib/git/commands/checkout/files.rb +116 -0
  54. data/lib/git/commands/checkout.rb +38 -0
  55. data/lib/git/commands/checkout_index.rb +106 -0
  56. data/lib/git/commands/clean.rb +102 -0
  57. data/lib/git/commands/clone.rb +241 -0
  58. data/lib/git/commands/commit.rb +273 -0
  59. data/lib/git/commands/commit_tree.rb +101 -0
  60. data/lib/git/commands/config_option_syntax/add.rb +86 -0
  61. data/lib/git/commands/config_option_syntax/get.rb +121 -0
  62. data/lib/git/commands/config_option_syntax/get_all.rb +118 -0
  63. data/lib/git/commands/config_option_syntax/get_color.rb +95 -0
  64. data/lib/git/commands/config_option_syntax/get_color_bool.rb +96 -0
  65. data/lib/git/commands/config_option_syntax/get_regexp.rb +119 -0
  66. data/lib/git/commands/config_option_syntax/get_urlmatch.rb +111 -0
  67. data/lib/git/commands/config_option_syntax/list.rb +111 -0
  68. data/lib/git/commands/config_option_syntax/remove_section.rb +79 -0
  69. data/lib/git/commands/config_option_syntax/rename_section.rb +83 -0
  70. data/lib/git/commands/config_option_syntax/replace_all.rb +109 -0
  71. data/lib/git/commands/config_option_syntax/set.rb +119 -0
  72. data/lib/git/commands/config_option_syntax/unset.rb +92 -0
  73. data/lib/git/commands/config_option_syntax/unset_all.rb +94 -0
  74. data/lib/git/commands/config_option_syntax.rb +56 -0
  75. data/lib/git/commands/describe.rb +156 -0
  76. data/lib/git/commands/diff.rb +657 -0
  77. data/lib/git/commands/diff_files.rb +519 -0
  78. data/lib/git/commands/diff_index.rb +499 -0
  79. data/lib/git/commands/fetch.rb +354 -0
  80. data/lib/git/commands/fsck.rb +138 -0
  81. data/lib/git/commands/gc.rb +134 -0
  82. data/lib/git/commands/grep.rb +339 -0
  83. data/lib/git/commands/init.rb +101 -0
  84. data/lib/git/commands/log.rb +634 -0
  85. data/lib/git/commands/ls_files.rb +195 -0
  86. data/lib/git/commands/ls_remote.rb +161 -0
  87. data/lib/git/commands/ls_tree.rb +135 -0
  88. data/lib/git/commands/maintenance/register.rb +77 -0
  89. data/lib/git/commands/maintenance/run.rb +109 -0
  90. data/lib/git/commands/maintenance/start.rb +71 -0
  91. data/lib/git/commands/maintenance/stop.rb +60 -0
  92. data/lib/git/commands/maintenance/unregister.rb +84 -0
  93. data/lib/git/commands/maintenance.rb +31 -0
  94. data/lib/git/commands/merge/abort.rb +44 -0
  95. data/lib/git/commands/merge/continue.rb +44 -0
  96. data/lib/git/commands/merge/quit.rb +46 -0
  97. data/lib/git/commands/merge/start.rb +250 -0
  98. data/lib/git/commands/merge.rb +28 -0
  99. data/lib/git/commands/merge_base.rb +91 -0
  100. data/lib/git/commands/mv.rb +82 -0
  101. data/lib/git/commands/name_rev.rb +119 -0
  102. data/lib/git/commands/pull.rb +382 -0
  103. data/lib/git/commands/push.rb +251 -0
  104. data/lib/git/commands/read_tree.rb +154 -0
  105. data/lib/git/commands/remote/add.rb +96 -0
  106. data/lib/git/commands/remote/get_url.rb +68 -0
  107. data/lib/git/commands/remote/list.rb +56 -0
  108. data/lib/git/commands/remote/prune.rb +63 -0
  109. data/lib/git/commands/remote/remove.rb +52 -0
  110. data/lib/git/commands/remote/rename.rb +76 -0
  111. data/lib/git/commands/remote/set_branches.rb +70 -0
  112. data/lib/git/commands/remote/set_head.rb +89 -0
  113. data/lib/git/commands/remote/set_url.rb +78 -0
  114. data/lib/git/commands/remote/set_url_add.rb +70 -0
  115. data/lib/git/commands/remote/set_url_delete.rb +71 -0
  116. data/lib/git/commands/remote/show.rb +77 -0
  117. data/lib/git/commands/remote/update.rb +79 -0
  118. data/lib/git/commands/remote.rb +42 -0
  119. data/lib/git/commands/repack.rb +281 -0
  120. data/lib/git/commands/reset.rb +154 -0
  121. data/lib/git/commands/rev_parse.rb +304 -0
  122. data/lib/git/commands/revert/abort.rb +45 -0
  123. data/lib/git/commands/revert/continue.rb +62 -0
  124. data/lib/git/commands/revert/quit.rb +47 -0
  125. data/lib/git/commands/revert/skip.rb +44 -0
  126. data/lib/git/commands/revert/start.rb +158 -0
  127. data/lib/git/commands/revert.rb +29 -0
  128. data/lib/git/commands/rm.rb +113 -0
  129. data/lib/git/commands/show.rb +632 -0
  130. data/lib/git/commands/show_ref/exclude_existing.rb +119 -0
  131. data/lib/git/commands/show_ref/exists.rb +80 -0
  132. data/lib/git/commands/show_ref/list.rb +149 -0
  133. data/lib/git/commands/show_ref/verify.rb +122 -0
  134. data/lib/git/commands/show_ref.rb +42 -0
  135. data/lib/git/commands/stash/apply.rb +81 -0
  136. data/lib/git/commands/stash/branch.rb +67 -0
  137. data/lib/git/commands/stash/clear.rb +43 -0
  138. data/lib/git/commands/stash/create.rb +60 -0
  139. data/lib/git/commands/stash/drop.rb +73 -0
  140. data/lib/git/commands/stash/list.rb +43 -0
  141. data/lib/git/commands/stash/pop.rb +87 -0
  142. data/lib/git/commands/stash/push.rb +112 -0
  143. data/lib/git/commands/stash/show.rb +158 -0
  144. data/lib/git/commands/stash/store.rb +72 -0
  145. data/lib/git/commands/stash.rb +38 -0
  146. data/lib/git/commands/status.rb +174 -0
  147. data/lib/git/commands/symbolic_ref/delete.rb +72 -0
  148. data/lib/git/commands/symbolic_ref/read.rb +99 -0
  149. data/lib/git/commands/symbolic_ref/update.rb +79 -0
  150. data/lib/git/commands/symbolic_ref.rb +38 -0
  151. data/lib/git/commands/tag/create.rb +142 -0
  152. data/lib/git/commands/tag/delete.rb +57 -0
  153. data/lib/git/commands/tag/list.rb +146 -0
  154. data/lib/git/commands/tag/verify.rb +71 -0
  155. data/lib/git/commands/tag.rb +26 -0
  156. data/lib/git/commands/update_ref/batch.rb +145 -0
  157. data/lib/git/commands/update_ref/delete.rb +90 -0
  158. data/lib/git/commands/update_ref/update.rb +103 -0
  159. data/lib/git/commands/update_ref.rb +42 -0
  160. data/lib/git/commands/version.rb +60 -0
  161. data/lib/git/commands/worktree/add.rb +139 -0
  162. data/lib/git/commands/worktree/list.rb +64 -0
  163. data/lib/git/commands/worktree/lock.rb +58 -0
  164. data/lib/git/commands/worktree/management_base.rb +51 -0
  165. data/lib/git/commands/worktree/move.rb +66 -0
  166. data/lib/git/commands/worktree/prune.rb +67 -0
  167. data/lib/git/commands/worktree/remove.rb +63 -0
  168. data/lib/git/commands/worktree/repair.rb +76 -0
  169. data/lib/git/commands/worktree/unlock.rb +47 -0
  170. data/lib/git/commands/worktree.rb +43 -0
  171. data/lib/git/commands/write_tree.rb +68 -0
  172. data/lib/git/commands.rb +88 -0
  173. data/lib/git/config.rb +72 -5
  174. data/lib/git/config_entry_info.rb +106 -0
  175. data/lib/git/configuring.rb +795 -0
  176. data/lib/git/detached_head_info.rb +57 -0
  177. data/lib/git/diff.rb +437 -86
  178. data/lib/git/diff_file_numstat_info.rb +31 -0
  179. data/lib/git/diff_file_patch_info.rb +136 -0
  180. data/lib/git/diff_file_raw_info.rb +129 -0
  181. data/lib/git/diff_info.rb +162 -0
  182. data/lib/git/diff_path_status.rb +107 -0
  183. data/lib/git/diff_result.rb +34 -0
  184. data/lib/git/diff_stats.rb +111 -0
  185. data/lib/git/dirstat_info.rb +102 -0
  186. data/lib/git/encoding_utils.rb +32 -1
  187. data/lib/git/errors.rb +285 -0
  188. data/lib/git/escaped_path.rb +57 -5
  189. data/lib/git/execution_context/global.rb +31 -0
  190. data/lib/git/execution_context/repository.rb +151 -0
  191. data/lib/git/execution_context.rb +559 -0
  192. data/lib/git/factories.rb +813 -0
  193. data/lib/git/file_ref.rb +77 -0
  194. data/lib/git/fsck_object.rb +56 -0
  195. data/lib/git/fsck_result.rb +132 -0
  196. data/lib/git/log.rb +306 -90
  197. data/lib/git/object.rb +563 -141
  198. data/lib/git/parsers/branch.rb +240 -0
  199. data/lib/git/parsers/cat_file.rb +111 -0
  200. data/lib/git/parsers/config_entry.rb +110 -0
  201. data/lib/git/parsers/diff.rb +792 -0
  202. data/lib/git/parsers/fsck.rb +144 -0
  203. data/lib/git/parsers/grep.rb +42 -0
  204. data/lib/git/parsers/ls_remote.rb +79 -0
  205. data/lib/git/parsers/ls_tree.rb +58 -0
  206. data/lib/git/parsers/remote.rb +162 -0
  207. data/lib/git/parsers/stash.rb +292 -0
  208. data/lib/git/parsers/status.rb +251 -0
  209. data/lib/git/parsers/tag.rb +341 -0
  210. data/lib/git/parsers/worktree.rb +185 -0
  211. data/lib/git/path_resolver.rb +206 -0
  212. data/lib/git/remote.rb +165 -12
  213. data/lib/git/remote_info.rb +203 -0
  214. data/lib/git/repository/branching.rb +964 -0
  215. data/lib/git/repository/committing.rb +246 -0
  216. data/lib/git/repository/context_helpers.rb +293 -0
  217. data/lib/git/repository/diffing.rb +785 -0
  218. data/lib/git/repository/inspecting.rb +252 -0
  219. data/lib/git/repository/logging.rb +410 -0
  220. data/lib/git/repository/maintenance.rb +65 -0
  221. data/lib/git/repository/merging.rb +451 -0
  222. data/lib/git/repository/object_operations.rb +1551 -0
  223. data/lib/git/repository/remote_operations.rb +984 -0
  224. data/lib/git/repository/shared_private.rb +120 -0
  225. data/lib/git/repository/staging.rb +587 -0
  226. data/lib/git/repository/stashing.rb +623 -0
  227. data/lib/git/repository/status_operations.rb +249 -0
  228. data/lib/git/repository/worktree_operations.rb +339 -0
  229. data/lib/git/repository.rb +484 -2
  230. data/lib/git/stash.rb +109 -12
  231. data/lib/git/stash_info.rb +102 -0
  232. data/lib/git/stashes.rb +169 -26
  233. data/lib/git/status.rb +308 -122
  234. data/lib/git/status_file_info.rb +258 -0
  235. data/lib/git/status_info.rb +189 -0
  236. data/lib/git/tag_delete_failure.rb +34 -0
  237. data/lib/git/tag_delete_result.rb +66 -0
  238. data/lib/git/tag_info.rb +99 -0
  239. data/lib/git/url.rb +15 -8
  240. data/lib/git/version.rb +113 -2
  241. data/lib/git/version_constraint.rb +85 -0
  242. data/lib/git/worktree.rb +150 -8
  243. data/lib/git/worktree_info.rb +128 -0
  244. data/lib/git/worktrees.rb +118 -13
  245. data/lib/git.rb +632 -234
  246. metadata +369 -54
  247. data/.github/stale.yml +0 -25
  248. data/.github/workflows/continuous_integration.yml +0 -49
  249. data/.gitignore +0 -10
  250. data/Dockerfile.changelog-rs +0 -12
  251. data/Gemfile +0 -5
  252. data/ISSUE_TEMPLATE.md +0 -15
  253. data/PULL_REQUEST_TEMPLATE.md +0 -9
  254. data/RELEASING.md +0 -70
  255. data/Rakefile +0 -60
  256. data/lib/git/base/factory.rb +0 -99
  257. data/lib/git/base.rb +0 -711
  258. data/lib/git/command_line_result.rb +0 -86
  259. data/lib/git/failed_error.rb +0 -53
  260. data/lib/git/git_execute_error.rb +0 -7
  261. data/lib/git/index.rb +0 -5
  262. data/lib/git/lib.rb +0 -1328
  263. data/lib/git/path.rb +0 -31
  264. data/lib/git/signaled_error.rb +0 -50
  265. data/lib/git/working_directory.rb +0 -4
data/lib/git/url.rb CHANGED
@@ -8,9 +8,10 @@ module Git
8
8
  # Any URL that can be passed to `git clone` can be parsed by this class.
9
9
  #
10
10
  # @see https://git-scm.com/docs/git-clone#_git_urls GIT URLs
11
- # @see https://github.com/sporkmonger/addressable Addresable::URI
12
11
  #
13
- # @api public
12
+ # @see https://github.com/sporkmonger/addressable Addressable::URI
13
+ #
14
+ # @api private
14
15
  #
15
16
  class URL
16
17
  # Regexp used to match a Git URL with an alternative SSH syntax
@@ -23,14 +24,14 @@ module Git
23
24
  :(?!/) # : serparator is required, but must not be followed by /
24
25
  (?<path>.*?) # path is required
25
26
  $
26
- }x.freeze
27
+ }x
27
28
 
28
29
  # Parse a Git URL and return an Addressable::URI object
29
30
  #
30
31
  # The URI returned can be converted back to a string with 'to_s'. This is
31
32
  # guaranteed to return the same URL string that was parsed.
32
33
  #
33
- # @example
34
+ # @example Parsing clone target from HTTPS and local paths
34
35
  # uri = Git::URL.parse('https://github.com/ruby-git/ruby-git.git')
35
36
  # #=> #<Addressable::URI:0x44c URI:https://github.com/ruby-git/ruby-git.git>
36
37
  # uri.scheme #=> "https"
@@ -54,11 +55,15 @@ module Git
54
55
 
55
56
  # The directory `git clone` would use for the repository directory for the given URL
56
57
  #
57
- # @example
58
+ # @example Deriving the clone directory name
58
59
  # Git::URL.clone_to('https://github.com/ruby-git/ruby-git.git') #=> 'ruby-git'
59
60
  #
60
61
  # @param url [String] the Git URL containing the repository directory
61
62
  #
63
+ # @param bare [Boolean] whether the repository should be cloned as bare
64
+ #
65
+ # @param mirror [Boolean] whether the repository should be cloned as a mirror
66
+ #
62
67
  # @return [String] the name of the repository directory
63
68
  #
64
69
  def self.clone_to(url, bare: false, mirror: false)
@@ -80,7 +85,7 @@ module Git
80
85
  # This class is necessary to ensure that #to_s returns the same string
81
86
  # that was passed to the initializer.
82
87
  #
83
- # @api public
88
+ # @api private
84
89
  #
85
90
  class GitAltURI < Addressable::URI
86
91
  # Create a new GitAltURI object
@@ -90,7 +95,9 @@ module Git
90
95
  # uri.to_s #=> 'james@github.com/james/ruby-git'
91
96
  #
92
97
  # @param user [String, nil] the user from the URL or nil
98
+ #
93
99
  # @param host [String] the host from the URL
100
+ #
94
101
  # @param path [String] the path from the URL
95
102
  #
96
103
  def initialize(user:, host:, path:)
@@ -118,9 +125,9 @@ module Git
118
125
  #
119
126
  def to_s
120
127
  if user
121
- "#{user}@#{host}:#{path[1..-1]}"
128
+ "#{user}@#{host}:#{path[1..]}"
122
129
  else
123
- "#{host}:#{path[1..-1]}"
130
+ "#{host}:#{path[1..]}"
124
131
  end
125
132
  end
126
133
  end
data/lib/git/version.rb CHANGED
@@ -1,5 +1,116 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Git
2
4
  # The current gem version
3
- # @return [String] the current gem version.
4
- VERSION='1.19.1'
5
+ #
6
+ # @return [String] the current gem version
7
+ VERSION = '5.5.0'
8
+
9
+ # Represents a git version with major, minor, and patch components
10
+ #
11
+ # Git versions follow a strict major.minor.patch format. This class provides
12
+ # parsing from git command output (which may include platform suffixes) and
13
+ # comparison operations for version gating.
14
+ #
15
+ # @!attribute [r] major
16
+ # The major version number
17
+ #
18
+ # @return [Integer]
19
+ #
20
+ # @!attribute [r] minor
21
+ # The minor version number
22
+ #
23
+ # @return [Integer]
24
+ #
25
+ # @!attribute [r] patch
26
+ # The patch version number
27
+ #
28
+ # @return [Integer]
29
+ #
30
+ # @example Creating a version directly
31
+ # version = Git::Version.new(2, 42, 1)
32
+ # version.to_s #=> "2.42.1"
33
+ #
34
+ # @example Parsing from git version output
35
+ # Git::Version.parse('git version 2.42.1') #=> Git::Version.new(2, 42, 1)
36
+ # Git::Version.parse('2.39.2 (Apple Git-143)') #=> Git::Version.new(2, 39, 2)
37
+ #
38
+ # @example Parsing versions with platform suffixes
39
+ # Git::Version.parse('2.42.0.windows.1') #=> Git::Version.new(2, 42, 0)
40
+ #
41
+ # @example Comparing versions
42
+ # Git::Version.new(2, 42, 1) > Git::Version.new(2, 28, 0) #=> true
43
+ #
44
+ # @api public
45
+ #
46
+ Version = Data.define(:major, :minor, :patch) do
47
+ include Comparable
48
+
49
+ # Parse a version string into a Version object
50
+ #
51
+ # Handles git's version output format, stripping platform suffixes
52
+ # (like `.windows.1` or `.vfs.0`) and padding two-segment versions
53
+ # to three segments.
54
+ #
55
+ # @example Parse various version string formats
56
+ # Git::Version.parse('2.42.1') #=> Git::Version.new(2, 42, 1)
57
+ # Git::Version.parse('git version 2.42.1') #=> Git::Version.new(2, 42, 1)
58
+ # Git::Version.parse('2.42.0.windows.1') #=> Git::Version.new(2, 42, 0)
59
+ #
60
+ # @param string [String] version string to parse
61
+ #
62
+ # @return [Git::Version] the parsed version
63
+ #
64
+ # @raise [Git::UnexpectedResultError] if the string cannot be parsed as a version
65
+ #
66
+ def self.parse(string)
67
+ version_match = string&.match(/(\d+)\.(\d+)(?:\.(\d+))?/)
68
+ raise Git::UnexpectedResultError, "Invalid version: #{string.inspect}" unless version_match
69
+
70
+ major = version_match[1].to_i
71
+ minor = version_match[2].to_i
72
+ patch = (version_match[3] || '0').to_i
73
+
74
+ new(major, minor, patch)
75
+ end
76
+
77
+ # Compare this version to another
78
+ #
79
+ # @param other [Git::Version] the version to compare to
80
+ #
81
+ # @return [Integer] -1, 0, or 1
82
+ #
83
+ def <=>(other)
84
+ [major, minor, patch] <=> [other.major, other.minor, other.patch]
85
+ end
86
+
87
+ # Return the version as a dotted string
88
+ #
89
+ # @return [String] the version in "major.minor.patch" format
90
+ #
91
+ def to_s
92
+ "#{major}.#{minor}.#{patch}"
93
+ end
94
+
95
+ # Return a readable representation
96
+ #
97
+ # @return [String] inspect string
98
+ #
99
+ def inspect
100
+ "#<Git::Version #{self}>"
101
+ end
102
+
103
+ # Return the version as an array of integers
104
+ #
105
+ # Useful when legacy code expects the array shape returned by the
106
+ # deprecated `Git::Lib#current_command_version` method.
107
+ #
108
+ # @example Get version as integer components
109
+ # Git.git_version.to_a #=> [2, 42, 0]
110
+ #
111
+ # @return [Array<Integer>] [major, minor, patch]
112
+ def to_a
113
+ deconstruct
114
+ end
115
+ end
5
116
  end
@@ -0,0 +1,85 @@
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
+ # Initialize a new VersionConstraint
30
+ #
31
+ # @param min [Git::Version, nil] minimum version (inclusive)
32
+ #
33
+ # @param before [Git::Version, nil] upper bound version (exclusive)
34
+ #
35
+ def initialize(min: nil, before: nil)
36
+ super
37
+ end
38
+
39
+ # Check if the given version is too old (below the minimum)
40
+ #
41
+ # @param version [Git::Version] the version to check
42
+ #
43
+ # @return [Boolean] true if version is below the minimum, false otherwise
44
+ #
45
+ def too_old?(version)
46
+ return false unless min
47
+
48
+ version < min
49
+ end
50
+
51
+ # Check if the given version is too new (at or past the upper bound)
52
+ #
53
+ # @param version [Git::Version] the version to check
54
+ #
55
+ # @return [Boolean] true if version is at or past the upper bound, false otherwise
56
+ #
57
+ def too_new?(version)
58
+ return false unless before
59
+
60
+ version >= before
61
+ end
62
+
63
+ # Check if the given version satisfies this constraint
64
+ #
65
+ # @param version [Git::Version] the version to check
66
+ #
67
+ # @return [Boolean] true if the version satisfies the constraint
68
+ #
69
+ def satisfied_by?(version)
70
+ !too_old?(version) && !too_new?(version)
71
+ end
72
+
73
+ # Return a human-readable representation of this constraint
74
+ #
75
+ # @return [String] the constraint in git version range form
76
+ #
77
+ def to_s
78
+ return ">= #{min}, < #{before}" if min && before
79
+ return ">= #{min}" if min
80
+ return "< #{before}" if before
81
+
82
+ 'any version'
83
+ end
84
+ end
85
+ end
data/lib/git/worktree.rb CHANGED
@@ -1,38 +1,180 @@
1
- require 'git/path'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Git
4
+ # A worktree in a Git repository
5
+ #
6
+ # Represents a single linked or main worktree. Constructed by
7
+ # {Git::Repository::WorktreeOperations#worktree} or populated by
8
+ # {Git::Worktrees}.
9
+ #
10
+ # @example Add and remove a linked worktree
11
+ # worktree = repo.worktree('/path/to/new-worktree')
12
+ # worktree.add
13
+ # worktree.remove
14
+ #
15
+ # @deprecated Use {Git::Repository::WorktreeOperations#worktree_list} and the
16
+ # path-based worktree operations on {Git::Repository} instead
17
+ #
18
+ # {Git::Repository::WorktreeOperations#worktree_list} returns immutable
19
+ # {Git::WorktreeInfo} value objects. Operations that lived on this class are
20
+ # called on the repository with the worktree path instead (for example
21
+ # {Git::Repository::WorktreeOperations#worktree_add} and
22
+ # {Git::Repository::WorktreeOperations#worktree_remove}). {#gcommit},
23
+ # {#add}, and {#remove} each emit a deprecation warning; the `dir`, `full`,
24
+ # `to_s`, and `to_a` readers do not.
25
+ #
26
+ # @api public
27
+ #
28
+ class Worktree
29
+ # Full worktree descriptor including the optional commitish
30
+ #
31
+ # @return [String] the filesystem path, space-separated with the commitish
32
+ # when one was given at construction time
33
+ #
34
+ attr_accessor :full
4
35
 
5
- class Worktree < Path
6
-
7
- attr_accessor :full, :dir, :gcommit
36
+ # Filesystem path of this worktree
37
+ #
38
+ # @return [String] the filesystem path of the worktree directory
39
+ #
40
+ attr_accessor :dir
8
41
 
42
+ # Creates a new Worktree object
43
+ #
44
+ # @param base [Git::Repository] the repository that owns this
45
+ # worktree
46
+ #
47
+ # @param dir [String] filesystem path of the worktree
48
+ #
49
+ # @param gcommit [String, nil] commitish associated with the worktree;
50
+ # when non-nil it is appended to {#full}
51
+ #
52
+ # @return [void]
53
+ #
9
54
  def initialize(base, dir, gcommit = nil)
10
55
  @full = dir
11
- @full += ' ' + gcommit if !gcommit.nil?
56
+ @full += " #{gcommit}" unless gcommit.nil?
12
57
  @base = base
13
58
  @dir = dir
14
59
  @gcommit = gcommit
15
60
  end
16
61
 
62
+ # Returns the commit (or commitish string) associated with this worktree
63
+ #
64
+ # When a commitish string was supplied at construction time (e.g. by
65
+ # {Git::Worktrees} which passes the raw SHA from `git worktree list`), that
66
+ # string is returned as-is. Otherwise the value is lazily resolved on first
67
+ # call via `worktree_repository.gcommit(@full)` and the result is memoized.
68
+ #
69
+ # @example When resolved lazily (no commitish at construction)
70
+ # worktree = repo.worktree('/path/to/wt')
71
+ # worktree.gcommit # => #<Git::Object::Commit ...>
72
+ #
73
+ # @example When the commitish was given at construction
74
+ # worktree = repo.worktrees['/path/to/wt']
75
+ # worktree.gcommit # => "4bef5ab8c9..." (raw SHA string)
76
+ #
77
+ # @return [Git::Object::Commit, String] a commit object when lazily
78
+ # resolved, or the raw commitish string when pre-set at construction
79
+ #
80
+ # @raise [Git::FailedError] if git must resolve the commit and exits with a
81
+ # non-zero exit status
82
+ #
83
+ # @deprecated Use {Git::WorktreeInfo#head} from
84
+ # {Git::Repository::WorktreeOperations#worktree_list} instead
85
+ #
86
+ # `head` is always the commit SHA as a `String` (or `nil` for a bare main
87
+ # worktree). Call `repo.gcommit(info.head)` for the commit object.
88
+ #
89
+ # @see Git::WorktreeInfo#head
90
+ #
17
91
  def gcommit
18
- @gcommit ||= @base.gcommit(@full)
92
+ Git::Deprecation.warn(
93
+ 'Git::Worktree#gcommit is deprecated and will be removed in v6.0.0. ' \
94
+ 'Use Git::WorktreeInfo#head from Git::Repository#worktree_list instead.'
95
+ )
96
+ @gcommit ||= worktree_repository.gcommit(@full)
19
97
  @gcommit
20
98
  end
21
99
 
100
+ # Creates this worktree on disk
101
+ #
102
+ # Runs `git worktree add` for {#dir}, optionally at the commitish passed
103
+ # at construction time.
104
+ #
105
+ # @example Add a worktree
106
+ # worktree = repo.worktree('/path/to/new-worktree')
107
+ # worktree.add
108
+ #
109
+ # @return [String] stdout from the git command
110
+ #
111
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
112
+ #
113
+ # @deprecated Use {Git::Repository::WorktreeOperations#worktree_add} instead
114
+ #
115
+ # @see Git::Repository::WorktreeOperations#worktree_add
116
+ #
22
117
  def add
23
- @base.lib.worktree_add(@dir, @gcommit)
118
+ Git::Deprecation.warn(
119
+ 'Git::Worktree#add is deprecated and will be removed in v6.0.0. ' \
120
+ 'Use Git::Repository#worktree_add instead.'
121
+ )
122
+ worktree_repository.worktree_add(@dir, @gcommit)
24
123
  end
25
124
 
125
+ # Removes this worktree from disk
126
+ #
127
+ # Runs `git worktree remove` for {#dir}.
128
+ #
129
+ # @example Remove a worktree
130
+ # worktree.remove
131
+ #
132
+ # @return [String] stdout from the git command (typically empty)
133
+ #
134
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
135
+ #
136
+ # @deprecated Use {Git::Repository::WorktreeOperations#worktree_remove} instead
137
+ #
138
+ # @see Git::Repository::WorktreeOperations#worktree_remove
139
+ #
26
140
  def remove
27
- @base.lib.worktree_remove(@dir)
141
+ Git::Deprecation.warn(
142
+ 'Git::Worktree#remove is deprecated and will be removed in v6.0.0. ' \
143
+ 'Use Git::Repository#worktree_remove instead.'
144
+ )
145
+ worktree_repository.worktree_remove(@dir)
28
146
  end
29
147
 
148
+ # Returns an array containing the full worktree descriptor
149
+ #
150
+ # @example Get the descriptor array
151
+ # worktree.to_a # => ["/path/to/worktree"]
152
+ #
153
+ # @return [Array<String>] array containing the full worktree descriptor
154
+ #
30
155
  def to_a
31
156
  [@full]
32
157
  end
33
158
 
159
+ # Returns the full worktree descriptor as a string
160
+ #
161
+ # @example Get the descriptor string
162
+ # worktree.to_s # => "/path/to/worktree"
163
+ #
164
+ # @return [String] the full worktree descriptor (path and optional commitish)
165
+ #
34
166
  def to_s
35
167
  @full
36
168
  end
169
+
170
+ private
171
+
172
+ # @return [Git::Repository] the repository used for worktree operations
173
+ #
174
+ # @api private
175
+ #
176
+ def worktree_repository
177
+ @base
178
+ end
37
179
  end
38
180
  end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ # Immutable value object for one entry of `git worktree list`
5
+ #
6
+ # Each entry carries what `git worktree list --porcelain` reports for a
7
+ # worktree: its path, the checked-out HEAD and branch, and whether it is bare,
8
+ # detached, locked, or prunable, with the reason git gives for the last two.
9
+ #
10
+ # @example A locked linked worktree with a branch checked out
11
+ # info = Git::WorktreeInfo.new(
12
+ # path: '/tmp/wt/linked',
13
+ # head: 'f3e2c1ffb860086504eeb27b77a1d0028b68fd8f',
14
+ # branch: 'refs/heads/linked',
15
+ # bare: false,
16
+ # detached: false,
17
+ # locked: true,
18
+ # lock_reason: 'on purpose',
19
+ # prunable: false,
20
+ # prune_reason: nil
21
+ # )
22
+ #
23
+ # info.path # => '/tmp/wt/linked'
24
+ # info.head # => 'f3e2c1ffb860086504eeb27b77a1d0028b68fd8f'
25
+ # info.branch # => 'refs/heads/linked'
26
+ # info.locked? # => true
27
+ # info.lock_reason # => 'on purpose'
28
+ # info.detached? # => false
29
+ # info.to_s # => '/tmp/wt/linked'
30
+ #
31
+ # @example Pass an entry back to a worktree operation
32
+ # info = repo.worktree_list.find { |w| w.branch == 'refs/heads/linked' }
33
+ # repo.worktree_remove(info)
34
+ #
35
+ # @see Git::Repository::WorktreeOperations#worktree_list for the repository
36
+ # method that returns these
37
+ #
38
+ # @api public
39
+ #
40
+ # @!attribute [r] path
41
+ # @return [String] the worktree directory as git reports it
42
+ #
43
+ # @!attribute [r] head
44
+ # @return [String, nil] the full object ID of the checked-out HEAD commit
45
+ # (the all-zero object ID when the branch has no commits yet), or nil for a
46
+ # bare main worktree
47
+ #
48
+ # @!attribute [r] branch
49
+ # @return [String, nil] the full refname of the checked-out branch (e.g.,
50
+ # 'refs/heads/main'), or nil when the worktree is bare or detached
51
+ #
52
+ # @!attribute [r] bare
53
+ # @return [Boolean] true if this is the main worktree of a bare repository
54
+ #
55
+ # @!attribute [r] detached
56
+ # @return [Boolean] true if HEAD is detached in this worktree
57
+ #
58
+ # @!attribute [r] locked
59
+ # @return [Boolean] true if the worktree is locked
60
+ #
61
+ # @!attribute [r] lock_reason
62
+ # @return [String, nil] the reason given when the worktree was locked, or nil
63
+ # when it is not locked or was locked without a reason
64
+ #
65
+ # @!attribute [r] prunable
66
+ # @return [Boolean] true if `git worktree prune` would remove this entry
67
+ #
68
+ # @!attribute [r] prune_reason
69
+ # @return [String, nil] git's explanation of why the entry is prunable, or
70
+ # nil when it is not prunable
71
+ #
72
+ WorktreeInfo = Data.define(
73
+ :path,
74
+ :head,
75
+ :branch,
76
+ :bare,
77
+ :detached,
78
+ :locked,
79
+ :lock_reason,
80
+ :prunable,
81
+ :prune_reason
82
+ ) do
83
+ # Whether this is the main worktree of a bare repository
84
+ #
85
+ # @example
86
+ # info.bare? # => false
87
+ #
88
+ # @return [Boolean] true if the worktree is bare
89
+ def bare? = bare
90
+
91
+ # Whether HEAD is detached in this worktree
92
+ #
93
+ # @example
94
+ # info.detached? # => false
95
+ #
96
+ # @return [Boolean] true if HEAD is detached
97
+ def detached? = detached
98
+
99
+ # Whether the worktree is locked
100
+ #
101
+ # @example
102
+ # info.locked? # => true
103
+ #
104
+ # @return [Boolean] true if the worktree is locked
105
+ def locked? = locked
106
+
107
+ # Whether `git worktree prune` would remove this entry
108
+ #
109
+ # @example
110
+ # info.prunable? # => false
111
+ #
112
+ # @return [Boolean] true if the entry is prunable
113
+ def prunable? = prunable
114
+
115
+ # Returns the worktree path
116
+ #
117
+ # Lets an entry be passed directly to the worktree operations that take a
118
+ # path, such as {Git::Repository::WorktreeOperations#worktree_remove}.
119
+ #
120
+ # @example Convert to string
121
+ # info.to_s # => '/tmp/wt/linked'
122
+ #
123
+ # @return [String] the worktree path
124
+ def to_s
125
+ path
126
+ end
127
+ end
128
+ end