p-mongo-git 1.8.1

Sign up to get free protection for your applications and to get access to all the features.
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