boof-grit 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/API.txt +101 -0
- data/History.txt +53 -0
- data/LICENSE +22 -0
- data/README.md +210 -0
- data/VERSION.yml +4 -0
- data/examples/ex_add_commit.rb +13 -0
- data/examples/ex_index.rb +14 -0
- data/lib/grit.rb +68 -0
- data/lib/grit/actor.rb +36 -0
- data/lib/grit/blame.rb +61 -0
- data/lib/grit/blob.rb +126 -0
- data/lib/grit/commit.rb +246 -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.rb +186 -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 +740 -0
- data/lib/grit/git.rb +141 -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 +177 -0
- data/lib/grit/repo.rb +452 -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/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 +35 -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 +207 -0
- data/test/test_commit_stats.rb +33 -0
- data/test/test_commit_write.rb +20 -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 +94 -0
- data/test/test_grit.rb +32 -0
- data/test/test_head.rb +72 -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 +381 -0
- data/test/test_rubygit.rb +192 -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 +103 -0
- data/test/test_tree.rb +101 -0
- metadata +143 -0
data/lib/grit/status.rb
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
module Grit
|
2
|
+
|
3
|
+
class Status
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
@base = nil
|
7
|
+
@files = nil
|
8
|
+
|
9
|
+
def initialize(base)
|
10
|
+
@base = base
|
11
|
+
construct_status
|
12
|
+
end
|
13
|
+
|
14
|
+
def changed
|
15
|
+
@files.select { |k, f| f.type == 'M' }
|
16
|
+
end
|
17
|
+
|
18
|
+
def added
|
19
|
+
@files.select { |k, f| f.type == 'A' }
|
20
|
+
end
|
21
|
+
|
22
|
+
def deleted
|
23
|
+
@files.select { |k, f| f.type == 'D' }
|
24
|
+
end
|
25
|
+
|
26
|
+
def untracked
|
27
|
+
@files.select { |k, f| f.untracked }
|
28
|
+
end
|
29
|
+
|
30
|
+
def pretty
|
31
|
+
out = ''
|
32
|
+
self.each do |file|
|
33
|
+
out << file.path
|
34
|
+
out << "\n\tsha(r) " + file.sha_repo.to_s + ' ' + file.mode_repo.to_s
|
35
|
+
out << "\n\tsha(i) " + file.sha_index.to_s + ' ' + file.mode_index.to_s
|
36
|
+
out << "\n\ttype " + file.type.to_s
|
37
|
+
out << "\n\tstage " + file.stage.to_s
|
38
|
+
out << "\n\tuntrac " + file.untracked.to_s
|
39
|
+
out << "\n"
|
40
|
+
end
|
41
|
+
out << "\n"
|
42
|
+
out
|
43
|
+
end
|
44
|
+
|
45
|
+
# enumerable method
|
46
|
+
|
47
|
+
def [](file)
|
48
|
+
@files[file]
|
49
|
+
end
|
50
|
+
|
51
|
+
def each
|
52
|
+
@files.each do |k, file|
|
53
|
+
yield file
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class StatusFile
|
58
|
+
attr_accessor :path, :type, :stage, :untracked
|
59
|
+
attr_accessor :mode_index, :mode_repo
|
60
|
+
attr_accessor :sha_index, :sha_repo
|
61
|
+
|
62
|
+
@base = nil
|
63
|
+
|
64
|
+
def initialize(base, hash)
|
65
|
+
@base = base
|
66
|
+
@path = hash[:path]
|
67
|
+
@type = hash[:type]
|
68
|
+
@stage = hash[:stage]
|
69
|
+
@mode_index = hash[:mode_index]
|
70
|
+
@mode_repo = hash[:mode_repo]
|
71
|
+
@sha_index = hash[:sha_index]
|
72
|
+
@sha_repo = hash[:sha_repo]
|
73
|
+
@untracked = hash[:untracked]
|
74
|
+
end
|
75
|
+
|
76
|
+
def blob(type = :index)
|
77
|
+
if type == :repo
|
78
|
+
@base.object(@sha_repo)
|
79
|
+
else
|
80
|
+
@base.object(@sha_index) rescue @base.object(@sha_repo)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def construct_status
|
89
|
+
@files = ls_files
|
90
|
+
|
91
|
+
Dir.chdir(@base.working_dir) do
|
92
|
+
# find untracked in working dir
|
93
|
+
Dir.glob('**/*') do |file|
|
94
|
+
if !@files[file]
|
95
|
+
@files[file] = {:path => file, :untracked => true} if !File.directory?(file)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# find modified in tree
|
100
|
+
diff_files.each do |path, data|
|
101
|
+
@files[path] ? @files[path].merge!(data) : @files[path] = data
|
102
|
+
end
|
103
|
+
|
104
|
+
# find added but not committed - new files
|
105
|
+
diff_index('HEAD').each do |path, data|
|
106
|
+
@files[path] ? @files[path].merge!(data) : @files[path] = data
|
107
|
+
end
|
108
|
+
|
109
|
+
@files.each do |k, file_hash|
|
110
|
+
@files[k] = StatusFile.new(@base, file_hash)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# compares the index and the working directory
|
116
|
+
def diff_files
|
117
|
+
hsh = {}
|
118
|
+
@base.git.diff_files.split("\n").each do |line|
|
119
|
+
(info, file) = line.split("\t")
|
120
|
+
(mode_src, mode_dest, sha_src, sha_dest, type) = info.split
|
121
|
+
hsh[file] = {:path => file, :mode_file => mode_src.to_s[1, 7], :mode_index => mode_dest,
|
122
|
+
:sha_file => sha_src, :sha_index => sha_dest, :type => type}
|
123
|
+
end
|
124
|
+
hsh
|
125
|
+
end
|
126
|
+
|
127
|
+
# compares the index and the repository
|
128
|
+
def diff_index(treeish)
|
129
|
+
hsh = {}
|
130
|
+
@base.git.diff_index({}, treeish).split("\n").each do |line|
|
131
|
+
(info, file) = line.split("\t")
|
132
|
+
(mode_src, mode_dest, sha_src, sha_dest, type) = info.split
|
133
|
+
hsh[file] = {:path => file, :mode_repo => mode_src.to_s[1, 7], :mode_index => mode_dest,
|
134
|
+
:sha_repo => sha_src, :sha_index => sha_dest, :type => type}
|
135
|
+
end
|
136
|
+
hsh
|
137
|
+
end
|
138
|
+
|
139
|
+
def ls_files
|
140
|
+
hsh = {}
|
141
|
+
lines = @base.git.ls_files({:stage => true})
|
142
|
+
lines.split("\n").each do |line|
|
143
|
+
(info, file) = line.split("\t")
|
144
|
+
(mode, sha, stage) = info.split
|
145
|
+
hsh[file] = {:path => file, :mode_index => mode, :sha_index => sha, :stage => stage}
|
146
|
+
end
|
147
|
+
hsh
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Grit
|
2
|
+
|
3
|
+
class Submodule
|
4
|
+
attr_reader :id
|
5
|
+
attr_reader :mode
|
6
|
+
attr_reader :name
|
7
|
+
|
8
|
+
# Create a Submodule containing just the specified attributes
|
9
|
+
# +repo+ is the Repo
|
10
|
+
# +atts+ is a Hash of instance variable data
|
11
|
+
#
|
12
|
+
# Returns Grit::Submodule (unbaked)
|
13
|
+
def self.create(repo, atts)
|
14
|
+
self.allocate.create_initialize(repo, atts)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Initializer for Submodule.create
|
18
|
+
# +repo+ is the Repo
|
19
|
+
# +atts+ is a Hash of instance variable data
|
20
|
+
#
|
21
|
+
# Returns Grit::Submodule
|
22
|
+
def create_initialize(repo, atts)
|
23
|
+
@repo = repo
|
24
|
+
atts.each do |k, v|
|
25
|
+
instance_variable_set("@#{k}".to_sym, v)
|
26
|
+
end
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
# The url of this submodule
|
31
|
+
# +ref+ is the committish that should be used to look up the url
|
32
|
+
#
|
33
|
+
# Returns String
|
34
|
+
def url(ref)
|
35
|
+
config = self.class.config(@repo, ref)
|
36
|
+
|
37
|
+
lookup = config.keys.inject({}) do |acc, key|
|
38
|
+
id = config[key]['id']
|
39
|
+
acc[id] = config[key]['url']
|
40
|
+
acc
|
41
|
+
end
|
42
|
+
|
43
|
+
lookup[@id]
|
44
|
+
end
|
45
|
+
|
46
|
+
# The configuration information for the given +repo+
|
47
|
+
# +repo+ is the Repo
|
48
|
+
# +ref+ is the committish (defaults to 'master')
|
49
|
+
#
|
50
|
+
# Returns a Hash of { <path:String> => { 'url' => <url:String>, 'id' => <id:String> } }
|
51
|
+
# Returns {} if no .gitmodules file was found
|
52
|
+
def self.config(repo, ref = "master")
|
53
|
+
commit = repo.commit(ref)
|
54
|
+
blob = commit.tree/'.gitmodules'
|
55
|
+
return {} unless blob
|
56
|
+
|
57
|
+
lines = blob.data.gsub(/\r\n?/, "\n" ).split("\n")
|
58
|
+
|
59
|
+
config = {}
|
60
|
+
current = nil
|
61
|
+
|
62
|
+
lines.each do |line|
|
63
|
+
if line =~ /^\[submodule "(.+)"\]$/
|
64
|
+
current = $1
|
65
|
+
config[current] = {}
|
66
|
+
config[current]['id'] = (commit.tree/current).id
|
67
|
+
elsif line =~ /^\t(\w+) = (.+)$/
|
68
|
+
config[current][$1] = $2
|
69
|
+
config[current]['id'] = (commit.tree/$2).id if $1 == 'path'
|
70
|
+
else
|
71
|
+
# ignore
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
config
|
76
|
+
end
|
77
|
+
|
78
|
+
def basename
|
79
|
+
File.basename(name)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Pretty object inspection
|
83
|
+
def inspect
|
84
|
+
%Q{#<Grit::Submodule "#{@id}">}
|
85
|
+
end
|
86
|
+
end # Submodule
|
87
|
+
|
88
|
+
end # Grit
|
data/lib/grit/tag.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
module Grit
|
2
|
+
|
3
|
+
class Tag < Ref
|
4
|
+
def self.find_all(repo, options = {})
|
5
|
+
refs = []
|
6
|
+
already = {}
|
7
|
+
|
8
|
+
Dir.chdir(repo.path) do
|
9
|
+
files = Dir.glob(prefix + '/**/*')
|
10
|
+
|
11
|
+
files.each do |ref|
|
12
|
+
next if !File.file?(ref)
|
13
|
+
|
14
|
+
id = File.read(ref).chomp
|
15
|
+
name = ref.sub("#{prefix}/", '')
|
16
|
+
commit = commit_from_sha(repo, id)
|
17
|
+
|
18
|
+
if !already[name]
|
19
|
+
refs << self.new(name, commit)
|
20
|
+
already[name] = true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
if File.file?('packed-refs')
|
25
|
+
lines = File.readlines('packed-refs')
|
26
|
+
lines.each_with_index do |line, i|
|
27
|
+
if m = /^(\w{40}) (.*?)$/.match(line)
|
28
|
+
next if !Regexp.new('^' + prefix).match(m[2])
|
29
|
+
name = m[2].sub("#{prefix}/", '')
|
30
|
+
|
31
|
+
# Annotated tags in packed-refs include a reference
|
32
|
+
# to the commit object on the following line.
|
33
|
+
next_line = lines[i+1]
|
34
|
+
if next_line && next_line[0] == ?^
|
35
|
+
commit = Commit.create(repo, :id => next_line[1..-1].chomp)
|
36
|
+
else
|
37
|
+
commit = commit_from_sha(repo, m[1])
|
38
|
+
end
|
39
|
+
|
40
|
+
if !already[name]
|
41
|
+
refs << self.new(name, commit)
|
42
|
+
already[name] = true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
refs
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.commit_from_sha(repo, id)
|
53
|
+
git_ruby_repo = GitRuby::Repository.new(repo.path)
|
54
|
+
object = git_ruby_repo.get_object_by_sha1(id)
|
55
|
+
|
56
|
+
if object.type == :commit
|
57
|
+
Commit.create(repo, :id => id)
|
58
|
+
elsif object.type == :tag
|
59
|
+
Commit.create(repo, :id => object.object)
|
60
|
+
else
|
61
|
+
raise "Unknown object type."
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
data/lib/grit/tree.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
module Grit
|
2
|
+
|
3
|
+
class Tree
|
4
|
+
lazy_reader :contents
|
5
|
+
attr_reader :id
|
6
|
+
attr_reader :mode
|
7
|
+
attr_reader :name
|
8
|
+
|
9
|
+
# Construct the contents of the tree
|
10
|
+
# +repo+ is the Repo
|
11
|
+
# +treeish+ is the reference
|
12
|
+
# +paths+ is an optional Array of directory paths to restrict the tree
|
13
|
+
#
|
14
|
+
# Returns Grit::Tree (baked)
|
15
|
+
def self.construct(repo, treeish, paths = [])
|
16
|
+
output = repo.git.ls_tree({}, treeish, *paths)
|
17
|
+
self.allocate.construct_initialize(repo, treeish, output)
|
18
|
+
end
|
19
|
+
|
20
|
+
def construct_initialize(repo, id, text)
|
21
|
+
@repo = repo
|
22
|
+
@id = id
|
23
|
+
@contents = []
|
24
|
+
|
25
|
+
text.split("\n").each do |line|
|
26
|
+
@contents << content_from_string(repo, line)
|
27
|
+
end
|
28
|
+
@contents.compact!
|
29
|
+
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
def lazy_source
|
34
|
+
Tree.construct(@repo, @id, [])
|
35
|
+
end
|
36
|
+
|
37
|
+
# Create an unbaked Tree containing just the specified attributes
|
38
|
+
# +repo+ is the Repo
|
39
|
+
# +atts+ is a Hash of instance variable data
|
40
|
+
#
|
41
|
+
# Returns Grit::Tree (unbaked)
|
42
|
+
def self.create(repo, atts)
|
43
|
+
self.allocate.create_initialize(repo, atts)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Initializer for Tree.create
|
47
|
+
# +repo+ is the Repo
|
48
|
+
# +atts+ is a Hash of instance variable data
|
49
|
+
#
|
50
|
+
# Returns Grit::Tree (unbaked)
|
51
|
+
def create_initialize(repo, atts)
|
52
|
+
@repo = repo
|
53
|
+
|
54
|
+
atts.each do |k, v|
|
55
|
+
instance_variable_set("@#{k}", v)
|
56
|
+
end
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
# Parse a content item and create the appropriate object
|
61
|
+
# +repo+ is the Repo
|
62
|
+
# +text+ is the single line containing the items data in `git ls-tree` format
|
63
|
+
#
|
64
|
+
# Returns Grit::Blob or Grit::Tree
|
65
|
+
def content_from_string(repo, text)
|
66
|
+
mode, type, id, name = text.split(" ", 4)
|
67
|
+
case type
|
68
|
+
when "tree"
|
69
|
+
Tree.create(repo, :id => id, :mode => mode, :name => name)
|
70
|
+
when "blob"
|
71
|
+
Blob.create(repo, :id => id, :mode => mode, :name => name)
|
72
|
+
when "link"
|
73
|
+
Blob.create(repo, :id => id, :mode => mode, :name => name)
|
74
|
+
when "commit"
|
75
|
+
Submodule.create(repo, :id => id, :mode => mode, :name => name)
|
76
|
+
else
|
77
|
+
raise "Invalid type: #{type}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Find the named object in this tree's contents
|
82
|
+
#
|
83
|
+
# Examples
|
84
|
+
# Repo.new('/path/to/grit').tree/'lib'
|
85
|
+
# # => #<Grit::Tree "6cc23ee138be09ff8c28b07162720018b244e95e">
|
86
|
+
# Repo.new('/path/to/grit').tree/'README.txt'
|
87
|
+
# # => #<Grit::Blob "8b1e02c0fb554eed2ce2ef737a68bb369d7527df">
|
88
|
+
#
|
89
|
+
# Returns Grit::Blob or Grit::Tree or nil if not found
|
90
|
+
def /(file)
|
91
|
+
if file =~ /\//
|
92
|
+
file.split("/").inject(self) { |acc, x| acc/x } rescue nil
|
93
|
+
else
|
94
|
+
self.contents.find { |c| c.name == file }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def basename
|
99
|
+
File.basename(name)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Pretty object inspection
|
103
|
+
def inspect
|
104
|
+
%Q{#<Grit::Tree "#{@id}">}
|
105
|
+
end
|
106
|
+
|
107
|
+
# Find only Tree objects from contents
|
108
|
+
def trees
|
109
|
+
contents.select {|v| v.kind_of? Tree}
|
110
|
+
end
|
111
|
+
|
112
|
+
# Find only Blob objects from contents
|
113
|
+
def blobs
|
114
|
+
contents.select {|v| v.kind_of? Blob}
|
115
|
+
end
|
116
|
+
|
117
|
+
# Compares trees by name
|
118
|
+
def <=>(other)
|
119
|
+
name <=> other.name
|
120
|
+
end
|
121
|
+
end # Tree
|
122
|
+
|
123
|
+
end # Grit
|
data/lib/open3_detach.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
module Open3
|
2
|
+
extend self
|
3
|
+
|
4
|
+
def popen3(*cmd)
|
5
|
+
pw = IO::pipe # pipe[0] for read, pipe[1] for write
|
6
|
+
pr = IO::pipe
|
7
|
+
pe = IO::pipe
|
8
|
+
|
9
|
+
pid = fork{
|
10
|
+
# child
|
11
|
+
fork{
|
12
|
+
# grandchild
|
13
|
+
pw[1].close
|
14
|
+
STDIN.reopen(pw[0])
|
15
|
+
pw[0].close
|
16
|
+
|
17
|
+
pr[0].close
|
18
|
+
STDOUT.reopen(pr[1])
|
19
|
+
pr[1].close
|
20
|
+
|
21
|
+
pe[0].close
|
22
|
+
STDERR.reopen(pe[1])
|
23
|
+
pe[1].close
|
24
|
+
|
25
|
+
exec(*cmd)
|
26
|
+
}
|
27
|
+
exit!(0)
|
28
|
+
}
|
29
|
+
|
30
|
+
pw[0].close
|
31
|
+
pr[1].close
|
32
|
+
pe[1].close
|
33
|
+
Process.waitpid(pid)
|
34
|
+
pi = [pw[1], pr[0], pe[0]]
|
35
|
+
pw[1].sync = true
|
36
|
+
if defined? yield
|
37
|
+
begin
|
38
|
+
return yield(*pi)
|
39
|
+
ensure
|
40
|
+
Process.detach(pid) if pid
|
41
|
+
pi.each { |p| p.close unless p.closed? }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
pi
|
45
|
+
end
|
46
|
+
end
|