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.
Files changed (45) hide show
  1. data/lib/multi_git/backend.rb +100 -0
  2. data/lib/multi_git/backend_set.rb +42 -0
  3. data/lib/multi_git/blob.rb +52 -0
  4. data/lib/multi_git/builder.rb +19 -0
  5. data/lib/multi_git/commit.rb +185 -0
  6. data/lib/multi_git/directory.rb +67 -0
  7. data/lib/multi_git/error.rb +67 -0
  8. data/lib/multi_git/executeable.rb +12 -0
  9. data/lib/multi_git/file.rb +35 -0
  10. data/lib/multi_git/git_backend/blob.rb +11 -0
  11. data/lib/multi_git/git_backend/cmd.rb +117 -0
  12. data/lib/multi_git/git_backend/commit.rb +75 -0
  13. data/lib/multi_git/git_backend/object.rb +34 -0
  14. data/lib/multi_git/git_backend/ref.rb +36 -0
  15. data/lib/multi_git/git_backend/repository.rb +162 -0
  16. data/lib/multi_git/git_backend/tree.rb +22 -0
  17. data/lib/multi_git/git_backend.rb +19 -0
  18. data/lib/multi_git/handle.rb +33 -0
  19. data/lib/multi_git/jgit_backend/blob.rb +10 -0
  20. data/lib/multi_git/jgit_backend/commit.rb +45 -0
  21. data/lib/multi_git/jgit_backend/object.rb +48 -0
  22. data/lib/multi_git/jgit_backend/ref.rb +117 -0
  23. data/lib/multi_git/jgit_backend/repository.rb +223 -0
  24. data/lib/multi_git/jgit_backend/rewindeable_io.rb +33 -0
  25. data/lib/multi_git/jgit_backend/tree.rb +28 -0
  26. data/lib/multi_git/jgit_backend.rb +15 -0
  27. data/lib/multi_git/object.rb +59 -0
  28. data/lib/multi_git/ref.rb +381 -0
  29. data/lib/multi_git/repository.rb +190 -0
  30. data/lib/multi_git/rugged_backend/blob.rb +8 -0
  31. data/lib/multi_git/rugged_backend/commit.rb +38 -0
  32. data/lib/multi_git/rugged_backend/object.rb +38 -0
  33. data/lib/multi_git/rugged_backend/ref.rb +32 -0
  34. data/lib/multi_git/rugged_backend/repository.rb +160 -0
  35. data/lib/multi_git/rugged_backend/tree.rb +18 -0
  36. data/lib/multi_git/rugged_backend.rb +16 -0
  37. data/lib/multi_git/submodule.rb +7 -0
  38. data/lib/multi_git/symlink.rb +42 -0
  39. data/lib/multi_git/tree/builder.rb +184 -0
  40. data/lib/multi_git/tree.rb +144 -0
  41. data/lib/multi_git/tree_entry.rb +86 -0
  42. data/lib/multi_git/utils.rb +57 -0
  43. data/lib/multi_git/version.rb +3 -0
  44. data/lib/multi_git.rb +44 -0
  45. metadata +138 -0
