Gentoo Archives: gentoo-commits

From: Brian Dolbec <dolsen@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] proj/portage:repoman commit in: pym/repoman/modules/vcs/cvs/, pym/repoman/modules/vcs/bzr/, ...
Date: Sat, 30 Jan 2016 08:00:32
Message-Id: 1454140222.7c06bfba625e89ccad8c8c565999893028e01119.dolsen@gentoo
1 commit: 7c06bfba625e89ccad8c8c565999893028e01119
2 Author: Brian Dolbec <dolsen <AT> gentoo <DOT> org>
3 AuthorDate: Sat Jan 30 01:58:36 2016 +0000
4 Commit: Brian Dolbec <dolsen <AT> gentoo <DOT> org>
5 CommitDate: Sat Jan 30 07:50:22 2016 +0000
6 URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=7c06bfba
7
8 repoman: Migrate actions.py vcs code to the vcs modules
9
10 Add missing changes code to the existing Changes classes.
11 This removes the duplicated code in the Actions class.
12 It alsosspeeds up the run by not calling the vcs binary to scan for changes a second time.
13 Optimize the code for the vcs modules.
14
15 pym/repoman/actions.py | 208 ++++---------------------------
16 pym/repoman/modules/vcs/None/__init__.py | 1 +
17 pym/repoman/modules/vcs/bzr/__init__.py | 1 +
18 pym/repoman/modules/vcs/bzr/changes.py | 15 ++-
19 pym/repoman/modules/vcs/changes.py | 13 ++
20 pym/repoman/modules/vcs/cvs/__init__.py | 1 +
21 pym/repoman/modules/vcs/cvs/changes.py | 13 +-
22 pym/repoman/modules/vcs/git/__init__.py | 1 +
23 pym/repoman/modules/vcs/git/changes.py | 20 +--
24 pym/repoman/modules/vcs/hg/__init__.py | 1 +
25 pym/repoman/modules/vcs/hg/changes.py | 27 +++-
26 pym/repoman/modules/vcs/settings.py | 5 +-
27 pym/repoman/modules/vcs/svn/__init__.py | 1 +
28 pym/repoman/modules/vcs/svn/changes.py | 24 +++-
29 14 files changed, 118 insertions(+), 213 deletions(-)
30
31 diff --git a/pym/repoman/actions.py b/pym/repoman/actions.py
32 index 3fd940c..8cf4a97 100644
33 --- a/pym/repoman/actions.py
34 +++ b/pym/repoman/actions.py
35 @@ -16,7 +16,6 @@ from itertools import chain
36 from _emerge.UserQuery import UserQuery
37
38 import portage
39 -from portage import cvstree
40 from portage import os
41 from portage import _encodings
42 from portage import _unicode_encode
43 @@ -26,8 +25,7 @@ from portage.package.ebuild.digestgen import digestgen
44 from portage.process import find_binary, spawn
45 from portage.util import writemsg_level
46
47 -from repoman._subprocess import repoman_popen, repoman_getstatusoutput
48 -from repoman.errors import err
49 +from repoman._subprocess import repoman_getstatusoutput
50 from repoman.gpg import gpgsign, need_signature
51 from repoman import utilities
52 from repoman.modules.vcs.vcs import vcs_files_to_cps
53 @@ -71,13 +69,11 @@ class Actions(object):
54
55
56 def perform(self, qa_output):
57 - myunadded, mydeleted = self._vcs_unadded()
58 + myautoadd = self._vcs_autoadd()
59
60 - myautoadd = self._vcs_autoadd(myunadded)
61 + self._vcs_deleted()
62
63 - self._vcs_deleted(mydeleted)
64 -
65 - changes = self.get_vcs_changed(mydeleted)
66 + changes = self.get_vcs_changed()
67
68 mynew, mychanged, myremoved, no_expansion, expansion = changes
69
70 @@ -127,8 +123,8 @@ class Actions(object):
71
72 print("* %s files being committed..." % green(str(len(myupdates))), end=' ')
73
74 - if self.vcs_settings.vcs not in ('cvs', 'svn'):
75 - # With git, bzr and hg, there's never any keyword expansion, so
76 + if self.vcs_settings.needs_keyword_expansion:
77 + # With some VCS types there's never any keyword expansion, so
78 # there's no need to regenerate manifests and all files will be
79 # committed in one big commit at the end.
80 print()
81 @@ -261,62 +257,8 @@ class Actions(object):
82 sys.exit(1)
83
84
85 - def _vcs_unadded(self):
86 - myunadded = []
87 - mydeleted = []
88 - if self.vcs_settings.vcs == "cvs":
89 - try:
90 - myvcstree = portage.cvstree.getentries("./", recursive=1)
91 - myunadded = portage.cvstree.findunadded(
92 - myvcstree, recursive=1, basedir="./")
93 - except SystemExit:
94 - raise # TODO propagate this
95 - except:
96 - err("Error retrieving CVS tree; exiting.")
97 - if self.vcs_settings.vcs == "svn":
98 - try:
99 - with repoman_popen("svn status --no-ignore") as f:
100 - svnstatus = f.readlines()
101 - myunadded = [
102 - "./" + elem.rstrip().split()[1]
103 - for elem in svnstatus
104 - if elem.startswith("?") or elem.startswith("I")]
105 - except SystemExit:
106 - raise # TODO propagate this
107 - except:
108 - err("Error retrieving SVN info; exiting.")
109 - if self.vcs_settings.vcs == "git":
110 - # get list of files not under version control or missing
111 - myf = repoman_popen("git ls-files --others")
112 - myunadded = ["./" + elem[:-1] for elem in myf]
113 - myf.close()
114 - if self.vcs_settings.vcs == "bzr":
115 - try:
116 - with repoman_popen("bzr status -S .") as f:
117 - bzrstatus = f.readlines()
118 - myunadded = [
119 - "./" + elem.rstrip().split()[1].split('/')[-1:][0]
120 - for elem in bzrstatus
121 - if elem.startswith("?") or elem[0:2] == " D"]
122 - except SystemExit:
123 - raise # TODO propagate this
124 - except:
125 - err("Error retrieving bzr info; exiting.")
126 - if self.vcs_settings.vcs == "hg":
127 - with repoman_popen("hg status --no-status --unknown .") as f:
128 - myunadded = f.readlines()
129 - myunadded = ["./" + elem.rstrip() for elem in myunadded]
130 -
131 - # Mercurial doesn't handle manually deleted files as removed from
132 - # the repository, so the user need to remove them before commit,
133 - # using "hg remove [FILES]"
134 - with repoman_popen("hg status --no-status --deleted .") as f:
135 - mydeleted = f.readlines()
136 - mydeleted = ["./" + elem.rstrip() for elem in mydeleted]
137 - return myunadded, mydeleted
138 -
139 -
140 - def _vcs_autoadd(self, myunadded):
141 + def _vcs_autoadd(self):
142 + myunadded = self.vcs_settings.changes.unadded
143 myautoadd = []
144 if myunadded:
145 for x in range(len(myunadded) - 1, -1, -1):
146 @@ -348,137 +290,35 @@ class Actions(object):
147 return myautoadd
148
149
150 - def _vcs_deleted(self, mydeleted):
151 - if self.vcs_settings.vcs == "hg" and mydeleted:
152 + def _vcs_deleted(self):
153 + if self.vcs_settings.changes.has_deleted():
154 print(red(
155 "!!! The following files are removed manually"
156 " from your local tree but are not"))
157 print(red(
158 "!!! removed from the repository."
159 - " Please remove them, using \"hg remove [FILES]\"."))
160 - for x in mydeleted:
161 + " Please remove them, using \"%s remove [FILES]\"."
162 + % self.vcs_settings.vcs))
163 + for x in self.vcs_settings.changes.deleted:
164 print(" ", x)
165 print()
166 print()
167 sys.exit(1)
168
169
170 - def get_vcs_changed(self, mydeleted):
171 + def get_vcs_changed(self):
172 '''Holding function which calls the approriate VCS module for the data'''
173 - changed = ([], [], [], [], [])
174 - if self.vcs_settings.vcs:
175 - vcs_module = getattr(self, '_get_changed_%s_' % self.vcs_settings.vcs)
176 - changed = vcs_module(mydeleted)
177 - mynew, mychanged, myremoved, no_expansion, expansion = changed
178 -
179 - a_file_is_changed = mychanged or mynew or myremoved
180 - a_file_is_deleted_hg = self.vcs_settings.vcs == "hg" and mydeleted
181 -
182 - if not (a_file_is_changed or a_file_is_deleted_hg):
183 - utilities.repoman_sez(
184 - "\"Doing nothing is not always good for QA.\"")
185 - print()
186 - print("(Didn't find any changed files...)")
187 - print()
188 - sys.exit(1)
189 - return changed
190 -
191 -
192 - def _get_changed_cvs_(self, mydeleted):
193 - mycvstree = cvstree.getentries("./", recursive=1)
194 - mychanged = cvstree.findchanged(mycvstree, recursive=1, basedir="./")
195 - mynew = cvstree.findnew(mycvstree, recursive=1, basedir="./")
196 - myremoved = portage.cvstree.findremoved(mycvstree, recursive=1, basedir="./")
197 - bin_blob_pattern = re.compile("^-kb$")
198 - no_expansion = set(portage.cvstree.findoption(
199 - mycvstree, bin_blob_pattern, recursive=1, basedir="./"))
200 - expansion = {}
201 - return (mynew, mychanged, myremoved, no_expansion, expansion)
202 -
203 - def _get_changed_svn_(self, mydeleted):
204 - with repoman_popen("svn status") as f:
205 - svnstatus = f.readlines()
206 - mychanged = [
207 - "./" + elem.split()[-1:][0]
208 - for elem in svnstatus
209 - if (elem[:1] in "MR" or elem[1:2] in "M")]
210 - mynew = [
211 - "./" + elem.split()[-1:][0]
212 - for elem in svnstatus
213 - if elem.startswith("A")]
214 - myremoved = [
215 - "./" + elem.split()[-1:][0]
216 - for elem in svnstatus
217 - if elem.startswith("D")]
218 - # Subversion expands keywords specified in svn:keywords properties.
219 - with repoman_popen("svn propget -R svn:keywords") as f:
220 - props = f.readlines()
221 - expansion = dict(
222 - ("./" + prop.split(" - ")[0], prop.split(" - ")[1].split())
223 - for prop in props if " - " in prop)
224 - no_expansion = set()
225 - return (mynew, mychanged, myremoved, no_expansion, expansion)
226 -
227 - def _get_changed_git_(self, mydeleted):
228 - with repoman_popen(
229 - "git diff-index --name-only "
230 - "--relative --diff-filter=M HEAD") as f:
231 - mychanged = f.readlines()
232 - mychanged = ["./" + elem[:-1] for elem in mychanged]
233 - with repoman_popen(
234 - "git diff-index --name-only "
235 - "--relative --diff-filter=A HEAD") as f:
236 - mynew = f.readlines()
237 - mynew = ["./" + elem[:-1] for elem in mynew]
238 - with repoman_popen(
239 - "git diff-index --name-only "
240 - "--relative --diff-filter=D HEAD") as f:
241 - myremoved = f.readlines()
242 - myremoved = ["./" + elem[:-1] for elem in myremoved]
243 - no_expansion = set()
244 - expansion = {}
245 - return (mynew, mychanged, myremoved, no_expansion, expansion)
246 -
247 - def _get_changed_bzr_(self, mydeleted):
248 - with repoman_popen("bzr status -S .") as f:
249 - bzrstatus = f.readlines()
250 - mychanged = [
251 - "./" + elem.split()[-1:][0].split('/')[-1:][0]
252 - for elem in bzrstatus
253 - if elem and elem[1:2] == "M"]
254 - mynew = [
255 - "./" + elem.split()[-1:][0].split('/')[-1:][0]
256 - for elem in bzrstatus
257 - if elem and (elem[1:2] in "NK" or elem[0:1] == "R")]
258 - myremoved = [
259 - "./" + elem.split()[-1:][0].split('/')[-1:][0]
260 - for elem in bzrstatus
261 - if elem.startswith("-")]
262 - myremoved = [
263 - "./" + elem.split()[-3:-2][0].split('/')[-1:][0]
264 - for elem in bzrstatus
265 - if elem and (elem[1:2] == "K" or elem[0:1] == "R")]
266 - # Bazaar expands nothing.
267 - no_expansion = set()
268 - expansion = {}
269 - return (mynew, mychanged, myremoved, no_expansion, expansion)
270 -
271 - def _get_changed_hg_(self, mydeleted):
272 - with repoman_popen("hg status --no-status --modified .") as f:
273 - mychanged = f.readlines()
274 - mychanged = ["./" + elem.rstrip() for elem in mychanged]
275 -
276 - with repoman_popen("hg status --no-status --added .") as f:
277 - mynew = f.readlines()
278 - mynew = ["./" + elem.rstrip() for elem in mynew]
279 -
280 - with repoman_popen("hg status --no-status --removed .") as f:
281 - myremoved = f.readlines()
282 - myremoved = ["./" + elem.rstrip() for elem in myremoved]
283 - no_expansion = set()
284 - expansion = {}
285 - return (mynew, mychanged, myremoved, no_expansion, expansion)
286 + changes = self.vcs_settings.vcs.changes
287
288 + if not changes.has_changes:
289 + utilities.repoman_sez(
290 + "\"Doing nothing is not always good for QA.\"")
291 + print()
292 + print("(Didn't find any changed files...)")
293 + print()
294 + sys.exit(1)
295 + return (changes.new, changes.changed, changes.removed,
296 + changes.no_expansion, changes.expansion)
297
298 def get_commit_footer(self):
299 portage_version = getattr(portage, "VERSION", None)
300
301 diff --git a/pym/repoman/modules/vcs/None/__init__.py b/pym/repoman/modules/vcs/None/__init__.py
302 index 4146e1e..2859325 100644
303 --- a/pym/repoman/modules/vcs/None/__init__.py
304 +++ b/pym/repoman/modules/vcs/None/__init__.py
305 @@ -19,6 +19,7 @@ module_spec = {
306 'func_desc': {
307 },
308 'vcs_preserves_mtime': False,
309 + 'needs_keyword_expansion': False,
310 },
311 'None-changes': {
312 'name': "None_changes",
313
314 diff --git a/pym/repoman/modules/vcs/bzr/__init__.py b/pym/repoman/modules/vcs/bzr/__init__.py
315 index ccdbddf..1192782 100644
316 --- a/pym/repoman/modules/vcs/bzr/__init__.py
317 +++ b/pym/repoman/modules/vcs/bzr/__init__.py
318 @@ -19,6 +19,7 @@ module_spec = {
319 'func_desc': {
320 },
321 'vcs_preserves_mtime': True,
322 + 'needs_keyword_expansion': False,
323 },
324 'bzr-changes': {
325 'name': "bzr_changes",
326
327 diff --git a/pym/repoman/modules/vcs/bzr/changes.py b/pym/repoman/modules/vcs/bzr/changes.py
328 index 0f70613..dab5d73 100644
329 --- a/pym/repoman/modules/vcs/bzr/changes.py
330 +++ b/pym/repoman/modules/vcs/bzr/changes.py
331 @@ -25,8 +25,13 @@ class Changes(ChangesBase):
332 "./" + elem.split()[-1:][0].split('/')[-1:][0]
333 for elem in bzrstatus
334 if elem and (elem[1:2] == "NK" or elem[0:1] == "R")]
335 - if self.options.if_modified == "y":
336 - self.removed = [
337 - "./" + elem.split()[-3:-2][0].split('/')[-1:][0]
338 - for elem in bzrstatus
339 - if elem and (elem[1:2] == "K" or elem[0:1] == "R")]
340 + #if self.options.if_modified == "y":
341 + self.removed = [
342 + "./" + elem.split()[-3:-2][0].split('/')[-1:][0]
343 + for elem in bzrstatus
344 + if elem and (elem[1:2] == "K" or elem[0:1] == "R")]
345 + self.unadded = [
346 + "./" + elem.rstrip().split()[1].split('/')[-1:][0]
347 + for elem in bzrstatus
348 + if elem.startswith("?") or elem[0:2] == " D"]
349 + # Bazaar expands nothing.
350
351 diff --git a/pym/repoman/modules/vcs/changes.py b/pym/repoman/modules/vcs/changes.py
352 index 6553ac9..b677964 100644
353 --- a/pym/repoman/modules/vcs/changes.py
354 +++ b/pym/repoman/modules/vcs/changes.py
355 @@ -21,6 +21,10 @@ class ChangesBase(object):
356 self.changed = []
357 self.new = []
358 self.removed = []
359 + self.no_expansion = set()
360 + self.expansion = {}
361 + self.deleted = []
362 + self.unadded = []
363
364 def scan(self):
365 self._reset()
366 @@ -37,3 +41,12 @@ class ChangesBase(object):
367 '''Placeholder for subclassing'''
368 pass
369
370 + @property
371 + def has_deleted(self):
372 + '''Placeholder for VCS that requires manual deletion of files'''
373 + return self.deleted != []
374 +
375 + @property
376 + def has_changes(self):
377 + '''Placeholder for VCS repo common has changes result'''
378 + return (self.changed or self.new or self.removed or self.deleted) == []
379
380 diff --git a/pym/repoman/modules/vcs/cvs/__init__.py b/pym/repoman/modules/vcs/cvs/__init__.py
381 index 6db6078..ba60e2c 100644
382 --- a/pym/repoman/modules/vcs/cvs/__init__.py
383 +++ b/pym/repoman/modules/vcs/cvs/__init__.py
384 @@ -19,6 +19,7 @@ module_spec = {
385 'func_desc': {
386 },
387 'vcs_preserves_mtime': True,
388 + 'needs_keyword_expansion': True,
389 },
390 'cvs-changes': {
391 'name': "cvs_changes",
392
393 diff --git a/pym/repoman/modules/vcs/cvs/changes.py b/pym/repoman/modules/vcs/cvs/changes.py
394 index f0893a1..ca07f1f 100644
395 --- a/pym/repoman/modules/vcs/cvs/changes.py
396 +++ b/pym/repoman/modules/vcs/cvs/changes.py
397 @@ -1,7 +1,9 @@
398
399
400 -from portage import cvstree
401 +from repoman._portage import portage
402 from repoman.modules.vcs.changes import ChangesBase
403 +from portage import cvstree
404 +
405
406 class Changes(ChangesBase):
407 '''Class object to scan and hold the resultant data
408 @@ -14,9 +16,12 @@ class Changes(ChangesBase):
409 super(Changes, self).__init__(options)
410
411 def _scan(self):
412 - tree = cvstree.getentries("./", recursive=1)
413 + tree = portage.cvstree.getentries("./", recursive=1)
414 self.changed = cvstree.findchanged(tree, recursive=1, basedir="./")
415 self.new = cvstree.findnew(tree, recursive=1, basedir="./")
416 - if self.options.if_modified == "y":
417 - self.removed = cvstree.findremoved(tree, recursive=1, basedir="./")
418 + self.removed = cvstree.findremoved(tree, recursive=1, basedir="./")
419 + myunadded = portage.cvstree.findunadded(myvcstree, recursive=1, basedir="./")
420 + bin_blob_pattern = re.compile("^-kb$")
421 + self.no_expansion = set(portage.cvstree.findoption(
422 + mycvstree, bin_blob_pattern, recursive=1, basedir="./"))
423 del tree
424
425 diff --git a/pym/repoman/modules/vcs/git/__init__.py b/pym/repoman/modules/vcs/git/__init__.py
426 index 4e1d599..e077767 100644
427 --- a/pym/repoman/modules/vcs/git/__init__.py
428 +++ b/pym/repoman/modules/vcs/git/__init__.py
429 @@ -19,6 +19,7 @@ module_spec = {
430 'func_desc': {
431 },
432 'vcs_preserves_mtime': False,
433 + 'needs_keyword_expansion': False,
434 },
435 'git-changes': {
436 'name': "git_changes",
437
438 diff --git a/pym/repoman/modules/vcs/git/changes.py b/pym/repoman/modules/vcs/git/changes.py
439 index 6ee39a0..26ffff3 100644
440 --- a/pym/repoman/modules/vcs/git/changes.py
441 +++ b/pym/repoman/modules/vcs/git/changes.py
442 @@ -27,11 +27,17 @@ class Changes(ChangesBase):
443 "--relative --diff-filter=A HEAD") as f:
444 new = f.readlines()
445 self.new = ["./" + elem[:-1] for elem in new]
446 - if self.options.if_modified == "y":
447 - with repoman_popen(
448 - "git diff-index --name-only "
449 - "--relative --diff-filter=D HEAD") as f:
450 - removed = f.readlines()
451 - self.removed = ["./" + elem[:-1] for elem in removed]
452 - del removed
453 + del new
454
455 + with repoman_popen(
456 + "git diff-index --name-only "
457 + "--relative --diff-filter=D HEAD") as f:
458 + removed = f.readlines()
459 + self.removed = ["./" + elem[:-1] for elem in removed]
460 + del removed
461 +
462 + # get list of files not under version control or missing
463 + with repoman_popen("git ls-files --others") as f:
464 + unadded = f.readlines()
465 + self.unadded = ["./" + elem[:-1] for elem in unadded]
466 + del unadded
467
468 diff --git a/pym/repoman/modules/vcs/hg/__init__.py b/pym/repoman/modules/vcs/hg/__init__.py
469 index 6f8a376..6737dfb 100644
470 --- a/pym/repoman/modules/vcs/hg/__init__.py
471 +++ b/pym/repoman/modules/vcs/hg/__init__.py
472 @@ -19,6 +19,7 @@ module_spec = {
473 'func_desc': {
474 },
475 'vcs_preserves_mtime': False,
476 + 'needs_keyword_expansion': False,
477 },
478 'hg-changes': {
479 'name': "hg_changes",
480
481 diff --git a/pym/repoman/modules/vcs/hg/changes.py b/pym/repoman/modules/vcs/hg/changes.py
482 index 86dffff..621b0d4 100644
483 --- a/pym/repoman/modules/vcs/hg/changes.py
484 +++ b/pym/repoman/modules/vcs/hg/changes.py
485 @@ -18,12 +18,27 @@ class Changes(ChangesBase):
486 with repoman_popen("hg status --no-status --modified .") as f:
487 changed = f.readlines()
488 self.changed = ["./" + elem.rstrip() for elem in changed]
489 + del changed
490 +
491 with repoman_popen("hg status --no-status --added .") as f:
492 new = f.readlines()
493 self.new = ["./" + elem.rstrip() for elem in new]
494 - if self.options.if_modified == "y":
495 - with repoman_popen("hg status --no-status --removed .") as f:
496 - removed = f.readlines()
497 - self.removed = ["./" + elem.rstrip() for elem in removed]
498 - del removed
499 - del changed, new
500 + del new
501 +
502 + with repoman_popen("hg status --no-status --removed .") as f:
503 + removed = f.readlines()
504 + self.removed = ["./" + elem.rstrip() for elem in removed]
505 + del removed
506 +
507 + with repoman_popen("hg status --no-status --unknown .") as f:
508 + unadded = f.readlines()
509 + self.unadded = ["./" + elem.rstrip() for elem in unadded]
510 + del unadded
511 +
512 + # Mercurial doesn't handle manually deleted files as removed from
513 + # the repository, so the user need to remove them before commit,
514 + # using "hg remove [FILES]"
515 + with repoman_popen("hg status --no-status --deleted .") as f:
516 + deleted = f.readlines()
517 + self.deleted = ["./" + elem.rstrip() for elem in deleted]
518 + del deleted
519
520 diff --git a/pym/repoman/modules/vcs/settings.py b/pym/repoman/modules/vcs/settings.py
521 index 34f1c78..bcd5f18 100644
522 --- a/pym/repoman/modules/vcs/settings.py
523 +++ b/pym/repoman/modules/vcs/settings.py
524 @@ -4,7 +4,6 @@ from __future__ import print_function, unicode_literals
525 import logging
526 import sys
527
528 -from repoman._portage import portage
529 from portage.output import red
530 from repoman.modules.vcs import module_controller, module_names
531 from repoman.modules.vcs.vcs import FindVCS
532 @@ -58,6 +57,8 @@ class VCSSettings(object):
533 logging.error("VCSSettings: Unknown VCS type: %s", self.vcs)
534 logging.error("Available modules: %s", module_controller.parents)
535
536 + self.needs_keyword_expansion = module_controller.modules[
537 + "%s_status" % self.vcs]['needs_keyword_expansion']
538 self.vcs_local_opts = repoman_settings.get(
539 "REPOMAN_VCS_LOCAL_OPTS", "").split()
540 self.vcs_global_opts = repoman_settings.get(
541 @@ -88,5 +89,5 @@ class VCSSettings(object):
542 def changes(self):
543 if not self._changes:
544 changes = self.module_controller.get_class('%s_changes' % self.vcs)
545 - self._changes = changes(self.options)
546 + self._changes = changes(self.options, self.vcs)
547 return self._changes
548
549 diff --git a/pym/repoman/modules/vcs/svn/__init__.py b/pym/repoman/modules/vcs/svn/__init__.py
550 index 41e481a..becb93e 100644
551 --- a/pym/repoman/modules/vcs/svn/__init__.py
552 +++ b/pym/repoman/modules/vcs/svn/__init__.py
553 @@ -19,6 +19,7 @@ module_spec = {
554 'func_desc': {
555 },
556 'vcs_preserves_mtime': False,
557 + 'needs_keyword_expansion': True,
558 },
559 'svn-changes': {
560 'name': "svn_changes",
561
562 diff --git a/pym/repoman/modules/vcs/svn/changes.py b/pym/repoman/modules/vcs/svn/changes.py
563 index 3567b61..f12b47d 100644
564 --- a/pym/repoman/modules/vcs/svn/changes.py
565 +++ b/pym/repoman/modules/vcs/svn/changes.py
566 @@ -25,9 +25,23 @@ class Changes(ChangesBase):
567 "./" + elem.split()[-1:][0]
568 for elem in svnstatus
569 if elem.startswith("A")]
570 - if self.options.if_modified == "y":
571 - self.removed = [
572 - "./" + elem.split()[-1:][0]
573 - for elem in svnstatus
574 - if elem.startswith("D")]
575 + self.removed = [
576 + "./" + elem.split()[-1:][0]
577 + for elem in svnstatus
578 + if elem.startswith("D")]
579 +
580 + # Subversion expands keywords specified in svn:keywords properties.
581 + with repoman_popen("svn propget -R svn:keywords") as f:
582 + props = f.readlines()
583 + self.expansion = dict(
584 + ("./" + prop.split(" - ")[0], prop.split(" - ")[1].split())
585 + for prop in props if " - " in prop)
586 + del props
587
588 + with repoman_popen("svn status --no-ignore") as f:
589 + svnstatus = f.readlines()
590 + self.unadded = [
591 + "./" + elem.rstrip().split()[1]
592 + for elem in svnstatus
593 + if elem.startswith("?") or elem.startswith("I")]
594 + del svnstatus