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.
- 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/lib/svn/errors.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'ffi'
|
3
|
+
|
4
|
+
module Svn
|
5
|
+
|
6
|
+
class Error < RuntimeError
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
# checks error and raises an exception for error if necessary
|
11
|
+
def check_and_raise( err )
|
12
|
+
return if err.null?
|
13
|
+
raise Error.new( err )
|
14
|
+
end
|
15
|
+
|
16
|
+
# returns a proc that calls check_and_raise
|
17
|
+
def return_check
|
18
|
+
@@return_check ||= Proc.new { |ptr| Error.check_and_raise( ptr ) }
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :c_error
|
24
|
+
|
25
|
+
def initialize( message_or_c_error )
|
26
|
+
if message_or_c_error.is_a? CError
|
27
|
+
super( message_or_c_error.best_message )
|
28
|
+
@c_error = message_or_c_error
|
29
|
+
else
|
30
|
+
super( message_or_c_error )
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
class CError < FFI::ManagedStruct
|
37
|
+
layout(
|
38
|
+
:apr_error, :int,
|
39
|
+
:message, :string,
|
40
|
+
:child, :pointer,
|
41
|
+
:pool, :pointer,
|
42
|
+
:filename, :string,
|
43
|
+
:line, :long
|
44
|
+
)
|
45
|
+
|
46
|
+
class << self
|
47
|
+
def release( ptr )
|
48
|
+
C.clear( ptr )
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# returns the most accurate message for an error
|
53
|
+
def best_message
|
54
|
+
# create a buffer, which may be used to hold the best message
|
55
|
+
buf = FFI::MemoryPointer.new( 1024 )
|
56
|
+
msg = C.best_message( self, buf, 1024 )
|
57
|
+
|
58
|
+
# return a duplicate of msg, since it may be stored in the buffer
|
59
|
+
# allocated above
|
60
|
+
msg.dup
|
61
|
+
end
|
62
|
+
|
63
|
+
module C
|
64
|
+
extend FFI::Library
|
65
|
+
ffi_lib 'libsvn_subr-1.so.1'
|
66
|
+
|
67
|
+
typedef CError.by_ref, :error
|
68
|
+
typedef :int, :size
|
69
|
+
|
70
|
+
attach_function :best_message,
|
71
|
+
:svn_err_best_message,
|
72
|
+
[ :error, :buffer_inout, :size ],
|
73
|
+
:string
|
74
|
+
|
75
|
+
attach_function :clear,
|
76
|
+
:svn_error_clear,
|
77
|
+
[ :error ],
|
78
|
+
:void
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
data/lib/svn/logs.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'ffi'
|
4
|
+
|
5
|
+
module Svn #:nodoc:
|
6
|
+
|
7
|
+
module Log
|
8
|
+
|
9
|
+
# description of a changed path
|
10
|
+
class ChangedPathStruct < FFI::Struct
|
11
|
+
layout(
|
12
|
+
:action, :char, # 'A'dd, 'D'elete, 'R'eplace, 'M'odify
|
13
|
+
:copyfrom_path, :string,
|
14
|
+
:copyfrom_rev, :long,
|
15
|
+
:node_kind, NodeKind
|
16
|
+
)
|
17
|
+
|
18
|
+
# returns a character that represents the type of the change: :added,
|
19
|
+
# :deleted, :replaced, :modified
|
20
|
+
def action
|
21
|
+
Actions[ self[:action] ]
|
22
|
+
end
|
23
|
+
|
24
|
+
# returns the path's node type (:none, :file, :dir, :unknown)
|
25
|
+
def kind
|
26
|
+
self[:node_kind]
|
27
|
+
end
|
28
|
+
|
29
|
+
# returns whether the "copied from" values are known
|
30
|
+
def copyfrom_known?
|
31
|
+
( self[:copyfrom_rev] >= 0 )
|
32
|
+
end
|
33
|
+
|
34
|
+
# if the node was copied from another path/rev, returns the [path, rev]
|
35
|
+
# pair or nil otherwise
|
36
|
+
def copied_from
|
37
|
+
[ self[:copyfrom_path], self[:copyfrom_rev] ] if copyfrom_known?
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_h
|
41
|
+
h = { :action => action, :kind => kind }
|
42
|
+
h[:copied_from] = copied_from if copyfrom_known?
|
43
|
+
h
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# create a mapped type for use elsewhere
|
48
|
+
ChangedPath = ChangedPathStruct.by_ref
|
49
|
+
|
50
|
+
# A subversion log entry
|
51
|
+
class EntryStruct < FFI::Struct
|
52
|
+
layout(
|
53
|
+
:old_changed_paths, AprHash.factory( :string, :pointer ),
|
54
|
+
:rev, :long,
|
55
|
+
:rev_props, AprHash.factory( :string, [:pointer, :string] ),
|
56
|
+
:has_children, :int,
|
57
|
+
:changed_paths, AprHash.factory( :string, ChangedPath )
|
58
|
+
)
|
59
|
+
|
60
|
+
# returns the revision number
|
61
|
+
def rev
|
62
|
+
self[:rev]
|
63
|
+
end
|
64
|
+
alias_method :num, :rev
|
65
|
+
|
66
|
+
# returns whether this revision has children
|
67
|
+
def has_children?
|
68
|
+
( self[:has_children] == 1 )
|
69
|
+
end
|
70
|
+
|
71
|
+
# returns a Hash of the revision's changed paths
|
72
|
+
def changed_paths
|
73
|
+
@changed_paths ||= ( self[:changed_paths].null? ? nil :
|
74
|
+
self[:changed_paths].to_h.tap { |by_path|
|
75
|
+
by_path.each_key { |k| by_path[k] = by_path[k].to_h }
|
76
|
+
}
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
# returns a Hash of the revision's properties
|
81
|
+
def props
|
82
|
+
@props ||= ( self[:rev_props].null? ? nil : self[:rev_props].to_h )
|
83
|
+
end
|
84
|
+
|
85
|
+
# return the revision's log message
|
86
|
+
def message
|
87
|
+
props[ LOG_PROP_NAME ] if props
|
88
|
+
end
|
89
|
+
alias_method :log, :message
|
90
|
+
|
91
|
+
# return the revision's author
|
92
|
+
def author
|
93
|
+
props[ AUTHOR_PROP_NAME ] if props
|
94
|
+
end
|
95
|
+
|
96
|
+
# return the Time that this revision was committed
|
97
|
+
def timestamp
|
98
|
+
Time.parse( props[ TIMESTAMP_PROP_NAME ] ) if props
|
99
|
+
end
|
100
|
+
|
101
|
+
# get the contents of this log entry as a multi-level hash
|
102
|
+
def to_h
|
103
|
+
h = {
|
104
|
+
:rev => rev,
|
105
|
+
:log => message,
|
106
|
+
:author => author,
|
107
|
+
:timestamp => timestamp,
|
108
|
+
:has_children? => has_children?,
|
109
|
+
}
|
110
|
+
h[:changed_paths] = changed_paths if changed_paths
|
111
|
+
h
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# create a mapped type for use elsewhere
|
116
|
+
Entry = EntryStruct.by_ref
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
data/lib/svn/misc.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'ffi'
|
3
|
+
|
4
|
+
module Svn #:nodoc:
|
5
|
+
|
6
|
+
extend FFI::Library
|
7
|
+
|
8
|
+
NodeKind = enum( :none, :file, :dir, :unknown )
|
9
|
+
|
10
|
+
Actions = enum(
|
11
|
+
:added, 65,
|
12
|
+
:deleted, 68,
|
13
|
+
:replaced, 82,
|
14
|
+
:modified, 77
|
15
|
+
)
|
16
|
+
|
17
|
+
PathChangeKind = enum( :modified, :added, :deleted, :replaced, :reset )
|
18
|
+
|
19
|
+
class ChangedPathStruct < FFI::Struct
|
20
|
+
layout(
|
21
|
+
:id, :pointer,
|
22
|
+
:change_kind, PathChangeKind,
|
23
|
+
:text_mods, :int,
|
24
|
+
:prop_mods, :int,
|
25
|
+
:node_kind, NodeKind,
|
26
|
+
:copyfrom_known, :int,
|
27
|
+
:copyfrom_rev, :long,
|
28
|
+
:copyfrom_path, :string
|
29
|
+
)
|
30
|
+
|
31
|
+
def change_kind
|
32
|
+
self[ :change_kind ]
|
33
|
+
end
|
34
|
+
|
35
|
+
def node_kind
|
36
|
+
self[ :node_kind ]
|
37
|
+
end
|
38
|
+
|
39
|
+
def text_mods?
|
40
|
+
( self[ :text_mods ] == 1 )
|
41
|
+
end
|
42
|
+
|
43
|
+
def prop_mods?
|
44
|
+
( self[ :prop_mods ] == 1 )
|
45
|
+
end
|
46
|
+
|
47
|
+
def copyfrom_known?
|
48
|
+
( self[ :copyfrom_known ] == 1 )
|
49
|
+
end
|
50
|
+
|
51
|
+
def copied_from
|
52
|
+
[ self[:copyfrom_path], self[:copyfrom_rev] ] unless copyfrom_known?
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_h
|
56
|
+
h = {
|
57
|
+
:change_kind => change_kind,
|
58
|
+
:node_kind => node_kind,
|
59
|
+
:text_mods? => text_mods?,
|
60
|
+
:prop_mods? => prop_mods?,
|
61
|
+
}
|
62
|
+
h[:copied_from] = copied_from if copyfrom_known?
|
63
|
+
h
|
64
|
+
end
|
65
|
+
end
|
66
|
+
ChangedPath = ChangedPathStruct.by_ref
|
67
|
+
|
68
|
+
end
|
data/lib/svn/pools.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'ffi'
|
3
|
+
|
4
|
+
module Svn #:nodoc:
|
5
|
+
|
6
|
+
# APR memory pool
|
7
|
+
class Pool < FFI::AutoPointer
|
8
|
+
|
9
|
+
class << self
|
10
|
+
# create a new pool in +parent+. if +parent+ is nil, the allocate a root pool.
|
11
|
+
def create( parent=nil )
|
12
|
+
out = FFI::MemoryPointer.new( :pointer )
|
13
|
+
C.create( out, parent, C::RaiseOutOfMemory, nil )
|
14
|
+
return new( out.read_pointer )
|
15
|
+
end
|
16
|
+
|
17
|
+
# free and release a pool
|
18
|
+
def release( ptr )
|
19
|
+
C.destroy( ptr )
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module C
|
24
|
+
extend FFI::Library
|
25
|
+
ffi_lib 'libapr-1.so.0'
|
26
|
+
|
27
|
+
typedef :int, :apr_status
|
28
|
+
typedef Pool, :pool
|
29
|
+
|
30
|
+
callback :abort_function, [:apr_status], :int
|
31
|
+
|
32
|
+
# life-cycle methods
|
33
|
+
attach_function :initialize, :apr_initialize, [], :apr_status
|
34
|
+
|
35
|
+
attach_function :create,
|
36
|
+
:apr_pool_create_ex,
|
37
|
+
[:pointer, :pool, :abort_function, :pointer],
|
38
|
+
:apr_status
|
39
|
+
|
40
|
+
attach_function :clear, :apr_pool_clear, [:pool], :void
|
41
|
+
|
42
|
+
attach_function :destroy, :apr_pool_destroy, [:pool], :void
|
43
|
+
|
44
|
+
# instance methods
|
45
|
+
# apr_pool_cleanup_register
|
46
|
+
# apr_pool_userdata_set
|
47
|
+
# apr_pool_userdata_get
|
48
|
+
|
49
|
+
RaiseOutOfMemory = FFI::Function.new( :int, [:int] ) do |status|
|
50
|
+
raise NoMemoryError.new('Could not allocate new pool')
|
51
|
+
end
|
52
|
+
|
53
|
+
initialize
|
54
|
+
end
|
55
|
+
|
56
|
+
# use the C module for all bound methods
|
57
|
+
bind_to C
|
58
|
+
|
59
|
+
# clear all allocated memory in the pool so it can be reused
|
60
|
+
bind :clear
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
# create the root pool that will be the default pool
|
65
|
+
RootPool = Pool.create
|
66
|
+
|
67
|
+
end
|
data/lib/svn/repos.rb
ADDED
@@ -0,0 +1,333 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'ffi'
|
3
|
+
|
4
|
+
module Svn #:nodoc:
|
5
|
+
|
6
|
+
INVALID_REVNUM = -1
|
7
|
+
|
8
|
+
# A subverion repository object
|
9
|
+
#
|
10
|
+
# This represents both the repository and the filesystem
|
11
|
+
class Repo < FFI::AutoPointer
|
12
|
+
|
13
|
+
attr_reader :pool
|
14
|
+
|
15
|
+
def initialize( ptr, pool )
|
16
|
+
super( ptr )
|
17
|
+
@pool = pool
|
18
|
+
end
|
19
|
+
|
20
|
+
class << self
|
21
|
+
#--
|
22
|
+
# several methods remove trailing separators; this is to avoid triggering
|
23
|
+
# assertions in SVN libs
|
24
|
+
#++
|
25
|
+
def open( path, parent=RootPool )
|
26
|
+
# get a new pool for all interactions with this repository
|
27
|
+
pool = Pool.create( parent )
|
28
|
+
|
29
|
+
# TODO: we may need to call find_root_path for this, if C.open expects
|
30
|
+
# an exact repository root path
|
31
|
+
out = FFI::MemoryPointer.new( :pointer )
|
32
|
+
|
33
|
+
Error.check_and_raise(
|
34
|
+
C.open( out, path.chomp(File::SEPARATOR), pool )
|
35
|
+
)
|
36
|
+
|
37
|
+
new( out.read_pointer, pool )
|
38
|
+
end
|
39
|
+
|
40
|
+
def create( path, parent=RootPool )
|
41
|
+
# get a new pool for all interactions with this repository
|
42
|
+
pool = Pool.create( parent )
|
43
|
+
|
44
|
+
out = FFI::MemoryPointer.new( :pointer )
|
45
|
+
|
46
|
+
Error.check_and_raise(
|
47
|
+
C.create(
|
48
|
+
out, # an out pointer to the newly created repository
|
49
|
+
path.chomp(File::SEPARATOR), # path on disk
|
50
|
+
nil, nil, # unused
|
51
|
+
nil, nil, # configs are not implemeted
|
52
|
+
pool # the pool to use for any allocations
|
53
|
+
)
|
54
|
+
)
|
55
|
+
|
56
|
+
new( out.read_pointer, pool )
|
57
|
+
end
|
58
|
+
|
59
|
+
def delete( path, pool=RootPool )
|
60
|
+
Error.check_and_raise(
|
61
|
+
C.delete( path.chomp(File::SEPARATOR), pool )
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
# this release method does nothing because the repo will be released when
|
66
|
+
# its pool is destroyed
|
67
|
+
def release( ptr )
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# A filesystem object. Repositories completely encapsulate the filesystem,
|
72
|
+
# so it is unnecessary to use it directly.
|
73
|
+
class FileSystem < FFI::AutoPointer
|
74
|
+
class << self
|
75
|
+
# this release method does nothing because the filesystem will be
|
76
|
+
# released with its pool, which is associated with the repo that owns
|
77
|
+
# the filesystem
|
78
|
+
def release( ptr )
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# returns the repository's filesystem
|
84
|
+
#
|
85
|
+
# this is for internal use because Repo objects should handle all fs
|
86
|
+
# functionality
|
87
|
+
def filesystem
|
88
|
+
@fs ||= C.filesystem( self )
|
89
|
+
end
|
90
|
+
alias_method :fs, :filesystem
|
91
|
+
|
92
|
+
module C
|
93
|
+
extend FFI::Library
|
94
|
+
ffi_lib 'libsvn_repos-1.so.1'
|
95
|
+
|
96
|
+
# convenience pointers
|
97
|
+
typedef :pointer, :out_pointer
|
98
|
+
typedef :string, :path
|
99
|
+
typedef :long, :revnum
|
100
|
+
typedef :int, :bool
|
101
|
+
|
102
|
+
# data types
|
103
|
+
typedef Pool, :pool
|
104
|
+
typedef AprHash, :hash
|
105
|
+
typedef AprArray, :array
|
106
|
+
typedef CError.by_ref, :error
|
107
|
+
typedef FileSystem, :filesystem
|
108
|
+
typedef Log::Entry, :log_entry
|
109
|
+
typedef Root, :root
|
110
|
+
typedef Revision, :revision
|
111
|
+
#typedef Transaction, :transaction
|
112
|
+
typedef Repo, :repo
|
113
|
+
|
114
|
+
callback :history_function,
|
115
|
+
[ :pointer, :path, :revnum, :pool ],
|
116
|
+
:error
|
117
|
+
|
118
|
+
CollectChanges = FFI::Function.new(
|
119
|
+
:pointer, [ :pointer, :string, :long, :pointer ]
|
120
|
+
) do |data_ptr, path, rev, pool|
|
121
|
+
arr = Utils.unwrap( data_ptr )
|
122
|
+
arr << [ path, rev ]
|
123
|
+
nil # no error
|
124
|
+
end
|
125
|
+
|
126
|
+
callback :authz_function,
|
127
|
+
[ :out_pointer, :root, :path, :pointer, :pool ],
|
128
|
+
:error
|
129
|
+
|
130
|
+
callback :log_entry_function,
|
131
|
+
[ :pointer, :log_entry, :pool ],
|
132
|
+
:error
|
133
|
+
|
134
|
+
CollectHistory = FFI::Function.new(
|
135
|
+
:pointer, [ :pointer, :pointer, :pointer ]
|
136
|
+
) do |data_ptr, log_entry_ptr, pool|
|
137
|
+
arr = Utils.unwrap( data_ptr )
|
138
|
+
# the struct passed here is shared for all calls and the data is freed
|
139
|
+
# and overwritten, so the data from each Log::Entry struct needs to be
|
140
|
+
# copied out using :to_h
|
141
|
+
arr << Utils.content_for( log_entry_ptr, Log::Entry ).to_h
|
142
|
+
nil # no error
|
143
|
+
end
|
144
|
+
|
145
|
+
# misc functions
|
146
|
+
attach_function :find,
|
147
|
+
:svn_repos_find_root_path,
|
148
|
+
[ :path, :pool ],
|
149
|
+
:string
|
150
|
+
|
151
|
+
# repository functions
|
152
|
+
attach_function :open,
|
153
|
+
:svn_repos_open,
|
154
|
+
[ :out_pointer, :path, :pool ],
|
155
|
+
:error
|
156
|
+
attach_function :create,
|
157
|
+
:svn_repos_create,
|
158
|
+
[ :out_pointer, :path,
|
159
|
+
:pointer, :pointer, # both unused
|
160
|
+
:hash, :hash, # config, fs-config
|
161
|
+
:pool ],
|
162
|
+
:error
|
163
|
+
attach_function :delete,
|
164
|
+
:svn_repos_delete,
|
165
|
+
[ :path, :pool ],
|
166
|
+
:error
|
167
|
+
|
168
|
+
# filesystem accessor
|
169
|
+
attach_function :filesystem,
|
170
|
+
:svn_repos_fs,
|
171
|
+
[ :repo ],
|
172
|
+
:filesystem
|
173
|
+
|
174
|
+
# repository-level inspection
|
175
|
+
attach_function :history,
|
176
|
+
:svn_repos_history2,
|
177
|
+
[ :filesystem, :path,
|
178
|
+
:history_function, :pointer, # history callback and data
|
179
|
+
:authz_function, :pointer, # authz callback and data
|
180
|
+
:revnum, :revnum, # start rev and end rev
|
181
|
+
:bool, :pool ], # cross copies?
|
182
|
+
:error
|
183
|
+
|
184
|
+
attach_function :logs,
|
185
|
+
:svn_repos_get_logs4,
|
186
|
+
[ :repo,
|
187
|
+
:array, # file paths
|
188
|
+
:revnum, :revnum, :int, # start rev, end rev, and limit
|
189
|
+
:bool, # discover changed paths?
|
190
|
+
:bool, # strict history? strict = no cross copies
|
191
|
+
:bool, # include merged revisions?
|
192
|
+
:array, # rev-prop names to get; NULL=all, []=none
|
193
|
+
:authz_function, :pointer, # authz callback and data
|
194
|
+
:log_entry_function, :pointer, # receiver callback and data
|
195
|
+
:pool
|
196
|
+
],
|
197
|
+
:error
|
198
|
+
|
199
|
+
# transaction (root) accessor, creation, and manipulation
|
200
|
+
#attach_function :create_transaction,
|
201
|
+
# :svn_repos_fs_begin_txn_for_commit2,
|
202
|
+
# [ :out_pointer, :repo, :revnum, :hash, :pool ],
|
203
|
+
# :error
|
204
|
+
#attach_function :commit_transaction,
|
205
|
+
# :svn_repos_fs_commit_txn,
|
206
|
+
# [ :out_pointer, :repo, :out_pointer, :transaction ],
|
207
|
+
# :error
|
208
|
+
|
209
|
+
ffi_lib 'libsvn_fs-1.so.1'
|
210
|
+
|
211
|
+
# youngest revision number accessor
|
212
|
+
attach_function :youngest,
|
213
|
+
:svn_fs_youngest_rev,
|
214
|
+
[ :out_pointer, :filesystem, :pool ],
|
215
|
+
:error
|
216
|
+
|
217
|
+
# revision (root) accessor
|
218
|
+
attach_function :revision,
|
219
|
+
:svn_fs_revision_root,
|
220
|
+
[ :out_pointer, :filesystem, :revnum, :pool ],
|
221
|
+
:error
|
222
|
+
|
223
|
+
#attach_function :open_transaction,
|
224
|
+
# :svn_fs_begin_txn2,
|
225
|
+
# [ :out_pointer, :filesystem, :string, :pool ],
|
226
|
+
# :error
|
227
|
+
end
|
228
|
+
|
229
|
+
use_fs_and_add_pool = Proc.new { |out, this, *args|
|
230
|
+
[ out, fs, *args, pool ]
|
231
|
+
}
|
232
|
+
|
233
|
+
# use the above C module for the source of bound functions
|
234
|
+
bind_to C
|
235
|
+
|
236
|
+
# returns the number of the youngest revision in the repository
|
237
|
+
bind :youngest,
|
238
|
+
:returning => :long,
|
239
|
+
:before_return => Proc.new { |rev| revision(rev) },
|
240
|
+
:validate => Error.return_check,
|
241
|
+
&use_fs_and_add_pool
|
242
|
+
|
243
|
+
alias_method :latest, :youngest
|
244
|
+
|
245
|
+
# returns a Revision for rev +num+ or raises Svn::Error if the revision
|
246
|
+
# does not exist
|
247
|
+
bind :revision,
|
248
|
+
:returning => :pointer,
|
249
|
+
:before_return => Proc.new { |ptr|
|
250
|
+
Revision.new( ptr, self, pool ) unless ptr.null?
|
251
|
+
},
|
252
|
+
:validate => Error.return_check,
|
253
|
+
&use_fs_and_add_pool
|
254
|
+
|
255
|
+
# returns an array of "interesting" [path, rev] pairs for path
|
256
|
+
#
|
257
|
+
# for more detailed information, use +history+
|
258
|
+
#
|
259
|
+
# if a block is given, each pair will be yielded
|
260
|
+
#
|
261
|
+
# options:
|
262
|
+
# +start_rev+ :: restricts revisions to newer than :start_rev
|
263
|
+
# +end_rev+ :: restricts revisions to older than :end_rev
|
264
|
+
# +cross_copies+ :: continue history at filesystem copies?
|
265
|
+
def changes( path, options={}, &block )
|
266
|
+
# ensure the options can be passed to C successfully
|
267
|
+
start_rev = (options[:start_rev] || 0).to_i
|
268
|
+
end_rev = (options[:end_rev] || youngest).to_i
|
269
|
+
cross_copies = ( options[:cross_copies] ? 1 : 0 )
|
270
|
+
|
271
|
+
# collect the change [path, rev] pairs
|
272
|
+
changes = []
|
273
|
+
Error.check_and_raise( C.history(
|
274
|
+
fs, path,
|
275
|
+
C::CollectChanges, Utils.wrap( changes ),
|
276
|
+
nil, nil, start_rev, end_rev, cross_copies, pool
|
277
|
+
) )
|
278
|
+
|
279
|
+
# if the caller supplied a block, use it
|
280
|
+
changes.each( &block ) if block_given?
|
281
|
+
|
282
|
+
changes
|
283
|
+
end
|
284
|
+
|
285
|
+
# returns an array of log entry hashes for relevant revisions, containing:
|
286
|
+
# * revision number (+rev+), +log+, +author+, and +timestamp+
|
287
|
+
#
|
288
|
+
# if a block is given, each log entry hash will be yielded
|
289
|
+
#
|
290
|
+
# options:
|
291
|
+
# +start_rev+ :: restricts revisions to newer than :start_rev
|
292
|
+
# +end_rev+ :: restricts revisions to older than :end_rev
|
293
|
+
# +cross_copies+ :: continue history at filesystem copies?
|
294
|
+
# +include_merged+ :: include merged history?
|
295
|
+
# +include_changes+ :: include change info in +:changed_paths+?
|
296
|
+
#
|
297
|
+
# starting and ending revisions can be switched to reverse the final order
|
298
|
+
def history( paths=nil, options={}, &block )
|
299
|
+
# if no paths were passed, but options were, then paths will be a hash
|
300
|
+
if paths.is_a? Hash
|
301
|
+
options = paths
|
302
|
+
paths = nil
|
303
|
+
end
|
304
|
+
|
305
|
+
# ensure the options can be passed to C successfully
|
306
|
+
paths_c_arr = AprArray.create_from( Array( paths ), :string )
|
307
|
+
start_rev = (options[:start_rev] || 0).to_i
|
308
|
+
end_rev = (options[:end_rev] || youngest).to_i
|
309
|
+
limit = (options[:limit] || 0).to_i # 0 => all entries
|
310
|
+
discover_changed_paths = ( options[:include_changes] ? 1 : 0 )
|
311
|
+
strict_history = ( options[:cross_copies] ? 0 : 1 )
|
312
|
+
include_merged = ( options[:include_merged] ? 1 : 0 )
|
313
|
+
|
314
|
+
# collect the history entries
|
315
|
+
history = []
|
316
|
+
Error.check_and_raise( C.logs(
|
317
|
+
self, paths_c_arr,
|
318
|
+
start_rev, end_rev, limit,
|
319
|
+
discover_changed_paths, strict_history, include_merged,
|
320
|
+
nil, nil, nil,
|
321
|
+
C::CollectHistory, Utils.wrap( history ),
|
322
|
+
pool
|
323
|
+
) )
|
324
|
+
|
325
|
+
# if the caller supplied a block, use it
|
326
|
+
history.each( &block ) if block_given?
|
327
|
+
|
328
|
+
history
|
329
|
+
end
|
330
|
+
|
331
|
+
end
|
332
|
+
|
333
|
+
end
|