svn 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,133 @@
1
+ require 'time'
2
+ require 'rubygems'
3
+ require 'ffi'
4
+
5
+ module Svn #:nodoc:
6
+
7
+ LOG_PROP_NAME = 'svn:log'
8
+ AUTHOR_PROP_NAME = 'svn:author'
9
+ TIMESTAMP_PROP_NAME = 'svn:date'
10
+
11
+ class Revision < Root
12
+
13
+ include Comparable
14
+
15
+ attr_reader :num
16
+ attr_reader :repo
17
+
18
+ def initialize( ptr, repo, pool )
19
+ super( ptr )
20
+ @repo = repo
21
+ @pool = pool
22
+ @num = revnum
23
+ end
24
+
25
+ def to_i
26
+ @num
27
+ end
28
+
29
+ def <=>( other )
30
+ to_i <=> other.to_i
31
+ end
32
+
33
+ module C
34
+ extend FFI::Library
35
+ ffi_lib 'libsvn_fs-1.so.1'
36
+
37
+ typedef :pointer, :out_pointer
38
+ typedef Pool, :pool
39
+ typedef CError.by_ref, :error
40
+ typedef Root, :root
41
+ typedef :long, :revnum
42
+ typedef :string, :name
43
+ typedef Repo::FileSystem, :fs
44
+ typedef CountedString, :counted_string
45
+
46
+ attach_function :revnum,
47
+ :svn_fs_revision_root_revision,
48
+ [ :root ],
49
+ :revnum
50
+
51
+ attach_function :prop,
52
+ :svn_fs_revision_prop,
53
+ [ :out_pointer, :fs, :revnum, :name, :pool ],
54
+ :error
55
+
56
+ attach_function :proplist,
57
+ :svn_fs_revision_proplist,
58
+ [ :out_pointer, :fs, :revnum, :pool ],
59
+ :error
60
+ end
61
+
62
+ # use the C module for all bound methods
63
+ bind_to C
64
+
65
+ # gets the numeric identifier for this revision
66
+ bind :revnum
67
+ private :revnum
68
+
69
+ # returns the revision property +name+
70
+ bind( :prop,
71
+ :returning => CountedString,
72
+ :before_return => :to_s,
73
+ :validate => Error.return_check
74
+ ) { |out, this, name| [ out, repo.fs, num, name, pool ] }
75
+
76
+ # returns a hash of revision properties
77
+ bind( :props, :to => :proplist,
78
+ :returning => AprHash.factory( :string, [:pointer, :string] ),
79
+ :before_return => :to_h,
80
+ :validate => Error.return_check
81
+ ) { |out, this| [ out, repo.fs, num, pool ] }
82
+
83
+ def message
84
+ prop( LOG_PROP_NAME )
85
+ end
86
+ alias_method :log, :message
87
+
88
+ def author
89
+ prop( AUTHOR_PROP_NAME )
90
+ end
91
+
92
+ def timestamp
93
+ Time.parse( prop( TIMESTAMP_PROP_NAME ) )
94
+ end
95
+
96
+ # diffs +path+ with another revision. if no revision is specified, the
97
+ # previous revision is used.
98
+ def diff( path, options={} )
99
+ raise Svn::Error, "cannot diff directory #{path}@#{to_s}" if dir? path
100
+
101
+ other = options[:with] if options[:with].is_a? Root
102
+ other = repo.revision(options[:with]) if options[:with].is_a? Numeric
103
+ other ||= repo.revision(to_i - 1)
104
+
105
+ return other.diff( path, :with => self ) if other < self
106
+
107
+ content = ""
108
+ begin
109
+ content = file_content_stream( path ).to_counted_string
110
+ rescue Svn::Error => err
111
+ raise if options[:raise_errors]
112
+ end
113
+
114
+ other_content = ""
115
+ begin
116
+ other_content= other.file_content_stream( path ).to_counted_string
117
+ rescue Svn::Error => err
118
+ raise if options[:raise_errors]
119
+ end
120
+
121
+ Diff.string_diff( content, other_content ).unified(
122
+ :original_header => "#{path}@#{to_s}",
123
+ :modified_header => "#{path}@#{other}"
124
+ )
125
+ end
126
+
127
+ def to_s
128
+ "r#{to_i}"
129
+ end
130
+
131
+ end
132
+
133
+ end
@@ -0,0 +1,161 @@
1
+ require 'rubygems'
2
+ require 'ffi'
3
+
4
+ module Svn #:nodoc:
5
+
6
+ class Root < FFI::AutoPointer
7
+
8
+ class << self
9
+ def release( ptr )
10
+ C.close_root( ptr )
11
+ end
12
+ end
13
+
14
+ module C
15
+ extend FFI::Library
16
+ ffi_lib 'libsvn_fs-1.so.1'
17
+
18
+ typedef :pointer, :out_pointer
19
+ typedef Pool, :pool
20
+ typedef CError.by_ref, :error
21
+ typedef Root, :root
22
+ typedef :string, :path
23
+ typedef :string, :name
24
+
25
+ # lifecycle functions
26
+ attach_function :close_root,
27
+ :svn_fs_close_root,
28
+ [ :root ],
29
+ :void
30
+
31
+ # node metadata
32
+ attach_function :is_dir,
33
+ :svn_fs_is_dir,
34
+ [ :out_pointer, :root, :path, :pool ],
35
+ :error
36
+ attach_function :is_file,
37
+ :svn_fs_is_file,
38
+ [ :out_pointer, :root, :path, :pool ],
39
+ :error
40
+ attach_function :created_rev,
41
+ :svn_fs_node_created_rev,
42
+ [ :out_pointer, :root, :path, :pool ],
43
+ :error
44
+ attach_function :created_path,
45
+ :svn_fs_node_created_path,
46
+ [ :out_pointer, :root, :path, :pool ],
47
+ :error
48
+
49
+ # props
50
+ attach_function :node_prop,
51
+ :svn_fs_node_prop,
52
+ [ :out_pointer, :root, :path, :name, :pool ],
53
+ :error
54
+ attach_function :node_proplist,
55
+ :svn_fs_node_proplist,
56
+ [ :out_pointer, :root, :path, :pool ],
57
+ :error
58
+
59
+ # files
60
+ attach_function :file_size,
61
+ :svn_fs_file_length,
62
+ [ :out_pointer, :root, :path, :pool ],
63
+ :error
64
+ attach_function :file_content,
65
+ :svn_fs_file_contents,
66
+ [ :out_pointer, :root, :path, :pool ],
67
+ :error
68
+
69
+ # dirs
70
+ attach_function :dir_content,
71
+ :svn_fs_dir_entries,
72
+ [ :out_pointer, :root, :path, :pool ],
73
+ :error
74
+
75
+ # changes
76
+ attach_function :changes,
77
+ :svn_fs_paths_changed2,
78
+ [ :out_pointer, :root, :pool ],
79
+ :error
80
+ end
81
+
82
+ def pool
83
+ @pool ||= RootPool
84
+ end
85
+
86
+ # helper procs for method binding
87
+ test_c_true = Proc.new { |i| i == 1 }
88
+ add_pool = Proc.new { |out, this, *args| [ out, this, *args, pool ] }
89
+
90
+ # use the above C module for the source of bound functions
91
+ bind_to C
92
+
93
+ # bound method definitions
94
+ bind :dir?, :to => :is_dir,
95
+ :returning => :int,
96
+ :before_return => test_c_true,
97
+ :validate => Error.return_check,
98
+ &add_pool
99
+
100
+ bind :dir_content,
101
+ :returning => AprHash.factory( :string, :pointer ),
102
+ :before_return => Proc.new { |h| h.to_h.keys },
103
+ :validate => Error.return_check,
104
+ &add_pool
105
+
106
+ bind :file?, :to => :is_file,
107
+ :returning => :int,
108
+ :before_return => test_c_true,
109
+ :validate => Error.return_check,
110
+ &add_pool
111
+
112
+ bind :file_size,
113
+ :returning => :int64,
114
+ :validate => Error.return_check,
115
+ &add_pool
116
+
117
+ bind :file_content,
118
+ :returning => Stream,
119
+ :before_return => :to_string_io,
120
+ :validate => Error.return_check,
121
+ &add_pool
122
+
123
+ bind :file_content_stream, :to => :file_content,
124
+ :returning => Stream,
125
+ :validate => Error.return_check,
126
+ &add_pool
127
+
128
+ bind :created_rev,
129
+ :returning => :long,
130
+ :validate => Error.return_check,
131
+ &add_pool
132
+
133
+ bind :created_path,
134
+ :returning => :string,
135
+ :validate => Error.return_check,
136
+ &add_pool
137
+
138
+ # returns the +path+'s property value for +name+
139
+ bind :prop_for, :to => :node_prop,
140
+ :returning => CountedString,
141
+ :before_return => :to_s,
142
+ :validate => Error.return_check,
143
+ &add_pool
144
+
145
+ # returns a hash of name to property values for +path+
146
+ bind :props_for, :to => :node_proplist,
147
+ :returning => AprHash.factory( :string, [:pointer, :string] ),
148
+ :before_return => :to_h,
149
+ :validate => Error.return_check,
150
+ &add_pool
151
+
152
+ # return the changes in this revision or transaction
153
+ bind :changes,
154
+ :returning => AprHash.factory( :string, ChangedPath ),
155
+ :before_return => :to_h,
156
+ :validate => Error.return_check,
157
+ &add_pool
158
+
159
+ end
160
+
161
+ end
@@ -0,0 +1,149 @@
1
+ require 'stringio'
2
+ require 'rubygems'
3
+ require 'ffi'
4
+
5
+ module Svn
6
+
7
+ class Stream < FFI::AutoPointer
8
+
9
+ class << self
10
+ # Wraps an IO object to be used as a subversion stream
11
+ def wrap_io( io, pool=RootPool )
12
+ stream = new( C.create( Svn::Utils.wrap(io), pool ) )
13
+ C.set_write( stream, C::WriteToIO )
14
+ C.set_read( stream, C::ReadFromIO )
15
+ return stream
16
+ end
17
+
18
+ def release( ptr )
19
+ Error.check_and_raise(
20
+ C.close( ptr )
21
+ )
22
+ end
23
+ end
24
+
25
+ module C
26
+ extend FFI::Library
27
+ ffi_lib 'libsvn_subr-1.so.1'
28
+
29
+ typedef :pointer, :in_out_len
30
+ typedef CError.by_ref, :error
31
+ typedef Pool, :pool
32
+ typedef Stream, :stream
33
+ typedef :size_t, :apr_size
34
+
35
+ callback :read_function, [:pointer, :pointer, :pointer], :error
36
+ callback :write_function, [:pointer, :string, :pointer], :error
37
+
38
+ # lifecycle functions
39
+ attach_function :create,
40
+ :svn_stream_create,
41
+ [:pointer, :pool], :pointer
42
+ attach_function :set_read,
43
+ :svn_stream_set_read,
44
+ [:stream, :read_function], :void
45
+ attach_function :set_write,
46
+ :svn_stream_set_write,
47
+ [:stream, :write_function], :void
48
+ attach_function :close,
49
+ :svn_stream_close,
50
+ [:stream], :error
51
+
52
+ # note: the SVN docs say that short reads indicate the end of the stream
53
+ # and short writes indicate an error. this means that we cannot use
54
+ # read_nonblock and write_nonblock, since they do not guarantee anything
55
+ # will happen.
56
+
57
+ ReadFromIO = FFI::Function.new(
58
+ :pointer, [:pointer, :pointer, :pointer]
59
+ ) do |io_ptr, out_buffer, in_out_len|
60
+
61
+ # read the number of bytes requested and unwrap the io object
62
+ bytes_to_read = in_out_len.read_int
63
+ io = Svn::Utils.unwrap(io_ptr)
64
+
65
+ # read the bytes from IO and write them to the pointer object
66
+ bytes_read = io.read( bytes_to_read )
67
+ out_buffer.write_string( bytes_read )
68
+
69
+ # write the number of bytes read from io
70
+ in_out_len.write_int( bytes_read.length )
71
+
72
+ nil # return no error
73
+ end
74
+
75
+ WriteToIO = FFI::Function.new(
76
+ :pointer, [:pointer, :string, :pointer]
77
+ ) do |io_ptr, in_string, in_out_len|
78
+
79
+ # read the size of in_string and unwrap the io object
80
+ bytes_to_write = in_out_len.read_int
81
+ io = Svn::Utils.unwrap(io_ptr)
82
+
83
+ # should we check that in_string isn't longer than in_out_len?
84
+ bytes_written = io.write( in_string )
85
+
86
+ # write the actual number of bytes written to io
87
+ in_out_len.write_int( bytes_written )
88
+
89
+ nil # return no error
90
+ end
91
+
92
+ # accessor functions
93
+ attach_function :read,
94
+ :svn_stream_read,
95
+ [ :stream, :buffer_out, :in_out_len ],
96
+ :error
97
+ end
98
+
99
+ def read( size=8192 )
100
+ # setup the pointers
101
+ @in_out_len ||= FFI::MemoryPointer.new( :size_t )
102
+ @in_out_len.write_ulong( size )
103
+
104
+ # make sure a buffer for reading exists and save it for reuse
105
+ if @read_buf.nil? or @read_buf.size < size
106
+ @read_buf = FFI::Buffer.alloc_out( size )
107
+ end
108
+
109
+ # call read to fill the buffer
110
+ Error.check_and_raise(
111
+ C.read( self, @read_buf, @in_out_len )
112
+ )
113
+
114
+ @read_buf.read_bytes( @in_out_len.read_ulong )
115
+ end
116
+
117
+ # reads the stream contents into a String object
118
+ def read_all
119
+ content = String.new
120
+ while bytes = read and !bytes.empty?
121
+ content << bytes
122
+ end
123
+ content
124
+ end
125
+ alias_method :to_s, :read_all
126
+
127
+ # reads the entire stream and creates a CountedString from the contents
128
+ #
129
+ # Note that this function copies the entire stream into Ruby memory and
130
+ # then copies it again into C memory. There is probably a more efficient
131
+ # way to do this, by allocating a big string and re-allocing when the size
132
+ # required overruns that memory
133
+ def to_counted_string
134
+ CountedString.from_string( read_all )
135
+ end
136
+
137
+ # reads the stream contents into a StringIO object
138
+ def to_string_io
139
+ content = StringIO.new
140
+ while bytes = read and !bytes.empty?
141
+ content.write( bytes )
142
+ end
143
+ content.rewind
144
+ content
145
+ end
146
+
147
+ end
148
+
149
+ end
@@ -0,0 +1,51 @@
1
+ require 'rubygems'
2
+ require 'ffi'
3
+
4
+ module Svn #:nodoc:
5
+
6
+ class Transaction < Root
7
+
8
+ def initialize( txn_ptr, pool )
9
+ # using the transaction object, get it's root
10
+ out_ptr = FFI::MemoryPointer.new(:pointer)
11
+ Error.check_and_raise(
12
+ C.root( out_ptr, txn_ptr, pool )
13
+ )
14
+
15
+ # call super with the root pointer, rather than the transaction pointer.
16
+ # this is so Transactions can be used as Roots in external calls:
17
+ super( out_ptr.read_pointer )
18
+
19
+ # save off the others
20
+ @txn_ptr = txn_ptr
21
+ @pool = pool
22
+ end
23
+
24
+ class << self
25
+ def release
26
+ end
27
+ end
28
+
29
+ module C
30
+ extend FFI::Library
31
+ ffi_lib 'libsvn_fs-1.so.1'
32
+
33
+ typedef :pointer, :out_pointer
34
+ typedef Pool, :pool
35
+ typedef CError.by_ref, :error
36
+ typedef :pointer, :transaction
37
+ typedef :long, :revnum
38
+
39
+ attach_function :cancel_transaction,
40
+ :svn_fs_abort_txn,
41
+ [ :transaction, :pool ],
42
+ :error
43
+ attach_function :root,
44
+ :svn_fs_txn_root,
45
+ [ :out_pointer, :pointer, :pool ],
46
+ :error
47
+ end
48
+
49
+ end
50
+
51
+ end