14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
from bzrlib import osutils, ui, urlutils
18
from bzrlib.errors import InvalidRevisionId, NoSuchRevision
19
from bzrlib.inventory import Inventory
20
from bzrlib.repository import InterRepository
21
from bzrlib.trace import info
22
from bzrlib.tsort import topo_sort
17
from cStringIO import (
21
from dulwich.client import (
22
SimpleFetchGraphWalker,
24
from dulwich.objects import (
34
from bzrlib.errors import (
38
from bzrlib.inventory import (
41
from bzrlib.repository import (
44
from bzrlib.tsort import (
48
from bzrlib.plugins.git.converter import (
24
51
from bzrlib.plugins.git.repository import (
29
from bzrlib.plugins.git.converter import GitObjectConverter
30
from bzrlib.plugins.git.remote import RemoteGitRepository
33
from dulwich.client import SimpleFetchGraphWalker
34
from dulwich.objects import Commit
36
from cStringIO import StringIO
56
from bzrlib.plugins.git.remote import (
39
61
class BzrFetchGraphWalker(object):
78
def import_git_blob(repo, mapping, path, blob, inv, parent_invs, gitmap, executable):
100
def import_git_blob(texts, mapping, path, blob, inv, parent_invs, shagitmap,
79
102
"""Import a git blob object into a bzr repository.
81
:param repo: bzr repository
104
:param texts: VersionedFiles to add to
82
105
:param path: Path in the tree
83
106
:param blob: A git blob
107
:return: Inventory entry
85
109
file_id = mapping.generate_file_id(path)
86
text_revision = inv.revision_id
87
repo.texts.add_lines((file_id, text_revision),
88
[(file_id, p[file_id].revision) for p in parent_invs if file_id in p],
89
osutils.split_lines(blob.data))
90
110
ie = inv.add_path(path, "file", file_id)
91
ie.revision = text_revision
92
111
ie.text_size = len(blob.data)
93
112
ie.text_sha1 = osutils.sha_string(blob.data)
94
113
ie.executable = executable
95
gitmap._idmap.add_entry(blob.sha().hexdigest(), "blob", (ie.file_id, ie.revision))
98
def import_git_tree(repo, mapping, path, tree, inv, parent_invs,
99
gitmap, lookup_object):
114
# See if this is the same revision as one of the parents unchanged
116
for pinv in parent_invs:
117
if not file_id in pinv:
119
if pinv[file_id].text_sha1 == ie.text_sha1:
120
ie.revision = pinv[file_id].revision
122
parent_keys.append((file_id, pinv[file_id].revision))
123
ie.revision = inv.revision_id
124
assert file_id is not None
125
assert ie.revision is not None
126
texts.add_lines((file_id, ie.revision), parent_keys,
127
osutils.split_lines(blob.data))
128
shagitmap.add_entry(blob.sha().hexdigest(), "blob",
129
(ie.file_id, ie.revision))
133
def import_git_tree(texts, mapping, path, tree, inv, parent_invs, shagitmap,
100
135
"""Import a git tree object into a bzr repository.
102
:param repo: A Bzr repository object
137
:param texts: VersionedFiles object to add to
103
138
:param path: Path in the tree
104
139
:param tree: A git tree object
105
140
:param inv: Inventory object
107
142
file_id = mapping.generate_file_id(path)
108
text_revision = inv.revision_id
109
repo.texts.add_lines((file_id, text_revision),
110
[(file_id, p[file_id].revision) for p in parent_invs if file_id in p],
112
143
ie = inv.add_path(path, "directory", file_id)
113
ie.revision = text_revision
114
gitmap._idmap.add_entry(tree.sha().hexdigest(), "tree", (file_id, text_revision))
146
for pinv in parent_invs:
147
if not file_id in pinv:
150
tree_sha = shagitmap.lookup_tree(path, pinv[file_id].revision)
154
if tree_sha == tree.id:
155
ie.revision = pinv[file_id].revision
157
parent_keys.append((file_id, pinv[file_id].revision))
158
if ie.revision is None:
159
ie.revision = inv.revision_id
160
texts.add_lines((file_id, ie.revision), parent_keys, [])
161
shagitmap.add_entry(tree.id, "tree", (file_id, ie.revision))
115
162
for mode, name, hexsha in tree.entries():
116
163
entry_kind = (mode & 0700000) / 0100000
117
164
basename = name.decode("utf-8")
119
166
child_path = name
121
168
child_path = urlutils.join(path, name)
169
obj = lookup_object(hexsha)
122
170
if entry_kind == 0:
123
tree = lookup_object(hexsha)
124
import_git_tree(repo, mapping, child_path, tree, inv, parent_invs, gitmap, lookup_object)
171
import_git_tree(texts, mapping, child_path, obj, inv, parent_invs,
172
shagitmap, lookup_object)
125
173
elif entry_kind == 1:
126
blob = lookup_object(hexsha)
127
174
fs_mode = mode & 0777
128
import_git_blob(repo, mapping, child_path, blob, inv, parent_invs, gitmap, bool(fs_mode & 0111))
175
import_git_blob(texts, mapping, child_path, obj, inv, parent_invs,
176
shagitmap, bool(fs_mode & 0111))
130
178
raise AssertionError("Unknown blob kind, perms=%r." % (mode,))
133
182
def import_git_objects(repo, mapping, object_iter, target_git_object_retriever,
167
217
return object_iter[sha]
168
218
return target_git_object_retriever[sha]
169
219
parent_invs = [repo.get_inventory(r) for r in rev.parent_ids]
170
import_git_tree(repo, mapping, "", root_tree, inv, parent_invs,
171
target_git_object_retriever, lookup_object)
220
import_git_tree(repo.texts, mapping, "", root_tree, inv, parent_invs,
221
target_git_object_retriever._idmap, lookup_object)
172
222
repo.add_revision(rev.revision_id, rev, inv)
223
target_git_object_retriever._idmap.commit()
175
226
class InterGitNonGitRepository(InterRepository):
227
"""InterRepository that copies revisions from a Git into a non-Git
177
_matching_repo_format = GitFormat()
230
_matching_repo_format = GitRepositoryFormat()
180
233
def _get_repo_format_to_test():
213
266
create_pb.finished()
215
268
def fetch(self, revision_id=None, pb=None, find_ghosts=False,
269
mapping=None, fetch_spec=None):
270
self.fetch_refs(revision_id=revision_id, pb=pb, find_ghosts=find_ghosts,
271
mapping=mapping, fetch_spec=fetch_spec)
273
def fetch_refs(self, revision_id=None, pb=None, find_ghosts=False,
274
mapping=None, fetch_spec=None):
217
275
if mapping is None:
218
276
mapping = self.source.get_mapping()
219
def determine_wants(heads):
220
if revision_id is None:
277
if revision_id is not None:
278
interesting_heads = [revision_id]
279
elif fetch_spec is not None:
280
interesting_heads = fetch_spec.heads
282
interesting_heads = None
284
def determine_wants(refs):
286
if interesting_heads is None:
287
ret = [sha for (ref, sha) in refs.iteritems() if not ref.endswith("^{}")]
223
ret = [mapping.revision_id_bzr_to_foreign(revision_id)[0]]
289
ret = [mapping.revision_id_bzr_to_foreign(revid)[0] for revid in interesting_heads]
224
290
return [rev for rev in ret if not self.target.has_revision(mapping.revision_id_foreign_to_bzr(rev))]
225
return self.fetch_objects(determine_wants, mapping, pb)
291
self.fetch_objects(determine_wants, mapping, pb)
228
295
def is_compatible(source, target):
246
314
self.fetch(revision_id, pb, find_ghosts=False)
248
316
def fetch(self, revision_id=None, pb=None, find_ghosts=False,
317
mapping=None, fetch_spec=None):
250
318
if mapping is None:
251
319
mapping = self.source.get_mapping()
252
320
def progress(text):
253
info("git: %s", text)
321
trace.info("git: %s", text)
254
322
r = self.target._git
255
if revision_id is None:
256
determine_wants = lambda x: [y for y in x.values() if not y in r.object_store]
323
if revision_id is not None:
258
324
args = [mapping.revision_id_bzr_to_foreign(revision_id)[0]]
325
elif fetch_spec is not None:
326
args = [mapping.revision_id_bzr_to_foreign(revid)[0] for revid in fetch_spec.heads]
327
if fetch_spec is None and revision_id is None:
328
determine_wants = r.object_store.determine_wants_all
259
330
determine_wants = lambda x: [y for y in args if not y in r.object_store]
261
332
graphwalker = SimpleFetchGraphWalker(r.heads().values(), r.get_parents)