1
# Copyright (C) 2008-2009 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
20
from bzrlib.branch import Branch
21
from bzrlib.commands import Command, Option
24
class ForeignBranch(Branch):
25
"""Branch that exists in a foreign version control system."""
27
def __init__(self, mapping):
28
self.mapping = mapping
29
super(ForeignBranch, self).__init__()
31
def dpull(self, source, stop_revision=None):
32
"""Pull deltas from another branch.
34
:note: This does not, like pull, retain the revision ids from
35
the source branch and will, rather than adding bzr-specific
36
metadata, push only those semantics of the revision that can be
37
natively represented in this branch.
39
:param source: Source branch
40
:param stop_revision: Revision to pull, defaults to last revision.
41
:return: Revision id map and file id map
43
raise NotImplementedError(self.dpull)
46
class FakeControlFiles(object):
47
"""Dummy implementation of ControlFiles.
49
This is required as some code relies on controlfiles being
51
def get_utf8(self, name):
52
raise errors.NoSuchFile(name)
55
raise errors.NoSuchFile(name)
61
class cmd_dpush(Command):
62
"""Push diffs into a foreign version control system without any
63
Bazaar-specific metadata.
65
This will afterwards rebase the local Bazaar branch on the remote
66
branch unless the --no-rebase option is used, in which case
67
the two branches will be out of sync.
69
takes_args = ['location?']
70
takes_options = ['remember', Option('directory',
71
help='Branch to push from, '
72
'rather than the one containing the working directory.',
76
Option('no-rebase', help="Don't rebase after push")]
78
def run(self, location=None, remember=False, directory=None,
80
from bzrlib import urlutils
81
from bzrlib.bzrdir import BzrDir
82
from bzrlib.errors import BzrCommandError, NoWorkingTree
83
from bzrlib.trace import info
84
from bzrlib.workingtree import WorkingTree
85
from upgrade import update_workinginv_fileids
90
source_wt = WorkingTree.open_containing(directory)[0]
91
source_branch = source_wt.branch
93
source_branch = Branch.open_containing(directory)[0]
95
stored_loc = source_branch.get_push_location()
97
if stored_loc is None:
98
raise BzrCommandError("No push location known or specified.")
100
display_url = urlutils.unescape_for_display(stored_loc,
102
self.outf.write("Using saved location: %s\n" % display_url)
103
location = stored_loc
105
bzrdir = BzrDir.open(location)
106
target_branch = bzrdir.open_branch()
107
target_branch.lock_write()
109
if getattr(target_branch, "dpull", None) is None:
110
info("target branch is not a foreign branch, using regular push.")
111
target_branch.pull(source_branch)
114
revid_map = target_branch.dpull(source_branch)
115
# We successfully created the target, remember it
116
if source_branch.get_push_location() is None or remember:
117
source_branch.set_push_location(target_branch.base)
119
_, old_last_revid = source_branch.last_revision_info()
120
new_last_revid = revid_map[old_last_revid]
121
if source_wt is not None:
122
source_wt.pull(target_branch, overwrite=True,
123
stop_revision=new_last_revid)
124
source_wt.lock_write()
126
update_workinginv_fileids(source_wt,
127
source_wt.branch.repository.get_inventory(old_last_revid),
128
source_wt.branch.repository.get_inventory(new_last_revid))
132
source_branch.pull(target_branch, overwrite=True,
133
stop_revision=new_last_revid)
135
target_branch.unlock()
138
from unittest import TestSuite
139
from bzrlib.tests import TestUtil
140
loader = TestUtil.TestLoader()
142
testmod_names = ['test_versionedfiles', ]
143
suite.addTest(loader.loadTestsFromModuleNames(testmod_names))
147
def escape_commit_message(message):
148
"""Replace xml-incompatible control characters."""
152
# FIXME: RBC 20060419 this should be done by the revision
153
# serialiser not by commit. Then we can also add an unescaper
154
# in the deserializer and start roundtripping revision messages
155
# precisely. See repository_implementations/test_repository.py
157
# Python strings can include characters that can't be
158
# represented in well-formed XML; escape characters that
159
# aren't listed in the XML specification
160
# (http://www.w3.org/TR/REC-xml/#NT-Char).
161
message, _ = re.subn(
162
u'[^\x09\x0A\x0D\u0020-\uD7FF\uE000-\uFFFD]+',
163
lambda match: match.group(0).encode('unicode_escape'),