git 5.0.0.beta.1 → 5.0.0.beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.github/copilot-instructions.md +6 -0
  3. data/.github/prompts/iteratively-address-copilot-reviews.prompt.md +188 -0
  4. data/.github/skills/extract-facade-from-base-lib/KEYWORD_ARG_REMEDIATION.md +22 -0
  5. data/.github/skills/extract-facade-from-base-lib/SKILL.md +28 -14
  6. data/.github/skills/facade-implementation/SKILL.md +14 -0
  7. data/.github/skills/facade-test-conventions/SKILL.md +14 -0
  8. data/.rubocop.yml +5 -0
  9. data/README.md +51 -11
  10. data/UPGRADING.md +141 -0
  11. data/git.gemspec +5 -0
  12. data/lib/git/branch.rb +7 -18
  13. data/lib/git/branches.rb +2 -10
  14. data/lib/git/command_line/base.rb +10 -0
  15. data/lib/git/command_line/capturing.rb +5 -3
  16. data/lib/git/command_line/streaming.rb +5 -3
  17. data/lib/git/command_line.rb +3 -3
  18. data/lib/git/commands/base.rb +7 -6
  19. data/lib/git/commands/cat_file/batch.rb +6 -1
  20. data/lib/git/commands/cat_file/raw.rb +7 -1
  21. data/lib/git/commands/config_option_syntax/get_urlmatch.rb +5 -0
  22. data/lib/git/commands/show_ref/exclude_existing.rb +1 -1
  23. data/lib/git/commands/update_ref/batch.rb +1 -1
  24. data/lib/git/commands/version.rb +5 -0
  25. data/lib/git/commands.rb +5 -7
  26. data/lib/git/config.rb +17 -0
  27. data/lib/git/config_entry_info.rb +106 -0
  28. data/lib/git/configuring.rb +665 -0
  29. data/lib/git/deprecation.rb +9 -0
  30. data/lib/git/diff.rb +4 -8
  31. data/lib/git/diff_path_status.rb +2 -13
  32. data/lib/git/diff_stats.rb +1 -9
  33. data/lib/git/execution_context/global.rb +3 -28
  34. data/lib/git/execution_context/repository.rb +30 -41
  35. data/lib/git/execution_context.rb +43 -24
  36. data/lib/git/log.rb +3 -9
  37. data/lib/git/object.rb +14 -21
  38. data/lib/git/parsers/config_entry.rb +110 -0
  39. data/lib/git/parsers/ls_remote.rb +79 -0
  40. data/lib/git/remote.rb +7 -20
  41. data/lib/git/repository/branching.rb +183 -12
  42. data/lib/git/repository/committing.rb +64 -68
  43. data/lib/git/repository/configuring.rb +208 -13
  44. data/lib/git/repository/context_helpers.rb +264 -0
  45. data/lib/git/repository/factories.rb +682 -0
  46. data/lib/git/repository/inspecting.rb +99 -0
  47. data/lib/git/repository/maintenance.rb +65 -0
  48. data/lib/git/repository/merging.rb +63 -1
  49. data/lib/git/repository/object_operations.rb +133 -35
  50. data/lib/git/repository/path_resolver.rb +1 -1
  51. data/lib/git/repository/remote_operations.rb +166 -21
  52. data/lib/git/repository/staging.rb +187 -23
  53. data/lib/git/repository/stashing.rb +39 -3
  54. data/lib/git/repository/status_operations.rb +21 -0
  55. data/lib/git/repository.rb +68 -129
  56. data/lib/git/stash.rb +2 -9
  57. data/lib/git/stashes.rb +2 -7
  58. data/lib/git/status.rb +8 -17
  59. data/lib/git/version.rb +2 -2
  60. data/lib/git/worktree.rb +2 -15
  61. data/lib/git/worktrees.rb +2 -15
  62. data/lib/git.rb +180 -77
  63. data/redesign/3_architecture_implementation.md +148 -111
  64. data/redesign/Phase 4 - Step A.md +360 -0
  65. data/redesign/beta_release.md +107 -0
  66. data/redesign/c1c2_audit.md +566 -0
  67. data/redesign/c1c2_bucket6_lib_orphans.md +626 -0
  68. data/redesign/config_design.rb +501 -0
  69. metadata +19 -5
  70. data/lib/git/base.rb +0 -1204
  71. data/lib/git/lib.rb +0 -2855
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ module Parsers
5
+ # Parser for `git ls-remote` command output
6
+ #
7
+ # @api private
8
+ #
9
+ module LsRemote
10
+ module_function
11
+
12
+ # Parse `git ls-remote` stdout lines into a structured Hash
13
+ #
14
+ # @param lines [Array<String>] the individual stdout lines
15
+ #
16
+ # @return [Hash{String => Hash}] a map of ref type to ref data
17
+ #
18
+ # The structure differs by key:
19
+ #
20
+ # - `"head"` maps directly to `{ ref: String, sha: String }`
21
+ # - Other keys (e.g. `"branches"`, `"tags"`) map to
22
+ # `{ name => { ref: String, sha: String } }`
23
+ #
24
+ def parse_output(lines)
25
+ lines.each_with_object(Hash.new { |h, k| h[k] = {} }) do |line, hsh|
26
+ type, name, value = parse_line(line)
27
+ if name
28
+ hsh[type][name] = value
29
+ else
30
+ hsh[type].update(value)
31
+ end
32
+ end
33
+ end
34
+
35
+ # Parse a single `git ls-remote` output line
36
+ #
37
+ # @param line [String] a single line from ls-remote stdout
38
+ #
39
+ # @return [Array(String, String|nil, Hash)] `[type, name, value]` where
40
+ # `value` is `{ ref: String, sha: String }`
41
+ #
42
+ # @raise [Git::UnexpectedResultError] if the line is not in `<sha>\t<ref>` format
43
+ #
44
+ def parse_line(line)
45
+ unless line.include?("\t") && line.match?(/\A[0-9a-f]{4,}\t/)
46
+ raise Git::UnexpectedResultError, "Unexpected ls-remote output line: #{line.inspect}"
47
+ end
48
+
49
+ sha, info = line.split("\t", 2)
50
+ ref, type, name = info.split('/', 3)
51
+
52
+ type ||= 'head'
53
+ type = 'branches' if type == 'heads'
54
+
55
+ value = { ref: ref, sha: sha }
56
+
57
+ [type, name, value]
58
+ end
59
+
60
+ # Parse `git ls-remote --symref <repo> HEAD` output into a branch name
61
+ #
62
+ # @param output [String] command stdout
63
+ #
64
+ # @return [String] the default branch name
65
+ #
66
+ # @raise [Git::UnexpectedResultError] when the branch cannot be determined
67
+ #
68
+ def parse_default_branch(output)
69
+ match_data = output.match(%r{^ref: refs/remotes/[^/]+/(?<default_branch>[^\t]+)\t})
70
+ return match_data[:default_branch] if match_data
71
+
72
+ match_data = output.match(%r{^ref: refs/heads/(?<default_branch>[^\t]+)\tHEAD$})
73
+ return match_data[:default_branch] if match_data
74
+
75
+ raise Git::UnexpectedResultError, 'Unable to determine the default branch'
76
+ end
77
+ end
78
+ end
79
+ end
data/lib/git/remote.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'git/base'
4
3
  require 'git/branch'
