/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to object_store.py

  • Committer: Jelmer Vernooij
  • Date: 2018-05-05 18:26:04 UTC
  • mfrom: (0.200.1933 work)
  • mto: (0.200.1934 work)
  • mto: This revision was merged to the branch mainline in revision 6960.
  • Revision ID: jelmer@jelmer.uk-20180505182604-rf3zyekbhwhlwddi
Merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
31
31
    )
32
32
from dulwich.pack import (
33
33
    pack_objects_to_data,
 
34
    PackData,
 
35
    Pack,
34
36
    )
35
37
 
36
38
from ... import (
37
39
    errors,
38
40
    lru_cache,
39
41
    trace,
 
42
    osutils,
40
43
    ui,
41
44
    urlutils,
42
45
    )
53
56
    )
54
57
from .mapping import (
55
58
    default_mapping,
56
 
    directory_to_tree,
 
59
    entry_mode,
57
60
    extract_unusual_modes,
58
61
    mapping_registry,
59
62
    symlink_to_blob,
66
69
import stat
67
70
 
68
71
 
 
72
BANNED_FILENAMES = ['.git']
 
73
 
 
74
 
69
75
def get_object_store(repo, mapping=None):
70
76
    git = getattr(repo, "_git", None)
71
77
    if git is not None:
168
174
            expected_sha))
169
175
 
170
176
 
 
177
def directory_to_tree(path, children, lookup_ie_sha1, unusual_modes, empty_file_name,
 
178
                      allow_empty=False):
 
179
    """Create a Git Tree object from a Bazaar directory.
 
180
 
 
181
    :param path: directory path
 
182
    :param children: Children inventory entries
 
183
    :param lookup_ie_sha1: Lookup the Git SHA1 for a inventory entry
 
184
    :param unusual_modes: Dictionary with unusual file modes by file ids
 
185
    :param empty_file_name: Name to use for dummy files in empty directories,
 
186
        None to ignore empty directories.
 
187
    """
 
188
    tree = Tree()
 
189
    for value in children:
 
190
        if value.name in BANNED_FILENAMES:
 
191
            continue
 
192
        child_path = osutils.pathjoin(path, value.name)
 
193
        try:
 
194
            mode = unusual_modes[child_path]
 
195
        except KeyError:
 
196
            mode = entry_mode(value)
 
197
        hexsha = lookup_ie_sha1(child_path, value)
 
198
        if hexsha is not None:
 
199
            tree.add(value.name.encode("utf-8"), mode, hexsha)
 
200
    if not allow_empty and len(tree) == 0:
 
201
        # Only the root can be an empty tree
 
202
        if empty_file_name is not None:
 
203
            tree.add(empty_file_name, stat.S_IFREG | 0644, Blob().id)
 
204
        else:
 
205
            return None
 
206
    return tree
 
207
 
 
208
 
171
209
def _tree_to_objects(tree, parent_trees, idmap, unusual_modes,
172
 
                     dummy_file_name=None):
 
210
                     dummy_file_name=None, add_cache_entry=None):
173
211
    """Iterate over the objects that were introduced in a revision.
174
212
 
175
213
    :param idmap: id map
181
219
    """
182
220
    dirty_dirs = set()
183
221
    new_blobs = []
 
222
    new_contents = {}
184
223
    shamap = {}
185
224
    try:
186
225
        base_tree = parent_trees[0]
209
248
    # Find all the changed blobs
210
249
    for (file_id, path, changed_content, versioned, parent, name, kind,
211
250
         executable) in tree.iter_changes(base_tree):
 
251
        if name[1] in BANNED_FILENAMES:
 
252
            continue
212
253
        if kind[1] == "file":
213
 
            if changed_content:
 
254
            sha1 = tree.get_file_sha1(path[1], file_id)
 
255
            blob_id = None
 
256
            try:
 
257
                (pfile_id, prevision) = find_unchanged_parent_ie(file_id, kind[1], sha1, other_parent_trees)
 
