1
# Copyright (C) 2008, 2009, 2010 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
25
22
from bzrlib.commands import Command, Option
26
23
from bzrlib.repository import Repository
27
24
from bzrlib.revision import Revision
38
35
class VcsMapping(object):
39
"""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.
42
39
# Whether this is an experimental mapping that is still open to changes.
45
42
# Whether this mapping supports exporting and importing all bzr semantics.
46
43
roundtripping = False
48
# Prefix used when importing revisions native to the foreign VCS (as
49
# opposed to roundtripping bzr-native revisions) using this mapping.
45
# Prefix used when importing native foreign revisions (not roundtripped)
50
47
revid_prefix = None
52
49
def __init__(self, vcs):
119
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)
122
138
class ForeignVcs(object):
123
139
"""A foreign version control system."""
127
repository_format = None
129
def __init__(self, mapping_registry, abbreviation=None):
130
"""Create a new foreign vcs instance.
132
:param mapping_registry: Registry with mappings for this VCS.
133
:param abbreviation: Optional abbreviation ('bzr', 'svn', 'git', etc)
135
self.abbreviation = abbreviation
141
def __init__(self, mapping_registry):
136
142
self.mapping_registry = mapping_registry
138
144
def show_foreign_revid(self, foreign_revid):
146
def serialize_foreign_revid(self, foreign_revid):
147
"""Serialize a foreign revision id for this VCS.
149
:param foreign_revid: Foreign revision id
150
:return: Bytestring with serialized revid, will not contain any
153
raise NotImplementedError(self.serialize_foreign_revid)
156
153
class ForeignVcsRegistry(registry.Registry):
157
154
"""Registry for Foreign VCSes.
179
176
:param revid: The bzr revision id
180
177
:return: tuple with foreign revid and vcs mapping
182
if not ":" in revid or not "-" in revid:
183
180
raise errors.InvalidRevisionId(revid, None)
185
182
foreign_vcs = self.get(revid.split("-")[0])
225
222
"""Get the default mapping for this repository."""
226
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))
229
249
class ForeignBranch(Branch):
230
250
"""Branch that exists in a foreign version control system."""
233
253
self.mapping = mapping
234
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)
237
273
def update_workingtree_fileids(wt, target_tree):
238
274
"""Update the file ids in a working tree based on another tree.
261
297
class cmd_dpush(Command):
262
__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.
264
This will afterwards rebase the local branch on the remote
301
This will afterwards rebase the local Bazaar branch on the remote
265
302
branch unless the --no-rebase option is used, in which case
266
the two branches will be out of sync after the push.
303
the two branches will be out of sync.
269
306
takes_args = ['location?']
273
help='Branch to push from, '
274
'rather than the one containing the working directory.',
278
Option('no-rebase', help="Do not rebase after push."),
280
help='Refuse to push if there are uncommitted changes in'
281
' 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.")]
284
def run(self, location=None, remember=False, directory=None,
285
no_rebase=False, strict=None):
315
def run(self, location=None, remember=False, directory=None,
286
317
from bzrlib import urlutils
287
318
from bzrlib.bzrdir import BzrDir
288
319
from bzrlib.errors import BzrCommandError, NoWorkingTree
320
from bzrlib.trace import info
289
321
from bzrlib.workingtree import WorkingTree
291
323
if directory is None:
296
328
except NoWorkingTree:
297
329
source_branch = Branch.open(directory)
299
if source_wt is not None:
300
source_wt.check_changed_or_out_of_date(
301
strict, 'dpush_strict',
302
more_error='Use --no-strict to force the push.',
303
more_warning='Uncommitted changes will not be pushed.')
304
331
stored_loc = source_branch.get_push_location()
305
332
if location is None:
306
333
if stored_loc is None:
314
341
bzrdir = BzrDir.open(location)
315
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)
316
347
target_branch.lock_write()
319
push_result = source_branch.lossy_push(target_branch)
320
except errors.LossyPushToSameVCS:
321
raise BzrCommandError("%r and %r are in the same VCS, lossy "
322
"push not necessary. Please use regular push." %
323
(source_branch, target_branch))
349
revid_map = dpull(source_branch)
324
350
# We successfully created the target, remember it
325
351
if source_branch.get_push_location() is None or remember:
326
352
source_branch.set_push_location(target_branch.base)
336
362
update_workingtree_fileids(source_wt, target)
338
364
source_wt.unlock()
339
push_result.report(self.outf)
341
366
target_branch.unlock()
344
class InterToForeignBranch(InterBranch):
346
def lossy_push(self, stop_revision=None):
347
"""Push deltas into another branch.
349
:note: This does not, like push, retain the revision ids from
350
the source branch and will, rather than adding bzr-specific
351
metadata, push only those semantics of the revision that can be
352
natively represented by this branch' VCS.
354
:param target: Target branch
355
:param stop_revision: Revision to push, defaults to last revision.
356
:return: BranchPushResult with an extra member revidmap:
357
A dictionary mapping revision ids from the target branch
358
to new revision ids in the target branch, for each
359
revision that was pushed.
361
raise NotImplementedError(self.lossy_push)