5
4
  require 'git/branch_info'
6
5
 
@@ -8,9 +7,8 @@ module Git
8
7
  # A remote in a Git repository
9
8
  #
10
9
  # Remote objects provide access to remote metadata and operations like fetch,
11
- # merge, and remove. They should be obtained via {Git::Base#remote} or the
12
- # `remote` factory method on a `Git::Repository` instance, not constructed
13
- # directly.
10
+ # merge, and remove. They should be obtained via `Git::Repository#remote`,
11
+ # not constructed directly.
14
12
  #
15
13
  # @example Getting a remote
16
14
  # git = Git.open('.')
@@ -40,16 +38,11 @@ module Git
40
38
 
41
39
  # Initialize a new Remote object
42
40
  #
43
- # @param base [Git::Base, Git::Repository] the git repository
44
- #
45
- # Accepts either a {Git::Base} (legacy) or a {Git::Repository} (new form).
46
- # The `is_a?(Git::Base)` guard will be removed when {Git::Base} is deleted
47
- # in Phase 4.
41
+ # @param base [Git::Repository] the git repository
48
42
  #
49
43
  # @param name [String] the remote name (e.g. `'origin'`)
50
44
  #
51
- # @note Use {Git::Base#remote} or the `remote` factory method on a
52
- # `Git::Repository` instance instead of constructing directly
45
+ # @note Use `Git::Repository#remote` instead of constructing directly
53
46
  #
