~bzr-svn/bzr-svn/trunk

2308 by Jelmer Vernooij
Update copyright years.
1
# Copyright (C) 2006-2009 Jelmer Vernooij <jelmer@samba.org>
1929.1.2 by Jelmer Vernooij
Add experimental svn-serve subcommand.
2
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
2632 by Jelmer Vernooij
Change license back to GPLv2+.
5
# the Free Software Foundation; either version 2 of the License, or
1929.1.2 by Jelmer Vernooij
Add experimental svn-serve subcommand.
6
# (at your option) any later version.
7
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
3015 by Jelmer Vernooij
merge bzr svn-serve back into bzr serve --svn.
16
1929.1.2 by Jelmer Vernooij
Add experimental svn-serve subcommand.
17
"""Subversion server implementation."""
18
3015 by Jelmer Vernooij
merge bzr svn-serve back into bzr serve --svn.
19
20
from bzrlib.plugins.svn import lazy_check_versions
21
lazy_check_versions()
22
2700 by Jelmer Vernooij
Reformat imports.
23
import os
24
import subvertpy
25
from subvertpy import properties
3015 by Jelmer Vernooij
merge bzr svn-serve back into bzr serve --svn.
26
from subvertpy.ra_svn import (
27
    SVN_PORT,
28
    SVNServer,
29
    TCPSVNServer,
30
    )
2700 by Jelmer Vernooij
Reformat imports.
31
from subvertpy.server import (
32
    ServerBackend,
33
    ServerRepositoryBackend,
34
    )
3015 by Jelmer Vernooij
merge bzr svn-serve back into bzr serve --svn.
35
import sys
2700 by Jelmer Vernooij
Reformat imports.
36
import time
37
3015 by Jelmer Vernooij
merge bzr svn-serve back into bzr serve --svn.
38
from bzrlib import (
39
    trace,
40
    urlutils,
41
    )
1929.1.2 by Jelmer Vernooij
Add experimental svn-serve subcommand.
42
from bzrlib.branch import Branch
1929.1.21 by Jelmer Vernooij
Get most of the tree content reporting working.
43
from bzrlib.inventory import Inventory
44
45
from bzrlib.plugins.svn.commit import dir_editor_send_changes
1929.1.2 by Jelmer Vernooij
Add experimental svn-serve subcommand.
46
3015 by Jelmer Vernooij
merge bzr svn-serve back into bzr serve --svn.
47
1929.1.6 by Jelmer Vernooij
Support log -v.
48
def determine_changed_paths(repository, branch_path, rev, revno):
49
    def fixpath(p):
50
        return "%s/%s" % (branch_path, p.encode("utf-8"))
51
    changes = {}
52
    changes[branch_path] = ("M", None, -1) # Always changes
53
    delta = repository.get_revision_delta(rev.revision_id)
54
    for (path, id, kind) in delta.added:
55
        changes[fixpath(path)] = ("A", None, -1)
56
    for (path, id, kind) in delta.removed:
57
        changes[fixpath(path)] = ("D", None, -1)
58
    for (oldpath, newpath, id, kind, text_modified, meta_modified) in delta.renamed:
59
        changes[fixpath(newpath)] = ("A", fixpath(oldpath), revno-1)
60
        changes[fixpath(oldpath)] = ("D", None, -1)
61
    for (path, id, kind, text_modified, meta_modified) in delta.modified:
62
        changes[fixpath(path)] = ("M", None, -1)
63
    return changes
64
65
1929.1.2 by Jelmer Vernooij
Add experimental svn-serve subcommand.
66
class RepositoryBackend(ServerRepositoryBackend):
67
68
    def __init__(self, branch):
69
        self.branch = branch
70
71
    def get_uuid(self):
72
        config = self.branch.get_config()
73
        uuid = config.get_user_option('svn_uuid')
74
        if uuid is None:
75
            import uuid
76
            uuid = uuid.uuid4()
77
            config.set_user_option('svn_uuid', uuid)
78
        return str(uuid)
79
1929.1.4 by Jelmer Vernooij
Claim edit-pipeline capability, to make svn 1.5 happy.
80
    def get_latest_revnum(self):
81
        return self.branch.revno()
82
1929.1.14 by Jelmer Vernooij
Return revision properties.
83
    def _get_revid(self, revnum):
1936 by Jelmer Vernooij
Some work implementing stat() in the server code.
84
        """Find the revision id and branch path a particular revnum refers to."""
1929.1.14 by Jelmer Vernooij
Return revision properties.
85
        return "/trunk", self.branch.get_rev_id(revnum)
