git 5.0.0.beta.1 → 5.0.0.beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/copilot-instructions.md +6 -0
- data/.github/prompts/iteratively-address-copilot-reviews.prompt.md +188 -0
- data/.github/skills/extract-facade-from-base-lib/KEYWORD_ARG_REMEDIATION.md +22 -0
- data/.github/skills/extract-facade-from-base-lib/SKILL.md +28 -14
- data/.github/skills/facade-implementation/SKILL.md +14 -0
- data/.github/skills/facade-test-conventions/SKILL.md +14 -0
- data/.rubocop.yml +5 -0
- data/README.md +51 -11
- data/UPGRADING.md +141 -0
- data/git.gemspec +5 -0
- data/lib/git/branch.rb +7 -18
- data/lib/git/branches.rb +2 -10
- data/lib/git/command_line/base.rb +10 -0
- data/lib/git/command_line/capturing.rb +5 -3
- data/lib/git/command_line/streaming.rb +5 -3
- data/lib/git/command_line.rb +3 -3
- data/lib/git/commands/base.rb +7 -6
- data/lib/git/commands/cat_file/batch.rb +6 -1
- data/lib/git/commands/cat_file/raw.rb +7 -1
- data/lib/git/commands/config_option_syntax/get_urlmatch.rb +5 -0
- data/lib/git/commands/show_ref/exclude_existing.rb +1 -1
- data/lib/git/commands/update_ref/batch.rb +1 -1
- data/lib/git/commands/version.rb +5 -0
- data/lib/git/commands.rb +5 -7
- data/lib/git/config.rb +17 -0
- data/lib/git/config_entry_info.rb +106 -0
- data/lib/git/configuring.rb +665 -0
- data/lib/git/deprecation.rb +9 -0
- data/lib/git/diff.rb +4 -8
- data/lib/git/diff_path_status.rb +2 -13
- data/lib/git/diff_stats.rb +1 -9
- data/lib/git/execution_context/global.rb +3 -28
- data/lib/git/execution_context/repository.rb +30 -41
- data/lib/git/execution_context.rb +43 -24
- data/lib/git/log.rb +3 -9
- data/lib/git/object.rb +14 -21
- data/lib/git/parsers/config_entry.rb +110 -0
- data/lib/git/parsers/ls_remote.rb +79 -0
- data/lib/git/remote.rb +7 -20
- data/lib/git/repository/branching.rb +183 -12
- data/lib/git/repository/committing.rb +64 -68
- data/lib/git/repository/configuring.rb +208 -13
- data/lib/git/repository/context_helpers.rb +264 -0
- data/lib/git/repository/factories.rb +682 -0
- data/lib/git/repository/inspecting.rb +99 -0
- data/lib/git/repository/maintenance.rb +65 -0
- data/lib/git/repository/merging.rb +63 -1
- data/lib/git/repository/object_operations.rb +133 -35
- data/lib/git/repository/path_resolver.rb +1 -1
- data/lib/git/repository/remote_operations.rb +166 -21
- data/lib/git/repository/staging.rb +187 -23
- data/lib/git/repository/stashing.rb +39 -3
- data/lib/git/repository/status_operations.rb +21 -0
- data/lib/git/repository.rb +68 -129
- data/lib/git/stash.rb +2 -9
- data/lib/git/stashes.rb +2 -7
- data/lib/git/status.rb +8 -17
- data/lib/git/version.rb +2 -2
- data/lib/git/worktree.rb +2 -15
- data/lib/git/worktrees.rb +2 -15
- data/lib/git.rb +180 -77
- data/redesign/3_architecture_implementation.md +148 -111
- data/redesign/Phase 4 - Step A.md +360 -0
- data/redesign/beta_release.md +107 -0
- data/redesign/c1c2_audit.md +566 -0
- data/redesign/c1c2_bucket6_lib_orphans.md +626 -0
- data/redesign/config_design.rb +501 -0
- metadata +19 -5
- data/lib/git/base.rb +0 -1204
- data/lib/git/lib.rb +0 -2855
data/lib/git/base.rb
DELETED
|
@@ -1,1204 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'logger'
|
|
4
|
-
require 'pathname'
|
|
5
|
-
require 'git/repository/path_resolver'
|
|
6
|
-
|
|
7
|
-
module Git
|
|
8
|
-
# The main public interface for interacting with Git commands
|
|
9
|
-
#
|
|
10
|
-
# Instead of creating a Git::Base directly, obtain a Git::Base instance by
|
|
11
|
-
# calling one of the follow {Git} class methods: {Git.open}, {Git.init},
|
|
12
|
-
# {Git.clone}, or {Git.bare}.
|
|
13
|
-
#
|
|
14
|
-
# @api public
|
|
15
|
-
#
|
|
16
|
-
class Base
|
|
17
|
-
# (see Git.bare)
|
|
18
|
-
def self.bare(git_dir, options = {})
|
|
19
|
-
paths = Git::Repository::PathResolver.resolve_paths(repository: git_dir, bare: true)
|
|
20
|
-
new(options.merge(paths))
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# (see Git.clone)
|
|
24
|
-
def self.clone(repository_url, directory, options = {})
|
|
25
|
-
lib_options = {}
|
|
26
|
-
lib_options[:git_ssh] = options[:git_ssh] if options.key?(:git_ssh)
|
|
27
|
-
clone_result = Git::Lib.new(lib_options, options[:log]).clone(repository_url, directory, options)
|
|
28
|
-
bare = options[:bare] || options[:mirror]
|
|
29
|
-
paths = Git::Repository::PathResolver.resolve_paths(
|
|
30
|
-
working_directory: clone_result[:working_directory],
|
|
31
|
-
repository: clone_result[:repository],
|
|
32
|
-
bare: bare
|
|
33
|
-
)
|
|
34
|
-
new(options.merge(paths))
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# (see Git.default_branch)
|
|
38
|
-
def self.repository_default_branch(repository, options = {})
|
|
39
|
-
Git::Lib.new(nil, options[:log]).repository_default_branch(repository)
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
# Returns (and initialize if needed) a Git::Config instance
|
|
43
|
-
#
|
|
44
|
-
# @return [Git::Config] the current config instance.
|
|
45
|
-
def self.config
|
|
46
|
-
@config ||= Config.new
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# @deprecated Use {Git.git_version} instead, which returns a {Git::Version} (not an Array).
|
|
50
|
-
# For the legacy array shape, call: `Git.git_version.to_a`
|
|
51
|
-
#
|
|
52
|
-
def self.binary_version(binary_path)
|
|
53
|
-
Git::Deprecation.warn(
|
|
54
|
-
'Git::Base.binary_version is deprecated and will be removed in 6.0. ' \
|
|
55
|
-
'Use Git.git_version instead, which returns a Git::Version ' \
|
|
56
|
-
'(not an Array). For the legacy array shape, call: Git.git_version.to_a'
|
|
57
|
-
)
|
|
58
|
-
Git.git_version(binary_path).to_a
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# Find the root of the working tree that contains `working_dir`
|
|
62
|
-
#
|
|
63
|
-
# Delegates to {Git::Repository::PathResolver.root_of_worktree}, using the
|
|
64
|
-
# global config for `binary_path` and `git_ssh`.
|
|
65
|
-
#
|
|
66
|
-
# @param working_dir [String] a path inside the working tree
|
|
67
|
-
#
|
|
68
|
-
# @return [String] the absolute path to the root of the working tree
|
|
69
|
-
#
|
|
70
|
-
# @raise [ArgumentError] if `working_dir` does not exist or is not inside a
|
|
71
|
-
# git working tree
|
|
72
|
-
#
|
|
73
|
-
def self.root_of_worktree(working_dir)
|
|
74
|
-
Git::Repository::PathResolver.root_of_worktree(working_dir)
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
# (see Git.open)
|
|
78
|
-
def self.open(working_dir, options = {})
|
|
79
|
-
raise ArgumentError, "'#{working_dir}' is not a directory" unless Dir.exist?(working_dir)
|
|
80
|
-
|
|
81
|
-
working_dir = root_of_worktree(working_dir) unless options[:repository]
|
|
82
|
-
|
|
83
|
-
paths = Git::Repository::PathResolver.resolve_paths(
|
|
84
|
-
working_directory: working_dir,
|
|
85
|
-
repository: options[:repository],
|
|
86
|
-
index: options[:index]
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
new(options.merge(paths))
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
# Create an object that executes Git commands in the context of a working
|
|
93
|
-
# copy or a bare repository.
|
|
94
|
-
#
|
|
95
|
-
# @param [Hash] options The options for this command (see list of valid
|
|
96
|
-
# options below)
|
|
97
|
-
#
|
|
98
|
-
# @option options [Pathname] :working_directory the path to the root of the working
|
|
99
|
-
# directory or `nil` if executing commands on a bare repository
|
|
100
|
-
#
|
|
101
|
-
# @option options [Pathname] :repository used to specify a non-standard path to
|
|
102
|
-
# the repository directory
|
|
103
|
-
#
|
|
104
|
-
# The default is `"<working_directory>/.git"`.
|
|
105
|
-
#
|
|
106
|
-
# @option options [Pathname] :index used to specify a non-standard path to an
|
|
107
|
-
# index file
|
|
108
|
-
#
|
|
109
|
-
# The default is `"<working_directory>/.git/index"`
|
|
110
|
-
#
|
|
111
|
-
# @option options [Logger] :log A logger to use for Git operations
|
|
112
|
-
#
|
|
113
|
-
# Git commands are logged at the `:info` level. Additional logging is done
|
|
114
|
-
# at the `:debug` level.
|
|
115
|
-
#
|
|
116
|
-
# @option options [String, nil] :git_ssh Path to a custom SSH executable or script
|
|
117
|
-
#
|
|
118
|
-
# Controls how SSH is configured for this {Git::Base} instance:
|
|
119
|
-
# - If this option is not provided, the global Git::Base.config.git_ssh setting is used.
|
|
120
|
-
# - If this option is explicitly set to nil, SSH is disabled for this instance.
|
|
121
|
-
# - If this option is a non-empty String, that value is used as the SSH command for
|
|
122
|
-
# this instance, overriding the global Git::Base.config.git_ssh setting.
|
|
123
|
-
#
|
|
124
|
-
# @option options [String, :use_global_config] :binary_path Path to the git binary
|
|
125
|
-
#
|
|
126
|
-
# Controls which git binary is used for commands routed through
|
|
127
|
-
# {Git::ExecutionContext} (i.e., commands already migrated to
|
|
128
|
-
# +Git::Commands::*+ classes). Commands still delegating through +Git::Lib+
|
|
129
|
-
# continue to use the global `Git::Base.config.binary_path` setting.
|
|
130
|
-
#
|
|
131
|
-
# This limitation will be resolved when the architectural migration to
|
|
132
|
-
# +Git::Repository+ is complete.
|
|
133
|
-
#
|
|
134
|
-
# - If this option is not provided, the global Git::Base.config.binary_path setting is used.
|
|
135
|
-
# - If this option is a String, that value is used as the git binary path for
|
|
136
|
-
# migrated commands, overriding the global Git::Base.config.binary_path setting.
|
|
137
|
-
# - Passing `nil` raises ArgumentError — there is no "unset the binary" semantic.
|
|
138
|
-
#
|
|
139
|
-
# @return [Git::Base] an object that can execute git commands on a working copy or
|
|
140
|
-
# bare repository
|
|
141
|
-
#
|
|
142
|
-
# @raise [ArgumentError] if `binary_path` is `nil`
|
|
143
|
-
#
|
|
144
|
-
def initialize(options = {})
|
|
145
|
-
setup_logger(options[:log])
|
|
146
|
-
@git_ssh = options.key?(:git_ssh) ? options[:git_ssh] : :use_global_config
|
|
147
|
-
if options.key?(:binary_path)
|
|
148
|
-
raise ArgumentError, 'binary_path must not be nil' if options[:binary_path].nil?
|
|
149
|
-
|
|
150
|
-
@binary_path = options[:binary_path]
|
|
151
|
-
else
|
|
152
|
-
@binary_path = :use_global_config
|
|
153
|
-
end
|
|
154
|
-
initialize_components(options)
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
# Update the index from the current worktree to prepare for the next commit
|
|
158
|
-
#
|
|
159
|
-
# @overload add(paths = '.', **options)
|
|
160
|
-
#
|
|
161
|
-
# @example Stage all changed files
|
|
162
|
-
# git.add
|
|
163
|
-
#
|
|
164
|
-
# @example Stage a specific file
|
|
165
|
-
# git.add('path/to/file.rb')
|
|
166
|
-
#
|
|
167
|
-
# @example Stage multiple files
|
|
168
|
-
# git.add(['path/to/file1.rb', 'path/to/file2.rb'])
|
|
169
|
-
#
|
|
170
|
-
# @example Stage all changes including deletions
|
|
171
|
-
# git.add(all: true)
|
|
172
|
-
#
|
|
173
|
-
# @param paths [String, Array<String>] a file or files to add (relative to
|
|
174
|
-
# the worktree root); defaults to `'.'` (all files)
|
|
175
|
-
#
|
|
176
|
-
# @param options [Hash] options for the add command
|
|
177
|
-
#
|
|
178
|
-
# @option options [Boolean, nil] :all (nil) add, modify, and remove index
|
|
179
|
-
# entries to match the worktree
|
|
180
|
-
#
|
|
181
|
-
# @option options [Boolean, nil] :force (nil) allow adding otherwise ignored
|
|
182
|
-
# files
|
|
183
|
-
#
|
|
184
|
-
# @return [String] git's stdout from the add
|
|
185
|
-
#
|
|
186
|
-
# @raise [ArgumentError] if unsupported options are provided
|
|
187
|
-
#
|
|
188
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
189
|
-
#
|
|
190
|
-
def add(paths = '.', **)
|
|
191
|
-
facade_repository.add(paths, **)
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
# adds a new remote to this repository
|
|
195
|
-
# url can be a git url or a Git::Base object if it's a local reference
|
|
196
|
-
#
|
|
197
|
-
# @git.add_remote('scotts_git', 'git://repo.or.cz/rubygit.git')
|
|
198
|
-
# @git.fetch('scotts_git')
|
|
199
|
-
# @git.merge('scotts_git/master')
|
|
200
|
-
#
|
|
201
|
-
# Options:
|
|
202
|
-
# :fetch => true
|
|
203
|
-
# :track => <branch_name>
|
|
204
|
-
def add_remote(name, url, opts = {})
|
|
205
|
-
facade_repository.add_remote(name, url, opts)
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
# changes current working directory for a block
|
|
209
|
-
# to the git working directory
|
|
210
|
-
#
|
|
211
|
-
# example
|
|
212
|
-
# @git.chdir do
|
|
213
|
-
# # write files
|
|
214
|
-
# @git.add
|
|
215
|
-
# @git.commit('message')
|
|
216
|
-
# end
|
|
217
|
-
def chdir # :yields: the working directory Pathname
|
|
218
|
-
Dir.chdir(dir.to_s) do
|
|
219
|
-
yield dir
|
|
220
|
-
end
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
# g.config('user.name', 'Scott Chacon') # sets value
|
|
224
|
-
# g.config('user.email', 'email@email.com') # sets value
|
|
225
|
-
# g.config('user.email', 'email@email.com', file: 'path/to/custom/config') # sets value in file
|
|
226
|
-
# g.config('user.name') # returns 'Scott Chacon'
|
|
227
|
-
# g.config # returns whole config hash
|
|
228
|
-
def config(name = nil, value = nil, options = {})
|
|
229
|
-
facade_repository.config(name, value, options)
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
# Returns a reference to the working directory
|
|
233
|
-
#
|
|
234
|
-
# @example
|
|
235
|
-
# @git.dir.to_s
|
|
236
|
-
# @git.dir.writable?
|
|
237
|
-
#
|
|
238
|
-
# @return [Pathname] the working directory path
|
|
239
|
-
#
|
|
240
|
-
def dir
|
|
241
|
-
@working_directory
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
# Returns a reference to the git index file
|
|
245
|
-
#
|
|
246
|
-
# @return [Pathname] the index file path
|
|
247
|
-
#
|
|
248
|
-
attr_reader :index
|
|
249
|
-
|
|
250
|
-
# Returns a reference to the git repository directory
|
|
251
|
-
#
|
|
252
|
-
# @example
|
|
253
|
-
# @git.repo.to_s
|
|
254
|
-
#
|
|
255
|
-
# @return [Pathname] the repository directory path
|
|
256
|
-
#
|
|
257
|
-
def repo
|
|
258
|
-
@repository
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
# returns the repository size in bytes
|
|
262
|
-
def repo_size
|
|
263
|
-
all_files = Dir.glob(File.join(repo.path, '**', '*'), File::FNM_DOTMATCH)
|
|
264
|
-
|
|
265
|
-
all_files.reject { |file| file.include?('..') }
|
|
266
|
-
.map { |file| File.expand_path(file) }
|
|
267
|
-
.uniq
|
|
268
|
-
.sum { |file| File.stat(file).size.to_i }
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
private
|
|
272
|
-
|
|
273
|
-
def deprecate_check_argument(check, must_exist)
|
|
274
|
-
unless check.nil?
|
|
275
|
-
Git::Deprecation.warn(
|
|
276
|
-
'The "check" argument is deprecated and will be removed in a future version. ' \
|
|
277
|
-
'Use "must_exist:" instead.'
|
|
278
|
-
)
|
|
279
|
-
end
|
|
280
|
-
# default is true
|
|
281
|
-
must_exist.nil? && check.nil? ? true : must_exist | check
|
|
282
|
-
end
|
|
283
|
-
|
|
284
|
-
def validate_path(path, must_exist)
|
|
285
|
-
Pathname.new(File.expand_path(path.to_s)).tap do |expanded_path|
|
|
286
|
-
raise ArgumentError, "path does not exist: #{expanded_path}" if must_exist && !expanded_path.exist?
|
|
287
|
-
end
|
|
288
|
-
end
|
|
289
|
-
|
|
290
|
-
public
|
|
291
|
-
|
|
292
|
-
def set_index(index_file, check = nil, must_exist: nil)
|
|
293
|
-
must_exist = deprecate_check_argument(check, must_exist)
|
|
294
|
-
@lib = nil
|
|
295
|
-
@facade_repository = nil
|
|
296
|
-
@index = validate_path(index_file, must_exist)
|
|
297
|
-
end
|
|
298
|
-
|
|
299
|
-
def set_working(work_dir, check = nil, must_exist: nil)
|
|
300
|
-
must_exist = deprecate_check_argument(check, must_exist)
|
|
301
|
-
@lib = nil
|
|
302
|
-
@facade_repository = nil
|
|
303
|
-
@working_directory = validate_path(work_dir, must_exist)
|
|
304
|
-
end
|
|
305
|
-
|
|
306
|
-
# returns +true+ if the branch exists locally
|
|
307
|
-
def local_branch?(branch)
|
|
308
|
-
facade_repository.local_branch?(branch)
|
|
309
|
-
end
|
|
310
|
-
|
|
311
|
-
def is_local_branch?(branch) # rubocop:disable Naming/PredicatePrefix
|
|
312
|
-
Git::Deprecation.warn(
|
|
313
|
-
'Git::Base#is_local_branch? is deprecated and will be removed in a future version. ' \
|
|
314
|
-
'Use Git::Base#local_branch? instead.'
|
|
315
|
-
)
|
|
316
|
-
local_branch?(branch)
|
|
317
|
-
end
|
|
318
|
-
|
|
319
|
-
# returns +true+ if the branch exists remotely
|
|
320
|
-
def remote_branch?(branch)
|
|
321
|
-
facade_repository.remote_branch?(branch)
|
|
322
|
-
end
|
|
323
|
-
|
|
324
|
-
def is_remote_branch?(branch) # rubocop:disable Naming/PredicatePrefix
|
|
325
|
-
Git::Deprecation.warn(
|
|
326
|
-
'Git::Base#is_remote_branch? is deprecated and will be removed in a future version. ' \
|
|
327
|
-
'Use Git::Base#remote_branch? instead.'
|
|
328
|
-
)
|
|
329
|
-
remote_branch?(branch)
|
|
330
|
-
end
|
|
331
|
-
|
|
332
|
-
# returns +true+ if the branch exists
|
|
333
|
-
def branch?(branch)
|
|
334
|
-
facade_repository.branch?(branch)
|
|
335
|
-
end
|
|
336
|
-
|
|
337
|
-
def is_branch?(branch) # rubocop:disable Naming/PredicatePrefix
|
|
338
|
-
Git::Deprecation.warn(
|
|
339
|
-
'Git::Base#is_branch? is deprecated and will be removed in a future version. ' \
|
|
340
|
-
'Use Git::Base#branch? instead.'
|
|
341
|
-
)
|
|
342
|
-
branch?(branch)
|
|
343
|
-
end
|
|
344
|
-
|
|
345
|
-
# this is a convenience method for accessing the class that wraps all the
|
|
346
|
-
# actual 'git' forked system calls. At some point I hope to replace the Git::Lib
|
|
347
|
-
# class with one that uses native methods or libgit C bindings
|
|
348
|
-
def lib
|
|
349
|
-
@lib ||= Git::Lib.new(self, @logger)
|
|
350
|
-
end
|
|
351
|
-
|
|
352
|
-
# Returns the {Git::Repository} facade for this repository
|
|
353
|
-
#
|
|
354
|
-
# @return [Git::Repository]
|
|
355
|
-
# @api private
|
|
356
|
-
def facade_repository
|
|
357
|
-
@facade_repository ||= Git::Repository.new(
|
|
358
|
-
execution_context: Git::ExecutionContext::Repository.from_base(self, logger: @logger)
|
|
359
|
-
)
|
|
360
|
-
end
|
|
361
|
-
|
|
362
|
-
# Returns the per-instance git_ssh configuration value
|
|
363
|
-
#
|
|
364
|
-
# This may be:
|
|
365
|
-
# * a [String] path when an explicit git_ssh command has been configured
|
|
366
|
-
# * the Symbol `:use_global_config` when this instance is using the global config
|
|
367
|
-
# * `nil` when SSH has been explicitly disabled for this instance
|
|
368
|
-
#
|
|
369
|
-
# @return [String, Symbol, nil] the git_ssh configuration value for this instance
|
|
370
|
-
# @api private
|
|
371
|
-
attr_reader :git_ssh
|
|
372
|
-
|
|
373
|
-
# Returns the per-instance git binary path configuration value
|
|
374
|
-
#
|
|
375
|
-
# This may be:
|
|
376
|
-
# * a [String] path when an explicit binary path has been configured
|
|
377
|
-
# * the Symbol `:use_global_config` when this instance is using the global config
|
|
378
|
-
#
|
|
379
|
-
# @return [String, Symbol] the binary_path configuration value for this instance
|
|
380
|
-
# @api private
|
|
381
|
-
attr_reader :binary_path
|
|
382
|
-
|
|
383
|
-
# Run a grep for 'string' on the HEAD of the git repository
|
|
384
|
-
#
|
|
385
|
-
# @example Limit grep's scope by calling grep() from a specific object:
|
|
386
|
-
# git.object("v2.3").grep('TODO')
|
|
387
|
-
#
|
|
388
|
-
# @example Using grep results:
|
|
389
|
-
# git.grep("TODO").each do |sha, arr|
|
|
390
|
-
# puts "in blob #{sha}:"
|
|
391
|
-
# arr.each do |line_no, match_string|
|
|
392
|
-
# puts "\t line #{line_no}: '#{match_string}'"
|
|
393
|
-
# end
|
|
394
|
-
# end
|
|
395
|
-
#
|
|
396
|
-
# @param string [String] the string to search for
|
|
397
|
-
# @param path_limiter [String, Pathname, Array<String, Pathname>] a path or array
|
|
398
|
-
# of paths to limit the search to or nil for no limit
|
|
399
|
-
# @param opts [Hash] options to pass to the underlying `git grep` command
|
|
400
|
-
#
|
|
401
|
-
# @option opts [Boolean, nil] :ignore_case (nil) ignore case when matching
|
|
402
|
-
# @option opts [Boolean, nil] :invert_match (nil) select non-matching lines
|
|
403
|
-
# @option opts [Boolean, nil] :extended_regexp (nil) use extended regular expressions
|
|
404
|
-
# @option opts [String] :object (HEAD) the object to search from
|
|
405
|
-
#
|
|
406
|
-
# @return [Hash<String, Array>] a hash of arrays
|
|
407
|
-
# ```Ruby
|
|
408
|
-
# {
|
|
409
|
-
# 'tree-ish1' => [[line_no1, match_string1], ...],
|
|
410
|
-
# 'tree-ish2' => [[line_no1, match_string1], ...],
|
|
411
|
-
# ...
|
|
412
|
-
# }
|
|
413
|
-
# ```
|
|
414
|
-
#
|
|
415
|
-
def grep(string, path_limiter = nil, opts = {})
|
|
416
|
-
opts = opts.merge(object: facade_repository.rev_parse('HEAD')) unless opts.key?(:object)
|
|
417
|
-
facade_repository.grep(string, path_limiter, opts)
|
|
418
|
-
end
|
|
419
|
-
|
|
420
|
-
# List the files in the worktree that are ignored by git
|
|
421
|
-
# @return [Array<String>] the list of ignored files relative to the root of the worktree
|
|
422
|
-
#
|
|
423
|
-
def ignored_files
|
|
424
|
-
facade_repository.ignored_files
|
|
425
|
-
end
|
|
426
|
-
|
|
427
|
-
# removes file(s) from the git repository
|
|
428
|
-
def rm(path = '.', opts = {})
|
|
429
|
-
facade_repository.rm(path, opts)
|
|
430
|
-
end
|
|
431
|
-
|
|
432
|
-
alias remove rm
|
|
433
|
-
|
|
434
|
-
# resets the working directory to the provided commitish
|
|
435
|
-
def reset(commitish = nil, opts = {})
|
|
436
|
-
facade_repository.reset(commitish, **opts)
|
|
437
|
-
end
|
|
438
|
-
|
|
439
|
-
# resets the working directory to the commitish with '--hard'
|
|
440
|
-
#
|
|
441
|
-
# @deprecated Use {#reset} with `hard: true` instead.
|
|
442
|
-
#
|
|
443
|
-
def reset_hard(commitish = nil, opts = {})
|
|
444
|
-
Git::Deprecation.warn(
|
|
445
|
-
'Git::Base#reset_hard is deprecated and will be removed in a future version. ' \
|
|
446
|
-
'Use Git::Base#reset(commitish, hard: true) instead.'
|
|
447
|
-
)
|
|
448
|
-
opts = { hard: true }.merge(opts)
|
|
449
|
-
lib.reset(commitish, opts)
|
|
450
|
-
end
|
|
451
|
-
|
|
452
|
-
# cleans the working directory
|
|
453
|
-
#
|
|
454
|
-
# options:
|
|
455
|
-
# :force
|
|
456
|
-
# :d
|
|
457
|
-
# :ff
|
|
458
|
-
#
|
|
459
|
-
def clean(opts = {})
|
|
460
|
-
facade_repository.clean(opts)
|
|
461
|
-
end
|
|
462
|
-
|
|
463
|
-
# returns the most recent tag that is reachable from a commit
|
|
464
|
-
#
|
|
465
|
-
# options:
|
|
466
|
-
# :all
|
|
467
|
-
# :tags
|
|
468
|
-
# :contains
|
|
469
|
-
# :debug
|
|
470
|
-
# :exact_match
|
|
471
|
-
# :dirty
|
|
472
|
-
# :abbrev
|
|
473
|
-
# :candidates
|
|
474
|
-
# :long
|
|
475
|
-
# :always
|
|
476
|
-
# :match
|
|
477
|
-
#
|
|
478
|
-
def describe(committish = nil, opts = {})
|
|
479
|
-
lib.describe(committish, opts)
|
|
480
|
-
end
|
|
481
|
-
|
|
482
|
-
# reverts the working directory to the provided commitish.
|
|
483
|
-
# Accepts a range, such as comittish..HEAD
|
|
484
|
-
#
|
|
485
|
-
# options:
|
|
486
|
-
# :no_edit
|
|
487
|
-
#
|
|
488
|
-
def revert(commitish = nil, opts = {})
|
|
489
|
-
facade_repository.revert(commitish, **opts)
|
|
490
|
-
end
|
|
491
|
-
|
|
492
|
-
# commits all pending changes in the index file to the git repository
|
|
493
|
-
#
|
|
494
|
-
# options:
|
|
495
|
-
# :all
|
|
496
|
-
# :allow_empty
|
|
497
|
-
# :amend
|
|
498
|
-
# :author
|
|
499
|
-
#
|
|
500
|
-
def commit(message, opts = {})
|
|
501
|
-
facade_repository.commit(message, **opts)
|
|
502
|
-
end
|
|
503
|
-
|
|
504
|
-
# commits all pending changes in the index file to the git repository,
|
|
505
|
-
# but automatically adds all modified files without having to explicitly
|
|
506
|
-
# calling @git.add() on them.
|
|
507
|
-
def commit_all(message, opts = {})
|
|
508
|
-
facade_repository.commit_all(message, **opts)
|
|
509
|
-
end
|
|
510
|
-
|
|
511
|
-
# checks out a branch as the new git working directory
|
|
512
|
-
def checkout(*, **)
|
|
513
|
-
facade_repository.checkout(*, **)
|
|
514
|
-
end
|
|
515
|
-
|
|
516
|
-
# checks out an old version of a file
|
|
517
|
-
def checkout_file(version, file)
|
|
518
|
-
facade_repository.checkout_file(version, file)
|
|
519
|
-
end
|
|
520
|
-
|
|
521
|
-
# fetches changes from a remote branch - this does not modify the working directory,
|
|
522
|
-
# it just gets the changes from the remote if there are any
|
|
523
|
-
def fetch(remote = 'origin', opts = {})
|
|
524
|
-
facade_repository.fetch(remote, opts)
|
|
525
|
-
end
|
|
526
|
-
|
|
527
|
-
# Push changes to a remote repository
|
|
528
|
-
#
|
|
529
|
-
# @overload push(remote = nil, branch = nil, options = {})
|
|
530
|
-
#
|
|
531
|
-
# @param remote [String] the remote repository to push to
|
|
532
|
-
#
|
|
533
|
-
# @param branch [String] the branch to push
|
|
534
|
-
#
|
|
535
|
-
# @param options [Hash] options to pass to the push command
|
|
536
|
-
#
|
|
537
|
-
# @option options [Boolean, nil] :mirror (nil) push all refs under refs/heads/, refs/tags/ and refs/remotes/
|
|
538
|
-
#
|
|
539
|
-
# @option options [Boolean, nil] :delete (nil) delete refs that don't exist on the remote
|
|
540
|
-
#
|
|
541
|
-
# @option options [Boolean, nil] :force (nil) force updates
|
|
542
|
-
#
|
|
543
|
-
# @option options [Boolean, nil] :tags (nil) push all refs under refs/tags/
|
|
544
|
-
#
|
|
545
|
-
# @option options [String, Array<String>] :push_option (nil) push options to transmit
|
|
546
|
-
#
|
|
547
|
-
# @return [String] the stdout output from the push command
|
|
548
|
-
#
|
|
549
|
-
# @raise [Git::FailedError] if the push fails
|
|
550
|
-
# @raise [ArgumentError] if a branch is given without a remote
|
|
551
|
-
#
|
|
552
|
-
def push(*, **)
|
|
553
|
-
facade_repository.push(*, **)
|
|
554
|
-
end
|
|
555
|
-
|
|
556
|
-
# merges one or more branches into the current working branch
|
|
557
|
-
#
|
|
558
|
-
# you can specify more than one branch to merge by passing an array of branches
|
|
559
|
-
def merge(branch, message = 'merge', opts = {})
|
|
560
|
-
facade_repository.merge(branch, message, opts)
|
|
561
|
-
end
|
|
562
|
-
|
|
563
|
-
# iterates over the files which are unmerged
|
|
564
|
-
def each_conflict(&)
|
|
565
|
-
facade_repository.each_conflict(&)
|
|
566
|
-
end
|
|
567
|
-
|
|
568
|
-
# Pulls the given branch from the given remote into the current branch
|
|
569
|
-
#
|
|
570
|
-
# @param remote [String] the remote repository to pull from
|
|
571
|
-
# @param branch [String] the branch to pull from
|
|
572
|
-
# @param opts [Hash] options to pass to the pull command
|
|
573
|
-
#
|
|
574
|
-
# @option opts [Boolean, nil] :allow_unrelated_histories (nil) merges histories of
|
|
575
|
-
# two projects that started their lives independently
|
|
576
|
-
# @example pulls from origin/master
|
|
577
|
-
# @git.pull
|
|
578
|
-
# @example pulls from upstream/master
|
|
579
|
-
# @git.pull('upstream')
|
|
580
|
-
# @example pulls from upstream/develop
|
|
581
|
-
# @git.pull('upstream', 'develop')
|
|
582
|
-
#
|
|
583
|
-
# @return [String] the stdout output from the pull command
|
|
584
|
-
#
|
|
585
|
-
# @raise [Git::FailedError] if the pull fails
|
|
586
|
-
# @raise [ArgumentError] if a branch is given without a remote
|
|
587
|
-
def pull(remote = nil, branch = nil, opts = {})
|
|
588
|
-
facade_repository.pull(remote, branch, opts)
|
|
589
|
-
end
|
|
590
|
-
|
|
591
|
-
# returns an array of Git:Remote objects
|
|
592
|
-
def remotes
|
|
593
|
-
facade_repository.remotes
|
|
594
|
-
end
|
|
595
|
-
|
|
596
|
-
# sets the url for a remote
|
|
597
|
-
# url can be a git url or a Git::Base object if it's a local reference
|
|
598
|
-
#
|
|
599
|
-
# @git.set_remote_url('scotts_git', 'git://repo.or.cz/rubygit.git')
|
|
600
|
-
#
|
|
601
|
-
def set_remote_url(name, url)
|
|
602
|
-
facade_repository.set_remote_url(name, url)
|
|
603
|
-
end
|
|
604
|
-
|
|
605
|
-
# Configures which branches are fetched for a remote
|
|
606
|
-
#
|
|
607
|
-
# Uses `git remote set-branches` to set or append fetch refspecs. When the `add:`
|
|
608
|
-
# option is not given, the `--add` option is not passed to the git command
|
|
609
|
-
#
|
|
610
|
-
# @example Replace fetched branches with a single glob pattern
|
|
611
|
-
# git = Git.open('/path/to/repo')
|
|
612
|
-
# # Only fetch branches matching "feature/*" from origin
|
|
613
|
-
# git.remote_set_branches('origin', 'feature/*')
|
|
614
|
-
#
|
|
615
|
-
# @example Append a glob pattern to existing fetched branches
|
|
616
|
-
# git = Git.open('/path/to/repo')
|
|
617
|
-
# # Keep existing fetch refspecs and add all release branches
|
|
618
|
-
# git.remote_set_branches('origin', 'release/*', add: true)
|
|
619
|
-
#
|
|
620
|
-
# @example Configure multiple explicit branches
|
|
621
|
-
# git = Git.open('/path/to/repo')
|
|
622
|
-
# git.remote_set_branches('origin', 'main', 'development', 'hotfix')
|
|
623
|
-
#
|
|
624
|
-
# @param name [String] the remote name (for example, "origin")
|
|
625
|
-
# @param branches [Array<String>] branch names or globs (for example, '*')
|
|
626
|
-
# @param add [Boolean] when true, append to existing refspecs instead of replacing them
|
|
627
|
-
#
|
|
628
|
-
# @return [nil]
|
|
629
|
-
#
|
|
630
|
-
# @raise [ArgumentError] if no branches are provided @raise [Git::FailedError] if
|
|
631
|
-
# the underlying git command fails
|
|
632
|
-
#
|
|
633
|
-
def remote_set_branches(name, *branches, add: false)
|
|
634
|
-
facade_repository.remote_set_branches(name, *branches, add: add)
|
|
635
|
-
end
|
|
636
|
-
|
|
637
|
-
# removes a remote from this repository
|
|
638
|
-
#
|
|
639
|
-
# @git.remove_remote('scott_git')
|
|
640
|
-
def remove_remote(name)
|
|
641
|
-
facade_repository.remove_remote(name)
|
|
642
|
-
end
|
|
643
|
-
|
|
644
|
-
# returns an array of all Git::Object::Tag objects for this repository
|
|
645
|
-
def tags
|
|
646
|
-
facade_repository.tags
|
|
647
|
-
end
|
|
648
|
-
|
|
649
|
-
# Create a new git tag
|
|
650
|
-
#
|
|
651
|
-
# @example
|
|
652
|
-
# repo.add_tag('tag_name', object_reference)
|
|
653
|
-
# repo.add_tag('tag_name', object_reference, {:options => 'here'})
|
|
654
|
-
# repo.add_tag('tag_name', {:options => 'here'})
|
|
655
|
-
#
|
|
656
|
-
# @param [String] name The name of the tag to add
|
|
657
|
-
# @param [Hash] options Options to pass to `git tag`.
|
|
658
|
-
# See [git-tag](https://git-scm.com/docs/git-tag) for more details.
|
|
659
|
-
# @option options [Boolean, nil] :annotate (nil) make an unsigned, annotated tag object
|
|
660
|
-
# @option options [Boolean, nil] :a (nil) an alias for the `:annotate` option
|
|
661
|
-
# @option options [Boolean, nil] :d (nil) delete existing tag with the given name —
|
|
662
|
-
# deprecated; use {#delete_tag} instead (alias: `:delete`)
|
|
663
|
-
# @option options [Boolean, nil] :delete (nil) delete existing tag with the given name —
|
|
664
|
-
# deprecated; use {#delete_tag} instead (alias: `:d`)
|
|
665
|
-
# @option options [Boolean, nil] :f (nil) replace an existing tag with the given name (instead of failing)
|
|
666
|
-
# @option options [String] :message Use the given tag message
|
|
667
|
-
# @option options [String] :m An alias for the `:message` option
|
|
668
|
-
# @option options [Boolean, nil] :s (nil) make a GPG-signed tag
|
|
669
|
-
#
|
|
670
|
-
def add_tag(name, *options)
|
|
671
|
-
facade_repository.add_tag(name, *options)
|
|
672
|
-
end
|
|
673
|
-
|
|
674
|
-
# deletes a tag
|
|
675
|
-
def delete_tag(name)
|
|
676
|
-
facade_repository.delete_tag(name)
|
|
677
|
-
end
|
|
678
|
-
|
|
679
|
-
# Creates an archive of the given tree-ish and writes it to a file
|
|
680
|
-
#
|
|
681
|
-
# @api public
|
|
682
|
-
#
|
|
683
|
-
# @param treeish [String] the commit, tag, branch, or tree to archive
|
|
684
|
-
#
|
|
685
|
-
# @param file [String, nil] destination file path; a temp file is created
|
|
686
|
-
# if `nil`
|
|
687
|
-
#
|
|
688
|
-
# @param opts [Hash] archive options (see {Git::Repository::ObjectOperations#archive})
|
|
689
|
-
#
|
|
690
|
-
# @return [String] the path to the written archive file
|
|
691
|
-
#
|
|
692
|
-
# @raise [ArgumentError] if unsupported options are provided
|
|
693
|
-
#
|
|
694
|
-
# @raise [ArgumentError] if `file` is an existing directory
|
|
695
|
-
#
|
|
696
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
697
|
-
#
|
|
698
|
-
# @example Archive HEAD to a zip file
|
|
699
|
-
# git.archive('HEAD', '/tmp/release.zip', format: 'zip')
|
|
700
|
-
# #=> "/tmp/release.zip"
|
|
701
|
-
#
|
|
702
|
-
def archive(treeish, file = nil, opts = {})
|
|
703
|
-
facade_repository.archive(treeish, file, opts)
|
|
704
|
-
end
|
|
705
|
-
|
|
706
|
-
# repacks the repository
|
|
707
|
-
def repack
|
|
708
|
-
lib.repack
|
|
709
|
-
end
|
|
710
|
-
|
|
711
|
-
def gc
|
|
712
|
-
lib.gc
|
|
713
|
-
end
|
|
714
|
-
|
|
715
|
-
# Verifies the connectivity and validity of objects in the database
|
|
716
|
-
#
|
|
717
|
-
# Runs `git fsck` to check repository integrity and identify dangling,
|
|
718
|
-
# missing, or unreachable objects.
|
|
719
|
-
#
|
|
720
|
-
# @overload fsck(objects = [], options = {})
|
|
721
|
-
# @param objects [Array<String>] specific objects to treat as heads for unreachability trace.
|
|
722
|
-
# If no objects are given, git fsck defaults to using the index file, all SHA-1
|
|
723
|
-
# references in the refs namespace, and all reflogs.
|
|
724
|
-
# @param [Hash] options options to pass to the underlying `git fsck` command
|
|
725
|
-
#
|
|
726
|
-
# @option options [Boolean, nil] :unreachable (nil) print unreachable objects
|
|
727
|
-
# @option options [Boolean, nil] :strict (nil) enable strict checking
|
|
728
|
-
# @option options [Boolean, nil] :connectivity_only (nil) check only connectivity (faster)
|
|
729
|
-
# @option options [Boolean, nil] :root (nil) report root nodes
|
|
730
|
-
# @option options [Boolean, nil] :tags (nil) report tags
|
|
731
|
-
# @option options [Boolean, nil] :cache (nil) consider objects in the index
|
|
732
|
-
# @option options [Boolean, nil] :no_reflogs (nil) do not consider reflogs
|
|
733
|
-
# @option options [Boolean, nil] :lost_found (nil) write dangling objects to .git/lost-found
|
|
734
|
-
# (note: this modifies the repository by creating files)
|
|
735
|
-
# @option options [Boolean, nil] :dangling print dangling objects (true/false/nil for default)
|
|
736
|
-
# @option options [Boolean, nil] :full check objects in alternate pools (true/false/nil for default)
|
|
737
|
-
# @option options [Boolean, nil] :name_objects name objects by refs (true/false/nil for default)
|
|
738
|
-
# @option options [Boolean, nil] :references check refs database consistency (true/false/nil for default)
|
|
739
|
-
#
|
|
740
|
-
# @return [Git::FsckResult] categorized objects flagged by fsck
|
|
741
|
-
#
|
|
742
|
-
# @example Check repository integrity
|
|
743
|
-
# result = git.fsck
|
|
744
|
-
# result.dangling.each { |obj| puts "#{obj.type}: #{obj.oid}" }
|
|
745
|
-
#
|
|
746
|
-
# @example Check with strict mode and suppress dangling output
|
|
747
|
-
# result = git.fsck(strict: true, no_dangling: true)
|
|
748
|
-
#
|
|
749
|
-
# @example Check if repository has any issues
|
|
750
|
-
# result = git.fsck
|
|
751
|
-
# puts "Repository is clean" if result.empty?
|
|
752
|
-
#
|
|
753
|
-
# @example List root commits
|
|
754
|
-
# result = git.fsck(root: true)
|
|
755
|
-
# result.root.each { |obj| puts obj.oid }
|
|
756
|
-
#
|
|
757
|
-
# @example Check specific objects
|
|
758
|
-
# result = git.fsck('abc1234', 'def5678')
|
|
759
|
-
#
|
|
760
|
-
# rubocop:disable Style/ArgumentsForwarding
|
|
761
|
-
def fsck(*objects, **opts)
|
|
762
|
-
facade_repository.fsck(*objects, **opts)
|
|
763
|
-
end
|
|
764
|
-
# rubocop:enable Style/ArgumentsForwarding
|
|
765
|
-
|
|
766
|
-
def apply(file)
|
|
767
|
-
return unless File.exist?(file)
|
|
768
|
-
|
|
769
|
-
lib.apply(file)
|
|
770
|
-
end
|
|
771
|
-
|
|
772
|
-
def apply_mail(file)
|
|
773
|
-
lib.apply_mail(file) if File.exist?(file)
|
|
774
|
-
end
|
|
775
|
-
|
|
776
|
-
# Shows objects
|
|
777
|
-
#
|
|
778
|
-
# @param [String|NilClass] objectish the target object reference (nil == HEAD)
|
|
779
|
-
# @param [String|NilClass] path the path of the file to be shown
|
|
780
|
-
# @return [String] the object information
|
|
781
|
-
def show(objectish = nil, path = nil)
|
|
782
|
-
facade_repository.show(objectish, path)
|
|
783
|
-
end
|
|
784
|
-
|
|
785
|
-
## LOWER LEVEL INDEX OPERATIONS ##
|
|
786
|
-
|
|
787
|
-
def with_index(new_index) # :yields: new_index
|
|
788
|
-
old_index = @index
|
|
789
|
-
set_index(new_index, false)
|
|
790
|
-
return_value = yield @index
|
|
791
|
-
set_index(old_index)
|
|
792
|
-
return_value
|
|
793
|
-
end
|
|
794
|
-
|
|
795
|
-
def with_temp_index(&)
|
|
796
|
-
# Workaround for JRUBY, since they handle the TempFile path different.
|
|
797
|
-
# MUST be improved to be safer and OS independent.
|
|
798
|
-
if RUBY_PLATFORM == 'java'
|
|
799
|
-
temp_path = "/tmp/temp-index-#{(0...15).map { ('a'..'z').to_a[rand(26)] }.join}"
|
|
800
|
-
else
|
|
801
|
-
tempfile = Tempfile.new('temp-index')
|
|
802
|
-
temp_path = tempfile.path
|
|
803
|
-
tempfile.close
|
|
804
|
-
tempfile.unlink
|
|
805
|
-
end
|
|
806
|
-
|
|
807
|
-
with_index(temp_path, &)
|
|
808
|
-
end
|
|
809
|
-
|
|
810
|
-
def checkout_index(opts = {})
|
|
811
|
-
facade_repository.checkout_index(opts)
|
|
812
|
-
end
|
|
813
|
-
|
|
814
|
-
def read_tree(treeish, opts = {})
|
|
815
|
-
lib.read_tree(treeish, opts)
|
|
816
|
-
end
|
|
817
|
-
|
|
818
|
-
def write_tree
|
|
819
|
-
facade_repository.write_tree
|
|
820
|
-
end
|
|
821
|
-
|
|
822
|
-
def write_and_commit_tree(opts = {})
|
|
823
|
-
Git::Object::Commit.new(self, facade_repository.write_and_commit_tree(**opts))
|
|
824
|
-
end
|
|
825
|
-
|
|
826
|
-
def update_ref(branch, commit)
|
|
827
|
-
facade_repository.update_ref(branch, commit)
|
|
828
|
-
end
|
|
829
|
-
|
|
830
|
-
def ls_files(location = nil)
|
|
831
|
-
facade_repository.ls_files(location)
|
|
832
|
-
end
|
|
833
|
-
|
|
834
|
-
def with_working(work_dir) # :yields: the working directory Pathname
|
|
835
|
-
return_value = false
|
|
836
|
-
old_working = @working_directory
|
|
837
|
-
set_working(work_dir)
|
|
838
|
-
Dir.chdir work_dir do
|
|
839
|
-
return_value = yield @working_directory
|
|
840
|
-
end
|
|
841
|
-
set_working(old_working)
|
|
842
|
-
return_value
|
|
843
|
-
end
|
|
844
|
-
|
|
845
|
-
def with_temp_working(&)
|
|
846
|
-
tempfile = Tempfile.new('temp-workdir')
|
|
847
|
-
temp_dir = tempfile.path
|
|
848
|
-
tempfile.close
|
|
849
|
-
tempfile.unlink
|
|
850
|
-
Dir.mkdir(temp_dir, 0o700)
|
|
851
|
-
with_working(temp_dir, &)
|
|
852
|
-
end
|
|
853
|
-
|
|
854
|
-
# runs git rev-parse to convert the objectish to a full sha
|
|
855
|
-
#
|
|
856
|
-
# @example
|
|
857
|
-
# git.rev_parse("HEAD^^")
|
|
858
|
-
# git.rev_parse('v2.4^{tree}')
|
|
859
|
-
# git.rev_parse('v2.4:/doc/index.html')
|
|
860
|
-
#
|
|
861
|
-
def rev_parse(objectish)
|
|
862
|
-
facade_repository.rev_parse(objectish)
|
|
863
|
-
end
|
|
864
|
-
|
|
865
|
-
# For backwards compatibility
|
|
866
|
-
alias revparse rev_parse
|
|
867
|
-
|
|
868
|
-
# Returns the number of entries in a git tree object
|
|
869
|
-
#
|
|
870
|
-
# @example Count recursive entries in the HEAD tree
|
|
871
|
-
# git.tree_depth('HEAD^{tree}') #=> 42
|
|
872
|
-
#
|
|
873
|
-
# @param objectish [String] the tree-ish object to recurse into
|
|
874
|
-
#
|
|
875
|
-
# @return [Integer] the number of entries in the recursive tree listing
|
|
876
|
-
#
|
|
877
|
-
# @raise [Git::FailedError] when git exits with a non-zero exit status
|
|
878
|
-
#
|
|
879
|
-
# @see Git::Repository::ObjectOperations#tree_depth
|
|
880
|
-
#
|
|
881
|
-
def tree_depth(objectish)
|
|
882
|
-
facade_repository.tree_depth(objectish)
|
|
883
|
-
end
|
|
884
|
-
|
|
885
|
-
# Lists the objects in a git tree object
|
|
886
|
-
#
|
|
887
|
-
# @example List all top-level objects
|
|
888
|
-
# git.ls_tree('HEAD')
|
|
889
|
-
# # => { 'blob' => { 'README.md' => { mode: '100644', sha: '...' } }, ... }
|
|
890
|
-
#
|
|
891
|
-
# @param objectish [String] the tree-ish object to list
|
|
892
|
-
#
|
|
893
|
-
# @param opts [Hash] additional options
|
|
894
|
-
#
|
|
895
|
-
# @option opts [Boolean, nil] :recursive (nil) recurse into subtrees
|
|
896
|
-
#
|
|
897
|
-
# @option opts [String, Array<String>] :path (nil) limit the listing to
|
|
898
|
-
# the given path or array of paths
|
|
899
|
-
#
|
|
900
|
-
# @return [Hash<String, Hash<String, Hash>>] a three-level Hash keyed by
|
|
901
|
-
# object type (`'blob'`, `'tree'`, `'commit'`), then by filename, then
|
|
902
|
-
# holding `:mode` and `:sha` values
|
|
903
|
-
#
|
|
904
|
-
# @raise [ArgumentError] when unsupported options are provided
|
|
905
|
-
#
|
|
906
|
-
# @raise [Git::FailedError] when git exits with a non-zero exit status
|
|
907
|
-
#
|
|
908
|
-
def ls_tree(objectish, opts = {})
|
|
909
|
-
facade_repository.ls_tree(objectish, opts)
|
|
910
|
-
end
|
|
911
|
-
|
|
912
|
-
def cat_file(objectish)
|
|
913
|
-
lib.cat_file(objectish)
|
|
914
|
-
end
|
|
915
|
-
|
|
916
|
-
# The name of the branch HEAD refers to or 'HEAD' if detached
|
|
917
|
-
#
|
|
918
|
-
# Returns one of the following:
|
|
919
|
-
# * The branch name that HEAD refers to (even if it is an unborn branch)
|
|
920
|
-
# * 'HEAD' if in a detached HEAD state
|
|
921
|
-
#
|
|
922
|
-
# @return [String] the name of the branch HEAD refers to or 'HEAD' if detached
|
|
923
|
-
#
|
|
924
|
-
def current_branch
|
|
925
|
-
facade_repository.current_branch
|
|
926
|
-
end
|
|
927
|
-
|
|
928
|
-
# @return [Git::Branch] an object for branch_name
|
|
929
|
-
def branch(branch_name = current_branch)
|
|
930
|
-
facade_repository.branch(branch_name)
|
|
931
|
-
end
|
|
932
|
-
|
|
933
|
-
# @return [Git::Branches] a collection of all the branches in the repository.
|
|
934
|
-
# Each branch is represented as a {Git::Branch}.
|
|
935
|
-
def branches
|
|
936
|
-
facade_repository.branches
|
|
937
|
-
end
|
|
938
|
-
|
|
939
|
-
# Returns a {Git::Worktree} object for the given path and optional commitish
|
|
940
|
-
#
|
|
941
|
-
# @example Create a worktree object for an existing path
|
|
942
|
-
# worktree = repo.worktree('/path/to/worktree')
|
|
943
|
-
#
|
|
944
|
-
# @param dir [String] filesystem path of the worktree
|
|
945
|
-
#
|
|
946
|
-
# @param commitish [String, nil] branch, tag, or commit to check out
|
|
947
|
-
#
|
|
948
|
-
# @return [Git::Worktree] worktree object for the given path
|
|
949
|
-
#
|
|
950
|
-
def worktree(dir, commitish = nil)
|
|
951
|
-
facade_repository.worktree(dir, commitish)
|
|
952
|
-
end
|
|
953
|
-
|
|
954
|
-
# Returns a {Git::Worktrees} collection of all worktrees in the repository
|
|
955
|
-
#
|
|
956
|
-
# @example List paths for all worktrees
|
|
957
|
-
# repo.worktrees.each { |wt| puts wt.dir }
|
|
958
|
-
#
|
|
959
|
-
# @return [Git::Worktrees] all linked and main worktrees
|
|
960
|
-
#
|
|
961
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
962
|
-
#
|
|
963
|
-
def worktrees
|
|
964
|
-
facade_repository.worktrees
|
|
965
|
-
end
|
|
966
|
-
|
|
967
|
-
# @return [Git::Object::Commit] a commit object
|
|
968
|
-
def commit_tree(tree = nil, opts = {})
|
|
969
|
-
Git::Object::Commit.new(self, facade_repository.commit_tree(tree, **opts))
|
|
970
|
-
end
|
|
971
|
-
|
|
972
|
-
# @return [Git::Diff] a Git::Diff object
|
|
973
|
-
def diff(objectish = 'HEAD', obj2 = nil)
|
|
974
|
-
facade_repository.diff(objectish, obj2)
|
|
975
|
-
end
|
|
976
|
-
|
|
977
|
-
# @return [Git::Object] a Git object
|
|
978
|
-
def gblob(objectish)
|
|
979
|
-
facade_repository.gblob(objectish)
|
|
980
|
-
end
|
|
981
|
-
|
|
982
|
-
# @return [Git::Object] a Git object
|
|
983
|
-
def gcommit(objectish)
|
|
984
|
-
facade_repository.gcommit(objectish)
|
|
985
|
-
end
|
|
986
|
-
|
|
987
|
-
# @return [Git::Object] a Git object
|
|
988
|
-
def gtree(objectish)
|
|
989
|
-
facade_repository.gtree(objectish)
|
|
990
|
-
end
|
|
991
|
-
|
|
992
|
-
# @return [Git::Log] a log with the specified number of commits
|
|
993
|
-
def log(count = 30)
|
|
994
|
-
facade_repository.log(count)
|
|
995
|
-
end
|
|
996
|
-
|
|
997
|
-
# Return commits that are within the given revision range
|
|
998
|
-
#
|
|
999
|
-
# @param opts [Hash] options for the log query
|
|
1000
|
-
# @return [Array<Hash>] the parsed raw log output for each commit
|
|
1001
|
-
def full_log_commits(opts = {})
|
|
1002
|
-
facade_repository.full_log_commits(opts)
|
|
1003
|
-
end
|
|
1004
|
-
|
|
1005
|
-
# returns a Git::Object of the appropriate type
|
|
1006
|
-
# you can also call @git.gtree('tree'), but that's
|
|
1007
|
-
# just for readability. If you call @git.gtree('HEAD') it will
|
|
1008
|
-
# still return a Git::Object::Commit object.
|
|
1009
|
-
#
|
|
1010
|
-
# object calls a method that will run a rev-parse
|
|
1011
|
-
# on the objectish and determine the type of the object and return
|
|
1012
|
-
# an appropriate object for that type
|
|
1013
|
-
#
|
|
1014
|
-
# @return [Git::Object] an instance of the appropriate type of Git::Object
|
|
1015
|
-
def object(objectish)
|
|
1016
|
-
facade_repository.object(objectish)
|
|
1017
|
-
end
|
|
1018
|
-
|
|
1019
|
-
# @return [Git::Remote] a remote of the specified name
|
|
1020
|
-
def remote(remote_name = 'origin')
|
|
1021
|
-
facade_repository.remote(remote_name)
|
|
1022
|
-
end
|
|
1023
|
-
|
|
1024
|
-
# @return [Git::Status] a status object
|
|
1025
|
-
def status
|
|
1026
|
-
facade_repository.status
|
|
1027
|
-
end
|
|
1028
|
-
|
|
1029
|
-
# @return [Git::Object::Tag] a tag object
|
|
1030
|
-
def tag(tag_name)
|
|
1031
|
-
facade_repository.tag(tag_name)
|
|
1032
|
-
end
|
|
1033
|
-
|
|
1034
|
-
# Find as good common ancestors as possible for a merge
|
|
1035
|
-
# example: g.merge_base('master', 'some_branch', 'some_sha', octopus: true)
|
|
1036
|
-
#
|
|
1037
|
-
# @return [Array<Git::Object::Commit>] a collection of common ancestors
|
|
1038
|
-
def merge_base(*)
|
|
1039
|
-
facade_repository.merge_base(*).map { |sha| gcommit(sha) }
|
|
1040
|
-
end
|
|
1041
|
-
|
|
1042
|
-
# Returns the full unified diff patch text between two commits
|
|
1043
|
-
#
|
|
1044
|
-
# @example Get the patch for the most recent commit
|
|
1045
|
-
# repo.diff_full #=> "diff --git a/lib/foo.rb b/lib/foo.rb\n..."
|
|
1046
|
-
#
|
|
1047
|
-
# @param obj1 [String] the first commit or object to compare; defaults to
|
|
1048
|
-
# `'HEAD'`
|
|
1049
|
-
#
|
|
1050
|
-
# @param obj2 [String, nil] the second commit or object to compare
|
|
1051
|
-
#
|
|
1052
|
-
# When `nil`, the comparison is against the index or working tree.
|
|
1053
|
-
#
|
|
1054
|
-
# @param opts [Hash] options to filter the diff
|
|
1055
|
-
#
|
|
1056
|
-
# @option opts [String, Pathname, Array<String, Pathname>, nil] :path_limiter (nil)
|
|
1057
|
-
# limit the diff to the given path(s)
|
|
1058
|
-
#
|
|
1059
|
-
# @return [String] the unified diff patch output
|
|
1060
|
-
#
|
|
1061
|
-
# @note Unknown option keys are silently ignored for backward compatibility;
|
|
1062
|
-
# only `:path_limiter` is forwarded to the underlying command.
|
|
1063
|
-
#
|
|
1064
|
-
# @raise [Git::FailedError] if git exits outside the allowed range (exit code > 1)
|
|
1065
|
-
#
|
|
1066
|
-
# @see Git::Repository::Diffing#diff_full
|
|
1067
|
-
#
|
|
1068
|
-
def diff_full(obj1 = 'HEAD', obj2 = nil, opts = {})
|
|
1069
|
-
facade_repository.diff_full(obj1, obj2, opts.slice(:path_limiter))
|
|
1070
|
-
end
|
|
1071
|
-
|
|
1072
|
-
# Returns a lazy {Git::DiffStats} object for accessing diff statistics
|
|
1073
|
-
#
|
|
1074
|
-
# Compares (1) two commits, (2) a commit against the working tree, or (3) the
|
|
1075
|
-
# index against the working tree and constructs a lazy {Git::DiffStats} that
|
|
1076
|
-
# computes per-file insertion and deletion counts on demand when its accessor
|
|
1077
|
-
# methods are called.
|
|
1078
|
-
#
|
|
1079
|
-
# **Comparing two commits**
|
|
1080
|
-
#
|
|
1081
|
-
# When both objectish and obj2 are provided, the comparison is between those two
|
|
1082
|
-
# refs (commits, tags, branches, etc.).
|
|
1083
|
-
#
|
|
1084
|
-
# **Comparing a commit against the working tree**
|
|
1085
|
-
#
|
|
1086
|
-
# When only objectish is provided (and isn't nil), the comparison is between
|
|
1087
|
-
# objectish and the working tree; the stats reflect all changes since objectish.
|
|
1088
|
-
#
|
|
1089
|
-
# **Comparing the index against the working tree**
|
|
1090
|
-
#
|
|
1091
|
-
# When objectish is explicitly `nil` then obj2 must be omitted or `nil`. In this
|
|
1092
|
-
# case, the comparison is between the index and the working tree; the stats reflect
|
|
1093
|
-
# unstaged changes.
|
|
1094
|
-
#
|
|
1095
|
-
# @example Get working tree stats since HEAD
|
|
1096
|
-
# repo.diff_stats.insertions #=> 3
|
|
1097
|
-
#
|
|
1098
|
-
# @example Compare two specific commits
|
|
1099
|
-
# repo.diff_stats('abc1234', 'def5678')
|
|
1100
|
-
#
|
|
1101
|
-
# @example Get unstaged stats (index vs. working tree)
|
|
1102
|
-
# repo.diff_stats(nil).insertions
|
|
1103
|
-
#
|
|
1104
|
-
# @example Limit stats to a sub-path
|
|
1105
|
-
# repo.diff_stats('HEAD~1', 'HEAD', path_limiter: 'lib/')
|
|
1106
|
-
#
|
|
1107
|
-
# @param objectish [String, nil] the first commit or object to compare; defaults to
|
|
1108
|
-
# `'HEAD'`; pass `nil` to compare the index against the working tree
|
|
1109
|
-
#
|
|
1110
|
-
# @param obj2 [String, nil] the second commit or object to compare
|
|
1111
|
-
#
|
|
1112
|
-
# @param opts [Hash] options to filter the diff
|
|
1113
|
-
#
|
|
1114
|
-
# @option opts [String, Pathname, Array<String, Pathname>, nil] :path_limiter (nil)
|
|
1115
|
-
# limit the stats to the given path(s)
|
|
1116
|
-
#
|
|
1117
|
-
# @return [Git::DiffStats] a lazy stats object for the comparison
|
|
1118
|
-
#
|
|
1119
|
-
# @note Unknown option keys are silently ignored for backward compatibility;
|
|
1120
|
-
# only `:path_limiter` is forwarded to the underlying command.
|
|
1121
|
-
#
|
|
1122
|
-
# @raise [ArgumentError] if `objectish` or `obj2` starts with `"-"`
|
|
1123
|
-
#
|
|
1124
|
-
# @raise [ArgumentError] if `objectish` is `nil` but `obj2` is not
|
|
1125
|
-
#
|
|
1126
|
-
# @see Git::Repository::Diffing#diff_stats
|
|
1127
|
-
#
|
|
1128
|
-
def diff_stats(objectish = 'HEAD', obj2 = nil, opts = {})
|
|
1129
|
-
facade_repository.diff_stats(objectish, obj2, opts.slice(:path_limiter))
|
|
1130
|
-
end
|
|
1131
|
-
|
|
1132
|
-
# Returns the file path status between two commits
|
|
1133
|
-
#
|
|
1134
|
-
# @example Get all changed files between HEAD and the previous commit
|
|
1135
|
-
# repo.diff_path_status.to_h #=> { "README.md" => "M", "lib/foo.rb" => "A" }
|
|
1136
|
-
#
|
|
1137
|
-
# @param objectish [String] the first commit or object to compare; defaults to
|
|
1138
|
-
# `'HEAD'`
|
|
1139
|
-
#
|
|
1140
|
-
# @param obj2 [String, nil] the second commit or object to compare
|
|
1141
|
-
#
|
|
1142
|
-
# @param opts [Hash] options to filter the diff
|
|
1143
|
-
#
|
|
1144
|
-
# @option opts [String, Pathname, Array<String, Pathname>, nil] :path_limiter (nil)
|
|
1145
|
-
# limit the status report to specified path(s)
|
|
1146
|
-
#
|
|
1147
|
-
# @option opts [String, Pathname, Array<String, Pathname>, nil] :path (nil)
|
|
1148
|
-
# deprecated; use `:path_limiter` instead
|
|
1149
|
-
#
|
|
1150
|
-
# @return [Git::DiffPathStatus] the name-status report for the comparison
|
|
1151
|
-
#
|
|
1152
|
-
# @raise [ArgumentError] if `objectish` or `obj2` starts with `"-"`
|
|
1153
|
-
#
|
|
1154
|
-
# @raise [Git::FailedError] if git exits outside the allowed range (exit code > 1)
|
|
1155
|
-
#
|
|
1156
|
-
# @see Git::Repository::Diffing#diff_path_status
|
|
1157
|
-
#
|
|
1158
|
-
def diff_path_status(objectish = 'HEAD', obj2 = nil, opts = {})
|
|
1159
|
-
facade_repository.diff_path_status(objectish, obj2, opts.slice(:path_limiter, :path))
|
|
1160
|
-
end
|
|
1161
|
-
|
|
1162
|
-
# Compares the index and the working directory
|
|
1163
|
-
#
|
|
1164
|
-
# @example List all files with unstaged changes
|
|
1165
|
-
# repo.diff_files #=> { "lib/foo.rb" => { mode_index: "100644", ... } }
|
|
1166
|
-
#
|
|
1167
|
-
# @return [Hash{String => Hash}] a hash keyed by file path; see
|
|
1168
|
-
# {Git::Repository::Diffing#diff_files} for the full key list
|
|
1169
|
-
#
|
|
1170
|
-
# @raise [Git::FailedError] if git exits outside the allowed range (exit code > 1)
|
|
1171
|
-
#
|
|
1172
|
-
# @see Git::Repository::Diffing#diff_files
|
|
1173
|
-
#
|
|
1174
|
-
def diff_files
|
|
1175
|
-
facade_repository.diff_files
|
|
1176
|
-
end
|
|
1177
|
-
|
|
1178
|
-
# Alias for {#diff_path_status}; provided for backward compatibility
|
|
1179
|
-
#
|
|
1180
|
-
# @return [Git::DiffPathStatus] the name-status report for the comparison
|
|
1181
|
-
#
|
|
1182
|
-
# @deprecated Use {#diff_path_status} instead
|
|
1183
|
-
#
|
|
1184
|
-
# @see #diff_path_status
|
|
1185
|
-
alias diff_name_status diff_path_status
|
|
1186
|
-
|
|
1187
|
-
private
|
|
1188
|
-
|
|
1189
|
-
# Initializes the logger from the provided options
|
|
1190
|
-
# @param log_option [Logger, nil] The logger instance from options.
|
|
1191
|
-
def setup_logger(log_option)
|
|
1192
|
-
@logger = log_option || Logger.new(nil)
|
|
1193
|
-
@logger.info('Starting Git')
|
|
1194
|
-
end
|
|
1195
|
-
|
|
1196
|
-
# Initializes the core git objects based on the provided options
|
|
1197
|
-
# @param options [Hash] The processed options hash.
|
|
1198
|
-
def initialize_components(options)
|
|
1199
|
-
@working_directory = Pathname.new(options[:working_directory]) if options[:working_directory]
|
|
1200
|
-
@repository = Pathname.new(options[:repository]) if options[:repository]
|
|
1201
|
-
@index = Pathname.new(options[:index]) if options[:index]
|
|
1202
|
-
end
|
|
1203
|
-
end
|
|
1204
|
-
end
|