54
47
  # @api private
55
48
  #
@@ -66,7 +59,7 @@ module Git
66
59
  # @example Fetch from origin
67
60
  # git.remote('origin').fetch
68
61
  #
69
- # @param opts [Hash] fetch options (see {Git::Base#fetch})
62
+ # @param opts [Hash] fetch options (see `Git::Repository#fetch`)
70
63
  #
71
64
  # @return [String] git's stdout from the fetch
72
65
  #
@@ -119,7 +112,7 @@ module Git
119
112
  # @raise [Git::FailedError] if git exits with a non-zero exit status
120
113
  #
121
114
  def remove
122
- remote_repository.remove_remote(@name)
115
+ remote_repository.remote_remove(@name)
123
116
  end
124
117
 
125
118
  # Returns the name of this remote as a string
@@ -135,18 +128,12 @@ module Git
135
128
 
136
129
  private
137
130
 
138
- # Resolves the {Git::Repository} for this remote
139
- #
140
- # Accepts either a {Git::Repository} (new form) or a {Git::Base} (legacy).
141
- # The `is_a?(Git::Base)` guard will be removed when {Git::Base} is deleted
142
- # in Phase 4.
143
- #
144
131
  # @return [Git::Repository]
145
132
  #
146
133
  # @api private
147
134
  #
148
135
  def remote_repository
149
- @base.is_a?(Git::Base) ? @base.facade_repository : @base
136
+ @base
150
137
  end
151
138
 
152
139
  def build_branch_info(refname)
@@ -11,7 +11,9 @@ require 'git/commands/branch/show_current'
11
11
  require 'git/commands/checkout/branch'
12
12
  require 'git/commands/checkout/files'
13
13
  require 'git/commands/checkout_index'
14
+ require 'git/commands/rev_parse'
14
15
  require 'git/commands/update_ref/update'
16
+ require 'git/commands/symbolic_ref/update'
15
17
  require 'git/parsers/branch'
16
18
  require 'git/repository/shared_private'
17
19
 
@@ -24,7 +26,17 @@ module Git
24
26
  #
25
27
  # @api public
26
28
  #
27
- module Branching
29
+ module Branching # rubocop:disable Metrics/ModuleLength
30
+ # Represents the state of HEAD in a repository
31
+ #
32
+ # @!attribute [r] state
33
+ # @return [Symbol] one of `:active`, `:unborn`, or `:detached`
34
+ #
35
+ # @!attribute [r] name
36
+ # @return [String] the branch name, or `'HEAD'` when detached
37
+ #
38
+ HeadState = Data.define(:state, :name)
39
+
28
40
  # Option keys accepted by {#checkout}
29
41
  #
30
42
  CHECKOUT_ALLOWED_OPTS = %i[force f new_branch b start_point].freeze
@@ -54,6 +66,39 @@ module Git
54
66
  name.empty? ? 'HEAD' : name
55
67
  end
56
68
 
