durin42-hgsubversion-77b22e5b4ea6/.hg_archival.txt0000644000000000000000000000016712043462603020116 0ustar 00000000000000repo: f2636cfed11500fdc47d1e3822d8e4a2bd636bf7 node: 77b22e5b4ea6c248e079afd0f1e544cb5690ce20 branch: default tag: 1.5 durin42-hgsubversion-77b22e5b4ea6/.hgignore0000644000000000000000000000032012043462603016622 0ustar 00000000000000syntax:glob build *.pyc *.pyo .DS_Store *.swp *~ .coverage cover *.py,cover MANIFEST dist *.egg-info hgsubversion/__version__.py nbproject .project .pydevproject .settings *.orig .noseids tests/fixtures/temp durin42-hgsubversion-77b22e5b4ea6/.hgtags0000644000000000000000000000063512043462603016306 0ustar 0000000000000097f2079e3778511944ffb6a662520580650a3993 1.0 09c016174e332eceda015d3f43d96d7e0228acf3 1.0.1 4359ddd73b009fbb356c52ea5d99cba25222ee7a 1.1 8e621dbb82d4363a85317638ad237e2817c56347 1.1.1 093ae2915b452539b44390ee4ea14987484e1eee 1.1.2 708234ad6c97fb52417e0b46a86c8373e25123a5 1.2 4bbc6bf947f56a92e95a04a27b94a9f72d5482d7 1.2.1 0cbf9fd89672e73165e1bb4db1ec8f7f65b95c94 1.3 07234759a3f750029ccaa001837d42fa12dd33ee 1.4 durin42-hgsubversion-77b22e5b4ea6/COPYING0000644000000000000000000004311012043462603016056 0ustar 00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. durin42-hgsubversion-77b22e5b4ea6/MANIFEST.in0000644000000000000000000000013312043462603016557 0ustar 00000000000000include Makefile hgsubversion/help/*.rst recursive-include tests *.py *.sh *.svndump *.txt durin42-hgsubversion-77b22e5b4ea6/Makefile0000644000000000000000000000166312043462603016472 0ustar 00000000000000# Makefile for testing hgsubversion PYTHON=python .PHONY: all check check-demandimport check-subvertpy check-swig all: @echo "Use the following commands to build and install hgsubversion:" @echo @echo "$$ cd $(PWD)" @echo "$$ $(PYTHON) ./setup.py install" @echo @exit 1 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 HGRCPATH=/dev/null \ hg --config extensions.hgsubversion=./hgsubversion \ version 2>&1 \ | egrep '(^abort:|failed to import extension)' LC_ALL=C HGSUBVERSION_BINDINGS=none HGRCPATH=/dev/null \ hg --config extensions.hgsubversion=./hgsubversion \ version --svn 2>&1 \ | egrep '(^abort:|failed to import extension)' check-subvertpy: $(PYTHON) tests/run.py --all --bindings=subvertpy check-swig: $(PYTHON) tests/run.py --all --bindings=swig durin42-hgsubversion-77b22e5b4ea6/README0000644000000000000000000000472612043462603015715 0ustar 00000000000000.. -*-restructuredtext-*- ============ hgsubversion ============ hgsubversion is an extension for Mercurial that allows using Mercurial as a Subversion client. At this point, hgsubversion is usable by users reasonably familiar with Mercurial as a VCS. It's not recommended to dive into hgsubversion as an introduction to Mercurial, since hgsubversion "bends the rules" a little and violates some of the typical assumptions of early Mercurial users. Installation ------------ You need to have either have Subversion 1.5 (or later) installed along with either Subvertpy 0.7.4 (or later) or the Subversion SWIG Python bindings. You need Mercurial 1.3 or later. .. _mercurial: http://selenic.com/repo/hg .. _mercurial-stable: http://selenic.com/repo/hg-stable .. _crew: http://hg.intevation.org/mercurial/crew .. _crew-stable: http://hg.intevation.org/mercurial/crew-stable If you are unfamiliar with installing Mercurial extensions, please see the UsingExtensions_ page in the Mercurial wiki. Look at the example for specifying an absolute path near the bottom of the page. You want to give the path to the top level of your clone of this repository. .. _UsingExtensions: http://mercurial.selenic.com/wiki/UsingExtensions Before using hgsubversion, I *strongly* encourage you to run the automated tests. Just use nose_ if you have it (or ``easy_install nose`` if you want it), or use ``python tests/run.py`` to run the suite with the conventional test runner. Note that because I use nose, there's a lot of stdout spew in the tests right now. The important part is that all the tests pass. .. _nose: http://code.google.com/p/python-nose/ You can check that hgsubversion is installed and properly activated using the following command:: $ hg version --svn Mercurial Distributed SCM (version ...) Copyright (C) 2005-2010 Matt Mackall and others This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. hgsubversion: ... Subversion: ... bindings: Subvertpy ... If your bindings are listed as `SWIG`, please consider installing Subvertpy_. .. _Subvertpy: http://pypi.python.org/pypi/subvertpy Further Reading --------------- More information on how to use hgsubversion is available from within Mercurial in the `subversion` help topic. To view it, use:: $ hg help subversion The Restructured Text source for this topic is also available in the file ``hgsubverson/help/subversion.rst``. durin42-hgsubversion-77b22e5b4ea6/hgsubversion/__init__.py0000644000000000000000000001627412043462603021665 0ustar 00000000000000'''integration with Subversion repositories hgsubversion is an extension for Mercurial that allows it to act as a Subversion client, offering fast, incremental and bidirectional synchronisation. At this point, hgsubversion is usable by users reasonably familiar with Mercurial as a VCS. It's not recommended to dive into hgsubversion as an introduction to Mercurial, since hgsubversion "bends the rules" a little and violates some of the typical assumptions of early Mercurial users. Before using hgsubversion, we *strongly* encourage running the automated tests. See 'README' in the hgsubversion directory for details. For more information and instructions, see :hg:`help subversion`. ''' import os import sys import traceback from mercurial import commands from mercurial import extensions from mercurial import help from mercurial import hg from mercurial import util as hgutil from mercurial import demandimport demandimport.ignore.extend([ 'svn', 'svn.client', 'svn.core', 'svn.delta', 'svn.ra', ]) try: from mercurial import templatekw # force demandimport to load templatekw templatekw.keywords except ImportError: templatekw = None try: from mercurial import revset # force demandimport to load revset revset.methods except ImportError: revset = None try: from mercurial import subrepo # require svnsubrepo and hg >= 1.7.1 subrepo.svnsubrepo hgutil.checknlink except (ImportError, AttributeError), e: subrepo = None import svncommands import util import svnrepo import wrappers import svnexternals svnopts = [ ('', 'stupid', None, 'use slower, but more compatible, protocol for Subversion'), ] # generic means it picks up all options from svnopts # fixdoc means update the docstring # TODO: fixdoc hoses l18n wrapcmds = { # cmd: generic, target, fixdoc, ppopts, opts 'parents': (False, None, False, False, [ ('', 'svn', None, 'show parent svn revision instead'), ]), 'diff': (False, None, False, False, [ ('', 'svn', None, 'show svn diffs against svn parent'), ]), 'pull': (True, 'sources', True, True, []), 'push': (True, 'destinations', True, True, []), 'incoming': (False, 'sources', True, True, []), 'version': (False, None, False, False, [ ('', 'svn', None, 'print hgsubversion information as well')]), 'clone': (False, 'sources', True, True, [ ('T', 'tagpaths', '', 'list of paths to search for tags in Subversion repositories'), ('A', 'authors', '', 'file mapping Subversion usernames to Mercurial authors'), ('', 'filemap', '', 'file containing rules for remapping Subversion repository paths'), ('', 'layout', 'auto', ('import standard layout or single ' 'directory? Can be standard, single, or auto.')), ('', 'branchmap', '', 'file containing rules for branch conversion'), ('', 'tagmap', '', 'file containing rules for renaming tags'), ('', 'startrev', '', ('convert Subversion revisions starting at the one ' 'specified, either an integer revision or HEAD; ' 'HEAD causes only the latest revision to be ' 'pulled')), ]), } try: from mercurial import discovery def findcommonoutgoing(orig, *args, **opts): capable = getattr(args[1], 'capable', lambda x: False) if capable('subversion'): return wrappers.findcommonoutgoing(*args, **opts) else: return orig(*args, **opts) extensions.wrapfunction(discovery, 'findcommonoutgoing', findcommonoutgoing) except AttributeError: # only need the discovery variant of this code when we drop hg < 1.6 def findoutgoing(orig, *args, **opts): capable = getattr(args[1], 'capable', lambda x: False) if capable('subversion'): return wrappers.findoutgoing(*args, **opts) else: return orig(*args, **opts) extensions.wrapfunction(discovery, 'findoutgoing', findoutgoing) except ImportError: pass def extsetup(): """insert command wrappers for a bunch of commands""" # add the ui argument to this function once we drop support for 1.3 docvals = {'extension': 'hgsubversion'} for cmd, (generic, target, fixdoc, ppopts, opts) in wrapcmds.iteritems(): if fixdoc and wrappers.generic.__doc__: docvals['command'] = cmd docvals['Command'] = cmd.capitalize() docvals['target'] = target doc = wrappers.generic.__doc__.strip() % docvals fn = getattr(commands, cmd) fn.__doc__ = fn.__doc__.rstrip() + '\n\n ' + doc wrapped = generic and wrappers.generic or getattr(wrappers, cmd) entry = extensions.wrapcommand(commands.table, cmd, wrapped) if ppopts: entry[1].extend(svnopts) if opts: entry[1].extend(opts) try: rebase = extensions.find('rebase') if not rebase: return entry = extensions.wrapcommand(rebase.cmdtable, 'rebase', wrappers.rebase) entry[1].append(('', 'svn', None, 'automatic svn rebase')) except: pass helpdir = os.path.join(os.path.dirname(__file__), 'help') entries = ( (['subversion'], "Working with Subversion Repositories", lambda: open(os.path.join(helpdir, 'subversion.rst')).read()), ) # in 1.6 and earler the help table is a tuple if getattr(help.helptable, 'extend', None): help.helptable.extend(entries) else: help.helptable = help.helptable + entries if templatekw: templatekw.keywords.update(util.templatekeywords) if revset: revset.symbols.update(util.revsets) if subrepo: subrepo.types['hgsubversion'] = svnexternals.svnsubrepo 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): if util.islocalrepo(url): return svnrepo else: return _old_local(url) # install scheme handlers hg.schemes.update({ 'file': _lookup, 'http': svnrepo, 'https': svnrepo, 'svn': svnrepo, 'svn+ssh': svnrepo, 'svn+http': svnrepo, 'svn+https': svnrepo}) commands.optionalrepo += ' svn' cmdtable = { "svn": (svncommands.svn, [('u', 'svn-url', '', 'path to the Subversion server.'), ('', 'stupid', False, 'be stupid and use diffy replay.'), ('A', 'authors', '', 'username mapping filename'), ('', 'filemap', '', 'remap file to exclude paths or include only certain paths'), ('', 'force', False, 'force an operation to happen'), ('', 'username', '', 'username for authentication'), ('', 'password', '', 'password for authentication'), ('r', 'rev', '', 'Mercurial revision'), ('', 'unsafe-skip-uuid-check', False, 'skip repository uuid check in rebuildmeta'), ], 'hg svn ...', ), } # only these methods are public __all__ = ('cmdtable', 'reposetup', 'uisetup') durin42-hgsubversion-77b22e5b4ea6/hgsubversion/editor.py0000644000000000000000000005735612043462603021422 0ustar 00000000000000import errno import sys import tempfile import shutil import os from mercurial import util as hgutil from mercurial import revlog from mercurial import node import svnwrap import util import svnexternals class EditingError(Exception): pass class FileStore(object): def __init__(self, maxsize=None): self._tempdir = None self._files = {} self._created = 0 self._maxsize = maxsize if self._maxsize is None: self._maxsize = 100*(2**20) self._size = 0 self._data = {} self._popped = set() def setfile(self, fname, data): if fname in self._popped: raise EditingError('trying to set a popped file %s' % fname) if self._maxsize < 0 or (len(data) + self._size) <= self._maxsize: self._data[fname] = data self._size += len(data) else: if self._tempdir is None: self._tempdir = tempfile.mkdtemp(prefix='hg-subversion-') # Avoid filename issues with these simple names fn = str(self._created) fp = hgutil.posixfile(os.path.join(self._tempdir, fn), 'wb') try: fp.write(data) finally: fp.close() self._created += 1 self._files[fname] = fn def delfile(self, fname): if fname in self._popped: raise EditingError('trying to delete a popped file %s' % fname) if fname in self._data: del self._data[fname] elif fname in self._files: path = os.path.join(self._tempdir, self._files.pop(fname)) os.unlink(path) def getfile(self, fname): if fname in self._popped: raise EditingError('trying to get a popped file %s' % fname) if fname in self._data: return self._data[fname] if self._tempdir is None or fname not in self._files: raise IOError path = os.path.join(self._tempdir, self._files[fname]) fp = hgutil.posixfile(path, 'rb') try: return fp.read() finally: fp.close() def popfile(self, fname): self.delfile(fname) self._popped.add(fname) def files(self): return list(self._files) + list(self._data) def close(self): if self._tempdir is not None: tempdir, self._tempdir = self._tempdir, None shutil.rmtree(tempdir) self._files = None self._data = None class RevisionData(object): __slots__ = [ 'file', 'added', 'deleted', 'rev', 'execfiles', 'symlinks', 'copies', 'emptybranches', 'base', 'externals', 'ui', 'exception', 'store', ] def __init__(self, ui): self.ui = ui self.clear() def clear(self): self.store = FileStore(util.getfilestoresize(self.ui)) self.added = set() self.deleted = {} self.rev = None self.execfiles = {} self.symlinks = {} # Map fully qualified destination file paths to module source path self.copies = {} self.emptybranches = {} self.externals = {} self.exception = None def set(self, path, data, isexec=False, islink=False, copypath=None): self.store.setfile(path, data) self.execfiles[path] = isexec self.symlinks[path] = islink if path in self.deleted: del self.deleted[path] if copypath is not None: self.copies[path] = copypath def get(self, path): if path in self.deleted: raise IOError(errno.ENOENT, '%s is deleted' % path) data = self.store.getfile(path) isexec = self.execfiles.get(path) islink = self.symlinks.get(path) copied = self.copies.get(path) return data, isexec, islink, copied def pop(self, path): ret = self.get(path) self.store.popfile(path) return ret def delete(self, path): self.deleted[path] = True self.store.delfile(path) self.execfiles[path] = False self.symlinks[path] = False self.ui.note('D %s\n' % path) def files(self): """Return a sorted list of changed files.""" files = set(self.store.files()) for g in (self.symlinks, self.execfiles, self.deleted): files.update(g) return sorted(files) def close(self): self.store.close() class CopiedFile(object): def __init__(self, node, path, copypath): self.node = node self.path = path self.copypath = copypath def resolve(self, getctxfn, ctx=None): if ctx is None: ctx = getctxfn(self.node) fctx = ctx[self.path] data = fctx.data() flags = fctx.flags() islink = 'l' in flags if islink: data = 'link ' + data return data, 'x' in flags, islink, self.copypath class HgEditor(svnwrap.Editor): def __init__(self, meta): self.meta = meta self.ui = meta.ui self.repo = meta.repo self.current = RevisionData(meta.ui) self._clear() def setsvn(self, svn): self._svn = svn def _clear(self): self._filecounter = 0 # A mapping of svn paths to CopiedFile entries self._svncopies = {} # A mapping of batons to (path, data, isexec, islink, copypath) tuples # data is a SimpleStringIO if the file was edited, a string # otherwise. self._openfiles = {} # A mapping of file paths to batons self._openpaths = {} self._deleted = set() self._getctx = util.lrucachefunc(self.repo.changectx, 3) # A stack of opened directory (baton, path) pairs. self._opendirs = [] self._missing = set() def _openfile(self, path, data, isexec, islink, copypath, create=False): if path in self._openpaths: raise EditingError('trying to open an already opened file %s' % path) if not create and path in self._deleted: raise EditingError('trying to open a deleted file %s' % path) if path in self._deleted: self._deleted.remove(path) self._filecounter += 1 baton = 'f%d-%s' % (self._filecounter, path) self._openfiles[baton] = (path, data, isexec, islink, copypath) self._openpaths[path] = baton return baton def _opendir(self, path): self._filecounter += 1 baton = 'f%d-%s' % (self._filecounter, path) self._opendirs.append((baton, path)) return baton def _checkparentdir(self, baton): if not self._opendirs: raise EditingError('trying to operate on an already closed ' 'directory: %s' % baton) if self._opendirs[-1][0] != baton: raise EditingError('can only operate on the most recently ' 'opened directory: %s != %s' % (self._opendirs[-1][0], baton)) def _deletefile(self, path): if self.meta.is_path_valid(path): self._deleted.add(path) if path in self._svncopies: del self._svncopies[path] self._missing.discard(path) def addmissing(self, path, isdir=False): svn = self._svn root = svn.subdir and svn.subdir[1:] or '' if not isdir: self._missing.add(path[len(root):]) else: # Resolve missing directories content immediately so the # missing files maybe processed by delete actions. rev = self.current.rev.revnum path = path + '/' parentdir = path[len(root):] for f, k in svn.list_files(parentdir, rev): if k != 'f': continue f = parentdir + f if not self.meta.is_path_valid(f, False): continue self._missing.add(f) @svnwrap.ieditor def delete_entry(self, path, revision_bogus, parent_baton, pool=None): self._checkparentdir(parent_baton) br_path, branch = self.meta.split_branch_path(path)[:2] if br_path == '': if self.meta.get_path_tag(path): # Tag deletion is not handled as branched deletion return self.meta.closebranches.add(branch) # Delete copied entries, no need to check they exist in hg # parent revision. if path in self._svncopies: del self._svncopies[path] prefix = path + '/' for f in list(self._svncopies): if f.startswith(prefix): self._deletefile(f) if path in self._missing: self._missing.remove(path) else: for f in list(self._missing): if f.startswith(prefix): self._missing.remove(f) if br_path is not None: ha = self.meta.get_parent_revision(self.current.rev.revnum, branch) if ha == revlog.nullid: return ctx = self._getctx(ha) if br_path not in ctx: br_path2 = '' if br_path != '': br_path2 = br_path + '/' # assuming it is a directory self.current.externals[path] = None for f in ctx.walk(util.PrefixMatch(br_path2)): f_p = '%s/%s' % (path, f[len(br_path2):]) self._deletefile(f_p) self._deletefile(path) @svnwrap.ieditor def open_file(self, path, parent_baton, base_revision, p=None): self._checkparentdir(parent_baton) if not self.meta.is_path_valid(path): return None fpath, branch = self.meta.split_branch_path(path)[:2] self.ui.note('M %s\n' % path) if path in self._svncopies: copy = self._svncopies.pop(path) base, isexec, islink, copypath = copy.resolve(self._getctx) return self._openfile(path, base, isexec, islink, copypath) baserev = base_revision if baserev is None or baserev == -1: baserev = self.current.rev.revnum - 1 # Use exact=True because during replacements ('R' action) we select # replacing branch as parent, but svn delta editor provides delta # agains replaced branch. parent = self.meta.get_parent_revision(baserev + 1, branch, True) ctx = self._getctx(parent) if fpath not in ctx: self.addmissing(path) return None fctx = ctx.filectx(fpath) base = fctx.data() flags = fctx.flags() if 'l' in flags: base = 'link ' + base return self._openfile(path, base, 'x' in flags, 'l' in flags, None) @svnwrap.ieditor def add_file(self, path, parent_baton=None, copyfrom_path=None, copyfrom_revision=None, file_pool=None): self._checkparentdir(parent_baton) # Use existing=False because we use the fact a file is being # added here to populate the branchmap which is used with # existing=True. fpath, branch = self.meta.split_branch_path(path, existing=False)[:2] if not fpath or fpath not in self.meta.filemap: return None if path in self._svncopies: raise EditingError('trying to replace copied file %s' % path) if path in self._deleted: self._deleted.remove(path) if (branch not in self.meta.branches and not self.meta.get_path_tag(self.meta.remotename(branch))): # we know this branch will exist now, because it has at # least one file. Rock. self.meta.branches[branch] = None, 0, self.current.rev.revnum if not copyfrom_path: self.ui.note('A %s\n' % path) self.current.added.add(path) return self._openfile(path, '', False, False, None, create=True) self.ui.note('A+ %s\n' % path) (from_file, from_branch) = self.meta.split_branch_path(copyfrom_path)[:2] if not from_file: self.addmissing(path) return None # Use exact=True because during replacements ('R' action) we select # replacing branch as parent, but svn delta editor provides delta # agains replaced branch. ha = self.meta.get_parent_revision(copyfrom_revision + 1, from_branch, True) ctx = self._getctx(ha) if from_file not in ctx: self.addmissing(path) return None fctx = ctx.filectx(from_file) flags = fctx.flags() self.current.set(path, fctx.data(), 'x' in flags, 'l' in flags) copypath = None if from_branch == branch: parentid = self.meta.get_parent_revision( self.current.rev.revnum, branch) if parentid != revlog.nullid: parentctx = self._getctx(parentid) if util.issamefile(parentctx, ctx, from_file): copypath = from_file return self._openfile(path, fctx.data(), 'x' in flags, 'l' in flags, copypath, create=True) @svnwrap.ieditor def close_file(self, file_baton, checksum, pool=None): if file_baton is None: return if file_baton not in self._openfiles: raise EditingError('trying to close a non-open file %s' % file_baton) path, data, isexec, islink, copypath = self._openfiles.pop(file_baton) del self._openpaths[path] if not isinstance(data, basestring): # Files can be opened, properties changed and apply_text # never called, in which case data is still a string. data = data.getvalue() self.current.set(path, data, isexec, islink, copypath) @svnwrap.ieditor def add_directory(self, path, parent_baton, copyfrom_path, copyfrom_revision, dir_pool=None): self._checkparentdir(parent_baton) baton = self._opendir(path) br_path, branch = self.meta.split_branch_path(path)[:2] if br_path is not None: if not copyfrom_path and not br_path: self.current.emptybranches[branch] = True else: self.current.emptybranches[branch] = False if br_path is None or not copyfrom_path: return baton if self.meta.get_path_tag(path): del self.current.emptybranches[branch] return baton tag = self.meta.get_path_tag(copyfrom_path) if tag not in self.meta.tags: tag = None if not self.meta.is_path_valid(copyfrom_path, existing=False): # The source path only exists at copyfrom_revision, use # existing=False to guess a possible branch location and # test it against the filemap. The actual path and # revision will be resolved below if necessary. self.addmissing(path, isdir=True) return baton if tag: changeid = self.meta.tags[tag] source_rev, source_branch = self.meta.get_source_rev(changeid)[:2] frompath = '' else: source_rev = copyfrom_revision frompath, source_branch = self.meta.split_branch_path(copyfrom_path)[:2] new_hash = self.meta.get_parent_revision(source_rev + 1, source_branch, True) if new_hash == node.nullid: self.addmissing(path, isdir=True) return baton fromctx = self._getctx(new_hash) if frompath != '/' and frompath != '': frompath = '%s/' % frompath else: frompath = '' copyfromparent = False if frompath == '' and br_path == '': pnode = self.meta.get_parent_revision( self.current.rev.revnum, branch) if pnode == new_hash: # Data parent is topological parent and relative paths # are the same, not need to do anything but restore # files marked as deleted. copyfromparent = True # Get the parent which would have been used for this branch # without the replace action. oldpnode = self.meta.get_parent_revision( self.current.rev.revnum, branch, exact=True) if (oldpnode != revlog.nullid and util.isancestor(self._getctx(oldpnode), fromctx)): # Branch-wide replacement, unmark the branch as deleted self.meta.closebranches.discard(branch) svncopies = {} copies = {} for f in fromctx: if not f.startswith(frompath): continue dest = path + '/' + f[len(frompath):] if not self.meta.is_path_valid(dest): continue if dest in self._deleted: self._deleted.remove(dest) if copyfromparent: continue svncopies[dest] = CopiedFile(new_hash, f, None) if branch == source_branch: copies[dest] = f if copies: # Preserve the directory copy records if no file was changed between # the source and destination revisions, or discard it completely. parentid = self.meta.get_parent_revision( self.current.rev.revnum, branch) if parentid != revlog.nullid: parentctx = self._getctx(parentid) for k, v in copies.iteritems(): if util.issamefile(parentctx, fromctx, v): svncopies[k].copypath = v self._svncopies.update(svncopies) # Copy the externals definitions of copied directories fromext = svnexternals.parse(self.ui, fromctx) for p, v in fromext.iteritems(): pp = p and (p + '/') or '' if pp.startswith(frompath): dest = (path + '/' + pp[len(frompath):]).rstrip('/') self.current.externals[dest] = v return baton @svnwrap.ieditor def change_file_prop(self, file_baton, name, value, pool=None): if file_baton is None: return path, data, isexec, islink, copypath = self._openfiles[file_baton] changed = False if name == 'svn:executable': changed = True isexec = bool(value is not None) elif name == 'svn:special': changed = True islink = bool(value is not None) if changed: self._openfiles[file_baton] = (path, data, isexec, islink, copypath) @svnwrap.ieditor def change_dir_prop(self, dir_baton, name, value, pool=None): self._checkparentdir(dir_baton) if len(self._opendirs) == 1: return path = self._opendirs[-1][1] if name == 'svn:externals': self.current.externals[path] = value @svnwrap.ieditor def open_root(self, edit_baton, base_revision, dir_pool=None): # We should not have to reset these, unfortunately the editor is # reused for different revisions. self._clear() return self._opendir('') @svnwrap.ieditor def open_directory(self, path, parent_baton, base_revision, dir_pool=None): self._checkparentdir(parent_baton) baton = self._opendir(path) p_, branch = self.meta.split_branch_path(path)[:2] if p_ == '' or (self.meta.layout == 'single' and p_): if not self.meta.get_path_tag(path): self.current.emptybranches[branch] = False return baton @svnwrap.ieditor def close_directory(self, dir_baton, dir_pool=None): self._checkparentdir(dir_baton) self._opendirs.pop() @svnwrap.ieditor def apply_textdelta(self, file_baton, base_checksum, pool=None): if file_baton is None: return lambda x: None if file_baton not in self._openfiles: raise EditingError('trying to patch a closed file %s' % file_baton) path, base, isexec, islink, copypath = self._openfiles[file_baton] if not isinstance(base, basestring): raise EditingError('trying to edit a file again: %s' % path) if not self.meta.is_path_valid(path): return lambda x: None target = svnwrap.SimpleStringIO(closing=False) self.stream = target handler = svnwrap.apply_txdelta(base, target) if not callable(handler): # pragma: no cover raise hgutil.Abort('Error in Subversion bindings: ' 'cannot call handler!') def txdelt_window(window): try: if not self.meta.is_path_valid(path): return try: handler(window) except AssertionError, e: # pragma: no cover # Enhance the exception message msg, others = e.args[0], e.args[1:] if msg: msg += '\n' msg += _TXDELT_WINDOW_HANDLER_FAILURE_MSG e.args = (msg,) + others raise e # window being None means commit this file if not window: self._openfiles[file_baton] = ( path, target, isexec, islink, copypath) except svnwrap.SubversionException, e: # pragma: no cover if e.args[1] == svnwrap.ERR_INCOMPLETE_DATA: self.addmissing(path) else: # pragma: no cover raise hgutil.Abort(*e.args) except: # pragma: no cover self._exception_info = sys.exc_info() raise return txdelt_window def close(self): if self._openfiles: for e in self._openfiles.itervalues(): self.ui.debug('error: %s was not closed\n' % e[0]) raise EditingError('%d edited files were not closed' % len(self._openfiles)) if self._opendirs: raise EditingError('directory %s was not closed' % self._opendirs[-1][1]) # Resolve by changelog entries to avoid extra reads nodes = {} for path, copy in self._svncopies.iteritems(): nodes.setdefault(copy.node, []).append((path, copy)) for node, copies in nodes.iteritems(): for path, copy in copies: data, isexec, islink, copied = copy.resolve(self._getctx) self.current.set(path, data, isexec, islink, copied) self._svncopies.clear() # Resolve missing files if self._missing: missing = sorted(self._missing) self.ui.debug('fetching %s files that could not use replay.\n' % len(missing)) if self.ui.configbool('hgsubversion', 'failonmissing', False): raise EditingError('missing entry: %s' % missing[0]) svn = self._svn rev = self.current.rev.revnum root = svn.subdir and svn.subdir[1:] or '' i = 1 for f in missing: if self.ui.debugflag: self.ui.debug('fetching %s\n' % f) else: self.ui.note('.') self.ui.flush() if i % 50 == 0: svn.init_ra_and_client() i += 1 data, mode = svn.get_file(f, rev) self.current.set(f, data, 'x' in mode, 'l' in mode) if not self.ui.debugflag: self.ui.note('\n') for f in self._deleted: self.current.delete(f) self._deleted.clear() _TXDELT_WINDOW_HANDLER_FAILURE_MSG = ( "Your SVN repository may not be supplying correct replay deltas." " It is strongly" "\nadvised that you repull the entire SVN repository using" " hg pull --stupid." "\nAlternatively, re-pull just this revision using --stupid and verify" " that the" "\nchangeset is correct." ) durin42-hgsubversion-77b22e5b4ea6/hgsubversion/help/subversion.rst0000644000000000000000000004057312043462603023434 0ustar 00000000000000Basic Use --------- Converting a Subversion repository to Mercurial with hgsubversion is done by cloning it. Subversion repositories are specified using the same, regular URL syntax that Subversion uses. hgsubversion accepts URIs such as the following:: http://user:sekrit@example.com/repo https://user@example.com/repo svn://example.com/repo svn+ssh://example.com/repo file:///tmp/repo In the case of the two first schemas, HTTP and HTTPS, the repository is first treated as a Mercurial repository, and a Subversion pull attempted should it fail. As this can be particularly annoying for repositories that require authentication, such repositories may also specified using a svn+http or svn+https schema. To create a new Mercurial clone, you can use a command such as the following:: $ hg clone [destination] Or with a real example:: $ hg clone http://python-nose.googlecode.com/svn nose-hg Please note that there are two slightly different ways of cloning repositories: The most common desire is to have the full history of a repository, including all its tags and branches. In such cases you should clone from one level above trunk, as in the example above. This is known as `standard layout`, and works with repositories that use the conventional ``trunk``, ``tags`` and ``branches`` directories. By default, hgsubversion will use this layout whenever it finds any of these directories at the specified directory on the server. If you instead want to clone just a single directory or branch, clone the specific directory path. In the example above, to get *only* trunk, you would issue ``hg clone http://python-nose.googlecode.com/svn/trunk nose-trunk``. This works with any directory with a Subversion repository, and is known as a single directory clone. Normally, converted changesets will be marked as belonging to the ``default`` branch, but this can be changed by using the ``-b/--branch`` option when using Mercurial 1.5 or later. To force single directory clone, use hgsubversion.layout option (see below for detailed help) :: $ hg clone --layout single svn+http://python-nose.googlecode.com/svn nose-hg Pulling new revisions into an already-converted repository is the same as from any other Mercurial source. Within the first example above, the following three commands are all equivalent:: $ hg pull $ hg pull default $ hg pull http://python-nose.googlecode.com/svn Sometimes, past repository history is of little or no interest, and all one wants is to start from today and work forward. Using ``--startrev HEAD`` causes the initial clone to only convert the latest revision; later pulls will convert all subsequent revisions. Please note that this only works for single-directory clones:: $ hg clone --startrev HEAD http://python-nose.googlecode.com/svn/trunk nose-hg Finding and displaying Subversion revisions ------------------------------------------- For revealing the relationship between Mercurial changesets and Subversion revisions, hgsubversion provides three template keywords: :svnrev: Expanded to the original Subversion revision number. :svnpath: The path within the repository that the changeset represents. :svnuuid: The Universally Unique Identifier of the Subversion repository. An example:: $ hg log --template='{rev}:{node|short} {author|user}\nsvn: {svnrev}\n' The template keywords are available when using Mercurial 1.5 or later. For finding changesets from Subversion, hgsubversion extends revsets to provide two new selectors: :fromsvn: Select changesets that originate from Subversion. Takes no arguments. :svnrev: Select changesets that originate in a specific Subversion revision. Takes a revision argument. For example:: $ hg log -r 'fromsvn()' $ hg log -r 'svnrev(500)' Revsets are available when using Mercurial 1.6 or later and are accepted by several Mercurial commands for specifying revisions. See ``hg help revsets`` for details. Support for externals --------------------- Subversion externals conversion is implemented for standard layouts. Using .hgsvnexternals (default mode) ==================================== .hgsvnexternals has been implemented before Mercurial supported proper subrepositories. Externals as subrepositories should now be preferred as they offer almost all .hgsvnexternals features with the benefit of a better integration with Mercurial commands. ``svn:externals`` properties are serialized into a single ``.hgsvnexternals`` file having the following syntax:: [.] common1 http://path/to/external/svn/repo1 ...additional svn:externals properties lines... [dir2] common2 -r123 http://path/to/external/svn/repo2 ...additional svn:externals properties lines... A header line in brackets specifies the directory the property applies to, where '.' indicates the project root directory. The property content follows the header, with every content line being prefixed by a single space. Note that the property lines have a format identical to svn:externals properties as used in Subversion, and do not support the hgsubversion extended svn+http:// URL format. Issuing the command ``hg svn updateexternals`` with the ``.hgsvnexternals`` example above would fetch the latest revision of `repo1` into the subdirectory `./common1`, and revision 123 of `repo2` into `dir2/common2`. Note that ``.hgsvnexternals`` must be tracked by Mercurial before this will work. If ``.hgsvnexternals`` is created or changed, it will not be pushed to the related Subversion repository, but its contents **will** be used to update ``svn:externals`` properties on the related Subversion repository. Alternatively, one can use the ``hgsubversion.externals`` in hgrc to specify ``subrepos`` as the externals mode. In this mode, ``.hgsub`` and ``.hgsubstate`` files will be used instead of ``.hgsvnexternals``. This feature requires Mercurial 1.7.1 or later. Using Subrepositories ===================== Set: [hgsubversion] externals = subrepos to enable this mode. ``svn:externals`` properties are serialized into the subrepositories metadata files, ``.hgsub`` and ``.hgsubstate``. The following ``svn:externals`` entry: -r23 ^/externals/project1 deps/project1 set on the "subdir" directory becomes: (.hgsub) subdir/deps/project1 = [hgsubversion] subdir:-r{REV} ^/externals/project1 deps/project1 (.hgsubstate) 23 subdir/deps/project1 At this point everything works like a regular svn subrepository. The right part of the .hgsub entry reads like: TARGETDIR:REWRITTEN_EXTERNAL_DEFINITION where REWRITTEN_EXTERNAL_DEFINITION is like the original definition with the revision identifier replaced with {REV}. This mode has the following limitations: * Require Mercurial >= 1.7.1 to work correctly on all platforms. * "hgsubversion" subrepositories require hgsubversion extension to be available. To operate transparently on ``svn:externals`` we have to stay as close as possible to their original property format. Besides, relative externals require a parent subversion repository to be resolved while stock Mercurial only supports absolute subversion paths. * Leading or trailing whitespaces in the external definitions are lost * Leading or trailing whitespaces in the target directory are lost * The external definition should not contain {REV} * Unversioned definitions are pulled but the behaviour upon update/merge is not clearly defined. We tried to preserve the .hgsubstate as "HEAD" but the subrepository will probably not be updated when the hg repository is updated. Given subrepositories were designed not to support unversioned dependencies, this is unlikely to be fixed. * .hgsub and .hgsubstate are currently overwritten and non-[hgsubversion] subrepos entries are lost. This could be fixed by editing these files more carefully. Limitations ----------- Currently, pushing to Subversion destroys the original changesets and replaces them with new ones converted from the resulting commits. Due to the intricacies of Subversion semantics, these converted changesets may differ in subtle ways from the original Mercurial changesets. For example, the commit date almost always changes. This makes hgsubversion unsuitable for use as a two-way bridge. When converting from Subversion, hgsubversion does not recognize merge-info, and does not create merges based on it. Similarly, Mercurial merges cannot be pushed to Subversion. Changesets that create tags cannot be pushed to Subversion, as support for creating Subversion tags has not yet been implemented. Standard layout does not work with repositories that use unconventional layouts. Thus, only single directory clones can be made of such repositories. When interacting with Subversion, hgsubversion relies on information about the previously converted changesets. This information will not be updated if pushing or pulling converted changesets to or from any other source. To regenerate the stored metadata, run ``hg svn rebuildmeta [URI]``. This must also be done if any converted changesets are ever removed from the repository. Under certain circumstances a long-running conversion can leak substantial amounts of memory, on the order of 100MB per 1000 converted revisions. The leaks appear to be persistent and unavoidable using the SWIG bindings. When using the new experimental Subvertpy bindings, leaks have only been observed accessing FSFS repositories over the file protocol. Should the initial clone fail with an error, Mercurial will delete the entire repository, including any revisions successfully converted. This can be particularly undesirable for long-running clones. In these cases, we suggest using the ``-r/--rev`` option to only clone a few revisions initially. After that, an ``hg pull`` in the cloned repository will be perfectly safe. It is not possible to interact with more than one Subversion repository per Mercurial clone. Please note that this also applies to more than one path within the same Subversion repository. Mercurial does not track directories, and as a result, any empty directories in Subversion cannot be represented in the resulting Mercurial repository. Externals support requires that the ``svn`` command line utility is available. In addition, externals support has been disabled for single directory clones, due to known bugs. Advanced Configuration ---------------------- The operation of hgsubversion can be customized by the following configuration settings: ``hgsubversion.authormap`` Path to a file for mapping usernames from Subversion to Mercurial. For example:: joe = Joe User Some Subversion conversion tools create revisions without specifying an author. Such author names are mapped to ``(no author)``, similar to how ``svn log`` will display them. ``hgsubversion.defaulthost`` This option specifies the hostname to append to unmapped Subversion usernames. The default is to append the UUID of the Subversion repository as a hostname. That is, an author of ``bob`` may be mapped to ``bob@0b1d8996-7ded-4192-9199-38e2bec458fb``. If this option set to an empty string, the Subversion authors will be used with no hostname component. ``hgsubversion.defaultmessage`` This option selects what to substitute for an empty log message. The default is to substitute three dots, or ``...``. ``hgsubversion.defaultauthors`` Setting this boolean option to false will cause hgsubversion to abort a conversion if a revision has an author not listed in the author map. ``hgsubversion.branch`` Mark converted changesets as belonging to this branch or, if unspecified, ``default``. Please note that this option is not supported for standard layout clones. ``hgsubversion.branchmap`` Path to a file for changing branch names during the conversion from Subversion to Mercurial. ``hgsubversion.filemap`` Path to a file for filtering files during the conversion. Files may either be included or excluded. See the documentation for ``hg convert`` for more information on filemaps. ``hgsubversion.filestoresize`` Maximum amount of temporary edited files data to be kept in memory, in megabytes. The replay and stupid mode pull data by retrieving delta information from the subversion repository and applying it on known files data. Since the order of file edits is driven by the subversion delta information order, edited files cannot be committed immediately and are kept until all of them have been processed for each changeset. ``filestoresize`` defines the maximum amount of files data to be kept in memory before falling back to storing them in a temporary directory. This setting is important with repositories containing many files or large ones as both the application of deltas and Mercurial commit process require the whole file data to be available in memory. By limiting the amount of temporary data kept in memory, larger files can be retrieved, at the price of slower disk operations. Set it to a negative value to disable the fallback behaviour and keep everything in memory. Default to 200. ``hgsubversion.username``, ``hgsubversion.password`` 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 using very old versions of Subversion, and hgsubversion falls back to it when necessary. ``hgsubversion.externals`` Set to ``subrepos`` to switch to subrepos-based externals support (requires Mercurial 1.7.1 or later.) Default is ``svnexternals``, which uses a custom hgsubversion-specific format and works on older versions of Mercurial. Use ``ignore`` to avoid converting externals. The following options only have an effect on the initial clone of a repository: ``hgsubversion.layout`` Set the layout of the repository. ``standard`` assumes a normal trunk/branches/tags layout. ``single`` means that the entire repository is converted into a single branch. The default, ``auto``, causes hgsubversion to assume a standard layout if any of trunk, branches, or tags exist within the specified directory on the server. ``hgsubversion.startrev`` Convert Subversion revisions starting at the one specified, either an integer revision or ``HEAD``; ``HEAD`` causes only the latest revision to be pulled. The default is to pull everything. ``hgsubversion.tagpaths`` Specifies one or more paths in the Subversion repository that contain tags. The default is to only look in ``tags``. This option has no effect for single-directory clones. ``hgsubversion.unsafeskip`` A space or comma separated list of Subversion revision numbers to skip over when pulling or cloning. This can be useful for troublesome commits, such as someone accidentally deleting trunk and then restoring it. (In delete-and-restore cases, you may also need to clone or pull in multiple steps, to help hgsubversion track history correctly.) NOTE: this option is dangerous. Careless use can make it impossible to pull later Subversion revisions cleanly, e.g. if the content of a file depends on changes made in a skipped rev. Skipping a rev may also prevent future invocations of ``hg svn verify`` from succeeding (if the contents of the Mercurial repo become out of step with the contents of the Subversion repo). If you use this option, be sure to carefully check the result of a pull afterwards. Please note that some of these options may be specified as command line options as well, and when done so, will override the configuration. If an authormap, filemap or branchmap is specified, its contents will be read and stored for use in future pulls. Finally, the following environment variables can be used for testing a deployment of hgsubversion: ``HGSUBVERSION_BINDINGS`` By default, hgsubversion will use Subvertpy, but fall back to the SWIG bindings. Set this variable to either ``SWIG`` or ``Subvertpy`` (case- insensitive) to force that set of bindings. durin42-hgsubversion-77b22e5b4ea6/hgsubversion/hooks/__init__.py0000644000000000000000000000000012043462603022764 0ustar 00000000000000durin42-hgsubversion-77b22e5b4ea6/hgsubversion/hooks/updatemeta.py0000644000000000000000000000167412043462603023400 0ustar 00000000000000# Mercurial hook to update/rebuild svn metadata if there are svn changes in # the incoming changegroup. # # To install, add the following to your hgrc: # [hooks] # changegroup = python:hgsubversion.hooks.updatemeta.hook from mercurial import node import hgsubversion import hgsubversion.util import hgsubversion.svncommands def hook(ui, repo, **kwargs): updatemeta = False startrev = repo[node.bin(kwargs["node"])].rev() # Check each rev until we find one that contains svn metadata for rev in xrange(startrev, len(repo)): svnrev = hgsubversion.util.getsvnrev(repo[rev]) if svnrev and svnrev.startswith("svn:"): updatemeta = True break if updatemeta: try: hgsubversion.svncommands.updatemeta(ui, repo, args=[]) ui.status("Updated svn metadata\n") except Exception, e: ui.warn("Failed to update svn metadata: %s" % str(e)) return False durin42-hgsubversion-77b22e5b4ea6/hgsubversion/maps.py0000644000000000000000000003533612043462603021066 0ustar 00000000000000''' Module for self-contained maps. ''' import errno import os from mercurial import util as hgutil from mercurial import node import svncommands import util class AuthorMap(dict): '''A mapping from Subversion-style authors to Mercurial-style authors, and back. The data is stored persistently on disk. If the 'hgsubversion.defaultauthors' configuration option is set to false, attempting to obtain an unknown author will fail with an Abort. ''' def __init__(self, ui, path, defaulthost=None): '''Initialise a new AuthorMap. The ui argument is used to print diagnostic messages. The path argument is the location of the backing store, typically .hg/svn/authors. ''' self.ui = ui self.path = path if defaulthost: self.defaulthost = '@%s' % defaulthost.lstrip('@') else: self.defaulthost = '' self.super = super(AuthorMap, self) self.super.__init__() self.load(path) def load(self, path): ''' Load mappings from a file at the specified path. ''' path = os.path.expandvars(path) if not os.path.exists(path): return writing = False if path != self.path: writing = open(self.path, 'a') self.ui.note('reading authormap from %s\n' % path) f = open(path, 'r') for number, line_org in enumerate(f): line = line_org.split('#')[0] if not line.strip(): continue try: src, dst = line.split('=', 1) except (IndexError, ValueError): msg = 'ignoring line %i in author map %s: %s\n' self.ui.status(msg % (number, path, line.rstrip())) continue src = src.strip() dst = dst.strip() if writing: if not src in self: self.ui.debug('adding author %s to author map\n' % src) elif dst != self[src]: msg = 'overriding author: "%s" to "%s" (%s)\n' self.ui.status(msg % (self[src], dst, src)) writing.write(line_org) self[src] = dst f.close() if writing: writing.close() def __getitem__(self, author): ''' Similar to dict.__getitem__, except in case of an unknown author. In such cases, a new value is generated and added to the dictionary as well as the backing store. ''' if author is None: author = '(no author)' if author in self: result = self.super.__getitem__(author) elif self.ui.configbool('hgsubversion', 'defaultauthors', True): self[author] = result = '%s%s' % (author, self.defaulthost) msg = 'substituting author "%s" for default "%s"\n' self.ui.note(msg % (author, result)) else: msg = 'author %s has no entry in the author map!' raise hgutil.Abort(msg % author) self.ui.debug('mapping author "%s" to "%s"\n' % (author, result)) return result def reverselookup(self, author): for svnauthor, hgauthor in self.iteritems(): if author == hgauthor: return svnauthor else: # Mercurial incorrectly splits at e.g. '.', so we roll our own. return author.rsplit('@', 1)[0] class Tags(dict): """Map tags to converted node identifier. tag names are non-empty strings. Tags are saved in a file called tagmap, for backwards compatibility reasons. """ VERSION = 2 @classmethod def filepath(cls, repo): return os.path.join(repo.path, 'svn', 'tagmap') def __init__(self, repo, endrev=None): dict.__init__(self) self.path = self.filepath(repo) self.endrev = endrev if os.path.isfile(self.path): self._load(repo) else: self._write() def _load(self, repo): f = open(self.path) ver = int(f.readline()) if ver < self.VERSION: repo.ui.status('tag map outdated, running rebuildmeta...\n') f.close() os.unlink(self.path) svncommands.rebuildmeta(repo.ui, repo, ()) return elif ver != self.VERSION: raise hgutil.Abort('tagmap too new -- please upgrade') for l in f: ha, revision, tag = l.split(' ', 2) revision = int(revision) tag = tag[:-1] if self.endrev is not None and revision > self.endrev: break if not tag: continue dict.__setitem__(self, tag, node.bin(ha)) f.close() def _write(self): assert self.endrev is None f = open(self.path, 'w') f.write('%s\n' % self.VERSION) f.close() def update(self, other): for k, v in other.iteritems(): self[k] = v def __contains__(self, tag): return (tag and dict.__contains__(self, tag) and dict.__getitem__(self, tag) != node.nullid) def __getitem__(self, tag): if tag and tag in self: return dict.__getitem__(self, tag) raise KeyError() def __setitem__(self, tag, info): if not tag: raise hgutil.Abort('tag cannot be empty') ha, revision = info f = open(self.path, 'a') f.write('%s %s %s\n' % (node.hex(ha), revision, tag)) f.close() dict.__setitem__(self, tag, ha) class RevMap(dict): VERSION = 1 def __init__(self, repo): dict.__init__(self) self.path = self.mappath(repo) self.repo = repo self.ypath = os.path.join(repo.path, 'svn', 'lastpulled') # TODO(durin42): Consider moving management of the youngest # file to svnmeta itself rather than leaving it here. # must load youngest file first, or else self._load() can # clobber the info _yonngest_str = util.load_string(self.ypath, '0') self._youngest = int(_yonngest_str.strip()) self.oldest = 0 if os.path.isfile(self.path): self._load() else: self._write() def _set_youngest(self, rev): self._youngest = max(self._youngest, rev) util.save_string(self.ypath, str(self._youngest) + '\n') def _get_youngest(self): return self._youngest youngest = property(_get_youngest, _set_youngest) def hashes(self): return dict((v, k) for (k, v) in self.iteritems()) def branchedits(self, branch, rev): check = lambda x: x[0][1] == branch and x[0][0] < rev.revnum return sorted(filter(check, self.iteritems()), reverse=True) @staticmethod def mappath(repo): return os.path.join(repo.path, 'svn', 'rev_map') @classmethod def readmapfile(cls, repo, missingok=True): try: f = open(cls.mappath(repo)) except IOError, err: if not missingok or err.errno != errno.ENOENT: raise return iter([]) ver = int(f.readline()) if ver != cls.VERSION: raise hgutil.Abort('revmap too new -- please upgrade') return f def _load(self): for l in self.readmapfile(self.repo): revnum, ha, branch = l.split(' ', 2) if branch == '\n': branch = None else: branch = branch[:-1] revnum = int(revnum) if revnum > self.youngest or not self.youngest: self.youngest = revnum if revnum < self.oldest or not self.oldest: self.oldest = revnum dict.__setitem__(self, (revnum, branch), node.bin(ha)) def _write(self): f = open(self.path, 'w') f.write('%s\n' % self.VERSION) f.close() def __setitem__(self, key, ha): revnum, branch = key f = open(self.path, 'a') b = branch or '' f.write(str(revnum) + ' ' + node.hex(ha) + ' ' + b + '\n') f.close() if revnum > self.youngest or not self.youngest: self.youngest = revnum if revnum < self.oldest or not self.oldest: self.oldest = revnum dict.__setitem__(self, (revnum, branch), ha) class FileMap(object): 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 = {} if os.path.isfile(self.path): self._load() else: self._write() def _rpairs(self, 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 in m: return m[pre] return -1 def __contains__(self, 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 = 0 if len(self.exclude): exc = self.check('exclude', path) else: exc = -1 # respect rule order: newer rules override older return inc > exc # Needed so empty filemaps are false def __len__(self): return len(self.include) + len(self.exclude) def add(self, fn, m, path): mapping = getattr(self, m) if path in mapping: msg = 'duplicate %s entry in %s: "%s"\n' self.ui.status(msg % (m, fn, path)) return bits = m.rstrip('e'), path self.ui.debug('%sing %s\n' % bits) # 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 try: cmd, path = line.split(' ', 1) cmd = cmd.strip() path = path.strip() if cmd in ('include', 'exclude'): self.add(fn, cmd, path) continue self.ui.warn('unknown filemap command %s\n' % cmd) 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: raise hgutil.Abort('filemap too new -- please upgrade') 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): '''Facility for controlled renaming of branch names. Example: oldname = newname other = default All changes on the oldname branch will now be on the newname branch; all changes on other will now be on default (have no branch name set). ''' def __init__(self, ui, path): self.ui = ui self.path = path self.super = super(BranchMap, self) self.super.__init__() self.load(path) def load(self, path): '''Load mappings from a file at the specified path.''' if not os.path.exists(path): return writing = False if path != self.path: writing = open(self.path, 'a') self.ui.note('reading branchmap from %s\n' % path) f = open(path, 'r') for number, line in enumerate(f): if writing: writing.write(line) line = line.split('#')[0] if not line.strip(): continue try: src, dst = line.split('=', 1) except (IndexError, ValueError): msg = 'ignoring line %i in branch map %s: %s\n' self.ui.status(msg % (number, path, line.rstrip())) continue src = src.strip() dst = dst.strip() self.ui.debug('adding branch %s to branch map\n' % src) if not dst: # prevent people from assuming such lines are valid raise hgutil.Abort('removing branches is not supported, yet\n' '(line %i in branch map %s)' % (number, path)) elif src in self and dst != self[src]: msg = 'overriding branch: "%s" to "%s" (%s)\n' self.ui.status(msg % (self[src], dst, src)) self[src] = dst f.close() if writing: writing.close() class TagMap(dict): '''Facility for controlled renaming of tags. Example: oldname = newname other = The oldname tag from SVN will be represented as newname in the hg tags; the other tag will not be reflected in the hg repository. ''' def __init__(self, ui, path): self.ui = ui self.path = path self.super = super(TagMap, self) self.super.__init__() self.load(path) def load(self, path): '''Load mappings from a file at the specified path.''' if not os.path.exists(path): return writing = False if path != self.path: writing = open(self.path, 'a') self.ui.note('reading tag renames from %s\n' % path) f = open(path, 'r') for number, line in enumerate(f): if writing: writing.write(line) line = line.split('#')[0] if not line.strip(): continue try: src, dst = line.split('=', 1) except (IndexError, ValueError): msg = 'ignoring line %i in tag renames %s: %s\n' self.ui.status(msg % (number, path, line.rstrip())) continue src = src.strip() dst = dst.strip() self.ui.debug('adding tag %s to tag renames\n' % src) if src in self and dst != self[src]: msg = 'overriding tag rename: "%s" to "%s" (%s)\n' self.ui.status(msg % (self[src], dst, src)) self[src] = dst f.close() if writing: writing.close() durin42-hgsubversion-77b22e5b4ea6/hgsubversion/pushmod.py0000644000000000000000000001741712043462603021605 0ustar 00000000000000from mercurial import util as hgutil import svnwrap import svnexternals import util class NoFilesException(Exception): """Exception raised when you try and commit without files. """ def _isdir(svn, branchpath, svndir): try: path = '' if branchpath: path = branchpath + '/' svn.list_dir('%s%s' % (path, svndir)) return True except svnwrap.SubversionException: return False def _getdirchanges(svn, branchpath, parentctx, ctx, changedfiles, extchanges): """Compute directories to add or delete when moving from parentctx to ctx, assuming only 'changedfiles' files changed, and 'extchanges' external references changed (as returned by svnexternals.diff()). Return (added, deleted) where 'added' is the list of all added directories and 'deleted' the list of deleted directories. Intermediate directories are included: if a/b/c is new and requires the addition of a/b and a, those will be listed too. Intermediate deleted directories are also listed, but item order of undefined in either list. """ def finddirs(path, includeself=False): if includeself and path: yield path pos = path.rfind('/') while pos != -1: yield path[:pos] pos = path.rfind('/', 0, pos) # Include the root path, properties can be set explicitely on it # (like externals), and you want to preserve it if there are any # other child item still existing. yield '' def getctxdirs(ctx, keptdirs, extdirs): dirs = {} for f in ctx.manifest(): for d in finddirs(f): if d in dirs: break if d in keptdirs: dirs[d] = 1 for extdir in extdirs: for d in finddirs(extdir, True): dirs[d] = 1 return dirs deleted, added = [], [] changeddirs = {} for f in changedfiles: if f in parentctx and f in ctx: # Updated files cannot cause directories to be created # or removed. continue for d in finddirs(f): changeddirs[d] = 1 for e in extchanges: if not e[1] or not e[2]: for d in finddirs(e[0], True): changeddirs[d] = 1 if not changeddirs: return added, deleted olddirs = getctxdirs(parentctx, changeddirs, [e[0] for e in extchanges if e[1]]) newdirs = getctxdirs(ctx, changeddirs, [e[0] for e in extchanges if e[2]]) for d in newdirs: if d not in olddirs and not _isdir(svn, branchpath, d): added.append(d) for d in olddirs: if not d: # Do not remove the root directory when the hg repo becomes # empty. hgsubversion cannot create branches, do not remove # them. continue if d not in newdirs and _isdir(svn, branchpath, d): deleted.append(d) return added, deleted def commit(ui, repo, rev_ctx, meta, base_revision, svn): """Build and send a commit from Mercurial to Subversion. """ file_data = {} parent = rev_ctx.parents()[0] parent_branch = rev_ctx.parents()[0].branch() branch_path = 'trunk' if meta.layout == 'single': branch_path = '' elif parent_branch and parent_branch != 'default': branch_path = 'branches/%s' % parent_branch extchanges = svnexternals.diff(svnexternals.parse(ui, parent), svnexternals.parse(ui, rev_ctx)) addeddirs, deleteddirs = _getdirchanges(svn, branch_path, parent, rev_ctx, rev_ctx.files(), extchanges) deleteddirs = set(deleteddirs) props = {} copies = {} for file in rev_ctx.files(): if file in util.ignoredfiles: continue new_data = base_data = '' action = '' if file in rev_ctx: fctx = rev_ctx.filectx(file) new_data = fctx.data() if 'x' in fctx.flags(): props.setdefault(file, {})['svn:executable'] = '*' if 'l' in fctx.flags(): props.setdefault(file, {})['svn:special'] = '*' isbinary = hgutil.binary(new_data) if isbinary: props.setdefault(file, {})['svn:mime-type'] = 'application/octet-stream' if file not in parent: renamed = fctx.renamed() if renamed: # TODO current model (and perhaps svn model) does not support # this kind of renames: a -> b, b -> c copies[file] = renamed[0] base_data = parent[renamed[0]].data() else: autoprops = svn.autoprops_config.properties(file) if autoprops: props.setdefault(file, {}).update(autoprops) action = 'add' dirname = '/'.join(file.split('/')[:-1] + ['']) else: base_data = parent.filectx(file).data() if ('x' in parent.filectx(file).flags() and 'x' not in rev_ctx.filectx(file).flags()): props.setdefault(file, {})['svn:executable'] = None if ('l' in parent.filectx(file).flags() and 'l' not in rev_ctx.filectx(file).flags()): props.setdefault(file, {})['svn:special'] = None if hgutil.binary(base_data) and not isbinary: props.setdefault(file, {})['svn:mime-type'] = None action = 'modify' else: pos = file.rfind('/') if pos >= 0: if file[:pos] in deleteddirs: # This file will be removed when its directory is removed continue action = 'delete' file_data[file] = base_data, new_data, action def svnpath(p): return ('%s/%s' % (branch_path, p)).strip('/') changeddirs = [] for d, v1, v2 in extchanges: props.setdefault(svnpath(d), {})['svn:externals'] = v2 if d not in deleteddirs and d not in addeddirs: changeddirs.append(svnpath(d)) # Now we are done with files, we can prune deleted directories # against themselves: ignore a/b if a/ is already removed deleteddirs2 = list(deleteddirs) deleteddirs2.sort(reverse=True) for d in deleteddirs2: pos = d.rfind('/') if pos >= 0 and d[:pos] in deleteddirs: deleteddirs.remove(d) newcopies = {} for source, dest in copies.iteritems(): newcopies[svnpath(source)] = (svnpath(dest), base_revision) new_target_files = [svnpath(f) for f in file_data] for tf, ntf in zip(file_data, new_target_files): if tf in file_data and tf != ntf: file_data[ntf] = file_data[tf] if tf in props: props[ntf] = props.pop(tf) del file_data[tf] addeddirs = [svnpath(d) for d in addeddirs] deleteddirs = [svnpath(d) for d in deleteddirs] new_target_files += addeddirs + deleteddirs + changeddirs if not new_target_files: raise NoFilesException() try: svn.commit(new_target_files, rev_ctx.description(), file_data, base_revision, set(addeddirs), set(deleteddirs), props, newcopies) except svnwrap.SubversionException, e: if len(e.args) > 0 and e.args[1] in (svnwrap.ERR_FS_TXN_OUT_OF_DATE, svnwrap.ERR_FS_CONFLICT): raise hgutil.Abort('Outgoing changesets parent is not at ' 'subversion HEAD\n' '(pull again and rebase on a newer revision)') else: raise return True durin42-hgsubversion-77b22e5b4ea6/hgsubversion/replay.py0000644000000000000000000002007612043462603021415 0ustar 00000000000000import errno import traceback from mercurial import revlog from mercurial import node from mercurial import context from mercurial import util as hgutil import svnexternals import util class MissingPlainTextError(Exception): """Exception raised when the repo lacks a source file required for replaying a txdelta. """ class ReplayException(Exception): """Exception raised when you try and commit but the replay encountered an exception. """ def updateexternals(ui, meta, current): # TODO fix and re-enable externals for single-directory clones if not current.externals or meta.layout == 'single': return # accumulate externals records for all branches revnum = current.rev.revnum branches = {} for path, entry in current.externals.iteritems(): if not meta.is_path_valid(path): continue p, b, bp = meta.split_branch_path(path) if bp not in branches: parent = meta.get_parent_revision(revnum, b) pctx = meta.repo[parent] branches[bp] = (svnexternals.parse(ui, pctx), pctx) branches[bp][0][p] = entry # register externals file changes for bp, (external, pctx) in branches.iteritems(): if bp and bp[-1] != '/': bp += '/' updates = svnexternals.getchanges(ui, meta.repo, pctx, external) for fn, data in updates.iteritems(): path = (bp and bp + fn) or fn if data is not None: current.set(path, data, False, False) else: current.delete(path) def _safe_message(msg): if msg: try: msg.decode('utf-8') except UnicodeDecodeError: # ancient svn failed to enforce utf8 encoding return msg.decode('iso-8859-1').encode('utf-8') return msg def convert_rev(ui, meta, svn, r, tbdelta, firstrun): try: return _convert_rev(ui, meta, svn, r, tbdelta, firstrun) finally: meta.editor.current.close() def _convert_rev(ui, meta, svn, r, tbdelta, firstrun): editor = meta.editor editor.current.clear() editor.current.rev = r editor.setsvn(svn) if firstrun and meta.revmap.oldest <= 0: # We know nothing about this project, so fetch everything before # trying to apply deltas. ui.debug('replay: fetching full revision\n') svn.get_revision(r.revnum, editor) else: svn.get_replay(r.revnum, editor, meta.revmap.oldest) editor.close() current = editor.current updateexternals(ui, meta, current) if current.exception is not None: # pragma: no cover traceback.print_exception(*current.exception) raise ReplayException() files_to_commit = current.files() branch_batches = {} rev = current.rev date = meta.fixdate(rev.date) # build up the branches that have files on them failoninvalid = ui.configbool('hgsubversion', 'failoninvalidreplayfile', False) for f in files_to_commit: if not meta.is_path_valid(f): if failoninvalid: raise hgutil.Abort('file %s should not be in commit list' % f) continue p, b = meta.split_branch_path(f)[:2] if b not in branch_batches: branch_batches[b] = [] branch_batches[b].append((p, f)) closebranches = {} for branch in tbdelta['branches'][1]: branchedits = meta.revmap.branchedits(branch, rev) if len(branchedits) < 1: # can't close a branch that never existed continue ha = branchedits[0][1] closebranches[branch] = ha extraempty = (set(tbdelta['branches'][0]) - (set(current.emptybranches) | set(branch_batches.keys()))) current.emptybranches.update([(x, False) for x in extraempty]) # 1. handle normal commits closedrevs = closebranches.values() for branch, files in branch_batches.iteritems(): if branch in current.emptybranches and files: del current.emptybranches[branch] files = dict(files) parents = meta.get_parent_revision(rev.revnum, branch), revlog.nullid if parents[0] in closedrevs and branch in meta.closebranches: continue extra = meta.genextra(rev.revnum, branch) tag = None if branch is not None: # New regular tag without modifications, it will be committed by # svnmeta.committag(), we can skip the whole branch for now tag = meta.get_path_tag(meta.remotename(branch)) if (tag and tag not in meta.tags and branch not in meta.branches and branch not in meta.repo.branchtags() and not files): continue parentctx = meta.repo.changectx(parents[0]) if tag: if parentctx.node() == node.nullid: continue extra.update({'branch': parentctx.extra().get('branch', None), 'close': 1}) def filectxfn(repo, memctx, path): current_file = files[path] data, isexec, islink, copied = current.pop(current_file) if isexec is None or islink is None: flags = parentctx.flags(path) if isexec is None: isexec = 'x' in flags if islink is None: islink = 'l' in flags if data is not None: if islink: if data.startswith('link '): data = data[len('link '):] else: ui.debug('file marked as link, but may contain data: ' '%s\n' % current_file) else: data = parentctx.filectx(path).data() return context.memfilectx(path=path, data=data, islink=islink, isexec=isexec, copied=copied) message = _safe_message(rev.message) meta.mapbranch(extra) current_ctx = context.memctx(meta.repo, parents, message or util.default_commit_msg(ui), files.keys(), filectxfn, meta.authors[rev.author], date, extra) new_hash = meta.repo.svn_commitctx(current_ctx) util.describe_commit(ui, new_hash, branch) if (rev.revnum, branch) not in meta.revmap and not tag: meta.revmap[rev.revnum, branch] = new_hash if tag: meta.movetag(tag, new_hash, rev, date) meta.addedtags.pop(tag, None) # 2. handle branches that need to be committed without any files for branch in current.emptybranches: ha = meta.get_parent_revision(rev.revnum, branch) if ha == node.nullid: continue parent_ctx = meta.repo.changectx(ha) def del_all_files(*args): 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 raise hgutil.Abort('Empty commit to an open branch attempted. ' 'Please report this issue.') extra = meta.genextra(rev.revnum, branch) meta.mapbranch(extra) current_ctx = context.memctx(meta.repo, (ha, node.nullid), _safe_message(rev.message) or ' ', [], del_all_files, meta.authors[rev.author], date, extra) new_hash = meta.repo.svn_commitctx(current_ctx) util.describe_commit(ui, new_hash, branch) if (rev.revnum, branch) not in meta.revmap: meta.revmap[rev.revnum, branch] = new_hash return closebranches durin42-hgsubversion-77b22e5b4ea6/hgsubversion/stupid.py0000644000000000000000000007650712043462603021443 0ustar 00000000000000import cStringIO import errno import re from mercurial import context from mercurial import node from mercurial import patch from mercurial import revlog from mercurial import util as hgutil 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 index_header = r'''Index: ([^\n]*) =* ''' property_header = r'''Property changes on: ([^\n]*) _* ''' 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 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") def mempatchproxy(parentctx, files): # Avoid circular references patch.patchfile -> mempatch patchfile = patch.patchfile # TODO(durin42): make this a compat path for hg < 1.6. class mempatch(patchfile): def __init__(self, ui, fname, opener, missing=False, eolmode=None): patchfile.__init__(self, ui, fname, None, False, eolmode) def readlines(self, fname): if fname not in parentctx: raise IOError(errno.ENOENT, 'Cannot find %r to patch' % fname) fctx = parentctx[fname] data = fctx.data() if 'l' in fctx.flags(): data = 'link ' + data return cStringIO.StringIO(data).readlines() def writelines(self, fname, lines): files[fname] = ''.join(lines) def unlink(self, fname): files[fname] = None return mempatch def filteriterhunks(meta): iterhunks = patch.iterhunks def filterhunks(*args, **kwargs): # ui, fp, sourcefile=None, textmode=False applycurrent = False # Passing False instead of textmode because we should never # be ignoring EOL type. if iterhunks.func_code.co_argcount == 1: # Since 1.9 (28762bb767dc) fp = args[0] gen = iterhunks(fp) else: ui, fp = args[:2] if len(args) > 2: sourcefile = args[2] else: sourcefile = kwargs.get('sourcefile', None) if len(args) > 3: textmode = args[3] else: textmode = kwargs.get('textmode', False) if not iterhunks.func_defaults: # Since 1.7 (cfedc529e4a1) gen = iterhunks(ui, fp) elif len(iterhunks.func_defaults) == 1: gen = iterhunks(ui, fp, sourcefile) else: gen = iterhunks(ui, fp, sourcefile, textmode) for data in gen: if data[0] == 'file': if data[1][1] in meta.filemap: applycurrent = True else: applycurrent = False assert data[0] != 'git', 'Filtering git hunks not supported.' if applycurrent: yield data return filterhunks def patchrepoold(ui, meta, parentctx, patchfp): files = {} try: oldpatchfile = patch.patchfile olditerhunks = patch.iterhunks patch.patchfile = mempatchproxy(parentctx, files) patch.iterhunks = filteriterhunks(meta) try: # We can safely ignore the changed list since we are # handling non-git patches. Touched files are known # by our memory patcher. patch_st = patch.applydiff(ui, patchfp, {}, strip=0) finally: patch.patchfile = oldpatchfile patch.iterhunks = olditerhunks except patch.PatchError: # TODO: this happens if the svn server has the wrong mime # type stored and doesn't know a file is binary. It would # be better to do one file at a time and only do a # full fetch on files that had problems. raise BadPatchApply('patching failed') # if this patch didn't apply right, fall back to exporting the # entire rev. if patch_st == -1: assert False, ('This should only happen on case-insensitive' ' volumes.') elif patch_st == 1: # When converting Django, I saw fuzz on .po files that was # causing revisions to end up failing verification. If that # can be fixed, maybe this won't ever be reached. raise BadPatchApply('patching succeeded with fuzz') return files try: class svnbackend(patch.repobackend): def getfile(self, fname): data, (islink, isexec) = super(svnbackend, self).getfile(fname) if islink: data = 'link ' + data return data, (islink, isexec) except AttributeError: svnbackend = None def patchrepo(ui, meta, parentctx, patchfp): if not svnbackend: return patchrepoold(ui, meta, parentctx, patchfp) store = patch.filestore(util.getfilestoresize(ui)) try: touched = set() backend = svnbackend(ui, meta.repo, parentctx, store) try: ret = patch.patchbackend(ui, backend, patchfp, 0, touched) if ret < 0: raise BadPatchApply('patching failed') if ret > 0: raise BadPatchApply('patching succeeded with fuzz') except patch.PatchError, e: raise BadPatchApply(str(e)) files = {} for f in touched: try: data, mode, copied = store.getfile(f) files[f] = data except IOError: files[f] = None return files finally: store.close() def diff_branchrev(ui, svn, meta, branch, branchpath, r, parentctx): """Extract all 'branch' content at a given revision. Return a tuple (files, filectxfn) where 'files' is the list of all files in the branch at the given revision, and 'filectxfn' is a memctx compatible callable to retrieve individual file information. Raise BadPatchApply upon error. """ try: prev, pbranch, ppath = meta.get_source_rev(ctx=parentctx) except KeyError: prev, pbranch, ppath = None, None, None try: if prev is None or pbranch == branch: # letting patch handle binaries sounded # cool, but it breaks patch in sad ways d = svn.get_unified_diff(branchpath, r.revnum, other_rev=prev, deleted=False, ignore_type=False) else: d = svn.get_unified_diff(branchpath, r.revnum, other_path=ppath, other_rev=prev, deleted=True, ignore_type=True) if d: raise BadPatchApply('branch creation with mods') except svnwrap.SubversionRepoCanNotDiff: raise BadPatchApply('subversion diffing code is not supported') except svnwrap.SubversionException, e: if len(e.args) > 1 and e.args[1] != svnwrap.ERR_FS_NOT_FOUND: raise raise BadPatchApply('previous revision does not exist') if '\0' in d: raise BadPatchApply('binary diffs are not supported') files_data = {} 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 = 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) else: ui.status('Not using patch for %s, diff had no hunks.\n' % r.revnum) unknown_files = set() for p in r.paths: action = r.paths[p].action if not p.startswith(branchpath) or action not in 'DR': continue if branchpath: p2 = p[len(branchpath)+1:].strip('/') else: p2 = p if p2 in parentctx: toucheds = [p2] else: # If this isn't in the parent ctx, it must've been a dir toucheds = [f for f in parentctx if f.startswith(p2 + '/')] if action == 'R': # Files were replaced, we don't know if they still exist unknown_files.update(toucheds) else: files_data.update((f, None) for f in toucheds) 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) if path in binary_files or path in unknown_files: pa = path if branchpath: pa = branchpath + '/' + path data, mode = svn.get_file(pa, r.revnum) isexe = 'x' in mode islink = 'l' in mode else: isexe = exec_files.get(path, 'x' in parentctx.flags(path)) islink = link_files.get(path, 'l' in parentctx.flags(path)) data = '' if path in files_data: data = files_data[path] if islink: data = data[len('link '):] elif path in parentctx: data = parentctx[path].data() copied = copies.get(path) # TODO this branch feels like it should not be required, # and this may actually imply a bug in getcopies if copied not in parentctx.manifest(): copied = None return context.memfilectx(path=path, data=data, islink=islink, isexec=isexe, copied=copied) return list(touched_files), filectxfn def makecopyfinder(meta, r, branchpath): """Return a function detecting copies. Returned copyfinder(path) returns None if no copy information can be found or ((source, sourcerev), sourcepath) where "sourcepath" is the copy source path, "sourcerev" the source svn revision and "source" is the copy record path causing the copy to occur. If a single file was copied "sourcepath" and "source" are the same, while file copies dectected from directory copies return the copied source directory in "source". """ # cache changeset contexts and map them to source svn revisions ctxs = {} def getctx(branch, svnrev): if svnrev in ctxs: return ctxs[svnrev] changeid = meta.get_parent_revision(svnrev + 1, branch, True) ctx = None if changeid != revlog.nullid: ctx = meta.repo.changectx(changeid) ctxs[svnrev] = ctx return ctx # filter copy information for current branch branchpath = (branchpath and branchpath + '/') or '' copies = [] for path, e in r.paths.iteritems(): if not e.copyfrom_path: continue if not path.startswith(branchpath): continue # compute converted source path and revision frompath, frombranch = meta.split_branch_path(e.copyfrom_path)[:2] if frompath is None: continue fromctx = getctx(frombranch, e.copyfrom_rev) if fromctx is None: continue destpath = path[len(branchpath):] copies.append((destpath, (frompath, fromctx))) copies.sort(reverse=True) exactcopies = dict(copies) def finder(path): if path in exactcopies: return exactcopies[path], exactcopies[path][0] # look for parent directory copy, longest first for dest, (source, sourcectx) in copies: dest = dest + '/' if not path.startswith(dest): continue sourcepath = source + '/' + path[len(dest):] return (source, sourcectx), sourcepath return None return finder def getcopies(svn, meta, branch, branchpath, r, files, parentctx): """Return a mapping {dest: source} for every file copied into r. """ if parentctx.node() == revlog.nullid: return {} # Extract svn copy information, group them by copy source. # The idea is to duplicate the replay behaviour where copies are # evaluated per copy event (one event for all files in a directory copy, # one event for single file copy). We assume that copy events match # copy sources in revision info. svncopies = {} finder = makecopyfinder(meta, r, branchpath) for f in files: copy = finder(f) if copy: svncopies.setdefault(copy[0], []).append((f, copy[1])) if not svncopies: return {} # check svn copies really make sense in mercurial hgcopies = {} for (sourcepath, sourcectx), copies in svncopies.iteritems(): for k, v in copies: if not util.issamefile(sourcectx, parentctx, v): continue hgcopies.update({k: v}) return hgcopies def fetch_externals(ui, svn, branchpath, r, parentctx): """Extract svn:externals for the current revision and branch Return an externalsfile instance or None if there are no externals to convert and never were. """ externals = svnexternals.parse(ui, parentctx) # Detect property additions only, changes are handled by checking # existing entries individually. Projects are unlikely to store # externals on many different root directories, so we trade code # duplication and complexity for a constant lookup price at every # revision in the common case. dirs = set(externals) if parentctx.node() == revlog.nullid: dirs.update([p for p, k in svn.list_files(branchpath, r.revnum) if k == 'd']) dirs.add('') else: branchprefix = (branchpath and branchpath + '/') or branchpath for path, e in r.paths.iteritems(): if e.action == 'D': continue if not path.startswith(branchprefix) and path != branchpath: continue kind = svn.checkpath(path, r.revnum) if kind != 'd': continue path = path[len(branchprefix):] dirs.add(path) if e.action == 'M' or (e.action == 'A' and e.copyfrom_path): # Do not recurse in copied directories, changes are marked # as 'M', except for the copied one. continue for child, k in svn.list_files(branchprefix + path, r.revnum): if k == 'd': dirs.add((path + '/' + child).strip('/')) # Retrieve new or updated values for dir in dirs: try: dpath = (branchpath and branchpath + '/' + dir) or dir values = svn.list_props(dpath, r.revnum) externals[dir] = values.get('svn:externals', '') except IOError: externals[dir] = '' return externals def fetch_branchrev(svn, meta, branch, branchpath, r, parentctx): """Extract all 'branch' content at a given revision. Return a tuple (files, filectxfn) where 'files' is the list of all files in the branch at the given revision, and 'filectxfn' is a memctx compatible callable to retrieve individual file information. """ files = [] if parentctx.node() == revlog.nullid: # Initial revision, fetch all files for path, kind in svn.list_files(branchpath, r.revnum): if kind == 'f': files.append(path) else: branchprefix = (branchpath and branchpath + '/') or '' for path, e in r.paths.iteritems(): if path == branchpath: if e.action != 'R' or branch not in meta.branches: # Full-branch replacements are handled as reverts, # skip everything else. continue elif not path.startswith(branchprefix): continue if not meta.is_path_valid(path): continue kind = svn.checkpath(path, r.revnum) path = path[len(branchprefix):] if kind == 'f': files.append(path) elif kind == 'd': if e.action == 'M': continue dirpath = branchprefix + path for child, k in svn.list_files(dirpath, r.revnum): if k == 'f': if path: childpath = '%s/%s' % (path, child) else: childpath = child files.append(childpath) if e.action == 'R': # Check all files in replaced directory path = path + '/' files += [f for f in parentctx if f.startswith(path)] else: if path in parentctx: files.append(path) continue # Assume it's a deleted directory path = path + '/' deleted = [f for f in parentctx if f.startswith(path)] files += deleted copies = getcopies(svn, meta, branch, branchpath, r, files, parentctx) def filectxfn(repo, memctx, path): svnpath = path if branchpath: svnpath = branchpath + '/' + path data, mode = svn.get_file(svnpath, r.revnum) isexec = 'x' in mode islink = 'l' in mode copied = copies.get(path) # TODO this branch feels like it should not be required, # and this may actually imply a bug in getcopies if copied not in parentctx.manifest(): copied = None return context.memfilectx(path=path, data=data, islink=islink, isexec=isexec, copied=copied) return files, filectxfn def checkbranch(meta, r, branch): branchedits = meta.revmap.branchedits(branch, r) if not branchedits: return None branchtip = branchedits[0][1] for child in meta.repo[branchtip].children(): b = child.branch() != 'default' and child.branch() or None if b == branch and child.extra().get('close'): return None return branchtip def branches_in_paths(meta, tbdelta, paths, revnum, checkpath, listdir): '''Given a list of paths, return mapping of all branches touched to their branch path. ''' branches = {} paths_need_discovery = [] for p in paths: relpath, branch, branchpath = meta.split_branch_path(p) if relpath is not None: branches[branch] = branchpath elif paths[p].action == 'D' and not meta.get_path_tag(p): ln = meta.localname(p) # must check in branches_to_delete as well, because this runs after we # already updated the branch map if ln in meta.branches or ln in tbdelta['branches'][1]: branches[ln] = p else: paths_need_discovery.append(p) if not paths_need_discovery: return branches paths_need_discovery = [(len(p), p) for p in paths_need_discovery] paths_need_discovery.sort() paths_need_discovery = [p[1] for p in paths_need_discovery] actually_files = [] while paths_need_discovery: p = paths_need_discovery.pop(0) path_could_be_file = True ind = 0 while ind < len(paths_need_discovery) and not paths_need_discovery: if op.startswith(p): path_could_be_file = False ind += 1 if path_could_be_file: if checkpath(p, revnum) == 'f': actually_files.append(p) # if there's a copyfrom_path and there were files inside that copyfrom, # we need to detect those branches. It's a little thorny and slow, but # seems to be the best option. elif paths[p].copyfrom_path and not p.startswith('tags/'): paths_need_discovery.extend(['%s/%s' % (p, x[0]) for x in listdir(p, revnum) if x[1] == 'f']) if not actually_files: continue filepaths = [p.split('/') for p in actually_files] filepaths = [(len(p), p) for p in filepaths] filepaths.sort() filepaths = [p[1] for p in filepaths] while filepaths: path = filepaths.pop(0) parentdir = '/'.join(path[:-1]) filepaths = [p for p in filepaths if not '/'.join(p).startswith(parentdir)] branchpath = meta.normalize(parentdir) if branchpath.startswith('tags/'): continue branchname = meta.localname(branchpath) if branchpath.startswith('trunk/'): branches[meta.localname('trunk')] = 'trunk' continue if branchname and branchname.startswith('../'): continue branches[branchname] = branchpath return branches def convert_rev(ui, meta, svn, r, tbdelta, firstrun): # this server fails at replay if meta.filemap: raise hgutil.Abort('filemaps currently unsupported with stupid replay.') branches = branches_in_paths(meta, tbdelta, r.paths, r.revnum, svn.checkpath, svn.list_files) brpaths = branches.values() bad_branch_paths = {} for br, bp in branches.iteritems(): bad_branch_paths[br] = [] # This next block might be needed, but for now I'm omitting it until it # can be proven necessary. # for bad in brpaths: # if bad.startswith(bp) and len(bad) > len(bp): # bad_branch_paths[br].append(bad[len(bp)+1:]) # We've go a branch that contains other branches. We have to be careful # to get results similar to real replay in this case. for existingbr in meta.branches: bad = meta.remotename(existingbr) if bad.startswith(bp) and len(bad) > len(bp): bad_branch_paths[br].append(bad[len(bp)+1:]) deleted_branches = {} for p in r.paths: tag = meta.get_path_tag(p) if tag and tag not in meta.tags: continue branch = meta.localname(p) if not (r.paths[p].action == 'R' and branch in meta.branches): continue # Check the branch is not being replaced by one of its # ancestors, it happens a lot with project-wide reverts. frompath = r.paths[p].copyfrom_path frompath, frombranch = meta.split_branch_path( frompath, existing=False)[:2] if frompath == '': fromnode = meta.get_parent_revision( r.paths[p].copyfrom_rev + 1, frombranch, exact=True) if fromnode != node.nullid: fromctx = meta.repo[fromnode] pctx = meta.repo[meta.get_parent_revision( r.revnum, branch, exact=True)] if util.isancestor(pctx, fromctx): continue closed = checkbranch(meta, r, branch) if closed is not None: deleted_branches[branch] = closed date = meta.fixdate(r.date) check_deleted_branches = set(tbdelta['branches'][1]) for b in branches: parentctx = meta.repo[meta.get_parent_revision(r.revnum, b)] tag = meta.get_path_tag(meta.remotename(b)) kind = svn.checkpath(branches[b], r.revnum) if kind != 'd': if not tag: # Branch does not exist at this revision. Get parent # revision and remove everything. deleted_branches[b] = parentctx.node() continue # 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: files_touched, filectxfn2 = diff_branchrev( ui, svn, meta, b, branches[b], r, parentctx) except BadPatchApply, e: # Either this revision or the previous one does not exist. ui.note("Fetching entire revision: %s.\n" % e.args[0]) incremental = False if not incremental: files_touched, filectxfn2 = fetch_branchrev( svn, meta, b, branches[b], r, parentctx) externals = {} if meta.layout != 'single': externals = fetch_externals(ui, svn, branches[b], r, parentctx) externals = svnexternals.getchanges(ui, meta.repo, parentctx, externals) files_touched.extend(externals) def filectxfn(repo, memctx, path): if path in externals: if externals[path] is None: raise IOError(errno.ENOENT, 'no externals') return context.memfilectx(path=path, data=externals[path], islink=False, isexec=False, copied=None) for bad in bad_branch_paths[b]: if path.startswith(bad): raise IOError(errno.ENOENT, 'Path %s is bad' % path) return filectxfn2(repo, memctx, path) if '' in files_touched: files_touched.remove('') excluded = [f for f in files_touched if f not in meta.filemap] for f in excluded: files_touched.remove(f) if b: # Regular tag without modifications, it will be committed by # svnmeta.committag(), we can skip the whole branch for now if (tag and tag not in meta.tags and b not in meta.branches and b not in meta.repo.branchtags() and not files_touched): continue if parentctx.node() == node.nullid and not files_touched: meta.repo.ui.debug('skipping commit since parent is null and no files touched.\n') continue for f in files_touched: if f: # this is a case that really shouldn't ever happen, it means # something is very wrong assert f[0] != '/' extra = meta.genextra(r.revnum, b) if tag: if parentctx.node() == node.nullid: continue extra.update({'branch': parentctx.extra().get('branch', None), 'close': 1}) origbranch = extra.get('branch', None) meta.mapbranch(extra) current_ctx = context.memctx(meta.repo, [parentctx.node(), revlog.nullid], r.message or util.default_commit_msg(ui), files_touched, filectxfn, meta.authors[r.author], date, extra) ha = meta.repo.svn_commitctx(current_ctx) if not tag: if (not origbranch in meta.branches and not meta.get_path_tag(meta.remotename(origbranch))): meta.branches[origbranch] = None, 0, r.revnum meta.revmap[r.revnum, b] = ha else: meta.movetag(tag, ha, r, date) meta.addedtags.pop(tag, None) util.describe_commit(ui, ha, b) # These are branches with an 'R' status in svn log. This means they were # replaced by some other branch, so we need to verify they get marked as closed. for branch in check_deleted_branches: closed = checkbranch(meta, r, branch) if closed is not None: deleted_branches[branch] = closed return deleted_branches durin42-hgsubversion-77b22e5b4ea6/hgsubversion/svncommands.py0000644000000000000000000004262012043462603022450 0ustar 00000000000000import os import posixpath import cPickle as pickle import sys import traceback import urlparse from mercurial import commands from mercurial import hg from mercurial import node from mercurial import util as hgutil from mercurial import error import maps import svnwrap import svnrepo import util import svnexternals import verify def updatemeta(ui, repo, args, **opts): """Do a partial rebuild of the subversion metadata. Assumes that the metadata that currently exists is valid, but that some is missing, e.g. because you have pulled some revisions via a native mercurial method. """ return _buildmeta(ui, repo, args, partial=True) def rebuildmeta(ui, repo, args, unsafe_skip_uuid_check=False, **opts): """rebuild hgsubversion metadata using values stored in revisions """ return _buildmeta(ui, repo, args, partial=False, skipuuid=unsafe_skip_uuid_check) def _buildmeta(ui, repo, args, partial=False, skipuuid=False): if repo is None: raise error.RepoError("There is no Mercurial repository" " here (.hg not found)") dest = None if len(args) == 1: dest = args[0] elif len(args) > 1: raise hgutil.Abort('rebuildmeta takes 1 or no arguments') uuid = None url = repo.ui.expandpath(dest or repo.ui.config('paths', 'default-push') or repo.ui.config('paths', 'default') or '') svn = svnrepo.svnremoterepo(ui, url).svn subdir = svn.subdir svnmetadir = os.path.join(repo.path, 'svn') if not os.path.exists(svnmetadir): os.makedirs(svnmetadir) youngest = 0 startrev = 0 sofar = [] branchinfo = {} if partial: try: youngestpath = os.path.join(svnmetadir, 'lastpulled') foundpartialinfo = False if os.path.exists(youngestpath): youngest = int(util.load_string(youngestpath).strip()) sofar = list(maps.RevMap.readmapfile(repo)) if sofar and len(sofar[-1].split(' ', 2)) > 1: lasthash = sofar[-1].split(' ', 2)[1] startrev = repo[lasthash].rev() + 1 branchinfo = pickle.load(open(os.path.join(svnmetadir, 'branch_info'))) foundpartialinfo = True if not foundpartialinfo: ui.status('missing some metadata -- doing a full rebuild\n') partial = False except IOError, err: if err.errno != errno.ENOENT: raise ui.status('missing some metadata -- doing a full rebuild') except AttributeError: ui.status('no metadata available -- doing a full rebuild') lastpulled = open(os.path.join(svnmetadir, 'lastpulled'), 'wb') revmap = open(os.path.join(svnmetadir, 'rev_map'), 'w') revmap.write('1\n') revmap.writelines(sofar) last_rev = -1 tagfile = os.path.join(svnmetadir, 'tagmap') if not partial and os.path.exists(maps.Tags.filepath(repo)) : os.unlink(maps.Tags.filepath(repo)) tags = maps.Tags(repo) layout = None skipped = set() closed = set() numrevs = len(repo) - startrev subdirfile = open(os.path.join(svnmetadir, 'subdir'), 'w') subdirfile.write(subdir.strip('/')) subdirfile.close() # ctx.children() visits all revisions in the repository after ctx. Calling # it would make us use O(revisions^2) time, so we perform an extra traversal # of the repository instead. During this traversal, we find all converted # changesets that close a branch, and store their first parent for rev in xrange(startrev, len(repo)): util.progress(ui, 'prepare', rev - startrev, total=numrevs) ctx = repo[rev] convinfo = util.getsvnrev(ctx, None) if not convinfo: continue svnrevnum = int(convinfo.rsplit('@', 1)[1]) youngest = max(youngest, svnrevnum) if ctx.extra().get('close', None) is None: continue droprev = lambda x: x.rsplit('@', 1)[0] parentctx = ctx.parents()[0] parentinfo = util.getsvnrev(parentctx, '@') if droprev(parentinfo) == droprev(convinfo): if parentctx.rev() < startrev: parentbranch = parentctx.branch() if parentbranch == 'default': parentbranch = None branchinfo.pop(parentbranch) else: closed.add(parentctx.rev()) lastpulled.write(str(youngest) + '\n') util.progress(ui, 'prepare', None, total=numrevs) for rev in xrange(startrev, len(repo)): util.progress(ui, 'rebuild', rev-startrev, total=numrevs) ctx = repo[rev] convinfo = util.getsvnrev(ctx, None) if not convinfo: continue if '.hgtags' in ctx.files(): parent = ctx.parents()[0] parentdata = '' if '.hgtags' in parent: parentdata = parent.filectx('.hgtags').data() newdata = ctx.filectx('.hgtags').data() for newtag in newdata[len(parentdata):-1].split('\n'): ha, tag = newtag.split(' ', 1) tagged = util.getsvnrev(repo[ha], None) if tagged is None: tagged = -1 else: tagged = int(tagged[40:].split('@')[1]) # This is max(tagged rev, tagging rev) because if it is a normal # tag, the tagging revision has the right rev number. However, if it # was an edited tag, then the tagged revision has the correct revision # number. tagging = int(convinfo[40:].split('@')[1]) tagrev = max(tagged, tagging) tags[tag] = node.bin(ha), tagrev # check that the conversion metadata matches expectations assert convinfo.startswith('svn:') revpath, revision = convinfo[40:].split('@') if subdir and subdir[0] != '/': subdir = '/' + subdir if subdir and subdir[-1] == '/': subdir = subdir[:-1] assert revpath.startswith(subdir), ('That does not look like the ' 'right location in the repo.') if layout is None: if (subdir or '/') == revpath: layout = 'single' else: layout = 'standard' f = open(os.path.join(svnmetadir, 'layout'), 'w') f.write(layout) f.close() elif layout == 'single': assert (subdir or '/') == revpath, ('Possible layout detection' ' defect in replay') # write repository uuid if required if uuid is None: uuid = convinfo[4:40] if not skipuuid: if uuid != svn.uuid: raise hgutil.Abort('remote svn repository identifier ' 'does not match') uuidfile = open(os.path.join(svnmetadir, 'uuid'), 'w') uuidfile.write(svn.uuid) uuidfile.close() # don't reflect closed branches if (ctx.extra().get('close') and not ctx.files() or ctx.parents()[0].node() in skipped): skipped.add(ctx.node()) continue # find commitpath, write to revmap commitpath = revpath[len(subdir)+1:] if layout == 'standard': if commitpath.startswith('branches/'): commitpath = commitpath[len('branches/'):] elif commitpath == 'trunk': commitpath = '' else: if commitpath.startswith('tags/') and ctx.extra().get('close'): continue commitpath = '../' + commitpath else: commitpath = '' revmap.write('%s %s %s\n' % (revision, ctx.hex(), commitpath)) revision = int(revision) if revision > last_rev: last_rev = revision # deal with branches if not commitpath: branch = None elif not commitpath.startswith('../'): branch = commitpath elif ctx.parents()[0].node() != node.nullid: parent = ctx while parent.node() != node.nullid: parentextra = parent.extra() parentinfo = util.getsvnrev(parent) assert parentinfo parent = parent.parents()[0] parentpath = parentinfo[40:].split('@')[0][len(subdir) + 1:] if parentpath.startswith('tags/') and parentextra.get('close'): continue elif parentpath.startswith('branches/'): branch = parentpath[len('branches/'):] elif parentpath == 'trunk': branch = None else: branch = '../' + parentpath break else: branch = commitpath if rev in closed: # a direct child of this changeset closes the branch; drop it branchinfo.pop(branch, None) elif ctx.extra().get('close'): pass elif branch not in branchinfo: parent = ctx.parents()[0] if (parent.node() not in skipped and util.getsvnrev(parent, '').startswith('svn:') and parent.branch() != ctx.branch()): parentbranch = parent.branch() if parentbranch == 'default': parentbranch = None else: parentbranch = None # branchinfo is a map from mercurial branch to a # (svn branch, svn parent revision, svn revision) tuple parentrev = util.getsvnrev(parent, '@').split('@')[1] or 0 branchinfo[branch] = (parentbranch, int(parentrev), revision) util.progress(ui, 'rebuild', None, total=numrevs) # save off branch info branchinfofile = open(os.path.join(svnmetadir, 'branch_info'), 'w') pickle.dump(branchinfo, branchinfofile) branchinfofile.close() def help_(ui, args=None, **opts): """show help for a given subcommands or a help overview """ if args: subcommand = args[0] if subcommand not in table: candidates = [] for c in table: if c.startswith(subcommand): candidates.append(c) if len(candidates) == 1: subcommand = candidates[0] elif len(candidates) > 1: raise error.AmbiguousCommand(subcommand, candidates) return doc = table[subcommand].__doc__ if doc is None: doc = "No documentation available for %s." % subcommand ui.status(doc.strip(), '\n') return commands.help_(ui, 'svn') def update(ui, args, repo, clean=False, **opts): """update to a specified Subversion revision number """ try: rev = int(args[0]) except IndexError: raise error.CommandError('svn', "no revision number specified for 'update'") except ValueError: raise error.Abort("'%s' is not a valid Subversion revision number" % args[0]) meta = repo.svnmeta() answers = [] for k, v in meta.revmap.iteritems(): if k[0] == rev: answers.append((v, k[1])) if len(answers) == 1: if clean: return hg.clean(repo, answers[0][0]) return hg.update(repo, answers[0][0]) elif len(answers) == 0: ui.status('revision %s did not produce an hg revision\n' % rev) return 1 else: ui.status('ambiguous revision!\n') revs = ['%s on %s' % (node.hex(a[0]), a[1]) for a in answers] + [''] ui.status('\n'.join(revs)) return 1 def genignore(ui, repo, force=False, **opts): """generate .hgignore from svn:ignore properties. """ if repo is None: raise error.RepoError("There is no Mercurial repository" " here (.hg not found)") ignpath = repo.wjoin('.hgignore') if not force and os.path.exists(ignpath): raise hgutil.Abort('not overwriting existing .hgignore, try --force?') svn = svnrepo.svnremoterepo(repo.ui).svn meta = repo.svnmeta() hashes = meta.revmap.hashes() parent = util.parentrev(ui, repo, meta, hashes) r, br = hashes[parent.node()] if meta.layout == 'single': branchpath = '' else: branchpath = br and ('branches/%s/' % br) or 'trunk/' ignorelines = ['.hgignore', 'syntax:glob'] dirs = [''] + [d[0] for d in svn.list_files(branchpath, r) if d[1] == 'd'] for dir in dirs: path = '%s%s' % (branchpath, dir) props = svn.list_props(path, r) if 'svn:ignore' not in props: continue lines = props['svn:ignore'].strip().split('\n') ignorelines += [dir and (dir + '/' + prop) or prop for prop in lines] repo.wopener('.hgignore', 'w').write('\n'.join(ignorelines) + '\n') def info(ui, repo, **opts): """show Subversion details similar to `svn info' """ if repo is None: raise error.RepoError("There is no Mercurial repository" " here (.hg not found)") meta = repo.svnmeta() hashes = meta.revmap.hashes() if opts.get('rev'): parent = repo[opts['rev']] else: parent = util.parentrev(ui, repo, meta, hashes) pn = parent.node() if pn not in hashes: ui.status('Not a child of an svn revision.\n') return 0 r, br = hashes[pn] subdir = util.getsvnrev(parent)[40:].split('@')[0] if meta.layout == 'single': branchpath = '' elif br == None: branchpath = '/trunk' elif br.startswith('../'): branchpath = '/%s' % br[3:] subdir = subdir.replace('branches/../', '') else: branchpath = '/branches/%s' % br remoterepo = svnrepo.svnremoterepo(repo.ui) url = '%s%s' % (remoterepo.svnurl, branchpath) author = meta.authors.reverselookup(parent.user()) # cleverly figure out repo root w/o actually contacting the server reporoot = url[:len(url)-len(subdir)] ui.write('''URL: %(url)s Repository Root: %(reporoot)s Repository UUID: %(uuid)s Revision: %(revision)s Node Kind: directory Last Changed Author: %(author)s Last Changed Rev: %(revision)s Last Changed Date: %(date)s\n''' % {'reporoot': reporoot, 'uuid': meta.uuid, 'url': url, 'author': author, 'revision': r, # TODO I'd like to format this to the user's local TZ if possible 'date': hgutil.datestr(parent.date(), '%Y-%m-%d %H:%M:%S %1%2 (%a, %d %b %Y)') }) def listauthors(ui, args, authors=None, **opts): """list all authors in a Subversion repository """ if not len(args): ui.status('No repository specified.\n') return svn = svnrepo.svnremoterepo(ui, args[0]).svn author_set = set() for rev in svn.revisions(): if rev.author is None: author_set.add('(no author)') else: author_set.add(rev.author) if authors: authorfile = open(authors, 'w') authorfile.write('%s=\n' % '=\n'.join(sorted(author_set))) authorfile.close() else: ui.write('%s\n' % '\n'.join(sorted(author_set))) def _helpgen(): ret = ['subcommands for Subversion integration', '', 'list of subcommands:', ''] for name, func in sorted(table.items()): if func.__doc__: short_description = func.__doc__.splitlines()[0] else: short_description = '' ret.append(" :%s: %s" % (name, short_description)) return '\n'.join(ret) + '\n' def svn(ui, repo, subcommand, *args, **opts): '''see detailed help for list of subcommands''' # guess command if prefix if subcommand not in table: candidates = [] for c in table: if c.startswith(subcommand): candidates.append(c) if len(candidates) == 1: subcommand = candidates[0] elif not candidates: raise error.CommandError('svn', "unknown subcommand '%s'" % subcommand) else: raise error.AmbiguousCommand(subcommand, candidates) # override subversion credentials for key in ('username', 'password'): if key in opts: ui.setconfig('hgsubversion', key, opts[key]) try: commandfunc = table[subcommand] return commandfunc(ui, args=args, repo=repo, **opts) except svnwrap.SubversionConnectionException, e: raise hgutil.Abort(*e.args) except TypeError: tb = traceback.extract_tb(sys.exc_info()[2]) if len(tb) == 1: ui.status('Bad arguments for subcommand %s\n' % subcommand) else: raise except KeyError, e: tb = traceback.extract_tb(sys.exc_info()[2]) if len(tb) == 1: ui.status('Unknown subcommand %s\n' % subcommand) else: raise table = { 'genignore': genignore, 'info': info, 'listauthors': listauthors, 'update': update, 'help': help_, 'updatemeta': updatemeta, 'rebuildmeta': rebuildmeta, 'updateexternals': svnexternals.updateexternals, 'verify': verify.verify, } svn.__doc__ = _helpgen() durin42-hgsubversion-77b22e5b4ea6/hgsubversion/svnexternals.py0000644000000000000000000003716512043462603022664 0ustar 00000000000000import cStringIO import os, re, shutil, stat, subprocess from mercurial import util as hgutil from mercurial.i18n import _ try: from mercurial import subrepo # require svnsubrepo and hg >= 1.7.1 subrepo.svnsubrepo hgutil.checknlink except (ImportError, AttributeError), e: subrepo = None passpegrev = True # see svnsubrepo below try: canonpath = hgutil.canonpath except (ImportError, AttributeError): from mercurial import scmutil canonpath = scmutil.canonpath passpegrev = False import util class externalsfile(dict): """Map svn directories to lists of externals entries. """ def __init__(self): super(externalsfile, self).__init__() self.encoding = 'utf-8' def __setitem__(self, key, value): if value is None: value = [] elif isinstance(value, basestring): value = value.splitlines() if key == '.': key = '' if not value: if key in self: del self[key] else: super(externalsfile, self).__setitem__(key, value) def write(self): fp = cStringIO.StringIO() for target in sorted(self): lines = self[target] if not lines: continue if not target: target = '.' fp.write('[%s]\n' % target) for l in lines: l = ' ' + l + '\n' fp.write(l) return fp.getvalue() def read(self, data): self.clear() fp = cStringIO.StringIO(data) target = None for line in fp.readlines(): if not line.strip(): continue if line.startswith('['): line = line.strip() if line[-1] != ']': raise hgutil.Abort('invalid externals section name: %s' % line) target = line[1:-1] if target == '.': target = '' elif line.startswith(' '): line = line.rstrip('\n') if target is None or not line: continue self.setdefault(target, []).append(line[1:]) def diff(ext1, ext2): """Compare 2 externalsfile and return a list of tuples like (dir, value1, value2) where value1 is the external value in ext1 for dir or None, and value2 the same in ext2. """ changes = [] for d in ext1: if d not in ext2: changes.append((d, '\n'.join(ext1[d]), None)) elif ext1[d] != ext2[d]: changes.append((d, '\n'.join(ext1[d]), '\n'.join(ext2[d]))) for d in ext2: if d not in ext1: changes.append((d, None, '\n'.join(ext2[d]))) return changes 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+]+://') def parsedefinition(line): """Parse an external definition line, return a tuple (path, rev, source) or raise BadDefinition. """ # The parsing is probably not correct wrt path with whitespaces or # potential quotes. svn documentation is not really talkative about # these either. pegrev, revgroup = None, 1 m = re_defnew.search(line) if m: rev, source, path = m.group(1, 2, 3) if '@' in source: source, pegrev = source.rsplit('@', 1) else: m = re_defold.search(line) if not m: raise BadDefinition() revgroup = 2 path, rev, source = m.group(1, 2, 3) try: int(rev) # ensure revision is int()able, so we bail otherwise norevline = line[:m.start(revgroup)] + '{REV}' + line[m.end(revgroup):] except (TypeError, ValueError): norevline = line return (path, rev, source, pegrev, norevline) class RelativeSourceError(Exception): pass def resolvesource(ui, svnroot, source): if re_scheme.search(source): return source if source.startswith('^/'): if svnroot is None: raise RelativeSourceError() return svnroot + source[1:] ui.warn(_('ignoring unsupported non-fully qualified external: %r\n' % source)) return None def parsedefinitions(ui, repo, svnroot, exts): """Return (targetdir, revision, source) tuples. Fail if nested targetdirs are detected. source is an svn project URL. """ defs = [] for base in sorted(exts): for line in exts[base]: if not line.strip() or line.lstrip().startswith('#'): # Ignore comments and blank lines continue try: path, rev, source, pegrev, norevline = parsedefinition(line) except BadDefinition: ui.warn(_('ignoring invalid external definition: %r\n' % line)) continue source = resolvesource(ui, svnroot, source) if source is None: continue wpath = hgutil.pconvert(os.path.join(base, path)) wpath = canonpath(repo.root, '', wpath) defs.append((wpath, rev, source, pegrev, norevline, base)) # Check target dirs are not nested defs.sort() for i, d in enumerate(defs): for d2 in defs[i+1:]: if d2[0].startswith(d[0] + '/'): raise hgutil.Abort(_('external directories cannot nest:\n%s\n%s') % (d[0], d2[0])) return defs def computeactions(ui, repo, svnroot, ext1, ext2): def listdefs(data): defs = {} exts = externalsfile() exts.read(data) for d in parsedefinitions(ui, repo, svnroot, exts): defs[d[0]] = d return defs ext1 = listdefs(ext1) ext2 = listdefs(ext2) for wp1 in ext1: if wp1 in ext2: yield 'u', ext2[wp1] else: yield 'd', ext1[wp1] for wp2 in ext2: if wp2 not in ext1: yield 'u', ext2[wp2] def getsvninfo(svnurl): """Return a tuple (url, root) for supplied svn URL or working directory path. """ # Yes, this is ugly, but good enough for now args = ['svn', 'info', '--xml', svnurl] shell = os.name == 'nt' p = subprocess.Popen(args, shell=shell, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout = p.communicate()[0] if p.returncode: raise hgutil.Abort(_('cannot get information about %s') % svnurl) m = re.search(r'(.*)', stdout, re.S) if not m: raise hgutil.Abort(_('cannot find SVN repository root from %s') % svnurl) root = m.group(1).rstrip('/') m = re.search(r'(.*)', stdout, re.S) if not m: raise hgutil.Abort(_('cannot find SVN repository URL from %s') % svnurl) url = m.group(1) m = re.search(r']+revision="([^"]+)"', stdout, re.S) if not m: raise hgutil.Abort(_('cannot find SVN revision from %s') % svnurl) rev = m.group(1) return url, root, rev class externalsupdater: def __init__(self, ui, repo): self.repo = repo self.ui = ui def update(self, wpath, rev, source, pegrev): path = self.repo.wjoin(wpath) revspec = [] if rev: revspec = ['-r', rev] if os.path.isdir(path): exturl, _extroot, extrev = getsvninfo(path) # Comparing the source paths is not enough, but I don't # know how to compare path+pegrev. The following update # might fail if the path was replaced by another unrelated # one. It can be fixed manually by deleting the externals # and updating again. if source == exturl: if extrev != rev: self.ui.status(_('updating external on %s@%s\n') % (wpath, rev or 'HEAD')) cwd = os.path.join(self.repo.root, path) self.svn(['update'] + revspec, cwd) return self.delete(wpath) cwd, dest = os.path.split(path) cwd = os.path.join(self.repo.root, cwd) if not os.path.isdir(cwd): os.makedirs(cwd) if not pegrev and rev: pegrev = rev if pegrev: source = '%s@%s' % (source, pegrev) self.ui.status(_('fetching external %s@%s\n') % (wpath, rev or 'HEAD')) self.svn(['co'] + revspec + [source, dest], cwd) def delete(self, wpath): path = self.repo.wjoin(wpath) if os.path.isdir(path): self.ui.status(_('removing external %s\n') % wpath) def onerror(function, path, excinfo): if function is not os.remove: raise # read-only files cannot be unlinked under Windows s = os.stat(path) if (s.st_mode & stat.S_IWRITE) != 0: raise os.chmod(path, stat.S_IMODE(s.st_mode) | stat.S_IWRITE) os.remove(path) shutil.rmtree(path, onerror=onerror) return 1 def svn(self, args, cwd): args = ['svn'] + args self.ui.debug(_('updating externals: %r, cwd=%s\n') % (args, cwd)) shell = os.name == 'nt' p = subprocess.Popen(args, cwd=cwd, shell=shell, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) for line in p.stdout: self.ui.note(line) p.wait() if p.returncode != 0: raise hgutil.Abort("subprocess '%s' failed" % ' '.join(args)) def updateexternals(ui, args, repo, **opts): """update repository externals """ if len(args) > 2: raise hgutil.Abort(_('updateexternals expects at most one changeset')) node = None if len(args) == 2: svnurl = util.normalize_url(repo.ui.expandpath(args[0])) args = args[1:] else: svnurl = util.normalize_url(repo.ui.expandpath('default')) if args: node = args[0] svnroot = getsvninfo(svnurl)[1] # Retrieve current externals status try: oldext = file(repo.join('svn/externals'), 'rb').read() except IOError: oldext = '' newext = '' ctx = repo[node] if '.hgsvnexternals' in ctx: newext = ctx['.hgsvnexternals'].data() updater = externalsupdater(ui, repo) actions = computeactions(ui, repo, svnroot, oldext, newext) for action, ext in actions: if action == 'u': updater.update(ext[0], ext[1], ext[2], ext[3]) elif action == 'd': updater.delete(ext[0]) else: raise hgutil.Abort(_('unknown update actions: %r') % action) file(repo.join('svn/externals'), 'wb').write(newext) def getchanges(ui, repo, parentctx, exts): """Take a parent changectx and the new externals definitions as an externalsfile and return a dictionary mapping the special file hgsubversion needs for externals bookkeeping, to their new content as raw bytes or None if the file has to be removed. """ mode = ui.config('hgsubversion', 'externals', 'svnexternals') if mode == 'svnexternals': files = { '.hgsvnexternals': None, } if exts: files['.hgsvnexternals'] = exts.write() elif mode == 'subrepos': # XXX: clobering the subrepos files is good enough for now files = { '.hgsub': None, '.hgsubstate': None, } if exts: defs = parsedefinitions(ui, repo, '', exts) hgsub, hgsubstate = [], [] for path, rev, _source, _pegrev, norevline, base in sorted(defs): hgsub.append('%s = [hgsubversion] %s:%s\n' % (path, base, norevline)) if rev is None: rev = 'HEAD' hgsubstate.append('%s %s\n' % (rev, path)) files['.hgsub'] = ''.join(hgsub) files['.hgsubstate'] = ''.join(hgsubstate) elif mode == 'ignore': files = {} else: raise hgutil.Abort(_('unknown externals modes: %s') % mode) # Should the really be updated? updates = {} for fn, data in files.iteritems(): if data is not None: if fn not in parentctx or parentctx[fn].data() != data: updates[fn] = data else: if fn in parentctx: updates[fn] = None return updates def parse(ui, ctx): """Return the externals definitions stored in ctx as a (possibly empty) externalsfile(). """ external = externalsfile() mode = ui.config('hgsubversion', 'externals', 'svnexternals') if mode == 'svnexternals': if '.hgsvnexternals' in ctx: external.read(ctx['.hgsvnexternals'].data()) elif mode == 'subrepos': for path in ctx.substate: src, rev = ctx.substate[path][:2] base, norevline = src.split(':', 1) base = base.strip() if rev is None: rev = 'HEAD' line = norevline.replace('{REV}', rev) external.setdefault(base, []).append(line) elif mode == 'ignore': pass else: raise hgutil.Abort(_('unknown externals modes: %s') % mode) return external if subrepo: class svnsubrepo(subrepo.svnsubrepo): def __init__(self, ctx, path, state): state = (state[0].split(':', 1)[1], state[1]) super(svnsubrepo, self).__init__(ctx, path, state) def get(self, state, *args, **kwargs): # Resolve source first line = state[0].split(':', 1)[1] source, pegrev = parsedefinition(line)[2:4] try: # Getting the root SVN repository URL is expensive. # Assume the externals is absolute. source = resolvesource(self._ui, None, source) except RelativeSourceError: svnurl = self._ctx._repo.ui.expandpath('default') svnroot = getsvninfo(util.normalize_url(svnurl))[1] source = resolvesource(self._ui, svnroot, source) # hg < 1.9 svnsubrepo calls "svn checkout" with --rev # only, so peg revisions are correctly used. 1.9 and # higher, append the rev as a peg revision to the source # URL, so we cannot add our own. We assume that "-r10 # url@2" will be similar to "url@10" most of the time. if pegrev is not None and passpegrev: source = source + '@' + pegrev state = (source, state[1]) # hg-1.7.4-c19b9282d3a7 introduced the overwrite argument return super(svnsubrepo, self).get(state, *args, **kwargs) def dirty(self, ignoreupdate=False): # You cannot compare anything with HEAD. Just accept it # can be anything. if hasattr(self, '_wcrevs'): wcrevs = self._wcrevs() else: wcrev = self._wcrev() wcrevs = (wcrev, wcrev) if (('HEAD' in wcrevs or self._state[1] == 'HEAD' or self._state[1] in wcrevs or ignoreupdate) and not self._wcchanged()[0]): return False return True def commit(self, text, user, date): rev = super(svnsubrepo, self).commit(text, user, date) # Keep unversioned externals unversioned 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() durin42-hgsubversion-77b22e5b4ea6/hgsubversion/svnmeta.py0000644000000000000000000007315112043462603021600 0ustar 00000000000000import cPickle as pickle import posixpath import os import tempfile from mercurial import context from mercurial import util as hgutil from mercurial import revlog from mercurial import node import util import maps import editor 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. """ 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: f.close() class SVNMeta(object): def __init__(self, repo, uuid=None, subdir=None): """path is the path to the target hg repo. subdir is the subdirectory of the edits *on the svn server*. It is needed for stripping paths off in certain cases. """ self.ui = repo.ui self.repo = repo self.path = os.path.normpath(repo.join('..')) if not os.path.isdir(self.meta_data_dir): os.makedirs(self.meta_data_dir) self.uuid = uuid self.subdir = subdir self.revmap = maps.RevMap(repo) author_host = self.ui.config('hgsubversion', 'defaulthost', uuid) authors = self.ui.config('hgsubversion', 'authormap') tag_locations = self.ui.configlist('hgsubversion', 'tagpaths', ['tags']) self.usebranchnames = self.ui.configbool('hgsubversion', '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): f = open(self.branch_info_file) self.branches = pickle.load(f) f.close() self.prevbranches = dict(self.branches) self.tags = maps.Tags(repo) if os.path.exists(self.tag_locations_file): f = open(self.tag_locations_file) self.tag_locations = pickle.load(f) f.close() else: self.tag_locations = tag_locations if os.path.exists(self.layoutfile): f = open(self.layoutfile) self._layout = f.read().strip() f.close() self.repo.ui.setconfig('hgsubversion', 'layout', self._layout) else: self._layout = None pickle_atomic(self.tag_locations, self.tag_locations_file) # ensure nested paths are handled properly self.tag_locations.sort() self.tag_locations.reverse() self.authors = maps.AuthorMap(self.ui, self.authors_file, defaulthost=author_host) if authors: self.authors.load(authors) self.branchmap = maps.BranchMap(self.ui, self.branchmapfile) if branchmap: self.branchmap.load(branchmap) self.tagmap = maps.TagMap(self.ui, self.tagmapfile) 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.addedtags = {} self.deletedtags = {} @property def layout(self): # this method can't determine the layout, but it needs to be # resolved into something other than auto before this ever # gets called if not self._layout or self._layout == 'auto': lo = self.repo.ui.config('hgsubversion', 'layout', default='auto') if lo == 'auto': raise hgutil.Abort('layout not yet determined') self._layout = lo f = open(self.layoutfile, 'w') f.write(self._layout) f.close() return self._layout @property def editor(self): if not hasattr(self, '_editor'): self._editor = editor.HgEditor(self) return self._editor def _get_subdir(self): return self.__subdir def _set_subdir(self, subdir): if subdir: subdir = '/'.join(p for p in subdir.split('/') if p) subdirfile = os.path.join(self.meta_data_dir, 'subdir') if os.path.isfile(subdirfile): stored_subdir = open(subdirfile).read() assert stored_subdir is not None if subdir is None: self.__subdir = stored_subdir elif subdir != stored_subdir: raise hgutil.Abort('unable to work on a different path in the ' 'repository') else: self.__subdir = subdir elif subdir is not None: f = open(subdirfile, 'w') f.write(subdir) f.close() self.__subdir = subdir else: raise hgutil.Abort("hgsubversion metadata unavailable; " "please run 'hg svn rebuildmeta'") subdir = property(_get_subdir, _set_subdir, None, 'Error-checked sub-directory of source Subversion ' 'repository.') def _get_uuid(self): return self.__uuid def _set_uuid(self, uuid): uuidfile = os.path.join(self.meta_data_dir, 'uuid') if os.path.isfile(uuidfile): stored_uuid = open(uuidfile).read() assert stored_uuid if uuid and uuid != stored_uuid: raise hgutil.Abort('unable to operate on unrelated repository') self.__uuid = uuid or stored_uuid elif uuid: f = open(uuidfile, 'w') f.write(uuid) f.close() self.__uuid = uuid else: raise hgutil.Abort("hgsubversion metadata unavailable; " "please run 'hg svn rebuildmeta'") uuid = property(_get_uuid, _set_uuid, None, 'Error-checked UUID of source Subversion repository.') @property def meta_data_dir(self): return os.path.join(self.path, '.hg', 'svn') @property def branch_info_file(self): return os.path.join(self.meta_data_dir, 'branch_info') @property def tag_locations_file(self): return os.path.join(self.meta_data_dir, 'tag_locations') @property def authors_file(self): 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') @property def tagmapfile(self): # called tag-renames for backwards compatibility return os.path.join(self.meta_data_dir, 'tag-renames') @property def layoutfile(self): return os.path.join(self.meta_data_dir, 'layout') def fixdate(self, date): if date is not None: date = date.replace('T', ' ').replace('Z', '').split('.')[0] date += ' -0000' self.lastdate = date else: date = self.lastdate return date def save(self): '''Save the Subversion metadata. This should really be called after every revision is created. ''' pickle_atomic(self.branches, self.branch_info_file) def localname(self, path): """Compute the local name for a branch located at path. """ if self.layout == 'single': return 'default' if path == 'trunk': return None elif path.startswith('branches/'): return path[len('branches/'):] return '../%s' % path def remotename(self, branch): if self.layout == 'single': return '' if branch == 'default' or branch is None: return 'trunk' elif branch.startswith('../'): return branch[3:] return 'branches/%s' % branch def genextra(self, revnum, branch): extra = {} subdir = self.subdir if subdir and subdir[-1] == '/': subdir = subdir[:-1] if subdir and subdir[0] != '/': subdir = '/' + subdir if self.layout == 'single': path = subdir or '/' else: branchpath = 'trunk' if branch: extra['branch'] = branch if branch.startswith('../'): branchpath = branch[3:] else: branchpath = 'branches/%s' % branch path = '%s/%s' % (subdir, branchpath) extra['convert_revision'] = 'svn:%(uuid)s%(path)s@%(rev)s' % { 'uuid': self.uuid, 'path': path, 'rev': revnum, } return extra def mapbranch(self, extra, close=False): if close: extra['close'] = 1 mapped = self.branchmap.get(extra.get('branch', 'default')) if not self.usebranchnames or mapped == 'default': extra.pop('branch', None) elif mapped: extra['branch'] = mapped if extra.get('branch') == 'default': extra.pop('branch', None) def normalize(self, path): '''Normalize a path to strip of leading slashes and our subdir if we have one. ''' if self.subdir and path == self.subdir[:-1]: return '' if path and path[0] == '/': path = path[1:] if path == self.subdir: return '' if path and path.startswith(self.subdir + '/'): path = path[len(self.subdir):] if path and path[0] == '/': path = path[1:] return path def get_path_tag(self, path): """If path could represent the path to a tag, returns the potential (non-empty) tag name. Otherwise, returns None Note that it's only a tag if it was copied from the path '' in a branch (or tag) we have, for our purposes. """ if self.layout != 'single': path = self.normalize(path) for tagspath in self.tag_locations: if path.startswith(tagspath + '/'): tag = path[len(tagspath) + 1:] if tag: return tag return None def split_branch_path(self, path, existing=True): """Figure out which branch inside our repo this path represents, and also figure out which path inside that branch it is. Returns a tuple of (path within branch, local branch name, server-side branch path). Note that tag paths can also be matched: assuming tags/tag-1.1 is a tag then: tags/tag-1.1 => ('', '../tags/tag-1.1', 'tags/tag-1.1') tags/tag-1.1/file => ('file', '../tags/tag-1.1', 'tags/tag-1.1') tags/tag-1.2 => (None, None, None) If existing=True, will return None, None, None if the file isn't on some known branch. If existing=False, then it will guess what the branch would be if it were known. Server-side branch path should be relative to our subdirectory. """ path = self.normalize(path) if self.layout == 'single': return (path, None, '') tag = self.get_path_tag(path) if tag: # consider the new tags when dispatching entries matched = [] for tags in (self.tags, self.addedtags): matched += [t for t in tags if (tag == t or tag.startswith(t + '/'))] if not matched: return None, None, None matched.sort(key=len, reverse=True) if tag == matched[0]: brpath = '' svrpath = path else: brpath = tag[len(matched[0])+1:] svrpath = path[:-(len(brpath)+1)] ln = self.localname(svrpath) return brpath, ln, svrpath test = '' path_comps = path.split('/') while self.localname(test) not in self.branches and len(path_comps): if not test: test = path_comps.pop(0) else: test += '/%s' % path_comps.pop(0) if self.localname(test) in self.branches: return path[len(test)+1:], self.localname(test), test if existing: return None, None, None if path == 'trunk' or path.startswith('trunk/'): path = '/'.join(path.split('/')[1:]) test = 'trunk' elif path.startswith('branches/'): elts = path.split('/') test = '/'.join(elts[:2]) path = '/'.join(elts[2:]) else: path = test.split('/')[-1] test = '/'.join(test.split('/')[:-1]) ln = self.localname(test) if ln and ln.startswith('../'): return None, None, None return path, ln, test def _determine_parent_branch(self, p, src_path, src_rev, revnum): if src_path is not None: src_file, src_branch = self.split_branch_path(src_path)[:2] src_tag = self.get_path_tag(src_path) if src_tag or src_file == '': ln = self.localname(p) if src_tag in self.tags: changeid = self.tags[src_tag] src_rev, src_branch = self.get_source_rev(changeid)[:2] return {ln: (src_branch, src_rev, revnum)} return {} def is_path_valid(self, path, existing=True): if path is None: return False subpath = self.split_branch_path(path, existing)[0] if subpath is None: return False return subpath in self.filemap def get_parent_svn_branch_and_rev(self, number, branch, exact=False): """Return the parent revision of branch at number as a tuple (parentnum, parentbranch) or (None, None) if undefined. By default, current revision copy records will be used to resolve the parent. For instance, if branch1 is replaced by branch2 in current revision, then the parent of current revision on branch1 will be branch2. In this case, use exact=True to select the existing branch before looking at the copy records. """ if (number, branch) in self.revmap: return number, branch real_num = 0 for num, br in self.revmap.iterkeys(): if br != branch: continue if num <= number and num > real_num: real_num = num if branch in self.branches: parent_branch = self.branches[branch][0] parent_branch_rev = self.branches[branch][1] # check to see if this branch already existed and is the same if parent_branch_rev < real_num: return real_num, branch # if that wasn't true, then this is the a new branch with the # same name as some old deleted branch if parent_branch_rev <= 0 and real_num == 0: return None, None branch_created_rev = self.branches[branch][2] if parent_branch == 'trunk': parent_branch = None if branch_created_rev <= number + 1 and branch != parent_branch: # did the branch exist in previous run if exact and branch in self.prevbranches: if self.prevbranches[branch][1] < real_num: return real_num, branch return self.get_parent_svn_branch_and_rev( parent_branch_rev, parent_branch) if real_num != 0: return real_num, branch return None, None def get_parent_revision(self, number, branch, exact=False): '''Get the parent revision hash for a commit on a specific branch. ''' tag = self.get_path_tag(self.remotename(branch)) if tag: # Reference a tag being created if tag in self.addedtags: tbranch, trev = self.addedtags[tag] fromtag = self.get_path_tag(self.remotename(tbranch)) if not fromtag: # Created from a regular branch, not another tag tagged = self.get_parent_svn_branch_and_rev(trev, tbranch) return node.hex(self.revmap[tagged]) tag = fromtag # Reference an existing tag limitedtags = maps.Tags(self.repo, endrev=number - 1) if tag in limitedtags: return limitedtags[tag] r, br = self.get_parent_svn_branch_and_rev(number - 1, branch, exact) if r is not None: return self.revmap[r, br] return revlog.nullid def get_source_rev(self, changeid=None, ctx=None): """Return the source svn revision, the branch name and the svn branch path or a converted changeset. If supplied revision has no conversion record, raise KeyError. If ctx is None, build one from supplied changeid """ if ctx is None: ctx = self.repo[changeid] extra = ctx.extra() if 'convert_revision' not in extra: raise KeyError('%s has no conversion record' % ctx) branchpath, revnum = extra['convert_revision'][40:].rsplit('@', 1) branch = self.localname(self.normalize(branchpath)) if self.layout == 'single': branchpath = '' if branchpath and branchpath[0] == '/': branchpath = branchpath[1:] return int(revnum), branch, branchpath def update_branch_tag_map_for_rev(self, revision): """Given a revision object, determine changes to branches. Returns: a dict of { 'branches': (added_branches, self.closebranches), } where adds are dicts where the keys are branch names and values are the place the branch came from. The deletions are sets of the deleted branches. """ if self.layout == 'single': return {'branches': ({None: (None, 0, -1), }, set()), } paths = revision.paths added_branches = {} # Reset the tags delta before detecting the new one, and take # care not to fill them until done since split_branch_path() # use them. self.addedtags, self.deletedtags = {}, {} addedtags, deletedtags = {}, {} self.closebranches = set() for p in sorted(paths): t_name = self.get_path_tag(p) if t_name: src_p, src_rev = paths[p].copyfrom_path, paths[p].copyfrom_rev if src_p is not None and src_rev is not None: file, branch = self.split_branch_path(src_p)[:2] from_tag = self.get_path_tag(src_p) if file is None and not from_tag: continue if from_tag and from_tag not in self.tags: # Ignore copies from unknown tags continue if not file: # Direct branch or tag copy if from_tag: changeid = self.tags[from_tag] src_rev, branch = self.get_source_rev(changeid)[:2] if t_name not in addedtags: addedtags[t_name] = branch, src_rev else: # Subbranch or subtag copy t_name = t_name[:-(len(file)+1)] found = t_name in addedtags if found and src_rev > addedtags[t_name][1]: addedtags[t_name] = branch, src_rev elif (paths[p].action == 'D' and p.endswith(t_name) and t_name in self.tags): branch = self.get_source_rev(self.tags[t_name])[1] deletedtags[t_name] = branch, None continue # At this point we know the path is not a tag. In that # case, we only care if it is the root of a new branch (in # this function). This is determined by the following # checks: # 1. Is the file located inside any currently known # branch? If yes, then we're done with it, this isn't # interesting. # 2. Does the file have copyfrom information? If yes, and # the branch is being replaced by what would be an # ancestor, treat it as a regular revert. Otherwise, # we're done: this is a new branch, and we record the # copyfrom in added_branches if it comes from the root # of another branch, or create it from scratch. # 3. Neither of the above. This could be a branch, but it # might never work out for us. It's only ever a branch # (as far as we're concerned) if it gets committed to, # which we have to detect at file-write time anyway. So # we do nothing here. # 4. It's the root of an already-known branch, with an # action of 'D'. We mark the branch as deleted. # 5. It's the parent directory of one or more # already-known branches, so we mark them as deleted. # 6. It's a branch being replaced by another branch - the # action will be 'R'. fi, br = self.split_branch_path(p)[:2] if fi is not None: if fi == '': if paths[p].action == 'D': self.closebranches.add(br) # case 4 elif paths[p].action == 'R': # Check the replacing source is not an ancestor # branch of the branch being replaced, this # would just be a revert. cfi, cbr = self.split_branch_path( paths[p].copyfrom_path, paths[p].copyfrom_rev)[:2] if cfi == '': cctx = self.repo[self.get_parent_revision( paths[p].copyfrom_rev + 1, cbr)] ctx = self.repo[self.get_parent_revision( revision.revnum, br)] if cctx and util.isancestor(ctx, cctx): continue parent = self._determine_parent_branch( p, paths[p].copyfrom_path, paths[p].copyfrom_rev, revision.revnum) added_branches.update(parent) continue # case 1 if paths[p].action == 'D': for known in self.branches: if self.remotename(known).startswith(p): self.closebranches.add(known) # case 5 parent = self._determine_parent_branch( p, paths[p].copyfrom_path, paths[p].copyfrom_rev, revision.revnum) if not parent and paths[p].copyfrom_path: bpath, branch = self.split_branch_path(p, False)[:2] if (bpath is not None and branch not in self.branches and branch not in added_branches): parent = {branch: (None, 0, revision.revnum)} elif bpath is None: srcpath = paths[p].copyfrom_path srcrev = paths[p].copyfrom_rev parent = {} for br in self.branches: rn = self.remotename(br) if rn.startswith(srcpath[1:] + '/'): bname = posixpath.basename(rn) newbr = posixpath.join(p, bname) parent.update( self._determine_parent_branch( newbr, rn, srcrev, revision.revnum)) added_branches.update(parent) self.addedtags, self.deletedtags = addedtags, deletedtags return { 'branches': (added_branches, self.closebranches), } def save_tbdelta(self, tbdelta): self.prevbranches = dict(self.branches) for br in tbdelta['branches'][1]: del self.branches[br] self.branches.update(tbdelta['branches'][0]) def movetag(self, tag, hash, rev, date): if tag in self.tags and self.tags[tag] == hash: return # determine branch from earliest unclosed ancestor branchparent = self.repo[hash] while branchparent.extra().get('close'): branchparent = branchparent.parents()[0] branch = self.get_source_rev(ctx=branchparent)[1] parentctx = self.repo[self.get_parent_revision(rev.revnum + 1, branch)] if '.hgtags' in parentctx: tagdata = parentctx.filectx('.hgtags').data() else: tagdata = '' tagdata += '%s %s\n' % (node.hex(hash), self.tagmap.get(tag, tag)) def hgtagsfn(repo, memctx, path): assert path == '.hgtags' return context.memfilectx(path=path, data=tagdata, islink=False, isexec=False, copied=False) revnum, branch = self.get_source_rev(ctx=parentctx)[:2] newparent = None for child in parentctx.children(): if (self.get_source_rev(ctx=child)[1] == branch and child.extra().get('close', False)): newparent = child if newparent: parentctx = newparent revnum, branch = self.get_source_rev(ctx=parentctx)[:2] ctx = context.memctx(self.repo, (parentctx.node(), node.nullid), rev.message or util.default_commit_msg(self.ui), ['.hgtags', ], hgtagsfn, self.authors[rev.author], date, parentctx.extra()) new_hash = self.repo.svn_commitctx(ctx) if not newparent: assert self.revmap[revnum, branch] == parentctx.node() self.revmap[revnum, branch] = new_hash self.tags[tag] = hash, rev.revnum util.describe_commit(self.ui, new_hash, branch) def committags(self, rev, endbranches): if not self.addedtags and not self.deletedtags: return date = self.fixdate(rev.date) # determine additions/deletions per branch branches = {} for tags in (self.addedtags, self.deletedtags): for tag, (branch, srcrev) in tags.iteritems(): op = srcrev is None and 'rm' or 'add' branches.setdefault(branch, []).append((op, tag, srcrev)) for b, tags in branches.iteritems(): # modify parent's .hgtags source parent = self.repo[self.get_parent_revision(rev.revnum, b)] if '.hgtags' not in parent: src = '' else: src = parent['.hgtags'].data() fromtag = self.get_path_tag(self.remotename(b)) for op, tag, r in sorted(tags, reverse=True): if tag in self.tagmap and not self.tagmap[tag]: continue tagged = node.hex(node.nullid) # op != 'add' if op == 'add': if fromtag: if fromtag in self.tags: tagged = node.hex(self.tags[fromtag]) else: tagged = node.hex(self.revmap[ self.get_parent_svn_branch_and_rev(r, b)]) src += '%s %s\n' % (tagged, self.tagmap.get(tag, tag)) self.tags[tag] = node.bin(tagged), rev.revnum # add new changeset containing updated .hgtags def fctxfun(repo, memctx, path): return context.memfilectx(path='.hgtags', data=src, islink=False, isexec=False, copied=None) extra = self.genextra(rev.revnum, b) if fromtag: extra['branch'] = parent.extra().get('branch', 'default') self.mapbranch(extra, b in endbranches or fromtag) ctx = context.memctx(self.repo, (parent.node(), node.nullid), rev.message or ' ', ['.hgtags'], fctxfun, self.authors[rev.author], date, extra) new = self.repo.svn_commitctx(ctx) if not fromtag and (rev.revnum, b) not in self.revmap: self.revmap[rev.revnum, b] = new if b in endbranches: endbranches.pop(b) bname = b or 'default' self.ui.status('Marked branch %s as closed.\n' % bname) def delbranch(self, branch, node, rev): pctx = self.repo[node] files = pctx.manifest().keys() extra = self.genextra(rev.revnum, branch) self.mapbranch(extra, True) ctx = context.memctx(self.repo, (node, revlog.nullid), rev.message or util.default_commit_msg(self.ui), [], lambda x, y, z: None, self.authors[rev.author], self.fixdate(rev.date), extra) new = self.repo.svn_commitctx(ctx) self.ui.status('Marked branch %s as closed.\n' % (branch or 'default')) durin42-hgsubversion-77b22e5b4ea6/hgsubversion/svnrepo.py0000644000000000000000000002437512043462603021623 0ustar 00000000000000""" repository class-based interface for hgsubversion Copyright (C) 2009, Dan Villiom Podlaski Christiansen See parent package for licensing. Internally, Mercurial assumes that every single repository is a localrepository subclass: pull() is called on the instance pull *to*, but not the one pulled *from*. To work around this, we create two classes: - svnremoterepo for Subversion repositories, but it doesn't really do anything. - svnlocalrepo for local repositories which handles both operations on itself -- the local, hgsubversion-enabled clone -- and the remote repository. Decorators are used to distinguish and filter these operations from others. """ import errno from mercurial import error from mercurial import util as hgutil try: from mercurial.peer import peerrepository from mercurial import httppeer except ImportError: from mercurial.repo import repository as peerrepository from mercurial import httprepo as httppeer try: from mercurial import phases phases.public # defeat demand import except ImportError: phases = None import re import util import wrappers import svnwrap import svnmeta propertycache = hgutil.propertycache class ctxctx(object): """Proxies a ctx object and ensures files is never empty.""" def __init__(self, ctx): self._ctx = ctx def files(self): return self._ctx.files() or ['.svn'] def filectx(self, path, filelog=None): if path == '.svn': raise IOError(errno.ENOENT, '.svn is a fake file') return self._ctx.filectx(path, filelog=filelog) def __getattr__(self, name): return getattr(self._ctx, name) def __getitem__(self, key): return self._ctx[key] def generate_repo_class(ui, repo): """ This function generates the local repository wrapper. """ superclass = repo.__class__ def remotesvn(fn): """ Filter for instance methods which require the first argument to be a remote Subversion repository instance. """ original = getattr(repo, fn.__name__, None) # remove when dropping support for hg < 1.6. if original is None and fn.__name__ == 'findoutgoing': return def wrapper(self, *args, **opts): capable = getattr(args[0], 'capable', lambda x: False) if capable('subversion'): return fn(self, *args, **opts) else: return original(*args, **opts) wrapper.__name__ = fn.__name__ + '_wrapper' wrapper.__doc__ = fn.__doc__ return wrapper class svnlocalrepo(superclass): def svn_commitctx(self, ctx): """Commits a ctx, but defeats manifest recycling introduced in hg 1.9.""" 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 def push(self, remote, force=False, revs=None, newbranch=None): return wrappers.push(self, remote, force, revs) @remotesvn def pull(self, remote, heads=[], force=False): return wrappers.pull(self, remote, heads, force) @remotesvn def findoutgoing(self, remote, base=None, heads=None, force=False): return wrappers.findoutgoing(repo, remote, heads, force) def svnmeta(self, uuid=None, subdir=None): return svnmeta.SVNMeta(self, uuid, subdir) repo.__class__ = svnlocalrepo class svnremoterepo(peerrepository): """ the dumb wrapper for actual Subversion repositories """ def __init__(self, ui, path=None): self.ui = ui if path is None: path = self.ui.config('paths', 'default-push') if path is None: path = self.ui.config('paths', 'default') if not path: 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 def _capabilities(self): return self.capabilities @propertycache def svnauth(self): # DO NOT default the user to hg's getuser(). If you provide # *any* default username to Subversion, it won't use any remembered # username for the desired realm, breaking OS X Keychain support, # GNOME keyring support, and all similar tools. user = self.ui.config('hgsubversion', 'username') passwd = self.ui.config('hgsubversion', 'password') url = util.normalize_url(self.path) user, passwd, url = svnwrap.parse_url(url, user, passwd) return url, user, passwd @property def svnurl(self): return self.svn.svn_url @propertycache def svn(self): try: return svnwrap.SubversionRepo(*self.svnauth, password_stores=self.password_stores) except svnwrap.SubversionConnectionException, e: self.ui.traceback() raise hgutil.Abort(e) def url(self): return self.path def lookup(self, key): return key def cancopy(self): return False def heads(self, *args, **opts): """ Whenever this function is hit, we abort. The traceback is useful for figuring out where to intercept the functionality. """ raise hgutil.Abort('command unavailable for Subversion repositories') def pushkey(self, namespace, key, old, new): return False def listkeys(self, namespace): return {} def instance(ui, url, create): if url.startswith('http://') or url.startswith('https://'): try: # may yield a bogus 'real URL...' message return httppeer.instance(ui, url, create) except error.RepoError: ui.traceback() ui.note('(falling back to Subversion support)\n') if create: raise hgutil.Abort('cannot create new remote Subversion repository') svnwrap.prompt_callback(SubversionPrompt(ui)) return svnremoterepo(ui, url) class SubversionPrompt(object): def __init__(self, ui): self.ui = ui def maybe_print_realm(self, realm): if realm: self.ui.write('Authentication realm: %s\n' % (realm,)) self.ui.flush() def username(self, realm, may_save, pool=None): self.maybe_print_realm(realm) username = self.ui.prompt('Username: ', default='') return (username, bool(may_save)) def simple(self, realm, default_username, may_save, pool=None): self.maybe_print_realm(realm) if default_username: username = default_username else: username = self.ui.prompt('Username: ', default='') password = self.ui.getpass('Password for \'%s\': ' % (username,), default='') return (username, password, bool(may_save)) def ssl_client_cert(self, realm, may_save, pool=None): self.maybe_print_realm(realm) cert_file = self.ui.prompt('Client certificate filename: ', default='') return (cert_file, bool(may_save)) def ssl_client_cert_pw(self, realm, may_save, pool=None): password = self.ui.getpass('Passphrase for \'%s\': ' % (realm,), default='') return (password, bool(may_save)) def insecure(fn): def fun(self, *args, **kwargs): failures = args[1] cert_info = args[2] # cert_info[0] is hostname # cert_info[1] is fingerprint fingerprint = self.ui.config('hostfingerprints', cert_info[0]) if fingerprint and fingerprint.lower() == cert_info[1].lower(): # same as the acceptance temporarily return (failures, False) cacerts = self.ui.config('web', 'cacerts') if not cacerts: # same as the acceptance temporarily return (failures, False) return fn(self, *args, **kwargs) return fun @insecure def ssl_server_trust(self, realm, failures, cert_info, may_save, pool=None): msg = 'Error validating server certificate for \'%s\':\n' % (realm,) if failures & svnwrap.SSL_UNKNOWNCA: msg += ( ' - The certificate is not issued by a trusted authority. Use the\n' ' fingerprint to validate the certificate manually!\n' ) if failures & svnwrap.SSL_CNMISMATCH: msg += ' - The certificate hostname does not match.\n' if failures & svnwrap.SSL_NOTYETVALID: msg += ' - The certificate is not yet valid.\n' if failures & svnwrap.SSL_EXPIRED: msg += ' - The certificate has expired.\n' if failures & svnwrap.SSL_OTHER: msg += ' - The certificate has an unknown error.\n' msg += ( 'Certificate information:\n' '- Hostname: %s\n' '- Valid: from %s until %s\n' '- Issuer: %s\n' '- Fingerprint: %s\n' ) % ( cert_info[0], # hostname cert_info[2], # valid_from cert_info[3], # valid_until cert_info[4], # issuer_dname cert_info[1], # fingerprint ) if may_save: msg += '(R)eject, accept (t)emporarily or accept (p)ermanently? ' choices = (('&Reject'), ('&Temporarily'), ('&Permanently')) else: msg += '(R)eject or accept (t)emporarily? ' choices = (('&Reject'), ('&Temporarily')) choice = self.ui.promptchoice(msg, choices, default=0) if choice == 1: creds = (failures, False) elif may_save and choice == 2: creds = (failures, True) else: creds = None return creds durin42-hgsubversion-77b22e5b4ea6/hgsubversion/svnwrap/__init__.py0000644000000000000000000000243112043462603023353 0ustar 00000000000000"""This is a special package because it contains (or will contain, as of now) two parallel implementations of the same code. One implementation, the original, uses the SWIG Python bindings. That's great, but those leak RAM and have a few other quirks. The goal is to have this file automatically contain the "best" available implementation without the user having to configure what is actually present. """ from common import * import os choice = os.environ.get('HGSUBVERSION_BINDINGS', '').lower() if choice == 'subvertpy': from subvertpy_wrapper import * elif choice == 'swig': from svn_swig_wrapper import * elif choice == 'none': # useful for verifying that demandimport works properly raise ImportError('cannot use hgsubversion; ' 'bindings disabled using HGSUBVERSION_BINDINGS') else: try: from subvertpy_wrapper import * except ImportError, e1: try: from svn_swig_wrapper import * except ImportError, e2: raise ImportError('no compatible bindings available:\n\n' '%s\n%s\n\n' 'Please install either Subvertpy or the ' 'Subversion Python SWIG bindings' % (e2, e1)) del os, choice durin42-hgsubversion-77b22e5b4ea6/hgsubversion/svnwrap/common.py0000644000000000000000000001171612043462603023112 0ustar 00000000000000import cStringIO import getpass import errno import os import shutil import sys import tempfile import urlparse import urllib import collections import fnmatch import ConfigParser import sys class SubversionRepoCanNotReplay(Exception): """Exception raised when the svn server is too old to have replay. """ class SubversionRepoCanNotDiff(Exception): """Exception raised when the svn API diff3() command cannot be used """ class SubversionConnectionException(Exception): """Exception raised when a generic error occurs when connecting to a repository. """ # Default chunk size used in fetch_history_at_paths() and revisions(). chunk_size = 1000 def parse_url(url, user=None, passwd=None): """Parse a URL and return a tuple (username, password, url) """ scheme, netloc, path, params, query, fragment = urlparse.urlparse(url) if '@' in netloc: userpass, netloc = netloc.split('@') if not user and not passwd: if ':' in userpass: user, passwd = userpass.split(':') else: user, passwd = userpass, '' user, passwd = urllib.unquote(user), urllib.unquote(passwd) if user and scheme == 'svn+ssh': netloc = '@'.join((user, netloc,)) url = urlparse.urlunparse((scheme, netloc, path, params, query, fragment)) return (user or None, passwd or None, url) class Revision(tuple): """Wrapper for a Subversion revision. Derives from tuple in an attempt to minimise the memory footprint. """ def __new__(self, revnum, author, message, date, paths, strip_path=''): _paths = {} if paths: for p in paths: _paths[p[len(strip_path):]] = paths[p] return tuple.__new__(self, (revnum, author, message, date, _paths)) @property def revnum(self): return self[0] @property def author(self): return self[1] @property def message(self): return self[2] @property def date(self): return self[3] @property def paths(self): return self[4] def __str__(self): return 'r%d by %s' % (self.revnum, self.author) _svn_config_dir = None class AutoPropsConfig(object): """Provides the subversion auto-props functionality when pushing new files. """ def __init__(self, config_dir=None): config_file = config_file_path(config_dir) self.config = ConfigParser.RawConfigParser() self.config.read([config_file]) def properties(self, file): """Returns a dictionary of the auto-props applicable for file. Takes enable-auto-props into account. """ properties = {} if self.autoprops_enabled(): for pattern,prop_list in self.config.items('auto-props'): if fnmatch.fnmatchcase(os.path.basename(file), pattern): properties.update(parse_autoprops(prop_list)) return properties def autoprops_enabled(self): return (self.config.has_option('miscellany', 'enable-auto-props') and self.config.getboolean( 'miscellany', 'enable-auto-props') and self.config.has_section('auto-props')) def config_file_path(config_dir): if config_dir == None: global _svn_config_dir config_dir = _svn_config_dir if config_dir == None: if sys.platform == 'win32': config_dir = os.path.join(os.environ['APPDATA'], 'Subversion') else: config_dir = os.path.join(os.environ['HOME'], '.subversion') return os.path.join(config_dir, 'config') def parse_autoprops(prop_list): """Parses a string of autoprops and returns a dictionary of the results. Emulates the parsing of core.auto_props_enumerator. """ def unquote(s): if len(s)>1 and s[0] in ['"', "'"] and s[0]==s[-1]: return s[1:-1] return s properties = {} for prop in prop_list.split(';'): if '=' in prop: prop, value = prop.split('=',1) value = unquote(value.strip()) else: value = '' properties[prop.strip()] = value return properties class SimpleStringIO(object): """SimpleStringIO can replace a StringIO in write mode. cStringIO reallocates and doubles the size of its internal buffer when it needs to append new data which requires two large blocks for large inputs. SimpleStringIO stores each individual blocks and joins them once done. This might cause more memory fragmentation but requires only one large block. In practice, ra.get_file() seems to write in 16kB blocks (svn 1.7.5) which should be friendly to memory allocators. """ def __init__(self, closing=True): self._blocks = [] self._closing = closing def write(self, s): self._blocks.append(s) def getvalue(self): return ''.join(self._blocks) def close(self): if self._closing: del self._blocks durin42-hgsubversion-77b22e5b4ea6/hgsubversion/svnwrap/subvertpy_wrapper.py0000644000000000000000000005231112043462603025421 0ustar 00000000000000import cStringIO import errno import os import shutil import sys import tempfile import urllib import collections import common import warnings warnings.filterwarnings('ignore', module='svn.core', category=DeprecationWarning) subvertpy_required = (0, 7, 4) subversion_required = (1, 5, 0) try: from subvertpy import client from subvertpy import delta from subvertpy import properties from subvertpy import ra import subvertpy except ImportError: raise ImportError('Subvertpy %d.%d.%d or later required, but not found' % subvertpy_required) def _versionstr(v): return '.'.join(str(d) for d in v) if subvertpy.__version__ < subvertpy_required: # pragma: no cover raise ImportError('Subvertpy %s or later required, ' 'but %s found' % (_versionstr(subvertpy_required), _versionstr(subvertpy.__version__))) subversion_version = subvertpy.wc.api_version() if subversion_version[:3] < subversion_required: raise ImportError('Subversion %s or later required, ' 'but Subvertpy is using %s' % (_versionstr(subversion_required), _versionstr(subversion_version[:3]))) def version(): svnvers = _versionstr(subversion_version[:3]) if subversion_version[3]: svnvers += '-' + subversion_version[3] return (svnvers, 'Subvertpy ' + _versionstr(subvertpy.__version__)) # exported values ERR_FS_CONFLICT = subvertpy.ERR_FS_CONFLICT ERR_FS_NOT_FOUND = subvertpy.ERR_FS_NOT_FOUND ERR_FS_TXN_OUT_OF_DATE = subvertpy.ERR_FS_TXN_OUT_OF_DATE ERR_INCOMPLETE_DATA = subvertpy.ERR_INCOMPLETE_DATA ERR_RA_DAV_PATH_NOT_FOUND = subvertpy.ERR_RA_DAV_PATH_NOT_FOUND ERR_RA_DAV_REQUEST_FAILED = subvertpy.ERR_RA_DAV_REQUEST_FAILED SSL_UNKNOWNCA = subvertpy.SSL_UNKNOWNCA SSL_CNMISMATCH = subvertpy.SSL_CNMISMATCH SSL_NOTYETVALID = subvertpy.SSL_NOTYETVALID SSL_EXPIRED = subvertpy.SSL_EXPIRED SSL_OTHER = subvertpy.SSL_OTHER SubversionException = subvertpy.SubversionException apply_txdelta = delta.apply_txdelta_handler # superclass for editor.HgEditor Editor = object def ieditor(fn): """No-op decorator to identify methods used by the SVN editor interface. This decorator is not needed for Subvertpy, but is retained for compatibility with the SWIG bindings. """ return fn _prompt = None def prompt_callback(callback): global _prompt _prompt = callback _svntypes = { subvertpy.NODE_DIR: 'd', subvertpy.NODE_FILE: 'f', } class PathAdapter(object): __slots__ = ('action', 'copyfrom_path', 'copyfrom_rev') def __init__(self, path): self.action, self.copyfrom_path, self.copyfrom_rev = path if self.copyfrom_path: self.copyfrom_path = intern(self.copyfrom_path) class AbstractEditor(object): __slots__ = ('editor', 'baton') def __init__(self, editor, baton=None): self.editor = editor self.baton = baton def set_target_revision(self, rev): pass def open_root(self, base_revnum): baton = self.editor.open_root(None, base_revnum) return DirectoryEditor(self.editor, baton) def abort(self): # TODO: should we do something special here? self.close() def close(self): del self.editor class FileEditor(AbstractEditor): def __init__(self, editor, baton): super(FileEditor, self).__init__(editor, baton) def change_prop(self, name, value): self.editor.change_file_prop(self.baton, name, value, pool=None) def apply_textdelta(self, base_checksum): return self.editor.apply_textdelta(self.baton, base_checksum) def close(self, checksum=None): self.editor.close_file(self.baton, checksum) super(FileEditor, self).close() class DirectoryEditor(AbstractEditor): def __init__(self, editor, baton): super(DirectoryEditor, self).__init__(editor, baton) def delete_entry(self, path, revnum): self.editor.delete_entry(path, revnum, self.baton) def open_directory(self, path, base_revnum): baton = self.editor.open_directory(path, self.baton, base_revnum) return DirectoryEditor(self.editor, baton) def add_directory(self, path, copyfrom_path=None, copyfrom_rev=-1): baton = self.editor.add_directory( path, self.baton, copyfrom_path, copyfrom_rev) return DirectoryEditor(self.editor, baton) def open_file(self, path, base_revnum): baton = self.editor.open_file(path, self.baton, base_revnum) return FileEditor(self.editor, baton) def add_file(self, path, copyfrom_path=None, copyfrom_rev=-1): baton = self.editor.add_file( path, self.baton, copyfrom_path, copyfrom_rev) return FileEditor(self.editor, baton) def change_prop(self, name, value): self.editor.change_dir_prop(self.baton, name, value, pool=None) def close(self): self.editor.close_directory(self.baton) super(DirectoryEditor, self).close() class SubversionRepo(object): """Wrapper for a Subversion repository. 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, 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.init_ra_and_client() self.svn_url = self.remote.get_url() self.uuid = self.remote.get_uuid() self.root = self.remote.get_repos_root() assert self.svn_url.startswith(self.root) # *will* have a leading '/', would not if we used get_repos_root2 self.subdir = self.svn_url[len(self.root):] if not self.subdir or self.subdir[-1] != '/': self.subdir += '/' # the RA interface always yields quoted paths, but the editor interface # expects unquoted paths self.subdir = urllib.unquote(self.subdir) self.hasdiff3 = True self.autoprops_config = common.AutoPropsConfig() def init_ra_and_client(self): """ Initializes the RA and client layers. With the SWIG bindings, getting unified diffs runs the remote server sometimes runs out of open files. It is not known whether the Subvertpy is affected by this. """ def getclientstring(): return 'hgsubversion' def simple(realm, username, may_save): return _prompt.simple(realm, username, may_save) def username(realm, may_save): return _prompt.username(realm, may_save) def ssl_client_cert(realm, may_save): return _prompt.ssl_client_cert(realm, may_save) def ssl_client_cert_pw(realm, may_save): return _prompt.ssl_client_cert_pw(realm, may_save) def ssl_server_trust(realm, failures, cert_info, may_save): creds = _prompt.ssl_server_trust(realm, failures, cert_info, may_save) if creds is None: # We need to reject the certificate, but subvertpy doesn't # handle None as a return value here, and requires # we instead return a tuple of (int, bool). Because of that, # we return (0, False) instead. creds = (0, False) return creds providers = ra.get_platform_specific_client_providers() providers += [ ra.get_simple_provider(), ra.get_username_provider(), ra.get_ssl_client_cert_file_provider(), ra.get_ssl_client_cert_pw_file_provider(), ra.get_ssl_server_trust_file_provider(), ] if _prompt: providers += [ ra.get_simple_prompt_provider(simple, 2), ra.get_username_prompt_provider(username, 2), ra.get_ssl_client_cert_prompt_provider(ssl_client_cert, 2), ra.get_ssl_client_cert_pw_prompt_provider(ssl_client_cert_pw, 2), ra.get_ssl_server_trust_prompt_provider(ssl_server_trust), ] auth = ra.Auth(providers) if self.username: auth.set_parameter(subvertpy.AUTH_PARAM_DEFAULT_USERNAME, self.username) if self.password: auth.set_parameter(subvertpy.AUTH_PARAM_DEFAULT_PASSWORD, self.password) try: self.remote = ra.RemoteAccess(url=self.svn_url, client_string_func=getclientstring, auth=auth) except SubversionException, e: # e.child contains a detailed error messages msglist = [] svn_exc = e while svn_exc: if svn_exc.args[0]: msglist.append(svn_exc.args[0]) svn_exc = svn_exc.child msg = '\n'.join(msglist) raise common.SubversionConnectionException(msg) self.client = client.Client() self.client.auth = auth @property def HEAD(self): return self.remote.get_latest_revnum() @property def last_changed_rev(self): try: holder = [] def callback(paths, revnum, props, haschildren): holder.append(revnum) self.remote.get_log(paths=[''], start=self.HEAD, end=1, limit=1, discover_changed_paths=False, callback=callback) return holder[-1] except SubversionException, e: if e.args[0] == ERR_FS_NOT_FOUND: raise else: return self.HEAD def list_dir(self, path, revision=None): """List the contents of a server-side directory. Returns a dict-like object with one dict key per directory entry. Args: dir: the directory to list, no leading slash rev: the revision at which to list the directory, defaults to HEAD """ # TODO: reject leading slashes like the docstring says if path: path = path.rstrip('/') + '/' r = self.remote.get_dir(path, revision or self.HEAD, ra.DIRENT_ALL) dirents, fetched_rev, properties = r return dirents def revisions(self, paths=None, start=0, stop=0, chunk_size=common.chunk_size): """Load the history of this repo. This is LAZY. It returns a generator, and fetches a small number of revisions at a time. The reason this is lazy is so that you can use the same repo object to perform RA calls to get deltas. """ if paths is None: paths = [''] if not stop: stop = self.HEAD while stop > start: def callback(paths, revnum, props, haschildren): if paths is None: return r = common.Revision(revnum, props.get(properties.PROP_REVISION_AUTHOR), props.get(properties.PROP_REVISION_LOG), props.get(properties.PROP_REVISION_DATE), dict([(k, PathAdapter(v)) for k, v in paths.iteritems()]), strip_path=self.subdir) revisions.append(r) # we only access revisions in a FIFO manner revisions = collections.deque() revprops = [properties.PROP_REVISION_AUTHOR, properties.PROP_REVISION_DATE, properties.PROP_REVISION_LOG] try: # TODO: using min(start + chunk_size, stop) may be preferable; # ra.get_log(), even with chunk_size set, takes a while # when converting the 65k+ rev. in LLVM. self.remote.get_log(paths=paths, revprops=revprops, start=start + 1, end=stop, limit=chunk_size, discover_changed_paths=True, callback=callback) except SubversionException, e: if e.args[1] == ERR_FS_NOT_FOUND: msg = ('%s not found at revision %d!' % (self.subdir.rstrip('/'), stop)) raise common.SubversionConnectionException(msg) elif e.args[1] == subvertpy.ERR_FS_NO_SUCH_REVISION: raise common.SubversionConnectionException(e.args[0]) else: raise while len(revisions) > 1: yield revisions.popleft() if len(revisions) == 0: # exit the loop; there is no history for the path. break else: r = revisions.popleft() start = r.revnum yield r def commit(self, paths, message, file_data, base_revision, addeddirs, deleteddirs, props, copies): """Commits the appropriate targets from revision in editor's store. """ def commitcb(*args): commit_info.append(args) commit_info = [] revprops = { properties.PROP_REVISION_LOG: message } # revprops.update(props) commiteditor = self.remote.get_commit_editor(revprops, commitcb) paths = set(paths) paths.update(addeddirs) paths.update(deleteddirs) # ensure that all parents are visited too; this may be slow for path in paths.copy(): for i in xrange(path.count('/'), -1, -1): p = path.rsplit('/', i)[0] if p in paths: continue paths.add(p) paths = sorted(paths) def visitdir(editor, directory, paths, pathidx): while pathidx < len(paths): path = paths[pathidx] if directory and not path.startswith(directory + '/'): return pathidx pathidx += 1 if path in file_data: # visiting a file base_text, new_text, action = file_data[path] if action == 'modify': fileeditor = editor.open_file(path, base_revision) elif action == 'add': frompath, fromrev = copies.get(path, (None, -1)) if frompath: frompath = self.path2url(frompath) fileeditor = editor.add_file(path, frompath, fromrev) elif action == 'delete': editor.delete_entry(path, base_revision) continue else: assert False, 'invalid action \'%s\'' % action if path in props: if props[path].get('svn:special', None): new_text = 'link %s' % new_text for p, v in props[path].iteritems(): fileeditor.change_prop(p, v) handler = fileeditor.apply_textdelta() delta.send_stream(cStringIO.StringIO(new_text), handler) fileeditor.close() else: # visiting a directory if path in addeddirs: direditor = editor.add_directory(path) elif path in deleteddirs: direditor = editor.delete_entry(path, base_revision) continue else: direditor = editor.open_directory(path) if path in props: for p, v in props[path].iteritems(): direditor.change_prop(p, v) pathidx = visitdir(direditor, path, paths, pathidx) direditor.close() return pathidx try: rooteditor = commiteditor.open_root() visitdir(rooteditor, '', paths, 0) rooteditor.close() commiteditor.close() except: commiteditor.abort() raise def get_replay(self, revision, editor, oldestrev=0): try: self.remote.replay(revision, oldestrev, AbstractEditor(editor)) 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 e.args[1] == subvertpy.ERR_UNSUPPORTED_FEATURE): msg = ('This Subversion server is older than 1.4.0, and ' 'cannot satisfy replay requests.') raise common.SubversionRepoCanNotReplay(msg) else: raise def get_revision(self, revision, editor): ''' feed the contents of the given revision to the given editor ''' reporter = self.remote.do_update(revision, '', True, AbstractEditor(editor)) reporter.set_path('', revision, True) reporter.finish() def get_unified_diff(self, path, revision, other_path=None, other_rev=None, deleted=True, ignore_type=False): """Gets a unidiff of path at revision against revision-1. """ url = self.path2url(path) url2 = (other_path and self.path2url(other_path) or url) if other_rev is None: other_rev = revision - 1 outfile, errfile = self.client.diff(other_rev, revision, url2, url, no_diff_deleted=deleted, ignore_content_type=ignore_type) error = errfile.read() assert not error, error return outfile.read() def get_file(self, path, revision): """Return content and mode of file at given path and revision. "link " prefix is dropped from symlink content. Mode is 'x' if file is executable, 'l' if a symlink, the empty string otherwise. If the file does not exist at this revision, raise IOError. """ mode = '' try: out = common.SimpleStringIO() rev, info = self.remote.get_file(path, out, revision) data = out.getvalue() out.close() if isinstance(info, list): info = info[-1] mode = (properties.PROP_EXECUTABLE in info) and 'x' or '' mode = (properties.PROP_SPECIAL in info) and 'l' or mode except SubversionException, e: if e.args[1] in (ERR_FS_NOT_FOUND, ERR_RA_DAV_PATH_NOT_FOUND): # File not found raise IOError(errno.ENOENT, e.args[0]) raise if mode == 'l': linkprefix = "link " if data.startswith(linkprefix): data = data[len(linkprefix):] return data, mode def list_props(self, path, revision): """Return a mapping of property names to values, raise IOError if specified path does not exist. """ try: pl = self.client.proplist(self.path2url(path), revision, client.depth_empty) except SubversionException, e: # Specified path does not exist at this revision if e.args[1] == subvertpy.ERR_NODE_UNKNOWN_KIND: raise IOError(errno.ENOENT, e.args[0]) raise return pl and pl[0][1] or {} def list_files(self, dirpath, revision): """List the content of a directory at a given revision, recursively. Yield tuples (path, kind) where 'path' is the entry path relatively to 'dirpath' and 'kind' is 'f' if the entry is a file, 'd' if it is a directory. Raise IOError if the directory cannot be found at given revision. """ try: entries = self.client.list(self.path2url(dirpath), revision, client.depth_infinity, ra.DIRENT_KIND) except SubversionException, e: if e.args[1] == subvertpy.ERR_FS_NOT_FOUND: raise IOError(errno.ENOENT, '%s cannot be found at r%d' % (dirpath, revision)) raise for path, e in entries.iteritems(): if not path: continue kind = _svntypes.get(e['kind']) yield path, kind def checkpath(self, path, revision): """Return the entry type at the given revision, 'f', 'd' or None if the entry does not exist. """ kind = self.remote.check_path(path, revision) return _svntypes.get(kind) def path2url(self, path): """Build svn URL for path, URL-escaping path. """ if not path or path == '.': return self.svn_url assert path[0] != '/', path return '/'.join((self.svn_url, urllib.quote(path).rstrip('/'),)) durin42-hgsubversion-77b22e5b4ea6/hgsubversion/svnwrap/svn_swig_wrapper.py0000644000000000000000000006033312043462603025220 0ustar 00000000000000import cStringIO import errno import os import shutil import sys import tempfile import urllib import collections import common import warnings warnings.filterwarnings('ignore', module='svn.core', category=DeprecationWarning) required_bindings = (1, 5, 0) try: from svn import client from svn import core from svn import delta from svn import ra current_bindings = (core.SVN_VER_MAJOR, core.SVN_VER_MINOR, core.SVN_VER_MICRO) 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 raise ImportError('Subversion %d.%d.%d or later required, ' 'but bindings for %d.%d.%d found' % (required_bindings + current_bindings)) def version(): return '%d.%d.%d' % current_bindings, 'SWIG' # exported values ERR_FS_CONFLICT = core.SVN_ERR_FS_CONFLICT ERR_FS_NOT_FOUND = core.SVN_ERR_FS_NOT_FOUND ERR_FS_TXN_OUT_OF_DATE = core.SVN_ERR_FS_TXN_OUT_OF_DATE ERR_INCOMPLETE_DATA = core.SVN_ERR_INCOMPLETE_DATA ERR_RA_DAV_REQUEST_FAILED = core.SVN_ERR_RA_DAV_REQUEST_FAILED SSL_UNKNOWNCA = core.SVN_AUTH_SSL_UNKNOWNCA SSL_CNMISMATCH = core.SVN_AUTH_SSL_CNMISMATCH SSL_NOTYETVALID = core.SVN_AUTH_SSL_NOTYETVALID SSL_EXPIRED = core.SVN_AUTH_SSL_EXPIRED SSL_OTHER = core.SVN_AUTH_SSL_OTHER SubversionException = core.SubversionException Editor = delta.Editor def apply_txdelta(base, target): handler, baton = delta.svn_txdelta_apply(cStringIO.StringIO(base), target, None) return (lambda window: handler(window, baton)) def optrev(revnum): optrev = core.svn_opt_revision_t() optrev.kind = core.svn_opt_revision_number optrev.value.number = revnum return optrev core.svn_config_ensure(None) svn_config = core.svn_config_get_config(None) class RaCallbacks(ra.Callbacks): @staticmethod def open_tmp_file(pool): # pragma: no cover (fd, fn) = tempfile.mkstemp() os.close(fd) return fn @staticmethod def get_client_string(pool): return 'hgsubversion' def ieditor(fn): """Helps identify methods used by the SVN editor interface. Stash any exception raised in the method on self. This is required because the SWIG bindings just mutate any exception into a generic Subversion exception with no way of telling what the original was. This allows the editor object to notice when you try and commit and really got an exception in the replay process. """ def fun(self, *args, **kwargs): try: return fn(self, *args, **kwargs) except: # pragma: no cover if self.current.exception is not None: self.current.exception = sys.exc_info() raise return fun _prompt = None def prompt_callback(callback): global _prompt _prompt = callback def _simple(realm, default_username, ms, pool): ret = _prompt.simple(realm, default_username, ms, pool) creds = core.svn_auth_cred_simple_t() (creds.username, creds.password, creds.may_save) = ret return creds def _username(realm, ms, pool): ret = _prompt.username(realm, ms, pool) creds = core.svn_auth_cred_username_t() (creds.username, creds.may_save) = ret return creds def _ssl_client_cert(realm, may_save, pool): ret = _prompt.ssl_client_cert(realm, may_save, pool) creds = core.svn_auth_cred_ssl_client_cert_t() (creds.cert_file, creds.may_save) = ret return creds def _ssl_client_cert_pw(realm, may_save, pool): ret = _prompt.ssl_client_cert_pw(realm, may_save, pool) creds = core.svn_auth_cred_ssl_client_cert_pw_t() (creds.password, creds.may_save) = ret return creds def _ssl_server_trust(realm, failures, cert_info, may_save, pool): cert = [ cert_info.hostname, cert_info.fingerprint, cert_info.valid_from, cert_info.valid_until, cert_info.issuer_dname, ] ret = _prompt.ssl_server_trust(realm, failures, cert, may_save, pool) if ret: creds = core.svn_auth_cred_ssl_server_trust_t() (creds.accepted_failures, creds.may_save) = ret else: creds = None return creds def _create_auth_baton(pool, password_stores): """Create a Subversion authentication baton. """ # Give the client context baton a suite of authentication # providers.h platform_specific = ['svn_auth_get_gnome_keyring_simple_provider', 'svn_auth_get_gnome_keyring_ssl_client_cert_pw_provider', 'svn_auth_get_keychain_simple_provider', 'svn_auth_get_keychain_ssl_client_cert_pw_provider', 'svn_auth_get_kwallet_simple_provider', 'svn_auth_get_kwallet_ssl_client_cert_pw_provider', 'svn_auth_get_ssl_client_cert_file_provider', 'svn_auth_get_windows_simple_provider', 'svn_auth_get_windows_ssl_server_trust_provider', ] providers = [] # Platform-dependant authentication methods getprovider = getattr(core, 'svn_auth_get_platform_specific_provider', None) if getprovider: # Available in svn >= 1.6 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: providers.append(p) else: for p in platform_specific: if hasattr(core, p): try: providers.append(getattr(core, p)()) except RuntimeError: pass providers += [ client.get_simple_provider(), client.get_username_provider(), client.get_ssl_client_cert_file_provider(), client.get_ssl_client_cert_pw_file_provider(), client.get_ssl_server_trust_file_provider(), ] if _prompt: providers += [ client.get_simple_prompt_provider(_simple, 2), client.get_username_prompt_provider(_username, 2), client.get_ssl_client_cert_prompt_provider(_ssl_client_cert, 2), client.get_ssl_client_cert_pw_prompt_provider(_ssl_client_cert_pw, 2), client.get_ssl_server_trust_prompt_provider(_ssl_server_trust), ] return core.svn_auth_open(providers, pool) _svntypes = { core.svn_node_dir: 'd', core.svn_node_file: 'f', } class SubversionRepo(object): """Wrapper for a Subversion repository. It uses the SWIG Python bindings, see above for requirements. """ 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 = core.svn_path_canonicalize(parsed[2]) self.auth_baton_pool = core.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() self.init_ra_and_client() self.uuid = ra.get_uuid(self.ra, self.pool) self.svn_url = ra.get_session_url(self.ra, self.pool) self.root = ra.get_repos_root(self.ra, self.pool) assert self.svn_url.startswith(self.root) # *will* have a leading '/', would not if we used get_repos_root2 self.subdir = self.svn_url[len(self.root):] if not self.subdir or self.subdir[-1] != '/': self.subdir += '/' # the RA interface always yields quoted paths, but the editor interface # expects unquoted paths self.subdir = urllib.unquote(self.subdir) self.hasdiff3 = True self.autoprops_config = common.AutoPropsConfig() def init_ra_and_client(self): """Initializes the RA and client layers, because sometimes getting unified diffs runs the remote server out of open files. """ # while we're in here we'll recreate our pool self.pool = core.Pool() if self.username: core.svn_auth_set_parameter(self.auth_baton, core.SVN_AUTH_PARAM_DEFAULT_USERNAME, self.username) if self.password: core.svn_auth_set_parameter(self.auth_baton, core.SVN_AUTH_PARAM_DEFAULT_PASSWORD, self.password) self.client_context = client.create_context() self.client_context.auth_baton = self.auth_baton self.client_context.config = svn_config callbacks = RaCallbacks() callbacks.auth_baton = self.auth_baton self.callbacks = callbacks try: self.ra = ra.open2(self.svn_url, callbacks, svn_config, self.pool) except SubversionException, e: # e.child contains a detailed error messages msglist = [] svn_exc = e while svn_exc: if svn_exc.args[0]: msglist.append(svn_exc.args[0]) svn_exc = svn_exc.child msg = '\n'.join(msglist) raise common.SubversionConnectionException(msg) @property def HEAD(self): return ra.get_latest_revnum(self.ra, self.pool) @property def last_changed_rev(self): try: holder = [] ra.get_log(self.ra, [''], self.HEAD, 1, 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: holder.append(revnum), self.pool) return holder[-1] except SubversionException, e: if e.apr_err not in [core.SVN_ERR_FS_NOT_FOUND]: raise else: return self.HEAD def list_dir(self, dir, revision=None): """List the contents of a server-side directory. Returns a dict-like object with one dict key per directory entry. Args: dir: the directory to list, no leading slash rev: the revision at which to list the directory, defaults to HEAD """ # TODO this should just not accept leading slashes like the docstring says if dir and dir[-1] == '/': dir = dir[:-1] if revision is None: revision = self.HEAD r = ra.get_dir2(self.ra, dir, revision, core.SVN_DIRENT_KIND, self.pool) folders, props, junk = r return folders def revisions(self, paths=None, start=0, stop=0, chunk_size=common.chunk_size): """Load the history of this repo. This is LAZY. It returns a generator, and fetches a small number of revisions at a time. The reason this is lazy is so that you can use the same repo object to perform RA calls to get deltas. """ if paths is None: paths = [''] if not stop: stop = self.HEAD while stop > start: def callback(paths, revnum, author, date, message, pool): r = common.Revision(revnum, author, message, date, paths, strip_path=self.subdir) revisions.append(r) # we only access revisions in a FIFO manner revisions = collections.deque() try: # TODO: using min(start + chunk_size, stop) may be preferable; # ra.get_log(), even with chunk_size set, takes a while # when converting the 65k+ rev. in LLVM. ra.get_log(self.ra, paths, start + 1, stop, chunk_size, # limit of how many log messages to load True, # don't need to know changed paths True, # stop on copies callback, self.pool) except core.SubversionException, e: if e.apr_err == core.SVN_ERR_FS_NOT_FOUND: msg = ('%s not found at revision %d!' % (self.subdir.rstrip('/'), stop)) raise common.SubversionConnectionException(msg) elif e.apr_err == core.SVN_ERR_FS_NO_SUCH_REVISION: raise common.SubversionConnectionException(e.message) else: raise while len(revisions) > 1: yield revisions.popleft() if len(revisions) == 0: # exit the loop; there is no history for the path. break else: r = revisions.popleft() start = r.revnum yield r self.init_ra_and_client() def commit(self, paths, message, file_data, base_revision, addeddirs, deleteddirs, properties, copies): """Commits the appropriate targets from revision in editor's store. """ self.init_ra_and_client() commit_info = [] def commit_cb(_commit_info, pool): commit_info.append(_commit_info) editor, edit_baton = ra.get_commit_editor2(self.ra, message, commit_cb, None, False, self.pool) checksum = [] # internal dir batons can fall out of scope and get GCed before svn is # done with them. This prevents that (credit to gvn for the idea). batons = [edit_baton, ] def driver_cb(parent, path, pool): if not parent: bat = editor.open_root(edit_baton, base_revision, self.pool) batons.append(bat) return bat if path in deleteddirs: bat = editor.delete_entry(path, base_revision, parent, pool) batons.append(bat) return bat if path not in file_data: if path in addeddirs: bat = editor.add_directory(path, parent, None, -1, pool) else: bat = editor.open_directory(path, parent, base_revision, pool) batons.append(bat) props = properties.get(path, {}) if 'svn:externals' in props: value = props['svn:externals'] editor.change_dir_prop(bat, 'svn:externals', value, pool) return bat base_text, new_text, action = file_data[path] compute_delta = True if action == 'modify': baton = editor.open_file(path, parent, base_revision, pool) elif action == 'add': frompath, fromrev = copies.get(path, (None, -1)) if frompath: frompath = self.path2url(frompath) baton = editor.add_file(path, parent, frompath, fromrev, pool) elif action == 'delete': baton = editor.delete_entry(path, base_revision, parent, pool) compute_delta = False if path in properties: if properties[path].get('svn:special', None): new_text = 'link %s' % new_text for p, v in properties[path].iteritems(): editor.change_file_prop(baton, p, v) if compute_delta: handler, wh_baton = editor.apply_textdelta(baton, None, self.pool) txdelta_stream = delta.svn_txdelta( cStringIO.StringIO(base_text), cStringIO.StringIO(new_text), self.pool) delta.svn_txdelta_send_txstream(txdelta_stream, handler, wh_baton, pool) # TODO pass md5(new_text) instead of None editor.close_file(baton, None, pool) try: delta.path_driver(editor, edit_baton, base_revision, paths, driver_cb, self.pool) editor.close_edit(edit_baton, self.pool) except: # If anything went wrong on the preceding lines, we should # abort the in-progress transaction. editor.abort_edit(edit_baton, self.pool) raise def get_replay(self, revision, editor, oldest_rev_i_have=0): # this method has a tendency to chew through RAM if you don't re-init self.init_ra_and_client() e_ptr, e_baton = delta.make_editor(editor) try: ra.replay(self.ra, revision, oldest_rev_i_have, True, e_ptr, e_baton, self.pool) 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): msg = ('This Subversion server is older than 1.4.0, and ' 'cannot satisfy replay requests.') raise common.SubversionRepoCanNotReplay(msg) else: raise # if we're not pulling the whole repo, svn fails to report # file properties for files merged from subtrees outside ours if self.svn_url != self.root: links, execs = editor.current.symlinks, editor.current.execfiles l = len(self.subdir) - 1 for f in editor.current.added: sf = f[l:] if links[f] or execs[f]: continue props = self.list_props(sf, revision) links[f] = props.get('svn:special') == '*' execs[f] = props.get('svn:executable') == '*' def get_revision(self, revision, editor): ''' feed the contents of the given revision to the given editor ''' e_ptr, e_baton = delta.make_editor(editor) reporter, reporter_baton = ra.do_update(self.ra, revision, "", True, e_ptr, e_baton) reporter.set_path(reporter_baton, "", revision, True, None) reporter.finish_report(reporter_baton) def get_unified_diff(self, path, revision, other_path=None, other_rev=None, deleted=True, ignore_type=False): """Gets a unidiff of path at revision against revision-1. """ if not self.hasdiff3: raise common.SubversionRepoCanNotDiff() # works around an svn server keeping too many open files (observed # in an svnserve from the 1.2 era) self.init_ra_and_client() url = self.path2url(path) url2 = url url2 = (other_path and self.path2url(other_path) or url) if other_rev is None: other_rev = revision - 1 old_cwd = os.getcwd() tmpdir = tempfile.mkdtemp('svnwrap_temp') out, err = None, None try: # hot tip: the swig bridge doesn't like StringIO for these bad boys out_path = os.path.join(tmpdir, 'diffout') error_path = os.path.join(tmpdir, 'differr') out = open(out_path, 'w') err = open(error_path, 'w') try: client.diff3([], url2, optrev(other_rev), url, optrev(revision), True, True, deleted, ignore_type, 'UTF-8', out, err, self.client_context, self.pool) except SubversionException, e: # "Can't write to stream: The handle is invalid." # This error happens systematically under Windows, possibly # related to file handles being non-write shareable by default. if e.apr_err != 720006: raise self.hasdiff3 = False raise common.SubversionRepoCanNotDiff() out.close() err.close() out, err = None, None assert len(open(error_path).read()) == 0 diff = open(out_path).read() return diff finally: if out: out.close() if err: err.close() shutil.rmtree(tmpdir) os.chdir(old_cwd) def get_file(self, path, revision): """Return content and mode of file at given path and revision. "link " prefix is dropped from symlink content. Mode is 'x' if file is executable, 'l' if a symlink, the empty string otherwise. If the file does not exist at this revision, raise IOError. """ assert not path.startswith('/') mode = '' try: out = common.SimpleStringIO() info = ra.get_file(self.ra, path, revision, out) data = out.getvalue() out.close() if isinstance(info, list): info = info[-1] mode = ("svn:executable" in info) and 'x' or '' mode = ("svn:special" in info) and 'l' or mode except SubversionException, e: notfound = (core.SVN_ERR_FS_NOT_FOUND, core.SVN_ERR_RA_DAV_PATH_NOT_FOUND) if e.args[1] in notfound: # File not found raise IOError(errno.ENOENT, e.args[0]) raise if mode == 'l': linkprefix = "link " if data.startswith(linkprefix): data = data[len(linkprefix):] return data, mode def list_props(self, path, revision): """Return a mapping of property names to values, raise IOError if specified path does not exist. """ self.init_ra_and_client() rev = optrev(revision) rpath = self.path2url(path) try: pl = client.proplist2(rpath, rev, rev, False, self.client_context, self.pool) except SubversionException, e: # Specified path does not exist at this revision if e.apr_err == core.SVN_ERR_NODE_UNKNOWN_KIND: raise IOError(errno.ENOENT, e.args[0]) raise if not pl: return {} return pl[0][1] def list_files(self, dirpath, revision): """List the content of a directory at a given revision, recursively. Yield tuples (path, kind) where 'path' is the entry path relatively to 'dirpath' and 'kind' is 'f' if the entry is a file, 'd' if it is a directory. Raise IOError if the directory cannot be found at given revision. """ rpath = self.path2url(dirpath) pool = core.Pool() rev = optrev(revision) try: entries = client.ls(rpath, rev, True, self.client_context, pool) except SubversionException, e: if e.apr_err == core.SVN_ERR_FS_NOT_FOUND: raise IOError(errno.ENOENT, '%s cannot be found at r%d' % (dirpath, revision)) raise for path, e in entries.iteritems(): kind = _svntypes.get(e.kind) yield path, kind def checkpath(self, path, revision): """Return the entry type at the given revision, 'f', 'd' or None if the entry does not exist. """ kind = ra.check_path(self.ra, path.strip('/'), revision) return _svntypes.get(kind) def path2url(self, path): """Build svn URL for path, URL-escaping path. """ if not path or path == '.': return self.svn_url assert path[0] != '/', path path = path.rstrip('/') try: # new in svn 1.7 return core.svn_uri_canonicalize(self.svn_url + '/' + path) except AttributeError: return self.svn_url + '/' + urllib.quote(path) durin42-hgsubversion-77b22e5b4ea6/hgsubversion/util.py0000644000000000000000000002605512043462603021101 0ustar 00000000000000import errno import re import os import urllib from collections import deque from mercurial import cmdutil from mercurial import error from mercurial import hg from mercurial import node from mercurial import util as hgutil try: from mercurial import revset except ImportError: pass import maps ignoredfiles = set(['.hgtags', '.hgsvnexternals', '.hgsub', '.hgsubstate']) b_re = re.compile(r'^\+\+\+ b\/([^\n]*)', re.MULTILINE) a_re = re.compile(r'^--- a\/([^\n]*)', re.MULTILINE) devnull_re = re.compile(r'^([-+]{3}) /dev/null', re.MULTILINE) header_re = re.compile(r'^diff --git .* b\/(.*)', re.MULTILINE) newfile_devnull_re = re.compile(r'^--- /dev/null\n\+\+\+ b/([^\n]*)', re.MULTILINE) def formatrev(rev): if rev == -1: return '\t(working copy)' return '\t(revision %d)' % rev def filterdiff(diff, oldrev, newrev): diff = newfile_devnull_re.sub(r'--- \1\t(revision 0)' '\n' r'+++ \1\t(working copy)', diff) oldrev = formatrev(oldrev) newrev = formatrev(newrev) diff = a_re.sub(r'--- \1' + oldrev, diff) diff = b_re.sub(r'+++ \1' + newrev, diff) diff = devnull_re.sub(r'\1 /dev/null\t(working copy)', diff) diff = header_re.sub(r'Index: \1' + '\n' + ('=' * 67), diff) return diff def parentrev(ui, repo, meta, hashes): """Find the svn parent revision of the repo's dirstate. """ workingctx = repo.parents()[0] outrev = outgoing_revisions(repo, hashes, workingctx.node()) if outrev: workingctx = repo[outrev[-1]].parents()[0] return workingctx def islocalrepo(url): path = str(url) # convert once up front if path.startswith('file:///'): prefixlen = len('file://') elif path.startswith('file:/'): prefixlen = len('file:') else: return False if '#' in path.split('/')[-1]: # strip off #anchor path = path[:path.rfind('#')] path = url[prefixlen:] path = urllib.url2pathname(path).replace(os.sep, '/') while '/' in path: if reduce(lambda x, y: x and y, map(lambda p: os.path.exists(os.path.join(path, p)), ('hooks', 'format', 'db',))): return True path = path.rsplit('/', 1)[0] return False def version(ui): """Return version information if available.""" try: import __version__ return __version__.version except ImportError: try: dn = os.path.dirname repo = hg.repository(ui, dn(dn(__file__))) ver = repo.dirstate.parents()[0] return node.hex(ver)[:12] except: return 'unknown' def normalize_url(url): if not url: return url if url.startswith('svn+http://') or url.startswith('svn+https://'): url = url[4:] url, revs, checkout = parseurl(url) url = url.rstrip('/') if checkout: url = '%s#%s' % (url, checkout) return url def load_string(file_path, default=None, limit=1024): if not os.path.exists(file_path): return default try: f = open(file_path, 'r') ret = f.read(limit) f.close() except: return default if ret == '': return default return ret def save_string(file_path, string): if string is None: string = "" f = open(file_path, 'wb') f.write(str(string)) f.close() # TODO remove when we drop 1.3 support def progress(ui, *args, **kwargs): if getattr(ui, 'progress', False): return ui.progress(*args, **kwargs) # TODO remove when we drop 1.5 support remoteui = getattr(cmdutil, 'remoteui', getattr(hg, 'remoteui', False)) if not remoteui: raise ImportError('Failed to import remoteui') def parseurl(url, heads=[]): parsed = hg.parseurl(url, heads) if len(parsed) == 3: # old hg, remove when we can be 1.5-only svn_url, heads, checkout = parsed else: svn_url, heads = parsed if isinstance(heads, tuple) and len(heads) == 2: # hg 1.6 or later _junk, heads = heads if heads: checkout = heads[0] else: checkout = None return svn_url, heads, checkout class PrefixMatch(object): def __init__(self, prefix): self.p = prefix def files(self): return [] def __call__(self, fn): return fn.startswith(self.p) def outgoing_revisions(repo, reverse_map, sourcerev): """Given a repo and an hg_editor, determines outgoing revisions for the current working copy state. """ outgoing_rev_hashes = [] if sourcerev in reverse_map: return sourcerev = repo[sourcerev] while (not sourcerev.node() in reverse_map and sourcerev.node() != node.nullid): outgoing_rev_hashes.append(sourcerev.node()) sourcerev = sourcerev.parents() if len(sourcerev) != 1: raise hgutil.Abort("Sorry, can't find svn parent of a merge revision.") sourcerev = sourcerev[0] if sourcerev.node() != node.nullid: return outgoing_rev_hashes def outgoing_common_and_heads(repo, reverse_map, sourcerev): """Given a repo and an hg_editor, determines outgoing revisions for the current working copy state. Returns a tuple (common, heads) like discovery.findcommonoutgoing does. """ if sourcerev in reverse_map: return ([sourcerev], [sourcerev]) # nothing outgoing sourcecx = repo[sourcerev] while (not sourcecx.node() in reverse_map and sourcecx.node() != node.nullid): ps = sourcecx.parents() if len(ps) != 1: raise hgutil.Abort("Sorry, can't find svn parent of a merge revision.") sourcecx = ps[0] if sourcecx.node() != node.nullid: return ([sourcecx.node()], [sourcerev]) return ([sourcerev], [sourcerev]) # nothing outgoing def default_commit_msg(ui): return ui.config('hgsubversion', 'defaultmessage', '') def describe_commit(ui, h, b): ui.note(' committed to "%s" as %s\n' % ((b or 'default'), node.short(h))) def swap_out_encoding(new_encoding="UTF-8"): from mercurial import encoding old = encoding.encoding encoding.encoding = new_encoding return old def isancestor(ctx, ancestorctx): """Return True if ancestorctx is equal or an ancestor of ctx.""" if ctx == ancestorctx: return True for actx in ctx.ancestors(): if actx == ancestorctx: return True return False def issamefile(parentctx, childctx, f): """Return True if f exists and is the same in childctx and parentctx""" if f not in parentctx or f not in childctx: return False if parentctx == childctx: return True if parentctx.rev() > childctx.rev(): parentctx, childctx = childctx, parentctx def selfandancestors(selfctx): yield selfctx for ctx in selfctx.ancestors(): yield ctx for pctx in selfandancestors(childctx): if pctx.rev() <= parentctx.rev(): return True if f in pctx.files(): return False # parentctx is not an ancestor of childctx, files are unrelated return False def getsvnrev(ctx, defval=None): '''Extract SVN revision from commit metadata''' return ctx.extra().get('convert_revision', defval) def _templatehelper(ctx, kw): ''' Helper function for displaying information about converted changesets. ''' convertinfo = getsvnrev(ctx, '') if not convertinfo or not convertinfo.startswith('svn:'): return '' if kw == 'svnuuid': return convertinfo[4:40] elif kw == 'svnpath': return convertinfo[40:].rsplit('@', 1)[0] elif kw == 'svnrev': return convertinfo[40:].rsplit('@', 1)[-1] else: raise hgutil.Abort('unrecognized hgsubversion keyword %s' % kw) def svnrevkw(**args): """:svnrev: String. Converted subversion revision number.""" return _templatehelper(args['ctx'], 'svnrev') def svnpathkw(**args): """:svnpath: String. Converted subversion revision project path.""" return _templatehelper(args['ctx'], 'svnpath') def svnuuidkw(**args): """:svnuuid: String. Converted subversion revision repository identifier.""" return _templatehelper(args['ctx'], 'svnuuid') templatekeywords = { 'svnrev': svnrevkw, 'svnpath': svnpathkw, 'svnuuid': svnuuidkw, } def revset_fromsvn(repo, subset, x): '''``fromsvn()`` Select changesets that originate from Subversion. ''' args = revset.getargs(x, 0, 0, "fromsvn takes no arguments") rev = repo.changelog.rev bin = node.bin try: svnrevs = set(rev(bin(l.split(' ', 2)[1])) for l in maps.RevMap.readmapfile(repo, missingok=False)) return filter(svnrevs.__contains__, subset) except IOError, err: if err.errno != errno.ENOENT: raise raise hgutil.Abort("svn metadata is missing - " "run 'hg svn rebuildmeta' to reconstruct it") def revset_svnrev(repo, subset, x): '''``svnrev(number)`` Select changesets that originate in the given Subversion revision. ''' args = revset.getargs(x, 1, 1, "svnrev takes one argument") rev = revset.getstring(args[0], "the argument to svnrev() must be a number") try: revnum = int(rev) except ValueError: raise error.ParseError("the argument to svnrev() must be a number") rev = rev + ' ' revs = [] try: for l in maps.RevMap.readmapfile(repo, missingok=False): if l.startswith(rev): n = l.split(' ', 2)[1] r = repo[node.bin(n)].rev() if r in subset: revs.append(r) return revs except IOError, err: if err.errno != errno.ENOENT: raise raise hgutil.Abort("svn metadata is missing - " "run 'hg svn rebuildmeta' to reconstruct it") revsets = { 'fromsvn': revset_fromsvn, 'svnrev': revset_svnrev, } def getfilestoresize(ui): """Return the replay or stupid file memory store size in megabytes or -1""" size = ui.configint('hgsubversion', 'filestoresize', 200) if size >= 0: size = size*(2**20) else: size = -1 return size # Copy-paste from mercurial.util to avoid having to deal with backward # compatibility, plus the cache size is configurable. def lrucachefunc(func, size): '''cache most recent results of function calls''' cache = {} order = deque() if func.func_code.co_argcount == 1: def f(arg): if arg not in cache: if len(cache) > size: del cache[order.popleft()] cache[arg] = func(arg) else: order.remove(arg) order.append(arg) return cache[arg] else: def f(*args): if args not in cache: if len(cache) > size: del cache[order.popleft()] cache[args] = func(*args) else: order.remove(args) order.append(args) return cache[args] return f durin42-hgsubversion-77b22e5b4ea6/hgsubversion/verify.py0000644000000000000000000001537412043462603021432 0ustar 00000000000000import posixpath from mercurial import util as hgutil from mercurial import error import svnwrap import svnrepo import util import editor def verify(ui, repo, args=None, **opts): '''verify current revision against Subversion repository ''' if repo is None: raise error.RepoError("There is no Mercurial repository" " here (.hg not found)") ctx = repo[opts.get('rev', '.')] if 'close' in ctx.extra(): ui.write('cannot verify closed branch') return 0 convert_revision = ctx.extra().get('convert_revision') if convert_revision is None or not convert_revision.startswith('svn:'): raise hgutil.Abort('revision %s not from SVN' % ctx) if args: url = repo.ui.expandpath(args[0]) else: url = repo.ui.expandpath('default') svn = svnrepo.svnremoterepo(ui, url).svn meta = repo.svnmeta(svn.uuid, svn.subdir) 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 %s@%i\n' % (ctx, branchurl, srev)) if opts.get('stupid', ui.configbool('hgsubversion', 'stupid')): svnfiles = set() result = 0 hgfiles = set(ctx) - util.ignoredfiles svndata = svn.list_files(branchpath, srev) for i, (fn, type) in enumerate(svndata): util.progress(ui, 'verify', i, total=len(hgfiles)) if type != 'f': continue svnfiles.add(fn) fp = fn if branchpath: fp = branchpath + '/' + fn data, mode = svn.get_file(posixpath.normpath(fp), srev) try: fctx = ctx[fn] except error.LookupError: result = 1 continue if not fctx.data() == data: ui.write('difference in: %s\n' % fn) result = 1 if not fctx.flags() == mode: ui.write('wrong flags for: %s\n' % fn) result = 1 if hgfiles != svnfiles: unexpected = hgfiles - svnfiles for f in sorted(unexpected): ui.write('unexpected file: %s\n' % f) missing = svnfiles - hgfiles for f in sorted(missing): ui.write('missing file: %s\n' % f) result = 1 util.progress(ui, 'verify', None, total=len(hgfiles)) else: class VerifyEditor(svnwrap.Editor): """editor that verifies a repository against the given context.""" def __init__(self, ui, ctx): self.ui = ui self.ctx = ctx self.unexpected = set(ctx) - util.ignoredfiles self.missing = set() self.failed = False self.total = len(self.unexpected) self.seen = 0 def open_root(self, base_revnum, pool=None): pass def add_directory(self, path, parent_baton, copyfrom_path, copyfrom_revision, pool=None): self.file = None self.props = None def open_directory(self, path, parent_baton, base_revision, pool=None): self.file = None self.props = None def add_file(self, path, parent_baton=None, copyfrom_path=None, copyfrom_revision=None, file_pool=None): if path in self.unexpected: self.unexpected.remove(path) self.file = path self.props = {} else: self.total += 1 self.missing.add(path) self.failed = True self.file = None self.props = None self.seen += 1 util.progress(self.ui, 'verify', self.seen, total=self.total) def open_file(self, path, base_revnum): raise NotImplementedError() def apply_textdelta(self, file_baton, base_checksum, pool=None): stream = svnwrap.SimpleStringIO(closing=False) handler = svnwrap.apply_txdelta('', stream) if not callable(handler): raise hgutil.Abort('Error in Subversion bindings: ' 'cannot call handler!') def txdelt_window(window): handler(window) # window being None means we're done if window: return fctx = self.ctx[self.file] hgdata = fctx.data() svndata = stream.getvalue() if 'svn:executable' in self.props: if fctx.flags() != 'x': self.ui.warn('wrong flags for: %s\n' % self.file) self.failed = True elif 'svn:special' in self.props: hgdata = 'link ' + hgdata if fctx.flags() != 'l': self.ui.warn('wrong flags for: %s\n' % self.file) self.failed = True elif fctx.flags(): self.ui.warn('wrong flags for: %s\n' % self.file) self.failed = True if hgdata != svndata: self.ui.warn('difference in: %s\n' % self.file) self.failed = True if self.file is not None: return txdelt_window def change_dir_prop(self, dir_baton, name, value, pool=None): pass def change_file_prop(self, file_baton, name, value, pool=None): if self.props is not None: self.props[name] = value def close_file(self, file_baton, checksum, pool=None): pass def close_directory(self, dir_baton, pool=None): pass def delete_entry(self, path, revnum, pool=None): raise NotImplementedError() def check(self): util.progress(self.ui, 'verify', None, total=self.total) for f in self.unexpected: self.ui.warn('unexpected file: %s\n' % f) self.failed = True for f in self.missing: self.ui.warn('missing file: %s\n' % f) self.failed = True return not self.failed v = VerifyEditor(ui, ctx) svnrepo.svnremoterepo(ui, branchurl).svn.get_revision(srev, v) if v.check(): result = 0 else: result = 1 return result durin42-hgsubversion-77b22e5b4ea6/hgsubversion/wrappers.py0000644000000000000000000005452512043462603021772 0ustar 00000000000000from 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 from mercurial import node from mercurial import i18n from mercurial import extensions from mercurial import repair import replay import pushmod import stupid as stupidmod import svnwrap import svnrepo import util try: from mercurial import scmutil revpair = scmutil.revpair except ImportError: revpair = cmdutil.revpair pullfuns = { True: replay.convert_rev, False: stupidmod.convert_rev, } revmeta = [ ('revision', 'revnum'), ('user', 'author'), ('date', 'date'), ('message', 'message'), ] def version(orig, ui, *args, **opts): svn = opts.pop('svn', None) orig(ui, *args, **opts) if svn: svnversion, bindings = svnwrap.version() ui.status('\n') ui.status('hgsubversion: %s\n' % util.version(ui)) ui.status('Subversion: %s\n' % svnversion) ui.status('bindings: %s\n' % bindings) def parents(orig, ui, repo, *args, **opts): """show Mercurial & Subversion parents of the working dir or revision """ if not opts.get('svn', False): return orig(ui, repo, *args, **opts) meta = repo.svnmeta() hashes = meta.revmap.hashes() ha = util.parentrev(ui, repo, meta, hashes) if ha.node() == node.nullid: raise hgutil.Abort('No parent svn revision!') displayer = cmdutil.show_changeset(ui, repo, opts, buffered=False) displayer.show(ha) return 0 def getpeer(ui, opts, source): # Since 2.3 (1ac628cd7113) peer = getattr(hg, 'peer', None) if peer: return peer(ui, opts, source) return hg.repository(ui, source) def getlocalpeer(ui, opts, source): peer = getpeer(ui, opts, source) repo = getattr(peer, 'local', lambda: peer)() if isinstance(repo, bool): repo = peer return repo def getcaps(other): return (getattr(other, 'caps', None) or getattr(other, 'capabilities', None) or set()) def incoming(orig, ui, repo, origsource='default', **opts): """show incoming revisions from Subversion """ source, revs, checkout = util.parseurl(ui.expandpath(origsource)) other = getpeer(ui, opts, source) if 'subversion' not in getcaps(other): return orig(ui, repo, origsource, **opts) svn = other.svn meta = repo.svnmeta(svn.uuid, svn.subdir) ui.status('incoming changes from %s\n' % other.svnurl) for r in svn.revisions(start=meta.revmap.youngest): ui.status('\n') for label, attr in revmeta: l1 = label + ':' val = str(getattr(r, attr)).strip() if not ui.verbose: val = val.split('\n')[0] ui.status('%s%s\n' % (l1.ljust(13), val)) def findcommonoutgoing(repo, other, onlyheads=None, force=False, commoninc=None): assert other.capable('subversion') # split off #rev; TODO implement --revision/#rev support svn = other.svn meta = repo.svnmeta(svn.uuid, svn.subdir) parent = repo.parents()[0].node() hashes = meta.revmap.hashes() 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): """show changesets not found in the Subversion repository """ assert dest.capable('subversion') # split off #rev; TODO implement --revision/#rev support # svnurl, revs, checkout = util.parseurl(dest.svnurl, heads) svn = dest.svn meta = repo.svnmeta(svn.uuid, svn.subdir) parent = repo.parents()[0].node() hashes = meta.revmap.hashes() return util.outgoing_revisions(repo, hashes, parent) def diff(orig, ui, repo, *args, **opts): """show a diff of the most recent revision against its parent from svn """ if not opts.get('svn', False) or opts.get('change', None): return orig(ui, repo, *args, **opts) meta = repo.svnmeta() hashes = meta.revmap.hashes() if not opts.get('rev', None): parent = repo.parents()[0] o_r = util.outgoing_revisions(repo, hashes, parent.node()) if o_r: parent = repo[o_r[-1]].parents()[0] opts['rev'] = ['%s:.' % node.hex(parent.node()), ] node1, node2 = revpair(repo, opts['rev']) baserev, _junk = hashes.get(node1, (-1, 'junk')) newrev, _junk = hashes.get(node2, (-1, 'junk')) it = patch.diff(repo, node1, node2, opts=patch.diffopts(ui, opts={'git': True, 'show_function': False, 'ignore_all_space': False, 'ignore_space_change': False, 'ignore_blank_lines': False, 'unified': True, 'text': False, })) ui.write(util.filterdiff(''.join(it), baserev, newrev)) def push(repo, dest, force, revs): """push revisions starting at a specified head back to Subversion. """ assert not revs, 'designated revisions for push remains unimplemented.' if hasattr(cmdutil, 'bail_if_changed'): cmdutil.bail_if_changed(repo) else: # Since 1.9 (d68ddccf276b) cmdutil.bailifchanged(repo) checkpush = getattr(repo, 'checkpush', None) if checkpush: checkpush(force, revs) ui = repo.ui old_encoding = util.swap_out_encoding() try: # TODO: implement --rev/#rev support # TODO: do credentials specified in the URL still work? svn = dest.svn meta = repo.svnmeta(svn.uuid, svn.subdir) # Strategy: # 1. Find all outgoing commits from this head if len(repo.parents()) != 1: ui.status('Cowardly refusing to push branch merge\n') return 0 # results in nonzero exit status, see hg's commands.py workingrev = repo.parents()[0] ui.status('searching for changes\n') hashes = meta.revmap.hashes() outgoing = util.outgoing_revisions(repo, hashes, workingrev.node()) to_strip=[] if not (outgoing and len(outgoing)): ui.status('no changes found\n') return 1 # so we get a sane exit status, see hg's commands.push while outgoing: # 2. Commit oldest revision that needs to be pushed oldest = outgoing.pop(-1) old_ctx = repo[oldest] old_pars = old_ctx.parents() if len(old_pars) != 1: ui.status('Found a branch merge, this needs discussion and ' 'implementation.\n') # results in nonzero exit status, see hg's commands.py return 0 # We will commit to svn against this node's parent rev. Any # file-level conflicts here will result in an error reported # by svn. base_ctx = old_pars[0] base_revision = hashes[base_ctx.node()][0] svnbranch = base_ctx.branch() # Find most recent svn commit we have on this branch. This # node will become the nearest known ancestor of the pushed # rev. oldtipctx = base_ctx old_children = oldtipctx.descendants() seen = set(c.node() for c in old_children) samebranchchildren = [c for c in old_children if c.branch() == svnbranch and c.node() in hashes] if samebranchchildren: # The following relies on descendants being sorted by rev. oldtipctx = samebranchchildren[-1] # All set, so commit now. try: pushmod.commit(ui, repo, old_ctx, meta, base_revision, svn) except pushmod.NoFilesException: ui.warn("Could not push revision %s because it had no changes " "in svn.\n" % old_ctx) return 1 # 3. Fetch revisions from svn # TODO: this probably should pass in the source explicitly - # rev too? r = repo.pull(dest, force=force) assert not r or r == 0 # 4. Find the new head of the target branch # We expect to get our own new commit back, but we might # also get other commits that happened since our last pull, # or even right after our own commit (race). for c in oldtipctx.descendants(): if c.node() not in seen and c.branch() == svnbranch: newtipctx = c # 5. Rebase all children of the currently-pushing rev to the # new head # # there may be commits descended from the one we just # pushed to svn that we aren't going to push to svn in # this operation oldhex = node.hex(old_ctx.node()) needs_rebase_set = "%s:: and not(%s)" % (oldhex, oldhex) def extrafn(ctx, extra): extra['branch'] = ctx.branch() util.swap_out_encoding(old_encoding) try: hgrebase.rebase(ui, repo, dest=node.hex(newtipctx.node()), rev=[needs_rebase_set], extrafn=extrafn, # We actually want to strip one more rev than # we're rebasing keep=True) finally: util.swap_out_encoding() to_strip.append(old_ctx.node()) # don't trust the pre-rebase repo. Do not reuse # contexts across this. newtip = newtipctx.node() repo = getlocalpeer(ui, {}, meta.path) newtipctx = repo[newtip] rebasemap = dict() for child in newtipctx.descendants(): rebasesrc = child.extra().get('rebase_source') if rebasesrc: rebasemap[node.bin(rebasesrc)] = child.node() outgoing = [rebasemap.get(n) or n for n in outgoing] meta = repo.svnmeta(svn.uuid, svn.subdir) hashes = meta.revmap.hashes() util.swap_out_encoding(old_encoding) try: hg.update(repo, repo['tip'].node()) finally: util.swap_out_encoding() repair.strip(ui, repo, to_strip, "all") finally: util.swap_out_encoding(old_encoding) return 1 # so we get a sane exit status, see hg's commands.push def pull(repo, source, heads=[], force=False): """pull new revisions from Subversion""" assert source.capable('subversion') svn_url = source.svnurl # Split off #rev svn_url, heads, checkout = util.parseurl(svn_url, heads) old_encoding = util.swap_out_encoding() total = None try: try: stopat_rev = int(checkout or 0) except ValueError: raise hgutil.Abort('unrecognised Subversion revision %s: ' 'only numbers work.' % checkout) have_replay = not repo.ui.configbool('hgsubversion', 'stupid') if not have_replay: repo.ui.note('fetching stupidly...\n') svn = source.svn meta = repo.svnmeta(svn.uuid, svn.subdir) layout = repo.ui.config('hgsubversion', 'layout', 'auto') if layout == 'auto': try: rootlist = svn.list_dir('', revision=(stopat_rev or None)) except svnwrap.SubversionException, e: err = "%s (subversion error: %d)" % (e.args[0], e.args[1]) raise hgutil.Abort(err) if sum(map(lambda x: x in rootlist, ('branches', 'tags', 'trunk'))): layout = 'standard' else: layout = 'single' repo.ui.setconfig('hgsubversion', 'layout', layout) repo.ui.note('using %s layout\n' % layout) branch = repo.ui.config('hgsubversion', 'branch') if branch: if layout != 'single': msg = ('branch cannot be specified for Subversion clones using ' 'standard directory layout') raise hgutil.Abort(msg) meta.branchmap['default'] = branch ui = repo.ui start = meta.revmap.youngest origrevcount = len(meta.revmap) if start <= 0: # we are initializing a new repository start = repo.ui.config('hgsubversion', 'startrev', 0) if isinstance(start, str) and start.upper() == 'HEAD': start = svn.last_changed_rev else: start = int(start) if start > 0: if layout == 'standard': raise hgutil.Abort('non-zero start revisions are only ' 'supported for single-directory clones.') ui.note('starting at revision %d; any prior will be ignored\n' % start) # fetch all revisions *including* the one specified... start -= 1 # anything less than zero makes no sense if start < 0: start = 0 skiprevs = repo.ui.configlist('hgsubversion', 'unsafeskip', '') try: skiprevs = set(map(int, skiprevs)) except ValueError: raise hgutil.Abort('unrecognised Subversion revisions %r: ' 'only numbers work.' % checkout) oldrevisions = len(meta.revmap) if stopat_rev: total = stopat_rev - start else: total = svn.HEAD - start lastpulled = None try: # start converting revisions firstrun = True for r in svn.revisions(start=start, stop=stopat_rev): if r.revnum in skiprevs: ui.status('[r%d SKIPPED]\n' % r.revnum) continue lastpulled = r.revnum if (r.author is None and r.message == 'This is an empty revision for padding.'): continue tbdelta = meta.update_branch_tag_map_for_rev(r) # got a 502? Try more than once! tries = 0 converted = False while not converted: try: msg = '' if r.message: msg = r.message.strip() if not msg: msg = util.default_commit_msg(ui) else: msg = [s.strip() for s in msg.splitlines() if s][0] if getattr(ui, 'termwidth', False): w = ui.termwidth() else: w = hgutil.termwidth() bits = (r.revnum, r.author, msg) ui.status(('[r%d] %s: %s' % bits)[:w] + '\n') util.progress(ui, 'pull', r.revnum - start, total=total) meta.save_tbdelta(tbdelta) close = pullfuns[have_replay](ui, meta, svn, r, tbdelta, firstrun) meta.committags(r, close) for branch, parent in close.iteritems(): if parent in (None, node.nullid): continue meta.delbranch(branch, parent, r) meta.save() converted = True firstrun = False 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 if (e.args[1] == svnwrap.ERR_RA_DAV_REQUEST_FAILED and '502' in str(e) and tries < 3): tries += 1 ui.status('Got a 502, retrying (%s)\n' % tries) else: ui.traceback() raise hgutil.Abort(*e.args) except KeyboardInterrupt: ui.traceback() finally: if total is not None: util.progress(ui, 'pull', None, total=total) util.swap_out_encoding(old_encoding) if lastpulled is not None: meta.revmap.youngest = lastpulled revisions = len(meta.revmap) - oldrevisions if revisions == 0: ui.status(i18n._("no changes found\n")) return 0 else: ui.status("pulled %d revisions\n" % revisions) def rebase(orig, ui, repo, **opts): """rebase current unpushed revisions onto the Subversion head This moves a line of development from making its own head to the top of Subversion development, linearizing the changes. In order to make sure you rebase on top of the current top of Subversion work, you should probably run 'hg svn pull' before running this. Also looks for svnextrafn and svnsourcerev in **opts. """ if not opts.get('svn', False): return orig(ui, repo, **opts) def extrafn2(ctx, extra): """defined here so we can add things easily. """ extra['branch'] = ctx.branch() extrafn = opts.get('svnextrafn', extrafn2) sourcerev = opts.get('svnsourcerev', repo.parents()[0].node()) meta = repo.svnmeta() hashes = meta.revmap.hashes() o_r = util.outgoing_revisions(repo, hashes, sourcerev=sourcerev) if not o_r: ui.status('Nothing to rebase!\n') return 0 if len(repo[sourcerev].children()): ui.status('Refusing to rebase non-head commit like a coward\n') return 0 parent_rev = repo[o_r[-1]].parents()[0] target_rev = parent_rev p_n = parent_rev.node() exhausted_choices = False while target_rev.children() and not exhausted_choices: for c in target_rev.children(): exhausted_choices = True n = c.node() if (n in hashes and hashes[n][1] == hashes[p_n][1]): target_rev = c exhausted_choices = False break if parent_rev == target_rev: ui.status('Already up to date!\n') return 0 return orig(ui, repo, dest=node.hex(target_rev.node()), base=node.hex(sourcerev), extrafn=extrafn) optionmap = { 'tagpaths': ('hgsubversion', 'tagpaths'), 'authors': ('hgsubversion', 'authormap'), 'filemap': ('hgsubversion', 'filemap'), 'branchmap': ('hgsubversion', 'branchmap'), 'tagmap': ('hgsubversion', 'tagmap'), 'stupid': ('hgsubversion', 'stupid'), 'defaulthost': ('hgsubversion', 'defaulthost'), 'defaultauthors': ('hgsubversion', 'defaultauthors'), 'usebranchnames': ('hgsubversion', 'usebranchnames'), 'layout': ('hgsubversion', 'layout'), 'startrev': ('hgsubversion', 'startrev'), } dontretain = { 'hgsubversion': set(['authormap', 'filemap', 'layout', ]) } def clone(orig, ui, source, dest=None, **opts): """ Some of the options listed below only apply to Subversion %(target)s. See 'hg help %(extension)s' for more information on them as well as other ways of customising the conversion process. """ data = {} def hgclonewrapper(orig, ui, *args, **opts): if getattr(hg, 'peer', None): # Since 1.9 (d976542986d2) origsource = args[1] else: origsource = args[0] if isinstance(origsource, str): source, branch, checkout = util.parseurl(ui.expandpath(origsource), opts.get('branch')) srcrepo = getpeer(ui, opts, source) else: srcrepo = origsource if srcrepo.capable('subversion'): branches = opts.pop('branch', None) if branches: data['branches'] = branches ui.setconfig('hgsubversion', 'branch', branches[-1]) 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))) # calling hg.clone directoly to get the repository instances it returns, # breaks in subtle ways, so we double-wrap orighgclone = extensions.wrapfunction(hg, 'clone', hgclonewrapper) orig(ui, source, dest, **opts) hg.clone = orighgclone # do this again; the ui instance isn't shared between the wrappers if data.get('branches'): ui.setconfig('hgsubversion', 'branch', data['branches'][-1]) dstrepo = data.get('dstrepo') srcrepo = data.get('srcrepo') if dstrepo.local() and srcrepo.capable('subversion'): dst = dstrepo.local() if isinstance(dst, bool): # Apparently <= hg@1.9 fd = dstrepo.opener("hgrc", "a", text=True) else: fd = dst.opener("hgrc", "a", text=True) for section in set(s for s, v in optionmap.itervalues()): config = dict(ui.configitems(section)) for name in dontretain[section]: config.pop(name, None) if config: fd.write('\n[%s]\n' % section) map(fd.write, ('%s = %s\n' % p for p in config.iteritems())) def generic(orig, ui, repo, *args, **opts): """ Subversion %(target)s can be used for %(command)s. See 'hg help %(extension)s' for more on the conversion process. """ branch = opts.get('branch', None) if branch: ui.setconfig('hgsubversion', 'branch', branch[-1]) for opt, (section, name) in optionmap.iteritems(): if opt in opts and opts[opt]: if isinstance(repo, str): ui.setconfig(section, name, opts.pop(opt)) else: repo.ui.setconfig(section, name, opts.pop(opt)) return orig(ui, repo, *args, **opts) durin42-hgsubversion-77b22e5b4ea6/notes/hgsubversion.png0000644000000000000000000025406112043462603021410 0ustar 00000000000000‰PNG  IHDR+ƒsBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î<W®IDATxÚìÝ|TÕúïÿ‹‚EìB UšôÞE¤QA¤JïéŠ@z%¤IHÄα÷Þ{/ÿSôxŽëÏ³Ï —@ffï=mÏÌ'¯×ûõ»÷H&möÞëù®µžõ”Rÿø7~ àÛÀŠƒn½èvà3n5ür‘>ã €@ €€@ @@ € €@ €€~q@ Þ°*ì‚Zy^mŠ>§¶ÇS¡ gÕÞCgÔ>=’þŸgMØ“xFíŒ?«¶ÆžS£Î«µçÕJþ&`¬—Â<,ù´ŠN;­â3N©¤c'UZÖIu,§HÏ+T'žSÏ=g=y…*ûâ÷wôx‘J½øý&=©}ÿQùy$<Ÿo{ÜYµùâÏ»þâϽ:œ÷€øpQÿLÔ9m–üÀá3ZìK…¼§¸ø{ÈÍ/T™¹E*#»H¾ø{ŠI;¥ö'Q;âΪ Q¬0/Yq^[/Ëî#RN«Ä#§Tzöÿ {Šz÷„Y¹…*%ó¤ŠM?¥+°È„•a¼À™Yüèsj×ÿÅ—¢3ùØÿf·X8pQvn¡JÍ<©â.þ&ŸQ»Îj¿U„ d©þžÄ³*2õÿÍâçä3‹ïOdU†l½mÒ‹@VHãBÞÿü,ßsNë‚/³Ä²×<¿€â8É–‚CGÿ·@¶s°Z@€.ß—‚nÿÅÂ.!ã”Ö•þËö¡£ÏÀ‘‹ïÙî!½¤¿×€‹X~^;Vî`òim673§ˆb.=ÚPʶð`±/EXxÊií¨8iüF‘ ¶ðq«Ã/htÚim?Å'¬¾u 4áìÅ÷-«€€»¤Qß¶¸sÚrkéÆÏ¾}øj §ȶ9Šk¸hKÌ9íh69ý€‚~H¶ªÈɲš…í@@2#*{§e¿4X£@D ‘+9ó¤vBņH¶ ~DŠœ}‡ÎhMÓrò)øËÉ©ÒãBN²XÉê À×lŽ9§¢ROkÝÒ)ò}ò UÒÑ“Z`¶Žã€+ïå—™LŠ~O7œ;¡òóó5yyy*77W“““£Ž?®ÉÎÎÖdee©ÌÌLͱcÇÔÑ£G5GŽQšôôtü¿å¿É¿“ϓבה¯QPP }]~ÿî%' HCL9j{ `‰¢?›¢ßeżØRlK‘.…yZZšJNNV‡Rñññ*&&FEFFª°°0µÿ~õì³Ïz|ýhßKDD„ö}EGGkßc\\œJHHP‰‰‰*))I¥¦¦j?„ P s<¯P[U³1Š•@€‡ÈldŒýyýz‹z™9—™t+óÞpðàAíçÕ‚ùÝÈêù]I"«x]-#»H=›tF­ ' ÜRôŸÒf!)ÀJÙ»}±P•%ö²l^ŠX™ù–™p)ì­¨wÇ*Y] a‰ü^>¬ýžå÷è«äDé Ç ®ä>˜µíbÑ›NÑ_Ú,¾,i—%î2{-Kà)Ô½'<<\[AP ÈÊ@ ä„ ÙŽ³)š~@€²¿Xfús¸è—=øÒüNfò¥È—“BÛ÷ȪùûÉßQzR0 Í>£Ör’p99{|wÂY•šu2ðfMsr´âPfe‰¹ìI§xöÿíÒA‚YÑáÏ}NœxN>vR…^¼¾å:ç~€µ>ò¼vĘ,„%ü2, 椟ìÏgé>ŠÉVy_H$+@üñȽxË–žÍ1l@À†a2+x ~9fNŠ}i'Å] v؇yÒ¼QÞ;ò’àÈß®c9Eê`òi¶ÀÉ@?ìâ€?+·Ðo÷íK8)Úè¼wW(SRRTVV–ßôSâ2Ni½?¸OÀÇI'ÿCGOi};vOõɲmÙÓM‘ O÷m$Ò;BúøC «‚ä¸Oî›@ð!«ÃÏ«ýIg´e¾þ´‡_ -)¸¢¢¢(Ba¹@@N-¾ÞT0#»HkȽ¶!ò¼¶œ7¿À?Š~Yj-Úe¦•=üð%RÉv9eÂg¯¿ÜB-H\Åé@°Vá±ð?áËü333µeýÇ®’’’´@ËWOˆH¡a (ü]°´_ŠþÄÄDŠ~D3AiT)Ç Ð0°o½þÅûù)úè}äd´´4í ~Qøý2ûIÑ\-&&F;ÊÒ—NH§a \_øÇù`áyÑàÀŠ<@¹VduŒ/õ †ÏÒ0 ð ÿÜÜ\í¸>fúçDDDh§ øÊãy…jß¡3Ü¿ þé§´¦[¾TøËl¿ìi¦p\O®-iè [Žå©]ñl @@°iMøyëc…~~¾6C)GQ¤ž9I@ŽÊÌÎζüý!-«Hm‰¡Y @ ²lVÎÛö™=¿YY*>>^ëdNQxGdd¤JMMÕ‚8+ß/’Žžäø@€Í1çTFv‘oœ^P Y&û’)¾k)(ÜñãÇ-Üô9mkÓÚ‚ ËýOøBc¯‹E…t%§“?àÇ J?Ën*xNE¤œV«Ãy €½>²Ü?''G›U¤¨|s{@FF†e›æ\¼îO:£Vrt €?Ú}N¥ûÀr À„……i:e ï7™¹E*4üÄjYîOáø/Ù“””¤òòò,yÿ‘^(Ûb91ÀÇ—ûçX|¹?…?@Ã@«H>vR=M€àkËý³Š,_ø'$$P4 ´Ö‰ÅeœÒš¥ò<@°¬•E¦žÖ޼²j៛›Ká DÃ@+9y…ô@°¦ ‘çµ}¬–=~+?_;΂@i¢££UVV–åî]‡Të"Y €`»Ϫ¼ëîõOOOW¤ÈàPll¬åzÈýõÙ¤3 ¸Ðޏ³*'¿Ð’Ëýe‰®,Õ¥@à áááÚj#Ë y…jW>Þ½’TkØ€üOXòiKþrL–4È¢ˆÂÃõ­OÞ¾Ï+ÔV†ñœ@ÿîéH]P bcc)¼Ã‡k¨··„§œæy €|ÝÃg,Uüçååigd3ð€ÿ‰ŠŠ²DƒÀÔ¬“j][€OÚwÈZÅÿñãÇUXX~°hƒÀœüBµ#î,ÏPàKö$žµTñìØ1m€Ë@¬ß 02•-À'„&œÕötrÌøY)åŠÉÉɪ|ùòªråʪGjíÚµÚ6,½ŸŸtì¤ZÆ3XÖŽxkõ'3ÿÿàRøÿŸ‹CòbÕªUSsæÌѽÊàÈñ"ú +ÚwÎRÅff&Å?8!::Zåçç;ÕxµmÛ¶%BQ»vmµaÃÝGnŠæ¨@`[bΩü‚BËÿ²|•â\³%@š¨:Ó€µyóæW…¢M›6Úë;z y¾ìŒ§9 ðºÍÑçTž…Šÿììlþ€ I *ýTœY‘Õ´iÓRC€2eʨþýû«´´4»¯qâ"9Z–ç.@@^òLÔ9•›_h©£þ<È€ÜÔÀ™p¶uëÖ¥†â¦›nR›6mrø:±é§ÔJž¿xÖúÈóÚÞL«ÿ999ÚRRéà>±±±¦ ÌÍÍU;w¶Èj€Ñ£G;<- 9ó¤ZÎs XvAedY¦ø—A%Å?xFDD„ºš¹_KSÁ>}úØ D£FT\\œÝ×9šS¤Ñ<“p³¸ôS–)þe¦(<<œA9xôZ‘£VÍÜ·åxÁÁƒÛ *Uª¤/^låW~¡Ö„–ç2@@n²÷ÐËÿ2ˆŒŠŠb0^’ššjú.Ëýí…âž{îÑNv±õrülh'àrrsþ‰ç,$&&2/KHHÐY3÷ñ‰': ‚‚‚´Þö^',ù4Ïi€€\eMøy••k¦ ¼À"d5–£æ}¶<õÔSZ@{!@•*UThh¨Ý׉Ï8¥õ¨á™ €“¤ë²•Žû“s©t€uH3V³Í,X ®¹æ»!@ùòåÕªU«ì¾NjÖI-°æ¹ €I)§-SüKišþ€5|˜Á5øÙ¦uôèQS÷ú;wªë¯¿ÞaеkWmK˜­×ÉÎ-Ôžc<Ïp`UØ•‘e™9ošA5ø–ôôtS÷| ¤ñŸ£ I“&v¿Fv^¡ÚEð‹»¤›²UŠÿÜÜ\mI)ƒið=)))¦îý‘‘‘êæ›ovÔ¨QCÅÅÅÙn›W¨6€Òm;k™â_ÈñR ¢ÀwÉ.3÷ÿøøxuÇw8 n¹å»!@N~¡z&êÏx€påÒÿ#™9–)þÓÒÒ<€HLL4õHNNVÁÁÁN‡¹ù…jS4!@¸ä@¼uŠÿ¼¼<–þ€‘}{ÝûmÉÈÈP 4pÜzë­Ú×°l&Ô†ð“*??ß2€Ìä0`ÿ£ ŒŸ0#G 6oÞÜé ¯ Pm‰! ÀÅIJLñ/ÇG1Pÿ$½]ÌÎrì_»ví\l% P÷þo:`™â_f†ÂÂÂ$€Ÿ‡fVÈö°¶mÛ:äªm„š9ëâUff–e€C‡18€mª'€¬hÚ´©® !!ÁNðœÚG@€Xºï´Ú¶m§eŠÿììlÅ@bccM…GŽQuëÖuÜvÛmvC€‚Ï©qgàÿf,Xo™â_€‘‘‘ ˆ ÀHÓW3!@JJŠºóÎ;u…öŽ!”`gs6lØ Ê”)ã0 Q™™™vÂëçÔÎø³Œ-°®æwwQ‹-¶Lñ/bccÌ KHH0õÜyâ‰'t йsg»!C~ÁsjKÌ9ÆXÏ£sCÕ]MïR¹¹¹–)þe çþýûÈLq´Tß–{ï½WW0~üx»¯““_¨6Dgœ€µ´lÛM-X°ÀR³ÿýpÖÑ£G ?$ oÚ´©Ã@¶ ¬Y³Æîke媵„XÄcóöªzuë™êœìNÒ‰Á+À²’ìøñ㆟A©©©êÖ[ouTªTIEFFÚïgs¼H­g¼€´jßS~`´¥Š #¸\!,,ÌTƒ[ùܲeË: äÁcÇŽÙ}­”Ì“jec€¼èñ…ûU;k¨ÐÐPK‡fÐ pYUfæd€©S§êêо}{‡¯Ÿqб@ÞÓºcÕì®f¦Eî$ç83`¸R\\œáç‘<Û¶m«+x衇¾Þ¾¤3Œ?ð¼I‹Ã´ÙÿÉ“&[ªøÏÊÊb  p Yaf¦@ÕªUu…+W®´ûZ'8 /hÓ¹ŸÄÄÄX*HLLd pGûõK³aí뿣 bÅŠÚ*6{¯•W¨Ö„s2@2ei„VüwêØÉRÅ¿8xð T€ÛÈsÆLSÀ#FèZpçwª#GŽ8l Èx èÐ} Ì{zÝÿ'::Úð3*77WÕ«WOWЦM‡ýu"SO3&À½æmËVA5‚TÍ š*=-ÝR@zz:S€eûDFFªòåËë Fv|Äî®ø³ŒMpŸá.ÒfÿûßÛßrËÿ”<&33Óð³jΜ9º±lÙ2»¯•WP¨6DÑ 7iÖª“¬Y³Ær@XXR€Gûäçç~^uíÚUWP©R%k÷µŽ/R«ÂŸàbO­MÒŠÿzuëiûí­TüËÞJ£O3sŽ4ù»å–[t… 4pØt0áÈ)Æ)¸Vÿû§jÀˆá#,7ûŸ‘‘Á@à)))†Ÿ[Û·o×u4 9r¤Ã×{6é c€\cùó*¤a3-Xºd)ûÿ¸LVV–ág×СCu÷X»v­Ý×*8ñœÚsŽ1 @Î{|á~­øq±q– ÂÃÀ¼FúÐí Mo½õV]À7Þ¨’’’ì¾Þñ¼Bµ6‚¦€8©Gÿ1Zñ߸QcËÿ²7’Á'ÀÛ5ì+͆ t¯hÞ¼¹*((°ûz©Y'·`Þ’=E*¸v=-åŠV Ž=ÊÀ` ©©©†Ÿc}úôÑŒ7ÎáëE¥žfü€9OæÒòÿ X.ÁƒN€ìß¿_?~ÜÐs,==]U­ZUW ·mÛæð5w%œe €qí» ¸DEEY.8tèƒN€e˜yV.Y²D÷*€êÕ«k¡Ýíq…jcý0`ÞöTS+þCꇨ'NX.=— 8¾¾ cÇŽºC€öíÛ;|&§g1–ÀÀòÿ§6_šý8` åŠÁ``¹­¹¹¹†žgÒå¿R¥JºC€©S§:|Ͱdú SïÁ^ æÎkÉàÀ 6–cø™6sæLÝ@Ù²eÕÞ½{í¾^Á‰çÔ3ÑçÓàXÓæí.´\ñ/g.3ÈX•£½úW’eýrÜŸÞàŽ;îP™™™v_óÈñ"µ2Œ1 @vÌß‘{©ø¯W·žVl[-ÈÎÎf€ °,Y¥–——gèÙ&+®»î:Ý!@Ïž=o—Ka+@v<2kû¥ _¿~–\þäȘK‹‹‹3ü|›8q¢î@,\¸ÐÁÊ‚çÔæ¶`Ã=ÿL˜0Á’@JJ ƒK€åI`mt‹[ýúõu•+WVÉÉÉv_óX[°¡Y«N—€óX28tèK€åI£[éBCCU™2et‡:tpøšQ©là w¨5‚.[¶l±d˜˜ÈÀà ?çúöíkh+Àüùóío¸hk,[¸Ì„§C/ÿ"::Ú’€ ¦T|űcÇ =çdYÅŠu×_½JJJ²ûš™¹Ej[(Öÿþ'JÒmߊ@||<J€Ï S†žu“&M2´  ]»vŽOH;Åx €ÿiÙ¶û¥â¿QÃF–,þ ¾èðáÆžurŒ`5 …O?ý´Ã×ÝÇV€@À[¼§HÕ¼tíÒÕ²€­Ä`àKöï߯õFžwëÖ­3TªTIk”kï5³s ÕêpÆ=€€6eid‰åÿÇ·lË`àsd›Ñg^ûöí …mÚ´q¤g° КþL‰`Êä)¸˜Ñþ:111ªlÙ²†B€9sæ8|ÝqgÿÕà‡ç”–.]jÙ@C "¾(**Êðsï0¼@ŽÌµ÷šÇó ÕšðóŒ¨ç€±%€];wY6ã D|Õ‘#G =÷233UµjÕ …wß}·:qâ„ýcu° Útº§Dàhæ€sÂÃÃçWZ°`¡@Ìœ9Óñ©±œ 8 ·(äääÐ7III1ôì“À qãÆ†€Š+:l‰#À_DEE~nÚ´Ép ¥‘ ½×L=ÍøüUÓíKw5½Ëò@DDF€_ÉÌÌ4üì•   €"ÀoÅÅÅ~6ÊL~ݺu ·ß~»ÊËËcø»  šWóžž§k !û«U«¦RSS½DDD0Hø­ÜÜ\ÃÏÆ 6˜Z0gÎV€€€¿«Y³ÖU€4Ò3ÈèׯŸ6hèØ±£W€ÄÄDˆ¿•””dêùتU+Àô`ø¹š]<0ê]ŒN:éž9p9­€"À_¬cÏ!WÚw0¼@4iÒDkÎçÉ >>ž"ÀoEEE™z>ÆÄĘZ0cÆ V€€€¿ê=hüU@ãFu .F}ÕÀaêÔ© Ž9Âàײ³³M=#{ôèa8¨^½ºÊÉÉaø£Acf]ÕRùùùóæÍ»jà [¤9Ÿ'”Ó üUBB‚©g¤l“3s"ÀôéÓYþhÔ¤•WBÏ’ÃÐÐÐRíÛ·÷è*é=Àà¯$èÖÌ—FžÉfV?~œU àoÆÏÙYj°lÙ2]Ëïm –.]ê± =="À¯¥¦¦šzFîØ±ÃÔ*G[úX~q|ÐÔå1¥?ô°®EÕªUK8Èÿ.'™apðgááᦟ“wÝu—áছnbø›¹›•têØÉðQ€Wêß¿¿ÇVH·cˆvìØ1SÏÈõë×›Z0eÊV€€€?Yºït©@­šµv´;xضm›G€´´4‡¿&=oÌ>'ëÖ­k8Õ|ŽN `ÀÇÔ­×°Ô`ÿ³ûMp¹   •››ë‘Ó¤Û1D€?3ûL]²d‰©U“'Ofø“»Zt(5X0®¥÷Ž?ü°GV$%%18øµÃ‡›Êï¼óNÃ@•*Uèþ¤S¯a¥£îåT#ÀbeË–UndV„Á!€f€¥›3gŽ©U²ÚU à'†Œ[jкuk]Š®]»:<4mÚT8qÂí!@\\D€_s´/ß^P^½zuÃ@HHˆÃ×ÞÍ*€O˜¸àÙR€ A*33ÓáCÚ´iº3gÎt{••Åàà×:dú99fÌS«víÚe÷uã3N1¦ø‚;óK „£¾Ø·oŸ®ÁC¥J•Lï]4"**Š"Ào………™~FFFFš zõêe÷uó žSkÂÏ3®ø‚FMï.5˜5k–ÃÁD~~¾VÜë@téÒÅí@FFD€_Ó³BÏ–† ¤ŸOrr²Ý×=xø c*€/èÚ÷þR€èLôîÝ[÷ báÂ…n ¤×€ÌŽ0@ø«ÄÄDÓÏÉéÓ§›Z0~üxûÛðr S|Áðñ K j×ÖÕlhÅŠº²Z !!Á­!@JJ D€ß:xð éæºéééÚŒ¾Ñ@æååÙ}íñgW¬nòâ0›}6¬ßàp0!!Áu×]§{Ѽys·ž Û8À à·Ž;fú9Ù©S'S«–.]j÷u“9`}‹v?§jÔ*59b¤[?þ8«0É™Õt+W®4Üu×]_{C$ÍA–×´y»R€zuëig;zàÏŸ?ßpC¡ýû÷Ó d¥›ÙÕtò\¯\¹²©@¾®½×ŽN;͸ `uÝû±¹ `ÛÖmGŽQ×^{­¡ADpp°ÊÉÉáDLg¯ÙgäàÁƒM÷ÝwŸýp!¿P­ c\°´‘—Ù Æ<8F×`¢U«V†ÇwëV€¨¨(‰¿gúùj*(_¾¼Ãàaß!ŽXÚÔå16€F ©‚‚‡ƒ‰9sæH”)SFmÚ´Ém@VVƒD€_’­tzžÏ¶™ &OžlUàñ"ÆV +[~༪ÒÄf°{÷n‡‰ÌÌLU±bESG 9³ŒÑ‘øøxж\áÑG5ÜqÇûl=Çø `e½·Œ?^×`bÀ€¦Ý»ww[ ÍŽd–„"€ÓþŸƒšzf‹ ìœxäc+€•M\ð¬Í஦wéê6¼wï^Óƒ‰… º-8|ø0E€ß‘oœ91禛n2õÌîÓ§ƒ×~N­àH@€e-{ö¬ªS·ÍààƒºõêÕ35˜¨T©’S3öÈI™é`°ð7Ç7ý|ìÛ·¯©gv… ~ÝðŽXZ;ÇNž4Y×`bÆŒ¦W4oÞÜô¹Æ D©©©¦Ÿ²úÎì3{éÒ¥v_ûx^¡ZÉØ `]ãçì´4iÒDååå9L;vL;&Èì€âñÇwÛV92‰Á"ÀŸÄÄĘ~.¦¤¤h'ò˜y^wêÔÉñqƒ g_¬jÉÞS*¸v=›!ÀªU«t (úõëg:([¶¬Ö´Ï@~~¾¶_’#ÀŸŽtfõœÙ­{ò¼vt AZÖIÆW +ëÒw¤Í SÇNº¡¡¡¦¬rrrÜÈ Œ"Ï6³ÏÅÑ£G›~^Ï™3Çáëoˆ¤ À²ÆÎØb3{öìÑ5 hܸ±S!ÀðáÃݶàСC ~CžkfŸ‰›7o6ý¬nÙ²¥Ã×K¦ À²ï.TµjÕ¶ 8H×€bõêÕN²'qÓ¦Mn d©dDDƒF€_gšÙgbnn®éÞ=ò¬–£ví½þÑãEŒ¯@VÖ±ç›@P •˜˜¨«È–¥ü΄իWw¸¿Ð,9¾HöM2pø)äÍ>Û¶mkúYýÄO8|ýg¢Î1¾XÕ˜iëínxôÑGÝ~¼P±:¸íh@é~Ì àÒÓÓM?§Njú9Ý A‡¯™Ê6€e- =a÷4€zuë©ÌÌL]]÷o½õV§C€GyÄmý¢££8|žuköY([œyN;:Š03—m Kë;l¢ÝUO?ý´®AÅôéÓdáúõëÝÈ’É0xø4y–9ó<¬\¹²[ƒúÍ1lXÖ¬éªF ›@‹æ-TAA®½öU«Vu:¸á†Tll,G`Cvv¶éga«V­L?£ƒ‚‚¯¸KcÀg›Š 6èTL˜0Áé@Ô­[W ܤ¦¦2xø´´´4ÓÏÁ‘#G:õŒ–¯oïõ³ó [Àµ&ü¼Ú}N…&œUŸQ1i§Ô¡£§Ôác'UjÖI•ž]¤!“•[¨r.><ò Õñ‹ÿWö“¹ø¿§]ü7É™'/~ÎIŸqJE¤œV{Q[cÏ©uçù;áñEì=º÷Ð5¨Nþ•*UrIлwo·õÓ @|UBB‚égà‚ œz>=Úá×±ã+bUصíâ?,ù´JºX¬Í)ÒŠyws—Ñ<§Ž]üZ(H8°3þ¬:ð7ѧY«NvC€Ý¡»uýÆç’@H·bw¼Wä´idÄ à‹¤™ŸÙg ôpæÙ\»vm‡_#6ýc+þjcÔyµïЗqJ›Á?qâ9·ûFÈ ‚Ä#§Ôþ¤34¦±ãÁ©ëì:vÒuLŸœàŠ^âÚk¯UÛ·owËûBúÈŠ$ÀééÏSš¼¼GÏ1½¶H¯gžÍsæÌqø5¶Çe|ÀWI¡,Kêeï½оVôÛLÁ µ>»ΪÕáü™e7hÒ¤‰®ÎÃùùùZ§`W…òueÆÂשּׁ,µÿ~“Ÿ’’’búÙwÏ=÷8õ\îÚµ«Ã¯!cFÆÐ |py¿ç’ëGE¿½ò° ämó·ç¨š5ƒí†z÷å¯\¹Òe€8p Ûþö &>%..ÎôsOžåÎ<“¥á¯£`^&YXi ÀGfûw'žÕ:óû{Ño³›ýñ"­¯Áª|põ¼ïa»@ÚuTrr²®ßcÓ¦M]Lš4ÉmsÙfÀ€à+ÂÂÂL?ó¶nÝêô3YOž]ñl`écú"SOûÕWlN¶ë#ç4ékíâþ‘÷ëúýíڵ˥@™2eÔÒ¥K9€‹Ìn“•oÎ>“ÇŒãø¹z„m ,gõÅÂ_ŽÍË/ ð·Ù1þÄs*&ý”ZA@».ýíA5‚´æyz~w]ºtqiP®\9µcÇB@À;zô¨éçÝ-·ÜâÔó¸^½z:¶WäjJ–$7ä°äÀØßïÊ>Q©§µÕ~½ `u¢VäÛ zôè¡ëw­ççÊ råÊ***ŠМ9%§C‡N?õl ”m¥Œ»AxÙþÃgXêïäÖ€ð”Ó~j÷0ÖáV€ ë7èú} 2Ä¥€¸ýöÛUjjªÛþÆrÆ1ƒK€•ÅÄĘ~Î 6Ìégñ¼yóØÀÊ6GŸÓÜYjy}AÊÉÉQÇ×È1sr4›3n;¦‘enGŽÑþßòïä¨9oEj[œž0o[¶ ®]ÏnвEK•››ë¸©âÅ¿[ÕªU]4lØP×±„„¤w;^i¤±®³Ïa=«sò ƒƒðÆr9ÎïÄ /-¿X¬K¡&… œ[› - ——3>9¿=""BKÀå5e)\ZZšè)L]EŽôÇmƒžãpÀìY³uýŽ–,Yâò@tìØQ ‘Üõ·MJJb °,³ãÅ‹»dKžžgð3Ñçƒð”qgUv®ç–û˃(==]+ÈeŸ¶³E¾3ÂÃõýÜ<¸{Å€l©ð·}nKöžT 5³Ô«[O+’õüŽÚ¶më–@¶¸óoK°*Y-iæÙ&Çø¹âêðk8|†19OÌúÇeœòÈ~™q—åÒ2oå‡ddd¤VÌÉ6wÍ'=©¬à/žÜèp@¯ž½Ô‰'¯”ˆWåË—wK0yòdB@À‘•fžkqqq.yþŽ;Öá×JÎ<ÉØ€;ɹõîÜë/Ëù¥ó«,ã—eø¾øÀ”ï[ Ré'àŽÞ›üe¹ÛóªE›nC€Y³féúÝH¡îŽ L™2jÙ²en d‹ ƒM€•è]…wÕÊÅœ—<5j¤ë¥•ÀMKþãϺåh?iÊ'óüîá«ý|.]qâ9µ÷,y›¼$ÂaP«f-mˆž~õë×wKP¶lYµjÕ*·†2Ó€`¥1ŒÙgZ•*Uœ~öÊQ¿z&Süµi2¯ŠH9íÒbG–uË~~Y:QYÑ Û\ù;”mþzwî3Âa {üeFÁÑïdïÞ½ÚŒ½;Bˆ,_¾Ü­!€¼Gü1ø=á»-® äwìØáðkE¦žf¬À•ûý;éÒF~²¤Ì› ü¼Ý+@ú¸ê÷™ž]äó§Ì~&CÕ¬ì0?~¼®ßɈ#܈k®¹F-Z´È½Msr´&“ >ÞÞÒhöY&'éxªOÆÅ±cv.°:ü‚JÍrMñ/d¥) Ôÿ‘-®:=àhN‘ZáÛ!@ÿûŸpÕÒ5 [.n½õV·…²Â`Þ¼yn?ÞRVp­|ñ(ÀAƒ¹ä™ÛµkWÇ«J/òÇ#“Ax”ÜH%Quz†:=Ýòü½%,,Ìe«²r Õ†Hß}ø- =¡ê7hê0hÚ¤©®ßÙÚµk݇³gÏvk Ûdä¨I®€·˜Ý¾8a—–ã\ñ/M[¢¢¢xxêà’ÕÇó Õ3Q¾Ûg¼=1tÈP]¿º5Ó§Owûq˜©©©\'Ÿ: pþüù.{ÖÊ8IO_$Æð Ló—[èÔ¬¥iæ«Gøys5€+šÊ) ¾|L`Ÿ!t…+W®ÔB»=xâ‰'ÜÐà ‡2õÜÚ´i“Ëž³K—.Õµ’q<Ëþ™ù—}þ4/sNrr²óMäò µ Ç߃KöžTMšµqÔ«[O×ùĬ\wÝun&Nœèö€æ€_9 pÏž=.{Æ>\××ôå­ ø ¶ ÆÝA€ìÉd› ÀÌ<×$¬vÕ³µlÙ²ºÆDIGO2¦ GtÚif"-Hö|;Ûà> —í?«Zµë©+X²d‰® ÊG^yd‘Eè‰-‘‘‘\+Ë([]ùlݵk—®>HŒëA8°ïÐÃ7õ¼¼< ’£)#SOûä{sö3é*¸v=‡@­šµÔ®Ž;vìP×\sGB€:uê¨øøx·‡ÅM7¹Nî’møù$Göz£áî–˜sŒïAز1ê¼Ê/0Øe>7—Fd^‘‘a¾H¼h[¬o>GOY­k@Hýçðw1~üx¢råÊjóæÍn´îÇYYÚ~K®€«™Y(“E®|¦vëÖM×× O>Í€­¦GŽëø/Ǫ±ßß{d»Ù1;¯Ðg›vì9DWЪe+‡¿#iؼys…²â`êÔ© äg“U\++LB¸rÕÝÍ7߬ëk¦dÒ@©¢ROžaärß 6nܨ-C—Ya_ï0G®ªÒXWЫg/mÖÁn“ ¤$mvÞS!€¸çž{œnꨗ Ô¸V®’ššjêyT±bE—>KõÿK¥åà'N8RîØ1:Ž[„üd_£¿ÙÖ­[µ‡å 7Ü í}/þß÷òÍ#rª+ŒzÀáïG–æ_{íµ ä#Ù¯ï‰@¶êDGGsÍœföÙUµjU—>G7lØ ëë®<Ïx@±UaTfvž¡B‚ÙDëà¨#¯œÒpã7jÌråÊ©µk×þï/(ôÙc¿“u‡3gÌtøÞž9s¦GQ­Z5ê‘ ø¸@®_€3M=ƒn¿ýv—>C§L™¢ëë†&œeÌàR×ÿØlCÆéöoMQQQÿ~÷Þ{磊¦,ˉ‰Ñþ÷Ä#§|ò½»|ÿ9Õ¡û ]@P ]3Æ óx çÏ;×c!€„A \7SâââL=‚ƒƒ]úüìß¿¿_Ÿ~Àõöæ©ãö!S4X›œ±kïï'³þ—?8ëÖ­{iº¯“³xw¡º«eG]!@Ú%{ Øjœ×¦M‡bàÀZo OòµôFÉ–23φ ºô¹Ù¸qc‚0bëŽ=†–óг~?{góJ±_¡B…«ŠNùoéÙE>û>ž»ù˜ªÒDWФI‡A‰ô¸¨U«–WB€;î¸Cmß¾Ýc!ÛFEDD˜zÞ¸úÔJ•*éÛ¾J#@Ô“ ·iKúuw±¨¤éŸï<”íý]{ôèqÕtÉ’%ÚÛè»{ä¦.¹X´×Ö´o×ÞáF±±±? X™2eÔˆ#´c6Ù°9Ú̳¦iÓ¦.fê9 €F€ @À›¹!U»ëÝ÷Îχ¤¤¤Øü{®Y³æª‡§ôàìÜB­)¤¯¾¯ÇÎØ¢») „ééévßûÛ¶mÓöç{#AAAmȶ€Þ‡fž1 4pù³ò™gž¡ {–={F 1F÷ÍZÎzåaç[ìísÏËË+uf»zõêêСCjŸ XlðC³u‡mÛ¶UéiöC€9sæx-(^ 0zôh‡§<°-àI²rÌè³¥víÚ.NN:•F€ ì¹oÔ“ºÏM•›»,óâAç{Ž=jóï:`À€R¢ÒðPj–Ͽǻõ­;hÓºVìÚ»FŽéÕ@Hçd½«v\¹-@ŽzbûàJ9šH«Q£†ËŸ2¦¡ [Kÿ×§¨-ZêNme_9ßT|Ä_i¶nÝj÷Aº-öœO¿Ï—î;­înßKwкukm¥‹½“Ú·oïõàÚk¯UãÆÓVqx2Հ˙éSsë­·ºüÙ(}ô|í‚HÎLŸ=k¶®¥ øý3—¾¶ÜÉ’óõ[÷ûü{}þŽ\Õ¨I+Ý!ÀÝ­î¶Û;AöÆ»£y‘õë×wxœ¡»‚iÈ}`ïÔ![ªV­êògâ 7Ü ûëÓ(<«ƒnoiøåèîûdO¿­¿¯½³î娼µç|þ=?cÝaU§Nˆ¡ÀÞ™™™ªQ£F–d5ÀðáÃÕ‘#G¼p€rlŸ;ž‰ŽŽ÷-¶‹F€ @ X¾ÿœº«e5úÑú–Iåäðpórzƒ­¿ñÃ?l÷a:eÖ ¿ ¾jÖ Ö´lÑR>|ØæïM´K„BVr<ùä“ß@MVÆ}n”+WÎ-ÏÂ-[¶ÐÀåFMZ©7›7mÖuƒ”ˆ‡›ýëz,±§î®æ~óþŸ0oªU«¶¡ÀÞ¹ÂZÃD«„ÅG®]»Öã!@q`HŽÈ6Cw<§OŸ®ëë'Ó”cÿBÞ¥j×ÖݰEfŽy¸9w>nDD„Š×Â)Ž¢¢¢¼r¬š­%z²\ÎÑuîÆÃ~s<6Ÿ¡ y³æv·PÈÉÒ™ßJ!€¸ûÒ€ ‹l‹3òŒ•jîzö <˜F€ Š=üÔ&­ pŸ¾cR¤`äÁ¦ôRàË© /½ô’úøãÕ?þ¨~ûí7õÿýÿ_©~þùgõÉ'Ÿ¨çŸ^KÏ%pç÷˜žnûœ{[‹õ:Öïú`×1H÷{›G ¥¤h3ïV d†eàÀvO6pw á‰7/€cËj691ÈS€ü{w=÷䤽ßÇ:‚þ®eÛîZ1³tÉRŽþs™Ù—çÍ7ßT?ýô“ÍB߈÷ß_{0º£»º½¥ìŽŠ×jÕoVËø×ƒòñETpp]Ý!€4DW‰GNQ#€þiÞ¶lU3¨–V¸ŒºÝÿM$ÛŸ~ú©G ÿ+{H±éΠOŸ>¨í:÷òß2Ë¢Tí:õ …ÆÓŠýÒ~Ÿò÷1b„¥CQ½zu5yòdÃû7]I~W²r"::šû xÈ_|Qê˜CÆûÌk…ÿ•Ο?ïÔÏióï}ß}÷9| V­z“ßo“©S'ÄPÐ¥s»[j¦Nê¶£Ž\½drìØ±vEz‚ÌÚH³Eš€ûHÁnk¬ñâ‹/º=Xºt©[ŸiÛ·o×÷ÌÉã$ÀOµëzߥ‚e×®]ìÿw@V?X©ð¿Ü©S§Lÿ\2Ãjëïݽ{w]ÕÙ›Žúõµ2mE¬ªS×XpWӻ췷|ùrU®\9ˇ¢|ùòjøðáv=ôTÓ@ #œéJ (ô1²5ÎøðÃMõF2ròÉ'Ýú,“^7z¿—•aÔ €ŸY´û9U³f­KÅŠž›´eˆDiJ&ö¯¿þjÉ⿘Gf~>YÕaëoÞ¦M]ÕÇìõûkfÚÊxU·^CC!@ÚuÔ†õlþ~wìØ¡ªT©â!€([¶¬º÷Þ{µŽÐÞ Š{HÇhw“ @^{c éàîࡇrë3L¶¶éý^6Fq àgÆÍÜZb¦’€¶;äðÁ–.ü‹Éé²LÚèÏ(Í9@<8iI@\7Ó×$ª†Í …rÂÆô'§Ûl(ƒ.½¿g«í ;vTk֬тA+„²å‚0ÌyþùçŽ3Œ®¾2º ÿþn}vÉJ6½ßËŽ8ŽüL÷{G_*Púöí«ëf(ì@zÊq4ßÿ½OÿÅ$¬0úsJh`ëo^«V-]Õ# ˜kçé­ÙªE›n†B![Í¥€2dˆO…ŪU«¦Fm÷8IO’Ö)))Zo õ Ï×_ípŒ!3úî<P‚ew>¯ºvíªû{y6é õàGœ/±”Yº–ë¹&$$̃Pr?ýô“OÿŤI¡Ñ ÃVöŠ+êz¨vìvO@]CKöžT]úŒ4téb¿9àâÅ‹µýö¾ˆfÍš©ùóçë>jÉÝ$p‘#åh)ø`nù±ììlÃM“ܳ5jäÖg”¬¶Óû½D¥ž¦^üÇìgÒK%?ô0'\FŠ«ï÷·Çh£[Þåï­÷¡Ú´E›€ Ò>8Ãp [nöîÝkó:“cƒ‚‚|6•*URT{öì±DP|’€4òde”töìY]ã £½ Œ†Á·Þz«[ŸM·Ür‹îïåÐÑSÔ €ÿxdÖöÉäIúš¢BçmÙ¯&{é}µø/î`äìtY2]jÜ•+u?T5i°×ÓÃOmRµjÕ6Ü`Ê”)6÷ÏK¿nݺùtP¬N:jÚ´ivW>x£g€¬ò‘UM¤ÐôžpTTTdèum/l¹îºëÜú<ºæšk´†Éz¾—Œì"jÀ |pf‰bdÖ¬Yºn†FŠJ_${á}uÙÿ•´ýk:~f{ÇÔÉùïzªõBô55uyŒªß ©ñ-»ØmÂøÔSOùô–€+6mÚTëÂlïèIo­¾rÊ©E\\œî±Å™3g ½¶‘&±|{âY$ÛÂtm!Ë+¤füGÇžCJ!zÏEõçÛ²,ø›o¾ñ‹â_¼üòËNŸ`¤Opíº]ÍÝ’©šµêd8£—.YjwëÍ]wÝå!ÀåjÖ¬©|ðAjó„oïEWIo zðw2«¯wlñ·¿ýÍÐk¹·Kë‰g½-xWZFÍø‰[”(@ä(/=7BÞ;ûöÛoûMñ/äçqfÿ¿,‘–}Üz¨w֨ɵuÑâ=EªS¯¡†C1hà ›g&Ë jêÔ©n_é-7Ýt“0`€Z·nÍ“¼½]@Â2¶ ð7ï¿ÿ¾î±ÅK/½¤ûue5•‘{íÎ;=ò¼Ù´i“îïicÔyÆ6 €ï[zâªÂcéÒ¥ºn„þ:&ËÎü©øü±î†‡¥ý­·lÙRâ9hÐ »Ô;.¾¸¾þgùóªÿ¨i¦B€&Mš¨íÛ·Û]…#]Œý1(V¡Bí¨¦™3gj[T¬´: x»€ô ‘e¤ ÈßäÀ|ŽÜ»~þùgÝc‹×_]÷kK`jäÞº|ùrú¨ÍæIÒÀèñÇWåÊ•óë  Øõ×_¯Ú·o¯&Nœ¨vìØa¹—¯0QVÕÈöÙºá÷LþCš³[¼óÎ;º_[&ŒÜCåï‰gʼyótOÏ&a<øA³²1WÒ¡;P›ž;wÎïŠñÚk¯éjücëo}çw^zXÖ®][ÉÇc=fóÚ¼e®¯RLYaª9 hß®½6»loEN³fÍ"¸\Ù²eµf‚£GVk×®Õfã­\¾}CB‹£Gj¥Ù¨ÜKÙNÀÛNŸ>íòÉ…böž_¥¹÷Þ{=ò Ñ;æÕVÝ¥f,ø¾Çæï»ªÐ˜0a‚®¡™åoKß¾ÿþ{¿ .\¸àðç·µß\öí]¹ü_>V¯^móÚ±Ko®/æïȽªñ¦^Áµ‚ÕÓO?mwüŠ+ÔwÜah$«ä˜Á‡~XÛïë§ ÔªUKÝsÏ=júôéj÷îÝ–]%PÚªi8(Û dÅ¡«îÿŸ~ú©î×–¦~Fjv++ìô~O‡Žždø¾±36_UdH'n=7ÂÇûÕƒOöòúcñ/ÒÒÒìþì²4ÙVQÙ£GËE‹i€½½½ú åúràÉ«Tpp]SAÀ=}ïÑ–jÚº6e»€¯g¤q£¨_¿¾É,ºÜd¹½?¬¸öÚkUHHˆ8p ¶ÅIÂ>#ÇQy“ü-åï!Û䚣¿+ìÿ_}õ•¡ãÜûªV­ê‘çèQ£tOÙEŒ_@ß7jÒÊ«Š‹¡C‡êºÊÞVzøÉR6,þ¿þúk‡?»¶Žá‘ÕË–²òC>²³³m>P Çõ¥§džTÕ¢M7S!@HýµpÁB­€­kTþ®²bãšk®14 >|¸úì³ÏÔ?þñm‰ú!CTùòåýjë€ü<²u@~V µd¾Õ Ú<:'G j:¤-«•U:0<¹ÿ_|ûí·º__Æzïq²ÊSÏ †uß{ó »€¾oè¸yW;tÔu#”Wþòà“ýÓþ:ûæÌ‡Íÿl>ýû÷¿êaùæ›ojÀ•[.÷ðä\_:-{ö¬0ú)T#ÈT ×ë¾}û6ìlÓ¦áf{6lPÿþ÷¿µ¿·Ì ÉëôíÛW›Q÷׃­ZµÒV?Hgh)°}!ëWV0I(Ëñ„̱‚;&ŠÉªQ½÷4y¦yê¾ß³gOC÷Û•Œ[@_7lü‚« ŠÚÁµu-•¥©œ|û‚ß~ûMÛKlïg—Fd¥îu»XüHƒµ+dûóÏ?µ‚pÁ‚6¨O.ÝÇõeÐä%ªa“–¦B!Ëm­ä(&}pp°¡Á‘3(æå2ëª:wî|Õ #ËP;tè Æ¯ýþýŽ­t<¡Ü×äúg…w¬‚”•bz__J½÷¯%K–xìß®];C÷Ö5áç³€¾¿¹´b".6N׬“¿ ,ågñÇ@ú8Úóg+ì‘¥ÑW>(å<öâ)8m=PìÈáú2aQèsªû½£M‡õêÖSóžžg7À“ÿöÔSO©o¼ÑÐ IŽ‚|ñÅÕ•ÒjãÆªuëÖsò€4YìׯŸv|”ؾ°B@VlÉ œl ØpåXà—_~1<ÆøðÃu ¹é½gIàê©û¹l3r?ÝIø¸‡ŸÚ\j!!3]zn„Žf—}…6þÈ, ™%y2ËYÚžïÅ‹_*ül-)¯|±°äÚrþº¬S7ÄtЮm;­û½½kWŽ¡»ÿþû¯ZåáHŸ>}´Õ?¥}|ôÑGjÓ¦Mª}ûö~¿2àrÕ«WךeΘ1C………Y¾—€ô¿¿¬ò‘íO@@`“Ùy3cŒwß}W÷×ç†Þ{”422’ ðq/¼ð‚é1Ç©S§t} #÷°Y³fyüž+ã½ßßÁdÀ´íro©EBçNuß}}€??ýô“ÖÕßÞÏ)Å•­¿¡t1·õp¬_¿þ¥"N¶KØúw{ âšòi+ãU«v=Z AÀãW¯q™%š~þùg-`\¸p¡Ö? B… »J@Ñ£Gk½õsð¤ììlOó@ÀG}ñŦÇðê=e@ï=eÀ€¿¿Ê}L÷x7å4cÀ÷õ1¹Ôâ@fu/&„/?ß~ûmŸoú' »½ŸQfëäø¯Òþ~‹-²ûp”¢£øC¶ Øúw÷OZÉ5åIΫ§®Uõê7v:˜4i’®¥çR|Ο?_Õ®]ÛÔ@«cÇŽZg| ¬Œ~üñÇêìÙ³ê™gžQƒ6FøYѵkW5gÎm –U¶H)[Œ(ªß [zœ{ÈÒy=_ÇVÓáÒÈ„ƒ§ï©F¾¿˜´SŒ=@ß'„­Â@–Ÿê¹!J ,d|õ!øÒK/ùlñ/M×õaA¹­Îã²Ä»´®ÿ—wÈýå—_.a 6´¹ùémǹ¦¼`á®ÕwècNm õêÖSS¦LÑΈ×sÍoذAµlÙÒÔ€«|ùòjäÈ‘Ú×úóÏ?•™Y•"«wdöyܸqªnݺÈÏm•Õò¾Ù>¶Ö'¼3ã½[@õž|"ÿÎhßWnzïqñ €˜º"ÆfAЧwC[}õ!XXXè“Å¿4Q“æ:ö~6)Žlu—Áz‹-ì>¥ ,þϱõïîjÕëÉÛ§z¬NTw·ïåTPÈ>|=A€Ø·oŸ¶…Äìž}9‡yæÌ™Zçì‡ôÁ™è3f¨Ö­[ÜIÅ«æÎëõÕRȶ# -Àš¤i°3c½«?õž*%«Ã¼qßÔû¬Ó_=Éxð}KöžT5kÛ,d‰˜Þ™G è¬J–>ûZñ/Ç©é\ÛÛß-{º=ßxãKÅ•Ì2Úúw#çø?«l 3m½ªÒÄé  ~½újÚ´iº—GÊ^ðaÆÙ]Qâˆ4ÿ“m&RÈ»âC®™_¾|¹êÓ§ªV­Z@rº€„9²ÒÇ›M%Œ(ºëq„3ãG[‹Ô{¯ Ø÷I#'¯¤d€~¢]×ûlr䛿¯ˆ‰‰ñ©âÿóÏ?×¾gG?—½@)²-µ“cà.?²íºë®+õß]W¾‚Z´»kÉB…žP÷ \Õt:©¢¦?9]w •—,Y¢Ú´icóXI=Gãõïß_[áò¯ýK¹òCN{•„ÒK Ž ”¿CÓ¦Mµ#½uª€?*÷$Þ'­³cG+‹¿ŽÞ{Ä}÷Ýç•û£‘&…éÙEŒ1@ÿ0ò±¥v„Y¥ç`EÒqÜŠ9²GO£-{çîîÞ½ÛaGõråÊiÿ‹?V¯^móß6o߇ëÈ¢žZ›¤:öìtPÌxj†Í-%¶zL<öØc*((Èôà¬J•*jüøñZSÒ¿ÿýïÊßÿ½¶R@ Êñ…2sî¯ÇJмysm¶---Í++¤( ¼çôéÓNGô¬úÔÛLZÔ«WÏ+÷D#÷Ác9 €Ÿ˜ýL†ÝÿìÙ³ 5’m¾ö0üðÃ-]øK"ËÔôü,öfþe9^åÊ•>åèµâi4(E˜­;æ©-\G–?60Nuè1È%A@ƒjêÔ©†g’wíÚ¥ñ$ûÔÍÔ¤y ¬ ½¢r|•;?$lS$0›8q¢ÖS@¾¾?…rÜ}÷ÝÚ‰zŽƒtõ‚zV2p½Ï>ûÌéD®<@z«g‹‘";ÕŽ €iÒ¼Íóf͵å›FŽ„òµã dÙ²U‹9¦PoWí””»{´õ&gŽÿóŸÿ¼TIC5[ÿö–;j©åÎq ùPÓÏݺ$¨TS ÐÕ²Ç^ŠhÙBãËaÀÍ7߬}ôQ»¢«É>\_m$ ø’S§N9=>‘}ˆˆË74œ8ñcÀ<±,Êî¿q£Æ†÷²¬Ê—ŠgΜ±Dáÿ믿j3šz—ÇJÐboù®ôpÐ{FºœÍ~ùÇ<`óßV®R]-ÙÇ™¸>}Ý/¶ÛÔ¨ºuꪱcǪèèhÃàŽ;´åýÎl(và 7¨áÇk3ËìyúCÊýdË–-Ú5åLo’F¡½zõÒ¶ox"-d2§Q à>ÒÕÙqŠ„®l(÷~oÝ猙º*Œ±àGšßÝÅîà~ò¤É†tr&·¯<¥XýîÞ*üûí7-UÕý=ËV YBko¹õ]wÝ¥ë!xûí·—8~M¶rØ[¢ÝgÄT®? Ûuíï² @ôêÙK­_¿ÞðrryÏJŠN:Ù]}bdŸ{çÎÕ† Ô[o½¥¼õ!= ¤¸;w®êÒ¥‹ªX±¢O…õë××¾wùûx¢?€ÌR¬Ö[þ/äue@½“î`ô˜ÔÕáç7€þCη7 ¯\ÛpR*dvÚWftd€îé¤ðýõ× wÆ–R¤ÛÛ[ÛªU+ÝÍÕÎ;w©Xùé§ŸT5lÿû •Ô‚]\7~fÊÒHն˽. š4i¢ž|òICƒÁbrâ€×'³Ð®XP\ÈΚ5K{ý?ÿüÓk€ôxþùçÕÖ­[UïÞ½}fÛ€4½ÿþûµ ÒÝ«d`NÑXkù¿± «J˜à­€fV€~eé¾Óª^ýÆvó#†0=£ã+û;%°eøî.ü¿ùæ­0st¢£½¹òšÁÁÁº€2Û¦wé¿è9d׌›¼$BµíÜÏ¥A@­šµÔСCµ®úf“²TÔÞ©FÜtÓMj̘1*11QýòË/Ê›J){ïo»í6Ÿ8Nðž{î1|„QRDÐpO>ùÄ%ã=פÞ€rüª7ïeFC]Iø›£Ÿr8ßjn/_™Ñ‘}ó²ÞÕE¿¼æ… Lo‡®£sØ¥›nÕªUu?ü¤ùÎå²DÐnÑtójÉ^öþFþ¿5‚\´kÛN; ÀÞöG÷’mÛ¶iûüo¹å— e¾OŸ>Ú~w${óC Ê}bÙ²e.9-Ád+Ãã?®4î d‰ÑRJ’U;®:–Ø• |ðA¯ÞÃŒnkblø§·f©šAµìÞïjz—V ›ÌÉÍVš„Yýa)Ûä{ýüóÏM/íÿòË/µ}ǧOŸv:üÂÜÑ~j9ÂËÈYåRðüç?ÿ¹TxH7sGáÁƒOnæZ 0³6¦©~#&«:uC\¾*`À€jõêÕƒ-{KÅ÷îÝ«Í仲Ù^‹-ÔÒ¥KÕ /¼ äÞüûˆœT2dÈuýõ×[2¸ãŽ;Ôš5kܺàÈ‘#>wÄ,`'OžtIðÑG¹´`£F¼º’Iž!œ¼žÆ:¸4ØéÁœ øe©º/ôe®²ß^f夠—P@å2SøþûïkÿÛk¯½¦ýw™ “ýq®¨JsB={é&Mšdh¦°yóæÚ^ÿË÷ý7lØÐîç„4ëÄ5À–ì)R£§¬VÍïîìÒ  ¸ÇÈÁCÔ†õTNNŽéûŠÌ<É2ú— ¥Æ”)S<~Ä`iòõ%˜œ6mšKŽOtµÖ­[šý3JîÃrO¤ ¼³üÿÕW_uY@ÛH£Voݯ*T¨`lõà¯æmËVÁµë9°/Y²Ä%:YÎ+g@KS;™åâAý?áááv÷»|yì}÷Ýgè¡'ËŠüñÇ ÉzöìéàH°ëÔSëR¸Fp©a`·~£UÍšµ\Èq‚Òodó¦ÍN--—€QþuíÚU;ÐFy#F¨¨¨(íÔooæO?ý´ªW¯žeBiè5lØ0mÆÞ!€<3Œœ–°ü?Öe[eE££¯§wE—ôuñæ½JV<ÚŽTPÈóð_ƒšíp^§v·t‚–Ù?)|eɼ ²-•’žëY–&½ôvú/Ö¾}{í¤ƒË?&L˜àðóz¥ñ®6{Ž2v®jШ¹ËƒQ¿^}­)¥ìù7z¤à•E£4 ”Õ²úEθwE¡Û­[7µyófm%·?dfNNMhÖ¬™%‚éÏ ÷4w`¦*ˆ¤hwU +ì}-³é]V?jÔ(¯Þ£äøcC+@_êÒ°™ÃÁyûvíµe±î>Z–}ÊR1éÖ*Á€ôð•£þRÐë-r¤‰šÑYM9\z\þ!ƒ£Ï«Ó¨µZ~à×lZ¾ÿœ?{‡Ë¼\à ÕC=¤5êÓÛaÚÞÒÓµk×jkÕªå’ÁdãÆÕüùóÕ™3gÔÿû_¯†HlܸQ ü¼ÙDPz’¸jµXid{`ß×_í²ÀQc#C®ÜªeFíÚµF“Oø¹1ÓÖë”÷¿·¿S3sÎÌÉ239O8¾ºR@¾wY*«71—D–4}ÐÉÿ¿ÿýï— )Pä|vGŸwýÕÔÜ-Y\ÐmæúuϰÇU:!n 7j¬yäµoß>CMœl‘`qÞ¼yªW¯^.9fðÖ[oÕVÖÈö¦üã^ ¾øâ -4‘{€·ÎÛ–NßîzÈïØßaÀ•G»ò4#Ùžhïëé=VOšI{û„i@hh…jàï3zΫmºéŒypŒÇ[%¶z“(y€74òó­ZµÊÐŤaØüQ¢‘ØÈ‘#uuÈ7{×LY¼§H=0y•º«eG·Å'“Œ;Në`ö4+ÃE)(¥±¦ôË#=*oРAZHùÍ7ßx5 ¾rœ¨œ¾`äÄWÕîº÷Ë‘’ô®öæ›oº¬ø—û‡£¯§·o‹œâímJ²…ÒÈ}&›”™¼à຺á3žša‰àòm’DË »f‡¤3vbb¢®Žþ¥%å÷Üsá‡[¥J•´æŠ—üòË/ª{÷îº>¿ÛÀ \p‰i+bÕ½#§¨š¹5 £{ö詞šþ”:xà Ó[Š.}æ™g´ýªuëÖuúØ©:¨uëÖ©·ß~Û«a€l’}ô÷߿˚$:2zôh·ÝóåïÄ1@ÉS„~ýõW—rT°£É ½×«4SõvбcGc“L¹ @€c¿ô¾W¯Zm©àò=™!’ÜÑò5W>xå8@)ÞÍn‘Ï[¸p¡ºùæ› ?Øêׯ¯5»r)°Þaräß²ýìû‡‹8¯¦,P}‡>¦ê‡4qk „4PÆSË–-ÓV¹â~"M:¥‡„rÕ«Wwúؼ;v¨ï¾ûΫaÀ¿þõ/m+ÒøñãUµjÕÜ:è–­î\ÆvàŠŠŠ\ºü_Ž:¶÷õd+•ÞkÕÙ0Õd[”¡Þ1¹E<ÃA€Àѹ÷pݳoÒxÊŠ!À•…µæRÈ ¹ÙY#hÊ̾¼†„ Ò@‚ggeõœ9sÔwÜaê¡6tèPm¦ÿò·ÞzKÕ¬YS×çÕmª–ì=Å{noøøÂýª×Àqªn½†nD›Öm´“¶nݪÍ»â~"¡âÔ©Sµ†{f—ÕË©ÔBCÙ¢ãÍ9T i)Ô]Ñ¡46lpÛý]‚ Š?àYõå—_º4Œö¾žŒ«ôöíðöþÑ¿c[r@€²pWîc¾‚ji³mVJ[% {×dà+Msdû€•ÿMÈNN>pGãC)H¤1Ÿ™ÿâc·âââ®ÐË@7Ýt“®×¨vK µ0ô9Þ÷ð¨eÏžQžUÝï}PÕ®]Ï#a@p­`Õ§w5kÖ,­ˆwE3A ï$\Ó 6lhj+}>¤ÿÀ©S§¼~´ ô‘‚Z~žÊ•+»là-÷#½ÍÂÌ0–LV*¹²ør“½ ½“+W®´ÄQ¥Fû’=N˜É‹ÃTPPM݃ëÙ³g»dfmîܹÚU™]“q™a«S§Žvþ¶ J,X ï-ƒT_ .?’Lüfü;v¬Ö çÊé MÈtõ ¸þF5Gïwx÷Ò½§Ô#³¶«.}FªZµj{$ 6ÒšcÊàTÏ®¸¶%4”£6ï»ï>íd£×µ,“•Ï—½·Þþ@ ½+‰éÔ©“[﫲*‹BJ¶º²øÿöÛoí~=Y ©÷Ú”&¤Vd ihu@?Ç&Ošlz­øfŽÞêÑ£‡zâ‰'´c¯\µÄ×dû,óïÒ¥‹îÝÖ9¶òs^ùñÕW_©!C†8¯»‚š±!•÷9¬u’ÀîBõðôgTÇžC.žµ<ˆ¶mÛj{â7oÞlªygi¤7ˆl×¾îºë ÌrìáÏ?ÿìõ-QQQê®»îrzØÞª. @)hdòDî® ä4{_SVÝè½.íâ*rB’¡@7›PF?eh-G9îNöлª9ŒœyÝ A­–fzFjW“¥q¡¡¡Zâ, úœýÙd¹¿&ÿüç?¯ K‘¡wÉÿÿN ¸A=¹úïoX~+ÒƒSת=©`m(V3¨¦JJ¸(+Žôue÷LéœítYál ¬+¯­TÕRŒ{ë㯿þÒî×!!!¦ïcò³¸ó¾,÷]« ¸šÙ®^þòäI»_Sï„ËÞ½{-Qü‹¥K—ºŸ¤g€¬Gÿ1†Ï2p–sïõÜ`'L˜àÖ¾ìcmÞ¼¹¶$W–ݯZµJKË]µZ@¼Ò W–ÊÊ ¿{Õ¹sguã7ºäû—mr~®<¯üøì³ÏÔ½÷Þkèõn¼±Šš¾&‘÷5|®gÀ¤ÅajÀÓU‹Ö]UA êÔ®£úõë§Í`»ª€Ü7¤œM}Í5×èº~¥_ÈôéÓÕóÏ?ïÕäTidhæž&½ÜÑSåò¾  0DÀøôÓO]HÏ[_O)ë½Çg™`Û¶mÆV§fäù òàû¬j×¥¿áýµ²,ߪGÃH³.Y–&Kêå¨npè¡jñâÅ*!>Á%G Μ9SµlÙRw#ÁÆ«uëÖiÇ}zãCö·k×ÎÔýMî«î\%\) d{¡«‹9UÈÞiI úïy®Xé*²:ÈPsÑL À-ÚýœjÙ¶›¡A²œ0kæ,»³eî>ƒÚ—ȌڠAƒ´¥¾þùg©ƒî>úHõêÕËÐëÊì⭷ܦÆÍÜÊ{~iÆúd5ò±¥ªC÷A*8¸®ÇæÍš«G?¨6mÚ¤-õw¶£ýŒ3´•Kz¹o<øàƒÚ™Ýžþøïÿ«m·2z¯»þúë]ÖgÁÙj@šÿ÷Þ{ïÙýš²Hï*'+±Œ6N<ÂñÈ ´]m:ÝcxpÇs€-SWĨ[˜ü¶o×^ÀÊֽͮæÛo¿uÙY¶œ>}Z[ž+Gþ™ihSù†ÊêŽÛï(ñ»ì?jšZ¾Ÿ‡à ¤=üÔÿíàÄ=ÎÈ6A©Í›7«üü|ÃÖøøxuÿý÷ëZQ%«–V­Z¥5sõÇü¡ûˆ@9†ÕÝ€ü.i!ÍEÝQü˽@&cl}]½=;$”¼é¦›,5–“ãœÞ7Ö„Ÿç9ÀѹÜíºöwjðÛ¥sµjå*Ëœ[µjUõòË/»t`,GuÍž=[™úž*T¨ ªÜXEÛã_¢óx5~ÎNÞ‹€ÍÚ˜¦­zêÜ{¸v͹3 hÒ¤‰š¡ê…¸¶`­šµÔ<`¨k÷êÕ«UÍš5ÞÇdËÓóÏ?o(øûßÿ®Ê•+ç𵃃ƒ=ˆððpŠJø)pÝ5û/+ l}]ib¬÷ºÚºu«WÆWb¾÷Þ{6ÿ{= ß#žM¢·—Z²÷¤6~ªW¿‘ÛöÇÞvëmê–›oѵwÞy§ªT©’ÇF@Hi¦%3üRô›ù:öª¦¯Iä=øy¿”GfoWºÒVú¸ê(¯5hà mfOÏ€7??_Íœ9SU®\Ùa˜)ÍN|È‘†zîF¶1p$ …V?þø£ÛY5dëk9ydàÀ^ då“„¶þû˜1c ß#BÎò|à‹v?§í iØÌ­Gh]ÞC@VHƒ=ù¿ÚŒüE²r@Y–/K]m5”ÿ]¿²Ú@VH/3ùRäk¯Y­º<85hª©1>mE,ï ÀÈižÚ¨éÝ.½÷õîÕ[mß¾]×ÀWŠöíÛ;<¡dÍš5ºG¯Wlß¾} ²²²(,á3Ξ=ë¶âÿ«¯¾²ùu£¢¢ ³é(}Úæ×NOO×}=­_¿Þk[,僴´4›ÿ]¶&½?lˆ¤Ñ2ϰ!Uõ1YÕ­×вa€ËÔRí» PãçìÔÎçïકR¡'´“?š·vÝ)Ò©[O³À„„‡Çk }ºÃ=¾²´Vþ]ùòåm°»uë¦~ùå›À€t æe±§€£GR`ÂÒ"##Ý:ûÿÆoØüÚé½–Ž?®môF0aÂí³víZ›'È=ÌȽ!¯ ç o“Ùñɋô•š´ò¹¢?8¸®êÔk˜3m½Z°3¿)§HsЮ÷ŒÒEgïOÍ›5WëÖ­s8(–Sš6mjs .+¾ùæ›R€úõëëÌß}÷Ý ¤(°uô`.\p[ñ/RSSm~íÌÌLÝ×’lÝñÖòÿÇk÷YÕTÚ—FІÃÁãE={ôÔºÛË’àI“&Ù<ÖO ý>ú¨Dñ/Û¤¹ªžÁ¼œžâ© 8ԠЄUgÿþùg·ÿŸþ¹ÝSŒ\G]»võJñ/›åw$õêÕ+õßHR£÷…Ä#ŒÙ@XÞ‚ùj¼=jàƒ3´Â[:h× ªå‘b¿^ýƪe»ªïÐÇÔCOnÔzH/þ.ɤV°zúé§í§%KŠ¥‰Ÿ3ƒz™Môd $Ü ð„UÈì¼;‹ÿ>øÀæ×–‚ÞÈ‘yÆ óZððÃ_ê3Ò¼ys—‘ÍÖN?!ýKœ99¥ß=ýTZZšÍÁ³Ì¶mÛÖô ^òžâãã)^xá›ÿîñÇ7|?8˜|†ç€ÿ™¿#WõôˆvôŸ™ `Ö¬YvÒ+V¬P+V44°_¾|¹ÇYµ@ñ o“ ÊÅ¿#ûl}ýÈÈHC×M‹-¼<ôÐC—–ÿË‘¤¶þÝ3Ïzô¨jß¾½ÃA}ݺu=^ü YöL o’ýôîžýÏÊʲùõå$Œüü|Ý׌+þtfù¿|tìØ±ÔW­Z5Ã÷‚¼‚Bž cüœªn½††C€>½ûhË‹íõxòÉ'U… lì'Ožì•@Š/ŠPx‹tÞÿöÛoÝZüþùçv¿‡¸¸8ýEr^žºé¦›¼Œ;öRñÿßÿþW]ýõ¥þ»víÚ¾¤s@ yzk¶jÛå^Ã!@‡ö.¹MLLÔæWÖëׯ¯ÞäëRˆÂ[Μ9ãöÙG«\dŒÞëeÕªU^]þ/=;Š?¤i¢½>FïqœˆœWÃÆ/P5kÖ24oÖ\ÅÅ:žM\²d‰ºûî»Õí·ß®úöí«mðFñ_¼:BÞ­~þùg·ÿ_|ñ…Ýï!,,ÌÐõ¢g;»T©REýñÇ—9Ä• E÷æ€öäªxÕ¤Y[C!@à UB|‚× z3d4)<íÕW_uûìÿñãÇí~ÉÉɺ¯“C‡iÍ:½Œ7N]þÑ£G›ÿ666Öð}`{Ü9îû Þ pO‘êÔk˜¡ õÝ­½:«oøìïƒ)HáQ‡v{ñÿå—_Ú ·ä¿Ùz#ûï½¹ü_¶*üòË/ªlÙ²¥þ;éQ`æ>°–@pA-ßNõü¨¡ w¯Þ^Û×o”,ƒ¦(…'}øá‡^Ÿý7Ô,³jÕª^+þåk_¾ü_V#Øú·Ý»w7Þ 4Ÿ@P ‡f xàVW¦|Þžý/þ>ô^#óæÍóêìÿ#û?oÞ¼«fÿå{³÷9½zõbÿ?¬fØøvWÈŒ¦'‹½K¨G^zé%ÿo¾ù¦®ïIúèí­![aÊ—/ïÕâ¿\¹rZˆråÇØ±cí~Þ¢E‹Øÿ«Y¸«@×®g3˜>}ºG€¬¬,ŠW8MöØ{ªøÿå—_´F}z¾¯ôôtÝ×ÂèÑ£½>û/ü®ü-•*U²ù9²eÁÈ*‡béìÿ€ûõú˜Í gž4„O‰ŠŠÒºñ{*8wï+,,L÷ìÿ‘#GìÙžòâ‹/^Ȫ {ŸÓ©S'S×þÁä3ÜAþlmÄyµ)úœÚwVíI<£=ü£ÓN«Ä#§TJæI­°8rüjG¯ ÿ[òÅϑϕ×K>­ö:£v%œU[cÏ©QçÕšðóüÞRÌÚ˜¦jÔ*5¨U³–6+ï© >>ž"Ny÷Ýw=Vüûí·Za¯çû’z¯ƒGyÄëÅ÷îÝUi:t°ûy+V¬0uí¯ä ðyRx‡^,Â#RN«ÃÇNªc9E*'¿PðBgñâ=†Y¹…*ùâ÷"݆÷:£6ÇœS«Âø[!°uì9Äæ*€Í›7Ó>¡°°ÐcÅ¿ÈÉÉqùìff¦ÖEßÛ€,ã¿òã­·Þ²û97ÜpƒÊÍÍeù? fó·ÇUŸQ GNi3ò'¼S䛕-Á@æImAq0°’¿-ÄÄÏÚ &=>É#×`~~>E,L“#ód?¾§Šÿ?þX÷÷&[[ô^'Nôzñ¢õwåÇÓO?m÷óhêÚ—±÷a€Ågö÷_|`ˬ¾ÌèûR¡o¨ )ø_( Û$àoµìÙ³ª¶f€C‡õÈõ&ûž)daÆÔçŸîÑÙÿäädÝÇêý—&VØû¿{÷ÿ?ÿüSÝ~ûív?oçΦ®ýu,ÿXn†OâYmvÿxžÿüŽäj¡‡ÌVHÿÞð«m=—téÒÅ#×Wbb"Å,Lyá…|¸×‹ÿjÕª©üãWÐÙû¼;î¸CwÐq¹´¬“ÜAÞ&{âwÄŸÕ–ÃK“½@-øÉÍ/TIGOjáçÃ×|li©@ãF=r=Éé³0êèÑ£-þþùg­{ö¿  @wL9BÏÛÀ¢E‹Jmþ'+ì}Þ¸qãL]÷Ï&±üà+/ýÒ°Of¸}mÿ¾ÈïLÂùÒT¾hÆúd›}¤Ù™[ôÜ\ŠY§~üñG§NÒýý¥¤¤è¾zöìéõâÿºë®S_}õÕUÅ¿œvP®\9»Ÿcª1ïZ–ÿƒ½ßãáÇuÁ'TíÚµ-;v¬Ô`Ê”)v?¯sçΦ®û”L–ÿƒ\j}äy“vJåPp[¹q ­È‘‚°Š;óJ –-[æ¶ë ))‰ÂºHþÓO?y|ößHã?#³ÿóæÍ³Dñ/Küÿú믫Šÿýë_ªJ•*v?wåÊ•¦®û½‡Xþp‰Qçµ=ç'èäïS¤’4 ä= o ®]ïê-ËÜ·@š¹QÜÂ9&òË/¿ôxñÿé§Ÿªýû÷ëþ>%ÐÒó¾?~ü¸ºùæ›-ÈŠ…Ò>¤7‡½Ï«\¹²v‚‡™ð›åÿ 'mŽ9§CG1íÛŽ/R;h/º«eõÈÊÊ¢¸….o¿ý¶Ç‹Ùj`d…Šzgÿ'Nœh‰â_–6û/½{÷¶û¹ƒ 2uÝËqÃÜkA&ÉòqÙKçKâ ´YÙ‹˜žž®ÍJ$$$hË>…ÌFÄÆÆjç ‹èèh¥"##5Úÿ&ÿ.11QûüÔÔT­A‘¼¦ì5–Ù ½G3y“üMÙoèØcðUÀŠ+Üò>—ë”⎜={ÖãÅ¿8þ¼¡ïSïì¿<ß*Uªd‰@z*”öñÉ'Ÿ8<šp×®]¦®û=‰„Ü Ãdù\Lºï-õ—_ ò””mð/½îžî._O‚C‡iƒ1 ¬ $dœRë"Y* Ïé;lâUÀÊ+]þÞ–hrV:.ì‘£÷¼QüËv#ïOù·zgÿ‡n‰â¿}ûöÊÖ‡„~ö>7((Ètï9•ˆ{-À9ÎO:É[¾Û}n®:räˆ6+"Ŷ‘}”Þ"a„¬6€BV äååY¢Y œÀ  ž0üÑEW+]ÈõE {d˜7šþ Y=fä{•¯ç}/Ï9:Ï €<£KûøÏþ£‚ƒƒí~îŒ3Ì5ýdù?@¿-1çÔ‘ãE–,öeæCôÉÉÉÚ¬¾?ÍìIó) 1d¿²7Ç9ù…jÒµ2Œkî3jÒÊ«€­[·º~uËÅâŽ"¶Èö®¯¿þÚ+ÅÿK/½dø!+Zô¼ï{öìi‰â¿[·n6gÿeûœ½Ï½á†´Usf®ûЖÿƒZq^;.ÎjENNŽ6S.µ@”J°!…‹ì›Ô;àsG£@úÀmÀã+® d«—ÿ{zÛ|‡¬ÄúüóϽRüûí·ZAoäû••nºŽ¾Û»×á¾zO9yò¤Í S§Nv?÷0uÝg窕ÜcAö8|FåZj¿ÌÈ-Щ²¥AJ½{?]¹- ,ù4×\îþ‰Ë¯ \½úE & ]Ø Y?úè#¯ÿBÂ]£+ô¾ï¥ã¾Šÿ¾}ûÚ,þ/\¸`÷s¯¹æ­wŽ©#?yfl[q^;Þ E¿ þe ¼ÑY‘@#a€Ùe‘fed©Q4 „ëŒ|li‰â?¤~ˆËß·r­pÏ@i¡ê;ï¼ãµâÿõ×_7ü=ë ÇÖ¯_o‰â_H‘oëCf÷í}n÷îÝÍm,xNk^Ì=PŠñg½ÞäOfú¥K?]º“åÒ2‹äÉÕ²R„k®0bÂâ@Ç]Þ/Ä‚Âó^{í5¯ÿ?üðƒvœ¬ÑÐWï–—:uêX¢ø4hÍâÿÓO?uØ 044Ô\è—qŠû+àJ«Â.¨Øtïíõ—AŠ,Í•–Á¨kö±ÊöOõ HË:©6pd \| À ƒ\ú>•k‚û®ôüóÏ{­øÒÓÆèjù=ïùyóæY¢ø—þ/¿ü²Í`îܹv?¿Q£F¦¯ûgè[(IŽGs¼Óá_f䤙_XXQ7ß«ü~=Ñ' ¿ P;&’k f ¿ Dð裺ô=Ã}%œ>}Ú«Åÿ»ï¾kø{Ö»^VÓÝ|óÍ–FŽi³ø—ßC•*Uì~þâÅ‹M]ó©™ý(áÙ¤3Ú2nOþ²_]:Ú³×3¤ë¹“è‰qé§8.¦ 3«D°pÁB—½/óòò¸ „‚‚¯ÿ?ýô“áPJîåz݉'Z¢ø—æ}o¾ù¦Í`ÇŽv?¿zõêÚõk溗mÜ[A­¿ ’Žy¾ÑŸ,ó¤£û¬¸5ÀÍeKM—`TÏcKRì¸ê=™ššÊ=—È}ð·ß~ój ¬Ñï[VtéÝîR©R%K?ü°Íâÿ¿ÿý¯ªW¯žÝÏì±ÇÌ5Î-ä¾ (îò/ç¹{²ð?vì˜á&GpYB*³_n=Ááâà‹½—0¢]—þ—ŠÿàZÁ*77×eïG‚GËÈÈP¿þú«W‹9nÐÌ–.½«¸FŒa‰â_û½ÿþû6€´´4»Ÿ_¾|y-Ì0sÍÓ ð÷ûÏó\—iTË Ó¢ý$˜qw_€],Á„NMšµ¹tíÒÕ¥÷!®yÏ ÿüóÏ^-þ%|-pf‚ ½½.uÔ÷™½·÷ѵkW»Ÿ?`ÀÓÏžÕ¬B€@·=îœÊ+(ôXs?9ʧõÅÇÇ»½I`xòi®AØw༪U«ö¥`üøñ.{ÿ>|˜kÚ4oÿBýÞeÞ÷{«V­,Qü_wÝuê“O>±Yü¿ð _C¶­™jø™ÎÑ àö$žU'Nxæ8?™a‘½» 8}ÇÁƒµÁ±;ß‡ŽžÒŽ›äzDiæoÏ)±ÿÍê5.»'Éû›ë<°IG|o/ûŸ}ö™©æ·zWkÍŸ?ßÅ¿˜6mšÝÙÿxÀîç·nÝÚôu¿1ŠÙ`aɧ=2ë/Ë9ÎÏ÷—Ǻ»9 !J3me\‰@–H»ê¾ÄµØ$òvÃ?!߃™Õ(²¤_×ý5-MU®\ÙŵjÕÔ?ü`³øçw´Óì½ÆúõëM]óÉÇ8úÔÊ‹äH6wþÒ¨‹[þÕ žöȬ헊ÿ† Òü.!Ëí½]ø»pá‚éÕ zÞë}úô±Ììÿž={ìÎþ7Îîç™>¶vG}g@ @:zÒ#³þ,÷÷?qqq¦_„0cÀÓ/Ç wÉûLŽzãz\Ï?ÿ¼eŠYúoæY©7Ý´i“eŠéA ÇûÙúøðÃ6)œ1c†¹‡rЏŸ‚@`ŠÏpïÌ¿!g¦‹1|Gtt´[›àrm:÷»¬^½Ú%ï1ipɵxdý«¯¾j™âÿ—_~1õ¼”-uzŽk•wÜq‡%Šÿ2eʨ3gÎØýŸ8q¢Ý׸ᆴðÎÌ5ÿlGÿ@ŠNsïžy0‡‡‡3Ð ÒÙ•g± 4Ë÷ŸSÁµëiÅP —4¤ÌËË3Õl ¾ßÐôí·ß¶Lñ/d5•ÙS ô¼×Gm™Ùÿ±cÇÚ-þ?ýôSU®\9»¯!ÍM]ó…oÔ¨‘efÿ·lÙb·øÿòË/Uùòåí¾FÿþýM]óù…jmGÿ@Ù{È}Å¿œ?Ì9Ú¬;&qtSÀê9`ì¥@ï̧=²tšk6pÈñwß}÷¥Šaö}¨÷8Ö'Ÿ|Ò2Å“&MÔŸþi7˜9s¦Ýׯ€Ò·ÃÌ5/«¹—‚@ÀM8{±0s_—–ü£˜ÙÁ™^ âRÓæí´â¿e‹–ýCNž<©~ûí7Ëÿ/¼ð‚é¾+z‚VY&_±bEËÒ0ÖÞÇ·ß~ëðû8p ©ë='¿P­ç> bkì9Uà¦â_fU`âJ©©©n $ÐâÚó·ç¨5‚´à± qôtï÷·Z³¿bŸþ¹éãq333u½Ï;vìh™âÿþûïWŽ>æÍ›çpößìÞÿýtþ€@!ûݎ纥£ìíOÍÊÊr[_ðœÚ}Žk<@Œ™¶þÒò9zÒÙ÷Ç“ú¿¤¤$­Ùœ‹9òO Y3?—¼wõ¼Ç—/_n™â¿R¥Jê³Ï>³[üÿðÃÚÑ~ö^gÈ!¦®÷¬ÜBµ’þ1 (R2Oºg/öÅÁƒL8j (!ÝvÔd EçÞõâ¿{·î·GùùlYòsZ‘mÅâ_š¾§ÊûWOOžêÕ«[&X³fÃÙÿ%K–Ø}ë®»N>|ØÔ5¿;‘c  "Ütܳg0ÒuÛMÓ²Š˜ÙñsËž=£j×®§+V¬pÉ1¥\›þ{ék¯½fÙÂ_¼÷Þ{¦¾ôôt]ïñAƒY¦ø¯W¯žúý÷ßíÿ?ÿü³ªR¥ŠÝ×1b„¹fŸÇ‹¸‚@`ØwVpq±%…\\\M’˜˜¨û=&Ëb{÷î­¶nݪûsâ2NqÍû±‰ žÕŠÿúõê«ãÇsôl6•}õV.þe™»Ùæ“z·¾ìܹS•)SÆ2€¬Fpô±páB»¯!Çê=õàJ2â> ~o]äy•“çÚâ_–TÊÑ[ 4a†4]Ó;x•Ÿ `‡®½ïô|Þ³4xò[÷ \ ÷Gÿ¡T¹¹¹ê§Ÿ~²tñ/¤6ÛS%''Çáû[¶\[¦ø—ŽýŽ>¾üòK­G€½×5j”©ë=5“Ù²:-ËõK­ccchÂ4½ç¶Ë¶B… —~={öÔµçUšnˆ¤€?jÔôn-£Ïœ½É,*×£ÿC~ø¡å ñÒK/™þ9õî}ôÑG-Süˬý|à0˜4i’Ýבçœ6dæzßC£XÑ©®oú'ƒ›p–ÞSºvíZbØ­[7]ÍS3Orð33Ö%kÅ—.]œ¾ÉûëÐŽ÷{á…Ôo¿ýæÅÿ_|¡5ð3û³êé£"áV¹rå,,^¼ØañÿÎ;ïhGûÙ{1cƘkT|”çøå`kì9—ÿGeÀ —뇵´ã«:uê¤+Øwˆ­þdÀÓµ`Ù²e¬b‚Fîßÿ½Oþâ×_uêÔÙ6 §·E‹-,SüתUKýãÿpHc?GÇfdd˜èõñœÚÅŠ0ðËü}éÿE©Ù.-þeÏátÂeô f¥Ñ[ÅŠ¯ ¶oß^Ûëkw AA¡Öƒ{‚?tÿ?«ê7hªêÕ­§»‡„Í##/~>ןo“"úã?ö™Â¿XQQ‘S õ¼¿Ÿ~úiËÿ"99Ùañᇯ3vìXS×;aA@„ÝÑÇ]ÞôO–2ð„+EFFêzÿÉI¥ ûôéãðs“±ôÓ<2{»6ûÿðC;}?ãôß%½dÿ¼¯,÷¿Üûï¿ïÔ‘†z𠦦¦ªÊ•+[¦øà”ž=zØ}믿^[hôZÏ/8¡ÖEƒ€ðskžRÇçÐô>A:±;zÿ­]»ÖæÀð©§žrøù{9úÉ×µï6@ ÂÃÂ^ÉÄuç{¤ó½Àrtž¯þÅGþ9Ót233S×û»W¯^–)þï¼óNõã?:,þeEŽ£×’††f®÷¨ÔÓÜ?A@ø¿½û#iúŸšÑÓs€­Y-ij÷ósó ÕZf|ÖœMGTAªSÇNNßϸî|ìijgϪï¾ûÎ' ÿbRÀ;sºž÷¶½ Ôdû–£¿þúËa¿¹÷ë @J„}y'Ôêpîû üÜ‚µÏº´øçœlx‚žÆN÷ÝwŸÍbõêÕµ¥¯ö>ÿ] }·ùßè§´Ùé$îÌýLzFÈL2לoÜÞzë-Ÿ\꥗_~Ù©€TO×Ùg_¥JËÿO<ñ„®¥ÿ²ºÐÑkMœ8ÑÔõ~0™&° üÜì )*)é°ËŠIïiúOÍò9z?nÞ¼Ùî ±yóæ*??ßþkp´ï5ÿÛV…4¼KÕ©]G÷Ñ‘6C C‡¸Þ,¾Hšä}ùå—>_ôûì³ÏLù'a•ž†—´jÕÊ2ÅHHˆ®®ÿüñ‡ªS§ŽÝ×’PÃÌuŸ•óœZÆý€ŸwÈ^°h™Kgÿ£¢¢”Âcû{ï²ÿ÷¦›n²;X¼ÿþûíÏ’e² À׌Ÿ³S›ýðÁºŸÉ6fÿ­I¶™½òÊ+êçŸö›Â_ÈþwgNʬ¾ž÷¶Ì[¥ø¿öÚkÕ¹sçtÍþïØ±ÃáëMž<ÙÔõ¾#޾/ üܘ)Ë´æV®*þÓÒҘ£ÒÓÓ¾/ûõëçpÀ¸fÍ»¯±…U>¥C÷A*¨FH:sOsæìu¸–¬,“ëýüùóÚ ¹?ý—“cNÍþŽäý®géÿž={´¢Û*€lÓÑó![;n¹å»¯%¯¬D4z­Ç§qïàßænÉTsç>í²â_fÊXúO“½ ŽÞ› .t8•A¥½†Q)¬ð{ÛæcZñ?xð`§îi²º„{šwû”PY–÷¿ûî»~7Ó_ 7œY¥'ЗûÜwÜa™â_¶!üûßÿÖ,[¶ÌáëM›6Í𵞙«ÖÐø€¿ëußNïåŒlXå˜/G'Rèˆ>Üîël‹e€/4f¦¶ü_ÏIޤq•~¶¼\Sr¾zqÁyæÌõüóÏ«_|Q]¸pAûÿKá.³ÑÒDQž5Ò /%%Eë© Ï ù¿RàKA*²üû^xA;÷þûï¿÷ûbÿJòs;³ÝD~—zÞ×½{÷¶Lñ_¡BõÆoè*þeÕG¥J•쾞4v5³ª14žcÿ@@ø¹G熚JÉmqfÉ"à‰ÓjÖ¬ép0zÍ5ר}ûöÙÞâ’Å*«[¾ÿœjШ™0`€S÷4 •Ì6aó'r½ïr¤žt×ÿꫯ®0÷„o¿ýV[ñàΆ¨bÁ‚–:òoË–-JïǨQ£¾ÞŒ3 _ë‘ñéÜ;A@ø·%{Oªæ-Új³7®(þe†'<<œB^#³‰ŽÞ§C† Ñ݉ÚÞŠ‚íq¬°z¸)³ÿR¼;s_“ã!uv_ž 2›ÿÅ_Pœ{À¯¿þêÔjÙ¦"+(½§%$¨X±¢eŠÿ=z¨¿þúKWñ_XXèðõj×®í°)ìU]ÿ³²Õº–þƒ€ðs÷zÒôù¸¶–WS„ÂêÛV®\©{`:uêT›¯“žE£(+ëØc°êo§CM)„åú‘°D–éðÁZ1JQîYò~sæïwäÈ]=z4h`™âÿÆoTŸ|ò‰®âÿ?ÿùv\«£×ܺu«ák}ãžÃÜ7A@ø·ë’U:uµÙ-Wÿ²×Ž#²`öø $—)SF×àTfÉd²Í^¬°¤§·dª  šÚûÓLKHHÐfúåØ9 qïxýõ×úÆÇÇëzOëY>ïIÒŸCïÇîÝ»u­&0z‡EÄrßàÿÚw Æç²ÙÙJñ +ÐjÉò~½ÔÎ;Û|Ä#§¸ŸXÐÀg¨¾}û²¥ÉÁrq n?üðC p/ûüóÏZi"Ÿ«gÉû3Ï<£;üô„¡C‡ê.þøáU­Z5»¯W¾|y»­­É‹•ûò¹o‚€ðoÓVÆi{câ\RüËŒ+…'¬4›éè=;räHCU[KJó žS«Ã¹§XÉ¢ÐçTº!ÚùæÎÜפ¡¤?^2S,³ýRPQ|{ßO?ý¤ýMܹê©x5ËM7Ýd™âÿÖ[oUß}÷îà‰'žpøš=ö˜áë|ÃÎhî› ü_§^ÃT×.]]6û/ç¯SxÂJg†»º¶œOmëµö:Ã}ÅB?Ýceèî™ üßÓ[³TÍšÁ*¸V°:vì˜ÓƒdYz(ÇHQ¼ÂY2 sõ¹Øzf„dªÑ¬¡%ËÃK{½íqç¸×xÉÒ}§UHûԖ-[œº¯=zÔ'¯!i~&³ÊØÖ'ï3gþÖ²$^Ï{yéÒ¥–*þ  þúë/ÝÅÿG}¤*T¨`÷5o¾ùfm•ƒ‘k|Kh8÷LáÞ‘S´ÙÿA¹dößÙ  xæßÖ@YV˜íÄ.çb;zËl”™lïÞ¥w˜OÈ8ŽÆKFMZ©:wêp³ÿr´Û§Ÿ~Jaí#Þxã §O8qÔàTÈvªë¯¿Þ2Å:u´ã| :ÔáëÊ #×wrrŠZ¹ÿ,÷LþoÙÅ^½ÆZ°~ýz—R`QÀÂÉÉÉÚrX{f³½líÕ¿rlf0+{NKë1Ç6¯Üßž=«6n¡vìpn↓Œò¥åþÒ/ƒ¢Úw|ñÅN=7eÅžcîòóóU“&M,SüË,þ‹/¾h¨ø—kÑÑë6oÞÜÐõ-¿—¥ÛsÏ€ÀðØü}Zñ/Ýÿ³²²œ.þå5(`ál£2=K–eO³3[ 5±4;¨5kVé§DÒXÊÓF?±Ö%MiÊæIÒßâË/¿¤¨ö!2ûíì‰9z·î=ôÐC–Zú/Á…‘Yùå¨ñŸ4Ô³Êër+Öíà~ £Gÿ1ZбCGšÿÁëdv^öwê8¿û¿ŽÌø8z/ßpà ¦µ¶fŸö:Ã=Çë›ßÕZ…‡…;uOËÌÌô‰ëæÔ©SWÍÀZ¤ uöd‰Ã‡ëÛß¾e‹Ö§Ä*Åÿ£>ªŒ~L›6Íáë6ÌÐõ½{÷^µd/[´ `ŽÆ:¥jש¯Œ{Ä%€?œ‘ ï9}ú´îÁówß}çTc4GïeG3MöšÊ9ëô𮇞ܨ†îô=-::ÚÒ׌4µ”sã)¨}¬Dröo¯ç=§*W®l™â¿eË–ê_ÿú—¡â_ž ŽŒ*UªhŠÞk[údL_Áý Ǹ™[µâ_HÓ3gÊ999±pj&ËÈ ¦ÌžȨ́™¯¥çœl™É7;À4iÒÕÛc8Ðc–8¯šµê¨»+º¯ÎþËûøÇ¤˜öA/¼ð‚S{Yæ®g%“¼‡ƒƒƒ-SüW­ZU}øá‡†Šÿßÿ]5jÔÈákÏ™3ÇБž˜Ïý K§^Ã.zÎF§û?ÜÙ¸ÌÌÞe³³³zú]ôèÑÃô ·nݺ¥¾æºúxÂØ[Ô#8¿ªIfX­x½È ¯½ö…´zçwL‡—Å[>ôÜäÈmß¾½eŠ™Á—z£ÒÑßÑk7lØP×)ÅæÌ§…žà~ C|5kkÅÿ]MïrÉò_i”ë‘NÐfÒ²jÀ]M³d/©3ƒ])Ò®ÚošÈ1Snw༺»]w•žžî— MSSSÕ×_M!í£>ûì3§OÊ)í¤‘ÒÜÿý–jú·páBÃÅÿ«¯¾ªÊ•+ç0Xؽ{·îk{ïÞ½êѹ»¸W@àuÇ.žý6t˜ÓÅ¿,E¤…2#dv0}ôèQ·  'L˜àÔ`W:n_µ—>˜ý߬ž|òI§ïi±±±–»V¤ÑŸl}¡öMÒ·ÄÙU%z›þÍ›7ÏRů^½ÔþóCÅ¿üû¶mÛ:|í{ï½W÷u-Ï›A÷Oä^ §C÷—€ùóç;=X–Ù6ŠY© ŠÍ¨ L}]iþäp‰èœ9N xå¼í«Î“Ï)âþãÎÎÿÏžQmÚwSÙÙÙNÝÏäó­Öåÿ•W^¡ˆöa?ÿü³éKÅâããu½wîܩʖ-k™â¿FÚÑ®F?ääG¯]©R%mUŒÞ}ÿ£FU‹÷p€lU§NÈ¥À虹¥‘ -Œzë­·œTŸ9sÆô2jGïé 85蕸4ƼòuW…qr—‘—i7šý—­$ï¿ÿ>E´÷çì–éw¢g»4¾”F{V)þeù¾Ü§~H£@)î½þÔ©Su_×óæÍWÓ×$r¯€ÀóÔÚ¤KÅ¿(­Háø?¸[^^žÓë—^zÉÔ×Ö³?\ö«:;ø•™¸+_wcÝañîBÕ¾cí}å̽L:ë[å‘ â‹/¾ ˆöq'Ožt:ÒÓñ_Bi@j¥¥ÿÛ·oWf>úôéãðµåt=¿!fGO^ɽ Ó¨I+/ÿ6rºøwö,c9—ú§Ÿ~rz`ýöÛo›úúÒ;À@iÇ ; 3S­]»Öéû™¼7­r,æ÷ßOíã^~ùe§OHÑÒËs¸sçΖ*þGeªø—ÀCÏëoݺU÷qžG>Ê} W÷þc.;uvzÀlµý²°6ÙÏüñÇ»dpýÉ'Ÿ˜úd@èè}½hÑ"§À:uºêu÷%á>äbó¶ç¨nÝ{:Ìʳÿòþ”=ãоM¶nxâ¸?!MG­Tü7jÔHûý.nºé&‡¯?`ÀÝ×õ˜‡Çsä@¶FMï¾ 4Øé@:ªSØB¯³gϺl€ýå—_šú¤Ðsô¾^¼x±Óƒà*Uª\õº‘©§¹¹XŸ!JÝnዽLŠŠŠèôïäÞäìÖ8½Çý¹â^åJ7ÜpƒÖßÅÌÇÈ‘#¾þ­·Þª+ÄK—.USWÄpŸ€ž)Ûv¼ÄþÿG}ÔéAsRR…-tIIIqiqóù矛î?àè}½dÉ— †¥yW‰&]G8 ЕfmLS÷ö»×éû˜,³ööʘ_|‘âÙüðÃNo%INNÖõ¾Ý³gºîºë,$$$˜*þ¥9«ž×æ™gtýn¢¢¢Ôý—pŸ€ÀöÈìí%€¹sæ:=pvö\cÙ×)Ë;]9Ðþì³ÏL}/z–ŠËÌ‘+ÃÒK Ä‘™YAåJ]ûŽÔÞ[ÎÞǤhñÖµ!û¼ß}÷]Šg?ð믿jA§3ïy/ê ß«U«f©âúô馊y6Ü|óÍ_ÿ¾ûîÓ½gÐÈGÔŠ4]€wïÈ)%€õë×;=p–Á+.yýõ×]>ØþôÓOMÍ´zrYí•«lŽçr/rYÚ;bø§ïa¹¹¹^».8 Þ{ï=Šg?áìJ Ôõ”Rà†„„Xªøïر£ú÷¿ÿm¸øÿ믿Խ÷Þëðõo¹åÝKÿÇ=2A-Ø™Ç} кc߀ÞbÈ9‚‡âŽÈ Øƒm3MÃÂÂt½·Ÿ|òI— ŠX²[÷E+ø¹Bû®÷jçž;Èkx«øçw(œý„ô7qæý =ôk'A÷îÝ-UüKq.GVšùþz¾ÆÆu]Ï ,P“—Dpˆ†[”d©¢/ï›…õÉYæ®8ò¯4}ô‘áï'22Ò£]µÛµkwÕk¯dYª³›·W=òÈ#.™ýw¦S»3{þß|óM g?!+œœy?H0)ïE=ïYyß[©ø¿öÚkµàÂÌÇo¼¡*T¨àðkôïß_×ïfwèn5tÜÓÜ#bùóªfP­€žó…9Î9R¤»kÐýá‡þž®lÊg‹ì5uÅà¸víÚW½ö†(gïem:ôPééé>9û/×…;¶ÄÀ;>øàm5‡3= ô>‹—/_®Ê”)c©@¶šùøã?T‹-¾¾ô8v옮žÝï¦Ý¸O´²Kÿ®d?….l9sæŒÛÞF¿'½KÆe?«+Ç•*Uºêµ78eÌ“´-Îÿr„7fÿ_yå g?ññÇk³÷ÎÿzŽ%òïË—/o©âìØ±ÊìÇœ9st} 6èê‰Ð£g_5o{÷H€Àå ³® ô–cõtÉ÷¤.]º¨ßÿÝTñ/[ô¬dæ€z~?Æ W<Ë= PâÀYÛ¯ œDgddPì¢ÔNÖß}÷Ûà²Ôè÷¦÷=Ûm·¹l |å1uæ t‘Z¸`¡OÎþÿío£pöÒðNú‰8SügeeéîµãÊ@ÒêÖ­«¾ÿþ{SÅ¿ô„©Q£†®¥ÿ2Éàè÷3sÆL5`ôSÜWùØÒ«½GêØ"³©¼¸²™ÕgŸ}æ‘Aø¹sç ï½ÖsÄ–påRÛíÛ·—xíg¢ÎqO2aQè Õ¾Cg]Òõìöäu!ïU gÿðÕW_©¨¨(§z@è}öJPåªíH®R¥JõÖ[o™^ú?räH—-ýߺu«jݱ·Z¶ÿ,÷H€À•îùÄU€žÆ:öÈ2oŠ^\îí·ßöØ@ÜèùízO8|ø°KÌ;vì(D˜qßOªuk×9]ü8մͨS§NQ8û‰o¾ùF[áäLñ¯÷¹+ae=,Uü—-[V %Ì~ÈQ‡z¾ÎСCþ~âââT³mÔ¼mǹ?JÓµïýWz–×9Ú—HÑ‹b.\ðè`Üh¥· Ì<¹3ØD`ØÓ[³T·nÝu¯à°ÊÊ%™é¥pöß~û­Vt:Süë}æÊû\޾³Rñ/öíÛgºø—S[*W®ìðkÔ©SÇaƒb¹®Úµë ¦.áþ°¥uÇ>WGŽ!€KȬ§äF÷àê=6nÒ¤I.4ïܹ“ÀI½ŽS»vîrºøÎ4n3"!!AÛïLñìûd¿»ü=y?yÞ>ÜrÅÿ¬Y³LÿÿùÏtme¸îºë®ê™RZ8r_ÿûÔƒS×qoØÓª}Ï«iâG8KÞ¿þú«Gä?þø£ÛöîÝÛ¥ç]»J®› ™±>Yõëw¯KŠY~í‰kBŠÙ+Nñìûä^ãì‰Fžµ=ôåŠÿªÿþ÷¿¦€•+Wêú:O=õ”ÃßÏ”ÉSÔ=ÃçÞpäîö½® RSS9N‘%±?üðƒWŽàrW@Y‚êÊÁshhh‰×_É)Ftî5Ôᬠ^Îìß6âwÞ¡xö²‚Cz‚8ó^лòHLœ8ÑrÅ‹-´ß…ÙóçÏk½}Y!àè÷³nÝ:Õ¦Ó=4ýè :ô¾*8xð O̦Áš¤(ûòË/½207z Þ€²•AÏ`Õlp⢕Üt›²4R>Â%ÅÿñãÇéøÝ~þùg-$wvu”Þ÷çôéÓ-Wüß~ûíÚ©.f?äw(G:ú:ÕªUsø»’{x³mÕ‚yÜf=ÇìØ#çS&™Qï½÷¼68?yò¤[J(æêAôž={þ_šWÈýÈ€¶ûêþÛ9"¯CÓ?èñË/¿8½ÂÍÈÌÿ¼yóT™2e,UüW¬XQýío3]üÿõ×_ÚÖG_G~îM›69\mx÷ÝmÔôÕ‰ÜÎ4œ;g®ǪÁz^|ñE¯ÐîÉÕ{îöÌ™3]>ŽÿÙì"îG:Ÿ³SÛíŠâ???_ ­húG¤Ÿ‰të÷D·±lÙ2ËÿòýÈÖg>Ö¬Y£ëk=ðÀ¯Ý>½û¨±36s_ ú^Œd¼SƒjY.M1xdöÝ›ôï¾ûÎÐ÷kd«K§N\>–ë¤øõ“Žžä~¤ÃòýçTóVµ"Ä€Ñ##iú˜~ûí7§V¶8p@wØ(Ö®]«®½öZË-ý—ïË™ÜÜ\uÍ5×8ü:!!!%î¥yðÁUÿQÓ¸/\ 0ЩAµ4U£ ,§OŸöú ýÕW_5ô=Ëj½–,{uå@ºzõê%›Ð¥â~¤Ãè'Ö¨±uIñ/÷©°°0·^ï¾û.´ÿrþ¼Ù÷€zOÑNÙ¼Y•+WÎrÅÿ¸qãœ*þ¥gÀÍ7ßìðëT¨PAkÊi·/“ÓUûnÔò4NÖ¦ÓÕ@ûví\»{` ë°Js3£[OôÊ·mÛæòÁtãÆKö8|†û‘KöžT›¶TÉÉÉ. äüuw^ò5( }Ù¿3[Úä9(áÞ÷¤ *°ÕŠÿ®]»ª?þøÃtñ/ŸÛ®];]_ëé§Ÿ¶û;Z±b…j|WµpW÷E€€¹àž«€zuë9=¸Ž¥8.\°Ì,tƒ6²4[ï{Y–šê(W®\Y׿ëÞ½{‰¯±;‘ã«:nžzdÜ#.)þETT”Û® ™Ádß¿ïõ' û̾"""´%ïzßÒ#àú믷\ñ_¯^=íHWg>žxâ ]_«GvG;¶ïPuë5P3Ö'sO˜Õ¥ÏÈ«!Gù93¸NJJ¢@ös/¼ð‚eëŸ|ò‰¡ïÝÈòúõë뼎;VMžYâë¬g/«ýûÔ§›“^NN`p×õ!áE´ïúâ‹/´fÿþRðZ-'=A¬Vü—-[V 1œù¾,•*Urøµd僽}ÿ2¡Ð¤I5è¡ÙÜκâòR€•+Vr ®§5s²Z“.t™Óû>ž6mš®ÁòúõëµïâÅ‹uýû 6\úGs8О§·f©ºuë«ÔÔTËý'Áé÷ßO!í£>üðCíohöï/ż4—Ôû^”UZ­ør8óñË/¿èZ=%«œ.`ëw$G'¶mÓVuê5L­ é@ÀyÎ -5xpôƒœ€¤išÌr[mÐþæ›oú9Œ,#¯[·®Ã¬k%³†òñðÃëðÊï²økÄ¥s€=}‡>¦}ôQ—Íþ§¥¥¹íyå•W(¤}”ÜGä¸>³û„„CÅ¿ZAAA–,þçÌ™ãTñÿ×_©!C†è>]ÀÖïHz(ôêÙK5¿»³Z¼»û!@ÀžZ›TjвEK§ÚÎ̤ÀZd‰½Ì´[qàn´ st¾t±½{÷êÀöë×ïÒÀWºe;ú÷µjÕ*ñuöÐÐöìÿ–L—ÎþkG.:±¼ÛiG!훞þy§þöFO¦PO¸è­ãþ¤€wæCVDéùZíÛ··šÈÿ>lè0Õ°q 5o[6÷C€€«, }®Ô@8ÛpKfD(ž}›,iµòYæŸþ¹áŸGïûwРAº±²Ÿ¼ø£fÍšÿ}ÿþýK|õ‘,kµ¥Ï j„ .+þ%üqW_Œ¯¾úŠbÚ™þ»ËŠY¢nä=( HëÔ©cÉâèСê?ÿùSÅ¿ô?¸öÚk~­5jØýÝM|l¢ªS—Žÿ·¨S7¤Ô`Íš5–>gî#[™õ—}œV¼Ë`Óhƒ6½=,ôÉU¥JõÏþSøÊQYz^~Îõñ<–µÚ2wK¦ªW/D¥§¥[~ùÿùóç)¦}̯¿þêT¯9JT>ßÈûOúÜzë­–,þûôé£~ÿýw§ŠÙ ¥çç«P¡‚Ý£XçÏŸ¯jÕª­&/‰à^pÙ_WZ0öá±N7Û¢˜ö=2+óõ×_[~/ýÂÂÂtÿ\2S¯÷½»hÑ"]ƒæÇüÒàW–€ëùœË›:z’{ ½?ªÍºªøFšEê%[ ¤˜¤¨ö­cþd[‰3þó'BCCUåÊ•-YüwèÐAýýïwªøÿã?TÇŽu}½eË–Ùü=mܸQÕTãfnå>p—N½†–´iÝÆ’n¸‡lÙð¥óËO:å–£ÿDÓ¦Mu de•Dñ‡4Îrô兩|ÏëþÃg¸•bÎæ£Ú™ßªX}ùÿßþö7Šj"÷8 áÌþ½ 5ûÒå¾|ùò–,þ›7o®~úé'åìÇØ±cu}½Q£FÙü=íÛ·OÕ®­FLXÌ} àNýGM³ÙÀÙ¸ô ¸¶.™A—™¬?þاñ²ßÚHÇn´ë}ÏnÞ¼Y×@¶Aƒ%ÀÒÐÊÑçÈ Y‰¯}Ž{Pi³ÿƒQO|Ü¥³ÿÎÌøÚ[.Å…µo,ùwf¿¿-$Fßw²åGN ±bñ¢¾ùæ§‹ÿ•+Wêúz­ZµÒ¶m•ö{’#f6h¨ú˜Ì= ànžÞm3¸ü¼r3dÏ5…¶õH1ôꫯªŸþÙ'óÇŽÓý³ÊyÖz;ÿ‹fÍšéÌnß¾ýÒøÿø‡*[¶¬ÃÏ‘½­ìÿw0û¿éˆ ®UÛé&¤žX$«P(®}#0”æ{Î4yÌÌÌ4üž{ì±Ç,Yø iXúÉ'Ÿ8]üKá®çëIo[ rŠB‹æ-T—>#Õò4Exà$€*¨F[úöºuëZ¶ø—Ùú_|Ñéâ_ŽûÓ»Âᡇ*õw%÷Â.»¨Úµë©é«¹ÿ¼A–ßÕ°Ñ æÅAyzšs§ÈR? u×ÏöËêŠ>úÈïõlÈr\wuìž7ožî´li¹ücÉ’%ºBƒg]GÑèêJ=úQuj×1ÕlÍÓËÿeù3ŶõýÉ–'gþ®²½Èè’ÿâ€ûöÛo·ôžiúêŠqãÆéúš}úô)u%4îѽ‡¶Õgâ‚g¹÷¼ÚàîÎ6WÌ;שA¸ hè2H}þùçýz¶ÿJ2ãjäw£û½)Eƒì‹Õ3¨½óÎ;ÕŸþyi0ü×_©Úµk;ü<épéëgùÿ•fmLÓfÿmÍ:٥කÂQp[‡4 ·õ>Éfо]{§â‡¢€w²ð—½ý¿ýö[@ ðÏŸ?o¸ù¡‘cÿ  {0½víÚ«º`ë™}»üû9xø ÷›R:ÿË}&22Ò值¦+¯CY¡@ám o¿ý¶á•A¥:R˜šyoIœ *X¶ø—ÀR~G®øçO™2e~Íààà§\¾§_¿~Úu>dì\î{+?g§Í@„‡…;½WfZ(æ‘s–eùf þâý÷ß7üž‘í&zß“¡¡¡ºµÅÛþóŸ%Å“&MrøyO<ñÄÿ[ sѺ–ÿ_¹ý(8¸Ž¶,ØÕÅ¿põ=Gš¨Q|{¹¿ÑUA¶z§˜m8¹`Á­Ã½U‹ÿ   õÞ{﹤ø—zŽû«V­šö;½òw%'± 0ð=…˜Î} `KöžRuê„Ø Æ?2ÞéÁ¸ (êõAõÊ+¯dá_ÜÉ[f献’Ðû^”A©‘¦]Òüòßÿ]ÝtÓMv?çúë¯/±§=ñÈ©ÿ¿½·¯ã8¶¯˜!vì˜I²Œ’e™A¶ÌÌÌÌÌÌÌvÛa‡©al°§ŒyÓ~m𤔤mš†š&iæ›gzäʲàœÙ=çìž½u]÷õýÞZšÝÙçžgžá]“‹‘36™÷Ë®»\þ£QðwÞ!÷qºv–P^«ÔáŽø‹'"+÷»ß¹üK½‹Ê•+ú7%Bd[^ïÙaÆ™1Þwä\Þyð^*îÔ|@ãF­S%³‘Th² ß*©å2Ù êD_ŽX‹4uûÆo4“ÍpŸÅ™3g†=¡–‚V¹ä>öïÆwÙßK2–›6mêéà?--Mýáp%øÿꫯTfffXwÅŠyÿãÇ7ã;sàd#ûxç À£+r©é­ó²Rç4 @Viɸ2%UŠÝ}uñý÷ß7üm³pŸ?9PÎÄwR½råÊ+&Ç"( ûwýúõ»ìï¾Àên:õ®jת­î»ï¾¨·•1JP$©çÆ·BÞ)N2LDÊþv/ÿ]ºt1+önü|óÍ7j̘1aý]90¯>›:å¿Ù„ÝúŽU[ þàmO\Q`€DN'æ2á'ðÿß^™ì²Ò÷gSð0Òþ‹$x”U©¶mÛ†=©–³½EÊäüùÏþShZ¬ÌYÑþÇYýÏÍêÛ£ÿ•à_pcå8'²¯šà<ºHúºÔÍpã~Éѳ¶Çûe³páBOûFŒ¡þõ¯¹üËûMŽã çïæÞâ”ýŽ>}º™+tî5Bm9÷ï;x•QµjÕÎW¤6NUO>ù¤£I•L"-ð–ˆHðú׿þ5ð“~)4eó<ˆ0ˆä¹‹dß¿ û…sÿH!ËÂþÝ€.û»Gï`õ?7ƒÆ/5ï“£GFM¸]à/ù AzßnflHAÐHŽÍ|ãÂM'"($hwãçÛo¿UÓ¦M ëïŽ9òŠ>{î¹ç.¥ýwì1Dm9Ë‘§ð m:ö.0 `ÙÒeŽ'ç2Á rð/«\ÿøÇ??ñ—“lªzK¾L8Ã}ÞŽ9ö¾aìØ±yN›5kVà¿+S¦Ìe{zò%Þ)¹Ørö5Õ QºjÔ°QD…#å‰'žpuÌ~ðÁë.óÖ[o™ñâf¡F'þÙ2P·n]Oþ’e$…÷Üú‘w[¸§ 2$Ï­}#†0󃌮ýÕæë_á]€?1~Áî€LÜsoFAÀÈý¨Aßï/×ÿòË/[õ_¤Á¿d”+W.ìÉuzzºúì³Ï®˜$ËóZØ¿={öeûĤÀæfòÒÃæ=2vÌØ¨ÿ‚Ô+qsÜ’­ãÞØÿå/©î¹çWïdçDò^È‹;w‰çåà_ “æ•ääG2 ÂùÛýû÷7|¹ÇÙ€þ̘nÛ©·Út†-O:Àwl8ý‚JNN)P,^¼Øñ]&7ÝtSàÒþÿùÏ:@Êvv¤Á¿íUªT {r]¾|yõë_ÿúŠ ²´»víÚ…Ö ÈYlì;O°ú_P†Ñ¡ƒ‡¢*YFxgÜÿûß7éùn¾SEðI¶‡SY${éeeÝËÁ¿ˆLyǸù³|ùò°þv¯^½®þ%“¯WÏ^f<·jŸ©6ž~‘wàWúŒ˜] hØ ¡ÙcëÆV€ œ …႞F,GêI?Ä"øÉ’’QZ­ ËëGŠ_öï·nÝšCn½ ÜÆÞÿÜ,Ù}¯y¤ÔMq|¢H8Üu×]®ß·ß~›@>BDvʘ—o…ÛïyÙ:$Ûr¥‘"ŽžòŸ-üã»ü¯]»6¬¿Ý½{÷+Þ½ò~•Ód<·hÓUm8õï8ÀϬ9úT¡Y³gÍve’.ǸAüæ7¿ l GTIQ.Û¾‹4ø—U²æÍ›G4ÁÞ°aCž“äŸýìgªX±bþ[ù[9ÿþ­ßal^ô6Ó¼;†õàßí£ò“Ÿԇɻï¾kúßVö„ˆÙ>à´~„ˆƒùóç:¶½€¼_~ÿûß»üoÞ¼9¬¿Ý©S§+ *Êqªí3Ú›±Ü¬U'µîÄwy¿ ‰@¿Qó uj×1)¶nLÔï¿ÿþ„Oýj0 âCŽ;´í;YÅd•ODA·nÝ"š`÷îÝ;ÏjÚR«cÇŽþ[ dñRñ¹§ŸW;näý‘I®[·¾ywÈ^ëX'Ò)7/¾ø"Á}!)þ?øÁ\ßÛŸûØT72Gä}ܺukÏþÙI?ÿüsWƒáü파Œ+‚ÙVÕºUk3ŽÓ[´Wk?Ãû €HäÞœ\¯@ «NŽ[ʉÛ{C½Ä/~ñ‹@®JJ½“•>YiŠ4àëÚµkDìúõë«¿ýíoyN”¥ ‘þ;Iá¿<3g»yg$ÕIr¥ˆh$UÝÝÃRx-èÅ;sóñÇ›¹ŸÑÜÊ%™n=3Û¶m‹¨(h¼(Z´¨:pà€rûG¶*…ó÷[µju…l‘£W›5mfÆqó6]ÔºãÏòn@ c(„E‹¹21“U^7÷ìz)t¤Âr^úSO=åø8¯H {I=‰¶mÛF4É–½¿ï½÷^že©×píµ×šš›3;á®ï ›­22Íû¢[×n1 þÝ>rT‚Ý ý2¾ô£™=à’ùÍw§¼D"ºñˆ@èׯŸ/Výå½ãv±¿o¾ùÆÈÊp·ȸÉÙ"aÒRÓÌnÝ¡{þ€De݉g/¥í懬èÉm·&ì‰v< ¤­!0UtI“–â\NWZ#Í*‘`DŽï‹d’”””ï¾ZÙ••Uà¿—ãÂDXý¯îÀ³jï-yoäÁê#OªZµj›÷ÅÌ3c*²W.ÝË’‘ÄU~ÙÆ#c;VYZ’M Ûœë—ÍŽ;": $Þûý¥à¤›?²…`ðàÁaý}Ùò$7gÿ;{Îþ5YÝrÔè ·¤Ð,€.»8.Ê”“|0aN 1‘„üãêâÅ‹Žt”û-÷ÝæY©W¯^D“ìZµj©ßþö·ùN˜%M¸°ß±~ýúËÚqÝ…§x_ä—þ?{Û¥wž}ûb.œf¤ää‡?ü!«üQ üE»µ­LêH;?þ¸qã\ßï/b¶C‡aýýþýû_ñ?yò¤ª—òß­€³Fª-g_å€@$:ëO>§RR*V,_áê¤]öºµ7žHvD"î–À_Ž¥’{7öWKÊk¤Ïˆü»ÚµkG4É®Q£†úõ¯ï„Y&ÀEŠ)ðwôèÑãòæ»PÛ)ü—/]ú_zOØH/ (.ï¼óNBå?ýéOêç?ÿyÜj±HŸJa>·$²üžyóæ©R¥Jù"ðÖ~ÉpjÔ¨QXm?~üý¸ÿ~U7¹®·™&©­ç8Ú€ C&­,TÈDARmÝž¸Ë*”LJe?¨_%À¯~õ«„ $=UVèܸ2ñ´Ð_6RÉZÒð#™hW­ZÕìãÎïçÏþ³ùßô;$•XŽ®Ìù|îºá{¼' ˆuj'ý·pX³æq þ³kŒ¸u,H= ®ü8~?üðCõÿ÷ê•W^1Ç$:ÍÜq‚d<ðÀ®¥ú §OŸŽ8#(žÈûDÞ§nÿˆœ­^½z¡ÿª«®R .¼¢Eè×mÛé3b¶ÚvžíM@ Ø|ý+*½yF¡ {·î®nȫʻ¤óJ¸Ç{ÌbR$J&‘²‚$)Ÿ" d¢ï¥-Ò>?gÈ_o¼ñ†«Eå>Ù¤úJ 7iÒ$3qd¢-“a9¡ "YÝ»w/ðw”,YÒ´=g[6¼À;¢&.Úéý0j䨸 €ì÷‡[)í"¯$Èòz–Îï~÷;õýïß¼3½’Q%òP2A"9Þ3Q,ûÜ#}/Ä“¨÷ßßõà_žópN:#L7mÚtEöÄ„ñ.ÙAã—ñ@ ‚Êü-·^Z(ˆÉ“&Çu’Ÿ38Y ’@ä€ã‹g¤¸ûIÈɲRèö_rr™ ¹—r6u¤í–-[ª?üáNš×®][èJ™¡•³=›·ïçÝPz¿ônX¿n}Üß »ùPRà¥ø—dW¸-Kd¿~¤Gû]V‰Z3²?ÒÉöðáÃÕgŸ}Và¤ùúë¯/ô÷L:5Wq¬SjíÑ'y/À–s¯©ääz—Þ §NòÄ;@D’ÛÁ±T­—  Æ£16EÊø|ë­·ÌŠ¾¬ÔÊ*º[Û¢‰däÜ6ã²m`õêÕ…nÙñ­[·6b5?RG œ ˆ *¨3gÎ\Q0±]ÛvfœŠè7o'ï0@ÀæøŸÔôÖ… 9PV8¼*r®ÉÄ=–û`e…æ'?ù‰'D€œu/{âe2íÖQiyMþE(86S¦L1…²"lK¥þo¿ý¶ÀI³¬Hö»¥è_ÎUKyf&-ØÁ;¡n¿ó²÷‚¬J{eìËjg4*ÜgW²é¥—ÌØ’Ì©ÄîV)0(5CdËæ/µqª£R¯còÒü¿ÿcÎÆ/é]56« ^—9Sƒ¥½±:öJD€¬äI€í@_VåïÈ åË/¿lŠEsåЭɿ=6½d¯¾Lr ûyõÕWM*lA¿«aÆ—mY•×!#ÆS; FÍÚré} «ŠÑ¬b+c%ÿD HŒÈA‚z‘n’‰tóÍ7û.À/lµßÍ4ÿlŽ9¢RSS}ø ÉÉÉæ½Ù~Ò¢E‹°Ú!ïR‘—9ûtÏî=—*ý'%ÕU3VŸæÝ¸’^C¦‡µ }F{õä“OúFd×¢}±<ÿZ‚ 2%3@&t’ê+¼" òe•ñ½÷Þ3)Âò{$¸•Õs Äc\¸5ù—vKʽ©Št²-)Á¯½öZ¡gIýöÚk ü]•*U2ý—³m3gÌT ·ßÁ{ ºösé]Ï ;2Σ,ƒL4WûåþHíæÍ›û.ðde^²½¢ñ#™U’ÎN;ZµjuE&Ö’ÅKþwŠOJ5wã¼·y³áô ªQjó°$ÀÁC¢²m$-^&µñ,¤%+ƒ’6-5 ‚¬âHÛd%Q2 â¹zèöäÿøñãª~ýúV“í¬¬,s¦ya?"XdU®°}²’%‘³m«V­R#glä&ÒZ\zôêÙËÓã]ÆW,¥Ÿß‘÷¢d0È…h¼ßå'P$œÿjÕª™÷s4~þóŸÿ¨-[¶„}âÁ˜1c.;jQ«ü·ì±)ãtÉî{yg fæÚ3a aá‚…¾9'K^¬¨h©¾Ò¿6þ³Sþ>\è~ÿìà?==½Àß'Å¥bÎöÉ„»sÖHÆ~˜¬>òÄeï€qcÇy~¬Ëj³ŒuÆwþGÊñÑJñÏNóïÖ­›UÍ/ AùܹsMW4~>úè#Õ¿ÿ°ÚRªT©+N.aÓ3³ç¥qÙ²]7µæØÓ¼³á1pÜ’°%ÀÆ}+Yá–ÉoWü$UZVæÜLõ•íÝ»w·>¿[‚y9þ,œŸ·ß~»Ðšr~¶:9Û¸wï^Õ¤Y;“ù¸)ËŽ\6þ-Zä›±.Û–Øð¿ _êDk¥?» ãâÅ‹ ÍÊñ:Íš5 kû‘ílíJII «-µk×6õ-röó]wÞ¥Z·ú_ßνF˜Â¾¼¯a³õüEÕ1sXX@Š€ù]²W_Òî=# gÀ/t·'ÿ²ÒÚ¯_?SÛv¥mÉ’%ê_ÿúWX“g©¬^³fÍgÙ²eÍ‘€9ÛyôèQU¯^µx×=Œù0vÑeãÅŠ¾ëRT¤_в$ûE‚~Ù3Íí[òwlVªýø—)SFíß¿_}ýõ×Q þe;RaK³éÔ©ÓûýOž8i ófÇþ£ç›ï7ï*@@Äl<ý¢jÚ¢C $@v±@ Œ%@–@ÙïABvÀ/ûù£ðg#5 †nUà/›5j˜UÚp~ðƒº—X&ñ×]wÝem•³²Sꦨñ ö0ÖV­\åëm@2Î%`MÔ"~":äHL9!š})cÍš5ªiÓ¦¾ú³0`€úýïµÀÿßÿþ·š?~ØRtúôé—½»åÿ{éÒ¥æÛ›}Ìߨ¹;yGœ±âÀê^ýÆ“y   ý dÒ‹€?Ù7IŠºE;Ý=¨ˆœU?7™pÏ™3Gýýïh-+s’öZØïo×®09ÛàÀ3q®ß M-ÛûãÙYi¼B¬YÈ1!±ã"ç$[@„“x"ô$h—ÕZÙr"ÿ¯{²`¶”±•-úD2Hv‘‡xÊ>¹ 6ûôéSè6?#A¶Ô‰öÜçk®¹&ì£O¥jî“j&NœxÙ¸“£KÉbD•-g_Uíºô‹H,^´8â ¼V­ZÖ: Pe¢$mwç)þÜÉþ^·&ÜPüð‡?Œxýë_ÿZ¥¦¦†U¸+÷ê躵ë̳(ûdí¸“±ì€«O_1Îý\þ›á YË—/7ãS q&jÀŸMëÖ­M¦V´DrF"´å¸A‘G9ïÔ#èÜ©óec®ÿ˜…j•þ1I>órD@}û¹S ­ h£FÌžNI›e¢~0 龸y„—l×x饗¬&ѲZ¡B…°ªdç¾–Y3g™g099EÍÝt3cØ!î½b|O›:±ã3±'ûЧM›f¶ZIU¢üÙÔ¯_ßl¯ŠdÛ‘í|ó ;ž4çªÿÂ… ¯åÇŽ»ìˆ¿:u’Ô¸ù»x¼/:vèhŠU…3A•|7'}Ra^ ÍmÙ²%"´@Iñ¯^½º«}ߦM›ˆŽõË}L–d V]\R”wïÞ}EÊìðaÃ/Mšg®9ÃØ’í7Œ#ïs(ÙP²K ªJœ(Õú#Í ;uê”úꫯ¢øñÅ&˜·mrl¢lÈýN–SQ²ø¤vÉü­·ñøG¤¥¦©ë¯¿>¬ ÜÑ:KZV»du[ËÜ“®  … å>ÈÔ:uŠÊßôôtSÑöçç?ÿyXÇd¥¥¥™}×9¯ONDÈì‘yiÊ”eG³.1~Áž+Æu›Öm¶=pj¹Ü´i“š4i’ɸIJJ2ò3hÁ~NDhîÝ»W}úé§*?/¾ø¢9±&ÜU9B1÷ª¿¼¿ú÷ëÙk×¹¯Z{üÞA€ÿI€ä¤dµuËÖB'´2 jß¾}Ô'ˆR˜I‚`)JwâÄ O(àæ* ¬~Íž=[eddD-åWÒðe5X £Ù¦ØJ•ÿƒ†u€ì­Í](M¤NÛ6m/=o¤ËºË¸ù»¯ÓIu’bzüd3ud;ÓéÓ§À;v¬Él’-S2ö‚èç¦aÆæ…/¿ü2&ÿ‡~¨¦NêhÕ_1Û¼YóËÆ×€1‹ÔÖs¯óþøW3¦Ï(4hÈNƒ,^¼xLÏ‚–Ue .·oßnV±ý(ˆÄdO|«V­×W’A U«£õÎ;ï¨îÝ»ú÷®¾újµk×®+®[ÒœSê¦üwå¿v5vîƨë`WžãY •¤;Û—/©úrĦs)BRŽÝëÖ­›É¦©V­šy7ÜŒNyg‹HŒÅˆÎóçÏ«k¯½ÖѪ¿ˆÌyóæ]–ò/EK§­<Á{8@Î’—㦠›Ëþôäää¸M*e…MÚ*7©# ™rÔW¼²dò(“\YÕ—”ßY³f™ãñd¿½ŠU  E¥/>ùäG“èo¾ùƬj–/_>¬”)â•;[dÈ!ÿË2I®§f¬>ÅØŒ†˜·3ϱ,Ï¢_‚m9–O‚ìeË–©¹sçšÚ"üä,ø &¨É“'y&ãJþï2¨üïW­Z¥Ö®]«6lØ`Þ" ¥þľ}ûLæŠД#'å=Y¥9cÆ 5nÜ8³?++Ëd‰˜“Tq9ñÄÍb›AENÿôûXþÈ6%¹—‘l‹ÊkÕ_ªþç®òß´eGµ|ÿwxç¼-:ö±hØ ¡Ú¹sgX{\%ÈóÚÄSV¿ëÖ­kªi÷ë×Ï0\ºt©¹¦3gΘ•Qi»“U¾GyÄœ.©¼rî·üßå p 29”³Áe%P¤‡ü{ $$‘àA¶DȾÞh¯è‡“æ/×ãF%íï}ï{ªeË–a­œÉ Y뮻N5Mozéyjа‰Z¸ýÆdŒ€È^Þ#Á¹¬¨»]ä⇈N‘5ˆÇòç³Ï>S«W¯[´Ê63‘G¹Wýåÿ_Ž(­›\÷²±Ô½ß8ó=å}ð<[Ï_4û#•‚÷6™—ÔïpVŠÁ]¤ÏÝHóÏþùóŸÿöirf÷wÜqÙsðÝï~×1Ì™2Û¤Y;µòà#ŒÅ(2cõé<Ço—.]<üË }¸G±?¨\¹²Z¹r¥zï½÷T¬zè!#aÃi§l]“ÌÇ{ìŠçR2ßúöé{Ù’ÓJFÏÞÊ{à?&-> 'Iu#–©SÕþýû ÔËʹ¤Ð2Ž>’¦|òäIÇiþÙ?r —¤HË>þp²+$:÷ý—¬‰îݺ_öì´îÐK­;ñ]Æ_”Y¶ïÁ<Ç®ˆ˜‡¾ógSL­ysÆp‚ ™G²}BNüãAcýóî»ïF”–™™yÅV¥löíݧ5ltÙøáˆ?@€ïY¸ívÕ°q3«l€Q#G™}݅탗Tûhatd…KVØ.^¼èJšö>ÙÞ Õ¹ÃiƒÌ]€QîùæM›Uƒú ._}Î¥6_ÿ ã.H?ç̺ȉlWñBð/{ö©ŠŸH©sòöÛo«xüHº¿<×eË– {Ÿ¿Ô3ɯÐã˜Ñc®7ñHÖ}Rµl×ÝJ4iÒÄÔ*l²/)™R ¿3¤ÈbvÐïæÏ×_mj„ø‹|سgÏ÷ùÜÙsWÊúšg¶ž0ÞbGã&-ó³C‡ {ð?iÒ$Æsìí—š'"EÆãGN¸á†LØp ÅnÝšÿñ¶R«$÷ñ~"ÒŽ]Ì€€Ä[1ìÞo¼•† –o*eN¤`^jj*èƒ~©jþÆo¸>–À_ª­×¯_?¬¶È1Z" rù“b‰cÇŒ½bÕ¹^ýÆjêŠcŒ±8Ñ¥žcµ~½ú¦à^¼NÆ ‘qí_4h öîÝ«þò—¿¨xþHÓ¦M›†ÕfÙÊ$ÅIŸyæ™<ŸKùïsfϹâýÕ8­…š»ñFÞ'€€ÄeøÔuælv T'IM›:­Ð"Èžñ *0¡Î9±@‚þïÿûQ™<úé§FƤ¤¤„Õž2eʘ#×dһȟ´S‚ÊÜÏC›ŽYjÕáÇWq"kèô|Ǫ‰°|ùrÆ·‘SdËÆK/½äÚv#ÛŸŸþô§æ¸×pÚ]´hQ5|øð¿IçÎSí3Ú_1FzôŸ 6œzw u¶ß¡Ò[´·Î½ßË—-WO?ýtÁ€¤ŽÊ‘_EŠ ü»D‰f?½œžðƒü ªgbKUþpk2Hšï°aÃÌ îûwüøqÕ¶MÛ+ ÍÕ®£O\AÊlœ;wG¾c´SÇN1þ¥(h¸{´ÁAÿÂ… Õ‹/¾hRíãýóÇ?þQM›6-ìïE§NL=“üžGÙë?eò”¼³––“µà–€þ£ä[H,š5mfŽ÷Ê}®rnn¾ùfÕ«W¯@‰©… ÕÏW¬XaVÕ?ÿüó¨Mœ¿üòKuáÂÕ¹sç°Û'Gc 0@Ý~ûíWܯ;n¿C <$Ï{Þ(­…š·ùfÆXuè±ǧœKЭ[7k#{ä-ZdVú½ôËœl"% )œk:&G-ðY”- R¿&÷˜hßm Zsô)Þ€€à2Ë­ªI³¶Ö@èØ¡£:qâD¡‚›R(PVqr]§N“FÇw¨>ø êű^~ùeµdÉsw¸m”½²&L0«µ¹ïÏM7Þdj=ä'…¤Êÿú“Ï1n5 ‚ <ú$''«9sæ˜ñ?þXyýÇ&ð—k”md…=sùù“¬¥ÞÃfªM׽Ļ6È*Jë½\¦\ç.jÍš5êÁ,p‚'A¬³ÔzauQVܤªÿŒ3Ô™3gÌQ~2 ÿôÓOÕ_|¡þýï›=ú’) ÿíÝwßU?þñÍuÜsÏ=æßìܹÓìeíСƒºæšk\9i --M-]ºT=òÈ#yfVlÞ´YõèÑ£Ðû"é²}F̦ȟY¼óîBï¯ÈŸh yÆåY$HwÙ¾3uêTuöìYõÖ[o)¿üØþr@aõcn¹åÕ§wŸ¼O¥iÕIÍßzï@¸Á´ÇŸ;59«W– P}ôÑ'}ÜJ+)ñAO5–‚X"ED¢äµ7V‚1ÙÛ/u“ë†u/Úvî«–ì¾—çÜÇdtí_à=NNJÎóÈG·¨X±"A»J•*¥ºtù¯•-@~ø¡òÛÔ+Ù¿D¿ÔVÙ²eK¡GÉÊ»nÜØqy¦ûK…ÿáS×±×ÑØoùdA),(™C‡U5kÖ Dp ×9bÄuðàÁ|S¹o¸áS¤0µqjØýÞ®K?5{Ãyží`Öº³…ÞoÉ),вEV« äÃGNñ1}øðaõú믛 "¿þ¼ýöÛæ8R9b4Üë—z/Û¶m+ôy”ïԇɫº¿Ð¡û µòà#¼M6œzA ™´R5hØÄU ÈDoÄðf§T¿/,ðUM™|JZ½¬¢ù=0Tê””5`ÀµråÊ|q“-;wìTcÇŒUéMÒÃïãZµUç^#ÔÂm·ó,'’]Øý—â’Ñ&L °/@àÉ‘¡ .4ï+ ˜áçµ×^3õZd{T$ÛvìØQhà/[dK@ZjÞ²¹a£t5eÙÆ= bÉæë_QãìQM[vt]d#Õê§M¦ì?ç>÷œÈê¸T¾_»v­5j”Ù2P¾|yO•*U2©¿³gÏVGŽ1•­ó[ ;z䨩APXÿ¼ 1&™#ý–g7A™´ø@X[o$“Äm ²®L™2 ðK–,iVô¥Fˆ¼w$p½pá‚©òÉ'Ÿ¨Dú‘:'÷ÝwŸ®‘ôQãÆMæV8Ï’¼Ûµk—¯ÀÌ:]m8õüðCuÏ=÷¨¹sçšýù¶}X¡B5vìØ°«ù rŠ<3½³zú|µjŸ©æo¹•± €ˆD ¼ª–ì¾WMYvD ž°\uÉ¥Ò[´Wuj'y*З½­m:f©ÞÃfª±sw˜‰ï†S/p!¦È6’H¥ÙäI“Íñ•nHÙ^¯^½¨ €£G&àÿç?ÿ©}ôQµlÙ2³R/ûómûMþm»víÔ¶mÛÂ.ê'<ôÐCjéÒ¥&s¤°ç©iËŽjÊò£ŒG€»ÙK÷Þ¯¦­<¡îjèäUæÔîýÆ©Œ®Tó6]T£Ôæ*)©®£À¾nJ•Ö´­Ùc-©¬à™¸Âùò·%Ð_}øqî x†Ñ³·Füœg´Ëˆh%¸ $\ö GS :4aþ/¾øÂ¬´KJ~ûöíUÑ¢E÷WÕªUÕ”)SÔÝwßѽ”£O¥°_AGùe#ïÜi+ŽSÙ_6ž~Q­<øˆ©ª/HF°tÏ}!î72aÙÞþ˾ÕÊCªMg¾Gÿ/AUPQ¶C¥¥¦…}ßå´•®½G«…Ûï` À/l:ó²ê5xª£S1¨®»î:Ç"@VÐG¥J”(õÀ¹\¹rF 4lØPµnÝÚÈ Ù’ ©ùW_}u\ƒú¨_¿¾?~¼:räHDÇöåÜ×/5Ú´nÑ}nØ(Ý»ºæè“Œø)˜R¯¡#Эk7µÿ~Ç[î»ï>5xðਞà'Ê—/¯zöì©Ö­[§î¿ÿ~«>• ÝÚuª[·nß×6³Ìó!Ǭ2Vȱ˜­22IS ¾UkµiÓ&õÌ3Ï8wÞy§ÙÇï4ûXS´hQ•žž®¦OŸ®Îœ9c%TdOÿéS§M…¶mÚF^ð1¹žê5dº9•±€€d˹×ÔÀqKT:IŽE€ì+Ÿ7wžÙ;ïDH|úôi5vìX“¶ŸhõêÕÍ€ ,0u "=ª/›‡zHmß¾] 6L5¨ßÀ®Àc³¶jÔÌÍjéçË÷=¨:tìXd“Ñ.C-]ºÔ¤¢;­ …¥h`:u|ìKí¶mÛª)S¦¨={ö˜ºNÄÈM7ÞdöówíÒ5ì#û®(êW'Yuî5BÍZwVm;‘çAdæš3ªI³v®‰€ìZk×®uüfsóÍ7«åË—›Uï-Z˜ýò^ô+T¨`Òø{÷î­¦Nj¶D\¸pÁQ}„§Ÿ~Ú[\¾l¹)¼Ø¨a#ûûP«¶jÛ©·7o§Zò9žuÀ¦øÛˆéUJJCWE€¬XwéÜEÍ™3G?vÜ:õ=7<ð€:tèZ¸p¡0`€JKK3+ïW]u•+Á½üž2eʘímÚ´1 çΫvìØaŽÕ{â‰'\¹Ž|ÐUœ9c¦YáOra[FÓ–Ô°©kÕªÃól òfíñgTß‘sUݺõ]ÙH€Û3³§Z´h‘YévZD0¯”y Î¥’¾¬Æ_ýõæ(½]»v© 6¨eË–©yóæ™ÿwýúõjçÎæÿ.…øn»í6óïäß;=å /}ôQów¤cFQ-[´t­_¥6SýG/P‹wÝÃs €)'«È 7ŠÈ&¥nŠÓ¦NS›7mVçΞsmu=^<þøãæ:¶lÙ¢fLŸ¡úô4iâ~ßÕk¤2NVs6Ü ¶²¯àlkÀ«jü‚=ªi‹Q¹iѼ…4pš?o¾Ú³{)„'éòrô]¼|Ù§Ï=÷˜ì‚½{÷ª5kÖ¨Y3g©þýú«fM›Eµ_Ò[´7+ýs7ÞhNsà@¸Ëù‹jÆêÓª}·ªví:1•¹k 4Ik¢:wêlŠäÉIR@ ð^2 Qi”Ú\e ®f¬>¥6y™gVzÔÔ hÖªÁ»%Rg¡KïQjäŒMñ@ Àû,Ýs¿6eÊèÚ_%'§Üç³…AdIïa3Õ¤ÅHë@ Àßl¾þ5wÓMjð„åªMÇ,UÇ…óíýHÝ”*£KÓ³ÖUN¿Àó€@@â²éº—Ô̵׫㖨Ž=†¨Æi-+Я[_µl×ÝË7bú5s͵êÐc¦x"÷€€@³áÔ jþ–[ÕØ¹;LJ|›Ž½ÍÙö^ òëÔN2úZµÏü_ ¿–@VÈ)‹wÝmVÑÇ/Ø­†LZeA§žÃUËŒ&—`Ü•À¾Vm•œ\OÕo˜¦š¶è 2º0Áý ñKÕ˜9ÛÕôU'ÕÂíwª5GŸR[ òŸ:’E°öØÓf~ù¾MýÛ.˜ú²ç^ŽÒ›½þœÉ4X¼ónó¿Y}øqµîÄwÕ¦3ߣ@ € €@€ @€@  €„aëù×Õòý«)+NªW«YãTËNU‹ŽýU³ö}UÓŒÞ*½]–jÒ&Sué?…>€ß +×D QÈÈQ€W·qkú-¬?ù|ÄÁ?eúšë­‚¼Yn¤ÿœeûB !8QÕ¬›fä5m—Eÿ!€ðã² ð)H"€ðÝÏ´–³¯Ò ¼N“6= €5Çž¦@x*5S €¥{¤@xâ%J:‹wßG?"€ð:å+VµEЍMg¾G?"€ð:¨Ù €rªÐ‡ ü€“"€I šÓ‡ ü@Ç>¬@û^cèC~`Þ– V^±â%ÔŠЇ üBã–]#ð:d¥ï@ø‰¹[n‹è8Àªµê«ÕGŸ¢ï@ø…;îV5’zì_ÇÞã9ú€€ŸÙröU5dêÕ¦Û0U+¥‰*YªŒªR£®JoÛKe£f­¿~B €@€@ €@ €@€@ €@€@  @Ðy@ ô  |Áæë_Qk?«–xX-Üq·š½ñf5sÝy5Ûjùþ‡ÕºßU[Ï¿îëë[}ô)µdÏýjΦеm½]-Ýû ùï›Î¼ÌsE¶œ{M­?ùœZuøq}0}?sý æ9“{ Ï×¶óé+'èþÛpêµâà£jÁö;MÿÎÙt‹éï5GŸV[ξèþ‘1¾æØÓf¹pÇ]ÿ}Çé>’w‚<›½§ëO>oî«ÌÉï¾ÏÌÅä5cíY3‡‘ÿ¶êðjãu/!—#^ùhŒš³Sõ>Oµêná~Õ¬}_“iÏþÑ%^\å>s«JIkÕ€¿P! ïdOHÚµ—¶€ˆ81)BX:Ùþ„­5’#éîk=c¶ùIÎJº¾|Û$+'žc^$—HÝXf»Èß’ÕrÉà“tvÉvlBÙ&Äû]#ó‘ýÝM7Ùr¼àGBõ¤Fªÿø•žÛîˆ@;IC•2Ikwp)²å`Ò²cC’ö(“lIYöòõÉ*MÖÈ…fˆÏß¼­Lʹ¤ç{­ïý.dÿæØûTÓŒÞfî)‰W´¨I¥{¶…~ÿM_s½IÉ÷\¹): Aú@Vk%mX¤¯lÅ’1)߃‚²C$8ö³t” T2`$í]¶»È÷@Þ¹rÝùcP{ùº¤F‡|Ïå^z.PÔ}-í‹F@-©ðòM—yŒ<Édìñ¸/²ª/s–xKîhQ©Z’©@€WùϽ®¦®:­2zŽö|.i­{Z°FV:é Ão’C([¾¢Y-H”gpêÊSf+‰ú^D€l ñ‹Ð›°ø°Jo—¥Jx, Í‹>£Ç-Óið”õžÌpÊ ÉJˆVµ|lÉ ðë;H2Žl®Ù‹@¾mÝÏ4õeüð>u³€ '”ïc¤v±¸/3ÖSízŒT*ÕHÈ€??q)µ¼ €Š|é—ñÄ¥GÍž²hïý‹RÀHÒSó2ï²!ÚûœcìKöós()´Í2úø²ïe¿¼WW<% 5w—ªV»ÏVŒêļ¯ä”/m÷‰¤J4 ¢JÝÛ6ùY ™ºÁ÷@RÙÛtê»Ôq©)äVØH<)Èí{#b¯)ýne{ÈéOÄy˾ßXí‹çê‚;’k—BgR@'Ñ®QjøñY:m£ïŸCIß4Ç z¨h”d'T®žìËþ”lœXõ•ÔÓ‰ãççONÁðÊ$Ùï@êÜøUÈqºÍ;ô ët/"§i¸U0ÖæïÇBÈV„ ÿÙȉñ|G EŽk ÚG§ÌÕטã¶9ÅnÊŠ“¾ªl,ÛM¥ÿe¿p¼‹HI& IkU…ÊþN-•âQ± ˜*û =:Á'À?@ŽŒ•ºñ,RéÓVŸq¥?d»“W€TíºøïöÌ̸ÕzAPÈñ„…Õ<ðJJ½´v ×ÿR MNˈ‡L‘ãû¥v‡T¿ŽvŸž·Çsþl‘º€à I'O¤l6©YàF¿È–8/ ù63OùRðTHº¢¤€—«PESY/QŠ£“``ò:O?{RÁ¯©éá ·b-aä8«DêÃ.ý§Dµ¿¤È`"õ—œ^‚ž“<å.^¢¤kÇGʽð¢ -¿gi¸µ¸hç=@ Ñ€T‘–ÛÆ-ºÛ+éÉSVž2©ÒkŽ=ïDQ>”rN¯œy={ãÍjÀ„U*µUwóöâ‘|)©mTû^cLð-G¬É5J»¥î€¸Ë¾–9›n6iúcæí1}!ÇŒ¹]q¼j­úž}îd¿uͺiѹzb%Ïšìç8qµ¿ø9ÆlÞ– —ž7)–&ç,O^~ÜœžÐü Õ²ó #žÜl‹;$ “w)F'Ç=É6ÉÊ/õ7dŒD²/YN#‰V_I¦D´ûCž§Ô–ÝT¯áóÕð™[ÿ÷,n½ –íûΟūù[o7ÿ}ÜÂjØŒ-æhÏô¶½"®.¿€ˆuð&Uóe»‘vKnØÒ)Ewå=,B¿DÉÒaÿ¾F-º¸Ö/^‚déÅó]-å¾Éý‘¹‡Ìj¥¤›÷uƒ¦U£æÍ7Zþﱨ%Ñ´]ð»€_^’²‚7iÙqWõÉ>šjÖ†Íþ±x}@åƒ)E·&/?a‚{w*n?nR·ÎhŽfådõÃíHr¤ˆ‘*ŽŠð¿h‚3 ÀÜ:2kÜÂý '$¯ŸÞ^u0U™¿×³áž€ ç«ËÉòœË¿ÏOɤԭ•À܈HŒF¿È¾ÞN}'š>‘ÀÞ­Bf"¤N†LØóûÛ²í#ý…@dÓEäÉIA'­Q³7Þ¤ÖŸ|>¢^D¸ö½Ä´7¿cHåøÅ ·O‘û#ïyŸÊB‹Èg¹~YŒ÷‡,:ÈÖŠz.éž{™oÉýi.uzZ¤ê4hînƒþ]ò]@>²ï9#sTTþ‚U³ò«F?ðÑ(yñËÇOVñ£º—[IòwœžÕ.Ù^{æäÔ ÷VX«˜}žÑ8NHîA߱˟L E'å(5¿ 9BPúC&MÑ(Â'²0ç8–UóhôшYÛ]ï™Ì‹Ð Ñ–gR³@Vèdå5gD€yi’Œð¿S8Zwj²Sܾ—2G¬ÉÈ~–Ë–¯hÞ»AúL°–Œ švP{7GUʱ¥" ãQHO¾7òý•çÄ­‚€@ À@Rû$ ›¸äHÔ'¿…¥”KúZ4&A²êÖmÐô˜q9‘”u+NÒ3WxÄ3Ï›¤7»%›äx»pW®ÀJ1:¯³‹†y!¥¬üÄ¢ý2‰•,Žû›l˜h¼ïJ¸Uð¯H³ÒæVÅr›k1_h.Zí@LèçI¶îŒ˜¹-f5LdeZ²®D0ºù{½,d»NÁm¸ÚvìÜo’ùÖEšqkЪó Wž½hÈ| \’j&+^c¸Ò_²*ïf5oÙ£'“’xte‹AîÕ¾H=w·'î¤D;]Mj$56{øcÙv Le¢“—±h³[@¤—¤ùÆB°Ä Fe²[™'³Ößà‰ë’´Y©!"iº€3Q™÷#Lƒ"„ËÞG:ø•L¢n§«kÏÆuA%žEU%£xTH¡/^ÏÐi›€¤ð–ìÞtæeßOd™PÄýÎ_4©žn Š×ñ†’š*…‘¼ZÑÞ '¡²’#Å*í]×.s¤+A’Lþ¢‘àU‰/*U«c¶î%Ú³ëu Çv6ïÐÏd[H¦Y"ôy_‡ÅU;dC «žÔÈÑ(ÇÑ„³zi»ÏNÒ”ãÝ~YMvZi:kÄ܇—UÕZõ¬Óé£-•œ7²Ÿ4ßsܸüK´DÊŠ@[ÈQ»=‡ÍMØgÚë Qq"¤$+ "FЇ9ºO¼x]-: °Îhˆg»¥>ƒTwrOd2î•û Uým¯cè´žŽNOð0’/'v8­°-YAAüF Wô2+¡Ÿ]@|ºC‘ýxE€cÏ €@d,vXØÐ«`Ò²c¾ îzŸçè~¤Çá|àÂ3îmWÙ±EÒl®üK½“ ~#‰,f#Q!s¨ý¶ÅXmGA $9²,Ñ€*èð‚cœâÑæõ'Ÿ³n³P±J-ó;¼v/¤0£í5E³  WöÉéU¹ •ÿ’yäo€›,Û÷†ÔD@€ˆiÓmh ¡NƒæV×$« ñ™€Í²O».V\ÍÞx³gïÅ5•ªÛÊ0oÀÏŸpmÕÚf €@@lbú[Ž‘#Ç÷%¢hÙi Õ5Iĺ­P:9ö¯Ïè%ž~Æ22GY]W§>1@N‹‰¼“£½rÔ€@ø 9ùƦÿ»š‘3fþÞ„R±Ù«Gй5¾ô|¿èég̶&CRƒæ€0lúfG«ÿ™Cçð@ `É”'­ú_ä:@D̼-RŒš»ËêšÚõûí õ›9(tÌócGŽÎ*QªŒÅÑ[%Õ–³¯"¢LízéÖ}‘Ô°…9=€o€;l¿ÓªÿåyF«ôóD’’lsMÍÚ÷i;ï¾Ïºÿ+רëùÕÿlꥵ³ºÆ9›nAxT 2qåû€@ öߣúéí€@ rdõ.€m`Ó°Y§˜¶³ÛÀéÖý?pâjߌŸ¶=FX]㩼/Ù«ÿ|8C2ݼ<¿D &„«ŠI8°pÇ]žÛw~…|9ÿºu…|™xÉÑm~?}Ç,µ+Ê8b Š[3J–.kÕ¬í|¸€Í» ¬‘cäM,Ùs¿Õ5U­U?fmœ¾æz뀡c+äGƒñ‹zª(#à 5yù ë>(S®‚|8çêk*!€ˆ%J–N8°üÀÃV×t͵ÕbÖÆîƒgÚ»V¤¨Z¶ï;¾?¶m»GD‰Yã¬û S߉|c€ ü(lÏ÷²° ðÊ–¯³6Êj›6¦¶êî»ñ³áÔ VךÞ. %*U«c×EŠ˜ ¾ €±@>eËUL8°öø³v©ÍW_“öm<ý¢õÖ‹3·ùo ¿hUk"ZUŽƒ.œœ>Q¯I߀@ ¿ €rª$œy^žÐLZvÜ:Xð[úÿ¥­&¥ÊD|­µRÒQ ß¸åÖ×?fþ^¾ €@~*ÕH8 Êl®©¤RcѾÎý&[µ¯|Ū¾Cå*TŽøz+UKBDM;Z]{é²åÍ‘U|€ |*®­Z;á€I9·¸¦â%JƤ}5ë¦Yµ¯YFŸ@í9¯\£. HÒÿ€@@d%œ¸ÁîxCù7±ÈN(R´¨UŸœ´Æ·c¨Fr㈯WD À]VzÌúÚ£u,#€@ @Ĉjµ$¤°Ùs.GìE»] ¶ßi(Ìßv‡oÇP­”&ž?A—±¾öÑswó=@ €ð³¨™œš@ö+Û\W´Û%A”íþë­ç_÷í²M ›uB¸L¯ó­¯ãÿ€ |.ä%ˆÀæƒ*l=÷z”'[³¬ÚÕ¨E_!›éí²nAºOí&ûW›Ú|€  #d~4Û•Þ¶—U»zZäë1t͵Õ"¾æV!\¦rõä„}—!@P@JZ[ €@¸!¢=¯Z«žU»f®;ïë1T¶\ň¯9£çh€‹l:ó²©sasÝûLà[€@ ™¯ÔÑ©,@€pÀ–s¯YN ¬>ú”¯ÇPI‹¢ŒÑª:T°x÷}Ö×=rö¾à‘ùJÃf€@àu`û-ZÌ÷û¯å"½îžÃæ"\dڪ묯{ÑÎ{ø €Gæ+±ª „ LYyʪM²ÞÏãgãé­®»ßØe1k»Õ5Ë‘š~>€@ m¾’Úª;@ ¼.l°Z)é¾?¶©çƒ§¬G¸ˆ’ âó‡@ ‘ §m8ý¢ZsôiµüÀÃf¾3oË5sý †ùÛîPK÷>h¶æIm•xÌWš´ÉD€×@ŸÑK¬Ú”Ú²›¯Çmêy´öUtÈkuÍ švä;€@ J¬:ü¸š¾æz5lúfÕ}Ð Õ¼C?3*[>ò‚µR\UŽJ-_±Šª\£®ªY7ÍÌÿäÔŸ®§©a3¶q°æØÓ®ÍWšFé˜\ éÔw¢U›Úõéëñc›ù0~ÑA€‹4iÓÓêšÓc4ÑD €h!GüNYqÒœh"AºíxuŠœˆ“Ö:Sõ¿ÒdH}›ùJ³ö}€@àu + 6mÊ:Ç×ãÇ6õ|ÆÚ³©S¿™Õ5·î:”ï€ð÷õÀIkTã]U‰’¥ãôD™r¬Šä¶èØ ^õšdXµiÈÔ ¾?¶©çË÷?Œp‘ •jX]s§>ø ßÙ¿ßsø<ÓF/ýnЪó  €@x]T­UߪM—ñõø±I=—)Ä„pÒeË[f Ìæ;€@ &ß#©I#ETe¾”’ÚF5lÖÑ|{å™YݤM¦ùþÖ¬›¦¿µuÌ÷µh±âQmSÇgE€%kŽ=ÈU [Æ-<€póù;jÿüÍÞxßà°úèS&ØŽÆ·§rõd“^?rö5sÝy³ÕÍ6 oãu/©•‡7™uýÆ-7²@¤¼dö9mgç~“€@àe°òÐcö ý…p™ÄÚ^ó¢÷ð@ ž¶GêæI‘"*µe7SëDÞ“±hÿ¦3/±*ã§M·aV[꺘Š/ €¥{$°Ù¢Íg#ˆ`ÉžûȘÇù O©OãÖ·FÒø¥öM¼ïÍ AÝÍ@€—€í3D·ìŠp™Ûï´¾fÙkËw€@Ä[ÈV:Ù_ïô#^:^·d鲞}.€`‰¬2܇GÏas.³Ø€Úzþu¾ î e§Ž¿/m»÷Ü;Íf @Ïáó€@àe0gÓÍ÷yL «'52û/åH£¾c—©± ö›O—k|Ôúš¥ßO0uå)ÇßœîƒgzòH‚H¯%kÄ €@xYÌXw.p~™«¯1G¦µÎ4Gõ¿R_tÐdCÄ3¢vÛ^³¤Ýò@ ñ›¯Å¯ç¤ÐßÀ‰«=Ùÿ[ξjuM}F/F€—À¬õ7ø6—#‹¤HѵUk›£—dÿdzÛ^ªM·¡ªs¿Iª×ˆùjà¤5æØ¤IËŽ›ýæ^Þ7D`;ÉbU€@ òbÜÂý޾a²uÀ«ý¿Q+m®I2æ€@àa°pÇ]Ö+|Keü •j˜ ¼Jºªz†&¯Ó ¹yÎêë ¼a³N&^‚ófíûª–™ ]Î4î5ÎëÝN7> Úe¡ÿø&x2uƒ1s›¿ø9³xþ¶;Là·áÔ 7žƒ(„¢ÅŠ[]óbuØ •þ–Ùo5»Ú¼ó-;K2ê€@àa°üÀÃVí)]¶ò¤ÕuÅjK€@ blù‹)ÊD¸Â5×V³ºæ™ëoà¹A qNêçT©™¢¶œ{ÍÓýo»80xò: €@xYÈ*ç°#â‰l±¹f©¾Ísƒ@ â!:÷›l=¶FÏÛãùþ_²ç«k2u#@ ¼,„¥ÊXµiå¡Ç‡ÇHÍ›kž°ø0Ï €ˆ‹¨\=Ùª}E‹SëO>ïùþ_¸ãn«ë6c @ ¼.ÊU¨’}ðò>JÔU4x`±åÜË/ó/Aj¬Ø\ŸíE€Ç@eËìÙof"Ó¨ygO¯4!@N¤€Ÿí¸Ê¹Ðý/ßw›ë5g'@ ¼.j¥¤[µi {°.Þ.ËêšMZËsƒ@ b.Ú÷c=®æo½Ýý?cíYOgf!ÀõšdXµiì‚ýŒC€cZulwŒÖ˜¥<7 æ YF«¶•¯XÅ7ý/EV½SkŽ>@ rsþ¢*Z´˜eÖÜJßôÿÈÙ;<]@ ègy¤+°7˜³éëë^²çžAbÛ·+>Š@ \̪‘ Ú/ý?tÚ&«kœ¶ê: €@x]L\zÔªMEŠUO¿ÈXD8Bž¡«Š±ÛoªŸ]ž‚)R´¨Uß.Ýû €ÈÅâÝ÷Y¿§'/?‘ðcHŽD€ÇÀ²}ß±žÐÌZcà˜ •kX]·d¯ðìL‰’¥­úvᎻ@.f¬;gýžž»å6ßô¿lW°¹Æ™1š ÀážÆâ%JYµkФµŒE€c5ïlWU»ÇHžB(]¶¼UßÎÛz€@¸”1',ßÿ°oú¿Ïè%V×8{ãÍ@ €ð¼ÐÔHnlÕ®6݆1ŽéÜo’Õu×Kkdz¥wŽŸ³{€h!+ܶïi?m«é5b¾]–Ãæ[€@àмC?«vÕJIg,"3lúf«ë._±*ÏNaÛ+*Ùm¯»`?€ÈÅ’=÷[¿§¥ài¢÷¬2‡€àÞ£Yµ«x‰’ņpÌì7Y_»×Ç|üߥé dà‡S¦¬8é›þï:`ªÕ5.Ø~'@ ü f9HkŒÕ±?€ÄN½`}í½†Ïçù)(Àl—eÕ¯²-€@\Y3§hÑbVm=ooú¿CÖX»9ØÎ{€@à°åÜkªd©2Vm“íŒG€S®©TÝêÚk&§òüX_a²U¿6Ëèƒ@ \üŽž¼Î7ýߺëP«k”-@ €ð6ëhÕ¶bÅK¨µÇŸeL"Ñ iGëë—É ÏPÞ œ´ÆªO“¶D yP­v«¶e\è›þèåB‡@€ˆc€ÿî^Á˜D8¢cŸ Ö×ßwì2ž¡|˜¸äˆeÅ* RRÛXn«™ì›þoܲ«Õ5®8ø(@ ü"ælºÙ:«Z«>càˆ3·Y_RÛëÛzþu €@øIHʯíÄF>c c`M¿qË­û@ŠQ.Þ}ÏR^ûy[Øíç-]¶¼ÚzîuƒIËŽY¿§ür¼fÅÊ5­ÞÁ±j€@ \¤NƒæÖ“›¥Ê„!b>©óã[?Ä‚;à¬?ùœ*RÄn»Rõ¤F¾èÿ²å+z²ï€†Mßì(«‘ÔXm¾þÆ(ÀŠZ)鎞¿Qsvò<åbÅGg©‚@ þGõ: ­ë•ÌÛrÁóý_¢d鈯Mæ7@ €ð¡ØröUGÅ…ö½Æ$Æ8Óϸ…Týôöj˹×1ÀÉiB¹ •Í ߈˱=»\9{€È|ãlÇSr£VžÿîÙ\—l@€€0dêFGA˜Ñs´o3¤ÝC§mTUjÔ½t=3£|Êà}/A¼“g¯y‡~¾Ù»«¢Y]LµîO™Øûi,#ÑfÆÚ³Î2•æîòlßÛžR¥f @ ü*ÜÈjÖMSK÷>è›1µþäóªÏèŪ|Å*W\KæÐÙ€ÑsØ\ÇÏ^ƒ¦Íýôlàîu#™*U«“vÎXwÎQJ í,À/@¸¶jmëñtM¥êjãu/y²ïWyÒî{ŸœŠ¿ AÒ~aÙ²1ó÷zöžÈdGêÈ‘g%K•É÷:’¶@Ĉ5GŸ¶Úƒš9ÊJ&Š^»¾ñ‹ê¶Õ»ÔÎa18=C„ƒíû'›Ô–Ý< ã`À„Uf%€ðº¨”¢¥^””"ím®'©As €@øYi­3]‘B½´vF*l:órÜ÷7ÎÝ|«9ËÙœ+R$¬ö-ZLm8õ FdXàÊswuùkÕ¬(oßÃòüשß,n§ ¶ÉqJ= O-zêø™“SVzÜ3ý>sÝyý]lb÷á),@€ˆÖêøÑ§¬Ž*l‚Ö¶ûp5{ãMQ¯..«• ¶ß©FÏÛcRøe²]®Bë¶Ë¾O@ìjä¬Áà„"E‹ª´Ö=ÔT(Æú:æo½Ý +]¶|þí+RÔd¢Ä"X¶}·æD®¥ÇYjíñgã¶GYîe·AÓU…Ê5\{7!‘Ò!kœãçN¶LYq2®ý-ßIÛg1›Æ-º"€Àï@»`¿« '’æ-uZt ²F.TãR‹vÞcVV$¸È+ÝXR™%à•£Íî¸[ÍÚp£™•lœh¼I^ÐÖ±H³ä©Áõ:?ç^3ïWnÔûÒÛöB@"“Ž­ƒóhI€pVoK–.k2ÜØî9[22GEM>É{¦uסªÿøf2,µÂÞSþuµæØÓFBÉžþÎý&™ßW´XqÏ=[¹‘ œhô§ÔÉèØg‚5g§Ùf³dÏý&³!ßm?ç/a"ý.ûŽåˆÄ“•}9ÉA¶K8­[€@øíû(Ï|“6=Í6(ƒòžq’-·òÐcF’÷·\µé6ÌŒÓRe®v}üˆÈG@‚A&öñ ¾½@ïQ‹1FVªdßy¬î±å+VU•ª%™¹: 𛽭’Þš’ÖÖ”ºn­¢gÓ}ÐŒ˜õéÚcϨ2W_Ó±#«ö²uàšk«ýOæ…Y€ð²à¼a³NÑ}>õX‘1#cG¶½H&“dÃÈ|Nþ¶)Ò™’ÚFÕHnlN((S®‚µ´AÄ"@ HÈ$§U—ÁûMFÄõ'Ÿ»¬j~"ÒwÌÒØn¯XyJ/Q2ðR€pKªU¨T#ÐãG¶ € ‘@Yw{õÓ/tê3'¤æCµÚ ò¹’”‘l?p‹IËŽ9®bŽ@ BAé¾ï˜ ¡ Ž©‚€ÂÄ%G¢²‡ÐËÈÙâÑ ÒaT?õBôSmcŒl7X¼û¾øZ\y*¦û콊ŽrL#à,[éù„{G…õ}¬Q7¦' ‹sœþ²ÚÍ Û¶,?ð°jÒ&3á'6²o¹Ïè%Q¯ÌìDxME úNô}Ь¼Kʬ¤Ç»O¥’yƒ).*Å',>œç‰#^Rx·PóÂ;Ê㽎ÔHo—eN=‰y¶…¥ðà ¢"šeôILP¤ˆZ¾ÿá„RpÌË×4iÙqS0-Ñ‚þ–©‰KÆäH&' l¹Š|ÿÉqv‘³ç%šêI³T½÷TŸž¿h޹LnÔ*¡ƒ)¤&A·êqf+â‘­àРiÏ\ÃàÉër‹lÅê5|¾ùfÅm»…¥hѱ?ñ 1€›V¢TSVl¸Tƒ•Z5“SM•Vs¤L 9ƒ·^Z;5|ÆÏ_W^@V2‹—(e‚NÙ \±rM“NW#©±©<¾`ûÞ_•=ÿºš²ò”ù¸Æû¨>?ýá3žuŸJÕöò«˜3›e;‚Tz–ÊÏr ]PßòÜÉ$[ÆŠž1Y}š½ñf_ÈYeñh"ÅÙ$Ð=o9vÐK}ŸÀQ¶Y‰–öW®žl/ù6Ê3ïŬ°Xy&³…•%*U«cú¥fÝ4³Ý 5¹ßër‡ˆZ?g+5hÚQ œ¸Ú3‹,ù 9mäÊùo}ó|tì=>0Ûå Œ¤,Ê*’œñ*½§_L˜IÁªÃ›|aÈÇHŽ óËuI‘ )f&{Ée?s<ÍXìÓ6}³ v¼vÌ×eAAå*µUw3Ñ”,†xß »ÙãY$Ý=žýôl{AˆÊ½¬\£®wž3ýüK–BÏasÕü­·û®OåYì:`ª‘c~yõ›d¨nƒ¦«ñ‹Åu•2ÜQ¾c«s«Z,5`žÁËèçS oŠ ª×$#îÅt%ƒR²JÚt¦LX¥¦¯¹žï#ÀÍUï™àlĬífeQ¶8H §¤sš€"ÌŒII”ÔXYí•É‹¤'Jm‹v=Fª®§™£ eKˆü­x÷o +«3×7b÷Á3U³ö}U­”t³¥ÂéäY¶gHŠ©üR˜PVË6zpµ,ZÛ/dû‰ŒeI­•>`\¶¦HŸÈø”­M‘ ”R&}WV %{"­u¦)¨ÖwÌR5fÞ5kÃF¬Èßçù†DG²Íoè´&—o™¤«Ë¶[É&¢]NC1$Û#DØI‘OùFÊ6ÐÌ¡sÌ»ÊÔ!A˜ â½:"”T%—ì™É„æúL°!¯íáÿ"+]ò\ÍÝr›yÆ$;dÒ²cfr•“3¸_—îÙ³úR2eªÄ°­Y¶ï•¸=ѯ¦yËÃÁH %€¾æÛ,úêþ8µõ=‹¶. À=œíSÍu‰¾'”ngSÑ75Žmndù¬š`÷®v‚ÿB«_KàÞü5ÈÇj†VI¿öðýyFöo𾜳é+I³×ÿ^½n¥.‰+²ÿ¶¶uf¤!c.älöо_¯""ZLجª¿‡v ­GÚÖc¸‡»|.„ þQÉ´ü˜œŽc›wX´÷“¼wm$øÿ2QS™#¨Øìu~åôÜfŸÞ¿AŽ>‹%O$â§BîÍÓ6õ-DVѼîÃ÷ÏãA>Ê1´§þú1+Fí]a»þð’)W,@ÀæÈ¸ŸÆ¡¥,ïçõ¸‡@|žÈGtJEiyùZ|H>‹cuY›=‹&hõÿD.×&ðøz$²4 Rq.}­iH7K½hÑGï8ˆlàó-crÜc…€Þ»F.õáïcq”³ÔÚˆt ¦@Oê{¹üPöKñ9)¦iÚRÐHÓC3Q³Zs6tþ¸Ó¿·€ðÑ=|(€pw‚\³ü˜Œ‰C[[ù¡NŒúb˜ÏJÙJrC"¯\Z)^fT€&Ýw8ì+9)ànͶÐV«¬Pq¬ê¡÷ØÀPõõ­š\¸7"e“€|ÒþžïŸÀeº„î_wûð:À o¼è xB“áß«J‰þ§ƒ¿û…ü €çxw<Ë?Õ¼Ú.óm$@÷þ¸Ì´ü܇¶Ú.’Þk—õÇ—q@2Mþ¨yYs£¦%AJž| #áÞÈ{!T SRöß ý.7'à¿B€¾ÆÆŠþ}¬Ù'GtFø7;kžtxîE òè«ë\~¼:“~vh›L[‘O¡mi£BÅ…O‡R÷?rùoïàýëá²xÏ ¬Ð“ù.–AÀï5ÍE-ƒÛÀCZ¼ïÂß-¢©¡™¤¹]ó¡ àçš«ôãrå6€/b‘N–«­¿µ9Î,Aï›­FUå`)úwÕ4 ­@ŸY§ȱ¸o·YöÍNNÔÿ¾Ÿƒ­"÷*3¶9úi„K£ò»"-ØÚf¸ÐÅã%€p,pÊUCw'ð=ûöÅ QnφÛó;ÆV°€¾ÎÂ?ÇòÔœÐéÿqÐÞŽÇ5ºEœ±E¢Œ,°M×"GmÛcѶð¡yÓò¼ëk¢Ü®w,Úµ€ H)°}í)xY³HK¾.Êm*áî÷3¶/ðÓöž³ÿý±mïmÇH­‡ÒA,&þÉQF䨲—ÃlËAx!QnÛ¶¸òâÎfªÇ‚)p“‚@¤ؾʖÏЯô~•µè‹çbЮ–¡â‹®Ï€«_8XQ¿=Nm–íH³l󗱨®à@8°Ýbâ¿;I}Íg…´ã M €pܶEm»7“‡:–“‡Ç£Ø¦Ãíy)÷ @âF?¥ÐÜeýñA„}!G6ŒA»6†Ñ–‡[§,¿ ÿŒeêíží x]…pe+@ÇDS-&þ˜·‹AP²°£ååŽ@D¹m‡,Ú¶' «ç,«…¶Hµò÷,Ú3€ H)´}E-+q¿’À÷ìu‹þxÚÍÚîÕÅÚð|ÀÎFGäÝ/ïzí\ø¾õoX¶ý{WxËc“½,ºXl²ú>:É`ÍCOþî;š»5uƒ4qB ¢Ü¶g,Ú63 «©–éQhKG‹v|¡)@¤Ú¾¯þŒã=»ÝAåôš1Ø¢°/$Eÿª¿"w³ÆV°€ƒñ,¤ûø›öu¬Ž#ö‘øÂ²/$²¨i° ßj6i®ŠA€r•æê Nœ€(¶«Ah,GÒ.96°N@&VWk>·øp<…¶µhǹO‚§í˲|†nHà{¶ÃAõ'9M%Fí,)[€}2Ýò¹}É#íÁõ/ËkŒ¸ü¨VÍ-þlijŸ¨@ë¿:Â[šÅšrLrß €-ÚukÀ&W·[ZøJ.¶á*ËX_€ %ªû…g$ð=åÂ^Ú‡E®01î“[,ŸÙ‰º†',¯áà2¶iZö¥œU2á@(ØäPd󉿔¦»¦/eÀÛ@ÿþI–™?é›\õ±üpÌr± -WàŠ")…¶­~•åsS7ï™ø¶K{jÿO³TÓˆñ€ˆAŸ¼cùœV÷Ð5Ì·Ý‚ƒ¸\8ÜÒ´/QÀ5šº$²ù\ó”f•¦U,¶ €ˆÚÓ!4N#mÓ#œ\ Ó‘~4žq± Çã± ‰¤„ÒÇmWÚ~€û6; ¶dßþͲÚꥀ‹±•0ýQ×ò¹|Çc×QÛò:>Eä)*Yœl’½ ] €P0°Ûe›¿†Š÷É©Õxa#ñúwV‘þÖüÇ¢= ÊÑ›y¼ÄX~4ªÄQ@¤#)¶©f!Õä cEî[ ËÓG"á¡#N³‚¼—àZL°|ïöàµüØòZj".¡?Ú²?iónòƒ¨:ÞOÅI!þ‘f§¦“¦(/pÀ] ÛpB㺞f€f›æÉÐV›{ô· ¥þçz‰7µühÌuáow%0jà¶Ð:V¬ÔŒ•ÂBRä‰ %>AŠþ½544ï;Z?’"¡¹w btô–ðY¨nÀÔ|éòýTÓŽIÖÙŸX|0žsáïž´ø»Kžç¯šåAYñŒg¢O-M?Í9¶/T¤ÓéýÛ°mP·ÇiœüJsHÓ“ì@˜ý±ËòYëâÁkÙky-óù €ê!kSܹM €P2Bóu$@Îì€74³5ex±#.¼Àï4‡æE¾Ür@5‡ï¿Dø7¥˜Ye€o8N’ÿd(ôï"áÕ¬ûMèäŒO£pÏ^ àYóŬœ¹…ÜË;½¨!<Õ×Y>_dÞ!lEä-B¿gše¿þ<é T&Xîv›hŽh"€˜ó/ÍV¹§L°.½È«Y®.pð7»[ü½‡xoü,¾µ-0€ Å‹H©}J=€'=rDòÌÒ”æÛ„ÈÕ÷X>S•=x--¯å ú]OYö펄¡`eL¨pŸ‘oCg•WA 1á1©@ПçËüq›ÕBï´ÅßðëR|-Ö$àïÀÒÎXRÇaªæ* ÔÏZ>KE=x-½-¯åz@¡ Ió‰åV€– )B˵š³¡\y$#`a‹"1n÷j2øó|™µøXüÇfÅP&#EÊþÄ}² .¤xžrdÝÅIî¡ûój¸“rÆV €YôÇ?=z--,ÇÀ‚€Ã§?ÕOH#piªÚï•å‘ 꼬¢¹Š 綾y)™(X|,[ü­žçt@ï‹ßÀn‚Ï"uvjžo]€e–ïÄhðMŽfDÚ¿·èß{ôZjXŽ…‡a €«4/E«Î‚¯@(x):%à¡8 ÌæWšÚ &¼¥™Æ„÷Ò ý‹Å÷,þÎõ'àK†¤xÉÜyHÃv¨Âïm¥ÐÑkÿç‘{w(H[WôÇï,úã]^KmË1ð  pú 5_Xü^)¸Ü<¡@®@¦†fƒæí8%ïj €˜qNSŒÉîÙn–…ÞjE˜þÿa„ãÿ|Oü,ž"HñTAÆïi‘îïhkÀmšÅ{[œ¢ÂØ ¤ø¾Í ½–6–Ïÿ €ð@è÷®²ìçK&T @Ž€æ*M7ÍÍãtrÀ‚ âIM¹€Op¯²L1\ÁßȲøýë¾ã!'ÇD"\ ú_Ó,DÒA¡÷»¢frHü%N÷v9c+À¶º{q^ËËk9€ˆHµG¦@ €<œJš±¡ŠýïÅ0 ÙŠ@$¨ø*tïmø™æ}Í7Qs?ÑÔ øäv‡ÅGâµ~ÿ9‹tåÚ€ˆùµæb ‘Õå;BÏOwö)Ç©æ<’€=&÷¿¹f¥æiËt[$ ¡1c+pàË祪¯e:'ËD_„~wSÍ¿-~¿ü›¦y<©š%šG5ŸFQHÔ €H@ð¾Kõ;ªjškz‡Æ¤E=e PéOfZ®4Ö ³°Ö_#üÝÏ<¸°ÃÎ<¤ˆØú8ÿ ýw7 Çíâéq.¦*YNBÕ´¿¢¸èÅãÝQí–ÏJª¯eåµÌFD&B¿‹ƒSbŠ!® BЇ¶ ìÒ|? ÛîMðàñõgÑ´§¥æ”æ#ãnMÀ'±¯[|$V„ñ{ûXüÞÉ@)o° §‚&%tïOjþŸÃ@QŽ´kƽŠË³QU3Qs‹æOQý[Û,Ÿ“¼–£–×2`%Šk~fÙçë…׆¶ ¼ì’øW"ïKÖ×öB‚ €c€Ø €íºZóˆå}yP!À“Öùˆ7Âø½‘ž2ð©¦,P€˜©ùÀA ø ž¤á±g%=´÷KàVÆV ÀbËçd•¯åNËkÉDD.r^üÆòÈØ&€ðö¡mN%À‚ÇËø]ÚYÅòÞmGÄGäØ&p—å½ÛàIêµ–ûÅêbŸÿáï»…€tãï\ã ˜ m1èÌ=óÌV¹š?8rOK1¶#[>'÷{ðZ~(Ûü"BgŸe¿¿‘sË ¼ Dö&í@<•ÀÁã­6«³qhg+Ë{·?j_Í-Úø… à êˆÕü¾~˜~Aнý­š{ŒŸy18ÀÏNeÍwJ€aŒ­À€J–u%þà±ë¨iy²ˆpÀ‘(*ló®Yë 'ðÃCÕû ySöÔk†Ä!Hê (”—ƒ¥8ôËI‹þ: WŸƒ-ïÝl@üŸiý÷“}“Àó‡ü¾›"ü]ï&ê‡@b+B¯ˆæ>ã'šúÜ·³å44ßÑüFóªæ´&)Æícº®wp?·1¶‚!B}ò¦åsRÓC×0ÏòIàûÚ#Vc_ÿ».–æËì Ï =q¯ öó›Øß'üb„¬µ B$à-– Áã.Ë>©ãvγlç€'@uË»<9–”ý¿Y| êåó»>Šð÷ì"¸D òý›rý?òé’]$bYcÜž’šÿ³¼—ç[g-Ÿ“)ºÛ­LkîÈ¿PY›{ðzHB{V|'ŒÉýù!Ť€e Y+AƒÇÕ–ý‘âQÑà¬ý7ÿÏ¢÷<°9寽ú¿ °ø= .€ÿn_‡©ãGz¿ê…‚ü‚úæ+Y%‹q»ºZ®Ê=ÆØ ”˜l9Þ_õHûË[Ö:"\Wk~o[TÒ“@OÚGG0Áã d¹e Ù.AƒÇ9–ýÑ?Æí¼Ù²ž6'Nü<àM†Å‡áÇyüž["ü¯X"aýí3€›x¿ž wßt¬³$,÷æþˆ±(P×Á˜oîöµlû’Mˆpoûþ÷½-ïÅ¿,¶uÆDÜÁÿÇ1Bú²’|YdYöÇŽ·óYËvÖExFŽÕʬþ;-,ï٠ϼFó­E;ÿMÀÙd Óû³ÿþ¶ÿí½•@ÄmØïpr¶% ÷jˆEß̉aûŽZ´ïc+p@¶|c9ÖïŽS›¯ÑüÙ²Íÿ”Ú€èœbù^ô¤°I×£@d¶EÛ>HðònË»wŒÚ·Ï²}Gž™–÷ð-óqxÁâeŸªlýÏÿÝ ú€ˆ¸ 2Ö~á`r&ÁD×Ü«ú}óžôoŒÚ÷#‹ömglK„úæã}DÚ{–‚¥Þ¡¿W"€ã6žb‘ ÿÆím{-ÁÈU–ÁÙk1h[IÍ,Û7à`{ÚÄc8æÃ0ÝfEÑÂ* )FŸ#ÖE;¿u0A“cèŠ&ø½**&iß,ŠÑ ©ÍVŽ)Œ­@ €nÆú±Ü jë·äd]@Ô@•PM_ ÛÊòc£„\¥yÏ¢]·'xÙÃò~Eý4ýû7;h[S@ü€þ[¥5¿·¼‡Çṗ¡œÅ¤ù—šÛ#ü7GéoÀQ[Î;œ¤ÍÀýú±E¿Hêré(·kå=ëÂØ žpð,ç<Ͻb ÚØ,t¢†m;ïȽŒ«µa¼ß@sËɾœ^4ŠÈaËv-N𲄄–}ó“hÕG þš/,ÛõvPöŽû@v qà\ú0ÜiñÂô¬ß–ô5à¨-•5w0I{7Véîq¼_§-ûfeÛTMó¶åÑh[Se?×TbûúZlÌMg@l@¨ûV„&ýOYNøoŒÆVý;WX¶ç‰\0Gÿlw¤½¡©är{ji~í MKƒòò²Ð瀃{(kà\ú(ô‹òþ°ŸÑÏ€+í™ëp,.MðûÕÀ²€šœ}Ý= í¹Úrï¿pš±hP,´’ïd¼Ë1¢©QhÛ<… ³y @÷Ò+ ¦ÅÑÍž]Lü¸)ôïgY\Ø ²†æ+÷ìM9bÑ¥¶4²<ö/ç W#â#B%S4?vp…[n.û(Õü%Š`%ýŒ@¸¶ÏýÆâ‡”&ø=»ÃA5òv.¶£¸æi˶H½€Œ­à €PÕÓ|âðû+Ùz{5e]hOÍõ.Ì dÛÀµ€Ø €P[fúV„—Lþ¿+)à‘4ÍÍ7–møRS-@ä]6 ¼OjÒ-ÿ~M͇"B8¤@¨¾F’f›ƒí$¹iFpsÅGáP”‚ÿo¢™†P01t¾o<¸&Ñ‹ÉyY¸Tps‚ß³týó¥f“g<$TÇ;<¹áÆ ÔO“\úˉ3l¶•È)"šû4_»Ð‘[=v=#BíyÖÏ ·ÃàSÍVMÃÿn+ÍýVý³9°@²“K›ð=Í)¨i¡©œëo• ¥ùwÓlѼJùvúw¿v+!Á€Œ­u–lô!cì5Í».H›ÜZ} ôj˜¾þÔ(¯ÒGƒ×5¥z¿$PðAg¡„j.íñËæFúµÀþnœ@@¨‘€÷è»ýð˵­HhEÍö~µ À›ì£ñó;yèýg3¶~ðoF(eêE›ùHK? €s7$éû1¡ ¥…O‚ÊULzÍýšè£ ñ=Mõß« üKáÏ⌿°>7»ôÁ—"\[,¸¯K:,ææ5ú%à=ºÕ¢žŠaûZ8v[)pê>Lôªÿ.}k^à»q¶œæ}¤~A‰€ß³ý¶$Fm[êÂ=þA\@(Piîðl÷hs ïe÷k¬ær\`Ӏߧ üKÑÈkw;äFÀýôgXýýÇ3ðþì°è‡31nãÛtó³•.g7¹É?4møîÛbÑW7ñݸTpò6|vk®â~iiß ˆQÛ®rA(=7 VÊiîñ`ÙVž÷« ç¹G 9¶°<÷èòš}øËÑ‘óäAÆ[Ä„YýY_ŸL Ð>ïÏ ‹~Xã6–±,öEÀŽtì:ÜKcæÎ “jy¼Ýf¾—õ¡Tòÿ³¿¿×Œá]v¯þa6ŽaÛj9,*{,® GвХcßœûÛ¥¡VÁ÷JŽì;å¡ ñsÍLîÍe÷h¾ƒÿ{ƒ¼uÃ…AJè¨ ÛÁ}ôcØ}]IóQÿ$bÁÇÐqI‘Ž… Ÿ”|(€ã­rè´x—ßh²þî«’Pž[>èG€ë5{๖ wYÐSþó¹OEø=-ãöI‚[Þ÷¶ž¡ ¥¡æ¦(œ!r2A|ÄuÎ…ðxŒ_†ž—FÜ+îMQÍKøÿÊ&9Â1q¯0þJ‹ý8XeþÚç`@ߟ3~_úooˆðžM èx“”×Ñq*¦ö¥÷’ú¼û̽ØAßÝCŸØ—×jÆéä‹ÏC÷²<÷"ßûÓ&‚š?‹âÔÆqu‰¬Ž-Eð’Zaþk”·4û4xÐݯ šåšÿ£ÀñϚ͚*ô÷å*Í Í ø¢9ªª©È}ŠÊÇ`[„ƒ'8öϺ¯›XVÅŽ7¿Õ Oð{S;´O;œIpŒÙpîÛù ¥ÿÐ_ƒ4Ïk¾‰â‘Uµ'CR•Ì´+÷F¿摲õè³°ßWkB•÷¿Šâs-¿û¥PöA ú>¬{sgýúëX¯þçjãÂ2>¶=M&–LMgÍ^ÍÏ5߸p¤ßE9W\޶ãÁŽJÀÙ&´‡ûÍÏ4_»8~ªy^³G3ˆÊðß—«554›BY·¸ÌY)šúýK4S5Ã4=CÏDê3ÄôcÐ7ŒýÅ_‡ Æ“óþn®ªÌ{4T)ûqs¨]’æ9$(÷\_gr!g&ÿ(–{6 iëÖB&þÛkWôYEÍ„Ðý‚£jîÒŒeU4¬mP’U–B_YõmyÍHÍ š?¹´·ÿLèUŽ>¶*Þx¸€…•Gd´:PÓ–ã¸Ü³ÒšN²G_³2tßNjnÓ<ºo?Ý·û5çCçØ¯ýÙfP”¾°J™íúpݧyMól(\ÁªdÕŒÒìÑ<‡CÅÌŠ{¬­uB'Ü  ®ÓL‘Á½ «ÿªhºkhNh.H¥ëPµlyÿ½ÚÓ{cèya7^ÓS“Îh«>J#"”1!ý:‚LWû8MÓ;ô.Xêc‘÷‡žëï…žóë5;5‹Ciáò\×§]»C¢öáP柜ä2ÔƒíìÊ&¹%ô¹W qJ[5Åœün€@' @€€ @ @€€N@€€8 €m7¼‘¤™¾%)0L£À· C @€ä/þ?.”hQlœKÇIEND®B`‚durin42-hgsubversion-77b22e5b4ea6/notes/hgsubversion.svg0000644000000000000000000010504212043462603021415 0ustar 00000000000000 image/svg+xml durin42-hgsubversion-77b22e5b4ea6/notes/metadata.txt0000644000000000000000000000602412043462603020477 0ustar 00000000000000Branches -------- In order to handle branches that are not immediate children of /branches/, the following information must be stored in the revmap: revision path Where path is the actual relative path of the branch in svn. An example, with the previous format for clarification: New | Old 3 trunk | 3 4 branches/foo | 4 foo Tags ---- Note that if a tag is committed to, we can handle that case by making a branch and then marking it as deleted. The revmap line would look something like this: 10 tags/the_tag And the commit would be done on the hg branch 'modified-tag/the_tag'. Note that if this was 'tags/releases/1.0.0', then the branch would be 'modified-tag/releases/1.0.0'. Detecting Closing of Branches ----------------------------- Subversion users typically remove branches when done with them. This means that if a commit performs a delete operation on the '' path inside a branch, we can be sure that the branch no longer exists. The branch should then be marked as inactive. Closing Branches ---------------- As of this writing, Mercurial marks branches as inactive by merging them so they have no active heads. In order to mark a branch as closed, the active head on the branch will be used as the first parent of the new changeset, and the second changeset will be either the active head on the hg branch 'closed-branches' or be the nullrev. The commit to mark the branch as inactive will happen on the 'closed-branches' branch in Mercurial. Recovery -------- hgsubversion stores several pieces of essential metadata in .hg/svn/. In order to rebuild this data, the key 'convert_revision' should be stored in the extra dictionary of the converted revision. The key should contain data in the format: 'svn:/abs/path@' where is the repo UUID, /abs/path is the absolute path to the location the edits were made in Subversion (that is, if it was a trunk commit on /foo/trunk, then /foo/trunk is what gets stored here, even though the project root does not equal the repo root), and is the revision number of the change in Subversion. This key (and its contents) have been chosen to be compatible with the convert extension so that repos originally converted to hg using convert can be maintained using hgsubversion if desired. Tags (tag_info) can be reconstructed by listing the tags directory and then running log on each tag to determine its parent changeset. The last revision converted (last_rev) can be converted simply by using the highest revision number encountered while rebuilding the revision map. The legacy tag_locations file does not need to be replaced - it will be obsoleted as part of the long-term branch refactor. The url will have to be provided by the user. The uuid can be re-requested from the repository. branch_info can be rebuilt during the rebuild of the revision map by recording the revisions of all active heads of server-side branches. branch_info contains a map from branch: (parent_branch, parent_branch_rev, branch_created_rev) durin42-hgsubversion-77b22e5b4ea6/setup.py0000755000000000000000000001121412043462603016540 0ustar 00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import os import re import subprocess import sys import time if not hasattr(sys, 'version_info') or sys.version_info < (2, 4, 0, 'final'): raise SystemExit("Mercurial requires python 2.4 or later.") try: from distutils.command.build_py import build_py_2to3 as build_py except ImportError: from distutils.command.build_py import build_py try: from setuptools import setup except ImportError: from distutils.core import setup def runcmd(cmd, env): shell = os.name == 'nt' p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=shell, stderr=subprocess.PIPE, env=env) out, err = p.communicate() # If root is executing setup.py, but the repository is owned by # another user (as in "sudo python setup.py install") we will get # trust warnings since the .hg/hgrc file is untrusted. That is # fine, we don't want to load it anyway. err = [e for e in err.splitlines() if not e.startswith('Not trusting file')] if err: return '' return out version = '' if os.path.isdir('.hg'): # Execute hg out of this directory with a custom environment which # includes the pure Python modules in mercurial/pure. We also take # care to not use any hgrc files and do no localization. env = {'HGRCPATH': '', 'LANGUAGE': 'C'} for copyenv in ('LD_LIBRARY_PATH', 'PYTHONPATH', 'PATH'): if copyenv in os.environ: env[copyenv] = os.environ[copyenv] if 'SystemRoot' in os.environ: # Copy SystemRoot into the custom environment for Python 2.6 # under Windows. Otherwise, the subprocess will fail with # error 0xc0150004. See: http://bugs.python.org/issue3440 env['SystemRoot'] = os.environ['SystemRoot'] cmd = ['hg', 'id', '-i', '-t'] l = runcmd(cmd, env).split() while len(l) > 1 and l[-1][0].isalpha(): # remove non-numbered tags l.pop() if len(l) > 1: # tag found version = l[-1] if l[0].endswith('+'): # propagate the dirty status to the tag version += '+' elif len(l) == 1: # no tag found cmd = ['hg', 'parents', '--template', '{latesttag}+{latesttagdistance}-'] version = runcmd(cmd, env) + l[0] if not version: version = runcmd(['hg', 'parents', '--template' '{node|short}\n'], env) if version: version = version.split()[0] if version.endswith('+'): version += time.strftime('%Y%m%d') elif os.path.exists('.hg_archival.txt'): kw = dict([t.strip() for t in l.split(':', 1)] for l in open('.hg_archival.txt')) if 'tag' in kw: version = kw['tag'] elif 'latesttag' in kw: version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw else: version = kw.get('node', '')[:12] verfile = os.path.join("hgsubversion", "__version__.py") if version: f = open(verfile, "w") f.write('# this file is autogenerated by setup.py\n') f.write('version = "%s"\n' % version) f.close() if os.path.exists(verfile): # scrape the version out with a regex because setuptools # needlessly swaps out file() for some non-object thing # and breaks importing hgsubversion entirely mat = re.findall('.*"(.*)"', open(verfile).read()) version = mat[0] if not version: version = 'unknown' requires = [] try: import mercurial except ImportError: requires.append('mercurial') # If the Subversion SWIG bindings aren't present, require Subvertpy try: from hgsubversion.svnwrap import svn_swig_wrapper except ImportError: requires.append('subvertpy>=0.7.4') setup( name='hgsubversion', version=version, url='http://bitbucket.org/durin42/hgsubversion', license='GNU GPL', author='Augie Fackler, others', author_email='durin42@gmail.com', description=('hgsubversion is a Mercurial extension for working with ' 'Subversion repositories.'), long_description=open(os.path.join(os.path.dirname(__file__), 'README')).read(), keywords='mercurial', packages=('hgsubversion', 'hgsubversion.hooks', 'hgsubversion.svnwrap'), package_data={ 'hgsubversion': ['help/subversion.rst'] }, platforms='any', install_requires=requires, classifiers=[ 'License :: OSI Approved :: GNU General Public License (GPL)', 'Intended Audience :: Developers', 'Topic :: Software Development :: Version Control', 'Development Status :: 4 - Beta', 'Programming Language :: Python', 'Operating System :: OS Independent', ], cmdclass={'build_py': build_py}, ) durin42-hgsubversion-77b22e5b4ea6/tests/comprehensive/test_stupid_pull.py0000644000000000000000000000416312043462603025016 0ustar 00000000000000import os import pickle import sys import unittest from mercurial import hg from mercurial import ui # wrapped in a try/except because of weirdness in how # run.py works as compared to nose. try: import test_util except ImportError: sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) import test_util from hgsubversion import wrappers def _do_case(self, name, layout): subdir = test_util.subdir.get(name, '') 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 = repo_path if subdir: checkout_path += '/' + subdir u.setconfig('hgsubversion', 'stupid', '1') u.setconfig('hgsubversion', 'layout', layout) test_util.hgclone(u, test_util.fileurl(checkout_path), wc2_path, update=False) if layout == 'single': self.assertEqual(len(self.repo.heads()), 1) self.repo2 = hg.repository(ui.ui(), wc2_path) self.assertEqual(self.repo.heads(), self.repo2.heads()) def buildmethod(case, name, layout): m = lambda self: self._do_case(case, layout) m.__name__ = name m.__doc__ = 'Test stupid produces same as real on %s. (%s)' % (case, layout) return m attrs = {'_do_case': _do_case, } for case in (f for f in os.listdir(test_util.FIXTURES) if f.endswith('.svndump')): if case == 'corrupt.svndump': continue name = 'test_' + case[:-len('.svndump')].replace('-', '_') # Automatic layout branchtag collision exposes a minor defect # here, but since it isn't a regression we suppress the test case. if case != 'branchtagcollision.svndump': attrs[name] = buildmethod(case, name, 'auto') name += '_single' attrs[name] = buildmethod(case, name, 'single') StupidPullTests = type('StupidPullTests', (test_util.TestBase,), attrs) def suite(): all_tests = [unittest.TestLoader().loadTestsFromTestCase(StupidPullTests), ] return unittest.TestSuite(all_tests) durin42-hgsubversion-77b22e5b4ea6/tests/comprehensive/test_verify_and_startrev.py0000644000000000000000000000774612043462603026544 0ustar 00000000000000import os import pickle import sys import unittest # wrapped in a try/except because of weirdness in how # run.py works as compared to nose. try: import test_util except ImportError: sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) import test_util from mercurial import hg from mercurial import ui from hgsubversion import verify # these fixtures contain no files at HEAD and would result in empty clones _skipshallow = set([ 'binaryfiles.svndump', 'binaryfiles-broken.svndump', 'emptyrepo.svndump', 'correct.svndump', 'corrupt.svndump', ]) _skipall = set([ 'project_root_not_repo_root.svndump', 'movetotrunk.svndump', ]) _skipstandard = set([ 'subdir_is_file_prefix.svndump', 'correct.svndump', 'corrupt.svndump', 'emptyrepo2.svndump', ]) def _do_case(self, name, stupid, layout): subdir = test_util.subdir.get(name, '') repo, svnpath = self.load_and_fetch(name, subdir=subdir, stupid=stupid, layout=layout) assert len(self.repo) > 0 for i in repo: ctx = repo[i] self.assertEqual(verify.verify(repo.ui, repo, rev=ctx.node(), stupid=True), 0) self.assertEqual(verify.verify(repo.ui, repo, rev=ctx.node(), stupid=False), 0) # check a startrev clone if layout == 'single' and name not in _skipshallow: self.wc_path += '_shallow' shallowrepo = self.fetch(svnpath, subdir=subdir, stupid=stupid, layout='single', startrev='HEAD') self.assertEqual(len(shallowrepo), 1, "shallow clone should have just one revision, not %d" % len(shallowrepo)) fulltip = repo['tip'] shallowtip = shallowrepo['tip'] repo.ui.pushbuffer() self.assertEqual(0, verify.verify(repo.ui, shallowrepo, rev=shallowtip.node(), stupid=True)) self.assertEqual(0, verify.verify(repo.ui, shallowrepo, rev=shallowtip.node(), stupid=False)) stupidui = ui.ui(repo.ui) stupidui.config('hgsubversion', 'stupid', True) self.assertEqual(verify.verify(stupidui, repo, rev=ctx.node(), stupid=True), 0) self.assertEqual(verify.verify(stupidui, repo, rev=ctx.node(), stupid=False), 0) # viewing diff's of lists of files is easier on the eyes self.assertMultiLineEqual('\n'.join(fulltip), '\n'.join(shallowtip), repo.ui.popbuffer()) for f in fulltip: self.assertMultiLineEqual(fulltip[f].data(), shallowtip[f].data()) def buildmethod(case, name, stupid, layout): m = lambda self: self._do_case(case, stupid, layout) m.__name__ = name bits = case, stupid and 'stupid' or 'real', layout m.__doc__ = 'Test verify on %s with %s replay. (%s)' % bits return m attrs = {'_do_case': _do_case} fixtures = [f for f in os.listdir(test_util.FIXTURES) if f.endswith('.svndump')] for case in fixtures: if case in _skipall: continue bname = 'test_' + case[:-len('.svndump')] if case not in _skipstandard: attrs[bname] = buildmethod(case, bname, False, 'standard') name = bname + '_stupid' attrs[name] = buildmethod(case, name, True, 'standard') name = bname + '_single' attrs[name] = buildmethod(case, name, False, 'single') # Disabled because the "stupid and real are the same" tests # verify this plus even more. # name = bname + '_single_stupid' # attrs[name] = buildmethod(case, name, True, 'single') VerifyTests = type('VerifyTests', (test_util.TestBase,), attrs) def suite(): all_tests = [unittest.TestLoader().loadTestsFromTestCase(VerifyTests)] return unittest.TestSuite(all_tests) durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/addspecial.sh0000644000000000000000000000153412043462603022467 0ustar 00000000000000#!/bin/sh mkdir temp cd temp svnadmin create repo svn co file://`pwd`/repo wc cd wc mkdir -p trunk branches svn add trunk branches svn ci -m'initial structure' cd trunk echo a>a svn add a svn ci -mci1 a cd .. svn up svn cp trunk branches/foo svn ci -m'branch foo' cd branches/foo ln -s a fnord svn add fnord svn ci -msymlink fnord mkdir 'spacy name' echo a > 'spacy name/spacy file' svn add 'spacy name' svn ci -mspacy 'spacy name' svn up echo b > 'spacy name/surprise ~' svn add 'spacy name/surprise ~' svn ci -mtilde 'spacy name' svn up ../.. echo foo > exe chmod +x exe svn add exe svn ci -mexecutable exe svn up ../.. cd ../../trunk svn merge ../branches/foo svn ci -mmerge svn up pwd cd ../../.. svnadmin dump temp/repo > addspecial.svndump echo echo 'Complete.' echo 'You probably want to clean up temp now.' echo 'Dump in addspecial.svndump' exit 0 durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/addspecial.svndump0000644000000000000000000001003512043462603023545 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 01df53ad-5d72-4756-8742-f669dc98f791 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2012-05-13T22:22:43.218190Z PROPS-END Revision-number: 1 Prop-content-length: 118 Content-length: 118 K 10 svn:author V 6 bryano K 8 svn:date V 27 2012-05-13T22:22:44.112163Z K 7 svn:log V 17 initial structure PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 103 Content-length: 103 K 10 svn:author V 6 bryano K 8 svn:date V 27 2012-05-13T22:22:45.111247Z K 7 svn:log V 3 ci1 PROPS-END Node-path: trunk/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Text-content-sha1: 3f786850e387550fdab836ed7e6dc881de23001b Content-length: 12 PROPS-END a Revision-number: 3 Prop-content-length: 111 Content-length: 111 K 10 svn:author V 6 bryano K 8 svn:date V 27 2012-05-13T22:22:48.110257Z K 7 svn:log V 10 branch foo PROPS-END Node-path: branches/foo Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk Revision-number: 4 Prop-content-length: 107 Content-length: 107 K 10 svn:author V 6 bryano K 8 svn:date V 27 2012-05-13T22:22:49.115096Z K 7 svn:log V 7 symlink PROPS-END Node-path: branches/foo/fnord Node-kind: file Node-action: add Prop-content-length: 33 Text-content-length: 6 Text-content-md5: c118dba188202a1efc975bef6064180b Text-content-sha1: 41f94e4692313bf7f7c92aa600002f1dff93d6bf Content-length: 39 K 11 svn:special V 1 * PROPS-END link a Revision-number: 5 Prop-content-length: 105 Content-length: 105 K 10 svn:author V 6 bryano K 8 svn:date V 27 2012-05-13T22:22:50.119266Z K 7 svn:log V 5 spacy PROPS-END Node-path: branches/foo/spacy name Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: branches/foo/spacy name/spacy file Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Text-content-sha1: 3f786850e387550fdab836ed7e6dc881de23001b Content-length: 12 PROPS-END a Revision-number: 6 Prop-content-length: 105 Content-length: 105 K 10 svn:author V 6 bryano K 8 svn:date V 27 2012-05-13T22:22:52.123367Z K 7 svn:log V 5 tilde PROPS-END Node-path: branches/foo/spacy name/surprise ~ Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 3b5d5c3712955042212316173ccf37be Text-content-sha1: 89e6c98d92887913cadf06b2adb97f26cde4849b Content-length: 12 PROPS-END b Revision-number: 7 Prop-content-length: 111 Content-length: 111 K 10 svn:author V 6 bryano K 8 svn:date V 27 2012-05-13T22:22:54.129462Z K 7 svn:log V 10 executable PROPS-END Node-path: branches/foo/exe Node-kind: file Node-action: add Prop-content-length: 36 Text-content-length: 4 Text-content-md5: d3b07384d113edec49eaa6238ad5ff00 Text-content-sha1: f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 Content-length: 40 K 14 svn:executable V 1 * PROPS-END foo Revision-number: 8 Prop-content-length: 105 Content-length: 105 K 10 svn:author V 6 bryano K 8 svn:date V 27 2012-05-13T22:22:57.111370Z K 7 svn:log V 5 merge PROPS-END Node-path: trunk Node-kind: dir Node-action: change Prop-content-length: 52 Content-length: 52 K 13 svn:mergeinfo V 17 /branches/foo:3-7 PROPS-END Node-path: trunk/exe Node-kind: file Node-action: add Node-copyfrom-rev: 7 Node-copyfrom-path: branches/foo/exe Text-copy-source-md5: d3b07384d113edec49eaa6238ad5ff00 Text-copy-source-sha1: f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 Node-path: trunk/fnord Node-kind: file Node-action: add Node-copyfrom-rev: 7 Node-copyfrom-path: branches/foo/fnord Text-copy-source-md5: c118dba188202a1efc975bef6064180b Text-copy-source-sha1: 41f94e4692313bf7f7c92aa600002f1dff93d6bf Node-path: trunk/spacy name Node-kind: dir Node-action: add Node-copyfrom-rev: 7 Node-copyfrom-path: branches/foo/spacy name durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/author-map-test.txt0000644000000000000000000003275312043462603023644 0ustar 00000000000000alpha01 = Alpha alpha02 = Alpha alpha03 = Alpha alpha04 = Alpha alpha05 = Alpha alpha06 = Alpha alpha07 = Alpha alpha08 = Alpha alpha09 = Alpha alpha10 = Alpha alpha11 = Alpha alpha12 = Alpha alpha13 = Alpha alpha14 = Alpha alpha15 = Alpha alpha16 = Alpha alpha17 = Alpha alpha18 = Alpha alpha19 = Alpha alpha20 = Alpha alpha21 = Alpha alpha22 = Alph5 alpha23 = Alpha alpha24 = Alpha alpha25 = Alpha alpha26 = Alpha alpha27 = Alpha alpha28 = Alpha alpha29 = Alpha alpha30 = Alpha alpha31 = Alpha alpha32 = Alpha alpha33 = Alpha alpha34 = Alpha alpha35 = Alpha alpha36 = Alpha alpha37 = Alph6 alpha38 = Alpha alpha39 = Alpha alpha40 = Alpha alpha41 = Alpha alpha42 = Alpha alpha43 = Alpha alpha44 = Alph8 alpha45 = Alpha alpha46 = Alpha alpha47 = Alpha alpha48 = Alpha alpha49 = Alpha alpha50 = Alpha alpha51 = Alpha alpha52 = Alpha alpha53 = Alpha alpha54 = Alpha alpha55 = Alpha alpha56 = Alpha alpha57 = Alph6 alpha58 = Alpha alpha59 = Alpha alpha60 = Alpha alpha61 = Alpha alpha62 = Alpha alpha63 = Alpha alpha64 = Alpha alpha65 = Alpha alpha66 = Alpha alpha67 = Alph6 alpha68 = Alpha alpha69 = Alpha alpha70 = Alpha alpha71 = Alpha alpha72 = Alpha alpha73 = Alpha alpha74 = Alpha alpha75 = Alpha alpha76 = Alpha alpha77 = Alph6 alpha78 = Alpha alpha79 = Alpha alpha80 = Alpha alpha81 = Alpha alpha82 = Alpha alpha83 = Alpha alpha84 = Alpha alpha85 = Alpha alpha86 = Alpha alpha87 = Alpha alpha88 = Alpha alpha89 = Alpha alpha90 = Alpha alpha91 = Alpha alpha92 = Alpha alpha93 = Alpha alpha94 = Alpha alpha95 = Alpha alpha96 = Alpha alpha97 = Alpha alpha98 = Alpha alpha99 = Alpha alpha101 = Alpha alpha102 = Alpha alpha103 = Alpha alpha104 = Alpha alpha105 = Alpha alpha106 = Alpha alpha107 = Alpha alpha108 = Alpha alpha109 = Alpha alpha110 = Alpha alpha111 = Alpha alpha112 = Alpha alpha113 = Alpha alpha114 = Alpha alpha115 = Alpha alpha116 = Alpha alpha117 = Alpha alpha118 = Alpha alpha119 = Alpha alpha120 = Alpha alpha121 = Alpha alpha122 = Alph5 alpha123 = Alpha alpha124 = Alpha alpha125 = Alpha alpha126 = Alpha alpha127 = Alpha alpha128 = Alpha alpha129 = Alpha alpha130 = Alpha alpha131 = Alpha alpha132 = Alpha alpha133 = Alpha alpha134 = Alpha alpha135 = Alpha alpha136 = Alpha alpha137 = Alph6 alpha138 = Alpha alpha139 = Alpha alpha140 = Alpha alpha141 = Alpha alpha142 = Alpha alpha143 = Alpha alpha144 = Alph8 alpha145 = Alpha alpha146 = Alpha alpha147 = Alpha alpha148 = Alpha alpha149 = Alpha alpha150 = Alpha alpha151 = Alpha alpha152 = Alpha alpha153 = Alpha alpha154 = Alpha alpha155 = Alpha alpha156 = Alpha alpha157 = Alph6 alpha158 = Alpha alpha159 = Alpha alpha160 = Alpha alpha161 = Alpha alpha162 = Alpha alpha163 = Alpha alpha164 = Alpha alpha165 = Alpha alpha166 = Alpha alpha167 = Alph6 alpha168 = Alpha alpha169 = Alpha alpha170 = Alpha alpha171 = Alpha alpha172 = Alpha alpha173 = Alpha alpha174 = Alpha alpha175 = Alpha alpha176 = Alpha alpha177 = Alph6 alpha178 = Alpha alpha179 = Alpha alpha180 = Alpha alpha181 = Alpha alpha182 = Alpha alpha183 = Alpha alpha184 = Alpha alpha185 = Alpha alpha186 = Alpha alpha187 = Alpha alpha188 = Alpha alpha189 = Alpha alpha190 = Alpha alpha191 = Alpha alpha192 = Alpha alpha193 = Alpha alpha194 = Alpha alpha195 = Alpha alpha196 = Alpha alpha197 = Alpha alpha198 = Alpha alpha199 = Alpha alpha201 = Alpha alpha202 = Alpha alpha203 = Alpha alpha204 = Alpha alpha205 = Alpha alpha206 = Alpha alpha207 = Alpha alpha208 = Alpha alpha209 = Alpha alpha210 = Alpha alpha211 = Alpha alpha212 = Alpha alpha213 = Alpha alpha214 = Alpha alpha215 = Alpha alpha216 = Alpha alpha217 = Alpha alpha218 = Alpha alpha219 = Alpha alpha220 = Alpha alpha221 = Alpha alpha222 = Alph5 alpha223 = Alpha alpha224 = Alpha alpha225 = Alpha alpha226 = Alpha alpha227 = Alpha alpha228 = Alpha alpha229 = Alpha alpha230 = Alpha alpha231 = Alpha alpha232 = Alpha alpha233 = Alpha alpha234 = Alpha alpha235 = Alpha alpha236 = Alpha alpha237 = Alph6 alpha238 = Alpha alpha239 = Alpha alpha240 = Alpha alpha241 = Alpha alpha242 = Alpha alpha243 = Alpha alpha244 = Alph8 alpha245 = Alpha alpha246 = Alpha alpha247 = Alpha alpha248 = Alpha alpha249 = Alpha alpha250 = Alpha alpha251 = Alpha alpha252 = Alpha alpha253 = Alpha alpha254 = Alpha alpha255 = Alpha alpha256 = Alpha alpha257 = Alph6 alpha258 = Alpha alpha259 = Alpha alpha260 = Alpha alpha261 = Alpha alpha262 = Alpha alpha263 = Alpha alpha264 = Alpha alpha265 = Alpha alpha266 = Alpha alpha267 = Alph6 alpha268 = Alpha alpha269 = Alpha alpha270 = Alpha alpha271 = Alpha alpha272 = Alpha alpha273 = Alpha alpha274 = Alpha alpha275 = Alpha alpha276 = Alpha alpha277 = Alph6 alpha278 = Alpha alpha279 = Alpha alpha280 = Alpha alpha281 = Alpha alpha282 = Alpha alpha283 = Alpha alpha284 = Alpha alpha285 = Alpha alpha286 = Alpha alpha287 = Alpha alpha288 = Alpha alpha289 = Alpha alpha290 = Alpha alpha291 = Alpha alpha292 = Alpha alpha293 = Alpha alpha294 = Alpha alpha295 = Alpha alpha296 = Alpha alpha297 = Alpha alpha298 = Alpha alpha299 = Alpha alpha301 = Alpha alpha302 = Alpha alpha303 = Alpha alpha304 = Alpha alpha305 = Alpha alpha306 = Alpha alpha307 = Alpha alpha308 = Alpha alpha309 = Alpha alpha310 = Alpha alpha311 = Alpha alpha312 = Alpha alpha313 = Alpha alpha314 = Alpha alpha315 = Alpha alpha316 = Alpha alpha317 = Alpha alpha318 = Alpha alpha319 = Alpha alpha320 = Alpha alpha321 = Alpha alpha322 = Alph5 alpha323 = Alpha alpha324 = Alpha alpha325 = Alpha alpha326 = Alpha alpha327 = Alpha alpha328 = Alpha alpha329 = Alpha alpha330 = Alpha alpha331 = Alpha alpha332 = Alpha alpha333 = Alpha alpha334 = Alpha alpha335 = Alpha alpha336 = Alpha alpha337 = Alph6 alpha338 = Alpha alpha339 = Alpha alpha340 = Alpha alpha341 = Alpha alpha342 = Alpha alpha343 = Alpha alpha344 = Alph8 alpha345 = Alpha alpha346 = Alpha alpha347 = Alpha alpha348 = Alpha alpha349 = Alpha alpha350 = Alpha alpha351 = Alpha alpha352 = Alpha alpha353 = Alpha alpha354 = Alpha alpha355 = Alpha alpha356 = Alpha alpha357 = Alph6 alpha358 = Alpha alpha359 = Alpha alpha360 = Alpha alpha361 = Alpha alpha362 = Alpha alpha363 = Alpha alpha364 = Alpha alpha365 = Alpha alpha366 = Alpha alpha367 = Alph6 alpha368 = Alpha alpha639 = Alpha alpha370 = Alpha alpha371 = Alpha alpha372 = Alpha alpha373 = Alpha alpha374 = Alpha alpha375 = Alpha alpha376 = Alpha alpha377 = Alph6 alpha738 = Alpha alpha379 = Alpha alpha380 = Alpha alpha381 = Alpha alpha382 = Alpha alpha383 = Alpha alpha384 = Alpha alpha385 = Alpha alpha386 = Alpha alpha387 = Alpha alpha388 = Alpha alpha389 = Alpha alpha390 = Alpha alpha91 = Alpha alph3a92 = Alpha alph3a93 = Alpha alph3a94 = Alpha alph3a95 = Alpha alph3a96 = Alpha alph3a97 = Alpha alph3a98 = Alpha alpha99 = Alpha dsadsakdoa = dksadosakfa durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/basic_tag_tests.svndump0000644000000000000000000000404612043462603024617 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: df2126f7-00ab-4d49-b42c-7e981dde0bcf Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-10-07T22:49:12.059692Z PROPS-END Revision-number: 1 Prop-content-length: 111 Content-length: 111 K 7 svn:log V 11 Empty dirs. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T22:49:41.118037Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 108 Content-length: 108 K 7 svn:log V 9 Add alpha K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T23:23:02.991743Z PROPS-END Node-path: trunk/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 12 Text-content-md5: 3c72ebf8bbd7fa88b1fdcee5398b5a17 Content-length: 22 PROPS-END file: alpha Revision-number: 3 Prop-content-length: 107 Content-length: 107 K 7 svn:log V 8 Add beta K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T23:28:22.651398Z PROPS-END Node-path: trunk/beta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 13 Text-content-md5: 981d1eb5fd0bbe05354c292105944863 Content-length: 23 PROPS-END Data of beta Revision-number: 4 Prop-content-length: 110 Content-length: 110 K 7 svn:log V 10 tagging r3 K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-08T23:18:29.837825Z PROPS-END Node-path: tags/tag_r3 Node-kind: dir Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: trunk Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Revision-number: 5 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 14 tag from a tag K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-08T23:18:51.091356Z PROPS-END Node-path: tags/copied_tag Node-kind: dir Node-action: add Node-copyfrom-rev: 4 Node-copyfrom-path: tags/tag_r3 durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/binaryfiles-broken.svndump0000644000000000000000000000345312043462603025247 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 4b83c728-5362-49a3-a872-f9349e919ff8 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-12-29T17:06:25.134644Z PROPS-END Revision-number: 1 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 12 init project K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-29T17:06:25.199237Z PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 12 add binaries K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-29T17:06:26.340200Z PROPS-END Node-path: trunk/binary3 Node-kind: file Node-action: add Prop-content-length: 59 Text-content-length: 7 Text-content-md5: 18952ae5b94101fb5399c2a07145e47c Content-length: 66 K 13 svn:mime-type V 24 application/octet-stream PROPS-END a bb Node-path: trunk/binary2 Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 7 Text-content-md5: 372757004513a4d1138fb24103d9ddb5 Content-length: 17 PROPS-END b cd Revision-number: 3 Prop-content-length: 117 Content-length: 117 K 7 svn:log V 15 change binaries K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-29T17:06:27.242109Z PROPS-END Node-path: trunk/binary3 Node-kind: file Node-action: change Text-content-length: 7 Text-content-md5: 4a78679940e45d9cc03f542baceeb029 Content-length: 7 a cd Node-path: trunk/binary2 Node-kind: file Node-action: change Text-content-length: 8 Content-length: 8 b df Revision-number: 4 Prop-content-length: 117 Content-length: 117 K 7 svn:log V 15 remove binaries K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-29T17:06:28.196365Z PROPS-END Node-path: trunk/binary3 Node-action: delete Node-path: trunk/binary2 Node-action: delete durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/binaryfiles.sh0000755000000000000000000000151412043462603022706 0ustar 00000000000000#!/bin/sh # # Generate binaryfiles.svndump # mkdir temp cd temp mkdir project-orig cd project-orig mkdir trunk cd .. svnadmin create testrepo svnurl=file://`pwd`/testrepo svn import project-orig $svnurl -m "init project" svn co $svnurl project cd project/trunk # Add a regular binary file, and an unflagged one python -c "file('binary1', 'wb').write('a\0\0\nb\0b')" python -c "file('binary2', 'wb').write('b\0\0\nc\0d')" svn add binary1 binary2 svn propset svn:mime-type application/octet-stream binary1 svn propdel svn:mime-type binary2 svn ci -m 'add binaries' # Update them python -c "file('binary1', 'wb').write('a\0\0\nc\0d')" python -c "file('binary2', 'wb').write('b\0\0\0\nd\0e')" svn ci -m 'change binaries' # Remove them svn rm binary1 binary2 svn ci -m 'remove binaries' cd ../.. svnadmin dump testrepo > ../binaryfiles.svndump durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/binaryfiles.svndump0000644000000000000000000000353612043462603023773 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 4b83c728-5362-49a3-a872-f9349e919ff8 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-12-29T17:06:25.134644Z PROPS-END Revision-number: 1 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 12 init project K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-29T17:06:25.199237Z PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 12 add binaries K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-29T17:06:26.340200Z PROPS-END Node-path: trunk/binary1 Node-kind: file Node-action: add Prop-content-length: 59 Text-content-length: 7 Text-content-md5: 18952ae5b94101fb5399c2a07145e47c Content-length: 66 K 13 svn:mime-type V 24 application/octet-stream PROPS-END a bb Node-path: trunk/binary2 Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 7 Text-content-md5: 372757004513a4d1138fb24103d9ddb5 Content-length: 17 PROPS-END b cd Revision-number: 3 Prop-content-length: 117 Content-length: 117 K 7 svn:log V 15 change binaries K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-29T17:06:27.242109Z PROPS-END Node-path: trunk/binary1 Node-kind: file Node-action: change Text-content-length: 7 Text-content-md5: 4a78679940e45d9cc03f542baceeb029 Content-length: 7 a cd Node-path: trunk/binary2 Node-kind: file Node-action: change Text-content-length: 8 Text-content-md5: 3a169d58b609c6003638b767f9afc408 Content-length: 8 b de Revision-number: 4 Prop-content-length: 117 Content-length: 117 K 7 svn:log V 15 remove binaries K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-29T17:06:28.196365Z PROPS-END Node-path: trunk/binary1 Node-action: delete Node-path: trunk/binary2 Node-action: delete durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/branch_create_with_dir_delete.sh0000755000000000000000000000135312043462603026373 0ustar 00000000000000#!/bin/sh mkdir temp cd temp svnadmin create repo svn co file://`pwd`/repo wc cd wc mkdir branches trunk tags svn add * svn ci -m 'btt' cd trunk for a in alpha beta gamma delta ; do echo $a > $a svn add $a done svn ci -m 'Add files.' mkdir al echo foo > al/foo svn add al svn ci -m 'add directory al to delete on the branch' cd .. svn up svn cp trunk branches/dev_branch svn rm branches/dev_branch/al svn ci -m 'branch' cd branches/dev_branch svn rm delta echo narf > alpha echo iota > iota svn add iota svn ci -m 'branch changes' cd ../../../.. svnadmin dump temp/repo > branch_create_with_dir_delete.svndump echo echo 'Complete.' echo 'You probably want to clean up temp now.' echo 'Dump in branch_create_with_dir_delete.svndump' exit 0 durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/branch_create_with_dir_delete.svndump0000644000000000000000000000601212043462603027447 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 88f76347-3f0e-4a36-b1e1-8dec7ad11590 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2009-04-07T22:37:33.524401Z PROPS-END Revision-number: 1 Prop-content-length: 102 Content-length: 102 K 7 svn:log V 3 btt K 10 svn:author V 5 durin K 8 svn:date V 27 2009-04-07T22:37:34.076074Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 110 Content-length: 110 K 7 svn:log V 10 Add files. K 10 svn:author V 5 durin K 8 svn:date V 27 2009-04-07T22:37:35.119223Z PROPS-END Node-path: trunk/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 9f9f90dbe3e5ee1218c86b8839db1995 Content-length: 16 PROPS-END alpha Node-path: trunk/beta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 5 Text-content-md5: f0cf2a92516045024a0c99147b28f05b Content-length: 15 PROPS-END beta Node-path: trunk/delta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: d2840cc81bc032bd1141b56687d0f93c Content-length: 16 PROPS-END delta Node-path: trunk/gamma Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 303febb9068384eca46b5b6516843b35 Content-length: 16 PROPS-END gamma Revision-number: 3 Prop-content-length: 140 Content-length: 140 K 7 svn:log V 40 add directory al to delete on the branch K 10 svn:author V 5 durin K 8 svn:date V 27 2009-04-07T22:37:36.075475Z PROPS-END Node-path: trunk/al Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/al/foo Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 4 Text-content-md5: d3b07384d113edec49eaa6238ad5ff00 Content-length: 14 PROPS-END foo Revision-number: 4 Prop-content-length: 105 Content-length: 105 K 7 svn:log V 6 branch K 10 svn:author V 5 durin K 8 svn:date V 27 2009-04-07T22:37:39.073788Z PROPS-END Node-path: branches/dev_branch Node-kind: dir Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: trunk Node-path: branches/dev_branch/al Node-action: delete Revision-number: 5 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 14 branch changes K 10 svn:author V 5 durin K 8 svn:date V 27 2009-04-07T22:37:40.093715Z PROPS-END Node-path: branches/dev_branch/alpha Node-kind: file Node-action: change Text-content-length: 5 Text-content-md5: 5e723ed52db2000686425ca28bc5ba4a Content-length: 5 narf Node-path: branches/dev_branch/iota Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 5 Text-content-md5: ebcf3971120220589f1dfbf8d56e25b9 Content-length: 15 PROPS-END iota Node-path: branches/dev_branch/delta Node-action: delete durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/branch_delete_parent_dir.sh0000755000000000000000000000077112043462603025371 0ustar 00000000000000#!/bin/sh mkdir temp cd temp svnadmin create repo svn co file://`pwd`/repo wc cd wc mkdir branches trunk tags svn add * svn ci -m 'btt' echo foo > trunk/foo svn add trunk/foo svn ci -m 'add file' svn up svn cp trunk branches/dev_branch svn ci -m 'branch' svn up svn rm branches svn ci -m 'delete branches dir' cd .. cd .. svnadmin dump temp/repo > branch_delete_parent_dir.svndump echo echo 'Complete.' echo 'You probably want to clean up temp now.' echo 'Dump in branch_delete_parent_dir.svndump' exit 0 durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/branch_delete_parent_dir.svndump0000644000000000000000000000310712043462603026444 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 60132d6f-a460-4b38-8ae6-633264894f73 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2009-06-30T23:57:20.562207Z PROPS-END Revision-number: 1 Prop-content-length: 101 Content-length: 101 K 7 svn:log V 3 btt K 10 svn:author V 4 maxb K 8 svn:date V 27 2009-06-30T23:57:21.078798Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 106 Content-length: 106 K 7 svn:log V 8 add file K 10 svn:author V 4 maxb K 8 svn:date V 27 2009-06-30T23:57:22.098826Z PROPS-END Node-path: trunk/foo Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 4 Text-content-md5: d3b07384d113edec49eaa6238ad5ff00 Text-content-sha1: f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 Content-length: 14 PROPS-END foo Revision-number: 3 Prop-content-length: 104 Content-length: 104 K 7 svn:log V 6 branch K 10 svn:author V 4 maxb K 8 svn:date V 27 2009-06-30T23:57:25.055724Z PROPS-END Node-path: branches/dev_branch Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk Revision-number: 4 Prop-content-length: 118 Content-length: 118 K 7 svn:log V 19 delete branches dir K 10 svn:author V 4 maxb K 8 svn:date V 27 2009-06-30T23:57:27.072926Z PROPS-END Node-path: branches Node-action: delete durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/branch_from_tag.svndump0000644000000000000000000000454012043462603024573 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: df2126f7-00ab-4d49-b42c-7e981dde0bcf Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-10-07T22:49:12.059692Z PROPS-END Revision-number: 1 Prop-content-length: 111 Content-length: 111 K 7 svn:log V 11 Empty dirs. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T22:49:41.118037Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 108 Content-length: 108 K 7 svn:log V 9 Add alpha K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T23:23:02.991743Z PROPS-END Node-path: trunk/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 12 Text-content-md5: 3c72ebf8bbd7fa88b1fdcee5398b5a17 Content-length: 22 PROPS-END file: alpha Revision-number: 3 Prop-content-length: 107 Content-length: 107 K 7 svn:log V 8 Add beta K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T23:28:22.651398Z PROPS-END Node-path: trunk/beta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 13 Text-content-md5: 981d1eb5fd0bbe05354c292105944863 Content-length: 23 PROPS-END Data of beta Revision-number: 4 Prop-content-length: 110 Content-length: 110 K 7 svn:log V 10 tagging r3 K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-08T23:18:29.837825Z PROPS-END Node-path: tags/tag_r3 Node-kind: dir Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: trunk Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Revision-number: 5 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 14 tag from a tag K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-08T23:18:51.091356Z PROPS-END Node-path: tags/copied_tag Node-kind: dir Node-action: add Node-copyfrom-rev: 4 Node-copyfrom-path: tags/tag_r3 Revision-number: 6 Prop-content-length: 125 Content-length: 125 K 7 svn:log V 25 Make a branch from a tag. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-09T00:07:48.076984Z PROPS-END Node-path: branches/branch_from_tag Node-kind: dir Node-action: add Node-copyfrom-rev: 5 Node-copyfrom-path: tags/tag_r3 durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/branch_prop_edit.sh0000755000000000000000000000127312043462603023703 0ustar 00000000000000#!/bin/sh mkdir temp cd temp svnadmin create repo svn co file://`pwd`/repo wc cd wc mkdir branches trunk svn add * svn ci -m 'branches trunk' svn up cd trunk for a in alpha beta gamma ; do echo $a > $a svn add $a done svn ci -m 'Files.' cd .. svn up svn cp trunk branches/dev_branch svn ci -m 'make a branch' svn up cd branches/dev_branch echo epsilon > epsilon svn add epsilon svn ci -m 'Add a file on the branch.' svn up cd ../.. cd branches/dev_branch svn ps 'svn:ignore' 'delta' . svn ci -m 'Commit bogus propchange.' svn up cd ../../.. pwd svnadmin dump repo > ../branch_prop_edit.svndump cd .. echo 'Dump created in branch_prop_edit.svndump. You can probably delete temp.' exit 0 durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/branch_prop_edit.svndump0000644000000000000000000000447412043462603024770 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: ab14e60b-7e4b-473f-980a-26ace458a00f Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-12-12T00:18:13.309956Z PROPS-END Revision-number: 1 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 14 branches trunk K 10 svn:author V 5 durin K 8 svn:date V 27 2008-12-12T00:18:14.083854Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 105 Content-length: 105 K 7 svn:log V 6 Files. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-12-12T00:18:16.270290Z PROPS-END Node-path: trunk/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 9f9f90dbe3e5ee1218c86b8839db1995 Content-length: 16 PROPS-END alpha Node-path: trunk/beta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 5 Text-content-md5: f0cf2a92516045024a0c99147b28f05b Content-length: 15 PROPS-END beta Node-path: trunk/gamma Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 303febb9068384eca46b5b6516843b35 Content-length: 16 PROPS-END gamma Revision-number: 3 Prop-content-length: 113 Content-length: 113 K 7 svn:log V 13 make a branch K 10 svn:author V 5 durin K 8 svn:date V 27 2008-12-12T00:18:19.069446Z PROPS-END Node-path: branches/dev_branch Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk Revision-number: 4 Prop-content-length: 125 Content-length: 125 K 7 svn:log V 25 Add a file on the branch. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-12-12T00:18:21.088611Z PROPS-END Node-path: branches/dev_branch/epsilon Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 8 Text-content-md5: c40719840583e3f3e6744c02828d7cd9 Content-length: 18 PROPS-END epsilon Revision-number: 5 Prop-content-length: 124 Content-length: 124 K 7 svn:log V 24 Commit bogus propchange. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-12-12T00:18:23.078263Z PROPS-END Node-path: branches/dev_branch Node-kind: dir Node-action: change Prop-content-length: 37 Content-length: 37 K 10 svn:ignore V 6 delta PROPS-END durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/branch_rename_to_trunk.sh0000755000000000000000000000137212043462603025112 0ustar 00000000000000#!/bin/sh mkdir temp cd temp svnadmin create repo svn co file://`pwd`/repo wc cd wc mkdir branches trunk tags svn add * svn ci -m 'btt' cd trunk for a in alpha beta gamma delta ; do echo $a > $a svn add $a done svn ci -m 'Add files.' cd .. svn up svn cp trunk branches/dev_branch svn ci -m 'branch' cd branches/dev_branch svn rm delta echo narf > alpha echo iota > iota svn add iota svn ci -m 'branch changes' cd ../.. svn up svn mv trunk branches/old_trunk svn ci -m 'move trunk to a branch' svn up svn mv branches/dev_branch trunk svn ci -m 'move dev to trunk' cd .. cd .. svnadmin dump temp/repo > branch_rename_to_trunk.svndump echo echo 'Complete.' echo 'You probably want to clean up temp now.' echo 'Dump in branch_rename_to_trunk.svndump' exit 0 durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/branch_rename_to_trunk.svndump0000644000000000000000000000620712043462603026173 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: f6ca240b-44b4-4753-9ebe-569095e6ee32 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-12-10T21:37:23.126889Z PROPS-END Revision-number: 1 Prop-content-length: 102 Content-length: 102 K 7 svn:log V 3 btt K 10 svn:author V 5 durin K 8 svn:date V 27 2008-12-10T21:37:24.068831Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 110 Content-length: 110 K 7 svn:log V 10 Add files. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-12-10T21:37:25.115468Z PROPS-END Node-path: trunk/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 9f9f90dbe3e5ee1218c86b8839db1995 Content-length: 16 PROPS-END alpha Node-path: trunk/beta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 5 Text-content-md5: f0cf2a92516045024a0c99147b28f05b Content-length: 15 PROPS-END beta Node-path: trunk/delta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: d2840cc81bc032bd1141b56687d0f93c Content-length: 16 PROPS-END delta Node-path: trunk/gamma Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 303febb9068384eca46b5b6516843b35 Content-length: 16 PROPS-END gamma Revision-number: 3 Prop-content-length: 105 Content-length: 105 K 7 svn:log V 6 branch K 10 svn:author V 5 durin K 8 svn:date V 27 2008-12-10T21:37:28.044281Z PROPS-END Node-path: branches/dev_branch Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk Revision-number: 4 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 14 branch changes K 10 svn:author V 5 durin K 8 svn:date V 27 2008-12-10T21:37:29.085784Z PROPS-END Node-path: branches/dev_branch/alpha Node-kind: file Node-action: change Text-content-length: 5 Text-content-md5: 5e723ed52db2000686425ca28bc5ba4a Content-length: 5 narf Node-path: branches/dev_branch/iota Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 5 Text-content-md5: ebcf3971120220589f1dfbf8d56e25b9 Content-length: 15 PROPS-END iota Node-path: branches/dev_branch/delta Node-action: delete Revision-number: 5 Prop-content-length: 122 Content-length: 122 K 7 svn:log V 22 move trunk to a branch K 10 svn:author V 5 durin K 8 svn:date V 27 2008-12-10T21:37:32.056153Z PROPS-END Node-path: branches/old_trunk Node-kind: dir Node-action: add Node-copyfrom-rev: 4 Node-copyfrom-path: trunk Node-path: trunk Node-action: delete Revision-number: 6 Prop-content-length: 117 Content-length: 117 K 7 svn:log V 17 move dev to trunk K 10 svn:author V 5 durin K 8 svn:date V 27 2008-12-10T21:37:35.046793Z PROPS-END Node-path: branches/dev_branch Node-action: delete Node-path: trunk Node-kind: dir Node-action: add Node-copyfrom-rev: 5 Node-copyfrom-path: branches/dev_branch durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/branchmap.sh0000755000000000000000000000106512043462603022333 0ustar 00000000000000#!/bin/sh # inspired by Python r62868 mkdir temp cd temp svnadmin create repo svn co file://`pwd`/repo wc export REPO=file://`pwd`/repo cd wc mkdir branches trunk tags svn add * svn ci -m 'btt' echo a > trunk/a svn add trunk/a svn ci -m 'Add file.' svn up svn cp trunk branches/badname svn ci -m 'Branch to be renamed.' svn up svn cp trunk branches/feature svn ci -m 'Branch to be unnamed.' svn up cd ../.. svnadmin dump temp/repo > branchmap.svndump echo echo 'Complete.' echo 'You probably want to clean up temp now.' echo 'Dump in branchmap.svndump' exit 0 durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/branchmap.svndump0000644000000000000000000000322212043462603023407 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: e45f170d-c19e-4c14-836b-556bb41c9429 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2010-02-23T17:55:37.753400Z PROPS-END Revision-number: 1 Prop-content-length: 100 Content-length: 100 K 7 svn:log V 3 btt K 10 svn:author V 3 djc K 8 svn:date V 27 2010-02-23T17:55:38.066481Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 106 Content-length: 106 K 7 svn:log V 9 Add file. K 10 svn:author V 3 djc K 8 svn:date V 27 2010-02-23T17:55:39.058424Z PROPS-END Node-path: trunk/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Text-content-sha1: 3f786850e387550fdab836ed7e6dc881de23001b Content-length: 12 PROPS-END a Revision-number: 3 Prop-content-length: 119 Content-length: 119 K 7 svn:log V 21 Branch to be renamed. K 10 svn:author V 3 djc K 8 svn:date V 27 2010-02-23T17:55:42.051658Z PROPS-END Node-path: branches/badname Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk Revision-number: 4 Prop-content-length: 119 Content-length: 119 K 7 svn:log V 21 Branch to be unnamed. K 10 svn:author V 3 djc K 8 svn:date V 27 2010-02-23T17:55:45.050455Z PROPS-END Node-path: branches/feature Node-kind: dir Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: trunk durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/branchtagcollision.sh0000755000000000000000000000121412043462603024241 0ustar 00000000000000#!/bin/bash # # Generate branchtagcollision.svndump # # Generates an svn repository with a branch and a tag that have the same name. mkdir temp cd temp svnadmin create testrepo svn checkout file://`pwd`/testrepo client cd client mkdir trunk mkdir branches mkdir tags svn add trunk branches tags svn commit -m "Initial commit" echo "fileA" >> trunk/fileA svn add trunk/fileA svn commit -m "Added fileA" svn cp trunk branches/A svn commit -m "added branch" echo "fileB" >> trunk/fileB svn add trunk/fileB svn commit -m "Added fileB" svn cp trunk tags/A svn commit -m "added bad tag" cd .. svnadmin dump testrepo > ../branchtagcollision.svndump durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/branchtagcollision.svndump0000644000000000000000000000536612043462603025334 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 764a21f0-1c44-4bc9-b81b-0321cc58934d Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2011-05-24T15:46:13.951233Z PROPS-END Revision-number: 1 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 14 Initial commit K 10 svn:author V 5 augie K 8 svn:date V 27 2011-05-24T15:46:14.518711Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 111 Content-length: 111 K 7 svn:log V 11 Added fileA K 10 svn:author V 5 augie K 8 svn:date V 27 2011-05-24T15:46:14.922304Z PROPS-END Node-path: trunk/fileA Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 017715e60b9a9450d604e0d489ebc83a Text-content-sha1: d0bcb0015aaadb5317419294648c8da6714af81f Content-length: 16 PROPS-END fileA Revision-number: 3 Prop-content-length: 112 Content-length: 112 K 7 svn:log V 12 added branch K 10 svn:author V 5 augie K 8 svn:date V 27 2011-05-24T15:46:15.328642Z PROPS-END Node-path: branches/A Node-kind: dir Node-action: add Node-copyfrom-rev: 1 Node-copyfrom-path: trunk Node-path: branches/A/fileA Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/fileA Text-copy-source-md5: 017715e60b9a9450d604e0d489ebc83a Text-copy-source-sha1: d0bcb0015aaadb5317419294648c8da6714af81f Revision-number: 4 Prop-content-length: 111 Content-length: 111 K 7 svn:log V 11 Added fileB K 10 svn:author V 5 augie K 8 svn:date V 27 2011-05-24T15:46:15.616098Z PROPS-END Node-path: trunk/fileB Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 4eb63bbdec5dfa425e3735dc1d4c5ee8 Text-content-sha1: 03939175ceac92b9c6464d037a0243e22563c423 Content-length: 16 PROPS-END fileB Revision-number: 5 Prop-content-length: 113 Content-length: 113 K 7 svn:log V 13 added bad tag K 10 svn:author V 5 augie K 8 svn:date V 27 2011-05-24T15:46:16.057440Z PROPS-END Node-path: tags/A Node-kind: dir Node-action: add Node-copyfrom-rev: 1 Node-copyfrom-path: trunk Node-path: tags/A/fileA Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/fileA Text-copy-source-md5: 017715e60b9a9450d604e0d489ebc83a Text-copy-source-sha1: d0bcb0015aaadb5317419294648c8da6714af81f Node-path: tags/A/fileB Node-kind: file Node-action: add Node-copyfrom-rev: 4 Node-copyfrom-path: trunk/fileB Text-copy-source-md5: 4eb63bbdec5dfa425e3735dc1d4c5ee8 Text-copy-source-sha1: 03939175ceac92b9c6464d037a0243e22563c423 durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/commit-to-tag.sh0000755000000000000000000000366612043462603023072 0ustar 00000000000000#!/bin/sh mkdir temp cd temp svnadmin create repo REPOPATH="file://`pwd`/repo" svn co $REPOPATH wc cd wc mkdir -p branches/magic trunk tags svn add * svn ci -m 'btt' cd branches/magic for a in alpha beta gamma; do echo $a > $a svn add $a svn ci -m "Add file $a" done cd ../.. svn up svn cp $REPOPATH/branches/magic $REPOPATH/tags/will-edit -m 'Make tag to edit' svn up cd branches/magic for a in delta iota lambda; do echo $a > $a svn add $a svn ci -m "Add file $a" done cd ../.. cd tags/will-edit svn rm alpha svn ci -m 'removed alpha on a tag. Moves tag, implicit branch.' cd ../.. cd branches/magic for a in omega; do echo $a > $a svn add $a svn ci -m "Add file $a" done cd ../.. svn up svn cp $REPOPATH/branches/magic $REPOPATH/tags/also-edit -m 'Make tag to edit' svn up echo not omega > branches/magic/omega echo not omega > tags/also-edit/omega svn ci -m 'edit both the tag and its source branch at the same time' echo more stupidity > tags/also-edit/omega svn ci -m 'Edit an edited tag.' svn cp $REPOPATH/tags/also-edit $REPOPATH/tags/did-edits -m 'Tag an edited tag' svn cp $REPOPATH/branches/magic $REPOPATH/branches/closeme -m 'Make extra branch for another bogus case' svn cp $REPOPATH/branches/closeme $REPOPATH/tags/edit-later -m 'Make tag to edit after branch closes' svn rm $REPOPATH/branches/closeme -m 'Close the branch' svn up echo boofar > tags/edit-later/delta svn ci -m 'Edit this tag after its parent closed' # try and revert will-edit to its original state svn up svn merge -r9:8 $REPOPATH . svn ci -m 'Revert revision 9.' # make a tag from a branch and edit it at the same time svn up svn cp branches/magic tags/edit-at-create echo alpha >> tags/edit-at-create/alpha svn ci -m 'make a tag from a branch and edit it at the same time' cd ../.. svnadmin dump temp/repo > commit-to-tag.svndump echo echo 'Complete.' echo 'You probably want to clean up temp now.' echo 'Dump in commit-to-tag.svndump' exit 0 durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/commit-to-tag.svndump0000644000000000000000000001704312043462603024143 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: af82cc90-c2d2-43cd-b1aa-c8a78449440a Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2009-06-24T02:53:15.860217Z PROPS-END Revision-number: 1 Prop-content-length: 102 Content-length: 102 K 7 svn:log V 3 btt K 10 svn:author V 5 durin K 8 svn:date V 27 2009-06-24T02:53:17.530891Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: branches/magic Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 14 Add file alpha K 10 svn:author V 5 durin K 8 svn:date V 27 2009-06-24T02:53:18.122469Z PROPS-END Node-path: branches/magic/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 9f9f90dbe3e5ee1218c86b8839db1995 Content-length: 16 PROPS-END alpha Revision-number: 3 Prop-content-length: 113 Content-length: 113 K 7 svn:log V 13 Add file beta K 10 svn:author V 5 durin K 8 svn:date V 27 2009-06-24T02:53:19.101676Z PROPS-END Node-path: branches/magic/beta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 5 Text-content-md5: f0cf2a92516045024a0c99147b28f05b Content-length: 15 PROPS-END beta Revision-number: 4 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 14 Add file gamma K 10 svn:author V 5 durin K 8 svn:date V 27 2009-06-24T02:53:20.081433Z PROPS-END Node-path: branches/magic/gamma Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 303febb9068384eca46b5b6516843b35 Content-length: 16 PROPS-END gamma Revision-number: 5 Prop-content-length: 116 Content-length: 116 K 7 svn:log V 16 Make tag to edit K 10 svn:author V 5 durin K 8 svn:date V 27 2009-06-24T02:53:22.103708Z PROPS-END Node-path: tags/will-edit Node-kind: dir Node-action: add Node-copyfrom-rev: 4 Node-copyfrom-path: branches/magic Revision-number: 6 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 14 Add file delta K 10 svn:author V 5 durin K 8 svn:date V 27 2009-06-24T02:53:23.077083Z PROPS-END Node-path: branches/magic/delta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: d2840cc81bc032bd1141b56687d0f93c Content-length: 16 PROPS-END delta Revision-number: 7 Prop-content-length: 113 Content-length: 113 K 7 svn:log V 13 Add file iota K 10 svn:author V 5 durin K 8 svn:date V 27 2009-06-24T02:53:24.107494Z PROPS-END Node-path: branches/magic/iota Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 5 Text-content-md5: ebcf3971120220589f1dfbf8d56e25b9 Content-length: 15 PROPS-END iota Revision-number: 8 Prop-content-length: 115 Content-length: 115 K 7 svn:log V 15 Add file lambda K 10 svn:author V 5 durin K 8 svn:date V 27 2009-06-24T02:53:25.092428Z PROPS-END Node-path: branches/magic/lambda Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 7 Text-content-md5: 8c8a4646591ee0d9a43d3149320ed577 Content-length: 17 PROPS-END lambda Revision-number: 9 Prop-content-length: 151 Content-length: 151 K 7 svn:log V 51 removed alpha on a tag. Moves tag, implicit branch. K 10 svn:author V 5 durin K 8 svn:date V 27 2009-06-24T02:53:26.443673Z PROPS-END Node-path: tags/will-edit/alpha Node-action: delete Revision-number: 10 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 14 Add file omega K 10 svn:author V 5 durin K 8 svn:date V 27 2009-06-24T02:53:27.067556Z PROPS-END Node-path: branches/magic/omega Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 14723c69541ee556d75c581b787dc217 Content-length: 16 PROPS-END omega Revision-number: 11 Prop-content-length: 116 Content-length: 116 K 7 svn:log V 16 Make tag to edit K 10 svn:author V 5 durin K 8 svn:date V 27 2009-06-24T02:53:29.049217Z PROPS-END Node-path: tags/also-edit Node-kind: dir Node-action: add Node-copyfrom-rev: 10 Node-copyfrom-path: branches/magic Revision-number: 12 Prop-content-length: 156 Content-length: 156 K 7 svn:log V 56 edit both the tag and its source branch at the same time K 10 svn:author V 5 durin K 8 svn:date V 27 2009-06-24T02:53:30.063026Z PROPS-END Node-path: branches/magic/omega Node-kind: file Node-action: change Text-content-length: 10 Text-content-md5: 9b26a47955b0778e131aae04743f2b8c Content-length: 10 not omega Node-path: tags/also-edit/omega Node-kind: file Node-action: change Text-content-length: 10 Text-content-md5: 9b26a47955b0778e131aae04743f2b8c Content-length: 10 not omega Revision-number: 13 Prop-content-length: 119 Content-length: 119 K 7 svn:log V 19 Edit an edited tag. K 10 svn:author V 5 durin K 8 svn:date V 27 2009-06-26T15:28:20.055574Z PROPS-END Node-path: tags/also-edit/omega Node-kind: file Node-action: change Text-content-length: 15 Text-content-md5: a8d56f18cc28a34d6fe2cee5291ac1cc Content-length: 15 more stupidity Revision-number: 14 Prop-content-length: 117 Content-length: 117 K 7 svn:log V 17 Tag an edited tag K 10 svn:author V 5 durin K 8 svn:date V 27 2009-06-26T15:57:06.047408Z PROPS-END Node-path: tags/did-edits Node-kind: dir Node-action: add Node-copyfrom-rev: 13 Node-copyfrom-path: tags/also-edit Revision-number: 15 Prop-content-length: 140 Content-length: 140 K 7 svn:log V 40 Make extra branch for another bogus case K 10 svn:author V 5 durin K 8 svn:date V 27 2009-06-26T19:26:28.086924Z PROPS-END Node-path: branches/closeme Node-kind: dir Node-action: add Node-copyfrom-rev: 14 Node-copyfrom-path: branches/magic Revision-number: 16 Prop-content-length: 136 Content-length: 136 K 7 svn:log V 36 Make tag to edit after branch closes K 10 svn:author V 5 durin K 8 svn:date V 27 2009-06-26T19:26:28.119751Z PROPS-END Node-path: tags/edit-later Node-kind: dir Node-action: add Node-copyfrom-rev: 15 Node-copyfrom-path: branches/closeme Revision-number: 17 Prop-content-length: 116 Content-length: 116 K 7 svn:log V 16 Close the branch K 10 svn:author V 5 durin K 8 svn:date V 27 2009-06-29T00:12:57.367624Z PROPS-END Node-path: branches/closeme Node-action: delete Revision-number: 18 Prop-content-length: 137 Content-length: 137 K 7 svn:log V 37 Edit this tag after its parent closed K 10 svn:author V 5 durin K 8 svn:date V 27 2009-06-26T19:26:29.059216Z PROPS-END Node-path: tags/edit-later/delta Node-kind: file Node-action: change Text-content-length: 7 Text-content-md5: 5bbd00dab68c937673171d0b2e205c96 Content-length: 7 boofar Revision-number: 19 Prop-content-length: 118 Content-length: 118 K 7 svn:log V 18 Revert revision 9. K 10 svn:author V 5 durin K 8 svn:date V 27 2009-06-29T00:13:01.537589Z PROPS-END Node-path: tags/will-edit/alpha Node-kind: file Node-action: add Node-copyfrom-rev: 8 Node-copyfrom-path: tags/will-edit/alpha Text-copy-source-md5: 9f9f90dbe3e5ee1218c86b8839db1995 Revision-number: 20 Prop-content-length: 153 Content-length: 153 K 7 svn:log V 53 make a tag from a branch and edit it at the same time K 10 svn:author V 5 augie K 8 svn:date V 27 2010-02-06T13:31:13.153406Z PROPS-END Node-path: tags/edit-at-create Node-kind: dir Node-action: add Node-copyfrom-rev: 19 Node-copyfrom-path: branches/magic Node-path: tags/edit-at-create/alpha Node-kind: file Node-action: change Text-content-length: 12 Text-content-md5: 7b77a55a273801087b943dfbe257f4db Content-length: 12 alpha alpha durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/copies.sh0000755000000000000000000000102412043462603021655 0ustar 00000000000000#!/bin/sh # # Generate copies.svndump # rm -rf temp mkdir temp cd temp mkdir -p import/trunk/dir echo a > import/trunk/dir/a svnadmin create testrepo svnurl=file://`pwd`/testrepo svn import import $svnurl -m init svn co $svnurl project cd project svn cp trunk/dir trunk/dir2 echo b >> trunk/dir2/a svn ci -m 'copy/edit trunk/dir/a' svn up svn cp trunk/dir2 trunk/dir3 svn ci -m 'copy dir2 to dir3' svn rm trunk/dir3/a svn cp trunk/dir2/a trunk/dir3/a svn ci -m 'copy and remove' cd .. svnadmin dump testrepo > ../copies.svndump durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/copies.svndump0000644000000000000000000000410212043462603022734 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 6f377846-a035-4244-a154-e87a9351a653 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2012-10-15T19:02:56.936694Z PROPS-END Revision-number: 1 Prop-content-length: 105 Content-length: 105 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2012-10-15T19:02:56.958201Z K 7 svn:log V 4 init PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/dir Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/dir/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Text-content-sha1: 3f786850e387550fdab836ed7e6dc881de23001b Content-length: 12 PROPS-END a Revision-number: 2 Prop-content-length: 123 Content-length: 123 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2012-10-15T19:02:58.046478Z K 7 svn:log V 21 copy/edit trunk/dir/a PROPS-END Node-path: trunk/dir2 Node-kind: dir Node-action: add Node-copyfrom-rev: 1 Node-copyfrom-path: trunk/dir Node-path: trunk/dir2/a Node-kind: file Node-action: change Text-content-length: 4 Text-content-md5: dd8c6a395b5dd36c56d23275028f526c Text-content-sha1: 05dec960e24d918b8a73a1c53bcbbaac2ee5c2e0 Content-length: 4 a b Revision-number: 3 Prop-content-length: 119 Content-length: 119 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2012-10-15T19:03:01.045897Z K 7 svn:log V 17 copy dir2 to dir3 PROPS-END Node-path: trunk/dir3 Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/dir2 Revision-number: 4 Prop-content-length: 117 Content-length: 117 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2012-10-15T19:03:03.046654Z K 7 svn:log V 15 copy and remove PROPS-END Node-path: trunk/dir3/a Node-kind: file Node-action: delete Node-path: trunk/dir3/a Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/dir2/a Text-copy-source-md5: dd8c6a395b5dd36c56d23275028f526c Text-copy-source-sha1: 05dec960e24d918b8a73a1c53bcbbaac2ee5c2e0 durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/copybeforeclose.sh0000755000000000000000000000106312043462603023561 0ustar 00000000000000#!/bin/sh mkdir temp cd temp svnadmin create repo svn co file://`pwd`/repo wc cd wc mkdir branches trunk tags svn add * svn ci -m 'btt' cd trunk echo a > a svn add a svn ci -m 'Add file.' svn up cd .. svn cp trunk branches/test svn ci -m 'Branch.' svn up cd branches/test/ svn mv a b svn ci -m 'Move on branch.' svn up cd ../../ svn up svn rm branches/test svn ci -m 'Close branch.' cd ../.. svnadmin dump temp/repo > copybeforeclose.svndump echo echo 'Complete.' echo 'You probably want to clean up temp now.' echo 'Dump in copybeforeclose.svndump' exit 0 durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/copybeforeclose.svndump0000644000000000000000000000362012043462603024641 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 1e1f7d3f-4361-4205-84f8-c0d471d161d2 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2009-06-11T15:51:46.768965Z PROPS-END Revision-number: 1 Prop-content-length: 100 Content-length: 100 K 7 svn:log V 3 btt K 10 svn:author V 3 djc K 8 svn:date V 27 2009-06-11T15:51:47.134555Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 106 Content-length: 106 K 7 svn:log V 9 Add file. K 10 svn:author V 3 djc K 8 svn:date V 27 2009-06-11T15:51:48.129578Z PROPS-END Node-path: trunk/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Content-length: 12 PROPS-END a Revision-number: 3 Prop-content-length: 104 Content-length: 104 K 7 svn:log V 7 Branch. K 10 svn:author V 3 djc K 8 svn:date V 27 2009-06-11T15:51:51.120532Z PROPS-END Node-path: branches/test Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk Revision-number: 4 Prop-content-length: 113 Content-length: 113 K 7 svn:log V 15 Move on branch. K 10 svn:author V 3 djc K 8 svn:date V 27 2009-06-11T15:51:54.124503Z PROPS-END Node-path: branches/test/b Node-kind: file Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: branches/test/a Text-copy-source-md5: 60b725f10c9c85c70d97880dfe8191b3 Node-path: branches/test/a Node-action: delete Revision-number: 5 Prop-content-length: 111 Content-length: 111 K 7 svn:log V 13 Close branch. K 10 svn:author V 3 djc K 8 svn:date V 27 2009-06-11T15:51:57.130547Z PROPS-END Node-path: branches/test Node-action: delete durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/correct.svndump0000644000000000000000000000323112043462603023115 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 00000000-0000-0000-0000-000000000000 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2010-11-30T15:10:25.898546Z PROPS-END Revision-number: 1 Prop-content-length: 100 Content-length: 100 K 7 svn:log V 0 K 10 svn:author V 6 danchr K 8 svn:date V 27 2010-11-30T15:16:01.077550Z PROPS-END Node-path: empty-file Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 0 Text-content-md5: d41d8cd98f00b204e9800998ecf8427e Text-content-sha1: da39a3ee5e6b4b0d3255bfef95601890afd80709 Content-length: 10 PROPS-END Node-path: executable-file Node-kind: file Node-action: add Prop-content-length: 36 Text-content-length: 11 Text-content-md5: 01839ba8c81c3b2c7486607e0c683e62 Text-content-sha1: 5e70f8a25fe8ad4ad971bfd3388c258b019268d4 Content-length: 47 K 14 svn:executable V 1 * PROPS-END Executable Node-path: regular-file Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 10 Text-content-md5: 2e01b7f4ab0c18c05a3059eb2e2420d9 Text-content-sha1: 6e530e985be313a43dc9734251656be8f0c94ab8 Content-length: 20 PROPS-END Contents. Node-path: another-regular-file Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 10 Text-content-md5: 2e01b7f4ab0c18c05a3059eb2e2420d9 Text-content-sha1: 6e530e985be313a43dc9734251656be8f0c94ab8 Content-length: 20 PROPS-END Contents. Node-path: symlink Node-kind: file Node-action: add Prop-content-length: 33 Text-content-length: 6 Text-content-md5: 654580f41818cd6f51408c7cbd313728 Text-content-sha1: 130b8faaf3e1acc1b95f77ac835e9c8b6eee5c96 Content-length: 39 K 11 svn:special V 1 * PROPS-END link A durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/corrupt.svndump0000644000000000000000000000317112043462603023155 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 00000000-0000-0000-0000-000000000000 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2010-11-30T15:10:25.898546Z PROPS-END Revision-number: 1 Prop-content-length: 100 Content-length: 100 K 10 svn:author V 6 danchr K 8 svn:date V 27 2010-11-30T15:16:01.077550Z K 7 svn:log V 0 PROPS-END Node-path: another-regular-file Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 0 Text-content-md5: d41d8cd98f00b204e9800998ecf8427e Text-content-sha1: da39a3ee5e6b4b0d3255bfef95601890afd80709 Content-length: 10 PROPS-END Node-path: executable-file Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 11 Text-content-md5: 01839ba8c81c3b2c7486607e0c683e62 Text-content-sha1: 5e70f8a25fe8ad4ad971bfd3388c258b019268d4 Content-length: 21 PROPS-END Executable Node-path: missing-file Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 0 Text-content-md5: d41d8cd98f00b204e9800998ecf8427e Text-content-sha1: da39a3ee5e6b4b0d3255bfef95601890afd80709 Content-length: 10 PROPS-END Node-path: regular-file Node-kind: file Node-action: add Prop-content-length: 33 Text-content-length: 18 Text-content-md5: adf66a0cec83e25644c63f3c3007ae7c Text-content-sha1: 047e6e482d0c9cb812f89d18a9f07a43caab76bb Content-length: 51 K 11 svn:special V 1 * PROPS-END link Bad contents. Node-path: symlink Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 1 Text-content-md5: 7fc56270e7a70fa81a5935b72eacbe29 Text-content-sha1: 6dcd4ce23d88e2ee9568ba546c007c63d9131c1b Content-length: 11 PROPS-END A durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/delentries.sh0000755000000000000000000000135512043462603022540 0ustar 00000000000000#!/bin/sh # # Generate delentries.svndump # mkdir temp cd temp mkdir project-orig cd project-orig mkdir trunk cd .. svnadmin create testrepo svnurl=file://`pwd`/testrepo svn import project-orig $svnurl -m "init project" svn co $svnurl project cd project/trunk # Regular file deletion echo a > a # Another file starting like the deleted file echo aa > aa mkdir d1 mkdir d1/d2 mkdir d1/d2/d3 echo c > d1/c # Test directory deletion echo d > d1/d2/c # Test subdirectory deletion echo e > d1/d2/d3/e echo f > d1/d2/d3/f # This file starts as the deleted directory, can be confusing echo d2prefix > d1/d2prefix svn add a aa d1 svn ci -m "add entries" svn rm a d1/d2 svn ci -m "remove entries" cd ../.. svnadmin dump testrepo > ../delentries.svndump durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/delentries.svndump0000644000000000000000000000504212043462603023614 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: c91f719a-92f6-401b-a65d-c6820897c57c Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-12-15T21:48:44.999817Z PROPS-END Revision-number: 1 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 12 init project K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-15T21:48:45.636944Z PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 113 Content-length: 113 K 7 svn:log V 11 add entries K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-15T21:48:46.249879Z PROPS-END Node-path: trunk/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Content-length: 12 PROPS-END a Node-path: trunk/aa Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 3 Text-content-md5: d404401c8c6495b206fc35c95e55a6d5 Content-length: 13 PROPS-END aa Node-path: trunk/d1 Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/d1/c Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 2cd6ee2c70b0bde53fbe6cac3c8b8bb1 Content-length: 12 PROPS-END c Node-path: trunk/d1/d2 Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/d1/d2/c Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: e29311f6f1bf1af907f9ef9f44b8328b Content-length: 12 PROPS-END d Node-path: trunk/d1/d2/d3 Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/d1/d2/d3/e Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 9ffbf43126e33be52cd2bf7e01d627f9 Content-length: 12 PROPS-END e Node-path: trunk/d1/d2/d3/f Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 9a8ad92c50cae39aa2c5604fd0ab6d8c Content-length: 12 PROPS-END f Node-path: trunk/d1/d2prefix Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 9 Text-content-md5: 74409dc69996f46a4f59d3f7f39bf7c0 Content-length: 19 PROPS-END d2prefix Revision-number: 3 Prop-content-length: 116 Content-length: 116 K 7 svn:log V 14 remove entries K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-15T21:48:47.188246Z PROPS-END Node-path: trunk/d1/d2 Node-action: delete Node-path: trunk/a Node-action: delete durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/delete_restore_trunk.sh0000755000000000000000000000112412043462603024624 0ustar 00000000000000#!/bin/bash set -e mkdir temp cd temp svnadmin create repo svn co file://`pwd`/repo wc cd wc mkdir branches trunk tags svn add * svn ci -m 'btt' echo foo > trunk/foo svn add trunk/foo svn ci -m 'add file' svn up svn rm trunk svn ci -m 'delete trunk' svn up cd .. svn cp -m 'restore trunk' file://`pwd`/repo/trunk@2 file://`pwd`/repo/trunk cd wc svn up echo bar >> trunk/foo svn ci -m 'append to file' svn up cd ../.. svnadmin dump temp/repo > delete_restore_trunk.svndump echo echo 'Complete.' echo 'You probably want to clean up temp now.' echo 'Dump in branch_delete_parent_dir.svndump' exit 0 durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/delete_restore_trunk.svndump0000644000000000000000000000372012043462603025707 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: fca176f4-a346-479b-ae2c-78c8442c3809 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2012-05-16T22:55:55.613464Z PROPS-END Revision-number: 1 Prop-content-length: 103 Content-length: 103 K 7 svn:log V 3 btt K 10 svn:author V 6 bryano K 8 svn:date V 27 2012-05-16T22:55:56.081065Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 108 Content-length: 108 K 7 svn:log V 8 add file K 10 svn:author V 6 bryano K 8 svn:date V 27 2012-05-16T22:55:57.071178Z PROPS-END Node-path: trunk/foo Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 4 Text-content-md5: d3b07384d113edec49eaa6238ad5ff00 Text-content-sha1: f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 Content-length: 14 PROPS-END foo Revision-number: 3 Prop-content-length: 113 Content-length: 113 K 7 svn:log V 12 delete trunk K 10 svn:author V 6 bryano K 8 svn:date V 27 2012-05-16T22:55:59.058026Z PROPS-END Node-path: trunk Node-action: delete Revision-number: 4 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 13 restore trunk K 10 svn:author V 6 bryano K 8 svn:date V 27 2012-05-16T22:56:01.055887Z PROPS-END Node-path: trunk Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk Revision-number: 5 Prop-content-length: 115 Content-length: 115 K 7 svn:log V 14 append to file K 10 svn:author V 6 bryano K 8 svn:date V 27 2012-05-16T22:56:02.060991Z PROPS-END Node-path: trunk/foo Node-kind: file Node-action: change Text-content-length: 8 Text-content-md5: f47c75614087a8dd938ba4acff252494 Text-content-sha1: 4e48e2c9a3d2ca8a708cb0cc545700544efb5021 Content-length: 8 foo bar durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/empty-log-message.svndump0000644000000000000000000000163012043462603025014 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 2abea918-74b5-4124-b744-a1f76c91de90 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2010-11-18T14:30:05.275138Z PROPS-END Revision-number: 1 Prop-content-length: 100 Content-length: 100 K 7 svn:log V 0 K 10 svn:author V 6 danchr K 8 svn:date V 27 2010-11-18T14:30:36.517160Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/A Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 0 Text-content-md5: d41d8cd98f00b204e9800998ecf8427e Text-content-sha1: da39a3ee5e6b4b0d3255bfef95601890afd80709 Content-length: 10 PROPS-END durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/empty_dir_in_trunk_not_repo_root.sh0000755000000000000000000000062512043462603027256 0ustar 00000000000000#!/bin/sh mkdir temp || exit 1 cd temp svnadmin create repo svn co file://`pwd`/repo wc pushd wc mkdir -p project/trunk svn add project svn ci -m 'trunk' cd project/trunk echo a > a mkdir narf svn add a narf svn ci -m 'file and empty dir' popd svnadmin dump repo > ../empty_dir_in_trunk_not_repo_root.svndump echo 'dump in empty_dir_in_trunk_not_repo_root.svndump' echo 'you can probably delete temp now' durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/empty_dir_in_trunk_not_repo_root.svndump0000644000000000000000000000206712043462603030337 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: c866b883-4c03-404b-8609-dade481701a6 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2009-04-15T03:39:30.544797Z PROPS-END Revision-number: 1 Prop-content-length: 104 Content-length: 104 K 7 svn:log V 5 trunk K 10 svn:author V 5 durin K 8 svn:date V 27 2009-04-15T03:39:31.069518Z PROPS-END Node-path: project Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: project/trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 118 Content-length: 118 K 7 svn:log V 18 file and empty dir K 10 svn:author V 5 durin K 8 svn:date V 27 2009-04-15T03:39:32.069497Z PROPS-END Node-path: project/trunk/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Content-length: 12 PROPS-END a Node-path: project/trunk/narf Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/emptyrepo.sh0000755000000000000000000000073512043462603022427 0ustar 00000000000000#!/bin/sh # # Generate renames.svndump # mkdir temp cd temp mkdir project-orig cd project-orig mkdir trunk mkdir branches cd .. svnadmin create testrepo svnurl=file://`pwd`/testrepo svn import project-orig $svnurl -m "init project" svn co $svnurl project cd project/trunk # Create and remove a file, hgsubversion does not like # empty repositories echo a > a svn add a svn ci -m "add a" svn rm a svn ci -m "remove a" cd ../.. svnadmin dump testrepo > ../emptyrepo.svndump durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/emptyrepo.svndump0000644000000000000000000000221012043462603023474 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: d3a5b63a-6d97-4aaf-9707-be98248d5c9e Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-11-10T14:47:35.612188Z PROPS-END Revision-number: 1 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 12 init project K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-11-10T14:47:35.655615Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 106 Content-length: 106 K 7 svn:log V 5 add a K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-11-10T14:47:36.172222Z PROPS-END Node-path: trunk/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Content-length: 12 PROPS-END a Revision-number: 3 Prop-content-length: 109 Content-length: 109 K 7 svn:log V 8 remove a K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-11-10T14:47:37.171799Z PROPS-END Node-path: trunk/a Node-action: delete durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/emptyrepo2.sh0000755000000000000000000000147112043462603022507 0ustar 00000000000000#!/bin/sh # # Create emptyrepo2.svndump # # The generated repository contains a sequence of empty revisions # created with a combination of svnsync and filtering mkdir temp cd temp mkdir project-orig cd project-orig mkdir -p sub/trunk other echo a > other/a cd .. svnadmin create testrepo svnurl=file://`pwd`/testrepo svn import project-orig $svnurl -m init svn co $svnurl project cd project echo a >> other/a svn ci -m othera echo a >> other/a svn ci -m othera2 echo b > sub/trunk/a svn add sub/trunk/a svn ci -m adda cd .. svnadmin create testrepo2 cat > testrepo2/hooks/pre-revprop-change < ../emptyrepo2.svndump durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/emptyrepo2.svndump0000644000000000000000000000301712043462603023564 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 293d1f29-635d-48b8-9cdf-468fd987067a Revision-number: 0 Prop-content-length: 261 Content-length: 261 K 8 svn:date V 27 2012-10-03T18:58:42.535317Z K 17 svn:sync-from-url V 74 file:///Users/pmezard/dev/hg/hgsubversion/tests/fixtures/temp/testrepo/sub K 18 svn:sync-from-uuid V 36 241badf9-093f-4e71-8a58-1028abf52758 K 24 svn:sync-last-merged-rev V 1 4 PROPS-END Revision-number: 1 Prop-content-length: 105 Content-length: 105 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2012-10-03T18:58:42.556405Z K 7 svn:log V 4 init PROPS-END Node-path: sub Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: sub/trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 107 Content-length: 107 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2012-10-03T18:58:43.040912Z K 7 svn:log V 6 othera PROPS-END Revision-number: 3 Prop-content-length: 108 Content-length: 108 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2012-10-03T18:58:44.042124Z K 7 svn:log V 7 othera2 PROPS-END Revision-number: 4 Prop-content-length: 105 Content-length: 105 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2012-10-03T18:58:45.053459Z K 7 svn:log V 4 adda PROPS-END Node-path: sub/trunk/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 3b5d5c3712955042212316173ccf37be Text-content-sha1: 89e6c98d92887913cadf06b2adb97f26cde4849b Content-length: 12 PROPS-END b durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/executable_file_empty_prop.svndump0000644000000000000000000000206312043462603027054 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 60adb0cc-4d5c-4038-bbb4-90f4595cf81c Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-11-25T15:02:16.557895Z PROPS-END Revision-number: 1 Prop-content-length: 115 Content-length: 115 K 7 svn:log V 15 Basic structure K 10 svn:author V 5 Augie K 8 svn:date V 27 2008-11-25T15:02:45.454954Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 103 Content-length: 103 K 7 svn:log V 4 blah K 10 svn:author V 5 Augie K 8 svn:date V 27 2008-11-25T15:03:45.151223Z PROPS-END Node-path: trunk/foo Node-kind: file Node-action: add Prop-content-length: 36 Text-content-length: 4 Text-content-md5: c157a79031e1c40f85931829bc5fc552 Content-length: 40 K 14 svn:executable V 0 PROPS-END bar durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/executebit.sh0000755000000000000000000000145512043462603022544 0ustar 00000000000000#!/bin/sh # # Generate executebit.svndump # mkdir temp cd temp mkdir project-orig cd project-orig mkdir trunk cd .. svnadmin create testrepo svnurl=file://`pwd`/testrepo svn import project-orig $svnurl -m "init project" svn co $svnurl project cd project/trunk echo text > text1 echo text > text2 touch empty1 touch empty2 python -c "file('binary1', 'wb').write('a\x00b')" python -c "file('binary2', 'wb').write('a\x00b')" svn add text1 text2 binary1 binary2 empty1 empty2 svn propset svn:mime-type application/octet-stream binary1 binary2 svn propset svn:executable yes binary1 text1 empty1 svn ci -m init # switch exec properties svn propdel svn:executable binary1 text1 empty1 svn propset svn:executable yes binary2 text2 empty2 svn ci -m changeexec cd ../.. svnadmin dump testrepo > ../executebit.svndump durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/executebit.svndump0000644000000000000000000000570712043462603023627 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 741b4b73-8316-43bc-aba1-3e2a4eb00a8f Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-12-04T22:12:04.994174Z PROPS-END Revision-number: 1 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 12 init project K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-04T22:12:05.112273Z PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 105 Content-length: 105 K 7 svn:log V 4 init K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-04T22:12:06.484582Z PROPS-END Node-path: trunk/binary1 Node-kind: file Node-action: add Prop-content-length: 85 Text-content-length: 3 Text-content-md5: 70350f6027bce3713f6b76473084309b Content-length: 88 K 14 svn:executable V 1 * K 13 svn:mime-type V 24 application/octet-stream PROPS-END ab Node-path: trunk/binary2 Node-kind: file Node-action: add Prop-content-length: 59 Text-content-length: 3 Text-content-md5: 70350f6027bce3713f6b76473084309b Content-length: 62 K 13 svn:mime-type V 24 application/octet-stream PROPS-END ab Node-path: trunk/empty1 Node-kind: file Node-action: add Prop-content-length: 36 Text-content-length: 0 Text-content-md5: d41d8cd98f00b204e9800998ecf8427e Content-length: 36 K 14 svn:executable V 1 * PROPS-END Node-path: trunk/empty2 Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 0 Text-content-md5: d41d8cd98f00b204e9800998ecf8427e Content-length: 10 PROPS-END Node-path: trunk/text1 Node-kind: file Node-action: add Prop-content-length: 36 Text-content-length: 5 Text-content-md5: e1cbb0c3879af8347246f12c559a86b5 Content-length: 41 K 14 svn:executable V 1 * PROPS-END text Node-path: trunk/text2 Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 5 Text-content-md5: e1cbb0c3879af8347246f12c559a86b5 Content-length: 15 PROPS-END text Revision-number: 3 Prop-content-length: 112 Content-length: 112 K 7 svn:log V 10 changeexec K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-04T22:12:07.239533Z PROPS-END Node-path: trunk/binary1 Node-kind: file Node-action: change Prop-content-length: 59 Content-length: 59 K 13 svn:mime-type V 24 application/octet-stream PROPS-END Node-path: trunk/binary2 Node-kind: file Node-action: change Prop-content-length: 85 Content-length: 85 K 14 svn:executable V 1 * K 13 svn:mime-type V 24 application/octet-stream PROPS-END Node-path: trunk/empty1 Node-kind: file Node-action: change Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/empty2 Node-kind: file Node-action: change Prop-content-length: 36 Content-length: 36 K 14 svn:executable V 1 * PROPS-END Node-path: trunk/text1 Node-kind: file Node-action: change Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/text2 Node-kind: file Node-action: change Prop-content-length: 36 Content-length: 36 K 14 svn:executable V 1 * PROPS-END durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/externals.sh0000755000000000000000000000372112043462603022406 0ustar 00000000000000#!/bin/sh # # Generate externals.svndump # mkdir temp cd temp mkdir project-orig cd project-orig mkdir trunk mkdir branches mkdir externals cd .. svnadmin create testrepo svnurl=file://`pwd`/testrepo svn import project-orig $svnurl -m "init project" svn co $svnurl project cd project/externals mkdir project1 echo a > project1/a svn add project1 mkdir project2 echo a > project2/b svn add project2 svn ci -m "configure externals projects" cd ../trunk # Add an external reference echo a > a svn add a cat > externals < externals < externals < externals <> a svn ci -m 'change a' cd ../.. svnadmin dump testrepo > ../externals.svndump durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/externals.svndump0000644000000000000000000001257312043462603023472 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 2fcff1c7-6cef-40bf-9072-468ceec83032 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2011-02-25T14:33:01.299441Z PROPS-END Revision-number: 1 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 12 init project K 10 svn:author V 7 pmezard K 8 svn:date V 27 2011-02-25T14:33:01.321997Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: externals Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 130 Content-length: 130 K 7 svn:log V 28 configure externals projects K 10 svn:author V 7 pmezard K 8 svn:date V 27 2011-02-25T14:33:02.082464Z PROPS-END Node-path: externals/project1 Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: externals/project1/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Text-content-sha1: 3f786850e387550fdab836ed7e6dc881de23001b Content-length: 12 PROPS-END a Node-path: externals/project2 Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: externals/project2/b Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Text-content-sha1: 3f786850e387550fdab836ed7e6dc881de23001b Content-length: 12 PROPS-END a Revision-number: 3 Prop-content-length: 120 Content-length: 120 K 7 svn:log V 18 set externals on . K 10 svn:author V 7 pmezard K 8 svn:date V 27 2011-02-25T14:33:03.076026Z PROPS-END Node-path: trunk Node-kind: dir Node-action: change Prop-content-length: 70 Content-length: 70 K 13 svn:externals V 35 ^/externals/project1 deps/project1 PROPS-END Node-path: trunk/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Text-content-sha1: 3f786850e387550fdab836ed7e6dc881de23001b Content-length: 12 PROPS-END a Revision-number: 4 Prop-content-length: 123 Content-length: 123 K 7 svn:log V 21 update externals on . K 10 svn:author V 7 pmezard K 8 svn:date V 27 2011-02-25T14:33:04.058124Z PROPS-END Node-path: trunk Node-kind: dir Node-action: change Prop-content-length: 169 Content-length: 169 K 13 svn:externals V 133 # A comment, then an empty line, then a blank line ^/externals/project1 deps/project1 -r2 ^/externals/project2@2 deps/project2 PROPS-END Revision-number: 5 Prop-content-length: 115 Content-length: 115 K 7 svn:log V 13 add on subdir K 10 svn:author V 7 pmezard K 8 svn:date V 27 2011-02-25T14:33:05.096545Z PROPS-END Node-path: trunk Node-kind: dir Node-action: change Prop-content-length: 76 Content-length: 76 K 13 svn:externals V 41 -r2 ^/externals/project2@2 deps/project2 PROPS-END Node-path: trunk/subdir Node-kind: dir Node-action: add Prop-content-length: 70 Content-length: 70 K 13 svn:externals V 35 ^/externals/project1 deps/project1 PROPS-END Node-path: trunk/subdir2 Node-kind: dir Node-action: add Prop-content-length: 70 Content-length: 70 K 13 svn:externals V 35 ^/externals/project1 deps/project1 PROPS-END Revision-number: 6 Prop-content-length: 122 Content-length: 122 K 7 svn:log V 20 externals in subtree K 10 svn:author V 7 pmezard K 8 svn:date V 27 2011-02-25T14:33:08.059874Z PROPS-END Node-path: branches/branch1 Node-kind: dir Node-action: add Node-copyfrom-rev: 5 Node-copyfrom-path: trunk Node-path: branches/branch1/subdir2 Node-kind: dir Node-action: change Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 7 Prop-content-length: 139 Content-length: 139 K 7 svn:log V 37 externals in subtree, removed on root K 10 svn:author V 7 pmezard K 8 svn:date V 27 2011-02-25T14:33:10.059295Z PROPS-END Node-path: branches/branch2 Node-kind: dir Node-action: add Node-copyfrom-rev: 5 Node-copyfrom-path: trunk Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: branches/branch2/subdir2 Node-kind: dir Node-action: change Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 8 Prop-content-length: 125 Content-length: 125 K 7 svn:log V 23 remove externals subdir K 10 svn:author V 7 pmezard K 8 svn:date V 27 2011-02-25T14:33:11.058317Z PROPS-END Node-path: trunk/subdir Node-action: delete Revision-number: 9 Prop-content-length: 126 Content-length: 126 K 7 svn:log V 24 remove externals subdir2 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2011-02-25T14:33:12.058270Z PROPS-END Node-path: trunk/subdir2 Node-kind: dir Node-action: change Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 10 Prop-content-length: 127 Content-length: 127 K 7 svn:log V 25 remove externals project2 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2011-02-25T14:33:14.058780Z PROPS-END Node-path: externals/project2 Node-action: delete Revision-number: 11 Prop-content-length: 109 Content-length: 109 K 7 svn:log V 8 change a K 10 svn:author V 7 pmezard K 8 svn:date V 27 2011-02-25T14:33:15.045928Z PROPS-END Node-path: trunk/a Node-kind: file Node-action: change Text-content-length: 4 Text-content-md5: 0d227f1abf8c2932d342e9b99cc957eb Text-content-sha1: d7c8127a20a396cff08af086a1c695b0636f0c29 Content-length: 4 a a durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/fetch_missing_files_subdir.svndump0000644000000000000000000000455712043462603027044 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 90158077-d23c-442d-9351-5dd160359962 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-11-25T21:55:11.516061Z PROPS-END Revision-number: 1 Prop-content-length: 112 Content-length: 112 K 7 svn:log V 12 initial add. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-11-25T22:00:55.483808Z PROPS-END Node-path: foo Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: foo/branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: foo/tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: foo/trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: foo/trunk/foo Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 4 Text-content-md5: d3b07384d113edec49eaa6238ad5ff00 Content-length: 14 PROPS-END foo Revision-number: 2 Prop-content-length: 130 Content-length: 130 K 7 svn:log V 30 commit bar in the wrong place. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-11-25T22:01:32.415795Z PROPS-END Node-path: foo/bar Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: foo/bar/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 9f9f90dbe3e5ee1218c86b8839db1995 Content-length: 16 PROPS-END alpha Node-path: foo/bar/beta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 5 Text-content-md5: f0cf2a92516045024a0c99147b28f05b Content-length: 15 PROPS-END beta Node-path: foo/bar/delta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: d2840cc81bc032bd1141b56687d0f93c Content-length: 16 PROPS-END delta Node-path: foo/bar/gamma Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 303febb9068384eca46b5b6516843b35 Content-length: 16 PROPS-END gamma Revision-number: 3 Prop-content-length: 126 Content-length: 126 K 7 svn:log V 26 move bar to the sane place K 10 svn:author V 5 durin K 8 svn:date V 27 2008-11-25T22:02:24.410165Z PROPS-END Node-path: foo/trunk/bar Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: foo/bar Node-path: foo/bar Node-action: delete durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/file_mixed_with_branches.svndump0000644000000000000000000000272012043462603026463 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: df2126f7-00ab-4d49-b42c-7e981dde0bcf Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-10-07T22:49:12.059692Z PROPS-END Revision-number: 1 Prop-content-length: 111 Content-length: 111 K 7 svn:log V 11 Empty dirs. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T22:49:41.118037Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 108 Content-length: 108 K 7 svn:log V 9 Add alpha K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T23:23:02.991743Z PROPS-END Node-path: trunk/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 12 Text-content-md5: 3c72ebf8bbd7fa88b1fdcee5398b5a17 Content-length: 22 PROPS-END file: alpha Revision-number: 3 Prop-content-length: 129 Content-length: 129 K 7 svn:log V 29 Adding a readme for branches. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-27T03:08:00.088667Z PROPS-END Node-path: branches/README Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 40 Text-content-md5: 05e0cee6729d14caf6688d01b4bbfa0d Content-length: 50 PROPS-END This is a readme on how to use branches durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/file_not_in_trunk_root.sh0000755000000000000000000000071012043462603025147 0ustar 00000000000000#!/bin/sh mkdir temp cd temp svnadmin create repo svn co file://`pwd`/repo wc cd wc mkdir branches trunk tags svn add * svn ci -m 'btt' cd trunk mkdir narf cd narf for a in alpha beta gamma delta ; do echo $a > $a done cd .. svn add narf svn ci -m 'Add files.' cd ../../.. svnadmin dump temp/repo > file_not_in_trunk_root.svndump echo echo 'Complete.' echo 'You probably want to clean up temp now.' echo 'Dump in file_not_in_trunk_root.svndump' exit 0 durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/file_not_in_trunk_root.svndump0000644000000000000000000000371012043462603026231 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 8904f924-d410-4c11-97ad-f853c35ee419 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2009-03-09T16:36:52.319733Z PROPS-END Revision-number: 1 Prop-content-length: 102 Content-length: 102 K 7 svn:log V 3 btt K 10 svn:author V 5 Augie K 8 svn:date V 27 2009-03-09T16:36:53.075933Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 110 Content-length: 110 K 7 svn:log V 10 Add files. K 10 svn:author V 5 Augie K 8 svn:date V 27 2009-03-09T16:36:54.163467Z PROPS-END Node-path: trunk/narf Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/narf/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 9f9f90dbe3e5ee1218c86b8839db1995 Text-content-sha1: d046cd9b7ffb7661e449683313d41f6fc33e3130 Content-length: 16 PROPS-END alpha Node-path: trunk/narf/beta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 5 Text-content-md5: f0cf2a92516045024a0c99147b28f05b Text-content-sha1: 6c007a14875d53d9bf0ef5a6fc0257c817f0fb83 Content-length: 15 PROPS-END beta Node-path: trunk/narf/delta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: d2840cc81bc032bd1141b56687d0f93c Text-content-sha1: 4bd6315d6d7824c4e376847ca7d116738ad2f29a Content-length: 16 PROPS-END delta Node-path: trunk/narf/gamma Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 303febb9068384eca46b5b6516843b35 Text-content-sha1: 37f385b028bf2f93a4b497ca9ff44eea63945b7f Content-length: 16 PROPS-END gamma durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/file_renamed_in_from_outside_btt.svndump0000644000000000000000000000572312043462603030214 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: df2126f7-00ab-4d49-b42c-7e981dde0bcf Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-10-07T22:49:12.059692Z PROPS-END Revision-number: 1 Prop-content-length: 111 Content-length: 111 K 7 svn:log V 11 Empty dirs. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T22:49:41.118037Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 108 Content-length: 108 K 7 svn:log V 9 Add alpha K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T23:23:02.991743Z PROPS-END Node-path: trunk/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 12 Text-content-md5: 3c72ebf8bbd7fa88b1fdcee5398b5a17 Content-length: 22 PROPS-END file: alpha Revision-number: 3 Prop-content-length: 115 Content-length: 115 K 7 svn:log V 15 Add third_party K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-27T03:16:13.831333Z PROPS-END Node-path: third_party Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: third_party/magic Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: third_party/magic/the_magic_software Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 7 Text-content-md5: bc82cb068ef102a1f4e296992e5979ef Content-length: 17 PROPS-END magic! Revision-number: 4 Prop-content-length: 131 Content-length: 131 K 7 svn:log V 31 Copy magic in from third_party. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-27T03:16:32.737023Z PROPS-END Node-path: trunk/magic Node-kind: dir Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: third_party/magic Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Revision-number: 5 Prop-content-length: 132 Content-length: 132 K 7 svn:log V 32 making a file I can rename over. K 10 svn:author V 5 Augie K 8 svn:date V 27 2008-10-27T17:06:06.012783Z PROPS-END Node-path: third_party/LICENSE.file Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 12 Text-content-md5: 42b502668b588785b91cfea1a3fa834a Content-length: 22 PROPS-END A joke file Revision-number: 6 Prop-content-length: 149 Content-length: 149 K 7 svn:log V 49 Making a rename that might demonstrate a problem. K 10 svn:author V 5 Augie K 8 svn:date V 27 2008-10-27T17:06:32.785437Z PROPS-END Node-path: third_party/LICENSE.file Node-action: delete Node-path: trunk/LICENSE.file Node-kind: file Node-action: add Node-copyfrom-rev: 5 Node-copyfrom-path: third_party/LICENSE.file Text-copy-source-md5: 42b502668b588785b91cfea1a3fa834a Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/filecase.sh0000755000000000000000000000133012043462603022146 0ustar 00000000000000#!/bin/sh # # Generate filecase.svndump # WARNING: this script must be run on a case-sensitive file system # mkdir temp cd temp mkdir project-orig cd project-orig mkdir trunk cd .. svnadmin create testrepo svnurl=file://`pwd`/testrepo svn import project-orig $svnurl -m "init project" svn co $svnurl project cd project/trunk # Test files and directories differing in case only echo a > a echo A > A echo b > b mkdir d echo a > d/a mkdir D echo a > D/a mkdir e echo a > e/a mkdir f echo a > f/a echo F > F svn add a A b d D e f F svn ci -m 'add files' # Rename files and directories, changing only their case svn mv b B svn mv d/a d/A svn mv e E svn ci -m 'change case' cd ../.. svnadmin dump testrepo > ../filecase.svndump durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/filecase.svndump0000644000000000000000000000670412043462603023237 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 43703c40-3731-4eff-a4bd-16f771fbf860 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-12-07T13:00:57.919880Z PROPS-END Revision-number: 1 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 12 init project K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-07T13:00:58.039069Z PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 110 Content-length: 110 K 7 svn:log V 9 add files K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-07T13:00:59.209541Z PROPS-END Node-path: trunk/A Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: bf072e9119077b4e76437a93986787ef Content-length: 12 PROPS-END A Node-path: trunk/D Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/D/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Content-length: 12 PROPS-END a Node-path: trunk/F Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: d2a33790e5bf28b33cdbf61722a06989 Content-length: 12 PROPS-END F Node-path: trunk/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Content-length: 12 PROPS-END a Node-path: trunk/b Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 3b5d5c3712955042212316173ccf37be Content-length: 12 PROPS-END b Node-path: trunk/d Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/d/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Content-length: 12 PROPS-END a Node-path: trunk/e Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/e/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Content-length: 12 PROPS-END a Node-path: trunk/f Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/f/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Content-length: 12 PROPS-END a Revision-number: 3 Prop-content-length: 113 Content-length: 113 K 7 svn:log V 11 change case K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-07T13:01:03.159028Z PROPS-END Node-path: trunk/B Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/b Text-copy-source-md5: 3b5d5c3712955042212316173ccf37be Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Node-path: trunk/E Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/e Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Node-path: trunk/d/A Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/d/a Text-copy-source-md5: 60b725f10c9c85c70d97880dfe8191b3 Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Node-path: trunk/d/a Node-action: delete Node-path: trunk/b Node-action: delete Node-path: trunk/e Node-action: delete durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/ignores.svndump0000644000000000000000000000312712043462603023126 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 8ad10f29-7a81-4fb6-a789-b0b0ade2ec54 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2009-02-24T20:04:33.977054Z PROPS-END Revision-number: 1 Prop-content-length: 111 Content-length: 111 K 7 svn:log V 11 Add a file. K 10 svn:author V 5 durin K 8 svn:date V 27 2009-02-24T20:05:12.825468Z PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/bar Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 4 Text-content-md5: d3b07384d113edec49eaa6238ad5ff00 Text-content-sha1: f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 Content-length: 14 PROPS-END foo Revision-number: 2 Prop-content-length: 105 Content-length: 105 K 7 svn:log V 6 ignore K 10 svn:author V 5 durin K 8 svn:date V 27 2009-02-24T20:05:49.334191Z PROPS-END Node-path: trunk Node-kind: dir Node-action: change Prop-content-length: 47 Content-length: 47 K 10 svn:ignore V 15 blah otherblah PROPS-END Revision-number: 3 Prop-content-length: 124 Content-length: 124 K 7 svn:log V 24 another file and ignore. K 10 svn:author V 5 durin K 8 svn:date V 27 2009-02-24T20:07:13.884019Z PROPS-END Node-path: trunk/baz Node-kind: dir Node-action: add Prop-content-length: 37 Content-length: 37 K 10 svn:ignore V 6 magic PROPS-END Node-path: trunk/baz/xyzzy Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 89d447eb9afaeb94e463615e8ded6479 Text-content-sha1: 9b3802ecbad114267dd0d1a431e57b0bca95930a Content-length: 16 PROPS-END xyzzy durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/invalid_utf8.sh0000755000000000000000000000132612043462603022774 0ustar 00000000000000#!/bin/bash #-*- coding: utf-8 -*- # # Generate invalid_utf8.svndump # #check svnadmin version, must be >= 1.7 SVNVERSION=$(svnadmin --version | head -n 1 | cut -d \ -f 3) if [[ "$SVNVERSION" < '1.7' ]] ; then echo "You MUST have svn 1.7 or above to use this script" exit 1 fi set -x TMPDIR=$(mktemp -d) WD=$(pwd) cd $TMPDIR svnadmin create failrepo svn co file://$PWD/failrepo fail ( cd fail touch A svn add A svn ci -m blabargrod ) svnadmin --pre-1.6-compatible create invalid_utf8 svnadmin dump failrepo | \ sed "s/blabargrod/$(echo blÃ¥bærgrød | iconv -f utf-8 -t latin1)/g" | \ svnadmin load --bypass-prop-validation invalid_utf8 tar cz -C invalid_utf8 -f "$WD"/invalid_utf8.tar.gz . durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/invalid_utf8.tar.gz0000644000000000000000000002004312043462603023561 0ustar 00000000000000‹\@´Oì]{sÛ8’Ïßú895²S¢ägrç”÷Öãh&¾‰í”ídö6››…HHæš"t)E¹¹ûì×€IމåÉZ¬©‰L  Ñèþu7¶ÚOîýZ‡ëÅÎþ»³½³EoloÓ¿öz²±o¬o¯olÁýMx]ìÜÓž<ÉL*!žÆ¿ˆT8PÉUï©Ä,¢A‹½ZíÓÎþ«£N+ý”ÞW8ÀÏy¼çŽÿÖöÎÔøo¢¸ˆõûjPùzäã~ÿIq–uGÐÅPÇ"QCmÂT'“—ú-Ò %fË`Æ ‘jÁÿ„ú$áoU Ó–¯´ˆu*d4E "•ª¦Ð‰è ìMD/Œ”*Q"‹á§‰ËXÅ…×€˜é0¾N’l˜†qŸ*-ÒªÕÞ‡ðS\¤ép·Ý6yk[r(ý ÕÒI¿-zT%ÔÆðs S|¡öÐLþ_­ö…Ö—æ^­Àíõÿæ6ÜZêÿ\nü‡‰ò"í_¶ÒÁ0úÆu\£ÿ·Ÿ¯oNÿ<_êÿE\+ÿÒî†qÛ\Ôj+âíiÇ{srð‹x}ròKmv¢!PRÐV„ñH_ª@tUU­ŒÁøQf‘ôbh ¨Ÿ(™ªLCÙ²d±ÍDˆXwÂÄPßK¨H÷9PxÕøI8L›@YùY*»˜h¨L&p/õ[k"–hCõ®!VQù/BÿPhuظ¥ Äš³Öã0½ ãÒÓQ¤ÇXµN°LI?¨85»Ôy!>l|§·'gÞÛýó×pc %RÐ\Ga¢Ö¸ÈæGa_æ«(ÆSFMÈ®ÎR$ÕeΩÀÙú(ÞuN«DP™­Î>b[bû£889:ꟗJøz€ýº7ýúÎGqvÞÙÃã ¯oˆ_¢Z ci2¡j´0©’QNø&X_#r!M7~Чa–Ûã °ùn7ée~·|8£u©f“×u_ª˜l:±šîé¡Jج ¬a„µ#JÑH!{`S–€Ôà˜å¢ŸÉDÆ©b«D¯•ÊKßõd62@AÆÑ«Îé© ‰ü†ŽñÌÙ‹B½ê•“¦à»8ü$Ì„iÀtch6ßWVŒB§Å…5RLkl)é¡¡PâzK¯ÚŽÁ@º:ë_`ÇrbŸ|7Šzˆ^™‡Ç:EQiY‰¤tUIï ’róbÕ¬†ÑNfmà¶Õt2 }EÅ»„TÖxÒ/‘]T=ÅŠ€õ–Ð1qÆ‹ÔHOT2 iL„Æ4˜3€xÅqö×0ôØäÌE‰·²„JrFR \ÞãVW¦ êÅèz£‰3UíJú" ”¤‚;Fº)ðZñ¸‘  cmMN}ã$°Coí 0-U Tÿß­ÕH»îÕŸnÔk¨5á×f½†ª~mÕkV¥ÁÛõ),ø¹S¯ñ,› ÆÔM€MÑcB4 “±×ƒ‚†ƒg¢ã8)¨øaØpþ‘î5©Ž@Ó¬ªV¿e-ú;LÞó ¾­V«±Fò¼z$']Åz†{ š–§.´A `"¹YÀ: š”üûZíìýñ°¬{íÌ$ÌŽQ»j?öèNF¸vÖyÅB¬ÕPCÿvòë1ðäïO- ¦[Jì«Ãd^]ü.þV¥ë)ÿ:Á슼ðˆCçÚöV»Ýø»åæX6LaZ‚”Ôi½DƒRâ&Ò”MTÃàs6ÌÅ’²&qU¼[åòZU4½.öD½.>¾Ä14X¯õÂZ¡Îx”ˆêRpßÀ±+ìAƒ7¹KÕOQ¦®¬ÿ;7 ˜‹±jÀŒì£+su¦"5U¶UsNYîÖXQï$‰nÒ0@ë@#kÑQŸþOÑœÿmÕÅÆŸ~جQ 6Ø9uøQpê!^oþû×àÿ-ÀüSøÿÅæó%þ_ÄUÁÿgçû§çêÜÃóªP+ý€2ÐO?Å%ÌA¶Òa2f€Hꡧ Ýü4Fs=…›º hñÊ](; Wº ˆîßW˜…ù2C•œ@XJ`A¦ØªÔâqîCá)ì¿ÝÿñðÍáùaçL¬"C#@pF %#ÛÌ#2Ü—CÙ £0 ·Åâ¸y—= â^‚aC¼x©«€k9~Ú%é¨4k uGvÆjŒ#_ÙÖC!ø^"U`Q‰Dqë ß"RÒ0©ÃÜì+ ìÕ‹® ²æE0q0±ÜW²],~Ä ”Ë*‹î íß-U²kô@yDÍ+ª«ó‘TðüZá¸JnzŽÏUŽ‚´Ÿc‹qâT›FT2+ ˆtUz4޾†9` hiîMe@±Ö&‚jK È ]*lq®cv¢¢æJÎfÀœà°D₦E§®rpœ+o§dÃ\éð!£áðX5åk˜Dq¦Ì­=K‡0Õ€*œŠCŸçj%×¼¡C”ãöÛyDýµ¯¨ª1‹gTAB¹wT¹{cIØÉSÛ‚!V†²&a ƒ21ÂDÇ6nƒ¼q=En [OÐ?A£¬ÇÕ´6XÏa\Ø ~À¨š£2Õ‘LB¿”s6è) CaU™É@pF2ÊT£€¡1Y7²žup"™Å`@mFÇÁQ(…ìe)Û?«¢ù iWÔ#Y Ó4 °­¶“RV¸Õ´ð³h˜XÆPQÌÍ\ÕÏ P\›ÛGcGáëU$ò“K-Ùòìp¢×<††¥``I7ÉVÂŒ3ú±ž¹¹5°n(æ6Ÿ5ª$²è­¶4½R¢«ÂŒ¹lcöÍj÷¸ñ¦-¹ )ÔIØ­«Uœr늃S^cá÷È÷– #ðŒK½ÉQÏ£iì\®ßÖÓ10Þ¡Œ<84Ï¿l 'Ó¯{ü˜gûV©4tp0•à a ÑJΠž5ëÛ=4¶þ®<ÿ£Í}¹×ù[Ï_lLû;Ï×—þß"®jþçäl¾ûW’ŽïOöë_íÆ¹€ü>×»q@Äå{ЦÜÈ‹+|8 qnÜi罨ºqq6èclê%Q£Xò„QÜþ”Ò=›HøQùÒ-ñp,M‚[C\˱óelñÔ”›¶^Ò·¾”ëmï ¬ôÚR.p ¡i›\^e‚áÆ†ÈRëŠþUÑ‘;­7±ˆ\Ëhâåœi¢ÔÁuYª“q(‹ñcÖe#SäJ7—Àz ¬ °ƒaq5¦‹OOœ.¥uà­ºhãXCóÚöM ÞûVà¶¼þçžàßuøïùÎöÌúŸ­çKü·ˆkfýÏ\ø— Ç-bÿ¨[œq¾ý" [¯š…„½ èü/ÇÞñþQ§ Ñ0Zˆ„L*/ÿ™‚„@âìüÕáñG™Cïüä—Îñ™xöljqŒ¡7;Ãd¨PË’Û@S(T†±M¥"†ª—Èîþ-¶aí:þ Fú2Ñ@ üC´åÜ’øiJ¬ÁÀvL· †‚h‹rÿàœlq/J²ÑòL.]âžšh ô‰ˆŽ#|d"ü ™HŸM=ïàâœÒ²!GÃÐâ«ÞzÊø#º8|MÛ›±˜Ëf@Œò:Dã÷Fñ²{±T¯ej™òky{¿«C.®íDòÖtKÀ.¬„%†í—— ;0}uȹ(Ü(ï·vw¡Ð2÷ç"˜ãÏ„8>9ïìŠó×RÄ ˜O~>Ý?GïÎÎñ™8:yuøÓÒ  fš¢ó—ƒÎÛs*\ùéäýÒóÓc$ñ¶sÊ)Â(ÄôÒ(Þ4ª=ú‰Q7¬µ„%BdÜî€ñÅ—‡€ Ò‚BÌJKôeàÑÜœ„+!ÎUõ2^ŽW²È óâ"•EÖ=*XM$¢”2dnW²Ä@h™JJ3Š í= uŠ@F‘lè`ð@ÁÄŽC3Àá¢T ‘=e‡Ïh±JF ·Ø~ ;!ð‹¦ÓaH¯¯ rï5Lý®QIÂþ®¦ƒ QJˆDQ7rá.šɤɉ úÄ’“Ëë›_åüO¢F ÏC£ß^»ÿc&ÿó ,ñß"®™üÏiç=† ½ƒ×ûÇ?wfó@U!¹:”/xÁ·U’NP»< ºóÎPMòi!7ÎÕVî°„ÃCÕ¶£%¼„üR®Ûro ˆr²C›Ž•¼ÁñrŽÉî§£’.^놣Ø6ŽÃ]ÍS¹wlÍŒ²*˘Š]äûç^sJ‡Îoì7xØG ;ð m¼jðØOç¼°Iï÷ß—ðÂm$9YŠ}•íTÚë6‰¤é™¶Þ~¯@‰Æ q{)“jÍ .^ ËÉ @[[‘…ÜééÅdŽ'Üó_÷OÞň_˜ã;ö*æ%ˆ0! GŒS+Hƒ(6æL x«}ÎÕ<.JD*!}„†¸“§«†pKÉ͉h`¾°‚\ÍA3@ CíÈw–„Ö?šî0¶]¤wÇï;§˜Þè¼¢dÌRN‘;醺—EåQš ž$óÓp¤^ jžd—ËUp?·#ùи_Ë©¼7] ø'N•ÇèÚ·ö|UžiVS-füT½¼ÏŸJbaêÙ2Á°L0<ŽC¾L­8“Áá>”í¿=•Áî½ç{´ïþ¨.<‰çØBtÓ¦žóíøv3þK»ñyCýN4”“yªŸ• ˆeœg±$ú"ì† Quñ‡Ù_ÿG¿*ñ˜È÷‘¸.þ¿¾õbfÿÇ΋¥ÿ·ˆk6þÿîøŠ K«M6ãS~aQ9®í;ÉâÔ“›ç/¯Í +ÍWf\t}N& oÈãÈõM²_®·¾¸€½›Rõ—Œ^5hoo~÷aûD ô¨¶·j|¸_^Oªñÿ{‚מÿº±1»ÿ^[â¿\3ñÿ¹ð/Ž[ž›[ÓEœ; kßã9°p]{¬eîü³`igG¥Li·W”NÌÿñ´³ÿ‹Ù#a»I)‘0{ì»»œ‡š÷ÿÖ1;âLÔ…bÂÒ¤z4°°ƒ•ðmá7ƃóÎF¥)ÎA8š·sNF½Ýq¨8¾Ý5"Ú»rúOvÊ©=ïÓj¯<Ù”¥èËg›Þ¬ÂEžgú=œcº¼îvUâ¿÷´è:ü›½§Öÿl®/×/äšÿ~qЬ”\yм@n£0Û—9tq÷s`çôáFAäÂc¸× òÖqˆwÈ<ÀR ¼r»&äF+€ðý¯^üƒk+nºø§à­,îù^Ž4šêé×dɼéõh¼‡9V´X¾ëB’åGË•$i%I‘¯(ÔøæÌÉHÎ *¯/)–Ÿ,3×_-ºÞý~ößÿÛ\ß\~ÿo—˜éп‘¢9ò­ë¸îü¯õÍéüÏö‹Íå÷ÿr­¬¬ð)/äñØ=ïŒß”ã|¢Iø™n²Pg‘bÿ yëñ·[ìøºãE¾0Dcv@n:NñÌ”‚§Åg49”7!εr€Îp»²”NÕ±ºýxT˜–JÎ÷=þYËÍ”neh(¥IÉŽ‘ŒÅ^©ªþÀ„ÌX·‘Ÿ²eí¹Ôˆé“üË:++Ser±r3UÞ(ôù„2iãD®ÛD”mVÙ0ˆfYˆbm;±'Ž&â§01©8ÍßÊÅ“ŽNó\¨Ïóq†;IÅ0˜)Ì5~6 {4 ?«\waQãÖq R–‰µ!ûxbP:+ÞV´y%CEPr AS%¥«G(e³†)U‚'O‚XàOQ/Ê!LŒòÔé›ü.ýœû–3•Öx­ò·²†ôe×¼§Òx!pÍÑÖZ`Ðt GxdÊ’J[ÚB<"÷†Íp~O`µŒ49j˜'²ã Eñ7œtc§åyq0I2CêDa7‘1Tå²%^Ù†áD–‘Q­¢J£xi‚݋炕Ör‰9`ÔÍB\®Ê…êfH€êØÀ è$}I²ƒç5éÜ—FNÁó,×$ìä´Ñœó¹Y§ E¿¦ç†RFà £âoaŒXá! ¡i’å0—mS2ê´Š%Ä®™4QqzဪûzV$'xöÞŠ>åL§¶àJçDÂ$tvRêWÅ~2òòè ¾Í"§ªTùó%ümãÀëH&}·3eƒ„Ü× ée ¬jV½£6q Z%â†#늊ùc¤bcó_m³à—×ÅLñÚZ‹4­>B„±W"¾'Öñ¦üT½¹¹ó| yT—õÿI{ßW×øÿ›ë/¦ã?[›Ëý?‹¹ªþ5¬=‹ÍHGå±"ÆS)!\p›)+°f·Kƒ$:ÐÁo$`¿á7•ga2%j˜Ú>ó:Ž­&£®;cÆ M•@žž`÷Å!€U62Z+ûÝÎÅQÏL:æ$K õW*@‡µ%ö§ÚnO˜–{¨õõ.‘î=çc³QjšÅÝ~¢³!²„F»ôa”öëXâ½d>ºÆÅá­(”W£gj^9ÀsƒÅ!¾§•¼N4§Àž±sžçw§‹Êx‚YÕòËψK_&ì¿ñÀóŠ(bg—‰*÷ÕÌ!°7ü”Ÿ7œ‘ˆ_èþ¿FKœõ­·ÒOв“¾ÚHkyü|öÊáÁ¸ô¤'vÉ@}å;ç'ØÉhDw탽¿üµ}v¾÷ ^…ÖµßìÅxææ‚ö‰ýã$ŒšâM´Ú'ïöNµÈºu(:L³TµŽ÷þÈíCÿdÑ¢nÊZ&Éä7áß %õöøN“þÊŸÓ_ô7«ôNó¸… 9>´{Z·»2ÉÉba üÀ…ð0âgbÞ-&È.ùÜîeŸ±ØŸg›CžQñ‡Ö[ËëÛ\ÖþsÜãžê¸Æþ¯o?ß™¶ÿÛ[Ëý ¹¾dÿ+±²kM¿ àÄ%eÃ_É*µ ã^ ¸ºØÞÛ¯öÒ1ŸäßÙgpU„qÁÿoïÚvܶ(úª¯З6ˆl’ºz‹<,°$H° ’M ¤( J¤wÕÚ–+ÉÙì·ßÐÇr†ÔÅvR×A¤¤»þ/þ¯vÏƒÐÆá]ÌGc2é°ìôvÿƒÂrÁò?FÚ¿~¿ò²M Ä@ Wû™ë8´þÛ·ÇÌÎÿc µÿº,jxW}þÙàxÿBYÿhãûCÕqØÿw×>óíþï(°º½û ôà XÇAÿv÷XèÛûÿ(”‡"žL…ïÜŸyiJ¥Ç¸çŒA8íqwþoDI,ýǯÿüÙç¿Q°m2ÈøûG‘oí?víO¨ãÐþ/ wó¿ùq`õ£à™K‰Óåÿu~„ÿw}án …ª+«ÚtûÈ¥,žõ‡žPÊ(EµÏ²¨¥×5ê‘»¼íZåzb¨2pÝø;Ö’`L…ØÇ]hšzŠÎ¼j }ã>yñúòäå‹W—“íîiíbh˜Õ5/1Èk}Q›–›E[§¯žÒÐëâ’%5z`/&¯tØ”–å+W]vò=ý•‘ªA¶«¬vß¼yzfÂb@Øy¤ib¨QÓN:r˜«Ü–F-!yx#ÄЇ+ð†g”Ë£FF­á‹Ê–º^¸æš ÄÐm±ŠN˜ÅE B>i…Qôà*1ÈMÿÕ›åGú$=½iÞDÄ)7UKÐߟ‡ú 4Ï\÷€gD%®„@}n‘e˜ZÝ¥Ší… Ñ£A -Á GÊÍóÕ#‡WWZÑkŠÊù ŽU „­amX檺…îßž¸ë;(k€Br#$B¥ötj.î{ítKþ»6r¯m+‰•` Í€­vqODÙ¥‘¯õ-ŒñªºX7ê ø« TÕ3ó€ìËh”ª5‡ldRÏZ8¼†8á]‘ƒ‚¯BÄ ÄšU— cK',ÓlpÔñé|õ³!p·ZàxËŠªÆ8-êç&i=ÇL jRXò¼U^òN 9ía;µ´„få ­M|_«Te´„xin…Apôˆr-W0ÀÝÍÚ$ì«©ßžÒ‘Ú,Seõi‘þ&³ºÚâàq“ïŒ/‘8A„z£sꩦ¿‡:ât¨;Wöëß7ÒRuƵю§(qä5‘ˆZ*øåVLæ5/Õ´!å´6å’½÷C)ƒœµn»¦05"ÑõT[àrŠùÌÊ8“‚N úTVµîOO÷§ ª§.©E#O†á¥¦*ÕÓbã,Òu.?&;Ýjt§sV>oTZ0¸õ»^ÿ»f”vÿÇõNdC0?4Žçø‘åÿŽƒæù¨½Àñû¿,bvÿwtöfïð ö#ÿiôí?ÄÞ?àÐþ_H÷ô$°çÿ£àìüùå)„lÿæüâìÕùK''.ñ¨z:-锯N}»–'¸vvP=¡>ujù^½R—¸ú'*’LÌ’9!)#œ%„Ìf‰ÌæIÀbõ;x†;q§§ª„õmYPˆ;uœ—ÏOŸ^8Ï\êœÂqAäà&D¿vØpì7¬ù€˜v‰¼tÔʹùˆÀoL3iÛLÊB×gð—Gó(ó M8™¡HË$ñ"DÛÔÝ–þ Å×Ä#¹—d:æ\”ã¨ö¸lö¿¤Ò÷ý(;xþùûþO¬ÿí‚r3Òy?xÙŽótþÀe‚ÍâXÐl̓$æ© 8•Œ0!ˆŸ󣛸”X6ÑW³ÿªãðóßÞýŸöüoÀÑõÈû‹]ýÏuöÿ½ø¯$°úÏQ`ùÿ÷ÚÿK¾ªt6»!ößÿ üÐòG^ÿ à ÿ‹íñ?IHíü?|gÁo!&8 )Ô!öžpo0™Â±ß§?Ÿ”ÿÁ‰ÿÇ@c‘z‹âªš q|hýÏèîþ_F6ÿß(ØŠýñ€ÎT¥C'þä=ž€rTNœ'-׫® Q!2©#Yÿ±Áh¼@ªÀÐËužê¤g@ r¶ eºPÉËEÅ~鮸—èü×Gïÿ>žÿ[ÿÖÿ-,,,,,,,,,,,,,î>þÁŽd•ðdurin42-hgsubversion-77b22e5b4ea6/tests/fixtures/many_special_cases.svndump0000644000000000000000000001433612043462603025306 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: df2126f7-00ab-4d49-b42c-7e981dde0bcf Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-10-07T22:49:12.059692Z PROPS-END Revision-number: 1 Prop-content-length: 111 Content-length: 111 K 7 svn:log V 11 Empty dirs. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T22:49:41.118037Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 108 Content-length: 108 K 7 svn:log V 9 Add alpha K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T23:23:02.991743Z PROPS-END Node-path: trunk/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 12 Text-content-md5: 3c72ebf8bbd7fa88b1fdcee5398b5a17 Content-length: 22 PROPS-END file: alpha Revision-number: 3 Prop-content-length: 107 Content-length: 107 K 7 svn:log V 8 Add beta K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T23:28:22.651398Z PROPS-END Node-path: trunk/beta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 13 Text-content-md5: 981d1eb5fd0bbe05354c292105944863 Content-length: 23 PROPS-END Data of beta Revision-number: 4 Prop-content-length: 113 Content-length: 113 K 7 svn:log V 13 Make a branch K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-08T01:35:39.984789Z PROPS-END Node-path: branches/the_branch Node-kind: dir Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: trunk Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Revision-number: 5 Prop-content-length: 123 Content-length: 123 K 7 svn:log V 23 add delta on the branch K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-08T01:39:05.520779Z PROPS-END Node-path: branches/the_branch/delta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: d2840cc81bc032bd1141b56687d0f93c Content-length: 16 PROPS-END delta Revision-number: 6 Prop-content-length: 119 Content-length: 119 K 7 svn:log V 19 Add gamma on trunk. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-08T01:39:29.950892Z PROPS-END Node-path: trunk/gamma Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 303febb9068384eca46b5b6516843b35 Content-length: 16 PROPS-END gamma Revision-number: 7 Prop-content-length: 192 Content-length: 192 K 7 svn:log V 92 Add a dir that is part of another file so I can delete it to test a case in the delete code. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-08T21:58:28.902105Z PROPS-END Node-path: trunk/gam Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/gam/bar Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 4 Text-content-md5: c157a79031e1c40f85931829bc5fc552 Content-length: 14 PROPS-END bar Node-path: trunk/gam/baz Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 4 Text-content-md5: 258622b1688250cb619f3c9ccaefb7eb Content-length: 14 PROPS-END baz Node-path: trunk/gam/foo Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 4 Text-content-md5: d3b07384d113edec49eaa6238ad5ff00 Content-length: 14 PROPS-END foo Revision-number: 8 Prop-content-length: 150 Content-length: 150 K 7 svn:log V 50 delete gam to test that previously-mentioned case. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-08T21:58:43.789735Z PROPS-END Node-path: trunk/gam Node-action: delete Revision-number: 9 Prop-content-length: 113 Content-length: 113 K 7 svn:log V 13 Add a symlink K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-08T22:44:17.202691Z PROPS-END Node-path: trunk/delta Node-kind: file Node-action: add Prop-content-length: 33 Text-content-length: 10 Text-content-md5: 9d4142e2eb58c20531a1ac599bb0f7fd Content-length: 43 K 11 svn:special V 1 * PROPS-END link alpha Revision-number: 10 Prop-content-length: 149 Content-length: 149 K 7 svn:log V 49 redirect an existing symlink to a different file. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-08T22:45:46.927432Z PROPS-END Node-path: trunk/delta Node-kind: file Node-action: change Text-content-length: 9 Text-content-md5: 404afec23f4019f7d784b2f78ff5503d Content-length: 9 link beta Revision-number: 11 Prop-content-length: 117 Content-length: 117 K 7 svn:log V 17 remove a symlink. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-08T22:46:50.607212Z PROPS-END Node-path: trunk/delta Node-action: delete Revision-number: 12 Prop-content-length: 123 Content-length: 123 K 7 svn:log V 23 add an executable file. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-08T22:47:47.863563Z PROPS-END Node-path: trunk/delta Node-kind: file Node-action: add Prop-content-length: 36 Text-content-length: 6 Text-content-md5: d2840cc81bc032bd1141b56687d0f93c Content-length: 42 K 14 svn:executable V 1 * PROPS-END delta Revision-number: 13 Prop-content-length: 127 Content-length: 127 K 7 svn:log V 27 remove execute from a file. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-08T22:48:13.734895Z PROPS-END Node-path: trunk/delta Node-kind: file Node-action: change Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 14 Prop-content-length: 124 Content-length: 124 K 7 svn:log V 24 add executable to a file K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-08T22:48:33.143919Z PROPS-END Node-path: trunk/delta Node-kind: file Node-action: change Prop-content-length: 36 Content-length: 36 K 14 svn:executable V 1 * PROPS-END Revision-number: 15 Prop-content-length: 131 Content-length: 131 K 7 svn:log V 31 Add epsilon as a copy of alpha. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-08T23:15:08.250550Z PROPS-END Node-path: trunk/epsilon Node-kind: file Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: trunk/alpha Text-copy-source-md5: 3c72ebf8bbd7fa88b1fdcee5398b5a17 Prop-content-length: 34 Text-content-length: 14 Text-content-md5: dc626a565ffebe5389e0109508e8ee31 Content-length: 48 K 13 svn:mergeinfo V 0 PROPS-END file: epsilon durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/mergeexternals.sh0000644000000000000000000000155312043462603023424 0ustar 00000000000000#!/bin/sh # # Generate mergeexternals.svndump # mkdir temp cd temp mkdir project-orig cd project-orig mkdir trunk mkdir branches cd .. svnadmin create testrepo svnurl=file://`pwd`/testrepo svn import project-orig $svnurl -m "init project" svn co $svnurl project cd project/trunk mkdir d1 echo a > d1/a mkdir d2 echo b > d2/b mkdir -p common/ext echo c > common/ext/c svn add d1 d2 common svn ci -m addfiles svn up svn propset svn:externals '^/trunk/common/ext ext' d1 svn propset svn:externals '^/trunk/common/ext ext' d2 svn ci -m addexternals cd .. svn up svn cp trunk branches/branch cd branches svn ci -m addbranch cd branch mkdir d3 echo d > d3/d svn add d3 svn propset svn:externals '^/trunk/common/ext ext3' d3 svn ci -m touchbranch cd ../../trunk svn merge '^/branches/branch' svn up svn ci -m 'merge' cd ../.. svnadmin dump testrepo > ../mergeexternals.svndump durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/mergeexternals.svndump0000644000000000000000000000745312043462603024513 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: b402ceb9-6185-4dce-93a1-92de515c5c8b Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2011-02-25T13:54:38.654361Z PROPS-END Revision-number: 1 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 12 init project K 10 svn:author V 7 pmezard K 8 svn:date V 27 2011-02-25T13:54:38.675100Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 109 Content-length: 109 K 7 svn:log V 8 addfiles K 10 svn:author V 7 pmezard K 8 svn:date V 27 2011-02-25T13:54:39.078800Z PROPS-END Node-path: trunk/common Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/common/ext Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/common/ext/c Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 2cd6ee2c70b0bde53fbe6cac3c8b8bb1 Text-content-sha1: 2b66fd261ee5c6cfc8de7fa466bab600bcfe4f69 Content-length: 12 PROPS-END c Node-path: trunk/d1 Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/d1/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Text-content-sha1: 3f786850e387550fdab836ed7e6dc881de23001b Content-length: 12 PROPS-END a Node-path: trunk/d2 Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/d2/b Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 3b5d5c3712955042212316173ccf37be Text-content-sha1: 89e6c98d92887913cadf06b2adb97f26cde4849b Content-length: 12 PROPS-END b Revision-number: 3 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 12 addexternals K 10 svn:author V 7 pmezard K 8 svn:date V 27 2011-02-25T13:54:41.071346Z PROPS-END Node-path: trunk/d1 Node-kind: dir Node-action: change Prop-content-length: 58 Content-length: 58 K 13 svn:externals V 23 ^/trunk/common/ext ext PROPS-END Node-path: trunk/d2 Node-kind: dir Node-action: change Prop-content-length: 58 Content-length: 58 K 13 svn:externals V 23 ^/trunk/common/ext ext PROPS-END Revision-number: 4 Prop-content-length: 110 Content-length: 110 K 7 svn:log V 9 addbranch K 10 svn:author V 7 pmezard K 8 svn:date V 27 2011-02-25T13:54:44.043149Z PROPS-END Node-path: branches/branch Node-kind: dir Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: trunk Revision-number: 5 Prop-content-length: 113 Content-length: 113 K 7 svn:log V 11 touchbranch K 10 svn:author V 7 pmezard K 8 svn:date V 27 2011-02-25T13:54:45.080319Z PROPS-END Node-path: branches/branch/d3 Node-kind: dir Node-action: add Prop-content-length: 59 Content-length: 59 K 13 svn:externals V 24 ^/trunk/common/ext ext3 PROPS-END Node-path: branches/branch/d3/d Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: e29311f6f1bf1af907f9ef9f44b8328b Text-content-sha1: e983f374794de9c64e3d1c1de1d490c0756eeeff Content-length: 12 PROPS-END d Revision-number: 6 Prop-content-length: 106 Content-length: 106 K 7 svn:log V 5 merge K 10 svn:author V 7 pmezard K 8 svn:date V 27 2011-02-25T13:54:48.049151Z PROPS-END Node-path: trunk Node-kind: dir Node-action: change Prop-content-length: 55 Content-length: 55 K 13 svn:mergeinfo V 20 /branches/branch:4-5 PROPS-END Node-path: trunk/d3 Node-kind: dir Node-action: add Node-copyfrom-rev: 5 Node-copyfrom-path: branches/branch/d3 Prop-content-length: 59 Content-length: 59 K 13 svn:externals V 24 ^/trunk/common/ext ext3 PROPS-END durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/most-recent-is-edit-tag.sh0000755000000000000000000000142512043462603024745 0ustar 00000000000000#!/bin/sh mkdir temp cd temp svnadmin create repo svn co file://`pwd`/repo wc cd wc mkdir branches trunk tags mkdir tags/versions mkdir tags/blah svn add * svn ci -m 'btt' cd trunk for a in alpha beta gamma delta ; do echo $a > $a svn add $a done svn ci -m 'Add files.' cd .. svn up svn cp trunk branches/dev_branch svn ci -m 'branch' cd branches/dev_branch svn rm delta echo narf > alpha echo iota > iota svn add iota svn ci -m 'branch changes' cd ../.. svn up svn cp branches/dev_branch tags/some-tag svn ci -m 'Make a tag.' svn up echo foo > tags/some-tag/alpha svn ci -m 'edit that tag' cd ../.. svnadmin dump temp/repo > most-recent-is-edit-tag.svndump echo echo 'Complete.' echo 'You probably want to clean up temp now.' echo 'Dump in most-recent-is-edit-tag.svndump' exit 0 durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/most-recent-is-edit-tag.svndump0000644000000000000000000000647112043462603026032 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 65efcde9-3b35-4f89-9c6b-23da1cf93d9b Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2009-07-19T03:04:55.657240Z PROPS-END Revision-number: 1 Prop-content-length: 102 Content-length: 102 K 7 svn:log V 3 btt K 10 svn:author V 5 durin K 8 svn:date V 27 2009-07-19T03:04:56.082834Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags/blah Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags/versions Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 110 Content-length: 110 K 7 svn:log V 10 Add files. K 10 svn:author V 5 durin K 8 svn:date V 27 2009-07-19T03:04:57.109656Z PROPS-END Node-path: trunk/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 9f9f90dbe3e5ee1218c86b8839db1995 Content-length: 16 PROPS-END alpha Node-path: trunk/beta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 5 Text-content-md5: f0cf2a92516045024a0c99147b28f05b Content-length: 15 PROPS-END beta Node-path: trunk/delta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: d2840cc81bc032bd1141b56687d0f93c Content-length: 16 PROPS-END delta Node-path: trunk/gamma Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 303febb9068384eca46b5b6516843b35 Content-length: 16 PROPS-END gamma Revision-number: 3 Prop-content-length: 105 Content-length: 105 K 7 svn:log V 6 branch K 10 svn:author V 5 durin K 8 svn:date V 27 2009-07-19T03:05:00.048169Z PROPS-END Node-path: branches/dev_branch Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk Revision-number: 4 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 14 branch changes K 10 svn:author V 5 durin K 8 svn:date V 27 2009-07-19T03:05:01.081946Z PROPS-END Node-path: branches/dev_branch/alpha Node-kind: file Node-action: change Text-content-length: 5 Text-content-md5: 5e723ed52db2000686425ca28bc5ba4a Content-length: 5 narf Node-path: branches/dev_branch/iota Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 5 Text-content-md5: ebcf3971120220589f1dfbf8d56e25b9 Content-length: 15 PROPS-END iota Node-path: branches/dev_branch/delta Node-action: delete Revision-number: 5 Prop-content-length: 111 Content-length: 111 K 7 svn:log V 11 Make a tag. K 10 svn:author V 5 durin K 8 svn:date V 27 2009-07-19T03:05:04.056268Z PROPS-END Node-path: tags/some-tag Node-kind: dir Node-action: add Node-copyfrom-rev: 4 Node-copyfrom-path: branches/dev_branch Revision-number: 6 Prop-content-length: 113 Content-length: 113 K 7 svn:log V 13 edit that tag K 10 svn:author V 5 durin K 8 svn:date V 27 2009-07-19T03:05:06.057723Z PROPS-END Node-path: tags/some-tag/alpha Node-kind: file Node-action: change Text-content-length: 4 Text-content-md5: d3b07384d113edec49eaa6238ad5ff00 Content-length: 4 foo durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/move_into_trunk.svndump0000644000000000000000000000377412043462603024712 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 124c25c6-3b41-4c6b-a3a3-a0c9809dc9cb Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2010-08-14T19:08:00.090868Z PROPS-END Revision-number: 1 Prop-content-length: 111 Content-length: 111 K 7 svn:log V 10 Add files. K 10 svn:author V 9 anonymous K 8 svn:date V 27 2010-08-14T19:09:04.290181Z PROPS-END Node-path: A Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 16 Text-content-md5: 98475036dc73d318982805bf4b16e8b2 Text-content-sha1: d7dff2b1ef48b9c20c23d7b3a08b557957cec3c9 Content-length: 26 PROPS-END This is a file. Node-path: B Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 22 Text-content-md5: d54ff73404ed6041a3bd66850b061bff Text-content-sha1: 4bdb40dfd6ec75cb730e678b5d7786e30170c5fb Content-length: 32 PROPS-END This is another file. Revision-number: 2 Prop-content-length: 111 Content-length: 111 K 7 svn:log V 10 Add trunk. K 10 svn:author V 9 anonymous K 8 svn:date V 27 2010-08-14T19:09:23.652445Z PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 3 Prop-content-length: 113 Content-length: 113 K 7 svn:log V 12 Move a file. K 10 svn:author V 9 anonymous K 8 svn:date V 27 2010-08-14T19:09:33.945291Z PROPS-END Node-path: trunk/A Node-kind: file Node-action: add Node-copyfrom-rev: 1 Node-copyfrom-path: A Text-copy-source-md5: 98475036dc73d318982805bf4b16e8b2 Text-copy-source-sha1: d7dff2b1ef48b9c20c23d7b3a08b557957cec3c9 Node-path: A Node-action: delete Revision-number: 4 Prop-content-length: 122 Content-length: 122 K 7 svn:log V 18 Move another file. K 10 svn:author V 9 anonymous K 8 svn:date V 27 2010-08-14T19:10:03.135714Z PROPS-END Node-path: trunk/B Node-kind: file Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: B Text-copy-source-md5: d54ff73404ed6041a3bd66850b061bff Text-copy-source-sha1: 4bdb40dfd6ec75cb730e678b5d7786e30170c5fb Node-path: B Node-action: delete durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/movetotrunk.sh0000755000000000000000000000103612043462603022773 0ustar 00000000000000#!/bin/sh # # Generate movetotrunk.svndump # mkdir temp cd temp mkdir project-orig cd project-orig cd .. svnadmin create testrepo svnurl=file://`pwd`/testrepo svn mkdir --parents $svnurl/sub1/sub2 -m subpaths svn import project-orig $svnurl/sub1/sub2 -m "init project" svn co $svnurl/sub1/sub2 project cd project echo a > a svn add a mkdir dir echo b > dir/b svn add dir svn ci -m adda svn up mkdir trunk svn add trunk svn mv a trunk/a svn mv dir trunk/dir svn ci -m 'move to trunk' cd .. svnadmin dump testrepo > ../movetotrunk.svndump durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/movetotrunk.svndump0000644000000000000000000000407712043462603024062 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: bb3f8dfd-83a8-4fe0-b57e-00a3838532ab Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2012-10-20T20:23:15.254324Z PROPS-END Revision-number: 1 Prop-content-length: 109 Content-length: 109 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2012-10-20T20:23:15.271492Z K 7 svn:log V 8 subpaths PROPS-END Node-path: sub1 Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: sub1/sub2 Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 105 Content-length: 105 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2012-10-20T20:23:16.068226Z K 7 svn:log V 4 adda PROPS-END Node-path: sub1/sub2/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Text-content-sha1: 3f786850e387550fdab836ed7e6dc881de23001b Content-length: 12 PROPS-END a Node-path: sub1/sub2/dir Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: sub1/sub2/dir/b Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 3b5d5c3712955042212316173ccf37be Text-content-sha1: 89e6c98d92887913cadf06b2adb97f26cde4849b Content-length: 12 PROPS-END b Revision-number: 3 Prop-content-length: 115 Content-length: 115 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2012-10-20T20:23:20.043626Z K 7 svn:log V 13 move to trunk PROPS-END Node-path: sub1/sub2/trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: sub1/sub2/trunk/a Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: sub1/sub2/a Text-copy-source-md5: 60b725f10c9c85c70d97880dfe8191b3 Text-copy-source-sha1: 3f786850e387550fdab836ed7e6dc881de23001b Node-path: sub1/sub2/trunk/dir Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: sub1/sub2/dir Node-path: sub1/sub2/dir Node-action: delete Node-path: sub1/sub2/a Node-action: delete durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/no-author.svndump0000644000000000000000000000205212043462603023370 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: df2126f7-00ab-4d49-b42c-7e981dde0bcf Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-10-07T22:49:12.059692Z PROPS-END Revision-number: 1 Prop-content-length: 85 Content-length: 85 K 7 svn:log V 11 Empty dirs. K 8 svn:date V 27 2008-10-07T22:49:41.118037Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 82 Content-length: 82 K 7 svn:log V 9 Add alpha K 8 svn:date V 27 2008-10-07T23:23:02.991743Z PROPS-END Node-path: trunk/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 12 Text-content-md5: 3c72ebf8bbd7fa88b1fdcee5398b5a17 Text-content-sha1: f552a50b53177d35b29a4a0ab1cece918b5b5e9b Content-length: 22 PROPS-END file: alpha durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/non_ascii_path_1.svndump0000644000000000000000000000272412043462603024660 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: a7b92ce5-70a8-4e75-99da-c3ca360ae490 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2010-08-16T13:08:38.494252Z PROPS-END Revision-number: 1 Prop-content-length: 121 Content-length: 121 K 7 svn:log V 20 Create project bøb. K 10 svn:author V 6 danchr K 8 svn:date V 27 2010-08-16T13:10:12.395716Z PROPS-END Node-path: bøb Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: bøb/branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: bøb/tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: bøb/trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: bøb/trunk/A Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 1 Text-content-md5: 68b329da9893e34099c7d8ad5cb9c940 Text-content-sha1: adc83b19e793491b1c6ea0fd8b46cd9f32e592fc Content-length: 11 PROPS-END Revision-number: 2 Prop-content-length: 112 Content-length: 112 K 7 svn:log V 11 Add a file. K 10 svn:author V 6 danchr K 8 svn:date V 27 2010-08-16T14:04:00.624391Z PROPS-END Node-path: bøb/trunk/B Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 1 Text-content-md5: 68b329da9893e34099c7d8ad5cb9c940 Text-content-sha1: adc83b19e793491b1c6ea0fd8b46cd9f32e592fc Content-length: 11 PROPS-END durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/non_ascii_path_2.svndump0000644000000000000000000000272412043462603024661 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: a7b92ce5-70a8-4e75-99da-c3ca360ae490 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2010-08-16T13:08:38.494252Z PROPS-END Revision-number: 1 Prop-content-length: 121 Content-length: 121 K 7 svn:log V 20 Create project bøb. K 10 svn:author V 6 danchr K 8 svn:date V 27 2010-08-16T13:10:12.395716Z PROPS-END Node-path: bøb Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: bøb/branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: bøb/tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: bøb/trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: bøb/trunk/A Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 1 Text-content-md5: 68b329da9893e34099c7d8ad5cb9c940 Text-content-sha1: adc83b19e793491b1c6ea0fd8b46cd9f32e592fc Content-length: 11 PROPS-END Revision-number: 2 Prop-content-length: 112 Content-length: 112 K 7 svn:log V 11 Add a file. K 10 svn:author V 6 danchr K 8 svn:date V 27 2010-08-16T14:04:00.624391Z PROPS-END Node-path: bøb/trunk/B Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 1 Text-content-md5: 68b329da9893e34099c7d8ad5cb9c940 Text-content-sha1: adc83b19e793491b1c6ea0fd8b46cd9f32e592fc Content-length: 11 PROPS-END durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/project_name_with_space.sh0000644000000000000000000000135712043462603025255 0ustar 00000000000000#!/bin/sh mkdir temp cd temp svnadmin create repo svn co file://`pwd`/repo wc cd wc mkdir 'project name' cd 'project name' mkdir branches trunk tags cd .. svn add * svn ci -m 'btt' cd 'project name'/trunk for a in alpha beta gamma delta ; do echo $a > $a svn add $a done svn ci -m 'Add files.' mkdir al echo foo > al/foo svn add al svn ci -m 'add directory al' cd .. svn up svn cp trunk branches/dev_branch svn ci -m 'branch' cd branches/dev_branch svn rm delta echo narf > alpha echo iota > iota svn add iota svn ci -m 'branch changes' cd ../../../../.. svnadmin dump temp/repo > project_name_with_space.svndump echo echo 'Complete.' echo 'You probably want to clean up temp now.' echo 'Dump in project_name_with_space.svndump' exit 0 durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/project_name_with_space.svndump0000644000000000000000000000720412043462603026334 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 5696e9c1-c844-4b75-be77-832074ca2506 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2009-12-24T18:41:45.385996Z PROPS-END Revision-number: 1 Prop-content-length: 102 Content-length: 102 K 7 svn:log V 3 btt K 10 svn:author V 5 augie K 8 svn:date V 27 2009-12-24T18:41:46.148218Z PROPS-END Node-path: project name Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: project name/branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: project name/tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: project name/trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 110 Content-length: 110 K 7 svn:log V 10 Add files. K 10 svn:author V 5 augie K 8 svn:date V 27 2009-12-24T18:41:47.184399Z PROPS-END Node-path: project name/trunk/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 9f9f90dbe3e5ee1218c86b8839db1995 Text-content-sha1: d046cd9b7ffb7661e449683313d41f6fc33e3130 Content-length: 16 PROPS-END alpha Node-path: project name/trunk/beta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 5 Text-content-md5: f0cf2a92516045024a0c99147b28f05b Text-content-sha1: 6c007a14875d53d9bf0ef5a6fc0257c817f0fb83 Content-length: 15 PROPS-END beta Node-path: project name/trunk/delta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: d2840cc81bc032bd1141b56687d0f93c Text-content-sha1: 4bd6315d6d7824c4e376847ca7d116738ad2f29a Content-length: 16 PROPS-END delta Node-path: project name/trunk/gamma Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 303febb9068384eca46b5b6516843b35 Text-content-sha1: 37f385b028bf2f93a4b497ca9ff44eea63945b7f Content-length: 16 PROPS-END gamma Revision-number: 3 Prop-content-length: 116 Content-length: 116 K 7 svn:log V 16 add directory al K 10 svn:author V 5 augie K 8 svn:date V 27 2009-12-24T18:41:48.125685Z PROPS-END Node-path: project name/trunk/al Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: project name/trunk/al/foo Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 4 Text-content-md5: d3b07384d113edec49eaa6238ad5ff00 Text-content-sha1: f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 Content-length: 14 PROPS-END foo Revision-number: 4 Prop-content-length: 105 Content-length: 105 K 7 svn:log V 6 branch K 10 svn:author V 5 augie K 8 svn:date V 27 2009-12-24T18:41:51.073403Z PROPS-END Node-path: project name/branches/dev_branch Node-kind: dir Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: project name/trunk Revision-number: 5 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 14 branch changes K 10 svn:author V 5 augie K 8 svn:date V 27 2009-12-24T18:41:52.164995Z PROPS-END Node-path: project name/branches/dev_branch/alpha Node-kind: file Node-action: change Text-content-length: 5 Text-content-md5: 5e723ed52db2000686425ca28bc5ba4a Text-content-sha1: 89dc052326e95e1d1c5b08e2b8471d99112d1fa4 Content-length: 5 narf Node-path: project name/branches/dev_branch/iota Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 5 Text-content-md5: ebcf3971120220589f1dfbf8d56e25b9 Text-content-sha1: 47e9aceee5149402971cda8590e9b912c1b1053e Content-length: 15 PROPS-END iota Node-path: project name/branches/dev_branch/delta Node-action: delete durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/project_root_at_repo_root.svndump0000644000000000000000000001173312043462603026747 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: e72213dc-9746-48d2-846a-a19c2c569b0d Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-10-09T14:46:06.470091Z PROPS-END Revision-number: 1 Prop-content-length: 111 Content-length: 111 K 7 svn:log V 11 Empty dirs. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-09T14:46:07.147162Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 14 Initial Files. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-09T14:46:08.147874Z PROPS-END Node-path: trunk/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 15 Text-content-md5: 8717538ba2f18a613eaa4892e8d178f7 Content-length: 25 PROPS-END This is alpha. Node-path: trunk/beta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 14 Text-content-md5: 952a13b33256f343982d0a8f0e0af277 Content-length: 24 PROPS-END This is beta. Node-path: trunk/delta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 15 Text-content-md5: e48d0bca73f04e800e7bd9fb58d49a62 Content-length: 25 PROPS-END This is delta. Revision-number: 3 Prop-content-length: 110 Content-length: 110 K 7 svn:log V 10 Tag rev 1. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-09T14:46:10.131807Z PROPS-END Node-path: tags/rev1 Node-kind: dir Node-action: add Node-copyfrom-rev: 1 Node-copyfrom-path: trunk Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Node-path: tags/rev1/alpha Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/alpha Text-copy-source-md5: 8717538ba2f18a613eaa4892e8d178f7 Node-path: tags/rev1/beta Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/beta Text-copy-source-md5: 952a13b33256f343982d0a8f0e0af277 Node-path: tags/rev1/delta Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/delta Text-copy-source-md5: e48d0bca73f04e800e7bd9fb58d49a62 Revision-number: 4 Prop-content-length: 116 Content-length: 116 K 7 svn:log V 16 Branch to crazy. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-09T14:46:12.131174Z PROPS-END Node-path: branches/crazy Node-kind: dir Node-action: add Node-copyfrom-rev: 1 Node-copyfrom-path: trunk Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Node-path: branches/crazy/alpha Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/alpha Text-copy-source-md5: 8717538ba2f18a613eaa4892e8d178f7 Node-path: branches/crazy/beta Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/beta Text-copy-source-md5: 952a13b33256f343982d0a8f0e0af277 Node-path: branches/crazy/delta Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/delta Text-copy-source-md5: e48d0bca73f04e800e7bd9fb58d49a62 Revision-number: 5 Prop-content-length: 108 Content-length: 108 K 7 svn:log V 9 Add gamma K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-09T14:46:13.143196Z PROPS-END Node-path: trunk/gamma Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 15 Text-content-md5: 255b705986e84fbb13db9c5a832739ae Content-length: 25 PROPS-END This is gamma. Revision-number: 6 Prop-content-length: 108 Content-length: 108 K 7 svn:log V 9 Add omega K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-09T14:46:14.144244Z PROPS-END Node-path: branches/crazy/omega Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 15 Text-content-md5: 22219e00c8dbb47ce790f0eb658e8014 Content-length: 25 PROPS-END This is omega. Revision-number: 7 Prop-content-length: 121 Content-length: 121 K 7 svn:log V 21 Branch to more_crazy. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-09T14:46:16.135900Z PROPS-END Node-path: branches/more_crazy Node-kind: dir Node-action: add Node-copyfrom-rev: 1 Node-copyfrom-path: trunk Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Node-path: branches/more_crazy/alpha Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/alpha Text-copy-source-md5: 8717538ba2f18a613eaa4892e8d178f7 Node-path: branches/more_crazy/beta Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/beta Text-copy-source-md5: 952a13b33256f343982d0a8f0e0af277 Node-path: branches/more_crazy/delta Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/delta Text-copy-source-md5: e48d0bca73f04e800e7bd9fb58d49a62 Node-path: branches/more_crazy/gamma Node-kind: file Node-action: add Node-copyfrom-rev: 5 Node-copyfrom-path: trunk/gamma Text-copy-source-md5: 255b705986e84fbb13db9c5a832739ae durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/project_root_not_repo_root.svndump0000644000000000000000000001263412043462603027144 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 1916bd89-caf8-4f3d-96a7-7d5a7968f6ea Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-10-09T15:32:50.842663Z PROPS-END Revision-number: 1 Prop-content-length: 111 Content-length: 111 K 7 svn:log V 11 Empty dirs. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-09T15:32:51.151627Z PROPS-END Node-path: dummyproj Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: dummyproj/branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: dummyproj/tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: dummyproj/trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 14 Initial Files. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-09T15:32:52.149125Z PROPS-END Node-path: dummyproj/trunk/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 15 Text-content-md5: 8717538ba2f18a613eaa4892e8d178f7 Content-length: 25 PROPS-END This is alpha. Node-path: dummyproj/trunk/beta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 14 Text-content-md5: 952a13b33256f343982d0a8f0e0af277 Content-length: 24 PROPS-END This is beta. Node-path: dummyproj/trunk/delta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 15 Text-content-md5: e48d0bca73f04e800e7bd9fb58d49a62 Content-length: 25 PROPS-END This is delta. Revision-number: 3 Prop-content-length: 110 Content-length: 110 K 7 svn:log V 10 Tag rev 1. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-09T15:32:54.131909Z PROPS-END Node-path: dummyproj/tags/rev1 Node-kind: dir Node-action: add Node-copyfrom-rev: 1 Node-copyfrom-path: dummyproj/trunk Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Node-path: dummyproj/tags/rev1/alpha Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: dummyproj/trunk/alpha Text-copy-source-md5: 8717538ba2f18a613eaa4892e8d178f7 Node-path: dummyproj/tags/rev1/beta Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: dummyproj/trunk/beta Text-copy-source-md5: 952a13b33256f343982d0a8f0e0af277 Node-path: dummyproj/tags/rev1/delta Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: dummyproj/trunk/delta Text-copy-source-md5: e48d0bca73f04e800e7bd9fb58d49a62 Revision-number: 4 Prop-content-length: 116 Content-length: 116 K 7 svn:log V 16 Branch to crazy. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-09T15:32:56.132149Z PROPS-END Node-path: dummyproj/branches/crazy Node-kind: dir Node-action: add Node-copyfrom-rev: 1 Node-copyfrom-path: dummyproj/trunk Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Node-path: dummyproj/branches/crazy/alpha Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: dummyproj/trunk/alpha Text-copy-source-md5: 8717538ba2f18a613eaa4892e8d178f7 Node-path: dummyproj/branches/crazy/beta Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: dummyproj/trunk/beta Text-copy-source-md5: 952a13b33256f343982d0a8f0e0af277 Node-path: dummyproj/branches/crazy/delta Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: dummyproj/trunk/delta Text-copy-source-md5: e48d0bca73f04e800e7bd9fb58d49a62 Revision-number: 5 Prop-content-length: 108 Content-length: 108 K 7 svn:log V 9 Add gamma K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-09T15:32:57.142842Z PROPS-END Node-path: dummyproj/trunk/gamma Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 15 Text-content-md5: 255b705986e84fbb13db9c5a832739ae Content-length: 25 PROPS-END This is gamma. Revision-number: 6 Prop-content-length: 108 Content-length: 108 K 7 svn:log V 9 Add omega K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-09T15:32:58.146324Z PROPS-END Node-path: dummyproj/branches/crazy/omega Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 15 Text-content-md5: 22219e00c8dbb47ce790f0eb658e8014 Content-length: 25 PROPS-END This is omega. Revision-number: 7 Prop-content-length: 121 Content-length: 121 K 7 svn:log V 21 Branch to more_crazy. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-09T15:33:00.138992Z PROPS-END Node-path: dummyproj/branches/more_crazy Node-kind: dir Node-action: add Node-copyfrom-rev: 1 Node-copyfrom-path: dummyproj/trunk Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Node-path: dummyproj/branches/more_crazy/alpha Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: dummyproj/trunk/alpha Text-copy-source-md5: 8717538ba2f18a613eaa4892e8d178f7 Node-path: dummyproj/branches/more_crazy/beta Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: dummyproj/trunk/beta Text-copy-source-md5: 952a13b33256f343982d0a8f0e0af277 Node-path: dummyproj/branches/more_crazy/delta Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: dummyproj/trunk/delta Text-copy-source-md5: e48d0bca73f04e800e7bd9fb58d49a62 Node-path: dummyproj/branches/more_crazy/gamma Node-kind: file Node-action: add Node-copyfrom-rev: 5 Node-copyfrom-path: dummyproj/trunk/gamma Text-copy-source-md5: 255b705986e84fbb13db9c5a832739ae durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/propset-branch.svndump0000644000000000000000000000333012043462603024403 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 6506bfb9-9ce8-4b91-8357-4dc8bef2ecc6 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2010-07-10T11:55:08.483993Z PROPS-END Revision-number: 1 Prop-content-length: 122 Content-length: 122 K 7 svn:log V 21 create initial layout K 10 svn:author V 6 danchr K 8 svn:date V 27 2010-07-10T11:55:57.419315Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 111 Content-length: 111 K 7 svn:log V 10 add a file K 10 svn:author V 6 danchr K 8 svn:date V 27 2010-07-10T11:56:54.708286Z PROPS-END Node-path: trunk/a.txt Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 0 Text-content-md5: d41d8cd98f00b204e9800998ecf8427e Text-content-sha1: da39a3ee5e6b4b0d3255bfef95601890afd80709 Content-length: 10 PROPS-END Revision-number: 3 Prop-content-length: 116 Content-length: 116 K 7 svn:log V 15 create a branch K 10 svn:author V 6 danchr K 8 svn:date V 27 2010-07-10T11:57:29.710978Z PROPS-END Node-path: branches/the-branch Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk Revision-number: 4 Prop-content-length: 115 Content-length: 115 K 7 svn:log V 14 set a property K 10 svn:author V 6 danchr K 8 svn:date V 27 2010-07-10T11:58:41.440627Z PROPS-END Node-path: branches/the-branch Node-kind: dir Node-action: change Prop-content-length: 42 Content-length: 42 K 12 the-property V 9 something PROPS-END durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/pushexternals.sh0000755000000000000000000000122712043462603023305 0ustar 00000000000000#!/bin/sh # # Generate pushexternals.svndump # mkdir temp cd temp mkdir project-orig cd project-orig mkdir trunk mkdir externals cd .. svnadmin create testrepo svnurl=file://`pwd`/testrepo svn import project-orig $svnurl -m "init project" svn co $svnurl project cd project/externals mkdir project1 echo a > project1/a svn add project1 mkdir project2 echo a > project2/b svn add project2 svn ci -m "configure externals projects" cd ../trunk echo a > a # dir is used to set svn:externals on an already existing directory mkdir dir svn add a dir svn ci -m "add a and dir" svn rm a svn ci -m "remove a" cd ../.. svnadmin dump testrepo > ../pushexternals.svndump durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/pushexternals.svndump0000644000000000000000000000407112043462603024364 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: ce6cbbbe-6533-4ba7-91e1-cc165717826f Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-12-27T19:48:52.687312Z PROPS-END Revision-number: 1 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 12 init project K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-27T19:48:52.751303Z PROPS-END Node-path: externals Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 130 Content-length: 130 K 7 svn:log V 28 configure externals projects K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-27T19:48:53.230452Z PROPS-END Node-path: externals/project1 Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: externals/project1/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Content-length: 12 PROPS-END a Node-path: externals/project2 Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: externals/project2/b Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Content-length: 12 PROPS-END a Revision-number: 3 Prop-content-length: 115 Content-length: 115 K 7 svn:log V 13 add a and dir K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-27T19:48:54.187575Z PROPS-END Node-path: trunk/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Content-length: 12 PROPS-END a Node-path: trunk/dir Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 4 Prop-content-length: 109 Content-length: 109 K 7 svn:log V 8 remove a K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-27T19:48:55.175595Z PROPS-END Node-path: trunk/a Node-action: delete durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/pushrenames.sh0000755000000000000000000000066112043462603022733 0ustar 00000000000000#!/bin/sh # # Generate pushrenames.svndump # mkdir temp cd temp mkdir project-orig cd project-orig mkdir trunk mkdir branches cd .. svnadmin create testrepo svnurl=file://`pwd`/testrepo svn import project-orig $svnurl -m "init project" svn co $svnurl project cd project/trunk echo a > a echo b > b echo c > c echo d > d echo e > e svn add a b c d e svn ci -m "add files" cd ../.. svnadmin dump testrepo > ../pushrenames.svndump durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/pushrenames.svndump0000644000000000000000000000322412043462603024010 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 7b554dd8-6cf3-486c-abe4-86e652fb70b5 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-11-02T21:17:17.157936Z PROPS-END Revision-number: 1 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 12 init project K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-11-02T21:17:17.217532Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 110 Content-length: 110 K 7 svn:log V 9 add files K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-11-02T21:17:18.184945Z PROPS-END Node-path: trunk/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Content-length: 12 PROPS-END a Node-path: trunk/b Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 3b5d5c3712955042212316173ccf37be Content-length: 12 PROPS-END b Node-path: trunk/c Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 2cd6ee2c70b0bde53fbe6cac3c8b8bb1 Content-length: 12 PROPS-END c Node-path: trunk/d Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: e29311f6f1bf1af907f9ef9f44b8328b Content-length: 12 PROPS-END d Node-path: trunk/e Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 9ffbf43126e33be52cd2bf7e01d627f9 Content-length: 12 PROPS-END e durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/remove_tag_test.svndump0000644000000000000000000000515612043462603024653 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: df2126f7-00ab-4d49-b42c-7e981dde0bcf Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-10-07T22:49:12.059692Z PROPS-END Revision-number: 1 Prop-content-length: 111 Content-length: 111 K 7 svn:log V 11 Empty dirs. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T22:49:41.118037Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 108 Content-length: 108 K 7 svn:log V 9 Add alpha K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T23:23:02.991743Z PROPS-END Node-path: trunk/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 12 Text-content-md5: 3c72ebf8bbd7fa88b1fdcee5398b5a17 Content-length: 22 PROPS-END file: alpha Revision-number: 3 Prop-content-length: 107 Content-length: 107 K 7 svn:log V 8 Add beta K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T23:28:22.651398Z PROPS-END Node-path: trunk/beta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 13 Text-content-md5: 981d1eb5fd0bbe05354c292105944863 Content-length: 23 PROPS-END Data of beta Revision-number: 4 Prop-content-length: 110 Content-length: 110 K 7 svn:log V 10 tagging r3 K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-08T23:18:29.837825Z PROPS-END Node-path: tags/tag_r3 Node-kind: dir Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: trunk Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Revision-number: 5 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 14 tag from a tag K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-08T23:18:51.091356Z PROPS-END Node-path: tags/copied_tag Node-kind: dir Node-action: add Node-copyfrom-rev: 4 Node-copyfrom-path: tags/tag_r3 Revision-number: 6 Prop-content-length: 112 Content-length: 112 K 7 svn:log V 12 rename a tag K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-08T23:20:23.431085Z PROPS-END Node-path: tags/other_tag_r3 Node-kind: dir Node-action: add Node-copyfrom-rev: 5 Node-copyfrom-path: tags/copied_tag Node-path: tags/copied_tag Node-action: delete Revision-number: 7 Prop-content-length: 120 Content-length: 120 K 7 svn:log V 20 remove a tag as well K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-08T23:21:43.223062Z PROPS-END Node-path: tags/other_tag_r3 Node-action: delete durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/rename_branch_parent_dir.sh0000755000000000000000000000126712043462603025377 0ustar 00000000000000#!/bin/sh mkdir temp cd temp svnadmin create repo svn co file://`pwd`/repo wc cd wc mkdir brances trunk tags svn add * svn ci -m 'btt' cd trunk for a in alpha beta gamma delta ; do echo $a > $a svn add $a done svn ci -m 'Add files.' cd .. svn up svn cp trunk brances/dev_branch svn ci -m 'branch' cd brances/dev_branch svn rm delta echo narf > alpha echo iota > iota svn add iota svn ci -m 'branch changes' cd ../.. svn up svn mv brances branches svn ci -m 'move branches to branches' cd .. cd .. svnadmin dump temp/repo > rename_branch_parent_dir.svndump echo echo 'Complete.' echo 'You probably want to clean up temp now.' echo 'Dump in rename_branch_parent_dir.svndump' exit 0 durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/rename_branch_parent_dir.svndump0000644000000000000000000000544312043462603026456 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: dceec716-b592-46cb-b5fe-41acbbe32b85 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2010-02-06T17:32:02.297006Z PROPS-END Revision-number: 1 Prop-content-length: 102 Content-length: 102 K 7 svn:log V 3 btt K 10 svn:author V 5 augie K 8 svn:date V 27 2010-02-06T17:32:03.176895Z PROPS-END Node-path: brances Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 110 Content-length: 110 K 7 svn:log V 10 Add files. K 10 svn:author V 5 augie K 8 svn:date V 27 2010-02-06T17:32:04.202558Z PROPS-END Node-path: trunk/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 9f9f90dbe3e5ee1218c86b8839db1995 Content-length: 16 PROPS-END alpha Node-path: trunk/beta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 5 Text-content-md5: f0cf2a92516045024a0c99147b28f05b Content-length: 15 PROPS-END beta Node-path: trunk/delta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: d2840cc81bc032bd1141b56687d0f93c Content-length: 16 PROPS-END delta Node-path: trunk/gamma Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 303febb9068384eca46b5b6516843b35 Content-length: 16 PROPS-END gamma Revision-number: 3 Prop-content-length: 105 Content-length: 105 K 7 svn:log V 6 branch K 10 svn:author V 5 augie K 8 svn:date V 27 2010-02-06T17:32:07.137254Z PROPS-END Node-path: brances/dev_branch Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk Revision-number: 4 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 14 branch changes K 10 svn:author V 5 augie K 8 svn:date V 27 2010-02-06T17:32:08.190998Z PROPS-END Node-path: brances/dev_branch/alpha Node-kind: file Node-action: change Text-content-length: 5 Text-content-md5: 5e723ed52db2000686425ca28bc5ba4a Content-length: 5 narf Node-path: brances/dev_branch/iota Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 5 Text-content-md5: ebcf3971120220589f1dfbf8d56e25b9 Content-length: 15 PROPS-END iota Node-path: brances/dev_branch/delta Node-action: delete Revision-number: 5 Prop-content-length: 125 Content-length: 125 K 7 svn:log V 25 move branches to branches K 10 svn:author V 5 augie K 8 svn:date V 27 2010-02-06T17:32:11.150006Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Node-copyfrom-rev: 4 Node-copyfrom-path: brances Node-path: brances Node-action: delete durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/rename_tag_test.sh0000755000000000000000000000130312043462603023534 0ustar 00000000000000#!/bin/sh mkdir temp cd temp svnadmin create repo svn co file://`pwd`/repo wc export REPO=file://`pwd`/repo cd wc mkdir branches trunk tags svn add * svn ci -m 'Empty dirs.' echo 'file: alpha' > trunk/alpha svn add trunk/alpha svn ci -m 'Add alpha' svn up echo 'Data of beta' > trunk/beta svn add trunk/beta svn ci -m 'Add beta' svn up cd .. svn cp -m 'tagging r3' $REPO/trunk@3 $REPO/tags/tag_r3 svn cp -m 'tag from a tag' $REPO/tags/tag_r3 $REPO/tags/copied_tag svn mv -m 'rename a tag' $REPO/tags/copied_tag $REPO/tags/other_tag_r3 cd .. svnadmin dump temp/repo > rename_tag_test.svndump echo echo 'Complete.' echo 'You probably want to clean up temp now.' echo 'Dump in renametagdir.svndump' exit 0 durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/rename_tag_test.svndump0000644000000000000000000000466712043462603024633 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: b94d0721-81cf-435e-ab14-c40b19ce1924 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2010-01-17T19:05:32.787484Z PROPS-END Revision-number: 1 Prop-content-length: 113 Content-length: 113 K 7 svn:log V 11 Empty dirs. K 10 svn:author V 7 pmezard K 8 svn:date V 27 2010-01-17T19:05:33.116308Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 110 Content-length: 110 K 7 svn:log V 9 Add alpha K 10 svn:author V 7 pmezard K 8 svn:date V 27 2010-01-17T19:05:34.099166Z PROPS-END Node-path: trunk/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 12 Text-content-md5: 3c72ebf8bbd7fa88b1fdcee5398b5a17 Text-content-sha1: f552a50b53177d35b29a4a0ab1cece918b5b5e9b Content-length: 22 PROPS-END file: alpha Revision-number: 3 Prop-content-length: 109 Content-length: 109 K 7 svn:log V 8 Add beta K 10 svn:author V 7 pmezard K 8 svn:date V 27 2010-01-17T19:05:36.110949Z PROPS-END Node-path: trunk/beta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 13 Text-content-md5: 981d1eb5fd0bbe05354c292105944863 Text-content-sha1: 5d40e0a9ceda69f3d98d4851a6bee02c10a6e277 Content-length: 23 PROPS-END Data of beta Revision-number: 4 Prop-content-length: 112 Content-length: 112 K 7 svn:log V 10 tagging r3 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2010-01-17T19:05:38.066217Z PROPS-END Node-path: tags/tag_r3 Node-kind: dir Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: trunk Revision-number: 5 Prop-content-length: 116 Content-length: 116 K 7 svn:log V 14 tag from a tag K 10 svn:author V 7 pmezard K 8 svn:date V 27 2010-01-17T19:05:38.116376Z PROPS-END Node-path: tags/copied_tag Node-kind: dir Node-action: add Node-copyfrom-rev: 4 Node-copyfrom-path: tags/tag_r3 Revision-number: 6 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 12 rename a tag K 10 svn:author V 7 pmezard K 8 svn:date V 27 2010-01-17T19:05:38.179177Z PROPS-END Node-path: tags/other_tag_r3 Node-kind: dir Node-action: add Node-copyfrom-rev: 5 Node-copyfrom-path: tags/copied_tag Node-path: tags/copied_tag Node-action: delete durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/renamedproject.sh0000755000000000000000000000307612043462603023406 0ustar 00000000000000#!/bin/sh # # Convert a project moving from a non-canonical to canonical # layout, exercizing the missing plaintext code paths. It also tests # branch creations where the branch source is not a canonical branch. # mkdir temp cd temp svnadmin create testrepo svnurl=file://`pwd`/testrepo mkdir project-orig cd project-orig echo a > a echo b > b echo c > c mkdir d echo a > d/a cd .. # Let's suppose it was actually branched in a previous life mkdir project-branch cd project-branch echo a > a echo b > b cd .. svn import project-orig $svnurl/project-orig -m "init project" svn import project-branch $svnurl/project-branch -m "init branch" svn mkdir $svnurl/project -m "create new project hierarchy" svn mv $svnurl/project-orig $svnurl/project/project -m "rename as project" svn mv $svnurl/project/project $svnurl/project/trunk -m "rename as project" svn mkdir $svnurl/project/branches -m "add branches root" svn mv $svnurl/project-branch $svnurl/project/misplaced -m "incorrect move of the branch" svn mv $svnurl/project/misplaced $svnurl/project/branches/branch -m "move of the branch" svn co $svnurl/project cd project echo a >> trunk/a svn ci -m "change a" echo a >> trunk/a echo b >> trunk/b svn rm trunk/c echo a >> trunk/d/a svn ci -m "change files in trunk" # Try the same thing with the branch echo a >> branches/branch/a svn rm branches/branch/b svn ci -m "change a in branch" cd .. # Add this to make test_rebuildmeta happy, needs something to convert svn import project-orig $svnurl/trunk -m "init fake trunk for rebuild_meta" svnadmin dump testrepo > ../renamedproject.svndump durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/renamedproject.svndump0000644000000000000000000001667112043462603024472 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 169a5fe1-3c9f-4eef-ad86-f932c54e53dc Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2009-05-01T17:53:40.957980Z PROPS-END Revision-number: 1 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 12 init project K 10 svn:author V 7 pmezard K 8 svn:date V 27 2009-05-01T17:53:41.129010Z PROPS-END Node-path: project-orig Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: project-orig/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Text-content-sha1: 3f786850e387550fdab836ed7e6dc881de23001b Content-length: 12 PROPS-END a Node-path: project-orig/b Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 3b5d5c3712955042212316173ccf37be Text-content-sha1: 89e6c98d92887913cadf06b2adb97f26cde4849b Content-length: 12 PROPS-END b Node-path: project-orig/c Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 2cd6ee2c70b0bde53fbe6cac3c8b8bb1 Text-content-sha1: 2b66fd261ee5c6cfc8de7fa466bab600bcfe4f69 Content-length: 12 PROPS-END c Node-path: project-orig/d Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: project-orig/d/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Text-content-sha1: 3f786850e387550fdab836ed7e6dc881de23001b Content-length: 12 PROPS-END a Revision-number: 2 Prop-content-length: 113 Content-length: 113 K 7 svn:log V 11 init branch K 10 svn:author V 7 pmezard K 8 svn:date V 27 2009-05-01T17:53:41.193926Z PROPS-END Node-path: project-branch Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: project-branch/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Text-content-sha1: 3f786850e387550fdab836ed7e6dc881de23001b Content-length: 12 PROPS-END a Node-path: project-branch/b Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 3b5d5c3712955042212316173ccf37be Text-content-sha1: 89e6c98d92887913cadf06b2adb97f26cde4849b Content-length: 12 PROPS-END b Revision-number: 3 Prop-content-length: 130 Content-length: 130 K 7 svn:log V 28 create new project hierarchy K 10 svn:author V 7 pmezard K 8 svn:date V 27 2009-05-01T17:53:41.236131Z PROPS-END Node-path: project Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 4 Prop-content-length: 119 Content-length: 119 K 7 svn:log V 17 rename as project K 10 svn:author V 7 pmezard K 8 svn:date V 27 2009-05-01T17:53:41.288164Z PROPS-END Node-path: project/project Node-kind: dir Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: project-orig Node-path: project-orig Node-action: delete Revision-number: 5 Prop-content-length: 119 Content-length: 119 K 7 svn:log V 17 rename as project K 10 svn:author V 7 pmezard K 8 svn:date V 27 2009-05-01T17:53:41.335332Z PROPS-END Node-path: project/trunk Node-kind: dir Node-action: add Node-copyfrom-rev: 4 Node-copyfrom-path: project/project Node-path: project/project Node-action: delete Revision-number: 6 Prop-content-length: 119 Content-length: 119 K 7 svn:log V 17 add branches root K 10 svn:author V 7 pmezard K 8 svn:date V 27 2009-05-01T17:53:41.377684Z PROPS-END Node-path: project/branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 7 Prop-content-length: 130 Content-length: 130 K 7 svn:log V 28 incorrect move of the branch K 10 svn:author V 7 pmezard K 8 svn:date V 27 2009-05-01T17:53:41.426928Z PROPS-END Node-path: project/misplaced Node-kind: dir Node-action: add Node-copyfrom-rev: 6 Node-copyfrom-path: project-branch Node-path: project-branch Node-action: delete Revision-number: 8 Prop-content-length: 120 Content-length: 120 K 7 svn:log V 18 move of the branch K 10 svn:author V 7 pmezard K 8 svn:date V 27 2009-05-01T17:53:41.478644Z PROPS-END Node-path: project/branches/branch Node-kind: dir Node-action: add Node-copyfrom-rev: 7 Node-copyfrom-path: project/misplaced Node-path: project/misplaced Node-action: delete Revision-number: 9 Prop-content-length: 109 Content-length: 109 K 7 svn:log V 8 change a K 10 svn:author V 7 pmezard K 8 svn:date V 27 2009-05-01T17:53:42.078428Z PROPS-END Node-path: project/trunk/a Node-kind: file Node-action: change Text-content-length: 4 Text-content-md5: 0d227f1abf8c2932d342e9b99cc957eb Text-content-sha1: d7c8127a20a396cff08af086a1c695b0636f0c29 Content-length: 4 a a Revision-number: 10 Prop-content-length: 123 Content-length: 123 K 7 svn:log V 21 change files in trunk K 10 svn:author V 7 pmezard K 8 svn:date V 27 2009-05-01T17:53:43.109915Z PROPS-END Node-path: project/trunk/a Node-kind: file Node-action: change Text-content-length: 6 Text-content-md5: 7d4ebf8f298d22fc349a91725b00af1c Text-content-sha1: 92f31bc48f52339253fce6cad9f2f0c95b302f7e Content-length: 6 a a a Node-path: project/trunk/b Node-kind: file Node-action: change Text-content-length: 4 Text-content-md5: 06ac26ed8b614fc0b141e4542aa067c2 Text-content-sha1: f6980469e74f7125178e88ec571e06fe6ce86e95 Content-length: 4 b b Node-path: project/trunk/d/a Node-kind: file Node-action: change Text-content-length: 4 Text-content-md5: 0d227f1abf8c2932d342e9b99cc957eb Text-content-sha1: d7c8127a20a396cff08af086a1c695b0636f0c29 Content-length: 4 a a Node-path: project/trunk/c Node-action: delete Revision-number: 11 Prop-content-length: 120 Content-length: 120 K 7 svn:log V 18 change a in branch K 10 svn:author V 7 pmezard K 8 svn:date V 27 2009-05-01T17:53:44.100553Z PROPS-END Node-path: project/branches/branch/a Node-kind: file Node-action: change Text-content-length: 4 Text-content-md5: 0d227f1abf8c2932d342e9b99cc957eb Text-content-sha1: d7c8127a20a396cff08af086a1c695b0636f0c29 Content-length: 4 a a Node-path: project/branches/branch/b Node-action: delete Revision-number: 12 Prop-content-length: 134 Content-length: 134 K 7 svn:log V 32 init fake trunk for rebuild_meta K 10 svn:author V 7 pmezard K 8 svn:date V 27 2009-05-01T17:53:45.089483Z PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Text-content-sha1: 3f786850e387550fdab836ed7e6dc881de23001b Content-length: 12 PROPS-END a Node-path: trunk/b Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 3b5d5c3712955042212316173ccf37be Text-content-sha1: 89e6c98d92887913cadf06b2adb97f26cde4849b Content-length: 12 PROPS-END b Node-path: trunk/c Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 2cd6ee2c70b0bde53fbe6cac3c8b8bb1 Text-content-sha1: 2b66fd261ee5c6cfc8de7fa466bab600bcfe4f69 Content-length: 12 PROPS-END c Node-path: trunk/d Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/d/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Text-content-sha1: 3f786850e387550fdab836ed7e6dc881de23001b Content-length: 12 PROPS-END a durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/renames.sh0000755000000000000000000000542512043462603022036 0ustar 00000000000000#!/bin/sh # # Generate renames.svndump # mkdir temp cd temp mkdir project-orig cd project-orig mkdir trunk mkdir branches cd .. svnadmin create testrepo svnurl=file://`pwd`/testrepo svn import project-orig $svnurl -m "init project" svn co $svnurl project cd project/trunk # Entries for regular tests echo a > a echo b > b mkdir -p da/db echo c > da/daf echo d > da/db/dbf # Entries to test delete + copy echo deleted > deletedfile mkdir deleteddir echo deleteddir > deleteddir/f # Entries to test copy before change echo changed > changed mkdir changeddir echo changed2 > changeddir/f # Entries unchanged in the rest of history echo unchanged > unchanged mkdir unchangeddir echo unchanged2 > unchangeddir/f # One of the files will be changed afterwards, to test # group copies detection mkdir groupdir echo a > groupdir/a echo b > groupdir/b svn add a b da deletedfile deleteddir changed changeddir unchanged unchangeddir groupdir svn ci -m "add a and b" # Remove files to be copied later svn rm deletedfile svn rm deleteddir # Update files to be copied before this change echo changed >> changed echo changed2 >> changeddir/f # Update one of the groupdir files echo a >> groupdir/a svn ci -m "delete files and dirs" cd ../branches svn cp ../trunk branch1 svn ci -m "create branch1" cd branch1 echo c > c svn add c svn ci -m "add c" cd ../../trunk # Regular copy and rename svn cp a a1 svn mv a a2 # Copy and update of source and dest svn cp b b1 echo b >> b echo c >> b1 # Directory copy and renaming svn cp da da1 svn mv da da2 # Test one copy operation in branch cd ../branches/branch1 svn cp c c1 echo c >> c1 cd ../.. svn ci -m "rename and copy a, b and da" cd trunk # Copy across branch svn cp ../branches/branch1/c c svn ci -m "copy b from branch1" # Copy deleted stuff from the past svn cp $svnurl/trunk/deletedfile@2 deletedfile svn cp $svnurl/trunk/deleteddir@2 deleteddir svn ci -m "copy stuff from the past" # Copy data from the past before it was changed svn cp $svnurl/trunk/changed@2 changed2 svn cp $svnurl/trunk/changeddir@2 changeddir2 # Harder, copy from the past before change and change it again # This confused the stupid diff path svn cp $svnurl/trunk/changed@2 changed3 echo changed3 >> changed3 svn ci -m "copy stuff from the past before change" # Copy unchanged stuff from the past. Since no changed occured in these files # between the source and parent revision, we record them as copy from parent # instead of source rev. svn cp $svnurl/trunk/unchanged@2 unchanged2 svn cp $svnurl/trunk/unchangeddir@2 unchangeddir2 svn ci -m "copy unchanged stuff from the past" # Copy groupdir, unfortunately one file was changed after r2 so the # copy should not be recorded at all svn cp $svnurl/trunk/groupdir@2 groupdir2 svn ci -m "copy groupdir from the past" cd ../.. svnadmin dump testrepo > ../renames.svndump durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/renames.svndump0000644000000000000000000002641512043462603023117 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 113560bd-ec36-42a6-acef-e4688a33b129 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-12-05T22:48:38.139917Z PROPS-END Revision-number: 1 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 12 init project K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-05T22:48:38.525864Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 113 Content-length: 113 K 7 svn:log V 11 add a and b K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-05T22:48:39.313010Z PROPS-END Node-path: trunk/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Content-length: 12 PROPS-END a Node-path: trunk/b Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 3b5d5c3712955042212316173ccf37be Content-length: 12 PROPS-END b Node-path: trunk/changed Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 8 Text-content-md5: ec1bebaea2c042beb68f7679ddd106a4 Content-length: 18 PROPS-END changed Node-path: trunk/changeddir Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/changeddir/f Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 9 Text-content-md5: 2dfdfd8492a2c558ec838d69f73f5f6b Content-length: 19 PROPS-END changed2 Node-path: trunk/da Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/da/daf Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 2cd6ee2c70b0bde53fbe6cac3c8b8bb1 Content-length: 12 PROPS-END c Node-path: trunk/da/db Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/da/db/dbf Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: e29311f6f1bf1af907f9ef9f44b8328b Content-length: 12 PROPS-END d Node-path: trunk/deleteddir Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/deleteddir/f Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 11 Text-content-md5: 49b72b575e26ecddb296dd59b24c3e67 Content-length: 21 PROPS-END deleteddir Node-path: trunk/deletedfile Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 8 Text-content-md5: 4d742b2f247bec99b41a60acbebc149a Content-length: 18 PROPS-END deleted Node-path: trunk/groupdir Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/groupdir/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Content-length: 12 PROPS-END a Node-path: trunk/groupdir/b Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 3b5d5c3712955042212316173ccf37be Content-length: 12 PROPS-END b Node-path: trunk/unchanged Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 10 Text-content-md5: 85ae5b04dd0a666efad8633d142a4635 Content-length: 20 PROPS-END unchanged Node-path: trunk/unchangeddir Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/unchangeddir/f Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 11 Text-content-md5: a11092875079a002afb9ecef07f510e7 Content-length: 21 PROPS-END unchanged2 Revision-number: 3 Prop-content-length: 123 Content-length: 123 K 7 svn:log V 21 delete files and dirs K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-05T22:48:40.224632Z PROPS-END Node-path: trunk/changed Node-kind: file Node-action: change Text-content-length: 16 Text-content-md5: 1725f40a29aad369a39b0f96c82d50f9 Content-length: 16 changed changed Node-path: trunk/changeddir/f Node-kind: file Node-action: change Text-content-length: 18 Text-content-md5: 984b8c4ab9193b7659b9f914897a949c Content-length: 18 changed2 changed2 Node-path: trunk/groupdir/a Node-kind: file Node-action: change Text-content-length: 4 Text-content-md5: 0d227f1abf8c2932d342e9b99cc957eb Content-length: 4 a a Node-path: trunk/deleteddir Node-action: delete Node-path: trunk/deletedfile Node-action: delete Revision-number: 4 Prop-content-length: 116 Content-length: 116 K 7 svn:log V 14 create branch1 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-05T22:48:42.184704Z PROPS-END Node-path: branches/branch1 Node-kind: dir Node-action: add Node-copyfrom-rev: 1 Node-copyfrom-path: trunk Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Node-path: branches/branch1/a Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/a Text-copy-source-md5: 60b725f10c9c85c70d97880dfe8191b3 Node-path: branches/branch1/b Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/b Text-copy-source-md5: 3b5d5c3712955042212316173ccf37be Node-path: branches/branch1/changed Node-kind: file Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: trunk/changed Text-copy-source-md5: 1725f40a29aad369a39b0f96c82d50f9 Node-path: branches/branch1/changeddir Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/changeddir Node-path: branches/branch1/changeddir/f Node-kind: file Node-action: delete Node-path: branches/branch1/changeddir/f Node-kind: file Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: trunk/changeddir/f Text-copy-source-md5: 984b8c4ab9193b7659b9f914897a949c Node-path: branches/branch1/da Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/da Node-path: branches/branch1/groupdir Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/groupdir Node-path: branches/branch1/groupdir/a Node-kind: file Node-action: delete Node-path: branches/branch1/groupdir/a Node-kind: file Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: trunk/groupdir/a Text-copy-source-md5: 0d227f1abf8c2932d342e9b99cc957eb Node-path: branches/branch1/unchanged Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/unchanged Text-copy-source-md5: 85ae5b04dd0a666efad8633d142a4635 Node-path: branches/branch1/unchangeddir Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/unchangeddir Revision-number: 5 Prop-content-length: 106 Content-length: 106 K 7 svn:log V 5 add c K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-05T22:48:43.175723Z PROPS-END Node-path: branches/branch1/c Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 2cd6ee2c70b0bde53fbe6cac3c8b8bb1 Content-length: 12 PROPS-END c Revision-number: 6 Prop-content-length: 129 Content-length: 129 K 7 svn:log V 27 rename and copy a, b and da K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-05T22:48:50.200094Z PROPS-END Node-path: branches/branch1/c1 Node-kind: file Node-action: add Node-copyfrom-rev: 5 Node-copyfrom-path: branches/branch1/c Text-copy-source-md5: 2cd6ee2c70b0bde53fbe6cac3c8b8bb1 Prop-content-length: 34 Text-content-length: 4 Text-content-md5: 63fad9092ad37713ebe26b3193f89c41 Content-length: 38 K 13 svn:mergeinfo V 0 PROPS-END c c Node-path: trunk/a1 Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/a Text-copy-source-md5: 60b725f10c9c85c70d97880dfe8191b3 Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Node-path: trunk/a2 Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/a Text-copy-source-md5: 60b725f10c9c85c70d97880dfe8191b3 Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Node-path: trunk/b Node-kind: file Node-action: change Text-content-length: 4 Text-content-md5: 06ac26ed8b614fc0b141e4542aa067c2 Content-length: 4 b b Node-path: trunk/b1 Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/b Text-copy-source-md5: 3b5d5c3712955042212316173ccf37be Prop-content-length: 34 Text-content-length: 4 Text-content-md5: 33cb6785d50937d8d307ebb66d6259a7 Content-length: 38 K 13 svn:mergeinfo V 0 PROPS-END b c Node-path: trunk/da1 Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/da Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Node-path: trunk/da2 Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/da Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Node-path: trunk/a Node-action: delete Node-path: trunk/da Node-action: delete Revision-number: 7 Prop-content-length: 121 Content-length: 121 K 7 svn:log V 19 copy b from branch1 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-05T22:48:52.154125Z PROPS-END Node-path: trunk/c Node-kind: file Node-action: add Node-copyfrom-rev: 5 Node-copyfrom-path: branches/branch1/c Text-copy-source-md5: 2cd6ee2c70b0bde53fbe6cac3c8b8bb1 Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Revision-number: 8 Prop-content-length: 126 Content-length: 126 K 7 svn:log V 24 copy stuff from the past K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-05T22:48:55.160439Z PROPS-END Node-path: trunk/deleteddir Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/deleteddir Node-path: trunk/deletedfile Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/deletedfile Text-copy-source-md5: 4d742b2f247bec99b41a60acbebc149a Revision-number: 9 Prop-content-length: 140 Content-length: 140 K 7 svn:log V 38 copy stuff from the past before change K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-05T22:48:59.163460Z PROPS-END Node-path: trunk/changed2 Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/changed Text-copy-source-md5: ec1bebaea2c042beb68f7679ddd106a4 Node-path: trunk/changed3 Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/changed Text-copy-source-md5: ec1bebaea2c042beb68f7679ddd106a4 Text-content-length: 17 Text-content-md5: 7d93e8c4d61c2a7b05c20b7d8bf11f83 Content-length: 17 changed changed3 Node-path: trunk/changeddir2 Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/changeddir Revision-number: 10 Prop-content-length: 136 Content-length: 136 K 7 svn:log V 34 copy unchanged stuff from the past K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-05T22:49:02.160163Z PROPS-END Node-path: trunk/unchanged2 Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/unchanged Text-copy-source-md5: 85ae5b04dd0a666efad8633d142a4635 Node-path: trunk/unchangeddir2 Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/unchangeddir Revision-number: 11 Prop-content-length: 129 Content-length: 129 K 7 svn:log V 27 copy groupdir from the past K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-05T22:49:04.157303Z PROPS-END Node-path: trunk/groupdir2 Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/groupdir durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/renametagdir.sh0000755000000000000000000000145612043462603023046 0ustar 00000000000000#!/bin/sh # inspired by Python r62868 mkdir temp cd temp svnadmin create repo svn co file://`pwd`/repo wc export REPO=file://`pwd`/repo cd wc mkdir branches trunk tags svn add * svn ci -m 'btt' echo a > trunk/a svn add trunk/a svn ci -m 'Add file.' svn up svn cp trunk branches/test svn ci -m 'Branch.' svn up cd .. svn cp -m 'First tag.' $REPO/branches/test@3 $REPO/tags/test-0.1 svn cp -m 'Weird tag.' $REPO/branches/test@3 $REPO/tags/test-0.1/test svn mv -m 'Fix tag pt 1.' $REPO/tags/test-0.1/test $REPO/tags/test-0.1-real svn rm -m 'Remove weird.' $REPO/tags/test-0.1 svn mv -m 'Fix tag pt 2.' $REPO/tags/test-0.1-real $REPO/tags/test-0.1 cd .. svnadmin dump temp/repo > renametagdir.svndump echo echo 'Complete.' echo 'You probably want to clean up temp now.' echo 'Dump in renametagdir.svndump' exit 0 durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/renametagdir.svndump0000644000000000000000000000553412043462603024126 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: b3e24de6-f874-406f-9561-e48db081ff5e Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2010-01-20T12:58:05.107637Z PROPS-END Revision-number: 1 Prop-content-length: 104 Content-length: 104 K 7 svn:log V 3 btt K 10 svn:author V 7 pmezard K 8 svn:date V 27 2010-01-20T12:58:06.111903Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 110 Content-length: 110 K 7 svn:log V 9 Add file. K 10 svn:author V 7 pmezard K 8 svn:date V 27 2010-01-20T12:58:07.088858Z PROPS-END Node-path: trunk/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Text-content-sha1: 3f786850e387550fdab836ed7e6dc881de23001b Content-length: 12 PROPS-END a Revision-number: 3 Prop-content-length: 108 Content-length: 108 K 7 svn:log V 7 Branch. K 10 svn:author V 7 pmezard K 8 svn:date V 27 2010-01-20T12:58:10.062719Z PROPS-END Node-path: branches/test Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk Revision-number: 4 Prop-content-length: 112 Content-length: 112 K 7 svn:log V 10 First tag. K 10 svn:author V 7 pmezard K 8 svn:date V 27 2010-01-20T12:58:12.060717Z PROPS-END Node-path: tags/test-0.1 Node-kind: dir Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: branches/test Revision-number: 5 Prop-content-length: 112 Content-length: 112 K 7 svn:log V 10 Weird tag. K 10 svn:author V 7 pmezard K 8 svn:date V 27 2010-01-20T12:58:12.110400Z PROPS-END Node-path: tags/test-0.1/test Node-kind: dir Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: branches/test Revision-number: 6 Prop-content-length: 115 Content-length: 115 K 7 svn:log V 13 Fix tag pt 1. K 10 svn:author V 7 pmezard K 8 svn:date V 27 2010-01-20T12:58:12.169328Z PROPS-END Node-path: tags/test-0.1/test Node-action: delete Node-path: tags/test-0.1-real Node-kind: dir Node-action: add Node-copyfrom-rev: 5 Node-copyfrom-path: tags/test-0.1/test Revision-number: 7 Prop-content-length: 115 Content-length: 115 K 7 svn:log V 13 Remove weird. K 10 svn:author V 7 pmezard K 8 svn:date V 27 2010-01-20T12:58:12.212355Z PROPS-END Node-path: tags/test-0.1 Node-action: delete Revision-number: 8 Prop-content-length: 115 Content-length: 115 K 7 svn:log V 13 Fix tag pt 2. K 10 svn:author V 7 pmezard K 8 svn:date V 27 2010-01-20T12:58:12.317421Z PROPS-END Node-path: tags/test-0.1 Node-kind: dir Node-action: add Node-copyfrom-rev: 7 Node-copyfrom-path: tags/test-0.1-real Node-path: tags/test-0.1-real Node-action: delete durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/replace_branch_with_branch.sh0000755000000000000000000000251412043462603025700 0ustar 00000000000000#!/bin/sh RSVN="`pwd`/rsvn.py" export PATH=/bin:/usr/bin mkdir temp cd temp svnadmin create repo svn co file://`pwd`/repo wc cd wc mkdir trunk branches cd trunk echo a > a cd .. svn add * svn ci -m 'initial' svn up svn cp trunk branches/branch1 svn ci -m 'branch1' svn up echo b > branches/branch1/b echo d > branches/branch1/d mkdir branches/branch1/dir echo e > branches/branch1/dir/e echo f > branches/branch1/f echo g > branches/branch1/g svn add branches/branch1/b branches/branch1/d branches/branch1/dir \ branches/branch1/f branches/branch1/g svn ci -m 'add b to branch1' svn cp trunk branches/branch2 svn ci -m 'branch2' svn up echo c > branches/branch2/c mkdir branches/branch2/dir echo e2 > branches/branch2/dir/e echo f2 > branches/branch2/f svn add branches/branch2/c branches/branch2/dir branches/branch2/f svn ci -m 'add c to branch2' svn up # Clobber branch1 with branch2 cd .. cat > clobber.rsvn < ../replace_branch_with_branch.svndump durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/replace_branch_with_branch.svndump0000644000000000000000000001274112043462603026762 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 6c4a9db2-8b38-4988-bc82-df1f3bb0afcd Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2010-03-01T23:13:19.505202Z PROPS-END Revision-number: 1 Prop-content-length: 108 Content-length: 108 K 7 svn:log V 7 initial K 10 svn:author V 7 pmezard K 8 svn:date V 27 2010-03-01T23:13:20.066689Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Text-content-sha1: 3f786850e387550fdab836ed7e6dc881de23001b Content-length: 12 PROPS-END a Revision-number: 2 Prop-content-length: 108 Content-length: 108 K 7 svn:log V 7 branch1 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2010-03-01T23:13:23.056920Z PROPS-END Node-path: branches/branch1 Node-kind: dir Node-action: add Node-copyfrom-rev: 1 Node-copyfrom-path: trunk Revision-number: 3 Prop-content-length: 118 Content-length: 118 K 7 svn:log V 16 add b to branch1 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2010-03-01T23:13:25.099593Z PROPS-END Node-path: branches/branch1/b Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 3b5d5c3712955042212316173ccf37be Text-content-sha1: 89e6c98d92887913cadf06b2adb97f26cde4849b Content-length: 12 PROPS-END b Node-path: branches/branch1/d Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: e29311f6f1bf1af907f9ef9f44b8328b Text-content-sha1: e983f374794de9c64e3d1c1de1d490c0756eeeff Content-length: 12 PROPS-END d Node-path: branches/branch1/dir Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: branches/branch1/dir/e Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 9ffbf43126e33be52cd2bf7e01d627f9 Text-content-sha1: 094e3afb2fe8dfe82f63731cdcd3b999f4856cff Content-length: 12 PROPS-END e Node-path: branches/branch1/f Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 9a8ad92c50cae39aa2c5604fd0ab6d8c Text-content-sha1: a9fcd54b25e7e863d72cd47c08af46e61b74b561 Content-length: 12 PROPS-END f Node-path: branches/branch1/g Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: f5302386464f953ed581edac03556e55 Text-content-sha1: a5938ace3f424be1a26904781cdb06d55b614e6b Content-length: 12 PROPS-END g Revision-number: 4 Prop-content-length: 108 Content-length: 108 K 7 svn:log V 7 branch2 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2010-03-01T23:13:27.051018Z PROPS-END Node-path: branches/branch2 Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk Revision-number: 5 Prop-content-length: 118 Content-length: 118 K 7 svn:log V 16 add c to branch2 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2010-03-01T23:13:29.108780Z PROPS-END Node-path: branches/branch2/c Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 2cd6ee2c70b0bde53fbe6cac3c8b8bb1 Text-content-sha1: 2b66fd261ee5c6cfc8de7fa466bab600bcfe4f69 Content-length: 12 PROPS-END c Node-path: branches/branch2/dir Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: branches/branch2/dir/e Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 3 Text-content-md5: c18e1214a2500c0c3636092222f8a850 Text-content-sha1: 4a078b075f259063b9a730521378f70ec3223238 Content-length: 13 PROPS-END e2 Node-path: branches/branch2/f Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 3 Text-content-md5: 575c5638d60271457e54ab7d07309502 Text-content-sha1: 1c49a440c352f3473efa9512255033b94dc7def0 Content-length: 13 PROPS-END f2 Revision-number: 6 Prop-content-length: 102 Content-length: 102 K 7 svn:log V 4 blah K 10 svn:author V 4 evil K 8 svn:date V 27 2010-03-01T23:13:31.520483Z PROPS-END Node-path: branches/branch1 Node-kind: dir Node-action: delete Node-path: branches/branch1 Node-kind: dir Node-action: add Node-copyfrom-rev: 5 Node-copyfrom-path: branches/branch2 Node-path: branches/branch1/a Node-kind: file Node-action: delete Node-path: branches/branch1/a Node-kind: file Node-action: add Node-copyfrom-rev: 5 Node-copyfrom-path: branches/branch1/d Text-copy-source-md5: e29311f6f1bf1af907f9ef9f44b8328b Text-copy-source-sha1: e983f374794de9c64e3d1c1de1d490c0756eeeff Node-path: branches/branch1/dir Node-kind: dir Node-action: delete Node-path: branches/branch1/dir Node-kind: dir Node-action: add Node-copyfrom-rev: 5 Node-copyfrom-path: branches/branch1/dir Node-path: branches/branch1/dir2 Node-kind: dir Node-action: add Node-copyfrom-rev: 5 Node-copyfrom-path: branches/branch1/dir Node-path: branches/branch1/f Node-kind: file Node-action: delete Node-path: branches/branch1/f Node-kind: file Node-action: add Node-copyfrom-rev: 5 Node-copyfrom-path: branches/branch1/f Text-copy-source-md5: 9a8ad92c50cae39aa2c5604fd0ab6d8c Text-copy-source-sha1: a9fcd54b25e7e863d72cd47c08af46e61b74b561 Node-path: branches/branch1/g Node-kind: file Node-action: add Node-copyfrom-rev: 5 Node-copyfrom-path: branches/branch1/g Text-copy-source-md5: f5302386464f953ed581edac03556e55 Text-copy-source-sha1: a5938ace3f424be1a26904781cdb06d55b614e6b durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/replace_trunk.sh0000755000000000000000000000105212043462603023232 0ustar 00000000000000#!/bin/sh RSVN="`pwd`/rsvn.py" export PATH=/bin:/usr/bin mkdir temp cd temp svnadmin create repo svn co file://`pwd`/repo wc cd wc mkdir trunk branches cd trunk for a in alpha beta gamma ; do echo $a > $a done cd .. svn add * svn ci -m 'initial' svn up svn cp trunk branches/test svn ci -m 'branch' svn up echo foo >> branches/test/alpha svn ci -m 'Mod.' cd .. echo rdelete trunk > tmp echo rcopy branches/test trunk >> tmp python $RSVN --message=blah --username=evil `pwd`/repo < tmp svnadmin dump repo > ../replace_trunk_with_branch.svndump durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/replace_trunk_with_branch.svndump0000644000000000000000000000406712043462603026672 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 5b65bade-98f3-4993-a01f-b7a6710da339 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-12-16T16:37:53.651472Z PROPS-END Revision-number: 1 Prop-content-length: 106 Content-length: 106 K 7 svn:log V 7 initial K 10 svn:author V 5 Augie K 8 svn:date V 27 2008-12-16T16:37:54.139835Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 9f9f90dbe3e5ee1218c86b8839db1995 Content-length: 16 PROPS-END alpha Node-path: trunk/beta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 5 Text-content-md5: f0cf2a92516045024a0c99147b28f05b Content-length: 15 PROPS-END beta Node-path: trunk/gamma Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 303febb9068384eca46b5b6516843b35 Content-length: 16 PROPS-END gamma Revision-number: 2 Prop-content-length: 105 Content-length: 105 K 7 svn:log V 6 branch K 10 svn:author V 5 Augie K 8 svn:date V 27 2008-12-16T16:37:57.116844Z PROPS-END Node-path: branches/test Node-kind: dir Node-action: add Node-copyfrom-rev: 1 Node-copyfrom-path: trunk Revision-number: 3 Prop-content-length: 103 Content-length: 103 K 7 svn:log V 4 Mod. K 10 svn:author V 5 Augie K 8 svn:date V 27 2008-12-16T16:37:59.118952Z PROPS-END Node-path: branches/test/alpha Node-kind: file Node-action: change Text-content-length: 10 Text-content-md5: 54add8695a828f4e66224b1ecfd9c576 Content-length: 10 alpha foo Revision-number: 4 Prop-content-length: 102 Content-length: 102 K 7 svn:log V 4 blah K 10 svn:author V 4 evil K 8 svn:date V 27 2008-12-16T16:38:00.236345Z PROPS-END Node-path: trunk Node-kind: dir Node-action: delete Node-path: trunk Node-kind: dir Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: branches/test durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/revert.sh0000755000000000000000000000117212043462603021706 0ustar 00000000000000#!/bin/sh # # Generate revert.svndump # rm -rf temp mkdir temp cd temp mkdir -p import/trunk/dir cd import/trunk echo a > a echo b > dir/b cd ../.. svnadmin create testrepo svnurl=file://`pwd`/testrepo svn import import $svnurl -m init svn co $svnurl project cd project echo a >> trunk/a echo b >> trunk/dir/b svn ci -m changefiles svn up # Test directory revert svn rm trunk svn cp $svnurl/trunk@1 trunk svn st svn ci -m revert svn up # Test file revert svn rm trunk/a svn rm trunk/dir/b svn cp $svnurl/trunk/a@2 trunk/a svn cp $svnurl/trunk/dir/b@2 trunk/dir/b svn ci -m revert2 cd .. svnadmin dump testrepo > ../revert.svndump durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/revert.svndump0000644000000000000000000000530312043462603022765 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 307f02f4-2d74-44cb-98a4-4e162241d396 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2012-10-06T08:50:46.559327Z PROPS-END Revision-number: 1 Prop-content-length: 105 Content-length: 105 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2012-10-06T08:50:46.581582Z K 7 svn:log V 4 init PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Text-content-sha1: 3f786850e387550fdab836ed7e6dc881de23001b Content-length: 12 PROPS-END a Node-path: trunk/dir Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/dir/b Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 3b5d5c3712955042212316173ccf37be Text-content-sha1: 89e6c98d92887913cadf06b2adb97f26cde4849b Content-length: 12 PROPS-END b Revision-number: 2 Prop-content-length: 113 Content-length: 113 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2012-10-06T08:50:47.048033Z K 7 svn:log V 11 changefiles PROPS-END Node-path: trunk/a Node-kind: file Node-action: change Text-content-length: 4 Text-content-md5: 0d227f1abf8c2932d342e9b99cc957eb Text-content-sha1: d7c8127a20a396cff08af086a1c695b0636f0c29 Content-length: 4 a a Node-path: trunk/dir/b Node-kind: file Node-action: change Text-content-length: 4 Text-content-md5: 06ac26ed8b614fc0b141e4542aa067c2 Text-content-sha1: f6980469e74f7125178e88ec571e06fe6ce86e95 Content-length: 4 b b Revision-number: 3 Prop-content-length: 107 Content-length: 107 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2012-10-06T08:50:50.058224Z K 7 svn:log V 6 revert PROPS-END Node-path: trunk Node-kind: dir Node-action: delete Node-path: trunk Node-kind: dir Node-action: add Node-copyfrom-rev: 1 Node-copyfrom-path: trunk Revision-number: 4 Prop-content-length: 108 Content-length: 108 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2012-10-06T08:50:54.047396Z K 7 svn:log V 7 revert2 PROPS-END Node-path: trunk/a Node-kind: file Node-action: delete Node-path: trunk/a Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/a Text-copy-source-md5: 0d227f1abf8c2932d342e9b99cc957eb Text-copy-source-sha1: d7c8127a20a396cff08af086a1c695b0636f0c29 Node-path: trunk/dir/b Node-kind: file Node-action: delete Node-path: trunk/dir/b Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/dir/b Text-copy-source-md5: 06ac26ed8b614fc0b141e4542aa067c2 Text-copy-source-sha1: f6980469e74f7125178e88ec571e06fe6ce86e95 durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/rsvn.py0000755000000000000000000002003512043462603021404 0ustar 00000000000000#!/usr/bin/env python2 # LICENSE # # Copyright (c) 2004, Francois Beausoleil # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # * Neither the name of the Francois Beausoleil nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import getopt import sys import os import re import traceback from svn import core, repos, fs VERSION='$Id: rsvn.py 20 2005-09-23 15:08:08Z fbos $' RCOPY_RE = re.compile('^\s*rcopy\s+(.+)\s+(.+)$') RMOVE_RE = re.compile('^\s*rmove\s+(.+)\s+(.+)$') RMKDIR_RE = re.compile('^\s*rmkdir\s+(.+)$') RDELETE_RE = re.compile('^\s*rdelete\s+(.+)$') COMMENT_RE = re.compile('(?:^\s*#)|(?:^\s*$)') def usage(error=None): if error: print 'Error: %s\n' % error print 'USAGE: %s --message=MESSAGE repos_path [--username=USERNAME]' % ( os.path.basename(sys.argv[0])) print '' print ' --help, -h print this usage message and exit with success' print ' --version print the version number' print ' --username=USERNAME Username to execute the commands under' print ' --message=LOG_MSG Log message to execute the commit with' print '' print 'Reads STDIN and parses the following commands, to execute them on the server, ' print 'all within the same transaction:' print '' print ' rcopy SRC DEST Copy the HEAD revision of a file or folder' print ' rmove SRC DEST Copy + delete the HEAD revision of a file or folder' print ' rdelete TARGET Deletes something from the repository' print ' rmkdir TARGET Creates a new folder (must create parents first)' print ' # Initiates a comment' print '' # 12345678901234567890123456789012345678901234567890123456789012345678901234567890 class Transaction: """Represents a transaction in a Subversion repository Transactions are long-lived objects which exist in the repository, and are used to build an intermediate representation of a new revision. Once the transaction is committed, the repository bumps the revision number, and links the new transaction in the Subversion filesystem.""" def __init__(self, repository, rev, username, message, pool, logger=None): if logger: self.logger = logger else: self.logger = sys.stdout self.pool = pool self.rev = rev self.fsptr = repos.svn_repos_fs(repository) self.rev_root = fs.revision_root(self.fsptr, self.rev, self.pool) self.txnp = repos.svn_repos_fs_begin_txn_for_commit( repository, self.rev, username, message, self.pool) self.txn_root = fs.txn_root(self.txnp, self.pool) self.log('Base revision %d\n' % rev) def commit(self): values = fs.commit_txn(self.txnp, self.pool) return values[1] def rollback(self): fs.abort_txn(self.txnp, self.pool) def copy(self, src, dest, subpool): self.log('A + %s\n' % dest) fs.copy(self.rev_root, src, self.txn_root, dest, subpool) def delete(self, entry, subpool): self.log('D %s\n' % entry) fs.delete(self.txn_root, entry, subpool) def mkdir(self, entry, subpool): self.log('A %s\n' % entry) fs.make_dir(self.txn_root, entry, subpool) def move(self, src, dest, subpool): self.copy(src, dest, subpool) self.delete(src, subpool) def log(self, msg): self.logger.write(msg) class Repository: """Represents a Subversion repository, and allows common operations on it.""" def __init__(self, repos_path, pool, logger=None): if logger: self.logger = logger else: self.logger = sys.stdout self.pool = pool assert self.pool self.repo = repos.svn_repos_open(repos_path, self.pool) self.fsptr = repos.svn_repos_fs(self.repo) def get_youngest(self): """Returns the youngest revision in the repository.""" return fs.youngest_rev(self.fsptr, self.pool) def begin(self, username, log_msg): """Initiate a new Transaction""" return Transaction(self.repo, self.get_youngest(), username, log_msg, self.pool, self.logger) def close(self): """Close the repository, aborting any uncommitted transactions""" core.svn_pool_destroy(self.pool) core.apr_terminate() def subpool(self): """Instantiates a new pool from the master pool""" return core.svn_pool_create(self.pool) def delete_pool(self, pool): """Deletes the passed-in pool. Returns None, to assign to pool in caller.""" core.svn_pool_destroy(pool) return None def rsvn(pool): log_msg = None try: opts, args = getopt.getopt(sys.argv[1:], 'vh', ["help", "username=", "message=", "version"]) except getopt.GetoptError, e: sys.stderr.write(str(e) + '\n\n') usage() sys.exit(1) for opt, value in opts: if opt == '--version': print '%s version %s' % (os.path.basename(sys.argv[0]), VERSION) sys.exit(0) elif opt == '--help' or opt == '-h': usage() sys.exit(0) elif opt == '--username': username = value elif opt == '--message': log_msg = value if log_msg == None: usage('Missing --message argument') sys.exit(1) if len(args) != 1: usage('Missing repository path argument') sys.exit(1) repos_path = args[0] print 'Accessing repository at [%s]' % repos_path repository = Repository(repos_path, pool) sub = repository.subpool() try: txn = repository.begin(username, log_msg) # Read commands from STDIN lineno = 0 for line in sys.stdin: lineno += 1 core.svn_pool_clear(sub) try: if COMMENT_RE.search(line): continue match = RCOPY_RE.search(line) if match: src = match.group(1) dest = match.group(2) txn.copy(src, dest, sub) continue match = RMOVE_RE.search(line) if match: src = match.group(1) dest = match.group(2) txn.move(src, dest, sub) continue match = RMKDIR_RE.search(line) if match: entry = match.group(1) txn.mkdir(entry, sub) continue match = RDELETE_RE.search(line) if match: entry = match.group(1) txn.delete(entry, sub) continue raise NameError, ('Unknown command [%s] on line %d' % (line, lineno)) except: sys.stderr.write(('Exception occured while processing line %d:\n' % lineno)) etype, value, tb = sys.exc_info() traceback.print_exception(etype, value, tb, None, sys.stderr) sys.stderr.write('\n') txn.rollback() sys.exit(1) new_rev = txn.commit() print '\nCommitted revision %d.' % new_rev finally: print '\nRepository closed.' def main(): core.run_app(rsvn) if __name__ == '__main__': main() durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/siblingbranchfix.sh0000755000000000000000000000104712043462603023714 0ustar 00000000000000#!/bin/sh mkdir temp cd temp svnadmin create repo svn co file://`pwd`/repo wc cd wc mkdir branches trunk tags svn add * svn ci -m 'btt' cd trunk echo a > a svn add a svn ci -m 'Add file.' svn up cd .. svn cp trunk wrongbranch svn ci -m 'Branch to repo root dir.' svn up svn mv wrongbranch branches/wrongbranch svn ci -m 'Move branch to correct branches location' svn up cd ../.. svnadmin dump temp/repo > siblingbranchfix.svndump echo echo 'Complete.' echo 'You probably want to clean up temp now.' echo 'Dump in siblingbranchfix.svndump' exit 0 durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/siblingbranchfix.svndump0000644000000000000000000000333212043462603024772 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 380991ed-036e-45db-832e-7a8ca661c4f0 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2009-09-19T07:20:51.801386Z PROPS-END Revision-number: 1 Prop-content-length: 100 Content-length: 100 K 7 svn:log V 3 btt K 10 svn:author V 3 djc K 8 svn:date V 27 2009-09-19T07:20:52.057610Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 106 Content-length: 106 K 7 svn:log V 9 Add file. K 10 svn:author V 3 djc K 8 svn:date V 27 2009-09-19T07:20:53.056417Z PROPS-END Node-path: trunk/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Text-content-sha1: 3f786850e387550fdab836ed7e6dc881de23001b Content-length: 12 PROPS-END a Revision-number: 3 Prop-content-length: 122 Content-length: 122 K 7 svn:log V 24 Branch to repo root dir. K 10 svn:author V 3 djc K 8 svn:date V 27 2009-09-19T07:20:56.043593Z PROPS-END Node-path: wrongbranch Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk Revision-number: 4 Prop-content-length: 138 Content-length: 138 K 7 svn:log V 40 Move branch to correct branches location K 10 svn:author V 3 djc K 8 svn:date V 27 2009-09-19T07:20:59.045512Z PROPS-END Node-path: branches/wrongbranch Node-kind: dir Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: wrongbranch Node-path: wrongbranch Node-action: delete durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/simple_branch.sh0000755000000000000000000000114512043462603023205 0ustar 00000000000000#!/bin/sh # # Generate simple_branch.svndump # mkdir temp cd temp mkdir project-orig cd project-orig mkdir trunk branches tags cd .. svnadmin create testrepo svnurl=file://`pwd`/testrepo svn import --username durin project-orig $svnurl -m "Empty dirs." svn co $svnurl project cd project echo 'file: alpha' > trunk/alpha svn add trunk/alpha svn ci --username durin -m 'Add alpha' echo 'Data of beta' > trunk/beta svn add trunk/beta svn ci --username durin -m 'Add beta' svn up svn cp trunk branches/the_branch svn ci --username durin -m 'Make a branch' cd .. svnadmin dump testrepo > ../simple_branch.svndump durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/simple_branch.svndump0000644000000000000000000000346512043462603024273 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 3cd547df-371e-4add-bccf-aba732a2baf5 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2010-02-02T21:54:11.642093Z PROPS-END Revision-number: 1 Prop-content-length: 111 Content-length: 111 K 7 svn:log V 11 Empty dirs. K 10 svn:author V 5 durin K 8 svn:date V 27 2010-02-02T21:54:11.673761Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 108 Content-length: 108 K 7 svn:log V 9 Add alpha K 10 svn:author V 5 durin K 8 svn:date V 27 2010-02-02T21:54:12.073120Z PROPS-END Node-path: trunk/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 12 Text-content-md5: 3c72ebf8bbd7fa88b1fdcee5398b5a17 Text-content-sha1: f552a50b53177d35b29a4a0ab1cece918b5b5e9b Content-length: 22 PROPS-END file: alpha Revision-number: 3 Prop-content-length: 107 Content-length: 107 K 7 svn:log V 8 Add beta K 10 svn:author V 5 durin K 8 svn:date V 27 2010-02-02T21:54:13.096862Z PROPS-END Node-path: trunk/beta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 13 Text-content-md5: 981d1eb5fd0bbe05354c292105944863 Text-content-sha1: 5d40e0a9ceda69f3d98d4851a6bee02c10a6e277 Content-length: 23 PROPS-END Data of beta Revision-number: 4 Prop-content-length: 113 Content-length: 113 K 7 svn:log V 13 Make a branch K 10 svn:author V 5 durin K 8 svn:date V 27 2010-02-02T21:54:16.042325Z PROPS-END Node-path: branches/the_branch Node-kind: dir Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: trunk durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/single_rev.svndump0000644000000000000000000000204612043462603023614 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: df2126f7-00ab-4d49-b42c-7e981dde0bcf Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-10-07T22:49:12.059692Z PROPS-END Revision-number: 1 Prop-content-length: 111 Content-length: 111 K 7 svn:log V 11 Empty dirs. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T22:49:41.118037Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 108 Content-length: 108 K 7 svn:log V 9 Add alpha K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T23:23:02.991743Z PROPS-END Node-path: trunk/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 12 Text-content-md5: 3c72ebf8bbd7fa88b1fdcee5398b5a17 Content-length: 22 PROPS-END file: alpha durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/spaces-in-path.sh0000755000000000000000000000127612043462603023220 0ustar 00000000000000#!/bin/sh mkdir temp cd temp svnadmin create repo svn co file://`pwd`/repo wc cd wc mkdir branches trunk tags mkdir tags/versions mkdir tags/blah svn add * svn ci -m 'btt' cd trunk for a in alpha beta gamma delta ; do echo $a > $a svn add $a done svn ci -m 'Add files.' echo 'foo bar' > 'foo bar' svn add 'foo bar' svn ci -m 'Add files.' mkdir 'blah blah' echo 'another file' > 'blah blah/another file' svn add 'blah blah' svn ci -m 'Add files.' cd .. svn up svn cp trunk branches/dev_branch svn ci -m 'Make a branch' cd ../.. svnadmin dump temp/repo > spaces-in-path.svndump echo echo 'Complete.' echo 'You probably want to clean up temp now.' echo 'Dump in spaces-in-path.svndump' exit 0 durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/spaces-in-path.svndump0000644000000000000000000000645712043462603024305 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: fbe42c69-30e8-40e8-8707-cf3ddc79d968 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2009-09-16T01:53:29.172947Z PROPS-END Revision-number: 1 Prop-content-length: 102 Content-length: 102 K 7 svn:log V 3 btt K 10 svn:author V 5 augie K 8 svn:date V 27 2009-09-16T01:53:30.119780Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags/blah Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags/versions Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 110 Content-length: 110 K 7 svn:log V 10 Add files. K 10 svn:author V 5 augie K 8 svn:date V 27 2009-09-16T01:53:31.149862Z PROPS-END Node-path: trunk/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 9f9f90dbe3e5ee1218c86b8839db1995 Text-content-sha1: d046cd9b7ffb7661e449683313d41f6fc33e3130 Content-length: 16 PROPS-END alpha Node-path: trunk/beta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 5 Text-content-md5: f0cf2a92516045024a0c99147b28f05b Text-content-sha1: 6c007a14875d53d9bf0ef5a6fc0257c817f0fb83 Content-length: 15 PROPS-END beta Node-path: trunk/delta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: d2840cc81bc032bd1141b56687d0f93c Text-content-sha1: 4bd6315d6d7824c4e376847ca7d116738ad2f29a Content-length: 16 PROPS-END delta Node-path: trunk/gamma Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 303febb9068384eca46b5b6516843b35 Text-content-sha1: 37f385b028bf2f93a4b497ca9ff44eea63945b7f Content-length: 16 PROPS-END gamma Revision-number: 3 Prop-content-length: 110 Content-length: 110 K 7 svn:log V 10 Add files. K 10 svn:author V 5 augie K 8 svn:date V 27 2009-09-16T01:53:32.079732Z PROPS-END Node-path: trunk/foo bar Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 8 Text-content-md5: 5ceaa7ed396ccb8e959c02753cb4bd18 Text-content-sha1: d53a205a336e07cf9eac45471b3870f9489288ec Content-length: 18 PROPS-END foo bar Revision-number: 4 Prop-content-length: 110 Content-length: 110 K 7 svn:log V 10 Add files. K 10 svn:author V 5 augie K 8 svn:date V 27 2009-09-16T01:53:33.091599Z PROPS-END Node-path: trunk/blah blah Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/blah blah/another file Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 13 Text-content-md5: fb319cc56653b713a8c7a54aa92f6efd Text-content-sha1: 98f46145eb13fc9d662fb2d07fe9faf9969be54b Content-length: 23 PROPS-END another file Revision-number: 5 Prop-content-length: 113 Content-length: 113 K 7 svn:log V 13 Make a branch K 10 svn:author V 5 augie K 8 svn:date V 27 2009-09-16T01:53:36.062701Z PROPS-END Node-path: branches/dev_branch Node-kind: dir Node-action: add Node-copyfrom-rev: 4 Node-copyfrom-path: trunk durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/subdir_is_file_prefix.svndump0000644000000000000000000000167412043462603026024 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 924a052a-5e5a-4a8e-a677-da5565bec340 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2011-03-04T12:33:29.342045Z PROPS-END Revision-number: 1 Prop-content-length: 123 Content-length: 123 K 7 svn:log V 22 Create directory flaf. K 10 svn:author V 6 danchr K 8 svn:date V 27 2011-03-04T12:34:00.349950Z PROPS-END Node-path: flaf Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 138 Content-length: 138 K 7 svn:log V 37 Create the file flaf.txt within flaf. K 10 svn:author V 6 danchr K 8 svn:date V 27 2011-03-04T12:45:01.701033Z PROPS-END Node-path: flaf/flaf.txt Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 15 Text-content-md5: 8c0059c8f7998e8003836b8e8fcb74d7 Text-content-sha1: b7d680bc5411f46395c4ef267001e1a307d7b0d5 Content-length: 25 PROPS-END Goodbye world. durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/symlinks.sh0000755000000000000000000000173612043462603022256 0ustar 00000000000000#!/bin/sh # # Generate symlinks.svndump # mkdir temp cd temp mkdir project-orig cd project-orig mkdir trunk mkdir branches cd .. svnadmin create testrepo svnurl=file://`pwd`/testrepo svn import project-orig $svnurl -m "init project" svn co $svnurl project cd project/trunk echo a > a ln -s a linka ln -s a linka2 mkdir d ln -s a d/linka svn add a linka linka2 d svn ci -m "add symlinks" # Move symlinks svn mv linka linkaa svn mv d d2 svn commit -m "moving symlinks" # Update symlinks (test "link " prefix vs applydelta) echo b > b rm linkaa ln -s b linkaa rm d2/linka ln -s b d2/linka svn ci -m "update symlinks" # Replace a symlink with a regular file rm linkaa echo data > linkaa svn propdel svn:special linkaa # Replace another symlink with a regular file in another way # It should mark linka2 as R(eplaced) svn rm linka2 echo data2 > linka2 svn add linka2 svn propdel svn:special linka2 svn rm d2/linka svn ci -m "undo link" cd ../.. svnadmin dump testrepo > ../symlinks.svndump durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/symlinks.svndump0000644000000000000000000001306512043462603023333 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: bcc1a3f4-ccae-49ba-b1c5-003ce86e3e31 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-12-15T21:58:13.685247Z PROPS-END Revision-number: 1 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 12 init project K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-15T21:58:13.747067Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 12 add symlinks K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-15T21:58:14.243223Z PROPS-END Node-path: trunk/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Text-content-sha1: 3f786850e387550fdab836ed7e6dc881de23001b Content-length: 12 PROPS-END a Node-path: trunk/d Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/d/linka Node-kind: file Node-action: add Prop-content-length: 33 Text-content-length: 6 Text-content-md5: c118dba188202a1efc975bef6064180b Text-content-sha1: 41f94e4692313bf7f7c92aa600002f1dff93d6bf Content-length: 39 K 11 svn:special V 1 * PROPS-END link a Node-path: trunk/linka Node-kind: file Node-action: add Prop-content-length: 33 Text-content-length: 6 Text-content-md5: c118dba188202a1efc975bef6064180b Text-content-sha1: 41f94e4692313bf7f7c92aa600002f1dff93d6bf Content-length: 39 K 11 svn:special V 1 * PROPS-END link a Node-path: trunk/linka2 Node-kind: file Node-action: add Prop-content-length: 33 Text-content-length: 6 Text-content-md5: c118dba188202a1efc975bef6064180b Text-content-sha1: 41f94e4692313bf7f7c92aa600002f1dff93d6bf Content-length: 39 K 11 svn:special V 1 * PROPS-END link a Revision-number: 3 Prop-content-length: 117 Content-length: 117 K 7 svn:log V 15 moving symlinks K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-15T21:58:17.162148Z PROPS-END Node-path: trunk/d2 Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/d Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Node-path: trunk/linkaa Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/linka Text-copy-source-md5: c118dba188202a1efc975bef6064180b Text-copy-source-sha1: 41f94e4692313bf7f7c92aa600002f1dff93d6bf Prop-content-length: 57 Content-length: 57 K 11 svn:special V 1 * K 13 svn:mergeinfo V 0 PROPS-END Node-path: trunk/linka Node-action: delete Node-path: trunk/d Node-action: delete Revision-number: 4 Prop-content-length: 117 Content-length: 117 K 7 svn:log V 15 update symlinks K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-15T21:58:18.188491Z PROPS-END Node-path: trunk/d2/linka Node-kind: file Node-action: change Text-content-length: 6 Text-content-md5: e9292b8c4fca95ac8c70b4ae040d0b79 Text-content-sha1: 7325442a5f7383205e66db563025d51535883784 Content-length: 6 link b Node-path: trunk/linkaa Node-kind: file Node-action: change Text-content-length: 6 Text-content-md5: e9292b8c4fca95ac8c70b4ae040d0b79 Text-content-sha1: 7325442a5f7383205e66db563025d51535883784 Content-length: 6 link b Revision-number: 5 Prop-content-length: 110 Content-length: 110 K 7 svn:log V 9 undo link K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-15T21:58:19.297689Z PROPS-END Node-path: trunk/d2/linka Node-action: delete Node-path: trunk/linka2 Node-kind: file Node-action: replace Prop-content-length: 10 Text-content-length: 6 Text-content-md5: edc3d3797971f12c7f5e1d106dd5cee2 Text-content-sha1: eff1098d818d1f471af4a2cbdb0223e4e030a158 Content-length: 16 PROPS-END data2 Node-path: trunk/linkaa Node-kind: file Node-action: change Prop-content-length: 34 Text-content-length: 5 Text-content-md5: 6137cde4893c59f76f005a8123d8e8e6 Text-content-sha1: c5d84736ba451747dd5f0eb9d17e104f3697ef47 Content-length: 39 K 13 svn:mergeinfo V 0 PROPS-END data Revision-number: 6 Prop-content-length: 116 Content-length: 116 K 7 svn:log V 15 recreate a link K 10 svn:author V 6 danchr K 8 svn:date V 27 2010-11-08T14:57:19.667088Z PROPS-END Node-path: trunk/linka3 Node-kind: file Node-action: add Prop-content-length: 59 Text-content-length: 6 Text-content-md5: c118dba188202a1efc975bef6064180b Text-content-sha1: 41f94e4692313bf7f7c92aa600002f1dff93d6bf Content-length: 65 K 11 svn:special V 1 * K 14 svn:needs-lock V 1 * PROPS-END link a Revision-number: 7 Prop-content-length: 128 Content-length: 128 K 7 svn:log V 27 delete property from a link K 10 svn:author V 6 danchr K 8 svn:date V 27 2010-11-08T14:57:40.331635Z PROPS-END Node-path: trunk/linka3 Node-kind: file Node-action: change Prop-content-length: 33 Content-length: 33 K 11 svn:special V 1 * PROPS-END Revision-number: 8 Prop-content-length: 145 Content-length: 145 K 7 svn:log V 44 add an uglily named file and a symlink to it K 10 svn:author V 6 danchr K 8 svn:date V 27 2010-11-08T15:11:12.267958Z PROPS-END Node-path: trunk/link to this Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 0 Text-content-md5: d41d8cd98f00b204e9800998ecf8427e Text-content-sha1: da39a3ee5e6b4b0d3255bfef95601890afd80709 Content-length: 10 PROPS-END Node-path: trunk/linka4 Node-kind: file Node-action: add Prop-content-length: 33 Text-content-length: 17 Text-content-md5: aecd1324085a1fc5b7aa8950f92dcfb9 Text-content-sha1: d22de0907c0b1878b057c873092e82fd9c50849b Content-length: 50 K 11 svn:special V 1 * PROPS-END link link to this durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/tag_by_rename_branch.svndump0000644000000000000000000000535212043462603025573 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: df2126f7-00ab-4d49-b42c-7e981dde0bcf Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-10-07T22:49:12.059692Z PROPS-END Revision-number: 1 Prop-content-length: 111 Content-length: 111 K 7 svn:log V 11 Empty dirs. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T22:49:41.118037Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 108 Content-length: 108 K 7 svn:log V 9 Add alpha K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T23:23:02.991743Z PROPS-END Node-path: trunk/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 12 Text-content-md5: 3c72ebf8bbd7fa88b1fdcee5398b5a17 Content-length: 22 PROPS-END file: alpha Revision-number: 3 Prop-content-length: 107 Content-length: 107 K 7 svn:log V 8 Add beta K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T23:28:22.651398Z PROPS-END Node-path: trunk/beta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 13 Text-content-md5: 981d1eb5fd0bbe05354c292105944863 Content-length: 23 PROPS-END Data of beta Revision-number: 4 Prop-content-length: 110 Content-length: 110 K 7 svn:log V 10 tagging r3 K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-08T23:18:29.837825Z PROPS-END Node-path: tags/tag_r3 Node-kind: dir Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: trunk Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Revision-number: 5 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 14 tag from a tag K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-08T23:18:51.091356Z PROPS-END Node-path: tags/copied_tag Node-kind: dir Node-action: add Node-copyfrom-rev: 4 Node-copyfrom-path: tags/tag_r3 Revision-number: 6 Prop-content-length: 113 Content-length: 113 K 7 svn:log V 13 make a branch K 10 svn:author V 5 Augie K 8 svn:date V 27 2008-10-20T21:31:18.706649Z PROPS-END Node-path: branches/dummy Node-kind: dir Node-action: add Node-copyfrom-rev: 5 Node-copyfrom-path: trunk Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Revision-number: 7 Prop-content-length: 118 Content-length: 118 K 7 svn:log V 18 make a tag with mv K 10 svn:author V 5 Augie K 8 svn:date V 27 2008-10-20T21:31:41.138126Z PROPS-END Node-path: branches/dummy Node-action: delete Node-path: tags/dummy Node-kind: dir Node-action: add Node-copyfrom-rev: 6 Node-copyfrom-path: branches/dummy durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/tag_deletion_tag_branch.svndump0000644000000000000000000000537012043462603026270 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: eef88803-8121-4c2d-a584-b15733364666 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2009-05-30T18:12:25.144447Z PROPS-END Revision-number: 1 Prop-content-length: 103 Content-length: 103 K 7 svn:log V 4 init K 10 svn:author V 5 durin K 8 svn:date V 27 2009-05-30T18:13:59.514904Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 18 Text-content-md5: 207dd96a2baf1c1bfe2728ea67da7e37 Content-length: 28 PROPS-END content of alpha. Node-path: trunk/beta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 17 Text-content-md5: 868f9a1cd3f678461c02fee4d8b7d44c Content-length: 27 PROPS-END content of beta. Node-path: trunk/delta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 18 Text-content-md5: cee60c4f880a73eea79fac90547d0142 Content-length: 28 PROPS-END content of delta. Node-path: trunk/gamma Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 18 Text-content-md5: 865ce111ce132951995b1b7b63f0f2dc Content-length: 28 PROPS-END content of gamma. Revision-number: 2 Prop-content-length: 105 Content-length: 105 K 7 svn:log V 6 remove K 10 svn:author V 5 durin K 8 svn:date V 27 2009-05-30T18:14:10.610589Z PROPS-END Node-path: trunk/delta Node-action: delete Revision-number: 3 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 14 trunk at 2 tag K 10 svn:author V 5 durin K 8 svn:date V 27 2009-05-30T18:14:43.562547Z PROPS-END Node-path: tags/trunk_at_2 Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk Revision-number: 4 Prop-content-length: 115 Content-length: 115 K 7 svn:log V 15 branch of trunk K 10 svn:author V 5 durin K 8 svn:date V 27 2009-05-30T18:15:06.771293Z PROPS-END Node-path: branches/from_2 Node-kind: dir Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: trunk Revision-number: 5 Prop-content-length: 106 Content-length: 106 K 7 svn:log V 7 Untag 2 K 10 svn:author V 5 durin K 8 svn:date V 27 2009-05-30T18:15:30.999412Z PROPS-END Node-path: tags/trunk_at_2 Node-action: delete Revision-number: 6 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 14 make a new tag K 10 svn:author V 5 durin K 8 svn:date V 27 2009-05-30T18:16:01.917326Z PROPS-END Node-path: tags/new_tag Node-kind: dir Node-action: add Node-copyfrom-rev: 5 Node-copyfrom-path: branches/from_2 durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/tag_name_same_as_branch.sh0000755000000000000000000000131612043462603025157 0ustar 00000000000000#!/bin/sh mkdir temp cd temp svnadmin create repo REPOPATH="file://`pwd`/repo" svn co $REPOPATH wc cd wc mkdir -p branches/magic trunk tags svn add * svn ci -m 'btt' cd branches/magic for a in alpha beta gamma delta iota zeta eta theta ; do echo $a > $a svn add $a svn ci -m "Add file $a" done cd ../.. svn up svn cp $REPOPATH/branches/magic $REPOPATH/tags/magic -m 'Make magic tag' svn rm $REPOPATH/branches/magic/theta -m 'remove a file' svn cp $REPOPATH/branches/magic $REPOPATH/tags/magic2 -m 'Tag magic again' cd ../.. svnadmin dump temp/repo > tag_name_same_as_branch.svndump echo echo 'Complete.' echo 'You probably want to clean up temp now.' echo 'Dump in tag_name_same_as_branch.svndump' exit 0 durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/tag_name_same_as_branch.svndump0000644000000000000000000001205612043462603026241 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 8ede5731-e772-49cc-a297-c19c9844b692 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2009-06-01T16:09:33.850620Z PROPS-END Revision-number: 1 Prop-content-length: 105 Content-length: 105 K 7 svn:log V 3 btt K 10 svn:author V 8 afackler K 8 svn:date V 27 2009-06-01T16:09:34.084216Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: branches/magic Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 117 Content-length: 117 K 7 svn:log V 14 Add file alpha K 10 svn:author V 8 afackler K 8 svn:date V 27 2009-06-01T16:09:35.078795Z PROPS-END Node-path: branches/magic/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 9f9f90dbe3e5ee1218c86b8839db1995 Text-content-sha1: d046cd9b7ffb7661e449683313d41f6fc33e3130 Content-length: 16 PROPS-END alpha Revision-number: 3 Prop-content-length: 116 Content-length: 116 K 7 svn:log V 13 Add file beta K 10 svn:author V 8 afackler K 8 svn:date V 27 2009-06-01T16:09:36.385254Z PROPS-END Node-path: branches/magic/beta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 5 Text-content-md5: f0cf2a92516045024a0c99147b28f05b Text-content-sha1: 6c007a14875d53d9bf0ef5a6fc0257c817f0fb83 Content-length: 15 PROPS-END beta Revision-number: 4 Prop-content-length: 117 Content-length: 117 K 7 svn:log V 14 Add file gamma K 10 svn:author V 8 afackler K 8 svn:date V 27 2009-06-01T16:09:37.081077Z PROPS-END Node-path: branches/magic/gamma Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 303febb9068384eca46b5b6516843b35 Text-content-sha1: 37f385b028bf2f93a4b497ca9ff44eea63945b7f Content-length: 16 PROPS-END gamma Revision-number: 5 Prop-content-length: 117 Content-length: 117 K 7 svn:log V 14 Add file delta K 10 svn:author V 8 afackler K 8 svn:date V 27 2009-06-01T16:09:38.071461Z PROPS-END Node-path: branches/magic/delta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: d2840cc81bc032bd1141b56687d0f93c Text-content-sha1: 4bd6315d6d7824c4e376847ca7d116738ad2f29a Content-length: 16 PROPS-END delta Revision-number: 6 Prop-content-length: 116 Content-length: 116 K 7 svn:log V 13 Add file iota K 10 svn:author V 8 afackler K 8 svn:date V 27 2009-06-01T16:09:39.073363Z PROPS-END Node-path: branches/magic/iota Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 5 Text-content-md5: ebcf3971120220589f1dfbf8d56e25b9 Text-content-sha1: 47e9aceee5149402971cda8590e9b912c1b1053e Content-length: 15 PROPS-END iota Revision-number: 7 Prop-content-length: 116 Content-length: 116 K 7 svn:log V 13 Add file zeta K 10 svn:author V 8 afackler K 8 svn:date V 27 2009-06-01T16:09:40.082055Z PROPS-END Node-path: branches/magic/zeta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 5 Text-content-md5: 2db8f255a13ae1e49099d9dad57b4a37 Text-content-sha1: 2c5ea36ead157ee3089bcd883f26ea2c899b2521 Content-length: 15 PROPS-END zeta Revision-number: 8 Prop-content-length: 115 Content-length: 115 K 7 svn:log V 12 Add file eta K 10 svn:author V 8 afackler K 8 svn:date V 27 2009-06-01T16:09:41.072675Z PROPS-END Node-path: branches/magic/eta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 4 Text-content-md5: 655d1abbe3e97298b0948ba964ad2522 Text-content-sha1: 3c106a7ec1fe510152eebcf2cc76e135cca63f62 Content-length: 14 PROPS-END eta Revision-number: 9 Prop-content-length: 117 Content-length: 117 K 7 svn:log V 14 Add file theta K 10 svn:author V 8 afackler K 8 svn:date V 27 2009-06-01T16:09:42.080715Z PROPS-END Node-path: branches/magic/theta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 9485edf7b8ec0fdf0bc3bdb9d5fc6c28 Text-content-sha1: dde607ddc995205a6a521f47511bd04fa506e286 Content-length: 16 PROPS-END theta Revision-number: 10 Prop-content-length: 117 Content-length: 117 K 7 svn:log V 14 Make magic tag K 10 svn:author V 8 afackler K 8 svn:date V 27 2009-06-01T16:09:44.048672Z PROPS-END Node-path: tags/magic Node-kind: dir Node-action: add Node-copyfrom-rev: 9 Node-copyfrom-path: branches/magic Revision-number: 11 Prop-content-length: 116 Content-length: 116 K 7 svn:log V 13 remove a file K 10 svn:author V 8 afackler K 8 svn:date V 27 2009-06-01T16:09:44.080801Z PROPS-END Node-path: branches/magic/theta Node-action: delete Revision-number: 12 Prop-content-length: 118 Content-length: 118 K 7 svn:log V 15 Tag magic again K 10 svn:author V 8 afackler K 8 svn:date V 27 2009-06-01T16:09:44.115205Z PROPS-END Node-path: tags/magic2 Node-kind: dir Node-action: add Node-copyfrom-rev: 11 Node-copyfrom-path: branches/magic durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/tagged_vendor_and_oldest_not_trunk.svndump0000644000000000000000000001031712043462603030566 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 0e935f28-8caa-dd11-b3dc-00105ae0362c Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-11-04T16:18:04.048615Z PROPS-END Revision-number: 1 Prop-content-length: 102 Content-length: 102 K 7 svn:log V 3 btt K 10 svn:author V 5 durin K 8 svn:date V 27 2008-11-04T16:18:29.661251Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 119 Content-length: 119 K 7 svn:log V 19 Add the vendor dir. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-11-04T16:19:16.816658Z PROPS-END Node-path: vendor Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: vendor/foobaz Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: vendor/foobaz/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 9f9f90dbe3e5ee1218c86b8839db1995 Content-length: 16 PROPS-END alpha Node-path: vendor/foobaz/beta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 5 Text-content-md5: f0cf2a92516045024a0c99147b28f05b Content-length: 15 PROPS-END beta Node-path: vendor/foobaz/delta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: d2840cc81bc032bd1141b56687d0f93c Content-length: 16 PROPS-END delta Node-path: vendor/foobaz/gamma Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 303febb9068384eca46b5b6516843b35 Content-length: 16 PROPS-END gamma Revision-number: 3 Prop-content-length: 139 Content-length: 139 K 7 svn:log V 39 Adding oldest data, which is not trunk. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-11-04T16:22:06.704260Z PROPS-END Node-path: branches/oldest Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: branches/oldest/five Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 5 Text-content-md5: 014835e36358e38c7f7897d6571e4529 Content-length: 15 PROPS-END five Node-path: branches/oldest/four Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 5 Text-content-md5: 75ffdb827341e578959bfcabde3789d8 Content-length: 15 PROPS-END four Node-path: branches/oldest/one Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 4 Text-content-md5: 5bbf5a52328e7439ae6e719dfe712200 Content-length: 14 PROPS-END one Node-path: branches/oldest/three Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: febe6995bad457991331348f7b9c85fa Content-length: 16 PROPS-END three Node-path: branches/oldest/two Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 4 Text-content-md5: c193497a1a06b2c72230e6146ff47080 Content-length: 14 PROPS-END two Revision-number: 4 Prop-content-length: 105 Content-length: 105 K 7 svn:log V 6 delete K 10 svn:author V 5 durin K 8 svn:date V 27 2008-11-04T16:22:49.936769Z PROPS-END Node-path: trunk Node-action: delete Revision-number: 5 Prop-content-length: 124 Content-length: 124 K 7 svn:log V 24 create trunk from branch K 10 svn:author V 5 durin K 8 svn:date V 27 2008-11-04T16:23:00.562964Z PROPS-END Node-path: tags/foobaz_1 Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: vendor/foobaz Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: branches/oldest Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Revision-number: 6 Prop-content-length: 146 Content-length: 146 K 7 svn:log V 46 copy data from a vendor branch tag into trunk. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-11-04T16:23:32.980956Z PROPS-END Node-path: trunk/foobaz Node-kind: dir Node-action: add Node-copyfrom-rev: 5 Node-copyfrom-path: tags/foobaz_1 durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/test_files_copied_from_outside_btt.svndump0000644000000000000000000000404012043462603030567 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: df2126f7-00ab-4d49-b42c-7e981dde0bcf Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-10-07T22:49:12.059692Z PROPS-END Revision-number: 1 Prop-content-length: 111 Content-length: 111 K 7 svn:log V 11 Empty dirs. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T22:49:41.118037Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 108 Content-length: 108 K 7 svn:log V 9 Add alpha K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T23:23:02.991743Z PROPS-END Node-path: trunk/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 12 Text-content-md5: 3c72ebf8bbd7fa88b1fdcee5398b5a17 Content-length: 22 PROPS-END file: alpha Revision-number: 3 Prop-content-length: 115 Content-length: 115 K 7 svn:log V 15 Add third_party K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-27T03:16:13.831333Z PROPS-END Node-path: third_party Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: third_party/magic Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: third_party/magic/the_magic_software Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 7 Text-content-md5: bc82cb068ef102a1f4e296992e5979ef Content-length: 17 PROPS-END magic! Revision-number: 4 Prop-content-length: 131 Content-length: 131 K 7 svn:log V 31 Copy magic in from third_party. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-27T03:16:32.737023Z PROPS-END Node-path: trunk/magic Node-kind: dir Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: third_party/magic Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/test_no_dates.svndump0000644000000000000000000000276012043462603024315 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 3b9ee152-ff20-493a-9d97-5d739715df90 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2009-04-08T10:33:20.055686Z PROPS-END Revision-number: 1 Prop-content-length: 55 Content-length: 55 K 7 svn:log V 4 init K 10 svn:author V 3 djc PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 56 Content-length: 56 K 7 svn:log V 5 add a K 10 svn:author V 3 djc PROPS-END Node-path: trunk/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Content-length: 12 PROPS-END a Revision-number: 3 Prop-content-length: 98 Content-length: 98 K 7 svn:log V 1 b K 10 svn:author V 3 djc K 8 svn:date V 27 2009-04-08T10:35:21.843827Z PROPS-END Node-path: trunk/a Node-kind: file Node-action: change Text-content-length: 3 Text-content-md5: daa8075d6ac5ff8d0c6d4650adb4ef29 Content-length: 3 ab Revision-number: 4 Prop-content-length: 52 Content-length: 52 K 7 svn:log V 1 c K 10 svn:author V 3 djc PROPS-END Node-path: trunk/a Node-kind: file Node-action: change Text-content-length: 4 Text-content-md5: 0bee89b07a248e27c83fc3d5951213c1 Content-length: 4 abc durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/truncatedhistory.sh0000755000000000000000000000110412043462603024005 0ustar 00000000000000#!/bin/sh # # Generate truncatedhistory.svndump # mkdir temp cd temp mkdir project-orig cd project-orig mkdir project1 mkdir project2 cd .. svnadmin create testrepo svnurl=file://`pwd`/testrepo svn import project-orig $svnurl -m "init project" svn co $svnurl project # Make a single revision in trunk cd project/project1 echo a > a svn add a svn ci -m "add a" cd .. svn up # Rename the project svn mv project1 project2/trunk svn ci -m "rename project1" cd project2/trunk echo b > b svn add b svn ci -m "add b" cd ../../.. svnadmin dump testrepo > ../truncatedhistory.svndump durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/truncatedhistory.svndump0000644000000000000000000000330012043462603025064 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: a632509c-657a-43d2-ae0b-85387476b20e Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-11-11T19:00:25.260082Z PROPS-END Revision-number: 1 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 12 init project K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-11-11T19:00:25.322759Z PROPS-END Node-path: project1 Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: project2 Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 106 Content-length: 106 K 7 svn:log V 5 add a K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-11-11T19:00:26.173269Z PROPS-END Node-path: project1/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Content-length: 12 PROPS-END a Revision-number: 3 Prop-content-length: 117 Content-length: 117 K 7 svn:log V 15 rename project1 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-11-11T19:00:29.152593Z PROPS-END Node-path: project2/trunk Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: project1 Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Node-path: project1 Node-action: delete Revision-number: 4 Prop-content-length: 106 Content-length: 106 K 7 svn:log V 5 add b K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-11-11T19:00:30.178261Z PROPS-END Node-path: project2/trunk/b Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 3b5d5c3712955042212316173ccf37be Content-length: 12 PROPS-END b durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/two_heads.svndump0000644000000000000000000000501712043462603023435 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: df2126f7-00ab-4d49-b42c-7e981dde0bcf Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-10-07T22:49:12.059692Z PROPS-END Revision-number: 1 Prop-content-length: 111 Content-length: 111 K 7 svn:log V 11 Empty dirs. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T22:49:41.118037Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 108 Content-length: 108 K 7 svn:log V 9 Add alpha K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T23:23:02.991743Z PROPS-END Node-path: trunk/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 12 Text-content-md5: 3c72ebf8bbd7fa88b1fdcee5398b5a17 Content-length: 22 PROPS-END file: alpha Revision-number: 3 Prop-content-length: 107 Content-length: 107 K 7 svn:log V 8 Add beta K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T23:28:22.651398Z PROPS-END Node-path: trunk/beta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 13 Text-content-md5: 981d1eb5fd0bbe05354c292105944863 Content-length: 23 PROPS-END Data of beta Revision-number: 4 Prop-content-length: 113 Content-length: 113 K 7 svn:log V 13 Make a branch K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-08T01:35:39.984789Z PROPS-END Node-path: branches/the_branch Node-kind: dir Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: trunk Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Revision-number: 5 Prop-content-length: 123 Content-length: 123 K 7 svn:log V 23 add delta on the branch K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-08T01:39:05.520779Z PROPS-END Node-path: branches/the_branch/delta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: d2840cc81bc032bd1141b56687d0f93c Content-length: 16 PROPS-END delta Revision-number: 6 Prop-content-length: 119 Content-length: 119 K 7 svn:log V 19 Add gamma on trunk. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-08T01:39:29.950892Z PROPS-END Node-path: trunk/gamma Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 303febb9068384eca46b5b6516843b35 Content-length: 16 PROPS-END gamma durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/two_revs.svndump0000644000000000000000000000263212043462603023330 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: df2126f7-00ab-4d49-b42c-7e981dde0bcf Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-10-07T22:49:12.059692Z PROPS-END Revision-number: 1 Prop-content-length: 111 Content-length: 111 K 7 svn:log V 11 Empty dirs. K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T22:49:41.118037Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 108 Content-length: 108 K 7 svn:log V 9 Add alpha K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T23:23:02.991743Z PROPS-END Node-path: trunk/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 12 Text-content-md5: 3c72ebf8bbd7fa88b1fdcee5398b5a17 Content-length: 22 PROPS-END file: alpha Revision-number: 3 Prop-content-length: 107 Content-length: 107 K 7 svn:log V 8 Add beta K 10 svn:author V 5 durin K 8 svn:date V 27 2008-10-07T23:28:22.651398Z PROPS-END Node-path: trunk/beta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 13 Text-content-md5: 981d1eb5fd0bbe05354c292105944863 Content-length: 23 PROPS-END Data of beta durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/unorderedbranch.sh0000755000000000000000000000133512043462603023545 0ustar 00000000000000#!/bin/sh # # Generate unorderedbranch.svndump # mkdir temp cd temp mkdir project-orig cd project-orig mkdir trunk mkdir branches cd .. svnadmin create testrepo svnurl=file://`pwd`/testrepo svn import project-orig $svnurl -m "init project" svn co $svnurl project cd project/trunk echo a > a svn add a svn ci -m "add a in trunk" echo b > b echo z > z svn add b z svn ci -m "add b and z in trunk" svn up cd ../branches # Copy from trunk past revision. The converted used to take the last # trunk revision as branch parent instead of the specified one. svn cp -r 2 ../trunk branch svn cp ../trunk/z branch echo c > branch/c svn add branch/c svn ci -m 'branch and add c' cd ../.. svnadmin dump testrepo > ../unorderedbranch.svndump durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/unorderedbranch.svndump0000644000000000000000000000430212043462603024621 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 0f1e934c-2a07-4fc9-acf5-47264bd6bc41 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-12-06T16:48:56.303240Z PROPS-END Revision-number: 1 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 12 init project K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-06T16:48:56.429957Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 116 Content-length: 116 K 7 svn:log V 14 add a in trunk K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-06T16:48:57.178238Z PROPS-END Node-path: trunk/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Content-length: 12 PROPS-END a Revision-number: 3 Prop-content-length: 122 Content-length: 122 K 7 svn:log V 20 add b and z in trunk K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-06T16:48:58.191379Z PROPS-END Node-path: trunk/b Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 3b5d5c3712955042212316173ccf37be Content-length: 12 PROPS-END b Node-path: trunk/z Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: a8a78d0ff555c931f045b6f448129846 Content-length: 12 PROPS-END z Revision-number: 4 Prop-content-length: 118 Content-length: 118 K 7 svn:log V 16 branch and add c K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-12-06T16:49:02.185634Z PROPS-END Node-path: branches/branch Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk Node-path: branches/branch/c Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 2cd6ee2c70b0bde53fbe6cac3c8b8bb1 Content-length: 12 PROPS-END c Node-path: branches/branch/z Node-kind: file Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: trunk/z Text-copy-source-md5: a8a78d0ff555c931f045b6f448129846 Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/unrelatedbranch.sh0000755000000000000000000000161412043462603023541 0ustar 00000000000000#!/bin/sh # # Generate unrelatedbranch.svndump # mkdir temp cd temp mkdir project-orig cd project-orig mkdir trunk mkdir branches cd .. svnadmin create testrepo svnurl=file://`pwd`/testrepo svn import project-orig $svnurl -m "init project" svn co $svnurl project cd project/trunk echo a > a svn add a svn ci -m "add a in trunk" cd ../branches # Create an unrelated branch with another file. It used to lead the converter # to think branch1 was a copy of trunk, even without copy information. mkdir branch1 echo b > branch1/b svn add branch1 svn ci -m "add b in branch1" # Make a real branch too for comparison svn cp ../trunk branch2 echo b > branch2/b svn add branch2/b svn ci -m "add b to branch2" # Add a file in the branch root for fun echo c > c svn add c svn ci -m "add c in branches/" # Even update it echo c >> c svn ci -m "change c" cd ../.. svnadmin dump testrepo > ../unrelatedbranch.svndump durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/unrelatedbranch.svndump0000644000000000000000000000551112043462603024620 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: 20b2173d-0f26-4070-9891-00196be06b32 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2008-11-25T23:08:28.726784Z PROPS-END Revision-number: 1 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 12 init project K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-11-25T23:08:28.798249Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 116 Content-length: 116 K 7 svn:log V 14 add a in trunk K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-11-25T23:08:29.163194Z PROPS-END Node-path: trunk/a Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3 Content-length: 12 PROPS-END a Revision-number: 3 Prop-content-length: 118 Content-length: 118 K 7 svn:log V 16 add b in branch1 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-11-25T23:08:30.176855Z PROPS-END Node-path: branches/branch1 Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: branches/branch1/b Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 3b5d5c3712955042212316173ccf37be Content-length: 12 PROPS-END b Revision-number: 4 Prop-content-length: 118 Content-length: 118 K 7 svn:log V 16 add b to branch2 K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-11-25T23:08:32.174817Z PROPS-END Node-path: branches/branch2 Node-kind: dir Node-action: add Node-copyfrom-rev: 1 Node-copyfrom-path: trunk Prop-content-length: 34 Content-length: 34 K 13 svn:mergeinfo V 0 PROPS-END Node-path: branches/branch2/a Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/a Text-copy-source-md5: 60b725f10c9c85c70d97880dfe8191b3 Node-path: branches/branch2/b Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 3b5d5c3712955042212316173ccf37be Content-length: 12 PROPS-END b Revision-number: 5 Prop-content-length: 120 Content-length: 120 K 7 svn:log V 18 add c in branches/ K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-11-25T23:08:33.166454Z PROPS-END Node-path: branches/c Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 2 Text-content-md5: 2cd6ee2c70b0bde53fbe6cac3c8b8bb1 Content-length: 12 PROPS-END c Revision-number: 6 Prop-content-length: 109 Content-length: 109 K 7 svn:log V 8 change c K 10 svn:author V 7 pmezard K 8 svn:date V 27 2008-11-25T23:08:34.144541Z PROPS-END Node-path: branches/c Node-kind: file Node-action: change Text-content-length: 4 Text-content-md5: 63fad9092ad37713ebe26b3193f89c41 Content-length: 4 c c durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/unusual_tags.sh0000755000000000000000000000152412043462603023112 0ustar 00000000000000#!/bin/sh mkdir temp cd temp svnadmin create repo svn co file://`pwd`/repo wc cd wc mkdir branches trunk tags mkdir tags/versions mkdir tags/blah svn add * svn ci -m 'btt' cd trunk for a in alpha beta gamma delta ; do echo $a > $a svn add $a done svn ci -m 'Add files.' cd .. svn up svn cp trunk branches/dev_branch svn ci -m 'branch' cd branches/dev_branch svn rm delta echo narf > alpha echo iota > iota svn add iota svn ci -m 'branch changes' cd ../.. svn up svn cp branches/dev_branch tags/versions/branch_version svn ci -m 'Make a tag in tags/versions from branches/dev_branch' svn up svn cp trunk tags/blah/trunktag svn ci -m 'Make a tag in tags/blah from trunk' svn up cd ../.. svnadmin dump temp/repo > unusual_tags.svndump echo echo 'Complete.' echo 'You probably want to clean up temp now.' echo 'Dump in unusual_tags.svndump' exit 0 durin42-hgsubversion-77b22e5b4ea6/tests/fixtures/unusual_tags.svndump0000644000000000000000000000651712043462603024200 0ustar 00000000000000SVN-fs-dump-format-version: 2 UUID: f9b744e7-45eb-476b-878a-5ef2bae08236 Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2009-05-31T00:36:58.665604Z PROPS-END Revision-number: 1 Prop-content-length: 102 Content-length: 102 K 7 svn:log V 3 btt K 10 svn:author V 5 durin K 8 svn:date V 27 2009-05-31T00:36:59.110531Z PROPS-END Node-path: branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags/blah Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: tags/versions Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 110 Content-length: 110 K 7 svn:log V 10 Add files. K 10 svn:author V 5 durin K 8 svn:date V 27 2009-05-31T00:37:00.120547Z PROPS-END Node-path: trunk/alpha Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 9f9f90dbe3e5ee1218c86b8839db1995 Content-length: 16 PROPS-END alpha Node-path: trunk/beta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 5 Text-content-md5: f0cf2a92516045024a0c99147b28f05b Content-length: 15 PROPS-END beta Node-path: trunk/delta Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: d2840cc81bc032bd1141b56687d0f93c Content-length: 16 PROPS-END delta Node-path: trunk/gamma Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 303febb9068384eca46b5b6516843b35 Content-length: 16 PROPS-END gamma Revision-number: 3 Prop-content-length: 105 Content-length: 105 K 7 svn:log V 6 branch K 10 svn:author V 5 durin K 8 svn:date V 27 2009-05-31T00:37:03.055780Z PROPS-END Node-path: branches/dev_branch Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk Revision-number: 4 Prop-content-length: 114 Content-length: 114 K 7 svn:log V 14 branch changes K 10 svn:author V 5 durin K 8 svn:date V 27 2009-05-31T00:37:04.091514Z PROPS-END Node-path: branches/dev_branch/alpha Node-kind: file Node-action: change Text-content-length: 5 Text-content-md5: 5e723ed52db2000686425ca28bc5ba4a Content-length: 5 narf Node-path: branches/dev_branch/iota Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 5 Text-content-md5: ebcf3971120220589f1dfbf8d56e25b9 Content-length: 15 PROPS-END iota Node-path: branches/dev_branch/delta Node-action: delete Revision-number: 5 Prop-content-length: 152 Content-length: 152 K 7 svn:log V 52 Make a tag in tags/versions from branches/dev_branch K 10 svn:author V 5 durin K 8 svn:date V 27 2009-05-31T00:37:07.052404Z PROPS-END Node-path: tags/versions/branch_version Node-kind: dir Node-action: add Node-copyfrom-rev: 4 Node-copyfrom-path: branches/dev_branch Revision-number: 6 Prop-content-length: 134 Content-length: 134 K 7 svn:log V 34 Make a tag in tags/blah from trunk K 10 svn:author V 5 durin K 8 svn:date V 27 2009-05-31T00:37:10.055893Z PROPS-END Node-path: tags/blah/trunktag Node-kind: dir Node-action: add Node-copyfrom-rev: 5 Node-copyfrom-path: trunk durin42-hgsubversion-77b22e5b4ea6/tests/run.py0000644000000000000000000000761712043462603017357 0ustar 00000000000000import optparse import os import sys import unittest import test_util test_util.SkipTest = None def tests(): import test_binaryfiles import test_diff import test_externals import test_fetch_branches import test_fetch_command import test_fetch_command_regexes import test_fetch_exec import test_fetch_mappings import test_fetch_renames import test_fetch_symlinks import test_fetch_truncated import test_hooks import test_pull import test_pull_fallback import test_push_command import test_push_renames import test_push_dirs import test_push_eol import test_push_autoprops import test_rebuildmeta import test_single_dir_clone import test_svnwrap import test_tags import test_template_keywords import test_utility_commands import test_unaffected_core import test_updatemeta import test_urls sys.path.append(os.path.dirname(__file__)) sys.path.append(os.path.join(os.path.dirname(__file__), 'comprehensive')) import test_stupid_pull import test_verify_and_startrev return locals() def comprehensive(mod): dir = os.path.basename(os.path.dirname(mod.__file__)) return dir == 'comprehensive' if __name__ == '__main__': description = ("This script runs the hgsubversion tests. If no tests are " "specified, all known tests are implied.") parser = optparse.OptionParser(usage="%prog [options] [TESTS ...]", description=description) parser.add_option("-A", "--all", dest="comprehensive", action="store_true", default=False, help="include slow, but comprehensive tests") parser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False, help="enable verbose output") parser.add_option("", "--no-demandimport", dest="demandimport", action="store_false", default=True, help="disable Mercurial demandimport loading") parser.add_option("", "--bindings", dest="bindings", action="store", default=None, choices=["swig", "subvertpy"], help="test using the specified bindings (swig or " "subvertpy)") parser.add_option("", "--show-stdout", dest="showstdout", action="store_true", default=False, help="show stdout (hidden by default)") (options, args) = parser.parse_args() if options.verbose: testargs = { 'descriptions': 3, 'verbosity': 2 } else: testargs = {'descriptions': 2} sys.path.append(os.path.dirname(os.path.dirname(__file__))) if options.demandimport: from mercurial import demandimport demandimport.enable() if options.bindings: os.environ['HGSUBVERSION_BINDINGS'] = options.bindings # make sure our copy of hgsubversion gets imported by loading test_util import test_util test_util.TestBase # silence output when running outside nose if not options.showstdout: import tempfile sys.stdout = tempfile.TemporaryFile() all_tests = tests() args = [i.split('.py')[0].replace('-', '_') for i in args] if not args: check = lambda x: options.comprehensive or not comprehensive(x) mods = [m for (n, m) in sorted(all_tests.iteritems()) if check(m)] suite = [m.suite() for m in mods] else: suite = [] for arg in args: if arg == 'test_util': continue elif arg not in all_tests: print >> sys.stderr, 'test module %s not available' % arg else: suite.append(all_tests[arg].suite()) runner = unittest.TextTestRunner(**testargs) result = runner.run(unittest.TestSuite(suite)) if not result.wasSuccessful(): sys.exit(1) durin42-hgsubversion-77b22e5b4ea6/tests/test_binaryfiles.py0000644000000000000000000000076112043462603022112 0ustar 00000000000000import test_util import unittest class TestFetchBinaryFiles(test_util.TestBase): def test_binaryfiles(self, stupid=False): repo = self._load_fixture_and_fetch('binaryfiles.svndump', stupid=stupid) self.assertEqual('cce7fe400d8d', str(repo['tip'])) def test_binaryfiles_stupid(self): self.test_binaryfiles(True) def suite(): all_tests = [unittest.TestLoader().loadTestsFromTestCase(TestFetchBinaryFiles), ] return unittest.TestSuite(all_tests) durin42-hgsubversion-77b22e5b4ea6/tests/test_diff.py0000644000000000000000000000217012043462603020507 0ustar 00000000000000import test_util import unittest from mercurial import ui from hgsubversion import wrappers expected_diff_output = '''Index: alpha =================================================================== --- alpha\t(revision 3) +++ alpha\t(working copy) @@ -1,1 +1,3 @@ -file: alpha +alpha + +added line Index: foo =================================================================== new file mode 100644 --- foo\t(revision 0) +++ foo\t(working copy) @@ -0,0 +1,1 @@ +This is missing a newline. \ No newline at end of file ''' class DiffTests(test_util.TestBase): def test_diff_output(self): self._load_fixture_and_fetch('two_revs.svndump') self.commitchanges([('foo', 'foo', 'This is missing a newline.'), ('alpha', 'alpha', 'alpha\n\nadded line\n'), ]) u = ui.ui() u.pushbuffer() wrappers.diff(lambda x, y, z: None, u, self.repo, svn=True) self.assertEqual(u.popbuffer(), expected_diff_output) def suite(): all_tests = [unittest.TestLoader().loadTestsFromTestCase(DiffTests), ] return unittest.TestSuite(all_tests) durin42-hgsubversion-77b22e5b4ea6/tests/test_externals.py0000644000000000000000000003637712043462603021624 0ustar 00000000000000import test_util import os, unittest, sys from mercurial import commands from mercurial import util as hgutil try: from mercurial import subrepo # require svnsubrepo and hg >= 1.7.1 subrepo.svnsubrepo hgutil.checknlink except (ImportError, AttributeError), e: print >> sys.stderr, 'test_externals: skipping .hgsub tests' subrepo = None from hgsubversion import svnexternals class TestFetchExternals(test_util.TestBase): def test_externalsfile(self): f = svnexternals.externalsfile() f['t1'] = 'dir1 -r10 svn://foobar' f['t 2'] = 'dir2 -r10 svn://foobar' f['t3'] = ['dir31 -r10 svn://foobar', 'dir32 -r10 svn://foobar'] refext = """[t 2] dir2 -r10 svn://foobar [t1] dir1 -r10 svn://foobar [t3] dir31 -r10 svn://foobar dir32 -r10 svn://foobar """ value = f.write() self.assertEqual(refext, value) f2 = svnexternals.externalsfile() f2.read(value) self.assertEqual(sorted(f), sorted(f2)) for t in f: self.assertEqual(f[t], f2[t]) def test_parsedefinitions(self): # Taken from svn book samples = [ ('third-party/sounds http://svn.example.com/repos/sounds', ('third-party/sounds', None, 'http://svn.example.com/repos/sounds', None, 'third-party/sounds http://svn.example.com/repos/sounds')), ('third-party/skins -r148 http://svn.example.com/skinproj', ('third-party/skins', '148', 'http://svn.example.com/skinproj', None, 'third-party/skins -r{REV} http://svn.example.com/skinproj')), ('third-party/skins -r 148 http://svn.example.com/skinproj', ('third-party/skins', '148', 'http://svn.example.com/skinproj', None, 'third-party/skins -r {REV} http://svn.example.com/skinproj')), ('http://svn.example.com/repos/sounds third-party/sounds', ('third-party/sounds', None, 'http://svn.example.com/repos/sounds', None, 'http://svn.example.com/repos/sounds third-party/sounds')), ('-r148 http://svn.example.com/skinproj third-party/skins', ('third-party/skins', '148', 'http://svn.example.com/skinproj', None, '-r{REV} http://svn.example.com/skinproj third-party/skins')), ('-r 148 http://svn.example.com/skinproj third-party/skins', ('third-party/skins', '148', 'http://svn.example.com/skinproj', None, '-r {REV} http://svn.example.com/skinproj third-party/skins')), ('http://svn.example.com/skin-maker@21 third-party/skins/toolkit', ('third-party/skins/toolkit', None, 'http://svn.example.com/skin-maker', '21', 'http://svn.example.com/skin-maker@21 third-party/skins/toolkit')), ] for line, expected in samples: self.assertEqual(expected, svnexternals.parsedefinition(line)) def test_externals(self, stupid=False): repo = self._load_fixture_and_fetch('externals.svndump', stupid=stupid) ref0 = """[.] ^/externals/project1 deps/project1 """ self.assertMultiLineEqual(ref0, repo[0]['.hgsvnexternals'].data()) ref1 = """\ [.] # A comment, then an empty line, then a blank line ^/externals/project1 deps/project1 -r2 ^/externals/project2@2 deps/project2 """ self.assertMultiLineEqual(ref1, repo[1]['.hgsvnexternals'].data()) ref2 = """[.] -r2 ^/externals/project2@2 deps/project2 [subdir] ^/externals/project1 deps/project1 [subdir2] ^/externals/project1 deps/project1 """ actual = repo[2]['.hgsvnexternals'].data() self.assertEqual(ref2, actual) ref3 = """[.] -r2 ^/externals/project2@2 deps/project2 [subdir] ^/externals/project1 deps/project1 """ self.assertEqual(ref3, repo[3]['.hgsvnexternals'].data()) ref4 = """[subdir] ^/externals/project1 deps/project1 """ self.assertEqual(ref4, repo[4]['.hgsvnexternals'].data()) ref5 = """[.] -r2 ^/externals/project2@2 deps/project2 [subdir2] ^/externals/project1 deps/project1 """ self.assertEqual(ref5, repo[5]['.hgsvnexternals'].data()) ref6 = """[.] -r2 ^/externals/project2@2 deps/project2 """ self.assertEqual(ref6, repo[6]['.hgsvnexternals'].data()) def test_externals_stupid(self): self.test_externals(True) def test_updateexternals(self): def checkdeps(deps, nodeps, repo, rev=None): svnexternals.updateexternals(ui, [rev], repo) for d in deps: p = os.path.join(repo.root, d) self.assertTrue(os.path.isdir(p), 'missing: %s@%r' % (d, rev)) for d in nodeps: p = os.path.join(repo.root, d) self.assertTrue(not os.path.isdir(p), 'unexpected: %s@%r' % (d, rev)) ui = self.ui() repo = self._load_fixture_and_fetch('externals.svndump', stupid=0) commands.update(ui, repo) checkdeps(['deps/project1'], [], repo, 0) checkdeps(['deps/project1', 'deps/project2'], [], repo, 1) checkdeps(['subdir/deps/project1', 'subdir2/deps/project1', 'deps/project2'], ['deps/project1'], repo, 2) checkdeps(['subdir/deps/project1', 'deps/project2'], ['subdir2/deps/project1'], repo, 3) checkdeps(['subdir/deps/project1'], ['deps/project2'], repo, 4) def test_hgsub(self, stupid=False): if subrepo is None: return repo = self._load_fixture_and_fetch('externals.svndump', externals='subrepos', stupid=stupid) self.assertEqual("""\ deps/project1 = [hgsubversion] :^/externals/project1 deps/project1 """, repo[0]['.hgsub'].data()) self.assertEqual("""\ HEAD deps/project1 """, repo[0]['.hgsubstate'].data()) self.assertEqual("""\ deps/project1 = [hgsubversion] :^/externals/project1 deps/project1 deps/project2 = [hgsubversion] :-r{REV} ^/externals/project2@2 deps/project2 """, repo[1]['.hgsub'].data()) self.assertEqual("""\ HEAD deps/project1 2 deps/project2 """, repo[1]['.hgsubstate'].data()) self.assertEqual("""\ deps/project2 = [hgsubversion] :-r{REV} ^/externals/project2@2 deps/project2 subdir/deps/project1 = [hgsubversion] subdir:^/externals/project1 deps/project1 subdir2/deps/project1 = [hgsubversion] subdir2:^/externals/project1 deps/project1 """, repo[2]['.hgsub'].data()) self.assertEqual("""\ 2 deps/project2 HEAD subdir/deps/project1 HEAD subdir2/deps/project1 """, repo[2]['.hgsubstate'].data()) self.assertMultiLineEqual("""\ deps/project2 = [hgsubversion] :-r{REV} ^/externals/project2@2 deps/project2 subdir/deps/project1 = [hgsubversion] subdir:^/externals/project1 deps/project1 """, repo[3]['.hgsub'].data()) self.assertEqual("""\ 2 deps/project2 HEAD subdir/deps/project1 """, repo[3]['.hgsubstate'].data()) self.assertEqual("""\ subdir/deps/project1 = [hgsubversion] subdir:^/externals/project1 deps/project1 """, repo[4]['.hgsub'].data()) self.assertEqual("""\ HEAD subdir/deps/project1 """, repo[4]['.hgsubstate'].data()) self.assertEqual("""\ deps/project2 = [hgsubversion] :-r{REV} ^/externals/project2@2 deps/project2 subdir2/deps/project1 = [hgsubversion] subdir2:^/externals/project1 deps/project1 """, repo[5]['.hgsub'].data()) self.assertEqual("""\ 2 deps/project2 HEAD subdir2/deps/project1 """, repo[5]['.hgsubstate'].data()) self.assertEqual("""\ deps/project2 = [hgsubversion] :-r{REV} ^/externals/project2@2 deps/project2 """, repo[6]['.hgsub'].data()) self.assertEqual("""\ 2 deps/project2 """, repo[6]['.hgsubstate'].data()) def test_hgsub_stupid(self): self.test_hgsub(True) def test_ignore(self): repo = self._load_fixture_and_fetch('externals.svndump', externals='ignore') for rev in repo: ctx = repo[rev] self.assertTrue('.hgsvnexternals' not in ctx) self.assertTrue('.hgsub' not in ctx) self.assertTrue('.hgsubstate' not in ctx) def test_updatehgsub(self): def checkdeps(ui, repo, rev, deps, nodeps): commands.update(ui, repo, node=str(rev)) for d in deps: p = os.path.join(repo.root, d) self.assertTrue(os.path.isdir(p), 'missing: %s@%r' % (d, repo[None].rev())) for d in nodeps: p = os.path.join(repo.root, d) self.assertTrue(not os.path.isdir(p), 'unexpected: %s@%r' % (d, repo[None].rev())) if subrepo is None: return ui = self.ui() repo = self._load_fixture_and_fetch('externals.svndump', stupid=0, externals='subrepos') checkdeps(ui, repo, 0, ['deps/project1'], []) checkdeps(ui, repo, 1, ['deps/project1', 'deps/project2'], []) checkdeps(ui, repo, 2, ['subdir/deps/project1', 'subdir2/deps/project1', 'deps/project2'], ['deps/project1']) checkdeps(ui, repo, 3, ['subdir/deps/project1', 'deps/project2'], ['subdir2/deps/project1']) checkdeps(ui, repo, 4, ['subdir/deps/project1'], ['deps/project2']) # Test update --clean, used to crash repo.wwrite('subdir/deps/project1/a', 'foobar', '') commands.update(ui, repo, node='4', clean=True) def test_mergeexternals(self, stupid=False): if subrepo is None: return repo = self._load_fixture_and_fetch('mergeexternals.svndump', externals='subrepos', stupid=stupid) # Check merged directories externals are fine self.assertEqual("""\ d1/ext = [hgsubversion] d1:^/trunk/common/ext ext d2/ext = [hgsubversion] d2:^/trunk/common/ext ext d3/ext3 = [hgsubversion] d3:^/trunk/common/ext ext3 """, repo['tip']['.hgsub'].data()) def test_mergeexternals_stupid(self): self.test_mergeexternals(True) class TestPushExternals(test_util.TestBase): def test_push_externals(self, stupid=False): repo = self._load_fixture_and_fetch('pushexternals.svndump') # Add a new reference on an existing and non-existing directory changes = [ ('.hgsvnexternals', '.hgsvnexternals', """[dir] ../externals/project2 deps/project2 [subdir1] ../externals/project1 deps/project1 [subdir2] ../externals/project2 deps/project2 """), ('subdir1/a', 'subdir1/a', 'a'), ('subdir2/a', 'subdir2/a', 'a'), ] self.commitchanges(changes) self.pushrevisions(stupid) self.assertchanges(changes, self.repo['tip']) # Remove all references from one directory, add a new one # to the other (test multiline entries) changes = [ ('.hgsvnexternals', '.hgsvnexternals', """[subdir1] ../externals/project1 deps/project1 ../externals/project2 deps/project2 """), # This removal used to trigger the parent directory removal ('subdir1/a', None, None), ] self.commitchanges(changes) self.pushrevisions(stupid) self.assertchanges(changes, self.repo['tip']) # Check subdir2/a is still there even if the externals were removed self.assertTrue('subdir2/a' in self.repo['tip']) self.assertTrue('subdir1/a' not in self.repo['tip']) # Test externals removal changes = [ ('.hgsvnexternals', None, None), ] self.commitchanges(changes) self.pushrevisions(stupid) self.assertchanges(changes, self.repo['tip']) def test_push_externals_stupid(self): self.test_push_externals(True) def test_push_hgsub(self, stupid=False): if subrepo is None: return 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', """\ dir/deps/project2 = [hgsubversion] dir:^/externals/project2 deps/project2 subdir1/deps/project1 = [hgsubversion] subdir1:^/externals/project1 deps/project1 subdir2/deps/project2 = [hgsubversion] subdir2:^/externals/project2 deps/project2 """), ('.hgsubstate', '.hgsubstate', """\ HEAD dir/deps/project2 HEAD subdir1/deps/project1 HEAD subdir2/deps/project2 """), ('subdir1/a', 'subdir1/a', 'a'), ('subdir2/a', 'subdir2/a', 'a'), ] 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'], test_util.svnls(repo_path, 'trunk')) # Remove all references from one directory, add a new one # to the other (test multiline entries) changes = [ ('.hgsub', '.hgsub', """\ subdir1/deps/project1 = [hgsubversion] subdir1:^/externals/project1 deps/project1 subdir1/deps/project2 = [hgsubversion] subdir1:^/externals/project2 deps/project2 """), ('.hgsubstate', '.hgsubstate', """\ HEAD subdir1/deps/project1 HEAD subdir1/deps/project2 """), # This removal used to trigger the parent directory removal ('subdir1/a', None, None), ] 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']) # Check subdir2/a is still there even if the externals were removed self.assertTrue('subdir2/a' in self.repo['tip']) self.assertTrue('subdir1/a' not in self.repo['tip']) # Move the externals so they are defined on the base directory, # this used to cause full branch removal when deleting the .hgsub changes = [ ('.hgsub', '.hgsub', """\ subdir1/deps/project1 = [hgsubversion] :^/externals/project1 subdir1/deps/project1 """), ('.hgsubstate', '.hgsubstate', """\ HEAD subdir1/deps/project1 """), ] self.commitchanges(changes) self.pushrevisions(stupid) self.assertchanges(changes, self.repo['tip']) # Test externals removal changes = [ ('.hgsub', None, None), ('.hgsubstate', None, None), ] self.commitchanges(changes) self.pushrevisions(stupid) self.assertchanges(changes, self.repo['tip']) def suite(): all_tests = [unittest.TestLoader().loadTestsFromTestCase(TestFetchExternals), unittest.TestLoader().loadTestsFromTestCase(TestPushExternals), ] return unittest.TestSuite(all_tests) durin42-hgsubversion-77b22e5b4ea6/tests/test_fetch_branches.py0000644000000000000000000001707512043462603022547 0ustar 00000000000000import test_util import unittest from mercurial import hg from mercurial import node from mercurial import util as hgutil class TestFetchBranches(test_util.TestBase): def _load_fixture_and_fetch_with_anchor(self, fixture_name, 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) def branches(self, repo): hctxs = [repo[hn] for hn in repo.heads()] openbranches = set(ctx.branch() for ctx in hctxs if ctx.extra().get('close', None) != '1') closedbranches = set(ctx.branch() for ctx in hctxs if ctx.extra().get('close', None) == '1') return sorted(openbranches), sorted(closedbranches) def openbranches(self, repo): 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=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 self.assertEqual(['dev_branch'], self.openbranches(repo)) def test_rename_branch_parent_stupid(self): self.test_rename_branch_parent(stupid=True) def test_unrelatedbranch(self, stupid=False): 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 self.assertEqual(heads['branch1'].manifest().keys(), ['b']) self.assertEqual(heads['branch2'].manifest().keys(), ['a', 'b']) def test_unrelatedbranch_stupid(self): self.test_unrelatedbranch(True) def test_unorderedbranch(self, stupid=False): 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())) def test_unorderedbranch_stupid(self): self.test_unorderedbranch(True) def test_renamed_branch_to_trunk(self, stupid=False): config = {'hgsubversion.failonmissing': 'true'} repo = self._load_fixture_and_fetch('branch_rename_to_trunk.svndump', stupid=stupid, config=config) self.assertEqual(repo['default'].parents()[0].branch(), 'dev_branch') self.assert_('iota' in repo['default']) self.assertEqual(repo['old_trunk'].parents()[0].branch(), 'default') self.assert_('iota' not in repo['old_trunk']) expected = ['default', 'old_trunk'] self.assertEqual(self.openbranches(repo), expected) def test_renamed_branch_to_trunk_stupid(self): self.test_renamed_branch_to_trunk(stupid=True) def test_replace_trunk_with_branch(self, stupid=False): repo = self._load_fixture_and_fetch('replace_trunk_with_branch.svndump', 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=stupid) self.assertEqual(repo['tip'].branch(), 'test') self.assertEqual(repo['test'].extra().get('close'), '1') self.assertEqual(repo['test']['b'].data(), 'a\n') def test_copybeforeclose_stupid(self): self.test_copybeforeclose(True) def test_replace_trunk_with_branch_stupid(self): self.test_replace_trunk_with_branch(stupid=True) 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) 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=stupid, noupdate=False) self.assertEqual(repo[None].branch(), 'default') self.assertTrue('tip' not in repo[None].tags()) def test_branch_tip_update_to_default_stupid(self): self.test_branch_tip_update_to_default(True) def test_branch_pull_anchor(self): self.assertRaises(hgutil.Abort, self._load_fixture_and_fetch_with_anchor, 'unorderedbranch.svndump', 'NaN') repo = self._load_fixture_and_fetch_with_anchor( 'unorderedbranch.svndump', '4') self.assertTrue('c' not in repo.branchtags()) def test_branches_weird_moves(self, stupid=False): 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) mdefault = sorted(heads['default'].manifest().keys()) mbranch = sorted(heads['branch'].manifest().keys()) self.assertEqual(mdefault, ['a', 'b', 'd/a']) self.assertEqual(mbranch, ['a']) def test_branches_weird_moves_stupid(self): self.test_branches_weird_moves(True) def test_branch_delete_parent_dir(self, stupid=False): repo = self._load_fixture_and_fetch('branch_delete_parent_dir.svndump', stupid=stupid) openb, closedb = self.branches(repo) self.assertEqual(openb, []) self.assertEqual(closedb, ['dev_branch']) self.assertEqual(list(repo['dev_branch']), ['foo']) def test_replace_branch_with_branch(self, stupid=False): repo = self._load_fixture_and_fetch('replace_branch_with_branch.svndump', stupid=stupid) self.assertEqual(7, len(repo)) # tip is former topological branch1 being closed ctx = repo['tip'] self.assertEqual('1', ctx.extra().get('close', '0')) self.assertEqual('branch1', ctx.branch()) # r5 is where the replacement takes place ctx = repo[5] self.assertEqual(set(['a', 'c', 'dir/e', 'dir2/e', 'f', 'g']), set(ctx)) self.assertEqual('0', ctx.extra().get('close', '0')) self.assertEqual('branch1', ctx.branch()) self.assertEqual('c\n', ctx['c'].data()) self.assertEqual('d\n', ctx['a'].data()) self.assertEqual('e\n', ctx['dir/e'].data()) self.assertEqual('e\n', ctx['dir2/e'].data()) self.assertEqual('f\n', ctx['f'].data()) self.assertEqual('g\n', ctx['g'].data()) for f in ctx: self.assertTrue(not ctx[f].renamed()) def test_replace_branch_with_branch_stupid(self, stupid=False): self.test_replace_branch_with_branch(True) def suite(): all_tests = [unittest.TestLoader().loadTestsFromTestCase(TestFetchBranches), ] return unittest.TestSuite(all_tests) durin42-hgsubversion-77b22e5b4ea6/tests/test_fetch_command.py0000644000000000000000000003033712043462603022374 0ustar 00000000000000import test_util import os import unittest import urllib from mercurial import commands from mercurial import hg from mercurial import node from mercurial import ui from mercurial import encoding class TestBasicRepoLayout(test_util.TestBase): def test_no_dates(self): repo = self._load_fixture_and_fetch('test_no_dates.svndump') local_epoch = repo[0].date() self.assertEqual(local_epoch[0], local_epoch[1]) self.assertEqual(repo[1].date(), repo[2].date()) def test_fresh_fetch_single_rev(self): repo = self._load_fixture_and_fetch('single_rev.svndump') self.assertEqual(node.hex(repo['tip'].node()), '434ed487136c1b47c1e8f952edb4dc5a8e6328df') self.assertEqual(repo['tip'].extra()['convert_revision'], 'svn:df2126f7-00ab-4d49-b42c-7e981dde0bcf/trunk@2') self.assertEqual(repo['tip'], repo[0]) def test_fresh_fetch_two_revs(self): repo = self._load_fixture_and_fetch('two_revs.svndump') self.assertEqual(node.hex(repo[0].node()), '434ed487136c1b47c1e8f952edb4dc5a8e6328df') self.assertEqual(node.hex(repo['tip'].node()), 'c95251e0dd04697deee99b79cc407d7db76e6a5f') self.assertEqual(repo['tip'], repo[1]) def test_branches(self): repo = self._load_fixture_and_fetch('simple_branch.svndump') self.assertEqual(node.hex(repo[0].node()), 'a1ff9f5d90852ce7f8e607fa144066b0a06bdc57') self.assertEqual(node.hex(repo['tip'].node()), '545e36ed13615e39c5c8fb0c325109d8cb8e00c3') self.assertEqual(len(repo['tip'].parents()), 1) self.assertEqual(repo['tip'].parents()[0], repo['default']) self.assertEqual(repo['tip'].extra()['convert_revision'], 'svn:3cd547df-371e-4add-bccf-aba732a2baf5/branches/the_branch@4') self.assertEqual(repo['default'].extra()['convert_revision'], 'svn:3cd547df-371e-4add-bccf-aba732a2baf5/trunk@3') self.assertEqual(len(repo.heads()), 1) def test_two_branches_with_heads(self): repo = self._load_fixture_and_fetch('two_heads.svndump') self.assertEqual(node.hex(repo[0].node()), '434ed487136c1b47c1e8f952edb4dc5a8e6328df') self.assertEqual(node.hex(repo['tip'].node()), '1083037b18d85cd84fa211c5adbaeff0fea2cd9f') self.assertEqual(node.hex(repo['the_branch'].node()), '4e256962fc5df545e2e0a51d0d1dc61c469127e6') self.assertEqual(node.hex(repo['the_branch'].parents()[0].node()), 'f1ff5b860f5dbb9a59ad0921a79da77f10f25109') self.assertEqual(len(repo['tip'].parents()), 1) self.assertEqual(repo['tip'], repo['default']) self.assertEqual(len(repo.heads()), 2) def test_many_special_cases_replay(self): repo = self._load_fixture_and_fetch('many_special_cases.svndump') self._many_special_cases_checks(repo) def test_many_special_cases_diff(self): repo = self._load_fixture_and_fetch('many_special_cases.svndump', stupid=True) self._many_special_cases_checks(repo) def _many_special_cases_checks(self, repo): self.assertEquals(node.hex(repo[0].node()), '434ed487136c1b47c1e8f952edb4dc5a8e6328df') # two possible hashes for bw compat to hg < 1.5, since hg 1.5 # sorts entries in extra() self.assertTrue(node.hex(repo['tip'].node()) in ('e92012d8c170a0236c84166167f149c2e28548c6', 'b7bdc73041b1852563deb1ef3f4153c2fe4484f2')) self.assertEqual(node.hex(repo['the_branch'].node()), '4e256962fc5df545e2e0a51d0d1dc61c469127e6') self.assertEqual(node.hex(repo['the_branch'].parents()[0].node()), 'f1ff5b860f5dbb9a59ad0921a79da77f10f25109') self.assertEqual(len(repo['tip'].parents()), 1) self.assertEqual(repo['tip'], repo['default']) self.assertEqual(len(repo.heads()), 2) def test_file_mixed_with_branches(self): repo = self._load_fixture_and_fetch('file_mixed_with_branches.svndump') self.assertEqual(node.hex(repo['default'].node()), '434ed487136c1b47c1e8f952edb4dc5a8e6328df') assert 'README' not in repo assert '../branches' not in repo def test_files_copied_from_outside_btt(self, stupid=False): repo = self._load_fixture_and_fetch( 'test_files_copied_from_outside_btt.svndump', stupid=stupid) self.assertEqual(node.hex(repo['tip'].node()), '3c78170e30ddd35f2c32faa0d8646ab75bba4f73') self.assertEqual(len(repo.changelog), 2) def test_files_copied_from_outside_btt_stupid(self): self.test_files_copied_from_outside_btt(stupid=True) def test_file_renamed_in_from_outside_btt(self): repo = self._load_fixture_and_fetch( 'file_renamed_in_from_outside_btt.svndump') self.assert_('LICENSE.file' in repo['default']) def test_renamed_dir_in_from_outside_btt_not_repo_root(self): repo = self._load_fixture_and_fetch( 'fetch_missing_files_subdir.svndump', subdir='foo') self.assertEqual(node.hex(repo['tip'].node()), '269dcdd4361b2847e9f4288d4500e55d35df1f52') self.assert_('bar/alpha' in repo['tip']) self.assert_('foo' in repo['tip']) self.assert_('bar/alpha' not in repo['tip'].parents()[0]) self.assert_('foo' in repo['tip'].parents()[0]) def test_oldest_not_trunk_and_tag_vendor_branch(self): repo = self._load_fixture_and_fetch( 'tagged_vendor_and_oldest_not_trunk.svndump') self.assertEqual(node.hex(repo['oldest'].node()), '926671740dec045077ab20f110c1595f935334fa') self.assertEqual(repo['tip'].parents()[0].parents()[0], repo['oldest']) self.assertEqual(node.hex(repo['tip'].node()), '1a6c3f30911d57abb67c257ec0df3e7bc44786f7') def test_propedit_with_nothing_else(self, stupid=False): repo = self._load_fixture_and_fetch('branch_prop_edit.svndump', stupid=stupid) self.assertEqual(repo['tip'].description(), 'Commit bogus propchange.') self.assertEqual(repo['tip'].branch(), 'dev_branch') def test_propedit_with_nothing_else_stupid(self): self.test_propedit_with_nothing_else(stupid=True) def test_entry_deletion(self, stupid=False): repo = self._load_fixture_and_fetch('delentries.svndump', stupid=stupid) files = list(sorted(repo['tip'].manifest())) self.assertEqual(['aa', 'd1/c', 'd1/d2prefix'], files) def test_entry_deletion_stupid(self): self.test_entry_deletion(stupid=True) def test_fetch_when_trunk_has_no_files(self, stupid=False): repo = self._load_fixture_and_fetch('file_not_in_trunk_root.svndump', stupid=stupid) self.assertEqual(repo['tip'].branch(), 'default') def test_fetch_when_trunk_has_no_files_stupid(self): self.test_fetch_when_trunk_has_no_files(stupid=True) def test_path_quoting(self, stupid=False): repo_path = self.load_svndump('non_ascii_path_1.svndump') subdir = '/b\xC3\xB8b' quoted_subdir = urllib.quote(subdir) repo_url = test_util.fileurl(repo_path) wc_path = self.wc_path wc2_path = wc_path + '-2' ui = self.ui(stupid=stupid) commands.clone(ui, repo_url + subdir, wc_path) commands.clone(ui, repo_url + quoted_subdir, wc2_path) repo = hg.repository(ui, wc_path) repo2 = hg.repository(ui, wc2_path) self.assertEqual(repo['tip'].extra()['convert_revision'], repo2['tip'].extra()['convert_revision']) self.assertEqual(len(repo), len(repo2)) for r in repo: self.assertEqual(repo[r].hex(), repo2[r].hex()) def test_path_quoting_stupid(self): repo = self.test_path_quoting(True) def test_identical_fixtures(self): '''ensure that the non_ascii_path_N fixtures are identical''' fixturepaths = [ os.path.join(test_util.FIXTURES, 'non_ascii_path_1.svndump'), os.path.join(test_util.FIXTURES, 'non_ascii_path_2.svndump'), ] self.assertMultiLineEqual(open(fixturepaths[0]).read(), open(fixturepaths[1]).read()) def test_invalid_message(self): repo = self._load_fixture_and_fetch('invalid_utf8.tar.gz') # changelog returns descriptions in local encoding desc = encoding.fromlocal(repo[0].description()) self.assertEqual(desc.decode('utf8'), u'bl\xe5b\xe6rgr\xf8d') class TestStupidPull(test_util.TestBase): def test_stupid(self): 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()), '1083037b18d85cd84fa211c5adbaeff0fea2cd9f') self.assertEqual(node.hex(repo['the_branch'].node()), '4e256962fc5df545e2e0a51d0d1dc61c469127e6') self.assertEqual(repo['the_branch'].extra()['convert_revision'], 'svn:df2126f7-00ab-4d49-b42c-7e981dde0bcf/branches/the_branch@5') self.assertEqual(node.hex(repo['the_branch'].parents()[0].node()), 'f1ff5b860f5dbb9a59ad0921a79da77f10f25109') self.assertEqual(len(repo['tip'].parents()), 1) self.assertEqual(repo['default'].extra()['convert_revision'], 'svn:df2126f7-00ab-4d49-b42c-7e981dde0bcf/trunk@6') self.assertEqual(repo['tip'], repo['default']) self.assertEqual(len(repo.heads()), 2) def test_oldest_not_trunk_and_tag_vendor_branch(self): repo = self._load_fixture_and_fetch( 'tagged_vendor_and_oldest_not_trunk.svndump', stupid=True) self.assertEqual(node.hex(repo['oldest'].node()), '926671740dec045077ab20f110c1595f935334fa') self.assertEqual(repo['tip'].parents()[0].parents()[0], repo['oldest']) self.assertEqual(node.hex(repo['tip'].node()), '1a6c3f30911d57abb67c257ec0df3e7bc44786f7') def test_empty_repo(self, stupid=False): # This used to crash HgEditor because it could be closed without # having been initialized again. self._load_fixture_and_fetch('emptyrepo2.svndump', stupid=stupid) def test_empty_repo_stupid(self): self.test_empty_repo(stupid=True) def test_fetch_revert(self, stupid=False): repo = self._load_fixture_and_fetch('revert.svndump', stupid=stupid) graph = self.getgraph(repo) refgraph = """\ o changeset: 3:937dcd1206d4 | branch: | tags: tip | summary: revert2 | files: a dir/b | o changeset: 2:9317a748b7c3 | branch: | tags: | summary: revert | files: a dir/b | o changeset: 1:243259a4138a | branch: | tags: | summary: changefiles | files: a dir/b | o changeset: 0:ab86791fc857 branch: tags: summary: init files: a dir/b """ self.assertEqual(refgraph.strip(), graph.strip()) def test_fetch_revert_stupid(self): self.test_fetch_revert(stupid=True) def test_fetch_movetotrunk(self, stupid=False): repo = self._load_fixture_and_fetch('movetotrunk.svndump', stupid=stupid, subdir='sub1/sub2') graph = self.getgraph(repo) refgraph = """\ o changeset: 0:02996a5980ba branch: tags: tip summary: move to trunk files: a dir/b """ self.assertEqual(refgraph.strip(), graph.strip()) def test_fetch_movetotrunk_stupid(self): self.test_fetch_movetotrunk(stupid=True) def suite(): all_tests = [unittest.TestLoader().loadTestsFromTestCase(TestBasicRepoLayout), unittest.TestLoader().loadTestsFromTestCase(TestStupidPull), ] return unittest.TestSuite(all_tests) durin42-hgsubversion-77b22e5b4ea6/tests/test_fetch_command_regexes.py0000644000000000000000000001102112043462603024103 0ustar 00000000000000import unittest from hgsubversion import stupid two_empties = """Index: __init__.py =================================================================== Index: bar/__init__.py =================================================================== Index: bar/test_muhaha.py =================================================================== --- bar/test_muhaha.py (revision 0) +++ bar/test_muhaha.py (revision 1) @@ -0,0 +1,2 @@ + +blah blah blah, I'm a fake patch \ No newline at end of file """ binary_delta = """Index: trunk/functional_tests/doc_tests/test_doctest_fixtures/doctest_fixtures_fixtures.pyc =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: trunk/functional_tests/doc_tests/test_doctest_fixtures/doctest_fixtures_fixtures.pyc ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Index: trunk/functional_tests/doc_tests/test_doctest_fixtures/doctest_fixtures.rst =================================================================== """ special_delta = """Index: delta =================================================================== --- delta(revision 0) +++ delta(revision 9) @@ -0,0 +1 @@ +link alpha \ No newline at end of file Property changes on: delta ___________________________________________________________________ Name: svn:special + * """ class RegexTests(unittest.TestCase): def test_empty_file_re(self): 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 =================================================================== ''' changed = stupid.parsediff(pat) self.assertEqual(['trunk/django/contrib/admin/urls/__init__.py'], [f.name for f in changed]) def test_special_re(self): changed = stupid.parsediff(special_delta) self.assertEqual(1, len(changed)) self.assert_(changed[0].symlink) def test_any_file_re(self): 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): 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) durin42-hgsubversion-77b22e5b4ea6/tests/test_fetch_exec.py0000644000000000000000000000247412043462603021703 0ustar 00000000000000import test_util import unittest from mercurial import node class TestFetchExec(test_util.TestBase): 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=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) self.assertexec(repo[1], ['text2', 'binary2', 'empty2'], True) def test_exec_stupid(self): 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=stupid) self.assertEqual(node.hex(repo['tip'].node()), '08e6b380bf291b361a418203a1cb9427213cd1fd') self.assertEqual(repo['tip']['foo'].flags(), 'x') def test_empty_prop_val_executable_stupid(self): self.test_empty_prop_val_executable(True) def suite(): all_tests = [unittest.TestLoader().loadTestsFromTestCase(TestFetchExec), ] return unittest.TestSuite(all_tests) durin42-hgsubversion-77b22e5b4ea6/tests/test_fetch_mappings.py0000644000000000000000000003651112043462603022574 0ustar 00000000000000"""Tests for author maps and file maps. """ import test_util import os import unittest from mercurial import commands from mercurial import hg from mercurial import node from mercurial import util as hgutil from hgsubversion import maps from hgsubversion import svncommands from hgsubversion import util from hgsubversion import verify class MapTests(test_util.TestBase): @property def authors(self): return os.path.join(self.tmpdir, 'authormap') @property def filemap(self): return os.path.join(self.tmpdir, 'filemap') @property def branchmap(self): return os.path.join(self.tmpdir, 'branchmap') @property def tagmap(self): return os.path.join(self.tmpdir, 'tagmap') def test_author_map(self, stupid=False): repo_path = self.load_svndump('replace_trunk_with_branch.svndump') authormap = open(self.authors, 'w') authormap.write('Augie=Augie Fackler # stuffy\n') authormap.write("Augie Fackler \n") authormap.close() ui = self.ui(stupid) ui.setconfig('hgsubversion', 'authormap', self.authors) commands.clone(ui, test_util.fileurl(repo_path), self.wc_path, authors=self.authors) self.assertEqual(self.repo[0].user(), 'Augie Fackler ') self.assertEqual(self.repo['tip'].user(), 'evil@5b65bade-98f3-4993-a01f-b7a6710da339') def test_author_map_stupid(self): self.test_author_map(True) def test_author_map_closing_author(self, stupid=False): repo_path = self.load_svndump('replace_trunk_with_branch.svndump') authormap = open(self.authors, 'w') authormap.write("evil=Testy ") authormap.close() ui = self.ui(stupid) ui.setconfig('hgsubversion', 'authormap', self.authors) 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') self.assertEqual(self.repo['tip'].user(), 'Testy ') def test_author_map_closing_author_stupid(self): self.test_author_map_closing_author(True) def test_author_map_no_author(self, stupid=False): 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) test_util.rmtree(self.wc_path) authormap = open(self.authors, 'w') authormap.write("(no author)=Testy ") authormap.close() ui = self.ui(stupid) ui.setconfig('hgsubversion', 'authormap', self.authors) 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 '] self.assertEqual(sorted(users), expected_users) def test_author_map_no_author_stupid(self): self.test_author_map_no_author(True) def test_author_map_no_overwrite(self): cwd = os.path.dirname(__file__) orig = os.path.join(cwd, 'fixtures', 'author-map-test.txt') new = open(self.authors, 'w') new.write(open(orig).read()) new.close() test = maps.AuthorMap(self.ui(), self.authors) fromself = set(test) test.load(orig) all_tests = set(test) self.assertEqual(fromself.symmetric_difference(all_tests), set()) def _loadwithfilemap(self, svndump, filemapcontent, stupid=False, failonmissing=True): repo_path = self.load_svndump(svndump) filemap = open(self.filemap, 'w') filemap.write(filemapcontent) filemap.close() ui = self.ui(stupid) ui.setconfig('hgsubversion', 'filemap', self.filemap) ui.setconfig('hgsubversion', 'failoninvalidreplayfile', 'true') ui.setconfig('hgsubversion', 'failonmissing', failonmissing) commands.clone(ui, test_util.fileurl(repo_path), self.wc_path, filemap=self.filemap) return self.repo def test_file_map(self, stupid=False): repo = self._loadwithfilemap('replace_trunk_with_branch.svndump', "include alpha\n", stupid) self.assertEqual(node.hex(repo[0].node()), '88e2c7492d83e4bf30fbb2dcbf6aa24d60ac688d') self.assertEqual(node.hex(repo['default'].node()), 'e524296152246b3837fe9503c83b727075835155') def test_file_map_stupid(self): # TODO: re-enable test if we ever reinstate this feature self.assertRaises(hgutil.Abort, self.test_file_map, True) def test_file_map_exclude(self, stupid=False): repo = self._loadwithfilemap('replace_trunk_with_branch.svndump', "exclude alpha\n", stupid) self.assertEqual(node.hex(repo[0].node()), '2c48f3525926ab6c8b8424bcf5eb34b149b61841') self.assertEqual(node.hex(repo['default'].node()), 'b37a3c0297b71f989064d9b545b5a478bbed7cc1') def test_file_map_exclude_stupid(self): # 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 = self._loadwithfilemap('replace_trunk_with_branch.svndump', "exclude alpha\ninclude .\nexclude gamma\n") # 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_file_map_copy(self): # Exercise excluding files copied from a non-excluded directory. # There will be missing files as we are copying from an excluded # directory. repo = self._loadwithfilemap('copies.svndump', "exclude dir2\n", failonmissing=False) self.assertEqual(['dir/a', 'dir3/a'], list(repo[2])) def test_file_map_exclude_copy_source_and_dest(self): # dir3 is excluded and copied from dir2 which is also excluded. # dir3 files should not be marked as missing and fetched. repo = self._loadwithfilemap('copies.svndump', "exclude dir2\nexclude dir3\n") self.assertEqual(['dir/a'], list(repo[2])) def test_file_map_include_file_exclude_dir(self): # dir3 is excluded but we want dir3/a, which is also copied from # an exluded dir2. dir3/a should be fetched. repo = self._loadwithfilemap('copies.svndump', "include .\nexclude dir2\nexclude dir3\ninclude dir3/a\n", failonmissing=False) self.assertEqual(['dir/a', 'dir3/a'], list(repo[2])) def test_file_map_delete_dest(self): repo = self._loadwithfilemap('copies.svndump', 'exclude dir3\n') self.assertEqual(['dir/a', 'dir2/a'], list(repo[3])) def test_branchmap(self, stupid=False): 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(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) self.assert_('good-name' in branches) self.assertEquals(self.repo[2].branch(), 'default') def test_branchmap_stupid(self): self.test_branchmap(True) def test_branchmap_tagging(self, stupid=False): '''test tagging a renamed branch, which used to raise an exception''' 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(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']) def test_branchmap_tagging_stupid(self): self.test_branchmap_tagging(True) def test_branchmap_empty_commit(self, stupid=False): '''test mapping an empty commit on a renamed branch''' 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(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']) def test_branchmap_empty_commit_stupid(self): '''test mapping an empty commit on a renamed branch (stupid)''' self.test_branchmap_empty_commit(True) def test_branchmap_combine(self, stupid=False): '''test combining two branches, but retaining heads''' 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(repo_path), self.wc_path, branchmap=self.branchmap) branches = set(self.repo[i].branch() for i in self.repo) self.assertEquals(sorted(branches), ['default']) self.assertEquals(len(self.repo.heads()), 2) self.assertEquals(len(self.repo.branchheads('default')), 2) # test that the mapping does not affect branch info branches = self.repo.svnmeta().branches self.assertEquals(sorted(branches.keys()), [None, 'badname', 'feature']) def test_branchmap_combine_stupid(self): '''test combining two branches, but retaining heads (stupid)''' self.test_branchmap_combine(True) def test_branchmap_rebuildmeta(self, stupid=False): '''test rebuildmeta on a branchmapped clone''' 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(repo_path), self.wc_path, branchmap=self.branchmap) originfo = self.repo.svnmeta().branches # clone & rebuild ui = self.ui(stupid) src, dest = test_util.hgclone(ui, self.wc_path, self.wc_path + '_clone', update=False) src = test_util.getlocalpeer(src) dest = test_util.getlocalpeer(dest) svncommands.rebuildmeta(ui, dest, 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 self.assertEquals(sorted(src.svnmeta().branches), sorted(dest.svnmeta().branches)) def test_branchmap_rebuildmeta_stupid(self): '''test rebuildmeta on a branchmapped clone (stupid)''' self.test_branchmap_rebuildmeta(True) def test_branchmap_verify(self, stupid=False): '''test verify on a branchmapped clone''' 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(repo_path), self.wc_path, branchmap=self.branchmap) repo = self.repo for r in repo: self.assertEquals(verify.verify(ui, repo, rev=r), 0) def test_branchmap_verify_stupid(self): '''test verify on a branchmapped clone (stupid)''' self.test_branchmap_verify(True) def test_branchmap_no_replacement(self): ''' test that empty mappings are rejected Empty mappings are lines like 'this ='. The most sensible thing to do is to not convert the 'this' branches. Until we can do that, we settle with aborting. ''' repo_path = self.load_svndump('propset-branch.svndump') branchmap = open(self.branchmap, 'w') branchmap.write("closeme =\n") branchmap.close() self.assertRaises(hgutil.Abort, maps.BranchMap, self.ui(), self.branchmap) def test_tagmap(self, stupid=False): 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") tagmap.close() ui = self.ui(stupid) ui.setconfig('hgsubversion', 'tagmap', self.tagmap) commands.clone(ui, test_util.fileurl(repo_path), self.wc_path, tagmap=self.tagmap) tags = self.repo.tags() assert 'tag_r3' not in tags assert '3.x' in tags assert 'copied_tag' not in tags def test_tagmap_stupid(self): self.test_tagmap(True) def test_tagren_changed(self, stupid=False): 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") tagmap.write("will-edit = edit-future\n") tagmap.close() ui = self.ui(stupid) ui.setconfig('hgsubversion', 'tagmap', self.tagmap) commands.clone(ui, test_util.fileurl(repo_path), self.wc_path, tagmap=self.tagmap) tags = self.repo.tags() def test_tagren_changed_stupid(self): self.test_tagren_changed(True) def test_empty_log_message(self, stupid=False): repo, repo_path = self.load_and_fetch('empty-log-message.svndump', stupid=stupid) self.assertEqual(repo['tip'].description(), '') test_util.rmtree(self.wc_path) ui = self.ui(stupid) ui.setconfig('hgsubversion', 'defaultmessage', 'blyf') commands.clone(ui, test_util.fileurl(repo_path), self.wc_path) self.assertEqual(self.repo['tip'].description(), 'blyf') def test_empty_log_message_stupid(self): self.test_empty_log_message(True) def suite(): return unittest.TestLoader().loadTestsFromTestCase(MapTests) durin42-hgsubversion-77b22e5b4ea6/tests/test_fetch_renames.py0000644000000000000000000000527712043462603022415 0ustar 00000000000000import test_util import sys import unittest class TestFetchRenames(test_util.TestBase): def _debug_print_copies(self, repo): w = sys.stderr.write for rev in repo: ctx = repo[rev] w('%d - %s\n' % (ctx.rev(), ctx.branch())) for f in ctx: fctx = ctx[f] w('%s: %r %r\n' % (f, fctx.data(), fctx.renamed())) def _test_rename(self, stupid): config = { 'hgsubversion.filestoresize': '0', } repo = self._load_fixture_and_fetch('renames.svndump', stupid=stupid, config=config) # Map revnum to mappings of dest name to (source name, dest content) copies = { 4: { 'a1': ('a', 'a\n'), 'a2': ('a', 'a\n'), 'b1': ('b', 'b\nc\n'), 'da1/daf': ('da/daf', 'c\n'), 'da1/db/dbf': ('da/db/dbf', 'd\n'), 'da2/daf': ('da/daf', 'c\n'), 'da2/db/dbf': ('da/db/dbf', 'd\n'), }, 5: { 'c1': ('c', 'c\nc\n'), }, 9: { 'unchanged2': ('unchanged', 'unchanged\n'), 'unchangeddir2/f': ('unchangeddir/f', 'unchanged2\n'), }, 10: { 'groupdir2/b': ('groupdir/b', 'b\n') }, } for rev in repo: ctx = repo[rev] copymap = copies.get(rev, {}) for f in ctx.manifest(): cp = ctx[f].renamed() self.assertEqual(bool(cp), bool(copymap.get(f)), 'copy records differ for %s in %d' % (f, rev)) if not cp: continue self.assertEqual(cp[0], copymap[f][0]) self.assertEqual(ctx[f].data(), copymap[f][1]) self.assertEqual(repo['tip']['changed3'].data(), 'changed\nchanged3\n') def test_rename(self): self._test_rename(False) def test_rename_stupid(self): self._test_rename(True) def _test_case(self, stupid): repo = self._load_fixture_and_fetch('filecase.svndump', stupid=stupid) files = { 0: ['A', 'a', 'e/a', 'b', 'd/a', 'D/a', 'f/a', 'F'], 1: ['A', 'a', 'E/a', 'B', 'd/A', 'D/a', 'f/a', 'F'], } for rev in repo: self.assertEqual(sorted(files[rev]), sorted(repo[rev].manifest())) def test_case(self): self._test_case(False) def test_case_stupid(self): self._test_case(True) def suite(): all_tests = [unittest.TestLoader().loadTestsFromTestCase(TestFetchRenames), ] return unittest.TestSuite(all_tests) durin42-hgsubversion-77b22e5b4ea6/tests/test_fetch_symlinks.py0000644000000000000000000000372512043462603022630 0ustar 00000000000000import test_util import unittest class TestFetchSymlinks(test_util.TestBase): def test_symlinks(self, stupid=False): repo = self._load_fixture_and_fetch('symlinks.svndump', stupid=stupid) # Check symlinks throughout history links = { 0: { 'linka': 'a', 'linka2': 'a', 'd/linka': 'a', }, 1: { 'linkaa': 'a', 'linka2': 'a', 'd2/linka': 'a', }, 2: { 'linkaa': 'b', 'linka2': 'a', 'd2/linka': 'b', }, 3: { }, 4: { 'linka3': 'a', }, 5: { 'linka3': 'a', }, 6: { 'linka3': 'a', 'linka4': 'link to this', }, } for rev in repo: ctx = repo[rev] for f in ctx.manifest(): 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]: self.assertTrue(f in ctx) def test_symlinks_stupid(self): self.test_symlinks(True) class TestMergeSpecial(test_util.TestBase): def test_special(self): repo = self._load_fixture_and_fetch('addspecial.svndump', subdir='trunk') ctx = repo['tip'] self.assertEqual(ctx['fnord'].flags(), 'l') self.assertEqual(ctx['exe'].flags(), 'x') def suite(): all_tests = [ unittest.TestLoader().loadTestsFromTestCase(TestFetchSymlinks), unittest.TestLoader().loadTestsFromTestCase(TestMergeSpecial), ] return unittest.TestSuite(all_tests) durin42-hgsubversion-77b22e5b4ea6/tests/test_fetch_truncated.py0000644000000000000000000000234712043462603022747 0ustar 00000000000000import test_util import unittest from mercurial import commands from mercurial import hg class TestFetchTruncatedHistory(test_util.TestBase): def test_truncated_history(self, stupid=False): # Test repository does not follow the usual layout 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) # We are converting /project2/trunk coming from: # # Changed paths: # D /project1 # A /project2/trunk (from /project1:2) # # Here a full fetch should be performed since we are starting # the conversion on an already filled branch. tip = repo['tip'] files = tip.manifest().keys() files.sort() self.assertEqual(files, ['a', 'b']) self.assertEqual(repo['tip']['a'].data(), 'a\n') def test_truncated_history_stupid(self): self.test_truncated_history(True) def suite(): all_tests = [unittest.TestLoader().loadTestsFromTestCase(TestFetchTruncatedHistory), ] return unittest.TestSuite(all_tests) durin42-hgsubversion-77b22e5b4ea6/tests/test_helpers.py0000644000000000000000000000231012043462603021235 0ustar 00000000000000import os, sys, unittest _rootdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, _rootdir) from hgsubversion import editor class TestHelpers(unittest.TestCase): def test_filestore(self): fs = editor.FileStore(2) fs.setfile('a', 'a') fs.setfile('b', 'b') self.assertEqual('a', fs._data.get('a')) self.assertEqual('b', fs._data.get('b')) fs.delfile('b') self.assertRaises(IOError, lambda: fs.getfile('b')) fs.setfile('bb', 'bb') self.assertTrue('bb' in fs._files) self.assertTrue('bb' not in fs._data) self.assertEqual('bb', fs.getfile('bb')) fs.delfile('bb') self.assertTrue('bb' not in fs._files) self.assertEqual([], os.listdir(fs._tempdir)) self.assertRaises(IOError, lambda: fs.getfile('bb')) fs.setfile('bb', 'bb') self.assertEqual(1, len(os.listdir(fs._tempdir))) fs.popfile('bb') self.assertEqual([], os.listdir(fs._tempdir)) self.assertRaises(editor.EditingError, lambda: fs.getfile('bb')) def suite(): return unittest.TestSuite([ unittest.TestLoader().loadTestsFromTestCase(TestHelpers), ]) durin42-hgsubversion-77b22e5b4ea6/tests/test_hooks.py0000644000000000000000000000333012043462603020721 0ustar 00000000000000import sys import test_util import unittest from mercurial import hg from mercurial import commands class TestHooks(test_util.TestBase): def setUp(self): super(TestHooks, self).setUp() def _loadupdate(self, fixture_name, *args, **kwargs): kwargs = kwargs.copy() kwargs.update(stupid=False, noupdate=False) repo, repo_path = self.load_and_fetch(fixture_name, *args, **kwargs) return repo, repo_path def test_updatemetahook(self): repo, repo_path = self._loadupdate('single_rev.svndump') state = repo.parents() self.add_svn_rev(repo_path, {'trunk/alpha': 'Changed'}) commands.pull(self.repo.ui, self.repo) # Clone to a new repository and add a hook new_wc_path = "%s-2" % self.wc_path commands.clone(self.repo.ui, self.wc_path, new_wc_path) newrepo = hg.repository(test_util.testui(), new_wc_path) newrepo.ui.setconfig('hooks', 'changegroup.meta', 'python:hgsubversion.hooks.updatemeta.hook') # Commit a rev that should trigger svn meta update self.add_svn_rev(repo_path, {'trunk/alpha': 'Changed Again'}) commands.pull(self.repo.ui, self.repo) self.called = False import hgsubversion.svncommands oldupdatemeta = hgsubversion.svncommands.updatemeta def _updatemeta(ui, repo, args=[]): self.called = True hgsubversion.svncommands.updatemeta = _updatemeta # Pull and make sure our updatemeta function gets called commands.pull(newrepo.ui, newrepo) hgsubversion.svncommands.updatemeta = oldupdatemeta self.assertTrue(self.called) def suite(): return unittest.findTestCases(sys.modules[__name__]) durin42-hgsubversion-77b22e5b4ea6/tests/test_pull.py0000644000000000000000000000644612043462603020565 0ustar 00000000000000import test_util import os.path import subprocess from mercurial import node from mercurial import ui from mercurial import util as hgutil from mercurial import commands from hgsubversion import verify class TestPull(test_util.TestBase): def setUp(self): super(TestPull, self).setUp() def _loadupdate(self, fixture_name, *args, **kwargs): kwargs = kwargs.copy() kwargs.update(stupid=False, noupdate=False) repo, repo_path = self.load_and_fetch(fixture_name, *args, **kwargs) return repo, repo_path def test_nochanges(self): 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, repo_path = self._loadupdate('single_rev.svndump') state = repo.parents() 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, repo_path = self._loadupdate('single_rev.svndump') state = repo.parents() 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, repo_path = self._loadupdate('single_rev.svndump') self.commitchanges((('alpha', 'alpha', 'Changed another way'),)) state = repo.parents() self.add_svn_rev(repo_path, {'trunk/alpha': 'Changed one way'}) try: commands.pull(self.repo.ui, repo, update=True) except hgutil.Abort: # hg < 1.9 raised when crossing branches pass self.assertEqual(state, repo.parents()) self.assertTrue('tip' not in repo[None].tags()) self.assertEqual(len(repo.heads()), 2) def test_tag_repull_doesnt_happen(self): 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())) def test_skip_basic(self): repo, repo_path = self._loadupdate('single_rev.svndump') self.add_svn_rev(repo_path, {'trunk/alpha': 'Changed'}) self.add_svn_rev(repo_path, {'trunk/beta': 'More changed'}) self.add_svn_rev(repo_path, {'trunk/gamma': 'Even more changeder'}) repo.ui.setconfig('hgsubversion', 'unsafeskip', '3 4') commands.pull(repo.ui, repo) tip = repo['tip'].rev() self.assertEqual(tip, 1) self.assertEquals(verify.verify(repo.ui, repo, rev=tip), 1) def test_skip_delete_restore(self): repo, repo_path = self._loadupdate('delete_restore_trunk.svndump', rev=2) repo.ui.setconfig('hgsubversion', 'unsafeskip', '3 4') commands.pull(repo.ui, repo) tip = repo['tip'].rev() self.assertEqual(tip, 1) self.assertEquals(verify.verify(repo.ui, repo, rev=tip), 0) def suite(): import unittest, sys return unittest.findTestCases(sys.modules[__name__]) durin42-hgsubversion-77b22e5b4ea6/tests/test_pull_fallback.py0000644000000000000000000000560212043462603022375 0ustar 00000000000000import test_util import re import mercurial from mercurial import commands from hgsubversion import stupid from hgsubversion import svnwrap from hgsubversion import wrappers class TestPullFallback(test_util.TestBase): def setUp(self): super(TestPullFallback, self).setUp() def _loadupdate(self, fixture_name, *args, **kwargs): kwargs = kwargs.copy() kwargs.update(noupdate=False) repo, repo_path = self.load_and_fetch(fixture_name, *args, **kwargs) return repo, repo_path def test_stupid_fallback_to_stupid_fullrevs(self): return to_patch = { 'mercurial.patch.patchbackend': _patchbackend_raise, 'stupid.diff_branchrev': stupid.diff_branchrev, 'stupid.fetch_branchrev': stupid.fetch_branchrev, } expected_calls = { 'mercurial.patch.patchbackend': 1, 'stupid.diff_branchrev': 1, 'stupid.fetch_branchrev': 1, } repo, repo_path = self._loadupdate( 'single_rev.svndump', stupid=True) # Passing stupid=True doesn't seem to be working - force it repo.ui.setconfig('hgsubversion', 'stupid', "true") state = repo.parents() calls, replaced = _monkey_patch(to_patch) try: 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()) self.assertEqual(expected_calls, calls) finally: _monkey_unpatch(replaced) def _monkey_patch(to_patch, start=None): if start is None: import sys start = sys.modules[__name__] calls = {} replaced = {} for path, replacement in to_patch.iteritems(): obj = start owner, attr = path.rsplit('.', 1) for a in owner.split('.', -1): obj = getattr(obj, a) replaced[path] = getattr(obj, attr) calls[path] = 0 def outer(path=path, calls=calls, replacement=replacement): def wrapper(*p, **kw): calls[path] += 1 return replacement(*p, **kw) return wrapper setattr(obj, attr, outer()) return calls, replaced def _monkey_unpatch(to_patch, start=None): if start is None: import sys start = sys.modules[__name__] replaced = {} for path, replacement in to_patch.iteritems(): obj = start owner, attr = path.rsplit('.', 1) for a in owner.split('.', -1): obj = getattr(obj, a) replaced[path] = getattr(obj, attr) setattr(obj, attr, replacement) return replaced def _patchbackend_raise(*p, **kw): raise mercurial.patch.PatchError("patch failed") def suite(): import unittest, sys return unittest.findTestCases(sys.modules[__name__]) durin42-hgsubversion-77b22e5b4ea6/tests/test_push_autoprops.py0000644000000000000000000000745312043462603022703 0ustar 00000000000000import subprocess import sys import unittest import os import test_util from hgsubversion import svnwrap class PushAutoPropsTests(test_util.TestBase): def setUp(self): test_util.TestBase.setUp(self) repo, self.repo_path = self.load_and_fetch('emptyrepo.svndump') def test_push_honors_svn_autoprops(self): self.setup_svn_config( "[miscellany]\n" "enable-auto-props = yes\n" "[auto-props]\n" "*.py = test:prop=success\n") changes = [('test.py', 'test.py', 'echo hallo')] self.commitchanges(changes) self.pushrevisions(True) prop_val = test_util.svnpropget( self.repo_path, "trunk/test.py", 'test:prop') self.assertEqual('success', prop_val) class AutoPropsConfigTest(test_util.TestBase): def test_use_autoprops_for_matching_file_when_enabled(self): self.setup_svn_config( "[miscellany]\n" "enable-auto-props = yes\n" "[auto-props]\n" "*.py = test:prop=success\n") props = self.new_autoprops_config().properties('xxx/test.py') self.assertEqual({ 'test:prop': 'success'}, props) def new_autoprops_config(self): return svnwrap.AutoPropsConfig(self.config_dir) def test_ignore_nonexisting_config(self): config_file = os.path.join(self.config_dir, 'config') os.remove(config_file) self.assertTrue(not os.path.exists(config_file)) props = self.new_autoprops_config().properties('xxx/test.py') self.assertEqual({}, props) def test_ignore_autoprops_when_file_doesnt_match(self): self.setup_svn_config( "[miscellany]\n" "enable-auto-props = yes\n" "[auto-props]\n" "*.py = test:prop=success\n") props = self.new_autoprops_config().properties('xxx/test.sh') self.assertEqual({}, props) def test_ignore_autoprops_when_disabled(self): self.setup_svn_config( "[miscellany]\n" "#enable-auto-props = yes\n" "[auto-props]\n" "*.py = test:prop=success\n") props = self.new_autoprops_config().properties('xxx/test.py') self.assertEqual({}, props) def test_combine_properties_of_multiple_matches(self): self.setup_svn_config( "[miscellany]\n" "enable-auto-props = yes\n" "[auto-props]\n" "*.py = test:prop=success\n" "test.* = test:prop2=success\n") props = self.new_autoprops_config().properties('xxx/test.py') self.assertEqual({ 'test:prop': 'success', 'test:prop2': 'success'}, props) class ParseAutoPropsTests(test_util.TestBase): def test_property_value_is_optional(self): props = svnwrap.parse_autoprops("svn:executable") self.assertEqual({'svn:executable': ''}, props) props = svnwrap.parse_autoprops("svn:executable=") self.assertEqual({'svn:executable': ''}, props) def test_property_value_may_be_quoted(self): props = svnwrap.parse_autoprops("svn:eol-style=\" native \"") self.assertEqual({'svn:eol-style': ' native '}, props) props = svnwrap.parse_autoprops("svn:eol-style=' native '") self.assertEqual({'svn:eol-style': ' native '}, props) def test_surrounding_whitespaces_are_ignored(self): props = svnwrap.parse_autoprops(" svn:eol-style = native ") self.assertEqual({'svn:eol-style': 'native'}, props) def test_multiple_properties_are_separated_by_semicolon(self): props = svnwrap.parse_autoprops( "svn:eol-style=native;svn:executable=true\n") self.assertEqual({ 'svn:eol-style': 'native', 'svn:executable': 'true'}, props) def suite(): return unittest.findTestCases(sys.modules[__name__]) durin42-hgsubversion-77b22e5b4ea6/tests/test_push_command.py0000644000000000000000000005570612043462603022271 0ustar 00000000000000import test_util import atexit import errno import os import sys import random import shutil import socket import subprocess import unittest from mercurial import context from mercurial import commands from mercurial import hg from mercurial import node from mercurial import revlog from mercurial import util as hgutil import time class PushTests(test_util.TestBase): def setUp(self): test_util.TestBase.setUp(self) self.repo_path = self.load_and_fetch('simple_branch.svndump')[1] def test_cant_push_empty_ctx(self): repo = self.repo def file_callback(repo, memctx, path): if path == 'adding_file': return context.memfilectx(path=path, data='foo', islink=False, isexec=False, copied=False) raise IOError() ctx = context.memctx(repo, (repo['default'].node(), node.nullid), 'automated test', [], file_callback, 'an_author', '2008-10-07 20:59:48 -0500', {'branch': 'default', }) new_hash = repo.commitctx(ctx) hg.update(repo, repo['tip'].node()) old_tip = repo['tip'].node() self.pushrevisions() tip = self.repo['tip'] self.assertEqual(tip.node(), old_tip) def test_cant_push_with_changes(self): repo = self.repo def file_callback(repo, memctx, path): return context.memfilectx( path=path, data='foo', islink=False, isexec=False, copied=False) ctx = context.memctx(repo, (repo['default'].node(), node.nullid), 'automated test', ['adding_file'], file_callback, 'an_author', '2008-10-07 20:59:48 -0500', {'branch': 'default', }) new_hash = repo.commitctx(ctx) hg.update(repo, repo['tip'].node()) # Touch an existing file repo.wwrite('beta', 'something else', '') try: self.pushrevisions() except hgutil.Abort: pass tip = self.repo['tip'] self.assertEqual(new_hash, tip.node()) def internal_push_over_svnserve(self, subdir='', commit=True): 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' % repo_path] svnserve = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) self.svnserve_pid = svnserve.pid try: time.sleep(2) import shutil shutil.rmtree(self.wc_path) commands.clone(self.ui(), 'svn://%s:%d/%s' % (self.host, self.port, subdir), self.wc_path, noupdate=True) repo = self.repo old_tip = repo['tip'].node() expected_parent = repo['default'].node() def file_callback(repo, memctx, path): if path == 'adding_file': return context.memfilectx(path=path, data='foo', islink=False, isexec=False, copied=False) raise IOError(errno.EINVAL, 'Invalid operation: ' + path) ctx = context.memctx(repo, parents=(repo['default'].node(), node.nullid), text='automated test', files=['adding_file'], filectxfn=file_callback, user='an_author', date='2008-10-07 20:59:48 -0500', extra={'branch': 'default', }) new_hash = repo.commitctx(ctx) if not commit: return # some tests use this test as an extended setup. hg.update(repo, repo['tip'].node()) oldauthor = repo['tip'].user() commands.push(repo.ui, repo) tip = self.repo['tip'] self.assertNotEqual(oldauthor, tip.user()) self.assertNotEqual(tip.node(), old_tip) self.assertEqual(tip.parents()[0].node(), expected_parent) self.assertEqual(tip['adding_file'].data(), 'foo') self.assertEqual(tip.branch(), 'default') # unintended behaviour: self.assertNotEqual('an_author', tip.user()) self.assertEqual('(no author)', tip.user().rsplit('@', 1)[0]) finally: 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() def test_push_over_svnserve_with_subdir(self): self.internal_push_over_svnserve(subdir='///branches////the_branch/////') def test_push_to_default(self, commit=True): repo = self.repo old_tip = repo['tip'].node() expected_parent = repo['default'].node() def file_callback(repo, memctx, path): if path == 'adding_file': return context.memfilectx(path=path, data='foo', islink=False, isexec=False, copied=False) raise IOError(errno.EINVAL, 'Invalid operation: ' + path) ctx = context.memctx(repo, (repo['default'].node(), node.nullid), 'automated test', ['adding_file'], file_callback, 'an_author', '2008-10-07 20:59:48 -0500', {'branch': 'default', }) new_hash = repo.commitctx(ctx) if not commit: return # some tests use this test as an extended setup. hg.update(repo, repo['tip'].node()) self.pushrevisions() tip = self.repo['tip'] self.assertNotEqual(tip.node(), old_tip) self.assertEqual(node.hex(tip.parents()[0].node()), node.hex(expected_parent)) self.assertEqual(tip['adding_file'].data(), 'foo') self.assertEqual(tip.branch(), 'default') def test_push_two_revs_different_local_branch(self): def filectxfn(repo, memctx, path): return context.memfilectx(path=path, data=path, islink=False, isexec=False, copied=False) oldtiphash = self.repo['default'].node() ctx = context.memctx(self.repo, (self.repo[0].node(), revlog.nullid,), 'automated test', ['gamma', ], filectxfn, 'testy', '2008-12-21 16:32:00 -0500', {'branch': 'localbranch', }) newhash = self.repo.commitctx(ctx) ctx = context.memctx(self.repo, (newhash, revlog.nullid), 'automated test2', ['delta', ], filectxfn, 'testy', '2008-12-21 16:32:00 -0500', {'branch': 'localbranch', }) newhash = self.repo.commitctx(ctx) repo = self.repo hg.update(repo, newhash) commands.push(repo.ui, repo) self.assertEqual(self.repo['tip'].parents()[0].parents()[0].node(), oldtiphash) self.assertEqual(self.repo['tip'].files(), ['delta', ]) self.assertEqual(self.repo['tip'].manifest().keys(), ['alpha', 'beta', 'gamma', 'delta']) def test_push_two_revs(self): # set up some work for us self.test_push_to_default(commit=False) repo = self.repo old_tip = repo['tip'].node() expected_parent = repo['tip'].parents()[0].node() def file_callback(repo, memctx, path): if path == 'adding_file2': return context.memfilectx(path=path, data='foo2', islink=False, isexec=False, copied=False) raise IOError(errno.EINVAL, 'Invalid operation: ' + path) ctx = context.memctx(repo, (repo['default'].node(), node.nullid), 'automated test', ['adding_file2'], file_callback, 'an_author', '2008-10-07 20:59:48 -0500', {'branch': 'default', }) new_hash = repo.commitctx(ctx) hg.update(repo, repo['tip'].node()) self.pushrevisions() tip = self.repo['tip'] self.assertNotEqual(tip.node(), old_tip) self.assertNotEqual(tip.parents()[0].node(), old_tip) self.assertEqual(tip.parents()[0].parents()[0].node(), expected_parent) self.assertEqual(tip['adding_file2'].data(), 'foo2') self.assertEqual(tip['adding_file'].data(), 'foo') self.assertEqual(tip.parents()[0]['adding_file'].data(), 'foo') try: self.assertEqual(tip.parents()[0]['adding_file2'].data(), 'foo') assert False, "this is impossible, adding_file2 should not be in this manifest." except revlog.LookupError, e: pass self.assertEqual(tip.branch(), 'default') def test_push_to_branch(self, push=True): repo = self.repo def file_callback(repo, memctx, path): if path == 'adding_file': return context.memfilectx(path=path, data='foo', islink=False, isexec=False, copied=False) raise IOError(errno.EINVAL, 'Invalid operation: ' + path) ctx = context.memctx(repo, (repo['the_branch'].node(), node.nullid), 'automated test', ['adding_file'], file_callback, 'an_author', '2008-10-07 20:59:48 -0500', {'branch': 'the_branch', }) new_hash = repo.commitctx(ctx) hg.update(repo, repo['tip'].node()) if push: self.pushrevisions() tip = self.repo['tip'] self.assertNotEqual(tip.node(), new_hash) self.assertEqual(tip['adding_file'].data(), 'foo') self.assertEqual(tip.branch(), 'the_branch') def test_push_to_non_tip(self): self.test_push_to_branch(push=False) wc2path = self.wc_path + '_clone' u = self.repo.ui test_util.hgclone(self.repo.ui, self.wc_path, wc2path, update=False) res = self.pushrevisions() self.assertEqual(0, res) oldf = open(os.path.join(self.wc_path, '.hg', 'hgrc')) hgrc = oldf.read() oldf.close() shutil.rmtree(self.wc_path) test_util.hgclone(u, wc2path, self.wc_path, update=False) oldf = open(os.path.join(self.wc_path, '.hg', 'hgrc'), 'w') oldf.write(hgrc) oldf.close() # do a commit here self.commitchanges([('foobaz', 'foobaz', 'This file is added on default.',), ], parent='default', message='commit to default') from hgsubversion import svncommands svncommands.rebuildmeta(u, self.repo, args=[test_util.fileurl(self.repo_path)]) hg.update(self.repo, self.repo['tip'].node()) oldnode = self.repo['tip'].hex() self.pushrevisions(expected_extra_back=1) self.assertNotEqual(oldnode, self.repo['tip'].hex(), 'Revision was not pushed.') def test_delete_file(self): repo = self.repo def file_callback(repo, memctx, path): raise IOError(errno.ENOENT, '%s is deleted' % path) old_files = set(repo['default'].manifest().keys()) ctx = context.memctx(repo, (repo['default'].node(), node.nullid), 'automated test', ['alpha'], file_callback, 'an author', '2008-10-29 21:26:00 -0500', {'branch': 'default', }) new_hash = repo.commitctx(ctx) hg.update(repo, repo['tip'].node()) self.pushrevisions() tip = self.repo['tip'] self.assertEqual(old_files, set(tip.manifest().keys() + ['alpha'])) self.assert_('alpha' not in tip.manifest()) def test_push_executable_file(self): self.test_push_to_default(commit=True) repo = self.repo def file_callback(repo, memctx, path): if path == 'gamma': return context.memfilectx(path=path, data='foo', islink=False, isexec=True, copied=False) raise IOError(errno.EINVAL, 'Invalid operation: ' + path) ctx = context.memctx(repo, (repo['tip'].node(), node.nullid), 'message', ['gamma', ], file_callback, 'author', '2008-10-29 21:26:00 -0500', {'branch': 'default', }) new_hash = repo.commitctx(ctx) hg.clean(repo, repo['tip'].node()) self.pushrevisions() tip = self.repo['tip'] self.assertNotEqual(tip.node(), new_hash) self.assert_('@' in self.repo['tip'].user()) self.assertEqual(tip['gamma'].flags(), 'x') self.assertEqual(tip['gamma'].data(), 'foo') self.assertEqual([x for x in tip.manifest().keys() if 'x' not in tip[x].flags()], ['alpha', 'beta', 'adding_file', ]) def test_push_symlink_file(self): self.test_push_to_default(commit=True) repo = self.repo def file_callback(repo, memctx, path): if path == 'gamma': return context.memfilectx(path=path, data='foo', islink=True, isexec=False, copied=False) raise IOError(errno.EINVAL, 'Invalid operation: ' + path) ctx = context.memctx(repo, (repo['tip'].node(), node.nullid), 'message', ['gamma', ], file_callback, 'author', '2008-10-29 21:26:00 -0500', {'branch': 'default', }) new_hash = repo.commitctx(ctx) hg.update(repo, repo['tip'].node()) self.pushrevisions() tip = self.repo['tip'] self.assertNotEqual(tip.node(), new_hash) self.assertEqual(tip['gamma'].flags(), 'l') self.assertEqual(tip['gamma'].data(), 'foo') self.assertEqual([x for x in tip.manifest().keys() if 'l' not in tip[x].flags()], ['alpha', 'beta', 'adding_file', ]) def test_push_existing_file_newly_symlink(self): self.test_push_existing_file_newly_execute(execute=False, link=True, expected_flags='l') def test_push_existing_file_newly_execute(self, execute=True, link=False, expected_flags='x'): self.test_push_to_default() repo = self.repo def file_callback(repo, memctx, path): return context.memfilectx(path=path, data='foo', islink=link, isexec=execute, copied=False) ctx = context.memctx(repo, (repo['default'].node(), node.nullid), 'message', ['alpha', ], file_callback, 'author', '2008-1-1 00:00:00 -0500', {'branch': 'default', }) new_hash = repo.commitctx(ctx) hg.update(repo, repo['tip'].node()) self.pushrevisions() tip = self.repo['tip'] self.assertNotEqual(tip.node(), new_hash) self.assertEqual(tip['alpha'].data(), 'foo') self.assertEqual(tip.parents()[0]['alpha'].flags(), '') self.assertEqual(tip['alpha'].flags(), expected_flags) # while we're here, double check pushing an already-executable file # works repo = self.repo def file_callback2(repo, memctx, path): return context.memfilectx(path=path, data='bar', islink=link, isexec=execute, copied=False) ctx = context.memctx(repo, (repo['default'].node(), node.nullid), 'message', ['alpha', ], file_callback2, 'author', '2008-1-1 00:00:00 -0500', {'branch': 'default', }) new_hash = repo.commitctx(ctx) hg.update(repo, repo['tip'].node()) self.pushrevisions() tip = self.repo['tip'] self.assertNotEqual(tip.node(), new_hash) self.assertEqual(tip['alpha'].data(), 'bar') self.assertEqual(tip.parents()[0]['alpha'].flags(), expected_flags) self.assertEqual(tip['alpha'].flags(), expected_flags) # now test removing the property entirely repo = self.repo def file_callback3(repo, memctx, path): return context.memfilectx(path=path, data='bar', islink=False, isexec=False, copied=False) ctx = context.memctx(repo, (repo['default'].node(), node.nullid), 'message', ['alpha', ], file_callback3, 'author', '2008-01-01 00:00:00 -0500', {'branch': 'default', }) new_hash = repo.commitctx(ctx) hg.update(repo, repo['tip'].node()) self.pushrevisions() tip = self.repo['tip'] self.assertNotEqual(tip.node(), new_hash) self.assertEqual(tip['alpha'].data(), 'bar') self.assertEqual(tip.parents()[0]['alpha'].flags(), expected_flags) self.assertEqual(tip['alpha'].flags(), '') def test_push_outdated_base_text(self): self.test_push_two_revs() changes = [('adding_file', 'adding_file', 'different_content',), ] par = self.repo['tip'].rev() self.commitchanges(changes, parent=par) self.pushrevisions() changes = [('adding_file', 'adding_file', 'even_more different_content',), ] self.commitchanges(changes, parent=par) try: self.pushrevisions() assert False, 'This should have aborted!' except hgutil.Abort, e: self.assertEqual(e.args[0], 'Outgoing changesets parent is not at subversion ' 'HEAD\n' '(pull again and rebase on a newer revision)') # verify that any pending transactions on the server got cleaned up self.assertEqual([], os.listdir( os.path.join(self.tmpdir, 'testrepo-1', 'db', 'transactions'))) def test_push_encoding(self): self.test_push_two_revs() # Writing then rebasing UTF-8 filenames in a cp1252 windows console # used to fail because hg internal encoding was being changed during # the interactions with subversion, *and during the rebase*, which # confused the dirstate and made it believe the file was deleted. fn = 'pi\xc3\xa8ce/test' changes = [(fn, fn, 'a')] par = self.repo['tip'].rev() self.commitchanges(changes, parent=par) self.pushrevisions() def test_push_emptying_changeset(self): r = self.repo['tip'] changes = [ ('alpha', None, None), ('beta', None, None), ] parent = self.repo['tip'].rev() self.commitchanges(changes, parent=parent) self.pushrevisions() self.assertEqual({}, self.repo['tip'].manifest()) # Try to re-add a file after emptying the branch changes = [ ('alpha', 'alpha', 'alpha'), ] self.commitchanges(changes, parent=self.repo['tip'].rev()) self.pushrevisions() self.assertEqual(['alpha'], list(self.repo['tip'].manifest())) def suite(): test_classes = [PushTests, ] all_tests = [] # This is the quickest hack I could come up with to load all the tests from # both classes. Would love a patch that simplifies this without adding # dependencies. for tc in test_classes: for attr in dir(tc): if attr.startswith('test_'): all_tests.append(tc(attr)) return unittest.TestSuite(all_tests) durin42-hgsubversion-77b22e5b4ea6/tests/test_push_dirs.py0000644000000000000000000001126212043462603021601 0ustar 00000000000000import test_util import unittest class TestPushDirectories(test_util.TestBase): def test_push_dirs(self): repo_path = self.load_and_fetch('emptyrepo.svndump')[1] changes = [ # Single file in single directory ('d1/a', 'd1/a', 'a\n'), # Two files in one directory ('d2/a', 'd2/a', 'a\n'), ('d2/b', 'd2/b', 'a\n'), # Single file in empty directory hierarchy ('d31/d32/d33/d34/a', 'd31/d32/d33/d34/a', 'a\n'), ('d31/d32/a', 'd31/d32/a', 'a\n'), ] self.commitchanges(changes) self.pushrevisions() 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']) # Add one revision with changed files only, no directory addition # or deletion. changes = [ ('d1/a', 'd1/a', 'aa\n'), ('d2/a', 'd2/a', 'aa\n'), ] self.commitchanges(changes) self.pushrevisions() changes = [ # Remove single file in single directory ('d1/a', None, None), # Remove one file out of two ('d2/a', None, None), # Removing this file should remove one empty parent dir too ('d31/d32/d33/d34/a', None, None), ] self.commitchanges(changes) self.pushrevisions() 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): 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(test_util.svnls(repo_path, 'foo/trunk'), ['bar', 'bar/alpha', 'bar/beta', 'bar/delta', 'bar/gamma', 'foo', 'magic_new', 'magic_new/a']) def test_push_new_file_existing_dir_root_not_repo_root(self): 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(test_util.svnls(repo_path, 'project/trunk'), ['a', 'narf', ]) self.pushrevisions() 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(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, 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(test_util.svnls(repo_path, 'tags'), ['copied_tag', 'copied_tag/alpha', 'copied_tag/beta', 'new_dir', 'new_dir/new', 'tag_r3', 'tag_r3/alpha', 'tag_r3/beta', 'tag_r3/new']) def suite(): all_tests = [unittest.TestLoader().loadTestsFromTestCase(TestPushDirectories), ] return unittest.TestSuite(all_tests) durin42-hgsubversion-77b22e5b4ea6/tests/test_push_eol.py0000644000000000000000000000236312043462603021421 0ustar 00000000000000import test_util import unittest class TestPushEol(test_util.TestBase): def setUp(self): test_util.TestBase.setUp(self) self._load_fixture_and_fetch('emptyrepo.svndump') def _test_push_dirs(self, stupid): changes = [ # Root files with LF, CRLF and mixed EOL ('lf', 'lf', 'a\nb\n\nc'), ('crlf', 'crlf', 'a\r\nb\r\n\r\nc'), ('mixed', 'mixed', 'a\r\nb\n\r\nc\nd'), ] self.commitchanges(changes) self.pushrevisions(stupid) self.assertchanges(changes, self.repo['tip']) changes = [ # Update all files once, with same EOL ('lf', 'lf', 'a\nb\n\nc\na\nb\n\nc'), ('crlf', 'crlf', 'a\r\nb\r\n\r\nc\r\na\r\nb\r\n\r\nc'), ('mixed', 'mixed', 'a\r\nb\n\r\nc\nd\r\na\r\nb\n\r\nc\nd'), ] self.commitchanges(changes) self.pushrevisions(stupid) self.assertchanges(changes, self.repo['tip']) def test_push_dirs(self): self._test_push_dirs(False) def test_push_dirs_stupid(self): self._test_push_dirs(True) def suite(): all_tests = [unittest.TestLoader().loadTestsFromTestCase(TestPushEol), ] return unittest.TestSuite(all_tests) durin42-hgsubversion-77b22e5b4ea6/tests/test_push_renames.py0000644000000000000000000001055412043462603022275 0ustar 00000000000000import test_util import sys import unittest class TestPushRenames(test_util.TestBase): def setUp(self): test_util.TestBase.setUp(self) self.repo_path = self.load_and_fetch('pushrenames.svndump', stupid=True)[1] def _debug_print_copies(self, ctx): w = sys.stderr.write for f in ctx.files(): if f not in ctx: w('R %s\n' % f) else: w('U %s %r\n' % (f, ctx[f].data())) if ctx[f].renamed(): w('%s copied from %s\n' % (f, ctx[f].renamed()[0])) def test_push_renames(self): repo = self.repo changes = [ # Regular copy of a single file ('a', 'a2', None), # Copy and update of target ('a', 'a3', 'aa\n'), # Regular move of a single file ('b', 'b2', None), ('b', None, None), # Regular move and update of target ('c', 'c2', 'c\nc\n'), ('c', None, None), # Copy and update of source and targets ('d', 'd2', 'd\nd2\n'), ('d', 'd', 'd\nd\n'), # Double copy and removal (aka copy and move) ('e', 'e2', 'e\ne2\n'), ('e', 'e3', 'e\ne3\n'), ('e', None, None), ] self.commitchanges(changes) self.pushrevisions() tip = self.repo['tip'] # self._debug_print_copies(tip) self.assertchanges(changes, tip) def test_push_rename_with_space(self): changes = [ ('random/dir with space/file with space', 'random/dir with space/file with space', 'file contents'), ] self.commitchanges(changes) changes = [ ('random/dir with space/file with space', 'random2/dir with space/file with space', None), ('random/dir with space/file with space', None, None), ] self.commitchanges(changes) self.pushrevisions() self.assertEqual(self.repo['tip'].manifest().keys(), ['a', 'c', 'b', 'e', 'd', 'random2/dir with space/file with space']) def test_push_rename_tree(self): repo = self.repo changes = [ ('geek/alpha', 'geek/alpha', 'content',), ('geek/beta', 'geek/beta', 'content',), ('geek/delta', 'geek/delta', 'content',), ('geek/gamma', 'geek/gamma', 'content',), ('geek/later/pi', 'geek/later/pi', 'content geek/later/pi',), ('geek/later/rho', 'geek/later/rho', 'content geek/later/rho',), ('geek/other/blah', 'geek/other/blah', 'content geek/other/blah',), ('geek/other/another/layer', 'geek/other/another/layer', 'content deep file',), ] self.commitchanges(changes) self.pushrevisions() self.assertchanges(changes, self.repo['tip']) changes = [ # rename (copy + remove) all of geek to greek ('geek/alpha', 'greek/alpha', None,), ('geek/beta', 'greek/beta', None,), ('geek/delta', 'greek/delta', None,), ('geek/gamma', 'greek/gamma', None,), ('geek/later/pi', 'greek/later/pi', None,), ('geek/later/rho', 'greek/later/rho', None,), ('geek/other/blah', 'greek/other/blah', None,), ('geek/other/another/layer', 'greek/other/another/layer', None,), ('geek/alpha', None, None,), ('geek/beta', None, None,), ('geek/delta', None, None,), ('geek/gamma', None, None,), ('geek/later/pi', None, None,), ('geek/later/rho', None, None,), ('geek/other/blah', None, None,), ('geek/other/another/layer', None, None,), ] self.commitchanges(changes) self.pushrevisions() assert reduce(lambda x, y: x and y, ('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.') def suite(): all_tests = [unittest.TestLoader().loadTestsFromTestCase(TestPushRenames), ] return unittest.TestSuite(all_tests) durin42-hgsubversion-77b22e5b4ea6/tests/test_rebuildmeta.py0000644000000000000000000001452312043462603022101 0ustar 00000000000000import test_util import os import pickle import unittest from mercurial import context from mercurial import extensions from mercurial import hg from mercurial import ui from hgsubversion import svncommands from hgsubversion import svnmeta # These test repositories have harmless skew in rebuildmeta for the # last-pulled-rev because the last rev in svn causes absolutely no # changes in hg. expect_youngest_skew = [('file_mixed_with_branches.svndump', False, False), ('file_mixed_with_branches.svndump', True, False), ('unrelatedbranch.svndump', False, False), ('unrelatedbranch.svndump', True, False), ] def _do_case(self, name, stupid, single): subdir = test_util.subdir.get(name, '') layout = 'auto' if single: layout = 'single' 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() src, dest = test_util.hgclone(u, self.wc_path, wc2_path, update=False) src = test_util.getlocalpeer(src) dest = test_util.getlocalpeer(dest) # insert a wrapper that prevents calling changectx.children() def failfn(orig, ctx): self.fail('calling %s is forbidden; it can cause massive slowdowns ' 'when rebuilding large repositories' % orig) origchildren = getattr(context.changectx, 'children') extensions.wrapfunction(context.changectx, 'children', failfn) try: svncommands.rebuildmeta(u, dest, args=[test_util.fileurl(repo_path + subdir), ]) finally: # remove the wrapper context.changectx.children = origchildren self._run_assertions(name, stupid, single, src, dest, u) wc3_path = self.wc_path + '_partial' src, dest = test_util.hgclone(u, self.wc_path, wc3_path, update=False, rev=[0]) srcrepo = test_util.getlocalpeer(src) dest = test_util.getlocalpeer(dest) # insert a wrapper that prevents calling changectx.children() extensions.wrapfunction(context.changectx, 'children', failfn) try: svncommands.rebuildmeta(u, dest, args=[test_util.fileurl(repo_path + subdir), ]) finally: # remove the wrapper context.changectx.children = origchildren dest.pull(src) # insert a wrapper that prevents calling changectx.children() extensions.wrapfunction(context.changectx, 'children', failfn) try: svncommands.updatemeta(u, dest, args=[test_util.fileurl(repo_path + subdir), ]) finally: # remove the wrapper context.changectx.children = origchildren self._run_assertions(name, stupid, single, srcrepo, dest, u) def _run_assertions(self, name, stupid, single, src, dest, u): self.assertTrue(os.path.isdir(os.path.join(src.path, 'svn')), 'no .hg/svn directory in the source!') self.assertTrue(os.path.isdir(os.path.join(dest.path, 'svn')), 'no .hg/svn directory in the destination!') dest = hg.repository(u, os.path.dirname(dest.path)) for tf in ('lastpulled', 'rev_map', 'uuid', 'tagmap', 'layout', 'subdir',): stf = os.path.join(src.path, 'svn', tf) self.assertTrue(os.path.isfile(stf), '%r is missing!' % stf) dtf = os.path.join(dest.path, 'svn', tf) self.assertTrue(os.path.isfile(dtf), '%r is missing!' % tf) old, new = open(stf).read(), open(dtf).read() if tf == 'lastpulled' and (name, stupid, single) in expect_youngest_skew: self.assertNotEqual(old, new, 'rebuildmeta unexpected match on youngest rev!') continue self.assertMultiLineEqual(old, new, tf + ' differs') self.assertEqual(src.branchtags(), dest.branchtags()) srcbi = pickle.load(open(os.path.join(src.path, 'svn', 'branch_info'))) destbi = pickle.load(open(os.path.join(dest.path, 'svn', 'branch_info'))) self.assertEqual(sorted(srcbi.keys()), sorted(destbi.keys())) revkeys = svnmeta.SVNMeta(dest).revmap.keys() for branch in destbi: srcinfo = srcbi[branch] destinfo = destbi[branch] if srcinfo[:2] == (None, 0) or destinfo[:2] == (None, 0): self.assertTrue(srcinfo[2] <= destinfo[2], 'Latest revision for %s decreased from %d to %d!' % (branch or 'default', srcinfo[2], destinfo[2])) self.assertEqual(srcinfo[0], destinfo[0]) else: pr = sorted(filter(lambda x: x[1] == srcinfo[0] and x[0] <= srcinfo[1], revkeys), reverse=True)[0][0] self.assertEqual(pr, destinfo[1]) self.assertEqual(srcinfo[2], destinfo[2]) def buildmethod(case, name, stupid, single): m = lambda self: self._do_case(case, stupid, single) m.__name__ = name m.__doc__ = ('Test rebuildmeta on %s with %s replay. (%s)' % (case, (stupid and 'stupid') or 'real', (single and 'single') or 'standard', ) ) return m skip = set([ 'project_root_not_repo_root.svndump', 'corrupt.svndump', ]) attrs = {'_do_case': _do_case, '_run_assertions': _run_assertions, } for case in [f for f in os.listdir(test_util.FIXTURES) if f.endswith('.svndump')]: # this fixture results in an empty repository, don't use it if case in skip: continue bname = 'test_' + case[:-len('.svndump')] attrs[bname] = buildmethod(case, bname, False, False) name = bname + '_stupid' attrs[name] = buildmethod(case, name, True, False) name = bname + '_single' attrs[name] = buildmethod(case, name, False, True) RebuildMetaTests = type('RebuildMetaTests', (test_util.TestBase,), attrs) def suite(): all_tests = [unittest.TestLoader().loadTestsFromTestCase(RebuildMetaTests), ] return unittest.TestSuite(all_tests) durin42-hgsubversion-77b22e5b4ea6/tests/test_single_dir_clone.py0000644000000000000000000003237512043462603023110 0ustar 00000000000000import test_util import errno import shutil import unittest from mercurial import commands from mercurial import context from mercurial import hg from mercurial import node from mercurial import ui class TestSingleDir(test_util.TestBase): def test_clone_single_dir_simple(self): repo = self._load_fixture_and_fetch('branch_from_tag.svndump', stupid=False, layout='single', subdir='') self.assertEqual(repo.branchtags().keys(), ['default']) self.assertEqual(repo['tip'].manifest().keys(), ['trunk/beta', 'tags/copied_tag/alpha', 'trunk/alpha', 'tags/copied_tag/beta', 'branches/branch_from_tag/alpha', 'tags/tag_r3/alpha', 'tags/tag_r3/beta', 'branches/branch_from_tag/beta']) def test_auto_detect_single(self): repo = self._load_fixture_and_fetch('branch_from_tag.svndump', stupid=False, layout='auto') self.assertEqual(repo.branchtags().keys(), ['default', 'branch_from_tag']) oldmanifest = test_util.filtermanifest(repo['default'].manifest().keys()) # remove standard layout shutil.rmtree(self.wc_path) # try again with subdir to get single dir clone repo = self._load_fixture_and_fetch('branch_from_tag.svndump', stupid=False, layout='auto', subdir='trunk') self.assertEqual(repo.branchtags().keys(), ['default', ]) self.assertEqual(repo['default'].manifest().keys(), oldmanifest) def test_clone_subdir_is_file_prefix(self, stupid=False): FIXTURE = 'subdir_is_file_prefix.svndump' repo = self._load_fixture_and_fetch(FIXTURE, stupid=stupid, layout='single', subdir=test_util.subdir[FIXTURE]) self.assertEqual(repo.branchtags().keys(), ['default']) self.assertEqual(repo['tip'].manifest().keys(), ['flaf.txt']) def test_externals_single(self): repo = self._load_fixture_and_fetch('externals.svndump', stupid=False, layout='single') for rev in repo: assert '.hgsvnexternals' not in repo[rev].manifest() return # TODO enable test when externals in single are fixed expect = """[.] -r2 ^/externals/project2@2 deps/project2 [subdir] ^/externals/project1 deps/project1 [subdir2] ^/externals/project1 deps/project1 """ test = 2 self.assertEqual(self.repo[test]['.hgsvnexternals'].data(), expect) def test_externals_single_whole_repo(self): # This is the test which demonstrates the brokenness of externals return # TODO enable test when externals in single are fixed repo = self._load_fixture_and_fetch('externals.svndump', stupid=False, layout='single', subdir='') for rev in repo: rc = repo[rev] if '.hgsvnexternals' in rc: extdata = rc['.hgsvnexternals'].data() assert '[.]' not in extdata print extdata expect = '' # Not honestly sure what this should be... test = 4 self.assertEqual(self.repo[test]['.hgsvnexternals'].data(), expect) def test_push_single_dir(self): # Tests simple pushing from default branch to a single dir repo 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, data='foo', islink=False, isexec=False, copied=False) elif path == 'adding_binary': return context.memfilectx(path=path, data='\0binary', islink=False, isexec=False, copied=False) raise IOError(errno.EINVAL, 'Invalid operation: ' + path) ctx = context.memctx(repo, (repo['tip'].node(), node.nullid), 'automated test', ['adding_file', 'adding_binary'], file_callback, 'an_author', '2009-10-19 18:49:30 -0500', {'branch': 'default', }) repo.commitctx(ctx) hg.update(repo, repo['tip'].node()) self.pushrevisions() self.assertTrue('adding_file' in test_util.svnls(repo_path, '')) self.assertEqual('application/octet-stream', 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('', 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', stupid=False, layout='single', subdir='trunk') def filectxfn(repo, memctx, path): return context.memfilectx(path=path, data='contents of %s' % path, islink=False, isexec=False, copied=False) ctx = context.memctx(repo, (repo['tip'].node(), node.nullid), 'automated test', ['bogus'], filectxfn, 'an_author', '2009-10-19 18:49:30 -0500', {'branch': 'localhacking', }) n = repo.commitctx(ctx) self.assertEqual(self.repo['tip']['bogus'].data(), 'contents of bogus') before = repo['tip'].hex() hg.update(repo, self.repo['tip'].hex()) self.pushrevisions() self.assertNotEqual(before, self.repo['tip'].hex()) self.assertEqual(self.repo['tip']['bogus'].data(), 'contents of bogus') def test_push_single_dir_one_incoming_and_two_outgoing(self): # Tests simple pushing from default branch to a single dir repo # 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, 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, islink=False, isexec=False, copied=False) for fn in ['one', 'two']: ctx = context.memctx(repo, (repo['tip'].node(), node.nullid), 'automated test', [fn], file_callback, 'an_author', '2009-10-19 18:49:30 -0500', {'branch': 'default', }) repo.commitctx(ctx) hg.update(repo, repo['tip'].node()) self.pushrevisions(expected_extra_back=1) 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, 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: return context.memfilectx(path=path, data=data, islink=False, isexec=False, copied=False) raise IOError(errno.EINVAL, 'Invalid operation: ' + path) return cb def commit_to_branch(name, parent): repo.commitctx(context.memctx(repo, (parent, node.nullid), 'automated test (%s)' % name, [name], file_callback(name), 'an_author', '2009-10-19 18:49:30 -0500', {'branch': name, })) parent = repo['tip'].node() commit_to_branch('default', parent) commit_to_branch('foo', parent) hg.update(repo, repo['foo'].node()) self.pushrevisions() repo = self.repo # repo is outdated after the rebase happens, refresh 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(), self.repo, self.repo.branchheads('default')[1], clean=True) self.pushrevisions() 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 repo_path = self.load_svndump('branch_from_tag.svndump') cmd = ['clone', '--layout=single', '--branch=flaf'] if stupid: cmd.append('--stupid') cmd += [test_util.fileurl(repo_path), self.wc_path] test_util.dispatch(cmd) def file_callback(repo, memctx, path): if path == 'adding_file': return context.memfilectx(path=path, data='foo', islink=False, isexec=False, copied=False) raise IOError(errno.EINVAL, 'Invalid operation: ' + path) ctx = context.memctx(self.repo, (self.repo['tip'].node(), node.nullid), 'automated test', ['adding_file'], file_callback, 'an_author', '2009-10-19 18:49:30 -0500', {'branch': 'default', }) self.repo.commitctx(ctx) hg.update(self.repo, self.repo['tip'].node()) self.pushrevisions() self.assertTrue('adding_file' in test_util.svnls(repo_path, '')) self.assertEquals(set(['flaf']), set(self.repo[i].branch() for i in self.repo)) @test_util.requiresoption('branch') def test_push_single_dir_renamed_branch_stupid(self): self.test_push_single_dir_renamed_branch(True) def suite(): all_tests = [unittest.TestLoader().loadTestsFromTestCase(TestSingleDir)] return unittest.TestSuite(all_tests) durin42-hgsubversion-77b22e5b4ea6/tests/test_svnwrap.py0000644000000000000000000000500712043462603021301 0ustar 00000000000000import test_util import os import subprocess import tempfile import unittest from hgsubversion import svnwrap class TestBasicRepoLayout(unittest.TestCase): def setUp(self): self.tmpdir = tempfile.mkdtemp('svnwrap_test') self.repo_path = '%s/testrepo' % self.tmpdir subprocess.call(['svnadmin', 'create', self.repo_path, ]) inp = open(os.path.join(os.path.dirname(__file__), 'fixtures', 'project_root_at_repo_root.svndump')) proc = subprocess.call(['svnadmin', 'load', self.repo_path, ], stdin=inp, close_fds=test_util.canCloseFds, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) assert proc == 0 self.repo = svnwrap.SubversionRepo(test_util.fileurl(self.repo_path)) def tearDown(self): del self.repo test_util.rmtree(self.tmpdir) def test_num_revs(self): revs = list(self.repo.revisions()) self.assertEqual(len(revs), 7) r = revs[1] self.assertEqual(r.revnum, 2) self.assertEqual(sorted(r.paths.keys()), ['trunk/alpha', 'trunk/beta', 'trunk/delta']) for r in revs: for p in r.paths: # make sure these paths are always non-absolute for sanity if p: assert p[0] != '/' revs = list(self.repo.revisions(start=3)) self.assertEqual(len(revs), 4) class TestRootAsSubdirOfRepo(TestBasicRepoLayout): def setUp(self): self.tmpdir = tempfile.mkdtemp('svnwrap_test') self.repo_path = '%s/testrepo' % self.tmpdir subprocess.call(['svnadmin', 'create', self.repo_path, ]) inp = open(os.path.join(os.path.dirname(__file__), 'fixtures', 'project_root_not_repo_root.svndump')) ret = subprocess.call(['svnadmin', 'load', self.repo_path, ], stdin=inp, close_fds=test_util.canCloseFds, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) assert ret == 0 self.repo = svnwrap.SubversionRepo(test_util.fileurl( self.repo_path + '/dummyproj' )) def suite(): all_tests = [unittest.TestLoader().loadTestsFromTestCase(TestBasicRepoLayout), unittest.TestLoader().loadTestsFromTestCase(TestRootAsSubdirOfRepo)] return unittest.TestSuite(all_tests) durin42-hgsubversion-77b22e5b4ea6/tests/test_tags.py0000644000000000000000000002574112043462603020546 0ustar 00000000000000import test_util import os, sys, cStringIO, difflib import unittest from mercurial import commands from mercurial import hg from mercurial import node from mercurial import ui from hgsubversion import svncommands from hgsubversion import svnrepo class TestTags(test_util.TestBase): def test_tags(self, stupid=False): repo = self._load_fixture_and_fetch('basic_tag_tests.svndump', stupid=stupid) self.assertEqual(sorted(repo.tags()), ['copied_tag', 'tag_r3', 'tip']) self.assertEqual(repo['tag_r3'], repo['copied_tag']) self.assertEqual(repo['tag_r3'].rev(), 1) def test_tags_stupid(self): self.test_tags(stupid=True) def test_remove_tag(self, stupid=False): repo = self._load_fixture_and_fetch('remove_tag_test.svndump', stupid=stupid) self.assertEqual(repo['tag_r3'].rev(), 1) self.assert_('copied_tag' not in repo.tags()) def test_remove_tag_stupid(self): self.test_remove_tag(stupid=True) def test_rename_tag(self, stupid=False): expected = """\ node: hg=default@2:svn=trunk@4 tagging r3 tag_r3: hg=default@1:svn=trunk@3 node: hg=default@3:svn=trunk@5 tag from a tag tag_r3: hg=default@1:svn=trunk@3 copied_tag: hg=default@1:svn=trunk@3 node: hg=default@4:svn=trunk@6 rename a tag tag_r3: hg=default@1:svn=trunk@3 copied_tag: hg=default@1:svn=trunk@3 copied_tag: hg=default@-1:svn=unk@unk other_tag_r3: hg=default@1:svn=trunk@3 """ self._test_tags('rename_tag_test.svndump', expected, stupid) def test_rename_tag_stupid(self): self.test_rename_tag(stupid=True) def test_branch_from_tag(self, stupid=False): repo = self._load_fixture_and_fetch('branch_from_tag.svndump', stupid=stupid) self.assert_('branch_from_tag' in repo.branchtags()) self.assertEqual(repo[1], repo['tag_r3']) self.assertEqual(repo['branch_from_tag'].parents()[0], repo['copied_tag']) def test_branch_from_tag_stupid(self): self.test_branch_from_tag(stupid=True) def test_tag_by_renaming_branch(self, stupid=False): repo = self._load_fixture_and_fetch('tag_by_rename_branch.svndump', stupid=stupid) branches = set(repo[h] for h in repo.heads()) self.assert_('dummy' not in branches) self.assertEqual(repo['dummy'], repo['tip'].parents()[0], '%r != %r[0]' % (repo['dummy'], repo['tip'].parents())) extra = repo['tip'].extra().copy() extra.pop('convert_revision', None) self.assertEqual(extra, {'branch': 'dummy', 'close': '1'}) def test_tag_by_renaming_branch_stupid(self): self.test_tag_by_renaming_branch(stupid=True) def test_deletion_of_tag_on_trunk_after_branching(self): repo = self._load_fixture_and_fetch('tag_deletion_tag_branch.svndump') branches = set(repo[h].extra()['branch'] for h in repo.heads()) self.assertEqual(branches, set(['default', 'from_2', ])) self.assertEqual( repo.tags(), {'tip': 'g\xdd\xcd\x93\x03g\x1e\x7f\xa6-[V%\x99\x07\xd3\x9d>(\x94', 'new_tag': '=\xb8^\xb5\x18\xa9M\xdb\xf9\xb62Z\xa0\xb5R6+\xfe6.'}) def test_tags_in_unusual_location(self): repo = self._load_fixture_and_fetch('unusual_tags.svndump') branches = set(repo[h].extra()['branch'] for h in repo.heads()) self.assertEqual(branches, set(['default', 'dev_branch'])) tags = repo.tags() del tags['tip'] self.assertEqual( tags, {'blah/trunktag': '\xd3$@\xd7\xd8Nu\xce\xa6%\xf1u\xd9b\x1a\xb2\x81\xc2\xb0\xb1', 'versions/branch_version': 'I\x89\x1c>z#\xfc._K#@:\xd6\x1f\x96\xd6\x83\x1b|', }) def test_most_recent_is_edited_stupid(self): self.test_most_recent_is_edited(True) def test_most_recent_is_edited(self, stupid=False): 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") wc2_path = self.wc_path + '2' src, dest = test_util.hgclone(repo.ui, self.wc_path, wc2_path, update=False) dest = test_util.getlocalpeer(dest) svncommands.rebuildmeta(repo.ui, dest, 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') srctags.pop('tip') self.assertEqual(dtags, srctags) self.assertEqual(dest.heads(), self.repo.heads()) def test_edited_tag_stupid(self): self.test_edited_tag(True) def test_edited_tag(self, stupid=False): repo = self._load_fixture_and_fetch('commit-to-tag.svndump', stupid=stupid) headcount = 6 self.assertEqual(len(repo.heads()), headcount) heads = repo.heads() openheads = [h for h in heads if not repo[h].extra().get('close', False)] closedheads = set(heads) - set(openheads) self.assertEqual(len(openheads), 1) self.assertEqual(len(closedheads), headcount - 1) closedheads = sorted(list(closedheads), cmp=lambda x, y: cmp(repo[x].rev(), repo[y].rev())) # closeme has no open heads for h in openheads: self.assertNotEqual('closeme', repo[openheads[0]].branch()) self.assertEqual(1, len(self.repo.branchheads('magic'))) alsoedit, editlater, closeme, willedit, editcreate, = closedheads self.assertEqual( repo[willedit].extra(), {'close': '1', 'branch': 'magic', 'convert_revision': 'svn:af82cc90-c2d2-43cd-b1aa-c8a78449440a/tags/will-edit@19'}) self.assertEqual(willedit, repo.tags()['will-edit']) self.assertEqual(repo['will-edit'].manifest().keys(), ['alpha', 'beta', 'gamma', ]) self.assertEqual( repo[alsoedit].extra(), {'close': '1', 'branch': 'magic', 'convert_revision': 'svn:af82cc90-c2d2-43cd-b1aa-c8a78449440a/tags/also-edit@14'}) self.assertEqual(repo[alsoedit].parents()[0].node(), repo.tags()['also-edit']) self.assertEqual(repo['also-edit'].manifest().keys(), ['beta', '.hgtags', 'delta', 'alpha', 'omega', 'iota', 'gamma', 'lambda', ]) self.assertEqual(editlater, repo['edit-later'].node()) self.assertEqual( repo[closeme].extra(), {'close': '1', 'branch': 'closeme', 'convert_revision': 'svn:af82cc90-c2d2-43cd-b1aa-c8a78449440a/branches/closeme@17'}) self.assertEqual('alpha\nalpha\n', repo['edit-at-create']['alpha'].data()) def test_tags_in_unusual_location(self): repo = self._load_fixture_and_fetch('tag_name_same_as_branch.svndump') self.assertEqual(len(repo.heads()), 1) branches = set(repo[h].extra()['branch'] for h in repo.heads()) self.assertEqual(branches, set(['magic', ])) tags = repo.tags() del tags['tip'] self.assertEqual( tags, {'magic': '\xa2b\xb9\x03\xc6\xbd\x903\x95\xf5\x0f\x94\xcey\xc4E\xfaE6\xaa', 'magic2': '\xa3\xa2D\x86aM\xc0v\xb9\xb0\x18\x14\xad\xacwBUi}\xe2', }) def test_old_tag_map_rebuilds(self): repo = self._load_fixture_and_fetch('tag_name_same_as_branch.svndump') tm = os.path.join(repo.path, 'svn', 'tagmap') open(tm, 'w').write('1\n') commands.pull(repo.ui, repo) self.assertEqual(open(tm).read().splitlines()[0], '2') def _debug_print_tags(self, repo, ctx, fp): def formatnode(ctx): crev = ctx.extra().get('convert_revision', 'unk/unk@unk') path, rev = crev.rsplit('@', 1) path = path.split('/', 1)[-1] branch = ctx.branch() or 'default' return 'hg=%s@%d:svn=%s@%s' % (branch, ctx.rev(), path, rev) w = fp.write desc = ctx.description().splitlines()[0].strip() if '.hgtags' not in ctx or not ctx['.hgtags'].data().strip(): return w('node: %s\n' % formatnode(ctx)) w('%s\n' % desc) for line in ctx['.hgtags'].data().splitlines(False): node, name = line.split(None, 1) w(' %s: %s\n' % (name, formatnode(repo[node]))) w('\n') def _test_tags(self, testpath, expected, stupid=False): repo = self._load_fixture_and_fetch(testpath, stupid=stupid) fp = cStringIO.StringIO() for r in repo: self._debug_print_tags(repo, repo[r], fp=fp) output = fp.getvalue().strip() expected = expected.strip() if expected == output: return expected = expected.splitlines() output = output.splitlines() diff = difflib.unified_diff(expected, output, 'expected', 'output') self.assert_(False, '\n' + '\n'.join(diff)) def test_tagging_into_tag(self, stupid=False): expected = """\ node: hg=test@2:svn=branches/test@4 First tag. test-0.1: hg=test@1:svn=branches/test@3 node: hg=test@3:svn=branches/test@5 Weird tag. test-0.1: hg=test@1:svn=branches/test@3 test-0.1/test: hg=test@1:svn=branches/test@3 node: hg=test@4:svn=branches/test@6 Fix tag pt 1. test-0.1: hg=test@1:svn=branches/test@3 test-0.1/test: hg=test@1:svn=branches/test@3 test-0.1/test: hg=default@-1:svn=unk@unk test-0.1-real: hg=test@1:svn=branches/test@3 node: hg=test@5:svn=branches/test@7 Remove weird. test-0.1: hg=test@1:svn=branches/test@3 test-0.1/test: hg=test@1:svn=branches/test@3 test-0.1/test: hg=default@-1:svn=unk@unk test-0.1-real: hg=test@1:svn=branches/test@3 test-0.1: hg=default@-1:svn=unk@unk node: hg=test@6:svn=branches/test@8 Fix tag pt 2. test-0.1: hg=test@1:svn=branches/test@3 test-0.1/test: hg=test@1:svn=branches/test@3 test-0.1/test: hg=default@-1:svn=unk@unk test-0.1-real: hg=test@1:svn=branches/test@3 test-0.1: hg=default@-1:svn=unk@unk test-0.1-real: hg=default@-1:svn=unk@unk test-0.1: hg=test@1:svn=branches/test@3 """ self._test_tags('renametagdir.svndump', expected, stupid=stupid) def test_tagging_into_tag_stupid(self): self.test_tagging_into_tag(True) def suite(): return unittest.TestLoader().loadTestsFromTestCase(TestTags) durin42-hgsubversion-77b22e5b4ea6/tests/test_template_keywords.py0000644000000000000000000000546712043462603023355 0ustar 00000000000000import test_util import unittest from mercurial import commands from mercurial import error from mercurial import ui try: from mercurial import templatekw templatekw.keywords except ImportError: templatekw = None try: from mercurial import revset revset.methods except ImportError: revset = None class CapturingUI(ui.ui): def __init__(self, *args, **kwds): super(CapturingUI, self).__init__(*args, **kwds) self._output = "" def write(self, msg, *args, **kwds): self._output += msg class TestLogKeywords(test_util.TestBase): @test_util.requiresmodule(templatekw) def test_svn_keywords(self): defaults = {'date': None, 'rev': None, 'user': None} repo = self._load_fixture_and_fetch('two_revs.svndump') # we want one commit that isn't from Subversion self.commitchanges([('foo', 'foo', 'frobnicate\n')]) ui = CapturingUI() commands.log(ui, repo, template='{rev}:{svnrev} ', **defaults) self.assertEqual(ui._output, '0:2 1:3 2: ') ui = CapturingUI() commands.log(ui, repo, template='{rev}:{svnpath} ', **defaults) self.assertEqual(ui._output, '0:/trunk 1:/trunk 2: ') ui = CapturingUI() commands.log(ui, repo, template='{rev}:{svnuuid} ', **defaults) self.assertEqual(ui._output, ('0:df2126f7-00ab-4d49-b42c-7e981dde0bcf ' '1:df2126f7-00ab-4d49-b42c-7e981dde0bcf ' '2: ')) @test_util.requiresmodule(revset) @test_util.requiresmodule(templatekw) def test_svn_revsets(self): repo = self._load_fixture_and_fetch('two_revs.svndump') # we want one commit that isn't from Subversion self.commitchanges([('foo', 'foo', 'frobnicate\n')]) defaults = {'date': None, 'rev': ['fromsvn()'], 'user': None} ui = CapturingUI() commands.log(ui, repo, template='{rev}:{svnrev} ', **defaults) self.assertEqual(ui._output, '0:2 1:3 ') defaults = {'date': None, 'rev': ['svnrev(2)'], 'user': None} ui = CapturingUI() commands.log(ui, repo, template='{rev}:{svnrev} ', **defaults) self.assertEqual(ui._output, '0:2 ') defaults = {'date': None, 'rev': ['fromsvn(1)'], 'user': None} self.assertRaises(error.ParseError, commands.log, self.ui(), repo, template='{rev}:{svnrev} ', **defaults) defaults = {'date': None, 'rev': ['svnrev(1, 2)'], 'user': None} self.assertRaises(error.ParseError, commands.log, self.ui(), repo, template='{rev}:{svnrev} ', **defaults) def suite(): all_tests = [unittest.TestLoader().loadTestsFromTestCase(TestLogKeywords), ] return unittest.TestSuite(all_tests) durin42-hgsubversion-77b22e5b4ea6/tests/test_unaffected_core.py0000644000000000000000000000517112043462603022717 0ustar 00000000000000import test_util import os import unittest from mercurial import commands from mercurial import dispatch from mercurial import error from mercurial import hg from mercurial import node from mercurial import ui def _dispatch(ui, cmd): try: req = dispatch.request(cmd, ui=ui) dispatch._dispatch(req) except AttributeError: dispatch._dispatch(ui, cmd) class TestMercurialCore(test_util.TestBase): ''' Test that the core Mercurial operations aren't broken by hgsubversion. ''' @test_util.requiresoption('updaterev') def test_update(self): ''' Test 'clone --updaterev' ''' ui = self.ui() _dispatch(ui, ['init', self.wc_path]) repo = self.repo repo.ui.setconfig('ui', 'username', 'anonymous') fpath = os.path.join(self.wc_path, 'it') f = file(fpath, 'w') f.write('C1') f.flush() commands.add(ui, repo) commands.commit(ui, repo, message="C1") f.write('C2') f.flush() commands.commit(ui, repo, message="C2") f.write('C3') f.flush() commands.commit(ui, repo, message="C3") self.assertEqual(len(repo), 3) updaterev = 1 _dispatch(ui, ['clone', self.wc_path, self.wc_path + '2', '--updaterev=%s' % updaterev]) repo2 = hg.repository(ui, self.wc_path + '2') self.assertEqual(str(repo[updaterev]), str(repo2['.'])) @test_util.requiresoption('branch') def test_branch(self): ''' Test 'clone --branch' ''' ui = self.ui() _dispatch(ui, ['init', self.wc_path]) repo = self.repo repo.ui.setconfig('ui', 'username', 'anonymous') fpath = os.path.join(self.wc_path, 'it') f = file(fpath, 'w') f.write('C1') f.flush() commands.add(ui, repo) commands.branch(ui, repo, label="B1") commands.commit(ui, repo, message="C1") f.write('C2') f.flush() commands.branch(ui, repo, label="default") commands.commit(ui, repo, message="C2") f.write('C3') f.flush() commands.branch(ui, repo, label="B2") commands.commit(ui, repo, message="C3") self.assertEqual(len(repo), 3) branch = 'B1' _dispatch(ui, ['clone', self.wc_path, self.wc_path + '2', '--branch', branch]) repo2 = hg.repository(ui, self.wc_path + '2') self.assertEqual(repo[branch].hex(), repo2['.'].hex()) def suite(): all_tests = [unittest.TestLoader().loadTestsFromTestCase(TestMercurialCore)] return unittest.TestSuite(all_tests) durin42-hgsubversion-77b22e5b4ea6/tests/test_updatemeta.py0000644000000000000000000000501412043462603021730 0ustar 00000000000000import test_util import os import pickle import unittest import test_rebuildmeta from mercurial import context from mercurial import extensions from mercurial import hg from mercurial import ui from hgsubversion import svncommands from hgsubversion import svnmeta def _do_case(self, name, stupid, single): subdir = test_util.subdir.get(name, '') layout = 'auto' if single: layout = 'single' 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() src, dest = test_util.hgclone(u, self.wc_path, wc2_path, update=False) src = test_util.getlocalpeer(src) dest = test_util.getlocalpeer(dest) # insert a wrapper that prevents calling changectx.children() def failfn(orig, ctx): self.fail('calling %s is forbidden; it can cause massive slowdowns ' 'when rebuilding large repositories' % orig) origchildren = getattr(context.changectx, 'children') extensions.wrapfunction(context.changectx, 'children', failfn) # test updatemeta on an empty repo try: svncommands.updatemeta(u, dest, args=[test_util.fileurl(repo_path + subdir), ]) finally: # remove the wrapper context.changectx.children = origchildren self._run_assertions(name, stupid, single, src, dest, u) def _run_assertions(self, name, stupid, single, src, dest, u): test_rebuildmeta._run_assertions(self, name, stupid, single, src, dest, u) skip = set([ 'project_root_not_repo_root.svndump', 'corrupt.svndump', ]) attrs = {'_do_case': _do_case, '_run_assertions': _run_assertions, } for case in [f for f in os.listdir(test_util.FIXTURES) if f.endswith('.svndump')]: # this fixture results in an empty repository, don't use it if case in skip: continue bname = 'test_' + case[:-len('.svndump')] attrs[bname] = test_rebuildmeta.buildmethod(case, bname, False, False) name = bname + '_stupid' attrs[name] = test_rebuildmeta.buildmethod(case, name, True, False) name = bname + '_single' attrs[name] = test_rebuildmeta.buildmethod(case, name, False, True) UpdateMetaTests = type('UpdateMetaTests', (test_util.TestBase,), attrs) def suite(): all_tests = [unittest.TestLoader().loadTestsFromTestCase(UpdateMetaTests), ] return unittest.TestSuite(all_tests) durin42-hgsubversion-77b22e5b4ea6/tests/test_urls.py0000644000000000000000000000616712043462603020576 0ustar 00000000000000import test_util import unittest import urllib from hgsubversion.svnwrap import parse_url from hgsubversion import svnrepo class TestSubversionUrls(test_util.TestBase): def test_standard_url(self): self.assertEqual((None, None, 'file:///var/svn/repo'), parse_url('file:///var/svn/repo')) def test_user_url(self): self.assertEqual( ('joe', None, 'https://svn.testurl.com/repo'), parse_url('https://joe@svn.testurl.com/repo')) self.assertEqual( ('bob', None, 'https://svn.testurl.com/repo'), parse_url('https://joe@svn.testurl.com/repo', 'bob')) def test_password_url(self): self.assertEqual( (None, 't3stpw', 'svn+ssh://svn.testurl.com/repo'), parse_url('svn+ssh://:t3stpw@svn.testurl.com/repo')) self.assertEqual( (None, '123abc', 'svn+ssh://svn.testurl.com/repo'), parse_url('svn+ssh://:t3stpw@svn.testurl.com/repo', None, '123abc')) def test_svnssh_preserve_user(self): self.assertEqual( ('user', 't3stpw', 'svn+ssh://user@svn.testurl.com/repo',), parse_url('svn+ssh://user:t3stpw@svn.testurl.com/repo')) self.assertEqual( ('bob', '123abc', 'svn+ssh://bob@svn.testurl.com/repo',), parse_url('svn+ssh://user:t3stpw@svn.testurl.com/repo', 'bob', '123abc')) self.assertEqual( ('user2', None, 'svn+ssh://user2@svn.testurl.com/repo',), parse_url('svn+ssh://user2@svn.testurl.com/repo')) self.assertEqual( ('bob', None, 'svn+ssh://bob@svn.testurl.com/repo',), parse_url('svn+ssh://user2@svn.testurl.com/repo', 'bob')) def test_user_password_url(self): self.assertEqual( ('joe', 't3stpw', 'https://svn.testurl.com/repo'), parse_url('https://joe:t3stpw@svn.testurl.com/repo')) self.assertEqual( ('bob', '123abc', 'https://svn.testurl.com/repo'), parse_url('https://joe:t3stpw@svn.testurl.com/repo', 'bob', '123abc')) def test_url_rewriting(self): ui = test_util.ui.ui() ui.setconfig('hgsubversion', 'username', 'bob') repo = svnrepo.svnremoterepo(ui, 'svn+ssh://joe@foo/bar') self.assertEqual('svn+ssh://bob@foo/bar', repo.svnauth[0]) repo = svnrepo.svnremoterepo(ui, 'svn+http://joe@foo/bar') self.assertEqual(('http://foo/bar', 'bob', None), repo.svnauth) repo = svnrepo.svnremoterepo(ui, 'svn+https://joe@foo/bar') self.assertEqual(('https://foo/bar', 'bob', None), repo.svnauth) def test_quoting(self): ui = self.ui() repo_path = self.load_svndump('non_ascii_path_1.svndump') repo_url = test_util.fileurl(repo_path) subdir = '/b\xC3\xB8b' quoted_subdir = urllib.quote(subdir) repo1 = svnrepo.svnremoterepo(ui, repo_url + subdir) repo2 = svnrepo.svnremoterepo(ui, repo_url + quoted_subdir) self.assertEqual(repo1.svnurl, repo2.svnurl) def suite(): all_tests = [unittest.TestLoader().loadTestsFromTestCase(TestSubversionUrls)] return unittest.TestSuite(all_tests) durin42-hgsubversion-77b22e5b4ea6/tests/test_util.py0000644000000000000000000004530512043462603020563 0ustar 00000000000000import StringIO import difflib import errno import gettext import os import shutil import stat import subprocess import sys import tarfile import tempfile import unittest import urllib _rootdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, _rootdir) from mercurial import cmdutil from mercurial import commands from mercurial import context from mercurial import dispatch as dispatchmod from mercurial import hg from mercurial import i18n from mercurial import node from mercurial import ui from mercurial import util from mercurial import extensions try: SkipTest = unittest.SkipTest except AttributeError: try: from unittest2 import SkipTest except ImportError: try: from nose import SkipTest except ImportError: SkipTest = None from hgsubversion import util from hgsubversion import svnwrap # Documentation for Subprocess.Popen() says: # "Note that on Windows, you cannot set close_fds to true and # also redirect the standard handles by setting stdin, stdout or # stderr." canCloseFds = 'win32' not in sys.platform if not 'win32' in sys.platform: def kill_process(popen_obj): os.kill(popen_obj.pid, 9) else: import ctypes from ctypes.wintypes import BOOL, DWORD, HANDLE, UINT def win_status_check(result, func, args): if result == 0: raise ctypes.WinError() return args def WINAPI(returns, func, *params): assert len(params) % 2 == 0 func.argtypes = tuple(params[0::2]) func.resvalue = returns func.errcheck = win_status_check return func # dwDesiredAccess PROCESS_TERMINATE = 0x0001 OpenProcess = WINAPI(HANDLE, ctypes.windll.kernel32.OpenProcess, DWORD, 'dwDesiredAccess', BOOL, 'bInheritHandle', DWORD, 'dwProcessId', ) CloseHandle = WINAPI(BOOL, ctypes.windll.kernel32.CloseHandle, HANDLE, 'hObject' ) TerminateProcess = WINAPI(BOOL, ctypes.windll.kernel32.TerminateProcess, HANDLE, 'hProcess', UINT, 'uExitCode' ) def kill_process(popen_obj): phnd = OpenProcess(PROCESS_TERMINATE, False, popen_obj.pid) TerminateProcess(phnd, 1) CloseHandle(phnd) # Fixtures that need to be pulled at a subdirectory of the repo path subdir = {'truncatedhistory.svndump': '/project2', 'fetch_missing_files_subdir.svndump': '/foo', 'empty_dir_in_trunk_not_repo_root.svndump': '/project', 'project_root_not_repo_root.svndump': '/dummyproj', 'project_name_with_space.svndump': '/project name', 'non_ascii_path_1.svndump': '/b\xC3\xB8b', 'non_ascii_path_2.svndump': '/b%C3%B8b', 'subdir_is_file_prefix.svndump': '/flaf', } FIXTURES = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'fixtures') def getlocalpeer(repo): localrepo = getattr(repo, 'local', lambda: repo)() if isinstance(localrepo, bool): localrepo = repo return localrepo def _makeskip(name, message): if SkipTest: def skip(*args, **kwargs): raise SkipTest(message) skip.__name__ = name return skip def requiresmodule(mod): """Skip a test if the specified module is not None.""" def decorator(fn): if fn is None: return if mod is not None: return fn return _makeskip(fn.__name__, 'missing required feature') return decorator def requiresoption(option): '''Skip a test if commands.clone does not take the specified option.''' def decorator(fn): for entry in cmdutil.findcmd('clone', commands.table)[1][1]: if entry[1] == option: return fn # no match found, so skip if SkipTest: return _makeskip(fn.__name__, 'test requires clone to accept %s' % option) # no skipping support, so erase decorated method return if not isinstance(option, str): raise TypeError('requiresoption takes a string argument') return decorator def filtermanifest(manifest): return [f for f in manifest if f not in util.ignoredfiles] def fileurl(path): path = os.path.abspath(path).replace(os.sep, '/') drive, path = os.path.splitdrive(path) if drive: # In svn 1.7, the swig svn wrapper returns local svn URLs # with an uppercase drive letter, try to match that to # simplify svn info tests. drive = '/' + drive.upper() url = 'file://%s%s' % (drive, path) return url def testui(stupid=False, layout='auto', startrev=0): u = ui.ui() bools = {True: 'true', False: 'false'} u.setconfig('ui', 'quiet', bools[True]) u.setconfig('extensions', 'hgsubversion', '') u.setconfig('hgsubversion', 'stupid', bools[stupid]) u.setconfig('hgsubversion', 'layout', layout) u.setconfig('hgsubversion', 'startrev', startrev) return u def dispatch(cmd): cmd = getattr(dispatchmod, 'request', lambda x: x)(cmd) dispatchmod.dispatch(cmd) def rmtree(path): # Read-only files cannot be removed under Windows for root, dirs, files in os.walk(path): for f in files: f = os.path.join(root, f) try: s = os.stat(f) except OSError, e: if e.errno == errno.ENOENT: continue raise if (s.st_mode & stat.S_IWRITE) == 0: os.chmod(f, s.st_mode | stat.S_IWRITE) shutil.rmtree(path) def _verify_our_modules(): ''' Verify that hgsubversion was imported from the correct location. The correct location is any location within the parent directory of the directory containing this file. ''' for modname, module in sys.modules.iteritems(): if not module or not modname.startswith('hgsubversion.'): continue modloc = module.__file__ cp = os.path.commonprefix((os.path.abspath(__file__), modloc)) assert cp.rstrip(os.sep) == _rootdir, ( 'Module location verification failed: hgsubversion was imported ' 'from the wrong path!' ) def hgclone(ui, source, dest, update=True, rev=None): if getattr(hg, 'peer', None): # Since 1.9 (d976542986d2) src, dest = hg.clone(ui, {}, source, dest, update=update, rev=rev) else: src, dest = hg.clone(ui, source, dest, update=update, rev=rev) 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() # the Python 2.7 default of 640 is obnoxiously low self.maxDiff = 4096 self.oldenv = dict([(k, os.environ.get(k, None),) for k in ('LANG', 'LC_ALL', 'HGRCPATH',)]) self.oldt = i18n.t os.environ['LANG'] = os.environ['LC_ALL'] = 'C' i18n.t = gettext.translation('hg', i18n.localedir, fallback=True) self.oldwd = os.getcwd() self.tmpdir = tempfile.mkdtemp( 'svnwrap_test', dir=os.environ.get('HGSUBVERSION_TEST_TEMP', None)) self.hgrc = os.path.join(self.tmpdir, '.hgrc') os.environ['HGRCPATH'] = self.hgrc rc = open(self.hgrc, 'w') for l in '[extensions]', 'hgsubversion=': print >> rc, l self.repocount = 0 self.wc_path = '%s/testrepo_wc' % self.tmpdir self.svn_wc = None self.config_dir = self.tmpdir svnwrap.common._svn_config_dir = self.config_dir self.setup_svn_config('') # Previously, we had a MockUI class that wrapped ui, and giving access # to the stream. The ui.pushbuffer() and ui.popbuffer() can be used # instead. Using the regular UI class, with all stderr redirected to # stdout ensures that the test setup is much more similar to usage # setups. self.patch = (ui.ui.write_err, ui.ui.write) setattr(ui.ui, self.patch[0].func_name, self.patch[1]) def setup_svn_config(self, config): with open(self.config_dir + '/config', 'w') as c: c.write(config) 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: del os.environ[var] else: os.environ[var] = val i18n.t = self.oldt rmtree(self.tmpdir) os.chdir(self.oldwd) setattr(ui.ui, self.patch[0].func_name, self.patch[0]) _verify_our_modules() def ui(self, stupid=False, layout='auto'): return testui(stupid, layout) 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_repo_tarball(self, fixture_name): '''Extracts a tarball of an svn repo and returns the svn repo path.''' path = self._makerepopath() assert not os.path.exists(path) os.mkdir(path) tarball = tarfile.open(os.path.join(FIXTURES, fixture_name)) # This is probably somewhat fragile, but I'm not sure how to # do better in particular, I think it assumes that the tar # entries are in the right order and that directories appear # before their contents. This is a valid assummption for sane # tarballs, from what I can tell. In particular, for a simple # tarball of a svn repo with paths relative to the repo root, # it seems to work for entry in tarball: tarball.extract(entry, path) return path def fetch(self, repo_path, subdir=None, stupid=False, layout='auto', startrev=0, externals=None, noupdate=True, dest=None, rev=None, config=None): if layout == 'single': if subdir is None: subdir = 'trunk' elif subdir is None: subdir = '' 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 rev is not None: cmd.append('--rev=%s' % rev) config = dict(config or {}) if externals: config['hgsubversion.externals'] = str(externals) for k,v in reversed(sorted(config.iteritems())): cmd[:0] = ['--config', '%s=%s' % (k, v)] dispatch(cmd) return hg.repository(testui(), self.wc_path) def load_and_fetch(self, fixture_name, *args, **opts): if fixture_name.endswith('.svndump'): repo_path = self.load_svndump(fixture_name) elif fixture_name.endswith('tar.gz'): repo_path = self.load_repo_tarball(fixture_name) else: assert False, 'Unknown fixture type' return self.fetch(repo_path, *args, **opts), 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(repo_path), self.svn_wc ], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) for filename, contents in changes.iteritems(): # filenames are / separated filename = filename.replace('/', os.path.sep) filename = os.path.join(self.svn_wc, filename) open(filename, 'w').write(contents) # may be redundant subprocess.call(['svn', 'add', '-q', filename], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) subprocess.call([ 'svn', 'commit', '-q', self.svn_wc, '-m', 'test changes'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) # define this as a property so that it reloads anytime we need it @property def repo(self): return hg.repository(testui(), self.wc_path) def pushrevisions(self, stupid=False, expected_extra_back=0): before = len(self.repo) self.repo.ui.setconfig('hgsubversion', 'stupid', str(stupid)) res = commands.push(self.repo.ui, self.repo) after = len(self.repo) self.assertEqual(expected_extra_back, after - before) return res 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(repo_path + '/' + svnpath) args = ['svn', 'co', '-r', rev, svnpath, path] p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout, stderr = p.communicate() if p.returncode: raise Exception('svn co failed on %s: %r' % (svnpath, stderr)) def commitchanges(self, changes, parent='tip', message='automated test'): """Commit changes to mercurial directory 'changes' is a sequence of tuples (source, dest, data). It can look like: - (source, source, data) to set source content to data - (source, dest, None) to set dest content to source one, and mark it as copied from source. - (source, dest, data) to set dest content to data, and mark it as copied from source. - (source, None, None) to remove source. """ repo = self.repo parentctx = repo[parent] changed, removed = [], [] for source, dest, newdata in changes: if dest is None: removed.append(source) else: changed.append(dest) def filectxfn(repo, memctx, path): if path in removed: raise IOError(errno.ENOENT, "File \"%s\" no longer exists" % path) entry = [e for e in changes if path == e[1]][0] source, dest, newdata = entry if newdata is None: newdata = parentctx[source].data() copied = None if source != dest: copied = source return context.memfilectx(path=dest, data=newdata, islink=False, isexec=False, copied=copied) ctx = context.memctx(repo, (parentctx.node(), node.nullid), message, changed + removed, filectxfn, 'an_author', '2008-10-07 20:59:48 -0500') nodeid = repo.commitctx(ctx) repo = self.repo hg.clean(repo, nodeid) return nodeid def assertchanges(self, changes, ctx): """Assert that all 'changes' (as in defined in commitchanged()) went into ctx. """ for source, dest, data in changes: if dest is None: self.assertTrue(source not in ctx) continue self.assertTrue(dest in ctx) if data is None: data = ctx.parents()[0][source].data() self.assertEqual(ctx[dest].data(), data) if dest != source: copy = ctx[dest].renamed() self.assertEqual(copy[0], source) def assertMultiLineEqual(self, first, second, msg=None): """Assert that two multi-line strings are equal. (Based on Py3k code.) """ try: return super(TestBase, self).assertMultiLineEqual(first, second, msg) except AttributeError: pass self.assert_(isinstance(first, str), ('First argument is not a string')) self.assert_(isinstance(second, str), ('Second argument is not a string')) if first != second: diff = ''.join(difflib.unified_diff(first.splitlines(True), second.splitlines(True), fromfile='a', tofile='b')) msg = '%s\n%s' % (msg or '', diff) raise self.failureException, msg def getgraph(self, repo): """Helper function displaying a repository graph, especially useful when debugging comprehensive tests. """ # Could be more elegant, but it works with stock hg _ui = ui.ui() _ui.setconfig('extensions', 'graphlog', '') extensions.loadall(_ui) graphlog = extensions.find('graphlog') templ = """\ changeset: {rev}:{node|short} branch: {branches} tags: {tags} summary: {desc|firstline} files: {files} """ _ui.pushbuffer() graphlog.graphlog(_ui, repo, rev=None, template=templ) return _ui.popbuffer() def draw(self, repo): sys.stdout.write(self.getgraph(repo)) durin42-hgsubversion-77b22e5b4ea6/tests/test_utility_commands.py0000644000000000000000000003325612043462603023174 0ustar 00000000000000import test_util import os import unittest import re from hgext import rebase from mercurial import hg from mercurial import revlog from mercurial import context from mercurial import node from mercurial import commands from mercurial import util as hgutil from hgsubversion import util from hgsubversion import svncommands from hgsubversion import verify from hgsubversion import wrappers expected_info_output = '''URL: %(repourl)s/%(branch)s Repository Root: %(repourl)s Repository UUID: df2126f7-00ab-4d49-b42c-7e981dde0bcf Revision: %(rev)s Node Kind: directory Last Changed Author: durin Last Changed Rev: %(rev)s Last Changed Date: %(date)s ''' def repourl(repo_path): return util.normalize_url(test_util.fileurl(repo_path)) class UtilityTests(test_util.TestBase): def test_info_output(self): repo, repo_path = self.load_and_fetch('two_heads.svndump') hg.update(self.repo, 'the_branch') u = self.ui() u.pushbuffer() svncommands.info(u, self.repo) actual = u.popbuffer() expected = (expected_info_output % {'date': '2008-10-08 01:39:05 +0000 (Wed, 08 Oct 2008)', 'repourl': repourl(repo_path), 'branch': 'branches/the_branch', 'rev': 5, }) self.assertMultiLineEqual(actual, expected) hg.update(self.repo, 'default') u.pushbuffer() svncommands.info(u, self.repo) actual = u.popbuffer() expected = (expected_info_output % {'date': '2008-10-08 01:39:29 +0000 (Wed, 08 Oct 2008)', 'repourl': repourl(repo_path), 'branch': 'trunk', 'rev': 6, }) self.assertMultiLineEqual(actual, expected) hg.update(self.repo, 'default') u.pushbuffer() svncommands.info(u, self.repo, rev=3) actual = u.popbuffer() expected = (expected_info_output % {'date': '2008-10-08 01:39:05 +0000 (Wed, 08 Oct 2008)', 'repourl': repourl(repo_path), 'branch': 'branches/the_branch', 'rev': 5, }) self.assertMultiLineEqual(actual, expected) destpath = self.wc_path + '_clone' test_util.hgclone(u, self.repo, destpath) repo2 = hg.repository(u, destpath) repo2.ui.setconfig('paths', 'default-push', self.repo.ui.config('paths', 'default')) hg.update(repo2, 'default') svncommands.rebuildmeta(u, repo2, []) u.pushbuffer() svncommands.info(u, repo2) actual = u.popbuffer() expected = (expected_info_output % {'date': '2008-10-08 01:39:29 +0000 (Wed, 08 Oct 2008)', 'repourl': repourl(repo_path), 'branch': 'trunk', 'rev': 6, }) self.assertMultiLineEqual(actual, expected) def test_info_single(self): repo, repo_path = self.load_and_fetch('two_heads.svndump', subdir='trunk') hg.update(self.repo, 'tip') u = self.ui() u.pushbuffer() svncommands.info(u, self.repo) actual = u.popbuffer() expected = (expected_info_output % {'date': '2008-10-08 01:39:29 +0000 (Wed, 08 Oct 2008)', 'repourl': repourl(repo_path), 'branch': 'trunk', 'rev': 6, }) self.assertMultiLineEqual(expected, actual) def test_missing_metadata(self): self._load_fixture_and_fetch('two_heads.svndump') test_util.rmtree(self.repo.join('svn')) self.assertRaises(hgutil.Abort, self.repo.svnmeta) self.assertRaises(hgutil.Abort, svncommands.info, self.ui(), repo=self.repo, args=[]) self.assertRaises(hgutil.Abort, svncommands.genignore, self.ui(), repo=self.repo, args=[]) os.remove(self.repo.join('hgrc')) self.assertRaises(hgutil.Abort, self.repo.svnmeta) self.assertRaises(hgutil.Abort, svncommands.info, self.ui(), repo=self.repo, args=[]) self.assertRaises(hgutil.Abort, svncommands.genignore, self.ui(), repo=self.repo, args=[]) self.assertRaises(hgutil.Abort, svncommands.rebuildmeta, self.ui(), repo=self.repo, args=[]) def test_parent_output(self): self._load_fixture_and_fetch('two_heads.svndump') u = self.ui() u.pushbuffer() parents = (self.repo['the_branch'].node(), revlog.nullid,) def filectxfn(repo, memctx, path): return context.memfilectx(path=path, data='added', islink=False, isexec=False, copied=False) ctx = context.memctx(self.repo, parents, 'automated test', ['added_bogus_file', 'other_added_file', ], filectxfn, 'testy', '2008-12-21 16:32:00 -0500', {'branch': 'localbranch', }) new = self.repo.commitctx(ctx) hg.update(self.repo, new) wrappers.parents(lambda x, y: None, u, self.repo, svn=True) actual = u.popbuffer() self.assertEqual(actual, '3:4e256962fc5d\n') hg.update(self.repo, 'default') # Make sure styles work u.pushbuffer() wrappers.parents(lambda x, y: None, u, self.repo, svn=True, style='compact') actual = u.popbuffer() self.assertEqual(actual, '4:1083037b18d8\n') # custom templates too u.pushbuffer() wrappers.parents(lambda x, y: None, u, self.repo, svn=True, template='{node}\n') actual = u.popbuffer() self.assertEqual(actual, '1083037b18d85cd84fa211c5adbaeff0fea2cd9f\n') u.pushbuffer() wrappers.parents(lambda x, y: None, u, self.repo, svn=True) actual = u.popbuffer() self.assertEqual(actual, '4:1083037b18d8\n') def test_outgoing_output(self): 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): return context.memfilectx(path=path, data='added', islink=False, isexec=False, copied=False) ctx = context.memctx(self.repo, parents, 'automated test', ['added_bogus_file', 'other_added_file', ], filectxfn, 'testy', '2008-12-21 16:32:00 -0500', {'branch': 'localbranch', }) new = self.repo.commitctx(ctx) hg.update(self.repo, new) u.pushbuffer() 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, repourl(repo_path)) actual = u.popbuffer() self.assertEqual(actual, '') def test_rebase(self): self._load_fixture_and_fetch('two_revs.svndump') parents = (self.repo[0].node(), revlog.nullid,) def filectxfn(repo, memctx, path): return context.memfilectx(path=path, data='added', islink=False, isexec=False, copied=False) ctx = context.memctx(self.repo, parents, 'automated test', ['added_bogus_file', 'other_added_file', ], filectxfn, 'testy', '2008-12-21 16:32:00 -0500', {'branch': 'localbranch', }) self.repo.commitctx(ctx) self.assertEqual(self.repo['tip'].branch(), 'localbranch') beforerebasehash = self.repo['tip'].node() hg.update(self.repo, 'tip') wrappers.rebase(rebase.rebase, self.ui(), self.repo, svn=True) self.assertEqual(self.repo['tip'].branch(), 'localbranch') self.assertEqual(self.repo['tip'].parents()[0].parents()[0], self.repo[0]) self.assertNotEqual(beforerebasehash, self.repo['tip'].node()) def test_genignore(self): """ Test generation of .hgignore file. """ repo = self._load_fixture_and_fetch('ignores.svndump', noupdate=False) u = self.ui() u.pushbuffer() 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') def test_genignore_single(self): self._load_fixture_and_fetch('ignores.svndump', subdir='trunk') hg.update(self.repo, 'tip') u = self.ui() u.pushbuffer() svncommands.genignore(u, self.repo, self.wc_path) self.assertMultiLineEqual(open(os.path.join(self.wc_path, '.hgignore')).read(), '.hgignore\nsyntax:glob\nblah\notherblah\nbaz/magic\n') def test_list_authors(self): repo_path = self.load_svndump('replace_trunk_with_branch.svndump') u = self.ui() u.pushbuffer() svncommands.listauthors(u, args=[test_util.fileurl(repo_path)], authors=None) actual = u.popbuffer() self.assertMultiLineEqual(actual, 'Augie\nevil\n') def test_list_authors_map(self): 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(repo_path)], authors=author_path) self.assertMultiLineEqual(open(author_path).read(), 'Augie=\nevil=\n') def test_svnverify(self, stupid=False): repo, repo_path = self.load_and_fetch('binaryfiles.svndump', noupdate=False, stupid=stupid) ret = verify.verify(self.ui(), repo, [], rev=1, stupid=stupid) self.assertEqual(0, ret) repo_path = self.load_svndump('binaryfiles-broken.svndump') u = self.ui() u.pushbuffer() ret = verify.verify(u, repo, [test_util.fileurl(repo_path)], rev=1, stupid=stupid) output = u.popbuffer() self.assertEqual(1, ret) output = re.sub(r'file://\S+', 'file://', output) self.assertMultiLineEqual("""\ verifying d51f46a715a1 against file:// difference in: binary2 unexpected file: binary1 missing file: binary3 """, output) def test_svnverify_stupid(self): self.test_svnverify(True) def test_corruption(self, stupid=False): SUCCESS = 0 FAILURE = 1 repo, repo_path = self.load_and_fetch('correct.svndump', layout='single', subdir='', stupid=stupid) ui = self.ui() self.assertEqual(SUCCESS, verify.verify(ui, self.repo, rev='tip', stupid=stupid)) corrupt_source = test_util.fileurl(self.load_svndump('corrupt.svndump')) repo.ui.setconfig('paths', 'default', corrupt_source) ui.pushbuffer() code = verify.verify(ui, repo, rev='tip') actual = ui.popbuffer() actual = actual.replace(corrupt_source, '$REPO') actual = set(actual.splitlines()) expected = set([ 'verifying 78e965230a13 against $REPO@1', 'missing file: missing-file', 'wrong flags for: executable-file', 'wrong flags for: symlink', 'wrong flags for: regular-file', 'difference in: another-regular-file', 'difference in: regular-file', 'unexpected file: empty-file', ]) self.assertEqual((FAILURE, expected), (code, actual)) def test_corruption_stupid(self): self.test_corruption(True) def test_svnrebuildmeta(self): otherpath = self.load_svndump('binaryfiles-broken.svndump') otherurl = test_util.fileurl(otherpath) self.load_and_fetch('replace_trunk_with_branch.svndump') # rebuildmeta with original repo svncommands.rebuildmeta(self.ui(), repo=self.repo, args=[]) # rebuildmeta with unrelated repo self.assertRaises(hgutil.Abort, svncommands.rebuildmeta, self.ui(), repo=self.repo, args=[otherurl]) # rebuildmeta --unsafe-skip-uuid-check with unrelated repo svncommands.rebuildmeta(self.ui(), repo=self.repo, args=[otherurl], unsafe_skip_uuid_check=True) def suite(): all_tests = [unittest.TestLoader().loadTestsFromTestCase(UtilityTests), ] return unittest.TestSuite(all_tests) durin42-hgsubversion-77b22e5b4ea6/tools/bisect-find-bad.sh0000755000000000000000000000007412043462603021437 0ustar 00000000000000#!/bin/bash . $(dirname $0)/common.sh hg svn verify exit $? durin42-hgsubversion-77b22e5b4ea6/tools/common.sh0000644000000000000000000000122512043462603020010 0ustar 00000000000000function verify_current_revision() { /bin/rm -rf * exportcmd="svn export `hg svn info 2> /dev/null | grep '^URL: ' | sed 's/URL: //'`@`hg svn info | grep ^Revision | sed 's/.*: //;s/ .*//'` . --force" `echo $exportcmd` > /dev/null x=$? if [[ "$x" != "0" ]] ; then echo $exportcmd echo 'export failed!' return 255 fi if [[ "`hg st | wc -l | python -c 'import sys; print sys.stdin.read().strip()'`" == "0" ]] ; then return 0 else if [[ $1 != "keep" ]] ; then revert_all_files fi return 1 fi } function revert_all_files() { hg revert --all hg purge } durin42-hgsubversion-77b22e5b4ea6/tools/verify-all-heads.sh0000755000000000000000000000021012043462603021650 0ustar 00000000000000#!/bin/sh . $(dirname $0)/common.sh for b in `hg branches -aq` ; do hg co $b || break echo verifying $b hg svn verify done