258
            except KeyError:
 
259
                pass
 
260
            else:
 
261
                # It existed in one of the parents, with the same contents.
 
262
                # So no need to yield any new git objects.
214
263
                try:
215
 
                    (pfile_id, prevision) = find_unchanged_parent_ie(file_id, kind[1], tree.get_file_sha1(path[1], file_id), other_parent_trees)
 
264
                    blob_id = idmap.lookup_blob_id(
 
265
                        pfile_id, prevision)
216
266
                except KeyError:
217
 
                    pass
218
 
                else:
219
 
                    try:
220
 
                        shamap[file_id] = idmap.lookup_blob_id(
221
 
                            pfile_id, prevision)
222
 
                    except KeyError:
 
267
                    if not changed_content:
223
268
                        # no-change merge ?
224
269
                        blob = Blob()
225
270
                        blob.data = tree.get_file_text(path[1], file_id)
226
 
                        shamap[file_id] = blob.id
227
 
            if not file_id in shamap:
 
271
                        blob_id = blob.id
 
272
            if blob_id is None:
228
273
                new_blobs.append((path[1], file_id))
 
274
            else:
 
275
                shamap[path[1]] = blob_id
 
276
                if add_cache_entry is not None:
 
277
                    add_cache_entry(("blob", blob_id), (file_id, tree.get_file_revision(path[1])), path[1])
229
278
        elif kind[1] == "symlink":
230
 
            if changed_content:
231
 
                target = tree.get_symlink_target(path[1], file_id)
232
 
                blob = symlink_to_blob(target)
233
 
                shamap[file_id] = blob.id
234
 
                try:
235
 
                    find_unchanged_parent_ie(file_id, kind[1], target, other_parent_trees)
236
 
                except KeyError:
 
279
            target = tree.get_symlink_target(path[1], file_id)
 
280
            blob = symlink_to_blob(target)
 
281
            shamap[path[1]] = blob.id
 
282
            if add_cache_entry is not None:
 
283
                add_cache_entry(blob, (file_id, tree.get_file_revision(path[1])), path[1])
 
284
            try:
 
285
                find_unchanged_parent_ie(file_id, kind[1], target, other_parent_trees)
 
286
            except KeyError:
 
287
                if changed_content:
237
288
                    yield path[1], blob, (file_id, tree.get_file_revision(path[1], file_id))
238
 
        elif kind[1] not in (None, "directory"):
 
289
        elif kind[1] is None:
 
290
            shamap[path[1]] = None
 
291
        elif kind[1] != 'directory':
239
292
            raise AssertionError(kind[1])
240
 
        for p in parent:
241
 
            if p and tree.has_id(p) and tree.kind(tree.id2path(p)) == "directory":
242
 
                dirty_dirs.add(p)
 
293
        for p in path:
 
294
            if p is None:
 
295
                continue
 
296
            dirty_dirs.add(osutils.dirname(p))
243
297
 
244
298
    # Fetch contents of the blobs that were changed
245
299
    for (path, file_id), chunks in tree.iter_files_bytes(
246
300
        [(path, (path, file_id)) for (path, file_id) in new_blobs]):
247
301
        obj = Blob()
248
302
        obj.chunked = chunks
 
303
        if add_cache_entry is not None:
 
304
            add_cache_entry(obj, (file_id, tree.get_file_revision(path)), path)
249
305
        yield path, obj, (file_id, tree.get_file_revision(path, file_id))
250
 
        shamap[file_id] = obj.id
 
306
        shamap[path] = obj.id
251
307
 
252
308
    for path in unusual_modes:
253
 
        parent_path = posixpath.dirname(path)
254
 
        file_id = tree.path2id(parent_path)
255
 
        if file_id is None:
256
 
            raise AssertionError("Unable to find file id for %r" % parent_path)
257
 
        dirty_dirs.add(file_id)
258
 
 
259
 
    try:
260
 
        inv = tree.root_inventory
261
 
    except AttributeError:
