1
# Copyright (C) 2008-2011 Canonical Ltd
1
# Copyright (C) 2008 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
18
18
"""Foreign branch utilities."""
21
from bzrlib.branch import (
21
from bzrlib.branch import Branch
24
22
from bzrlib.commands import Command, Option
25
23
from bzrlib.repository import Repository
26
24
from bzrlib.revision import Revision
28
26
lazy_import(globals(), """
29
27
from bzrlib import (
36
35
class VcsMapping(object):
37
"""Describes the mapping between the semantics of Bazaar and a foreign VCS.
36
"""Describes the mapping between the semantics of Bazaar and a foreign vcs.
40
39
# Whether this is an experimental mapping that is still open to changes.
43
42
# Whether this mapping supports exporting and importing all bzr semantics.
44
43
roundtripping = False
46
# Prefix used when importing revisions native to the foreign VCS (as
47
# opposed to roundtripping bzr-native revisions) using this mapping.
45
# Prefix used when importing native foreign revisions (not roundtripped)
48
47
revid_prefix = None
50
49
def __init__(self, vcs):
117
116
self.mapping = mapping
119
def show_foreign_properties(rev):
120
"""Custom log displayer for foreign revision identifiers.
122
:param rev: Revision object.
124
# Revision comes directly from a foreign repository
125
if isinstance(rev, ForeignRevision):
126
return rev.mapping.vcs.show_foreign_revid(rev.foreign_revid)
128
# Revision was once imported from a foreign repository
130
foreign_revid, mapping = \
131
foreign_vcs_registry.parse_revision_id(rev.revision_id)
132
except errors.InvalidRevisionId:
135
return mapping.vcs.show_foreign_revid(foreign_revid)
120
138
class ForeignVcs(object):
121
139
"""A foreign version control system."""
125
repository_format = None
127
def __init__(self, mapping_registry, abbreviation=None):
128
"""Create a new foreign vcs instance.
130
:param mapping_registry: Registry with mappings for this VCS.
131
:param abbreviation: Optional abbreviation ('bzr', 'svn', 'git', etc)
133
self.abbreviation = abbreviation
141
def __init__(self, mapping_registry):
134
142
self.mapping_registry = mapping_registry
136
144
def show_foreign_revid(self, foreign_revid):
144
def serialize_foreign_revid(self, foreign_revid):
145
"""Serialize a foreign revision id for this VCS.
147
:param foreign_revid: Foreign revision id
148
:return: Bytestring with serialized revid, will not contain any
151
raise NotImplementedError(self.serialize_foreign_revid)
154
153
class ForeignVcsRegistry(registry.Registry):
155
154
"""Registry for Foreign VCSes.
177
176
:param revid: The bzr revision id
178
177
:return: tuple with foreign revid and vcs mapping
180
if not ":" in revid or not "-" in revid:
181
180
raise errors.InvalidRevisionId(revid, None)
183
182
foreign_vcs = self.get(revid.split("-")[0])
223
222
"""Get the default mapping for this repository."""
224
223
raise NotImplementedError(self.get_default_mapping)
225
def get_inventory_xml(self, revision_id):
226
"""See Repository.get_inventory_xml()."""
227
return self.serialise_inventory(self.get_inventory(revision_id))
229
def get_inventory_sha1(self, revision_id):
230
"""Get the sha1 for the XML representation of an inventory.
232
:param revision_id: Revision id of the inventory for which to return
237
return osutils.sha_string(self.get_inventory_xml(revision_id))
239
def get_revision_xml(self, revision_id):
240
"""Return the XML representation of a revision.
242
:param revision_id: Revision for which to return the XML.
245
return self._serializer.write_revision_to_string(
246
self.get_revision(revision_id))
227
249
class ForeignBranch(Branch):
228
250
"""Branch that exists in a foreign version control system."""
231
253
self.mapping = mapping
232
254
super(ForeignBranch, self).__init__()
256
def dpull(self, source, stop_revision=None):
257
"""Pull deltas from another branch.
259
:note: This does not, like pull, retain the revision ids from
260
the source branch and will, rather than adding bzr-specific
261
metadata, push only those semantics of the revision that can be
262
natively represented by this branch' VCS.
264
:param source: Source branch
265
:param stop_revision: Revision to pull, defaults to last revision.
266
:return: Dictionary mapping revision ids from the source branch
267
to new revision ids in the target branch, for each
268
revision that was pull.
270
raise NotImplementedError(self.dpull)
235
273
def update_workingtree_fileids(wt, target_tree):
236
274
"""Update the file ids in a working tree based on another tree.
259
297
class cmd_dpush(Command):
260
__doc__ = """Push into a different VCS without any custom bzr metadata.
298
"""Push diffs into a foreign version control system without any
299
Bazaar-specific metadata.
262
This will afterwards rebase the local branch on the remote
301
This will afterwards rebase the local Bazaar branch on the remote
263
302
branch unless the --no-rebase option is used, in which case
264
the two branches will be out of sync after the push.
303
the two branches will be out of sync.
267
306
takes_args = ['location?']
271
help='Branch to push from, '
272
'rather than the one containing the working directory.',
276
Option('no-rebase', help="Do not rebase after push."),
278
help='Refuse to push if there are uncommitted changes in'
279
' the working tree, --no-strict disables the check.'),
307
takes_options = ['remember', Option('directory',
308
help='Branch to push from, '
309
'rather than the one containing the working directory.',
313
Option('no-rebase', help="Do not rebase after push.")]
282
def run(self, location=None, remember=False, directory=None,
283
no_rebase=False, strict=None):
315
def run(self, location=None, remember=False, directory=None,
284
317
from bzrlib import urlutils
285
318
from bzrlib.bzrdir import BzrDir
286
319
from bzrlib.errors import BzrCommandError, NoWorkingTree
320
from bzrlib.trace import info
287
321
from bzrlib.workingtree import WorkingTree
289
323
if directory is None:
294
328
except NoWorkingTree:
295
329
source_branch = Branch.open(directory)
297
if source_wt is not None:
298
source_wt.check_changed_or_out_of_date(
299
strict, 'dpush_strict',
300
more_error='Use --no-strict to force the push.',
301
more_warning='Uncommitted changes will not be pushed.')
302
331
stored_loc = source_branch.get_push_location()
303
332
if location is None:
304
333
if stored_loc is None:
312
341
bzrdir = BzrDir.open(location)
313
342
target_branch = bzrdir.open_branch()
343
dpull = getattr(target_branch, "dpull", None)
345
raise BzrCommandError("%r is not a foreign branch, use "
346
"regular push." % target_branch)
314
347
target_branch.lock_write()
317
push_result = source_branch.push(target_branch, lossy=True)
318
except errors.LossyPushToSameVCS:
319
raise BzrCommandError("%r and %r are in the same VCS, lossy "
320
"push not necessary. Please use regular push." %
321
(source_branch, target_branch))
349
revid_map = dpull(source_branch)
322
350
# We successfully created the target, remember it
323
351
if source_branch.get_push_location() is None or remember:
324
352
source_branch.set_push_location(target_branch.base)