1
# Copyright (C) 2008 Jelmer Vernooij <jelmer@samba.org>
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.
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.
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
17
"""Foreign branch utilities."""
19
from bzrlib import errors, registry
20
from bzrlib.branch import Branch
21
from bzrlib.commands import Command, Option
22
from bzrlib.trace import info
25
class VcsMapping(object):
26
"""Describes the mapping between the semantics of Bazaar and a foreign vcs.
33
def revision_id_bzr_to_foreign(self, bzr_revid):
34
"""Parse a bzr revision id and convert it to a foreign revid.
36
:param bzr_revid: The bzr revision id (a string).
37
:return: A foreign revision id, can be any sort of object.
39
raise NotImplementedError(self.revision_id_bzr_to_foreign)
41
def revision_id_foreign_to_bzr(self, foreign_revid):
42
"""Parse a foreign revision id and convert it to a bzr revid.
44
:param foreign_revid: Foreign revision id, can be any sort of object.
45
:return: A bzr revision id.
47
raise NotImplementedError(self.revision_id_foreign_to_bzr)
50
class VcsMappingRegistry(registry.Registry):
51
"""Registry for Bazaar<->foreign VCS mappings.
53
There should be one instance of this registry for every foreign VCS.
56
def register(self, key, factory, help):
57
"""Register a mapping between Bazaar and foreign VCS semantics.
59
The factory must be a callable that takes one parameter: the key.
60
It must produce an instance of VcsMapping when called.
62
registry.Registry.register(self, key, factory, help)
64
def set_default(self, key):
65
"""Set the 'default' key to be a clone of the supplied key.
67
This method must be called once and only once.
69
self._set_default_key(key)
71
def get_default(self):
72
"""Convenience function for obtaining the default mapping to use."""
73
return self.get(self._get_default_key())
76
class ForeignBranch(Branch):
78
def __init__(self, mapping):
79
super(ForeignBranch, self).__init__()
80
self.mapping = mapping
82
def dpull(self, source, stop_revision=None):
83
raise NotImplementedError(self.pull)
86
class FakeControlFiles(object):
87
"""Dummy implementation of ControlFiles.
89
This is required as some code relies on controlfiles being
91
def get_utf8(self, name):
92
raise errors.NoSuchFile(name)
95
raise errors.NoSuchFile(name)
101
class cmd_dpush(Command):
102
"""Push diffs into a foreign version control system without any
103
Bazaar-specific metadata.
105
This will afterwards rebase the local Bazaar branch on the remote
106
branch unless the --no-rebase option is used, in which case
107
the two branches will be out of sync.
109
takes_args = ['location?']
110
takes_options = ['remember', Option('directory',
111
help='Branch to push from, '
112
'rather than the one containing the working directory.',
116
Option('no-rebase', help="Don't rebase after push")]
118
def run(self, location=None, remember=False, directory=None,
120
from bzrlib import urlutils
121
from bzrlib.bzrdir import BzrDir
122
from bzrlib.branch import Branch
123
from bzrlib.errors import BzrCommandError, NoWorkingTree
124
from bzrlib.workingtree import WorkingTree
126
if directory is None:
129
source_wt = WorkingTree.open_containing(directory)[0]
130
source_branch = source_wt.branch
131
except NoWorkingTree:
132
source_branch = Branch.open_containing(directory)[0]
134
stored_loc = source_branch.get_push_location()
136
if stored_loc is None:
137
raise BzrCommandError("No push location known or specified.")
139
display_url = urlutils.unescape_for_display(stored_loc,
141
self.outf.write("Using saved location: %s\n" % display_url)
142
location = stored_loc
144
bzrdir = BzrDir.open(location)
145
target_branch = bzrdir.open_branch()
146
target_branch.lock_write()
147
if not isinstance(target_branch, ForeignBranch):
148
info("target branch is not a foreign branch, using regular push.")
149
target_branch.pull(source_branch)
152
revid_map = target_branch.dpull(source_branch)
153
# We successfully created the target, remember it
154
if source_branch.get_push_location() is None or remember:
155
source_branch.set_push_location(target_branch.base)
157
_, old_last_revid = source_branch.last_revision_info()
158
new_last_revid = revid_map[old_last_revid]
159
if source_wt is not None:
160
source_wt.pull(target_branch, overwrite=True,
161
stop_revision=new_last_revid)
163
source_branch.pull(target_branch, overwrite=True,
164
stop_revision=new_last_revid)
167
from unittest import TestSuite
168
from bzrlib.tests import TestUtil
169
loader = TestUtil.TestLoader()
171
import test_versionedfiles
172
testmod_names = ['test_versionedfiles',]
173
suite.addTest(loader.loadTestsFromModuleNames(testmod_names))
176
def escape_commit_message(message):
177
"""Replace xml-incompatible control characters."""
181
# FIXME: RBC 20060419 this should be done by the revision
182
# serialiser not by commit. Then we can also add an unescaper
183
# in the deserializer and start roundtripping revision messages
184
# precisely. See repository_implementations/test_repository.py
186
# Python strings can include characters that can't be
187
# represented in well-formed XML; escape characters that
188
# aren't listed in the XML specification
189
# (http://www.w3.org/TR/REC-xml/#NT-Char).
190
message, _ = re.subn(
191
u'[^\x09\x0A\x0D\u0020-\uD7FF\uE000-\uFFFD]+',
192
lambda match: match.group(0).encode('unicode_escape'),