smeagol 0.6.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/.index +82 -0
  3. data/HISTORY.md +14 -0
  4. data/lib/smeagol.rb +2 -1
  5. data/lib/smeagol/app.rb +1 -1
  6. data/lib/smeagol/gollum/file.rb +1 -1
  7. data/lib/smeagol/wiki.rb +1 -0
  8. data/vendor/grit/lib/grit.rb +75 -0
  9. data/vendor/grit/lib/grit/actor.rb +52 -0
  10. data/vendor/grit/lib/grit/blame.rb +70 -0
  11. data/vendor/grit/lib/grit/blob.rb +126 -0
  12. data/vendor/grit/lib/grit/commit.rb +313 -0
  13. data/vendor/grit/lib/grit/commit_stats.rb +128 -0
  14. data/vendor/grit/lib/grit/config.rb +44 -0
  15. data/vendor/grit/lib/grit/diff.rb +79 -0
  16. data/vendor/grit/lib/grit/errors.rb +10 -0
  17. data/vendor/grit/lib/grit/git-ruby.rb +262 -0
  18. data/vendor/grit/lib/grit/git-ruby/commit_db.rb +52 -0
  19. data/vendor/grit/lib/grit/git-ruby/git_object.rb +353 -0
  20. data/vendor/grit/lib/grit/git-ruby/internal/file_window.rb +58 -0
  21. data/vendor/grit/lib/grit/git-ruby/internal/loose.rb +137 -0
  22. data/vendor/grit/lib/grit/git-ruby/internal/pack.rb +397 -0
  23. data/vendor/grit/lib/grit/git-ruby/internal/raw_object.rb +44 -0
  24. data/vendor/grit/lib/grit/git-ruby/repository.rb +775 -0
  25. data/vendor/grit/lib/grit/git.rb +501 -0
  26. data/vendor/grit/lib/grit/index.rb +222 -0
  27. data/vendor/grit/lib/grit/lazy.rb +35 -0
  28. data/vendor/grit/lib/grit/merge.rb +45 -0
  29. data/vendor/grit/lib/grit/ref.rb +78 -0
  30. data/vendor/grit/lib/grit/repo.rb +709 -0
  31. data/vendor/grit/lib/grit/ruby1.9.rb +7 -0
  32. data/vendor/grit/lib/grit/status.rb +153 -0
  33. data/vendor/grit/lib/grit/submodule.rb +88 -0
  34. data/vendor/grit/lib/grit/tag.rb +102 -0
  35. data/vendor/grit/lib/grit/tree.rb +125 -0
  36. metadata +125 -56
  37. data/.ruby +0 -80