262
 
        inv = tree.inventory
263
 
 
264
 
    trees = {}
265
 
    while dirty_dirs:
266
 
        new_dirs = set()
267
 
        for file_id in dirty_dirs:
268
 
            if file_id is None or not inv.has_id(file_id):
269
 
                continue
270
 
            trees[inv.id2path(file_id)] = file_id
271
 
            ie = inv.get_entry(file_id)
272
 
            if ie.parent_id is not None:
273
 
                new_dirs.add(ie.parent_id)
274
 
        dirty_dirs = new_dirs
275
 
 
276
 
    def ie_to_hexsha(ie):
 
309
        dirty_dirs.add(posixpath.dirname(path))
 
310
 
 
311
    for dir in list(dirty_dirs):
 
312
        for parent in osutils.parent_directories(dir):
 
313
            if parent in dirty_dirs:
 
314
                break
 
315
            dirty_dirs.add(parent)
 
316
 
 
317
    if dirty_dirs:
 
318
        dirty_dirs.add('')
 
319
 
 
320
    def ie_to_hexsha(path, ie):
277
321
        try:
278
 
            return shamap[ie.file_id]
 
322
            return shamap[path]
279
323
        except KeyError:
280
 
            # FIXME: Should be the same as in parent
281
 
            if ie.kind in ("file", "symlink"):
282
 
                try:
283
 
                    return idmap.lookup_blob_id(ie.file_id, ie.revision)
284
 
                except KeyError:
285
 
                    # no-change merge ?
286
 
                    blob = Blob()
287
 
                    path = tree.id2path(ie.file_id)
288
 
                    blob.data = tree.get_file_text(path, ie.file_id)
289
 
                    return blob.id
290
 
            elif ie.kind == "directory":
291
 
                # Not all cache backends store the tree information,
292
 
                # calculate again from scratch
293
 
                ret = directory_to_tree(ie.children, ie_to_hexsha,
294
 
                    unusual_modes, dummy_file_name, ie.parent_id is None)
295
 
                if ret is None:
296
 
                    return ret
297
 
                return ret.id
298
 
            else:
299
 
                raise AssertionError
300
 
 
301
 
    for path in sorted(trees.keys(), reverse=True):
302
 
        file_id = trees[path]
303
 
        if tree.kind(path, file_id) != 'directory':
304
 
            raise AssertionError
305
 
        ie = inv.get_entry(file_id)
306
 
        obj = directory_to_tree(ie.children, ie_to_hexsha, unusual_modes,
307
 
            dummy_file_name, path == "")
308
 
        if obj is not None:
309
 
            yield path, obj, (file_id, )
310
 
            shamap[file_id] = obj.id
 
324
            pass
 
325
        # FIXME: Should be the same as in parent
 
326
        if ie.kind in ("file", "symlink"):
 
327
            try:
 
328
                return idmap.lookup_blob_id(ie.file_id, ie.revision)
 
329
            except KeyError:
 
330
                # no-change merge ?
 
331
                blob = Blob()
 
332
                blob.data = tree.get_file_text(path, ie.file_id)
 
333
                if add_cache_entry is not None:
 
334
                    add_cache_entry(blob, (ie.file_id, ie.revision), path)
 
335
                return blob.id
 
336
        elif ie.kind == "directory":
 
337
            # Not all cache backends store the tree information,
 
338
            # calculate again from scratch
 
339
            ret = directory_to_tree(path, ie.children.values(), ie_to_hexsha,
 
340
                unusual_modes, dummy_file_name, ie.parent_id is None)
 
341
            if ret is None:
 
342
                return ret
 
343
            return ret.id
 
344
        else:
 
345
            raise AssertionError
 
346
 
 
347
    for path in sorted(dirty_dirs, reverse=True):
 
348
        if not tree.has_filename(path):
 
349
            continue
 
350
 
 
351
        if tree.kind(path) != 'directory':
 
352
            raise AssertionError
 
353
 
 
354
        obj = Tree()
 
