multi_git 0.0.1.alpha1

Sign up to get free protection for your applications and to get access to all the features.
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