(revisions 56843:57448).

--HG--
branch : trunk
This commit is contained in:
guido 2008-08-18 22:37:39 +02:00
parent 8cefb88d9c
commit 623ad564ed
3 changed files with 194 additions and 5 deletions

View File

@ -73,7 +73,7 @@ class CommonSvnTests(CommonFSTests):
def setup_method(self, meth): def setup_method(self, meth):
bn = meth.func_name bn = meth.func_name
for x in 'test_remove', 'test_move': for x in 'test_remove', 'test_move', 'test_status_deleted':
if bn.startswith(x): if bn.startswith(x):
self._savedrepowc = save_repowc() self._savedrepowc = save_repowc()

View File

@ -4,6 +4,7 @@ from py.__.path.svn.testing.svntestbase import CommonSvnTests, getrepowc
from py.__.path.svn.wccommand import InfoSvnWCCommand from py.__.path.svn.wccommand import InfoSvnWCCommand
from py.__.path.svn.wccommand import parse_wcinfotime from py.__.path.svn.wccommand import parse_wcinfotime
from py.__.path.svn import svncommon from py.__.path.svn import svncommon
from py.__.conftest import option
if py.path.local.sysfind('svn') is None: if py.path.local.sysfind('svn') is None:
py.test.skip("cannot test py.path.svn, 'svn' binary not found") py.test.skip("cannot test py.path.svn, 'svn' binary not found")
@ -141,6 +142,78 @@ class TestWCSvnCommandPath(CommonSvnTests):
finally: finally:
self.root.revert(rec=1) self.root.revert(rec=1)
def test_status_ignored(self):
try:
d = self.root.join('sampledir')
p = py.path.local(d).join('ignoredfile')
p.ensure(file=True)
s = d.status()
assert [x.basename for x in s.unknown] == ['ignoredfile']
assert [x.basename for x in s.ignored] == []
d.propset('svn:ignore', 'ignoredfile')
s = d.status()
assert [x.basename for x in s.unknown] == []
assert [x.basename for x in s.ignored] == ['ignoredfile']
finally:
self.root.revert(rec=1)
def test_status_conflict(self):
if not option.runslowtests:
py.test.skip('skipping slow unit tests - use --runslowtests '
'to override')
wc = self.root
wccopy = py.path.svnwc(
py.test.ensuretemp('test_status_conflict_wccopy'))
wccopy.checkout(wc.url)
p = wc.ensure('conflictsamplefile', file=1)
p.write('foo')
wc.commit('added conflictsamplefile')
wccopy.update()
assert wccopy.join('conflictsamplefile').check()
p.write('bar')
wc.commit('wrote some data')
wccopy.join('conflictsamplefile').write('baz')
wccopy.update()
s = wccopy.status()
assert [x.basename for x in s.conflict] == ['conflictsamplefile']
def test_status_external(self):
if not option.runslowtests:
py.test.skip('skipping slow unit tests - use --runslowtests '
'to override')
otherrepo, otherwc = getrepowc('externalrepo', 'externalwc')
d = self.root.ensure('sampledir', dir=1)
try:
d.remove()
d.add()
d.update()
d.propset('svn:externals', 'otherwc %s' % (otherwc.url,))
d.update()
s = d.status()
assert [x.basename for x in s.external] == ['otherwc']
assert 'otherwc' not in [x.basename for x in s.unchanged]
s = d.status(rec=1)
assert [x.basename for x in s.external] == ['otherwc']
assert 'otherwc' in [x.basename for x in s.unchanged]
finally:
self.root.revert(rec=1)
def test_status_deleted(self):
d = self.root.ensure('sampledir', dir=1)
d.remove()
d.add()
self.root.commit()
d.ensure('deletefile', dir=0)
d.commit()
s = d.status()
assert 'deletefile' in [x.basename for x in s.unchanged]
assert not s.deleted
p = d.join('deletefile')
p.remove()
s = d.status()
assert 'deletefile' not in s.unchanged
assert [x.basename for x in s.deleted] == ['deletefile']
def test_diff(self): def test_diff(self):
p = self.root / 'anotherfile' p = self.root / 'anotherfile'
out = p.diff(rev=2) out = p.diff(rev=2)

View File

