Handle initial branch after push of custom revisions.
[jelmer/subvertpy.git] / revids.py
1 # Copyright (C) 2006-2007 Jelmer Vernooij <jelmer@samba.org>
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
5 # the Free Software Foundation; either version 2 of the License, or
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
16
17 from bzrlib.errors import (InvalidRevisionId, NoSuchRevision, 
18                            NotBranchError, UninitializableFormat)
19
20 MAPPING_VERSION = 3
21 REVISION_ID_PREFIX = "svn-v%d-" % MAPPING_VERSION
22
23 import urllib
24
25 try:
26     import sqlite3
27 except ImportError:
28     from pysqlite2 import dbapi2 as sqlite3
29
30 def escape_svn_path(x):
31     if isinstance(x, unicode):
32         x = x.encode("utf-8")
33     return urllib.quote(x, "")
34 unescape_svn_path = urllib.unquote
35
36
37 def parse_svn_revision_id(revid):
38     """Parse an existing Subversion-based revision id.
39
40     :param revid: The revision id.
41     :raises: InvalidRevisionId
42     :return: Tuple with uuid, branch path and revision number.
43     """
44
45     assert revid is not None
46     assert isinstance(revid, basestring)
47
48     if not revid.startswith(REVISION_ID_PREFIX):
49         raise InvalidRevisionId(revid, "")
50
51     try:
52         (version, uuid, branch_path, srevnum)= revid.split(":")
53     except ValueError:
54         raise InvalidRevisionId(revid, "")
55
56     revid = revid[len(REVISION_ID_PREFIX):]
57
58     return (uuid, unescape_svn_path(branch_path), int(srevnum))
59
60
61 def generate_svn_revision_id(uuid, revnum, path, scheme="undefined"):
62     """Generate a unambiguous revision id. 
63     
64     :param uuid: UUID of the repository.
65     :param revnum: Subversion revision number.
66     :param path: Branch path.
67     :param scheme: Name of the branching scheme in use
68
69     :return: New revision id.
70     """
71     assert isinstance(revnum, int)
72     assert isinstance(path, basestring)
73     assert revnum >= 0
74     assert revnum > 0 or path == "", \
75             "Trying to generate revid for (%r,%r)" % (path, revnum)
76     return "%s%s:%s:%s:%d" % (REVISION_ID_PREFIX, scheme, uuid, \
77                    escape_svn_path(path.strip("/")), revnum)
78
79
80 class RevidMap(object):
81     """Revision id mapping store. 
82
83     Stores mapping from revid -> (path, revnum, scheme)
84     """
85     def __init__(self, cache_db=None):
86         if cache_db is None:
87             self.cachedb = sqlite3.connect(":memory:")
88         else:
89             self.cachedb = cache_db
90         self.cachedb.executescript("""
91         create table if not exists revmap (revid text, path text, min_revnum integer, max_revnum integer, scheme text);
92         create index if not exists revid on revmap (revid);
93         """)
94         self.cachedb.commit()
95     
96     def lookup_revid(self, revid):
97         ret = self.cachedb.execute(
98             "select path, min_revnum, max_revnum, scheme from revmap where revid='%s'" % revid).fetchone()
99         if ret is None:
100             raise NoSuchRevision(self, revid)
101         return (str(ret[0]), ret[1], ret[2], ret[3])
102
103     def lookup_branch_revnum(self, revnum, path):
104         # FIXME: SCHEME MISSING
105         revid = self.cachedb.execute(
106                 "select revid from revmap where max_revnum = min_revnum and min_revnum='%s' and path='%s'" % (revnum, path)).fetchone()
107         if revid is not None:
108             return str(revid[0])
109         return None
110
111     def insert_revid(self, revid, branch, min_revnum, max_revnum, scheme):
112         assert revid is not None and revid != ""
113         self.cachedb.execute("insert into revmap (revid, path, min_revnum, max_revnum, scheme) VALUES (?, ?, ?, ?, ?)", (revid, branch, min_revnum, max_revnum, scheme))