summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQijiang Fan <fqj1994@gmail.com>2012-06-08 17:42:38 +0800
committerQijiang Fan <fqj1994@gmail.com>2012-06-08 17:42:38 +0800
commit41ab46d551a1287a4dfb60d04e132af468923d95 (patch)
treee3523593e894b9ae7dffb9e57856b5909096737c
parent4d96cae645ca33d56b4a4961f2b59bef82773b58 (diff)
Upstream version 1.4
-rw-r--r--.hg_archival.txt4
-rw-r--r--.hgtags1
-rw-r--r--MANIFEST.in2
-rw-r--r--Makefile4
-rw-r--r--hgsubversion/__init__.py2
-rw-r--r--hgsubversion/editor.py22
-rw-r--r--hgsubversion/help/subversion.rst11
-rw-r--r--hgsubversion/maps.py74
-rw-r--r--hgsubversion/replay.py4
-rw-r--r--hgsubversion/stupid.py189
-rw-r--r--hgsubversion/svncommands.py25
-rw-r--r--hgsubversion/svnexternals.py12
-rw-r--r--hgsubversion/svnmeta.py32
-rw-r--r--hgsubversion/svnrepo.py23
-rw-r--r--hgsubversion/svnwrap/subvertpy_wrapper.py11
-rw-r--r--hgsubversion/svnwrap/svn_swig_wrapper.py24
-rw-r--r--hgsubversion/wrappers.py22
-rw-r--r--tests/comprehensive/test_stupid_pull.py5
-rw-r--r--tests/fixtures/binaryfiles-broken.svndumpbin0 -> 1835 bytes
-rw-r--r--tests/test_externals.py22
-rw-r--r--tests/test_fetch_branches.py37
-rw-r--r--tests/test_fetch_command.py17
-rw-r--r--tests/test_fetch_command_regexes.py93
-rw-r--r--tests/test_fetch_exec.py9
-rw-r--r--tests/test_fetch_mappings.py84
-rw-r--r--tests/test_fetch_symlinks.py10
-rw-r--r--tests/test_fetch_truncated.py4
-rw-r--r--tests/test_pull.py24
-rw-r--r--tests/test_push_command.py17
-rw-r--r--tests/test_push_dirs.py32
-rw-r--r--tests/test_push_eol.py4
-rw-r--r--tests/test_push_renames.py9
-rw-r--r--tests/test_rebuildmeta.py5
-rw-r--r--tests/test_single_dir_clone.py49
-rw-r--r--tests/test_startrev.py1
-rw-r--r--tests/test_svnwrap.py14
-rw-r--r--tests/test_tags.py10
-rw-r--r--tests/test_urls.py5
-rw-r--r--tests/test_util.py155
-rw-r--r--tests/test_utility_commands.py67
40 files changed, 710 insertions, 425 deletions
diff --git a/.hg_archival.txt b/.hg_archival.txt
index 916b999..bfa4098 100644
--- a/.hg_archival.txt
+++ b/.hg_archival.txt
@@ -1,4 +1,4 @@
repo: f2636cfed11500fdc47d1e3822d8e4a2bd636bf7
-node: 0cbf9fd89672e73165e1bb4db1ec8f7f65b95c94
+node: 07234759a3f750029ccaa001837d42fa12dd33ee
branch: default
-tag: 1.3
+tag: 1.4
diff --git a/.hgtags b/.hgtags
index d9c1ced..ceff398 100644
--- a/.hgtags
+++ b/.hgtags
@@ -5,3 +5,4 @@
093ae2915b452539b44390ee4ea14987484e1eee 1.1.2
708234ad6c97fb52417e0b46a86c8373e25123a5 1.2
4bbc6bf947f56a92e95a04a27b94a9f72d5482d7 1.2.1
+0cbf9fd89672e73165e1bb4db1ec8f7f65b95c94 1.3
diff --git a/MANIFEST.in b/MANIFEST.in
index 9f0c6c7..b79e61d 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,2 +1,2 @@
-include Makefile *.rst
+include Makefile hgsubversion/help/*.rst
recursive-include tests *.py *.sh *.svndump *.txt
diff --git a/Makefile b/Makefile
index e91f89e..d872278 100644
--- a/Makefile
+++ b/Makefile
@@ -17,11 +17,11 @@ check: check-demandimport check-subvertpy check-swig
check-demandimport:
# verify that hgsubversion loads properly without bindings, but fails
# when actually used
- ! LC_ALL=C HGSUBVERSION_BINDINGS=none \
+ ! LC_ALL=C HGSUBVERSION_BINDINGS=none HGRCPATH=/dev/null \
hg --config extensions.hgsubversion=./hgsubversion \
version 2>&1 \
| egrep '(^abort:|failed to import extension)'
- LC_ALL=C HGSUBVERSION_BINDINGS=none \
+ LC_ALL=C HGSUBVERSION_BINDINGS=none HGRCPATH=/dev/null \
hg --config extensions.hgsubversion=./hgsubversion \
version --svn 2>&1 \
| egrep '(^abort:|failed to import extension)'
diff --git a/hgsubversion/__init__.py b/hgsubversion/__init__.py
index cc336c8..f87b52f 100644
--- a/hgsubversion/__init__.py
+++ b/hgsubversion/__init__.py
@@ -177,6 +177,8 @@ def extsetup():
def reposetup(ui, repo):
if repo.local():
svnrepo.generate_repo_class(ui, repo)
+ for tunnel in ui.configlist('hgsubversion', 'tunnels'):
+ hg.schemes['svn+' + tunnel] = svnrepo
_old_local = hg.schemes['file']
def _lookup(url):
diff --git a/hgsubversion/editor.py b/hgsubversion/editor.py
index 54c04ac..286a6e3 100644
--- a/hgsubversion/editor.py
+++ b/hgsubversion/editor.py
@@ -10,6 +10,18 @@ import svnwrap
import util
import svnexternals
+class NeverClosingStringIO(object):
+ def __init__(self):
+ self._fp = cStringIO.StringIO()
+
+ def __getattr__(self, name):
+ return getattr(self._fp, name)
+
+ def close(self):
+ # svn 1.7 apply_delta driver now calls close() on passed file
+ # object which prevent us from calling getvalue() afterwards.
+ pass
+
class RevisionData(object):
__slots__ = [
@@ -331,11 +343,11 @@ class HgEditor(svnwrap.Editor):
if self.current.file in self.current.missing:
return lambda x: None
base = self.current.files[self.current.file]
- target = cStringIO.StringIO()
+ target = NeverClosingStringIO()
self.stream = target
handler = svnwrap.apply_txdelta(base, target)
- if not callable(handler): #pragma: no cover
+ if not callable(handler): # pragma: no cover
raise hgutil.Abort('Error in Subversion bindings: '
'cannot call handler!')
def txdelt_window(window):
@@ -346,12 +358,12 @@ class HgEditor(svnwrap.Editor):
# window being None means commit this file
if not window:
self.current.files[self.current.file] = target.getvalue()
- except svnwrap.SubversionException, e: #pragma: no cover
+ except svnwrap.SubversionException, e: # pragma: no cover
if e.args[1] == svnwrap.ERR_INCOMPLETE_DATA:
self.current.missing.add(self.current.file)
- else: #pragma: no cover
+ else: # pragma: no cover
raise hgutil.Abort(*e.args)
- except: #pragma: no cover
+ except: # pragma: no cover
print len(base), self.current.file
self._exception_info = sys.exc_info()
raise
diff --git a/hgsubversion/help/subversion.rst b/hgsubversion/help/subversion.rst
index a5bf85e..0209c1c 100644
--- a/hgsubversion/help/subversion.rst
+++ b/hgsubversion/help/subversion.rst
@@ -306,6 +306,17 @@ settings:
Set the username or password for accessing Subversion repositories.
+ ``hgsubversion.password_stores``
+
+ List of methods to use for storing passwords (similar to the option of the
+ same name in the subversion configuration files). Default is
+ ``gnome_keyring,keychain,kwallet,windows``. Password stores can be disabled
+ completely by setting this to an empty value.
+
+ .. NOTE::
+
+ Password stores are only supported with the SWIG bindings.
+
``hgsubversion.stupid``
Setting this boolean option to true will force using a slower method for
pulling revisions from Subversion. This method is compatible with servers
diff --git a/hgsubversion/maps.py b/hgsubversion/maps.py
index 93a3cbb..6bfca52 100644
--- a/hgsubversion/maps.py
+++ b/hgsubversion/maps.py
@@ -21,7 +21,7 @@ class AuthorMap(dict):
The ui argument is used to print diagnostic messages.
The path argument is the location of the backing store,
- typically .hg/authormap.
+ typically .hg/svn/authors.
'''
self.ui = ui
self.path = path
@@ -252,41 +252,54 @@ class RevMap(dict):
class FileMap(object):
- def __init__(self, repo):
- self.ui = repo.ui
+ VERSION = 1
+
+ def __init__(self, ui, path):
+ '''Initialise a new FileMap.
+
+ The ui argument is used to print diagnostic messages.
+
+ The path argument is the location of the backing store,
+ typically .hg/svn/filemap.
+ '''
+ self.ui = ui
+ self.path = path
self.include = {}
self.exclude = {}
- filemap = repo.ui.config('hgsubversion', 'filemap')
- if filemap and os.path.exists(filemap):
- self.load(filemap)
+ if os.path.isfile(self.path):
+ self._load()
+ else:
+ self._write()
def _rpairs(self, name):
- yield '.', name
e = len(name)
while e != -1:
yield name[:e], name[e+1:]
e = name.rfind('/', 0, e)
+ yield '.', name
def check(self, m, path):
m = getattr(self, m)
for pre, _suf in self._rpairs(path):
- if pre not in m:
- continue
- return m[pre]
- return None
+ if pre in m:
+ return m[pre]
+ return -1
def __contains__(self, path):
- if len(self.include) and len(path):
+ if not len(path):
+ return True
+ if len(self.include):
inc = self.check('include', path)
+ elif not len(self.exclude):
+ return True
else:
- inc = path
- if len(self.exclude) and len(path):
+ inc = 0
+ if len(self.exclude):
exc = self.check('exclude', path)
else:
- exc = None
- if inc is None or exc is not None:
- return False
- return True
+ exc = -1
+ # respect rule order: newer rules override older
+ return inc > exc
# Needed so empty filemaps are false
def __len__(self):
@@ -300,11 +313,20 @@ class FileMap(object):
return
bits = m.strip('e'), path
self.ui.debug('%sing %s\n' % bits)
- mapping[path] = path
+ # respect rule order
+ mapping[path] = len(self)
+ if fn != self.path:
+ f = open(self.path, 'a')
+ f.write(m + ' ' + path + '\n')
+ f.close()
def load(self, fn):
self.ui.note('reading file map from %s\n' % fn)
f = open(fn, 'r')
+ self.load_fd(f, fn)
+ f.close()
+
+ def load_fd(self, f, fn):
for line in f:
if line.strip() == '' or line.strip()[0] == '#':
continue
@@ -319,6 +341,20 @@ class FileMap(object):
except IndexError:
msg = 'ignoring bad line in filemap %s: %s\n'
self.ui.warn(msg % (fn, line.rstrip()))
+
+ def _load(self):
+ self.ui.note('reading in-repo file map from %s\n' % self.path)
+ f = open(self.path)
+ ver = int(f.readline())
+ if ver != self.VERSION:
+ print 'filemap too new -- please upgrade'
+ raise NotImplementedError
+ self.load_fd(f, self.path)
+ f.close()
+
+ def _write(self):
+ f = open(self.path, 'w')
+ f.write('%s\n' % self.VERSION)
f.close()
class BranchMap(dict):
diff --git a/hgsubversion/replay.py b/hgsubversion/replay.py
index 10d3b48..9a59002 100644
--- a/hgsubversion/replay.py
+++ b/hgsubversion/replay.py
@@ -71,7 +71,7 @@ def convert_rev(ui, meta, svn, r, tbdelta, firstrun):
updateexternals(ui, meta, current)
- if current.exception is not None: #pragma: no cover
+ if current.exception is not None: # pragma: no cover
traceback.print_exception(*current.exception)
raise ReplayException()
if current.missing:
@@ -194,7 +194,7 @@ def convert_rev(ui, meta, svn, r, tbdelta, firstrun):
raise IOError(errno.ENOENT, 'deleting all files')
# True here meant nuke all files, shouldn't happen with branch closing
- if current.emptybranches[branch]: #pragma: no cover
+ if current.emptybranches[branch]: # pragma: no cover
raise hgutil.Abort('Empty commit to an open branch attempted. '
'Please report this issue.')
diff --git a/hgsubversion/stupid.py b/hgsubversion/stupid.py
index 159396e..cf39869 100644
--- a/hgsubversion/stupid.py
+++ b/hgsubversion/stupid.py
@@ -12,41 +12,114 @@ import svnwrap
import svnexternals
import util
+# Here is a diff mixing content and property changes in svn >= 1.7
+#
+# Index: a
+# ===================================================================
+# --- a (revision 12)
+# +++ a (working copy)
+# @@ -1,2 +1,3 @@
+# a
+# a
+# +a
+#
+# Property changes on: a
+# ___________________________________________________________________
+# Added: svn:executable
+# ## -0,0 +1 ##
+# +*
+
+class ParseError(Exception):
+ pass
-binary_file_re = re.compile(r'''Index: ([^\n]*)
+index_header = r'''Index: ([^\n]*)
=*
-Cannot display: file marked as a binary type.''')
-
-property_exec_set_re = re.compile(r'''Property changes on: ([^\n]*)
-_*
-(?:Added|Name): svn:executable
- \+''')
-
-property_exec_removed_re = re.compile(r'''Property changes on: ([^\n]*)
-_*
-(?:Deleted|Name): svn:executable
- -''')
-
-empty_file_patch_wont_make_re = re.compile(r'''Index: ([^\n]*)\n=*\n(?=Index:)''')
-
-any_file_re = re.compile(r'''^Index: ([^\n]*)\n=*\n''', re.MULTILINE)
-
-property_special_set_re = re.compile(r'''Property changes on: ([^\n]*)
-_*
-(?:Added|Name): svn:special
- \+''')
+'''
-property_special_removed_re = re.compile(r'''Property changes on: ([^\n]*)
+property_header = r'''Property changes on: ([^\n]*)
_*
-(?:Deleted|Name): svn:special
- \-''')
+'''
+
+headers_re = re.compile('(?:' + '|'.join([
+ index_header,
+ property_header,
+ ]) + ')')
+
+property_special_added = r'''(?:Added|Name): (svn:special)
+(?: \+|## -0,0 \+1 ##
+\+)'''
+
+property_special_deleted = r'''(?:Deleted|Name): (svn:special)
+(?: \-|## -1 \+0,0 ##
+\-)'''
+
+property_exec_added = r'''(?:Added|Name): (svn:executable)
+(?: \+|## -0,0 \+1 ##
+\+)'''
+
+property_exec_deleted = r'''(?:Deleted|Name): (svn:executable)
+(?: \-|## -1 \+0,0 ##
+\-)'''
+
+properties_re = re.compile('(?:' + '|'.join([
+ property_special_added,
+ property_special_deleted,
+ property_exec_added,
+ property_exec_deleted,
+ ]) + ')')
+
+class filediff:
+ def __init__(self, name):
+ self.name = name
+ self.diff = None
+ self.binary = False
+ self.executable = None
+ self.symlink = None
+ self.hasprops = False
+
+ def isempty(self):
+ return (not self.diff and not self.binary and not self.hasprops)
+
+ def maybedir(self):
+ return (not self.diff and not self.binary and self.hasprops
+ and self.symlink is None and self.executable is None)
+
+def parsediff(diff):
+ changes = {}
+ headers = headers_re.split(diff)[1:]
+ if (len(headers) % 3) != 0:
+ # headers should be a sequence of (index file, property file, data)
+ raise ParseError('unexpected diff format')
+ files = []
+ for i in xrange(len(headers)/3):
+ iname, pname, data = headers[3*i:3*i+3]
+ fname = iname or pname
+ if fname not in changes:
+ changes[fname] = filediff(fname)
+ files.append(changes[fname])
+ f = changes[fname]
+ if iname is not None:
+ if data.strip():
+ f.binary = data.lstrip().startswith(
+ 'Cannot display: file marked as a binary type.')
+ if not f.binary and '@@' in data:
+ # Non-empty diff
+ f.diff = data
+ else:
+ f.hasprops = True
+ for m in properties_re.finditer(data):
+ p = m.group(1, 2, 3, 4)
+ if p[0] or p[1]:
+ f.symlink = bool(p[0])
+ elif p[2] or p[3]:
+ f.executable = bool(p[2])
+ return files
class BadPatchApply(Exception):
pass
-
-def print_your_svn_is_old_message(ui): #pragma: no cover
+def print_your_svn_is_old_message(ui): # pragma: no cover
ui.status("In light of that, I'll fall back and do diffs, but it won't do "
"as good a job. You should really upgrade your server.\n")
@@ -215,17 +288,12 @@ def diff_branchrev(ui, svn, meta, branch, branchpath, r, parentctx):
if '\0' in d:
raise BadPatchApply('binary diffs are not supported')
files_data = {}
- # we have to pull each binary file by hand as a fulltext,
- # which sucks but we've got no choice
- binary_files = set(binary_file_re.findall(d))
- touched_files = set(binary_files)
- d2 = empty_file_patch_wont_make_re.sub('', d)
- d2 = property_exec_set_re.sub('', d2)
- d2 = property_exec_removed_re.sub('', d2)
+ changed = parsediff(d)
# Here we ensure that all files, including the new empty ones
# are marked as touched. Content is loaded on demand.
- touched_files.update(any_file_re.findall(d))
- if d2.strip() and len(re.findall('\n[-+]', d2.strip())) > 0:
+ touched_files = set(f.name for f in changed)
+ d2 = '\n'.join(f.diff for f in changed if f.diff)
+ if changed:
files_data = patchrepo(ui, meta, parentctx, cStringIO.StringIO(d2))
for x in files_data.iterkeys():
ui.note('M %s\n' % x)
@@ -233,23 +301,6 @@ def diff_branchrev(ui, svn, meta, branch, branchpath, r, parentctx):
ui.status('Not using patch for %s, diff had no hunks.\n' %
r.revnum)
- exec_files = {}
- for m in property_exec_removed_re.findall(d):
- exec_files[m] = False
- for m in property_exec_set_re.findall(d):
- exec_files[m] = True
- touched_files.update(exec_files)
- link_files = {}
- for m in property_special_set_re.findall(d):
- # TODO(augie) when a symlink is removed, patching will fail.
- # We're seeing that above - there's gotta be a better
- # workaround than just bailing like that.
- assert m in files_data
- link_files[m] = True
- for m in property_special_removed_re.findall(d):
- assert m in files_data
- link_files[m] = False
-
unknown_files = set()
for p in r.paths:
action = r.paths[p].action
@@ -273,9 +324,35 @@ def diff_branchrev(ui, svn, meta, branch, branchpath, r, parentctx):
touched_files.update(files_data)
touched_files.update(unknown_files)
+ # As of svn 1.7, diff may contain a lot of property changes for
+ # directories. We do not what to include these in our touched
+ # files list so we try to filter them while minimizing the number
+ # of svn API calls.
+ property_files = set(f.name for f in changed if f.maybedir())
+ property_files.discard('.')
+ touched_files.discard('.')
+ branchprefix = (branchpath and branchpath + '/') or branchpath
+ for f in list(property_files):
+ if f in parentctx:
+ continue
+ # We can be smarter here by checking if f is a subcomponent
+ # of a know path in parentctx or touched_files. KISS for now.
+ kind = svn.checkpath(branchprefix + f, r.revnum)
+ if kind == 'd':
+ touched_files.discard(f)
+
copies = getcopies(svn, meta, branch, branchpath, r, touched_files,
parentctx)
+ # We note binary files because svn's diff format doesn't describe
+ # what changed, only that a change occurred. This means we'll have
+ # to pull them as fulltexts from the server outside the diff
+ # apply.
+ binary_files = set(f.name for f in changed if f.binary)
+ exec_files = dict((f.name, f.executable) for f in changed
+ if f.executable is not None)
+ link_files = dict((f.name, f.symlink) for f in changed
+ if f.symlink is not None)
def filectxfn(repo, memctx, path):
if path in files_data and files_data[path] is None:
raise IOError(errno.ENOENT, '%s is deleted' % path)
@@ -638,7 +715,13 @@ def convert_rev(ui, meta, svn, r, tbdelta, firstrun):
deleted_branches[b] = parentctx.node()
continue
- incremental = (meta.revmap.oldest > 0)
+ # The nullrev check might not be necessary in theory but svn <
+ # 1.7 failed to diff branch creation so the diff_branchrev()
+ # path does not support this case with svn >= 1.7. We can fix
+ # it, or we can force the existing fetch_branchrev() path. Do
+ # the latter for now.
+ incremental = (meta.revmap.oldest > 0 and
+ parentctx.rev() != node.nullrev)
if incremental:
try:
diff --git a/hgsubversion/svncommands.py b/hgsubversion/svncommands.py
index 0ae1262..eec9e51 100644
--- a/hgsubversion/svncommands.py
+++ b/hgsubversion/svncommands.py
@@ -43,13 +43,16 @@ def verify(ui, repo, args=None, **opts):
srev, branch, branchpath = meta.get_source_rev(ctx=ctx)
branchpath = branchpath[len(svn.subdir.lstrip('/')):]
+ branchurl = ('%s/%s' % (url, branchpath)).strip('/')
- ui.write('verifying %s against r%i\n' % (ctx, srev))
+ ui.write('verifying %s against %s@%i\n' % (ctx, branchurl, srev))
svnfiles = set()
result = 0
- for fn, type in svn.list_files(branchpath, srev):
+ svndata = svn.list_files(branchpath, srev)
+ for i, (fn, type) in enumerate(svndata):
+ util.progress(ui, 'verify', i)
if type != 'f':
continue
svnfiles.add(fn)
@@ -57,7 +60,11 @@ def verify(ui, repo, args=None, **opts):
if branchpath:
fp = branchpath + '/' + fn
data, mode = svn.get_file(posixpath.normpath(fp), srev)
- fctx = ctx[fn]
+ try:
+ fctx = ctx[fn]
+ except error.LookupError:
+ result = 1
+ continue
dmatch = fctx.data() == data
mmatch = fctx.flags() == mode
if not (dmatch and mmatch):
@@ -66,8 +73,16 @@ def verify(ui, repo, args=None, **opts):
hgfiles = set(ctx) - util.ignoredfiles
if hgfiles != svnfiles:
- missing = set(hgfiles).symmetric_difference(svnfiles)
- ui.write('missing files: %s\n' % (', '.join(missing)))
+ unexpected = hgfiles - svnfiles
+ if unexpected:
+ ui.write('unexpected files:\n')
+ for f in sorted(unexpected):
+ ui.write(' %s\n' % f)
+ missing = svnfiles - hgfiles
+ if missing:
+ ui.write('missing files:\n')
+ for f in sorted(missing):
+ ui.write(' %s\n' % f)
result = 1
return result
diff --git a/hgsubversion/svnexternals.py b/hgsubversion/svnexternals.py
index 38cc40b..0169265 100644
--- a/hgsubversion/svnexternals.py
+++ b/hgsubversion/svnexternals.py
@@ -95,9 +95,9 @@ def diff(ext1, ext2):
class BadDefinition(Exception):
pass
-re_defold = re.compile(r'^\s*(.*?)\s+(?:-r\s*(\d+|\{REV\})\s+)?([a-zA-Z]+://.*)\s*$')
-re_defnew = re.compile(r'^\s*(?:-r\s*(\d+|\{REV\})\s+)?((?:[a-zA-Z]+://|\^/).*)\s+(\S+)\s*$')
-re_scheme = re.compile(r'^[a-zA-Z]+://')
+re_defold = re.compile(r'^\s*(.*?)\s+(?:-r\s*(\d+|\{REV\})\s+)?([a-zA-Z+]+://.*)\s*$')
+re_defnew = re.compile(r'^\s*(?:-r\s*(\d+|\{REV\})\s+)?((?:[a-zA-Z+]+://|\^/).*)\s+(\S+)\s*$')
+re_scheme = re.compile(r'^[a-zA-Z+]+://')
def parsedefinition(line):
"""Parse an external definition line, return a tuple (path, rev, source)
@@ -443,3 +443,9 @@ if subrepo:
if self._state[1] == 'HEAD':
rev = 'HEAD'
return rev
+
+ def basestate(self):
+ # basestate() was introduced by bcb973abcc0b in 2.2
+ if self._state[1] == 'HEAD':
+ return 'HEAD'
+ return super(svnsubrepo, self).basestate()
diff --git a/hgsubversion/svnmeta.py b/hgsubversion/svnmeta.py
index 68ab4a7..adc8f58 100644
--- a/hgsubversion/svnmeta.py
+++ b/hgsubversion/svnmeta.py
@@ -13,21 +13,20 @@ import maps
import editor
-def pickle_atomic(data, file_path, dir=None):
+def pickle_atomic(data, file_path):
"""pickle some data to a path atomically.
This is present because I kept corrupting my revmap by managing to hit ^C
during the pickle of that file.
"""
- try:
- f, path = tempfile.mkstemp(prefix='pickling', dir=dir)
- f = os.fdopen(f, 'w')
- pickle.dump(data, f)
- f.close()
- except: #pragma: no cover
- raise
+ f = hgutil.atomictempfile(file_path, 'w+b', 0644)
+ pickle.dump(data, f)
+ # Older versions of hg have .rename() instead of .close on
+ # atomictempfile.
+ if getattr(hgutil.atomictempfile, 'rename', False):
+ f.rename()
else:
- hgutil.rename(path, file_path)
+ f.close()
class SVNMeta(object):
@@ -55,6 +54,7 @@ class SVNMeta(object):
'usebranchnames', True)
branchmap = self.ui.config('hgsubversion', 'branchmap')
tagmap = self.ui.config('hgsubversion', 'tagmap')
+ filemap = self.ui.config('hgsubversion', 'filemap')
self.branches = {}
if os.path.exists(self.branch_info_file):
@@ -76,8 +76,7 @@ class SVNMeta(object):
self.repo.ui.setconfig('hgsubversion', 'layout', self._layout)
else:
self._layout = None
- pickle_atomic(self.tag_locations, self.tag_locations_file,
- self.meta_data_dir)
+ pickle_atomic(self.tag_locations, self.tag_locations_file)
# ensure nested paths are handled properly
self.tag_locations.sort()
self.tag_locations.reverse()
@@ -94,8 +93,11 @@ class SVNMeta(object):
if tagmap:
self.tagmap.load(tagmap)
+ self.filemap = maps.FileMap(self.ui, self.filemap_file)
+ if filemap:
+ self.filemap.load(filemap)
+
self.lastdate = '1970-01-01 00:00:00 -0000'
- self.filemap = maps.FileMap(repo)
self.addedtags = {}
self.deletedtags = {}
@@ -192,6 +194,10 @@ class SVNMeta(object):
return os.path.join(self.meta_data_dir, 'authors')
@property
+ def filemap_file(self):
+ return os.path.join(self.meta_data_dir, 'filemap')
+
+ @property
def branchmapfile(self):
return os.path.join(self.meta_data_dir, 'branchmap')
@@ -217,7 +223,7 @@ class SVNMeta(object):
'''Save the Subversion metadata. This should really be called after
every revision is created.
'''
- pickle_atomic(self.branches, self.branch_info_file, self.meta_data_dir)
+ pickle_atomic(self.branches, self.branch_info_file)
def localname(self, path):
"""Compute the local name for a branch located at path.
diff --git a/hgsubversion/svnrepo.py b/hgsubversion/svnrepo.py
index 6bb3cd9..c471fa5 100644
--- a/hgsubversion/svnrepo.py
+++ b/hgsubversion/svnrepo.py
@@ -21,6 +21,13 @@ from mercurial import util as hgutil
from mercurial import httprepo
import mercurial.repo
+try:
+ from mercurial import phases
+ phases.public # defeat demand import
+except ImportError:
+ phases = None
+
+import re
import util
import wrappers
import svnwrap
@@ -76,7 +83,11 @@ def generate_repo_class(ui, repo):
class svnlocalrepo(superclass):
def svn_commitctx(self, ctx):
"""Commits a ctx, but defeats manifest recycling introduced in hg 1.9."""
- return self.commitctx(ctxctx(ctx))
+ hash = self.commitctx(ctxctx(ctx))
+ if phases is not None and getattr(self, 'pushkey', False):
+ # set phase to be public
+ self.pushkey('phases', self[hash].hex(), str(phases.draft), str(phases.public))
+ return hash
# TODO use newbranch to allow branch creation in Subversion?
@remotesvn
@@ -107,6 +118,14 @@ class svnremoterepo(mercurial.repo.repository):
raise hgutil.Abort('no Subversion URL specified')
self.path = path
self.capabilities = set(['lookup', 'subversion'])
+ pws = self.ui.config('hgsubversion', 'password_stores', None)
+ if pws is not None:
+ # Split pws at comas and strip neighbouring whitespace (whitespace
+ # at the beginning and end of pws has already been removed by the
+ # config parser).
+ self.password_stores = re.split(r'\s*,\s*', pws)
+ else:
+ self.password_stores = None
@propertycache
def svnauth(self):
@@ -127,7 +146,7 @@ class svnremoterepo(mercurial.repo.repository):
@propertycache
def svn(self):
try:
- return svnwrap.SubversionRepo(*self.svnauth)
+ return svnwrap.SubversionRepo(*self.svnauth, password_stores=self.password_stores)
except svnwrap.SubversionConnectionException, e:
self.ui.traceback()
raise hgutil.Abort(e)
diff --git a/hgsubversion/svnwrap/subvertpy_wrapper.py b/hgsubversion/svnwrap/subvertpy_wrapper.py
index 0023d5a..d85ee80 100644
--- a/hgsubversion/svnwrap/subvertpy_wrapper.py
+++ b/hgsubversion/svnwrap/subvertpy_wrapper.py
@@ -31,7 +31,7 @@ except ImportError:
def _versionstr(v):
return '.'.join(str(d) for d in v)
-if subvertpy.__version__ < subvertpy_required: #pragma: no cover
+if subvertpy.__version__ < subvertpy_required: # pragma: no cover
raise ImportError('Subvertpy %s or later required, '
'but %s found'
% (_versionstr(subvertpy_required),
@@ -165,8 +165,11 @@ class SubversionRepo(object):
This wrapper uses Subvertpy, an alternate set of bindings for Subversion
that's more pythonic and sucks less. See earlier in this file for version
requirements.
+
+ Note that password stores do not work, the parameter is only here
+ to ensure that the API is the same as for the SWIG wrapper.
"""
- def __init__(self, url='', username='', password='', head=None):
+ def __init__(self, url='', username='', password='', head=None, password_stores=None):
parsed = common.parse_url(url, username, password)
# --username and --password override URL credentials
self.username = parsed[0]
@@ -338,7 +341,7 @@ class SubversionRepo(object):
commit_info.append(args)
commit_info = []
revprops = { properties.PROP_REVISION_LOG: message }
- #revprops.update(props)
+ # revprops.update(props)
commiteditor = self.remote.get_commit_editor(revprops, commitcb)
paths = set(paths)
@@ -417,7 +420,7 @@ class SubversionRepo(object):
try:
self.remote.replay(revision, oldestrev, AbstractEditor(editor))
- except (SubversionException, NotImplementedError), e: #pragma: no cover
+ except (SubversionException, NotImplementedError), e: # pragma: no cover
# can I depend on this number being constant?
if (isinstance(e, NotImplementedError) or
e.args[1] == subvertpy.ERR_RA_NOT_IMPLEMENTED or
diff --git a/hgsubversion/svnwrap/svn_swig_wrapper.py b/hgsubversion/svnwrap/svn_swig_wrapper.py
index 50ec898..d232f03 100644
--- a/hgsubversion/svnwrap/svn_swig_wrapper.py
+++ b/hgsubversion/svnwrap/svn_swig_wrapper.py
@@ -29,7 +29,7 @@ except ImportError:
raise ImportError('Subversion %d.%d.%d or later required, '
'but no bindings were found' % required_bindings)
-if current_bindings < required_bindings: #pragma: no cover
+if current_bindings < required_bindings: # pragma: no cover
raise ImportError('Subversion %d.%d.%d or later required, '
'but bindings for %d.%d.%d found' %
(required_bindings + current_bindings))
@@ -60,7 +60,7 @@ def optrev(revnum):
svn_config = core.svn_config_get_config(None)
class RaCallbacks(ra.Callbacks):
@staticmethod
- def open_tmp_file(pool): #pragma: no cover
+ def open_tmp_file(pool): # pragma: no cover
(fd, fn) = tempfile.mkstemp()
os.close(fd)
return fn
@@ -82,13 +82,13 @@ def ieditor(fn):
def fun(self, *args, **kwargs):
try:
return fn(self, *args, **kwargs)
- except: #pragma: no cover
+ except: # pragma: no cover
if self.current.exception is not None:
self.current.exception = sys.exc_info()
raise
return fun
-def user_pass_prompt(realm, default_username, ms, pool): #pragma: no cover
+def user_pass_prompt(realm, default_username, ms, pool): # pragma: no cover
# FIXME: should use getpass() and username() from mercurial.ui
creds = core.svn_auth_cred_simple_t()
creds.may_save = ms
@@ -103,7 +103,7 @@ def user_pass_prompt(realm, default_username, ms, pool): #pragma: no cover
creds.password = getpass.getpass('Password for %s: ' % creds.username)
return creds
-def _create_auth_baton(pool):
+def _create_auth_baton(pool, password_stores):
"""Create a Subversion authentication baton. """
# Give the client context baton a suite of authentication
# providers.h
@@ -124,7 +124,9 @@ def _create_auth_baton(pool):
None)
if getprovider:
# Available in svn >= 1.6
- for name in ('gnome_keyring', 'keychain', 'kwallet', 'windows'):
+ if password_stores is None:
+ password_stores = ('gnome_keyring', 'keychain', 'kwallet', 'windows')
+ for name in password_stores:
for type in ('simple', 'ssl_client_cert_pw', 'ssl_server_trust'):
p = getprovider(name, type, pool)
if p:
@@ -158,14 +160,14 @@ class SubversionRepo(object):
It uses the SWIG Python bindings, see above for requirements.
"""
- def __init__(self, url='', username='', password='', head=None):
+ def __init__(self, url='', username='', password='', head=None, password_stores=None):
parsed = common.parse_url(url, username, password)
# --username and --password override URL credentials
self.username = parsed[0]
self.password = parsed[1]
self.svn_url = parsed[2]
self.auth_baton_pool = core.Pool()
- self.auth_baton = _create_auth_baton(self.auth_baton_pool)
+ self.auth_baton = _create_auth_baton(self.auth_baton_pool, password_stores)
# self.init_ra_and_client() assumes that a pool already exists
self.pool = core.Pool()
@@ -233,7 +235,7 @@ class SubversionRepo(object):
holder = []
ra.get_log(self.ra, [''],
self.HEAD, 1,
- 1, #limit of how many log messages to load
+ 1, # limit of how many log messages to load
True, # don't need to know changed paths
True, # stop on copies
lambda paths, revnum, author, date, message, pool:
@@ -295,7 +297,7 @@ class SubversionRepo(object):
paths,
start + 1,
stop,
- chunk_size, #limit of how many log messages to load
+ chunk_size, # limit of how many log messages to load
True, # don't need to know changed paths
True, # stop on copies
callback,
@@ -403,7 +405,7 @@ class SubversionRepo(object):
try:
ra.replay(self.ra, revision, oldest_rev_i_have, True, e_ptr,
e_baton, self.pool)
- except SubversionException, e: #pragma: no cover
+ except SubversionException, e: # pragma: no cover
# can I depend on this number being constant?
if (e.apr_err == core.SVN_ERR_RA_NOT_IMPLEMENTED or
e.apr_err == core.SVN_ERR_UNSUPPORTED_FEATURE):
diff --git a/hgsubversion/wrappers.py b/hgsubversion/wrappers.py
index 3c16b31..e255569 100644
--- a/hgsubversion/wrappers.py
+++ b/hgsubversion/wrappers.py
@@ -1,6 +1,11 @@
from hgext import rebase as hgrebase
from mercurial import cmdutil
+try:
+ from mercurial import discovery
+ discovery.nullid # force demandimport to import the module
+except ImportError:
+ discovery = None
from mercurial import patch
from mercurial import hg
from mercurial import util as hgutil
@@ -90,7 +95,14 @@ def findcommonoutgoing(repo, other, onlyheads=None, force=False, commoninc=None)
meta = repo.svnmeta(svn.uuid, svn.subdir)
parent = repo.parents()[0].node()
hashes = meta.revmap.hashes()
- return util.outgoing_common_and_heads(repo, hashes, parent)
+ common, heads = util.outgoing_common_and_heads(repo, hashes, parent)
+ if discovery is not None:
+ outobj = getattr(discovery, 'outgoing', None)
+ if outobj is not None:
+ # Mercurial 2.1 and later
+ return outobj(repo.changelog, common, heads)
+ # Mercurial 2.0 and earlier
+ return common, heads
def findoutgoing(repo, dest=None, heads=None, force=False):
@@ -98,7 +110,7 @@ def findoutgoing(repo, dest=None, heads=None, force=False):
"""
assert dest.capable('subversion')
# split off #rev; TODO implement --revision/#rev support
- #svnurl, revs, checkout = util.parseurl(dest.svnurl, heads)
+ # svnurl, revs, checkout = util.parseurl(dest.svnurl, heads)
svn = dest.svn
meta = repo.svnmeta(svn.uuid, svn.subdir)
parent = repo.parents()[0].node()
@@ -354,11 +366,11 @@ def pull(repo, source, heads=[], force=False):
converted = True
firstrun = False
- except svnwrap.SubversionRepoCanNotReplay, e: #pragma: no cover
+ except svnwrap.SubversionRepoCanNotReplay, e: # pragma: no cover
ui.status('%s\n' % e.message)
stupidmod.print_your_svn_is_old_message(ui)
have_replay = False
- except svnwrap.SubversionException, e: #pragma: no cover
+ except svnwrap.SubversionException, e: # pragma: no cover
if (e.args[1] == svnwrap.ERR_RA_DAV_REQUEST_FAILED
and '502' in str(e)
and tries < 3):
@@ -476,6 +488,8 @@ def clone(orig, ui, source, dest=None, **opts):
data['srcrepo'], data['dstrepo'] = orig(ui, *args, **opts)
+ return data['srcrepo'], data['dstrepo']
+
for opt, (section, name) in optionmap.iteritems():
if opt in opts and opts[opt]:
ui.setconfig(section, name, str(opts.pop(opt)))
diff --git a/tests/comprehensive/test_stupid_pull.py b/tests/comprehensive/test_stupid_pull.py
index 466d460..17e8122 100644
--- a/tests/comprehensive/test_stupid_pull.py
+++ b/tests/comprehensive/test_stupid_pull.py
@@ -19,11 +19,12 @@ from hgsubversion import wrappers
def _do_case(self, name, layout):
subdir = test_util.subdir.get(name, '')
- self._load_fixture_and_fetch(name, subdir=subdir, stupid=False, layout=layout)
+ repo, repo_path = self.load_and_fetch(name, subdir=subdir, stupid=False,
+ layout=layout)
assert len(self.repo) > 0, 'Repo had no changes, maybe you need to add a subdir entry in test_util?'
wc2_path = self.wc_path + '_stupid'
u = ui.ui()
- checkout_path = self.repo_path
+ checkout_path = repo_path
if subdir:
checkout_path += '/' + subdir
u.setconfig('hgsubversion', 'stupid', '1')
diff --git a/tests/fixtures/binaryfiles-broken.svndump b/tests/fixtures/binaryfiles-broken.svndump
new file mode 100644
index 0000000..08863e0
--- /dev/null
+++ b/tests/fixtures/binaryfiles-broken.svndump
Binary files differ
diff --git a/tests/test_externals.py b/tests/test_externals.py
index 2927b11..50dd424 100644
--- a/tests/test_externals.py
+++ b/tests/test_externals.py
@@ -278,9 +278,7 @@ d3/ext3 = [hgsubversion] d3:^/trunk/common/ext ext3
class TestPushExternals(test_util.TestBase):
def test_push_externals(self, stupid=False):
- test_util.load_fixture_and_fetch('pushexternals.svndump',
- self.repo_path,
- self.wc_path)
+ repo = self._load_fixture_and_fetch('pushexternals.svndump')
# Add a new reference on an existing and non-existing directory
changes = [
('.hgsvnexternals', '.hgsvnexternals',
@@ -331,10 +329,8 @@ class TestPushExternals(test_util.TestBase):
if subrepo is None:
return
- test_util.load_fixture_and_fetch('pushexternals.svndump',
- self.repo_path,
- self.wc_path,
- externals='subrepos')
+ repo, repo_path = self.load_and_fetch('pushexternals.svndump',
+ externals='subrepos')
# Add a new reference on an existing and non-existing directory
changes = [
('.hgsub', '.hgsub', """\
@@ -350,16 +346,16 @@ HEAD subdir2/deps/project2
('subdir1/a', 'subdir1/a', 'a'),
('subdir2/a', 'subdir2/a', 'a'),
]
- self.svnco('externals/project2', '2', 'dir/deps/project2')
- self.svnco('externals/project1', '2', 'subdir1/deps/project1')
- self.svnco('externals/project2', '2', 'subdir2/deps/project2')
+ self.svnco(repo_path, 'externals/project2', '2', 'dir/deps/project2')
+ self.svnco(repo_path, 'externals/project1', '2', 'subdir1/deps/project1')
+ self.svnco(repo_path, 'externals/project2', '2', 'subdir2/deps/project2')
self.commitchanges(changes)
self.pushrevisions(stupid)
self.assertchanges(changes, self.repo['tip'])
# Check .hgsub and .hgsubstate were not pushed
self.assertEqual(['dir', 'subdir1', 'subdir1/a', 'subdir2',
- 'subdir2/a'], self.svnls('trunk'))
+ 'subdir2/a'], test_util.svnls(repo_path, 'trunk'))
# Remove all references from one directory, add a new one
# to the other (test multiline entries)
@@ -375,8 +371,8 @@ HEAD subdir1/deps/project2
# This removal used to trigger the parent directory removal
('subdir1/a', None, None),
]
- self.svnco('externals/project1', '2', 'subdir1/deps/project1')
- self.svnco('externals/project2', '2', 'subdir1/deps/project2')
+ self.svnco(repo_path, 'externals/project1', '2', 'subdir1/deps/project1')
+ self.svnco(repo_path, 'externals/project2', '2', 'subdir1/deps/project2')
self.commitchanges(changes)
self.pushrevisions(stupid)
self.assertchanges(changes, self.repo['tip'])
diff --git a/tests/test_fetch_branches.py b/tests/test_fetch_branches.py
index a9645f0..7a9287c 100644
--- a/tests/test_fetch_branches.py
+++ b/tests/test_fetch_branches.py
@@ -7,15 +7,9 @@ from mercurial import node
from mercurial import util as hgutil
class TestFetchBranches(test_util.TestBase):
- def _load_fixture_and_fetch(self, fixture_name, stupid, noupdate=True,
- subdir=''):
- return test_util.load_fixture_and_fetch(fixture_name, self.repo_path,
- self.wc_path, stupid=stupid,
- noupdate=noupdate, subdir=subdir)
-
def _load_fixture_and_fetch_with_anchor(self, fixture_name, anchor):
- test_util.load_svndump_fixture(self.repo_path, fixture_name)
- source = '%s#%s' % (test_util.fileurl(self.repo_path), anchor)
+ repo_path = self.load_svndump(fixture_name)
+ source = '%s#%s' % (test_util.fileurl(repo_path), anchor)
test_util.hgclone(self.ui(), source, self.wc_path)
return hg.repository(self.ui(), self.wc_path)
@@ -31,7 +25,8 @@ class TestFetchBranches(test_util.TestBase):
return self.branches(repo)[0]
def test_rename_branch_parent(self, stupid=False):
- repo = self._load_fixture_and_fetch('rename_branch_parent_dir.svndump', stupid)
+ repo = self._load_fixture_and_fetch('rename_branch_parent_dir.svndump',
+ stupid=stupid)
heads = [repo[n] for n in repo.heads()]
heads = dict([(ctx.branch(), ctx) for ctx in heads])
# Let these tests disabled yet as the fix is not obvious
@@ -41,7 +36,8 @@ class TestFetchBranches(test_util.TestBase):
self.test_rename_branch_parent(stupid=True)
def test_unrelatedbranch(self, stupid=False):
- repo = self._load_fixture_and_fetch('unrelatedbranch.svndump', stupid)
+ repo = self._load_fixture_and_fetch('unrelatedbranch.svndump',
+ stupid=stupid)
heads = [repo[n] for n in repo.heads()]
heads = dict([(ctx.branch(), ctx) for ctx in heads])
# Let these tests disabled yet as the fix is not obvious
@@ -52,7 +48,8 @@ class TestFetchBranches(test_util.TestBase):
self.test_unrelatedbranch(True)
def test_unorderedbranch(self, stupid=False):
- repo = self._load_fixture_and_fetch('unorderedbranch.svndump', stupid)
+ repo = self._load_fixture_and_fetch('unorderedbranch.svndump',
+ stupid=stupid)
r = repo['branch']
self.assertEqual(0, r.parents()[0].rev())
self.assertEqual(['a', 'c', 'z'], sorted(r.manifest()))
@@ -62,7 +59,7 @@ class TestFetchBranches(test_util.TestBase):
def test_renamed_branch_to_trunk(self, stupid=False):
repo = self._load_fixture_and_fetch('branch_rename_to_trunk.svndump',
- stupid)
+ stupid=stupid)
self.assertEqual(repo['default'].parents()[0].branch(), 'dev_branch')
self.assert_('iota' in repo['default'])
self.assertEqual(repo['old_trunk'].parents()[0].branch(), 'default')
@@ -75,14 +72,15 @@ class TestFetchBranches(test_util.TestBase):
def test_replace_trunk_with_branch(self, stupid=False):
repo = self._load_fixture_and_fetch('replace_trunk_with_branch.svndump',
- stupid)
+ stupid=stupid)
self.assertEqual(repo['default'].parents()[0].branch(), 'test')
self.assertEqual(repo['tip'].branch(), 'default')
self.assertEqual(repo['tip'].extra().get('close'), '1')
self.assertEqual(self.openbranches(repo), ['default'])
def test_copybeforeclose(self, stupid=False):
- repo = self._load_fixture_and_fetch('copybeforeclose.svndump', stupid)
+ repo = self._load_fixture_and_fetch('copybeforeclose.svndump',
+ stupid=stupid)
self.assertEqual(repo['tip'].branch(), 'test')
self.assertEqual(repo['test'].extra().get('close'), '1')
self.assertEqual(repo['test']['b'].data(), 'a\n')
@@ -95,13 +93,13 @@ class TestFetchBranches(test_util.TestBase):
def test_branch_create_with_dir_delete_works(self, stupid=False):
repo = self._load_fixture_and_fetch('branch_create_with_dir_delete.svndump',
- stupid)
+ stupid=stupid)
self.assertEqual(repo['tip'].manifest().keys(),
['alpha', 'beta', 'iota', 'gamma', ])
def test_branch_tip_update_to_default(self, stupid=False):
repo = self._load_fixture_and_fetch('unorderedbranch.svndump',
- stupid, noupdate=False)
+ stupid=stupid, noupdate=False)
self.assertEqual(repo[None].branch(), 'default')
self.assertTrue('tip' not in repo[None].tags())
@@ -117,7 +115,8 @@ class TestFetchBranches(test_util.TestBase):
self.assertTrue('c' not in repo.branchtags())
def test_branches_weird_moves(self, stupid=False):
- repo = self._load_fixture_and_fetch('renamedproject.svndump', stupid,
+ repo = self._load_fixture_and_fetch('renamedproject.svndump',
+ stupid=stupid,
subdir='project')
heads = [repo[n] for n in repo.heads()]
heads = dict((ctx.branch(), ctx) for ctx in heads)
@@ -131,7 +130,7 @@ class TestFetchBranches(test_util.TestBase):
def test_branch_delete_parent_dir(self, stupid=False):
repo = self._load_fixture_and_fetch('branch_delete_parent_dir.svndump',
- stupid)
+ stupid=stupid)
openb, closedb = self.branches(repo)
self.assertEqual(openb, [])
self.assertEqual(closedb, ['dev_branch'])
@@ -139,7 +138,7 @@ class TestFetchBranches(test_util.TestBase):
def test_replace_branch_with_branch(self, stupid=False):
repo = self._load_fixture_and_fetch('replace_branch_with_branch.svndump',
- stupid)
+ stupid=stupid)
self.assertEqual(7, len(repo))
# tip is former topological branch1 being closed
ctx = repo['tip']
diff --git a/tests/test_fetch_command.py b/tests/test_fetch_command.py
index f86416c..b4e3fce 100644
--- a/tests/test_fetch_command.py
+++ b/tests/test_fetch_command.py
@@ -152,12 +152,11 @@ class TestBasicRepoLayout(test_util.TestBase):
self.test_fetch_when_trunk_has_no_files(stupid=True)
def test_path_quoting(self, stupid=False):
- test_util.load_svndump_fixture(self.repo_path,
- 'non_ascii_path_1.svndump')
+ repo_path = self.load_svndump('non_ascii_path_1.svndump')
subdir = '/b\xC3\xB8b'
quoted_subdir = urllib.quote(subdir)
- repo_url = test_util.fileurl(self.repo_path)
+ repo_url = test_util.fileurl(repo_path)
wc_path = self.wc_path
wc2_path = wc_path + '-2'
@@ -189,10 +188,7 @@ class TestBasicRepoLayout(test_util.TestBase):
class TestStupidPull(test_util.TestBase):
def test_stupid(self):
- repo = test_util.load_fixture_and_fetch('two_heads.svndump',
- self.repo_path,
- self.wc_path,
- True)
+ repo = self._load_fixture_and_fetch('two_heads.svndump', stupid=True)
self.assertEqual(node.hex(repo[0].node()),
'434ed487136c1b47c1e8f952edb4dc5a8e6328df')
self.assertEqual(node.hex(repo['tip'].node()),
@@ -210,12 +206,9 @@ class TestStupidPull(test_util.TestBase):
self.assertEqual(len(repo.heads()), 2)
def test_oldest_not_trunk_and_tag_vendor_branch(self):
- repo = test_util.load_fixture_and_fetch(
+ repo = self._load_fixture_and_fetch(
'tagged_vendor_and_oldest_not_trunk.svndump',
- self.repo_path,
- self.wc_path,
- True)
- repo = hg.repository(ui.ui(), self.wc_path)
+ stupid=True)
self.assertEqual(node.hex(repo['oldest'].node()),
'926671740dec045077ab20f110c1595f935334fa')
self.assertEqual(repo['tip'].parents()[0].parents()[0],
diff --git a/tests/test_fetch_command_regexes.py b/tests/test_fetch_command_regexes.py
index c783f30..6c976f4 100644
--- a/tests/test_fetch_command_regexes.py
+++ b/tests/test_fetch_command_regexes.py
@@ -48,28 +48,99 @@ Name: svn:special
class RegexTests(unittest.TestCase):
def test_empty_file_re(self):
- matches = stupid.empty_file_patch_wont_make_re.findall(two_empties)
- assert sorted(matches) == ['__init__.py', 'bar/__init__.py']
+ changed = stupid.parsediff(two_empties)
+ self.assertEqual(3, len(changed))
+ self.assertEqual('__init__.py', changed[0].name)
+ self.assert_(changed[0].isempty())
+ self.assertEqual('bar/__init__.py', changed[1].name)
+ self.assert_(changed[1].isempty())
+ self.assertEqual('bar/test_muhaha.py', changed[2].name)
+ self.assert_(not changed[2].isempty())
def test_any_matches_just_one(self):
pat = '''Index: trunk/django/contrib/admin/urls/__init__.py
===================================================================
'''
- matches = stupid.any_file_re.findall(pat)
- assert len(matches) == 1
+ changed = stupid.parsediff(pat)
+ self.assertEqual(['trunk/django/contrib/admin/urls/__init__.py'],
+ [f.name for f in changed])
def test_special_re(self):
- matches = stupid.property_special_set_re.findall(special_delta)
- assert len(matches) == 1
+ changed = stupid.parsediff(special_delta)
+ self.assertEqual(1, len(changed))
+ self.assert_(changed[0].symlink)
def test_any_file_re(self):
- matches = stupid.any_file_re.findall(two_empties)
- assert sorted(matches) == ['__init__.py', 'bar/__init__.py',
- 'bar/test_muhaha.py']
+ changed = stupid.parsediff(two_empties)
+ self.assertEqual(['__init__.py', 'bar/__init__.py', 'bar/test_muhaha.py'],
+ [f.name for f in changed])
def test_binary_file_re(self):
- matches = stupid.binary_file_re.findall(binary_delta)
- assert matches == ['trunk/functional_tests/doc_tests/test_doctest_fixtures/doctest_fixtures_fixtures.pyc']
+ changed = stupid.parsediff(binary_delta)
+ binaries = [f.name for f in changed if f.binary]
+ self.assertEqual(['trunk/functional_tests/doc_tests/test_doctest_fixtures/doctest_fixtures_fixtures.pyc'],
+ binaries)
+
+ def test_diff16(self):
+ data = """Index: d3/d
+===================================================================
+--- d3/d (revision 0)
++++ d3/d (revision 6)
+@@ -0,0 +1 @@
++d
+
+Property changes on: d3
+___________________________________________________________________
+Added: svn:externals
+ + ^/trunk/common/ext ext3
+
+
+
+Property changes on: .
+___________________________________________________________________
+Added: svn:mergeinfo
+ Merged /branches/branch:r4-5
+"""
+ changed = stupid.parsediff(data)
+ self.assertEqual(['d3/d', 'd3', '.'], [f.name for f in changed])
+ data = """Property changes on: empty1
+___________________________________________________________________
+Deleted: svn:executable
+ - *
+
+
+Property changes on: empty2
+___________________________________________________________________
+Added: svn:executable
+ + *
+
+
+Property changes on: binary1
+___________________________________________________________________
+Deleted: svn:executable
+ - *
+
+
+Property changes on: text1
+___________________________________________________________________
+Deleted: svn:executable
+ - *
+
+
+Property changes on: binary2
+___________________________________________________________________
+Added: svn:executable
+ + *
+
+
+Property changes on: text2
+___________________________________________________________________
+Added: svn:executable
+ + *
+"""
+ changed = stupid.parsediff(data)
+ self.assertEqual(['empty1', 'empty2', 'binary1', 'text1', 'binary2', 'text2'],
+ [f.name for f in changed])
def suite():
return unittest.TestLoader().loadTestsFromTestCase(RegexTests)
diff --git a/tests/test_fetch_exec.py b/tests/test_fetch_exec.py
index 4742d27..3cd4c6e 100644
--- a/tests/test_fetch_exec.py
+++ b/tests/test_fetch_exec.py
@@ -5,16 +5,12 @@ import unittest
from mercurial import node
class TestFetchExec(test_util.TestBase):
- def _load_fixture_and_fetch(self, fixture_name, stupid):
- return test_util.load_fixture_and_fetch(fixture_name, self.repo_path,
- self.wc_path, stupid=stupid)
-
def assertexec(self, ctx, files, isexec=True):
for f in files:
self.assertEqual(isexec, 'x' in ctx[f].flags())
def test_exec(self, stupid=False):
- repo = self._load_fixture_and_fetch('executebit.svndump', stupid)
+ repo = self._load_fixture_and_fetch('executebit.svndump', stupid=stupid)
self.assertexec(repo[0], ['text1', 'binary1', 'empty1'], True)
self.assertexec(repo[0], ['text2', 'binary2', 'empty2'], False)
self.assertexec(repo[1], ['text1', 'binary1', 'empty1'], False)
@@ -24,7 +20,8 @@ class TestFetchExec(test_util.TestBase):
self.test_exec(True)
def test_empty_prop_val_executable(self, stupid=False):
- repo = self._load_fixture_and_fetch('executable_file_empty_prop.svndump', stupid)
+ repo = self._load_fixture_and_fetch('executable_file_empty_prop.svndump',
+ stupid=stupid)
self.assertEqual(node.hex(repo['tip'].node()),
'08e6b380bf291b361a418203a1cb9427213cd1fd')
self.assertEqual(repo['tip']['foo'].flags(), 'x')
diff --git a/tests/test_fetch_mappings.py b/tests/test_fetch_mappings.py
index 83149ef..02727f6 100644
--- a/tests/test_fetch_mappings.py
+++ b/tests/test_fetch_mappings.py
@@ -32,14 +32,14 @@ class MapTests(test_util.TestBase):
return os.path.join(self.tmpdir, 'tagmap')
def test_author_map(self, stupid=False):
- test_util.load_svndump_fixture(self.repo_path, 'replace_trunk_with_branch.svndump')
+ repo_path = self.load_svndump('replace_trunk_with_branch.svndump')
authormap = open(self.authors, 'w')
authormap.write('Augie=Augie Fackler <durin42@gmail.com> # stuffy\n')
authormap.write("Augie Fackler <durin42@gmail.com>\n")
authormap.close()
ui = self.ui(stupid)
ui.setconfig('hgsubversion', 'authormap', self.authors)
- commands.clone(ui, test_util.fileurl(self.repo_path),
+ commands.clone(ui, test_util.fileurl(repo_path),
self.wc_path, authors=self.authors)
self.assertEqual(self.repo[0].user(),
'Augie Fackler <durin42@gmail.com>')
@@ -50,13 +50,13 @@ class MapTests(test_util.TestBase):
self.test_author_map(True)
def test_author_map_closing_author(self, stupid=False):
- test_util.load_svndump_fixture(self.repo_path, 'replace_trunk_with_branch.svndump')
+ repo_path = self.load_svndump('replace_trunk_with_branch.svndump')
authormap = open(self.authors, 'w')
authormap.write("evil=Testy <test@test>")
authormap.close()
ui = self.ui(stupid)
ui.setconfig('hgsubversion', 'authormap', self.authors)
- commands.clone(ui, test_util.fileurl(self.repo_path),
+ commands.clone(ui, test_util.fileurl(repo_path),
self.wc_path, authors=self.authors)
self.assertEqual(self.repo[0].user(),
'Augie@5b65bade-98f3-4993-a01f-b7a6710da339')
@@ -67,7 +67,8 @@ class MapTests(test_util.TestBase):
self.test_author_map_closing_author(True)
def test_author_map_no_author(self, stupid=False):
- self._load_fixture_and_fetch('no-author.svndump', stupid=stupid)
+ repo, repo_path = self.load_and_fetch('no-author.svndump',
+ stupid=stupid)
users = set(self.repo[r].user() for r in self.repo)
expected_users = ['(no author)@%s' % self.repo.svnmeta().uuid]
self.assertEqual(sorted(users), expected_users)
@@ -78,7 +79,7 @@ class MapTests(test_util.TestBase):
authormap.close()
ui = self.ui(stupid)
ui.setconfig('hgsubversion', 'authormap', self.authors)
- commands.clone(ui, test_util.fileurl(self.repo_path),
+ commands.clone(ui, test_util.fileurl(repo_path),
self.wc_path, authors=self.authors)
users = set(self.repo[r].user() for r in self.repo)
expected_users = ['Testy <test@example.com>']
@@ -100,13 +101,13 @@ class MapTests(test_util.TestBase):
self.assertEqual(fromself.symmetric_difference(all_tests), set())
def test_file_map(self, stupid=False):
- test_util.load_svndump_fixture(self.repo_path, 'replace_trunk_with_branch.svndump')
+ repo_path = self.load_svndump('replace_trunk_with_branch.svndump')
filemap = open(self.filemap, 'w')
filemap.write("include alpha\n")
filemap.close()
ui = self.ui(stupid)
ui.setconfig('hgsubversion', 'filemap', self.filemap)
- commands.clone(ui, test_util.fileurl(self.repo_path),
+ commands.clone(ui, test_util.fileurl(repo_path),
self.wc_path, filemap=self.filemap)
self.assertEqual(node.hex(self.repo[0].node()), '88e2c7492d83e4bf30fbb2dcbf6aa24d60ac688d')
self.assertEqual(node.hex(self.repo['default'].node()), 'e524296152246b3837fe9503c83b727075835155')
@@ -116,13 +117,13 @@ class MapTests(test_util.TestBase):
self.assertRaises(hgutil.Abort, self.test_file_map, True)
def test_file_map_exclude(self, stupid=False):
- test_util.load_svndump_fixture(self.repo_path, 'replace_trunk_with_branch.svndump')
+ repo_path = self.load_svndump('replace_trunk_with_branch.svndump')
filemap = open(self.filemap, 'w')
filemap.write("exclude alpha\n")
filemap.close()
ui = self.ui(stupid)
ui.setconfig('hgsubversion', 'filemap', self.filemap)
- commands.clone(ui, test_util.fileurl(self.repo_path),
+ commands.clone(ui, test_util.fileurl(repo_path),
self.wc_path, filemap=self.filemap)
self.assertEqual(node.hex(self.repo[0].node()), '2c48f3525926ab6c8b8424bcf5eb34b149b61841')
self.assertEqual(node.hex(self.repo['default'].node()), 'b37a3c0297b71f989064d9b545b5a478bbed7cc1')
@@ -131,15 +132,34 @@ class MapTests(test_util.TestBase):
# TODO: re-enable test if we ever reinstate this feature
self.assertRaises(hgutil.Abort, self.test_file_map_exclude, True)
+ def test_file_map_rule_order(self):
+ repo_path = self.load_svndump('replace_trunk_with_branch.svndump')
+ filemap = open(self.filemap, 'w')
+ filemap.write("exclude alpha\n")
+ filemap.write("include .\n")
+ filemap.write("exclude gamma\n")
+ filemap.close()
+ ui = self.ui(False)
+ ui.setconfig('hgsubversion', 'filemap', self.filemap)
+ commands.clone(ui, test_util.fileurl(repo_path),
+ self.wc_path, filemap=self.filemap)
+ # The exclusion of alpha is overridden by the later rule to
+ # include all of '.', whereas gamma should remain excluded
+ # because it's excluded after the root directory.
+ self.assertEqual(self.repo[0].manifest().keys(),
+ ['alpha', 'beta'])
+ self.assertEqual(self.repo['default'].manifest().keys(),
+ ['alpha', 'beta'])
+
def test_branchmap(self, stupid=False):
- test_util.load_svndump_fixture(self.repo_path, 'branchmap.svndump')
+ repo_path = self.load_svndump('branchmap.svndump')
branchmap = open(self.branchmap, 'w')
branchmap.write("badname = good-name # stuffy\n")
branchmap.write("feature = default\n")
branchmap.close()
ui = self.ui(stupid)
ui.setconfig('hgsubversion', 'branchmap', self.branchmap)
- commands.clone(ui, test_util.fileurl(self.repo_path),
+ commands.clone(ui, test_util.fileurl(repo_path),
self.wc_path, branchmap=self.branchmap)
branches = set(self.repo[i].branch() for i in self.repo)
self.assert_('badname' not in branches)
@@ -151,13 +171,13 @@ class MapTests(test_util.TestBase):
def test_branchmap_tagging(self, stupid=False):
'''test tagging a renamed branch, which used to raise an exception'''
- test_util.load_svndump_fixture(self.repo_path, 'commit-to-tag.svndump')
+ repo_path = self.load_svndump('commit-to-tag.svndump')
branchmap = open(self.branchmap, 'w')
branchmap.write("magic = art\n")
branchmap.close()
ui = self.ui(stupid)
ui.setconfig('hgsubversion', 'branchmap', self.branchmap)
- commands.clone(ui, test_util.fileurl(self.repo_path),
+ commands.clone(ui, test_util.fileurl(repo_path),
self.wc_path, branchmap=self.branchmap)
branches = set(self.repo[i].branch() for i in self.repo)
self.assertEquals(sorted(branches), ['art', 'closeme'])
@@ -167,13 +187,13 @@ class MapTests(test_util.TestBase):
def test_branchmap_empty_commit(self, stupid=False):
'''test mapping an empty commit on a renamed branch'''
- test_util.load_svndump_fixture(self.repo_path, 'propset-branch.svndump')
+ repo_path = self.load_svndump('propset-branch.svndump')
branchmap = open(self.branchmap, 'w')
branchmap.write("the-branch = bob\n")
branchmap.close()
ui = self.ui(stupid)
ui.setconfig('hgsubversion', 'branchmap', self.branchmap)
- commands.clone(ui, test_util.fileurl(self.repo_path),
+ commands.clone(ui, test_util.fileurl(repo_path),
self.wc_path, branchmap=self.branchmap)
branches = set(self.repo[i].branch() for i in self.repo)
self.assertEquals(sorted(branches), ['bob', 'default'])
@@ -184,14 +204,14 @@ class MapTests(test_util.TestBase):
def test_branchmap_combine(self, stupid=False):
'''test combining two branches, but retaining heads'''
- test_util.load_svndump_fixture(self.repo_path, 'branchmap.svndump')
+ repo_path = self.load_svndump('branchmap.svndump')
branchmap = open(self.branchmap, 'w')
branchmap.write("badname = default\n")
branchmap.write("feature = default\n")
branchmap.close()
ui = self.ui(stupid)
ui.setconfig('hgsubversion', 'branchmap', self.branchmap)
- commands.clone(ui, test_util.fileurl(self.repo_path),
+ commands.clone(ui, test_util.fileurl(repo_path),
self.wc_path, branchmap=self.branchmap)
branches = set(self.repo[i].branch() for i in self.repo)
self.assertEquals(sorted(branches), ['default'])
@@ -209,14 +229,14 @@ class MapTests(test_util.TestBase):
def test_branchmap_rebuildmeta(self, stupid=False):
'''test rebuildmeta on a branchmapped clone'''
- test_util.load_svndump_fixture(self.repo_path, 'branchmap.svndump')
+ repo_path = self.load_svndump('branchmap.svndump')
branchmap = open(self.branchmap, 'w')
branchmap.write("badname = dit\n")
branchmap.write("feature = dah\n")
branchmap.close()
ui = self.ui(stupid)
ui.setconfig('hgsubversion', 'branchmap', self.branchmap)
- commands.clone(ui, test_util.fileurl(self.repo_path),
+ commands.clone(ui, test_util.fileurl(repo_path),
self.wc_path, branchmap=self.branchmap)
originfo = self.repo.svnmeta().branches
@@ -225,7 +245,7 @@ class MapTests(test_util.TestBase):
src, dest = test_util.hgclone(ui, self.wc_path, self.wc_path + '_clone',
update=False)
svncommands.rebuildmeta(ui, dest,
- args=[test_util.fileurl(self.repo_path)])
+ args=[test_util.fileurl(repo_path)])
# just check the keys; assume the contents are unaffected by the branch
# map and thus properly tested by other tests
@@ -238,14 +258,14 @@ class MapTests(test_util.TestBase):
def test_branchmap_verify(self, stupid=False):
'''test verify on a branchmapped clone'''
- test_util.load_svndump_fixture(self.repo_path, 'branchmap.svndump')
+ repo_path = self.load_svndump('branchmap.svndump')
branchmap = open(self.branchmap, 'w')
branchmap.write("badname = dit\n")
branchmap.write("feature = dah\n")
branchmap.close()
ui = self.ui(stupid)
ui.setconfig('hgsubversion', 'branchmap', self.branchmap)
- commands.clone(ui, test_util.fileurl(self.repo_path),
+ commands.clone(ui, test_util.fileurl(repo_path),
self.wc_path, branchmap=self.branchmap)
repo = self.repo
@@ -264,7 +284,7 @@ class MapTests(test_util.TestBase):
is to not convert the 'this' branches. Until we can do that, we settle
with aborting.
'''
- test_util.load_svndump_fixture(self.repo_path, 'propset-branch.svndump')
+ repo_path = self.load_svndump('propset-branch.svndump')
branchmap = open(self.branchmap, 'w')
branchmap.write("closeme =\n")
branchmap.close()
@@ -272,8 +292,7 @@ class MapTests(test_util.TestBase):
maps.BranchMap, self.ui(), self.branchmap)
def test_tagmap(self, stupid=False):
- test_util.load_svndump_fixture(self.repo_path,
- 'basic_tag_tests.svndump')
+ repo_path = self.load_svndump('basic_tag_tests.svndump')
tagmap = open(self.tagmap, 'w')
tagmap.write("tag_r3 = 3.x # stuffy\n")
tagmap.write("copied_tag = \n")
@@ -281,7 +300,7 @@ class MapTests(test_util.TestBase):
ui = self.ui(stupid)
ui.setconfig('hgsubversion', 'tagmap', self.tagmap)
- commands.clone(ui, test_util.fileurl(self.repo_path),
+ commands.clone(ui, test_util.fileurl(repo_path),
self.wc_path, tagmap=self.tagmap)
tags = self.repo.tags()
assert 'tag_r3' not in tags
@@ -292,8 +311,7 @@ class MapTests(test_util.TestBase):
self.test_tagmap(True)
def test_tagren_changed(self, stupid=False):
- test_util.load_svndump_fixture(self.repo_path,
- 'commit-to-tag.svndump')
+ repo_path = self.load_svndump('commit-to-tag.svndump')
tagmap = open(self.tagmap, 'w')
tagmap.write("edit-at-create = edit-past\n")
tagmap.write("also-edit = \n")
@@ -302,7 +320,7 @@ class MapTests(test_util.TestBase):
ui = self.ui(stupid)
ui.setconfig('hgsubversion', 'tagmap', self.tagmap)
- commands.clone(ui, test_util.fileurl(self.repo_path),
+ commands.clone(ui, test_util.fileurl(repo_path),
self.wc_path, tagmap=self.tagmap)
tags = self.repo.tags()
@@ -310,8 +328,8 @@ class MapTests(test_util.TestBase):
self.test_tagren_changed(True)
def test_empty_log_message(self, stupid=False):
- repo = self._load_fixture_and_fetch('empty-log-message.svndump',
- stupid=stupid)
+ repo, repo_path = self.load_and_fetch('empty-log-message.svndump',
+ stupid=stupid)
self.assertEqual(repo['tip'].description(), '')
@@ -319,7 +337,7 @@ class MapTests(test_util.TestBase):
ui = self.ui(stupid)
ui.setconfig('hgsubversion', 'defaultmessage', 'blyf')
- commands.clone(ui, test_util.fileurl(self.repo_path), self.wc_path)
+ commands.clone(ui, test_util.fileurl(repo_path), self.wc_path)
self.assertEqual(self.repo['tip'].description(), 'blyf')
diff --git a/tests/test_fetch_symlinks.py b/tests/test_fetch_symlinks.py
index ecb2522..7f9252b 100644
--- a/tests/test_fetch_symlinks.py
+++ b/tests/test_fetch_symlinks.py
@@ -3,12 +3,8 @@ import test_util
import unittest
class TestFetchSymlinks(test_util.TestBase):
- def _load_fixture_and_fetch(self, fixture_name, stupid):
- return test_util.load_fixture_and_fetch(fixture_name, self.repo_path,
- self.wc_path, stupid=stupid)
-
def test_symlinks(self, stupid=False):
- repo = self._load_fixture_and_fetch('symlinks.svndump', stupid)
+ repo = self._load_fixture_and_fetch('symlinks.svndump', stupid=stupid)
# Check symlinks throughout history
links = {
0: {
@@ -43,7 +39,9 @@ class TestFetchSymlinks(test_util.TestBase):
for rev in repo:
ctx = repo[rev]
for f in ctx.manifest():
- self.assertEqual(f in links[rev], 'l' in ctx[f].flags())
+ l = 'l' in ctx[f].flags()
+ lref = f in links[rev]
+ self.assertEqual(lref, l, '%r != %r for %s@%r' % (lref, l, f, rev))
if f in links[rev]:
self.assertEqual(links[rev][f], ctx[f].data())
for f in links[rev]:
diff --git a/tests/test_fetch_truncated.py b/tests/test_fetch_truncated.py
index 9cb7300..6f3e991 100644
--- a/tests/test_fetch_truncated.py
+++ b/tests/test_fetch_truncated.py
@@ -8,8 +8,8 @@ from mercurial import hg
class TestFetchTruncatedHistory(test_util.TestBase):
def test_truncated_history(self, stupid=False):
# Test repository does not follow the usual layout
- test_util.load_svndump_fixture(self.repo_path, 'truncatedhistory.svndump')
- svn_url = test_util.fileurl(self.repo_path + '/project2')
+ repo_path = self.load_svndump('truncatedhistory.svndump')
+ svn_url = test_util.fileurl(repo_path + '/project2')
commands.clone(self.ui(stupid), svn_url, self.wc_path, noupdate=True)
repo = hg.repository(self.ui(stupid), self.wc_path)
diff --git a/tests/test_pull.py b/tests/test_pull.py
index b0f61b2..556fbf1 100644
--- a/tests/test_pull.py
+++ b/tests/test_pull.py
@@ -11,38 +11,38 @@ class TestPull(test_util.TestBase):
def setUp(self):
super(TestPull, self).setUp()
- def _load_fixture_and_fetch(self, fixture_name):
- return test_util.load_fixture_and_fetch(fixture_name, self.repo_path,
- self.wc_path, stupid=False,
- noupdate=False)
+ def _loadupdate(self, fixture_name):
+ repo, repo_path = self.load_and_fetch(fixture_name, stupid=False,
+ noupdate=False)
+ return repo, repo_path
def test_nochanges(self):
- self._load_fixture_and_fetch('single_rev.svndump')
+ self._loadupdate('single_rev.svndump')
state = self.repo.parents()
commands.pull(self.repo.ui, self.repo)
self.assertEqual(state, self.repo.parents())
def test_onerevision_noupdate(self):
- repo = self._load_fixture_and_fetch('single_rev.svndump')
+ repo, repo_path = self._loadupdate('single_rev.svndump')
state = repo.parents()
- self._add_svn_rev({'trunk/alpha': 'Changed'})
+ self.add_svn_rev(repo_path, {'trunk/alpha': 'Changed'})
commands.pull(self.repo.ui, repo)
self.assertEqual(state, repo.parents())
self.assertTrue('tip' not in repo[None].tags())
def test_onerevision_doupdate(self):
- repo = self._load_fixture_and_fetch('single_rev.svndump')
+ repo, repo_path = self._loadupdate('single_rev.svndump')
state = repo.parents()
- self._add_svn_rev({'trunk/alpha': 'Changed'})
+ self.add_svn_rev(repo_path, {'trunk/alpha': 'Changed'})
commands.pull(self.repo.ui, repo, update=True)
self.failIfEqual(state, repo.parents())
self.assertTrue('tip' in repo[None].tags())
def test_onerevision_divergent(self):
- repo = self._load_fixture_and_fetch('single_rev.svndump')
+ repo, repo_path = self._loadupdate('single_rev.svndump')
self.commitchanges((('alpha', 'alpha', 'Changed another way'),))
state = repo.parents()
- self._add_svn_rev({'trunk/alpha': 'Changed one way'})
+ self.add_svn_rev(repo_path, {'trunk/alpha': 'Changed one way'})
try:
commands.pull(self.repo.ui, repo, update=True)
except hgutil.Abort:
@@ -53,7 +53,7 @@ class TestPull(test_util.TestBase):
self.assertEqual(len(repo.heads()), 2)
def test_tag_repull_doesnt_happen(self):
- repo = self._load_fixture_and_fetch('branchtagcollision.svndump')
+ repo = self._loadupdate('branchtagcollision.svndump')[0]
oldheads = map(node.hex, repo.heads())
commands.pull(repo.ui, repo)
self.assertEqual(oldheads, map(node.hex, repo.heads()))
diff --git a/tests/test_push_command.py b/tests/test_push_command.py
index 0f078b4..8342100 100644
--- a/tests/test_push_command.py
+++ b/tests/test_push_command.py
@@ -3,6 +3,7 @@ import test_util
import atexit
import errno
import os
+import sys
import random
import shutil
import socket
@@ -22,9 +23,7 @@ import time
class PushTests(test_util.TestBase):
def setUp(self):
test_util.TestBase.setUp(self)
- test_util.load_fixture_and_fetch('simple_branch.svndump',
- self.repo_path,
- self.wc_path)
+ self.repo_path = self.load_and_fetch('simple_branch.svndump')[1]
def test_cant_push_empty_ctx(self):
repo = self.repo
@@ -77,15 +76,15 @@ class PushTests(test_util.TestBase):
self.assertEqual(new_hash, tip.node())
def internal_push_over_svnserve(self, subdir='', commit=True):
- test_util.load_svndump_fixture(self.repo_path, 'simple_branch.svndump')
- open(os.path.join(self.repo_path, 'conf', 'svnserve.conf'),
+ repo_path = self.load_svndump('simple_branch.svndump')
+ open(os.path.join(repo_path, 'conf', 'svnserve.conf'),
'w').write('[general]\nanon-access=write\n[sasl]\n')
self.port = random.randint(socket.IPPORT_USERRESERVED, 65535)
self.host = 'localhost'
args = ['svnserve', '--daemon', '--foreground',
'--listen-port=%d' % self.port,
'--listen-host=%s' % self.host,
- '--root=%s' % self.repo_path]
+ '--root=%s' % repo_path]
svnserve = subprocess.Popen(args, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
@@ -133,8 +132,10 @@ class PushTests(test_util.TestBase):
self.assertNotEqual('an_author', tip.user())
self.assertEqual('(no author)', tip.user().rsplit('@', 1)[0])
finally:
- # TODO: use svnserve.kill() in Python >2.5
- test_util.kill_process(svnserve)
+ if sys.version_info >= (2,6):
+ svnserve.kill()
+ else:
+ test_util.kill_process(svnserve)
def test_push_over_svnserve(self):
self.internal_push_over_svnserve()
diff --git a/tests/test_push_dirs.py b/tests/test_push_dirs.py
index 3b43130..442d3fe 100644
--- a/tests/test_push_dirs.py
+++ b/tests/test_push_dirs.py
@@ -4,7 +4,7 @@ import unittest
class TestPushDirectories(test_util.TestBase):
def test_push_dirs(self):
- self._load_fixture_and_fetch('emptyrepo.svndump')
+ repo_path = self.load_and_fetch('emptyrepo.svndump')[1]
changes = [
# Single file in single directory
@@ -18,7 +18,7 @@ class TestPushDirectories(test_util.TestBase):
]
self.commitchanges(changes)
self.pushrevisions()
- self.assertEqual(self.svnls('trunk'),
+ self.assertEqual(test_util.svnls(repo_path, 'trunk'),
['d1', 'd1/a', 'd2', 'd2/a', 'd2/b', 'd31',
'd31/d32', 'd31/d32/a', 'd31/d32/d33',
'd31/d32/d33/d34', 'd31/d32/d33/d34/a'])
@@ -42,17 +42,17 @@ class TestPushDirectories(test_util.TestBase):
]
self.commitchanges(changes)
self.pushrevisions()
- self.assertEqual(self.svnls('trunk'),
+ self.assertEqual(test_util.svnls(repo_path, 'trunk'),
['d2', 'd2/b', 'd31', 'd31/d32', 'd31/d32/a', ])
def test_push_new_dir_project_root_not_repo_root(self):
- self._load_fixture_and_fetch('fetch_missing_files_subdir.svndump',
- subdir='foo')
+ repo_path = self.load_and_fetch('fetch_missing_files_subdir.svndump',
+ subdir='foo')[1]
changes = [('magic_new/a', 'magic_new/a', 'ohai',),
]
self.commitchanges(changes)
self.pushrevisions()
- self.assertEqual(self.svnls('foo/trunk'), ['bar',
+ self.assertEqual(test_util.svnls(repo_path, 'foo/trunk'), ['bar',
'bar/alpha',
'bar/beta',
'bar/delta',
@@ -62,38 +62,38 @@ class TestPushDirectories(test_util.TestBase):
'magic_new/a'])
def test_push_new_file_existing_dir_root_not_repo_root(self):
- self._load_fixture_and_fetch('empty_dir_in_trunk_not_repo_root.svndump',
- subdir='project')
+ repo_path = self.load_and_fetch('empty_dir_in_trunk_not_repo_root.svndump',
+ subdir='project')[1]
changes = [('narf/a', 'narf/a', 'ohai',),
]
self.commitchanges(changes)
- self.assertEqual(self.svnls('project/trunk'), ['a',
+ self.assertEqual(test_util.svnls(repo_path, 'project/trunk'), ['a',
'narf',
])
self.pushrevisions()
- self.assertEqual(self.svnls('project/trunk'), ['a',
+ self.assertEqual(test_util.svnls(repo_path, 'project/trunk'), ['a',
'narf',
'narf/a'])
changes = [('narf/a', None, None,),
]
self.commitchanges(changes)
self.pushrevisions()
- self.assertEqual(self.svnls('project/trunk'), ['a'])
+ self.assertEqual(test_util.svnls(repo_path, 'project/trunk'), ['a'])
def test_push_single_dir_change_in_subdir(self):
# Tests simple pushing from default branch to a single dir repo
# Changes a file in a subdir (regression).
- repo = self._load_fixture_and_fetch('branch_from_tag.svndump',
- stupid=False,
- layout='single',
- subdir='tags')
+ repo, repo_path = self.load_and_fetch('branch_from_tag.svndump',
+ stupid=False,
+ layout='single',
+ subdir='tags')
changes = [('tag_r3/alpha', 'tag_r3/alpha', 'foo'),
('tag_r3/new', 'tag_r3/new', 'foo'),
('new_dir/new', 'new_dir/new', 'foo'),
]
self.commitchanges(changes)
self.pushrevisions()
- self.assertEqual(self.svnls('tags'),
+ self.assertEqual(test_util.svnls(repo_path, 'tags'),
['copied_tag',
'copied_tag/alpha',
'copied_tag/beta',
diff --git a/tests/test_push_eol.py b/tests/test_push_eol.py
index fe89529..24eaf16 100644
--- a/tests/test_push_eol.py
+++ b/tests/test_push_eol.py
@@ -5,9 +5,7 @@ import unittest
class TestPushEol(test_util.TestBase):
def setUp(self):
test_util.TestBase.setUp(self)
- test_util.load_fixture_and_fetch('emptyrepo.svndump',
- self.repo_path,
- self.wc_path)
+ self._load_fixture_and_fetch('emptyrepo.svndump')
def _test_push_dirs(self, stupid):
changes = [
diff --git a/tests/test_push_renames.py b/tests/test_push_renames.py
index ea7acff..a5fbb4c 100644
--- a/tests/test_push_renames.py
+++ b/tests/test_push_renames.py
@@ -6,10 +6,8 @@ import unittest
class TestPushRenames(test_util.TestBase):
def setUp(self):
test_util.TestBase.setUp(self)
- test_util.load_fixture_and_fetch('pushrenames.svndump',
- self.repo_path,
- self.wc_path,
- True)
+ self.repo_path = self.load_and_fetch('pushrenames.svndump',
+ stupid=True)[1]
def _debug_print_copies(self, ctx):
w = sys.stderr.write
@@ -110,9 +108,8 @@ class TestPushRenames(test_util.TestBase):
]
self.commitchanges(changes)
self.pushrevisions()
- # print '\n'.join(sorted(self.svnls('trunk')))
assert reduce(lambda x, y: x and y,
- ('geek' not in f for f in self.svnls('trunk'))), (
+ ('geek' not in f for f in test_util.svnls(self.repo_path, 'trunk'))), (
'This failure means rename of an entire tree is broken.'
' There is a print on the preceding line commented out '
'that should help you.')
diff --git a/tests/test_rebuildmeta.py b/tests/test_rebuildmeta.py
index 7182dbb..bee8e37 100644
--- a/tests/test_rebuildmeta.py
+++ b/tests/test_rebuildmeta.py
@@ -28,7 +28,8 @@ def _do_case(self, name, stupid, single):
layout = 'auto'
if single:
layout = 'single'
- self._load_fixture_and_fetch(name, subdir=subdir, stupid=stupid, layout=layout)
+ repo, repo_path = self.load_and_fetch(name, subdir=subdir, stupid=stupid,
+ layout=layout)
assert len(self.repo) > 0
wc2_path = self.wc_path + '_clone'
u = ui.ui()
@@ -44,7 +45,7 @@ def _do_case(self, name, stupid, single):
try:
svncommands.rebuildmeta(u, dest,
- args=[test_util.fileurl(self.repo_path +
+ args=[test_util.fileurl(repo_path +
subdir), ])
finally:
# remove the wrapper
diff --git a/tests/test_single_dir_clone.py b/tests/test_single_dir_clone.py
index 2e035a5..52ebe0f 100644
--- a/tests/test_single_dir_clone.py
+++ b/tests/test_single_dir_clone.py
@@ -80,10 +80,10 @@ class TestSingleDir(test_util.TestBase):
def test_push_single_dir(self):
# Tests simple pushing from default branch to a single dir repo
- repo = self._load_fixture_and_fetch('branch_from_tag.svndump',
- stupid=False,
- layout='single',
- subdir='')
+ repo, repo_path = self.load_and_fetch('branch_from_tag.svndump',
+ stupid=False,
+ layout='single',
+ subdir='')
def file_callback(repo, memctx, path):
if path == 'adding_file':
return context.memfilectx(path=path,
@@ -109,14 +109,16 @@ class TestSingleDir(test_util.TestBase):
repo.commitctx(ctx)
hg.update(repo, repo['tip'].node())
self.pushrevisions()
- self.assertTrue('adding_file' in self.svnls(''))
+ self.assertTrue('adding_file' in test_util.svnls(repo_path, ''))
self.assertEqual('application/octet-stream',
- self.svnpropget('adding_binary', 'svn:mime-type'))
+ test_util.svnpropget(repo_path, 'adding_binary',
+ 'svn:mime-type'))
# Now add another commit and test mime-type being reset
changes = [('adding_binary', 'adding_binary', 'no longer binary')]
self.commitchanges(changes)
self.pushrevisions()
- self.assertEqual('', self.svnpropget('adding_binary', 'svn:mime-type'))
+ self.assertEqual('', test_util.svnpropget(repo_path, 'adding_binary',
+ 'svn:mime-type'))
def test_push_single_dir_at_subdir(self):
repo = self._load_fixture_and_fetch('branch_from_tag.svndump',
@@ -152,11 +154,11 @@ class TestSingleDir(test_util.TestBase):
# Pushes two outgoing over one incoming svn rev
# (used to cause an "unknown revision")
# This can happen if someone committed to svn since our last pull (race).
- repo = self._load_fixture_and_fetch('branch_from_tag.svndump',
- stupid=False,
- layout='single',
- subdir='trunk')
- self._add_svn_rev({'trunk/alpha': 'Changed'})
+ repo, repo_path = self.load_and_fetch('branch_from_tag.svndump',
+ stupid=False,
+ layout='single',
+ subdir='trunk')
+ self.add_svn_rev(repo_path, {'trunk/alpha': 'Changed'})
def file_callback(repo, memctx, path):
return context.memfilectx(path=path,
data='data of %s' % path,
@@ -175,18 +177,18 @@ class TestSingleDir(test_util.TestBase):
repo.commitctx(ctx)
hg.update(repo, repo['tip'].node())
self.pushrevisions(expected_extra_back=1)
- self.assertTrue('trunk/one' in self.svnls(''))
- self.assertTrue('trunk/two' in self.svnls(''))
+ self.assertTrue('trunk/one' in test_util.svnls(repo_path, ''))
+ self.assertTrue('trunk/two' in test_util.svnls(repo_path, ''))
def test_push_single_dir_branch(self):
# Tests local branches pushing to a single dir repo. Creates a fork at
# tip. The default branch adds a file called default, while branch foo
# adds a file called foo, then tries to push the foo branch and default
# branch in that order.
- repo = self._load_fixture_and_fetch('branch_from_tag.svndump',
- stupid=False,
- layout='single',
- subdir='')
+ repo, repo_path = self.load_and_fetch('branch_from_tag.svndump',
+ stupid=False,
+ layout='single',
+ subdir='')
def file_callback(data):
def cb(repo, memctx, path):
if path == data:
@@ -214,7 +216,7 @@ class TestSingleDir(test_util.TestBase):
hg.update(repo, repo['foo'].node())
self.pushrevisions()
repo = self.repo # repo is outdated after the rebase happens, refresh
- self.assertTrue('foo' in self.svnls(''))
+ self.assertTrue('foo' in test_util.svnls(repo_path, ''))
self.assertEqual(repo.branchtags().keys(), ['default'])
# Have to cross to another branch head, so hg.update doesn't work
commands.update(ui.ui(),
@@ -222,19 +224,18 @@ class TestSingleDir(test_util.TestBase):
self.repo.branchheads('default')[1],
clean=True)
self.pushrevisions()
- self.assertTrue('default' in self.svnls(''))
+ self.assertTrue('default' in test_util.svnls(repo_path, ''))
self.assertEquals(len(self.repo.branchheads('default')), 1)
@test_util.requiresoption('branch')
def test_push_single_dir_renamed_branch(self, stupid=False):
# Tests pulling and pushing with a renamed branch
# Based on test_push_single_dir
- test_util.load_svndump_fixture(self.repo_path,
- 'branch_from_tag.svndump')
+ repo_path = self.load_svndump('branch_from_tag.svndump')
cmd = ['clone', '--layout=single', '--branch=flaf']
if stupid:
cmd.append('--stupid')
- cmd += [test_util.fileurl(self.repo_path), self.wc_path]
+ cmd += [test_util.fileurl(repo_path), self.wc_path]
test_util.dispatch(cmd)
def file_callback(repo, memctx, path):
@@ -256,7 +257,7 @@ class TestSingleDir(test_util.TestBase):
self.repo.commitctx(ctx)
hg.update(self.repo, self.repo['tip'].node())
self.pushrevisions()
- self.assertTrue('adding_file' in self.svnls(''))
+ self.assertTrue('adding_file' in test_util.svnls(repo_path, ''))
self.assertEquals(set(['flaf']),
set(self.repo[i].branch() for i in self.repo))
diff --git a/tests/test_startrev.py b/tests/test_startrev.py
index 026e5be..e544bff 100644
--- a/tests/test_startrev.py
+++ b/tests/test_startrev.py
@@ -36,6 +36,7 @@ def buildmethod(case, name, subdir, stupid):
# these fixtures contain no files at HEAD and would result in empty clones
nofiles = set([
'binaryfiles.svndump',
+ 'binaryfiles-broken.svndump',
'emptyrepo.svndump',
])
diff --git a/tests/test_svnwrap.py b/tests/test_svnwrap.py
index c5a5d2e..dae7dff 100644
--- a/tests/test_svnwrap.py
+++ b/tests/test_svnwrap.py
@@ -1,23 +1,12 @@
import test_util
-import imp
import os
import subprocess
-import shutil
import tempfile
import unittest
from hgsubversion import svnwrap
-import os
-import stat
-def force_rm(path):
- os.chmod(
- path,
- os.stat(path).st_mode | stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH
- )
- os.remove(path)
-
class TestBasicRepoLayout(unittest.TestCase):
def setUp(self):
self.tmpdir = tempfile.mkdtemp('svnwrap_test')
@@ -35,8 +24,7 @@ class TestBasicRepoLayout(unittest.TestCase):
def tearDown(self):
del self.repo
- shutil.rmtree(self.tmpdir, onerror=lambda func, path, e: force_rm(path))
-
+ test_util.rmtree(self.tmpdir)
def test_num_revs(self):
revs = list(self.repo.revisions())
diff --git a/tests/test_tags.py b/tests/test_tags.py
index 55cbb0c..630e039 100644
--- a/tests/test_tags.py
+++ b/tests/test_tags.py
@@ -12,10 +12,6 @@ from hgsubversion import svncommands
from hgsubversion import svnrepo
class TestTags(test_util.TestBase):
- def _load_fixture_and_fetch(self, fixture_name, stupid=False):
- return test_util.load_fixture_and_fetch(fixture_name, self.repo_path,
- self.wc_path, stupid=stupid)
-
def test_tags(self, stupid=False):
repo = self._load_fixture_and_fetch('basic_tag_tests.svndump',
stupid=stupid)
@@ -109,8 +105,8 @@ rename a tag
self.test_most_recent_is_edited(True)
def test_most_recent_is_edited(self, stupid=False):
- repo = self._load_fixture_and_fetch('most-recent-is-edit-tag.svndump',
- stupid=stupid)
+ repo, repo_path = self.load_and_fetch('most-recent-is-edit-tag.svndump',
+ stupid=stupid)
self.repo.ui.status(
"Note: this test failing may be because of a rebuildmeta failure.\n"
"You should check that before assuming issues with this test.\n")
@@ -118,7 +114,7 @@ rename a tag
src, dest = test_util.hgclone(repo.ui, self.wc_path, wc2_path, update=False)
svncommands.rebuildmeta(repo.ui,
dest,
- args=[test_util.fileurl(self.repo_path), ])
+ args=[test_util.fileurl(repo_path), ])
commands.pull(self.repo.ui, self.repo, stupid=stupid)
dtags, srctags = dest.tags(), self.repo.tags()
dtags.pop('tip')
diff --git a/tests/test_urls.py b/tests/test_urls.py
index d18aeb5..249a743 100644
--- a/tests/test_urls.py
+++ b/tests/test_urls.py
@@ -63,10 +63,9 @@ class TestSubversionUrls(test_util.TestBase):
def test_quoting(self):
ui = self.ui()
- test_util.load_svndump_fixture(self.repo_path,
- 'non_ascii_path_1.svndump')
+ repo_path = self.load_svndump('non_ascii_path_1.svndump')
- repo_url = test_util.fileurl(self.repo_path)
+ repo_url = test_util.fileurl(repo_path)
subdir = '/b\xC3\xB8b'
quoted_subdir = urllib.quote(subdir)
diff --git a/tests/test_util.py b/tests/test_util.py
index 63b02f8..0343fe2 100644
--- a/tests/test_util.py
+++ b/tests/test_util.py
@@ -2,7 +2,6 @@ import StringIO
import difflib
import errno
import gettext
-import imp
import os
import shutil
import stat
@@ -158,18 +157,6 @@ def testui(stupid=False, layout='auto', startrev=0):
u.setconfig('hgsubversion', 'startrev', startrev)
return u
-def load_svndump_fixture(path, fixture_name):
- '''Loads an svnadmin dump into a fresh repo at path, which should not
- already exist.
- '''
- if os.path.exists(path): rmtree(path)
- subprocess.call(['svnadmin', 'create', path, ],
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- inp = open(os.path.join(FIXTURES, fixture_name))
- proc = subprocess.Popen(['svnadmin', 'load', path, ], stdin=inp,
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- proc.communicate()
-
def dispatch(cmd):
try:
req = dispatchmod.request(cmd)
@@ -177,31 +164,6 @@ def dispatch(cmd):
except AttributeError, e:
dispatchmod.dispatch(cmd)
-def load_fixture_and_fetch(fixture_name, repo_path, wc_path, stupid=False,
- subdir='', noupdate=True, layout='auto',
- startrev=0, externals=None):
- load_svndump_fixture(repo_path, fixture_name)
- if subdir:
- repo_path += '/' + subdir
-
- cmd = [
- 'clone',
- '--layout=%s' % layout,
- '--startrev=%s' % startrev,
- fileurl(repo_path),
- wc_path,
- ]
- if stupid:
- cmd.append('--stupid')
- if noupdate:
- cmd.append('--noupdate')
- if externals:
- cmd[:0] = ['--config', 'hgsubversion.externals=%s' % externals]
-
- dispatch(cmd)
-
- return hg.repository(testui(), wc_path)
-
def rmtree(path):
# Read-only files cannot be removed under Windows
for root, dirs, files in os.walk(path):
@@ -244,6 +206,32 @@ def hgclone(ui, source, dest, update=True):
src, dest = hg.clone(ui, source, dest, update=update)
return src, dest
+def svnls(repo_path, path, rev='HEAD'):
+ path = repo_path + '/' + path
+ path = util.normalize_url(fileurl(path))
+ args = ['svn', 'ls', '-r', rev, '-R', path]
+ p = subprocess.Popen(args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ stdout, stderr = p.communicate()
+ if p.returncode:
+ raise Exception('svn ls failed on %s: %r' % (path, stderr))
+ entries = [e.strip('/') for e in stdout.splitlines()]
+ entries.sort()
+ return entries
+
+def svnpropget(repo_path, path, prop, rev='HEAD'):
+ path = repo_path + '/' + path
+ path = util.normalize_url(fileurl(path))
+ args = ['svn', 'propget', '-r', str(rev), prop, path]
+ p = subprocess.Popen(args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ stdout, stderr = p.communicate()
+ if p.returncode:
+ raise Exception('svn ls failed on %s: %r' % (path, stderr))
+ return stdout.strip()
+
class TestBase(unittest.TestCase):
def setUp(self):
_verify_our_modules()
@@ -263,7 +251,7 @@ class TestBase(unittest.TestCase):
for l in '[extensions]', 'hgsubversion=':
print >> rc, l
- self.repo_path = '%s/testrepo' % self.tmpdir
+ self.repocount = 0
self.wc_path = '%s/testrepo_wc' % self.tmpdir
self.svn_wc = None
@@ -275,6 +263,10 @@ class TestBase(unittest.TestCase):
self.patch = (ui.ui.write_err, ui.ui.write)
setattr(ui.ui, self.patch[0].func_name, self.patch[1])
+ def _makerepopath(self):
+ self.repocount += 1
+ return '%s/testrepo-%d' % (self.tmpdir, self.repocount)
+
def tearDown(self):
for var, val in self.oldenv.iteritems():
if val is None:
@@ -291,24 +283,61 @@ class TestBase(unittest.TestCase):
def ui(self, stupid=False, layout='auto'):
return testui(stupid, layout)
- def _load_fixture_and_fetch(self, fixture_name, subdir=None, stupid=False,
- layout='auto', startrev=0, externals=None):
+ def load_svndump(self, fixture_name):
+ '''Loads an svnadmin dump into a fresh repo. Return the svn repo
+ path.
+ '''
+ path = self._makerepopath()
+ assert not os.path.exists(path)
+ subprocess.call(['svnadmin', 'create', path,],
+ stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ inp = open(os.path.join(FIXTURES, fixture_name))
+ proc = subprocess.Popen(['svnadmin', 'load', path,], stdin=inp,
+ stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ proc.communicate()
+ return path
+
+ def load_and_fetch(self, fixture_name, subdir=None, stupid=False,
+ layout='auto', startrev=0, externals=None,
+ noupdate=True):
if layout == 'single':
if subdir is None:
subdir = 'trunk'
elif subdir is None:
subdir = ''
- return load_fixture_and_fetch(fixture_name, self.repo_path,
- self.wc_path, subdir=subdir,
- stupid=stupid, layout=layout,
- startrev=startrev, externals=externals)
-
- def _add_svn_rev(self, changes):
+ repo_path = self.load_svndump(fixture_name)
+ projectpath = repo_path
+ if subdir:
+ projectpath += '/' + subdir
+
+ cmd = [
+ 'clone',
+ '--layout=%s' % layout,
+ '--startrev=%s' % startrev,
+ fileurl(projectpath),
+ self.wc_path,
+ ]
+ if stupid:
+ cmd.append('--stupid')
+ if noupdate:
+ cmd.append('--noupdate')
+ if externals:
+ cmd[:0] = ['--config', 'hgsubversion.externals=%s' % externals]
+
+ dispatch(cmd)
+
+ return hg.repository(testui(), self.wc_path), repo_path
+
+ def _load_fixture_and_fetch(self, *args, **kwargs):
+ repo, repo_path = self.load_and_fetch(*args, **kwargs)
+ return repo
+
+ def add_svn_rev(self, repo_path, changes):
'''changes is a dict of filename -> contents'''
if self.svn_wc is None:
self.svn_wc = os.path.join(self.tmpdir, 'testsvn_wc')
subprocess.call([
- 'svn', 'co', '-q', fileurl(self.repo_path),
+ 'svn', 'co', '-q', fileurl(repo_path),
self.svn_wc
],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
@@ -338,26 +367,12 @@ class TestBase(unittest.TestCase):
self.assertEqual(expected_extra_back, after - before)
return res
- def svnls(self, path, rev='HEAD'):
- path = self.repo_path + '/' + path
- path = util.normalize_url(fileurl(path))
- args = ['svn', 'ls', '-r', rev, '-R', path]
- p = subprocess.Popen(args,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT)
- stdout, stderr = p.communicate()
- if p.returncode:
- raise Exception('svn ls failed on %s: %r' % (path, stderr))
- entries = [e.strip('/') for e in stdout.splitlines()]
- entries.sort()
- return entries
-
- def svnco(self, svnpath, rev, path):
+ def svnco(self, repo_path, svnpath, rev, path):
path = os.path.join(self.wc_path, path)
subpath = os.path.dirname(path)
if not os.path.isdir(subpath):
os.makedirs(subpath)
- svnpath = fileurl(self.repo_path + '/' + svnpath)
+ svnpath = fileurl(repo_path + '/' + svnpath)
args = ['svn', 'co', '-r', rev, svnpath, path]
p = subprocess.Popen(args,
stdout=subprocess.PIPE,
@@ -366,18 +381,6 @@ class TestBase(unittest.TestCase):
if p.returncode:
raise Exception('svn co failed on %s: %r' % (svnpath, stderr))
- def svnpropget(self, path, prop, rev='HEAD'):
- path = self.repo_path + '/' + path
- path = util.normalize_url(fileurl(path))
- args = ['svn', 'propget', '-r', str(rev), prop, path]
- p = subprocess.Popen(args,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT)
- stdout, stderr = p.communicate()
- if p.returncode:
- raise Exception('svn ls failed on %s: %r' % (path, stderr))
- return stdout.strip()
-
def commitchanges(self, changes, parent='tip', message='automated test'):
"""Commit changes to mercurial directory
diff --git a/tests/test_utility_commands.py b/tests/test_utility_commands.py
index 2de14bb..10926e2 100644
--- a/tests/test_utility_commands.py
+++ b/tests/test_utility_commands.py
@@ -2,6 +2,7 @@ import test_util
import os
import unittest
+import re
from hgext import rebase
from mercurial import hg
@@ -25,13 +26,12 @@ Last Changed Rev: %(rev)s
Last Changed Date: %(date)s
'''
-class UtilityTests(test_util.TestBase):
- @property
- def repourl(self):
- return util.normalize_url(test_util.fileurl(self.repo_path))
+def repourl(repo_path):
+ return util.normalize_url(test_util.fileurl(repo_path))
+class UtilityTests(test_util.TestBase):
def test_info_output(self):
- self._load_fixture_and_fetch('two_heads.svndump')
+ repo, repo_path = self.load_and_fetch('two_heads.svndump')
hg.update(self.repo, 'the_branch')
u = self.ui()
u.pushbuffer()
@@ -39,7 +39,7 @@ class UtilityTests(test_util.TestBase):
actual = u.popbuffer()
expected = (expected_info_output %
{'date': '2008-10-08 01:39:05 +0000 (Wed, 08 Oct 2008)',
- 'repourl': self.repourl,
+ 'repourl': repourl(repo_path),
'branch': 'branches/the_branch',
'rev': 5,
})
@@ -50,7 +50,7 @@ class UtilityTests(test_util.TestBase):
actual = u.popbuffer()
expected = (expected_info_output %
{'date': '2008-10-08 01:39:29 +0000 (Wed, 08 Oct 2008)',
- 'repourl': self.repourl,
+ 'repourl': repourl(repo_path),
'branch': 'trunk',
'rev': 6,
})
@@ -61,14 +61,14 @@ class UtilityTests(test_util.TestBase):
actual = u.popbuffer()
expected = (expected_info_output %
{'date': '2008-10-08 01:39:05 +0000 (Wed, 08 Oct 2008)',
- 'repourl': self.repourl,
+ 'repourl': repourl(repo_path),
'branch': 'branches/the_branch',
'rev': 5,
})
self.assertMultiLineEqual(actual, expected)
def test_info_single(self):
- self._load_fixture_and_fetch('two_heads.svndump', subdir='trunk')
+ repo, repo_path = self.load_and_fetch('two_heads.svndump', subdir='trunk')
hg.update(self.repo, 'tip')
u = self.ui()
u.pushbuffer()
@@ -76,7 +76,7 @@ class UtilityTests(test_util.TestBase):
actual = u.popbuffer()
expected = (expected_info_output %
{'date': '2008-10-08 01:39:29 +0000 (Wed, 08 Oct 2008)',
- 'repourl': self.repourl,
+ 'repourl': repourl(repo_path),
'branch': 'trunk',
'rev': 6,
})
@@ -153,7 +153,7 @@ class UtilityTests(test_util.TestBase):
self.assertEqual(actual, '4:1083037b18d8\n')
def test_outgoing_output(self):
- self._load_fixture_and_fetch('two_heads.svndump')
+ repo, repo_path = self.load_and_fetch('two_heads.svndump')
u = self.ui()
parents = (self.repo['the_branch'].node(), revlog.nullid,)
def filectxfn(repo, memctx, path):
@@ -173,13 +173,13 @@ class UtilityTests(test_util.TestBase):
new = self.repo.commitctx(ctx)
hg.update(self.repo, new)
u.pushbuffer()
- commands.outgoing(u, self.repo, self.repourl)
+ commands.outgoing(u, self.repo, repourl(repo_path))
actual = u.popbuffer()
self.assertTrue(node.hex(self.repo['localbranch'].node())[:8] in actual)
self.assertEqual(actual.strip(), '5:6de15430fa20')
hg.update(self.repo, 'default')
u.pushbuffer()
- commands.outgoing(u, self.repo, self.repourl)
+ commands.outgoing(u, self.repo, repourl(repo_path))
actual = u.popbuffer()
self.assertEqual(actual, '')
@@ -211,11 +211,10 @@ class UtilityTests(test_util.TestBase):
def test_genignore(self):
""" Test generation of .hgignore file. """
- test_util.load_fixture_and_fetch('ignores.svndump', self.repo_path,
- self.wc_path, noupdate=False)
+ repo = self._load_fixture_and_fetch('ignores.svndump', noupdate=False)
u = self.ui()
u.pushbuffer()
- svncommands.genignore(u, self.repo, self.wc_path)
+ svncommands.genignore(u, repo, self.wc_path)
self.assertMultiLineEqual(open(os.path.join(self.wc_path, '.hgignore')).read(),
'.hgignore\nsyntax:glob\nblah\notherblah\nbaz/magic\n')
@@ -229,26 +228,44 @@ class UtilityTests(test_util.TestBase):
'.hgignore\nsyntax:glob\nblah\notherblah\nbaz/magic\n')
def test_list_authors(self):
- test_util.load_svndump_fixture(self.repo_path,
- 'replace_trunk_with_branch.svndump')
+ repo_path = self.load_svndump('replace_trunk_with_branch.svndump')
u = self.ui()
u.pushbuffer()
svncommands.listauthors(u,
- args=[test_util.fileurl(self.repo_path)],
+ args=[test_util.fileurl(repo_path)],
authors=None)
actual = u.popbuffer()
self.assertMultiLineEqual(actual, 'Augie\nevil\n')
-
def test_list_authors_map(self):
- test_util.load_svndump_fixture(self.repo_path,
- 'replace_trunk_with_branch.svndump')
- author_path = os.path.join(self.repo_path, 'authors')
+ repo_path = self.load_svndump('replace_trunk_with_branch.svndump')
+ author_path = os.path.join(repo_path, 'authors')
svncommands.listauthors(self.ui(),
- args=[test_util.fileurl(self.repo_path)],
- authors=author_path)
+ args=[test_util.fileurl(repo_path)],
+ authors=author_path)
self.assertMultiLineEqual(open(author_path).read(), 'Augie=\nevil=\n')
+ def test_svnverify(self):
+ repo, repo_path = self.load_and_fetch('binaryfiles.svndump',
+ noupdate=False)
+ ret = svncommands.verify(self.ui(), repo, [], rev=1)
+ self.assertEqual(0, ret)
+ repo_path = self.load_svndump('binaryfiles-broken.svndump')
+ u = self.ui()
+ u.pushbuffer()
+ ret = svncommands.verify(u, repo, [test_util.fileurl(repo_path)],
+ rev=1)
+ output = u.popbuffer()
+ self.assertEqual(1, ret)
+ output = re.sub(r'file://\S+', 'file://', output)
+ self.assertEqual("""\
+verifying d51f46a715a1 against file://
+difference in file binary2
+unexpected files:
+ binary1
+missing files:
+ binary3
+""", output)
def suite():
all_tests = [unittest.TestLoader().loadTestsFromTestCase(UtilityTests),