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,186 @@
|
|
1
|
+
require 'grit/git-ruby/repository'
|
2
|
+
require 'grit/git-ruby/file_index'
|
3
|
+
|
4
|
+
module Grit
|
5
|
+
|
6
|
+
# the functions in this module intercept the calls to git binary
|
7
|
+
# made buy the grit objects and attempts to run them in pure ruby
|
8
|
+
# if it will be faster, or if the git binary is not available (!!TODO!!)
|
9
|
+
module GitRuby
|
10
|
+
|
11
|
+
attr_accessor :ruby_git_repo, :git_file_index
|
12
|
+
|
13
|
+
def init(options)
|
14
|
+
if options.size == 0
|
15
|
+
Grit::GitRuby::Repository.init(@git_dir)
|
16
|
+
else
|
17
|
+
method_missing('init', options)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def cat_file(options, ref)
|
22
|
+
if options[:t]
|
23
|
+
file_type(ref)
|
24
|
+
elsif options[:s]
|
25
|
+
file_size(ref)
|
26
|
+
elsif options[:p]
|
27
|
+
try_run { ruby_git.cat_file(ref) }
|
28
|
+
end
|
29
|
+
rescue Grit::GitRuby::Repository::NoSuchShaFound
|
30
|
+
''
|
31
|
+
end
|
32
|
+
|
33
|
+
# lib/grit/tree.rb:16: output = repo.git.ls_tree({}, treeish, *paths)
|
34
|
+
def ls_tree(options, treeish, *paths)
|
35
|
+
sha = rev_parse({}, treeish)
|
36
|
+
ruby_git.ls_tree(sha, paths.flatten)
|
37
|
+
rescue Grit::GitRuby::Repository::NoSuchShaFound
|
38
|
+
''
|
39
|
+
end
|
40
|
+
|
41
|
+
# git diff --full-index 'ec037431382e83c3e95d4f2b3d145afbac8ea55d' 'f1ec1aea10986159456846b8a05615b87828d6c6'
|
42
|
+
def diff(options, sha1, sha2)
|
43
|
+
try_run { ruby_git.diff(sha1, sha2, options) }
|
44
|
+
end
|
45
|
+
|
46
|
+
def rev_list(options, ref = 'master')
|
47
|
+
options.delete(:skip) if options[:skip].to_i == 0
|
48
|
+
allowed_options = [:max_count, :since, :until, :pretty] # this is all I can do right now
|
49
|
+
if ((options.keys - allowed_options).size > 0)
|
50
|
+
return method_missing('rev-list', options, ref)
|
51
|
+
elsif (options.size == 0)
|
52
|
+
# pure rev-list
|
53
|
+
begin
|
54
|
+
return file_index.commits_from(rev_parse({}, ref)).join("\n") + "\n"
|
55
|
+
rescue
|
56
|
+
return method_missing('rev-list', options, ref)
|
57
|
+
end
|
58
|
+
else
|
59
|
+
aref = rev_parse({}, ref)
|
60
|
+
if aref.is_a? Array
|
61
|
+
return method_missing('rev-list', options, ref)
|
62
|
+
else
|
63
|
+
return try_run { ruby_git.rev_list(aref, options) }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def rev_parse(options, string)
|
69
|
+
raise RuntimeError, "invalid string: #{string}" unless string.is_a?(String)
|
70
|
+
|
71
|
+
if string =~ /\.\./
|
72
|
+
(sha1, sha2) = string.split('..')
|
73
|
+
return [rev_parse({}, sha1), rev_parse({}, sha2)]
|
74
|
+
end
|
75
|
+
|
76
|
+
if /^[0-9a-f]{40}$/.match(string) # passing in a sha - just no-op it
|
77
|
+
return string.chomp
|
78
|
+
end
|
79
|
+
|
80
|
+
head = File.join(@git_dir, 'refs', 'heads', string)
|
81
|
+
return File.read(head).chomp if File.file?(head)
|
82
|
+
|
83
|
+
head = File.join(@git_dir, 'refs', 'remotes', string)
|
84
|
+
return File.read(head).chomp if File.file?(head)
|
85
|
+
|
86
|
+
head = File.join(@git_dir, 'refs', 'tags', string)
|
87
|
+
return File.read(head).chomp if File.file?(head)
|
88
|
+
|
89
|
+
## check packed-refs file, too
|
90
|
+
packref = File.join(@git_dir, 'packed-refs')
|
91
|
+
if File.file?(packref)
|
92
|
+
File.readlines(packref).each do |line|
|
93
|
+
if m = /^(\w{40}) refs\/.+?\/(.*?)$/.match(line)
|
94
|
+
next if !Regexp.new(Regexp.escape(string) + '$').match(m[3])
|
95
|
+
return m[1].chomp
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
## !! more - partials and such !!
|
101
|
+
|
102
|
+
# revert to calling git - grr
|
103
|
+
return method_missing('rev-parse', {}, string).chomp
|
104
|
+
end
|
105
|
+
|
106
|
+
def file_size(ref)
|
107
|
+
try_run { ruby_git.cat_file_size(ref).to_s }
|
108
|
+
end
|
109
|
+
|
110
|
+
def file_type(ref)
|
111
|
+
try_run { ruby_git.cat_file_type(ref).to_s }
|
112
|
+
end
|
113
|
+
|
114
|
+
def blame_tree(commit, path = nil)
|
115
|
+
begin
|
116
|
+
path = [path].join('/').to_s + '/' if (path && path != '')
|
117
|
+
path = '' if !path.is_a? String
|
118
|
+
commits = file_index.last_commits(rev_parse({}, commit), looking_for(commit, path))
|
119
|
+
clean_paths(commits)
|
120
|
+
rescue FileIndex::IndexFileNotFound
|
121
|
+
{}
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def file_index
|
126
|
+
@git_file_index ||= FileIndex.new(@git_dir)
|
127
|
+
end
|
128
|
+
|
129
|
+
def ruby_git
|
130
|
+
@ruby_git_repo ||= Repository.new(@git_dir)
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def try_run
|
136
|
+
ret = ''
|
137
|
+
Timeout.timeout(self.class.git_timeout) do
|
138
|
+
ret = yield
|
139
|
+
end
|
140
|
+
@bytes_read += ret.size
|
141
|
+
|
142
|
+
#if @bytes_read > 5242880 # 5.megabytes
|
143
|
+
# bytes = @bytes_read
|
144
|
+
# @bytes_read = 0
|
145
|
+
# raise Grit::Git::GitTimeout.new(command, bytes)
|
146
|
+
#end
|
147
|
+
|
148
|
+
ret
|
149
|
+
rescue Timeout::Error => e
|
150
|
+
bytes = @bytes_read
|
151
|
+
@bytes_read = 0
|
152
|
+
raise Grit::Git::GitTimeout.new(command, bytes)
|
153
|
+
end
|
154
|
+
|
155
|
+
def looking_for(commit, path = nil)
|
156
|
+
tree_sha = ruby_git.get_subtree(rev_parse({}, commit), path)
|
157
|
+
|
158
|
+
looking_for = []
|
159
|
+
ruby_git.get_object_by_sha1(tree_sha).entry.each do |e|
|
160
|
+
if path && !(path == '' || path == '.' || path == './')
|
161
|
+
file = File.join(path, e.name)
|
162
|
+
else
|
163
|
+
file = e.name
|
164
|
+
end
|
165
|
+
file += '/' if e.type == :directory
|
166
|
+
looking_for << file
|
167
|
+
end
|
168
|
+
looking_for
|
169
|
+
end
|
170
|
+
|
171
|
+
def clean_paths(commit_array)
|
172
|
+
new_commits = {}
|
173
|
+
commit_array.each do |file, sha|
|
174
|
+
file = file.chop if file[file.size - 1 , 1] == '/'
|
175
|
+
new_commits[file] = sha
|
176
|
+
end
|
177
|
+
new_commits
|
178
|
+
end
|
179
|
+
|
180
|
+
# TODO
|
181
|
+
# git grep -n 'foo' 'master'
|
182
|
+
# git log --pretty='raw' --max-count='1' 'master' -- 'LICENSE'
|
183
|
+
# git log --pretty='raw' --max-count='1' 'master' -- 'test'
|
184
|
+
|
185
|
+
end
|
186
|
+
end
|
data/lib/grit/git.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
module Grit
|
2
|
+
|
3
|
+
class Git
|
4
|
+
class GitTimeout < RuntimeError
|
5
|
+
attr_reader :command, :bytes_read
|
6
|
+
|
7
|
+
def initialize(command = nil, bytes_read = nil)
|
8
|
+
@command = command
|
9
|
+
@bytes_read = bytes_read
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
undef_method :clone
|
14
|
+
|
15
|
+
include GitRuby
|
16
|
+
|
17
|
+
class << self
|
18
|
+
attr_accessor :git_binary, :git_timeout, :git_max_size
|
19
|
+
end
|
20
|
+
|
21
|
+
self.git_binary = "/usr/bin/env git"
|
22
|
+
self.git_timeout = 10
|
23
|
+
self.git_max_size = 5242880 # 5.megabytes
|
24
|
+
|
25
|
+
def self.with_timeout(timeout = 10.seconds)
|
26
|
+
old_timeout = Grit::Git.git_timeout
|
27
|
+
Grit::Git.git_timeout = timeout
|
28
|
+
yield
|
29
|
+
Grit::Git.git_timeout = old_timeout
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_accessor :git_dir, :bytes_read
|
33
|
+
|
34
|
+
def initialize(git_dir)
|
35
|
+
self.git_dir = git_dir
|
36
|
+
self.bytes_read = 0
|
37
|
+
end
|
38
|
+
|
39
|
+
def shell_escape(str)
|
40
|
+
str.to_s.gsub("'", "\\\\'").gsub(";", '\\;')
|
41
|
+
end
|
42
|
+
alias_method :e, :shell_escape
|
43
|
+
|
44
|
+
# Run the given git command with the specified arguments and return
|
45
|
+
# the result as a String
|
46
|
+
# +cmd+ is the command
|
47
|
+
# +options+ is a hash of Ruby style options
|
48
|
+
# +args+ is the list of arguments (to be joined by spaces)
|
49
|
+
#
|
50
|
+
# Examples
|
51
|
+
# git.rev_list({:max_count => 10, :header => true}, "master")
|
52
|
+
#
|
53
|
+
# Returns String
|
54
|
+
def method_missing(cmd, options = {}, *args)
|
55
|
+
run('', cmd, '', options, args)
|
56
|
+
end
|
57
|
+
|
58
|
+
def run(prefix, cmd, postfix, options, args)
|
59
|
+
timeout = options.delete(:timeout) rescue nil
|
60
|
+
timeout = true if timeout.nil?
|
61
|
+
|
62
|
+
opt_args = transform_options(options)
|
63
|
+
ext_args = args.reject { |a| a.empty? }.map { |a| (a == '--' || a[0].chr == '|') ? a : "'#{e(a)}'" }
|
64
|
+
|
65
|
+
call = "#{prefix}#{Git.git_binary} --git-dir='#{self.git_dir}' #{cmd.to_s.gsub(/_/, '-')} #{(opt_args + ext_args).join(' ')}#{e(postfix)}"
|
66
|
+
Grit.log(call) if Grit.debug
|
67
|
+
response, err = timeout ? sh(call) : wild_sh(call)
|
68
|
+
Grit.log(response) if Grit.debug
|
69
|
+
Grit.log(err) if Grit.debug
|
70
|
+
response
|
71
|
+
end
|
72
|
+
|
73
|
+
def sh(command)
|
74
|
+
ret, err = '', ''
|
75
|
+
Open3.popen3(command) do |_, stdout, stderr|
|
76
|
+
Timeout.timeout(self.class.git_timeout) do
|
77
|
+
while tmp = stdout.read(1024)
|
78
|
+
ret += tmp
|
79
|
+
if (@bytes_read += tmp.size) > self.class.git_max_size
|
80
|
+
bytes = @bytes_read
|
81
|
+
@bytes_read = 0
|
82
|
+
raise GitTimeout.new(command, bytes)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
while tmp = stderr.read(1024)
|
88
|
+
err += tmp
|
89
|
+
end
|
90
|
+
end
|
91
|
+
[ret, err]
|
92
|
+
rescue Timeout::Error, Grit::Git::GitTimeout
|
93
|
+
bytes = @bytes_read
|
94
|
+
@bytes_read = 0
|
95
|
+
raise GitTimeout.new(command, bytes)
|
96
|
+
end
|
97
|
+
|
98
|
+
def wild_sh(command)
|
99
|
+
ret, err = '', ''
|
100
|
+
Open3.popen3(command) do |_, stdout, stderr|
|
101
|
+
while tmp = stdout.read(1024)
|
102
|
+
ret += tmp
|
103
|
+
end
|
104
|
+
|
105
|
+
while tmp = stderr.read(1024)
|
106
|
+
err += tmp
|
107
|
+
end
|
108
|
+
end
|
109
|
+
[ret, err]
|
110
|
+
end
|
111
|
+
|
112
|
+
# Transform Ruby style options into git command line options
|
113
|
+
# +options+ is a hash of Ruby style options
|
114
|
+
#
|
115
|
+
# Returns String[]
|
116
|
+
# e.g. ["--max-count=10", "--header"]
|
117
|
+
def transform_options(options)
|
118
|
+
args = []
|
119
|
+
options.keys.each do |opt|
|
120
|
+
if opt.to_s.size == 1
|
121
|
+
if options[opt] == true
|
122
|
+
args << "-#{opt}"
|
123
|
+
else
|
124
|
+
val = options.delete(opt)
|
125
|
+
args << "-#{opt.to_s} '#{e(val)}'"
|
126
|
+
end
|
127
|
+
else
|
128
|
+
if options[opt] == true
|
129
|
+
args << "--#{opt.to_s.gsub(/_/, '-')}"
|
130
|
+
else
|
131
|
+
val = options.delete(opt)
|
132
|
+
args << "--#{opt.to_s.gsub(/_/, '-')}='#{e(val)}'"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
args
|
137
|
+
end
|
138
|
+
end # Git
|
139
|
+
|
140
|
+
end # Grit
|
data/lib/grit/index.rb
ADDED
@@ -0,0 +1,122 @@
|
|
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
|
+
# Sets the current tree
|
33
|
+
# +tree+ the branch/tag/sha... to use - a string
|
34
|
+
#
|
35
|
+
# Returns index (self)
|
36
|
+
def read_tree(tree)
|
37
|
+
self.current_tree = self.repo.tree(tree)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Commit the contents of the index
|
41
|
+
# +message+ is the commit message [nil]
|
42
|
+
# +parents+ is one or more commits to attach this commit to to form a new head [nil]
|
43
|
+
# +actor+ is a Grit::Actor of the user making the commit [nil]
|
44
|
+
# +last_tree+ is a tree to compare with - to avoid making empty commits [nil]
|
45
|
+
# +head+ is the branch to write this head to [master]
|
46
|
+
#
|
47
|
+
# Returns a String of the SHA1 of the commit
|
48
|
+
def commit(message, parents = nil, actor = nil, last_tree = nil, head = 'master')
|
49
|
+
tree_sha1 = write_tree(self.tree, self.current_tree)
|
50
|
+
return false if tree_sha1 == last_tree # don't write identical commits
|
51
|
+
|
52
|
+
contents = []
|
53
|
+
contents << ['tree', tree_sha1].join(' ')
|
54
|
+
parents.each do |p|
|
55
|
+
contents << ['parent', p].join(' ') if p
|
56
|
+
end if parents
|
57
|
+
|
58
|
+
if actor
|
59
|
+
name = actor.name
|
60
|
+
email = actor.email
|
61
|
+
else
|
62
|
+
config = Config.new(self.repo)
|
63
|
+
name = config['user.name']
|
64
|
+
email = config['user.email']
|
65
|
+
end
|
66
|
+
|
67
|
+
author_string = "#{name} <#{email}> #{Time.now.to_i} -0700" # !! TODO : gotta fix this
|
68
|
+
contents << ['author', author_string].join(' ')
|
69
|
+
contents << ['committer', author_string].join(' ')
|
70
|
+
contents << ''
|
71
|
+
contents << message
|
72
|
+
|
73
|
+
commit_sha1 = self.repo.git.ruby_git.put_raw_object(contents.join("\n"), 'commit')
|
74
|
+
|
75
|
+
self.repo.update_ref(head, commit_sha1)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Recursively write a tree to the index
|
79
|
+
# +tree+ is the tree
|
80
|
+
#
|
81
|
+
# Returns the SHA1 String of the tree
|
82
|
+
def write_tree(tree, now_tree = nil)
|
83
|
+
tree_contents = {}
|
84
|
+
|
85
|
+
# fill in original tree
|
86
|
+
now_tree.contents.each do |obj|
|
87
|
+
sha = [obj.id].pack("H*")
|
88
|
+
k = obj.name
|
89
|
+
k += '/' if (obj.class == Grit::Tree)
|
90
|
+
tree_contents[k] = "%s %s\0%s" % [obj.mode.to_s, obj.name, sha]
|
91
|
+
end if now_tree
|
92
|
+
|
93
|
+
# overwrite with new tree contents
|
94
|
+
tree.each do |k, v|
|
95
|
+
case v
|
96
|
+
when String
|
97
|
+
sha = write_blob(v)
|
98
|
+
sha = [sha].pack("H*")
|
99
|
+
str = "%s %s\0%s" % ['100644', k, sha]
|
100
|
+
tree_contents[k] = str
|
101
|
+
when Hash
|
102
|
+
ctree = now_tree/k if now_tree
|
103
|
+
sha = write_tree(v, ctree)
|
104
|
+
sha = [sha].pack("H*")
|
105
|
+
str = "%s %s\0%s" % ['040000', k, sha]
|
106
|
+
tree_contents[k + '/'] = str
|
107
|
+
end
|
108
|
+
end
|
109
|
+
tr = tree_contents.sort.map { |k, v| v }.join('')
|
110
|
+
self.repo.git.ruby_git.put_raw_object(tr, 'tree')
|
111
|
+
end
|
112
|
+
|
113
|
+
# Write the blob to the index
|
114
|
+
# +data+ is the data to write
|
115
|
+
#
|
116
|
+
# Returns the SHA1 String of the blob
|
117
|
+
def write_blob(data)
|
118
|
+
self.repo.git.ruby_git.put_raw_object(data, 'blob')
|
119
|
+
end
|
120
|
+
end # Index
|
121
|
+
|
122
|
+
end # Grit
|
data/lib/grit/lazy.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
##
|
2
|
+
# Allows attributes to be declared as lazy, meaning that they won't be
|
3
|
+
# computed until they are asked for.
|
4
|
+
#
|
5
|
+
# Works by delegating each lazy_reader to a cached lazy_source method.
|
6
|
+
#
|
7
|
+
# class Person
|
8
|
+
# lazy_reader :eyes
|
9
|
+
#
|
10
|
+
# def lazy_source
|
11
|
+
# OpenStruct.new(:eyes => 2)
|
12
|
+
# end
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# >> Person.new.eyes
|
16
|
+
# => 2
|
17
|
+
#
|
18
|
+
module Lazy
|
19
|
+
def lazy_reader(*args)
|
20
|
+
args.each do |arg|
|
21
|
+
ivar = "@#{arg}"
|
22
|
+
define_method(arg) do
|
23
|
+
if instance_variable_defined?(ivar)
|
24
|
+
val = instance_variable_get(ivar)
|
25
|
+
return val if val
|
26
|
+
end
|
27
|
+
instance_variable_set(ivar, (@lazy_source ||= lazy_source).send(arg))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
Object.extend Lazy unless Object.ancestors.include? Lazy
|
data/lib/grit/merge.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
module Grit
|
2
|
+
|
3
|
+
class Merge
|
4
|
+
|
5
|
+
STATUS_BOTH = 'both'
|
6
|
+
STATUS_OURS = 'ours'
|
7
|
+
STATUS_THEIRS = 'theirs'
|
8
|
+
|
9
|
+
attr_reader :conflicts, :text, :sections
|
10
|
+
|
11
|
+
def initialize(str)
|
12
|
+
status = STATUS_BOTH
|
13
|
+
|
14
|
+
section = 1
|
15
|
+
@conflicts = 0
|
16
|
+
@text = {}
|
17
|
+
|
18
|
+
lines = str.split("\n")
|
19
|
+
lines.each do |line|
|
20
|
+
if /^<<<<<<< (.*?)/.match(line)
|
21
|
+
status = STATUS_OURS
|
22
|
+
@conflicts += 1
|
23
|
+
section += 1
|
24
|
+
elsif line == '======='
|
25
|
+
status = STATUS_THEIRS
|
26
|
+
elsif /^>>>>>>> (.*?)/.match(line)
|
27
|
+
status = STATUS_BOTH
|
28
|
+
section += 1
|
29
|
+
else
|
30
|
+
@text[section] ||= {}
|
31
|
+
@text[section][status] ||= []
|
32
|
+
@text[section][status] << line
|
33
|
+
end
|
34
|
+
end
|
35
|
+
@text = @text.values
|
36
|
+
@sections = @text.size
|
37
|
+
end
|
38
|
+
|
39
|
+
# Pretty object inspection
|
40
|
+
def inspect
|
41
|
+
%Q{#<Grit::Merge}
|
42
|
+
end
|
43
|
+
end # Merge
|
44
|
+
|
45
|
+
end # Grit
|
data/lib/grit/ref.rb
ADDED
@@ -0,0 +1,99 @@
|
|
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
|
+
already = {}
|
15
|
+
Dir.chdir(repo.path) do
|
16
|
+
files = Dir.glob(prefix + '/**/*')
|
17
|
+
files.each do |ref|
|
18
|
+
next if !File.file?(ref)
|
19
|
+
id = File.read(ref).chomp
|
20
|
+
name = ref.sub("#{prefix}/", '')
|
21
|
+
commit = Commit.create(repo, :id => id)
|
22
|
+
if !already[name]
|
23
|
+
refs << self.new(name, commit)
|
24
|
+
already[name] = true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
if File.file?('packed-refs')
|
29
|
+
File.readlines('packed-refs').each do |line|
|
30
|
+
if m = /^(\w{40}) (.*?)$/.match(line)
|
31
|
+
next if !Regexp.new('^' + prefix).match(m[2])
|
32
|
+
name = m[2].sub("#{prefix}/", '')
|
33
|
+
commit = Commit.create(repo, :id => m[1])
|
34
|
+
if !already[name]
|
35
|
+
refs << self.new(name, commit)
|
36
|
+
already[name] = true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
refs
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
|
48
|
+
def prefix
|
49
|
+
"refs/#{name.to_s.gsub(/^.*::/, '').downcase}s"
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
attr_reader :name
|
55
|
+
attr_reader :commit
|
56
|
+
|
57
|
+
# Instantiate a new Head
|
58
|
+
# +name+ is the name of the head
|
59
|
+
# +commit+ is the Commit that the head points to
|
60
|
+
#
|
61
|
+
# Returns Grit::Head (baked)
|
62
|
+
def initialize(name, commit)
|
63
|
+
@name = name
|
64
|
+
@commit = commit
|
65
|
+
end
|
66
|
+
|
67
|
+
# Pretty object inspection
|
68
|
+
def inspect
|
69
|
+
%Q{#<#{self.class.name} "#{@name}">}
|
70
|
+
end
|
71
|
+
end # Ref
|
72
|
+
|
73
|
+
# A Head is a named reference to a Commit. Every Head instance contains a name
|
74
|
+
# and a Commit object.
|
75
|
+
#
|
76
|
+
# r = Grit::Repo.new("/path/to/repo")
|
77
|
+
# h = r.heads.first
|
78
|
+
# h.name # => "master"
|
79
|
+
# h.commit # => #<Grit::Commit "1c09f116cbc2cb4100fb6935bb162daa4723f455">
|
80
|
+
# h.commit.id # => "1c09f116cbc2cb4100fb6935bb162daa4723f455"
|
81
|
+
class Head < Ref
|
82
|
+
|
83
|
+
# Get the HEAD revision of the repo.
|
84
|
+
# +repo+ is the Repo
|
85
|
+
# +options+ is a Hash of options
|
86
|
+
#
|
87
|
+
# Returns Grit::Head (baked)
|
88
|
+
def self.current(repo, options = {})
|
89
|
+
head = File.open(File.join(repo.path, 'HEAD')).read.chomp
|
90
|
+
if /ref: refs\/heads\/(.*)/.match(head)
|
91
|
+
self.new($1, repo.git.rev_parse(options, 'HEAD'))
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end # Head
|
96
|
+
|
97
|
+
class Remote < Ref; end
|
98
|
+
|
99
|
+
end # Grit
|