(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):
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):
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 parse_wcinfotime
from py.__.path.svn import svncommon
from py.__.conftest import option
if py.path.local.sysfind('svn') is None:
py.test.skip("cannot test py.path.svn, 'svn' binary not found")
@ -141,6 +142,78 @@ class TestWCSvnCommandPath(CommonSvnTests):
finally:
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):
p = self.root / 'anotherfile'
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
from xml.dom import minidom
from xml.parsers.expat import ExpatError
import py
from py.__.path import common
from py.__.path.svn import cache
@ -255,9 +257,17 @@ class SvnWCCommandPath(common.FSPathBase):
else:
updates = ''
cmd = 'status -v %s %s %s' % (updates, rec, externals)
out = self._authsvn(cmd)
rootstatus = WCStatus(self).fromstring(out, self)
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)
rootstatus = WCStatus(self).fromstring(out, self)
else:
rootstatus = XMLWCStatus(self).fromstring(out, self)
return rootstatus
def diff(self, rev=None):
@ -528,7 +538,6 @@ class WCStatus:
# seem to be a more solid approach :(
_rex_status = re.compile(r'\s+(\d+|-)\s+(\S+)\s+(.+?)\s{2,}(.*)')
@staticmethod
def fromstring(data, rootwcpath, rev=None, modrev=None, author=None):
""" return a new WCStatus object from data 's'
"""
@ -573,6 +582,13 @@ class WCStatus:
if line.lower().find('against revision:')!=-1:
update_rev = int(rest.split(':')[1].strip())
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
raise ValueError, "could not parse line %r" % line
else:
@ -615,6 +631,106 @@ class WCStatus:
rootstatus.update_rev = update_rev
continue
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:
def __init__(self, output):