git 1.19.1 → 4.4.5
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/.commitlintrc.yml +38 -0
- data/.github/copilot-instructions.md +2733 -0
- data/.github/pull_request_template.md +17 -0
- data/.github/workflows/continuous_integration.yml +92 -21
- data/.github/workflows/enforce_conventional_commits.yml +29 -0
- data/.github/workflows/experimental_continuous_integration.yml +59 -0
- data/.github/workflows/release.yml +53 -0
- data/.gitignore +5 -0
- data/.husky/commit-msg +1 -0
- data/.release-please-manifest.json +3 -0
- data/.rubocop.yml +55 -0
- data/.rubocop_todo.yml +12 -0
- data/.yardopts +4 -1
- data/AI_POLICY.md +24 -0
- data/CHANGELOG.md +501 -0
- data/CODE_OF_CONDUCT.md +25 -0
- data/CONTRIBUTING.md +323 -102
- data/GOVERNANCE.md +106 -0
- data/LICENSE +1 -1
- data/MAINTAINERS.md +17 -4
- data/README.md +575 -246
- data/Rakefile +13 -55
- data/git.gemspec +36 -30
- data/lib/git/args_builder.rb +111 -0
- data/lib/git/author.rb +9 -7
- data/lib/git/base.rb +602 -173
- data/lib/git/branch.rb +318 -38
- data/lib/git/branches.rb +21 -24
- data/lib/git/command_line.rb +330 -0
- data/lib/git/command_line_result.rb +9 -3
- data/lib/git/config.rb +10 -6
- data/lib/git/diff.rb +149 -81
- data/lib/git/diff_path_status.rb +46 -0
- data/lib/git/diff_stats.rb +59 -0
- data/lib/git/errors.rb +212 -0
- data/lib/git/escaped_path.rb +2 -2
- data/lib/git/fsck_object.rb +48 -0
- data/lib/git/fsck_result.rb +121 -0
- data/lib/git/index.rb +2 -1
- data/lib/git/lib.rb +1648 -643
- data/lib/git/log.rb +143 -106
- data/lib/git/object.rb +151 -125
- data/lib/git/path.rb +23 -16
- data/lib/git/remote.rb +5 -4
- data/lib/git/repository.rb +2 -2
- data/lib/git/stash.rb +11 -12
- data/lib/git/stashes.rb +16 -15
- data/lib/git/status.rb +104 -143
- data/lib/git/url.rb +3 -3
- data/lib/git/version.rb +3 -1
- data/lib/git/working_directory.rb +2 -0
- data/lib/git/worktree.rb +6 -5
- data/lib/git/worktrees.rb +6 -6
- data/lib/git.rb +131 -28
- data/package.json +10 -0
- data/redesign/1_architecture_existing.md +66 -0
- data/redesign/2_architecture_redesign.md +130 -0
- data/redesign/3_architecture_implementation.md +138 -0
- data/redesign/index.md +34 -0
- data/release-please-config.json +36 -0
- data/tasks/gem_tasks.rake +10 -0
- data/tasks/rubocop.rake +12 -0
- data/tasks/test.rake +13 -0
- data/tasks/test_gem.rake +12 -0
- data/tasks/yard.rake +23 -0
- metadata +114 -37
- data/.github/stale.yml +0 -25
- data/Dockerfile.changelog-rs +0 -12
- data/PULL_REQUEST_TEMPLATE.md +0 -9
- data/RELEASING.md +0 -70
- data/lib/git/base/factory.rb +0 -99
- data/lib/git/failed_error.rb +0 -53
- data/lib/git/git_execute_error.rb +0 -7
- data/lib/git/signaled_error.rb +0 -50
- /data/{ISSUE_TEMPLATE.md → .github/issue_template.md} +0 -0
data/lib/git/branch.rb
CHANGED
|
@@ -1,9 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'git/path'
|
|
2
4
|
|
|
3
5
|
module Git
|
|
4
|
-
|
|
5
|
-
|
|
6
|
+
# Represents a Git branch
|
|
7
|
+
#
|
|
8
|
+
# Branch objects provide access to branch metadata and operations like checkout,
|
|
9
|
+
# delete, and merge. They should be obtained via {Git::Base#branch} or
|
|
10
|
+
# {Git::Base#branches}, not constructed directly.
|
|
11
|
+
#
|
|
12
|
+
# @example Getting a branch
|
|
13
|
+
# git = Git.open('.')
|
|
14
|
+
# branch = git.branch('main')
|
|
15
|
+
# branch.checkout
|
|
16
|
+
#
|
|
17
|
+
# @example Listing branches
|
|
18
|
+
# git.branches.each { |b| puts b.name }
|
|
19
|
+
#
|
|
20
|
+
# @api public
|
|
21
|
+
#
|
|
22
|
+
class Branch
|
|
23
|
+
# The full refname of this branch
|
|
24
|
+
#
|
|
25
|
+
# For local branches this is the short name (e.g. `'main'`). For
|
|
26
|
+
# remote-tracking branches obtained via {Git::Base#branches} this includes
|
|
27
|
+
# the `remotes/` prefix (e.g. `'remotes/origin/main'`). Branches constructed
|
|
28
|
+
# by {Git::Remote#branch} use the `<remote>/<branch>` form (e.g.
|
|
29
|
+
# `'origin/main'`) which does **not** populate {#remote}.
|
|
30
|
+
#
|
|
31
|
+
# @example
|
|
32
|
+
# git.branch('main').full #=> 'main'
|
|
33
|
+
# git.branch('remotes/origin/main').full #=> 'remotes/origin/main'
|
|
34
|
+
#
|
|
35
|
+
# @return [String] the full refname
|
|
36
|
+
#
|
|
37
|
+
attr_accessor :full
|
|
38
|
+
|
|
39
|
+
# The remote for this branch, or `nil` for local or bare-name remote-tracking branches
|
|
40
|
+
#
|
|
41
|
+
# Set to a {Git::Remote} object only when this branch was initialized with a
|
|
42
|
+
# `remotes/<remote>/` or `refs/remotes/<remote>/` prefix. `nil` for local
|
|
43
|
+
# branches and for remote-tracking branches in `<remote>/<branch>` form
|
|
44
|
+
# (such as those returned by {Git::Remote#branch}).
|
|
45
|
+
#
|
|
46
|
+
# @example
|
|
47
|
+
# git.branch('main').remote #=> nil
|
|
48
|
+
# git.branch('remotes/origin/main').remote #=> #<Git::Remote 'origin'>
|
|
49
|
+
# git.remote('origin').branch('main').remote #=> nil # uses 'origin/main' form
|
|
50
|
+
#
|
|
51
|
+
# @return [Git::Remote, nil] the remote object, or `nil`
|
|
52
|
+
#
|
|
53
|
+
attr_accessor :remote
|
|
54
|
+
|
|
55
|
+
# The short branch name without the remote prefix
|
|
56
|
+
#
|
|
57
|
+
# For branches initialized with a `remotes/` or `refs/remotes/` prefix, the
|
|
58
|
+
# prefix is stripped and this returns the bare branch name (e.g. `'main'`
|
|
59
|
+
# rather than `'remotes/origin/main'`). For branches in the
|
|
60
|
+
# `<remote>/<branch>` form (such as those created by {Git::Remote#branch}),
|
|
61
|
+
# no stripping occurs and `name` returns the full form (e.g. `'origin/main'`).
|
|
62
|
+
#
|
|
63
|
+
# @example
|
|
64
|
+
# git.branch('main').name #=> 'main'
|
|
65
|
+
# git.branch('remotes/origin/main').name #=> 'main'
|
|
66
|
+
# git.remote('origin').branch('main').name #=> 'origin/main'
|
|
67
|
+
#
|
|
68
|
+
# @return [String] the branch name
|
|
69
|
+
#
|
|
70
|
+
attr_accessor :name
|
|
6
71
|
|
|
72
|
+
# Initialize a new Branch object
|
|
73
|
+
#
|
|
74
|
+
# @api private
|
|
75
|
+
#
|
|
76
|
+
# @note Use {Git::Base#branch} or {Git::Base#branches} instead of constructing directly
|
|
77
|
+
#
|
|
78
|
+
# @param base [Git::Base] the git repository
|
|
79
|
+
#
|
|
80
|
+
# @param name [String] the full or short branch name
|
|
81
|
+
#
|
|
7
82
|
def initialize(base, name)
|
|
8
83
|
@full = name
|
|
9
84
|
@base = base
|
|
@@ -12,29 +87,102 @@ module Git
|
|
|
12
87
|
@remote, @name = parse_name(name)
|
|
13
88
|
end
|
|
14
89
|
|
|
90
|
+
# Returns the commit at the tip of this branch
|
|
91
|
+
#
|
|
92
|
+
# The result is memoized after the first call.
|
|
93
|
+
#
|
|
94
|
+
# @example Get the tip commit
|
|
95
|
+
# git.branch('main').gcommit #=> #<Git::Object ...>
|
|
96
|
+
#
|
|
97
|
+
# @return [Git::Object] the commit at the tip of this branch
|
|
98
|
+
#
|
|
15
99
|
def gcommit
|
|
16
100
|
@gcommit ||= @base.gcommit(@full)
|
|
17
101
|
@gcommit
|
|
18
102
|
end
|
|
19
103
|
|
|
104
|
+
# Returns the stash list for this repository
|
|
105
|
+
#
|
|
106
|
+
# The result is memoized after the first call.
|
|
107
|
+
#
|
|
108
|
+
# @example Iterate over stash entries
|
|
109
|
+
# git.branch('main').stashes.each { |s| puts s }
|
|
110
|
+
#
|
|
111
|
+
# @return [Git::Stashes] the stash list
|
|
112
|
+
#
|
|
20
113
|
def stashes
|
|
21
114
|
@stashes ||= Git::Stashes.new(@base)
|
|
22
115
|
end
|
|
23
116
|
|
|
117
|
+
# Checks out this branch, attempting to create it first if it does not already exist
|
|
118
|
+
#
|
|
119
|
+
# Branch creation is attempted via {#check_if_create}; any error from that
|
|
120
|
+
# step is silently ignored and the checkout proceeds regardless.
|
|
121
|
+
#
|
|
122
|
+
# **Note:** for remote-tracking branches (where {#remote} is not `nil`),
|
|
123
|
+
# {#full} is a ref such as `'remotes/origin/main'`. Checking out a
|
|
124
|
+
# remote-tracking ref places the repository in a **detached HEAD** state.
|
|
125
|
+
#
|
|
126
|
+
# @example Check out a branch
|
|
127
|
+
# git = Git.open('.')
|
|
128
|
+
# git.branch('main').checkout
|
|
129
|
+
#
|
|
130
|
+
# @return [String] git's stdout from the checkout
|
|
131
|
+
#
|
|
132
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
133
|
+
#
|
|
24
134
|
def checkout
|
|
25
135
|
check_if_create
|
|
26
136
|
@base.checkout(@full)
|
|
27
137
|
end
|
|
28
138
|
|
|
139
|
+
# Archives this branch and writes the result to a file
|
|
140
|
+
#
|
|
141
|
+
# @example Archive to a tar file
|
|
142
|
+
# git.branch('main').archive('/tmp/main.tar')
|
|
143
|
+
#
|
|
144
|
+
# @example Archive to a zip file
|
|
145
|
+
# git.branch('main').archive('/tmp/main.zip', format: 'zip')
|
|
146
|
+
#
|
|
147
|
+
# @param file [String] path to the destination archive file
|
|
148
|
+
#
|
|
149
|
+
# @param opts [Hash] archive options (see {Git::Base#archive})
|
|
150
|
+
#
|
|
151
|
+
# @return [String] the path to the written archive file
|
|
152
|
+
#
|
|
153
|
+
# @raise [Git::FailedError] if `git archive` fails
|
|
154
|
+
#
|
|
29
155
|
def archive(file, opts = {})
|
|
30
156
|
@base.lib.archive(@full, file, opts)
|
|
31
157
|
end
|
|
32
158
|
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
159
|
+
# Checks out this branch for the duration of a block, then restores the original branch
|
|
160
|
+
#
|
|
161
|
+
# If the block returns a truthy value, all pending changes are committed with the
|
|
162
|
+
# given message before switching back to the original branch. If the block returns
|
|
163
|
+
# a falsy value, a hard reset is performed before switching back.
|
|
164
|
+
#
|
|
165
|
+
# **Note:** the restore checkout is not wrapped in `ensure`. If the block,
|
|
166
|
+
# the commit, or the reset raises an exception, the repository will be left
|
|
167
|
+
# checked out on this branch rather than restored to the original.
|
|
168
|
+
#
|
|
169
|
+
# @example Commit a new file on a feature branch
|
|
170
|
+
# git.branch('feature').in_branch('Add README') do
|
|
171
|
+
# File.write('README.md', '# Hello')
|
|
172
|
+
# git.add('README.md')
|
|
173
|
+
# true # commit and return to original branch
|
|
174
|
+
# end
|
|
175
|
+
#
|
|
176
|
+
# @param message [String] commit message used when the block returns truthy
|
|
177
|
+
#
|
|
178
|
+
# @yield Executes the block with this branch checked out
|
|
179
|
+
#
|
|
180
|
+
# @yieldreturn [Object] return a truthy value to commit all changes, a falsy value to hard-reset
|
|
181
|
+
#
|
|
182
|
+
# @return [String] git's stdout from the final checkout back to the original branch
|
|
183
|
+
#
|
|
184
|
+
# @raise [Git::FailedError] if any of the underlying git operations (checkout, commit, reset) fail
|
|
185
|
+
#
|
|
38
186
|
def in_branch(message = 'in branch work')
|
|
39
187
|
old_current = @base.lib.branch_current
|
|
40
188
|
checkout
|
|
@@ -46,22 +194,112 @@ module Git
|
|
|
46
194
|
@base.checkout(old_current)
|
|
47
195
|
end
|
|
48
196
|
|
|
197
|
+
# Creates this branch if it does not already exist
|
|
198
|
+
#
|
|
199
|
+
# Silently ignores any error raised during branch creation (including the case
|
|
200
|
+
# where the branch already exists).
|
|
201
|
+
#
|
|
202
|
+
# @example Create a new branch
|
|
203
|
+
# git.branch('feature').create
|
|
204
|
+
#
|
|
205
|
+
# @return [String, nil] git's stdout from branch creation (typically empty),
|
|
206
|
+
# or `nil` if an error was rescued
|
|
207
|
+
#
|
|
49
208
|
def create
|
|
50
209
|
check_if_create
|
|
51
210
|
end
|
|
52
211
|
|
|
212
|
+
# Deletes this branch
|
|
213
|
+
#
|
|
214
|
+
# **Note:** this method only works correctly for local branches. Calling it on
|
|
215
|
+
# a remote-tracking branch (one where {#remote} is not `nil`) will attempt to
|
|
216
|
+
# delete a *local* branch with the same short name rather than the
|
|
217
|
+
# remote-tracking ref, which is almost certainly not what you want.
|
|
218
|
+
# See [ruby-git#1280](https://github.com/ruby-git/ruby-git/issues/1280) for
|
|
219
|
+
# the planned fix.
|
|
220
|
+
#
|
|
221
|
+
# @example Delete a local branch
|
|
222
|
+
# git.branch('old-feature').delete
|
|
223
|
+
#
|
|
224
|
+
# @return [String] git's deletion output
|
|
225
|
+
#
|
|
226
|
+
# @raise [Git::FailedError] if the branch cannot be deleted
|
|
227
|
+
#
|
|
53
228
|
def delete
|
|
54
229
|
@base.lib.branch_delete(@name)
|
|
55
230
|
end
|
|
56
231
|
|
|
57
|
-
|
|
58
|
-
|
|
232
|
+
# Returns true if this is the currently checked-out branch
|
|
233
|
+
#
|
|
234
|
+
# **Note:** this compares the current branch's short name against {#name}.
|
|
235
|
+
# For a remote-tracking branch (where {#remote} is not `nil`), {#name} is
|
|
236
|
+
# still the bare short name (e.g. `'main'`), so this will return `true`
|
|
237
|
+
# whenever the *local* branch with that name is checked out — not the
|
|
238
|
+
# remote-tracking ref itself.
|
|
239
|
+
#
|
|
240
|
+
# @example Check whether currently on main
|
|
241
|
+
# git.branch('main').current #=> true
|
|
242
|
+
#
|
|
243
|
+
# @return [Boolean] whether this branch is currently checked out
|
|
244
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
245
|
+
#
|
|
246
|
+
#
|
|
247
|
+
def current # rubocop:disable Naming/PredicateMethod
|
|
248
|
+
@base.lib.branch_current == @name
|
|
59
249
|
end
|
|
60
250
|
|
|
251
|
+
# Returns true if this branch contains the given commit
|
|
252
|
+
#
|
|
253
|
+
# **Note:** this queries local branches by short name. For a remote-tracking
|
|
254
|
+
# branch (where {#remote} is not `nil`), it checks the *local* branch with
|
|
255
|
+
# the same {#name} rather than the remote-tracking ref, which may give an
|
|
256
|
+
# inaccurate result.
|
|
257
|
+
#
|
|
258
|
+
# @example Check if a commit is reachable from this branch
|
|
259
|
+
# git.branch('main').contains?('abc1234') #=> true
|
|
260
|
+
#
|
|
261
|
+
# @param commit [String] the commit SHA or ref to check
|
|
262
|
+
#
|
|
263
|
+
# @return [Boolean] whether this branch contains the given commit
|
|
264
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
265
|
+
#
|
|
266
|
+
#
|
|
61
267
|
def contains?(commit)
|
|
62
|
-
!@base.lib.branch_contains(commit,
|
|
268
|
+
!@base.lib.branch_contains(commit, name).empty?
|
|
63
269
|
end
|
|
64
270
|
|
|
271
|
+
# Merges a branch into this branch, or merges this branch into the current branch
|
|
272
|
+
#
|
|
273
|
+
# @overload merge(branch, message = nil)
|
|
274
|
+
#
|
|
275
|
+
# Temporarily checks out this branch, merges the given branch into it,
|
|
276
|
+
# then restores the original branch.
|
|
277
|
+
#
|
|
278
|
+
# **Note:** if `self` is a remote-tracking branch (where {#remote} is not
|
|
279
|
+
# `nil`), this delegates to {#checkout} which has the detached-HEAD
|
|
280
|
+
# side-effect described there. The remote-tracking ref will not be updated.
|
|
281
|
+
#
|
|
282
|
+
# @example Merge a feature branch into main
|
|
283
|
+
# git.branch('main').merge('feature')
|
|
284
|
+
#
|
|
285
|
+
# @param branch [String] the name of the branch to merge into this one
|
|
286
|
+
#
|
|
287
|
+
# @param message [String, nil] commit message for the merge commit
|
|
288
|
+
#
|
|
289
|
+
# @return [String] git's stdout from the final checkout back to the original branch
|
|
290
|
+
#
|
|
291
|
+
# @overload merge()
|
|
292
|
+
#
|
|
293
|
+
# Merges this branch into the currently checked-out branch.
|
|
294
|
+
#
|
|
295
|
+
# @example Merge main into the current branch
|
|
296
|
+
# git.branch('main').merge
|
|
297
|
+
#
|
|
298
|
+
# @return [String] git's stdout from the merge command
|
|
299
|
+
#
|
|
300
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status during
|
|
301
|
+
# the merge, checkout, commit, or reset operations
|
|
302
|
+
#
|
|
65
303
|
def merge(branch = nil, message = nil)
|
|
66
304
|
if branch
|
|
67
305
|
in_branch do
|
|
@@ -75,6 +313,26 @@ module Git
|
|
|
75
313
|
end
|
|
76
314
|
end
|
|
77
315
|
|
|
316
|
+
# Updates the git ref for this branch to point to the given commit
|
|
317
|
+
#
|
|
318
|
+
# The target ref depends on whether {#remote} is set:
|
|
319
|
+
# - When {#remote} is not `nil` (i.e. the branch was initialised with a
|
|
320
|
+
# `remotes/<remote>/` or `refs/remotes/<remote>/` prefix), updates
|
|
321
|
+
# `refs/remotes/<remote>/<name>`.
|
|
322
|
+
# - Otherwise updates `refs/heads/<name>`. Note that branches in the
|
|
323
|
+
# `<remote>/<branch>` form (e.g. those returned by {Git::Remote#branch})
|
|
324
|
+
# have `remote == nil` and therefore update `refs/heads/<remote>/<name>`,
|
|
325
|
+
# **not** `refs/remotes/...`.
|
|
326
|
+
#
|
|
327
|
+
# @example Advance a local branch to a new commit
|
|
328
|
+
# git.branch('feature').update_ref('abc1234def5678')
|
|
329
|
+
#
|
|
330
|
+
# @param commit [String] the commit SHA to point this branch at
|
|
331
|
+
#
|
|
332
|
+
# @return [String] the stdout output from `git update-ref`
|
|
333
|
+
#
|
|
334
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
335
|
+
#
|
|
78
336
|
def update_ref(commit)
|
|
79
337
|
if @remote
|
|
80
338
|
@base.lib.update_ref("refs/remotes/#{@remote.name}/#{@name}", commit)
|
|
@@ -83,27 +341,38 @@ module Git
|
|
|
83
341
|
end
|
|
84
342
|
end
|
|
85
343
|
|
|
344
|
+
# Returns this branch as a single-element array containing its full refname
|
|
345
|
+
#
|
|
346
|
+
# @example Get branch as array
|
|
347
|
+
# git.branch('main').to_a #=> ['main']
|
|
348
|
+
#
|
|
349
|
+
# @return [Array<String>] a single-element array containing the full refname
|
|
350
|
+
#
|
|
86
351
|
def to_a
|
|
87
352
|
[@full]
|
|
88
353
|
end
|
|
89
354
|
|
|
355
|
+
# Returns the full refname of this branch as a string
|
|
356
|
+
#
|
|
357
|
+
# @example Get branch as string
|
|
358
|
+
# git.branch('main').to_s #=> 'main'
|
|
359
|
+
#
|
|
360
|
+
# @return [String] the full refname
|
|
361
|
+
#
|
|
90
362
|
def to_s
|
|
91
363
|
@full
|
|
92
364
|
end
|
|
93
365
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
@base.lib.branch_current == @name
|
|
102
|
-
end
|
|
103
|
-
|
|
366
|
+
# Regular expression for parsing branch refnames
|
|
367
|
+
#
|
|
368
|
+
# Matches full and short refnames, capturing an optional remote name and the
|
|
369
|
+
# branch name. Used internally to identify remote-tracking branches.
|
|
370
|
+
#
|
|
371
|
+
# @api private
|
|
372
|
+
#
|
|
104
373
|
BRANCH_NAME_REGEXP = %r{
|
|
105
374
|
^
|
|
106
|
-
# Optional 'refs/remotes/' at the
|
|
375
|
+
# Optional 'remotes/' or 'refs/remotes/' at the beginning to specify a remote tracking branch
|
|
107
376
|
# with a <remote_name>. <remote_name> is nil if not present.
|
|
108
377
|
(?:
|
|
109
378
|
(?:(?:refs/)?remotes/)(?<remote_name>[^/]+)/
|
|
@@ -112,32 +381,43 @@ module Git
|
|
|
112
381
|
$
|
|
113
382
|
}x
|
|
114
383
|
|
|
115
|
-
|
|
384
|
+
private
|
|
385
|
+
|
|
386
|
+
# Parses a full branch name into remote and short branch name components
|
|
116
387
|
#
|
|
117
|
-
#
|
|
118
|
-
#
|
|
119
|
-
#
|
|
388
|
+
# Strips an optional `remotes/` or `refs/remotes/` prefix. Only inputs that begin
|
|
389
|
+
# with one of those prefixes yield a remote object; all other inputs (including
|
|
390
|
+
# `'origin/master'`) are treated as local branch names with a `nil` remote.
|
|
120
391
|
#
|
|
121
|
-
#
|
|
122
|
-
#
|
|
123
|
-
# parse_name('master')
|
|
124
|
-
# parse_name('origin/master') #=> [nil, 'origin/master']
|
|
125
|
-
# parse_name('origin/master/v2') #=> [nil, 'origin/master']
|
|
392
|
+
# @example Local branches
|
|
393
|
+
# parse_name('master') #=> [nil, 'master']
|
|
394
|
+
# parse_name('origin/master') #=> [nil, 'origin/master']
|
|
126
395
|
#
|
|
127
|
-
#
|
|
128
|
-
# parse_name('remotes/origin/master')
|
|
129
|
-
# parse_name('remotes/origin/master
|
|
130
|
-
#
|
|
131
|
-
#
|
|
396
|
+
# @example Remote-tracking branches
|
|
397
|
+
# parse_name('remotes/origin/master') #=> [#<Git::Remote 'origin'>, 'master']
|
|
398
|
+
# parse_name('refs/remotes/origin/master') #=> [#<Git::Remote 'origin'>, 'master']
|
|
399
|
+
#
|
|
400
|
+
# @param name [String] the full branch name to parse
|
|
401
|
+
#
|
|
402
|
+
# @return [Array(Git::Remote, String)] a two-element array with the remote object
|
|
403
|
+
# (or `nil`) and the short branch name
|
|
132
404
|
#
|
|
133
|
-
# param [String] name branch full name.
|
|
134
|
-
# return [<Git::Remote,NilClass,String>] an Array containing the remote and branch names.
|
|
135
405
|
def parse_name(name)
|
|
136
406
|
# Expect this will always match
|
|
137
407
|
match = name.match(BRANCH_NAME_REGEXP)
|
|
138
408
|
remote = match[:remote_name] ? Git::Remote.new(@base, match[:remote_name]) : nil
|
|
139
409
|
branch_name = match[:branch_name]
|
|
140
|
-
[
|
|
410
|
+
[remote, branch_name]
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
# Creates the branch if it does not already exist, ignoring errors
|
|
414
|
+
#
|
|
415
|
+
# @return [String, nil] stdout from branch creation, or `nil` if an error was rescued
|
|
416
|
+
#
|
|
417
|
+
def check_if_create
|
|
418
|
+
@base.lib.branch_new(@name)
|
|
419
|
+
rescue StandardError
|
|
420
|
+
nil
|
|
141
421
|
end
|
|
142
422
|
end
|
|
143
423
|
end
|
data/lib/git/branches.rb
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Git
|
|
2
|
-
|
|
3
4
|
# object that holds all the available branches
|
|
4
5
|
class Branches
|
|
5
|
-
|
|
6
6
|
include Enumerable
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
def initialize(base)
|
|
9
9
|
@branches = {}
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
@base = base
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
@base.lib.branches_all.each do |b|
|
|
14
14
|
@branches[b[0]] = Git::Branch.new(@base, b[0])
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def local
|
|
19
|
-
|
|
19
|
+
reject(&:remote)
|
|
20
20
|
end
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
def remote
|
|
23
|
-
self.select
|
|
23
|
+
self.select(&:remote)
|
|
24
24
|
end
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
# array like methods
|
|
27
27
|
|
|
28
28
|
def size
|
|
29
29
|
@branches.size
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def each(&block)
|
|
33
|
-
@branches.values.each(&block)
|
|
34
30
|
end
|
|
35
|
-
|
|
31
|
+
|
|
32
|
+
def each(&)
|
|
33
|
+
@branches.values.each(&)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
36
|
# Returns the target branch
|
|
37
37
|
#
|
|
38
38
|
# Example:
|
|
@@ -47,25 +47,22 @@ module Git
|
|
|
47
47
|
# @param [#to_s] branch_name the target branch name.
|
|
48
48
|
# @return [Git::Branch] the target branch.
|
|
49
49
|
def [](branch_name)
|
|
50
|
-
@branches.values.
|
|
50
|
+
@branches.values.each_with_object(@branches) do |branch, branches|
|
|
51
51
|
branches[branch.full] ||= branch
|
|
52
52
|
|
|
53
|
-
# This is how Git (version 1.7.9.5) works.
|
|
54
|
-
# Lets you ignore the 'remotes' if its at the beginning of the branch full
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
branches
|
|
53
|
+
# This is how Git (version 1.7.9.5) works.
|
|
54
|
+
# Lets you ignore the 'remotes' if its at the beginning of the branch full
|
|
55
|
+
# name (even if is not a real remote branch).
|
|
56
|
+
branches[branch.full.sub('remotes/', '')] ||= branch if branch.full =~ %r{^remotes/.+}
|
|
58
57
|
end[branch_name.to_s]
|
|
59
58
|
end
|
|
60
|
-
|
|
59
|
+
|
|
61
60
|
def to_s
|
|
62
61
|
out = ''
|
|
63
|
-
@branches.
|
|
62
|
+
@branches.each_value do |b|
|
|
64
63
|
out << (b.current ? '* ' : ' ') << b.to_s << "\n"
|
|
65
64
|
end
|
|
66
65
|
out
|
|
67
66
|
end
|
|
68
|
-
|
|
69
67
|
end
|
|
70
|
-
|
|
71
68
|
end
|