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/object.rb CHANGED
@@ -1,16 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'git/author_info'
4
+ require 'git/diff'
5
+ require 'git/errors'
6
+ require 'git/log'
7
+
1
8
  module Git
2
-
3
- class GitTagNameDoesNotExist< StandardError
4
- end
5
-
6
9
  # represents a git object
7
10
  class Object
8
-
11
+ # A base class for all Git objects
12
+ #
13
+ # @api private
14
+ #
9
15
  class AbstractObject
10
- attr_accessor :objectish, :type, :mode
16
+ # @return [String] the object name, SHA, ref, or treeish path
17
+ #
18
+ attr_accessor :objectish
19
+
20
+ # @return [String, nil] the git object type
21
+ #
22
+ attr_accessor :type
23
+
24
+ # @return [String, nil] the file mode from tree listings
25
+ #
26
+ attr_accessor :mode
11
27
 
28
+ # Sets the size of the git object in bytes
29
+ #
30
+ # @example Set the size to 60 bytes
31
+ # object.size = 60
32
+ #
33
+ # @return [Integer] the size of the git object in bytes
34
+ #
12
35
  attr_writer :size
13
-
36
+
37
+ # Creates a lazy wrapper for a git object
38
+ #
39
+ # @param base [Git::Repository] the repository used to query object data
40
+ #
41
+ # @param objectish [String, #to_s] the object name, SHA, ref, or treeish path
42
+ #
14
43
  def initialize(base, objectish)
15
44
  @base = base
16
45
  @objectish = objectish.to_s
@@ -20,141 +49,342 @@ module Git
20
49
  @sha = nil
21
50
  end
22
51
 
52
+ # Returns the resolved SHA for this object
53
+ #
54
+ # @return [String] the resolved object SHA
55
+ #
23
56
  def sha
24
- @sha ||= @base.lib.revparse(@objectish)
57
+ @sha ||= object_repository.rev_parse(@objectish)
25
58
  end
26
-
59
+
60
+ # Returns the size of this object in bytes
61
+ #
62
+ # @return [Integer] the object size in bytes
63
+ #
27
64
  def size
28
- @size ||= @base.lib.object_size(@objectish)
65
+ @size ||= object_repository.cat_file_size(@objectish)
29
66
  end
30
-
31
- # Get the object's contents.
32
- # If no block is given, the contents are cached in memory and returned as a string.
33
- # If a block is given, it yields an IO object (via IO::popen) which could be used to
34
- # read a large file in chunks.
67
+
68
+ # Returns the raw content of this git object or streams it into a temporary file
69
+ #
70
+ # Without a block, the full content is buffered in memory and cached, then
71
+ # returned as a `String`. With a block, git output is streamed directly to a
72
+ # temporary file on disk — suitable for large objects.
73
+ #
74
+ # @api public
75
+ #
76
+ # @overload contents
77
+ # Returns the cached content as a string.
78
+ #
79
+ # @return [String] the raw content of the object, cached after first call
80
+ #
81
+ # @raise [Git::FailedError] if the object does not exist or the command fails
82
+ #
83
+ # @example Get the contents of a blob
84
+ # git.object('HEAD:README.md').contents # => "This is a README file\n"
85
+ #
86
+ # @overload contents(&block)
87
+ # Streams the content to a temporary file and yields it.
88
+ #
89
+ # Git output is written directly to a file without buffering in
90
+ # memory. Use this form for large blobs to avoid memory pressure.
35
91
  #
36
- # Use this for large files so that they are not held in memory.
37
- def contents(&block)
92
+ # @yield [file] the temporary file, positioned at the start of the content
93
+ #
94
+ # @yieldparam file [File] readable `IO` object positioned at the beginning
95
+ #
96
+ # @yieldreturn [Object] the value to return from this method
97
+ #
98
+ # @return [Object] the value returned by the block
99
+ #
100
+ # @raise [Git::FailedError] if the object does not exist or the command fails
101
+ #
102
+ # @example Read a large blob without loading it into memory
103
+ # git.object('HEAD:large_file.bin').contents { |f| upload(f) }
104
+ #
105
+ def contents(&)
38
106
  if block_given?