86
1929.1.6 by Jelmer Vernooij
Support log -v.
87
    def log(self, send_revision, target_path, start_rev, end_rev, report_changed_paths,
1929.1.3 by Jelmer Vernooij
Override log for server.
88
            strict_node, limit):
89
        i = 0
90
        revno = start_rev
91
        self.branch.repository.lock_read()
92
        try:
93
            # FIXME: check whether start_rev and end_rev actually exist
94
            while revno != end_rev:
95
                #TODO: Honor target_path, strict_node, changed_paths
96
                if end_rev > revno:
97
                    revno+=1
98
                else:
99
                    revno-=1
100
                if limit != 0 and i == limit:
101
                    break
1929.1.6 by Jelmer Vernooij
Support log -v.
102
                if revno > 0:
1929.1.14 by Jelmer Vernooij
Return revision properties.
103
                    (path, revid) = self._get_revid(revno)
104
                    rev = self.branch.repository.get_revision(revid)
1929.1.6 by Jelmer Vernooij
Support log -v.
105
                    if report_changed_paths:
1929.1.14 by Jelmer Vernooij
Return revision properties.
106
                        changes = determine_changed_paths(self.branch.repository, path, rev, revno)
1929.1.6 by Jelmer Vernooij
Support log -v.
107
                    else:
108
                        changes = None
109
                    send_revision(revno, 
110
                            rev.committer, time.strftime("%Y-%m-%dT%H:%M:%S.00000Z", time.gmtime(rev.timestamp)),
111
                            rev.message, changed_paths=changes)
1929.1.3 by Jelmer Vernooij
Override log for server.
112
        finally:
113
            self.branch.repository.unlock()
1929.1.8 by Jelmer Vernooij
Implement check_path, make update abstract.
114
1929.1.13 by Jelmer Vernooij
Support get-locations.
115
    def rev_proplist(self, revnum):
1929.1.14 by Jelmer Vernooij
Return revision properties.
116
        path, revid = self._get_revid(revnum)
117
        rev = self.branch.repository.get_revision(revid)
118
        ret = {
1961 by Jelmer Vernooij
Remove some more unused imports, fix formatting of imports.
119
                properties.PROP_REVISION_AUTHOR: rev.committer, 
120
                properties.PROP_REVISION_DATE: time.strftime("%Y-%m-%dT%H:%M:%S.00000Z", time.gmtime(rev.timestamp)),
121
                properties.PROP_REVISION_LOG: rev.message
1929.1.14 by Jelmer Vernooij
Return revision properties.
122
                }
123
        return ret
1929.1.13 by Jelmer Vernooij
Support get-locations.
124
1929.1.8 by Jelmer Vernooij
Implement check_path, make update abstract.
125
    def update(self, editor, revnum, target_path, recurse=True):
1929.1.17 by Jelmer Vernooij
Fix update.
126
        if revnum is None:
127
            revnum = self.get_latest_revnum()
1929.1.21 by Jelmer Vernooij
Get most of the tree content reporting working.
128
        path, revid = self._get_revid(revnum)
1944 by Jelmer Vernooij
More work getting checkout to work - a simple file diff works now.
129
        relpath = None # FIXME
1929.1.8 by Jelmer Vernooij
Implement check_path, make update abstract.
130
        editor.set_target_revision(revnum)
131
        root = editor.open_root()
1929.1.21 by Jelmer Vernooij
Get most of the tree content reporting working.
132
        old_inv = Inventory(None)
133
        self.branch.repository.lock_read()
134
        try:
135
            new_tree = self.branch.repository.revision_tree(revid)
136
            new_inv = new_tree.inventory
137
            modified_files = {}
138
            visit_dirs = set()
139
            for name, ie in new_inv.iter_entries():
140
                if ie.kind == "directory":
141
                    visit_dirs.add(ie.file_id)
142
                elif ie.kind == 'file':
143
                    modified_files[ie.file_id] = new_tree.get_file_text(ie.file_id)
144
                elif ie.kind == 'symlink':
145
                    modified_files[ie.file_id] = "link %s" % ie.symlink_target
146
147
            dir_editor_send_changes(old_inv, new_inv, "", new_inv.root.file_id, 
1944 by Jelmer Vernooij
More work getting checkout to work - a simple file diff works now.
148
                    root, "svn://localhost/", revnum-1, relpath, 
1929.1.21 by Jelmer Vernooij
Get most of the tree content reporting working.
149
                                modified_files, visit_dirs)
150
            root.close()
151
            editor.close()
152
        finally:
153
            self.branch.repository.unlock()
1929.1.8 by Jelmer Vernooij
Implement check_path, make update abstract.
154
155
    def check_path(self, path, revnum):
