Commit a477d318750f570a02736463194b1f47e3407a28

objectifying all ref-like objects

Commit diff

Manifest.txt

 
11History.txt
2Manifest.txt
3README.txt
4Rakefile
5lib/grit.rb
62lib/grit/actor.rb
73lib/grit/blob.rb
84lib/grit/commit.rb
5lib/grit/config.rb
96lib/grit/diff.rb
107lib/grit/errors.rb
118lib/grit/git.rb
12lib/grit/head.rb
139lib/grit/lazy.rb
10lib/grit/ref.rb
1411lib/grit/repo.rb
1512lib/grit/tree.rb
13lib/grit.rb
14Rakefile
15README.txt
1616test/fixtures/blame
1717test/fixtures/cat_file_blob
1818test/fixtures/cat_file_blob_size
19test/fixtures/diff_2
20test/fixtures/diff_2f
21test/fixtures/diff_f
22test/fixtures/diff_i
23test/fixtures/diff_mode_only
24test/fixtures/diff_new_mode
1925test/fixtures/diff_p
2026test/fixtures/for_each_ref
27test/fixtures/for_each_ref_remotes
28test/fixtures/for_each_ref_tags
2129test/fixtures/ls_tree_a
2230test/fixtures/ls_tree_b
31test/fixtures/ls_tree_commit
2332test/fixtures/rev_list
33test/fixtures/rev_list_count
2434test/fixtures/rev_list_single
2535test/fixtures/rev_parse
36test/fixtures/show_empty_commit
37test/fixtures/simple_config
2638test/helper.rb
2739test/profile.rb
2840test/suite.rb
2941test/test_actor.rb
3042test/test_blob.rb
3143test/test_commit.rb
44test/test_config.rb
45test/test_diff.rb
3246test/test_git.rb
3347test/test_head.rb
48test/test_real.rb
3449test/test_reality.rb
50test/test_remote.rb
3551test/test_repo.rb
36test/test_tree.rb
52test/test_tag.rb
53test/test_tree.rb
toggle raw diff

lib/grit.rb

 
1616require 'grit/lazy'
1717require 'grit/errors'
1818require 'grit/git'
19require 'grit/head'
20require 'grit/tag'
19require 'grit/ref'
2120require 'grit/commit'
2221require 'grit/tree'
2322require 'grit/blob'
2929 class << self
3030 attr_accessor :debug
3131 end
32
32
3333 self.debug = false
34
34
3535 VERSION = '0.7.0'
3636end
toggle raw diff