69
+ # Returns the current HEAD state as a structured value object
70
+ #
71
+ # HEAD can be in one of three states:
72
+ #
73
+ # - **`:active`** — HEAD points to a branch ref that has at least one commit.
74
+ # - **`:unborn`** — HEAD points to a branch ref that has been created but has
75
+ # no commits yet (e.g. immediately after `git init` before any commit).
76
+ # - **`:detached`** — HEAD points directly to a commit SHA rather than a branch.
77
+ #
78
+ # @example Active branch
79
+ # repo.current_branch_state
80
+ # # => #<data Git::Repository::Branching::HeadState state=:active, name="main">
81
+ #
82
+ # @example Unborn branch (no commits yet)
83
+ # repo.current_branch_state
84
+ # # => #<data Git::Repository::Branching::HeadState state=:unborn, name="main">
85
+ #
86
+ # @example Detached HEAD
87
+ # repo.current_branch_state
88
+ # # => #<data Git::Repository::Branching::HeadState state=:detached, name="HEAD">
89
+ #
90
+ # @return [Git::Repository::Branching::HeadState] the current HEAD state
91
+ #
92
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
93
+ #
94
+ def current_branch_state
95
+ branch_name = Git::Commands::Branch::ShowCurrent.new(@execution_context).call.stdout.strip
96
+ return HeadState.new(state: :detached, name: 'HEAD') if branch_name.empty?
97
+
98
+ state = Private.get_branch_state(@execution_context, branch_name)
99
+ HeadState.new(state: state, name: branch_name)
100
+ end
101
+
57
102
  # Restore working tree files from a tree-ish
58
103
  #
59
104
  # @example Restore README.md to its HEAD state
@@ -89,22 +134,22 @@ module Git
89
134
  # @param branch [String, nil] the branch to check out; defaults to nil
90
135
  # (i.e. restore HEAD state)
91
136
  #
92
- # @param options [Hash] options for the checkout command
137
+ # @param opts [Hash] options for the checkout command
93
138
  #
94
- # @option options [Boolean, nil] :force (nil) discard local changes when
139
+ # @option opts [Boolean, nil] :force (nil) discard local changes when
95
140
  # switching branches
96
141
  #
97
- # @option options [Boolean, String, nil] :new_branch (nil) when `true`,
142
+ # @option opts [Boolean, String, nil] :new_branch (nil) when `true`,
98
143
  # creates a new branch named `branch` from `:start_point`
99
144
  #
100
145
  # When a `String`, creates a new branch with that name, using `branch`
101
146
  # as the start point.
102
147
  #
103
- # @option options [Boolean, String, nil] :b (nil) alias for `:new_branch`
148
+ # @option opts [Boolean, String, nil] :b (nil) alias for `:new_branch`
104
149
  #
105
- # @option options [Boolean, nil] :f (nil) alias for `:force`
150
+ # @option opts [Boolean, nil] :f (nil) alias for `:force`
106
151
  #
107
- # @option options [String, nil] :start_point (nil) the commit or branch to
152
+ # @option opts [String, nil] :start_point (nil) the commit or branch to
108
153
  # start the new branch from; used together with `new_branch: true`
109
154
  #
110
155
  # @return [String] git's stdout from the checkout
@@ -113,15 +158,15 @@ module Git
113
158
  #
114
159
  # @raise [Git::FailedError] if git exits with a non-zero exit status
115
160
  #
116
- def checkout(branch = nil, options = {})
117
- if branch.is_a?(Hash) && options.empty?
118
- options = branch
161
+ def checkout(branch = nil, opts = {})
162
+ if branch.is_a?(Hash) && opts.empty?
163
+ opts = branch
119
164
  branch = nil
120
165
  end
121
166
 
122
- SharedPrivate.assert_valid_opts!(CHECKOUT_ALLOWED_OPTS, **options)
167
+ SharedPrivate.assert_valid_opts!(CHECKOUT_ALLOWED_OPTS, **opts)
123
168
 
124
- target, translated_opts = Private.translate_checkout_opts(branch, options)
169
+ target, translated_opts = Private.translate_checkout_opts(branch, opts)
125
170
  Git::Commands::Checkout::Branch.new(@execution_context).call(target, **translated_opts).stdout