1961 by Jelmer Vernooij
Remove some more unused imports, fix formatting of imports.
156
        return subvertpy.NODE_DIR
1929.1.13 by Jelmer Vernooij
Support get-locations.
157
158
    def get_locations(self, path, peg_revnum, revnums):
159
        if path.strip() in ("trunk", ""):
160
            return dict([(rev, path) for rev in revnums])
161
        raise NotImplementedError
1929.1.3 by Jelmer Vernooij
Override log for server.
162
    
1929.1.15 by Jelmer Vernooij
Improve stat() implementation.
163
    def stat(self, path, revnum):
1936 by Jelmer Vernooij
Some work implementing stat() in the server code.
164
        if revnum is None:
165
            revnum = self.get_latest_revnum()
166
        branch_path, revid = self._get_revid(revnum)
167
        inv = self.branch.repository.get_inventory(revid)
1990 by Jelmer Vernooij
Fix svn-serve.
168
        id = inv.path2id(path[len(branch_path):].strip("/"))
1936 by Jelmer Vernooij
Some work implementing stat() in the server code.
169
        if id is None:
170
            return None
171
        ie = inv[id]
172
        ret = { "name": urlutils.basename(path) }
173
        if ie.kind == "directory":
1961 by Jelmer Vernooij
Remove some more unused imports, fix formatting of imports.
174
            ret["kind"] = subvertpy.NODE_DIR
1990 by Jelmer Vernooij
Fix svn-serve.
175
            ret["size"] = 0
1936 by Jelmer Vernooij
Some work implementing stat() in the server code.
176
        else:
3124 by Jelmer Vernooij
Fix some errors pointed out by pyflakes.
177
            ret["kind"] = subvertpy.NODE_FILE
1990 by Jelmer Vernooij
Fix svn-serve.
178
            ret["size"] = ie.text_size
1936 by Jelmer Vernooij
Some work implementing stat() in the server code.
179
        ret["has-props"] = True
180
        ret["created-rev"] = 0 # FIXME
181
        ret["created-date"] = "" # FIXME
182
        ret["last-author"] = "" # FIXME
183
184
        return ret
1929.1.15 by Jelmer Vernooij
Improve stat() implementation.
185
1929.1.2 by Jelmer Vernooij
Add experimental svn-serve subcommand.
186
187
class BzrServerBackend(ServerBackend):
188
1929.1.4 by Jelmer Vernooij
Claim edit-pipeline capability, to make svn 1.5 happy.
189
    def __init__(self, rootdir):
190
        self.rootdir = rootdir
191
1929.1.2 by Jelmer Vernooij
Add experimental svn-serve subcommand.
192
    def open_repository(self, path):
1929.1.4 by Jelmer Vernooij
Claim edit-pipeline capability, to make svn 1.5 happy.
193
        (branch, relpath) = Branch.open_containing(os.path.join(self.rootdir, path))
1929.1.2 by Jelmer Vernooij
Add experimental svn-serve subcommand.
194
        return RepositoryBackend(branch), relpath
3015 by Jelmer Vernooij
merge bzr svn-serve back into bzr serve --svn.
195
196
197
def serve_svn(transport, host=None, port=None, inet=False):
198
    trace.warning("server support in bzr-svn is experimental.")
199
3019 by Jelmer Vernooij
Avoid using lstrip.
200
    if transport.base.startswith("readonly+"):
201
        url = transport.base[len("readonly+"):]
3016 by Jelmer Vernooij
Fix svn-serve.
202
    path = urlutils.local_path_from_url(url)
3015 by Jelmer Vernooij
merge bzr svn-serve back into bzr serve --svn.
203
3016 by Jelmer Vernooij
Fix svn-serve.
204
    backend = BzrServerBackend(path)
3015 by Jelmer Vernooij
merge bzr svn-serve back into bzr serve --svn.
205
    if inet:
206
        def send_fn(data):
207
            sys.stdout.write(data)
208
            sys.stdout.flush()
3016 by Jelmer Vernooij
Fix svn-serve.
209
        server = SVNServer(backend, sys.stdin.read, send_fn)
3015 by Jelmer Vernooij
merge bzr svn-serve back into bzr serve --svn.
210
    else:
211
        if port is None:
212
            port = SVN_PORT
213
        if host is None:
214
            host = '0.0.0.0'
3016 by Jelmer Vernooij
Fix svn-serve.
215
        server = TCPSVNServer(backend, (host, port))
3015 by Jelmer Vernooij
merge bzr svn-serve back into bzr serve --svn.
216
    server.serve()