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,75 @@
1
+ require 'multi_git/git_backend/object'
2
+ require 'multi_git/commit'
3
+ module MultiGit
4
+ module GitBackend
5
+ class Commit < Object
6
+ include MultiGit::Commit
7
+
8
+ def parents
9
+ read!
10
+ @parents ||= @parent_oids.map{|oid| repository.read(oid) }
11
+ end
12
+
13
+ def tree
14
+ read!
15
+ @tree ||= repository.read(@tree_oid)
16
+ end
17
+
18
+ def message
19
+ read!
20
+ @message
21
+ end
22
+
23
+ def committer
24
+ read!
25
+ @committer
26
+ end
27
+
28
+ def author
29
+ read!
30
+ @author
31
+ end
32
+
33
+ def time
34
+ read!
35
+ @time
36
+ end
37
+
38
+ def commit_time
39
+ read!
40
+ @commit_time
41
+ end
42
+
43
+ def read!
44
+ return if @read
45
+ @read = true
46
+ @header, @message = content.split("\n\n")
47
+ @parent_oids = []
48
+ @header.each_line do |line|
49
+ type, content = line.split(' ',2)
50
+ case(type)
51
+ when 'tree' then @tree_oid = content.chomp
52
+ when 'parent' then @parent_oids << content.chomp
53
+ when 'author' then
54
+ @author, @time = parse_signature(content)
55
+ when 'committer' then
56
+ @committer, @commit_time = parse_signature(content)
57
+ else
58
+ raise "Commit line type: #{type}"
59
+ end
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ SIGNATURE_RX = /\A(.+) <([^>]+)> (\d+) ([\-+]\d{2})(\d{2})\Z/
66
+
67
+ def parse_signature(content)
68
+ match = SIGNATURE_RX.match(content)
69
+ return MultiGit::Handle.new(match[1],match[2]), Time.at(match[3].to_i).localtime(match[4]+':'+match[5])
70
+ end
71
+
72
+ end
73
+ end
74
+ end
75
+
@@ -0,0 +1,34 @@
1
+ require 'multi_git/object'
2
+ require 'forwardable'
3
+ class MultiGit::GitBackend::Object
4
+
5
+ extend Forwardable
6
+
7
+ include MultiGit::Object
8
+
9
+ def initialize(repository, oid, content = nil)
10
+ @repository = repository
11
+ @git = repository.__backend__
12
+ @oid = oid
13
+ @content = content ? content.dup.freeze : nil
14
+ end
15
+
16
+ def bytesize
17
+ @size ||= begin
18
+ if @content
19
+ @content.bytesize
20
+ else
21
+ @git['cat-file',:s,@oid].to_i
22
+ end
23
+ end
24
+ end
25
+
26
+ def content
27
+ @content ||= @git['cat-file',type.to_s,@oid].freeze
28
+ end
29
+
30
+ def to_io
31
+ StringIO.new(content)
32
+ end
33
+
34
+ end
@@ -0,0 +1,36 @@
1
+ require 'fileutils'
2
+ require 'multi_git/ref'
3
+ module MultiGit
4
+ module GitBackend
5
+ class Ref
6
+
7
+ include MultiGit::Ref
8
+
9
+ attr :target
10
+
11
+ def initialize(repository, name)
12
+ super(repository, name)
13
+ read!
14
+ end
15
+
16
+ private
17
+
18
+ SHOW_REF_LINE = /\A(\h{40}) ([^\n]+)\Z/.freeze
19
+
20
+ def read!
21
+ begin
22
+ if symbolic?
23
+ content = repository.__backend__['symbolic-ref', name]
24
+ @target = repository.ref(content.chomp)
25
+ else
26
+ lines = repository.__backend__['show-ref', name].lines
27
+ match = SHOW_REF_LINE.match(lines.first)
28
+ @target = repository.read(match[1])
29
+ end
30
+ rescue Cmd::Error::ExitCode1
31
+ # doesn't exists
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,162 @@
1
+ require 'multi_git/utils'
2
+ require 'multi_git/tree_entry'
3
+ require 'multi_git/repository'
4
+ require 'multi_git/git_backend/cmd'
5
+ require 'multi_git/git_backend/blob'
6
+ require 'multi_git/git_backend/tree'
7
+ require 'multi_git/git_backend/commit'
8
+ require 'multi_git/git_backend/ref'
9
+ module MultiGit::GitBackend
10
+
11
+ class Repository < MultiGit::Repository
12
+
13
+ # @api private
14
+ def __backend__
15
+ @git
16
+ end
17
+
18
+ Utils = MultiGit::Utils
19
+
20
+ OBJECT_CLASSES = {
21
+ :blob => Blob,
22
+ :tree => Tree,
23
+ :commit => Commit
24
+ }
25
+
26
+ def bare?
27
+ git_work_tree.nil?
28
+ end
29
+
30
+ attr :git_dir
31
+ attr :git_work_tree
32
+ attr :git_binary
33
+
34
+ def initialize(path, options = {})
35
+ @git_binary = `which git`.chomp
36
+ options = initialize_options(path, options)
37
+ git_dir = options[:repository]
38
+ @git = Cmd.new(git_binary, :git_dir => git_dir )
39
+ if !::File.exists?(git_dir) || MultiGit::Utils.empty_dir?(git_dir)
40
+ if options[:init]
41
+ if options[:bare]
42
+ @git['init', :bare, git_dir]
43
+ else
44
+ @git['init', git_dir]
45
+ end
46
+ else
47
+ raise MultiGit::Error::NotARepository, options[:repository]
48
+ end
49
+ end
50
+ @git_dir = git_dir
51
+ @git_work_tree = options[:working_directory]
52
+ @index = options[:index]
53
+ verify_bareness(path, options)
54
+ end
55
+
56
+ # {include:MultiGit::Repository#write}
57
+ # @param (see MultiGit::Repository#write)
58
+ # @raise (see MultiGit::Repository#write)
59
+ # @return (see MultiGit::Repository#write)
60
+ def write(content, type = :blob)
61
+ if content.kind_of? MultiGit::Builder
62
+ return content >> self
63
+ end
64
+ validate_type(type)
65
+ oid = nil
66
+ if content.kind_of? MultiGit::Object
67
+ if include?(content.oid)
68
+ return read(content.oid)
69
+ end
70
+ content = content.to_io
71
+ end
72
+ if content.respond_to? :path
73
+ oid = @git["hash-object",:t, type.to_s,:w,'--', content.path].chomp
74
+ else
75
+ content = content.read if content.respond_to? :read
76
+ @git.call('hash-object',:t,type.to_s, :w, :stdin) do |stdin, stdout|
77
+ stdin.write(content)
78
+ stdin.close
79
+ oid = stdout.read.chomp
80
+ end
81
+ end
82
+ return OBJECT_CLASSES[type].new(self,oid)
83
+ end
84
+
85
+ # {include:MultiGit::Repository#read}
86
+ # @param (see MultiGit::Repository#read)
87
+ # @raise (see MultiGit::Repository#read)
88
+ # @return (see MultiGit::Repository#read)
89
+ def read(oidish)
90
+ oid = parse(oidish)
91
+ type = @git['cat-file',:t, oid].chomp
92
+ return OBJECT_CLASSES[type.to_sym].new(self, oid)
93
+ end
94
+
95
+ # {include:MultiGit::Repository#parse}
96
+ # @param (see MultiGit::Repository#parse)
97
+ # @raise (see MultiGit::Repository#parse)
98
+ # @return (see MultiGit::Repository#parse)
99
+ def parse(oidish)
100
+ begin
101
+ result = @git['rev-parse', :revs_only, :validate, oidish.to_s].chomp
102
+ if result == ""
103
+ raise MultiGit::Error::InvalidReference, oidish
104
+ end
105
+ return result
106
+ rescue Cmd::Error::ExitCode128
107
+ raise MultiGit::Error::InvalidReference, oidish
108
+ end
109
+ end
110
+
111
+ # {include:MultiGit::Repository#include?}
112
+ # @param (see MultiGit::Repository#include?)
113
+ # @raise (see MultiGit::Repository#include?)
114
+ # @return (see MultiGit::Repository#include?)
115
+ def include?(oid)
116
+ begin
117
+ @git['cat-file', :e, oid.to_s]
118
+ return true
119
+ rescue Cmd::Error::ExitCode1
120
+ return false
121
+ end
122
+ end
123
+
124
+ def ref(name)
125
+ MultiGit::GitBackend::Ref.new(self, name)
126
+ end
127
+
128
+ # @visibility private
129
+ MKTREE_FORMAT = "%06o %s %s\t%s\n"
130
+
131
+ # @visibility private
132
+ # @api private
133
+ def make_tree(entries)
134
+ @git.call('mktree') do |stdin, stdout|
135
+ entries.each do |name, mode, oid|
136
+ stdin.printf(MKTREE_FORMAT, mode, Utils.type_from_mode(mode), oid, name)
137
+ end
138
+ stdin.close
139
+ read(stdout.read.chomp)
140
+ end
141
+ end
142
+
143
+ # @visibility private
144
+ # @api private
145
+ def make_commit(commit)
146
+ env = {
147
+ 'GIT_AUTHOR_NAME'=>commit[:author].name,
148
+ 'GIT_AUTHOR_EMAIL'=>commit[:author].email,
149
+ 'GIT_AUTHOR_DATE' =>commit[:time].strftime('%s %z'),
150
+ 'GIT_COMMITTER_NAME'=>commit[:committer].name,
151
+ 'GIT_COMMITTER_EMAIL'=>commit[:committer].email,
152
+ 'GIT_COMMITTER_DATE' =>commit[:commit_time].strftime('%s %z')
153
+ }
154
+ @git.call_env(env, 'commit-tree', commit[:tree], commit[:parents].map{|p| [:p, p] } ) do |stdin, stdout|
155
+ stdin << commit[:message]
156
+ stdin.close
157
+ return read(stdout.read.chomp)
158
+ end
159
+ end
160
+
161
+ end
162
+ end
@@ -0,0 +1,22 @@
1
+ require 'multi_git/tree'
2
+ require 'multi_git/git_backend/object'
3
+ module MultiGit::GitBackend
4
+ class Tree < Object
5
+
6
+ LS_TREE_REGEX = /^([0-7]{6}) (?:blob|tree|commit) (\h{40})\t(.+)$/
7
+
8
+ include MultiGit::Tree
9
+
10
+ def raw_entries
11
+ @raw_entries ||= begin
12
+ @git.call('ls-tree', oid) do |stdout|
13
+ stdout.each_line.map do |line|
14
+ raise unless LS_TREE_REGEX =~ line
15
+ [$3,$1.to_i(8),$2]
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,19 @@
1
+ module MultiGit
2
+ module GitBackend
3
+ class << self
4
+
5
+ def load!
6
+ end
7
+
8
+ def available?
9
+ true
10
+ end
11
+
12
+ def open(directory, options = {})
13
+ Repository.new(directory, options)
14
+ end
15
+
16
+ end
17
+ end
18
+ end
19
+ require 'multi_git/git_backend/repository'
@@ -0,0 +1,33 @@
1
+ module MultiGit
2
+ # Encapsulates name + email. Mostly used for commits.
3
+ class Handle < Struct.new(:name, :email)
4
+
5
+ private
6
+ EMAIL = /\A\w+@\w+\.\w+\z/ # close enough, but
7
+ NAME_WITH_EMAIL = /\A(.*) <(\S+@\S+\.\S+)>\z/
8
+ public
9
+
10
+ # Parses a handle from a string
11
+ #
12
+ # Currently two formats are recognized. Either just a mail address
13
+ # (e.g. 'user@example.com') or user + mail address in brackets ( e.g.
14
+ # 'User <user@example.com>' ).
15
+ #
16
+ # @param string [String] a string containing a handle.
17
+ # @return [Handle]
18
+ #
19
+ # @example
20
+ # MultiGit::Handle.parse('me@mydom.ain') #=> be_a MultiGit::Handle
21
+ #
22
+ def self.parse(string)
23
+ case(string)
24
+ when EMAIL then return new(string, string)
25
+ when NAME_WITH_EMAIL then return new($1,$2)
26
+ else raise ArgumentError, "Unknown handle format: #{string}. Please use either 'user@example.com' or 'User <user@example.com>'"
27
+ end
28
+ end
29
+
30
+ DEFAULT = parse("MultiGit #{MultiGit::VERSION} <#{MultiGit::VERSION}@multi.git>")
31
+
32
+ end
33
+ end
@@ -0,0 +1,10 @@
1
+ require 'multi_git/blob'
2
+ require 'multi_git/jgit_backend/object'
3
+ module MultiGit::JGitBackend
4
+
5
+ class Blob < Object
6
+
7
+ include MultiGit::Blob
8
+
9
+ end
10
+ end
@@ -0,0 +1,45 @@
1
+ require 'multi_git/commit'
2
+ require 'multi_git/jgit_backend/object'
3
+ module MultiGit
4
+ module JGitBackend
5
+ class Commit < Object
6
+ include MultiGit::Commit
7
+
8
+ import 'org.eclipse.jgit.revwalk.RevWalk'
9
+
10
+ def parents
11
+ @parents ||= java_commit.parents.map{|pr| repository.read(pr.getId()) }
12
+ end
13
+
14
+ def tree
15
+ @tree ||= repository.read(java_commit.tree.id)
16
+ end
17
+
18
+ def message
19
+ @message ||= java_commit.full_message.freeze
20
+ end
21
+
22
+ def time
23
+ @time ||= java_commit.author_ident.when
24
+ end
25
+
26
+ def commit_time
27
+ @time ||= java_commit.committer_ident.when
28
+ end
29
+
30
+ def author
31
+ @author ||= MultiGit::Handle.new(java_commit.author_ident.name,java_commit.author_ident.email_address)
32
+ end
33
+
34
+ def committer
35
+ @committer ||= MultiGit::Handle.new(java_commit.committer_ident.name,java_commit.committer_ident.email_address)
36
+ end
37
+ private
38
+
39
+ def java_commit
40
+ @java_commit ||= repository.use_reader{|rd| RevWalk.new(rd).parseCommit(java_oid) }
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,48 @@
1
+ require 'multi_git/object'
2
+ require 'forwardable'
3
+ require 'multi_git/jgit_backend/rewindeable_io'
4
+ class MultiGit::JGitBackend::Object
5
+
6
+ import "org.eclipse.jgit.lib.ObjectId"
7
+
8
+ extend Forwardable
9
+
10
+ include MultiGit::Object
11
+
12
+ def initialize(repository,oid, object = nil)
13
+ @repository = repository
14
+ @java_oid = oid
15
+ @git = repository.__backend__
16
+ @oid = ObjectId.toString(oid)
17
+ @java_object = object
18
+ end
19
+
20
+ def bytesize
21
+ java_object.getSize
22
+ end
23
+
24
+ def to_io
25
+ MultiGit::JGitBackend::RewindeableIO.new( java_stream )
26
+ end
27
+
28
+ def content
29
+ @content ||= to_io.read.freeze
30
+ end
31
+
32
+ private
33
+
34
+ def java_stream
35
+ stream = java_object.openStream
36
+ stream.mark(bytesize)
37
+ stream
38
+ end
39
+
40
+ protected
41
+
42
+ attr :java_oid
43
+
44
+ def java_object
45
+ @java_object ||= repository.use_reader{|rdr| rdr.open(@java_oid) }
46
+ end
47
+
48
+ end
@@ -0,0 +1,117 @@
1
+ require 'multi_git/ref'
2
+ module MultiGit
3
+ module JGitBackend
4
+ class Ref
5
+
6
+ include MultiGit::Ref
7
+
8
+ # HACK!
9
+ # @api private
10
+ # @visibility private
11
+ class Java::OrgEclipseJgitStorageFile::RefDirectoryUpdate
12
+ public :tryLock, :unlock, :doUpdate, :doDelete
13
+ end
14
+
15
+ # @api developer
16
+ class Updater < MultiGit::Ref::Updater
17
+
18
+ import "org.eclipse.jgit.lib.ObjectId"
19
+ import 'org.eclipse.jgit.lib.RefUpdate'
20
+
21
+ protected
22
+
23
+ def do_update(ru, nx)
24
+ case nx
25
+ when nil then
26
+ ru.doDelete(RefUpdate::Result::FORCED)
27
+ when MultiGit::Object then
28
+ ru.new_object_id = ObjectId.fromString(nx.oid)
29
+ ru.doUpdate(RefUpdate::Result::FORCED)
30
+ when MultiGit::Ref then
31
+ ru.link( nx.name )
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ # @api developer
38
+ class OptimisticUpdater < Updater
39
+ def update(nx)
40
+ ru = repository.__backend__.updateRef(name)
41
+ begin
42
+ if !ru.try_lock(false)
43
+ raise
44
+ end
45
+ if ref.direct?
46
+ old_id = ObjectId.toString(ru.old_object_id)
47
+ if target.nil?
48
+ raise Error::ConcurrentRefUpdate if old_id != Utils::NULL_OID
49
+ elsif old_id != target.oid
50
+ raise Error::ConcurrentRefUpdate
51
+ end
52
+ end
53
+ nx = super
54
+ do_update(ru, nx)
55
+ return nx
56
+ ensure
57
+ ru.unlock
58
+ end
59
+ end
60
+
61
+ end
62
+
63
+ # @api developer
64
+ class PessimisticUpdater < Updater
65
+
66
+ def initialize(*_)
67
+ super
68
+ @ref_update = repository.__backend__.updateRef(name)
69
+ if !@ref_update.try_lock(false)
70
+ raise
71
+ end
72
+ self.ref = ref.reload
73
+ end
74
+
75
+ def update(nx)
76
+ nx = super
77
+ do_update(@ref_update, nx)
78
+ return nx
79
+ end
80
+
81
+ def destroy!
82
+ @ref_update.unlock
83
+ end
84
+ end
85
+
86
+ def initialize(repository, name)
87
+ super(repository, name)
88
+ @java_ref = repository.__backend__.getRef(name)
89
+ end
90
+
91
+ def target
92
+ return nil unless java_ref
93
+ @target ||= begin
94
+ if java_ref.symbolic?
95
+ repository.ref(java_ref.target.name)
96
+ else
97
+ repository.read(java_ref.getObjectId())
98
+ end
99
+ end
100
+ end
101
+
102
+ # @api private
103
+ # @visibility private
104
+ attr :java_ref
105
+
106
+ private
107
+
108
+ def optimistic_updater
109
+ OptimisticUpdater
110
+ end
111
+
112
+ def pessimistic_updater
113
+ PessimisticUpdater
114
+ end
115
+ end
116
+ end
117
+ end