355
        for value in tree.iter_child_entries(path):
 
356
            if value.name in BANNED_FILENAMES:
 
357
                trace.warning('not exporting %s with banned filename %s',
 
358
                              value.kind, value.name)
 
359
                continue
 
360
            child_path = osutils.pathjoin(path, value.name)
 
361
            try:
 
362
                mode = unusual_modes[child_path]
 
363
            except KeyError:
 
364
                mode = entry_mode(value)
 
365
            hexsha = ie_to_hexsha(child_path, value)
 
366
            if hexsha is not None:
 
367
                obj.add(value.name.encode("utf-8"), mode, hexsha)
 
368
 
 
369
        if len(obj) > 0:
 
370
            file_id = tree.path2id(path)
 
371
            if add_cache_entry is not None:
 
372
                add_cache_entry(obj, (file_id, tree.get_revision_id()), path)
 
373
            yield path, obj, (file_id, tree.get_revision_id())
 
374
            shamap[path] = obj.id
311
375
 
312
376
 
313
377
class PackTupleIterable(object):
355
419
 
356
420
    def _update_sha_map(self, stop_revision=None):
357
421
        if not self.is_locked():
358
 
            raise AssertionError()
 
422
            raise errors.LockNotHeld(self)
359
423
        if self._map_updated:
360
424
            return
