multi_git 0.0.1.alpha1
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/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
|