svn 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +39 -0
- data/lib/svn.rb +19 -0
- data/lib/svn/apr_utils.rb +313 -0
- data/lib/svn/counted_strings.rb +38 -0
- data/lib/svn/diffs.rb +209 -0
- data/lib/svn/errors.rb +83 -0
- data/lib/svn/logs.rb +120 -0
- data/lib/svn/misc.rb +68 -0
- data/lib/svn/pools.rb +67 -0
- data/lib/svn/repos.rb +333 -0
- data/lib/svn/revisions.rb +133 -0
- data/lib/svn/roots.rb +161 -0
- data/lib/svn/streams.rb +149 -0
- data/lib/svn/transactions.rb +51 -0
- data/lib/svn/utils.rb +249 -0
- metadata +71 -0
data/README
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
== About ==
|
2
|
+
|
3
|
+
This is a library for interacting with Subverison repositories. It is a
|
4
|
+
new set of ruby bindings that are simple to use and compatible with RVM
|
5
|
+
because they are dynamically linked to libsvn and do not require any native
|
6
|
+
extensions.
|
7
|
+
|
8
|
+
In addition, these new bindings expose a ruby-style API and try to hide the
|
9
|
+
underlying SVN details---for example, SVN separates repositories from their
|
10
|
+
inner filesystems, but this library hides that distinction.
|
11
|
+
|
12
|
+
== Getting Started ==
|
13
|
+
|
14
|
+
Example code:
|
15
|
+
|
16
|
+
require 'svn'
|
17
|
+
|
18
|
+
repo = Svn::Repo.open('/path/to/repo')
|
19
|
+
r4 = repo.revision(4)
|
20
|
+
puts "#{r}, by #{r.author} (on #{r.timestamp.strftime('%Y-%m-%d %H:%M')})\nLog: #{r.log}"
|
21
|
+
|
22
|
+
r4.changes.each_pair { |path, changes| ... }
|
23
|
+
puts r4.props.inspect
|
24
|
+
# => {"svn:log"=>"...", "svn:author"=>"blue", "svn:date"=>"2011-10-14T23:51:58.705026Z"}
|
25
|
+
puts r4.diff('/some/repo/file') # by default, against the previous revision
|
26
|
+
|
27
|
+
== Current Status ==
|
28
|
+
|
29
|
+
Version 0.1.0 supports read-only operations to inspect a subversion repository,
|
30
|
+
its files, and file properties. Transactions are planned and will be included
|
31
|
+
in a future version.
|
32
|
+
|
33
|
+
The current release, 0.1.0 is an experimental release and the API included is
|
34
|
+
subject to change. Rubygems version number conventions will be respected, so it
|
35
|
+
is recommended that callers specify the version of this library using the "~>"
|
36
|
+
operator:
|
37
|
+
spec.add_dependency 'ffi', '~> 0.1'
|
38
|
+
|
39
|
+
== Contributing ==
|
data/lib/svn.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'ffi'
|
3
|
+
|
4
|
+
require 'svn/utils'
|
5
|
+
require 'svn/misc'
|
6
|
+
require 'svn/errors'
|
7
|
+
require 'svn/pools'
|
8
|
+
require 'svn/apr_utils'
|
9
|
+
require 'svn/counted_strings'
|
10
|
+
|
11
|
+
# General Svn docs here!
|
12
|
+
module Svn
|
13
|
+
autoload :Stream, 'svn/streams'
|
14
|
+
autoload :Log, 'svn/logs'
|
15
|
+
autoload :Repo, 'svn/repos'
|
16
|
+
autoload :Root, 'svn/roots'
|
17
|
+
autoload :Revision, 'svn/revisions'
|
18
|
+
autoload :Diff, 'svn/diffs'
|
19
|
+
end
|
@@ -0,0 +1,313 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'ffi'
|
3
|
+
|
4
|
+
module Svn #:nodoc:
|
5
|
+
|
6
|
+
class AprHash < FFI::AutoPointer
|
7
|
+
|
8
|
+
# when used as key length, indicates that string length should be used
|
9
|
+
HASH_KEY_STRING = -1
|
10
|
+
|
11
|
+
include Enumerable
|
12
|
+
|
13
|
+
attr_accessor :keys_null_terminated
|
14
|
+
alias_method :keys_null_terminated?, :keys_null_terminated
|
15
|
+
|
16
|
+
def initialize( ptr, key_type, val_type, keys_null_terminated=true)
|
17
|
+
super( ptr )
|
18
|
+
@key_type = key_type
|
19
|
+
@val_type = val_type
|
20
|
+
@pointers = []
|
21
|
+
@keys_null_terminated = keys_null_terminated
|
22
|
+
end
|
23
|
+
|
24
|
+
class << self
|
25
|
+
# creates a new apr_hash_t that contains +contents+, if given
|
26
|
+
def create( key_type, val_type, pool=RootPool )
|
27
|
+
ptr = C.create( pool )
|
28
|
+
new( ptr, key_type, val_type )
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_from( rb_hash, key_type, val_type, pool=RootPool )
|
32
|
+
create( key_type, val_type, pool ).copy_from( rb_hash )
|
33
|
+
end
|
34
|
+
|
35
|
+
def release( ptr )
|
36
|
+
# memory will be released with the allocation pool
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
module C
|
41
|
+
extend FFI::Library
|
42
|
+
ffi_lib 'libapr-1.so.0'
|
43
|
+
|
44
|
+
typedef :pointer, :index
|
45
|
+
typedef :pointer, :out_pointer
|
46
|
+
typedef :long, :apr_ssize
|
47
|
+
typedef Pool, :pool
|
48
|
+
typedef AprHash, :hash
|
49
|
+
|
50
|
+
# lifecycle functions
|
51
|
+
# returns a :pointer instead of a :hash because AprHash#create needs to
|
52
|
+
# add extra args to the ruby instantiation
|
53
|
+
attach_function :create,
|
54
|
+
:apr_hash_make,
|
55
|
+
[ :pool ],
|
56
|
+
:pointer
|
57
|
+
|
58
|
+
# pool accessor -- returns a Pointer instead of a Pool because Pool
|
59
|
+
# objects will destroy the pool when they are garbage collected. If the
|
60
|
+
# pool was created in SVN, we should never destroy it and if the pool was
|
61
|
+
# created elsewhere, then we should let the original instance destroy it.
|
62
|
+
# This means that the function here should not actually return a Pool.
|
63
|
+
attach_function :pool,
|
64
|
+
:apr_hash_pool_get,
|
65
|
+
[ :hash ],
|
66
|
+
:pointer
|
67
|
+
|
68
|
+
# size
|
69
|
+
attach_function :count,
|
70
|
+
:apr_hash_count,
|
71
|
+
[ :hash ],
|
72
|
+
:uint
|
73
|
+
|
74
|
+
# getter/setter methods
|
75
|
+
attach_function :get,
|
76
|
+
:apr_hash_get,
|
77
|
+
# hash key klen
|
78
|
+
[ :hash, :pointer, :apr_ssize ],
|
79
|
+
:pointer
|
80
|
+
attach_function :set,
|
81
|
+
:apr_hash_set,
|
82
|
+
# hash key klen val (NULL = delete entry)
|
83
|
+
[ :hash, :pointer, :apr_ssize, :pointer ],
|
84
|
+
:void
|
85
|
+
|
86
|
+
# iteration functions
|
87
|
+
attach_function :first,
|
88
|
+
:apr_hash_first,
|
89
|
+
[ :pool, :hash ],
|
90
|
+
:index
|
91
|
+
attach_function :next,
|
92
|
+
:apr_hash_next,
|
93
|
+
[ :index ],
|
94
|
+
:index
|
95
|
+
attach_function :this,
|
96
|
+
:apr_hash_this,
|
97
|
+
[ :index, :out_pointer, :out_pointer, :out_pointer ],
|
98
|
+
:void
|
99
|
+
end
|
100
|
+
|
101
|
+
# use the above C module for the source of bound functions
|
102
|
+
bind_to C
|
103
|
+
|
104
|
+
# bound method definitions
|
105
|
+
bind :size, :to => :count
|
106
|
+
|
107
|
+
# because pool returns a Pointer and not a Pool as is expected, make it
|
108
|
+
# private so others can't use it.
|
109
|
+
bind :pool
|
110
|
+
private :pool
|
111
|
+
|
112
|
+
def each_pair
|
113
|
+
# outgoing pointers for keys and values
|
114
|
+
key_ptr = FFI::MemoryPointer.new( :pointer )
|
115
|
+
# apr_ssize_t => ssize_t => platform-specific long
|
116
|
+
len_ptr = FFI::MemoryPointer.new( :long )
|
117
|
+
val_ptr = FFI::MemoryPointer.new( :pointer )
|
118
|
+
|
119
|
+
# initialize a hash index to the first entry
|
120
|
+
iter = C.first( pool, self )
|
121
|
+
|
122
|
+
while !iter.null?
|
123
|
+
# get the key, key length, and val
|
124
|
+
C.this( iter, key_ptr, len_ptr, val_ptr )
|
125
|
+
|
126
|
+
# read the key
|
127
|
+
key_len = len_ptr.read_long
|
128
|
+
key = Utils.content_for( key_ptr.read_pointer, @key_type, key_len )
|
129
|
+
|
130
|
+
# yield the key and value
|
131
|
+
yield key, Utils.content_for( val_ptr.read_pointer, @val_type ) if block_given?
|
132
|
+
|
133
|
+
# advance to the next iteration
|
134
|
+
iter = C.next( iter )
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def []( key )
|
139
|
+
val = nil # keep val in scope
|
140
|
+
|
141
|
+
if key.is_a?( String ) && keys_null_terminated?
|
142
|
+
val = C.get( self, key, HASH_KEY_STRING )
|
143
|
+
elsif key.respond_to? :size
|
144
|
+
val = C.get( self, key, key.size )
|
145
|
+
elsif key.respond_to? :length
|
146
|
+
val = C.get( self, key, key.length )
|
147
|
+
else
|
148
|
+
raise ArgumentError, "Invalid key #{key}: cannot determine length"
|
149
|
+
end
|
150
|
+
|
151
|
+
Utils.content_for( val, @val_type )
|
152
|
+
end
|
153
|
+
|
154
|
+
def []=( key, val )
|
155
|
+
val_ptr = Utils.pointer_for( val, @val_type )
|
156
|
+
|
157
|
+
# because the pointers passed in are referenced in native code, keep
|
158
|
+
# track of the pointers so they aren't garbage collected until this hash
|
159
|
+
# is destroyed
|
160
|
+
@pointers << val_ptr
|
161
|
+
|
162
|
+
if key.is_a?( String ) && keys_null_terminated?
|
163
|
+
C.set( self, key, HASH_KEY_STRING, val_ptr )
|
164
|
+
elsif key.respond_to? :size
|
165
|
+
C.set( self, key, key.size, val_ptr )
|
166
|
+
elsif key.respond_to? :length
|
167
|
+
C.set( self, key, key.length, val_ptr )
|
168
|
+
else
|
169
|
+
raise ArgumentError, "Invalid key #{key}: cannot determine length"
|
170
|
+
end
|
171
|
+
|
172
|
+
val
|
173
|
+
end
|
174
|
+
|
175
|
+
def copy_from( rb_hash )
|
176
|
+
rb_hash.each_pair do |key, val|
|
177
|
+
self[ key ] = val
|
178
|
+
end
|
179
|
+
|
180
|
+
self
|
181
|
+
end
|
182
|
+
|
183
|
+
def to_h
|
184
|
+
rb_hash = {}
|
185
|
+
each_pair do |key, val|
|
186
|
+
rb_hash[ key ] = val
|
187
|
+
end
|
188
|
+
rb_hash
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
class AprArray < FFI::AutoPointer
|
194
|
+
|
195
|
+
include Enumerable
|
196
|
+
|
197
|
+
def initialize( ptr, type )
|
198
|
+
super( ptr )
|
199
|
+
@type = type
|
200
|
+
@pointers = []
|
201
|
+
end
|
202
|
+
|
203
|
+
class << self
|
204
|
+
# creates a new AprArray of +nelts+ elements of +type+; allocation is done
|
205
|
+
# in +pool+, which defaults to Svn::RootPool
|
206
|
+
def create( type, nelts, pool=RootPool )
|
207
|
+
ptr = C.create( pool, nelts, FFI::Pointer.size )
|
208
|
+
new( ptr, type )
|
209
|
+
end
|
210
|
+
|
211
|
+
def create_from( rb_arr, type, pool=RootPool )
|
212
|
+
create( type, rb_arr.size ).copy_from( rb_arr )
|
213
|
+
end
|
214
|
+
|
215
|
+
def release
|
216
|
+
# memory will be released with the allocation pool
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
module C
|
221
|
+
extend FFI::Library
|
222
|
+
ffi_lib 'libapr-1.so.0'
|
223
|
+
|
224
|
+
typedef :pointer, :index
|
225
|
+
typedef :pointer, :out_pointer
|
226
|
+
typedef :long, :apr_ssize
|
227
|
+
typedef Pool, :pool
|
228
|
+
typedef AprArray, :array
|
229
|
+
|
230
|
+
# allocation
|
231
|
+
# returns a :pointer instead of a :array because AprArray#create needs to
|
232
|
+
# add extra args to the ruby instantiation
|
233
|
+
attach_function :create,
|
234
|
+
:apr_array_make,
|
235
|
+
[ :pool, :int, :int ],
|
236
|
+
:pointer
|
237
|
+
|
238
|
+
# empty?
|
239
|
+
attach_function :is_empty,
|
240
|
+
:apr_is_empty_array,
|
241
|
+
[ :array ],
|
242
|
+
:int
|
243
|
+
|
244
|
+
# modifier methods
|
245
|
+
attach_function :push,
|
246
|
+
:apr_array_push,
|
247
|
+
[ :array ],
|
248
|
+
:pointer
|
249
|
+
|
250
|
+
attach_function :pop,
|
251
|
+
:apr_array_pop,
|
252
|
+
[ :array ],
|
253
|
+
:pointer
|
254
|
+
end
|
255
|
+
|
256
|
+
# helper procs for method binding
|
257
|
+
test_c_true = Proc.new { |i| i == 1 }
|
258
|
+
|
259
|
+
# use the above C module for the source of bound functions
|
260
|
+
bind_to C
|
261
|
+
|
262
|
+
# bound method definitions
|
263
|
+
bind :empty?, :to => :is_empty,
|
264
|
+
:before_return => test_c_true
|
265
|
+
|
266
|
+
def push( el )
|
267
|
+
# turn element into a pointer
|
268
|
+
ptr = Utils.pointer_for( el, @type )
|
269
|
+
# get the array element location and write ptr there
|
270
|
+
location = C.push( self )
|
271
|
+
location.write_pointer( ptr )
|
272
|
+
|
273
|
+
# because the pointers passed in are referenced in native code, keep
|
274
|
+
# track of the pointers so they aren't garbage collected until this hash
|
275
|
+
# is destroyed
|
276
|
+
@pointers << ptr
|
277
|
+
|
278
|
+
el
|
279
|
+
end
|
280
|
+
alias_method :add, :push
|
281
|
+
alias_method :<<, :push
|
282
|
+
|
283
|
+
def copy_from( arr )
|
284
|
+
arr.each do |el|
|
285
|
+
self << el
|
286
|
+
end
|
287
|
+
self
|
288
|
+
end
|
289
|
+
|
290
|
+
def pop
|
291
|
+
location = C.pop( self )
|
292
|
+
Utils.content_for( location.read_pointer, @type )
|
293
|
+
end
|
294
|
+
|
295
|
+
# these are commented out because they don't work. it doesn't look like APR
|
296
|
+
# array accesses happen this way.
|
297
|
+
# def []( num )
|
298
|
+
# num = num.to_i
|
299
|
+
# Utils.content_for( get_pointer( num * FFI::Pointer.size ), @type )
|
300
|
+
# end
|
301
|
+
#
|
302
|
+
# def []=( num, val )
|
303
|
+
# num = num.to_i
|
304
|
+
#
|
305
|
+
# ptr = Utils.pointer_for( val, @type )
|
306
|
+
# put_pointer( num * FFI::Pointer.size, ptr )
|
307
|
+
#
|
308
|
+
# val
|
309
|
+
# end
|
310
|
+
|
311
|
+
end
|
312
|
+
|
313
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'ffi'
|
3
|
+
|
4
|
+
module Svn #:nodoc:
|
5
|
+
|
6
|
+
# a struct for interacting with svn_string_t values
|
7
|
+
class CountedStringStruct < FFI::Struct
|
8
|
+
|
9
|
+
layout(
|
10
|
+
# because the data may not be NULL-terminated, treat it as a pointer
|
11
|
+
# and always read the string contents with FFI::Pointer#read_string
|
12
|
+
:data, :pointer,
|
13
|
+
:length, :size_t
|
14
|
+
)
|
15
|
+
|
16
|
+
# returns a new ruby String with the CountedString's contents.
|
17
|
+
def to_s
|
18
|
+
self[:data].read_string( self[:length] )
|
19
|
+
end
|
20
|
+
|
21
|
+
def inspect
|
22
|
+
to_s.inspect
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
# the svn_string_t pointer type, which is the one used externally
|
28
|
+
CountedString = CountedStringStruct.by_ref
|
29
|
+
|
30
|
+
def CountedString.from_string( content )
|
31
|
+
return content if content.is_a? CountedStringStruct
|
32
|
+
cstr = CountedStringStruct.new
|
33
|
+
cstr[:data] = FFI::MemoryPointer.from_string( content )
|
34
|
+
cstr[:length] = content.size
|
35
|
+
cstr
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
data/lib/svn/diffs.rb
ADDED
@@ -0,0 +1,209 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'stringio'
|
3
|
+
require 'ffi'
|
4
|
+
|
5
|
+
module Svn #:nodoc:
|
6
|
+
|
7
|
+
class Diff < FFI::AutoPointer
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def string_diff( original, modified, options={}, pool=RootPool )
|
11
|
+
options = FileOptions.from_hash( options ) if options.is_a? Hash
|
12
|
+
original = CountedString.from_string( original )
|
13
|
+
modified = CountedString.from_string( modified )
|
14
|
+
|
15
|
+
out = FFI::MemoryPointer.new( Diff )
|
16
|
+
|
17
|
+
Error.check_and_raise(
|
18
|
+
C.string_diff( out, original, modified, options, pool )
|
19
|
+
)
|
20
|
+
|
21
|
+
d = new( out.read_pointer )
|
22
|
+
|
23
|
+
return nil if d.null?
|
24
|
+
|
25
|
+
d.type = :string
|
26
|
+
d.original = original
|
27
|
+
d.modified = modified
|
28
|
+
d.options = options
|
29
|
+
d.pool = pool
|
30
|
+
|
31
|
+
return d
|
32
|
+
end
|
33
|
+
|
34
|
+
def file_diff( original_path, modified_path, options={}, pool=RootPool )
|
35
|
+
options = FileOptions.from_hash( options ) if options.is_a? Hash
|
36
|
+
|
37
|
+
out = FFI::MemoryPointer.new( Diff )
|
38
|
+
|
39
|
+
Error.check_and_raise(
|
40
|
+
C.file_diff( out, original_path, modified_path, options, pool )
|
41
|
+
)
|
42
|
+
|
43
|
+
d = new( out.read_pointer )
|
44
|
+
|
45
|
+
return nil if d.null?
|
46
|
+
|
47
|
+
d.type = :file
|
48
|
+
d.original_path = original_path
|
49
|
+
d.modified_path = modified_path
|
50
|
+
d.options = options
|
51
|
+
d.pool = pool
|
52
|
+
|
53
|
+
return d
|
54
|
+
end
|
55
|
+
|
56
|
+
def release( ptr )
|
57
|
+
# diff objects will probably need to keep track of the pool in which
|
58
|
+
# they are allocated so they can be freed in that pool
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
attr_accessor :type
|
63
|
+
attr_accessor :original
|
64
|
+
attr_accessor :original_path
|
65
|
+
attr_accessor :modified
|
66
|
+
attr_accessor :modified_path
|
67
|
+
attr_accessor :options
|
68
|
+
attr_accessor :pool
|
69
|
+
|
70
|
+
def changed?
|
71
|
+
( C.is_changed( self ) == 1 )
|
72
|
+
end
|
73
|
+
|
74
|
+
def conflicts?
|
75
|
+
( C.has_conflicts( self ) == 1 )
|
76
|
+
end
|
77
|
+
|
78
|
+
def unified( *args )
|
79
|
+
# keep these in scope
|
80
|
+
out_stream = nil
|
81
|
+
options = nil
|
82
|
+
pool = nil
|
83
|
+
|
84
|
+
case args.size
|
85
|
+
when 0
|
86
|
+
# use all defaults
|
87
|
+
when 1
|
88
|
+
if args.first.is_a? Hash
|
89
|
+
options = args.first
|
90
|
+
elsif args.first.is_a? IO or args.first.is_a? StringIO
|
91
|
+
out_stream = args.first
|
92
|
+
elsif args.first.is_a? Pool
|
93
|
+
pool = args.first
|
94
|
+
end
|
95
|
+
when 2, 3
|
96
|
+
out_stream, options, pool = args
|
97
|
+
else
|
98
|
+
raise ArgumentError, "wrong number of arguments (#{args.size} for 3)"
|
99
|
+
end
|
100
|
+
|
101
|
+
# defaults
|
102
|
+
out_stream ||= StringIO.new
|
103
|
+
options ||= {}
|
104
|
+
pool ||= RootPool
|
105
|
+
|
106
|
+
# get common options
|
107
|
+
encoding = options[:encoding] || 'utf-8'
|
108
|
+
original_header = options[:original_header]
|
109
|
+
modified_header = options[:modified_header]
|
110
|
+
|
111
|
+
case type
|
112
|
+
when :string
|
113
|
+
with_diff_header = ( options[:with_diff_header] ? 1 : 0 )
|
114
|
+
|
115
|
+
Error.check_and_raise( C.string_output_unified(
|
116
|
+
Svn::Stream.wrap_io( out_stream ), self,
|
117
|
+
original_header, modified_header, encoding,
|
118
|
+
original, modified, pool
|
119
|
+
) )
|
120
|
+
|
121
|
+
when :file
|
122
|
+
path_strip = options[:path_strip]
|
123
|
+
show_c_function = ( options[:show_c_function] ? 1 : 0 )
|
124
|
+
|
125
|
+
Error.check_and_raise( C.file_output_unified(
|
126
|
+
Svn::Stream.wrap_io( out_stream ), self,
|
127
|
+
original_path, modified_path,
|
128
|
+
original_header, modified_header, encoding, path_strip,
|
129
|
+
show_c_function, pool
|
130
|
+
) )
|
131
|
+
end
|
132
|
+
|
133
|
+
out_stream.rewind if out_stream.is_a? StringIO
|
134
|
+
|
135
|
+
out_stream
|
136
|
+
end
|
137
|
+
|
138
|
+
class FileOptionsStruct < FFI::Struct
|
139
|
+
layout(
|
140
|
+
:ignore_whitespace, :int, # :whitespace,
|
141
|
+
:ignore_eol_style, :int,
|
142
|
+
:show_c_function, :int
|
143
|
+
)
|
144
|
+
end
|
145
|
+
|
146
|
+
# create a mapped type for use elsewhere
|
147
|
+
FileOptions = FileOptionsStruct.by_ref
|
148
|
+
|
149
|
+
def FileOptions.from_hash( hash )
|
150
|
+
options = FileOptionsStruct.new
|
151
|
+
hash.each_pair do |key, val|
|
152
|
+
# add a check if key is in members?
|
153
|
+
options[key] = val
|
154
|
+
end
|
155
|
+
options
|
156
|
+
end
|
157
|
+
|
158
|
+
module C
|
159
|
+
|
160
|
+
extend FFI::Library
|
161
|
+
ffi_lib 'libsvn_diff-1.so.1'
|
162
|
+
|
163
|
+
typedef :pointer, :out_pointer
|
164
|
+
typedef Pool, :pool
|
165
|
+
typedef CError.by_ref, :error
|
166
|
+
typedef Stream, :stream
|
167
|
+
typedef FileOptions, :file_options
|
168
|
+
typedef CountedString, :counted_string
|
169
|
+
typedef Diff, :diff
|
170
|
+
|
171
|
+
enum :whitespace, [
|
172
|
+
:none, # do not ignore whitespace
|
173
|
+
:change, # treat as a single char
|
174
|
+
:all # ignore all whitespace chars
|
175
|
+
]
|
176
|
+
|
177
|
+
# diff functions
|
178
|
+
attach_function :file_diff,
|
179
|
+
:svn_diff_file_diff_2,
|
180
|
+
[ :out_pointer, :string, :string, :file_options, :pool ],
|
181
|
+
:error
|
182
|
+
|
183
|
+
attach_function :string_diff,
|
184
|
+
:svn_diff_mem_string_diff,
|
185
|
+
[ :out_pointer, :counted_string, :counted_string, :file_options,
|
186
|
+
:pool ],
|
187
|
+
:error
|
188
|
+
|
189
|
+
# diff inspection
|
190
|
+
attach_function :is_changed, :svn_diff_contains_diffs, [:diff], :int
|
191
|
+
attach_function :has_conflicts, :svn_diff_contains_conflicts, [:diff], :int
|
192
|
+
|
193
|
+
# output functions
|
194
|
+
attach_function :file_output_unified,
|
195
|
+
:svn_diff_file_output_unified3,
|
196
|
+
[ :stream, :diff, :string, :string, :string, :string, :string,
|
197
|
+
:string, :int, :pool ],
|
198
|
+
:error
|
199
|
+
|
200
|
+
attach_function :string_output_unified,
|
201
|
+
:svn_diff_mem_string_output_unified,
|
202
|
+
[ :stream, :diff, :string, :string, :string,
|
203
|
+
:counted_string, :counted_string, :pool ],
|
204
|
+
:error
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|