schacon-git 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +238 -0
- data/lib/git/author.rb +14 -0
- data/lib/git/base.rb +464 -0
- data/lib/git/branch.rb +106 -0
- data/lib/git/branches.rb +57 -0
- data/lib/git/diff.rb +143 -0
- data/lib/git/index.rb +5 -0
- data/lib/git/lib.rb +638 -0
- data/lib/git/log.rb +94 -0
- data/lib/git/object.rb +296 -0
- data/lib/git/path.rb +27 -0
- data/lib/git/remote.rb +42 -0
- data/lib/git/repository.rb +4 -0
- data/lib/git/stash.rb +26 -0
- data/lib/git/stashes.rb +49 -0
- data/lib/git/status.rb +118 -0
- data/lib/git/working_directory.rb +4 -0
- data/lib/git.rb +96 -0
- data/tests/all_tests.rb +4 -0
- data/tests/test_helper.rb +77 -0
- metadata +72 -0
data/lib/git/log.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
module Git
|
2
|
+
|
3
|
+
# object that holds the last X commits on given branch
|
4
|
+
class Log
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
@base = nil
|
8
|
+
@commits = nil
|
9
|
+
|
10
|
+
@object = nil
|
11
|
+
@path = nil
|
12
|
+
@count = nil
|
13
|
+
@since = nil
|
14
|
+
@between = nil
|
15
|
+
|
16
|
+
@dirty_flag = nil
|
17
|
+
|
18
|
+
def initialize(base, count = 30)
|
19
|
+
dirty_log
|
20
|
+
@base = base
|
21
|
+
@count = count
|
22
|
+
end
|
23
|
+
|
24
|
+
def object(objectish)
|
25
|
+
dirty_log
|
26
|
+
@object = objectish
|
27
|
+
return self
|
28
|
+
end
|
29
|
+
|
30
|
+
def path(path)
|
31
|
+
dirty_log
|
32
|
+
@path = path
|
33
|
+
return self
|
34
|
+
end
|
35
|
+
|
36
|
+
def since(date)
|
37
|
+
dirty_log
|
38
|
+
@since = date
|
39
|
+
return self
|
40
|
+
end
|
41
|
+
|
42
|
+
def between(sha1, sha2 = nil)
|
43
|
+
dirty_log
|
44
|
+
@between = [sha1, sha2]
|
45
|
+
return self
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
self.map { |c| c.to_s }.join("\n")
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
# forces git log to run
|
54
|
+
|
55
|
+
def size
|
56
|
+
check_log
|
57
|
+
@commits.size rescue nil
|
58
|
+
end
|
59
|
+
|
60
|
+
def each
|
61
|
+
check_log
|
62
|
+
@commits.each do |c|
|
63
|
+
yield c
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def first
|
68
|
+
check_log
|
69
|
+
@commits.first rescue nil
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def dirty_log
|
75
|
+
@dirty_flag = true
|
76
|
+
end
|
77
|
+
|
78
|
+
def check_log
|
79
|
+
if @dirty_flag
|
80
|
+
run_log
|
81
|
+
@dirty_flag = false
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# actually run the 'git log' command
|
86
|
+
def run_log
|
87
|
+
log = @base.lib.full_log_commits(:count => @count, :object => @object,
|
88
|
+
:path_limiter => @path, :since => @since, :between => @between)
|
89
|
+
@commits = log.map { |c| Git::Object::Commit.new(@base, c['sha'], c) }
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
data/lib/git/object.rb
ADDED
@@ -0,0 +1,296 @@
|
|
1
|
+
module Git
|
2
|
+
|
3
|
+
class GitTagNameDoesNotExist< StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
# represents a git object
|
7
|
+
class Object
|
8
|
+
|
9
|
+
class AbstractObject
|
10
|
+
attr_accessor :objectish, :size, :type, :mode
|
11
|
+
|
12
|
+
@base = nil
|
13
|
+
@contents = nil
|
14
|
+
@size = nil
|
15
|
+
@sha = nil
|
16
|
+
|
17
|
+
def initialize(base, objectish)
|
18
|
+
@base = base
|
19
|
+
@objectish = objectish.to_s
|
20
|
+
setup
|
21
|
+
end
|
22
|
+
|
23
|
+
def sha
|
24
|
+
@sha || @sha = @base.lib.revparse(@objectish)
|
25
|
+
end
|
26
|
+
|
27
|
+
def size
|
28
|
+
@size || @size = @base.lib.object_size(@objectish)
|
29
|
+
end
|
30
|
+
|
31
|
+
# get the object's contents
|
32
|
+
# if no block is given, the contents are cached in memory and returned as a string
|
33
|
+
# if a block is given, it yields an IO object (via IO::popen) which could be used to
|
34
|
+
# read a large file in chunks. use this for large files so that they are not held
|
35
|
+
# in memory
|
36
|
+
def contents(&block)
|
37
|
+
if block_given?
|
38
|
+
@base.lib.object_contents(@objectish, &block)
|
39
|
+
else
|
40
|
+
@contents || @contents = @base.lib.object_contents(@objectish)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def contents_array
|
45
|
+
self.contents.split("\n")
|
46
|
+
end
|
47
|
+
|
48
|
+
def setup
|
49
|
+
raise NotImplementedError
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_s
|
53
|
+
@objectish
|
54
|
+
end
|
55
|
+
|
56
|
+
def grep(string, path_limiter = nil, opts = {})
|
57
|
+
default = {:object => sha, :path_limiter => path_limiter}
|
58
|
+
grep_options = default.merge(opts)
|
59
|
+
@base.lib.grep(string, grep_options)
|
60
|
+
end
|
61
|
+
|
62
|
+
def diff(objectish)
|
63
|
+
Git::Diff.new(@base, @objectish, objectish)
|
64
|
+
end
|
65
|
+
|
66
|
+
def log(count = 30)
|
67
|
+
Git::Log.new(@base, count).object(@objectish)
|
68
|
+
end
|
69
|
+
|
70
|
+
# creates an archive of this object (tree)
|
71
|
+
def archive(file = nil, opts = {})
|
72
|
+
@base.lib.archive(@objectish, file, opts)
|
73
|
+
end
|
74
|
+
|
75
|
+
def tree?
|
76
|
+
@type == 'tree'
|
77
|
+
end
|
78
|
+
|
79
|
+
def blob?
|
80
|
+
@type == 'blob'
|
81
|
+
end
|
82
|
+
|
83
|
+
def commit?
|
84
|
+
@type == 'commit'
|
85
|
+
end
|
86
|
+
|
87
|
+
def tag?
|
88
|
+
@type == 'tag'
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
class Blob < AbstractObject
|
95
|
+
|
96
|
+
def initialize(base, sha, mode = nil)
|
97
|
+
super(base, sha)
|
98
|
+
@mode = mode
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def setup
|
104
|
+
@type = 'blob'
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class Tree < AbstractObject
|
109
|
+
|
110
|
+
@trees = nil
|
111
|
+
@blobs = nil
|
112
|
+
|
113
|
+
def initialize(base, sha, mode = nil)
|
114
|
+
super(base, sha)
|
115
|
+
@mode = mode
|
116
|
+
end
|
117
|
+
|
118
|
+
def children
|
119
|
+
blobs.merge(subtrees)
|
120
|
+
end
|
121
|
+
|
122
|
+
def blobs
|
123
|
+
check_tree
|
124
|
+
@blobs
|
125
|
+
end
|
126
|
+
alias_method :files, :blobs
|
127
|
+
|
128
|
+
def trees
|
129
|
+
check_tree
|
130
|
+
@trees
|
131
|
+
end
|
132
|
+
alias_method :subtrees, :trees
|
133
|
+
alias_method :subdirectories, :trees
|
134
|
+
|
135
|
+
def full_tree
|
136
|
+
@base.lib.full_tree(@objectish)
|
137
|
+
end
|
138
|
+
|
139
|
+
def depth
|
140
|
+
@base.lib.tree_depth(@objectish)
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
def setup
|
146
|
+
@type = 'tree'
|
147
|
+
end
|
148
|
+
|
149
|
+
# actually run the git command
|
150
|
+
def check_tree
|
151
|
+
if !@trees
|
152
|
+
@trees = {}
|
153
|
+
@blobs = {}
|
154
|
+
data = @base.lib.ls_tree(@objectish)
|
155
|
+
data['tree'].each { |k, d| @trees[k] = Git::Object::Tree.new(@base, d[:sha], d[:mode]) }
|
156
|
+
data['blob'].each { |k, d| @blobs[k] = Git::Object::Blob.new(@base, d[:sha], d[:mode]) }
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
class Commit < AbstractObject
|
163
|
+
|
164
|
+
@tree = nil
|
165
|
+
@parents = nil
|
166
|
+
@author = nil
|
167
|
+
@committer = nil
|
168
|
+
@message = nil
|
169
|
+
|
170
|
+
def initialize(base, sha, init = nil)
|
171
|
+
super(base, sha)
|
172
|
+
if init
|
173
|
+
set_commit(init)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def message
|
178
|
+
check_commit
|
179
|
+
@message
|
180
|
+
end
|
181
|
+
|
182
|
+
def name
|
183
|
+
@base.lib.namerev(sha)
|
184
|
+
end
|
185
|
+
|
186
|
+
def gtree
|
187
|
+
check_commit
|
188
|
+
Tree.new(@base, @tree)
|
189
|
+
end
|
190
|
+
|
191
|
+
def parent
|
192
|
+
parents.first
|
193
|
+
end
|
194
|
+
|
195
|
+
# array of all parent commits
|
196
|
+
def parents
|
197
|
+
check_commit
|
198
|
+
@parents
|
199
|
+
end
|
200
|
+
|
201
|
+
# git author
|
202
|
+
def author
|
203
|
+
check_commit
|
204
|
+
@author
|
205
|
+
end
|
206
|
+
|
207
|
+
def author_date
|
208
|
+
author.date
|
209
|
+
end
|
210
|
+
|
211
|
+
# git author
|
212
|
+
def committer
|
213
|
+
check_commit
|
214
|
+
@committer
|
215
|
+
end
|
216
|
+
|
217
|
+
def committer_date
|
218
|
+
committer.date
|
219
|
+
end
|
220
|
+
alias_method :date, :committer_date
|
221
|
+
|
222
|
+
def diff_parent
|
223
|
+
diff(parent)
|
224
|
+
end
|
225
|
+
|
226
|
+
def set_commit(data)
|
227
|
+
if data['sha']
|
228
|
+
@sha = data['sha']
|
229
|
+
end
|
230
|
+
@committer = Git::Author.new(data['committer'])
|
231
|
+
@author = Git::Author.new(data['author'])
|
232
|
+
@tree = Git::Object::Tree.new(@base, data['tree'])
|
233
|
+
@parents = data['parent'].map{ |sha| Git::Object::Commit.new(@base, sha) }
|
234
|
+
@message = data['message'].chomp
|
235
|
+
end
|
236
|
+
|
237
|
+
private
|
238
|
+
|
239
|
+
def setup
|
240
|
+
@type = 'commit'
|
241
|
+
end
|
242
|
+
|
243
|
+
# see if this object has been initialized and do so if not
|
244
|
+
def check_commit
|
245
|
+
if !@tree
|
246
|
+
data = @base.lib.commit_data(@objectish)
|
247
|
+
set_commit(data)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
end
|
252
|
+
|
253
|
+
class Tag < AbstractObject
|
254
|
+
attr_accessor :name
|
255
|
+
|
256
|
+
def initialize(base, sha, name)
|
257
|
+
super(base, sha)
|
258
|
+
@name = name
|
259
|
+
end
|
260
|
+
|
261
|
+
private
|
262
|
+
|
263
|
+
def setup
|
264
|
+
@type = 'tag'
|
265
|
+
end
|
266
|
+
|
267
|
+
end
|
268
|
+
|
269
|
+
class << self
|
270
|
+
# if we're calling this, we don't know what type it is yet
|
271
|
+
# so this is our little factory method
|
272
|
+
def new(base, objectish, type = nil, is_tag = false)
|
273
|
+
if is_tag
|
274
|
+
sha = base.lib.tag_sha(objectish)
|
275
|
+
if sha == ''
|
276
|
+
raise Git::GitTagNameDoesNotExist.new(objectish)
|
277
|
+
end
|
278
|
+
return Git::Object::Tag.new(base, sha, objectish)
|
279
|
+
else
|
280
|
+
if !type
|
281
|
+
type = base.lib.object_type(objectish)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
klass =
|
286
|
+
case type
|
287
|
+
when /blob/: Blob
|
288
|
+
when /commit/: Commit
|
289
|
+
when /tree/: Tree
|
290
|
+
end
|
291
|
+
klass::new(base, objectish)
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
end
|
296
|
+
end
|
data/lib/git/path.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module Git
|
2
|
+
class Path
|
3
|
+
|
4
|
+
attr_accessor :path
|
5
|
+
|
6
|
+
def initialize(path, check_path = true)
|
7
|
+
if !check_path || File.exists?(path)
|
8
|
+
@path = File.expand_path(path)
|
9
|
+
else
|
10
|
+
raise ArgumentError, "path does not exist", File.expand_path(path)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def readable?
|
15
|
+
File.readable?(@path)
|
16
|
+
end
|
17
|
+
|
18
|
+
def writable?
|
19
|
+
File.writable?(@path)
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
@path
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
data/lib/git/remote.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
module Git
|
2
|
+
class Remote < Path
|
3
|
+
|
4
|
+
attr_accessor :name, :url, :fetch_opts
|
5
|
+
|
6
|
+
@base = nil
|
7
|
+
|
8
|
+
def initialize(base, name)
|
9
|
+
@base = base
|
10
|
+
config = @base.lib.config_remote(name)
|
11
|
+
@name = name
|
12
|
+
@url = config['url']
|
13
|
+
@fetch_opts = config['fetch']
|
14
|
+
end
|
15
|
+
|
16
|
+
def remove
|
17
|
+
@base.remote_remove(@name)
|
18
|
+
end
|
19
|
+
|
20
|
+
def fetch
|
21
|
+
@base.fetch(@name)
|
22
|
+
end
|
23
|
+
|
24
|
+
# merge this remote locally
|
25
|
+
def merge(branch = 'master')
|
26
|
+
@base.merge("#{@name}/#{branch}")
|
27
|
+
end
|
28
|
+
|
29
|
+
def branch(branch = 'master')
|
30
|
+
Git::Branch.new(@base, "#{@name}/#{branch}")
|
31
|
+
end
|
32
|
+
|
33
|
+
def remove
|
34
|
+
@base.lib.remote_remove(@name)
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_s
|
38
|
+
@name
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
data/lib/git/stash.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module Git
|
2
|
+
class Stash
|
3
|
+
def initialize(base, message, existing=false)
|
4
|
+
@base = base
|
5
|
+
@message = message
|
6
|
+
save if existing == false
|
7
|
+
end
|
8
|
+
|
9
|
+
def save
|
10
|
+
@saved = @base.lib.stash_save(@message)
|
11
|
+
end
|
12
|
+
|
13
|
+
def saved?
|
14
|
+
@saved
|
15
|
+
end
|
16
|
+
|
17
|
+
def message
|
18
|
+
@message
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
message
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
data/lib/git/stashes.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
module Git
|
2
|
+
|
3
|
+
# object that holds all the available stashes
|
4
|
+
class Stashes
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
@base = nil
|
8
|
+
@stashes = nil
|
9
|
+
|
10
|
+
def initialize(base)
|
11
|
+
@stashes = []
|
12
|
+
|
13
|
+
@base = base
|
14
|
+
|
15
|
+
@base.lib.stashes_all.each do |id, message|
|
16
|
+
@stashes.unshift(Git::Stash.new(@base, message, true))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def save(message)
|
21
|
+
s = Git::Stash.new(@base, message)
|
22
|
+
@stashes.unshift(s) if s.saved?
|
23
|
+
end
|
24
|
+
|
25
|
+
def apply(index=0)
|
26
|
+
@base.lib.stash_apply(index.to_i)
|
27
|
+
end
|
28
|
+
|
29
|
+
def clear
|
30
|
+
@base.lib.stash_clear
|
31
|
+
@stashes = []
|
32
|
+
end
|
33
|
+
|
34
|
+
def size
|
35
|
+
@stashes.size
|
36
|
+
end
|
37
|
+
|
38
|
+
def each
|
39
|
+
@stashes.each do |s|
|
40
|
+
yield s
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def [](index)
|
45
|
+
@stashes[index.to_i]
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
data/lib/git/status.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
module Git
|
2
|
+
|
3
|
+
class Status
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
@base = nil
|
7
|
+
@files = nil
|
8
|
+
|
9
|
+
def initialize(base)
|
10
|
+
@base = base
|
11
|
+
construct_status
|
12
|
+
end
|
13
|
+
|
14
|
+
def changed
|
15
|
+
@files.select { |k, f| f.type == 'M' }
|
16
|
+
end
|
17
|
+
|
18
|
+
def added
|
19
|
+
@files.select { |k, f| f.type == 'A' }
|
20
|
+
end
|
21
|
+
|
22
|
+
def deleted
|
23
|
+
@files.select { |k, f| f.type == 'D' }
|
24
|
+
end
|
25
|
+
|
26
|
+
def untracked
|
27
|
+
@files.select { |k, f| f.untracked }
|
28
|
+
end
|
29
|
+
|
30
|
+
def pretty
|
31
|
+
out = ''
|
32
|
+
self.each do |file|
|
33
|
+
out << file.path
|
34
|
+
out << "\n\tsha(r) " + file.sha_repo.to_s + ' ' + file.mode_repo.to_s
|
35
|
+
out << "\n\tsha(i) " + file.sha_index.to_s + ' ' + file.mode_index.to_s
|
36
|
+
out << "\n\ttype " + file.type.to_s
|
37
|
+
out << "\n\tstage " + file.stage.to_s
|
38
|
+
out << "\n\tuntrac " + file.untracked.to_s
|
39
|
+
out << "\n"
|
40
|
+
end
|
41
|
+
out << "\n"
|
42
|
+
out
|
43
|
+
end
|
44
|
+
|
45
|
+
# enumerable method
|
46
|
+
|
47
|
+
def [](file)
|
48
|
+
@files[file]
|
49
|
+
end
|
50
|
+
|
51
|
+
def each
|
52
|
+
@files.each do |k, file|
|
53
|
+
yield file
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class StatusFile
|
58
|
+
attr_accessor :path, :type, :stage, :untracked
|
59
|
+
attr_accessor :mode_index, :mode_repo
|
60
|
+
attr_accessor :sha_index, :sha_repo
|
61
|
+
|
62
|
+
@base = nil
|
63
|
+
|
64
|
+
def initialize(base, hash)
|
65
|
+
@base = base
|
66
|
+
@path = hash[:path]
|
67
|
+
@type = hash[:type]
|
68
|
+
@stage = hash[:stage]
|
69
|
+
@mode_index = hash[:mode_index]
|
70
|
+
@mode_repo = hash[:mode_repo]
|
71
|
+
@sha_index = hash[:sha_index]
|
72
|
+
@sha_repo = hash[:sha_repo]
|
73
|
+
@untracked = hash[:untracked]
|
74
|
+
end
|
75
|
+
|
76
|
+
def blob(type = :index)
|
77
|
+
if type == :repo
|
78
|
+
@base.object(@sha_repo)
|
79
|
+
else
|
80
|
+
@base.object(@sha_index) rescue @base.object(@sha_repo)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def construct_status
|
90
|
+
@files = @base.lib.ls_files
|
91
|
+
|
92
|
+
# find untracked in working dir
|
93
|
+
Dir.chdir(@base.dir.path) do
|
94
|
+
Dir.glob('**/*') do |file|
|
95
|
+
if !@files[file]
|
96
|
+
@files[file] = {:path => file, :untracked => true} if !File.directory?(file)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# find modified in tree
|
102
|
+
@base.lib.diff_files.each do |path, data|
|
103
|
+
@files[path] ? @files[path].merge!(data) : @files[path] = data
|
104
|
+
end
|
105
|
+
|
106
|
+
# find added but not committed - new files
|
107
|
+
@base.lib.diff_index('HEAD').each do |path, data|
|
108
|
+
@files[path] ? @files[path].merge!(data) : @files[path] = data
|
109
|
+
end
|
110
|
+
|
111
|
+
@files.each do |k, file_hash|
|
112
|
+
@files[k] = StatusFile.new(@base, file_hash)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|