@@ -0,0 +1,44 @@
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
+ #
8
+ # provides native ruby access to git objects and pack files
9
+ #
10
+
11
+ require 'digest/sha1'
12
+
13
+ module Grit
14
+ module GitRuby
15
+ module Internal
16
+ OBJ_NONE = 0
17
+ OBJ_COMMIT = 1
18
+ OBJ_TREE = 2
19
+ OBJ_BLOB = 3
20
+ OBJ_TAG = 4
21
+
22
+ OBJ_TYPES = [nil, :commit, :tree, :blob, :tag].freeze
23
+
24
+ class RawObject
25
+ attr_accessor :type, :content
26
+ def initialize(type, content)
27
+ @type = type
28
+ @content = content
29
+ end
30
+
31
+ def sha1
32
+ Digest::SHA1.digest("%s %d\0" % [@type, @content.length] + @content)
33
+ end
34
+
35
+ def to_hash
36
+ {
37
+ :type => @type,
38
+ :content => @content
39
+ }
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,775 @@
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
+ # if asked for recrusive trees, will traverse trees
173
+ def ls_tree(sha, paths = [], recursive = false)
174
+ if paths.size > 0
175
+ # pathing
176
+ part = []
177
+ paths.each do |path|
178
+ part += ls_tree_path(sha, path)
179
+ end
180
+ return part.join("\n")
181
+ else
182
+ get_raw_tree(sha, recursive)
183
+ end
184
+ end
185
+
186
+ def get_raw_tree(sha, recursive = false)
187
+ o = get_raw_object_by_sha1(sha)
188
+ if o.type == :commit
189
+ tree = get_object_by_sha1(sha).tree
190
+ elsif o.type == :tag
191
+ commit_sha = get_object_by_sha1(sha).object
192
+ tree = get_object_by_sha1(commit_sha).tree
193
+ elsif o.type == :tree
194
+ tree = sha
195
+ else
196
+ return nil
197
+ end
198
+
199
+ recursive ? get_raw_trees(tree) : cat_file(tree)
200
+ end
201
+
202
+ # Grabs tree contents recursively,
203
+ # e.g. `git ls-tree -r sha`
204
+ def get_raw_trees(sha, path = '')
205
+ out = ''
206
+ cat_file(sha).split("\n").each do |line|
207
+ mode, type, sha, name = line.split(/\s/)
208
+
209
+ if type == 'tree'
210
+ full_name = path.empty? ? name : "#{path}/#{name}"
211
+ out << get_raw_trees(sha, full_name)
212
+ elsif path.empty?
213
+ out << line + "\n"
214
+ else
215
+ out << line.gsub(name, "#{path}/#{name}") + "\n"
216
+ end
217
+ end
218
+
219
+ out
220
+ end
221
+
222
+ # return array of tree entries
223
+ ## TODO : refactor this to remove the fugly
224
+ def ls_tree_path(sha, path, append = nil)
225
+ tree = get_raw_tree(sha)
226
+ if path =~ /\//
227
+ paths = path.split('/')
228
+ last = path[path.size - 1, 1]
229
+ if (last == '/') && (paths.size == 1)
230
+ append = append ? File.join(append, paths.first) : paths.first
231
+ dir_name = tree.split("\n").select { |p| p.split("\t")[1] == paths.first }.first
232
+ raise NoSuchPath if !dir_name
233
+ next_sha = dir_name.split(' ')[2]
234
+ tree = get_raw_tree(next_sha)
235
+ tree = tree.split("\n")
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
+ else
247
+ raise NoSuchPath if tree.nil?
248
+ next_path = paths.shift
249
+ dir_name = tree.split("\n").select { |p| p.split("\t")[1] == next_path }.first
250
+ raise NoSuchPath if !dir_name
251
+ next_sha = dir_name.split(' ')[2]
252
+ next_path = append ? File.join(append, next_path) : next_path
253
+ if (last == '/')
254
+ ls_tree_path(next_sha, paths.join("/") + '/', next_path)
255
+ else
256
+ ls_tree_path(next_sha, paths.join("/"), next_path)
257
+ end
258
+ end
259
+ else
260
+ raise NoSuchPath if tree.nil?
261
+ tree = tree.split("\n")
262
+ tree = tree.select { |p| p.split("\t")[1] == path }
263
+ if append
264
+ mod_tree = []
265
+ tree.each do |ent|
266
+ (info, fpath) = ent.split("\t")
267
+ mod_tree << [info, File.join(append, fpath)].join("\t")
268
+ end
269
+ mod_tree
270
+ else
271
+ tree
272
+ end
273
+ end
274
+ end
275
+
276
+ # returns an array of GitRuby Commit objects
277
+ # [ [sha, raw_output], [sha, raw_output], [sha, raw_output] ... ]
278
+ #
279
+ # takes the following options:
280
+ # :since - Time object specifying that you don't want commits BEFORE this
281
+ # :until - Time object specifying that you don't want commit AFTER this
282
+ # :first_parent - tells log to only walk first parent
283
+ # :path_limiter - string or array of strings to limit path
284
+ # :max_count - number to limit the output
285
+ def log(sha, options = {})
286
+ @already_searched = {}
287
+ walk_log(sha, options)
288
+ end
289
+
290
+ def truncate_arr(arr, sha)
291
+ new_arr = []
292
+ arr.each do |a|
293
+ if a[0] == sha
294
+ return new_arr
295
+ end
296
+ new_arr << a
297
+ end
298
+ return new_arr
299
+ end
300
+
301
+ def rev_list(sha, options)
302
+ if sha.is_a? Array
303
+ (end_sha, sha) = sha
304
+ end
305
+
306
+ log = log(sha, options)
307
+ log = log.sort { |a, b| a[2] <=> b[2] }.reverse
308
+
309
+ if end_sha
310
+ log = truncate_arr(log, end_sha)
311
+ end
312
+
313
+ # shorten the list if it's longer than max_count (had to get everything in branches)
314
+ if options[:max_count]
315
+ if (opt_len = options[:max_count].to_i) < log.size
316
+ log = log[0, opt_len]
317
+ end
318
+ end
319
+
320
+ if options[:pretty] == 'raw'
321
+ log.map {|k, v| v }.join('')
322
+ else
323
+ log.map {|k, v| k }.join("\n")
324
+ end
325
+ end
326
+
327
+ # called by log() to recursively walk the tree
328
+ def walk_log(sha, opts, total_size = 0)
329
+ return [] if @already_searched[sha] # to prevent rechecking branches
330
+ @already_searched[sha] = true
331
+
332
+ array = []
333
+ if (sha)
334
+ o = get_raw_object_by_sha1(sha)
335
+ if o.type == :tag
336
+ commit_sha = get_object_by_sha1(sha).object
337
+ c = get_object_by_sha1(commit_sha)
338
+ else
339
+ c = GitObject.from_raw(o)
340
+ end
341
+
342
+ return [] if c.type != :commit
343
+
344
+ add_sha = true
345
+
346
+ if opts[:since] && opts[:since].is_a?(Time) && (opts[:since] > c.committer.date)
347
+ add_sha = false
348
+ end
349
+ if opts[:until] && opts[:until].is_a?(Time) && (opts[:until] < c.committer.date)
350
+ add_sha = false
351
+ end
352
+
353
+ # follow all parents unless '--first-parent' is specified #
354
+ subarray = []
355
+
356
+ if !c.parent.first && opts[:path_limiter] # check for the last commit
357
+ add_sha = false
358
+ end
359
+
360
+ if (!opts[:max_count] || ((array.size + total_size) < opts[:max_count]))
361
+
362
+ if !opts[:path_limiter]
363
+ output = c.raw_log(sha)
364
+ array << [sha, output, c.committer.date]
365
+ end
366
+
367
+ if (opts[:max_count] && (array.size + total_size) >= opts[:max_count])
368
+ return array
369
+ end
370
+
371
+ c.parent.each do |psha|
372
+ if psha && !files_changed?(c.tree, get_object_by_sha1(psha).tree,
373
+ opts[:path_limiter])
374
+ add_sha = false
375
+ end
376
+ subarray += walk_log(psha, opts, (array.size + total_size))
377
+ next if opts[:first_parent]
378
+ end
379
+
380
+ if opts[:path_limiter] && add_sha
381
+ output = c.raw_log(sha)
382
+ array << [sha, output, c.committer.date]
383
+ end
384
+
385
+ if add_sha
386
+ array += subarray
387
+ end
388
+ end
389
+
390
+ end
391
+
392
+ array
393
+ end
394
+
395
+ def diff(commit1, commit2, options = {})
396
+ patch = ''
397
+
398
+ commit_obj1 = get_object_by_sha1(commit1)
399
+ tree1 = commit_obj1.tree
400
+ if commit2
401
+ tree2 = get_object_by_sha1(commit2).tree
402
+ else
403
+ tree2 = get_object_by_sha1(commit_obj1.parent.first).tree
404
+ end
405
+
406
+ qdiff = quick_diff(tree1, tree2)
407
+
408
+ qdiff.sort.each do |diff_arr|
409
+ path, status, treeSHA1, treeSHA2 = *diff_arr
410
+ format, lines, output = :unified, 3, ''
411
+ file_length_difference = 0
412
+
413
+ fileA = treeSHA1 ? cat_file(treeSHA1) : ''
414
+ fileB = treeSHA2 ? cat_file(treeSHA2) : ''
415
+
416
+ sha1 = treeSHA1 || '0000000000000000000000000000000000000000'
417
+ sha2 = treeSHA2 || '0000000000000000000000000000000000000000'
418
+
419
+ data_old = fileA.split(/\n/).map! { |e| e.chomp }
420
+ data_new = fileB.split(/\n/).map! { |e| e.chomp }
421
+
422
+ diffs = Difference::LCS.diff(data_old, data_new)
423
+ next if diffs.empty?
424
+
425
+ a_path = "a/#{path.gsub('./', '')}"
426
+ b_path = "b/#{path.gsub('./', '')}"
427
+
428
+ header = "diff --git #{a_path} #{b_path}"
429
+ if options[:full_index]
430
+ header << "\n" + 'index ' + sha1 + '..' + sha2
431
+ header << ' 100644' if treeSHA2 # hard coding this because i don't think we use it
432
+ else
433
+ header << "\n" + 'index ' + sha1[0,7] + '..' + sha2[0,7]
434
+ header << ' 100644' if treeSHA2 # hard coding this because i don't think we use it
435
+ end
436
+ header << "\n--- " + (treeSHA1 ? a_path : '/dev/null')
437
+ header << "\n+++ " + (treeSHA2 ? b_path : '/dev/null')
438
+ header += "\n"
439
+
440
+ oldhunk = hunk = nil
441
+
442
+ diffs.each do |piece|
443
+ begin
444
+ hunk = Difference::LCS::Hunk.new(data_old, data_new, piece, lines, file_length_difference)
445
+ file_length_difference = hunk.file_length_difference
446
+
447
+ next unless oldhunk
448
+
449
+ if lines > 0 && hunk.overlaps?(oldhunk)
450
+ hunk.unshift(oldhunk)
451
+ else
452
+ output << oldhunk.diff(format)
453
+ end
454
+ ensure
455
+ oldhunk = hunk
456
+ output << "\n"
457
+ end
458
+ end
459
+
460
+ output << oldhunk.diff(format)
461
+ output << "\n"
462
+
463
+ patch << header + output.lstrip
464
+ end
465
+ patch
466
+ rescue
467
+ '' # one of the trees was bad or lcs isn't there - no diff
468
+ end
469
+
470
+ # takes 2 tree shas and recursively walks them to find out what
471
+ # files or directories have been modified in them and returns an
472
+ # array of changes
473
+ # [ [full_path, 'added', tree1_hash, nil],
474
+ # [full_path, 'removed', nil, tree2_hash],
475
+ # [full_path, 'modified', tree1_hash, tree2_hash]
476
+ # ]
477
+ def quick_diff(tree1, tree2, path = '.', recurse = true)
478
+ # handle empty trees
479
+ changed = []
480
+ return changed if tree1 == tree2
481
+
482
+ t1 = list_tree(tree1) if tree1
483
+ t2 = list_tree(tree2) if tree2
484
+
485
+ # finding files that are different
486
+ t1['blob'].each do |file, hsh|
487
+ t2_file = t2['blob'][file] rescue nil
488
+ full = File.join(path, file)
489
+ if !t2_file
490
+ changed << [full, 'added', hsh[:sha], nil] # not in parent
491
+ elsif (hsh[:sha] != t2_file[:sha])
492
+ changed << [full, 'modified', hsh[:sha], t2_file[:sha]] # file changed
493
+ end
494
+ end if t1
495
+ t2['blob'].each do |file, hsh|
496
+ if !t1 || !t1['blob'][file]
497
+ changed << [File.join(path, file), 'removed', nil, hsh[:sha]]
498
+ end
499
+ end if t2
500
+
501
+ t1['tree'].each do |dir, hsh|
502
+ t2_tree = t2['tree'][dir] rescue nil
503
+ full = File.join(path, dir)
504
+ if !t2_tree
505
+ if recurse
506
+ changed += quick_diff(hsh[:sha], nil, full, true)
507
+ else
508
+ changed << [full, 'added', hsh[:sha], nil] # not in parent
509
+ end
510
+ elsif (hsh[:sha] != t2_tree[:sha])
511
+ if recurse
512
+ changed += quick_diff(hsh[:sha], t2_tree[:sha], full, true)
513
+ else
514
+ changed << [full, 'modified', hsh[:sha], t2_tree[:sha]] # file changed
515
+ end
516
+ end
517
+ end if t1
518
+ t2['tree'].each do |dir, hsh|
519
+ t1_tree = t1['tree'][dir] rescue nil
520
+ full = File.join(path, dir)
521
+ if !t1_tree
522
+ if recurse
523
+ changed += quick_diff(nil, hsh[:sha], full, true)
524
+ else
525
+ changed << [full, 'removed', nil, hsh[:sha]]
526
+ end
527
+ end
528
+ end if t2
529
+
530
+ changed
531
+ end
532
+
533
+ # returns true if the files in path_limiter were changed, or no path limiter
534
+ # used by the log() function when passed with a path_limiter
535
+ def files_changed?(tree_sha1, tree_sha2, path_limiter = nil)
536
+ if path_limiter
537
+ mod = quick_diff(tree_sha1, tree_sha2)
538
+ files = mod.map { |c| c.first }
539
+ path_limiter.to_a.each do |filepath|
540
+ if files.include?(filepath)
541
+ return true
542
+ end
543
+ end
544
+ return false
545
+ end
546
+ true
547
+ end
548
+
549
+ def get_subtree(commit_sha, path)
550
+ tree_sha = get_object_by_sha1(commit_sha).tree
551
+
552
+ if path && !(path == '' || path == '.' || path == './')
553
+ paths = path.split('/')
554
+ paths.each do |pathname|
555
+ tree = get_object_by_sha1(tree_sha)
556
+ if entry = tree.entry.select { |e| e.name == pathname }.first
557
+ tree_sha = entry.sha1 rescue nil
558
+ else
559
+ return false
560
+ end
561
+ end
562
+ end
563
+
564
+ tree_sha
565
+ end
566
+
567
+ def blame_tree(commit_sha, path)
568
+ # find subtree
569
+ tree_sha = get_subtree(commit_sha, path)
570
+ return {} if !tree_sha
571
+
572
+ looking_for = []
573
+ get_object_by_sha1(tree_sha).entry.each do |e|
574
+ looking_for << File.join('.', e.name)
575
+ end
576
+
577
+ @already_searched = {}
578
+ commits = look_for_commits(commit_sha, path, looking_for)
579
+
580
+ # cleaning up array
581
+ arr = {}
582
+ commits.each do |commit_array|
583
+ key = commit_array[0].gsub('./', '')
584
+ arr[key] = commit_array[1]
585
+ end
586
+ arr
587
+ end
588
+
589
+ def look_for_commits(commit_sha, path, looking_for, options = {})
590
+ return [] if @already_searched[commit_sha] # to prevent rechecking branches
591
+
592
+ @already_searched[commit_sha] = true
593
+
594
+ commit = get_object_by_sha1(commit_sha)
595
+ tree_sha = get_subtree(commit_sha, path)
596
+
597
+ found_data = []
598
+
599
+ # at the beginning of the branch
600
+ if commit.parent.size == 0
601
+ looking_for.each do |search|
602
+ # prevents the rare case of multiple branch starting points with
603
+ # files that have never changed
604
+ if found_data.assoc(search)
605
+ found_data << [search, commit_sha]
606
+ end
607
+ end
608
+ return found_data
609
+ end
610
+
611
+ # go through the parents recursively, looking for somewhere this has been changed
612
+ commit.parent.each do |pc|
613
+ diff = quick_diff(tree_sha, get_subtree(pc, path), '.', false)
614
+
615
+ # remove anything found
616
+ looking_for.each do |search|
617
+ if match = diff.assoc(search)
618
+ found_data << [search, commit_sha, match]
619
+ looking_for.delete(search)
620
+ end
621
+ end
622
+
623
+ if looking_for.size <= 0 # we're done
624
+ return found_data
625
+ end
626
+
627
+ found_data += look_for_commits(pc, path, looking_for) # recurse into parent
628
+ return found_data if options[:first_parent]
629
+ end
630
+
631
+ ## TODO : find most recent commit with change in any parent
632
+ found_data
633
+ end
634
+
635
+ # initialize a git repository
636
+ def self.init(dir, bare = true)
637
+
638
+ FileUtils.mkdir_p(dir) if !File.exists?(dir)
639
+
640
+ FileUtils.cd(dir) do
641
+ if(File.exists?('objects'))
642
+ return false # already initialized
643
+ else
644
+ # initialize directory
645
+ create_initial_config(bare)
646
+ FileUtils.mkdir_p('refs/heads')
647
+ FileUtils.mkdir_p('refs/tags')
648
+ FileUtils.mkdir_p('objects/info')
649
+ FileUtils.mkdir_p('objects/pack')
650
+ FileUtils.mkdir_p('branches')
651
+ add_file('description', 'Unnamed repository; edit this file to name it for gitweb.')
652
+ add_file('HEAD', "ref: refs/heads/master\n")
653
+ FileUtils.mkdir_p('hooks')
654
+ FileUtils.cd('hooks') do
655
+ add_file('applypatch-msg', '# add shell script and make executable to enable')
656
+ add_file('post-commit', '# add shell script and make executable to enable')
657
+ add_file('post-receive', '# add shell script and make executable to enable')
658
+ add_file('post-update', '# add shell script and make executable to enable')
659
+ add_file('pre-applypatch', '# add shell script and make executable to enable')
660
+ add_file('pre-commit', '# add shell script and make executable to enable')
661
+ add_file('pre-rebase', '# add shell script and make executable to enable')
662
+ add_file('update', '# add shell script and make executable to enable')
663
+ end
664
+ FileUtils.mkdir_p('info')
665
+ add_file('info/exclude', "# *.[oa]\n# *~")
666
+ end
667
+ end
668
+ end
669
+
670
+ def self.create_initial_config(bare = false)
671
+ bare ? bare_status = 'true' : bare_status = 'false'
672
+ config = "[core]\n\trepositoryformatversion = 0\n\tfilemode = true\n\tbare = #{bare_status}\n\tlogallrefupdates = true"
673
+ add_file('config', config)
674
+ end
675
+
676
+ def self.add_file(name, contents)
677
+ File.open(name, 'w') do |f|
678
+ f.write contents
679
+ end
680
+ end
681
+
682
+ def close
683
+ @packs.each do |pack|
684
+ pack.close
685
+ end if @packs
686
+ end
687
+
688
+ protected
689
+
690
+ def git_path(path)
691
+ return "#@git_dir/#{path}"
692
+ end
693
+
694
+ private
695
+
696
+ def initloose
697
+ @loaded = []
698
+ @loose = []
699
+ load_loose(git_path('objects'))
700
+ load_alternate_loose(git_path('objects'))
701
+ @loose
702
+ end
703
+
704
+ def each_alternate_path(path)
705
+ alt = File.join(path, 'info/alternates')
706
+ return if !File.exists?(alt)
707
+
708
+ File.readlines(alt).each do |line|
709
+ path = line.chomp
710
+ if path[0, 2] == '..'
711
+ yield File.expand_path(File.join(@git_dir, 'objects', path))
712
+
713
+ # XXX this is here for backward compatibility with grit < 2.3.0
714
+ # relative alternate objects paths are expanded relative to the
715
+ # objects directory, not the git repository directory.
716
+ yield File.expand_path(File.join(@git_dir, path))
717
+ else
718
+ yield path
719
+ end
720
+ end
721
+ end
722
+
723
+ def load_alternate_loose(pathname)
724
+ # load alternate loose, too
725
+ each_alternate_path pathname do |path|
726
+ next if @loaded.include?(path)
727
+ next if !File.exist?(path)
728
+ load_loose(path)
729
+ load_alternate_loose(path)
730
+ end
731
+ end
732
+
733
+ def load_loose(path)
734
+ @loaded << path
735
+ return if !File.exists?(path)
736
+ @loose << Grit::GitRuby::Internal::LooseStorage.new(path)
737
+ end
738
+
739
+ def initpacks
740
+ close
741
+ @loaded_packs = []
742
+ @packs = []
743
+ load_packs(git_path("objects/pack"))
744
+ load_alternate_packs(git_path('objects'))
745
+ @packs
746
+ end
747
+
748
+ def load_alternate_packs(pathname)
749
+ each_alternate_path pathname do |path|
750
+ full_pack = File.join(path, 'pack')
751
+ next if @loaded_packs.include?(full_pack)
752
+ load_packs(full_pack)
753
+ load_alternate_packs(path)
754
+ end
755
+ end
756
+
757
+ def load_packs(path)
758
+ @loaded_packs << path
759
+ return if !File.exists?(path)
760
+ Dir.open(path) do |dir|
761
+ dir.each do |entry|
762
+ next if !(entry =~ /\.pack$/i)
763
+ pack = Grit::GitRuby::Internal::PackStorage.new(File.join(path,entry))
764
+ if @options[:map_packfile]
765
+ pack.cache_objects
766
+ end
767
+ @packs << pack
768
+ end
769
+ end
770
+ end
771
+
772
+ end
773
+
774
+ end
775
+ end