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.
- checksums.yaml +4 -4
- data/.github/copilot-instructions.md +6 -0
- data/.github/prompts/iteratively-address-copilot-reviews.prompt.md +188 -0
- data/.github/skills/extract-facade-from-base-lib/KEYWORD_ARG_REMEDIATION.md +22 -0
- data/.github/skills/extract-facade-from-base-lib/SKILL.md +28 -14
- data/.github/skills/facade-implementation/SKILL.md +14 -0
- data/.github/skills/facade-test-conventions/SKILL.md +14 -0
- data/.rubocop.yml +5 -0
- data/README.md +51 -11
- data/UPGRADING.md +141 -0
- data/git.gemspec +5 -0
- data/lib/git/branch.rb +7 -18
- data/lib/git/branches.rb +2 -10
- data/lib/git/command_line/base.rb +10 -0
- data/lib/git/command_line/capturing.rb +5 -3
- data/lib/git/command_line/streaming.rb +5 -3
- data/lib/git/command_line.rb +3 -3
- data/lib/git/commands/base.rb +7 -6
- data/lib/git/commands/cat_file/batch.rb +6 -1
- data/lib/git/commands/cat_file/raw.rb +7 -1
- data/lib/git/commands/config_option_syntax/get_urlmatch.rb +5 -0
- data/lib/git/commands/show_ref/exclude_existing.rb +1 -1
- data/lib/git/commands/update_ref/batch.rb +1 -1
- data/lib/git/commands/version.rb +5 -0
- data/lib/git/commands.rb +5 -7
- data/lib/git/config.rb +17 -0
- data/lib/git/config_entry_info.rb +106 -0
- data/lib/git/configuring.rb +665 -0
- data/lib/git/deprecation.rb +9 -0
- data/lib/git/diff.rb +4 -8
- data/lib/git/diff_path_status.rb +2 -13
- data/lib/git/diff_stats.rb +1 -9
- data/lib/git/execution_context/global.rb +3 -28
- data/lib/git/execution_context/repository.rb +30 -41
- data/lib/git/execution_context.rb +43 -24
- data/lib/git/log.rb +3 -9
- data/lib/git/object.rb +14 -21
- data/lib/git/parsers/config_entry.rb +110 -0
- data/lib/git/parsers/ls_remote.rb +79 -0
- data/lib/git/remote.rb +7 -20
- data/lib/git/repository/branching.rb +183 -12
- data/lib/git/repository/committing.rb +64 -68
- data/lib/git/repository/configuring.rb +208 -13
- data/lib/git/repository/context_helpers.rb +264 -0
- data/lib/git/repository/factories.rb +682 -0
- data/lib/git/repository/inspecting.rb +99 -0
- data/lib/git/repository/maintenance.rb +65 -0
- data/lib/git/repository/merging.rb +63 -1
- data/lib/git/repository/object_operations.rb +133 -35
- data/lib/git/repository/path_resolver.rb +1 -1
- data/lib/git/repository/remote_operations.rb +166 -21
- data/lib/git/repository/staging.rb +187 -23
- data/lib/git/repository/stashing.rb +39 -3
- data/lib/git/repository/status_operations.rb +21 -0
- data/lib/git/repository.rb +68 -129
- data/lib/git/stash.rb +2 -9
- data/lib/git/stashes.rb +2 -7
- data/lib/git/status.rb +8 -17
- data/lib/git/version.rb +2 -2
- data/lib/git/worktree.rb +2 -15
- data/lib/git/worktrees.rb +2 -15
- data/lib/git.rb +180 -77
- data/redesign/3_architecture_implementation.md +148 -111
- data/redesign/Phase 4 - Step A.md +360 -0
- data/redesign/beta_release.md +107 -0
- data/redesign/c1c2_audit.md +566 -0
- data/redesign/c1c2_bucket6_lib_orphans.md +626 -0
- data/redesign/config_design.rb +501 -0
- metadata +19 -5
- data/lib/git/base.rb +0 -1204
- 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
|
|
12
|
-
#
|
|
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::
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
137
|
+
# @param opts [Hash] options for the checkout command
|
|
93
138
|
#
|
|
94
|
-
# @option
|
|
139
|
+
# @option opts [Boolean, nil] :force (nil) discard local changes when
|
|
95
140
|
# switching branches
|
|
96
141
|
#
|
|
97
|
-
# @option
|
|
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
|
|
148
|
+
# @option opts [Boolean, String, nil] :b (nil) alias for `:new_branch`
|
|
104
149
|
#
|
|
105
|
-
# @option
|
|
150
|
+
# @option opts [Boolean, nil] :f (nil) alias for `:force`
|
|
106
151
|
#
|
|
107
|
-
# @option
|
|
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,
|
|
117
|
-
if branch.is_a?(Hash) &&
|
|
118
|
-
|
|
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, **
|
|
167
|
+
SharedPrivate.assert_valid_opts!(CHECKOUT_ALLOWED_OPTS, **opts)
|
|
123
168
|
|
|
124
|
-
target, translated_opts = Private.translate_checkout_opts(branch,
|
|
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
|
-
# @
|
|
31
|
+
# @example Commit with a message
|
|
32
|
+
# repo.commit('Add README')
|
|
32
33
|
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
34
|
+
# @example Amend the previous commit, reusing its message
|
|
35
|
+
# repo.commit(nil, amend: true)
|
|
35
36
|
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
37
|
+
# @example Stage all modified files and commit
|
|
38
|
+
# repo.commit('Cleanup', all: true)
|
|
38
39
|
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
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
|
-
#
|
|
43
|
-
# (e.g. when using `:amend` to reuse the previous message)
|
|
43
|
+
# @param opts [Hash] options for the commit command
|
|
44
44
|
#
|
|
45
|
-
#
|
|
45
|
+
# @option opts [Boolean, nil] :all (nil) automatically stage modified and
|
|
46
|
+
# deleted files before committing
|
|
46
47
|
#
|
|
47
|
-
#
|
|
48
|
-
#
|
|
48
|
+
# @option opts [Boolean, nil] :amend (nil) replace the tip of the current
|
|
49
|
+
# branch with a new commit
|
|
49
50
|
#
|
|
50
|
-
#
|
|
51
|
-
#
|
|
51
|
+
# @option opts [Boolean, nil] :allow_empty (nil) allow committing with no
|
|
52
|
+
# changes
|
|
52
53
|
#
|
|
53
|
-
#
|
|
54
|
-
#
|
|
54
|
+
# @option opts [Boolean, nil] :allow_empty_message (nil) allow committing
|
|
55
|
+
# with an empty message
|
|
55
56
|
#
|
|
56
|
-
#
|
|
57
|
-
#
|
|
57
|
+
# @option opts [String] :author (nil) override the commit author in
|
|
58
|
+
# `A U Thor <author@example.com>` format
|
|
58
59
|
#
|
|
59
|
-
#
|
|
60
|
-
# `A U Thor <author@example.com>` format
|
|
60
|
+
# @option opts [String] :date (nil) override the author date
|
|
61
61
|
#
|
|
62
|
-
#
|
|
62
|
+
# @option opts [Boolean, nil] :gpg_sign (nil) GPG-sign the commit
|
|
63
63
|
#
|
|
64
|
-
#
|
|
64
|
+
# @option opts [Boolean, nil] :no_gpg_sign (nil) disable GPG signing
|
|
65
65
|
#
|
|
66
|
-
#
|
|
66
|
+
# @option opts [Boolean, nil] :no_verify (nil) bypass the pre-commit and
|
|
67
|
+
# commit-msg hooks
|
|
67
68
|
#
|
|
68
|
-
#
|
|
69
|
-
# commit-msg hooks
|
|
69
|
+
# @return [String] git's stdout from the commit
|
|
70
70
|
#
|
|
71
|
-
#
|
|
71
|
+
# @raise [ArgumentError] when unsupported options are provided
|
|
72
72
|
#
|
|
73
|
-
#
|
|
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 =
|
|
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
|
|
92
|
+
# Equivalent to calling {#commit} with `all: true` merged into `opts`.
|
|
94
93
|
#
|
|
95
|
-
# @
|
|
94
|
+
# @example Commit all changes with a message
|
|
95
|
+
# repo.commit_all('Update everything')
|
|
96
96
|
#
|
|
97
|
-
#
|
|
98
|
-
# repo.commit_all('Update everything')
|
|
97
|
+
# @param message [String] the commit message
|
|
99
98
|
#
|
|
100
|
-
#
|
|
99
|
+
# @param opts [Hash] additional options forwarded to {#commit}
|
|
101
100
|
#
|
|
102
|
-
#
|
|
101
|
+
# @return [String] git's stdout from the commit
|
|
103
102
|
#
|
|
104
|
-
#
|
|
103
|
+
# @raise [ArgumentError] when unsupported options are provided
|
|
105
104
|
#
|
|
106
|
-
#
|
|
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(
|
|
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
|
-
# @
|
|
116
|
+
# @example Commit a tree with a parent
|
|
117
|
+
# repo.commit_tree('deadbeef', message: 'snapshot', parent: 'HEAD')
|
|
120
118
|
#
|
|
121
|
-
#
|
|
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
|
-
#
|
|
121
|
+
# @param opts [Hash] options for the commit-tree command
|
|
125
122
|
#
|
|
126
|
-
#
|
|
123
|
+
# @option opts [String] :m (nil) the commit message (short form)
|
|
127
124
|
#
|
|
128
|
-
#
|
|
125
|
+
# @option opts [String] :message (nil) the commit message (normalized
|
|
126
|
+
# to `:m` before passing to the command)
|
|
129
127
|
#
|
|
130
|
-
#
|
|
131
|
-
# to `:m` before passing to the command)
|
|
128
|
+
# @option opts [String, Array<String>] :p (nil) parent commit SHA(s)
|
|
132
129
|
#
|
|
133
|
-
#
|
|
130
|
+
# @option opts [String] :parent (nil) a single parent commit SHA
|
|
131
|
+
# (normalized to `:p`)
|
|
134
132
|
#
|
|
135
|
-
#
|
|
136
|
-
#
|
|
133
|
+
# @option opts [Array<String>] :parents (nil) multiple parent commit
|
|
134
|
+
# SHAs (normalized to `:p`)
|
|
137
135
|
#
|
|
138
|
-
#
|
|
139
|
-
# SHAs (normalized to `:p`)
|
|
136
|
+
# @return [String] the SHA of the newly created commit object
|
|
140
137
|
#
|
|
141
|
-
#
|
|
138
|
+
# @raise [ArgumentError] when unsupported options are provided
|
|
142
139
|
#
|
|
143
|
-
#
|
|
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,
|
|
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
|
-
# @
|
|
171
|
+
# @example Commit the current index as a snapshot
|
|
172
|
+
# commit_sha = repo.write_and_commit_tree(message: 'snapshot')
|
|
176
173
|
#
|
|
177
|
-
#
|
|
178
|
-
# commit_sha = repo.write_and_commit_tree(message: 'snapshot')
|
|
174
|
+
# @param opts [Hash] options forwarded to {#commit_tree}
|
|
179
175
|
#
|
|
180
|
-
#
|
|
176
|
+
# @return [String] the SHA of the newly created commit object
|
|
181
177
|
#
|
|
182
|
-
#
|
|
178
|
+
# @raise [ArgumentError] when unsupported options are provided
|
|
183
179
|
#
|
|
184
|
-
#
|
|
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
|