126
171
  end
127
172
 
@@ -215,6 +260,71 @@ module Git
215
260
  local_branch?(branch) || remote_branch?(branch)
216
261
  end
217
262
 
263
+ # Checks whether the named branch exists locally
264
+ #
265
+ # @example Check whether main exists locally
266
+ # repo.is_local_branch?('main') # => true
267
+ #
268
+ # @param branch [String] the local branch name to look up
269
+ #
270
+ # @return [Boolean] `true` if the branch exists locally, `false` otherwise
271
+ #
272
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
273
+ #
274
+ # @deprecated use {#local_branch?} instead
275
+ #
276
+ def is_local_branch?(branch) # rubocop:disable Naming/PredicatePrefix
277
+ Git::Deprecation.warn(
278
+ 'Git::Repository#is_local_branch? is deprecated and will be removed in a future version. ' \
279
+ 'Use Git::Repository#local_branch? instead.'
280
+ )
281
+ local_branch?(branch)
282
+ end
283
+
284
+ # Checks whether the named branch exists as a remote-tracking branch
285
+ #
286
+ # @example Check whether master exists on any remote
287
+ # repo.is_remote_branch?('master') # => true
288
+ #
289
+ # @param branch [String] the short branch name to look up across all remotes
290
+ #
291
+ # @return [Boolean] `true` if a remote-tracking branch with that short name
292
+ # exists, `false` otherwise
293
+ #
294
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
295
+ #
296
+ # @deprecated use {#remote_branch?} instead
297
+ #
298
+ def is_remote_branch?(branch) # rubocop:disable Naming/PredicatePrefix
299
+ Git::Deprecation.warn(
300
+ 'Git::Repository#is_remote_branch? is deprecated and will be removed in a future version. ' \
301
+ 'Use Git::Repository#remote_branch? instead.'
302
+ )
303
+ remote_branch?(branch)
304
+ end
305
+
306
+ # Checks whether the named branch exists locally or as a remote-tracking branch
307
+ #
308
+ # @example Check whether main exists anywhere
309
+ # repo.is_branch?('main') # => true
310
+ #
311
+ # @param branch [String] the branch name to look up
312
+ #
313
+ # @return [Boolean] `true` if the branch exists locally or remotely,
314
+ # `false` otherwise
315
+ #
316
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
317
+ #
318
+ # @deprecated use {#branch?} instead
319
+ #
320
+ def is_branch?(branch) # rubocop:disable Naming/PredicatePrefix
321
+ Git::Deprecation.warn(
322
+ 'Git::Repository#is_branch? is deprecated and will be removed in a future version. ' \
323
+ 'Use Git::Repository#branch? instead.'
324
+ )
325
+ branch?(branch)
326
+ end
327
+
218
328
  # Option keys accepted by {#branch_new}
219
329
  #
220
330
  BRANCH_NEW_ALLOWED_OPTS = %i[].freeze
@@ -307,6 +417,39 @@ module Git
307
417
  result.stdout.strip
308
418
  end
309
419
 
420
+ # Writes the HEAD symbolic ref to point at the given branch
421
+ #
422
+ # Sets `HEAD` to `refs/heads/<branch_name>` via `git symbolic-ref`. This is
423
+ # equivalent to running `git symbolic-ref HEAD refs/heads/<branch_name>` on
424
+ # the command line and is the mechanism git uses internally for branch
425
+ # renaming and orphan-branch checkout.
426
+ #
427
+ # @example Change HEAD to point to an existing branch
428
+ # repo.change_head_branch('main')
429
+ #
430
+ # @example Initialize a repository with a custom default branch name (unborn-branch pattern)
431
+ # repo = Git.init('/path/to/repo')
432
+ # repo.change_head_branch('my-branch')
433
+ # # HEAD now points at refs/heads/my-branch before any commits exist
434
+ #
435
+ # @note Pointing HEAD at a branch that does not yet exist places the
436
+ # repository in unborn-branch state. This is intentional for repository
437
+ # initialization workflows — for example, setting a custom default branch
438
+ # name before any commits land — but is unexpected if done by mistake.
439
+ # The repository will appear to have no commits until the first commit is
440
+ # made on the new branch.
441
+ #
442
+ # @param branch_name [String] the branch name to point HEAD at
443
+ #
444
+ # @return [void]
445
+ #
446
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
447
+ #
448
+ def change_head_branch(branch_name)
449
+ Git::Commands::SymbolicRef::Update.new(@execution_context).call('HEAD', "refs/heads/#{branch_name}")
450
+ nil
451
+ end
452
+
310
453
  # Returns the `git branch --list --contains` stdout for a given commit
