2 # repos.py: public Python interface for repos components
4 # Subversion is a tool for revision control.
5 # See http://subversion.tigris.org for more information.
7 ######################################################################
9 # Copyright (c) 2000-2004 CollabNet. All rights reserved.
11 # This software is licensed as described in the file COPYING, which
12 # you should have received as part of this distribution. The terms
13 # are also available at http://subversion.tigris.org/license-1.html.
14 # If newer versions of this license are posted there, you may use a
15 # newer version instead, at your option.
17 ######################################################################
19 from libsvn
.repos
import *
20 from svn
.core
import _unprefix_names
, Pool
21 _unprefix_names(locals(), 'svn_repos_')
22 _unprefix_names(locals(), 'SVN_REPOS_')
26 # Names that are not to be exported
27 import svn
.core
as _svncore
, svn
.fs
as _svnfs
, svn
.delta
as _svndelta
29 # Available change actions
30 CHANGE_ACTION_MODIFY
= 0
32 CHANGE_ACTION_DELETE
= 2
33 CHANGE_ACTION_REPLACE
= 3
37 __slots__
= [ 'item_kind', 'prop_changes', 'text_changed',
38 'base_path', 'base_rev', 'path', 'added', 'action',
41 item_kind
, prop_changes
, text_changed
, base_path
, base_rev
,
42 path
, added
, action
=None):
43 self
.item_kind
= item_kind
44 self
.prop_changes
= prop_changes
45 self
.text_changed
= text_changed
46 self
.base_path
= base_path
47 self
.base_rev
= base_rev
49 if action
not in [None, CHANGE_ACTION_MODIFY
, CHANGE_ACTION_ADD
,
50 CHANGE_ACTION_DELETE
, CHANGE_ACTION_REPLACE
]:
51 raise Exception, "unsupported change type"
54 ### it would be nice to avoid this flag. however, without it, it would
55 ### be quite difficult to distinguish between a change to the previous
56 ### revision (which has a base_path/base_rev) and a copy from some
57 ### other path/rev. a change in path is obviously add-with-history,
58 ### but the same path could be a change to the previous rev or a restore
59 ### of an older version. when it is "change to previous", I'm not sure
60 ### if the rev is always repos.rev - 1, or whether it represents the
61 ### created or time-of-checkout rev. so... we use a flag (for now)
62 ### Note: This flag is also set for replaced paths unlike self.action
63 ### which is either set to CHANGE_ACTION_ADD or CHANGE_ACTION_REPLACE
67 class ChangeCollector(_svndelta
.Editor
):
68 """Available Since: 1.2.0
71 # BATON FORMAT: [path, base_path, base_rev]
73 def __init__(self
, fs_ptr
, root
, pool
=None, notify_cb
=None):
75 self
.changes
= { } # path -> ChangedPathEntry()
76 self
.roots
= { } # revision -> svn_svnfs_root_t
77 self
.notify_cb
= notify_cb
81 # Figger out the base revision and root properties.
82 if _svnfs
.is_revision_root(self
.fs_root
):
83 rev
= _svnfs
.revision_root_revision(self
.fs_root
)
84 self
.base_rev
= rev
- 1
85 self
.props
= _svnfs
.revision_proplist(self
.fs_ptr
, rev
)
87 txn_name
= _svnfs
.txn_root_name(self
.fs_root
)
88 txn_t
= _svnfs
.open_txn(self
.fs_ptr
, txn_name
)
89 self
.base_rev
= _svnfs
.txn_base_revision(txn_t
)
90 self
.props
= _svnfs
.txn_proplist(txn_t
)
92 def get_root_props(self
):
95 def get_changes(self
):
98 def _send_change(self
, path
):
100 change
= self
.changes
.get(path
)
102 self
.notify_cb(change
)
104 def _make_base_path(self
, parent_path
, path
):
105 idx
= path
.rfind('/')
107 parent_path
= parent_path
+ '/'
109 return parent_path
+ path
110 return parent_path
+ path
[idx
+1:]
112 def _get_root(self
, rev
):
114 return self
.roots
[rev
]
117 root
= self
.roots
[rev
] = _svnfs
.revision_root(self
.fs_ptr
, rev
)
120 def open_root(self
, base_revision
, dir_pool
=None):
121 return ('', '', self
.base_rev
) # dir_baton
123 def delete_entry(self
, path
, revision
, parent_baton
, pool
=None):
124 base_path
= self
._make
_base
_path
(parent_baton
[1], path
)
125 if _svnfs
.is_dir(self
._get
_root
(parent_baton
[2]), base_path
):
126 item_type
= _svncore
.svn_node_dir
128 item_type
= _svncore
.svn_node_file
129 self
.changes
[path
] = ChangedPath(item_type
,
133 parent_baton
[2], # base_rev
136 CHANGE_ACTION_DELETE
,
138 self
._send
_change
(path
)
140 def add_directory(self
, path
, parent_baton
,
141 copyfrom_path
, copyfrom_revision
, dir_pool
=None):
142 action
= self
.changes
.has_key(path
) and CHANGE_ACTION_REPLACE \
144 self
.changes
[path
] = ChangedPath(_svncore
.svn_node_dir
,
147 copyfrom_path
, # base_path
148 copyfrom_revision
, # base_rev
153 if copyfrom_path
and (copyfrom_revision
!= -1):
154 base_path
= copyfrom_path
157 base_rev
= copyfrom_revision
158 return (path
, base_path
, base_rev
) # dir_baton
160 def open_directory(self
, path
, parent_baton
, base_revision
, dir_pool
=None):
161 base_path
= self
._make
_base
_path
(parent_baton
[1], path
)
162 return (path
, base_path
, parent_baton
[2]) # dir_baton
164 def change_dir_prop(self
, dir_baton
, name
, value
, pool
=None):
165 dir_path
= dir_baton
[0]
166 if self
.changes
.has_key(dir_path
):
167 self
.changes
[dir_path
].prop_changes
= True
169 # can't be added or deleted, so this must be CHANGED
170 self
.changes
[dir_path
] = ChangedPath(_svncore
.svn_node_dir
,
173 dir_baton
[1], # base_path
174 dir_baton
[2], # base_rev
177 CHANGE_ACTION_MODIFY
,
180 def add_file(self
, path
, parent_baton
,
181 copyfrom_path
, copyfrom_revision
, file_pool
=None):
182 action
= self
.changes
.has_key(path
) and CHANGE_ACTION_REPLACE \
184 self
.changes
[path
] = ChangedPath(_svncore
.svn_node_file
,
187 copyfrom_path
, # base_path
188 copyfrom_revision
, # base_rev
193 if copyfrom_path
and (copyfrom_revision
!= -1):
194 base_path
= copyfrom_path
197 base_rev
= copyfrom_revision
198 return (path
, base_path
, base_rev
) # file_baton
200 def open_file(self
, path
, parent_baton
, base_revision
, file_pool
=None):
201 base_path
= self
._make
_base
_path
(parent_baton
[1], path
)
202 return (path
, base_path
, parent_baton
[2]) # file_baton
204 def apply_textdelta(self
, file_baton
, base_checksum
):
205 file_path
= file_baton
[0]
206 if self
.changes
.has_key(file_path
):
207 self
.changes
[file_path
].text_changed
= True
209 # an add would have inserted a change record already, and it can't
210 # be a delete with a text delta, so this must be a normal change.
211 self
.changes
[file_path
] = ChangedPath(_svncore
.svn_node_file
,
214 file_baton
[1], # base_path
215 file_baton
[2], # base_rev
218 CHANGE_ACTION_MODIFY
,
224 def change_file_prop(self
, file_baton
, name
, value
, pool
=None):
225 file_path
= file_baton
[0]
226 if self
.changes
.has_key(file_path
):
227 self
.changes
[file_path
].prop_changes
= True
229 # an add would have inserted a change record already, and it can't
230 # be a delete with a prop change, so this must be a normal change.
231 self
.changes
[file_path
] = ChangedPath(_svncore
.svn_node_file
,
234 file_baton
[1], # base_path
235 file_baton
[2], # base_rev
238 CHANGE_ACTION_MODIFY
,
240 def close_directory(self
, dir_baton
):
241 self
._send
_change
(dir_baton
[0])
243 def close_file(self
, file_baton
, text_checksum
):
244 self
._send
_change
(file_baton
[0])
247 class RevisionChangeCollector(ChangeCollector
):
248 """Deprecated: Use ChangeCollector.
249 This is a compatibility wrapper providing the interface of the
250 Subversion 1.1.x and earlier bindings.
252 Important difference: base_path members have a leading '/' character in
255 def __init__(self
, fs_ptr
, root
, pool
=None, notify_cb
=None):
256 root
= _svnfs
.revision_root(fs_ptr
, root
)
257 ChangeCollector
.__init
__(self
, fs_ptr
, root
, pool
, notify_cb
)
259 def _make_base_path(self
, parent_path
, path
):
260 idx
= path
.rfind('/')
262 return parent_path
+ '/' + path
263 return parent_path
+ path
[idx
:]
266 # enable True/False in older vsns of Python