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.
- checksums.yaml +7 -0
- data/.github/stale.yml +25 -0
- data/.github/workflows/continuous_integration.yml +45 -0
- data/.gitignore +10 -0
- data/.yardopts +11 -0
- data/CHANGELOG.md +119 -0
- data/CONTRIBUTING.md +151 -0
- data/Gemfile +4 -0
- data/ISSUE_TEMPLATE.md +15 -0
- data/LICENSE +21 -0
- data/MAINTAINERS.md +14 -0
- data/PULL_REQUEST_TEMPLATE.md +9 -0
- data/README.md +344 -0
- data/RELEASING.md +62 -0
- data/Rakefile +56 -0
- data/git.gemspec +46 -0
- data/lib/git.rb +306 -0
- data/lib/git/author.rb +14 -0
- data/lib/git/base.rb +599 -0
- data/lib/git/base/factory.rb +101 -0
- data/lib/git/branch.rb +126 -0
- data/lib/git/branches.rb +71 -0
- data/lib/git/config.rb +22 -0
- data/lib/git/diff.rb +156 -0
- data/lib/git/index.rb +5 -0
- data/lib/git/lib.rb +1222 -0
- data/lib/git/log.rb +135 -0
- data/lib/git/object.rb +312 -0
- data/lib/git/path.rb +31 -0
- data/lib/git/remote.rb +36 -0
- data/lib/git/repository.rb +6 -0
- data/lib/git/stash.rb +27 -0
- data/lib/git/stashes.rb +55 -0
- data/lib/git/status.rb +199 -0
- data/lib/git/version.rb +5 -0
- data/lib/git/working_directory.rb +4 -0
- data/lib/git/worktree.rb +38 -0
- data/lib/git/worktrees.rb +47 -0
- metadata +186 -0
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
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
|