dsoprea / PySvn

Lightweight Subversion library for Python.
GNU General Public License v2.0
217 stars 145 forks source link

Mishandling '@' symbol in svn remote client recursive list #127

Open chendrix4 opened 6 years ago

chendrix4 commented 6 years ago

Per SVN Book: Peg and Operative Revisions,

The perceptive reader is probably wondering at this point whether the peg revision syntax causes problems for working copy paths or URLs that actually have at signs in them. After all, how does svn know whether news@11 is the name of a directory in my tree or just a syntax for “revision 11 of news”? Thankfully, while svn will always assume the latter, there is a trivial workaround. You need only append an at sign to the end of the path, such as news@11@. svn cares only about the last at sign in the argument, and it is not considered illegal to omit a literal peg revision specifier after that at sign. This workaround even applies to paths that end in an at sign—you would use filename@@ to talk about a file named filename@.

I am trying to walk an SVN directory that looks like

dir1/
    dir1_base-master@1q2w3e4r/
    dir1_base-master@5a6s7d8f/
    ...

and the client is returning `E205000: Syntax error parsing peg revision \'1q2w3e4r\'\r\n' or whatever the string is after the @ symbol.

Is there any way to escape this?

chendrix4 commented 6 years ago

A partial fix is:

def list_recursive(self, rel_path=None, yield_dirs=False, path_filter_cb=None):
    q = [rel_path]
    while q:
        current_rel_path = q[0]
        del q[0]

        for entry in self.list(extended=True, rel_path=current_rel_path):
            if entry['is_directory'] is True:
                #if '@' in entry['name']:
                #    entry['name'] = entry['name'] + '@'
                if current_rel_path is not None:
                    next_rel_path = os.path.join(current_rel_path, entry['name'])

But this breaks down pretty quickly when you try to open subdirectories (i.e. dir1@1q2w3e4r@/subdir1 does not exist)

chendrix4 commented 6 years ago

Okay I was able to fix it as follows:

In common.py:CommonClient.list

full_url_or_path = self.__url_or_path
    if rel_path is not None:
        full_url_or_path += '/' + rel_path

    # FIX    
    if '@' in full_url_or_path and full_url_or_path[-1] != '@':
        full_url_or_path += '@'

Obviously, if your file ends with '@' in the first place, this doesn't work, but I don't care about resolving the nth degree of edge cases.

I'll leave it to you whether you want to close this or not.