svn 0.1.0

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.
@@ -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