git 2.1.1 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b171ab737c87e925d5752b52f2f05bdd2361592624d88b8db8db380dde051019
4
- data.tar.gz: d7b3b7cdaba4996288c4c0e8fffe7819da6c0933ade68ca810d9fb3519d5cda0
3
+ metadata.gz: 1e24e434e4639e6c31133234cee6d96708baf23d212c2f78cf4b94159810b090
4
+ data.tar.gz: 9fc37b011ac0cb0e87a0ee50b7993e751a7075a7496d6feb41431d2f3612e823
5
5
  SHA512:
6
- metadata.gz: '08dd5a4ea7b07230642e2dd87fca85ea634890f4700b77a996b3ff4443fc62d3c11a230c082c4d5b2c45c6c7d6916d9b98ce7362676e72d1a4bdd6f0fe2043e6'
7
- data.tar.gz: 568111f5579b6b1728974dbfa12dc4d742f3715e951645dbed9a54d91399492157b77659b2e956ee995c142c8dbb01f3ee07357fb4db2a9c2bd0e1870e83dc79
6
+ metadata.gz: 232624614200233e3f2ed308a4e73580345fef5be0a98e64f35992ecb222fa96c4163399b2f08afbfcdf69cd3b2f8f6c57c309fe9122f18a56e9bc31f827b301
7
+ data.tar.gz: 4ae699244e5ff9f679795279b8dbfeebe35ce8c9bfb7e5d9a18552b77b3d86b630f50bd9fedb4eec4f697269cfec6c90f72adfa2edeada25258ab457f3b5dad8
data/CHANGELOG.md CHANGED
@@ -5,6 +5,32 @@
5
5
 
6
6
  # Change Log
7
7
 