39
- @base.lib.object_contents(@objectish, &block)
107
+ object_repository.cat_file_contents(@objectish, &)
40
108
  else
41
- @contents ||= @base.lib.object_contents(@objectish)
109
+ @contents ||= object_repository.cat_file_contents(@objectish)
42
110
  end
43
111
  end
44
-
112
+
113
+ # Returns the object contents split into lines
114
+ #
115
+ # @return [Array<String>] the raw contents split on newline boundaries
116
+ #
45
117
  def contents_array
46
- self.contents.split("\n")
118
+ contents.split("\n")
47
119
  end
48
-
120
+
121
+ # Returns the original object expression
122
+ #
123
+ # @return [String] the object name, SHA, ref, or treeish path
124
+ #
49
125
  def to_s
50
126
  @objectish
51
127
  end
52
-
128
+
129
+ # Searches this object for matching tracked file contents
130
+ #
131
+ # Always searches this object's resolved SHA. A caller-provided `:object`
132
+ # option is ignored.
133
+ #
134
+ # @param string [String] the pattern to search for
135
+ #
136
+ # @param path_limiter [String, Pathname, Array<String, Pathname>, nil]
137
+ # path or paths to limit the search to
138
+ #
139
+ # @param opts [Hash] additional grep options
140
+ #
141
+ # @option opts [Boolean, nil] :ignore_case (nil) ignore case
142
+ # distinctions in the pattern and file contents
143
+ #
144
+ # Alias: :i
145
+ #
146
+ # @option opts [Boolean, nil] :invert_match (nil) select non-matching
147
+ # lines
148
+ #
149
+ # Alias: :v
150
+ #
151
+ # @option opts [Boolean, nil] :extended_regexp (nil) use POSIX extended
152
+ # regular expressions for the pattern
153
+ #
154
+ # Alias: :E
155
+ #
156
+ # @return [Hash<String, Array<Array(Integer, String)>>] matching lines by path
157
+ #
53
158
  def grep(string, path_limiter = nil, opts = {})
54
- opts = {:object => sha, :path_limiter => path_limiter}.merge(opts)
55
- @base.lib.grep(string, opts)
159
+ object_repository.grep(string, path_limiter, opts.merge(object: sha))
56
160
  end
57
-
161
+
162
+ # Returns a diff from this object to another object
163
+ #
164
+ # @param objectish [String] the object name, SHA, ref, or treeish path to diff
165
+ # against
166
+ #
167
+ # @return [Git::Diff] the diff between the two objects
168
+ #
58
169
  def diff(objectish)
59
170
  Git::Diff.new(@base, @objectish, objectish)
60
171
  end
61
-
172
+
173
+ # Returns a log scoped to this object
174
+ #
175
+ # @param count [Integer] maximum number of commits to include
176
+ #
177
+ # @return [Git::Log] the scoped log object
178
+ #
62
179
  def log(count = 30)
63
180
  Git::Log.new(@base, count).object(@objectish)
64
181
  end
65
-
66
- # creates an archive of this object (tree)
182
+
183
+ # Creates an archive of this object and writes it to a file
184
+ #
185
+ # @example Archive a tree to a zip file
186
+ # git.object('v1.0').archive('/tmp/release.zip', format: 'zip')
187
+ #
188
+ # @example Archive a tree to a temporary tar file
189
+ # git.object('v2.6').archive(nil, format: 'tar')
190
+ #
191
+ # @example Archive a tree to a tgz file with a path prefix
192
+ # git.object('v2.6').archive('/tmp/release.tgz', format: 'tgz', prefix: 'test/')
193
+ #
194
+ # @example Archive one directory with a path prefix
195
+ # git.object('v2.6').archive(
196
+ # '/tmp/ex-dir.tar',
197
+ # format: 'tar',
198
+ # prefix: 'test/',
199
+ # path: 'ex_dir/'
200
+ # )
201
+ #
202
+ # @param file [String, nil] destination file path; a temp file is created if `nil`
203
+ #
204
+ # @param opts [Hash] archive options (see {Git::Repository#archive})
205
+ #
206
+ # @option opts [String] :format ('zip') archive format: `'tar'`, `'zip'`,
207
+ # or `'tgz'`
208
+ #
209
+ # @option opts [String] :prefix (nil) prefix prepended to every filename
210
+ # in the archive
211
+ #
212
+ # @option opts [String] :path (nil) path within the tree to include
213
+ #
214
+ # @option opts [String] :remote (nil) retrieve the archive from a remote
215
+ # repository
216
+ #
217
+ # @option opts [Boolean, nil] :add_gzip (nil) apply gzip compression after
218
+ # writing the archive
219
+ #
220
+ # @return [String] the path to the written archive file
221
+ #
222
+ # @raise [ArgumentError] when archive options or destination path are invalid
223
+ #
224
+ # @raise [Git::FailedError] if `git archive` fails
225
+ #
226
+ # @api public
227
+ #
67
228
  def archive(file = nil, opts = {})