311
454
  #
312
455
  # The output format is the human-readable `git branch` listing: each
@@ -472,6 +615,34 @@ module Git
472
615
  module Private
473
616
  module_function
474
617
 
618
+ # Determines whether the given branch ref points to an existing commit
619
+ #
620
+ # Returns `:active` when the branch ref resolves successfully. Returns
621
+ # `:unborn` when the branch ref exists but has no commits yet (exit
622
+ # status 1 with empty stderr from `git rev-parse --verify --quiet`).
623
+ # Re-raises for any other failure.
624
+ #
625
+ # @param execution_context [Git::ExecutionContext::Repository] the
626
+ # execution context for git commands
627
+ #
628
+ # @param branch_name [String] the branch name to verify
629
+ #
630
+ # @return [:active, :unborn] the branch ref state
631
+ #
632
+ # @raise [Git::FailedError] if git exits with a failure unrelated to an
633
+ # unborn branch
634
+ #
635
+ # @api private
636
+ #
637
+ def get_branch_state(execution_context, branch_name)
638
+ Git::Commands::RevParse.new(execution_context).call(branch_name, verify: true, quiet: true)
639
+ :active
640
+ rescue Git::FailedError => e
641
+ raise unless e.result.status.exitstatus == 1 && e.result.stderr.empty?
642
+
643
+ :unborn
644
+ end
645
+
475
646
  # Translates legacy checkout options to the new command interface
476
647
  #
477
648
  # Legacy callers passed combinations like:
@@ -28,53 +28,52 @@ module Git
28
28
 
29
29
  # Record staged changes as a new commit
30
30
  #
31
- # @overload commit(message = nil, **options)
31
+ # @example Commit with a message
32
+ # repo.commit('Add README')
32
33
  #
33
- # @example Commit with a message
34
- # repo.commit('Add README')
34
+ # @example Amend the previous commit, reusing its message
35
+ # repo.commit(nil, amend: true)
35
36
  #
36
- # @example Amend the previous commit, reusing its message
37
- # repo.commit(nil, amend: true)
37
+ # @example Stage all modified files and commit
38
+ # repo.commit('Cleanup', all: true)
38
39
  #
39
- # @example Stage all modified files and commit
40
- # repo.commit('Cleanup', all: true)
40
+ # @param message [String, nil] the commit message; pass `nil` to omit
41
+ # (e.g. when using `:amend` to reuse the previous message)
41
42
  #
42
- # @param message [String, nil] the commit message; pass `nil` to omit
43
- # (e.g. when using `:amend` to reuse the previous message)
43
+ # @param opts [Hash] options for the commit command
44
44
  #
45
- # @param options [Hash] options for the commit command
45
+ # @option opts [Boolean, nil] :all (nil) automatically stage modified and
46
+ # deleted files before committing
46
47
  #
47
- # @option options [Boolean, nil] :all (nil) automatically stage modified and
48
- # deleted files before committing
48
+ # @option opts [Boolean, nil] :amend (nil) replace the tip of the current
49
+ # branch with a new commit
49
50
  #
50
- # @option options [Boolean, nil] :amend (nil) replace the tip of the current
51
- # branch with a new commit
51
+ # @option opts [Boolean, nil] :allow_empty (nil) allow committing with no
52
+ # changes
52
53
  #
