mojombo-grit 0.8.1 → 0.9.3
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/History.txt +7 -0
- data/Manifest.txt +18 -1
- data/grit.gemspec +56 -10
- data/lib/{mojombo-grit.rb → grit.rb} +20 -4
- data/lib/grit/commit.rb +32 -11
- data/lib/grit/commit_stats.rb +104 -0
- data/lib/grit/git-ruby.rb +182 -0
- data/lib/grit/git-ruby/commit_db.rb +52 -0
- data/lib/grit/git-ruby/file_index.rb +186 -0
- data/lib/grit/git-ruby/git_object.rb +344 -0
- data/lib/grit/git-ruby/internal/loose.rb +136 -0
- data/lib/grit/git-ruby/internal/mmap.rb +58 -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 +319 -0
- data/lib/grit/git-ruby/repository.rb +729 -0
- data/lib/grit/git.rb +33 -15
- data/lib/grit/head.rb +6 -15
- data/lib/grit/index.rb +121 -0
- data/lib/grit/ref.rb +95 -0
- data/lib/grit/repo.rb +95 -6
- data/lib/grit/status.rb +151 -0
- data/lib/grit/tree.rb +3 -2
- data/test/test_blob.rb +5 -0
- data/test/test_commit.rb +7 -5
- data/test/test_diff.rb +1 -1
- data/test/test_git.rb +20 -2
- data/test/test_grit.rb +32 -0
- data/test/test_head.rb +30 -5
- data/test/test_real.rb +8 -6
- data/test/test_remote.rb +14 -0
- data/test/test_repo.rb +86 -79
- data/test/test_tag.rb +2 -6
- data/test/test_tree.rb +5 -0
- metadata +40 -40
- data/test/fixtures/blame +0 -131
- data/test/fixtures/cat_file_blob +0 -1
- data/test/fixtures/cat_file_blob_size +0 -1
- data/test/fixtures/diff_2 +0 -54
- data/test/fixtures/diff_2f +0 -19
- data/test/fixtures/diff_f +0 -15
- data/test/fixtures/diff_i +0 -201
- data/test/fixtures/diff_mode_only +0 -1152
- data/test/fixtures/diff_new_mode +0 -17
- data/test/fixtures/diff_p +0 -610
- data/test/fixtures/for_each_ref +0 -0
- data/test/fixtures/for_each_ref_tags +0 -0
- data/test/fixtures/ls_tree_a +0 -7
- data/test/fixtures/ls_tree_b +0 -2
- data/test/fixtures/ls_tree_commit +0 -3
- data/test/fixtures/rev_list +0 -26
- data/test/fixtures/rev_list_count +0 -655
- data/test/fixtures/rev_list_single +0 -7
- data/test/fixtures/rev_parse +0 -1
- data/test/fixtures/show_empty_commit +0 -6
- data/test/fixtures/simple_config +0 -2
- data/test/helper.rb +0 -17
- data/test/profile.rb +0 -21
- data/test/suite.rb +0 -6
data/lib/grit/git.rb
CHANGED
@@ -19,6 +19,8 @@ module Grit
|
|
19
19
|
|
20
20
|
undef_method :clone
|
21
21
|
|
22
|
+
include GitRuby
|
23
|
+
|
22
24
|
class << self
|
23
25
|
attr_accessor :git_binary, :git_timeout
|
24
26
|
end
|
@@ -44,31 +46,41 @@ module Grit
|
|
44
46
|
#
|
45
47
|
# Returns String
|
46
48
|
def method_missing(cmd, options = {}, *args)
|
49
|
+
run('', cmd, '', options, args)
|
50
|
+
end
|
51
|
+
|
52
|
+
def run(prefix, cmd, postfix, options, args)
|
47
53
|
timeout = options.delete(:timeout)
|
48
54
|
timeout = true if timeout.nil?
|
49
55
|
|
50
56
|
opt_args = transform_options(options)
|
51
57
|
ext_args = args.map { |a| a == '--' ? a : "'#{a}'" }
|
52
58
|
|
53
|
-
call = "#{Git.git_binary} --git-dir='#{self.git_dir}' #{cmd.to_s.gsub(/_/, '-')} #{(opt_args + ext_args).join(' ')}"
|
54
|
-
|
55
|
-
response = timeout ? sh(call) : wild_sh(call)
|
56
|
-
|
59
|
+
call = "#{prefix}#{Git.git_binary} --git-dir='#{self.git_dir}' #{cmd.to_s.gsub(/_/, '-')} #{(opt_args + ext_args).join(' ')}#{postfix}"
|
60
|
+
Grit.log(call) if Grit.debug
|
61
|
+
response, err = timeout ? sh(call) : wild_sh(call)
|
62
|
+
Grit.log(response) if Grit.debug
|
63
|
+
Grit.log(err) if Grit.debug
|
57
64
|
response
|
58
65
|
end
|
59
66
|
|
60
67
|
def sh(command)
|
61
|
-
pid,
|
62
|
-
|
63
|
-
|
68
|
+
ret, pid, err = nil, nil, nil
|
69
|
+
Open4.popen4(command) do |id, _, stdout, stderr|
|
70
|
+
pid = id
|
71
|
+
ret = Timeout.timeout(self.class.git_timeout) { stdout.read }
|
72
|
+
err = stderr.read
|
73
|
+
@bytes_read += ret.size
|
64
74
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
75
|
+
if @bytes_read > 5242880 # 5.megabytes
|
76
|
+
bytes = @bytes_read
|
77
|
+
@bytes_read = 0
|
78
|
+
raise GitTimeout.new(command, bytes)
|
79
|
+
end
|
69
80
|
end
|
70
|
-
|
71
|
-
|
81
|
+
[ret, err]
|
82
|
+
rescue Errno::ECHILD
|
83
|
+
[ret, err]
|
72
84
|
rescue Object => e
|
73
85
|
Process.kill('KILL', pid) rescue nil
|
74
86
|
bytes = @bytes_read
|
@@ -77,8 +89,14 @@ module Grit
|
|
77
89
|
end
|
78
90
|
|
79
91
|
def wild_sh(command)
|
80
|
-
|
81
|
-
|
92
|
+
ret, err = nil, nil
|
93
|
+
Open4.popen4(command) {|pid, _, stdout, stderr|
|
94
|
+
ret = stdout.read
|
95
|
+
err = stderr.read
|
96
|
+
}
|
97
|
+
[ret, err]
|
98
|
+
rescue Errno::ECHILD
|
99
|
+
[ret, err]
|
82
100
|
end
|
83
101
|
|
84
102
|
# Transform Ruby style options into git command line options
|
data/lib/grit/head.rb
CHANGED
@@ -29,8 +29,11 @@ module Grit
|
|
29
29
|
#
|
30
30
|
# Returns Grit::Head[] (baked)
|
31
31
|
def self.find_all(repo, options = {})
|
32
|
-
default_options = {
|
33
|
-
|
32
|
+
default_options = {
|
33
|
+
:sort => "committerdate",
|
34
|
+
:format => "%(refname)%00%(objectname)",
|
35
|
+
:timeout => false
|
36
|
+
}
|
34
37
|
|
35
38
|
actual_options = default_options.merge(options)
|
36
39
|
|
@@ -38,19 +41,7 @@ module Grit
|
|
38
41
|
|
39
42
|
self.list_from_string(repo, output)
|
40
43
|
end
|
41
|
-
|
42
|
-
# Get the HEAD revision of the repo.
|
43
|
-
# +repo+ is the Repo
|
44
|
-
# +options+ is a Hash of options
|
45
|
-
#
|
46
|
-
# Returns Grit::Head (baked)
|
47
|
-
def self.current(repo, options = {})
|
48
|
-
head = File.open(File.join(repo.path, 'HEAD')).read.chomp
|
49
|
-
if /ref: refs\/heads\/(.*)/.match(head)
|
50
|
-
self.new($1, repo.git.rev_parse(options, 'HEAD'))
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
44
|
+
|
54
45
|
# Parse out head information into an array of baked head objects
|
55
46
|
# +repo+ is the Repo
|
56
47
|
# +text+ is the text output from the git command
|
data/lib/grit/index.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
module Grit
|
2
|
+
|
3
|
+
class Index
|
4
|
+
attr_accessor :repo, :tree, :current_tree
|
5
|
+
|
6
|
+
def initialize(repo)
|
7
|
+
self.repo = repo
|
8
|
+
self.tree = {}
|
9
|
+
self.current_tree = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
# Add a file to the index
|
13
|
+
# +path+ is the path (including filename)
|
14
|
+
# +data+ is the binary contents of the file
|
15
|
+
#
|
16
|
+
# Returns nothing
|
17
|
+
def add(file_path, data)
|
18
|
+
path = file_path.split('/')
|
19
|
+
filename = path.pop
|
20
|
+
|
21
|
+
current = self.tree
|
22
|
+
|
23
|
+
path.each do |dir|
|
24
|
+
current[dir] ||= {}
|
25
|
+
node = current[dir]
|
26
|
+
current = node
|
27
|
+
end
|
28
|
+
|
29
|
+
current[filename] = data
|
30
|
+
end
|
31
|
+
|
32
|
+
def read_tree(tree)
|
33
|
+
self.current_tree = self.repo.tree(tree)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Commit the contents of the index
|
37
|
+
# +message+ is the commit message
|
38
|
+
#
|
39
|
+
# Returns a String of the SHA1 of the commit
|
40
|
+
def commit(message, parents = nil, actor = nil, last_tree = nil, head = 'master')
|
41
|
+
tree_sha1 = write_tree(self.tree, self.current_tree)
|
42
|
+
return false if tree_sha1 == last_tree # don't write identical commits
|
43
|
+
|
44
|
+
contents = []
|
45
|
+
contents << ['tree', tree_sha1].join(' ')
|
46
|
+
parents.each do |p|
|
47
|
+
contents << ['parent', p].join(' ') if p
|
48
|
+
end if parents
|
49
|
+
|
50
|
+
if actor
|
51
|
+
name = actor.name
|
52
|
+
email = actor.email
|
53
|
+
else
|
54
|
+
config = Config.new(self.repo)
|
55
|
+
name = config['user.name']
|
56
|
+
email = config['user.email']
|
57
|
+
end
|
58
|
+
|
59
|
+
author_string = "#{name} <#{email}> #{Time.now.to_i} -0700" # !! TODO : gotta fix this
|
60
|
+
contents << ['author', author_string].join(' ')
|
61
|
+
contents << ['committer', author_string].join(' ')
|
62
|
+
contents << ''
|
63
|
+
contents << message
|
64
|
+
|
65
|
+
commit_sha1 = self.repo.git.ruby_git.put_raw_object(contents.join("\n"), 'commit')
|
66
|
+
|
67
|
+
# self.repo.git.update_ref({}, 'HEAD', commit_sha1)
|
68
|
+
ref_heads = File.join(self.repo.path, 'refs', 'heads')
|
69
|
+
FileUtils.mkdir_p(ref_heads)
|
70
|
+
File.open(File.join(ref_heads, head), 'w') do |f|
|
71
|
+
f.write(commit_sha1)
|
72
|
+
end if commit_sha1
|
73
|
+
|
74
|
+
commit_sha1
|
75
|
+
end
|
76
|
+
|
77
|
+
# Recursively write a tree to the index
|
78
|
+
# +tree+ is the tree
|
79
|
+
#
|
80
|
+
# Returns the SHA1 String of the tree
|
81
|
+
def write_tree(tree, now_tree = nil)
|
82
|
+
tree_contents = {}
|
83
|
+
|
84
|
+
# fill in original tree
|
85
|
+
now_tree.contents.each do |obj|
|
86
|
+
sha = [obj.id].pack("H*")
|
87
|
+
k = obj.name
|
88
|
+
k += '/' if (obj.class == Grit::Tree)
|
89
|
+
tree_contents[k] = "%s %s\0%s" % [obj.mode.to_s, obj.name, sha]
|
90
|
+
end if now_tree
|
91
|
+
|
92
|
+
# overwrite with new tree contents
|
93
|
+
tree.each do |k, v|
|
94
|
+
case v
|
95
|
+
when String:
|
96
|
+
sha = write_blob(v)
|
97
|
+
sha = [sha].pack("H*")
|
98
|
+
str = "%s %s\0%s" % ['100644', k, sha]
|
99
|
+
tree_contents[k] = str
|
100
|
+
when Hash:
|
101
|
+
ctree = now_tree/k if now_tree
|
102
|
+
sha = write_tree(v, ctree)
|
103
|
+
sha = [sha].pack("H*")
|
104
|
+
str = "%s %s\0%s" % ['040000', k, sha]
|
105
|
+
tree_contents[k + '/'] = str
|
106
|
+
end
|
107
|
+
end
|
108
|
+
tr = tree_contents.sort.map { |k, v| v }.join('')
|
109
|
+
self.repo.git.ruby_git.put_raw_object(tr, 'tree')
|
110
|
+
end
|
111
|
+
|
112
|
+
# Write the blob to the index
|
113
|
+
# +data+ is the data to write
|
114
|
+
#
|
115
|
+
# Returns the SHA1 String of the blob
|
116
|
+
def write_blob(data)
|
117
|
+
self.repo.git.ruby_git.put_raw_object(data, 'blob')
|
118
|
+
end
|
119
|
+
end # Index
|
120
|
+
|
121
|
+
end # Grit
|
data/lib/grit/ref.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
module Grit
|
2
|
+
|
3
|
+
class Ref
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
# Find all Refs
|
8
|
+
# +repo+ is the Repo
|
9
|
+
# +options+ is a Hash of options
|
10
|
+
#
|
11
|
+
# Returns Grit::Ref[] (baked)
|
12
|
+
def find_all(repo, options = {})
|
13
|
+
refs = []
|
14
|
+
|
15
|
+
Dir.chdir(repo.path) do
|
16
|
+
if File.file?('packed-refs')
|
17
|
+
File.readlines('packed-refs').each do |line|
|
18
|
+
if m = /^(\w{40}) (.*?)$/.match(line)
|
19
|
+
next if !Regexp.new('^' + prefix).match(m[2])
|
20
|
+
name = m[2].sub("#{prefix}/", '')
|
21
|
+
commit = Commit.create(repo, :id => m[1])
|
22
|
+
refs << self.new(name, commit)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
files = Dir.glob(prefix + '/**/*')
|
28
|
+
files.each do |ref|
|
29
|
+
next if !File.file?(ref)
|
30
|
+
id = File.read(ref).chomp
|
31
|
+
name = ref.sub("#{prefix}/", '')
|
32
|
+
commit = Commit.create(repo, :id => id)
|
33
|
+
refs << self.new(name, commit)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
refs
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
def prefix
|
43
|
+
"refs/#{name.to_s.gsub(/^.*::/, '').downcase}s"
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
attr_reader :name
|
49
|
+
attr_reader :commit
|
50
|
+
|
51
|
+
# Instantiate a new Head
|
52
|
+
# +name+ is the name of the head
|
53
|
+
# +commit+ is the Commit that the head points to
|
54
|
+
#
|
55
|
+
# Returns Grit::Head (baked)
|
56
|
+
def initialize(name, commit)
|
57
|
+
@name = name
|
58
|
+
@commit = commit
|
59
|
+
end
|
60
|
+
|
61
|
+
# Pretty object inspection
|
62
|
+
def inspect
|
63
|
+
%Q{#<#{self.class.name} "#{@name}">}
|
64
|
+
end
|
65
|
+
end # Ref
|
66
|
+
|
67
|
+
# A Head is a named reference to a Commit. Every Head instance contains a name
|
68
|
+
# and a Commit object.
|
69
|
+
#
|
70
|
+
# r = Grit::Repo.new("/path/to/repo")
|
71
|
+
# h = r.heads.first
|
72
|
+
# h.name # => "master"
|
73
|
+
# h.commit # => #<Grit::Commit "1c09f116cbc2cb4100fb6935bb162daa4723f455">
|
74
|
+
# h.commit.id # => "1c09f116cbc2cb4100fb6935bb162daa4723f455"
|
75
|
+
class Head < Ref
|
76
|
+
|
77
|
+
# Get the HEAD revision of the repo.
|
78
|
+
# +repo+ is the Repo
|
79
|
+
# +options+ is a Hash of options
|
80
|
+
#
|
81
|
+
# Returns Grit::Head (baked)
|
82
|
+
def self.current(repo, options = {})
|
83
|
+
head = File.open(File.join(repo.path, 'HEAD')).read.chomp
|
84
|
+
if /ref: refs\/heads\/(.*)/.match(head)
|
85
|
+
self.new($1, repo.git.rev_parse(options, 'HEAD'))
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end # Head
|
90
|
+
|
91
|
+
class Tag < Ref ; end
|
92
|
+
|
93
|
+
class Remote < Ref; end
|
94
|
+
|
95
|
+
end # Grit
|
data/lib/grit/repo.rb
CHANGED
@@ -5,6 +5,7 @@ module Grit
|
|
5
5
|
|
6
6
|
# The path of the git repo as a String
|
7
7
|
attr_accessor :path
|
8
|
+
attr_accessor :working_dir
|
8
9
|
attr_reader :bare
|
9
10
|
|
10
11
|
# The git command line interface object
|
@@ -18,13 +19,14 @@ module Grit
|
|
18
19
|
# g = Repo.new("/Users/tom/public/grit.git")
|
19
20
|
#
|
20
21
|
# Returns Grit::Repo
|
21
|
-
def initialize(path)
|
22
|
+
def initialize(path, options = {})
|
22
23
|
epath = File.expand_path(path)
|
23
24
|
|
24
25
|
if File.exist?(File.join(epath, '.git'))
|
26
|
+
self.working_dir = epath
|
25
27
|
self.path = File.join(epath, '.git')
|
26
28
|
@bare = false
|
27
|
-
elsif File.exist?(epath) && epath =~ /\.git$/
|
29
|
+
elsif File.exist?(epath) && (epath =~ /\.git$/ || options[:is_bare])
|
28
30
|
self.path = epath
|
29
31
|
@bare = true
|
30
32
|
elsif File.exist?(epath)
|
@@ -36,6 +38,13 @@ module Grit
|
|
36
38
|
self.git = Git.new(self.path)
|
37
39
|
end
|
38
40
|
|
41
|
+
def self.init(path)
|
42
|
+
# !! TODO !!
|
43
|
+
# create directory
|
44
|
+
# generate initial git directory
|
45
|
+
# create new Grit::Repo on that dir, return it
|
46
|
+
end
|
47
|
+
|
39
48
|
# The project's description. Taken verbatim from GIT_REPO/description
|
40
49
|
#
|
41
50
|
# Returns String
|
@@ -53,6 +62,10 @@ module Grit
|
|
53
62
|
|
54
63
|
alias_method :branches, :heads
|
55
64
|
|
65
|
+
def is_head?(head_name)
|
66
|
+
heads.find { |h| h.name == head_name }
|
67
|
+
end
|
68
|
+
|
56
69
|
# Object reprsenting the current repo head.
|
57
70
|
#
|
58
71
|
# Returns Grit::Head (baked)
|
@@ -60,6 +73,47 @@ module Grit
|
|
60
73
|
Head.current(self)
|
61
74
|
end
|
62
75
|
|
76
|
+
|
77
|
+
# Commits current index
|
78
|
+
#
|
79
|
+
# Returns true/false if commit worked
|
80
|
+
def commit_index(message)
|
81
|
+
self.git.commit({}, '-m', message)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Commits all tracked and modified files
|
85
|
+
#
|
86
|
+
# Returns true/false if commit worked
|
87
|
+
def commit_all(message)
|
88
|
+
self.git.commit({}, '-a', '-m', message)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Adds files to the index
|
92
|
+
def add(*files)
|
93
|
+
self.git.add({}, *files.flatten)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Adds files to the index
|
97
|
+
def remove(*files)
|
98
|
+
self.git.rm({}, *files.flatten)
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
def blame_tree(commit, path = nil)
|
103
|
+
commit_array = self.git.blame_tree(commit, path)
|
104
|
+
|
105
|
+
final_array = {}
|
106
|
+
commit_array.each do |file, sha|
|
107
|
+
final_array[file] = commit(sha)
|
108
|
+
end
|
109
|
+
final_array
|
110
|
+
end
|
111
|
+
|
112
|
+
def status
|
113
|
+
Status.new(self)
|
114
|
+
end
|
115
|
+
|
116
|
+
|
63
117
|
# An array of Tag objects that are available in this repo
|
64
118
|
#
|
65
119
|
# Returns Grit::Tag[] (baked)
|
@@ -67,9 +121,32 @@ module Grit
|
|
67
121
|
Tag.find_all(self)
|
68
122
|
end
|
69
123
|
|
124
|
+
# An array of Remote objects representing the remote branches in
|
125
|
+
# this repo
|
126
|
+
#
|
127
|
+
# Returns Grit::Remote[] (baked)
|
128
|
+
def remotes
|
129
|
+
Remote.find_all(self)
|
130
|
+
end
|
131
|
+
|
132
|
+
# An array of Ref objects representing the refs in
|
133
|
+
# this repo
|
134
|
+
#
|
135
|
+
# Returns Grit::Ref[] (baked)
|
136
|
+
def refs
|
137
|
+
[ Head.find_all(self), Tag.find_all(self), Remote.find_all(self) ].flatten
|
138
|
+
end
|
139
|
+
|
140
|
+
def commit_stats(start = 'master', max_count = 10, skip = 0)
|
141
|
+
options = {:max_count => max_count,
|
142
|
+
:skip => skip}
|
143
|
+
|
144
|
+
CommitStats.find_all(self, start, options)
|
145
|
+
end
|
146
|
+
|
70
147
|
# An array of Commit objects representing the history of a given ref/commit
|
71
148
|
# +start+ is the branch/commit name (default 'master')
|
72
|
-
# +max_count+ is the maximum number of commits to return (default 10)
|
149
|
+
# +max_count+ is the maximum number of commits to return (default 10, use +false+ for all)
|
73
150
|
# +skip+ is the number of commits to skip (default 0)
|
74
151
|
#
|
75
152
|
# Returns Grit::Commit[] (baked)
|
@@ -176,10 +253,10 @@ module Grit
|
|
176
253
|
# Grit::Repo.init_bare('/var/git/myrepo.git')
|
177
254
|
#
|
178
255
|
# Returns Grit::Repo (the newly created repo)
|
179
|
-
def self.init_bare(path,
|
256
|
+
def self.init_bare(path, git_options = {}, repo_options = {})
|
180
257
|
git = Git.new(path)
|
181
|
-
git.init(
|
182
|
-
self.new(path)
|
258
|
+
git.init(git_options)
|
259
|
+
self.new(path, repo_options)
|
183
260
|
end
|
184
261
|
|
185
262
|
# Fork a bare git repository from this repo
|
@@ -235,6 +312,14 @@ module Grit
|
|
235
312
|
options[:prefix] = prefix if prefix
|
236
313
|
self.git.archive(options, treeish, "| gzip")
|
237
314
|
end
|
315
|
+
|
316
|
+
# run archive directly to a file
|
317
|
+
def archive_to_file(treeish = 'master', prefix = nil, filename = 'archive.tar.gz')
|
318
|
+
options = {}
|
319
|
+
options[:prefix] = prefix if prefix
|
320
|
+
self.git.archive(options, treeish, "| gzip > #{filename}")
|
321
|
+
end
|
322
|
+
|
238
323
|
|
239
324
|
# Enable git-daemon serving of this repository by writing the
|
240
325
|
# git-daemon-export-ok file to its git directory
|
@@ -289,6 +374,10 @@ module Grit
|
|
289
374
|
@config ||= Config.new(self)
|
290
375
|
end
|
291
376
|
|
377
|
+
def index
|
378
|
+
Index.new(self)
|
379
|
+
end
|
380
|
+
|
292
381
|
# Pretty object inspection
|
293
382
|
def inspect
|
294
383
|
%Q{#<Grit::Repo "#{@path}">}
|