davetron5000-grit 1.1.2
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/API.txt +101 -0
- data/History.txt +49 -0
- data/LICENSE +22 -0
- data/README.md +216 -0
- data/VERSION.yml +4 -0
- data/examples/ex_add_commit.rb +13 -0
- data/examples/ex_index.rb +14 -0
- data/lib/grit/actor.rb +41 -0
- data/lib/grit/blame.rb +61 -0
- data/lib/grit/blob.rb +126 -0
- data/lib/grit/commit.rb +242 -0
- data/lib/grit/commit_stats.rb +128 -0
- data/lib/grit/config.rb +44 -0
- data/lib/grit/diff.rb +70 -0
- data/lib/grit/errors.rb +7 -0
- data/lib/grit/git-ruby/commit_db.rb +52 -0
- data/lib/grit/git-ruby/file_index.rb +193 -0
- data/lib/grit/git-ruby/git_object.rb +350 -0
- data/lib/grit/git-ruby/internal/file_window.rb +58 -0
- data/lib/grit/git-ruby/internal/loose.rb +137 -0
- data/lib/grit/git-ruby/internal/pack.rb +382 -0
- data/lib/grit/git-ruby/internal/raw_object.rb +37 -0
- data/lib/grit/git-ruby/object.rb +325 -0
- data/lib/grit/git-ruby/repository.rb +736 -0
- data/lib/grit/git-ruby.rb +186 -0
- data/lib/grit/git.rb +140 -0
- data/lib/grit/index.rb +122 -0
- data/lib/grit/lazy.rb +33 -0
- data/lib/grit/merge.rb +45 -0
- data/lib/grit/ref.rb +99 -0
- data/lib/grit/repo.rb +447 -0
- data/lib/grit/ruby1.9.rb +7 -0
- data/lib/grit/status.rb +151 -0
- data/lib/grit/submodule.rb +88 -0
- data/lib/grit/tag.rb +66 -0
- data/lib/grit/tree.rb +123 -0
- data/lib/grit.rb +68 -0
- data/lib/open3_detach.rb +46 -0
- data/test/bench/benchmarks.rb +126 -0
- data/test/helper.rb +18 -0
- data/test/profile.rb +21 -0
- data/test/suite.rb +6 -0
- data/test/test_actor.rb +51 -0
- data/test/test_blame.rb +32 -0
- data/test/test_blame_tree.rb +33 -0
- data/test/test_blob.rb +83 -0
- data/test/test_commit.rb +206 -0
- data/test/test_commit_stats.rb +33 -0
- data/test/test_commit_write.rb +54 -0
- data/test/test_config.rb +58 -0
- data/test/test_diff.rb +18 -0
- data/test/test_file_index.rb +56 -0
- data/test/test_git.rb +84 -0
- data/test/test_grit.rb +32 -0
- data/test/test_head.rb +47 -0
- data/test/test_index_status.rb +40 -0
- data/test/test_merge.rb +17 -0
- data/test/test_raw.rb +16 -0
- data/test/test_real.rb +19 -0
- data/test/test_reality.rb +17 -0
- data/test/test_remote.rb +14 -0
- data/test/test_repo.rb +347 -0
- data/test/test_rubygit.rb +188 -0
- data/test/test_rubygit_alt.rb +40 -0
- data/test/test_rubygit_index.rb +76 -0
- data/test/test_rubygit_iv2.rb +28 -0
- data/test/test_submodule.rb +69 -0
- data/test/test_tag.rb +67 -0
- data/test/test_tree.rb +101 -0
- metadata +141 -0
@@ -0,0 +1,736 @@
|
|
1
|
+
#
|
2
|
+
# converted from the gitrb project
|
3
|
+
#
|
4
|
+
# authors:
|
5
|
+
# Matthias Lederhofer <matled@gmx.net>
|
6
|
+
# Simon 'corecode' Schubert <corecode@fs.ei.tum.de>
|
7
|
+
# Scott Chacon <schacon@gmail.com>
|
8
|
+
#
|
9
|
+
# provides native ruby access to git objects and pack files
|
10
|
+
#
|
11
|
+
require 'grit/git-ruby/internal/raw_object'
|
12
|
+
require 'grit/git-ruby/internal/pack'
|
13
|
+
require 'grit/git-ruby/internal/loose'
|
14
|
+
require 'grit/git-ruby/git_object'
|
15
|
+
|
16
|
+
require 'rubygems'
|
17
|
+
require 'diff/lcs'
|
18
|
+
require 'diff/lcs/hunk'
|
19
|
+
|
20
|
+
# have to do this so it doesn't interfere with Grit::Diff
|
21
|
+
module Difference
|
22
|
+
include Diff
|
23
|
+
end
|
24
|
+
|
25
|
+
module Grit
|
26
|
+
module GitRuby
|
27
|
+
class Repository
|
28
|
+
|
29
|
+
class NoSuchShaFound < StandardError
|
30
|
+
end
|
31
|
+
|
32
|
+
class NoSuchPath < StandardError
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_accessor :git_dir, :options
|
36
|
+
|
37
|
+
def initialize(git_dir, options = {})
|
38
|
+
@git_dir = git_dir
|
39
|
+
@options = options
|
40
|
+
@packs = []
|
41
|
+
end
|
42
|
+
|
43
|
+
# returns the loose objects object lazily
|
44
|
+
def loose
|
45
|
+
@loose ||= initloose
|
46
|
+
end
|
47
|
+
|
48
|
+
# returns the array of pack list objects
|
49
|
+
def packs
|
50
|
+
@packs ||= initpacks
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
# prints out the type, shas and content of all of the pack files
|
55
|
+
def show
|
56
|
+
packs.each do |p|
|
57
|
+
puts p.name
|
58
|
+
puts
|
59
|
+
p.each_sha1 do |s|
|
60
|
+
puts "**#{p[s].type}**"
|
61
|
+
if p[s].type.to_s == 'commit'
|
62
|
+
puts s.unpack('H*')
|
63
|
+
puts p[s].content
|
64
|
+
end
|
65
|
+
end
|
66
|
+
puts
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
# returns a raw object given a SHA1
|
72
|
+
def get_raw_object_by_sha1(sha1o)
|
73
|
+
raise NoSuchShaFound if sha1o.nil? || sha1o.empty? || !sha1o.is_a?(String)
|
74
|
+
|
75
|
+
sha1 = [sha1o.chomp].pack("H*")
|
76
|
+
# try packs
|
77
|
+
packs.each do |pack|
|
78
|
+
o = pack[sha1]
|
79
|
+
return pack[sha1] if o
|
80
|
+
end
|
81
|
+
|
82
|
+
# try loose storage
|
83
|
+
loose.each do |lsobj|
|
84
|
+
o = lsobj[sha1]
|
85
|
+
return o if o
|
86
|
+
end
|
87
|
+
|
88
|
+
# try packs again, maybe the object got packed in the meantime
|
89
|
+
initpacks
|
90
|
+
packs.each do |pack|
|
91
|
+
o = pack[sha1]
|
92
|
+
return o if o
|
93
|
+
end
|
94
|
+
|
95
|
+
# puts "*#{sha1o}*"
|
96
|
+
raise NoSuchShaFound
|
97
|
+
end
|
98
|
+
|
99
|
+
def cached(key, object, do_cache = true)
|
100
|
+
object
|
101
|
+
end
|
102
|
+
|
103
|
+
# returns GitRuby object of any type given a SHA1
|
104
|
+
def get_object_by_sha1(sha1)
|
105
|
+
r = get_raw_object_by_sha1(sha1)
|
106
|
+
return nil if !r
|
107
|
+
GitObject.from_raw(r)
|
108
|
+
end
|
109
|
+
|
110
|
+
# writes a raw object into the git repo
|
111
|
+
def put_raw_object(content, type)
|
112
|
+
loose.first.put_raw_object(content, type)
|
113
|
+
end
|
114
|
+
|
115
|
+
# returns true or false if that sha exists in the db
|
116
|
+
def object_exists?(sha1)
|
117
|
+
sha_hex = [sha1].pack("H*")
|
118
|
+
return true if in_packs?(sha_hex)
|
119
|
+
return true if in_loose?(sha_hex)
|
120
|
+
initpacks
|
121
|
+
return true if in_packs?(sha_hex) #maybe the object got packed in the meantime
|
122
|
+
false
|
123
|
+
end
|
124
|
+
|
125
|
+
# returns true if the hex-packed sha is in the packfiles
|
126
|
+
def in_packs?(sha_hex)
|
127
|
+
# try packs
|
128
|
+
packs.each do |pack|
|
129
|
+
return true if pack[sha_hex]
|
130
|
+
end
|
131
|
+
false
|
132
|
+
end
|
133
|
+
|
134
|
+
# returns true if the hex-packed sha is in the loose objects
|
135
|
+
def in_loose?(sha_hex)
|
136
|
+
loose.each do |lsobj|
|
137
|
+
return true if lsobj[sha_hex]
|
138
|
+
end
|
139
|
+
false
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
# returns the file type (as a symbol) of this sha
|
144
|
+
def cat_file_type(sha)
|
145
|
+
get_raw_object_by_sha1(sha).type
|
146
|
+
end
|
147
|
+
|
148
|
+
# returns the file size (as an int) of this sha
|
149
|
+
def cat_file_size(sha)
|
150
|
+
get_raw_object_by_sha1(sha).content.size
|
151
|
+
end
|
152
|
+
|
153
|
+
# returns the raw file contents of this sha
|
154
|
+
def cat_file(sha)
|
155
|
+
get_object_by_sha1(sha).raw_content
|
156
|
+
end
|
157
|
+
|
158
|
+
# returns a 2-d hash of the tree
|
159
|
+
# ['blob']['FILENAME'] = {:mode => '100644', :sha => SHA}
|
160
|
+
# ['tree']['DIRNAME'] = {:mode => '040000', :sha => SHA}
|
161
|
+
def list_tree(sha)
|
162
|
+
data = {'blob' => {}, 'tree' => {}, 'link' => {}, 'commit' => {}}
|
163
|
+
get_object_by_sha1(sha).entry.each do |e|
|
164
|
+
data[e.format_type][e.name] = {:mode => e.format_mode, :sha => e.sha1}
|
165
|
+
end
|
166
|
+
data
|
167
|
+
end
|
168
|
+
|
169
|
+
# returns the raw (cat-file) output for a tree
|
170
|
+
# if given a commit sha, it will print the tree of that commit
|
171
|
+
# if given a path limiter array, it will limit the output to those
|
172
|
+
def ls_tree(sha, paths = [])
|
173
|
+
if paths.size > 0
|
174
|
+
# pathing
|
175
|
+
part = []
|
176
|
+
paths.each do |path|
|
177
|
+
part += ls_tree_path(sha, path)
|
178
|
+
end
|
179
|
+
return part.join("\n")
|
180
|
+
else
|
181
|
+
get_raw_tree(sha)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def get_raw_tree(sha)
|
186
|
+
o = get_raw_object_by_sha1(sha)
|
187
|
+
if o.type == :commit
|
188
|
+
cat_file(get_object_by_sha1(sha).tree)
|
189
|
+
elsif o.type == :tag
|
190
|
+
commit_sha = get_object_by_sha1(sha).object
|
191
|
+
cat_file(get_object_by_sha1(commit_sha).tree)
|
192
|
+
elsif o.type == :tree
|
193
|
+
cat_file(sha)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
# return array of tree entries
|
198
|
+
## TODO : refactor this to remove the fugly
|
199
|
+
def ls_tree_path(sha, path, append = nil)
|
200
|
+
tree = get_raw_tree(sha)
|
201
|
+
if path =~ /\//
|
202
|
+
paths = path.split('/')
|
203
|
+
last = path[path.size - 1, 1]
|
204
|
+
if (last == '/') && (paths.size == 1)
|
205
|
+
append = append ? File.join(append, paths.first) : paths.first
|
206
|
+
dir_name = tree.split("\n").select { |p| p.split("\t")[1] == paths.first }.first
|
207
|
+
raise NoSuchPath if !dir_name
|
208
|
+
next_sha = dir_name.split(' ')[2]
|
209
|
+
tree = get_raw_tree(next_sha)
|
210
|
+
tree = tree.split("\n")
|
211
|
+
if append
|
212
|
+
mod_tree = []
|
213
|
+
tree.each do |ent|
|
214
|
+
(info, fpath) = ent.split("\t")
|
215
|
+
mod_tree << [info, File.join(append, fpath)].join("\t")
|
216
|
+
end
|
217
|
+
mod_tree
|
218
|
+
else
|
219
|
+
tree
|
220
|
+
end
|
221
|
+
else
|
222
|
+
next_path = paths.shift
|
223
|
+
dir_name = tree.split("\n").select { |p| p.split("\t")[1] == next_path }.first
|
224
|
+
raise NoSuchPath if !dir_name
|
225
|
+
next_sha = dir_name.split(' ')[2]
|
226
|
+
next_path = append ? File.join(append, next_path) : next_path
|
227
|
+
if (last == '/')
|
228
|
+
ls_tree_path(next_sha, paths.join("/") + '/', next_path)
|
229
|
+
else
|
230
|
+
ls_tree_path(next_sha, paths.join("/"), next_path)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
else
|
234
|
+
tree = tree.split("\n")
|
235
|
+
tree = tree.select { |p| p.split("\t")[1] == path }
|
236
|
+
if append
|
237
|
+
mod_tree = []
|
238
|
+
tree.each do |ent|
|
239
|
+
(info, fpath) = ent.split("\t")
|
240
|
+
mod_tree << [info, File.join(append, fpath)].join("\t")
|
241
|
+
end
|
242
|
+
mod_tree
|
243
|
+
else
|
244
|
+
tree
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
# returns an array of GitRuby Commit objects
|
250
|
+
# [ [sha, raw_output], [sha, raw_output], [sha, raw_output] ... ]
|
251
|
+
#
|
252
|
+
# takes the following options:
|
253
|
+
# :since - Time object specifying that you don't want commits BEFORE this
|
254
|
+
# :until - Time object specifying that you don't want commit AFTER this
|
255
|
+
# :first_parent - tells log to only walk first parent
|
256
|
+
# :path_limiter - string or array of strings to limit path
|
257
|
+
# :max_count - number to limit the output
|
258
|
+
def log(sha, options = {})
|
259
|
+
@already_searched = {}
|
260
|
+
walk_log(sha, options)
|
261
|
+
end
|
262
|
+
|
263
|
+
def truncate_arr(arr, sha)
|
264
|
+
new_arr = []
|
265
|
+
arr.each do |a|
|
266
|
+
if a[0] == sha
|
267
|
+
return new_arr
|
268
|
+
end
|
269
|
+
new_arr << a
|
270
|
+
end
|
271
|
+
return new_arr
|
272
|
+
end
|
273
|
+
|
274
|
+
def rev_list(sha, options)
|
275
|
+
if sha.is_a? Array
|
276
|
+
(end_sha, sha) = sha
|
277
|
+
end
|
278
|
+
|
279
|
+
log = log(sha, options)
|
280
|
+
log = log.sort { |a, b| a[2] <=> b[2] }.reverse
|
281
|
+
|
282
|
+
if end_sha
|
283
|
+
log = truncate_arr(log, end_sha)
|
284
|
+
end
|
285
|
+
|
286
|
+
# shorten the list if it's longer than max_count (had to get everything in branches)
|
287
|
+
if options[:max_count]
|
288
|
+
if (opt_len = options[:max_count].to_i) < log.size
|
289
|
+
log = log[0, opt_len]
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
if options[:pretty] == 'raw'
|
294
|
+
log.map {|k, v| v }.join('')
|
295
|
+
else
|
296
|
+
log.map {|k, v| k }.join("\n")
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
# called by log() to recursively walk the tree
|
301
|
+
def walk_log(sha, opts, total_size = 0)
|
302
|
+
return [] if @already_searched[sha] # to prevent rechecking branches
|
303
|
+
@already_searched[sha] = true
|
304
|
+
|
305
|
+
array = []
|
306
|
+
if (sha)
|
307
|
+
o = get_raw_object_by_sha1(sha)
|
308
|
+
if o.type == :tag
|
309
|
+
commit_sha = get_object_by_sha1(sha).object
|
310
|
+
c = get_object_by_sha1(commit_sha)
|
311
|
+
else
|
312
|
+
c = GitObject.from_raw(o)
|
313
|
+
end
|
314
|
+
|
315
|
+
return [] if c.type != :commit
|
316
|
+
|
317
|
+
add_sha = true
|
318
|
+
|
319
|
+
if opts[:since] && opts[:since].is_a?(Time) && (opts[:since] > c.committer.date)
|
320
|
+
add_sha = false
|
321
|
+
end
|
322
|
+
if opts[:until] && opts[:until].is_a?(Time) && (opts[:until] < c.committer.date)
|
323
|
+
add_sha = false
|
324
|
+
end
|
325
|
+
|
326
|
+
# follow all parents unless '--first-parent' is specified #
|
327
|
+
subarray = []
|
328
|
+
|
329
|
+
if !c.parent.first && opts[:path_limiter] # check for the last commit
|
330
|
+
add_sha = false
|
331
|
+
end
|
332
|
+
|
333
|
+
if (!opts[:max_count] || ((array.size + total_size) < opts[:max_count]))
|
334
|
+
|
335
|
+
if !opts[:path_limiter]
|
336
|
+
output = c.raw_log(sha)
|
337
|
+
array << [sha, output, c.committer.date]
|
338
|
+
end
|
339
|
+
|
340
|
+
if (opts[:max_count] && (array.size + total_size) >= opts[:max_count])
|
341
|
+
return array
|
342
|
+
end
|
343
|
+
|
344
|
+
c.parent.each do |psha|
|
345
|
+
if psha && !files_changed?(c.tree, get_object_by_sha1(psha).tree,
|
346
|
+
opts[:path_limiter])
|
347
|
+
add_sha = false
|
348
|
+
end
|
349
|
+
subarray += walk_log(psha, opts, (array.size + total_size))
|
350
|
+
next if opts[:first_parent]
|
351
|
+
end
|
352
|
+
|
353
|
+
if opts[:path_limiter] && add_sha
|
354
|
+
output = c.raw_log(sha)
|
355
|
+
array << [sha, output, c.committer.date]
|
356
|
+
end
|
357
|
+
|
358
|
+
if add_sha
|
359
|
+
array += subarray
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
end
|
364
|
+
|
365
|
+
array
|
366
|
+
end
|
367
|
+
|
368
|
+
def diff(commit1, commit2, options = {})
|
369
|
+
patch = ''
|
370
|
+
|
371
|
+
commit_obj1 = get_object_by_sha1(commit1)
|
372
|
+
tree1 = commit_obj1.tree
|
373
|
+
if commit2
|
374
|
+
tree2 = get_object_by_sha1(commit2).tree
|
375
|
+
else
|
376
|
+
tree2 = get_object_by_sha1(commit_obj1.parent.first).tree
|
377
|
+
end
|
378
|
+
|
379
|
+
qdiff = quick_diff(tree1, tree2)
|
380
|
+
|
381
|
+
qdiff.sort.each do |diff_arr|
|
382
|
+
format, lines, output = :unified, 3, ''
|
383
|
+
file_length_difference = 0
|
384
|
+
|
385
|
+
fileA = (diff_arr[2]) ? cat_file(diff_arr[2]) : ''
|
386
|
+
fileB = (diff_arr[3]) ? cat_file(diff_arr[3]) : ''
|
387
|
+
|
388
|
+
sha1 = (diff_arr[2]) ? diff_arr[2] : '0000000000000000000000000000000000000000'
|
389
|
+
sha2 = (diff_arr[3]) ? diff_arr[3] : '0000000000000000000000000000000000000000'
|
390
|
+
|
391
|
+
data_old = fileA.split(/\n/).map! { |e| e.chomp }
|
392
|
+
data_new = fileB.split(/\n/).map! { |e| e.chomp }
|
393
|
+
|
394
|
+
diffs = Difference::LCS.diff(data_old, data_new)
|
395
|
+
next if diffs.empty?
|
396
|
+
|
397
|
+
header = 'diff --git a/' + diff_arr[0].gsub('./', '') + ' b/' + diff_arr[0].gsub('./', '')
|
398
|
+
if options[:full_index]
|
399
|
+
header << "\n" + 'index ' + sha1 + '..' + sha2
|
400
|
+
header << ' 100644' if diff_arr[3] # hard coding this because i don't think we use it
|
401
|
+
else
|
402
|
+
header << "\n" + 'index ' + sha1[0,7] + '..' + sha2[0,7]
|
403
|
+
header << ' 100644' if diff_arr[3] # hard coding this because i don't think we use it
|
404
|
+
end
|
405
|
+
header << "\n--- " + 'a/' + diff_arr[0].gsub('./', '')
|
406
|
+
header << "\n+++ " + 'b/' + diff_arr[0].gsub('./', '')
|
407
|
+
header += "\n"
|
408
|
+
|
409
|
+
oldhunk = hunk = nil
|
410
|
+
|
411
|
+
diffs.each do |piece|
|
412
|
+
begin
|
413
|
+
hunk = Difference::LCS::Hunk.new(data_old, data_new, piece, lines, file_length_difference)
|
414
|
+
file_length_difference = hunk.file_length_difference
|
415
|
+
|
416
|
+
next unless oldhunk
|
417
|
+
|
418
|
+
if lines > 0 && hunk.overlaps?(oldhunk)
|
419
|
+
hunk.unshift(oldhunk)
|
420
|
+
else
|
421
|
+
output << oldhunk.diff(format)
|
422
|
+
end
|
423
|
+
ensure
|
424
|
+
oldhunk = hunk
|
425
|
+
output << "\n"
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
output << oldhunk.diff(format)
|
430
|
+
output << "\n"
|
431
|
+
|
432
|
+
patch << header + output.lstrip
|
433
|
+
end
|
434
|
+
patch
|
435
|
+
rescue
|
436
|
+
'' # one of the trees was bad or lcs isn't there - no diff
|
437
|
+
end
|
438
|
+
|
439
|
+
# takes 2 tree shas and recursively walks them to find out what
|
440
|
+
# files or directories have been modified in them and returns an
|
441
|
+
# array of changes
|
442
|
+
# [ [full_path, 'added', tree1_hash, nil],
|
443
|
+
# [full_path, 'removed', nil, tree2_hash],
|
444
|
+
# [full_path, 'modified', tree1_hash, tree2_hash]
|
445
|
+
# ]
|
446
|
+
def quick_diff(tree1, tree2, path = '.', recurse = true)
|
447
|
+
# handle empty trees
|
448
|
+
changed = []
|
449
|
+
return changed if tree1 == tree2
|
450
|
+
|
451
|
+
t1 = list_tree(tree1) if tree1
|
452
|
+
t2 = list_tree(tree2) if tree2
|
453
|
+
|
454
|
+
# finding files that are different
|
455
|
+
t1['blob'].each do |file, hsh|
|
456
|
+
t2_file = t2['blob'][file] rescue nil
|
457
|
+
full = File.join(path, file)
|
458
|
+
if !t2_file
|
459
|
+
changed << [full, 'added', hsh[:sha], nil] # not in parent
|
460
|
+
elsif (hsh[:sha] != t2_file[:sha])
|
461
|
+
changed << [full, 'modified', hsh[:sha], t2_file[:sha]] # file changed
|
462
|
+
end
|
463
|
+
end if t1
|
464
|
+
t2['blob'].each do |file, hsh|
|
465
|
+
if !t1 || !t1['blob'][file]
|
466
|
+
changed << [File.join(path, file), 'removed', nil, hsh[:sha]]
|
467
|
+
end
|
468
|
+
end if t2
|
469
|
+
|
470
|
+
t1['tree'].each do |dir, hsh|
|
471
|
+
t2_tree = t2['tree'][dir] rescue nil
|
472
|
+
full = File.join(path, dir)
|
473
|
+
if !t2_tree
|
474
|
+
if recurse
|
475
|
+
changed += quick_diff(hsh[:sha], nil, full, true)
|
476
|
+
else
|
477
|
+
changed << [full, 'added', hsh[:sha], nil] # not in parent
|
478
|
+
end
|
479
|
+
elsif (hsh[:sha] != t2_tree[:sha])
|
480
|
+
if recurse
|
481
|
+
changed += quick_diff(hsh[:sha], t2_tree[:sha], full, true)
|
482
|
+
else
|
483
|
+
changed << [full, 'modified', hsh[:sha], t2_tree[:sha]] # file changed
|
484
|
+
end
|
485
|
+
end
|
486
|
+
end if t1
|
487
|
+
t2['tree'].each do |dir, hsh|
|
488
|
+
t1_tree = t1['tree'][dir] rescue nil
|
489
|
+
full = File.join(path, dir)
|
490
|
+
if !t1_tree
|
491
|
+
if recurse
|
492
|
+
changed += quick_diff(nil, hsh[:sha], full, true)
|
493
|
+
else
|
494
|
+
changed << [full, 'removed', nil, hsh[:sha]]
|
495
|
+
end
|
496
|
+
end
|
497
|
+
end if t2
|
498
|
+
|
499
|
+
changed
|
500
|
+
end
|
501
|
+
|
502
|
+
# returns true if the files in path_limiter were changed, or no path limiter
|
503
|
+
# used by the log() function when passed with a path_limiter
|
504
|
+
def files_changed?(tree_sha1, tree_sha2, path_limiter = nil)
|
505
|
+
if path_limiter
|
506
|
+
mod = quick_diff(tree_sha1, tree_sha2)
|
507
|
+
files = mod.map { |c| c.first }
|
508
|
+
path_limiter.to_a.each do |filepath|
|
509
|
+
if files.include?(filepath)
|
510
|
+
return true
|
511
|
+
end
|
512
|
+
end
|
513
|
+
return false
|
514
|
+
end
|
515
|
+
true
|
516
|
+
end
|
517
|
+
|
518
|
+
def get_subtree(commit_sha, path)
|
519
|
+
tree_sha = get_object_by_sha1(commit_sha).tree
|
520
|
+
|
521
|
+
if path && !(path == '' || path == '.' || path == './')
|
522
|
+
paths = path.split('/')
|
523
|
+
paths.each do |path|
|
524
|
+
tree = get_object_by_sha1(tree_sha)
|
525
|
+
if entry = tree.entry.select { |e| e.name == path }.first
|
526
|
+
tree_sha = entry.sha1 rescue nil
|
527
|
+
else
|
528
|
+
return false
|
529
|
+
end
|
530
|
+
end
|
531
|
+
end
|
532
|
+
|
533
|
+
tree_sha
|
534
|
+
end
|
535
|
+
|
536
|
+
def blame_tree(commit_sha, path)
|
537
|
+
# find subtree
|
538
|
+
tree_sha = get_subtree(commit_sha, path)
|
539
|
+
return {} if !tree_sha
|
540
|
+
|
541
|
+
looking_for = []
|
542
|
+
get_object_by_sha1(tree_sha).entry.each do |e|
|
543
|
+
looking_for << File.join('.', e.name)
|
544
|
+
end
|
545
|
+
|
546
|
+
@already_searched = {}
|
547
|
+
commits = look_for_commits(commit_sha, path, looking_for)
|
548
|
+
|
549
|
+
# cleaning up array
|
550
|
+
arr = {}
|
551
|
+
commits.each do |commit_array|
|
552
|
+
key = commit_array[0].gsub('./', '')
|
553
|
+
arr[key] = commit_array[1]
|
554
|
+
end
|
555
|
+
arr
|
556
|
+
end
|
557
|
+
|
558
|
+
def look_for_commits(commit_sha, path, looking_for, options = {})
|
559
|
+
return [] if @already_searched[commit_sha] # to prevent rechecking branches
|
560
|
+
|
561
|
+
@already_searched[commit_sha] = true
|
562
|
+
|
563
|
+
commit = get_object_by_sha1(commit_sha)
|
564
|
+
tree_sha = get_subtree(commit_sha, path)
|
565
|
+
|
566
|
+
found_data = []
|
567
|
+
|
568
|
+
# at the beginning of the branch
|
569
|
+
if commit.parent.size == 0
|
570
|
+
looking_for.each do |search|
|
571
|
+
# prevents the rare case of multiple branch starting points with
|
572
|
+
# files that have never changed
|
573
|
+
if found_data.assoc(search)
|
574
|
+
found_data << [search, commit_sha]
|
575
|
+
end
|
576
|
+
end
|
577
|
+
return found_data
|
578
|
+
end
|
579
|
+
|
580
|
+
# go through the parents recursively, looking for somewhere this has been changed
|
581
|
+
commit.parent.each do |pc|
|
582
|
+
diff = quick_diff(tree_sha, get_subtree(pc, path), '.', false)
|
583
|
+
|
584
|
+
# remove anything found
|
585
|
+
looking_for.each do |search|
|
586
|
+
if match = diff.assoc(search)
|
587
|
+
found_data << [search, commit_sha, match]
|
588
|
+
looking_for.delete(search)
|
589
|
+
end
|
590
|
+
end
|
591
|
+
|
592
|
+
if looking_for.size <= 0 # we're done
|
593
|
+
return found_data
|
594
|
+
end
|
595
|
+
|
596
|
+
found_data += look_for_commits(pc, path, looking_for) # recurse into parent
|
597
|
+
return found_data if options[:first_parent]
|
598
|
+
end
|
599
|
+
|
600
|
+
## TODO : find most recent commit with change in any parent
|
601
|
+
found_data
|
602
|
+
end
|
603
|
+
|
604
|
+
# initialize a git repository
|
605
|
+
def self.init(dir, bare = false)
|
606
|
+
|
607
|
+
FileUtils.mkdir_p(dir) if !File.exists?(dir)
|
608
|
+
|
609
|
+
FileUtils.cd(dir) do
|
610
|
+
if(File.exists?('objects'))
|
611
|
+
return false # already initialized
|
612
|
+
else
|
613
|
+
# initialize directory
|
614
|
+
create_initial_config(bare)
|
615
|
+
FileUtils.mkdir_p('refs/heads')
|
616
|
+
FileUtils.mkdir_p('refs/tags')
|
617
|
+
FileUtils.mkdir_p('objects/info')
|
618
|
+
FileUtils.mkdir_p('objects/pack')
|
619
|
+
FileUtils.mkdir_p('branches')
|
620
|
+
add_file('description', 'Unnamed repository; edit this file to name it for gitweb.')
|
621
|
+
add_file('HEAD', "ref: refs/heads/master\n")
|
622
|
+
FileUtils.mkdir_p('hooks')
|
623
|
+
FileUtils.cd('hooks') do
|
624
|
+
add_file('applypatch-msg', '# add shell script and make executable to enable')
|
625
|
+
add_file('post-commit', '# add shell script and make executable to enable')
|
626
|
+
add_file('post-receive', '# add shell script and make executable to enable')
|
627
|
+
add_file('post-update', '# add shell script and make executable to enable')
|
628
|
+
add_file('pre-applypatch', '# add shell script and make executable to enable')
|
629
|
+
add_file('pre-commit', '# add shell script and make executable to enable')
|
630
|
+
add_file('pre-rebase', '# add shell script and make executable to enable')
|
631
|
+
add_file('update', '# add shell script and make executable to enable')
|
632
|
+
end
|
633
|
+
FileUtils.mkdir_p('info')
|
634
|
+
add_file('info/exclude', "# *.[oa]\n# *~")
|
635
|
+
end
|
636
|
+
end
|
637
|
+
end
|
638
|
+
|
639
|
+
def self.create_initial_config(bare = false)
|
640
|
+
bare ? bare_status = 'true' : bare_status = 'false'
|
641
|
+
config = "[core]\n\trepositoryformatversion = 0\n\tfilemode = true\n\tbare = #{bare_status}\n\tlogallrefupdates = true"
|
642
|
+
add_file('config', config)
|
643
|
+
end
|
644
|
+
|
645
|
+
def self.add_file(name, contents)
|
646
|
+
File.open(name, 'w') do |f|
|
647
|
+
f.write contents
|
648
|
+
end
|
649
|
+
end
|
650
|
+
|
651
|
+
def close
|
652
|
+
@packs.each do |pack|
|
653
|
+
pack.close
|
654
|
+
end if @packs
|
655
|
+
end
|
656
|
+
|
657
|
+
protected
|
658
|
+
|
659
|
+
def git_path(path)
|
660
|
+
return "#@git_dir/#{path}"
|
661
|
+
end
|
662
|
+
|
663
|
+
private
|
664
|
+
|
665
|
+
def initloose
|
666
|
+
@loaded = []
|
667
|
+
@loose = []
|
668
|
+
load_loose(git_path('objects'))
|
669
|
+
load_alternate_loose(git_path('objects'))
|
670
|
+
@loose
|
671
|
+
end
|
672
|
+
|
673
|
+
def load_alternate_loose(path)
|
674
|
+
# load alternate loose, too
|
675
|
+
alt = File.join(path, 'info/alternates')
|
676
|
+
if File.exists?(alt)
|
677
|
+
File.readlines(alt).each do |line|
|
678
|
+
next if @loaded.include?(line.chomp)
|
679
|
+
if line[0, 2] == '..'
|
680
|
+
line = File.expand_path(File.join(@git_dir, line))
|
681
|
+
end
|
682
|
+
load_loose(line.chomp)
|
683
|
+
load_alternate_loose(line.chomp)
|
684
|
+
end
|
685
|
+
end
|
686
|
+
end
|
687
|
+
|
688
|
+
def load_loose(path)
|
689
|
+
@loaded << path
|
690
|
+
return if !File.exists?(path)
|
691
|
+
@loose << Grit::GitRuby::Internal::LooseStorage.new(path)
|
692
|
+
end
|
693
|
+
|
694
|
+
def initpacks
|
695
|
+
close
|
696
|
+
@loaded_packs = []
|
697
|
+
@packs = []
|
698
|
+
load_packs(git_path("objects/pack"))
|
699
|
+
load_alternate_packs(git_path('objects'))
|
700
|
+
@packs
|
701
|
+
end
|
702
|
+
|
703
|
+
def load_alternate_packs(path)
|
704
|
+
alt = File.join(path, 'info/alternates')
|
705
|
+
if File.exists?(alt)
|
706
|
+
File.readlines(alt).each do |line|
|
707
|
+
if line[0, 2] == '..'
|
708
|
+
line = File.expand_path(File.join(@git_dir, line))
|
709
|
+
end
|
710
|
+
full_pack = File.join(line.chomp, 'pack')
|
711
|
+
next if @loaded_packs.include?(full_pack)
|
712
|
+
load_packs(full_pack)
|
713
|
+
load_alternate_packs(File.join(line.chomp))
|
714
|
+
end
|
715
|
+
end
|
716
|
+
end
|
717
|
+
|
718
|
+
def load_packs(path)
|
719
|
+
@loaded_packs << path
|
720
|
+
return if !File.exists?(path)
|
721
|
+
Dir.open(path) do |dir|
|
722
|
+
dir.each do |entry|
|
723
|
+
next if !(entry =~ /\.pack$/i)
|
724
|
+
pack = Grit::GitRuby::Internal::PackStorage.new(File.join(path,entry))
|
725
|
+
if @options[:map_packfile]
|
726
|
+
pack.cache_objects
|
727
|
+
end
|
728
|
+
@packs << pack
|
729
|
+
end
|
730
|
+
end
|
731
|
+
end
|
732
|
+
|
733
|
+
end
|
734
|
+
|
735
|
+
end
|
736
|
+
end
|