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/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
|