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.
Files changed (70) hide show
  1. data/API.txt +101 -0
  2. data/History.txt +49 -0
  3. data/LICENSE +22 -0
  4. data/README.md +216 -0
  5. data/VERSION.yml +4 -0
  6. data/examples/ex_add_commit.rb +13 -0
  7. data/examples/ex_index.rb +14 -0
  8. data/lib/grit/actor.rb +41 -0
  9. data/lib/grit/blame.rb +61 -0
  10. data/lib/grit/blob.rb +126 -0
  11. data/lib/grit/commit.rb +242 -0
  12. data/lib/grit/commit_stats.rb +128 -0
  13. data/lib/grit/config.rb +44 -0
  14. data/lib/grit/diff.rb +70 -0
  15. data/lib/grit/errors.rb +7 -0
  16. data/lib/grit/git-ruby/commit_db.rb +52 -0
  17. data/lib/grit/git-ruby/file_index.rb +193 -0
  18. data/lib/grit/git-ruby/git_object.rb +350 -0
  19. data/lib/grit/git-ruby/internal/file_window.rb +58 -0
  20. data/lib/grit/git-ruby/internal/loose.rb +137 -0
  21. data/lib/grit/git-ruby/internal/pack.rb +382 -0
  22. data/lib/grit/git-ruby/internal/raw_object.rb +37 -0
  23. data/lib/grit/git-ruby/object.rb +325 -0
  24. data/lib/grit/git-ruby/repository.rb +736 -0
  25. data/lib/grit/git-ruby.rb +186 -0
  26. data/lib/grit/git.rb +140 -0
  27. data/lib/grit/index.rb +122 -0
  28. data/lib/grit/lazy.rb +33 -0
  29. data/lib/grit/merge.rb +45 -0
  30. data/lib/grit/ref.rb +99 -0
  31. data/lib/grit/repo.rb +447 -0
  32. data/lib/grit/ruby1.9.rb +7 -0
  33. data/lib/grit/status.rb +151 -0
  34. data/lib/grit/submodule.rb +88 -0
  35. data/lib/grit/tag.rb +66 -0
  36. data/lib/grit/tree.rb +123 -0
  37. data/lib/grit.rb +68 -0
  38. data/lib/open3_detach.rb +46 -0
  39. data/test/bench/benchmarks.rb +126 -0
  40. data/test/helper.rb +18 -0
  41. data/test/profile.rb +21 -0
  42. data/test/suite.rb +6 -0
  43. data/test/test_actor.rb +51 -0
  44. data/test/test_blame.rb +32 -0
  45. data/test/test_blame_tree.rb +33 -0
  46. data/test/test_blob.rb +83 -0
  47. data/test/test_commit.rb +206 -0
  48. data/test/test_commit_stats.rb +33 -0
  49. data/test/test_commit_write.rb +54 -0
  50. data/test/test_config.rb +58 -0
  51. data/test/test_diff.rb +18 -0
  52. data/test/test_file_index.rb +56 -0
  53. data/test/test_git.rb +84 -0
  54. data/test/test_grit.rb +32 -0
  55. data/test/test_head.rb +47 -0
  56. data/test/test_index_status.rb +40 -0
  57. data/test/test_merge.rb +17 -0
  58. data/test/test_raw.rb +16 -0
  59. data/test/test_real.rb +19 -0
  60. data/test/test_reality.rb +17 -0
  61. data/test/test_remote.rb +14 -0
  62. data/test/test_repo.rb +347 -0
  63. data/test/test_rubygit.rb +188 -0
  64. data/test/test_rubygit_alt.rb +40 -0
  65. data/test/test_rubygit_index.rb +76 -0
  66. data/test/test_rubygit_iv2.rb +28 -0
  67. data/test/test_submodule.rb +69 -0
  68. data/test/test_tag.rb +67 -0
  69. data/test/test_tree.rb +101 -0
  70. 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