ruby-git-yz 1.3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG +84 -0
- data/LICENSE +21 -0
- data/README.md +297 -0
- data/VERSION +2 -0
- data/lib/git/author.rb +14 -0
- data/lib/git/base.rb +540 -0
- data/lib/git/base/factory.rb +75 -0
- data/lib/git/branch.rb +122 -0
- data/lib/git/branches.rb +71 -0
- data/lib/git/config.rb +22 -0
- data/lib/git/diff.rb +158 -0
- data/lib/git/index.rb +5 -0
- data/lib/git/lib.rb +1018 -0
- data/lib/git/log.rb +128 -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 +44 -0
- data/lib/git/status.rb +117 -0
- data/lib/git/version.rb +7 -0
- data/lib/git/working_directory.rb +4 -0
- data/lib/ruby-git-yz.rb +166 -0
- metadata +120 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1bc4cd37c73a1a0de02f66af9e26a5f461da4a92
|
4
|
+
data.tar.gz: 9d18f38cfaa1b43c78d72159a4ffbb52cd1d09a4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 953a988118f8f471ade0d628ec2612b4de804a0627590859499b20ba6a1a211d64ebed3c2b2696c3aa814a779d0d1f58cd2c43bb5dc21e2d0c91fb37f390ed2a
|
7
|
+
data.tar.gz: 635260465a710f273317698071dd3ef53b68f2f703db8d8b4ff125b7354d1a1a7b9311a441746383ae244c1a214b32fca2743352379afd3dbbecbb1a186625d8
|
data/CHANGELOG
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
== 1.3.0
|
2
|
+
|
3
|
+
* Dropping Ruby 1.8.x support
|
4
|
+
|
5
|
+
== 1.2.10
|
6
|
+
|
7
|
+
* Adding Git::Diff.name_status
|
8
|
+
* Checking and fixing encoding on commands output to prevent encoding errors afterwards
|
9
|
+
|
10
|
+
== 1.2.9
|
11
|
+
|
12
|
+
* Adding Git.configure (to configure the git env)
|
13
|
+
* Adding Git.ls_remote [Git.ls_remote(repo_path_or_url='.')]
|
14
|
+
* Adding Git.describe [repo.describe(objectish, opts)]
|
15
|
+
* Adding Git.show [repo.show(objectish=nil, path=nil)]
|
16
|
+
* Fixing Git::Diff to support default references (implicit references)
|
17
|
+
* Fixing Git::Diff to support diff over git .patch files
|
18
|
+
* Fixing Git.checkout when using :new_branch opt
|
19
|
+
* Fixing Git::Object::Commit to preserve its sha after fetching metadata
|
20
|
+
* Fixing Git.is_remote_branch? to actually check against remote branches
|
21
|
+
* Improvements over how ENV variables are modified
|
22
|
+
* Improving thrade safety (using --git-dir and --work-tree git opts)
|
23
|
+
* Improving Git::Object::Tag. Adding annotated?, tagger and message
|
24
|
+
* Supporting a submodule path as a valid repo
|
25
|
+
* Git.checkout - supporting -f and -b
|
26
|
+
* Git.clone - supporting --branch
|
27
|
+
* Git.fetch - supporting --prune
|
28
|
+
* Git.tag - supporting
|
29
|
+
|
30
|
+
== 1.2.8
|
31
|
+
|
32
|
+
* Keeping the old escape format for windows users
|
33
|
+
* revparse: Supporting ref names containing SHA like substrings (40-hex strings)
|
34
|
+
* Fix warnings on Ruby 2.1.2
|
35
|
+
|
36
|
+
== 1.2.7
|
37
|
+
|
38
|
+
* Fixing mesages encoding
|
39
|
+
* Fixing -f flag in git push
|
40
|
+
* Fixing log parser for multiline messages
|
41
|
+
* Supporting object references on Git.add_tag
|
42
|
+
* Including dotfiles on Git.status
|
43
|
+
* Git.fetch - supporting --tags
|
44
|
+
* Git.clean - supporting -x
|
45
|
+
* Git.add_tag options - supporting -a, -m and -s
|
46
|
+
* Added Git.delete_tag
|
47
|
+
|
48
|
+
== 1.2.6
|
49
|
+
|
50
|
+
* Ruby 1.9.X/2.0 fully supported
|
51
|
+
* JRuby 1.8/1.9 support
|
52
|
+
* Rubinius support
|
53
|
+
* Git.clone - supporting --recursive and --config
|
54
|
+
* Git.log - supporting last and [] over the results
|
55
|
+
* Git.add_remote - supporting -f and -t
|
56
|
+
* Git.add - supporting --fore
|
57
|
+
* Git.init - supporting --bare
|
58
|
+
* Git.commit - supporting --all and --amend
|
59
|
+
* Added Git.remote_remote, Git.revert and Git.clean
|
60
|
+
* Added Bundler to the formula
|
61
|
+
* Travis configuration
|
62
|
+
* Licence included with the gem
|
63
|
+
|
64
|
+
== 1.0.4
|
65
|
+
|
66
|
+
* added camping/gitweb.rb frontend
|
67
|
+
* added a number of speed-ups
|
68
|
+
|
69
|
+
== 1.0.3
|
70
|
+
|
71
|
+
* Sped up most of the operations
|
72
|
+
* Added some predicate functions (commit?, tree?, etc)
|
73
|
+
* Added a number of lower level operations (read-tree, write-tree, checkout-index, etc)
|
74
|
+
* Fixed a bug with using bare repositories
|
75
|
+
* Updated a good amount of the documentation
|
76
|
+
|
77
|
+
== 1.0.2
|
78
|
+
|
79
|
+
* Added methods to the git objects that might be helpful
|
80
|
+
|
81
|
+
== 1.0.1
|
82
|
+
|
83
|
+
* Initial version
|
84
|
+
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2008 Scott Chacon
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,297 @@
|
|
1
|
+
# Git Library for Ruby
|
2
|
+
|
3
|
+
Library for using Git in Ruby.
|
4
|
+
|
5
|
+
## Homepage
|
6
|
+
|
7
|
+
Git public hosting of the project source code is at:
|
8
|
+
|
9
|
+
http://github.com/schacon/ruby-git
|
10
|
+
|
11
|
+
## Install
|
12
|
+
|
13
|
+
You can install Ruby/Git like this:
|
14
|
+
|
15
|
+
$ sudo gem install git
|
16
|
+
|
17
|
+
## Code Status
|
18
|
+
|
19
|
+
* [![Build Status](https://api.travis-ci.org/schacon/ruby-git.png)](https://travis-ci.org/schacon/ruby-git)
|
20
|
+
* [![Code Climate](https://codeclimate.com/github/schacon/ruby-git.png)](https://codeclimate.com/github/schacon/ruby-git)
|
21
|
+
* [![Gem Version](https://badge.fury.io/rb/git.png)](http://badge.fury.io/rb/git)
|
22
|
+
* [![Dependencies](https://gemnasium.com/schacon/ruby-git.png?travis)](https://gemnasium.com/schacon/ruby-git)
|
23
|
+
|
24
|
+
## Major Objects
|
25
|
+
|
26
|
+
**Git::Base** - The object returned from a `Git.open` or `Git.clone`. Most major actions are called from this object.
|
27
|
+
|
28
|
+
**Git::Object** - The base object for your tree, blob and commit objects, returned from `@git.gtree` or `@git.object` calls. the `Git::AbstractObject` will have most of the calls in common for all those objects.
|
29
|
+
|
30
|
+
**Git::Diff** - returns from a `@git.diff` command. It is an Enumerable that returns `Git::Diff:DiffFile` objects from which you can get per file patches and insertion/deletion statistics. You can also get total statistics from the Git::Diff object directly.
|
31
|
+
|
32
|
+
**Git::Status** - returns from a `@git.status` command. It is an Enumerable that returns
|
33
|
+
`Git:Status::StatusFile` objects for each object in git, which includes files in the working
|
34
|
+
directory, in the index and in the repository. Similar to running 'git status' on the command line to determine untracked and changed files.
|
35
|
+
|
36
|
+
**Git::Branches** - Enumerable object that holds `Git::Branch objects`. You can call .local or .remote on it to filter to just your local or remote branches.
|
37
|
+
|
38
|
+
**Git::Remote**- A reference to a remote repository that is tracked by this repository.
|
39
|
+
|
40
|
+
**Git::Log** - An Enumerable object that references all the `Git::Object::Commit` objects that encompass your log query, which can be constructed through methods on the `Git::Log object`,
|
41
|
+
like:
|
42
|
+
|
43
|
+
`@git.log(20).object("some_file").since("2 weeks ago").between('v2.6', 'v2.7').each { |commit| [block] }`
|
44
|
+
|
45
|
+
## Examples
|
46
|
+
|
47
|
+
Here are a bunch of examples of how to use the Ruby/Git package.
|
48
|
+
|
49
|
+
Ruby < 1.9 will require rubygems to be loaded.
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
require 'rubygems'
|
53
|
+
```
|
54
|
+
|
55
|
+
Require the 'git' gem.
|
56
|
+
```ruby
|
57
|
+
require 'git'
|
58
|
+
```
|
59
|
+
|
60
|
+
Git env config
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
Git.configure do |config|
|
64
|
+
# If you want to use a custom git binary
|
65
|
+
config.binary_path = '/git/bin/path'
|
66
|
+
|
67
|
+
# If you need to use a custom SSH script
|
68
|
+
config.git_ssh = '/path/to/ssh/script'
|
69
|
+
end
|
70
|
+
|
71
|
+
```
|
72
|
+
|
73
|
+
|
74
|
+
Here are the operations that need read permission only.
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
g = Git.open(working_dir, :log => Logger.new(STDOUT))
|
78
|
+
|
79
|
+
g.index
|
80
|
+
g.index.readable?
|
81
|
+
g.index.writable?
|
82
|
+
g.repo
|
83
|
+
g.dir
|
84
|
+
|
85
|
+
g.log # returns array of Git::Commit objects
|
86
|
+
g.log.since('2 weeks ago')
|
87
|
+
g.log.between('v2.5', 'v2.6')
|
88
|
+
g.log.each {|l| puts l.sha }
|
89
|
+
g.gblob('v2.5:Makefile').log.since('2 weeks ago')
|
90
|
+
|
91
|
+
g.object('HEAD^').to_s # git show / git rev-parse
|
92
|
+
g.object('HEAD^').contents
|
93
|
+
g.object('v2.5:Makefile').size
|
94
|
+
g.object('v2.5:Makefile').sha
|
95
|
+
|
96
|
+
g.gtree(treeish)
|
97
|
+
g.gblob(treeish)
|
98
|
+
g.gcommit(treeish)
|
99
|
+
|
100
|
+
|
101
|
+
commit = g.gcommit('1cc8667014381')
|
102
|
+
|
103
|
+
commit.gtree
|
104
|
+
commit.parent.sha
|
105
|
+
commit.parents.size
|
106
|
+
commit.author.name
|
107
|
+
commit.author.email
|
108
|
+
commit.author.date.strftime("%m-%d-%y")
|
109
|
+
commit.committer.name
|
110
|
+
commit.date.strftime("%m-%d-%y")
|
111
|
+
commit.message
|
112
|
+
|
113
|
+
tree = g.gtree("HEAD^{tree}")
|
114
|
+
|
115
|
+
tree.blobs
|
116
|
+
tree.subtrees
|
117
|
+
tree.children # blobs and subtrees
|
118
|
+
|
119
|
+
g.revparse('v2.5:Makefile')
|
120
|
+
|
121
|
+
g.branches # returns Git::Branch objects
|
122
|
+
g.branches.local
|
123
|
+
g.branches.remote
|
124
|
+
g.branches[:master].gcommit
|
125
|
+
g.branches['origin/master'].gcommit
|
126
|
+
|
127
|
+
g.grep('hello') # implies HEAD
|
128
|
+
g.blob('v2.5:Makefile').grep('hello')
|
129
|
+
g.tag('v2.5').grep('hello', 'docs/')
|
130
|
+
g.describe()
|
131
|
+
g.describe('0djf2aa')
|
132
|
+
g.describe('HEAD', {:all => true, :tags => true})
|
133
|
+
|
134
|
+
g.diff(commit1, commit2).size
|
135
|
+
g.diff(commit1, commit2).stats
|
136
|
+
g.diff(commit1, commit2).name_status
|
137
|
+
g.gtree('v2.5').diff('v2.6').insertions
|
138
|
+
g.diff('gitsearch1', 'v2.5').path('lib/')
|
139
|
+
g.diff('gitsearch1', @git.gtree('v2.5'))
|
140
|
+
g.diff('gitsearch1', 'v2.5').path('docs/').patch
|
141
|
+
g.gtree('v2.5').diff('v2.6').patch
|
142
|
+
|
143
|
+
g.gtree('v2.5').diff('v2.6').each do |file_diff|
|
144
|
+
puts file_diff.path
|
145
|
+
puts file_diff.patch
|
146
|
+
puts file_diff.blob(:src).contents
|
147
|
+
end
|
148
|
+
|
149
|
+
g.config('user.name') # returns 'Scott Chacon'
|
150
|
+
g.config # returns whole config hash
|
151
|
+
|
152
|
+
g.tags # returns array of Git::Tag objects
|
153
|
+
|
154
|
+
g.show()
|
155
|
+
g.show('HEAD')
|
156
|
+
g.show('v2.8', 'README.md')
|
157
|
+
|
158
|
+
Git.ls_remote('https://github.com/schacon/ruby-git.git') # returns a hash containing the available references of the repo.
|
159
|
+
Git.ls_remote('/path/to/local/repo')
|
160
|
+
Git.ls_remote() # same as Git.ls_remote('.')
|
161
|
+
|
162
|
+
```
|
163
|
+
|
164
|
+
And here are the operations that will need to write to your git repository.
|
165
|
+
|
166
|
+
```ruby
|
167
|
+
g = Git.init
|
168
|
+
Git.init('project')
|
169
|
+
Git.init('/home/schacon/proj',
|
170
|
+
{ :repository => '/opt/git/proj.git',
|
171
|
+
:index => '/tmp/index'} )
|
172
|
+
|
173
|
+
g = Git.clone(URI, NAME, :path => '/tmp/checkout')
|
174
|
+
g.config('user.name', 'Scott Chacon')
|
175
|
+
g.config('user.email', 'email@email.com')
|
176
|
+
|
177
|
+
g.add # git add -- "."
|
178
|
+
g.add(:all=>true) # git add --all -- "."
|
179
|
+
g.add('file_path') # git add -- "file_path"
|
180
|
+
g.add(['file_path_1', 'file_path_2']) # git add -- "file_path_1" "file_path_2"
|
181
|
+
|
182
|
+
g.remove() # git rm -f -- "."
|
183
|
+
g.remove('file.txt') # git rm -f -- "file.txt"
|
184
|
+
g.remove(['file.txt', 'file2.txt']) # git rm -f -- "file.txt" "file2.txt"
|
185
|
+
g.remove('file.txt', :recursive => true) # git rm -f -r -- "file.txt"
|
186
|
+
g.remove('file.txt', :cached => true) # git rm -f --cached -- "file.txt"
|
187
|
+
|
188
|
+
g.commit('message')
|
189
|
+
g.commit_all('message')
|
190
|
+
|
191
|
+
g = Git.clone(repo, 'myrepo')
|
192
|
+
g.chdir do
|
193
|
+
new_file('test-file', 'blahblahblah')
|
194
|
+
g.status.changed.each do |file|
|
195
|
+
puts file.blob(:index).contents
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
g.reset # defaults to HEAD
|
200
|
+
g.reset_hard(Git::Commit)
|
201
|
+
|
202
|
+
g.branch('new_branch') # creates new or fetches existing
|
203
|
+
g.branch('new_branch').checkout
|
204
|
+
g.branch('new_branch').delete
|
205
|
+
g.branch('existing_branch').checkout
|
206
|
+
|
207
|
+
g.checkout('new_branch')
|
208
|
+
g.checkout(g.branch('new_branch'))
|
209
|
+
|
210
|
+
g.branch(name).merge(branch2)
|
211
|
+
g.branch(branch2).merge # merges HEAD with branch2
|
212
|
+
|
213
|
+
g.branch(name).in_branch(message) { # add files } # auto-commits
|
214
|
+
g.merge('new_branch')
|
215
|
+
g.merge('origin/remote_branch')
|
216
|
+
g.merge(g.branch('master'))
|
217
|
+
g.merge([branch1, branch2])
|
218
|
+
|
219
|
+
r = g.add_remote(name, uri) # Git::Remote
|
220
|
+
r = g.add_remote(name, Git::Base) # Git::Remote
|
221
|
+
|
222
|
+
g.remotes # array of Git::Remotes
|
223
|
+
g.remote(name).fetch
|
224
|
+
g.remote(name).remove
|
225
|
+
g.remote(name).merge
|
226
|
+
g.remote(name).merge(branch)
|
227
|
+
|
228
|
+
g.fetch
|
229
|
+
g.fetch(g.remotes.first)
|
230
|
+
|
231
|
+
g.pull
|
232
|
+
g.pull(Git::Repo, Git::Branch) # fetch and a merge
|
233
|
+
|
234
|
+
g.add_tag('tag_name') # returns Git::Tag
|
235
|
+
g.add_tag('tag_name', 'object_reference')
|
236
|
+
g.add_tag('tag_name', 'object_reference', {:options => 'here'})
|
237
|
+
g.add_tag('tag_name', {:options => 'here'})
|
238
|
+
|
239
|
+
Options:
|
240
|
+
:a | :annotate
|
241
|
+
:d
|
242
|
+
:f
|
243
|
+
:m | :message
|
244
|
+
:s
|
245
|
+
|
246
|
+
g.delete_tag('tag_name')
|
247
|
+
|
248
|
+
g.repack
|
249
|
+
|
250
|
+
g.push
|
251
|
+
g.push(g.remote('name'))
|
252
|
+
```
|
253
|
+
|
254
|
+
Some examples of more low-level index and tree operations
|
255
|
+
|
256
|
+
```ruby
|
257
|
+
g.with_temp_index do
|
258
|
+
|
259
|
+
g.read_tree(tree3) # calls self.index.read_tree
|
260
|
+
g.read_tree(tree1, :prefix => 'hi/')
|
261
|
+
|
262
|
+
c = g.commit_tree('message')
|
263
|
+
# or #
|
264
|
+
t = g.write_tree
|
265
|
+
c = g.commit_tree(t, :message => 'message', :parents => [sha1, sha2])
|
266
|
+
|
267
|
+
g.branch('branch_name').update_ref(c)
|
268
|
+
g.update_ref(branch, c)
|
269
|
+
|
270
|
+
g.with_temp_working do # new blank working directory
|
271
|
+
g.checkout
|
272
|
+
g.checkout(another_index)
|
273
|
+
g.commit # commits to temp_index
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
g.set_index('/path/to/index')
|
278
|
+
|
279
|
+
|
280
|
+
g.with_index(path) do
|
281
|
+
# calls set_index, then switches back after
|
282
|
+
end
|
283
|
+
|
284
|
+
g.with_working(dir) do
|
285
|
+
# calls set_working, then switches back after
|
286
|
+
end
|
287
|
+
|
288
|
+
g.with_temp_working(dir) do
|
289
|
+
g.checkout_index(:prefix => dir, :path_limiter => path)
|
290
|
+
# do file work
|
291
|
+
g.commit # commits to index
|
292
|
+
end
|
293
|
+
```
|
294
|
+
|
295
|
+
## License
|
296
|
+
|
297
|
+
licensed under MIT License Copyright (c) 2008 Scott Chacon. See LICENSE for further details.
|
data/VERSION
ADDED
data/lib/git/author.rb
ADDED
data/lib/git/base.rb
ADDED
@@ -0,0 +1,540 @@
|
|
1
|
+
require 'git/base/factory'
|
2
|
+
|
3
|
+
module Git
|
4
|
+
|
5
|
+
class Base
|
6
|
+
|
7
|
+
include Git::Base::Factory
|
8
|
+
|
9
|
+
# opens a bare Git Repository - no working directory options
|
10
|
+
def self.bare(git_dir, opts = {})
|
11
|
+
self.new({:repository => git_dir}.merge(opts))
|
12
|
+
end
|
13
|
+
|
14
|
+
# clones a git repository locally
|
15
|
+
#
|
16
|
+
# repository - http://repo.or.cz/w/sinatra.git
|
17
|
+
# name - sinatra
|
18
|
+
#
|
19
|
+
# options:
|
20
|
+
# :repository
|
21
|
+
#
|
22
|
+
# :bare
|
23
|
+
# or
|
24
|
+
# :working_directory
|
25
|
+
# :index_file
|
26
|
+
#
|
27
|
+
def self.clone(repository, name, opts = {})
|
28
|
+
# run git-clone
|
29
|
+
self.new(Git::Lib.new.clone(repository, name, opts))
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns (and initialize if needed) a Git::Config instance
|
33
|
+
#
|
34
|
+
# @return [Git::Config] the current config instance.
|
35
|
+
def self.config
|
36
|
+
return @@config ||= Config.new
|
37
|
+
end
|
38
|
+
|
39
|
+
# initializes a git repository
|
40
|
+
#
|
41
|
+
# options:
|
42
|
+
# :bare
|
43
|
+
# :index
|
44
|
+
# :repository
|
45
|
+
#
|
46
|
+
def self.init(working_dir, opts = {})
|
47
|
+
opts[:working_directory] ||= working_dir
|
48
|
+
opts[:repository] ||= File.join(opts[:working_directory], '.git')
|
49
|
+
|
50
|
+
FileUtils.mkdir_p(opts[:working_directory]) if opts[:working_directory] && !File.directory?(opts[:working_directory])
|
51
|
+
|
52
|
+
init_opts = {
|
53
|
+
:bare => opts[:bare]
|
54
|
+
}
|
55
|
+
|
56
|
+
opts.delete(:working_directory) if opts[:bare]
|
57
|
+
|
58
|
+
# Submodules have a .git *file* not a .git folder.
|
59
|
+
# This file's contents point to the location of
|
60
|
+
# where the git refs are held (In the parent repo)
|
61
|
+
if File.file?('.git')
|
62
|
+
git_file = File.open('.git').read[8..-1].strip
|
63
|
+
opts[:repository] = git_file
|
64
|
+
opts[:index] = git_file + '/index'
|
65
|
+
end
|
66
|
+
|
67
|
+
Git::Lib.new(opts).init(init_opts)
|
68
|
+
|
69
|
+
self.new(opts)
|
70
|
+
end
|
71
|
+
|
72
|
+
# opens a new Git Project from a working directory
|
73
|
+
# you can specify non-standard git_dir and index file in the options
|
74
|
+
def self.open(working_dir, opts={})
|
75
|
+
self.new({:working_directory => working_dir}.merge(opts))
|
76
|
+
end
|
77
|
+
|
78
|
+
def initialize(options = {})
|
79
|
+
if working_dir = options[:working_directory]
|
80
|
+
options[:repository] ||= File.join(working_dir, '.git')
|
81
|
+
options[:index] ||= File.join(working_dir, '.git', 'index')
|
82
|
+
end
|
83
|
+
if options[:log]
|
84
|
+
@logger = options[:log]
|
85
|
+
@logger.info("Starting Git")
|
86
|
+
else
|
87
|
+
@logger = nil
|
88
|
+
end
|
89
|
+
|
90
|
+
@working_directory = options[:working_directory] ? Git::WorkingDirectory.new(options[:working_directory]) : nil
|
91
|
+
@repository = options[:repository] ? Git::Repository.new(options[:repository]) : nil
|
92
|
+
@index = options[:index] ? Git::Index.new(options[:index], false) : nil
|
93
|
+
end
|
94
|
+
|
95
|
+
# changes current working directory for a block
|
96
|
+
# to the git working directory
|
97
|
+
#
|
98
|
+
# example
|
99
|
+
# @git.chdir do
|
100
|
+
# # write files
|
101
|
+
# @git.add
|
102
|
+
# @git.commit('message')
|
103
|
+
# end
|
104
|
+
def chdir # :yields: the Git::Path
|
105
|
+
Dir.chdir(dir.path) do
|
106
|
+
yield dir.path
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
#g.config('user.name', 'Scott Chacon') # sets value
|
111
|
+
#g.config('user.email', 'email@email.com') # sets value
|
112
|
+
#g.config('user.name') # returns 'Scott Chacon'
|
113
|
+
#g.config # returns whole config hash
|
114
|
+
def config(name = nil, value = nil)
|
115
|
+
if(name && value)
|
116
|
+
# set value
|
117
|
+
lib.config_set(name, value)
|
118
|
+
elsif (name)
|
119
|
+
# return value
|
120
|
+
lib.config_get(name)
|
121
|
+
else
|
122
|
+
# return hash
|
123
|
+
lib.config_list
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# returns a reference to the working directory
|
128
|
+
# @git.dir.path
|
129
|
+
# @git.dir.writeable?
|
130
|
+
def dir
|
131
|
+
@working_directory
|
132
|
+
end
|
133
|
+
|
134
|
+
# returns reference to the git index file
|
135
|
+
def index
|
136
|
+
@index
|
137
|
+
end
|
138
|
+
|
139
|
+
# returns reference to the git repository directory
|
140
|
+
# @git.dir.path
|
141
|
+
def repo
|
142
|
+
@repository
|
143
|
+
end
|
144
|
+
|
145
|
+
# returns the repository size in bytes
|
146
|
+
def repo_size
|
147
|
+
Dir.chdir(repo.path) do
|
148
|
+
return `du -s`.chomp.split.first.to_i
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def set_index(index_file, check = true)
|
153
|
+
@lib = nil
|
154
|
+
@index = Git::Index.new(index_file.to_s, check)
|
155
|
+
end
|
156
|
+
|
157
|
+
def set_working(work_dir, check = true)
|
158
|
+
@lib = nil
|
159
|
+
@working_directory = Git::WorkingDirectory.new(work_dir.to_s, check)
|
160
|
+
end
|
161
|
+
|
162
|
+
# returns +true+ if the branch exists locally
|
163
|
+
def is_local_branch?(branch)
|
164
|
+
branch_names = self.branches.local.map {|b| b.name}
|
165
|
+
branch_names.include?(branch)
|
166
|
+
end
|
167
|
+
|
168
|
+
# returns +true+ if the branch exists remotely
|
169
|
+
def is_remote_branch?(branch)
|
170
|
+
branch_names = self.branches.remote.map {|b| b.name}
|
171
|
+
branch_names.include?(branch)
|
172
|
+
end
|
173
|
+
|
174
|
+
# returns +true+ if the branch exists
|
175
|
+
def is_branch?(branch)
|
176
|
+
branch_names = self.branches.map {|b| b.name}
|
177
|
+
branch_names.include?(branch)
|
178
|
+
end
|
179
|
+
|
180
|
+
# this is a convenience method for accessing the class that wraps all the
|
181
|
+
# actual 'git' forked system calls. At some point I hope to replace the Git::Lib
|
182
|
+
# class with one that uses native methods or libgit C bindings
|
183
|
+
def lib
|
184
|
+
@lib ||= Git::Lib.new(self, @logger)
|
185
|
+
end
|
186
|
+
|
187
|
+
# will run a grep for 'string' on the HEAD of the git repository
|
188
|
+
#
|
189
|
+
# to be more surgical in your grep, you can call grep() off a specific
|
190
|
+
# git object. for example:
|
191
|
+
#
|
192
|
+
# @git.object("v2.3").grep('TODO')
|
193
|
+
#
|
194
|
+
# in any case, it returns a hash of arrays of the type:
|
195
|
+
# hsh[tree-ish] = [[line_no, match], [line_no, match2]]
|
196
|
+
# hsh[tree-ish] = [[line_no, match], [line_no, match2]]
|
197
|
+
#
|
198
|
+
# so you might use it like this:
|
199
|
+
#
|
200
|
+
# @git.grep("TODO").each do |sha, arr|
|
201
|
+
# puts "in blob #{sha}:"
|
202
|
+
# arr.each do |match|
|
203
|
+
# puts "\t line #{match[0]}: '#{match[1]}'"
|
204
|
+
# end
|
205
|
+
# end
|
206
|
+
def grep(string, path_limiter = nil, opts = {})
|
207
|
+
self.object('HEAD').grep(string, path_limiter, opts)
|
208
|
+
end
|
209
|
+
|
210
|
+
# updates the repository index using the working directory content
|
211
|
+
#
|
212
|
+
# @git.add('path/to/file')
|
213
|
+
# @git.add(['path/to/file1','path/to/file2'])
|
214
|
+
# @git.add(:all => true)
|
215
|
+
#
|
216
|
+
# options:
|
217
|
+
# :all => true
|
218
|
+
#
|
219
|
+
# @param [String,Array] paths files paths to be added (optional, default='.')
|
220
|
+
# @param [Hash] options
|
221
|
+
def add(*args)
|
222
|
+
if args[0].instance_of?(String) || args[0].instance_of?(Array)
|
223
|
+
self.lib.add(args[0],args[1]||{})
|
224
|
+
else
|
225
|
+
self.lib.add('.', args[0]||{})
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
# removes file(s) from the git repository
|
230
|
+
def remove(path = '.', opts = {})
|
231
|
+
self.lib.remove(path, opts)
|
232
|
+
end
|
233
|
+
|
234
|
+
# resets the working directory to the provided commitish
|
235
|
+
def reset(commitish = nil, opts = {})
|
236
|
+
self.lib.reset(commitish, opts)
|
237
|
+
end
|
238
|
+
|
239
|
+
# resets the working directory to the commitish with '--hard'
|
240
|
+
def reset_hard(commitish = nil, opts = {})
|
241
|
+
opts = {:hard => true}.merge(opts)
|
242
|
+
self.lib.reset(commitish, opts)
|
243
|
+
end
|
244
|
+
|
245
|
+
# cleans the working directory
|
246
|
+
#
|
247
|
+
# options:
|
248
|
+
# :force
|
249
|
+
# :d
|
250
|
+
#
|
251
|
+
def clean(opts = {})
|
252
|
+
self.lib.clean(opts)
|
253
|
+
end
|
254
|
+
|
255
|
+
# returns the most recent tag that is reachable from a commit
|
256
|
+
#
|
257
|
+
# options:
|
258
|
+
# :all
|
259
|
+
# :tags
|
260
|
+
# :contains
|
261
|
+
# :debug
|
262
|
+
# :exact_match
|
263
|
+
# :dirty
|
264
|
+
# :abbrev
|
265
|
+
# :candidates
|
266
|
+
# :long
|
267
|
+
# :always
|
268
|
+
# :match
|
269
|
+
#
|
270
|
+
def describe(committish=nil, opts={})
|
271
|
+
self.lib.describe(committish, opts)
|
272
|
+
end
|
273
|
+
|
274
|
+
# reverts the working directory to the provided commitish.
|
275
|
+
# Accepts a range, such as comittish..HEAD
|
276
|
+
#
|
277
|
+
# options:
|
278
|
+
# :no_edit
|
279
|
+
#
|
280
|
+
def revert(commitish = nil, opts = {})
|
281
|
+
self.lib.revert(commitish, opts)
|
282
|
+
end
|
283
|
+
|
284
|
+
# commits all pending changes in the index file to the git repository
|
285
|
+
#
|
286
|
+
# options:
|
287
|
+
# :all
|
288
|
+
# :allow_empty
|
289
|
+
# :amend
|
290
|
+
# :author
|
291
|
+
#
|
292
|
+
def commit(message, opts = {})
|
293
|
+
self.lib.commit(message, opts)
|
294
|
+
end
|
295
|
+
|
296
|
+
# commits all pending changes in the index file to the git repository,
|
297
|
+
# but automatically adds all modified files without having to explicitly
|
298
|
+
# calling @git.add() on them.
|
299
|
+
def commit_all(message, opts = {})
|
300
|
+
opts = {:add_all => true}.merge(opts)
|
301
|
+
self.lib.commit(message, opts)
|
302
|
+
end
|
303
|
+
|
304
|
+
# checks out a branch as the new git working directory
|
305
|
+
def checkout(branch = 'master', opts = {})
|
306
|
+
self.lib.checkout(branch, opts)
|
307
|
+
end
|
308
|
+
|
309
|
+
# checks out an old version of a file
|
310
|
+
def checkout_file(version, file)
|
311
|
+
self.lib.checkout_file(version,file)
|
312
|
+
end
|
313
|
+
|
314
|
+
# fetches changes from a remote branch - this does not modify the working directory,
|
315
|
+
# it just gets the changes from the remote if there are any
|
316
|
+
def fetch(remote = 'origin', opts={})
|
317
|
+
self.lib.fetch(remote, opts)
|
318
|
+
end
|
319
|
+
|
320
|
+
# pushes changes to a remote repository - easiest if this is a cloned repository,
|
321
|
+
# otherwise you may have to run something like this first to setup the push parameters:
|
322
|
+
#
|
323
|
+
# @git.config('remote.remote-name.push', 'refs/heads/master:refs/heads/master')
|
324
|
+
#
|
325
|
+
def push(remote = 'origin', branch = 'master', opts = {})
|
326
|
+
# Small hack to keep backwards compatibility with the 'push(remote, branch, tags)' method signature.
|
327
|
+
opts = {:tags => opts} if [true, false].include?(opts)
|
328
|
+
|
329
|
+
self.lib.push(remote, branch, opts)
|
330
|
+
end
|
331
|
+
|
332
|
+
# merges one or more branches into the current working branch
|
333
|
+
#
|
334
|
+
# you can specify more than one branch to merge by passing an array of branches
|
335
|
+
def merge(branch, message = 'merge')
|
336
|
+
self.lib.merge(branch, message)
|
337
|
+
end
|
338
|
+
|
339
|
+
# iterates over the files which are unmerged
|
340
|
+
def each_conflict(&block) # :yields: file, your_version, their_version
|
341
|
+
self.lib.conflicts(&block)
|
342
|
+
end
|
343
|
+
|
344
|
+
# pulls the given branch from the given remote into the current branch
|
345
|
+
#
|
346
|
+
# @git.pull # pulls from origin/master
|
347
|
+
# @git.pull('upstream') # pulls from upstream/master
|
348
|
+
# @git.pull('upstream', 'develope') # pulls from upstream/develop
|
349
|
+
#
|
350
|
+
def pull(remote='origin', branch='master')
|
351
|
+
self.lib.pull(remote, branch)
|
352
|
+
end
|
353
|
+
|
354
|
+
# returns an array of Git:Remote objects
|
355
|
+
def remotes
|
356
|
+
self.lib.remotes.map { |r| Git::Remote.new(self, r) }
|
357
|
+
end
|
358
|
+
|
359
|
+
# adds a new remote to this repository
|
360
|
+
# url can be a git url or a Git::Base object if it's a local reference
|
361
|
+
#
|
362
|
+
# @git.add_remote('scotts_git', 'git://repo.or.cz/rubygit.git')
|
363
|
+
# @git.fetch('scotts_git')
|
364
|
+
# @git.merge('scotts_git/master')
|
365
|
+
#
|
366
|
+
# Options:
|
367
|
+
# :fetch => true
|
368
|
+
# :track => <branch_name>
|
369
|
+
def add_remote(name, url, opts = {})
|
370
|
+
url = url.repo.path if url.is_a?(Git::Base)
|
371
|
+
self.lib.remote_add(name, url, opts)
|
372
|
+
Git::Remote.new(self, name)
|
373
|
+
end
|
374
|
+
|
375
|
+
# removes a remote from this repository
|
376
|
+
#
|
377
|
+
# @git.remove_remote('scott_git')
|
378
|
+
def remove_remote(name)
|
379
|
+
self.lib.remote_remove(name)
|
380
|
+
end
|
381
|
+
|
382
|
+
# returns an array of all Git::Tag objects for this repository
|
383
|
+
def tags
|
384
|
+
self.lib.tags.map { |r| tag(r) }
|
385
|
+
end
|
386
|
+
|
387
|
+
# Creates a new git tag (Git::Tag)
|
388
|
+
# Usage:
|
389
|
+
# repo.add_tag('tag_name', object_reference)
|
390
|
+
# repo.add_tag('tag_name', object_reference, {:options => 'here'})
|
391
|
+
# repo.add_tag('tag_name', {:options => 'here'})
|
392
|
+
#
|
393
|
+
# Options:
|
394
|
+
# :a | :annotate -> true
|
395
|
+
# :d -> true
|
396
|
+
# :f -> true
|
397
|
+
# :m | :message -> String
|
398
|
+
# :s -> true
|
399
|
+
#
|
400
|
+
def add_tag(name, *opts)
|
401
|
+
self.lib.tag(name, *opts)
|
402
|
+
self.tag(name)
|
403
|
+
end
|
404
|
+
|
405
|
+
# deletes a tag
|
406
|
+
def delete_tag(name)
|
407
|
+
self.lib.tag(name, {:d => true})
|
408
|
+
end
|
409
|
+
|
410
|
+
# creates an archive file of the given tree-ish
|
411
|
+
def archive(treeish, file = nil, opts = {})
|
412
|
+
self.object(treeish).archive(file, opts)
|
413
|
+
end
|
414
|
+
|
415
|
+
# repacks the repository
|
416
|
+
def repack
|
417
|
+
self.lib.repack
|
418
|
+
end
|
419
|
+
|
420
|
+
def gc
|
421
|
+
self.lib.gc
|
422
|
+
end
|
423
|
+
|
424
|
+
def apply(file)
|
425
|
+
if File.exist?(file)
|
426
|
+
self.lib.apply(file)
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
def apply_mail(file)
|
431
|
+
self.lib.apply_mail(file) if File.exist?(file)
|
432
|
+
end
|
433
|
+
|
434
|
+
# Shows objects
|
435
|
+
#
|
436
|
+
# @param [String|NilClass] objectish the target object reference (nil == HEAD)
|
437
|
+
# @param [String|NilClass] path the path of the file to be shown
|
438
|
+
# @return [String] the object information
|
439
|
+
def show(objectish=nil, path=nil)
|
440
|
+
self.lib.show(objectish, path)
|
441
|
+
end
|
442
|
+
|
443
|
+
## LOWER LEVEL INDEX OPERATIONS ##
|
444
|
+
|
445
|
+
def with_index(new_index) # :yields: new_index
|
446
|
+
old_index = @index
|
447
|
+
set_index(new_index, false)
|
448
|
+
return_value = yield @index
|
449
|
+
set_index(old_index)
|
450
|
+
return_value
|
451
|
+
end
|
452
|
+
|
453
|
+
def with_temp_index &blk
|
454
|
+
# Workaround for JRUBY, since they handle the TempFile path different.
|
455
|
+
# MUST be improved to be safer and OS independent.
|
456
|
+
if RUBY_PLATFORM == 'java'
|
457
|
+
temp_path = "/tmp/temp-index-#{(0...15).map{ ('a'..'z').to_a[rand(26)] }.join}"
|
458
|
+
else
|
459
|
+
tempfile = Tempfile.new('temp-index')
|
460
|
+
temp_path = tempfile.path
|
461
|
+
tempfile.close
|
462
|
+
tempfile.unlink
|
463
|
+
end
|
464
|
+
|
465
|
+
with_index(temp_path, &blk)
|
466
|
+
end
|
467
|
+
|
468
|
+
def checkout_index(opts = {})
|
469
|
+
self.lib.checkout_index(opts)
|
470
|
+
end
|
471
|
+
|
472
|
+
def read_tree(treeish, opts = {})
|
473
|
+
self.lib.read_tree(treeish, opts)
|
474
|
+
end
|
475
|
+
|
476
|
+
def write_tree
|
477
|
+
self.lib.write_tree
|
478
|
+
end
|
479
|
+
|
480
|
+
def write_and_commit_tree(opts = {})
|
481
|
+
tree = write_tree
|
482
|
+
commit_tree(tree, opts)
|
483
|
+
end
|
484
|
+
|
485
|
+
def update_ref(branch, commit)
|
486
|
+
branch(branch).update_ref(commit)
|
487
|
+
end
|
488
|
+
|
489
|
+
|
490
|
+
def ls_files(location=nil)
|
491
|
+
self.lib.ls_files(location)
|
492
|
+
end
|
493
|
+
|
494
|
+
def with_working(work_dir) # :yields: the Git::WorkingDirectory
|
495
|
+
return_value = false
|
496
|
+
old_working = @working_directory
|
497
|
+
set_working(work_dir)
|
498
|
+
Dir.chdir work_dir do
|
499
|
+
return_value = yield @working_directory
|
500
|
+
end
|
501
|
+
set_working(old_working)
|
502
|
+
return_value
|
503
|
+
end
|
504
|
+
|
505
|
+
def with_temp_working &blk
|
506
|
+
tempfile = Tempfile.new("temp-workdir")
|
507
|
+
temp_dir = tempfile.path
|
508
|
+
tempfile.close
|
509
|
+
tempfile.unlink
|
510
|
+
Dir.mkdir(temp_dir, 0700)
|
511
|
+
with_working(temp_dir, &blk)
|
512
|
+
end
|
513
|
+
|
514
|
+
|
515
|
+
# runs git rev-parse to convert the objectish to a full sha
|
516
|
+
#
|
517
|
+
# @git.revparse("HEAD^^")
|
518
|
+
# @git.revparse('v2.4^{tree}')
|
519
|
+
# @git.revparse('v2.4:/doc/index.html')
|
520
|
+
#
|
521
|
+
def revparse(objectish)
|
522
|
+
self.lib.revparse(objectish)
|
523
|
+
end
|
524
|
+
|
525
|
+
def ls_tree(objectish)
|
526
|
+
self.lib.ls_tree(objectish)
|
527
|
+
end
|
528
|
+
|
529
|
+
def cat_file(objectish)
|
530
|
+
self.lib.object_contents(objectish)
|
531
|
+
end
|
532
|
+
|
533
|
+
# returns the name of the branch the working directory is currently on
|
534
|
+
def current_branch
|
535
|
+
self.lib.branch_current
|
536
|
+
end
|
537
|
+
|
538
|
+
end
|
539
|
+
|
540
|
+
end
|