361
425
        if (stop_revision is not None and
430
494
                file_ids[path] = ie.file_id
431
495
        return self.mapping.export_fileid_map(file_ids)
432
496
 
433
 
    def _revision_to_objects(self, rev, tree, lossy):
 
497
    def _revision_to_objects(self, rev, tree, lossy, add_cache_entry=None):
434
498
        """Convert a revision to a set of git objects.
435
499
 
436
500
        :param rev: Bazaar revision object
443
507
            [p for p in rev.parent_ids if p in present_parents])
444
508
        root_tree = None
445
509
        for path, obj, bzr_key_data in _tree_to_objects(tree, parent_trees,
446
 
                self._cache.idmap, unusual_modes, self.mapping.BZR_DUMMY_FILE):
 
510
                self._cache.idmap, unusual_modes,
 
511
                self.mapping.BZR_DUMMY_FILE, add_cache_entry):
447
512
            if path == "":
448
513
                root_tree = obj
449
514
                root_key_data = bzr_key_data
450
515
                # Don't yield just yet
451
516
            else:
452
 
                yield path, obj, bzr_key_data
 
517
                yield path, obj
453
518
        if root_tree is None:
454
519
            # Pointless commit - get the tree sha elsewhere
455
520
            if not rev.parent_ids:
457
522
            else:
458
523
                base_sha1 = self._lookup_revision_sha1(rev.parent_ids[0])
459
524
                root_tree = self[self[base_sha1].tree]
460
 
            root_key_data = (tree.get_root_id(), )
 
525
            root_key_data = (tree.get_root_id(), tree.get_revision_id())
461
526
        if not lossy and self.mapping.BZR_FILE_IDS_FILE is not None:
462
527
            b = self._create_fileid_map_blob(tree)
463
528
            if b is not None:
464
529
                root_tree[self.mapping.BZR_FILE_IDS_FILE] = (
465
530
                    (stat.S_IFREG | 0644), b.id)
466
 
                yield self.mapping.BZR_FILE_IDS_FILE, b, None
467
 
        yield "", root_tree, root_key_data
 
531
                yield self.mapping.BZR_FILE_IDS_FILE, b
 
532
        if add_cache_entry is not None:
 
533
            add_cache_entry(root_tree, root_key_data, "")
 
534
        yield "", root_tree
468
535
        if not lossy:
469
536
            testament3 = StrictTestament3(rev, tree)
470
537
            verifiers = { "testament3-sha1": testament3.as_sha1() }
479
546
            pass
480
547
        else:
481
548
            _check_expected_sha(foreign_revid, commit_obj)
482
 
        yield None, commit_obj, None
 
549
        if add_cache_entry is not None:
 
550
            add_cache_entry(commit_obj, verifiers, None)
 
551
 
 
552
        yield None, commit_obj
483
553
 
484
554
    def _get_updater(self, rev):
485
555
        return self._cache.get_updater(rev)
489
559
        tree = self.tree_cache.revision_tree(rev.revision_id)
490
560
        updater = self._get_updater(rev)
491
561
        # FIXME JRV 2011-12-15: Shouldn't we try both values for lossy ?
492
 
        for path, obj, ie in self._revision_to_objects(rev, tree, lossy=(not self.mapping.roundtripping)):
 
562
        for path, obj in self._revision_to_objects(
 
563
                rev, tree, lossy=(not self.mapping.roundtripping),
 
564
                add_cache_entry=updater.add_object):
493
565
            if isinstance(obj, Commit):
494
 
                testament3 = StrictTestament3(rev, tree)
495
 
                ie = { "testament3-sha1": testament3.as_sha1() }
496
 
            updater.add_object(obj, ie, path)
 
566
                commit_obj = obj
497
567
        commit_obj = updater.finish()
498
568
        return commit_obj.id
499
569
 
524
594
        :param fileid: fileid in the tree.
525
595
        :param revision: Revision of the tree.
526
596
        """
527
 
        def get_ie_sha1(entry):
 
597
        def get_ie_sha1(path, entry):
528
598
            if entry.kind == "directory":
529
599
                try:
530
600
                    return self._cache.idmap.lookup_tree_id(entry.file_id,
549
619
                return self._lookup_revision_sha1(entry.reference_revision)
550
620
            else:
551
621
                raise AssertionError("unknown entry kind '%s'" % entry.kind)
552
 
        try:
553
 
            inv = bzr_tree.root_inventory
554
 
        except AttributeError:
555
 
            inv = bzr_tree.inventory
556
 
        tree = directory_to_tree(inv.get_entry(fileid).children,
 
622
        path = bzr_tree.id2path(fileid)
 
623
        tree = directory_to_tree(
 
624
                path,
 
625
                bzr_tree.iter_child_entries(path),
557
626
                get_ie_sha1, unusual_modes, self.mapping.BZR_DUMMY_FILE,
558
627
                bzr_tree.get_root_id() == fileid)
559
628
        if (bzr_tree.get_root_id() == fileid and
661
730
        return self.lookup_git_shas([sha])[sha]
662
731
 
663
732
    def __getitem__(self, sha):
664
 
        if self._cache.content_cache is not None:
665
 
            try:
666
 
                return self._cache.content_cache[sha]
667
 
            except KeyError:
668
 
                pass
669
733
        for (kind, type_data) in self.lookup_git_sha(sha):
670
734
            # convert object to git object
671
735
            if kind == "commit":
749
813
        ret = PackTupleIterable(self)
750
814
        pb = ui.ui_factory.nested_progress_bar()
751
815
        try:
752
 
            for i, revid in enumerate(todo):
 
816
            for i, revid in enumerate(graph.iter_topo_order(todo)):
753
817
                pb.update("generating git objects", i, len(todo))
754
818
                try:
755
819
                    rev = self.repository.get_revision(revid)
756
820
                except errors.NoSuchRevision:
757
821
                    continue
758
822
                tree = self.tree_cache.revision_tree(revid)
759
 
                for path, obj, ie in self._revision_to_objects(rev, tree, lossy=lossy):
 
823
                for path, obj in self._revision_to_objects(
 
824
                        rev, tree, lossy=lossy):
760
825
                    ret.add(obj.id, path)
761
826
            return ret
762
827
        finally:
768
833
        fd, path = tempfile.mkstemp(suffix=".pack")
769
834
        f = os.fdopen(fd, 'wb')
770
835
        def commit():
771
 
            from dulwich.pack import PackData, Pack
772
836
            from .fetch import import_git_objects
773
837
            os.fsync(fd)
774
838
            f.close()