68
- @base.lib.archive(@objectish, file, opts)
229
+ object_repository.archive(@objectish, file, opts)
69
230
  end
70
-
71
- def tree?; false; end
72
-
73
- def blob?; false; end
74
-
75
- def commit?; false; end
76
231
 
77
- def tag?; false; end
78
-
232
+ # Returns whether this object is a tree
233
+ #
234
+ # @return [Boolean] `true` when this object is a tree
235
+ #
236
+ def tree? = false
237
+
238
+ # Returns whether this object is a blob
239
+ #
240
+ # @return [Boolean] `true` when this object is a blob
241
+ #
242
+ def blob? = false
243
+
244
+ # Returns whether this object is a commit
245
+ #
246
+ # @return [Boolean] `true` when this object is a commit
247
+ #
248
+ def commit? = false
249
+
250
+ # Returns whether this object is a tag
251
+ #
252
+ # @return [Boolean] `true` when this object is a tag
253
+ #
254
+ def tag? = false
255
+
256
+ private
257
+
258
+ # @return [Git::Repository] the repository used for object lookup
259
+ #
260
+ def object_repository
261
+ @base
262
+ end
79
263
  end
80
-
81
-
264
+
265
+ # A Git blob object
82
266
  class Blob < AbstractObject
83
-
267
+ # Creates a blob object wrapper
268
+ #
269
+ # @param base [Git::Repository] the repository used to query object data
270
+ #
271
+ # @param sha [String] the blob SHA or object expression
272
+ #
273
+ # @param mode [String, nil] the file mode from tree listings
274
+ #
84
275
  def initialize(base, sha, mode = nil)
85
276
  super(base, sha)
86
277
  @mode = mode
87
278
  end
88
-
279
+
280
+ # Returns whether this object is a blob
281
+ #
282
+ # @return [Boolean] `true`
283
+ #
89
284
  def blob?
90
285
  true
91
286
  end
92
-
93
287
  end
94
-
288
+
289
+ # A Git tree object
95
290
  class Tree < AbstractObject
96
-
291
+ # Creates a tree object wrapper
292
+ #
293
+ # @param base [Git::Repository] the repository used to query object data
294
+ #
295
+ # @param sha [String] the tree SHA or object expression
296
+ #
297
+ # @param mode [String, nil] the file mode from tree listings
298
+ #
97
299
  def initialize(base, sha, mode = nil)
98
300
  super(base, sha)
99
301
  @mode = mode
100
302
  @trees = nil
101
303
  @blobs = nil
102
304
  end
103
-
305
+
306
+ # Returns child blobs and subtrees keyed by name
307
+ #
308
+ # @return [Hash<String, Git::Object::AbstractObject>] child objects by name
309
+ #
104
310
  def children
105
311
  blobs.merge(subtrees)
106
312
  end
107
-
313
+
314
+ # Returns blobs directly under this tree
315
+ #
316
+ # @return [Hash<String, Git::Object::Blob>] blob objects by filename
317
+ #
108
318
  def blobs
109
319
  @blobs ||= check_tree[:blobs]
110
320
  end
111
- alias_method :files, :blobs
112
-
321
+ alias files blobs
322
+
323
+ # Returns subtrees directly under this tree
324
+ #
325
+ # @return [Hash<String, Git::Object::Tree>] subtree objects by directory name
326
+ #
113
327
  def trees
