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.branch import Branch
20
from bzrlib.commands import Command, Option
21
from bzrlib.repository import Repository
22
from bzrlib.revision import Revision
23
from bzrlib.lazy_import import lazy_import
24
lazy_import(globals(), """
34
class ForeignBranch(Branch):
35
"""Branch that exists in a foreign version control system."""
37
def __init__(self, mapping):
38
self.mapping = mapping
39
super(ForeignBranch, self).__init__()
41
def dpull(self, source, stop_revision=None):
42
"""Pull deltas from another branch.
44
:note: This does not, like pull, retain the revision ids from
45
the source branch and will, rather than adding bzr-specific metadata,
46
push only those semantics of the revision that can be natively
47
represented in this branch.
49
:param source: Source branch
50
:param stop_revision: Revision to pull, defaults to last revision.
52
raise NotImplementedError(self.dpull)
55
class FakeControlFiles(object):
56
"""Dummy implementation of ControlFiles.
58
This is required as some code relies on controlfiles being
60
def get_utf8(self, name):
61
raise errors.NoSuchFile(name)
64
raise errors.NoSuchFile(name)
70
class cmd_dpush(Command):
71
"""Push diffs into a foreign version control system without any
72
Bazaar-specific metadata.
74
This will afterwards rebase the local Bazaar branch on the remote
75
branch unless the --no-rebase option is used, in which case
76
the two branches will be out of sync.
78
takes_args = ['location?']
79
takes_options = ['remember', Option('directory',
80
help='Branch to push from, '
81
'rather than the one containing the working directory.',
85
Option('no-rebase', help="Don't rebase after push")]
87
def run(self, location=None, remember=False, directory=None,
89
from bzrlib import urlutils
90
from bzrlib.bzrdir import BzrDir
91
from bzrlib.errors import BzrCommandError, NoWorkingTree
92
from bzrlib.trace import info
93
from bzrlib.workingtree import WorkingTree
98
source_wt = WorkingTree.open_containing(directory)[0]
99
source_branch = source_wt.branch
100
except NoWorkingTree:
101
source_branch = Branch.open_containing(directory)[0]
103
stored_loc = source_branch.get_push_location()
105
if stored_loc is None:
106
raise BzrCommandError("No push location known or specified.")
108
display_url = urlutils.unescape_for_display(stored_loc,
110
self.outf.write("Using saved location: %s\n" % display_url)
111
location = stored_loc
113
bzrdir = BzrDir.open(location)
114
target_branch = bzrdir.open_branch()
115
target_branch.lock_write()
116
if not isinstance(target_branch, ForeignBranch):
117
info("target branch is not a foreign branch, using regular push.")
118
target_branch.pull(source_branch)
121
revid_map = target_branch.dpull(source_branch)
122
# We successfully created the target, remember it
123
if source_branch.get_push_location() is None or remember:
124
source_branch.set_push_location(target_branch.base)
126
_, old_last_revid = source_branch.last_revision_info()
127
new_last_revid = revid_map[old_last_revid]
128
if source_wt is not None:
129
source_wt.pull(target_branch, overwrite=True,
130
stop_revision=new_last_revid)
132
source_branch.pull(target_branch, overwrite=True,
133
stop_revision=new_last_revid)
136
from unittest import TestSuite
137
from bzrlib.tests import TestUtil
138
loader = TestUtil.TestLoader()
140
testmod_names = ['test_versionedfiles', ]
141
suite.addTest(loader.loadTestsFromModuleNames(testmod_names))
145
def escape_commit_message(message):
146
"""Replace xml-incompatible control characters."""
150
# FIXME: RBC 20060419 this should be done by the revision
151
# serialiser not by commit. Then we can also add an unescaper
152
# in the deserializer and start roundtripping revision messages
153
# precisely. See repository_implementations/test_repository.py
155
# Python strings can include characters that can't be
156
# represented in well-formed XML; escape characters that
157
# aren't listed in the XML specification
158
# (http://www.w3.org/TR/REC-xml/#NT-Char).
159
message, _ = re.subn(
160
u'[^\x09\x0A\x0D\u0020-\uD7FF\uE000-\uFFFD]+',
161
lambda match: match.group(0).encode('unicode_escape'),