@ -9,6 +9,8 @@ svn-Command based Implementation of a Subversion WorkingCopy Path.
""" """
import os, sys, time, re, calendar import os, sys, time, re, calendar
from xml.dom import minidom
from xml.parsers.expat import ExpatError
import py import py
from py.__.path import common from py.__.path import common
from py.__.path.svn import cache from py.__.path.svn import cache
@ -255,9 +257,17 @@ class SvnWCCommandPath(common.FSPathBase):
else: else:
updates = '' updates = ''
cmd = 'status -v %s %s %s' % (updates, rec, externals) try:
cmd = 'status -v --xml --no-ignore %s %s %s' % (
updates, rec, externals)
out = self._authsvn(cmd)
except py.process.cmdexec.Error:
cmd = 'status -v --no-ignore %s %s %s' % (
updates, rec, externals)
out = self._authsvn(cmd) out = self._authsvn(cmd)
rootstatus = WCStatus(self).fromstring(out, self) rootstatus = WCStatus(self).fromstring(out, self)
else:
rootstatus = XMLWCStatus(self).fromstring(out, self)
return rootstatus return rootstatus
def diff(self, rev=None): def diff(self, rev=None):
@ -528,7 +538,6 @@ class WCStatus:
# seem to be a more solid approach :( # seem to be a more solid approach :(
_rex_status = re.compile(r'\s+(\d+|-)\s+(\S+)\s+(.+?)\s{2,}(.*)') _rex_status = re.compile(r'\s+(\d+|-)\s+(\S+)\s+(.+?)\s{2,}(.*)')
@staticmethod
def fromstring(data, rootwcpath, rev=None, modrev=None, author=None): def fromstring(data, rootwcpath, rev=None, modrev=None, author=None):
""" return a new WCStatus object from data 's' """ return a new WCStatus object from data 's'
""" """
@ -573,6 +582,13 @@ class WCStatus:
if line.lower().find('against revision:')!=-1: if line.lower().find('against revision:')!=-1:
update_rev = int(rest.split(':')[1].strip()) update_rev = int(rest.split(':')[1].strip())
continue continue
if line.lower().find('status on external') > -1:
# XXX not sure what to do here... perhaps we want to
# store some state instead of just continuing, as right
# now it makes the top-level external get added twice
# (once as external, once as 'normal' unchanged item)
# because of the way SVN presents external items
continue
# keep trying # keep trying
raise ValueError, "could not parse line %r" % line raise ValueError, "could not parse line %r" % line
else: else:
@ -615,6 +631,106 @@ class WCStatus:
rootstatus.update_rev = update_rev rootstatus.update_rev = update_rev
continue continue
return rootstatus return rootstatus
fromstring = staticmethod(fromstring)
class XMLWCStatus(WCStatus):
def fromstring(data, rootwcpath, rev=None, modrev=None, author=None):
""" parse 'data' (XML string as outputted by svn st) into a status obj
"""
# XXX for externals, the path is shown twice: once
# with external information, and once with full info as if
# the item was a normal non-external... the current way of
# dealing with this issue is by ignoring it - this does make
# externals appear as external items as well as 'normal',
# unchanged ones in the status object so this is far from ideal
rootstatus = WCStatus(rootwcpath, rev, modrev, author)
update_rev = None
try:
doc = minidom.parseString(data)
except ExpatError, e:
raise ValueError(str(e))
urevels = doc.getElementsByTagName('against')
if urevels:
rootstatus.update_rev = urevels[-1].getAttribute('revision')
for entryel in doc.getElementsByTagName('entry'):
path = entryel.getAttribute('path')
statusel = entryel.getElementsByTagName('wc-status')[0]
itemstatus = statusel.getAttribute('item')
if itemstatus == 'unversioned':
wcpath = rootwcpath.join(path, abs=1)
rootstatus.unknown.append(wcpath)
continue
elif itemstatus == 'external':
wcpath = rootwcpath.__class__(
rootwcpath.localpath.join(path, abs=1),
auth=rootwcpath.auth)
rootstatus.external.append(wcpath)
continue
elif itemstatus == 'ignored':
wcpath = rootwcpath.join(path, abs=1)
rootstatus.ignored.append(wcpath)
continue
rev = statusel.getAttribute('revision')
if itemstatus == 'added' or itemstatus == 'none':
rev = '0'
modrev = '?'
author = '?'
date = ''
else:
print entryel.toxml()
commitel = entryel.getElementsByTagName('commit')[0]
if commitel:
modrev = commitel.getAttribute('revision')
author = ''
for c in commitel.getElementsByTagName('author')[0]\
.childNodes:
author += c.nodeValue
date = ''
for c in commitel.getElementsByTagName('date')[0]\
.childNodes:
date += c.nodeValue
wcpath = rootwcpath.join(path, abs=1)
assert itemstatus != 'modified' or wcpath.check(file=1), (
'did\'t expect a directory with changed content here')
itemattrname = {
'normal': 'unchanged',
'unversioned': 'unknown',
'conflicted': 'conflict',
'none': 'added',
}.get(itemstatus, itemstatus)
attr = getattr(rootstatus, itemattrname)
attr.append(wcpath)
propsstatus = statusel.getAttribute('props')
if propsstatus not in ('none', 'normal'):
rootstatus.prop_modified.append(wcpath)
if wcpath == rootwcpath:
rootstatus.rev = rev
rootstatus.modrev = modrev
rootstatus.author = author
rootstatus.date = date
# handle repos-status element (remote info)
rstatusels = entryel.getElementsByTagName('repos-status')
if rstatusels:
rstatusel = rstatusels[0]
ritemstatus = rstatusel.getAttribute('item')
if ritemstatus in ('added', 'modified'):
rootstatus.update_available.append(wcpath)
lockels = entryel.getElementsByTagName('lock')
if len(lockels):
rootstatus.locked.append(wcpath)
return rootstatus
fromstring = staticmethod(fromstring)
class InfoSvnWCCommand: class InfoSvnWCCommand:
def __init__(self, output): def __init__(self, output):