53
- # @option options [Boolean, nil] :allow_empty (nil) allow committing with no
54
- # changes
54
+ # @option opts [Boolean, nil] :allow_empty_message (nil) allow committing
55
+ # with an empty message
55
56
  #
56
- # @option options [Boolean, nil] :allow_empty_message (nil) allow committing
57
- # with an empty message
57
+ # @option opts [String] :author (nil) override the commit author in
58
+ # `A U Thor <author@example.com>` format
58
59
  #
59
- # @option options [String] :author (nil) override the commit author in
60
- # `A U Thor <author@example.com>` format
60
+ # @option opts [String] :date (nil) override the author date
61
61
  #
62
- # @option options [String] :date (nil) override the author date
62
+ # @option opts [Boolean, nil] :gpg_sign (nil) GPG-sign the commit
63
63
  #
64
- # @option options [Boolean, nil] :gpg_sign (nil) GPG-sign the commit
64
+ # @option opts [Boolean, nil] :no_gpg_sign (nil) disable GPG signing
65
65
  #
66
- # @option options [Boolean, nil] :no_gpg_sign (nil) disable GPG signing
66
+ # @option opts [Boolean, nil] :no_verify (nil) bypass the pre-commit and
67
+ # commit-msg hooks
67
68
  #
68
- # @option options [Boolean, nil] :no_verify (nil) bypass the pre-commit and
69
- # commit-msg hooks
69
+ # @return [String] git's stdout from the commit
70
70
  #
71
- # @return [String] git's stdout from the commit
71
+ # @raise [ArgumentError] when unsupported options are provided
72
72
  #
73
- # @raise [ArgumentError] when unsupported options are provided
74
- #
75
- # @raise [Git::FailedError] when git exits with a non-zero exit status
73
+ # @raise [Git::FailedError] when git exits with a non-zero exit status
76
74
  #
77
- def commit(message = nil, **opts)
75
+ def commit(message, opts = {})
76
+ opts = opts.dup
78
77
  if opts.key?(:add_all)
79
78
  Git::Deprecation.warn('The :add_all option for #commit is deprecated, use :all instead')
80
79
  opts[:all] = opts.delete(:add_all)
@@ -90,25 +89,23 @@ module Git
90
89
 
91
90
  # Commit all modified tracked files without explicitly staging them first
92
91
  #
93
- # Equivalent to `commit(message, all: true, **options)`.
92
+ # Equivalent to calling {#commit} with `all: true` merged into `opts`.
94
93
  #
95
- # @overload commit_all(message, **options)
94
+ # @example Commit all changes with a message
95
+ # repo.commit_all('Update everything')
96
96
  #
97
- # @example Commit all changes with a message
98
- # repo.commit_all('Update everything')
97
+ # @param message [String] the commit message
99
98
  #
100
- # @param message [String] the commit message
99
+ # @param opts [Hash] additional options forwarded to {#commit}
101
100
  #
102
- # @param options [Hash] additional options forwarded to {#commit}
101
+ # @return [String] git's stdout from the commit
103
102
  #
104
- # @return [String] git's stdout from the commit
103
+ # @raise [ArgumentError] when unsupported options are provided
105
104
  #
106
- # @raise [ArgumentError] when unsupported options are provided
107
- #
108
- # @raise [Git::FailedError] when git exits with a non-zero exit status
105
+ # @raise [Git::FailedError] when git exits with a non-zero exit status
109
106
  #
110
- def commit_all(*, **)
111
- commit(*, all: true, **)
107
+ def commit_all(message, opts = {})
108
+ commit(message, opts.merge(all: true))
112
109
  end
113
110
 
114
111
  # Create a commit object from a tree SHA without moving HEAD
@@ -116,37 +113,36 @@ module Git
116
113
  # Unlike {#commit}, this does not read the index; it directly wraps
117
114
  # `git commit-tree`.
118
115
  #
