1
# Copyright (C) 2008 Canonical Ltd
1
# Copyright (C) 2008, 2009, 2010 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 Branch
21
from bzrlib.branch import (
22
25
from bzrlib.commands import Command, Option
23
26
from bzrlib.repository import Repository
24
27
from bzrlib.revision import Revision
35
38
class VcsMapping(object):
36
"""Describes the mapping between the semantics of Bazaar and a foreign vcs.
39
"""Describes the mapping between the semantics of Bazaar and a foreign VCS.
39
42
# Whether this is an experimental mapping that is still open to changes.
42
45
# Whether this mapping supports exporting and importing all bzr semantics.
43
46
roundtripping = False
45
# Prefix used when importing native foreign revisions (not roundtripped)
48
# Prefix used when importing revisions native to the foreign VCS (as
49
# opposed to roundtripping bzr-native revisions) using this mapping.
47
50
revid_prefix = None
49
52
def __init__(self, vcs):
116
119
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)
138
122
class ForeignVcs(object):
139
123
"""A foreign version control system."""
141
def __init__(self, mapping_registry):
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
142
136
self.mapping_registry = mapping_registry
144
138
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)
153
156
class ForeignVcsRegistry(registry.Registry):
154
157
"""Registry for Foreign VCSes.
176
179
:param revid: The bzr revision id
177
180
:return: tuple with foreign revid and vcs mapping
182
if not ":" in revid or not "-" in revid:
180
183
raise errors.InvalidRevisionId(revid, None)
182
185
foreign_vcs = self.get(revid.split("-")[0])
222
225
"""Get the default mapping for this repository."""
223
226
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))
249
229
class ForeignBranch(Branch):
250
230
"""Branch that exists in a foreign version control system."""
253
233
self.mapping = mapping
254
234
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)
273
237
def update_workingtree_fileids(wt, target_tree):
274
238
"""Update the file ids in a working tree based on another tree.
297
261
class cmd_dpush(Command):
298
"""Push diffs into a foreign version control system without any
299
Bazaar-specific metadata.
262
__doc__ = """Push into a different VCS without any custom bzr metadata.
301
This will afterwards rebase the local Bazaar branch on the remote
264
This will afterwards rebase the local branch on the remote
302
265
branch unless the --no-rebase option is used, in which case
303
the two branches will be out of sync.
266
the two branches will be out of sync after the push.
306
269
takes_args = ['location?']
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.")]
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.'),
315
def run(self, location=None, remember=False, directory=None,
284
def run(self, location=None, remember=False, directory=None,
285
no_rebase=False, strict=None):
317
286
from bzrlib import urlutils
318
287
from bzrlib.bzrdir import BzrDir
319
288
from bzrlib.errors import BzrCommandError, NoWorkingTree
320
from bzrlib.trace import info
321
289
from bzrlib.workingtree import WorkingTree
323
291
if directory is None:
328
296
except NoWorkingTree:
329
297
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.')
331
304
stored_loc = source_branch.get_push_location()
332
305
if location is None:
333
306
if stored_loc is None:
341
314
bzrdir = BzrDir.open(location)
342
315
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)
347
316
target_branch.lock_write()
349
revid_map = dpull(source_branch)
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))
350
324
# We successfully created the target, remember it
351
325
if source_branch.get_push_location() is None or remember:
352
326
source_branch.set_push_location(target_branch.base)
362
336
update_workingtree_fileids(source_wt, target)
364
338
source_wt.unlock()
339
push_result.report(self.outf)
366
341
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)