114
328
  @trees ||= check_tree[:trees]
115
329
  end
116
- alias_method :subtrees, :trees
117
- alias_method :subdirectories, :trees
118
-
330
+ alias subtrees trees
331
+ alias subdirectories trees
332
+
333
+ # Returns the full tree listing for this tree
334
+ #
335
+ # @return [Hash] parsed recursive tree data
336
+ #
119
337
  def full_tree
120
- @base.lib.full_tree(@objectish)
338
+ object_repository.full_tree(@objectish)
121
339
  end
122
-
340
+
341
+ # Returns the maximum depth of this tree
342
+ #
343
+ # @return [Integer] maximum tree depth
344
+ #
123
345
  def depth
124
- @base.lib.tree_depth(@objectish)
346
+ object_repository.tree_depth(@objectish)
125
347
  end
126
-
348
+
349
+ # Returns whether this object is a tree
350
+ #
351
+ # @return [Boolean] `true`
352
+ #
127
353
  def tree?
128
354
  true
129
355
  end
130
-
356
+
131
357
  private
132
358
 
133
- # actually run the git command
134
- def check_tree
135
- @trees = {}
136
- @blobs = {}
137
-
138
- data = @base.lib.ls_tree(@objectish)
139
-
140
- data['tree'].each do |key, tree|
141
- @trees[key] = Git::Object::Tree.new(@base, tree[:sha], tree[:mode])
142
- end
143
-
144
- data['blob'].each do |key, blob|
145
- @blobs[key] = Git::Object::Blob.new(@base, blob[:sha], blob[:mode])
146
- end
147
-
148
- {
149
- :trees => @trees,
150
- :blobs => @blobs
151
- }
359
+ # actually run the git command
360
+ def check_tree
361
+ @trees = {}
362
+ @blobs = {}
363
+
364
+ data = object_repository.ls_tree(@objectish)
365
+
366
+ data['tree'].each do |key, tree|
367
+ @trees[key] = Git::Object::Tree.new(@base, tree[:sha], tree[:mode])
152
368
  end
153
-
369
+
370
+ data['blob'].each do |key, blob|
371
+ @blobs[key] = Git::Object::Blob.new(@base, blob[:sha], blob[:mode])
372
+ end
373
+
374
+ { trees: @trees, blobs: @blobs }
375
+ end
154
376
  end
155
-
377
+
378
+ # A Git commit object
156
379
  class Commit < AbstractObject
157
-
380
+ # Creates a commit object wrapper
381
+ #
382
+ # @param base [Git::Repository] the repository used to query object data
383
+ #
384
+ # @param sha [String] the commit SHA or object expression
385
+ #
386
+ # @param init [Hash, nil] parsed commit data used to initialize eagerly
387
+ #
158
388
  def initialize(base, sha, init = nil)
159
389
  super(base, sha)
160
390
  @tree = nil
@@ -162,151 +392,343 @@ module Git
162
392
  @author = nil
163
393
  @committer = nil
164
394
  @message = nil
165
- if init
166
- set_commit(init)
167
- end
395
+ return unless init
396
+
397
+ from_data(init)
168
398
  end
169
-
399
+
400
+ # Returns the commit message
401
+ #
402
+ # @return [String] the commit message without the trailing newline
403
+ #
170
404
  def message
171
405
  check_commit
172
406
  @message
173
407
  end
174
-
408
+
409
+ # Returns the symbolic name for this commit
410
+ #
411
+ # @return [String] the name produced by `git name-rev`
412
+ #
175
413
  def name
176
- @base.lib.namerev(sha)
414
+ object_repository.name_rev(sha)
177
415
  end
178
-
416
+
417
+ # Returns the tree for this commit
418
+ #
419
+ # @return [Git::Object::Tree] the commit tree
420
+ #
179
421
  def gtree
180
422
  check_commit
181
423
  Tree.new(@base, @tree)
182
424
  end
183
-
425
+
426
+ # Returns the first parent commit
427
+ #
428
+ # @return [Git::Object::Commit, nil] the first parent commit, or `nil`
429
+ # for a root commit
430
+ #
184
431
  def parent
185
432
  parents.first
