multi_git 0.0.1.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/multi_git/backend.rb +100 -0
- data/lib/multi_git/backend_set.rb +42 -0
- data/lib/multi_git/blob.rb +52 -0
- data/lib/multi_git/builder.rb +19 -0
- data/lib/multi_git/commit.rb +185 -0
- data/lib/multi_git/directory.rb +67 -0
- data/lib/multi_git/error.rb +67 -0
- data/lib/multi_git/executeable.rb +12 -0
- data/lib/multi_git/file.rb +35 -0
- data/lib/multi_git/git_backend/blob.rb +11 -0
- data/lib/multi_git/git_backend/cmd.rb +117 -0
- data/lib/multi_git/git_backend/commit.rb +75 -0
- data/lib/multi_git/git_backend/object.rb +34 -0
- data/lib/multi_git/git_backend/ref.rb +36 -0
- data/lib/multi_git/git_backend/repository.rb +162 -0
- data/lib/multi_git/git_backend/tree.rb +22 -0
- data/lib/multi_git/git_backend.rb +19 -0
- data/lib/multi_git/handle.rb +33 -0
- data/lib/multi_git/jgit_backend/blob.rb +10 -0
- data/lib/multi_git/jgit_backend/commit.rb +45 -0
- data/lib/multi_git/jgit_backend/object.rb +48 -0
- data/lib/multi_git/jgit_backend/ref.rb +117 -0
- data/lib/multi_git/jgit_backend/repository.rb +223 -0
- data/lib/multi_git/jgit_backend/rewindeable_io.rb +33 -0
- data/lib/multi_git/jgit_backend/tree.rb +28 -0
- data/lib/multi_git/jgit_backend.rb +15 -0
- data/lib/multi_git/object.rb +59 -0
- data/lib/multi_git/ref.rb +381 -0
- data/lib/multi_git/repository.rb +190 -0
- data/lib/multi_git/rugged_backend/blob.rb +8 -0
- data/lib/multi_git/rugged_backend/commit.rb +38 -0
- data/lib/multi_git/rugged_backend/object.rb +38 -0
- data/lib/multi_git/rugged_backend/ref.rb +32 -0
- data/lib/multi_git/rugged_backend/repository.rb +160 -0
- data/lib/multi_git/rugged_backend/tree.rb +18 -0
- data/lib/multi_git/rugged_backend.rb +16 -0
- data/lib/multi_git/submodule.rb +7 -0
- data/lib/multi_git/symlink.rb +42 -0
- data/lib/multi_git/tree/builder.rb +184 -0
- data/lib/multi_git/tree.rb +144 -0
- data/lib/multi_git/tree_entry.rb +86 -0
- data/lib/multi_git/utils.rb +57 -0
- data/lib/multi_git/version.rb +3 -0
- data/lib/multi_git.rb +44 -0
- metadata +138 -0
@@ -0,0 +1,160 @@
|
|
1
|
+
require 'multi_git/tree_entry'
|
2
|
+
require 'multi_git/repository'
|
3
|
+
require 'multi_git/rugged_backend/blob'
|
4
|
+
require 'multi_git/rugged_backend/tree'
|
5
|
+
require 'multi_git/rugged_backend/commit'
|
6
|
+
require 'multi_git/rugged_backend/ref'
|
7
|
+
module MultiGit::RuggedBackend
|
8
|
+
|
9
|
+
class Repository < MultiGit::Repository
|
10
|
+
|
11
|
+
extend Forwardable
|
12
|
+
|
13
|
+
private
|
14
|
+
OBJECT_CLASSES = {
|
15
|
+
:blob => Blob,
|
16
|
+
:tree => Tree,
|
17
|
+
:commit => Commit
|
18
|
+
}
|
19
|
+
|
20
|
+
public
|
21
|
+
|
22
|
+
# {include:MultiGit::Repository#bare?}
|
23
|
+
delegate "bare?" => "@git"
|
24
|
+
|
25
|
+
# {include:MultiGit::Repository#git_dir}
|
26
|
+
def git_dir
|
27
|
+
strip_slash @git.path
|
28
|
+
end
|
29
|
+
|
30
|
+
def git_work_tree
|
31
|
+
strip_slash @git.workdir
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize(path, options = {})
|
35
|
+
options = initialize_options(path,options)
|
36
|
+
begin
|
37
|
+
@git = Rugged::Repository.new(options[:repository])
|
38
|
+
if options[:working_directory]
|
39
|
+
@git.workdir = options[:working_directory]
|
40
|
+
end
|
41
|
+
rescue Rugged::RepositoryError, Rugged::OSError
|
42
|
+
if options[:init]
|
43
|
+
@git = Rugged::Repository.init_at(path, !!options[:bare])
|
44
|
+
else
|
45
|
+
raise MultiGit::Error::NotARepository, path
|
46
|
+
end
|
47
|
+
end
|
48
|
+
verify_bareness(path, options)
|
49
|
+
end
|
50
|
+
|
51
|
+
# {include:MultiGit::Repository#write}
|
52
|
+
# @param (see MultiGit::Repository#write)
|
53
|
+
# @raise (see MultiGit::Repository#write)
|
54
|
+
# @return (see MultiGit::Repository#write)
|
55
|
+
def write(content, type = :blob)
|
56
|
+
if content.kind_of? MultiGit::Builder
|
57
|
+
return content >> self
|
58
|
+
end
|
59
|
+
validate_type(type)
|
60
|
+
if content.kind_of? MultiGit::Object
|
61
|
+
if include?(content.oid)
|
62
|
+
return read(content.oid)
|
63
|
+
end
|
64
|
+
content = content.to_io
|
65
|
+
end
|
66
|
+
#if content.respond_to? :path
|
67
|
+
# file duck-type
|
68
|
+
# oid = @git.hash_file(content.path, type)
|
69
|
+
# return OBJECT_CLASSES[type].new(@git, oid)
|
70
|
+
#els
|
71
|
+
if content.respond_to? :read
|
72
|
+
# IO duck-type
|
73
|
+
content = content.read
|
74
|
+
end
|
75
|
+
oid = @git.write(content.to_s, type)
|
76
|
+
return OBJECT_CLASSES[type].new(self, oid)
|
77
|
+
end
|
78
|
+
|
79
|
+
# {include:MultiGit::Repository#read}
|
80
|
+
# @param (see MultiGit::Repository#read)
|
81
|
+
# @raise (see MultiGit::Repository#read)
|
82
|
+
# @return (see MultiGit::Repository#read)
|
83
|
+
def read(ref)
|
84
|
+
oid = parse(ref)
|
85
|
+
object = @git.lookup(oid)
|
86
|
+
return OBJECT_CLASSES[object.type].new(self, oid, object)
|
87
|
+
end
|
88
|
+
|
89
|
+
def ref(ref)
|
90
|
+
Ref.new(self, ref)
|
91
|
+
end
|
92
|
+
|
93
|
+
# {include:MultiGit::Repository#parse}
|
94
|
+
# @param (see MultiGit::Repository#parse)
|
95
|
+
# @raise (see MultiGit::Repository#parse)
|
96
|
+
# @return (see MultiGit::Repository#parse)
|
97
|
+
def parse(oidish)
|
98
|
+
begin
|
99
|
+
return Rugged::Object.rev_parse_oid(@git, oidish)
|
100
|
+
rescue Rugged::ReferenceError => e
|
101
|
+
raise MultiGit::Error::InvalidReference, e
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# {include:MultiGit::Repository#include?}
|
106
|
+
# @param (see MultiGit::Repository#include?)
|
107
|
+
# @raise (see MultiGit::Repository#include?)
|
108
|
+
# @return (see MultiGit::Repository#include?)
|
109
|
+
def include?(oid)
|
110
|
+
@git.include?(oid)
|
111
|
+
end
|
112
|
+
|
113
|
+
# @api private
|
114
|
+
# @visibility private
|
115
|
+
def __backend__
|
116
|
+
@git
|
117
|
+
end
|
118
|
+
|
119
|
+
# @api private
|
120
|
+
# @visibility private
|
121
|
+
def make_tree(entries)
|
122
|
+
builder = Rugged::Tree::Builder.new
|
123
|
+
entries.each do |name, mode, oid|
|
124
|
+
builder << { name: name, oid: oid, filemode: mode}
|
125
|
+
end
|
126
|
+
oid = builder.write(@git)
|
127
|
+
return read(oid)
|
128
|
+
end
|
129
|
+
|
130
|
+
# @api private
|
131
|
+
# @visibility private
|
132
|
+
def make_commit(options)
|
133
|
+
rugged_options = {
|
134
|
+
tree: options[:tree],
|
135
|
+
message: options[:message],
|
136
|
+
parents: options[:parents],
|
137
|
+
author: {
|
138
|
+
name: options[:author].name,
|
139
|
+
email: options[:author].email,
|
140
|
+
time: options[:time]
|
141
|
+
},
|
142
|
+
committer: {
|
143
|
+
name: options[:committer].name,
|
144
|
+
email: options[:committer].email,
|
145
|
+
time: options[:commit_time]
|
146
|
+
}
|
147
|
+
}
|
148
|
+
oid = Rugged::Commit.create(@git, rugged_options)
|
149
|
+
return read(oid)
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
|
154
|
+
def strip_slash(path)
|
155
|
+
return nil if path.nil?
|
156
|
+
return path[0..-2]
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'multi_git/tree'
|
2
|
+
require 'multi_git/rugged_backend/object'
|
3
|
+
module MultiGit::RuggedBackend
|
4
|
+
class Tree < Object
|
5
|
+
include MultiGit::Tree
|
6
|
+
|
7
|
+
def size
|
8
|
+
rugged_object.count
|
9
|
+
end
|
10
|
+
|
11
|
+
def raw_entries
|
12
|
+
@raw_entries ||= rugged_object.map do |entry|
|
13
|
+
[entry[:name], entry[:filemode], entry[:oid]]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'multi_git/tree_entry'
|
2
|
+
module MultiGit
|
3
|
+
|
4
|
+
class Symlink < TreeEntry
|
5
|
+
|
6
|
+
module Base
|
7
|
+
|
8
|
+
def target
|
9
|
+
object.content
|
10
|
+
end
|
11
|
+
|
12
|
+
def resolve
|
13
|
+
parent.traverse(target, :follow => :path)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Builder < TreeEntry::Builder
|
18
|
+
|
19
|
+
include Base
|
20
|
+
|
21
|
+
def make_inner(*args)
|
22
|
+
if args.any?
|
23
|
+
if args[0].kind_of? Blob::Builder
|
24
|
+
return args[0]
|
25
|
+
elsif args[0].kind_of? Blob
|
26
|
+
return args[0].to_builder
|
27
|
+
end
|
28
|
+
end
|
29
|
+
Blob::Builder.new(*args)
|
30
|
+
end
|
31
|
+
|
32
|
+
def target=(t)
|
33
|
+
object.content = t
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
include Base
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'multi_git/tree'
|
3
|
+
require 'multi_git/builder'
|
4
|
+
module MultiGit
|
5
|
+
class Tree::Builder
|
6
|
+
|
7
|
+
include MultiGit::Builder
|
8
|
+
include Tree::Base
|
9
|
+
|
10
|
+
attr :entries
|
11
|
+
|
12
|
+
def initialize(from = nil, &block)
|
13
|
+
@entries = {}
|
14
|
+
@from = from
|
15
|
+
instance_eval(&block) if block
|
16
|
+
end
|
17
|
+
|
18
|
+
def entry(key)
|
19
|
+
if @from
|
20
|
+
@entries.fetch(key) do
|
21
|
+
e = @from.entry(key)
|
22
|
+
if e
|
23
|
+
@entries[key] = e.to_builder.with_parent(self)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
else
|
27
|
+
@entries[key]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def each
|
32
|
+
return to_enum unless block_given?
|
33
|
+
names.each do |name|
|
34
|
+
yield entry(name)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# TODO: cache
|
39
|
+
def names
|
40
|
+
names = @from ? @from.names.dup : []
|
41
|
+
@entries.each do |k,v|
|
42
|
+
if v
|
43
|
+
unless names.include? k
|
44
|
+
names << k
|
45
|
+
end
|
46
|
+
else
|
47
|
+
names.delete(k)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
return names
|
51
|
+
end
|
52
|
+
|
53
|
+
def size
|
54
|
+
names.size
|
55
|
+
end
|
56
|
+
|
57
|
+
def >>(repository)
|
58
|
+
ent = []
|
59
|
+
@entries.each do |name, entry|
|
60
|
+
if entry
|
61
|
+
object = repository.write(entry)
|
62
|
+
ent << [name, object.mode, object.oid]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
if @from
|
66
|
+
@from.each do |entry|
|
67
|
+
unless @entries.key? entry.name
|
68
|
+
ent << [entry.name, entry.mode, entry.oid]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
return repository.make_tree(ent)
|
73
|
+
end
|
74
|
+
|
75
|
+
module DSL
|
76
|
+
|
77
|
+
def set(key, *args, &block)
|
78
|
+
case(args.size)
|
79
|
+
when 0
|
80
|
+
raise ArgumentError, "Expected a value or a block" unless block
|
81
|
+
value = block
|
82
|
+
when 1
|
83
|
+
if block
|
84
|
+
options = args[0]
|
85
|
+
value = block
|
86
|
+
else
|
87
|
+
value = args[0]
|
88
|
+
end
|
89
|
+
when 2
|
90
|
+
raise ArgumentError, "Expected either a value or a block, got both" if block
|
91
|
+
options = args[0]
|
92
|
+
value = args[1]
|
93
|
+
else
|
94
|
+
raise ArgumentError, "Expected 1-3 arguments, got #{args.size}"
|
95
|
+
end
|
96
|
+
# okay, a bit simple here for now
|
97
|
+
parts = key.split('/').reject{|k| k == '' || k == '.' }
|
98
|
+
if parts.any?{|p| p == ".." }
|
99
|
+
raise MultiGit::Error::InvalidTraversal, "Traversal to parent directories is currently not supported while setting."
|
100
|
+
end
|
101
|
+
return traverse_set( self, parts, value, true)
|
102
|
+
end
|
103
|
+
|
104
|
+
alias []= set
|
105
|
+
|
106
|
+
def entry_set(key, value)
|
107
|
+
entries[key] = make_entry(key, value)
|
108
|
+
end
|
109
|
+
|
110
|
+
def make_entry(key, value)
|
111
|
+
if value.kind_of? Proc
|
112
|
+
value = value.call(self, key)
|
113
|
+
end
|
114
|
+
if value.nil?
|
115
|
+
return value
|
116
|
+
elsif value.kind_of? String
|
117
|
+
return MultiGit::File::Builder.new(self, key, value)
|
118
|
+
elsif value.kind_of? MultiGit::Builder
|
119
|
+
return value.with_parent(self)
|
120
|
+
else
|
121
|
+
raise ArgumentError, "No idea what to do with #{value.inspect}"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def traverse_set(current, parts, value, create)
|
126
|
+
if parts.none?
|
127
|
+
raise
|
128
|
+
end
|
129
|
+
if parts.size == 1
|
130
|
+
current.entry_set(parts[0], value)
|
131
|
+
return current
|
132
|
+
end
|
133
|
+
part, *rest = parts
|
134
|
+
if !current.respond_to? :entry
|
135
|
+
raise MultiGit::Error::InvalidTraversal, "Can't traverse to #{path} from #{self.inspect}: #{current.inspect} doesn't contain an entry named #{part.inspect}"
|
136
|
+
end
|
137
|
+
entry = current.entry(part)
|
138
|
+
if !entry.kind_of? MultiGit::Directory::Builder
|
139
|
+
# fine
|
140
|
+
if entry.kind_of? MultiGit::Tree
|
141
|
+
entry = entry.to_builder
|
142
|
+
elsif create == :overwrite || ( entry.nil? && create )
|
143
|
+
entry = MultiGit::Directory::Builder.new(current, part)
|
144
|
+
else
|
145
|
+
raise MultiGit::Error::InvalidTraversal, "Can't traverse to #{path} from #{self.inspect}: #{current.inspect} doesn't contain an entry named #{part.inspect}" unless entry
|
146
|
+
end
|
147
|
+
end
|
148
|
+
current.entry_set(part, traverse_set(entry, rest, value, create))
|
149
|
+
return current
|
150
|
+
end
|
151
|
+
|
152
|
+
def file(name, content = nil, &block)
|
153
|
+
set(name){|parent, name|
|
154
|
+
File::Builder.new(parent, name, content, &block)
|
155
|
+
}
|
156
|
+
end
|
157
|
+
|
158
|
+
def directory(name, &block)
|
159
|
+
set(name){|parent, name|
|
160
|
+
Directory::Builder.new(parent, name, &block)
|
161
|
+
}
|
162
|
+
end
|
163
|
+
|
164
|
+
def link(name, target)
|
165
|
+
set(name){|parent, name|
|
166
|
+
Symlink::Builder.new(parent, name, target)
|
167
|
+
}
|
168
|
+
end
|
169
|
+
|
170
|
+
def delete(name)
|
171
|
+
set(name){ nil }
|
172
|
+
end
|
173
|
+
|
174
|
+
def to_builder
|
175
|
+
self
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|
179
|
+
|
180
|
+
include DSL
|
181
|
+
|
182
|
+
end
|
183
|
+
end
|
184
|
+
require 'multi_git/directory'
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'multi_git/object'
|
2
|
+
require 'forwardable'
|
3
|
+
module MultiGit
|
4
|
+
module Tree
|
5
|
+
|
6
|
+
# @visibility protected
|
7
|
+
SLASH = '/'.freeze
|
8
|
+
|
9
|
+
module Base
|
10
|
+
|
11
|
+
include Enumerable
|
12
|
+
|
13
|
+
def type
|
14
|
+
:tree
|
15
|
+
end
|
16
|
+
|
17
|
+
def parent?
|
18
|
+
false
|
19
|
+
end
|
20
|
+
|
21
|
+
def key?(key)
|
22
|
+
if key.kind_of? String
|
23
|
+
return entries.key?(key)
|
24
|
+
else
|
25
|
+
raise ArgumentError, "Expected a String, got #{key.inspect}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param [String] key
|
30
|
+
# @return [MultiGit::TreeEntry, nil]
|
31
|
+
def entry(key)
|
32
|
+
entries[key]
|
33
|
+
end
|
34
|
+
|
35
|
+
# Traverses to path
|
36
|
+
# @param [String] path
|
37
|
+
# @param [Hash] options
|
38
|
+
# @option options [Boolean] :follow follow sylinks ( default: true )
|
39
|
+
# @raise [MultiGit::Error::InvalidTraversal] if the path is not reacheable
|
40
|
+
# @raise [MultiGit::Error::CyclicSymlink] if a cyclic symlink is found
|
41
|
+
# @return [MultiGit::TreeEntry]
|
42
|
+
def traverse(path, options = {})
|
43
|
+
unless path.kind_of? String
|
44
|
+
raise ArgumentError, "Expected a String, got #{path.inspect}"
|
45
|
+
end
|
46
|
+
parts = path.split('/').reverse!
|
47
|
+
current = self
|
48
|
+
follow = options.fetch(:follow){true}
|
49
|
+
symlinks = Set.new
|
50
|
+
while parts.any?
|
51
|
+
part = parts.pop
|
52
|
+
if part == '..'
|
53
|
+
unless current.parent?
|
54
|
+
raise MultiGit::Error::InvalidTraversal, "Can't traverse to parent of #{current.inspect} since I don't know where it is."
|
55
|
+
end
|
56
|
+
current = current.parent
|
57
|
+
elsif part == '.' || part == ''
|
58
|
+
# do nothing
|
59
|
+
else
|
60
|
+
if !current.respond_to? :entry
|
61
|
+
raise MultiGit::Error::InvalidTraversal, "Can't traverse to #{path} from #{self.inspect}: #{current.inspect} doesn't contain an entry named #{part.inspect}"
|
62
|
+
end
|
63
|
+
entry = current.entry(part)
|
64
|
+
raise MultiGit::Error::InvalidTraversal, "Can't traverse to #{path} from #{self.inspect}: #{current.inspect} doesn't contain an entry named #{part.inspect}" unless entry
|
65
|
+
# may be a symlink
|
66
|
+
if entry.respond_to? :target
|
67
|
+
# this is a symlink
|
68
|
+
if symlinks.include? entry
|
69
|
+
# We have already seen this symlink
|
70
|
+
#TODO: it's okay to see a symlink twice if requested
|
71
|
+
raise MultiGit::Error::CyclicSymlink, "Cyclic symlink detected while traversing #{path} from #{self.inspect}."
|
72
|
+
else
|
73
|
+
symlinks << entry
|
74
|
+
end
|
75
|
+
if follow
|
76
|
+
parts.push(*entry.target.split(SLASH))
|
77
|
+
else
|
78
|
+
if parts.none?
|
79
|
+
return entry
|
80
|
+
else
|
81
|
+
raise ArgumentError, "Can't follow symlink #{entry.inspect} since you didn't allow me to"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
else
|
85
|
+
current = entry
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
return current
|
90
|
+
end
|
91
|
+
|
92
|
+
alias / traverse
|
93
|
+
alias [] traverse
|
94
|
+
|
95
|
+
# @yield [MultiGit::TreeEntry]
|
96
|
+
def each
|
97
|
+
return to_enum unless block_given?
|
98
|
+
entries.each do |name, entry|
|
99
|
+
yield entry
|
100
|
+
end
|
101
|
+
return self
|
102
|
+
end
|
103
|
+
|
104
|
+
# @return [Integer] number of entries
|
105
|
+
def size
|
106
|
+
entries.size
|
107
|
+
end
|
108
|
+
|
109
|
+
# @return [Array<String>] names of all entries
|
110
|
+
def names
|
111
|
+
entries.keys
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
include Base
|
117
|
+
include Object
|
118
|
+
|
119
|
+
def to_builder
|
120
|
+
Builder.new(self)
|
121
|
+
end
|
122
|
+
|
123
|
+
# @visibility private
|
124
|
+
def inspect
|
125
|
+
['#<',self.class.name,' ',oid,' repository:', repository.inspect,'>'].join
|
126
|
+
end
|
127
|
+
|
128
|
+
protected
|
129
|
+
# @return [Hash<String, MultiGit::TreeEntry>]
|
130
|
+
def entries
|
131
|
+
@entries ||= Hash[ raw_entries.map{|name, mode, oid| [name, make_entry(name, mode, oid) ] } ]
|
132
|
+
end
|
133
|
+
|
134
|
+
def raw_entries
|
135
|
+
raise Error::NotYetImplemented, "#{self.class}#each_entry"
|
136
|
+
end
|
137
|
+
|
138
|
+
def make_entry(name, mode, oid)
|
139
|
+
repository.read_entry(self, name,mode,oid)
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
end
|
144
|
+
require 'multi_git/tree/builder'
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'multi_git/utils'
|
2
|
+
require 'multi_git/object'
|
3
|
+
require 'multi_git/builder'
|
4
|
+
module MultiGit
|
5
|
+
|
6
|
+
base = Class.new
|
7
|
+
|
8
|
+
# @!parse
|
9
|
+
# class TreeEntry < TreeEntry::Base
|
10
|
+
# end
|
11
|
+
class TreeEntry < base
|
12
|
+
Base = superclass
|
13
|
+
end
|
14
|
+
|
15
|
+
# A tree entry is like a {MultiGit::Object} or a {MultiGit::Builder} but it
|
16
|
+
# also has knows it's parent tree.
|
17
|
+
class TreeEntry
|
18
|
+
|
19
|
+
class Base
|
20
|
+
|
21
|
+
# @return [String]
|
22
|
+
attr :name
|
23
|
+
# @return [MultiGit::Tree::Base]
|
24
|
+
attr :parent
|
25
|
+
|
26
|
+
extend MultiGit::Utils::AbstractMethods
|
27
|
+
|
28
|
+
# @!method mode
|
29
|
+
# @abstract
|
30
|
+
# @return [Integer] the git-internal entry mode
|
31
|
+
abstract :mode
|
32
|
+
|
33
|
+
# @visibility private
|
34
|
+
def initialize(parent, name, inner)
|
35
|
+
@parent = parent
|
36
|
+
@name = name
|
37
|
+
@object = inner
|
38
|
+
end
|
39
|
+
|
40
|
+
# @param [MultiGit::Object] parent
|
41
|
+
# @return [MultiGit::TreeEntry]
|
42
|
+
def with_parent(parent, name = self.name)
|
43
|
+
self.class.new(parent, name, @object)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
class Builder < Base
|
49
|
+
|
50
|
+
include MultiGit::Builder
|
51
|
+
|
52
|
+
# @return [MultiGit::Builder]
|
53
|
+
attr :object
|
54
|
+
|
55
|
+
# {include:MultiGit::Builder#>>}
|
56
|
+
# @param (see MultiGit::Builder#>>)
|
57
|
+
# @return [MultiGit::TreeEntry]
|
58
|
+
def >>(repository)
|
59
|
+
result = object >> repository
|
60
|
+
return repository.read_entry(parent, name, mode, result.oid)
|
61
|
+
end
|
62
|
+
|
63
|
+
# @visibility private
|
64
|
+
def initialize(parent, name, *args, &block)
|
65
|
+
super( parent, name , make_inner(*args) )
|
66
|
+
instance_eval(&block) if block
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
# @return [MultiGit::Object]
|
72
|
+
attr :object
|
73
|
+
|
74
|
+
include MultiGit::Object
|
75
|
+
|
76
|
+
extend Forwardable
|
77
|
+
|
78
|
+
delegate (MultiGit::Object.instance_methods - ::Object.instance_methods) => :object
|
79
|
+
|
80
|
+
def to_builder
|
81
|
+
self.class::Builder.new(parent, name, object)
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module MultiGit
|
2
|
+
module Utils
|
3
|
+
|
4
|
+
module AbstractMethods
|
5
|
+
|
6
|
+
def abstract(name)
|
7
|
+
class_eval <<RUBY
|
8
|
+
def #{name}(*args, &block)
|
9
|
+
raise NotImplementedError, "Please implement #{name} for \#{self}"
|
10
|
+
end
|
11
|
+
RUBY
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
NULL_OID = '0'*40
|
17
|
+
|
18
|
+
MODE_SYMLINK = 0120000
|
19
|
+
MODE_SUBMODULE = 0160000
|
20
|
+
MODE_DIRECTORY = 0040000
|
21
|
+
MODE_FILE = 0100644
|
22
|
+
MODE_EXECUTEABLE = 0100755
|
23
|
+
|
24
|
+
MODE_TYPES = {
|
25
|
+
MODE_SYMLINK => :blob,
|
26
|
+
MODE_SUBMODULE => :commit,
|
27
|
+
MODE_DIRECTORY => :tree,
|
28
|
+
MODE_FILE => :blob,
|
29
|
+
MODE_EXECUTEABLE => :blob
|
30
|
+
}
|
31
|
+
|
32
|
+
# @api private
|
33
|
+
DOTS = { '.' => true, '..' => true }
|
34
|
+
|
35
|
+
def empty_dir?(path)
|
36
|
+
Dir.new(path).reject{|path| DOTS[path] }.none?
|
37
|
+
end
|
38
|
+
|
39
|
+
# A
|
40
|
+
def looks_bare?(path)
|
41
|
+
return nil unless ::File.exists?(path)
|
42
|
+
return !::File.exists?(::File.join(path,'.git')) &&
|
43
|
+
::File.exists?(::File.join(path,'refs'))
|
44
|
+
end
|
45
|
+
|
46
|
+
# @api private
|
47
|
+
def file_loadeable?(file)
|
48
|
+
$LOAD_PATH.any?{|path| File.exists?( File.join(path, file) ) }
|
49
|
+
end
|
50
|
+
|
51
|
+
def type_from_mode(mode)
|
52
|
+
MODE_TYPES.fetch(mode.to_i){ raise "Unknown file mode #{mode}" }
|
53
|
+
end
|
54
|
+
|
55
|
+
extend self
|
56
|
+
end
|
57
|
+
end
|