lib/grit/head.rb

 
0module Grit
1 HEAD_PREFIX = 'refs/heads'
2
3 # A Head is a named reference to a Commit. Every Head instance contains a name
4 # and a Commit object.
5 #
6 # r = Grit::Repo.new("/path/to/repo")
7 # h = r.heads.first
8 # h.name # => "master"
9 # h.commit # => #<Grit::Commit "1c09f116cbc2cb4100fb6935bb162daa4723f455">
10 # h.commit.id # => "1c09f116cbc2cb4100fb6935bb162daa4723f455"
11 class Head
12 attr_reader :name
13 attr_reader :commit
14
15 # Instantiate a new Head
16 # +name+ is the name of the head
17 # +commit+ is the Commit that the head points to
18 #
19 # Returns Grit::Head (baked)
20 def initialize(name, commit)
21 @name = name
22 @commit = commit
23 end
24
25 # Find all Heads
26 # +repo+ is the Repo
27 # +options+ is a Hash of options
28 #
29 # Returns Grit::Head[] (baked)
30 def self.find_all(repo, options = {})
31 default_options = {:sort => "committerdate",
32 :format => "%(refname)%00%(objectname)"}
33
34 actual_options = default_options.merge(options)
35
36 output = repo.git.for_each_ref(actual_options, HEAD_PREFIX)
37
38 self.list_from_string(repo, output)
39 end
40
41 # Get the HEAD revision of the repo.
42 # +repo+ is the Repo
43 # +options+ is a Hash of options
44 #
45 # Returns Grit::Head (baked)
46 def self.current(repo, options = {})
47 head = File.open(File.join(repo.path, 'HEAD')).read.chomp
48 if /ref: refs\/heads\/(.*)/.match(head)
49 self.new($1, repo.git.rev_parse(options, 'HEAD'))
50 end
51 end
52
53 # Parse out head information into an array of baked head objects
54 # +repo+ is the Repo
55 # +text+ is the text output from the git command
56 #
57 # Returns Grit::Head[] (baked)
58 def self.list_from_string(repo, text)
59 heads = []
60
61 text.split("\n").each do |line|
62 heads << self.from_string(repo, line)
63 end
64
65 heads
66 end
67
68 # Create a new Head instance from the given string.
69 # +repo+ is the Repo
70 # +line+ is the formatted head information
71 #
72 # Format
73 # name: [a-zA-Z_/]+
74 # <null byte>
75 # id: [0-9A-Fa-f]{40}
76 #
77 # Returns Grit::Head (baked)
78 def self.from_string(repo, line)
79 full_name, id = line.split("\0")
80 name = full_name.sub("#{HEAD_PREFIX}/", '')
81 commit = Commit.create(repo, :id => id)
82 self.new(name, commit)
83 end
84
85 # Pretty object inspection
86 def inspect
87 %Q{#<Grit::Head "#{@name}">}
88 end
89 end # Head
90
91end # Grit
toggle raw diff

lib/grit/ref.rb

 
1module 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 default_options = {:sort => "committerdate",
14 :format => "%(refname)%00%(objectname)"}
15
16 actual_options = default_options.merge(options)
17
18 output = repo.git.for_each_ref(actual_options, prefix)
19
20 self.list_from_string(repo, output)
21 end
22
23 # Parse out ref information into an array of baked refs objects
24 # +repo+ is the Repo
25 # +text+ is the text output from the git command
26 #
27 # Returns Grit::Ref[] (baked)
28 def list_from_string(repo, text)
29 refs = []
30
31 text.split("\n").each do |line|
32 refs << self.from_string(repo, line)
33 end
34
35 refs
36 end
37
38 # Create a new Ref instance from the given string.
39 # +repo+ is the Repo
40 # +line+ is the formatted head information
41 #
42 # Format
43 # name: [a-zA-Z_/]+
44 # <null byte>
45 # id: [0-9A-Fa-f]{40}
46 #
47 # Returns Grit::Ref (baked)
48 def from_string(repo, line)
49 full_name, id = line.split("\0")
50 name = full_name.sub("#{prefix}/", '')
51 commit = Commit.create(repo, :id => id)
52 self.new(name, commit)
53 end
54
55 protected
56
57 def prefix
58 "refs/#{name.to_s.gsub(/^.*::/, '').downcase}s"
59 end
60
61 end
62
63 attr_reader :name
64 attr_reader :commit
65
66 # Instantiate a new Head
67 # +name+ is the name of the head
68 # +commit+ is the Commit that the head points to
69 #
70 # Returns Grit::Head (baked)
71 def initialize(name, commit)
72 @name = name
73 @commit = commit
74 end
75
76 # Pretty object inspection
77 def inspect
78 %Q{#<#{self.class.name} "#{@name}">}
79 end
80 end # Ref
81
82 # A Head is a named reference to a Commit. Every Head instance contains a name
83 # and a Commit object.
84 #
85 # r = Grit::Repo.new("/path/to/repo")
86 # h = r.heads.first
87 # h.name # => "master"
88 # h.commit # => #<Grit::Commit "1c09f116cbc2cb4100fb6935bb162daa4723f455">
89 # h.commit.id # => "1c09f116cbc2cb4100fb6935bb162daa4723f455"
90 class Head < Ref
91
92 # Get the HEAD revision of the repo.
93 # +repo+ is the Repo
94 # +options+ is a Hash of options
95 #
96 # Returns Grit::Head (baked)
97 def self.current(repo, options = {})
98 head = File.open(File.join(repo.path, 'HEAD')).read.chomp
99 if /ref: refs\/heads\/(.*)/.match(head)
100 self.new($1, repo.git.rev_parse(options, 'HEAD'))
101 end
102 end
103
104 end # Head
105
106 class Tag < Ref ; end
107
108 class Remote < Ref ; end
109
110end # Grit
toggle raw diff

lib/grit/repo.rb

 
11module Grit
2
2
33 class Repo
44 DAEMON_EXPORT_FILE = 'git-daemon-export-ok'
5
5
66 # The path of the git repo as a String
77 attr_accessor :path
88 attr_reader :bare
9
9
1010 # The git command line interface object
1111 attr_accessor :git
12
12
1313 # Create a new Repo instance
1414 # +path+ is the path to either the root git directory or the bare git repo
1515 #
2020 # Returns Grit::Repo
2121 def initialize(path)
2222 epath = File.expand_path(path)
23
23
2424 if File.exist?(File.join(epath, '.git'))
2525 self.path = File.join(epath, '.git')
2626 @bare = false
3232 else
3333 raise NoSuchPathError.new(epath)
3434 end
35
35
3636 self.git = Git.new(self.path)
3737 end
38
38
3939 # The project's description. Taken verbatim from GIT_REPO/description
4040 #
4141 # Returns String
4242 def description
4343 File.open(File.join(self.path, 'description')).read.chomp
4444 end
45
45
46 # An array of Ref objects representing the refs in
47 # this repo
48 #
49 # Returns Grit::Ref[] (baked)
50 def refs
51 [ Head.find_all(self), Tag.find_all(self), Remote.find_all(self) ].flatten
52 end
53
4654 # An array of Head objects representing the branch heads in
4755 # this repo
4856 #
5858 def heads
5959 Head.find_all(self)
6060 end
61
61
6262 alias_method :branches, :heads
6363
6464 # Object reprsenting the current repo head.
7474 def tags
7575 Tag.find_all(self)
7676 end
77
77
78 # An array of Remote objects representing the remote branches in
79 # this repo
80 #
81 # Returns Grit::Remote[] (baked)
82 def remotes
83 Remote.find_all(self)
84 end
85
7886 # An array of Commit objects representing the history of a given ref/commit
7987 # +start+ is the branch/commit name (default 'master')
8088 # +max_count+ is the maximum number of commits to return (default 10)
9292 def commits(start = 'master', max_count = 10, skip = 0)
9393 options = {:max_count => max_count,
9494 :skip => skip}
95
95
9696 Commit.find_all(self, start, options)
9797 end
98
98
9999 # The Commits objects that are reachable via +to+ but not via +from+
100100 # Commits are returned in chronological order.
101101 # +from+ is the branch/commit name of the younger item
105105 def commits_between(from, to)
106106 Commit.find_all(self, "#{from}..#{to}").reverse
107107 end
108
108
109109 # The Commits objects that are newer than the specified date.
110110 # Commits are returned in chronological order.
111111 # +start+ is the branch/commit name (default 'master')
115115 # Returns Grit::Commit[] (baked)
116116 def commits_since(start = 'master', since = '1970-01-01', extra_options = {})
117117 options = {:since => since}.merge(extra_options)
118
118
119119 Commit.find_all(self, start, options)
120120 end
121
121
122122 # The number of commits reachable by the given branch/commit
123123 # +start+ is the branch/commit name (default 'master')
124124 #
126126 def commit_count(start = 'master')
127127 Commit.count(self, start)
128128 end
129
129
130130 # The Commit object for the specified id
131131 # +id+ is the SHA1 identifier of the commit
132132 #
133133 # Returns Grit::Commit (baked)
134134 def commit(id)
135135 options = {:max_count => 1}
136
136
137137 Commit.find_all(self, id, options).first
138138 end
139
139
140140 # The Tree object for the given treeish reference
141141 # +treeish+ is the reference (default 'master')
142142 # +paths+ is an optional Array of directory paths to restrict the tree (deafult [])
148148 def tree(treeish = 'master', paths = [])
149149 Tree.construct(self, treeish, paths)
150150 end
151
151
152152 # The Blob object for the given id
153153 # +id+ is the SHA1 id of the blob
154154 #
167167 commits = self.git.log(actual_options, *arg)
168168 Commit.list_from_string(self, commits)
169169 end
170
170
171171 # The diff from commit +a+ to commit +b+, optionally restricted to the given file(s)
172172 # +a+ is the base commit
173173 # +b+ is the other commit
175175 def diff(a, b, *paths)
176176 self.git.diff({}, a, b, '--', *paths)
177177 end
178
178
179179 # The commit diff for the given commit
180180 # +commit+ is the commit name/id
181181 #
183183 def commit_diff(commit)
184184 Commit.diff(self, commit)
185185 end
186
186
187187 # Initialize a bare git repository at the given path
188188 # +path+ is the full path to the repo (traditionally ends with /<name>.git)
189189 # +options+ is any additional options to the git init command
197197 git.init(options)
198198 self.new(path)
199199 end
200
200
201201 # Fork a bare git repository from this repo
202202 # +path+ is the full path of the new repo (traditionally ends with /<name>.git)
203203 # +options+ is any additional options to the git clone command
209209 self.git.clone(real_options, self.path, path)
210210 Repo.new(path)
211211 end
212
212
213213 # Archive the given treeish
214214 # +treeish+ is the treeish name/id (default 'master')
215215 # +prefix+ is the optional prefix
230230 options[:prefix] = prefix if prefix
231231 self.git.archive(options, treeish)
232232 end
233
233
234234 # Archive and gzip the given treeish
235235 # +treeish+ is the treeish name/id (default 'master')
236236 # +prefix+ is the optional prefix
251251 options[:prefix] = prefix if prefix
252252 self.git.archive(options, treeish, "| gzip")
253253 end
254
254
255255 # Enable git-daemon serving of this repository by writing the
256256 # git-daemon-export-ok file to its git directory
257257 #
259259 def enable_daemon_serve
260260 FileUtils.touch(File.join(self.path, DAEMON_EXPORT_FILE))
261261 end
262
262
263263 # Disable git-daemon serving of this repository by ensuring there is no
264264 # git-daemon-export-ok file in its git directory
265265 #
267267 def disable_daemon_serve
268268 FileUtils.rm_f(File.join(self.path, DAEMON_EXPORT_FILE))
269269 end
270
270
271271 # The list of alternates for this repo
272272 #
273273 # Returns Array[String] (pathnames of alternates)
274274 def alternates
275275 alternates_path = File.join(self.path, *%w{objects info alternates})
276
276
277277 if File.exist?(alternates_path)
278278 File.read(alternates_path).strip.split("\n")
279279 else
280280 []
281281 end
282282 end
283
283
284284 # Sets the alternates
285285 # +alts+ is the Array of String paths representing the alternates
286286 #
291291 raise "Could not set alternates. Alternate path #{alt} must exist"
292292 end
293293 end
294
294
295295 if alts.empty?
296296 File.delete(File.join(self.path, *%w{objects info alternates}))
297297 else
300300 end
301301 end
302302 end
303
303
304304 def config
305305 @config ||= Config.new(self)
306306 end
307
307
308308 # Pretty object inspection
309309 def inspect
310310 %Q{#<Grit::Repo "#{@path}">}
311311 end
312312 end # Repo
313
313
314314end # Grit
toggle raw diff

lib/grit/tag.rb

 
0module Grit
1
2 class Tag
3 attr_reader :name
4 attr_reader :commit
5
6 # Instantiate a new Tag
7 # +name+ is the name of the head
8 # +commit+ is the Commit that the head points to
9 #
10 # Returns Grit::Tag (baked)
11 def initialize(name, commit)
12 @name = name
13 @commit = commit
14 end
15
16 # Find all Tags
17 # +repo+ is the Repo
18 # +options+ is a Hash of options
19 #
20 # Returns Grit::Tag[] (baked)
21 def self.find_all(repo, options = {})
22 default_options = {:sort => "committerdate",
23 :format => "%(refname)%00%(objectname)"}
24
25 actual_options = default_options.merge(options)
26
27 output = repo.git.for_each_ref(actual_options, "refs/tags")
28
29 self.list_from_string(repo, output)
30 end
31
32 # Parse out tag information into an array of baked Tag objects
33 # +repo+ is the Repo
34 # +text+ is the text output from the git command
35 #
36 # Returns Grit::Tag[] (baked)
37 def self.list_from_string(repo, text)
38 tags = []
39
40 text.split("\n").each do |line|
41 tags << self.from_string(repo, line)
42 end
43
44 tags
45 end
46
47 # Create a new Tag instance from the given string.
48 # +repo+ is the Repo
49 # +line+ is the formatted tag information
50 #
51 # Format
52 # name: [a-zA-Z_/]+
53 # <null byte>
54 # id: [0-9A-Fa-f]{40}
55 #
56 # Returns Grit::Tag (baked)
57 def self.from_string(repo, line)
58 full_name, id = line.split("\0")
59 name = full_name.split("/").last
60 commit = Commit.create(repo, :id => id)
61 self.new(name, commit)
62 end
63
64 # Pretty object inspection
65 def inspect
66 %Q{#<Grit::Tag "#{@name}">}
67 end
68 end # Tag
69
70end # Grit
toggle raw diff

test/fixtures/for_each_ref_remotes

 
toggle raw diff

test/test_remote.rb

 
1require File.dirname(__FILE__) + '/helper'
2
3class TestRemote < Test::Unit::TestCase
4 def setup
5 @r = Repo.new(GRIT_REPO)
6 Git.any_instance.expects(:for_each_ref).returns(fixture('for_each_ref_remotes'))
7 end
8
9 # inspect
10
11 def test_inspect
12 remote = @r.remotes.first
13 assert_equal %Q{#<Grit::Remote "#{remote.name}">}, remote.inspect
14 end
15end
toggle raw diff

test/test_repo.rb

 
44 def setup
55 @r = Repo.new(GRIT_REPO)
66 end
7
7
88 # new
9
9
1010 def test_new_should_raise_on_invalid_repo_location
1111 assert_raise(InvalidGitRepositoryError) do
1212 Repo.new("/tmp")
1313 end
1414 end
15
15
1616 def test_new_should_raise_on_non_existant_path
1717 assert_raise(NoSuchPathError) do
1818 Repo.new("/foobar")
1919 end
2020 end
21
21
2222 # descriptions
23
23
2424 def test_description
2525 assert_equal "Unnamed repository; edit this file to name it for gitweb.", @r.description
2626 end
27
27
28 # refs
29
30 def test_refs_should_return_array_of_ref_objects
31 @r.refs.each do |ref|
32 assert ref.is_a? Grit::Ref
33 end
34 end
35
2836 # heads
29
37
3038 def test_heads_should_return_array_of_head_objects
3139 @r.heads.each do |head|
3240 assert_equal Grit::Head, head.class
3341 end
3442 end
35
43
3644 def test_heads_should_populate_head_data
3745 Git.any_instance.expects(:for_each_ref).returns(fixture('for_each_ref'))
38
46
3947 head = @r.heads.firs