@@ -0,0 +1,100 @@
1
+ require 'multi_git/utils'
2
+ require 'multi_git/error'
3
+ module MultiGit
4
+
5
+ module Backend
6
+
7
+ attr :failed, :exception
8
+
9
+ def check(description, &check)
10
+ @checks ||= []
11
+ @checks << [description, check]
12
+ end
13
+
14
+ def check!
15
+ @checks.each do |description, check|
16
+ begin
17
+ result = check.call
18
+ if result == false
19
+ @failed = description
20
+ return
21
+ end
22
+ rescue Exception => e
23
+ @failed = description
24
+ @exception = e
25
+ end
26
+ end
27
+ end
28
+
29
+ private :check, :check!
30
+
31
+ # @abstract
32
+ #
33
+ # This method implements loading the backend files.
34
+ def load!
35
+ raise NotImplementedError, "Please implement #load! for #{self}"
36
+ end
37
+
38
+ # Opens a git repository.
39
+ #
40
+ # @return [Repository]
41
+ def open(directory, options = {})
42
+ load!
43
+ open(directory, options)
44
+ end
45
+
46
+ # Tests whether this backend is available.
47
+ def available?
48
+ check!
49
+ @failed.nil?
50
+ end
51
+
52
+ end
53
+
54
+ module RuggedBackend
55
+
56
+ extend Backend
57
+
58
+ check "Rugged available" do
59
+ defined?(::Rugged) ||
60
+ Utils.file_loadeable?('rugged.rb')
61
+ end
62
+
63
+ def self.load!
64
+ require 'multi_git/rugged_backend'
65
+ end
66
+
67
+ end
68
+
69
+ module JGitBackend
70
+
71
+ extend Backend
72
+
73
+ check "Using Jruby" do
74
+ RUBY_ENGINE == "jruby"
75
+ end
76
+
77
+ check "Jgit available" do
78
+ defined?(Java::OrgEclipseJgitLib::Repository)
79
+ end
80
+
81
+ def self.load!
82
+ require 'multi_git/jgit_backend'
83
+ end
84
+
85
+ end
86
+
87
+ module GitBackend
88
+
89
+ extend Backend
90
+
91
+ check "Git Executeable available" do
92
+ `git --version`
93
+ end
94
+
95
+ def self.load!
96
+ require 'multi_git/git_backend'
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,42 @@
1
+ require 'multi_git/backend'
2
+ module MultiGit
3
+
4
+ # @visibility private
5
+ # @api private
6
+ class BackendSet
7
+
8
+ def initialize
9
+ @backends = {}
10
+ @priorities = {}
11
+ end
12
+
13
+ def []=(name, options={}, value)
14
+ raise ArgumentError, "Expected a MultiGit::Backend, got #{value.inspect}" unless value.respond_to? :available?
15
+ @priorities[name] = options.fetch(:priority, 0)
16
+ @backends[name] = value
17
+ end
18
+
19
+ def [](name)
20
+ if name.kind_of? Backend
21
+ return name
22
+ elsif name == :best
23
+ return best
24
+ else
25
+ return @backends[name]
26
+ end
27
+ end
28
+
29
+ def priority(name)
30
+ @priorities[name]
31
+ end
32
+
33
+ def best
34
+ @priorities.sort_by{|_,value| -value}.find do |name, _|
35
+ be = self[name]
36
+ return be if be.available?
37
+ end
38
+ end
39
+
40
+ end
41
+
42
+ end
@@ -0,0 +1,52 @@
1
+ require 'stringio'
2
+ require 'multi_git/object'
3
+ require 'multi_git/builder'
4
+ module MultiGit
5
+ module Blob
6
+
7
+ module Base
8
+ def type
9
+ :blob
10
+ end
11
+ end
12
+
13
+ class Builder < StringIO
14
+ include Base
15
+ include MultiGit::Builder
16
+
17
+ def initialize(content = nil)
18
+ super()
19
+ if content.kind_of? Blob
20
+ self << content.content
21
+ elsif content.kind_of? String
22
+ self << content
23
+ elsif content.kind_of? IO
24
+ IO.copy_stream(content, self)
25
+ elsif content
26
+ raise ArgumentError
27
+ end
28
+ end
29
+
30
+ def content
31
+ string
32
+ end
33
+
34
+ def content=(value)
35
+ self.string = value.dup
36
+ end
37
+
38
+ def >>(repo)
39
+ rewind
40
+ return repo.write(read)
41
+ end
42
+ end
43
+
44
+ include Object
45
+ include Base
46
+
47
+ def to_builder
48
+ Builder.new(self)
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,19 @@
1
+ require 'multi_git/utils'
2
+ # Builders provides simple interfaces to create complex structures like trees and commits.
3
+ module MultiGit::Builder
4
+
5
+ extend MultiGit::Utils::AbstractMethods
6
+
7
+ # @!method >>(repository)
8
+ # @abstract
9
+ # Writes the content of this builder to the given repository.
10
+ # @param [Repository] repository
11
+ # @return [MultiGit::Object] the persisted object
12
+ abstract :>>
13
+
14
+ # @return [self] self
15
+ def to_builder
16
+ self
17
+ end
18
+
19
+ end
@@ -0,0 +1,185 @@
1
+ require 'forwardable'
2
+ require 'multi_git/utils'
3
+ require 'multi_git/handle'
4
+ require 'multi_git/builder'
5
+ module MultiGit
6
+
7
+ module Commit
8
+
9
+ module Base
10
+ extend Utils::AbstractMethods
11
+ extend Forwardable
12
+
13
+ # @return String
14
+ abstract :message
15
+
16
+ # @return Tree
17
+ abstract :tree
18
+
19
+ # @return [Array<Commit>]
20
+ abstract :parents
21
+
22
+ # @return [Time]
23
+ abstract :time
24
+ # @return [Handle]
25
+ abstract :author
26
+
27
+ # @return [Time]
28
+ abstract :commit_time
29
+ # @return [Handle]
30
+ abstract :committer
31
+
32
+ delegate [:[], :/] => :tree
33
+
34
+ def type
35
+ :commit
36
+ end
37
+ end
38
+
39
+ # A commit builder helps creating new commits by
40
+ # providing a simple interface, sane defaults and some
41
+ # validations.
42
+ #
43
+ # You can create a new commit using the commit builder like this:
44
+ #
45
+ # @example Commit an small tree
46
+ # #setup:
47
+ # dir = `mktemp -d`
48
+ # # example:
49
+ # builder = MultiGit::Commit::Builder.new
50
+ # builder.message = "My first commit"
51
+ # builder.by "me@example.com"
52
+ # builder.tree["a_file"] = "some content"
53
+ # # builder is now ready to be inserted
54
+ # repository = MultiGit.open(dir, init: true)
55
+ # commit = repository << builder #=> be_a MultiGit::Commit
56
+ # commit.tree['a_file'].content #=> eql "some content"
57
+ # # teardown:
58
+ # `rm -rf #{dir}`
59
+ class Builder
60
+ include MultiGit::Builder
61
+ include Base
62
+
63
+ # @return (see MultiGit::Commit::Base#message)
64
+ def message(*args)
65
+ if args.any?
66
+ self.message = args.first
67
+ return self
68
+ else
69
+ return @message
70
+ end
71
+ end
72
+
73
+ #
74
+ # @yield allows
75
+ # @return [Tree::Builder]
76
+ def tree(&block)
77
+ @tree.instance_eval(&block) if block
78
+ @tree
79
+ end
80
+ # @return [Array<Commit::Base>]
81
+ attr :parents
82
+
83
+ # @return (see MultiGit::Commit::Base#time)
84
+ attr :time
85
+ # @return (see MultiGit::Commit::Base#commit_time)
86
+ attr :commit_time
87
+
88
+ # @return (see MultiGit::Commit::Base#author)
89
+ attr :author
90
+ # @return (see MultiGit::Commit::Base#committer)
91
+ attr :committer
92
+
93
+ attr_writer :author, :committer
94
+ attr_writer :message
95
+
96
+ # @param time [Time]
97
+ def time=(time)
98
+ raise ArgumentError, "Expected a Time, got #{time.inspect}" unless time.kind_of? Time
99
+ @time = time
100
+ end
101
+
102
+ # @param time [Time]
103
+ def commit_time=(time)
104
+ raise ArgumentError, "Expected a Time, got #{time.inspect}" unless time.kind_of? Time
105
+ @commit_time = time
106
+ end
107
+
108
+ # @param handle [Handle, String]
109
+ def committer=(handle)
110
+ @committer = parse_handle(handle)
111
+ end
112
+
113
+ # @param handle [Handle, String]
114
+ def author=(handle)
115
+ @author = parse_handle(handle)
116
+ end
117
+
118
+ # DSL method to set author and committer in one step
119
+ # @param handle [Handle, String]
120
+ # @return self
121
+ def by(handle)
122
+ self.author = self.committer = handle
123
+ return self
124
+ end
125
+
126
+ def at(time)
127
+ self.time = self.commit_time = time
128
+ return self
129
+ end
130
+
131
+ def initialize(from = nil, &block)
132
+ @parents = []
133
+ if from.kind_of? Tree
134
+ @tree = from.to_builder
135
+ elsif from.kind_of? Tree::Builder
136
+ @tree = from
137
+ elsif from.kind_of? Commit
138
+ @tree = from.tree.to_builder
139
+ @parents << from
140
+ elsif from.nil?
141
+ @tree = Tree::Builder.new
142
+ end
143
+ @author = nil
144
+ @committer = nil
145
+ @time = @commit_time = Time.now
146
+ instance_eval(&block) if block
147
+ end
148
+
149
+ def >>(repo)
150
+ new_tree = repo << tree
151
+ new_parents = parents.map{|p| repo.write(p).oid }
152
+ c = committer || author || Handle::DEFAULT
153
+ a = author || committer || Handle::DEFAULT
154
+ return repo.make_commit(
155
+ :time => time,
156
+ :author => a,
157
+ :commit_time => commit_time,
158
+ :committer => c,
159
+ :parents => new_parents,
160
+ :tree => new_tree.oid,
161
+ :message => message || "Thank you for using multi_git",
162
+ :update_ref => []
163
+ )
164
+ end
165
+
166
+ protected
167
+
168
+ def parse_handle(handle)
169
+ case(handle)
170
+ when Handle then
171
+ return handle
172
+ when String then
173
+ return Handle.parse(handle)
174
+ else
175
+ raise ArgumentError, "Expected a String or a Handle, got #{handle.inspect}"
176
+ end
177
+ end
178
+
179
+ end
180
+
181
+ include Base
182
+
183
+ end
184
+ end
185
+
@@ -0,0 +1,67 @@
1
+ require 'multi_git/tree'
2
+ require 'multi_git/tree_entry'
3
+ module MultiGit
4
+
5
+ class Directory < TreeEntry
6
+
7
+ module Base
8
+ include Tree::Base
9
+ def mode
10
+ Utils::MODE_DIRECTORY
11
+ end
12
+
13
+ def parent?
14
+ !@parent.nil?
15
+ end
16
+
17
+ def size
18
+ object.size
19
+ end
20
+
21
+ def entry(key)
22
+ e = object.entry(key)
23
+ e.with_parent(self) if e
24
+ end
25
+
26
+ end
27
+
28
+ class Builder < TreeEntry::Builder
29
+ include Tree::Builder::DSL
30
+ include Base
31
+
32
+ def make_inner(*args)
33
+ if args.any?
34
+ if args[0].kind_of?(Tree::Builder)
35
+ return args[0]
36
+ elsif args[0].kind_of?(Directory)
37
+ return args[0].object.to_builder
38
+ elsif args[0].kind_of?(Tree)
39
+ return args[0].to_builder
40
+ end
41
+ end
42
+ Tree::Builder.new(*args)
43
+ end
44
+
45
+ def entry_set(key, value)
46
+ object.entry_set(key, make_entry(key, value))
47
+ end
48
+
49
+ def entries
50
+ Hash[
51
+ object.map{|entry| [entry.name, entry.with_parent(self) ] }
52
+ ]
53
+ end
54
+
55
+ end
56
+
57
+ include Base
58
+
59
+ def entries
60
+ @entries ||= Hash[
61
+ object.map{|entry| [entry.name, entry.with_parent(self) ] }
62
+ ]
63
+ end
64
+
65
+ end
66
+
67
+ end
@@ -0,0 +1,67 @@
1
+ module MultiGit
2
+
3
+ module Error
4
+
5
+ class NotARepository < ArgumentError
6
+ include Error
7
+ end
8
+
9
+ class RepositoryNotBare < NotARepository
10
+ include Error
11
+ end
12
+
13
+ class RepositoryBare < NotARepository
14
+ include Error
15
+ end
16
+
17
+ class InvalidObjectType < ArgumentError
18
+ include Error
19
+ end
20
+
21
+ class InvalidReference < ArgumentError
22
+ include Error
23
+ end
24
+
25
+ class InvalidTraversal < ArgumentError
26
+ include Error
27
+ end
28
+
29
+ class CyclicSymlink < Exception
30
+ include Error
31
+ end
32
+
33
+ class AmbiguousReference < InvalidReference
34
+ end
35
+
36
+ class BadRevisionSyntax < InvalidReference
37
+ end
38
+
39
+ class WrongTypeForMode < Exception
40
+ include Error
41
+
42
+ def initialize(expected, actual)
43
+ super("It looks like you expected a #{expected.inspect} but referenced entry is a #{actual.inspect}")
44
+ end
45
+ end
46
+
47
+ class NotYetImplemented < NotImplementedError
48
+ include Error
49
+ end
50
+
51
+ class Internal < Exception
52
+ include Error
53
+ end
54
+
55
+ class ConcurrentRefUpdate < Exception
56
+ include Error
57
+
58
+ def initialize
59
+ super 'Another process has updated the ref you are currently updating.
60
+ This is unlikely to be a problem, no data got lost. You may simply retry the update.
61
+ If this happens frequently, you may have want to run "git gc" to remove clobber.'
62
+ end
63
+ end
64
+
65
+ end
66
+
67
+ end
@@ -0,0 +1,12 @@
1
+ require 'multi_git/tree_entry'
2
+ require 'multi_git/file'
3
+ module MultiGit
4
+
5
+ class Executeable < File
6
+
7
+ def mode
8
+ Utils::MODE_EXECUTEABLE
9
+ end
10
+ end
11
+
12
+ end
@@ -0,0 +1,35 @@
1
+ require 'multi_git/tree_entry'
2
+ require 'multi_git/blob'
3
+ require 'forwardable'
4
+ module MultiGit
5
+
6
+ class File < TreeEntry
7
+
8
+ module Base
9
+ def mode
10
+ Utils::MODE_FILE
11
+ end
12
+ end
13
+
14
+ class Builder < TreeEntry::Builder
15
+ include Base
16
+
17
+ def make_inner(*args)
18
+ if args.any?
19
+ if args[0].kind_of? Blob::Builder
20
+ return args[0]
21
+ elsif args[0].kind_of? Blob
22
+ return args[0].to_builder
23
+ end
24
+ end
25
+ Blob::Builder.new(*args)
26
+ end
27
+ end
28
+
29
+ include Base
30
+ extend Forwardable
31
+
32
+ delegate (Blob.instance_methods - Object.instance_methods) => :object
33
+ end
34
+
35
+ end
@@ -0,0 +1,11 @@
1
+ require 'multi_git/blob'
2
+ require 'multi_git/git_backend/object'
3
+ module MultiGit::GitBackend
4
+
5
+ class Blob < Object
6
+
7
+ include MultiGit::Blob
8
+
9
+ end
10
+
11
+ end
@@ -0,0 +1,117 @@
1
+ require 'shellwords'
2
+ require 'open3'
3
+ require 'multi_git/error'
4
+ module MultiGit::GitBackend
5
+
6
+ class Cmd
7
+
8
+ class Error < MultiGit::Error::Internal
9
+
10
+ def self.const_missing(name)
11
+ if name =~ /\AExitCode\d+\z/
12
+ self.const_set(name, Class.new(self))
13
+ else
14
+ super
15
+ end
16
+ end
17
+
18
+ def self.[](exit_code)
19
+ return const_get("ExitCode#{exit_code}")
20
+ end
21
+ end
22
+
23
+ def initialize(command, options = {})
24
+ @cmd = command
25
+ @opts = options
26
+ end
27
+
28
+ READ_BLOCK = lambda{|io|
29
+ io.read
30
+ }
31
+
32
+ def call_env(env, *args, &block)
33
+ s = cmd(*args)
34
+ block ||= READ_BLOCK
35
+ result = nil
36
+ message = nil
37
+ status = popen_foo(env, s.join(' ')) do | stdin, stdout, stderr |
38
+ if block.arity == 1
39
+ stdin.close
40
+ result = block.call(stdout)
41
+ else
42
+ result = block.call(stdin, stdout)
43
+ end
44
+ message = stderr.read
45
+ end
46
+ if status.exitstatus == 0
47
+ return result
48
+ else
49
+ raise Error[status.exitstatus], message.chomp
50
+ end
51
+ end
52
+
53
+ def call(*args, &block)
54
+ call_env({}, *args, &block)
55
+ end
56
+
57
+ alias [] call
58
+
59
+ private
60
+
61
+ # @api private
62
+ #
63
+ # popen3 is broken in jruby, popen4 is not available in mri :(
64
+ def popen_foo(env={}, cmd)
65
+ if IO.respond_to? :popen4
66
+ # setting the env is broken in jruby
67
+ args = ['env -i', escape_opts(env), cmd]
68
+ IO.popen4(args.join(' ')) do |_pid, *yield_args|
69
+ yield *yield_args
70
+ end
71
+ return $?
72
+ else
73
+ Open3.popen3(env, cmd,:unsetenv_others => true ) do |*yield_args,_thr|
74
+ yield *yield_args
75
+ return _thr.value
76
+ end
77
+ end
78
+ end
79
+
80
+ def cmd(*args)
81
+ [ @cmd, escape_opts(@opts), escape_args(args) ].flatten
82
+ end
83
+
84
+ def escape_args(args)
85
+ args.map{|arg|
86
+ if arg.kind_of? Hash
87
+ escape_opts(arg)
88
+ elsif arg.kind_of? Array
89
+ escape_args(arg)
90
+ else
91
+ Shellwords.escape(opt_to_string(arg))
92
+ end
93
+ }
94
+ end
95
+
96
+ def escape_opts(opts)
97
+ opts.map{|k,v|
98
+ opt_to_string(k)+'='+Shellwords.escape(v)
99
+ }
100
+ end
101
+
102
+ def opt_to_string(opt)
103
+ if opt.kind_of? Symbol
104
+ s = opt.to_s
105
+ if s.size == 1
106
+ return '-'+s
107
+ else
108
+ return '--'+s.gsub('_','-')
109
+ end
110
+ else
111
+ return opt
112
+ end
113
+ end
114
+
115
+ end
116
+
117
+ end