p-mongo-git 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/git.gemspec ADDED
@@ -0,0 +1,46 @@
1
+ $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
2
+ require 'git/version'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.author = 'Scott Chacon and others'
6
+ s.email = 'schacon@gmail.com'
7
+ s.homepage = 'http://github.com/ruby-git/ruby-git'
8
+ s.license = 'MIT'
9
+ s.name = 'p-mongo-git'
10
+ s.summary = 'An API to create, read, and manipulate Git repositories'
11
+ s.description = <<~DESCRIPTION
12
+ The Git Gem provides an API that can be used to create, read, and manipulate
13
+ Git repositories by wrapping system calls to the `git` binary. The API can be
14
+ used for working with Git in complex interactions including branching and
15
+ merging, object inspection and manipulation, history, patch generation and
16
+ more.
17
+ DESCRIPTION
18
+ s.version = Git::VERSION
19
+
20
+ s.metadata['homepage_uri'] = s.homepage
21
+ s.metadata['source_code_uri'] = s.homepage
22
+ s.metadata['changelog_uri'] = 'http://rubydoc.info/gems/git/file.CHANGELOG.html'
23
+
24
+ s.require_paths = ['lib']
25
+ s.required_ruby_version = '>= 2.3'
26
+ s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to?(:required_rubygems_version=)
27
+ s.requirements = ['git 1.6.0.0, or greater']
28
+
29
+ s.add_runtime_dependency 'rchardet', '~> 1.8'
30
+
31
+ s.add_development_dependency 'minitar', '~> 0.9'
32
+ s.add_development_dependency 'rake', '~> 13.0'
33
+ s.add_development_dependency 'test-unit', '~> 3.3'
34
+
35
+ unless RUBY_PLATFORM == 'java'
36
+ s.add_development_dependency 'redcarpet', '~> 3.5'
37
+ s.add_development_dependency 'yard', '~> 0.9'
38
+ s.add_development_dependency 'yardstick', '~> 0.9'
39
+ end
40
+
41
+ # Specify which files should be added to the gem when it is released.
42
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
43
+ s.files = Dir.chdir(File.expand_path(__dir__)) do
44
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(tests|spec|features)/}) }
45
+ end
46
+ end
data/lib/git.rb ADDED
@@ -0,0 +1,306 @@
1
+ # Add the directory containing this file to the start of the load path if it
2
+ # isn't there already.
3
+ $:.unshift(File.dirname(__FILE__)) unless
4
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
5
+
6
+ require 'git/author'
7
+ require 'git/base'
8
+ require 'git/branch'
9
+ require 'git/branches'
10
+ require 'git/config'
11
+ require 'git/diff'
12
+ require 'git/index'
13
+ require 'git/lib'
14
+ require 'git/log'
15
+ require 'git/object'
16
+ require 'git/path'
17
+ require 'git/remote'
18
+ require 'git/repository'
19
+ require 'git/status'
20
+ require 'git/stash'
21
+ require 'git/stashes'
22
+ require 'git/version'
23
+ require 'git/working_directory'
24
+ require 'git/worktree'
25
+ require 'git/worktrees'
26
+
27
+ lib = Git::Lib.new(nil, nil)
28
+ unless lib.meets_required_version?
29
+ $stderr.puts "[WARNING] The git gem requires git #{lib.required_command_version.join('.')} or later, but only found #{lib.current_command_version.join('.')}. You should probably upgrade."
30
+ end
31
+
32
+ # The Git module provides the basic functions to open a git
33
+ # reference to work with. You can open a working directory,
34
+ # open a bare repository, initialize a new repo or clone an
35
+ # existing remote repository.
36
+ #
37
+ # @author Scott Chacon (mailto:schacon@gmail.com)
38
+ #
39
+ module Git
40
+ #g.config('user.name', 'Scott Chacon') # sets value
41
+ #g.config('user.email', 'email@email.com') # sets value
42
+ #g.config('user.name') # returns 'Scott Chacon'
43
+ #g.config # returns whole config hash
44
+ def config(name = nil, value = nil)
45
+ lib = Git::Lib.new
46
+ if(name && value)
47
+ # set value
48
+ lib.config_set(name, value)
49
+ elsif (name)
50
+ # return value
51
+ lib.config_get(name)
52
+ else
53
+ # return hash
54
+ lib.config_list
55
+ end
56
+ end
57
+
58
+ def self.configure
59
+ yield Base.config
60
+ end
61
+
62
+ def self.config
63
+ return Base.config
64
+ end
65
+
66
+ def global_config(name = nil, value = nil)
67
+ self.class.global_config(name, value)
68
+ end
69
+
70
+ # Open a bare repository
71
+ #
72
+ # Opens a bare repository located in the `git_dir` directory.
73
+ # Since there is no working copy, you can not checkout or commit
74
+ # but you can do most read operations.
75
+ #
76
+ # @see https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefbarerepositoryabarerepository
77
+ # What is a bare repository?
78
+ #
79
+ # @example Open a bare repository and retrieve the first commit SHA
80
+ # repository = Git.bare('ruby-git.git')
81
+ # puts repository.log[0].sha #=> "64c6fa011d3287bab9158049c85f3e85718854a0"
82
+ #
83
+ # @param [Pathname] git_dir The path to the bare repository directory
84
+ # containing an initialized Git repository. If a relative path is given, it
85
+ # is converted to an absolute path using
86
+ # [File.expand_path](https://www.rubydoc.info/stdlib/core/File.expand_path).
87
+ #
88
+ # @param [Hash] options The options for this command (see list of valid
89
+ # options below)
90
+ #
91
+ # @option options [Logger] :log A logger to use for Git operations. Git commands
92
+ # are logged at the `:info` level. Additional logging is done at the `:debug`
93
+ # level.
94
+ #
95
+ # @return [Git::Base] an object that can execute git commands in the context
96
+ # of the bare repository.
97
+ #
98
+ def self.bare(git_dir, options = {})
99
+ Base.bare(git_dir, options)
100
+ end
101
+
102
+ # Clone a repository into an empty or newly created directory
103
+ #
104
+ # @see https://git-scm.com/docs/git-clone git clone
105
+ # @see https://git-scm.com/docs/git-clone#_git_urls_a_id_urls_a GIT URLs
106
+ #
107
+ # @param [URI, Pathname] repository The (possibly remote) repository to clone
108
+ # from. See [GIT URLS](https://git-scm.com/docs/git-clone#_git_urls_a_id_urls_a)
109
+ # for more information.
110
+ #
111
+ # @param [Pathname] name The directory to clone into.
112
+ #
113
+ # @param [Hash] options The options for this command (see list of valid
114
+ # options below)
115
+ #
116
+ # @option options [Boolean] :bare Make a bare Git repository. See
117
+ # [what is a bare repository?](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefbarerepositoryabarerepository).
118
+ #
119
+ # @option options [String] :branch The name of a branch or tag to checkout
120
+ # instead of the default branch.
121
+ #
122
+ # @option options [Integer] :depth Create a shallow clone with a history
123
+ # truncated to the specified number of commits.
124
+ #
125
+ # @option options [Logger] :log A logger to use for Git operations. Git
126
+ # commands are logged at the `:info` level. Additional logging is done
127
+ # at the `:debug` level.
128
+ #
129
+ # @option options [Boolean] :mirror Set up a mirror of the source repository.
130
+ #
131
+ # @option options [String] :origin Use the value instead `origin` to track
132
+ # the upstream repository.
133
+ #
134
+ # @option options [Pathname] :path The directory to clone into. May be used
135
+ # as an alternative to the `directory` parameter. If specified, the
136
+ # `path` option is used instead of the `directory` parameter.
137
+ #
138
+ # @option options [Boolean] :recursive After the clone is created, initialize
139
+ # all submodules within, using their default settings.
140
+ #
141
+ # @example Clone into the default directory `ruby-git`
142
+ # git = Git.clone('https://github.com/ruby-git/ruby-git.git')
143
+ #
144
+ # @example Clone and then checkout the `development` branch
145
+ # git = Git.clone('https://github.com/ruby-git/ruby-git.git', branch: 'development')
146
+ #
147
+ # @example Clone into a different directory `my-ruby-git`
148
+ # git = Git.clone('https://github.com/ruby-git/ruby-git.git', 'my-ruby-git')
149
+ # # or:
150
+ # git = Git.clone('https://github.com/ruby-git/ruby-git.git', path: 'my-ruby-git')
151
+ #
152
+ # @example Create a bare repository in the directory `ruby-git.git`
153
+ # git = Git.clone('https://github.com/ruby-git/ruby-git.git', bare: true)
154
+ #
155
+ # @return [Git::Base] an object that can execute git commands in the context
156
+ # of the cloned local working copy or cloned repository.
157
+ #
158
+ def self.clone(repository, name, options = {})
159
+ Base.clone(repository, name, options)
160
+ end
161
+
162
+ # Export the current HEAD (or a branch, if <tt>options[:branch]</tt>
163
+ # is specified) into the +name+ directory, then remove all traces of git from the
164
+ # directory.
165
+ #
166
+ # See +clone+ for options. Does not obey the <tt>:remote</tt> option,
167
+ # since the .git info will be deleted anyway; always uses the default
168
+ # remote, 'origin.'
169
+ def self.export(repository, name, options = {})
170
+ options.delete(:remote)
171
+ repo = clone(repository, name, {:depth => 1}.merge(options))
172
+ repo.checkout("origin/#{options[:branch]}") if options[:branch]
173
+ Dir.chdir(repo.dir.to_s) { FileUtils.rm_r '.git' }
174
+ end
175
+
176
+ # Same as g.config, but forces it to be at the global level
177
+ #
178
+ #g.config('user.name', 'Scott Chacon') # sets value
179
+ #g.config('user.email', 'email@email.com') # sets value
180
+ #g.config('user.name') # returns 'Scott Chacon'
181
+ #g.config # returns whole config hash
182
+ def self.global_config(name = nil, value = nil)
183
+ lib = Git::Lib.new(nil, nil)
184
+ if(name && value)
185
+ # set value
186
+ lib.global_config_set(name, value)
187
+ elsif (name)
188
+ # return value
189
+ lib.global_config_get(name)
190
+ else
191
+ # return hash
192
+ lib.global_config_list
193
+ end
194
+ end
195
+
196
+ # Create an empty Git repository or reinitialize an existing Git repository
197
+ #
198
+ # @param [Pathname] directory If the `:bare` option is NOT given or is not
199
+ # `true`, the repository will be created in `"#{directory}/.git"`.
200
+ # Otherwise, the repository is created in `"#{directory}"`.
201
+ #
202
+ # All directories along the path to `directory` are created if they do not exist.
203
+ #
204
+ # A relative path is referenced from the current working directory of the process
205
+ # and converted to an absolute path using
206
+ # [File.expand_path](https://www.rubydoc.info/stdlib/core/File.expand_path).
207
+ #
208
+ # @param [Hash] options The options for this command (see list of valid
209
+ # options below)
210
+ #
211
+ # @option options [Boolean] :bare Instead of creating a repository at
212
+ # `"#{directory}/.git"`, create a bare repository at `"#{directory}"`.
213
+ # See [what is a bare repository?](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefbarerepositoryabarerepository).
214
+ #
215
+ # @option options [Pathname] :repository the path to put the newly initialized
216
+ # Git repository. The default for non-bare repository is `"#{directory}/.git"`.
217
+ #
218
+ # A relative path is referenced from the current working directory of the process
219
+ # and converted to an absolute path using
220
+ # [File.expand_path](https://www.rubydoc.info/stdlib/core/File.expand_path).
221
+ #
222
+ # @option options [Logger] :log A logger to use for Git operations. Git
223
+ # commands are logged at the `:info` level. Additional logging is done
224
+ # at the `:debug` level.
225
+ #
226
+ # @return [Git::Base] an object that can execute git commands in the context
227
+ # of the newly initialized repository
228
+ #
229
+ # @example Initialize a repository in the current directory
230
+ # git = Git.init
231
+ #
232
+ # @example Initialize a repository in some other directory
233
+ # git = Git.init '~/code/ruby-git'
234
+ #
235
+ # @example Initialize a bare repository
236
+ # git = Git.init '~/code/ruby-git.git', bare: true
237
+ #
238
+ # @example Initialize a repository in a non-default location (outside of the working copy)
239
+ # git = Git.init '~/code/ruby-git', repository: '~/code/ruby-git.git'
240
+ #
241
+ # @see https://git-scm.com/docs/git-init git init
242
+ #
243
+ def self.init(directory = '.', options = {})
244
+ Base.init(directory, options)
245
+ end
246
+
247
+ # returns a Hash containing information about the references
248
+ # of the target repository
249
+ #
250
+ # options
251
+ # :refs
252
+ #
253
+ # @param [String|NilClass] location the target repository location or nil for '.'
254
+ # @return [{String=>Hash}] the available references of the target repo.
255
+ def self.ls_remote(location = nil, options = {})
256
+ Git::Lib.new.ls_remote(location, options)
257
+ end
258
+
259
+ # Open a an existing Git working directory
260
+ #
261
+ # Git.open will most likely be the most common way to create
262
+ # a git reference, referring to an existing working directory.
263
+ #
264
+ # If not provided in the options, the library will assume
265
+ # the repository and index are in the default places (`.git/`, `.git/index`).
266
+ #
267
+ # @example Open the Git working directory in the current directory
268
+ # git = Git.open
269
+ #
270
+ # @example Open a Git working directory in some other directory
271
+ # git = Git.open('~/Projects/ruby-git')
272
+ #
273
+ # @example Use a logger to see what is going on
274
+ # logger = Logger.new(STDOUT)
275
+ # git = Git.open('~/Projects/ruby-git', log: logger)
276
+ #
277
+ # @example Open a working copy whose repository is in a non-standard directory
278
+ # git = Git.open('~/Projects/ruby-git', repository: '~/Project/ruby-git.git')
279
+ #
280
+ # @param [Pathname] working_dir the path to the working directory to use
281
+ # for git commands.
282
+ #
283
+ # A relative path is referenced from the current working directory of the process
284
+ # and converted to an absolute path using
285
+ # [File.expand_path](https://www.rubydoc.info/stdlib/core/File.expand_path).
286
+ #
287
+ # @param [Hash] options The options for this command (see list of valid
288
+ # options below)
289
+ #
290
+ # @option options [Pathname] :repository used to specify a non-standard path to
291
+ # the repository directory. The default is `"#{working_dir}/.git"`.
292
+ #
293
+ # @option options [Pathname] :index used to specify a non-standard path to an
294
+ # index file. The default is `"#{working_dir}/.git/index"`
295
+ #
296
+ # @option options [Logger] :log A logger to use for Git operations. Git
297
+ # commands are logged at the `:info` level. Additional logging is done
298
+ # at the `:debug` level.
299
+ #
300
+ # @return [Git::Base] an object that can execute git commands in the context
301
+ # of the opened working copy
302
+ #
303
+ def self.open(working_dir, options = {})
304
+ Base.open(working_dir, options)
305
+ end
306
+ end
data/lib/git/author.rb ADDED
@@ -0,0 +1,14 @@
1
+ module Git
2
+ class Author
3
+ attr_accessor :name, :email, :date
4
+
5
+ def initialize(author_string)
6
+ if m = /(.*?) <(.*?)> (\d+) (.*)/.match(author_string)
7
+ @name = m[1]
8
+ @email = m[2]
9
+ @date = Time.at(m[3].to_i)
10
+ end
11
+ end
12
+
13
+ end
14
+ end
data/lib/git/base.rb ADDED
@@ -0,0 +1,599 @@
1
+ require 'git/base/factory'
2
+
3
+ module Git
4
+ # Git::Base is the main public interface for interacting with Git commands.
5
+ #
6
+ # Instead of creating a Git::Base directly, obtain a Git::Base instance by
7
+ # calling one of the follow {Git} class methods: {Git.open}, {Git.init},
8
+ # {Git.clone}, or {Git.bare}.
9
+ #
10
+ class Base
11
+ include Git::Base::Factory
12
+
13
+ # (see Git.bare)
14
+ def self.bare(git_dir, options = {})
15
+ self.new({:repository => git_dir}.merge(options))
16
+ end
17
+
18
+ # (see Git.clone)
19
+ def self.clone(repository, name, options = {})
20
+ self.new(Git::Lib.new(nil, options[:log]).clone(repository, name, options))
21
+ end
22
+
23
+ # Returns (and initialize if needed) a Git::Config instance
24
+ #
25
+ # @return [Git::Config] the current config instance.
26
+ def self.config
27
+ return @@config ||= Config.new
28
+ end
29
+
30
+ # (see Git.init)
31
+ def self.init(directory, options = {})
32
+ options[:working_directory] ||= directory
33
+ options[:repository] ||= File.join(options[:working_directory], '.git')
34
+
35
+ FileUtils.mkdir_p(options[:working_directory]) if options[:working_directory] && !File.directory?(options[:working_directory])
36
+
37
+ init_options = { :bare => options[:bare] }
38
+
39
+ options.delete(:working_directory) if options[:bare]
40
+
41
+ # Submodules have a .git *file* not a .git folder.
42
+ # This file's contents point to the location of
43
+ # where the git refs are held (In the parent repo)
44
+ if options[:working_directory] && File.file?(File.join(options[:working_directory], '.git'))
45
+ git_file = File.open('.git').read[8..-1].strip
46
+ options[:repository] = git_file
47
+ options[:index] = git_file + '/index'
48
+ end
49
+
50
+ # TODO: this dance seems awkward: this creates a Git::Lib so we can call
51
+ # init so we can create a new Git::Base which in turn (ultimately)
52
+ # creates another/different Git::Lib.
53
+ #
54
+ # TODO: maybe refactor so this Git::Bare.init does this:
55
+ # self.new(opts).init(init_opts) and move all/some of this code into
56
+ # Git::Bare#init. This way the init method can be called on any
57
+ # repository you have a Git::Base instance for. This would not
58
+ # change the existing interface (other than adding to it).
59
+ #
60
+ Git::Lib.new(options).init(init_options)
61
+
62
+ self.new(options)
63
+ end
64
+
65
+ # (see Git.open)
66
+ def self.open(working_dir, options={})
67
+ # TODO: move this to Git.open?
68
+
69
+ options[:working_directory] ||= working_dir
70
+ options[:repository] ||= File.join(options[:working_directory], '.git')
71
+
72
+ # Submodules have a .git *file* not a .git folder.
73
+ # This file's contents point to the location of
74
+ # where the git refs are held (In the parent repo)
75
+ if options[:working_directory] && File.file?(File.join(options[:working_directory], '.git'))
76
+ git_file = File.open('.git').read[8..-1].strip
77
+ options[:repository] = git_file
78
+ options[:index] = git_file + '/index'
79
+ end
80
+
81
+ self.new(options)
82
+ end
83
+
84
+ # Create an object that executes Git commands in the context of a working
85
+ # copy or a bare repository.
86
+ #
87
+ # @param [Hash] options The options for this command (see list of valid
88
+ # options below)
89
+ #
90
+ # @option options [Pathname] :working_dir the path to the root of the working
91
+ # directory. Should be `nil` if executing commands on a bare repository.
92
+ #
93
+ # @option options [Pathname] :repository used to specify a non-standard path to
94
+ # the repository directory. The default is `"#{working_dir}/.git"`.
95
+ #
96
+ # @option options [Pathname] :index used to specify a non-standard path to an
97
+ # index file. The default is `"#{working_dir}/.git/index"`
98
+ #
99
+ # @option options [Logger] :log A logger to use for Git operations. Git
100
+ # commands are logged at the `:info` level. Additional logging is done
101
+ # at the `:debug` level.
102
+ #
103
+ # @return [Git::Base] an object that can execute git commands in the context
104
+ # of the opened working copy or bare repository
105
+ #
106
+ def initialize(options = {})
107
+ if working_dir = options[:working_directory]
108
+ options[:repository] ||= File.join(working_dir, '.git')
109
+ options[:index] ||= File.join(options[:repository], 'index')
110
+ end
111
+ if options[:log]
112
+ @logger = options[:log]
113
+ @logger.info("Starting Git")
114
+ else
115
+ @logger = nil
116
+ end
117
+
118
+ @working_directory = options[:working_directory] ? Git::WorkingDirectory.new(options[:working_directory]) : nil
119
+ @repository = options[:repository] ? Git::Repository.new(options[:repository]) : nil
120
+ @index = options[:index] ? Git::Index.new(options[:index], false) : nil
121
+ end
122
+
123
+ # changes current working directory for a block
124
+ # to the git working directory
125
+ #
126
+ # example
127
+ # @git.chdir do
128
+ # # write files
129
+ # @git.add
130
+ # @git.commit('message')
131
+ # end
132
+ def chdir # :yields: the Git::Path
133
+ Dir.chdir(dir.path) do
134
+ yield dir.path
135
+ end
136
+ end
137
+
138
+ #g.config('user.name', 'Scott Chacon') # sets value
139
+ #g.config('user.email', 'email@email.com') # sets value
140
+ #g.config('user.name') # returns 'Scott Chacon'
141
+ #g.config # returns whole config hash
142
+ def config(name = nil, value = nil)
143
+ if(name && value)
144
+ # set value
145
+ lib.config_set(name, value)
146
+ elsif (name)
147
+ # return value
148
+ lib.config_get(name)
149
+ else
150
+ # return hash
151
+ lib.config_list
152
+ end
153
+ end
154
+
155
+ # returns a reference to the working directory
156
+ # @git.dir.path
157
+ # @git.dir.writeable?
158
+ def dir
159
+ @working_directory
160
+ end
161
+
162
+ # returns reference to the git index file
163
+ def index
164
+ @index
165
+ end
166
+
167
+ # returns reference to the git repository directory
168
+ # @git.dir.path
169
+ def repo
170
+ @repository
171
+ end
172
+
173
+ # returns the repository size in bytes
174
+ def repo_size
175
+ Dir.glob(File.join(repo.path, '**', '*'), File::FNM_DOTMATCH).reject do |f|
176
+ f.include?('..')
177
+ end.map do |f|
178
+ File.expand_path(f)
179
+ end.uniq.map do |f|
180
+ File.stat(f).size.to_i
181
+ end.reduce(:+)
182
+ end
183
+
184
+ def set_index(index_file, check = true)
185
+ @lib = nil
186
+ @index = Git::Index.new(index_file.to_s, check)
187
+ end
188
+
189
+ def set_working(work_dir, check = true)
190
+ @lib = nil
191
+ @working_directory = Git::WorkingDirectory.new(work_dir.to_s, check)
192
+ end
193
+
194
+ # returns +true+ if the branch exists locally
195
+ def is_local_branch?(branch)
196
+ branch_names = self.branches.local.map {|b| b.name}
197
+ branch_names.include?(branch)
198
+ end
199
+
200
+ # returns +true+ if the branch exists remotely
201
+ def is_remote_branch?(branch)
202
+ branch_names = self.branches.remote.map {|b| b.name}
203
+ branch_names.include?(branch)
204
+ end
205
+
206
+ # returns +true+ if the branch exists
207
+ def is_branch?(branch)
208
+ branch_names = self.branches.map {|b| b.name}
209
+ branch_names.include?(branch)
210
+ end
211
+
212
+ # this is a convenience method for accessing the class that wraps all the
213
+ # actual 'git' forked system calls. At some point I hope to replace the Git::Lib
214
+ # class with one that uses native methods or libgit C bindings
215
+ def lib
216
+ @lib ||= Git::Lib.new(self, @logger)
217
+ end
218
+
219
+ # Run a grep for 'string' on the HEAD of the git repository
220
+ #
221
+ # @example Limit grep's scope by calling grep() from a specific object:
222
+ # git.object("v2.3").grep('TODO')
223
+ #
224
+ # @example Using grep results:
225
+ # git.grep("TODO").each do |sha, arr|
226
+ # puts "in blob #{sha}:"
227
+ # arr.each do |line_no, match_string|
228
+ # puts "\t line #{line_no}: '#{match_string}'"
229
+ # end
230
+ # end
231
+ #
232
+ # @return [Hash<String, Array>] a hash of arrays
233
+ # ```Ruby
234
+ # {
235
+ # 'tree-ish1' => [[line_no1, match_string1], ...],
236
+ # 'tree-ish2' => [[line_no1, match_string1], ...],
237
+ # ...
238
+ # }
239
+ # ```
240
+ #
241
+ def grep(string, path_limiter = nil, opts = {})
242
+ self.object('HEAD').grep(string, path_limiter, opts)
243
+ end
244
+
245
+ # updates the repository index using the working directory content
246
+ #
247
+ # @example
248
+ # git.add
249
+ # git.add('path/to/file')
250
+ # git.add(['path/to/file1','path/to/file2'])
251
+ # git.add(:all => true)
252
+ #
253
+ # options:
254
+ # :all => true
255
+ #
256
+ # @param [String,Array] paths files paths to be added (optional, default='.')
257
+ # @param [Hash] options
258
+ # @option options [boolean] :all
259
+ # Update the index not only where the working tree has a file matching
260
+ # <pathspec> but also where the index already has an entry.
261
+ # See [the --all option to git-add](https://git-scm.com/docs/git-add#Documentation/git-add.txt--A)
262
+ # for more details.
263
+ #
264
+ def add(paths = '.', **options)
265
+ self.lib.add(paths, options)
266
+ end
267
+
268
+ # removes file(s) from the git repository
269
+ def remove(path = '.', opts = {})
270
+ self.lib.remove(path, opts)
271
+ end
272
+
273
+ # resets the working directory to the provided commitish
274
+ def reset(commitish = nil, opts = {})
275
+ self.lib.reset(commitish, opts)
276
+ end
277
+
278
+ # resets the working directory to the commitish with '--hard'
279
+ def reset_hard(commitish = nil, opts = {})
280
+ opts = {:hard => true}.merge(opts)
281
+ self.lib.reset(commitish, opts)
282
+ end
283
+
284
+ # cleans the working directory
285
+ #
286
+ # options:
287
+ # :force
288
+ # :d
289
+ #
290
+ def clean(opts = {})
291
+ self.lib.clean(opts)
292
+ end
293
+
294
+ # returns the most recent tag that is reachable from a commit
295
+ #
296
+ # options:
297
+ # :all
298
+ # :tags
299
+ # :contains
300
+ # :debug
301
+ # :exact_match
302
+ # :dirty
303
+ # :abbrev
304
+ # :candidates
305
+ # :long
306
+ # :always
307
+ # :match
308
+ #
309
+ def describe(committish=nil, opts={})
310
+ self.lib.describe(committish, opts)
311
+ end
312
+
313
+ # reverts the working directory to the provided commitish.
314
+ # Accepts a range, such as comittish..HEAD
315
+ #
316
+ # options:
317
+ # :no_edit
318
+ #
319
+ def revert(commitish = nil, opts = {})
320
+ self.lib.revert(commitish, opts)
321
+ end
322
+
323
+ # commits all pending changes in the index file to the git repository
324
+ #
325
+ # options:
326
+ # :all
327
+ # :allow_empty
328
+ # :amend
329
+ # :author
330
+ #
331
+ def commit(message, opts = {})
332
+ self.lib.commit(message, opts)
333
+ end
334
+
335
+ # commits all pending changes in the index file to the git repository,
336
+ # but automatically adds all modified files without having to explicitly
337
+ # calling @git.add() on them.
338
+ def commit_all(message, opts = {})
339
+ opts = {:add_all => true}.merge(opts)
340
+ self.lib.commit(message, opts)
341
+ end
342
+
343
+ # checks out a branch as the new git working directory
344
+ def checkout(branch = 'master', opts = {})
345
+ self.lib.checkout(branch, opts)
346
+ end
347
+
348
+ # checks out an old version of a file
349
+ def checkout_file(version, file)
350
+ self.lib.checkout_file(version,file)
351
+ end
352
+
353
+ # fetches changes from a remote branch - this does not modify the working directory,
354
+ # it just gets the changes from the remote if there are any
355
+ def fetch(remote = 'origin', opts={})
356
+ self.lib.fetch(remote, opts)
357
+ end
358
+
359
+ # pushes changes to a remote repository - easiest if this is a cloned repository,
360
+ # otherwise you may have to run something like this first to setup the push parameters:
361
+ #
362
+ # @git.config('remote.remote-name.push', 'refs/heads/master:refs/heads/master')
363
+ #
364
+ def push(remote = 'origin', branch = 'master', opts = {})
365
+ # Small hack to keep backwards compatibility with the 'push(remote, branch, tags)' method signature.
366
+ opts = {:tags => opts} if [true, false].include?(opts)
367
+
368
+ self.lib.push(remote, branch, opts)
369
+ end
370
+
371
+ # merges one or more branches into the current working branch
372
+ #
373
+ # you can specify more than one branch to merge by passing an array of branches
374
+ def merge(branch, message = 'merge', opts = {})
375
+ self.lib.merge(branch, message, opts)
376
+ end
377
+
378
+ # iterates over the files which are unmerged
379
+ def each_conflict(&block) # :yields: file, your_version, their_version
380
+ self.lib.conflicts(&block)
381
+ end
382
+
383
+ # pulls the given branch from the given remote into the current branch
384
+ #
385
+ # @git.pull # pulls from origin/master
386
+ # @git.pull('upstream') # pulls from upstream/master
387
+ # @git.pull('upstream', 'develope') # pulls from upstream/develop
388
+ #
389
+ def pull(remote='origin', branch='master')
390
+ self.lib.pull(remote, branch)
391
+ end
392
+
393
+ # returns an array of Git:Remote objects
394
+ def remotes
395
+ self.lib.remotes.map { |r| Git::Remote.new(self, r) }
396
+ end
397
+
398
+ # adds a new remote to this repository
399
+ # url can be a git url or a Git::Base object if it's a local reference
400
+ #
401
+ # @git.add_remote('scotts_git', 'git://repo.or.cz/rubygit.git')
402
+ # @git.fetch('scotts_git')
403
+ # @git.merge('scotts_git/master')
404
+ #
405
+ # Options:
406
+ # :fetch => true
407
+ # :track => <branch_name>
408
+ def add_remote(name, url, opts = {})
409
+ url = url.repo.path if url.is_a?(Git::Base)
410
+ self.lib.remote_add(name, url, opts)
411
+ Git::Remote.new(self, name)
412
+ end
413
+
414
+ # sets the url for a remote
415
+ # url can be a git url or a Git::Base object if it's a local reference
416
+ #
417
+ # accepts options:
418
+ # :push
419
+ #
420
+ # @git.set_remote_url('scotts_git', 'git://repo.or.cz/rubygit.git')
421
+ #
422
+ def set_remote_url(name, url, opts = {})
423
+ url = url.repo.path if url.is_a?(Git::Base)
424
+ self.lib.remote_set_url(name, url, opts)
425
+ Git::Remote.new(self, name)
426
+ end
427
+
428
+ # removes a remote from this repository
429
+ #
430
+ # @git.remove_remote('scott_git')
431
+ def remove_remote(name)
432
+ self.lib.remote_remove(name)
433
+ end
434
+
435
+ # returns an array of all Git::Tag objects for this repository
436
+ def tags
437
+ self.lib.tags.map { |r| tag(r) }
438
+ end
439
+
440
+ # Creates a new git tag (Git::Tag)
441
+ #
442
+ # @example
443
+ # repo.add_tag('tag_name', object_reference)
444
+ # repo.add_tag('tag_name', object_reference, {:options => 'here'})
445
+ # repo.add_tag('tag_name', {:options => 'here'})
446
+ #
447
+ # @param [String] name The name of the tag to add
448
+ # @param [Hash] options Opstions to pass to `git tag`.
449
+ # See [git-tag](https://git-scm.com/docs/git-tag) for more details.
450
+ # @option options [boolean] :annotate Make an unsigned, annotated tag object
451
+ # @option options [boolean] :a An alias for the `:annotate` option
452
+ # @option options [boolean] :d Delete existing tag with the given names.
453
+ # @option options [boolean] :f Replace an existing tag with the given name (instead of failing)
454
+ # @option options [String] :message Use the given tag message
455
+ # @option options [String] :m An alias for the `:message` option
456
+ # @option options [boolean] :s Make a GPG-signed tag.
457
+ #
458
+ def add_tag(name, *options)
459
+ self.lib.tag(name, *options)
460
+ self.tag(name)
461
+ end
462
+
463
+ # deletes a tag
464
+ def delete_tag(name)
465
+ self.lib.tag(name, {:d => true})
466
+ end
467
+
468
+ # creates an archive file of the given tree-ish
469
+ def archive(treeish, file = nil, opts = {})
470
+ self.object(treeish).archive(file, opts)
471
+ end
472
+
473
+ # repacks the repository
474
+ def repack
475
+ self.lib.repack
476
+ end
477
+
478
+ def gc
479
+ self.lib.gc
480
+ end
481
+
482
+ def apply(file)
483
+ if File.exist?(file)
484
+ self.lib.apply(file)
485
+ end
486
+ end
487
+
488
+ def apply_mail(file)
489
+ self.lib.apply_mail(file) if File.exist?(file)
490
+ end
491
+
492
+ # Shows objects
493
+ #
494
+ # @param [String|NilClass] objectish the target object reference (nil == HEAD)
495
+ # @param [String|NilClass] path the path of the file to be shown
496
+ # @return [String] the object information
497
+ def show(objectish=nil, path=nil)
498
+ self.lib.show(objectish, path)
499
+ end
500
+
501
+ ## LOWER LEVEL INDEX OPERATIONS ##
502
+
503
+ def with_index(new_index) # :yields: new_index
504
+ old_index = @index
505
+ set_index(new_index, false)
506
+ return_value = yield @index
507
+ set_index(old_index)
508
+ return_value
509
+ end
510
+
511
+ def with_temp_index &blk
512
+ # Workaround for JRUBY, since they handle the TempFile path different.
513
+ # MUST be improved to be safer and OS independent.
514
+ if RUBY_PLATFORM == 'java'
515
+ temp_path = "/tmp/temp-index-#{(0...15).map{ ('a'..'z').to_a[rand(26)] }.join}"
516
+ else
517
+ tempfile = Tempfile.new('temp-index')
518
+ temp_path = tempfile.path
519
+ tempfile.close
520
+ tempfile.unlink
521
+ end
522
+
523
+ with_index(temp_path, &blk)
524
+ end
525
+
526
+ def checkout_index(opts = {})
527
+ self.lib.checkout_index(opts)
528
+ end
529
+
530
+ def read_tree(treeish, opts = {})
531
+ self.lib.read_tree(treeish, opts)
532
+ end
533
+
534
+ def write_tree
535
+ self.lib.write_tree
536
+ end
537
+
538
+ def write_and_commit_tree(opts = {})
539
+ tree = write_tree
540
+ commit_tree(tree, opts)
541
+ end
542
+
543
+ def update_ref(branch, commit)
544
+ branch(branch).update_ref(commit)
545
+ end
546
+
547
+
548
+ def ls_files(location=nil)
549
+ self.lib.ls_files(location)
550
+ end
551
+
552
+ def with_working(work_dir) # :yields: the Git::WorkingDirectory
553
+ return_value = false
554
+ old_working = @working_directory
555
+ set_working(work_dir)
556
+ Dir.chdir work_dir do
557
+ return_value = yield @working_directory
558
+ end
559
+ set_working(old_working)
560
+ return_value
561
+ end
562
+
563
+ def with_temp_working &blk
564
+ tempfile = Tempfile.new("temp-workdir")
565
+ temp_dir = tempfile.path
566
+ tempfile.close
567
+ tempfile.unlink
568
+ Dir.mkdir(temp_dir, 0700)
569
+ with_working(temp_dir, &blk)
570
+ end
571
+
572
+
573
+ # runs git rev-parse to convert the objectish to a full sha
574
+ #
575
+ # @example
576
+ # git.revparse("HEAD^^")
577
+ # git.revparse('v2.4^{tree}')
578
+ # git.revparse('v2.4:/doc/index.html')
579
+ #
580
+ def revparse(objectish)
581
+ self.lib.revparse(objectish)
582
+ end
583
+
584
+ def ls_tree(objectish)
585
+ self.lib.ls_tree(objectish)
586
+ end
587
+
588
+ def cat_file(objectish)
589
+ self.lib.object_contents(objectish)
590
+ end
591
+
592
+ # returns the name of the branch the working directory is currently on
593
+ def current_branch
594
+ self.lib.branch_current
595
+ end
596
+
597
+ end
598
+
599
+ end