186
433
  end
187
-
434
+
188
435
  # array of all parent commits
189
436
  def parents
190
437
  check_commit
191
- @parents
438
+ @parents
192
439
  end
193
-
194
- # git author
195
- def author
440
+
441
+ # Returns the commit author identity
442
+ #
443
+ # @return [Git::AuthorInfo] the author name, email, and author date
444
+ #
445
+ def author
196
446
  check_commit
197
447
  @author
198
448
  end
199
-
449
+
450
+ # Returns the author date
451
+ #
452
+ # @return [Time] the author timestamp
453
+ #
200
454
  def author_date
201
455
  author.date
202
456
  end
203
-
204
- # git author
457
+
458
+ # Returns the commit committer identity
459
+ #
460
+ # @return [Git::AuthorInfo] the committer name, email, and commit date
461
+ #
205
462
  def committer
206
463
  check_commit
207
464
  @committer
208
465
  end
209
-
210
- def committer_date
466
+
467
+ # Returns the committer date
468
+ #
469
+ # @return [Time] the committer timestamp
470
+ #
471
+ def committer_date
211
472
  committer.date
212
473
  end
213
- alias_method :date, :committer_date
474
+ alias date committer_date
214
475
 
476
+ # Returns the diff between this commit and its first parent
477
+ #
478
+ # @return [Git::Diff] the diff from the first parent to this commit
479
+ #
215
480
  def diff_parent
216
481
  diff(parent)
217
482
  end
218
-
219
- def set_commit(data)
483
+
484
+ # Sets parsed commit data on this commit object
485
+ #
486
+ # @param data [Hash] parsed commit data
487
+ #
488
+ # @return [void]
489
+ #
490
+ # @deprecated use {#from_data} instead
491
+ #
492
+ def set_commit(data) # rubocop:disable Naming/AccessorMethodName
493
+ Git::Deprecation.warn(
494
+ 'Git::Object::Commit#set_commit is deprecated and will be removed in v6.0.0. ' \
495
+ 'Use #from_data instead.'
496
+ )
497
+ from_data(data)
498
+ end
499
+
500
+ # Loads parsed commit data into this commit object
501
+ #
502
+ # @param data [Hash] parsed commit data from `git cat-file commit`
503
+ #
504
+ # @return [void]
505
+ #
506
+ def from_data(data)
220
507
  @sha ||= data['sha']
221
- @committer = Git::Author.new(data['committer'])
222
- @author = Git::Author.new(data['author'])
508
+ @committer = Git::AuthorInfo.parse(data['committer'])
509
+ @author = Git::AuthorInfo.parse(data['author'])
223
510
  @tree = Git::Object::Tree.new(@base, data['tree'])
224
- @parents = data['parent'].map{ |sha| Git::Object::Commit.new(@base, sha) }
511
+ @parents = data['parent'].map { |sha| Git::Object::Commit.new(@base, sha) }
225
512
  @message = data['message'].chomp
226
513
  end
227
-
514
+
515
+ # Returns whether this object is a commit
516
+ #
517
+ # @return [Boolean] `true`
518
+ #
228
519
  def commit?
229
520
  true
230
521
  end
231
522
 
232
523
  private
233
-
234
- # see if this object has been initialized and do so if not
235
- def check_commit
236
- return if @tree
237
-
238
- data = @base.lib.commit_data(@objectish)
239
- set_commit(data)
240
- end
241
-
524
+
525
+ # see if this object has been initialized and do so if not
526
+ def check_commit
527
+ return if @tree
528
+
529
+ data = object_repository.cat_file_commit(@objectish)
530
+ from_data(data)
531
+ end
242
532
  end