119
- # @overload commit_tree(tree, **options)
116
+ # @example Commit a tree with a parent
117
+ # repo.commit_tree('deadbeef', message: 'snapshot', parent: 'HEAD')
120
118
  #
121
- # @example Commit a tree with a parent
122
- # repo.commit_tree('deadbeef', message: 'snapshot', parent: 'HEAD')
119
+ # @param tree [String, nil] the tree SHA to commit; defaults to `nil`
123
120
  #
124
- # @param tree [String] the tree SHA to commit
121
+ # @param opts [Hash] options for the commit-tree command
125
122
  #
126
- # @param options [Hash] options for the commit-tree command
123
+ # @option opts [String] :m (nil) the commit message (short form)
127
124
  #
128
- # @option options [String] :m (nil) the commit message (short form)
125
+ # @option opts [String] :message (nil) the commit message (normalized
126
+ # to `:m` before passing to the command)
129
127
  #
130
- # @option options [String] :message (nil) the commit message (normalized
131
- # to `:m` before passing to the command)
128
+ # @option opts [String, Array<String>] :p (nil) parent commit SHA(s)
132
129
  #
133
- # @option options [String, Array<String>] :p (nil) parent commit SHA(s)
130
+ # @option opts [String] :parent (nil) a single parent commit SHA
131
+ # (normalized to `:p`)
134
132
  #
135
- # @option options [String] :parent (nil) a single parent commit SHA
136
- # (normalized to `:p`)
133
+ # @option opts [Array<String>] :parents (nil) multiple parent commit
134
+ # SHAs (normalized to `:p`)
137
135
  #
138
- # @option options [Array<String>] :parents (nil) multiple parent commit
139
- # SHAs (normalized to `:p`)
136
+ # @return [String] the SHA of the newly created commit object
140
137
  #
141
- # @return [String] the SHA of the newly created commit object
138
+ # @raise [ArgumentError] when unsupported options are provided
142
139
  #
143
- # @raise [ArgumentError] when unsupported options are provided
144
- #
145
- # @raise [Git::FailedError] when git exits with a non-zero exit status
140
+ # @raise [Git::FailedError] when git exits with a non-zero exit status
146
141
  #
147
- def commit_tree(tree, **opts)
142
+ def commit_tree(tree = nil, opts = {}) # rubocop:disable Metrics/AbcSize
148
143
  SharedPrivate.assert_valid_opts!(COMMIT_TREE_ALLOWED_OPTS, **opts)
149
144
 
145
+ opts = opts.dup
150
146
  opts[:p] = opts.delete(:parents) if opts.key?(:parents)
151
147
  opts[:p] = opts.delete(:parent) if opts.key?(:parent)
152
148
  opts[:m] = opts.delete(:message) if opts.key?(:message)
@@ -172,19 +168,19 @@ module Git
172
168
  #
173
169
  # Combines {#write_tree} and {#commit_tree} in a single call.
174
170
  #
175
- # @overload write_and_commit_tree(**options)
171
+ # @example Commit the current index as a snapshot
172
+ # commit_sha = repo.write_and_commit_tree(message: 'snapshot')
176
173
  #
177
- # @example Commit the current index as a snapshot
178
- # commit_sha = repo.write_and_commit_tree(message: 'snapshot')
174
+ # @param opts [Hash] options forwarded to {#commit_tree}
179
175
  #
180
- # @param options [Hash] options forwarded to {#commit_tree}
176
+ # @return [String] the SHA of the newly created commit object
181
177
  #
182
- # @return [String] the SHA of the newly created commit object
178
+ # @raise [ArgumentError] when unsupported options are provided
183
179
  #
184
- # @raise [Git::FailedError] when git exits with a non-zero exit status
180
+ # @raise [Git::FailedError] when git exits with a non-zero exit status
185
181
  #
186
- def write_and_commit_tree(**)
187
- commit_tree(write_tree, **)
182
+ def write_and_commit_tree(opts = {})
183
+ commit_tree(write_tree, opts)
188
184
  end
189
185
  end
190
186
  end