git 4.3.2 → 4.4.1
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/workflows/continuous_integration.yml +65 -0
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +26 -0
- data/README.md +12 -0
- data/lib/git/base.rb +4 -4
- data/lib/git/branch.rb +302 -23
- data/lib/git/lib.rb +59 -2
- data/lib/git/status.rb +0 -2
- data/lib/git/version.rb +1 -1
- data/lib/git.rb +39 -4
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a24c04e4ade577716464aaf17cdac3f1e1d7e860e60dcd9ed964eb8f0f503b4b
|
|
4
|
+
data.tar.gz: 7913679e60e68c2f51d9189c150dbf8310244afd71a38a0a7ff88845e58d2d96
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3e768e8f510d5919d7bacae8095da345e8446546e39e0567aaa2b242700fc4a2045bd6328bd8c5bfa0876375380b679f07aaa33992ebf4828b5298999e033201
|
|
7
|
+
data.tar.gz: a80276c4a8d613aae86a1adfdaa5f39aabfd3f85e9b4f3dc703612a4fea7afc452f37b9b5d4f3a6d632a2a1e8d12427a5f6f35697ed8daa16e8b2aaebfca5d29
|
|
@@ -53,3 +53,68 @@ jobs:
|
|
|
53
53
|
|
|
54
54
|
- name: Test Gem
|
|
55
55
|
run: bundle exec rake test:gem
|
|
56
|
+
|
|
57
|
+
# The branch ruleset cannot require the matrix job above directly. A required status
|
|
58
|
+
# check is matched by name, and `build`'s name is a template -- GitHub only expands it
|
|
59
|
+
# into "Ruby 3.2 on ubuntu-latest" and friends when it evaluates the matrix. The
|
|
60
|
+
# job-level `if:` short-circuits before that happens, so a release PR reports one check
|
|
61
|
+
# named literally "Ruby ${{ matrix.ruby }} on ${{ matrix.operating-system }}" and the
|
|
62
|
+
# six expanded names never report at all. Required checks that never report block a PR
|
|
63
|
+
# indefinitely rather than passing, which is what stranded the v5.0.5 release PR on
|
|
64
|
+
# main and the v4.4.1 release PR here.
|
|
65
|
+
#
|
|
66
|
+
# So the ruleset requires this job instead: one stable name, no matrix, no template to
|
|
67
|
+
# expand. It also decouples the required-check list from the matrix, so adding or
|
|
68
|
+
# dropping a Ruby version no longer needs a matching ruleset edit.
|
|
69
|
+
#
|
|
70
|
+
# Unlike main, this branch has no separate lint job: `rake default` runs RuboCop and
|
|
71
|
+
# YARD inside every matrix leg, so this one check covers everything CI does here.
|
|
72
|
+
#
|
|
73
|
+
# This job carries the same release-PR guard as the matrix above, so that a release PR
|
|
74
|
+
# reports it as skipped rather than as a success for specs that never ran. Skipping is
|
|
75
|
+
# safe here and was not safe for `build`: this job's name is a literal string, so the
|
|
76
|
+
# check run exists and reports a skipped conclusion whether or not the job runs, and
|
|
77
|
+
# rulesets treat skipped as passing. A matrix job's name does not exist at all until the
|
|
78
|
+
# matrix is evaluated, which is the whole reason this job exists.
|
|
79
|
+
#
|
|
80
|
+
# `always()` is load-bearing and must stay. Without a status check function a job-level
|
|
81
|
+
# `if:` implies `success()`, so a failing matrix would skip this job -- and a skipped
|
|
82
|
+
# required check counts as passing, meaning a red build would merge. `always()` forces a
|
|
83
|
+
# real verdict on every run that is not a release PR. `!cancelled()` is not a substitute:
|
|
84
|
+
# a cancelled run would then skip the gate, and a skipped conclusion is one rulesets treat
|
|
85
|
+
# as passing -- so a build that never finished would stop blocking the merge.
|
|
86
|
+
#
|
|
87
|
+
# The case below accepts `success` only. `build` and this job now carry the same release-PR
|
|
88
|
+
# guard, so this job runs exactly when `build` ran; a `skipped` result would mean the two
|
|
89
|
+
# guards have drifted apart, and failing closed on it turns that into a loud failure rather
|
|
90
|
+
# than a silent pass.
|
|
91
|
+
#
|
|
92
|
+
# Note that a matrix leg marked `experimental: Yes` carries continue-on-error, and a
|
|
93
|
+
# continue-on-error failure does not fail the job as a whole -- so it does not fail this
|
|
94
|
+
# gate either. That is what `experimental` is for; no leg currently sets it.
|
|
95
|
+
build-complete:
|
|
96
|
+
name: All Specs Passed
|
|
97
|
+
|
|
98
|
+
needs: [build]
|
|
99
|
+
|
|
100
|
+
# Report a verdict on every non-release PR regardless of the matrix result, and skip
|
|
101
|
+
# on release PRs, where the matrix was never meant to run.
|
|
102
|
+
if: >-
|
|
103
|
+
always() &&
|
|
104
|
+
(github.event_name == 'workflow_dispatch' ||
|
|
105
|
+
(github.event_name == 'pull_request' && !startsWith(github.event.pull_request.head.ref, 'release-please--')))
|
|
106
|
+
|
|
107
|
+
runs-on: ubuntu-latest
|
|
108
|
+
|
|
109
|
+
steps:
|
|
110
|
+
- name: Check the result of the build job
|
|
111
|
+
run: |
|
|
112
|
+
case "${{ needs.build.result }}" in
|
|
113
|
+
success)
|
|
114
|
+
echo "build: ${{ needs.build.result }}"
|
|
115
|
+
;;
|
|
116
|
+
*)
|
|
117
|
+
echo "::error::build: ${{ needs.build.result }}"
|
|
118
|
+
exit 1
|
|
119
|
+
;;
|
|
120
|
+
esac
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,32 @@
|
|
|
5
5
|
|
|
6
6
|
# Change Log
|
|
7
7
|
|
|
8
|
+
## [4.4.1](https://github.com/ruby-git/ruby-git/compare/v4.4.0...v4.4.1) (2026-08-09)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* Pin LC_ALL to a locale that exists on the host platform ([690360e](https://github.com/ruby-git/ruby-git/commit/690360e386bac1dafb7b5bd0adbf10072e834f69)), closes [#1669](https://github.com/ruby-git/ruby-git/issues/1669)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Other Changes
|
|
17
|
+
|
|
18
|
+
* Add a matrix-free All Specs Passed gate to 4.x ([5352d6d](https://github.com/ruby-git/ruby-git/commit/5352d6d4edfcb52c544139e265fb3636bc1937b7)), closes [#1675](https://github.com/ruby-git/ruby-git/issues/1675)
|
|
19
|
+
* Skip the All Specs Passed gate on release PRs ([52473b2](https://github.com/ruby-git/ruby-git/commit/52473b21a733499eb274a7b03d53f1a0efbf5c86)), closes [#1675](https://github.com/ruby-git/ruby-git/issues/1675)
|
|
20
|
+
|
|
21
|
+
## [4.4.0](https://github.com/ruby-git/ruby-git/compare/v4.3.2...v4.4.0) (2026-07-11)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Features
|
|
25
|
+
|
|
26
|
+
* Allow deprecation behavior env var ([fd622be](https://github.com/ruby-git/ruby-git/commit/fd622bea6513d78f2dbed8dc19c110be80d07ec8))
|
|
27
|
+
* Warn on first direct access to Git::Lib constant ([52059c6](https://github.com/ruby-git/ruby-git/commit/52059c63d5c23b4d80025b95332e946aba942f0c))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
### Other Changes
|
|
31
|
+
|
|
32
|
+
* Improve YARD documentation for Git::Branch ([#1277](https://github.com/ruby-git/ruby-git/issues/1277)) ([0769bf9](https://github.com/ruby-git/ruby-git/commit/0769bf91a21ecb24d48a31439de073b689bc62b6))
|
|
33
|
+
|
|
8
34
|
## [4.3.2](https://github.com/ruby-git/ruby-git/compare/v4.3.1...v4.3.2) (2026-03-31)
|
|
9
35
|
|
|
10
36
|
|
data/README.md
CHANGED
|
@@ -589,6 +589,18 @@ You can silence deprecation warnings by adding this line to your source code:
|
|
|
589
589
|
Git::Deprecation.behavior = :silence
|
|
590
590
|
```
|
|
591
591
|
|
|
592
|
+
Or by setting this environment variable before loading the gem:
|
|
593
|
+
|
|
594
|
+
```sh
|
|
595
|
+
GIT_DEPRECATION_BEHAVIOR=silence
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
Accepted environment variable values are the behavior names supported by your
|
|
599
|
+
installed ActiveSupport version.
|
|
600
|
+
|
|
601
|
+
If `GIT_DEPRECATION_BEHAVIOR` is set to an unsupported value, loading the gem
|
|
602
|
+
raises `ArgumentError` with the accepted behavior names.
|
|
603
|
+
|
|
592
604
|
See [the Active Support Deprecation
|
|
593
605
|
documentation](https://api.rubyonrails.org/classes/ActiveSupport/Deprecation.html)
|
|
594
606
|
for more details.
|
data/lib/git/base.rb
CHANGED
|
@@ -23,14 +23,14 @@ module Git
|
|
|
23
23
|
def self.clone(repository_url, directory, options = {})
|
|
24
24
|
lib_options = {}
|
|
25
25
|
lib_options[:git_ssh] = options[:git_ssh] if options.key?(:git_ssh)
|
|
26
|
-
new_options =
|
|
26
|
+
new_options = LibImpl.new(lib_options, options[:log]).clone(repository_url, directory, options)
|
|
27
27
|
normalize_paths(new_options, bare: options[:bare] || options[:mirror])
|
|
28
28
|
new(new_options)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
# (see Git.default_branch)
|
|
32
32
|
def self.repository_default_branch(repository, options = {})
|
|
33
|
-
|
|
33
|
+
LibImpl.new(nil, options[:log]).repository_default_branch(repository)
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
# Returns (and initialize if needed) a Git::Config instance
|
|
@@ -90,7 +90,7 @@ module Git
|
|
|
90
90
|
# repository you have a Git::Base instance for. This would not
|
|
91
91
|
# change the existing interface (other than adding to it).
|
|
92
92
|
#
|
|
93
|
-
|
|
93
|
+
LibImpl.new(options).init(init_options)
|
|
94
94
|
|
|
95
95
|
new(options)
|
|
96
96
|
end
|
|
@@ -335,7 +335,7 @@ module Git
|
|
|
335
335
|
# actual 'git' forked system calls. At some point I hope to replace the Git::Lib
|
|
336
336
|
# class with one that uses native methods or libgit C bindings
|
|
337
337
|
def lib
|
|
338
|
-
@lib ||=
|
|
338
|
+
@lib ||= LibImpl.new(self, @logger)
|
|
339
339
|
end
|
|
340
340
|
|
|
341
341
|
# Returns the per-instance git_ssh configuration value.
|
data/lib/git/branch.rb
CHANGED
|
@@ -4,9 +4,81 @@ require 'git/path'
|
|
|
4
4
|
|
|
5
5
|
module Git
|
|
6
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
|
+
#
|
|
7
22
|
class Branch
|
|
8
|
-
|
|
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
|
|
9
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
|
+
#
|
|
10
82
|
def initialize(base, name)
|
|
11
83
|
@full = name
|
|
12
84
|
@base = base
|
|
@@ -15,29 +87,102 @@ module Git
|
|
|
15
87
|
@remote, @name = parse_name(name)
|
|
16
88
|
end
|
|
17
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
|
+
#
|
|
18
99
|
def gcommit
|
|
19
100
|
@gcommit ||= @base.gcommit(@full)
|
|
20
101
|
@gcommit
|
|
21
102
|
end
|
|
22
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
|
+
#
|
|
23
113
|
def stashes
|
|
24
114
|
@stashes ||= Git::Stashes.new(@base)
|
|
25
115
|
end
|
|
26
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
|
+
#
|
|
27
134
|
def checkout
|
|
28
135
|
check_if_create
|
|
29
136
|
@base.checkout(@full)
|
|
30
137
|
end
|
|
31
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
|
+
#
|
|
32
155
|
def archive(file, opts = {})
|
|
33
156
|
@base.lib.archive(@full, file, opts)
|
|
34
157
|
end
|
|
35
158
|
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
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
|
+
#
|
|
41
186
|
def in_branch(message = 'in branch work')
|
|
42
187
|
old_current = @base.lib.branch_current
|
|
43
188
|
checkout
|
|
@@ -49,22 +194,112 @@ module Git
|
|
|
49
194
|
@base.checkout(old_current)
|
|
50
195
|
end
|
|
51
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
|
+
#
|
|
52
208
|
def create
|
|
53
209
|
check_if_create
|
|
54
210
|
end
|
|
55
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
|
+
#
|
|
56
228
|
def delete
|
|
57
229
|
@base.lib.branch_delete(@name)
|
|
58
230
|
end
|
|
59
231
|
|
|
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
|
+
#
|
|
60
247
|
def current # rubocop:disable Naming/PredicateMethod
|
|
61
248
|
@base.lib.branch_current == @name
|
|
62
249
|
end
|
|
63
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
|
+
#
|
|
64
267
|
def contains?(commit)
|
|
65
268
|
!@base.lib.branch_contains(commit, name).empty?
|
|
66
269
|
end
|
|
67
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
|
+
#
|
|
68
303
|
def merge(branch = nil, message = nil)
|
|
69
304
|
if branch
|
|
70
305
|
in_branch do
|
|
@@ -78,6 +313,26 @@ module Git
|
|
|
78
313
|
end
|
|
79
314
|
end
|
|
80
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
|
+
#
|
|
81
336
|
def update_ref(commit)
|
|
82
337
|
if @remote
|
|
83
338
|
@base.lib.update_ref("refs/remotes/#{@remote.name}/#{@name}", commit)
|
|
@@ -86,17 +341,38 @@ module Git
|
|
|
86
341
|
end
|
|
87
342
|
end
|
|
88
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
|
+
#
|
|
89
351
|
def to_a
|
|
90
352
|
[@full]
|
|
91
353
|
end
|
|
92
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
|
+
#
|
|
93
362
|
def to_s
|
|
94
363
|
@full
|
|
95
364
|
end
|
|
96
365
|
|
|
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
|
+
#
|
|
97
373
|
BRANCH_NAME_REGEXP = %r{
|
|
98
374
|
^
|
|
99
|
-
# Optional 'refs/remotes/' at the
|
|
375
|
+
# Optional 'remotes/' or 'refs/remotes/' at the beginning to specify a remote tracking branch
|
|
100
376
|
# with a <remote_name>. <remote_name> is nil if not present.
|
|
101
377
|
(?:
|
|
102
378
|
(?:(?:refs/)?remotes/)(?<remote_name>[^/]+)/
|
|
@@ -107,26 +383,25 @@ module Git
|
|
|
107
383
|
|
|
108
384
|
private
|
|
109
385
|
|
|
110
|
-
#
|
|
386
|
+
# Parses a full branch name into remote and short branch name components
|
|
387
|
+
#
|
|
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.
|
|
111
391
|
#
|
|
112
|
-
#
|
|
113
|
-
#
|
|
114
|
-
#
|
|
392
|
+
# @example Local branches
|
|
393
|
+
# parse_name('master') #=> [nil, 'master']
|
|
394
|
+
# parse_name('origin/master') #=> [nil, 'origin/master']
|
|
115
395
|
#
|
|
116
|
-
#
|
|
117
|
-
#
|
|
118
|
-
# parse_name('master') #=> [
|
|
119
|
-
# parse_name('origin/master') #=> [nil, 'origin/master']
|
|
120
|
-
# parse_name('origin/master/v2') #=> [nil, 'origin/master']
|
|
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']
|
|
121
399
|
#
|
|
122
|
-
#
|
|
123
|
-
#
|
|
124
|
-
#
|
|
125
|
-
#
|
|
126
|
-
# parse_name('refs/remotes/origin/master/v2') #=> ['origin', 'master/v2']
|
|
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
|
|
127
404
|
#
|
|
128
|
-
# param [String] name branch full name.
|
|
129
|
-
# return [<Git::Remote,NilClass,String>] an Array containing the remote and branch names.
|
|
130
405
|
def parse_name(name)
|
|
131
406
|
# Expect this will always match
|
|
132
407
|
match = name.match(BRANCH_NAME_REGEXP)
|
|
@@ -135,6 +410,10 @@ module Git
|
|
|
135
410
|
[remote, branch_name]
|
|
136
411
|
end
|
|
137
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
|
+
#
|
|
138
417
|
def check_if_create
|
|
139
418
|
@base.lib.branch_new(@name)
|
|
140
419
|
rescue StandardError
|
data/lib/git/lib.rb
CHANGED
|
@@ -2075,7 +2075,7 @@ module Git
|
|
|
2075
2075
|
# @example Unsetting an environment variable (used by worktree_command_line)
|
|
2076
2076
|
# env_overrides('GIT_INDEX_FILE' => nil)
|
|
2077
2077
|
# # => { 'GIT_DIR' => '/path/to/.git', 'GIT_WORK_TREE' => '/path/to/worktree',
|
|
2078
|
-
# # 'GIT_INDEX_FILE' => nil, 'GIT_SSH' => <git_ssh_value>, 'LC_ALL' =>
|
|
2078
|
+
# # 'GIT_INDEX_FILE' => nil, 'GIT_SSH' => <git_ssh_value>, 'LC_ALL' => <pinned_locale> }
|
|
2079
2079
|
# # When passed to Process.spawn, GIT_INDEX_FILE will be unset in the environment
|
|
2080
2080
|
#
|
|
2081
2081
|
# @see https://ruby-doc.org/core/Process.html#method-c-spawn Process.spawn
|
|
@@ -2088,10 +2088,67 @@ module Git
|
|
|
2088
2088
|
'GIT_WORK_TREE' => @git_work_dir,
|
|
2089
2089
|
'GIT_INDEX_FILE' => @git_index_file,
|
|
2090
2090
|
'GIT_SSH' => resolved_git_ssh,
|
|
2091
|
-
'
|
|
2091
|
+
# Pin the locale so git's behavior does not depend on the user's environment.
|
|
2092
|
+
# Added for issue #753, where a German user's `git branch` output
|
|
2093
|
+
# ("* (HEAD losgelöst bei origin/25.1)") broke branch parsing.
|
|
2094
|
+
#
|
|
2095
|
+
# The pin has two halves, and both are load-bearing here:
|
|
2096
|
+
#
|
|
2097
|
+
# - Messages: keeps git's human-readable text in English. `BRANCH_LINE_REGEXP`
|
|
2098
|
+
# matches the literal English strings "(HEAD detached at ...)" and
|
|
2099
|
+
# "(not a branch)" in `git branch -a` output, which is the exact issue #753
|
|
2100
|
+
# failure mode.
|
|
2101
|
+
# - Ctype: makes git's regex engine match *characters* rather than *bytes*.
|
|
2102
|
+
# Under a C ctype, `grep` and `log --grep` silently return wrong answers —
|
|
2103
|
+
# with exit status zero — for any pattern whose metacharacters span
|
|
2104
|
+
# non-ASCII text.
|
|
2105
|
+
#
|
|
2106
|
+
# So do not "simplify" this to `C`: that yields English messages and a broken
|
|
2107
|
+
# ctype, which is the worst of both. A pinned locale the host does not have
|
|
2108
|
+
# degrades to that same C ctype, which is why each branch below has to name a
|
|
2109
|
+
# locale that actually exists on the platform it applies to:
|
|
2110
|
+
#
|
|
2111
|
+
# - Non-Darwin: `C.UTF-8`. Stock Debian, Ubuntu, and RHEL images generate no
|
|
2112
|
+
# `en_US.UTF-8`, so pinning it there produced exactly the silent breakage
|
|
2113
|
+
# above. (musl ignores the locale name for ctype, so Alpine is UTF-8 either
|
|
2114
|
+
# way.)
|
|
2115
|
+
# - Darwin: `en_US.UTF-8`, which every macOS release ships. `C.UTF-8` did not
|
|
2116
|
+
# arrive until macOS 15, so macOS 11–14 — including Intel Macs that are
|
|
2117
|
+
# hardware-capped below 15 — would break under it. Delete this branch once
|
|
2118
|
+
# macOS 14 and earlier are out of support.
|
|
2119
|
+
#
|
|
2120
|
+
# Windows takes the non-Darwin branch, where the value makes no difference:
|
|
2121
|
+
# Git for Windows folds case and runs PCRE in UTF mode under every value, and
|
|
2122
|
+
# matches bytes for `.` and POSIX classes under every value — measured on git
|
|
2123
|
+
# 2.55.0 against `en_US.UTF-8`, `C.UTF-8`, `C`, and no pin at all.
|
|
2124
|
+
#
|
|
2125
|
+
# RHEL 7 (glibc 2.17) has neither locale and gets a C ctype whatever is pinned.
|
|
2126
|
+
# It is EOL, and there is deliberately no public override for this value.
|
|
2127
|
+
'LC_ALL' => darwin_platform? ? 'en_US.UTF-8' : 'C.UTF-8'
|
|
2092
2128
|
}.merge(additional_overrides)
|
|
2093
2129
|
end
|
|
2094
2130
|
|
|
2131
|
+
# Whether this process is running on macOS
|
|
2132
|
+
#
|
|
2133
|
+
# Checks `RUBY_DESCRIPTION` as well as `RUBY_PLATFORM` because JRuby reports
|
|
2134
|
+
# `RUBY_PLATFORM` as `"java"` on every host and records the real platform only in
|
|
2135
|
+
# `RUBY_DESCRIPTION` (as, for example, `"... [arm64-darwin]"`). Testing
|
|
2136
|
+
# `RUBY_PLATFORM` alone would put JRuby on macOS onto the non-Darwin branch, which is
|
|
2137
|
+
# the one platform pairing that branch must not be given. This is the same detection
|
|
2138
|
+
# the test suite uses for Windows, and for the same reason.
|
|
2139
|
+
#
|
|
2140
|
+
# Test the platform plainly rather than sniffing the Darwin version: `RUBY_PLATFORM`
|
|
2141
|
+
# records the version Ruby was *built* against, so a Ruby built on macOS 14 still
|
|
2142
|
+
# reports `darwin23` when run on macOS 15.
|
|
2143
|
+
#
|
|
2144
|
+
# @return [Boolean] true if this process is running on macOS
|
|
2145
|
+
#
|
|
2146
|
+
# @api private
|
|
2147
|
+
#
|
|
2148
|
+
def darwin_platform?
|
|
2149
|
+
RUBY_PLATFORM.include?('darwin') || RUBY_DESCRIPTION.include?('darwin')
|
|
2150
|
+
end
|
|
2151
|
+
|
|
2095
2152
|
# Resolve the git_ssh value to use for this instance
|
|
2096
2153
|
#
|
|
2097
2154
|
# @return [String, nil] the resolved git_ssh value
|
data/lib/git/status.rb
CHANGED
data/lib/git/version.rb
CHANGED
data/lib/git.rb
CHANGED
|
@@ -1,10 +1,29 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'active_support'
|
|
4
3
|
require 'active_support/deprecation'
|
|
5
4
|
|
|
5
|
+
# Define Git::Deprecation before requiring the rest of the library to ensure that
|
|
6
|
+
# any deprecation warnings emitted during the loading of the library are properly
|
|
7
|
+
# configured according to the GIT_DEPRECATION_BEHAVIOR environment variable.
|
|
8
|
+
#
|
|
6
9
|
module Git
|
|
10
|
+
# The deprecation instance used to emit deprecation warnings for the Git gem
|
|
11
|
+
#
|
|
12
|
+
# @api public
|
|
7
13
|
Deprecation = ActiveSupport::Deprecation.new('5.0.0', 'Git')
|
|
14
|
+
|
|
15
|
+
if (behavior = ENV.fetch('GIT_DEPRECATION_BEHAVIOR', nil))
|
|
16
|
+
behavior = behavior.strip
|
|
17
|
+
allowed_behaviors = ActiveSupport::Deprecation::DEFAULT_BEHAVIORS.keys.map(&:to_s)
|
|
18
|
+
|
|
19
|
+
unless allowed_behaviors.include?(behavior)
|
|
20
|
+
raise ArgumentError,
|
|
21
|
+
"Invalid GIT_DEPRECATION_BEHAVIOR=#{behavior.inspect}; " \
|
|
22
|
+
"expected one of: #{allowed_behaviors.join(', ')}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
Deprecation.behavior = behavior.to_sym
|
|
26
|
+
end
|
|
8
27
|
end
|
|
9
28
|
|
|
10
29
|
require 'git/author'
|
|
@@ -44,12 +63,28 @@ require 'git/worktrees'
|
|
|
44
63
|
# @author Scott Chacon (mailto:schacon@gmail.com)
|
|
45
64
|
#
|
|
46
65
|
module Git # rubocop:disable Style/OneClassPerFile
|
|
66
|
+
# Internal alias for Git::Lib, used by the gem itself after the public constant
|
|
67
|
+
# is deprecated. Code outside the gem should not reference this constant.
|
|
68
|
+
# @api private
|
|
69
|
+
LibImpl = remove_const(:Lib)
|
|
70
|
+
|
|
71
|
+
# @api private
|
|
72
|
+
def self.const_missing(name)
|
|
73
|
+
return super unless name == :Lib
|
|
74
|
+
|
|
75
|
+
Git::Deprecation.warn(
|
|
76
|
+
'Git::Lib is deprecated and will be removed in version 5.x. ' \
|
|
77
|
+
'Use the #lib accessor on the object returned by Git.init, Git.open, or Git.clone instead.'
|
|
78
|
+
)
|
|
79
|
+
const_set(:Lib, LibImpl)
|
|
80
|
+
end
|
|
81
|
+
|
|
47
82
|
# g.config('user.name', 'Scott Chacon') # sets value
|
|
48
83
|
# g.config('user.email', 'email@email.com') # sets value
|
|
49
84
|
# g.config('user.name') # returns 'Scott Chacon'
|
|
50
85
|
# g.config # returns whole config hash
|
|
51
86
|
def config(name = nil, value = nil)
|
|
52
|
-
lib =
|
|
87
|
+
lib = LibImpl.new
|
|
53
88
|
if name && value
|
|
54
89
|
# set value
|
|
55
90
|
lib.config_set(name, value)
|
|
@@ -287,7 +322,7 @@ module Git # rubocop:disable Style/OneClassPerFile
|
|
|
287
322
|
# g.config('user.name') # returns 'Scott Chacon'
|
|
288
323
|
# g.config # returns whole config hash
|
|
289
324
|
def self.global_config(name = nil, value = nil)
|
|
290
|
-
lib =
|
|
325
|
+
lib = LibImpl.new(nil, nil)
|
|
291
326
|
if name && value
|
|
292
327
|
# set value
|
|
293
328
|
lib.global_config_set(name, value)
|
|
@@ -369,7 +404,7 @@ module Git # rubocop:disable Style/OneClassPerFile
|
|
|
369
404
|
# @param [String|NilClass] location the target repository location or nil for '.'
|
|
370
405
|
# @return [{String=>Hash}] the available references of the target repo.
|
|
371
406
|
def self.ls_remote(location = nil, options = {})
|
|
372
|
-
|
|
407
|
+
LibImpl.new.ls_remote(location, options)
|
|
373
408
|
end
|
|
374
409
|
|
|
375
410
|
# Open a an existing Git working directory
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: git
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.
|
|
4
|
+
version: 4.4.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Scott Chacon and others
|
|
@@ -310,8 +310,8 @@ licenses:
|
|
|
310
310
|
metadata:
|
|
311
311
|
homepage_uri: http://github.com/ruby-git/ruby-git
|
|
312
312
|
source_code_uri: http://github.com/ruby-git/ruby-git
|
|
313
|
-
changelog_uri: https://rubydoc.info/gems/git/4.
|
|
314
|
-
documentation_uri: https://rubydoc.info/gems/git/4.
|
|
313
|
+
changelog_uri: https://rubydoc.info/gems/git/4.4.1/file/CHANGELOG.md
|
|
314
|
+
documentation_uri: https://rubydoc.info/gems/git/4.4.1
|
|
315
315
|
rubygems_mfa_required: 'true'
|
|
316
316
|
rdoc_options: []
|
|
317
317
|
require_paths:
|
|
@@ -328,7 +328,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
328
328
|
version: '0'
|
|
329
329
|
requirements:
|
|
330
330
|
- git 2.28.0 or greater
|
|
331
|
-
rubygems_version: 4.0.
|
|
331
|
+
rubygems_version: 4.0.16
|
|
332
332
|
specification_version: 4
|
|
333
333
|
summary: An API to create, read, and manipulate Git repositories
|
|
334
334
|
test_files: []
|