115
119
self.mapping = mapping
118
def show_foreign_properties(rev):
119
"""Custom log displayer for foreign revision identifiers.
121
:param rev: Revision object.
123
# Revision comes directly from a foreign repository
124
if isinstance(rev, ForeignRevision):
125
return rev.mapping.vcs.show_foreign_revid(rev.foreign_revid)
127
# Revision was once imported from a foreign repository
129
foreign_revid, mapping = \
130
foreign_vcs_registry.parse_revision_id(rev.revision_id)
131
except errors.InvalidRevisionId:
134
return mapping.vcs.show_foreign_revid(foreign_revid)
137
122
class ForeignVcs(object):
138
123
"""A foreign version control system."""
140
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
141
136
self.mapping_registry = mapping_registry
143
138
def show_foreign_revid(self, foreign_revid):
144
139
"""Prepare a foreign revision id for formatting using bzr log.
146
141
:param foreign_revid: Foreign revision id.
147
142
:return: Dictionary mapping string keys to string values.
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)
152
156
class ForeignVcsRegistry(registry.Registry):
153
157
"""Registry for Foreign VCSes.
155
There should be one entry per foreign VCS. Example entries would be
159
There should be one entry per foreign VCS. Example entries would be
156
160
"git", "svn", "hg", "darcs", etc.
160
164
def register(self, key, foreign_vcs, help):
221
225
"""Get the default mapping for this repository."""
222
226
raise NotImplementedError(self.get_default_mapping)
224
def get_inventory_xml(self, revision_id):
225
"""See Repository.get_inventory_xml()."""
226
return self.serialise_inventory(self.get_inventory(revision_id))
228
def get_inventory_sha1(self, revision_id):
229
"""Get the sha1 for the XML representation of an inventory.
231
:param revision_id: Revision id of the inventory for which to return
236
return osutils.sha_string(self.get_inventory_xml(revision_id))
238
def get_revision_xml(self, revision_id):
239
"""Return the XML representation of a revision.
241
:param revision_id: Revision for which to return the XML.
244
return self._serializer.write_revision_to_string(
245
self.get_revision(revision_id))
229
class ForeignBranch(Branch):
230
"""Branch that exists in a foreign version control system."""
232
def __init__(self, mapping):
233
self.mapping = mapping
234
super(ForeignBranch, self).__init__()
237
def update_workingtree_fileids(wt, target_tree):
238
"""Update the file ids in a working tree based on another tree.
240
:param wt: Working tree in which to update file ids
241
:param target_tree: Tree to retrieve new file ids from, based on path
243
tt = transform.TreeTransform(wt)
245
for f, p, c, v, d, n, k, e in target_tree.iter_changes(wt):
246
if v == (True, False):
247
trans_id = tt.trans_id_tree_path(p[0])
248
tt.unversion_file(trans_id)
249
elif v == (False, True):
250
trans_id = tt.trans_id_tree_path(p[1])
251
tt.version_file(f, trans_id)
255
if len(wt.get_parent_ids()) == 1:
256
wt.set_parent_trees([(target_tree.get_revision_id(), target_tree)])
258
wt.set_last_revision(target_tree.get_revision_id())
261
class cmd_dpush(Command):
262
__doc__ = """Push into a different VCS without any custom bzr metadata.
264
This will afterwards rebase the local branch on the remote
265
branch unless the --no-rebase option is used, in which case
266
the two branches will be out of sync after the push.
269
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.'),
284
def run(self, location=None, remember=False, directory=None,
285
no_rebase=False, strict=None):
286
from bzrlib import urlutils
287
from bzrlib.bzrdir import BzrDir
288
from bzrlib.errors import BzrCommandError, NoWorkingTree
289
from bzrlib.workingtree import WorkingTree
291
if directory is None:
294
source_wt = WorkingTree.open_containing(directory)[0]
295
source_branch = source_wt.branch
296
except NoWorkingTree:
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.')
304
stored_loc = source_branch.get_push_location()
306
if stored_loc is None:
307
raise BzrCommandError("No push location known or specified.")
309
display_url = urlutils.unescape_for_display(stored_loc,
311
self.outf.write("Using saved location: %s\n" % display_url)
312
location = stored_loc
314
bzrdir = BzrDir.open(location)
315
target_branch = bzrdir.open_branch()
316
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))
324
# We successfully created the target, remember it
325
if source_branch.get_push_location() is None or remember:
326
source_branch.set_push_location(target_branch.base)
328
old_last_revid = source_branch.last_revision()
329
source_branch.pull(target_branch, overwrite=True)
330
new_last_revid = source_branch.last_revision()
331
if source_wt is not None and old_last_revid != new_last_revid:
332
source_wt.lock_write()
334
target = source_wt.branch.repository.revision_tree(
336
update_workingtree_fileids(source_wt, target)
339
push_result.report(self.outf)
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)