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
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'file_ref'
4
+
5
+ module Git
6
+ # Immutable value object representing a single file's patch information
7
+ #
8
+ # DiffFilePatchInfo encapsulates the parsed data from a unified diff for one file,
9
+ # including source and destination file references, the patch text, change type,
10
+ # and line statistics.
11
+ #
12
+ #
13
+ # @example A modified file patch
14
+ # patch_info = Git::DiffFilePatchInfo.new(
15
+ # src: Git::FileRef.new(mode: '100644', sha: 'abc1234', path: 'lib/foo.rb'),
16
+ # dst: Git::FileRef.new(mode: '100644', sha: 'def5678', path: 'lib/foo.rb'),
17
+ # patch: "diff --git a/lib/foo.rb b/lib/foo.rb\n...",
18
+ # status: :modified,
19
+ # similarity: nil,
20
+ # binary: false,
21
+ # insertions: 10,
22
+ # deletions: 5
23
+ # )
24
+ #
25
+ # @example A new file patch (src is nil)
26
+ # patch_info = Git::DiffFilePatchInfo.new(
27
+ # src: nil,
28
+ # dst: Git::FileRef.new(mode: '100644', sha: 'abc1234', path: 'lib/new.rb'),
29
+ # patch: "diff --git a/lib/new.rb b/lib/new.rb\nnew file mode 100644\n...",
30
+ # status: :added,
31
+ # similarity: nil,
32
+ # binary: false,
33
+ # insertions: 20,
34
+ # deletions: 0
35
+ # )
36
+ #
37
+ # @!attribute [r] src
38
+ # @return [FileRef, nil] the source file reference, or nil for new/added files
39
+ #
40
+ # @!attribute [r] dst
41
+ # @return [FileRef, nil] the destination file reference, or nil for deleted files
42
+ #
43
+ # @!attribute [r] patch
44
+ # @return [String] the full unified diff patch text for this file
45
+ #
46
+ # @!attribute [r] status
47
+ # @return [Symbol] the change status (:added, :modified, :deleted, :renamed, :copied, :type_changed)
48
+ #
49
+ # @!attribute [r] similarity
50
+ # @return [Integer, nil] similarity percentage for renames/copies (0-100), nil otherwise
51
+ #
52
+ # @!attribute [r] binary
53
+ # @return [Boolean] whether this is a binary file
54
+ #
55
+ # @!attribute [r] insertions
56
+ # @return [Integer] number of lines inserted
57
+ #
58
+ # @!attribute [r] deletions
59
+ # @return [Integer] number of lines deleted
60
+ #
61
+ # @api private
62
+ #
63
+ # Work in progress; this class is internal for now and may be made public in a future release.
64
+ DiffFilePatchInfo = Data.define(
65
+ :src,
66
+ :dst,
67
+ :patch,
68
+ :status,
69
+ :similarity,
70
+ :binary,
71
+ :insertions,
72
+ :deletions
73
+ ) do
74
+ # Get the primary file path
75
+ #
76
+ # Returns the destination path if it exists, otherwise the source path.
77
+ # This is the "current" or "canonical" path for the file.
78
+ #
79
+ # @return [String] the file path
80
+ #
81
+ def path
82
+ dst&.path || src&.path
83
+ end
84
+
85
+ # Get the source file path
86
+ #
87
+ # Returns the source path if it exists. Useful for renames/copies to see
88
+ # where the file came from.
89
+ #
90
+ # @return [String, nil] the source file path, or nil if no source (added files)
91
+ #
92
+ def src_path
93
+ src&.path
94
+ end
95
+
96
+ # Check if this is a binary file
97
+ #
98
+ # @return [Boolean] true if the file is binary
99
+ #
100
+ def binary?
101
+ binary
102
+ end
103
+
104
+ # Check if this file was renamed
105
+ #
106
+ # @return [Boolean]
107
+ #
108
+ def renamed?
109
+ status == :renamed
110
+ end
111
+
112
+ # Check if this file was copied
113
+ #
114
+ # @return [Boolean]
115
+ #
116
+ def copied?
117
+ status == :copied
118
+ end
119
+
120
+ # Check if this file was added
121
+ #
122
+ # @return [Boolean]
123
+ #
124
+ def added?
125
+ status == :added
126
+ end
127
+
128
+ # Check if this file was deleted
129
+ #
130
+ # @return [Boolean]
131
+ #
132
+ def deleted?
133
+ status == :deleted
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'file_ref'
4
+
5
+ module Git
6
+ # Immutable value object representing status info for a single file from git raw diff output
7
+ #
8
+ # Contains the source and destination file references, change status,
9
+ # similarity percentage (for renames/copies), and line change statistics.
10
+ #
11
+ #
12
+ # @example A modified file
13
+ # info = Git::DiffFileRawInfo.new(
14
+ # src: Git::FileRef.new(mode: '100644', sha: 'abc1234', path: 'lib/foo.rb'),
15
+ # dst: Git::FileRef.new(mode: '100644', sha: 'def5678', path: 'lib/foo.rb'),
16
+ # status: :modified,
17
+ # similarity: nil,
18
+ # insertions: 5,
19
+ # deletions: 2,
20
+ # binary: false
21
+ # )
22
+ #
23
+ # @example A new file (src is nil)
24
+ # info = Git::DiffFileRawInfo.new(
25
+ # src: nil,
26
+ # dst: Git::FileRef.new(mode: '100644', sha: 'abc1234', path: 'lib/new.rb'),
27
+ # status: :added,
28
+ # similarity: nil,
29
+ # insertions: 10,
30
+ # deletions: 0,
31
+ # binary: false
32
+ # )
33
+ #
34
+ # @example A renamed file
35
+ # info = Git::DiffFileRawInfo.new(
36
+ # src: Git::FileRef.new(mode: '100644', sha: 'abc1234', path: 'lib/old.rb'),
37
+ # dst: Git::FileRef.new(mode: '100644', sha: 'def5678', path: 'lib/new.rb'),
38
+ # status: :renamed,
39
+ # similarity: 95,
40
+ # insertions: 2,
41
+ # deletions: 1,
42
+ # binary: false
43
+ # )
44
+ #
45
+ # @!attribute [r] src
46
+ # @return [FileRef, nil] the source file reference, or nil for new/added files
47
+ #
48
+ # @!attribute [r] dst
49
+ # @return [FileRef, nil] the destination file reference, or nil for deleted files
50
+ #
51
+ # @!attribute [r] status
52
+ # @return [Symbol] the change status (:added, :modified, :deleted, :renamed, :copied, :type_changed)
53
+ #
54
+ # @!attribute [r] similarity
55
+ # @return [Integer, nil] similarity percentage for renames/copies (0-100), nil otherwise
56
+ #
57
+ # @!attribute [r] insertions
58
+ # @return [Integer] number of lines inserted
59
+ #
60
+ # @!attribute [r] deletions
61
+ # @return [Integer] number of lines deleted
62
+ #
63
+ # @!attribute [r] binary
64
+ # @return [Boolean] whether this is a binary file
65
+ #
66
+ # @api private
67
+ #
68
+ # Work in progress; this class is internal for now and may be made public in a future release.
69
+ DiffFileRawInfo = Data.define(:src, :dst, :status, :similarity, :insertions, :deletions, :binary) do
70
+ # Get the primary file path
71
+ #
72
+ # Returns the destination path if it exists, otherwise the source path.
73
+ # This is the "current" or "canonical" path for the file.
74
+ #
75
+ # @return [String] the file path
76
+ #
77
+ def path
78
+ dst&.path || src&.path
79
+ end
80
+
81
+ # Get the source path (for renames/copies)
82
+ #
83
+ # @return [String, nil] the source path, or nil if file was added
84
+ #
85
+ def src_path
86
+ src&.path
87
+ end
88
+
89
+ # Check if this is a binary file
90
+ #
91
+ # @return [Boolean] true if the file is binary
92
+ #
93
+ def binary?
94
+ binary
95
+ end
96
+
97
+ # Check if this file was renamed
98
+ #
99
+ # @return [Boolean]
100
+ #
101
+ def renamed?
102
+ status == :renamed
103
+ end
104
+
105
+ # Check if this file was copied
106
+ #
107
+ # @return [Boolean]
108
+ #
109
+ def copied?
110
+ status == :copied
111
+ end
112
+
113
+ # Check if this file was added
114
+ #
115
+ # @return [Boolean]
116
+ #
117
+ def added?
118
+ status == :added
119
+ end
120
+
121
+ # Check if this file was deleted
122
+ #
123
+ # @return [Boolean]
124
+ #
125
+ def deleted?
126
+ status == :deleted
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ # Immutable value object representing a single file's diff information
5
+ #
6
+ # FileDiffInfo encapsulates the parsed data from a unified diff for one file,
7
+ # including the file path, patch text, mode changes, object identifiers, and type.
8
+ #
9
+ # This class is used by DiffInfo for the legacy diff API.
10
+ #
11
+ #
12
+ # @example Create a FileDiffInfo from parsed diff output
13
+ # diff_info = Git::FileDiffInfo.new(
14
+ # path: 'lib/example.rb',
15
+ # patch: "diff --git a/lib/example.rb b/lib/example.rb\n...",
16
+ # mode: '100644',
17
+ # src: 'abc1234',
18
+ # dst: 'def5678',
19
+ # type: 'modified',
20
+ # binary: false
21
+ # )
22
+ #
23
+ # @!attribute [r] path
24
+ # @return [String] the file path relative to the repository root
25
+ #
26
+ # @!attribute [r] patch
27
+ # @return [String] the full unified diff patch text for this file
28
+ #
29
+ # @!attribute [r] mode
30
+ # @return [String] the file mode (e.g., '100644' for regular files)
31
+ #
32
+ # @!attribute [r] src
33
+ # @return [String] the source blob object identifier
34
+ #
35
+ # @!attribute [r] dst
36
+ # @return [String] the destination blob object identifier
37
+ #
38
+ # @!attribute [r] type
39
+ # @return [String] the type of change: 'modified', 'new', 'deleted', 'renamed'
40
+ #
41
+ # @!attribute [r] binary
42
+ # @return [Boolean] whether this is a binary file
43
+ #
44
+ # @api private
45
+ #
46
+ # Work in progress; this class is internal for now and may be made public in a future release.
47
+ FileDiffInfo = Data.define(
48
+ :path,
49
+ :patch,
50
+ :mode,
51
+ :src,
52
+ :dst,
53
+ :type,
54
+ :binary
55
+ ) do
56
+ # Check if this is a binary file
57
+ #
58
+ # @return [Boolean] true if the file is binary
59
+ #
60
+ def binary?
61
+ binary
62
+ end
63
+ end
64
+
65
+ # Immutable value object representing diff statistics and optional file patches
66
+ #
67
+ # DiffInfo encapsulates the parsed output from various git commands that produce
68
+ # diff statistics (like `git stash show --stat`). When patches are requested,
69
+ # it also includes the full patch information for each file.
70
+ #
71
+ # The stats hash structure:
72
+ # - `:total` - Hash with `:insertions`, `:deletions`, `:lines`, `:files` keys
73
+ # - `:files` - Hash mapping file paths to `{ insertions:, deletions: }` hashes
74
+ #
75
+ # @example Create a DiffInfo from parsed stats output
76
+ # info = Git::DiffInfo.new(
77
+ # stats: {
78
+ # total: { insertions: 10, deletions: 5, lines: 15, files: 2 },
79
+ # files: {
80
+ # 'lib/foo.rb' => { insertions: 8, deletions: 3 },
81
+ # 'lib/bar.rb' => { insertions: 2, deletions: 2 }
82
+ # }
83
+ # },
84
+ # file_patches: []
85
+ # )
86
+ #
87
+ # @example Access statistics
88
+ # info.insertions # => 10
89
+ # info.deletions # => 5
90
+ # info.lines # => 15
91
+ # info.file_count # => 2
92
+ #
93
+ # @api private
94
+ #
95
+ # Work in progress; this class is internal for now and may be made public in a future release.
96
+ #
97
+ # @!attribute [r] stats
98
+ # @return [Hash] the statistics hash with :total and :files keys
99
+ #
100
+ # @!attribute [r] file_patches
101
+ # @return [Array<FileDiffInfo>] array of file diff info objects (empty if patch not requested)
102
+ #
103
+ DiffInfo = Data.define(:stats, :file_patches) do
104
+ # Total number of lines inserted
105
+ #
106
+ # @return [Integer]
107
+ #
108
+ def insertions
109
+ stats[:total][:insertions]
110
+ end
111
+
112
+ # Total number of lines deleted
113
+ #
114
+ # @return [Integer]
115
+ #
116
+ def deletions
117
+ stats[:total][:deletions]
118
+ end
119
+
120
+ # Total number of lines changed (insertions + deletions)
121
+ #
122
+ # @return [Integer]
123
+ #
124
+ def lines
125
+ stats[:total][:lines]
126
+ end
127
+
128
+ # Number of files changed
129
+ #
130
+ # @return [Integer]
131
+ #
132
+ def file_count
133
+ stats[:total][:files]
134
+ end
135
+
136
+ # Per-file statistics hash
137
+ #
138
+ # @return [Hash<String, Hash>] mapping file paths to `{ insertions:, deletions: }` hashes
139
+ #
140
+ def file_stats
141
+ stats[:files]
142
+ end
143
+
144
+ # Check if patch information is available
145
+ #
146
+ # @return [Boolean] true if file_patches were loaded
147
+ #
148
+ def patches?
149
+ !file_patches.empty?
150
+ end
151
+
152
+ # Get patch info for a specific file
153
+ #
154
+ # @param path [String] the file path
155
+ #
156
+ # @return [FileDiffInfo, nil] the diff info or nil if not found
157
+ #
158
+ def patch_for(path)
159
+ file_patches.find { |p| p.path == path }
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ # The files and their status (e.g., added, modified, deleted) between two commits
5
+ #
6
+ # @example Iterate over path statuses
7
+ # git = Git.open('/path/to/repo')
8
+ # git.diff_path_status.each { |path, status| puts "#{path}: #{status}" }
9
+ #
10
+ # @api public
11
+ #
12
+ class DiffPathStatus
13
+ include Enumerable
14
+
15
+ # @private
16
+ #
17
+ # @param base_or_hash [Git::Repository, Hash{String => String}] either a
18
+ # repository object for legacy initialization or a pre-fetched name-status hash
19
+ #
20
+ # @param from [String, nil] the first commit/object to compare when using
21
+ # legacy initialization
22
+ #
23
+ # @param to [String, nil] the second commit/object to compare when using
24
+ # legacy initialization
25
+ #
26
+ # @param path_limiter [String, Pathname, Array, nil] path(s) to limit the
27
+ # diff when using legacy initialization
28
+ #
29
+ def initialize(base_or_hash, from = nil, to = nil, path_limiter = nil)
30
+ if base_or_hash.is_a?(Hash)
31
+ # New form: pre-fetched hash passed directly from Git::Repository::Diffing
32
+ @fetch_path_status = base_or_hash
33
+ else
34
+ # Legacy form: (base, from, to, path_limiter)
35
+ # Used by Git::Diff#path_status_provider and direct instantiation in tests.
36
+ initialize_legacy(base_or_hash, from, to, path_limiter)
37
+ end
38
+ end
39
+
40
+ # Iterates over each file path and its status code
41
+ #
42
+ # @example Print each path and status
43
+ # diff_path_status.each { |path, status| puts "#{path}: #{status}" }
44
+ #
45
+ # @return [Enumerator, Hash{String => String}] an `Enumerator` when no block
46
+ # is given; the name-status hash when a block is given
47
+ #
48
+ # @yield [path, status] each file path with its git status code
49
+ #
50
+ # @yieldparam path [String] the file path relative to the repository root
51
+ #
52
+ # @yieldparam status [String] the git status code (e.g. `"M"`, `"A"`, `"D"`)
53
+ #
54
+ # @yieldreturn [void]
55
+ #
56
+ def each(&)
57
+ return to_enum(__method__) unless block_given?
58
+
59
+ fetch_path_status.each(&)
60
+ end
61
+
62
+ # Returns the name-status report as a hash
63
+ #
64
+ # @example Basic usage
65
+ # diff_path_status.to_h #=> { "README.md" => "M", "lib/foo.rb" => "A" }
66
+ #
67
+ # @return [Hash{String => String}] a mapping of file paths to their status codes
68
+ #
69
+ def to_h
70
+ fetch_path_status
71
+ end
72
+
73
+ private
74
+
75
+ # Lazily fetches and caches the path status from the git lib
76
+ #
77
+ # @return [Hash{String => String}] a mapping of file paths to status codes
78
+ #
79
+ def fetch_path_status
80
+ @fetch_path_status ||= @base.diff_name_status(@from, @to, path_limiter: @path_limiter).to_h
81
+ end
82
+
83
+ # Sets up legacy (base, from, to, path_limiter) instance state
84
+ #
85
+ # @param base [Git::Repository] the git object
86
+ #
87
+ # @param from [String] the first commit or object to compare
88
+ #
89
+ # @param to [String, nil] the second commit or object to compare
90
+ #
91
+ # @param path_limiter [String, Pathname, Array, nil] path(s) to limit the diff
92
+ #
93
+ # @return [void]
94
+ #
95
+ # @raise [ArgumentError] when `from` or `to` starts with `"-"`
96
+ #
97
+ def initialize_legacy(base, from, to, path_limiter)
98
+ [from, to].compact.each do |arg|
99
+ raise ArgumentError, "Invalid argument: '#{arg}'" if arg.start_with?('-')
100
+ end
101
+ @base = base
102
+ @from = from
103
+ @to = to
104
+ @path_limiter = path_limiter
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'dirstat_info'
4
+
5
+ module Git
6
+ # Immutable result object from git diff commands
7
+ #
8
+ # Contains summary statistics and per-file information from various diff formats.
9
+ # The `files` array contains one of:
10
+ # - DiffFileNumstatInfo (from --numstat)
11
+ # - DiffFileRawInfo (from --raw)
12
+ # - DiffFilePatchInfo (from --patch)
13
+ #
14
+ # @api private
15
+ #
16
+ # Work in progress; this class is internal for now and may be made public in a future release.
17
+ #
18
+ # @!attribute [r] files_changed
19
+ # @return [Integer] number of files changed (from --shortstat)
20
+ #
21
+ # @!attribute [r] total_insertions
22
+ # @return [Integer] total lines inserted across all files (from --shortstat)
23
+ #
24
+ # @!attribute [r] total_deletions
25
+ # @return [Integer] total lines deleted across all files (from --shortstat)
26
+ #
27
+ # @!attribute [r] files
28
+ # @return [Array<DiffFileNumstatInfo, DiffFileRawInfo, DiffFilePatchInfo>] per-file information
29
+ #
30
+ # @!attribute [r] dirstat
31
+ # @return [DirstatInfo, nil] directory statistics if requested, nil otherwise
32
+ #
33
+ DiffResult = Data.define(:files_changed, :total_insertions, :total_deletions, :files, :dirstat)
34
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ # Lazy diff statistics for a comparison between two trees
5
+ #
6
+ # Supports comparing (1) two commits, (2) a commit against the working tree,
7
+ # or (3) the index against the working tree.
8
+ #
9
+ # @example Get the insertion and deletion counts
10
+ # stats = repo.diff_stats
11
+ # stats.insertions #=> 3
12
+ # stats.deletions #=> 1
13
+ #
14
+ # @api public
15
+ class DiffStats
16
+ # Initializes diff-stats state for a tree comparison.
17
+ #
18
+ # @private
19
+ #
20
+ # @param base [Git::Repository] the git object used to fetch diff stats
21
+ #
22
+ # @param from [String] the first commit or object to compare
23
+ #
24
+ # @param to [String, nil] the second commit or object to compare
25
+ #
26
+ # @param path_limiter [String, Pathname, Array<String, Pathname>, nil]
27
+ # path(s) to limit the diff
28
+ #
29
+ # @return [void]
30
+ #
31
+ # @raise [ArgumentError] when `from` or `to` starts with `"-"`
32
+ def initialize(base, from, to, path_limiter = nil)
33
+ # Eagerly check for invalid arguments
34
+ [from, to].compact.each do |arg|
35
+ raise ArgumentError, "Invalid argument: '#{arg}'" if arg.start_with?('-')
36
+ end
37
+
38
+ @base = base
39
+ @from = from
40
+ @to = to
41
+ @path_limiter = path_limiter
42
+ @fetch_stats = nil
43
+ end
44
+
45
+ # Returns the total number of lines deleted
46
+ #
47
+ # @example Get the deletion count
48
+ # stats = repo.diff_stats
49
+ # stats.deletions #=> 5
50
+ #
51
+ # @return [Integer] the total deletion count
52
+ def deletions
53
+ fetch_stats[:total][:deletions]
54
+ end
55
+
56
+ # Returns the total number of lines inserted
57
+ #
58
+ # @example Get the insertion count
59
+ # stats = repo.diff_stats
60
+ # stats.insertions #=> 3
61
+ #
62
+ # @return [Integer] the total insertion count
63
+ def insertions
64
+ fetch_stats[:total][:insertions]
65
+ end
66
+
67
+ # Returns the total number of lines changed (insertions + deletions)
68
+ #
69
+ # @example Get the total changed-line count
70
+ # stats = repo.diff_stats
71
+ # stats.lines #=> 8
72
+ #
73
+ # @return [Integer] the total changed-line count
74
+ def lines
75
+ fetch_stats[:total][:lines]
76
+ end
77
+
78
+ # Returns a hash of statistics for each file in the diff
79
+ #
80
+ # @example Get per-file statistics
81
+ # stats = repo.diff_stats
82
+ # stats.files #=> { "lib/foo.rb" => { insertions: 3, deletions: 1 } }
83
+ #
84
+ # @return [Hash{String=>Hash{Symbol=>Integer}}]
85
+ # per-file statistics keyed by file path
86
+ def files
87
+ fetch_stats[:files]
88
+ end
89
+
90
+ # Returns a hash of the total statistics for the diff
91
+ #
92
+ # @example Get total statistics
93
+ # stats = repo.diff_stats
94
+ # stats.total #=> { insertions: 3, deletions: 1, lines: 4, files: 1 }
95
+ #
96
+ # @return [Hash{Symbol=>Integer}]
97
+ # aggregate statistics for the entire diff
98
+ def total
99
+ fetch_stats[:total]
100
+ end
101
+
102
+ private
103
+
104
+ # Lazily fetches and caches the stats from the git lib
105
+ #
106
+ # @return [Hash] the fetched stats hash
107
+ def fetch_stats
108
+ @fetch_stats ||= @base.diff_numstat(@from, @to, path_limiter: @path_limiter)
109
+ end
110
+ end
111
+ end