243
-
533
+
534
+ # A Git tag object
535
+ #
536
+ # This class represents a tag in Git, which can be either annotated or lightweight.
537
+ #
538
+ # Annotated tags contain additional metadata such as the tagger's name, email, and
539
+ # the date when the tag was created, along with a message.
540
+ #
541
+ # @deprecated Use {Git::Repository::ObjectOperations#tag_list} and
542
+ # {Git::TagInfo} instead
543
+ #
544
+ # {Git::TagInfo} is an immutable value object carrying the tag's `name`,
545
+ # `oid`, `target_oid`, `annotated?`, `message`, and `tagger`. Call the
546
+ # corresponding {Git::Repository} method (e.g. `archive`, `log`, `diff`,
547
+ # `cat_file_contents`) with `info.oid || info.target_oid` for operations
548
+ # on a tag; that is the object this class resolves and pins at
549
+ # construction, so a later move of the tag does not redirect an existing
550
+ # object, whereas the tag name would. Constructing a `Git::Object::Tag`
551
+ # emits a deprecation warning.
552
+ #
244
553
  class Tag < AbstractObject
554
+ # @return [String] the tag name
555
+ #
245
556
  attr_accessor :name
246
-
247
- def initialize(base, sha, name)
557
+
558
+ # @overload initialize(base, name)
559
+ #
560
+ # @param base [Git::Repository] the git repository
561
+ #
562
+ # @param name [String] the name of the tag
563
+ #
564
+ # @overload initialize(base, sha, name)
565
+ #
566
+ # `sha` is kept as the object that the inherited operations (`size`,
567
+ # `contents`, `grep`, `diff`, `log`, `archive`) run against; `annotated?`,
568
+ # `message`, and `tagger` read the ref `name`. {Git::TagInfo} describes a
569
+ # ref, so there is no OID-based replacement for this form: pass `sha` to
570
+ # the {Git::Repository} operation directly, or read the tag object with
571
+ # {Git::Repository::ObjectOperations#cat_file_tag}.
572
+ #
573
+ # @param base [Git::Repository] the git repository
574
+ #
575
+ # @param sha [String] the SHA of the tag object
576
+ #
577
+ # @param name [String] the name of the tag
578
+ #
579
+ def initialize(base, sha, name = nil)
580
+ Git::Deprecation.warn(
581
+ 'Git::Object::Tag is deprecated and will be removed in v6.0.0. ' \
582
+ 'Use Git::Repository#tag_list and Git::TagInfo instead.'
583
+ )
584
+ sha, name = resolve_sha_and_name(base, sha, name)
248
585
  super(base, sha)
586
+
249
587
  @name = name
250
588
  @annotated = nil
251
589
  @loaded = false
252
590
  end
253
591
 
592
+ # Returns whether this tag is annotated
593
+ #
594
+ # @return [Boolean] `true` when the tag has an annotated tag object
595
+ #
254
596
  def annotated?
255
- @annotated ||= (@base.lib.object_type(self.name) == 'tag')
597
+ @annotated = @annotated.nil? ? (object_repository.cat_file_type(name) == 'tag') : @annotated
256
598
  end
257
599
 
600
+ # Returns the tag message
601
+ #
602
+ # @return [String, nil] the annotated tag message, or `nil` for a
603
+ # lightweight tag
604
+ #
258
605
  def message
259
- check_tag()
260
- return @message
606
+ check_tag
607
+ @message
261
608
  end
262
-
609
+
610
+ # Returns whether this object is a tag
611
+ #
612
+ # @return [Boolean] `true`
613
+ #
263
614
  def tag?
264
615
  true
265
616
  end
266
617
 
618
+ # Returns the tagger identity
619
+ #
620
+ # @return [Git::AuthorInfo, nil] the tagger for an annotated tag, or `nil`
621
+ # for a lightweight tag
622
+ #
267
623
  def tagger
268
- check_tag()
269
- return @tagger
624
+ check_tag
625
+ @tagger
270
626
  end
271
627
 
272
628
  private
273
629
 
630
+ # Resolves the two-argument constructor form to a SHA and a tag name
631
+ #
632
+ # In the two-argument form `sha` carries the tag name and the SHA is
633
+ # looked up from the repository.
634
+ #
635
+ # @param base [Git::Repository] the git repository
636
+ #
637
+ # @param sha [String] the SHA of the tag object, or the tag name in the
638
+ # two-argument form
639
+ #
640
+ # @param name [String, nil] the tag name, or `nil` in the two-argument form
641
+ #
642
+ # @return [Array(String, String)] the resolved `[sha, name]` pair
643
+ #
644
+ # @raise [Git::UnexpectedResultError] if the tag does not exist
645
+ #
646
+ def resolve_sha_and_name(base, sha, name)
647
+ return [sha, name] unless name.nil?
648
+
649
+ resolved = base.tag_sha(sha)
650
+ raise Git::UnexpectedResultError, "Tag '#{sha}' does not exist." if resolved == ''
651
+
652
+ [resolved, sha]
653
+ end
654
+
655
+ # Loads annotated tag data when available
656
+ #
657
+ # @return [void]
658
+ #
274
659
  def check_tag
275
660
  return if @loaded
276
661
 
277
- if !self.annotated?
278
- @message = @tagger = nil
279
- else
280
- tdata = @base.lib.tag_data(@name)
662
+ if annotated?
663
+ tdata = object_repository.cat_file_tag(@name)
281
664
  @message = tdata['message'].chomp
282
- @tagger = Git::Author.new(tdata['tagger'])
665
+ @tagger = Git::AuthorInfo.parse(tdata['tagger'])
666
+ else
667
+ @message = @tagger = nil
283
668
  end
284
669
 
285
670
  @loaded = true
286
671
  end
287
-
288
672
  end
289
-
673
+
290
674
  # if we're calling this, we don't know what type it is yet
291
675
  # so this is our little factory method
292
- def self.new(base, objectish, type = nil, is_tag = false)
293
- if is_tag
294
- sha = base.lib.tag_sha(objectish)
295
- if sha == ''
296
- raise Git::GitTagNameDoesNotExist.new(objectish)
297
- end
298
- return Git::Object::Tag.new(base, sha, objectish)
299
- end
300
-
301
- type ||= base.lib.object_type(objectish)
676
+ #
677
+ # @param base [Git::Repository] the repository used to query object data
678
+ #
679
+ # @param objectish [String] the object name, SHA, ref, or treeish path
680
+ #
681
+ # @param type [String, nil] object type hint: `blob`, `commit`, or `tree`
682
+ #
683
+ # @param is_tag [Boolean] whether to construct a tag object
684
+ #
685
+ # @return [Git::Object::AbstractObject] the concrete object wrapper
686
+ #
687
+ def self.new(base, objectish, type = nil, is_tag = false) # rubocop:disable Style/OptionalBooleanParameter
688
+ return new_tag(base, objectish) if is_tag
689
+
690
+ type ||= object_repository_for(base).cat_file_type(objectish)
691
+ # TODO: why not handle tag case here too?
302
692
  klass =
303
693
  case type
304
- when /blob/ then Blob
694
+ when /blob/ then Blob
305
695
  when /commit/ then Commit
306
696
  when /tree/ then Tree
307
697
  end
308
698
  klass.new(base, objectish)
309
699
  end
310
-
700
+
701
+ # Creates a tag object through the deprecated factory path
702
+ #
703
+ # @param base [Git::Repository] the repository used to query object data
704
+ #
705
+ # @param objectish [String] the tag name or SHA
706
+ #
707
+ # @return [Git::Object::Tag] the tag object wrapper
708
+ #
709
+ # @deprecated Use {Git::Repository::ObjectOperations#tag_list} instead
710
+ #
711
+ # The warning names `Git::Object::Tag.new`, the replacement this path
712
+ # shipped with, and the {Git::Object::Tag} constructor is deprecated as
713
+ # well; this method silences it so one call emits one warning. Go
714
+ # straight to `Git::Repository#tag_list(name).first`.
715
+ #
716
+ private_class_method def self.new_tag(base, objectish)
717
+ Git::Deprecation.warn(
718
+ 'Git::Object.new with is_tag argument is deprecated and will be removed in v6.0.0. ' \
719
+ 'Use Git::Object::Tag.new instead.'
720
+ )
721
+ Git::Deprecation.silence { Git::Object::Tag.new(base, objectish) }
722
+ end
723
+
724
+ # Returns the repository used for object lookup
725
+ #
726
+ # @param base [Git::Repository] the repository to return
727
+ #
728
+ # @return [Git::Repository] the repository used for object lookup
729
+ #
730
+ private_class_method def self.object_repository_for(base)
731
+ base
732
+ end
311
733
  end
312
734
  end