8
+ ## v2.3.0 (2024-09-01)
9
+
10
+ [Full Changelog](https://github.com/ruby-git/ruby-git/compare/v2.2.0..v2.3.0)
11
+
12
+ Changes since v2.2.0:
13
+
14
+ * f8bc987 Fix windows CI build error
15
+ * 471f5a8 Sanatize object ref sent to cat-file command
16
+ * 604a9a2 Make Git::Base#branch work when HEAD is detached
17
+
18
+ ## v2.2.0 (2024-08-26)
19
+
20
+ [Full Changelog](https://github.com/ruby-git/ruby-git/compare/v2.1.1..v2.2.0)
21
+
22
+ Changes since v2.1.1:
23
+
24
+ * 7292f2c Omit the test for signed commit data on Windows
25
+ * 2d6157c Document this gem's (aspirational) design philosophy
26
+ * d4f66ab Sanitize non-option arguments passed to `git name-rev`
27
+ * 0296442 Refactor Git::Lib#rev_parse
28
+ * 9b9b31e Verify that the revision-range passed to git log does not resemble a command-line option
29
+ * dc46ede Verify that the commit-ish passed to git describe does not resemble a command-line option
30
+ * 00c4939 Verify that the commit(s) passed to git diff do not resemble a command-line option
31
+ * a08f89b Update README
32
+ * 737c4bb ls-tree optional recursion into subtrees
33
+
8
34
  ## v2.1.1 (2024-06-01)
9
35
 
10
36
  [Full Changelog](https://github.com/ruby-git/ruby-git/compare/v2.1.0..v2.1.1)
data/CONTRIBUTING.md CHANGED
@@ -3,116 +3,191 @@
3
3
  # @title How To Contribute
4
4
  -->
5
5
 
6
- # Contributing to ruby-git
7
-
8
- Thank you for your interest in contributing to the ruby-git project.
9
-
10
- This document gives the guidelines for contributing to the ruby-git project.
11
- These guidelines may not fit every situation. When contributing use your best
12
- judgement.
13
-
14
- Propose changes to these guidelines with a pull request.
6
+ * [How to contribute](#how-to-contribute)
7
+ * [How to report an issue or request a feature](#how-to-report-an-issue-or-request-a-feature)
8
+ * [How to submit a code or documentation change](#how-to-submit-a-code-or-documentation-change)
9
+ * [Commit your changes to a fork of `ruby-git`](#commit-your-changes-to-a-fork-of-ruby-git)
10
+ * [Create a pull request](#create-a-pull-request)
11
+ * [Get your pull request reviewed](#get-your-pull-request-reviewed)
12
+ * [Design philosophy](#design-philosophy)
13
+ * [Direct mapping to git commands](#direct-mapping-to-git-commands)
14
+ * [Parameter naming](#parameter-naming)
15
+ * [Output processing](#output-processing)
16
+ * [Coding standards](#coding-standards)
17
+ * [1 PR = 1 Commit](#1-pr--1-commit)
18
+ * [Unit tests](#unit-tests)
19
+ * [Continuous integration](#continuous-integration)
20
+ * [Documentation](#documentation)
21
+ * [Licensing](#licensing)
22
+
23
+
24
+ # Contributing to the git gem
25
+
26
+ Thank you for your interest in contributing to the `ruby-git` project.
27
+
28
+ This document provides guidelines for contributing to the `ruby-git` project. While
29
+ these guidelines may not cover every situation, we encourage you to use your best
30
+ judgment when contributing.
31
+
32
+ If you have suggestions for improving these guidelines, please propose changes via a
33
+ pull request.
15
34
 
16
35
  ## How to contribute
17
36
 
18
- You can contribute in two ways:
37
+ You can contribute in the following ways:
19
38
 
20
- 1. [Report an issue or make a feature request](#how-to-report-an-issue-or-make-a-feature-request)
21
- 2. [Submit a code or documentation change](#how-to-submit-a-code-or-documentation-change)
39
+ 1. [Report an issue or request a
40
+ feature](#how-to-report-an-issue-or-request-a-feature)
41
+ 2. [Submit a code or documentation
42
+ change](#how-to-submit-a-code-or-documentation-change)
22
43
 
23
- ## How to report an issue or make a feature request
44
+ ## How to report an issue or request a feature
24
45
 
25
- ruby-git utilizes [GitHub Issues](https://help.github.com/en/github/managing-your-work-on-github/about-issues)
46
+ `ruby-git` utilizes [GitHub
47
+ Issues](https://help.github.com/en/github/managing-your-work-on-github/about-issues)
26
48
  for issue tracking and feature requests.
27
49
 
28
- Report an issue or feature request by [creating a ruby-git Github issue](https://github.com/ruby-git/ruby-git/issues/new).
29
- Fill in the template to describe the issue or feature request the best you can.
50
+ To report an issue or request a feature, please [create a `ruby-git` GitHub
51
+ issue](https://github.com/ruby-git/ruby-git/issues/new). Fill in the template as
52
+ thoroughly as possible to describe the issue or feature request.
30
53
 
31
54
  ## How to submit a code or documentation change
32
55
 
33
- There is three step process for code or documentation changes:
56
+ There is a three-step process for submitting code or documentation changes:
34
57
 
35
- 1. [Commit your changes to a fork of ruby-git](#commit-changes-to-a-fork-of-ruby-git)
58
+ 1. [Commit your changes to a fork of
59
+ `ruby-git`](#commit-your-changes-to-a-fork-of-ruby-git)
36
60
  2. [Create a pull request](#create-a-pull-request)
37
61
  3. [Get your pull request reviewed](#get-your-pull-request-reviewed)
38
62
 
39
- ### Commit changes to a fork of ruby-git
63
+ ### Commit your changes to a fork of `ruby-git`
40
64
 
41
- Make your changes in a fork of the ruby-git repository.
65
+ Make your changes in a fork of the `ruby-git` repository.
42
66
 
43
67
  ### Create a pull request
44
68
 
45
- See [this article](https://help.github.com/articles/about-pull-requests/) if you
46
- are not familiar with GitHub Pull Requests.
69
+ If you are not familiar with GitHub Pull Requests, please refer to [this
70
+ article](https://help.github.com/articles/about-pull-requests/).
47
71
 
48
72
  Follow the instructions in the pull request template.
49
73
 
50
74
  ### Get your pull request reviewed
51
75
 
52
- Code review takes place in a GitHub pull request using the [the Github pull request review feature](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-request-reviews).
76
+ Code review takes place in a GitHub pull request using the [GitHub pull request
77
+ review
78
+ feature](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-request-reviews).
53
79
 
54
80
  Once your pull request is ready for review, request a review from at least one
55
- [maintainer](MAINTAINERS.md) and any number of other contributors.
81
+ [maintainer](MAINTAINERS.md) and any other contributors you deem necessary.
82
+
83
+ During the review process, you may need to make additional commits, which should be
84
+ squashed. Additionally, you may need to rebase your branch to the latest `master`
85
+ branch if other changes have been merged.
86
+
87
+ At least one approval from a project maintainer is required before your pull request
88
+ can be merged. The maintainer is responsible for ensuring that the pull request meets
89
+ [the project's coding standards](#coding-standards).
90
+
91
+ ## Design philosophy
92
+
93
+ *Note: As of v2.x of the `git` gem, this design philosophy is aspirational. Future
94
+ versions may include interface changes to fully align with these principles.*
95
+
96
+ The `git` gem is designed as a lightweight wrapper around the `git` command-line
97
+ tool, providing Ruby developers with a simple and intuitive interface for
98
+ programmatically interacting with Git.
99
+
100
+ This gem adheres to the "principle of least surprise," ensuring that it does not
101
+ introduce unnecessary abstraction layers or modify Git's core functionality. Instead,
102
+ the gem maintains a close alignment with the existing `git` command-line interface,
103
+ avoiding extensions or alterations that could lead to unexpected behaviors.
104
+
105
+ By following this philosophy, the `git` gem allows users to leverage their existing
106
+ knowledge of Git while benefiting from the expressiveness and power of Ruby's syntax
107
+ and paradigms.
108
+
109
+ ### Direct mapping to git commands
56
110
 
57
- During the review process, you may need to make additional commits which would
58
- need to be squashed. It may also be necessary to rebase to master again if other
59
- changes are merged before your PR.
111
+ Git commands are implemented within the `Git::Base` class, with each method directly
112
+ corresponding to a `git` command. When a `Git::Base` object is instantiated via
113
+ `Git.open`, `Git.clone`, or `Git.init`, the user can invoke these methods to interact
114
+ with the underlying Git repository.
60
115
 
61
- At least one approval is required from a project maintainer before your pull
62
- request can be merged. The maintainer is responsible for ensuring that the pull
63
- request meets [the project's coding standards](#coding-standards).
116
+ For example, the `git add` command is implemented as `Git::Base#add`, and the `git
117
+ ls-files` command is implemented as `Git::Base#ls_files`.
118
+
119
+ When a single Git command serves multiple distinct purposes, method names within the
120
+ `Git::Base` class should use the `git` command name as a prefix, followed by a
121
+ descriptive suffix to indicate the specific function.
122
+
123
+ For instance, `#ls_files_untracked` and `#ls_files_staged` could be used to execute
124
+ the `git ls-files` command and return untracked and staged files, respectively.
125
+
126
+ To enhance usability, aliases may be introduced to provide more user-friendly method
127
+ names where appropriate.
128
+
129
+ ### Parameter naming
130
+
131
+ Parameters within the `git` gem methods are named after their corresponding long
132
+ command-line options, ensuring familiarity and ease of use for developers already
133
+ accustomed to Git. Note that not all Git command options are supported.
134
+
135
+ ### Output processing
136
+
137
+ The `git` gem translates the output of many Git commands into Ruby objects, making it
138
+ easier to work with programmatically.
139
+
140
+ These Ruby objects often include methods that allow for further Git operations where
141
+ useful, providing additional functionality while staying true to the underlying Git
142
+ behavior.
64
143
 
65
144
  ## Coding standards
66
145
 
67
- In order to ensure high quality, all pull requests must meet these requirements:
146
+ To ensure high-quality contributions, all pull requests must meet the following
147
+ requirements:
68
148
 
69
149
  ### 1 PR = 1 Commit
70
150
 
71
- * All commits for a PR must be squashed into one commit
72
- * To avoid an extra merge commit, the PR must be able to be merged as [a fast forward
73
- merge](https://git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging)
74
- * The easiest way to ensure a fast forward merge is to rebase your local branch to
75
- the ruby-git master branch
151
+ * All commits for a PR must be squashed into a single commit.
152
+ * To avoid an extra merge commit, the PR must be able to be merged as [a fast-forward
153
+ merge](https://git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging).
154
+ * The easiest way to ensure a fast-forward merge is to rebase your local branch to
155
+ the `ruby-git` master branch.
76
156
 
77
157
  ### Unit tests
78
158
 
79
- * All changes must be accompanied by new or modified unit tests
159
+ * All changes must be accompanied by new or modified unit tests.
80
160
  * The entire test suite must pass when `bundle exec rake default` is run from the
81
161
  project's local working copy.
82
162
 
83
- While working on specific features you can run individual test files or
84
- a group of tests using `bin/test`:
163
+ While working on specific features, you can run individual test files or a group of
164
+ tests using `bin/test`:
85
165
 
86
- # run a single file (from tests/units):
87
- $ bin/test test_object
166
+ ```bash
167
+ # run a single file (from tests/units):
168
+ $ bin/test test_object
88
169
 
89
- # run multiple files:
90
- $ bin/test test_object test_archive
170
+ # run multiple files:
171
+ $ bin/test test_object test_archive
91
172
 
92
- # run all unit tests:
93
- $ bin/test
173
+ # run all unit tests:
174
+ $ bin/test
175
+ ```
94
176
 
95
177
  ### Continuous integration
96
178
 
97
- * All tests must pass in the project's [GitHub Continuous Integration
98
- build](https://github.com/ruby-git/ruby-git/actions?query=workflow%3ACI) before the
99
- pull request will be merged.
100
- * The [Continuous Integration
101
- workflow](https://github.com/ruby-git/ruby-git/blob/master/.github/workflows/continuous_integration.yml)
102
- runs both `bundle exec rake default` and `bundle exec rake test:gem` from the
103
- project's [Rakefile](https://github.com/ruby-git/ruby-git/blob/master/Rakefile).
179
+ All tests must pass in the project's [GitHub Continuous Integration build](https://github.com/ruby-git/ruby-git/actions?query=workflow%3ACI) before the pull request will be merged.
180
+
181
+ The [Continuous Integration workflow](https://github.com/ruby-git/ruby-git/blob/master/.github/workflows/continuous_integration.yml) runs both `bundle exec rake default` and `bundle exec rake test:gem` from the project's [Rakefile](https://github.com/ruby-git/ruby-git/blob/master/Rakefile).
104
182
 
105
183
  ### Documentation
106
184
 
107
- * New and updated public methods must have [YARD](https://yardoc.org/) documentation
108
- added to them
109
- * New and updated public facing features should be documented in the project's
110
- [README.md](README.md)
185
+ New and updated public methods must include [YARD](https://yardoc.org/) documentation.
186
+
187
+ New and updated public-facing features should be documented in the project's [README.md](README.md).
111
188
 
112
189
  ## Licensing
113
190
 
114
- ruby-git uses [the MIT license](https://choosealicense.com/licenses/mit/) as
115
- declared in the [LICENSE](LICENSE) file.
191
+ `ruby-git` uses [the MIT license](https://choosealicense.com/licenses/mit/) as declared in the [LICENSE](LICENSE) file.
116
192
 
117
- Licensing is very important to open source projects. It helps ensure the
118
- software continues to be available under the terms that the author desired.
193
+ Licensing is critical to open-source projects as it ensures the software remains available under the terms desired by the author.
data/README.md CHANGED
@@ -236,6 +236,9 @@ g.index.writable?
236
236
  g.repo
237
237
  g.dir
238
238
 
239
+ # ls-tree with recursion into subtrees (list files)
240
+ g.ls_tree("HEAD", recursive: true)
241
+
239
242
  # log - returns a Git::Log object, which is an Enumerator of Git::Commit objects
240
243
  # default configuration returns a max of 30 commits
241
244
  g.log
@@ -274,7 +277,7 @@ tree.blobs
274
277
  tree.subtrees
275
278
  tree.children # blobs and subtrees
276
279
 
277
- g.revparse('v2.5:Makefile')
280
+ g.rev_parse('v2.0.0:README.md')
278
281
 
279
282
  g.branches # returns Git::Branch objects
280
283
  g.branches.local
data/lib/git/base.rb CHANGED
@@ -634,23 +634,33 @@ module Git
634
634
  # runs git rev-parse to convert the objectish to a full sha
635
635
  #
636
636
  # @example
637
- # git.revparse("HEAD^^")
638
- # git.revparse('v2.4^{tree}')
639
- # git.revparse('v2.4:/doc/index.html')
637
+ # git.rev_parse("HEAD^^")
638
+ # git.rev_parse('v2.4^{tree}')
639
+ # git.rev_parse('v2.4:/doc/index.html')
640
640
  #
641
- def revparse(objectish)
642
- self.lib.revparse(objectish)
641
+ def rev_parse(objectish)
642
+ self.lib.rev_parse(objectish)
643
643
  end
644
644
 
645
- def ls_tree(objectish)
646
- self.lib.ls_tree(objectish)
645
+ # For backwards compatibility
646
+ alias revparse rev_parse
647
+
648
+ def ls_tree(objectish, opts = {})
649
+ self.lib.ls_tree(objectish, opts)
647
650
  end
648
651
 
649
652
  def cat_file(objectish)
650
- self.lib.object_contents(objectish)
653
+ self.lib.cat_file(objectish)
651
654
  end
652
655
 
653
- # returns the name of the branch the working directory is currently on
656
+ # The name of the branch HEAD refers to or 'HEAD' if detached
657
+ #
658
+ # Returns one of the following:
659
+ # * The branch name that HEAD refers to (even if it is an unborn branch)
660
+ # * 'HEAD' if in a detached HEAD state
661
+ #
662
+ # @return [String] the name of the branch HEAD refers to or 'HEAD' if detached
663
+ #
654
664
  def current_branch
655
665
  self.lib.branch_current
656
666
  end
data/lib/git/lib.rb CHANGED
@@ -169,27 +169,33 @@ module Git
169
169
 
170
170
  ## READ COMMANDS ##
171
171
 
172
+ # Finds most recent tag that is reachable from a commit
172
173
  #
173
- # Returns most recent tag that is reachable from a commit
174
+ # @see https://git-scm.com/docs/git-describe git-describe
174
175
  #
175
- # accepts options:
176
- # :all
177
- # :tags
178
- # :contains
179
- # :debug
180
- # :exact_match
181
- # :dirty
182
- # :abbrev
183
- # :candidates
184
- # :long
185
- # :always
186
- # :math
187
- #
188
- # @param [String|NilClass] committish target commit sha or object name
189
- # @param [{Symbol=>Object}] opts the given options
190
- # @return [String] the tag name
191
- #
192
- def describe(committish=nil, opts={})
176
+ # @param commit_ish [String, nil] target commit sha or object name
177
+ #
178
+ # @param opts [Hash] the given options
179
+ #
180
+ # @option opts :all [Boolean]
181
+ # @option opts :tags [Boolean]
182
+ # @option opts :contains [Boolean]
183
+ # @option opts :debug [Boolean]
184
+ # @option opts :long [Boolean]
185
+ # @option opts :always [Boolean]
186
+ # @option opts :exact_match [Boolean]
187
+ # @option opts :dirty [true, String]
188
+ # @option opts :abbrev [String]
189
+ # @option opts :candidates [String]
190
+ # @option opts :match [String]
191
+ #
192
+ # @return [String] the tag name
193
+ #
194
+ # @raise [ArgumentError] if the commit_ish is a string starting with a hyphen
195
+ #
196
+ def describe(commit_ish = nil, opts = {})
197
+ assert_args_are_not_options('commit-ish object', commit_ish)
198
+
193
199
  arr_opts = []
194
200
 
195
201
  arr_opts << '--all' if opts[:all]
@@ -207,12 +213,42 @@ module Git
207
213
  arr_opts << "--candidates=#{opts[:candidates]}" if opts[:candidates]
208
214
  arr_opts << "--match=#{opts[:match]}" if opts[:match]
209
215
 
210
- arr_opts << committish if committish
216
+ arr_opts << commit_ish if commit_ish
211
217
 
212
218
  return command('describe', *arr_opts)
213
219
  end
214
220
 
215
- def log_commits(opts={})
221
+ # Return the commits that are within the given revision range
222
+ #
223
+ # @see https://git-scm.com/docs/git-log git-log
224
+ #
225
+ # @param opts [Hash] the given options
226
+ #
227
+ # @option opts :count [Integer] the maximum number of commits to return (maps to max-count)
228
+ # @option opts :all [Boolean]
229
+ # @option opts :cherry [Boolean]
230
+ # @option opts :since [String]
231
+ # @option opts :until [String]
232
+ # @option opts :grep [String]
233
+ # @option opts :author [String]
234
+ # @option opts :between [Array<String>] an array of two commit-ish strings to specify a revision range
235
+ #
236
+ # Only :between or :object options can be used, not both.
237
+ #
238
+ # @option opts :object [String] the revision range for the git log command
239
+ #
240
+ # Only :between or :object options can be used, not both.
241
+ #
242
+ # @option opts :path_limiter [Array<String>, String] only include commits that impact files from the specified paths
243
+ #
244
+ # @return [Array<String>] the log output
245
+ #
246
+ # @raise [ArgumentError] if the resulting revision range is a string starting with a hyphen
247
+ #
248
+ def log_commits(opts = {})
249
+ assert_args_are_not_options('between', opts[:between]&.first)
250
+ assert_args_are_not_options('object', opts[:object])
251
+
216
252
  arr_opts = log_common_options(opts)
217
253
 
218
254
  arr_opts << '--pretty=oneline'
@@ -222,7 +258,47 @@ module Git
222
258
  command_lines('log', *arr_opts).map { |l| l.split.first }
223
259
  end
224
260
 
225
- def full_log_commits(opts={})
261
+ # Return the commits that are within the given revision range
262
+ #
263
+ # @see https://git-scm.com/docs/git-log git-log
264
+ #
265
+ # @param opts [Hash] the given options
266
+ #
267
+ # @option opts :count [Integer] the maximum number of commits to return (maps to max-count)
268
+ # @option opts :all [Boolean]
269
+ # @option opts :cherry [Boolean]
270
+ # @option opts :since [String]
271
+ # @option opts :until [String]
272
+ # @option opts :grep [String]
273
+ # @option opts :author [String]
274
+ # @option opts :between [Array<String>] an array of two commit-ish strings to specify a revision range
275
+ #
276
+ # Only :between or :object options can be used, not both.
277
+ #
278
+ # @option opts :object [String] the revision range for the git log command
279
+ #
280
+ # Only :between or :object options can be used, not both.
281
+ #
282
+ # @option opts :path_limiter [Array<String>, String] only include commits that impact files from the specified paths
283
+ # @option opts :skip [Integer]
284
+ #
285
+ # @return [Array<Hash>] the log output parsed into an array of hashs for each commit
286
+ #
287
+ # Each hash contains the following keys:
288
+ # * 'sha' [String] the commit sha
289
+ # * 'author' [String] the author of the commit
290
+ # * 'message' [String] the commit message
291
+ # * 'parent' [Array<String>] the commit shas of the parent commits
292
+ # * 'tree' [String] the tree sha
293
+ # * 'author' [String] the author of the commit and timestamp of when the changes were created
294
+ # * 'committer' [String] the committer of the commit and timestamp of when the commit was applied
295
+ #
296
+ # @raise [ArgumentError] if the revision range (specified with :between or :object) is a string starting with a hyphen
297
+ #
298
+ def full_log_commits(opts = {})
299
+ assert_args_are_not_options('between', opts[:between]&.first)
300
+ assert_args_are_not_options('object', opts[:object])
301
+
226
302
  arr_opts = log_common_options(opts)
227
303
 
228
304
  arr_opts << '--pretty=raw'
@@ -235,36 +311,147 @@ module Git
235
311
  process_commit_log_data(full_log)
236
312
  end
237
313
 
238
- def revparse(string)
239
- return string if string =~ /^[A-Fa-f0-9]{40}$/ # passing in a sha - just no-op it
240
- rev = ['head', 'remotes', 'tags'].map do |d|
241
- File.join(@git_dir, 'refs', d, string)
242
- end.find do |path|
243
- File.file?(path)
244
- end
245
- return File.read(rev).chomp if rev
246
- command('rev-parse', string)
314
+ # Verify and resolve a Git revision to its full SHA
315
+ #
316
+ # @see https://git-scm.com/docs/git-rev-parse git-rev-parse
317
+ # @see https://git-scm.com/docs/git-rev-parse#_specifying_revisions Valid ways to specify revisions
318
+ # @see https://git-scm.com/docs/git-rev-parse#Documentation/git-rev-parse.txt-emltrefnamegtemegemmasterememheadsmasterememrefsheadsmasterem Ref disambiguation rules
319
+ #
320
+ # @example
321
+ # lib.rev_parse('HEAD') # => '9b9b31e704c0b85ffdd8d2af2ded85170a5af87d'
322
+ # lib.rev_parse('9b9b31e') # => '9b9b31e704c0b85ffdd8d2af2ded85170a5af87d'
323
+ #
324
+ # @param revision [String] the revision to resolve
325
+ #
326
+ # @return [String] the full commit hash
327
+ #
328
+ # @raise [Git::FailedError] if the revision cannot be resolved
329
+ # @raise [ArgumentError] if the revision is a string starting with a hyphen
330
+ #
331
+ def rev_parse(revision)
332
+ assert_args_are_not_options('rev', revision)
333
+
334
+ command('rev-parse', revision)
247
335
  end
248
336
 
249
- def namerev(string)
250
- command('name-rev', string).split[1]
337
+ # For backwards compatibility with the old method name
338
+ alias :revparse :rev_parse
339
+
340
+ # Find the first symbolic name for given commit_ish
341
+ #
342
+ # @param commit_ish [String] the commit_ish to find the symbolic name of
343
+ #
344
+ # @return [String, nil] the first symbolic name or nil if the commit_ish isn't found
345
+ #
346
+ # @raise [ArgumentError] if the commit_ish is a string starting with a hyphen
347
+ #
348
+ def name_rev(commit_ish)
349
+ assert_args_are_not_options('commit_ish', commit_ish)
350
+
351
+ command('name-rev', commit_ish).split[1]
251
352
  end
252
353
 
253
- def object_type(sha)
254
- command('cat-file', '-t', sha)
354
+ alias :namerev :name_rev
355
+
356
+ # Output the contents or other properties of one or more objects.
357
+ #
358
+ # @see https://git-scm.com/docs/git-cat-file git-cat-file
359
+ #
360
+ # @example Get the contents of a file without a block
361
+ # lib.cat_file_contents('README.md') # => "This is a README file\n"
362
+ #
363
+ # @example Get the contents of a file with a block
364
+ # lib.cat_file_contents('README.md') { |f| f.read } # => "This is a README file\n"
365
+ #
366
+ # @param object [String] the object whose contents to return
367
+ #
368
+ # @return [String] the object contents
369
+ #
370
+ # @raise [ArgumentError] if object is a string starting with a hyphen
371
+ #
372
+ def cat_file_contents(object, &block)
373
+ assert_args_are_not_options('object', object)
374
+
375
+ if block_given?
376
+ Tempfile.create do |file|
377
+ # If a block is given, write the output from the process to a temporary
378
+ # file and then yield the file to the block
379
+ #
380
+ command('cat-file', "-p", object, out: file, err: file)
381
+ file.rewind
382
+ yield file
383
+ end
384
+ else
385
+ # If a block is not given, return the file contents as a string
386
+ command('cat-file', '-p', object)
387
+ end
255
388
  end
256
389
 
257
- def object_size(sha)
258
- command('cat-file', '-s', sha).to_i
390
+ alias :object_contents :cat_file_contents
391
+
392
+ # Get the type for the given object
393
+ #
394
+ # @see https://git-scm.com/docs/git-cat-file git-cat-file
395
+ #
396
+ # @param object [String] the object to get the type
397
+ #
398
+ # @return [String] the object type
399
+ #
400
+ # @raise [ArgumentError] if object is a string starting with a hyphen
401
+ #
402
+ def cat_file_type(object)
403
+ assert_args_are_not_options('object', object)
404
+
405
+ command('cat-file', '-t', object)
259
406
  end
260
407
 
261
- # returns useful array of raw commit object data
262
- def commit_data(sha)
263
- sha = sha.to_s
264
- cdata = command_lines('cat-file', 'commit', sha)
265
- process_commit_data(cdata, sha)
408
+ alias :object_type :cat_file_type
409
+
410
+ # Get the size for the given object
411
+ #
412
+ # @see https://git-scm.com/docs/git-cat-file git-cat-file
413
+ #
414
+ # @param object [String] the object to get the type
415
+ #
416
+ # @return [String] the object type
417
+ #
418
+ # @raise [ArgumentError] if object is a string starting with a hyphen
419
+ #
420
+ def cat_file_size(object)
421
+ assert_args_are_not_options('object', object)
422
+
423
+ command('cat-file', '-s', object).to_i
424
+ end
425
+
426
+ alias :object_size :cat_file_size
427
+
428
+ # Return a hash of commit data
429
+ #
430
+ # @see https://git-scm.com/docs/git-cat-file git-cat-file
431
+ #
432
+ # @param object [String] the object to get the type
433
+ #
434
+ # @return [Hash] commit data
435
+ #
436
+ # The returned commit data has the following keys:
437
+ # * tree [String]
438
+ # * parent [Array<String>]
439
+ # * author [String] the author name, email, and commit timestamp
440
+ # * committer [String] the committer name, email, and merge timestamp
441
+ # * message [String] the commit message
442
+ # * gpgsig [String] the public signing key of the commit (if signed)
443
+ #
444
+ # @raise [ArgumentError] if object is a string starting with a hyphen
445
+ #
446
+ def cat_file_commit(object)
447
+ assert_args_are_not_options('object', object)
448
+
449
+ cdata = command_lines('cat-file', 'commit', object)
450
+ process_commit_data(cdata, object)
266
451
  end
267
452
 
453
+ alias :commit_data :cat_file_commit
454
+
268
455
  def process_commit_data(data, sha)
269
456
  hsh = {
270
457
  'sha' => sha,
@@ -299,12 +486,50 @@ module Git
299
486
  end
300
487
  end
301
488
 
302
- def tag_data(name)
303
- sha = sha.to_s
304
- tdata = command_lines('cat-file', 'tag', name)
305
- process_tag_data(tdata, name)
489
+ # Return a hash of annotated tag data
490
+ #
491
+ # Does not work with lightweight tags. List all annotated tags in your repository with the following command:
492
+ #
493
+ # ```sh
494
+ # git for-each-ref --format='%(refname:strip=2)' refs/tags | while read tag; do git cat-file tag $tag >/dev/null 2>&1 && echo $tag; done
495
+ # ```
496
+ #
497
+ # @see https://git-scm.com/docs/git-cat-file git-cat-file
498
+ #
499
+ # @param object [String] the tag to retrieve
500
+ #
501
+ # @return [Hash] tag data
502
+ #
503
+ # Example tag data returned:
504
+ # ```ruby
505
+ # {
506
+ # "name" => "annotated_tag",
507
+ # "object" => "46abbf07e3c564c723c7c039a43ab3a39e5d02dd",
508
+ # "type" => "commit",
509
+ # "tag" => "annotated_tag",
510
+ # "tagger" => "Scott Chacon <schacon@gmail.com> 1724799270 -0700",
511
+ # "message" => "Creating an annotated tag\n"
512
+ # }
513
+ # ```
514
+ #
515
+ # The returned commit data has the following keys:
516
+ # * object [String] the sha of the tag object
517
+ # * type [String]
518
+ # * tag [String] tag name
519
+ # * tagger [String] the name and email of the user who created the tag and the timestamp of when the tag was created
520
+ # * message [String] the tag message
521
+ #
522
+ # @raise [ArgumentError] if object is a string starting with a hyphen
523
+ #
524
+ def cat_file_tag(object)
525
+ assert_args_are_not_options('object', object)
526
+
527
+ tdata = command_lines('cat-file', 'tag', object)
528
+ process_tag_data(tdata, object)
306
529
  end
307
530
 
531
+ alias :tag_data :cat_file_tag
532
+
308
533
  def process_tag_data(data, name)
309
534
  hsh = { 'name' => name }
310
535
 
@@ -358,26 +583,15 @@ module Git
358
583
  return hsh_array
359
584
  end
360
585
 
361
- def object_contents(sha, &block)
362
- if block_given?
363
- Tempfile.create do |file|
364
- # If a block is given, write the output from the process to a temporary
365
- # file and then yield the file to the block
366
- #
367
- command('cat-file', "-p", sha, out: file, err: file)
368
- file.rewind
369
- yield file
370
- end
371
- else
372
- # If a block is not given, return stdout
373
- command('cat-file', '-p', sha)
374
- end
375
- end
376
-
377
- def ls_tree(sha)
586
+ def ls_tree(sha, opts = {})
378
587
  data = { 'blob' => {}, 'tree' => {}, 'commit' => {} }
379
588
 
380
- command_lines('ls-tree', sha).each do |line|
589
+ ls_tree_opts = []
590
+ ls_tree_opts << '-r' if opts[:recursive]
591
+ # path must be last arg
592
+ ls_tree_opts << opts[:path] if opts[:path]
593
+
594
+ command_lines('ls-tree', sha, *ls_tree_opts).each do |line|
381
595
  (info, filenm) = line.split("\t")
382
596
  (mode, type, sha) = info.split
383
597
  data[type][filenm] = {:mode => mode, :sha => sha}
@@ -483,8 +697,54 @@ module Git
483
697
  files
484
698
  end
485
699
 
700
+ # The state and name of branch pointed to by `HEAD`
701
+ #
702
+ # HEAD can be in the following states:
703
+ #
704
+ # **:active**: `HEAD` points to a branch reference which in turn points to a
705
+ # commit representing the tip of that branch. This is the typical state when
706
+ # working on a branch.
707
+ #
708
+ # **:unborn**: `HEAD` points to a branch reference that does not yet exist
709
+ # because no commits have been made on that branch. This state occurs in two
710
+ # scenarios:
711
+ #
712
+ # * When a repository is newly initialized, and no commits have been made on the
713
+ # initial branch.
714
+ # * When a new branch is created using `git checkout --orphan <branch>`, starting
715
+ # a new branch with no history.
716
+ #
717
+ # **:detached**: `HEAD` points directly to a specific commit (identified by its
718
+ # SHA) rather than a branch reference. This state occurs when you check out a
719
+ # commit, a tag, or any state that is not directly associated with a branch. The
720
+ # branch name in this case is `HEAD`.
721
+ #
722
+ HeadState = Struct.new(:state, :name)
723
+
724
+ # The current branch state which is the state of `HEAD`
725
+ #
726
+ # @return [HeadState] the state and name of the current branch
727
+ #
728
+ def current_branch_state
729
+ branch_name = command('branch', '--show-current')
730
+ return HeadState.new(:detached, 'HEAD') if branch_name.empty?
731
+
732
+ state =
733
+ begin
734
+ command('rev-parse', '--verify', '--quiet', branch_name)
735
+ :active
736
+ rescue Git::FailedError => e
737
+ raise unless e.result.status.exitstatus == 1 && e.result.stderr.empty?
738
+
739
+ :unborn
740
+ end
741
+
742
+ return HeadState.new(state, branch_name)
743
+ end
744
+
486
745
  def branch_current
487
- branches_all.select { |b| b[1] }.first[0] rescue nil
746
+ branch_name = command('branch', '--show-current')
747
+ branch_name.empty? ? 'HEAD' : branch_name
488
748
  end
489
749
 
490
750
  def branch_contains(commit, branch_name="")
@@ -521,7 +781,24 @@ module Git
521
781
  hsh
522
782
  end
523
783
 
784
+ # Validate that the given arguments cannot be mistaken for a command-line option
785
+ #
786
+ # @param arg_name [String] the name of the arguments to mention in the error message
787
+ # @param args [Array<String, nil>] the arguments to validate
788
+ #
789
+ # @raise [ArgumentError] if any of the parameters are a string starting with a hyphen
790
+ # @return [void]
791
+ #
792
+ def assert_args_are_not_options(arg_name, *args)
793
+ invalid_args = args.select { |arg| arg&.start_with?('-') }
794
+ if invalid_args.any?
795
+ raise ArgumentError, "Invalid #{arg_name}: '#{invalid_args.join("', '")}'"
796
+ end
797
+ end
798
+
524
799
  def diff_full(obj1 = 'HEAD', obj2 = nil, opts = {})
800
+ assert_args_are_not_options('commit or commit range', obj1, obj2)
801
+
525
802
  diff_opts = ['-p']
526
803
  diff_opts << obj1
527
804
  diff_opts << obj2 if obj2.is_a?(String)
@@ -531,6 +808,8 @@ module Git
531
808
  end
532
809
 
533
810
  def diff_stats(obj1 = 'HEAD', obj2 = nil, opts = {})
811
+ assert_args_are_not_options('commit or commit range', obj1, obj2)
812
+
534
813
  diff_opts = ['--numstat']
535
814
  diff_opts << obj1
536
815
  diff_opts << obj2 if obj2.is_a?(String)
@@ -551,6 +830,8 @@ module Git
551
830
  end
552
831
 
553
832
  def diff_name_status(reference1 = nil, reference2 = nil, opts = {})
833
+ assert_args_are_not_options('commit or commit range', reference1, reference2)
834
+
554
835
  opts_arr = ['--name-status']
555
836
  opts_arr << reference1 if reference1
556
837
  opts_arr << reference2 if reference2
data/lib/git/object.rb CHANGED
@@ -23,11 +23,11 @@ module Git
23
23
  end
24
24
 
25
25
  def sha
26
- @sha ||= @base.lib.revparse(@objectish)
26
+ @sha ||= @base.lib.rev_parse(@objectish)
27
27
  end
28
28
 
29
29
  def size
30
- @size ||= @base.lib.object_size(@objectish)
30
+ @size ||= @base.lib.cat_file_size(@objectish)
31
31
  end
32
32
 
33
33
  # Get the object's contents.
@@ -38,9 +38,9 @@ module Git
38
38
  # Use this for large files so that they are not held in memory.
39
39
  def contents(&block)
40
40
  if block_given?
41
- @base.lib.object_contents(@objectish, &block)
41
+ @base.lib.cat_file_contents(@objectish, &block)
42
42
  else
43
- @contents ||= @base.lib.object_contents(@objectish)
43
+ @contents ||= @base.lib.cat_file_contents(@objectish)
44
44
  end
45
45
  end
46
46
 
@@ -175,7 +175,7 @@ module Git
175
175
  end
176
176
 
177
177
  def name
178
- @base.lib.namerev(sha)
178
+ @base.lib.name_rev(sha)
179
179
  end
180
180
 
181
181
  def gtree
@@ -237,7 +237,7 @@ module Git
237
237
  def check_commit
238
238
  return if @tree
239
239
 
240
- data = @base.lib.commit_data(@objectish)
240
+ data = @base.lib.cat_file_commit(@objectish)
241
241
  set_commit(data)
242
242
  end
243
243
 
@@ -254,7 +254,7 @@ module Git
254
254
  end
255
255
 
256
256
  def annotated?
257
- @annotated ||= (@base.lib.object_type(self.name) == 'tag')
257
+ @annotated ||= (@base.lib.cat_file_type(self.name) == 'tag')
258
258
  end
259
259
 
260
260
  def message
@@ -279,7 +279,7 @@ module Git
279
279
  if !self.annotated?
280
280
  @message = @tagger = nil
281
281
  else
282
- tdata = @base.lib.tag_data(@name)
282
+ tdata = @base.lib.cat_file_tag(@name)
283
283
  @message = tdata['message'].chomp
284
284
  @tagger = Git::Author.new(tdata['tagger'])
285
285
  end
@@ -300,7 +300,7 @@ module Git
300
300
  return Git::Object::Tag.new(base, sha, objectish)
301
301
  end
302
302
 
303
- type ||= base.lib.object_type(objectish)
303
+ type ||= base.lib.cat_file_type(objectish)
304
304
  klass =
305
305
  case type
306
306
  when /blob/ then Blob
data/lib/git/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Git
2
2
  # The current gem version
3
3
  # @return [String] the current gem version.
4
- VERSION='2.1.1'
4
+ VERSION='2.3.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.1
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Chacon and others
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-06-01 00:00:00.000000000 Z
11
+ date: 2024-09-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -243,8 +243,8 @@ licenses:
243
243
  metadata:
244
244
  homepage_uri: http://github.com/ruby-git/ruby-git
245
245
  source_code_uri: http://github.com/ruby-git/ruby-git
246
- changelog_uri: https://rubydoc.info/gems/git/2.1.1/file/CHANGELOG.md
247
- documentation_uri: https://rubydoc.info/gems/git/2.1.1
246
+ changelog_uri: https://rubydoc.info/gems/git/2.3.0/file/CHANGELOG.md
247
+ documentation_uri: https://rubydoc.info/gems/git/2.3.0
248
248
  post_install_message:
249
249
  rdoc_options: []
250
250
  require_paths: